#include <Wire.h>
#include <TimeLib.h>
#include "RTClib.h" // RealTimeClock Library for DS1307 and DS3231
#include "SdFs.h"
#include "record_queue.h"
#include "input_i2s.h"
#include "mixer.h"
#include "filter_biquad.h"
#include "MAX17043.h"
//Enable record only mode to use device only as recorder
#define DEBUG 1 //uncomment this to enable serial print AT command output
#ifdef DEBUG //TODO: Print filename
#define DEBUG_PRINT(x) \
Serial.print(x); \
Serial.print(" : Line: "); \
Serial.print(__LINE__); \
Serial.println ("");
#else
#define DEBUG_PRINT(x) do {} while (0)
#endif
//************defines*****************//
#define BAUD 115200
#define NEW_BAUDRATE 921600
#define BYTES_IN_HEADER 256
#define BYTES_WAVHEADER 36
#define HEADER_SIZE 264
//***********pin_configuration**********//
#define GSM_RTS 33
#define GSM_CTS 34
#define BUTTON 4
#define LED_REC 36 // RECLED used to indicate initialization
#define LED_UPL 37 // SDLED used for upload indication
#define LED_NETSTAT 38 // NETSTATLED
#define ERR_LED 39 // ERR LED used to indicate Error
#define PWR_PIN 20// PWR pin of GSM
#define NTWRK_STAT 24// Network status pin of GSM
#define MODE_SEL_SWITCH 32 //This slider switch is interfaced externally
#define REC_TIME 10// Length of recorded wav file
//***********MIC constants**********//
#define SAMPLE_RATE 16000
#define MIC_GAIN 15
#define FILE_NAME "P" //Add single char for filename
#define DEVICE_ID "DEV1"
#define FOLDER "DAY"
/************************************************************************************
SDCARD SETTINGS (NEW LIBRARY)
************************************************************************************/
// SD_FAT_TYPE = 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
#define SD_FAT_TYPE 3
// SDCARD_SS_PIN is defined for the built-in SD on some boards.
#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else // SDCARD_SS_PIN
// Assume built-in SD is used.
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif // SDCARD_SS_PIN
// Try to select the best SD card configuration.
#if HAS_SDIO_CLASS
#define SD_CONFIG SdioConfig(FIFO_SDIO)
#elif ENABLE_DEDICATED_SPI
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI)
#else // HAS_SDIO_CLASS
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI)
#endif // HAS_SDIO_CLASS
#if SD_FAT_TYPE == 1
SdFat sd;
File frec;
#elif SD_FAT_TYPE == 2
SdExFat sd;
ExFile file;
#elif SD_FAT_TYPE == 3
SdFs sd;
FsFile frec,ftx;
#else // SD_FAT_TYPE
//#error Invalid SD_FAT_TYPE
#endif // SD_FAT_TYPE
//************************************************************************************//
/*Change the value of recording flag to enable 4k,8k,16k write
1 : 4K writes
2 : 8K writes
3 : 16K writes
4 : 12K writes
comment RECORDING_FLAG to select 512bytes writes
*/
#define RECORDING_FLAG 5
#if RECORDING_FLAG == 1
byte buffer[4096] = {0};
#elif RECORDING_FLAG == 2
byte buffer[8192] = {0};
#elif RECORDING_FLAG == 3
byte buffer[16384] = {0};
#elif RECORDING_FLAG == 4
byte buffer[12288] = {0};
#else
byte buffer[512] = {0};
#endif
RTC_DS1307 RTC;
AudioInputI2S i2s1;
AudioAmplifier amp1;
AudioFilterBiquad biquad1;
AudioRecordQueue queue1;
AudioConnection patchCord1(i2s1, 0, amp1, 0);
AudioConnection patchCord2(amp1, biquad1);
AudioConnection patchCord3(biquad1, queue1);
//********** recording_flags **********************************//
bool recordInProgress = false;
bool recording_flag = false;
bool recording_started = false;
bool first_file_flag = false;
//********** recording_constants **********************************//
#define SUB_CHUNK_1_SIZE 16
#define AUDIO_FORMAT 1 //WAV FILE
#define NUM_CHANNELS 1 //MONO:1 STEREO:2
#define BITS_PER_SAMPLE 16
#define BITS_PER_BYTE 8
#define BYTE_RATE SAMPLE_RATE*NUM_CHANNELS*(BITS_PER_SAMPLE/BITS_PER_BYTE) // SAMPLE_RATE x channels x (BITS_PER_SAMPLE / 8)
#define BLOCK_ALIGN NUM_CHANNELS*(BITS_PER_SAMPLE/BITS_PER_BYTE)
//********** recording_variables **********************************//
unsigned long ChunkSize = 0L;
unsigned long Subchunk2Size = 0L;
unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;
char const* Firm_rev = "Ver1.0";
unsigned long sizeof_icmt_chunk = 0;
unsigned long length_icmt_comment = 200;
char icmt_comment[200]={0};
byte Fdate = 0, Fmonth = 0, Fhour = 0, Fmin = 0, Fsec = 0;
uint16_t Fyear = 0;
char filename[50] = {0};
char folder_buffer[30] = {0};
uint32_t filenumber = 0;
uint16_t folder_variable = 0;
char folder_files[30] = {0};
uint16_t previous_day = 0;
static char header[264] = {0};
//********** recording_functions **********************************//
void startRecording(void);
void stopRecording(void);
void recording(void);
void writeWavHeader(void);
void continueRecording(void);
void dateTime(uint16_t* date, uint16_t* time);
void writeOutHeader(void);
void Set_Icmt_comment(void);
char * wavHeader(void);
//***************************MAIN CODE************************************
void setup()
{
Serial.begin(BAUD); //initialise UART
Wire.begin();
RTC.begin();
DateTime now = RTC.now();
DateTime compiled = DateTime(__DATE__, __TIME__);
if (now.unixtime() < compiled.unixtime()) {
Serial.println("RTC is older than compile time! Updating");
// following line sets the RTC to the date & time this sketch was compiled
RTC.adjust(DateTime(__DATE__, __TIME__));
}
pinMode_setup();
FuelGauge.begin(); //initialise fuel gauge
Fhour = now.hour();Fmin=now.minute();Fsec=now.second();Fdate=now.day();Fmonth=now.month();Fyear=now.year();
setTime(Fhour, Fmin, Fsec, Fdate, Fmonth,Fyear);
SDcard_check();
DEBUG_PRINT((icmt_comment));
microphone_initialization();
queue1.begin();
first_file_flag = true;
}
void loop()
{
recording();
}
//*****************************************************************************************************************/
//Set wave file header with Operator, Device ID, Batt voltage and %
void Set_Icmt_comment(void)
{
snprintf(icmt_comment, sizeof(icmt_comment), "Operator:%s, DeviceID:%s, Batt: %.1fV, Pct:%.1f, Signal: %s-%s, Firm_rev: %s, Timestamp:%d/%02d/%02d-%02d:%02d:%02d, Latitude:%s, Longitude:%s,Clock:%d ", "NA",DEVICE_ID, FuelGauge.voltage(), FuelGauge.percent(),"NA","NA",Firm_rev,year(),month(),day(),hour(),minute(),second(),"NA","NA",F_CPU);
DEBUG_PRINT((icmt_comment));
}
//*****************************************************************************************************************/
//initialize microphone and sdcard
void microphone_initialization()
{
AudioMemory(200);
amp1.gain(MIC_GAIN);
biquad1.setHighpass(0, 200, 0.707); //200 is cutoff frequency
setI2SFreq(SAMPLE_RATE);
}
//*****************************************************************************************************************/
void SDcard_check(void)
{
// if (!(sd.begin(SD_CONFIG)))
if (!(sd.begin(SdioConfig(FIFO_SDIO))))
{
DEBUG_PRINT(("SD Error!"));
// stop here, but print a message repetitively
digitalWrite(LED_UPL,HIGH); // SD LED set
while (1)
{
DEBUG_PRINT(("Unable to access the SD card"));
digitalWrite(ERR_LED,HIGH); // ERRLED set
delay(500);
digitalWrite(ERR_LED,LOW); // ERRLED set
delay(500);
}
}
previous_day = day();
FsDateTime::callback = dateTime; //callback function to set time on recorded files
}
//*************************Pin initialisation********************************************
void pinMode_setup()
{
pinMode(LED_REC, OUTPUT);
pinMode(ERR_LED,OUTPUT);
pinMode(LED_UPL,OUTPUT);
pinMode(GSM_RTS, OUTPUT);
pinMode(GSM_CTS, INPUT);
pinMode(PWR_PIN, OUTPUT);
pinMode(NTWRK_STAT, INPUT);
pinMode(LED_NETSTAT,OUTPUT);
pinMode(BUTTON,INPUT_PULLUP);
pinMode(MODE_SEL_SWITCH,INPUT);
}
//*********************************Start Recording******************************************************
void startRecording(void)
{
if((day() > previous_day))
{
previous_day = day();
snprintf(folder_files, sizeof(folder_files), "%s%d%02d%02d_%02d",FOLDER,year(),month(),day(),hour());
if (!sd.mkdir(folder_files))
{
DEBUG_PRINT(("Create Folder1 failed"));
}
}
else if((day() == 1) && (previous_day == 30))
{
previous_day = day();
snprintf(folder_files, sizeof(folder_files), "%s%d%02d%02d_%02d",FOLDER,year(),month(),day(),hour());
if (!sd.mkdir(folder_files))
{
DEBUG_PRINT(("Create Folder1 failed"));
}
}
else if((day() == 1) && (previous_day == 31))
{
previous_day = day();
snprintf(folder_files, sizeof(folder_files), "%s%d%02d%02d_%02d",FOLDER,year(),month(),day(),hour());
if (!sd.mkdir(folder_files))
{
DEBUG_PRINT(("Create Folder1 failed"));
}
}
else if(first_file_flag)
{
first_file_flag = false;
snprintf(folder_files, sizeof(folder_files), "%s%d%02d%02d_%02d",FOLDER,year(),month(),day(),hour());
if (!sd.mkdir(folder_files))
{
DEBUG_PRINT(("Create Folder1 failed"));
}
}
recordInProgress = true;
digitalWrite(LED_UPL, LOW);
DEBUG_PRINT(("startRecording"));
DEBUG_PRINT(("new filename"));
Fhour = hour();Fmin=minute();Fsec=second();Fdate=day();Fmonth=month();Fyear=year();
snprintf(filename, sizeof(filename), "%s/%s%d%02d%02d_%02d%02d%02d.wav",folder_files,FILE_NAME,Fyear, Fmonth, Fdate, Fhour, Fmin,Fsec);
DEBUG_PRINT((filename));
digitalWrite(LED_REC, HIGH);
Set_Icmt_comment();
frec = sd.open(filename,O_CREAT | O_TRUNC |O_RDWR);
DEBUG_PRINT(("File OPEN!"));
recByteSaved = 0L;
recording_started = true;
memcpy(header,wavHeader(),264);
frec.seek(0);
frec.write(header,HEADER_SIZE);
}
//******************************Continue recording**********************************************
void continueRecording(void)
{
if(recording_started)
{
#if RECORDING_FLAG == 1
if (queue1.available() >= 16) //accumulate 4K bytes of data in queue
{
for(int k=0;k<16;k++)
{
uint16_t buff_count = k*256;
memcpy(buffer+buff_count, queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.write(buffer, 4096); // write all 4K bytes to the SD card
recByteSaved += 4096;
}
#elif RECORDING_FLAG == 2
if (queue1.available() >= 32) //accumulate 8K bytes of data in queue
{
for(int k=0;k<32;k++)
{
uint16_t buff_count = k*256;
memcpy(buffer+buff_count, queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.write(buffer, 8192); // write all 8K bytes to the SD card
recByteSaved += 8192;
}
#elif RECORDING_FLAG == 3
if (queue1.available() >= 64) ////accumulate 16K bytes of data in queue
{
for(int k=0;k<64;k++)
{
uint16_t buff_count = k*256;
memcpy(buffer+buff_count, queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.write(buffer, 16384); // write all 16K bytes to the SD card
recByteSaved += 16384;
}
#elif RECORDING_FLAG == 4
if (queue1.available() >= 48) //accumulate 12K bytes of data in queue
{
for(int k=0;k<48;k++)
{
uint16_t buff_count = k*256;
memcpy(buffer+buff_count, queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.write(buffer, 12288); // write all 12K bytes to the SD card
recByteSaved += 12288;
}
#else
if (queue1.available() >= 2) //accumulate 512 bytes of data in queue
{
memcpy(buffer, queue1.readBuffer(), 256);
queue1.freeBuffer();
memcpy(buffer + 256, queue1.readBuffer(), 256);
queue1.freeBuffer();
frec.write(buffer, 512); // write all 512 bytes to the SD card
recByteSaved += 512;
}
#endif
}
}
//***********************************************Stop recording*********************************
void stopRecording(void)
{
uint16_t eof_rec = 0;
DEBUG_PRINT(("stopRecording"));
// frec.truncate();
// writeOutHeader();
memcpy(header,wavHeader(),264);
frec.seek(0);
frec.write(header,HEADER_SIZE);
eof_rec =(Subchunk2Size+HEADER_SIZE);
frec.seek(eof_rec);
frec.close();
digitalWrite(LED_REC, LOW);
digitalWrite(LED_UPL, HIGH);
recording_started = false;
recordInProgress = false;
recording_flag = true;
filenumber++;
}
//**************************************************************************************************
char * wavHeader(void)
{
static char wavheader_buffer[264] = {0};
sizeof_icmt_chunk = (sizeof(icmt_comment)+ 12);
ChunkSize = recByteSaved + BYTES_IN_HEADER;
Subchunk2Size = ChunkSize - BYTES_IN_HEADER;
strcpy(wavheader_buffer,"RIFF");
strcpy(wavheader_buffer+8,"WAVE");
strcpy(wavheader_buffer+12,"fmt ");
strcpy(wavheader_buffer+36,"LIST");
strcpy(wavheader_buffer+44,"INFO");
strcpy(wavheader_buffer+48,"ICMT");
strcpy(wavheader_buffer+56,icmt_comment);
strcpy(wavheader_buffer+256,"data");
*(int32_t*)(wavheader_buffer+4)=ChunkSize;
*(int32_t*)(wavheader_buffer+16)= SUB_CHUNK_1_SIZE;// chunk_size
*(int16_t*)(wavheader_buffer+20)= AUDIO_FORMAT; // PCM
*(int16_t*)(wavheader_buffer+22)=NUM_CHANNELS;// numChannels
*(int32_t*)(wavheader_buffer+24)= SAMPLE_RATE; // sample rate
*(int32_t*)(wavheader_buffer+28)= BYTE_RATE; // byte rate
*(int16_t*)(wavheader_buffer+32)=BLOCK_ALIGN; // block align
*(int16_t*)(wavheader_buffer+34)=BITS_PER_SAMPLE; // bits per sample
*(int32_t*)(wavheader_buffer+40)=sizeof_icmt_chunk;
*(int32_t*)(wavheader_buffer+52)=length_icmt_comment;
*(int32_t*)(wavheader_buffer+260)=Subchunk2Size;
return wavheader_buffer;
}
//**************************************************************************************************
/* User provided date time callback function.
See SdFile::dateTimeCallback() for usage.
*/
void dateTime(uint16_t* date, uint16_t* time)
{
// DateTime now = rtc.now();
// User gets date and time from GPS or real-time
// clock in real callback function
// return date using FAT_DATE macro to format fields
// *date = FAT_DATE(year, month, day);
*date = FAT_DATE(year(), month(), day());
// return time using FAT_TIME macro to format fields
// *time = FAT_TIME(hours, minutes, seconds);
*time = FAT_TIME(hour(), minute(), second());
}
//*****************************************************************************************************************/
//Function to set sample rate
void setI2SFreq(int freq)
{
typedef struct
{
uint8_t mult;
uint16_t div;
} tmclk;
const int numfreqs = 14;
const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)44117.64706 * 2, 96000, 176400, (int)44117.64706 * 4, 192000};
#if (F_PLL==16000000)
const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==168000000)
const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
#elif (F_PLL==180000000)
const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif
for (int f = 0; f < numfreqs; f++) {
if ( freq == samplefreqs[f] ) {
while (I2S0_MCR & I2S_MCR_DUF) ;
I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
return;
}
}
}
//*****************************************************************************************************************/
void recording(void)
{
if (!recordInProgress)
{
startRecording();
}
else if ((recByteSaved < (SAMPLE_RATE * 2 * REC_TIME)))
{
continueRecording();
}
else if (recordInProgress && (recByteSaved >= (SAMPLE_RATE * 2 * REC_TIME)))
{
stopRecording();
}
}