SD card not printing

I’m currently having trouble with my SD card not being able to print / read my data that is being collected from my Oceanus Co2 sensor. I have it fully working laid out on the bench. But having trouble understanding why the data isn’t being collected or stored. I have looked at my code and looked at the guide, but nothing has really stood out to me.

Below Is my code that I have written:

// ----------------------------
// Mini CO₂ Sensor Integration with Timed Sampling for Bristlemouth Dev Kit
// Samples for 1 minute every 5 minutes
// Logs corrected_CO₂, sensor_temp, gas_pressure, ir_temp, supply_voltage
// ----------------------------

#include "user_code.h"
#include "bristlefin.h"
#include "payload_uart.h"
#include "task_priorities.h"
#include "uptime.h"
#include "stm32_rtc.h"
#include "bm_printf.h"

#define LED_ON_TIME_MS 20
#define LED_PERIOD_MS 1000
#define DEFAULT_BAUD_RATE 19200
#define DEFAULT_LINE_TERM 10  // '\n'

#define SAMPLE_INTERVAL_MS (60 * 1000)  // 1 min
#define SAMPLE_DURATION_MS (30 * 1000)  // 30 sec

extern Bristlefin bristlefin;
extern cfg::Configuration *userConfigurationPartition;

static uint32_t baud_rate_config = DEFAULT_BAUD_RATE;
static uint32_t line_term_config = DEFAULT_LINE_TERM;
static uint64_t last_sample_start_ms = 0;
static bool sampling_active = false;

char payload_buffer[256];

// Simple stats collector
class AveragingSampler {
  float sum = 0.0f;
  int count = 0;
  float minVal = 1e30f;
  float maxVal = -1e30f;
public:
  void addSample(float val) {
    sum += val;
    count++;
    if (val < minVal) minVal = val;
    if (val > maxVal) maxVal = val;
  }
  float getAverage() const { return (count > 0) ? sum / count : 0.0f; }
  float getMin() const { return (count > 0) ? minVal : 0.0f; }
  float getMax() const { return (count > 0) ? maxVal : 0.0f; }
  int sampleCount() const { return count; }
  void reset() {
    sum = 0.0f;
    count = 0;
    minVal = 1e30f;
    maxVal = -1e30f;
  }
};

static AveragingSampler co2_stats;

void setup() {
  printf("Mini CO₂ setup() started\n");

  userConfigurationPartition->getConfig("plUartBaudRate", strlen("plUartBaudRate"), baud_rate_config);
  userConfigurationPartition->getConfig("plUartLineTerm", strlen("plUartLineTerm"), line_term_config);

  last_sample_start_ms = uptimeGetMs();
}

void loop() {
  uint64_t now = uptimeGetMs();

  // ---------- Timed Sampling Control ----------
  if (!sampling_active && now - last_sample_start_ms >= SAMPLE_INTERVAL_MS) {
    // Start sampling
    bristlefin.enableVbus();
    vTaskDelay(pdMS_TO_TICKS(10));
    bristlefin.enableVout();

    PLUART::init(USER_TASK_PRIORITY);
    PLUART::setBaud(baud_rate_config);
    PLUART::setTerminationCharacter((char)line_term_config);
    PLUART::setUseByteStreamBuffer(false);
    PLUART::setUseLineBuffer(true);
    PLUART::enable();

    vTaskDelay(pdMS_TO_TICKS(500)); // sensor boot time

    uint8_t startCmd[] = "1\r";
    PLUART::write(startCmd, sizeof(startCmd) - 1);

    sampling_active = true;
    last_sample_start_ms = now;
    printf("Sampling started\n");
  }

  if (sampling_active && now - last_sample_start_ms >= SAMPLE_DURATION_MS) {
    // Stop sampling
    uint8_t stopCmd[] = "2\r";
    PLUART::write(stopCmd, sizeof(stopCmd) - 1);

    bristlefin.disableVout();
    bristlefin.disableVbus();

    // Report summary
    printf("[CO2_STATS] samples: %d, avg: %.3f, min: %.3f, max: %.3f\n",
           co2_stats.sampleCount(),
           co2_stats.getAverage(),
           co2_stats.getMin(),
           co2_stats.getMax());

    co2_stats.reset();
    sampling_active = false;
    last_sample_start_ms = now;

    printf("Sampling stopped\n");
  }

  // ---------- LED Heartbeat ----------
  static bool led1State = false;
  static uint32_t ledPulseTimer = uptimeGetMs();
  static uint32_t ledOnTimer = 0;

  if (!led1State && uptimeGetMs() - ledPulseTimer >= LED_PERIOD_MS) {
    bristlefin.setLed(1, Bristlefin::LED_GREEN);
    ledOnTimer = uptimeGetMs();
    ledPulseTimer += LED_PERIOD_MS;
    led1State = true;
  } else if (led1State && uptimeGetMs() - ledOnTimer >= LED_ON_TIME_MS) {
    bristlefin.setLed(1, Bristlefin::LED_OFF);
    led1State = false;
  }

  // ---------- Sensor Data Parsing ----------
  if (sampling_active && PLUART::lineAvailable()) {
    uint16_t read_len = PLUART::readLine(payload_buffer, sizeof(payload_buffer) - 1);
    payload_buffer[read_len] = '\0';

    char *tokens[21] = {};
    uint8_t i = 0;
    char *tok = strtok(payload_buffer, ",");
    while (tok && i < 21) {
      tokens[i++] = tok;
      tok = strtok(nullptr, ",");
    }

    // Extract and parse sensor fields
    float corrected_CO2   = tokens[10] ? atof(tokens[10]) : -1.0;
    float sensor_temp     = tokens[11] ? atof(tokens[11]) : -1.0;
    float gas_pressure    = tokens[12] ? atof(tokens[12]) : -1.0;
    float ir_temp         = tokens[13] ? atof(tokens[13]) : -1.0;
    float supply_voltage  = tokens[14] ? atof(tokens[14]) : -1.0;

    if (corrected_CO2 >= 0.0f) {
      co2_stats.addSample(corrected_CO2);
    }

    // Timestamp
    RTCTimeAndDate_t time_and_date = {};
    rtcGet(&time_and_date);
    char rtcBuffer[32];
    rtcPrint(rtcBuffer, &time_and_date);

    //  Spotter-compatible [payload] log format
    printf("[payload] | tick: %llu, rtc: %s, line: %.3f, %.2f, %.2f, %.2f, %.2f\n",
           uptimeGetMs(), rtcBuffer, corrected_CO2, sensor_temp, gas_pressure, ir_temp, supply_voltage);

    //  Also logs to SD via bm_fprintf
    bm_fprintf(0, "payload_data.log",
               "tick: %llu, rtc: %s, line: %.3f, %.2f, %.2f, %.2f, %.2f\n",
               uptimeGetMs(), rtcBuffer, corrected_CO2, sensor_temp, gas_pressure, ir_temp, supply_voltage);
  }
}

Hello @Everardo!
Can I ask which version of bm_protocol you are currently using? This can be obtained from running the info console command on the mote, the output should look something like so:

APP_NAME: aanderaa_salinity
UID: 2035324142315015003f0037
MAC: 00:00:44:17:53:f4
IP addresses:
Link Local: FE80::8EC4:1988:4417:53F4
Unicast:    FD00::8EC4:1988:4417:53F4
FW Version: aanderaa_salinity@ENG-v0.13.9-2-gb9a3e2c6+310fcfe9
GIT SHA: B9A3E2C6
Build ID: 310fcfe9a02484cfe8c8c3c5a569b4d44bdf51d9
BSP: mote_rs232
Reset Reason: Invalid reset or first power on since flashing
Node ID: 8ec41988441753f4
Bootloader Information:
  Version: ENG-v0.13.4-59-gbee84c4f
  SHA: BEE84C4F
  Signature support: 0
  Encrytion support: 0
  Swap Type: NONE
info end

The item of interest is FW Version, which is aanderaa_salinity@ENG-v0.13.9-2-gb9a3e2c6+310fcfe9 in the case of the example above (which equates to v0.13.9 of bm_protocol).

On newer versions (>v0.13.0) bm_fprintf is a deprecated function and has been replaced with the API spotter_log.
Let me know what version and I can see how I can help!

Hey @matt001k !

I’m sorry for the late reply, the version I am currently using is ENG-v0.11.1+cbd71ff8

Here is an image

No problem!
There was a breaking change in Spotter v2.15.2 that might be causing what you are seeing.
Would you be able to validate your Spotter version as well?
You can do this by running the same command (info) while connected to the Spotter’s console.
If your Spotter is >=v2.15.2 the next best solution would be to update your bm_protocol version. We can walk through those steps after you validated your Spotter’s version!

My apologies for the late response once again, here is my spotter version. I am currently on 2.15.2.

Good to know.
A couple of follow up questions.

  • Where are you looking for the data to be stored at on the SD card? The location of the log files should be under the path bm/{your_node_id}.
  • What is bm_fprintf returning when you are running it in your loop? This function returns bm_printf_err_t. Knowing what this value is might help us debug the issue further.

It might be worth trying a simple bm_fprintf in your loop function that might look something like:

bm_fprintf(0, "test.log", "hello world\n");

To validate a simple print is making it to the log file correctly (again the path of this file would be bm/{your_node_id}/{XXXX}_test.log).

Hi Matt,

I did find out that it wasn’t in the correct pathway. That did clear up most of the problems that I had. I do have it where it is correctly printing but a new issue has appeared.

While it did print onto the SD card, it hasn’t printed anymore data onto the card. I tried power cycling and ran it multiple times to see if anything would change. I did change the name of the file to C02_log.

Hey I wanted to give an update, I got it to where it does print data but only if I send a certain command every time.
I’m not fully sure if the reason why I have to use a command to print it on the dev kit terminal is because its not connected to satellite. Would connecting to satellite fix that issue?

Hello @Everardo, I am sorry you are still having issues but could you clear something up for me:

I got it to where it does print data but only if I send a certain command every time

What command is being sent and what is being printed exactly?

I’m not fully sure if the reason why I have to use a command to print it on the dev kit terminal is because its not connected to satellite. Would connecting to satellite fix that issue?

Connecting to satellite will have nothing to do with seeing the data appear in the terminal. This would only affect remote data being sent/accessed.

One area I would look closer at is here:

// ---------- Sensor Data Parsing ----------
  if (sampling_active && PLUART::lineAvailable()) {
...

If this conditional is never hit, then the data will never get printed to the mote’s (dev kit’s) console (terminal) or the SD card.
I would suggest adding debugging statements around this conditional such as:

// ---------- Sensor Data Parsing ----------
  if (sampling_active && PLUART::lineAvailable()) {
 ...
} else {
  printf("Could not parse data, sampling_active: %d\n", sampling_active);
}

Note, this will most likely spam the console with print statements, but it will help you determine if the sensor is being read properly or not.

Hey @matt001k

To answer your questions:

Guide 3 Exploring Bristlemouth Features:
Write to an SD card log file: The command I use is spotter fprintf C02_log Hello, Spotter!

I did update my code here is a updated version below:
#include “user_code.h”
#include “bristlefin.h”
#include “payload_uart.h”
#include “task_priorities.h”
#include “uptime.h”
#include “stm32_rtc.h”
#include “bm_printf.h”

#define LED_ON_TIME_MS 20
#define LED_PERIOD_MS 1000
#define DEFAULT_BAUD_RATE 19200
#define DEFAULT_LINE_TERM 10

#define SAMPLE_INTERVAL_MS (5 * 60 * 1000) // every 5 min
#define SAMPLE_DURATION_MS (2* 60 * 1000) // sample for 2 min

extern Bristlefin bristlefin;
extern cfg::Configuration *userConfigurationPartition;

static uint32_t baud_rate_config = DEFAULT_BAUD_RATE;
static uint32_t line_term_config = DEFAULT_LINE_TERM;
static uint64_t last_sample_start_ms = 0;
static bool sampling_active = false;

char payload_buffer[256];

class AveragingSampler {
float sum = 0.0f;
int count = 0;
float minVal = 1e30f;
float maxVal = -1e30f;
public:
void addSample(float val) {
sum += val;
count++;
if (val < minVal) minVal = val;
if (val > maxVal) maxVal = val;
}
float getAverage() const { return (count > 0) ? sum / count : 0.0f; }
float getMin() const { return (count > 0) ? minVal : 0.0f; }
float getMax() const { return (count > 0) ? maxVal : 0.0f; }
int sampleCount() const { return count; }
void reset() { sum = 0; count = 0; minVal = 1e30f; maxVal = -1e30f; }
};

static AveragingSampler co2_stats;

void setup() {
printf(“Mini CO2 setup()\n”);

userConfigurationPartition->getConfig(“plUartBaudRate”, strlen(“plUartBaudRate”), baud_rate_config);
userConfigurationPartition->getConfig(“plUartLineTerm”, strlen(“plUartLineTerm”), line_term_config);

PLUART::init(USER_TASK_PRIORITY);
PLUART::setBaud(baud_rate_config);
PLUART::setTerminationCharacter((char)line_term_config);
PLUART::setUseByteStreamBuffer(true);
PLUART::setUseLineBuffer(true);
PLUART::enable();

last_sample_start_ms = uptimeGetMs();
}

void loop() {
uint64_t now = uptimeGetMs();

// --------- TIMED SAMPLING CONTROL ---------
if (!sampling_active && now - last_sample_start_ms >= SAMPLE_INTERVAL_MS) {
bristlefin.enableVbus();
vTaskDelay(pdMS_TO_TICKS(10));
bristlefin.enableVout();
vTaskDelay(pdMS_TO_TICKS(500));

uint8_t startCmd[] = "1\r";
PLUART::write(startCmd, sizeof(startCmd) - 1);

sampling_active = true;
last_sample_start_ms = now;
bm_printf(0, "[payload] CO2 sampling started\n");

}

if (sampling_active && now - last_sample_start_ms >= SAMPLE_DURATION_MS) {
uint8_t stopCmd = “2\r”;
PLUART::write(stopCmd, sizeof(stopCmd) - 1);

bristlefin.disableVout();
bristlefin.disableVbus();

bm_printf(0, "[payload] CO2 stats: samples=%d avg=%.2f min=%.2f max=%.2f\n",
          co2_stats.sampleCount(), co2_stats.getAverage(),
          co2_stats.getMin(), co2_stats.getMax());

co2_stats.reset();
sampling_active = false;
last_sample_start_ms = now;

}

// --------- LED HEARTBEAT ---------
static bool led1State = false;
static uint32_t ledPulseTimer = uptimeGetMs();
static uint32_t ledOnTimer = 0;

if (!led1State && uptimeGetMs() - ledPulseTimer >= LED_PERIOD_MS) {
bristlefin.setLed(1, Bristlefin::LED_GREEN);
ledOnTimer = uptimeGetMs();
ledPulseTimer += LED_PERIOD_MS;
led1State = true;
} else if (led1State && uptimeGetMs() - ledOnTimer >= LED_ON_TIME_MS) {
bristlefin.setLed(1, Bristlefin::LED_OFF);
led1State = false;
}

// --------- SENSOR DATA PARSING ---------
if (sampling_active && PLUART::lineAvailable()) {
uint16_t read_len = PLUART::readLine(payload_buffer, sizeof(payload_buffer) - 1);
payload_buffer[read_len] = ‘\0’;

char *tokens[21] = {};
uint8_t i = 0;
char *tok = strtok(payload_buffer, ",");
while (tok && i < 21) { tokens[i++] = tok; tok = strtok(nullptr, ","); }

float corrected_CO2  = tokens[10] ? atof(tokens[10]) : -1.0;
float sensor_temp    = tokens[11] ? atof(tokens[11]) : -1.0;
float gas_pressure   = tokens[12] ? atof(tokens[12]) : -1.0;
float ir_temp        = tokens[13] ? atof(tokens[13]) : -1.0;
float supply_voltage = tokens[14] ? atof(tokens[14]) : -1.0;

if (corrected_CO2 >= 0) co2_stats.addSample(corrected_CO2);

RTCTimeAndDate_t time_and_date = {};
rtcGet(&time_and_date);
char rtcBuffer[32];
rtcPrint(rtcBuffer, &time_and_date);

bm_printf(0, "[payload] | tick:%llu rtc:%s co2:%.2f temp:%.2f press:%.2f ir:%.2f v:%.2f\n",
          uptimeGetMs(), rtcBuffer, corrected_CO2, sensor_temp,
          gas_pressure, ir_temp, supply_voltage);

bm_fprintf(0, "C02_log",
           "tick:%llu rtc:%s %.2f %.2f %.2f %.2f %.2f\n",
           uptimeGetMs(), rtcBuffer, corrected_CO2, sensor_temp,
           gas_pressure, ir_temp, supply_voltage);

}
}