@m-Lewis The code that I sent you records in mp3 format for 10 seconds and then ends. That's why you don't get logs after that.
I noticed that WAV recording and GNSS usage doesn't work on SDK 2.0 together. There is a bug. I'll try to investigate it.
I have two solutions for now:
- You can use MP3 instead of WAV format. It works with SDK2.0
- If you want to use WAV format you can use SDK1.5.1
Below is the code for the second solution:
// GNSS and Audio at the same time example, while writing data to a logfile and output to Serial port
// M. Lewis
bool doAudio = true; // Record Audio
bool doGnss = true; // GNSS/GPS
bool doLed = true; // Blink LED
bool doStream = true; // Stream sampled data lines to Serial output at Sample Rate
bool doFile = true; // Stream sampled data lines to logFile at Sample Rate
#include <Arduino.h>
#include <arch/board/board.h>
#include <SDHCI.h>
#include <File.h>
#include <Storage.h>
#include <elapsedMillis.h>
#include <GNSS.h>
#include <RTC.h> // For setting realtime clock
#include <Audio.h>
SDClass theSD; // This is the SD card class
File logFile; // File object
//char* logName = "/mnt/spif/logfile.log";
char* logName = "logfile.log";
String logString;
elapsedMillis sinceSample;
int sampleMillis = 1000/10; // 10 Hz
////////////// LED Control //////////////
unsigned long blinkStartMS;
#define BLINK_MS 500 // number of millisecs for on or off
int blinkMillis = 1000/2; // 2 Hz
elapsedMillis sinceBlink;
void led_setup() {
Serial.println("led_setup()");
pinMode(LED0, OUTPUT); // Init the on-board SPresense LEDs
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
blinkStartMS = 0 ;
}
void led_loop() {
static bool state = true;
if (sinceBlink < blinkMillis) {
return;
}
sinceBlink = sinceBlink - blinkMillis;
state = ! state;
digitalWrite(PIN_LED0, state==false ? LOW : HIGH);
}
void led_setAll(bool state)
{
digitalWrite(PIN_LED0, state==false ? LOW : HIGH);
digitalWrite(PIN_LED1, state==false ? LOW : HIGH);
digitalWrite(PIN_LED2, state==false ? LOW : HIGH);
digitalWrite(PIN_LED3, state==false ? LOW : HIGH);
}
static void Led_isPosfix(bool state)
{
digitalWrite(PIN_LED1, state==false ? LOW : HIGH);
}
static void Led_isError(bool state)
{
digitalWrite(PIN_LED3, state==false ? LOW : HIGH);
}
////////////// Log File Control //////////////
void file_setup()
{
Serial.println("file_setup() start");
//logFile = Storage.open(logName, FILE_WRITE); // Open the file. Note: only one file can be open at a time
if (! theSD.begin()) {
Serial.print("uSD card is not present!!");
while(1){;}
}
logString = String("MSec");
if (doGnss) {
logString += ",";
logString += gnss_get_columns();
}
logString += "\r\n";
Serial.print(logString.c_str());
logFile = theSD.open(logName, FILE_WRITE); // Open the file. Note: only one file can be open at a time
if (!logFile) {
Serial.print("Could not open logFile");
return;
}
if (logFile.size() < 1) { // Empty or non-existant file. Add header line
logFile.write(logString.c_str(), strlen(logString.c_str()));
logFile.close(); /* Close the file */
//logFile.flush();
}
}
////////////// GNSS File Control //////////////
#define STRING_BUFFER_SIZE 128 /**< %Buffer size */
#define RESTART_CYCLE (60 * 5) /**< positioning test term */
static SpGnss Gnss; /**< SpGnss object */
SpNavData NavData;
uint32_t recordCount;
String gnssLatestString;
void gnss_update_latestString();
const uint16_t FAULT_GPS_TIME = 0x2; // NO GPS Time and Date
const uint16_t FAULT_GPS_FIX = 0x4; // NO GPS Location
uint16_t gps_fault = 0;
/**
* @enum ParamSat
* @brief Satellite system
*/
enum ParamSat {
eSatGps, /**< GPS World wide coverage */
eSatGlonass, /**< GLONASS World wide coverage */
eSatGpsSbas, /**< GPS+SBAS North America */
eSatGpsGlonass, /**< GPS+Glonass World wide coverage */
eSatGpsQz1c, /**< GPS+QZSS_L1CA East Asia & Oceania */
eSatGpsGlonassQz1c, /**< GPS+Glonass+QZSS_L1CA East Asia & Oceania */
eSatGpsQz1cQz1S, /**< GPS+QZSS_L1CA+QZSS_L1S Japan */
};
static enum ParamSat satType = eSatGps; /* Set this parameter depending on your current region. */
void gnss_setSatType()
{
/* Setup GNSS
* It is possible to setup up to two GNSS satellites systems.
* Depending on your location you can improve your accuracy by selecting different GNSS system than the GPS system.
* See: https://developer.sony.com/develop/spresense/developer-tools/get-started-using-nuttx/nuttx-developer-guide#_gnss
* for detailed information.
*/
switch (satType)
{
case eSatGps:
Gnss.select(GPS);
break;
case eSatGpsSbas:
Gnss.select(GPS);
Gnss.select(SBAS);
break;
case eSatGlonass:
Gnss.select(GLONASS);
break;
case eSatGpsGlonass:
Gnss.select(GPS);
Gnss.select(GLONASS);
break;
case eSatGpsQz1c:
Gnss.select(GPS);
Gnss.select(QZ_L1CA);
break;
case eSatGpsQz1cQz1S:
Gnss.select(GPS);
Gnss.select(QZ_L1CA);
Gnss.select(QZ_L1S);
break;
case eSatGpsGlonassQz1c:
default:
Gnss.select(GPS);
Gnss.select(GLONASS);
Gnss.select(QZ_L1CA);
break;
}
}
/**
* @brief Activate GNSS device and start positioning.
*/
void gnss_setup() {
int error_flag = 0;
recordCount = 0;
puts("gnss_setup() Initializing GNSS...");
RTC.begin(); // Initialize RTC at first
gps_fault = FAULT_GPS_FIX | FAULT_GPS_TIME; // assume no GPS time or fix, show a fault
sleep(3); // Wait HW initialization done
led_setAll(true); // Turn on all LEDs
Gnss.setDebugMode(PrintInfo);
if (Gnss.begin() != 0)
{
Serial.println("Gnss begin error!!");
error_flag = 1;
}
else
{
gnss_setSatType(); // Select International Sat Systems to use
if (Gnss.start(COLD_START) != 0) // Start positioning
{
Serial.println("Gnss COLD_START error!!");
error_flag = 1;
}
else
{
Serial.println("Gnss setup OK");
}
}
//Gnss.start1PPS(); /* Start 1PSS output to PIN_D02 */
led_setAll(false); // Turn off all LEDs
if (error_flag == 1)
{
Led_isError(true); // Set error LED3
exit(0);
}
puts("gnss_setup() done\n");
}
/**
* @brief %Print satellite condition.
*/
static void print_condition(SpNavData *pNavData)
{
char StringBuffer[STRING_BUFFER_SIZE];
unsigned long cnt;
snprintf(StringBuffer, STRING_BUFFER_SIZE, "numSatellites:%2d\n", pNavData->numSatellites); // Print satellite count
Serial.print(StringBuffer);
for (cnt = 0; cnt < pNavData->numSatellites; cnt++)
{
const char *pType = "---";
SpSatelliteType sattype = pNavData->getSatelliteType(cnt);
switch (sattype) // Get satellite 3 letter string
{
case GPS: pType = "GPS"; break;
case GLONASS: pType = "GLN"; break;
case QZ_L1CA: pType = "QCA"; break;
case SBAS: pType = "SBA"; break;
case QZ_L1S: pType = "Q1S"; break;
default: pType = "UKN"; break;
}
unsigned long Id = pNavData->getSatelliteId(cnt);
unsigned long Elv = pNavData->getSatelliteElevation(cnt);
unsigned long Azm = pNavData->getSatelliteAzimuth(cnt);
float sigLevel = pNavData->getSatelliteSignalLevel(cnt);
snprintf(StringBuffer, STRING_BUFFER_SIZE, "[%2d] Type:%s, Id:%2d, Elv:%2d, Azm:%3d, CN0:", cnt, pType, Id, Elv, Azm );
Serial.print(StringBuffer);
Serial.println(sigLevel, 6);
}
}
/**
* @brief %Print position information.
*/
static void print_pos(SpNavData *pNavData)
{
static int LastPrintMin = 0;
char StringBuffer[STRING_BUFFER_SIZE];
/* print time */
snprintf(StringBuffer, STRING_BUFFER_SIZE, "%04d/%02d/%02d ", pNavData->time.year, pNavData->time.month, pNavData->time.day);
Serial.print(StringBuffer);
snprintf(StringBuffer, STRING_BUFFER_SIZE, "%02d:%02d:%02d.%06d, ", pNavData->time.hour, pNavData->time.minute, pNavData->time.sec, pNavData->time.usec);
Serial.print(StringBuffer);
/* print satellites count */
snprintf(StringBuffer, STRING_BUFFER_SIZE, "numSat:%2d, ", pNavData->numSatellites);
Serial.print(StringBuffer);
/* print position data */
unsigned char fixMode = pNavData->posFixMode;
switch (fixMode)
{
case 2: Serial.print("2D"); break;
case 3: Serial.print("3D"); break;
case 0: // FixInvalid: //Serial.print("No-Fix, "); break; // FixInvalid:
default: Serial.print("No"); break;
}
Serial.print("-Fix(");
Serial.print(fixMode);
Serial.print("), ");
if (pNavData->posDataExist == 0)
{
Serial.print("No Position");
}
else
{
Serial.print("Lat=");
Serial.print(pNavData->latitude, 6);
Serial.print(", Lon=");
Serial.print(pNavData->longitude, 6);
}
Serial.println("");
if (NavData.time.minute != LastPrintMin) // Print satellite information every minute
{
print_condition(&NavData);
LastPrintMin = NavData.time.minute;
}
}
void gnss_restart()
{
int error_flag = 0;
ledOff(PIN_LED0); // Turn off LED0
Led_isPosfix(false); // Turn off posFix LED1
if (Gnss.stop() != 0)
{
Serial.println("Gnss stop error!!");
error_flag = 1;
}
else if (Gnss.end() != 0)
{
Serial.println("Gnss end error!!");
error_flag = 1;
}
else
{
Serial.println("Gnss stop OK.");
}
if (Gnss.begin() != 0)
{
Serial.println("Gnss begin error!!");
error_flag = 1;
}
else if (Gnss.start(HOT_START) != 0)
{
Serial.println("Gnss hot start error!!");
error_flag = 1;
}
else
{
Serial.println("Gnss restart OK.");
}
if (error_flag == 1)
{
Led_isError(true); // Set error LED3
exit(0);
}
}
void gnss_update_latestString() {
// Following code block was in loop()
bool goodFix;
int upd=Gnss.waitUpdate(0); //-1);
if (upd) // Check update
{
if (upd) { recordCount++; } // New GNSS data has come in
Gnss.getNavData(&NavData); // Get NaviData
goodFix = NavData.posDataExist && (NavData.posFixMode != FixInvalid);
Led_isPosfix( goodFix ); // Set posFix LED1 if there is a Fix
print_pos(&NavData); // Print position information
}
if (!upd) {
return;
}
//Serial.printf("secondsSinceTime()=%.3f\n", GPS.secondsSinceTime());
gnssLatestString = "GPS,";
if (NavData.time.hour < 10) { gnssLatestString += '0'; }
gnssLatestString += NavData.time.hour; gnssLatestString += ':';
if (NavData.time.minute < 10) { gnssLatestString += '0'; }
gnssLatestString += NavData.time.minute; gnssLatestString += ':';
if (NavData.time.sec < 10) { gnssLatestString += '0'; }
gnssLatestString += NavData.time.sec;
// handle millisecs - GPS.milliseconds can fail prior to a fix, then returns a random string
uint16_t millisecs = NavData.time.usec / 1000;
//Serial.printf("Millisec=%u\n", millisecs);
if (millisecs > 1000) {millisecs = 0;}
gnssLatestString += '.';
if (millisecs < 10) {
gnssLatestString += "00";
} else if (millisecs > 9 && millisecs < 100) {
gnssLatestString += "0";
}
gnssLatestString += millisecs;
gnssLatestString += ",";
gnssLatestString += NavData.time.year; gnssLatestString += '/';
gnssLatestString += NavData.time.month; gnssLatestString += '/';
gnssLatestString += NavData.time.day;
char fixBuf[300];
char lat, lon;
//lat = (!NavData.latitude) ? 'n' : NavData.latitude; // needed when never had fix since GPS.lat initialized to 0
//lon = (!NavData.longitude) ? 'w' : NavData.longitude;
lat = 'n';
lon = 'w';
sprintf(fixBuf, ",%.6f,%c,%.6f,%c,%.1f,%.1f,%d,%d,%lu",
NavData.latitude, lat, NavData.longitude, lon, NavData.altitude, NavData.velocity, (int)NavData.numSatellites, NavData.posFixMode, recordCount);
gnssLatestString += fixBuf;
}
char* gnss_get_columns() { // return the column headers
static char buf[200] = "GPS,Time,Date,Lat_deg,N/S,Lon_deg,E/W,Alt_m,Knots,Track_deg,Sats,Fix,Rcvd";
char* s = buf;
return s;
}
char* gnss_get_latest() // return pointer to CSV IMU data string
{
gnss_update_latestString();
gps_fault = 0;
int status = (NavData.posDataExist && (NavData.posFixMode != FixInvalid));
if (! NavData.posDataExist) {
gps_fault |= FAULT_GPS_FIX;
} else {
//gnss_upd_rtc(); // SPresense version of RTC
}
return (char*)gnssLatestString.c_str();
}
bool gnss_waitUpdate(int milliSecs) { // milliSecs to wait, or -1 to not wait. Assumes setup() was already called
bool upd = false;
upd=Gnss.waitUpdate(milliSecs); // Check update
return upd;
}
void gnss_loop() {} // Nothing here, GNSS is run at the much slower Sample rate. See gnss_get_latest() above
////////////// AUDIO RECORDING CONTROL //////////////
extern SDClass theSD; // Declared in sp_main.cpp
AudioClass *theAudio;
File wavFile;
int file_count = 0;
bool ErrEnd = false;
static const uint32_t recording_time = 10; // Recording Time (Seconds) per wav file
static const uint32_t recording_sampling_rate = 48000; // Sample Rate (16000 or 48000)
static const uint8_t recording_channel_number = 1; // Number of Input Mic Channels (1, 2, or 4)
static const uint8_t recording_bit_length = 16; // Audio Sample Bit Length (16 or 24)
static const int32_t recording_byte_per_second = recording_sampling_rate * recording_channel_number * recording_bit_length / 8; // Bytes per Second
static const int32_t recording_size = recording_byte_per_second * recording_time; // Total WAV File Size
static void audio_attention_cb(const ErrorAttentionParam *atprm) // Called when audio internal error occurs
{
switch (atprm->error_code) {
case AS_ATTENTION_CODE_INFORMATION:
case AS_ATTENTION_CODE_WARNING:
ErrEnd = false;
printf("Audio_Attention_CB() info/warn code=0x%x\n", atprm->error_code);
break;
case AS_ATTENTION_CODE_ERROR:
case AS_ATTENTION_CODE_FATAL:
default:
ErrEnd = true;
printf("Audio_Attention_CB() error/fatal code=0x%x\n", atprm->error_code);
}
}
void audio_setup()
{
puts("audio_setup() Initializing the Audio Library...");
theAudio = AudioClass::getInstance();
theAudio->begin(audio_attention_cb);
// Select microphone input device
theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC_A, 210); //210 = 21.0 dB Mic Gain
theAudio->initRecorder(AS_CODECTYPE_WAV,"/mnt/spif/BIN",recording_sampling_rate,recording_bit_length,recording_channel_number);
puts("Setting up the Recorder...");
// Open file for data write on SD card
//wavFile = theSD.open("Sound.wav", FILE_WRITE);
wavFile = theSD.open(String(file_count)+".wav", FILE_WRITE);
/* Verify file open */
if (!wavFile)
{
printf("Error opening the WAV file...");
exit(1);
}
puts("Writing the WAV File Header...");
theAudio->writeWavHeader(wavFile);
puts("audio_setup() done\n");
}
void audio_loop()
{
err_t errStatus; // recording end condition
if (theAudio->getRecordingSize() > recording_size)
{
theAudio->stopRecorder();
sleep(0.1);
errStatus = theAudio->readFrames(wavFile);
theAudio->closeOutputFile(wavFile);
wavFile.close();
file_count++;
goto restartRecording;
}
errStatus = theAudio->readFrames(wavFile); /* Read frames to record in file */
if (errStatus != AUDIOLIB_ECODE_OK)
{
printf("File Ended! =%d\n", errStatus);
theAudio->stopRecorder();
goto restartRecording;
}
if (ErrEnd)
{
printf("Error End\n");
theAudio->stopRecorder();
goto exitSketch;
}
if (file_count > 10)
{
goto exitSketch;
}
return;
restartRecording:
wavFile = theSD.open(String(file_count)+".wav", FILE_WRITE);
puts("Writing the WAV File Header...\n");
theAudio->writeWavHeader(wavFile);
puts("Starting the Recorder...\n");
theAudio->startRecorder();
puts("Next Recording Started...\n");
return;
exitSketch:
puts("Exiting Sketch!\n");
exit(1);
}
////////////// MAIN SETUP() and LOOP() //////////////
void main_setup() { // put your setup code here, to run once:
Serial.begin(115200);
//while (!Serial) { ; } // wait for serial port to connect. Needed for native USB port only
printf("main_setup() initializing logFile %s\n", logName);
if (doFile) { file_setup(); }
if (doAudio) { audio_setup(); }
if (doLed) { led_setup(); }
if (doGnss) { gnss_setup(); }
puts("Start Recording");
theAudio->startRecorder();
Serial.println("\nSetup Complete\n");
}
char* time_getstring() // return pointer to date/time string
{
static String datetime;
datetime = String((int)millis(), (unsigned char)DEC);
return (char*)datetime.c_str();
}
void sampleData() {
if (sinceSample < sampleMillis) { // check if the Sample timer has passed its interval
return;
}
sinceSample = sinceSample - sampleMillis;
// Sampling time has been reached, Sample the Data
char* dataStr;
logString = time_getstring(); // get the time string for the beginning of each line in the file
if (doGnss) {
dataStr = gnss_get_latest();
logString += String(",") + dataStr;
}
if (doStream) {
Serial.println(logString);
}
if (doFile) { // whether to do the data log file
logFile = theSD.open(logName, FILE_WRITE);
logFile.write(logString.c_str(), strlen(logString.c_str()));
logFile.write("\r\n", 2);
logFile.flush();
logFile.close();
}
}
void main_loop()
{
if (doAudio) { audio_loop(); }
if (doGnss) { gnss_loop(); }
if (doLed) { led_loop(); } // Blink SPresense On-board LED
sampleData();
}
void setup() { main_setup(); }
void loop() { main_loop(); }
Let me know if it works.
Best Regards,
Kamil Tomaszewski