To put it into a little bit of context, I'm building a mini satellite which will be dropped from ~1.5km altitude, that measures atmospheric, rotation and location data. I also have a camera because during the fall, it will take images of the ground, which I will then post-process using python scripts. By stopping writing sensor data while the image is being saved, I'm losing ~3s of data, which means approximately 1/4th of the data that could be recorded.
My board is an ESP-Wroom-32, and I'm using an Arducam Mega 5mp, an SD card, and a bunch of sensors (TMP117, DPS310, SAM-M10Q, SGP30, BNO085, BME680, all of which are wired through I2C). I have written a program that measures with the sensors as fast as it can, and saves each measurement onto the SD card into a csv file along with a timestamp (This is the code snippet below). Every 5 seconds, I also want to take a picture and save it to the SD card.
Even if I have the camera and the SD card on different SPI buses it seems that it cannot save the image and sensor data simultaneously, so while it's saving the image I have thought of saving the sensor data into the ESP's buffer until the image is saved and then clear it, but after weeks of trying to make it work I just couldn't.
Are there any better options? Should I just give up on the idea of writing into separate files simultaneously? Any comments are appreciated.
#include <Wire.h>
#include <SdFat.h>
#include <Adafruit_TMP117.h>
#include <Adafruit_DPS310.h>
#include <Adafruit_SGP30.h>
#include <Adafruit_BME680.h>
#include <Adafruit_BNO08x.h>
#include <SparkFun_u-blox_GNSS_Arduino_Library.h>
#include <sh2.h>
// SD Card Configuration
#define SD_CS 15
#define SD_SCK 14
#define SD_MISO 25
#define SD_MOSI 13
SdFat sd;
SdFile dataFile;
SPIClass spiSD(HSPI);
// Sensor Objects
Adafruit_TMP117 tmp117;
Adafruit_DPS310 dps310;
Adafruit_Sensor *dps_pressure;
Adafruit_SGP30 sgp30;
Adafruit_BME680 bme680;
Adafruit_BNO08x bno08x;
SFE_UBLOX_GNSS gps;
// CSV filename
const char* filename = "sensor_data1.csv";
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
spiSD.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
// Initialize SD Card
if (!sd.begin(SdSpiConfig(SD_CS, SHARED_SPI, SD_SCK_MHZ(10), &spiSD))) {
Serial.println("SD Card initialization failed!");
while (1);
}
// Write CSV Header if file does not exist
if (!sd.exists(filename)) {
dataFile.open(filename, O_RDWR | O_CREAT | O_APPEND);
if (dataFile) {
dataFile.println("Timestamp,TMP117_C,DPS310_C,DPS310_hPa,BME680_C,BME680_hPa,BME680_H%,BME680_VOC,SGP30_TVOC_ppb,SGP30_eCO2_ppm,BNO_QuatW,BNO_QuatX,BNO_QuatY,BNO_QuatZ,BNO_AccX,BNO_AccY,BNO_AccZ,BNO_GyroX,BNO_GyroY,BNO_GyroZ,BNO_MagX,BNO_MagY,BNO_MagZ,Latitude,Longitude,Altitude");
dataFile.close();
}
}
// Initialize Sensors
if (!tmp117.begin()) Serial.println("TMP117 not found!");
if (!dps310.begin_I2C(0x77)) Serial.println("DPS310 not found!");
else dps_pressure = dps310.getPressureSensor();
if (!bme680.begin(0x76)) Serial.println("BME680 not found!");
if (!sgp30.begin()) Serial.println("SGP30 not found!");
if (!bno08x.begin_I2C(0x4A)) Serial.println("BNO08x not found!");
else {
bno08x.enableReport(SH2_GAME_ROTATION_VECTOR);
bno08x.enableReport(SH2_ACCELEROMETER);
bno08x.enableReport(SH2_GYROSCOPE_CALIBRATED);
bno08x.enableReport(SH2_MAGNETIC_FIELD_CALIBRATED);
}
gps.begin();
}
void loop() {
sensors_event_t temp_event;
tmp117.getEvent(&temp_event);
float tempTMP = temp_event.temperature;
sensors_event_t pressure_event, temp_event_dps;
float pressure = -1.0, tempDPS = -1.0;
if (dps310.temperatureAvailable() && dps310.pressureAvailable()) {
dps310.getEvents(&temp_event_dps, &pressure_event);
tempDPS = temp_event_dps.temperature;
pressure = pressure_event.pressure;
}
float tempBME = -1, presBME = -1, humBME = -1, vocBME = -1;
if (bme680.performReading()) {
tempBME = bme680.temperature;
presBME = bme680.pressure / 100.0;
humBME = bme680.humidity;
vocBME = bme680.gas_resistance / 1000.0;
}
sgp30.IAQmeasure();
uint16_t tvoc = sgp30.TVOC;
uint16_t eco2 = sgp30.eCO2;
float quatW = 0, quatX = 0, quatY = 0, quatZ = 0;
float accX = 0, accY = 0, accZ = 0;
float gyroX = 0, gyroY = 0, gyroZ = 0;
float magX = 0, magY = 0, magZ = 0;
sh2_SensorValue_t sensorValue;
while (bno08x.getSensorEvent(&sensorValue)) {
if (sensorValue.sensorId == SH2_GAME_ROTATION_VECTOR) {
quatW = sensorValue.un.gameRotationVector.real;
quatX = sensorValue.un.gameRotationVector.i;
quatY = sensorValue.un.gameRotationVector.j;
quatZ = sensorValue.un.gameRotationVector.k;
} else if (sensorValue.sensorId == SH2_ACCELEROMETER) {
accX = sensorValue.un.accelerometer.x;
accY = sensorValue.un.accelerometer.y;
accZ = sensorValue.un.accelerometer.z;
} else if (sensorValue.sensorId == SH2_GYROSCOPE_CALIBRATED) {
gyroX = sensorValue.un.gyroscope.x;
gyroY = sensorValue.un.gyroscope.y;
gyroZ = sensorValue.un.gyroscope.z;
} else if (sensorValue.sensorId == SH2_MAGNETIC_FIELD_CALIBRATED) {
magX = sensorValue.un.magneticField.x;
magY = sensorValue.un.magneticField.y;
magZ = sensorValue.un.magneticField.z;
}
}
// Read GPS Data
double latitude = 0.0, longitude = 0.0, altitude = 0.0;
String gpsData = "No GPS fix"; // Default message if no fix
if (gps.getFixType() >= 3) {
latitude = gps.getLatitude() / 10000000.0;
longitude = gps.getLongitude() / 10000000.0;
altitude = gps.getAltitude() / 1000.0;
gpsData = "GPS fix acquired";
}
// Save to SD Card
dataFile.open(filename, O_WRITE | O_APPEND);
if (dataFile) {
dataFile.printf("%lu,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f,%.6f,%.6f,%.2f,%s\n",
millis(), tempTMP, tempDPS, pressure, tempBME, presBME, humBME, vocBME, tvoc, eco2,
quatW, quatX, quatY, quatZ, accX, accY, accZ, gyroX, gyroY, gyroZ, magX, magY, magZ,
latitude, longitude, altitude, gpsData.c_str());
dataFile.flush();
dataFile.close();
Serial.println("Data saved to SD");
} else {
Serial.println("Error opening file for writing!");
}
}