start sd implementation

This commit is contained in:
2025-05-03 23:09:11 +02:00
parent 76eb216716
commit ee54abb663
7 changed files with 187 additions and 218 deletions

View File

@@ -25,6 +25,8 @@ idf_component_register(SRCS
"components/servocontroller.h" "components/servocontroller.h"
"components/radio.c" "components/radio.c"
"components/radio.h" "components/radio.h"
"components/sdcard.c"
"components/sdcard.h"
"main.c" "main.c"
INCLUDE_DIRS ".") INCLUDE_DIRS ".")

View File

@@ -18,7 +18,7 @@ typedef struct __attribute__((packed))
char syncPhrase[10]; //10 char syncPhrase[10]; //10
uint32_t packetIndex; //14 uint32_t packetIndex; //14
uint8_t packetType; //15 uint8_t packetType; //15
uint32_t missionTimer; //19 uint64_t missionTimer; //19
uint32_t CRCCheck; uint32_t CRCCheck;
} DownBoundPacket; } DownBoundPacket;

View File

@@ -8,6 +8,7 @@
#include "esp_rom_crc.h" #include "esp_rom_crc.h"
#include <hw/buscfg.h> #include <hw/buscfg.h>
#include "sensors.h" #include "sensors.h"
#include "sdcard.h"
#define TAG "LoRa" #define TAG "LoRa"
@@ -71,6 +72,8 @@ static void send_packet_without_retries(uint8_t *data, uint16_t size)
{ {
if (xSemaphoreTake(loraRadioMutex, portMAX_DELAY) == pdTRUE) if (xSemaphoreTake(loraRadioMutex, portMAX_DELAY) == pdTRUE)
{ {
writeFile(sensFile, data, size);
if (!LoRaSend(data, size, SX126x_TXMODE_SYNC)) if (!LoRaSend(data, size, SX126x_TXMODE_SYNC))
{ {
ESP_LOGW(TAG, "LoRaSend failed"); ESP_LOGW(TAG, "LoRaSend failed");
@@ -85,10 +88,11 @@ static void send_packet_without_retries(uint8_t *data, uint16_t size)
} }
static void send_packet_with_retries(uint8_t *data, uint16_t size) static void send_packet_with_retries(uint8_t *data, uint16_t size)
{
for (int retry = 0; retry <= MAX_RETRIES; retry++)
{ {
if (xSemaphoreTake(loraRadioMutex, portMAX_DELAY) == pdTRUE) if (xSemaphoreTake(loraRadioMutex, portMAX_DELAY) == pdTRUE)
{
writeFile(sensFile, data, size);
for (int retry = 0; retry <= MAX_RETRIES; retry++)
{ {
if (!LoRaSend(data, size, SX126x_TXMODE_SYNC)) if (!LoRaSend(data, size, SX126x_TXMODE_SYNC))
{ {
@@ -135,6 +139,69 @@ void prepare_and_send_telemetry(uint64_t missionTimer)
memcpy(bufOut + offset, &telemetryPacket, sizeof(telemetryPacket)); memcpy(bufOut + offset, &telemetryPacket, sizeof(telemetryPacket));
offset += sizeof(telemetryPacket); offset += sizeof(telemetryPacket);
if (csvFile != NULL) {
fprintf(csvFile,
"%llu,%lu,%u,%u," // missionTimer, packetIndex, telemetryIndex, packetType
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d," // MPU
"%u,%u,%d,%u," // CCS
"%u,%u,%u,%lu,%u,%lu,%u," // INA, BME basic
"%s,%s,%u,%u," // bools and gas index/range
"%.2f,%.2f,%.2f,%.2f," // Bosch env data
"%u,%.2f,%.2f,%.2f," // IAQ + scores
"%lu,%ld,%ld,%d,%u,%u,%u,%u," // GPS data
"%ld,%ld,%d," // Predicted GPS
"%ld,%ld,%ld,%ld," // ADC
"%d,%d,%d,%d," // Servo
"%u\n", // presentDevices
missionTimer,
downboundPacket.packetIndex,
telemetryPacket.telemetryIndex,
downboundPacket.packetType,
telemetryPacket.accelerationX, telemetryPacket.accelerationY, telemetryPacket.accelerationZ,
telemetryPacket.gyroX, telemetryPacket.gyroY, telemetryPacket.gyroZ,
telemetryPacket.magnetX, telemetryPacket.magnetY, telemetryPacket.magnetZ,
telemetryPacket.accelerometer_temperature,
telemetryPacket.eCO2, telemetryPacket.tvoc, telemetryPacket.currentCCS, telemetryPacket.rawCCSData,
telemetryPacket.volts, telemetryPacket.current, telemetryPacket.power,
telemetryPacket.temperature, telemetryPacket.humidity, telemetryPacket.pressure, telemetryPacket.gas,
telemetryPacket.gas_valid ? "Yes" : "No",
telemetryPacket.heater_stable ? "Yes" : "No",
telemetryPacket.gas_range, telemetryPacket.gas_index,
telemetryPacket.air_temperature, telemetryPacket.relative_humidity,
telemetryPacket.barometric_pressure, telemetryPacket.gas_resistance,
telemetryPacket.iaq_score,
telemetryPacket.temperature_score, telemetryPacket.humidity_score, telemetryPacket.gas_score,
telemetryPacket.time_seconds,
telemetryPacket.latitude_centi_degrees,
telemetryPacket.longitude_centi_degrees,
telemetryPacket.altitude_centi_meters,
telemetryPacket.fix_quality,
telemetryPacket.num_satellites,
telemetryPacket.date_yyddmm,
telemetryPacket.speed_centi_knots,
telemetryPacket.predicted_latitude_centi_degrees,
telemetryPacket.predicted_longitude_centi_degrees,
telemetryPacket.predicted_altitude_centi_meters,
telemetryPacket.NH3, telemetryPacket.CO, telemetryPacket.NO2, telemetryPacket.UVC,
telemetryPacket.currentServoA, telemetryPacket.targetServoA,
telemetryPacket.currentServoB, telemetryPacket.targetServoB,
telemetryPacket.presentDevices);
fflush(csvFile);
fsync(fileno(csvFile)); // Critical: this ensures actual write to disk
}
send_packet_without_retries(bufOut, offset); send_packet_without_retries(bufOut, offset);
packetReadiness = 0; packetReadiness = 0;
} }
@@ -308,7 +375,6 @@ void lora_comms_task(void *pvParameters)
{ {
int64_t start_time = esp_timer_get_time(); int64_t start_time = esp_timer_get_time();
ESP_LOGI(TAG, "fdfgtfet");
if (packetReadiness) if (packetReadiness)
{ {
ESP_LOGI(TAG, "Preparing telemetry"); ESP_LOGI(TAG, "Preparing telemetry");

View File

@@ -1,124 +1,19 @@
#include "sdcard.h"
static void s_generate_numbered_filename(char *out_path, size_t max_len) { void writeFile(FILE *f, const void *data, size_t size)
DIR *dir = opendir(MOUNT_POINT);
if (!dir) {
ESP_LOGE(TAG, "Failed to open directory");
snprintf(out_path, max_len, MOUNT_POINT"/data1.txt"); // fallback
return;
}
int max_num = 0;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, "data", 4) == 0 && strstr(entry->d_name, ".txt")) {
int num = 0;
if (sscanf(entry->d_name, "data%d.txt", &num) == 1) {
if (num > max_num) {
max_num = num;
}
}
}
}
closedir(dir);
snprintf(out_path, max_len, MOUNT_POINT"/data%d.txt", max_num + 1);
}
/* SD card and FAT filesystem example.
This example uses SPI peripheral to communicate with SD card.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "sd_test_io.h"
#if SOC_SDMMC_IO_POWER_EXTERNAL
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#endif
#define EXAMPLE_MAX_CHAR_SIZE 64
static const char *TAG = "example";
#define MOUNT_POINT "/sdcard"
#ifdef CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS
const char* names[] = {"CLK ", "MOSI", "MISO", "CS "};
const int pins[] = {CONFIG_EXAMPLE_PIN_CLK,
CONFIG_EXAMPLE_PIN_MOSI,
CONFIG_EXAMPLE_PIN_MISO,
CONFIG_EXAMPLE_PIN_CS};
const int pin_count = sizeof(pins)/sizeof(pins[0]);
#if CONFIG_EXAMPLE_ENABLE_ADC_FEATURE
const int adc_channels[] = {CONFIG_EXAMPLE_ADC_PIN_CLK,
CONFIG_EXAMPLE_ADC_PIN_MOSI,
CONFIG_EXAMPLE_ADC_PIN_MISO,
CONFIG_EXAMPLE_ADC_PIN_CS};
#endif //CONFIG_EXAMPLE_ENABLE_ADC_FEATURE
pin_configuration_t config = {
.names = names,
.pins = pins,
#if CONFIG_EXAMPLE_ENABLE_ADC_FEATURE
.adc_channels = adc_channels,
#endif
};
#endif //CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS
// Pin assignments can be set in menuconfig, see "SD SPI Example Configuration" menu.
// You can also change the pin assignments here by changing the following 4 lines.
#define PIN_NUM_MISO CONFIG_EXAMPLE_PIN_MISO
#define PIN_NUM_MOSI CONFIG_EXAMPLE_PIN_MOSI
#define PIN_NUM_CLK CONFIG_EXAMPLE_PIN_CLK
#define PIN_NUM_CS CONFIG_EXAMPLE_PIN_CS
static esp_err_t s_example_write_file(const char *path, char *data)
{ {
ESP_LOGI(TAG, "Opening file %s", path); if (f != NULL)
FILE *f = fopen(path, "w");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return ESP_FAIL;
}
fprintf(f, data);
fclose(f);
ESP_LOGI(TAG, "File written");
return ESP_OK;
}
static esp_err_t s_example_read_file(const char *path)
{ {
ESP_LOGI(TAG, "Reading file %s", path); fwrite(data, size, 1, f);
FILE *f = fopen(path, "r"); fflush(f);
if (f == NULL) { fsync(fileno(f)); // Critical: this ensures actual write to disk
ESP_LOGE(TAG, "Failed to open file for reading");
return ESP_FAIL;
} }
char line[EXAMPLE_MAX_CHAR_SIZE];
fgets(line, sizeof(line), f);
fclose(f);
// strip newline
char *pos = strchr(line, '\n');
if (pos) {
*pos = '\0';
}
ESP_LOGI(TAG, "Read from file: '%s'", line);
return ESP_OK;
} }
void app_main(void) FILE *csvFile = NULL;
FILE *sensFile = NULL;
void initSD(void)
{ {
esp_err_t ret; esp_err_t ret;
@@ -126,137 +21,108 @@ void app_main(void)
// If format_if_mount_failed is set to true, SD card will be partitioned and // If format_if_mount_failed is set to true, SD card will be partitioned and
// formatted in case when mounting fails. // formatted in case when mounting fails.
esp_vfs_fat_sdmmc_mount_config_t mount_config = { esp_vfs_fat_sdmmc_mount_config_t mount_config = {
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
.format_if_mount_failed = true, .format_if_mount_failed = true,
#else .max_files = 100,
.format_if_mount_failed = false, .allocation_unit_size = 16 * 1024};
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
sdmmc_card_t *card; sdmmc_card_t *card;
const char mount_point[] = MOUNT_POINT; const char mount_point[] = MOUNT_POINT;
ESP_LOGI(TAG, "Initializing SD card"); ESP_LOGI(TAGSD, "Initializing SD card");
// Use settings defined above to initialize SD card and mount FAT filesystem. // Use settings defined above to initialize SD card and mount FAT filesystem.
// Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions. // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
// Please check its source code and implement error recovery when developing // Please check its source code and implement error recovery when developing
// production applications. // production applications.
ESP_LOGI(TAG, "Using SPI peripheral"); ESP_LOGI(TAGSD, "Using SPI peripheral");
// By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz) // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI) // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI)
// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
sdmmc_host_t host = SDSPI_HOST_DEFAULT(); sdmmc_host_t host = SDSPI_HOST_DEFAULT();
host.slot = SPI3_HOST;
spi_bus_config_t bus_cfg = {
.mosi_io_num = PIN_NUM_MOSI,
.miso_io_num = PIN_NUM_MISO,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4000,
};
ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize bus.");
return;
}
// This initializes the slot without card detect (CD) and write protect (WP) signals. // This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = PIN_NUM_CS; slot_config.gpio_cs = HSPI_SD_CS;
slot_config.host_id = host.slot; slot_config.host_id = host.slot;
ESP_LOGI(TAG, "Mounting filesystem"); ESP_LOGI(TAGSD, "Mounting filesystem");
ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card); ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK) { if (ret != ESP_OK)
if (ret == ESP_FAIL) { {
ESP_LOGE(TAG, "Failed to mount filesystem. " if (ret == ESP_FAIL)
{
ESP_LOGE(TAGSD, "Failed to mount filesystem. "
"If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
} else { }
ESP_LOGE(TAG, "Failed to initialize the card (%s). " else
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret)); {
#ifdef CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS ESP_LOGE(TAGSD, "Failed to initialize the card (%s). "
check_sd_card_pins(&config, pin_count); "Make sure SD card lines have pull-up resistors in place.",
#endif esp_err_to_name(ret));
} }
return; return;
} }
ESP_LOGI(TAG, "Filesystem mounted"); ESP_LOGI(TAGSD, "Filesystem mounted");
// Card has been initialized, print its properties // Card has been initialized, print its properties
sdmmc_card_print_info(stdout, card); sdmmc_card_print_info(stdout, card);
// Use POSIX and C standard library functions to work with files. int index = 0;
FILE *f = fopen(COUNTER_FILE, "r");
if (f)
{
fscanf(f, "%d", &index);
fclose(f);
}
// First create a file. // === 2. Increment and write back ===
const char *file_hello = MOUNT_POINT"/hello.txt"; f = fopen(COUNTER_FILE, "w");
char data[EXAMPLE_MAX_CHAR_SIZE]; if (!f)
snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Hello", card->cid.name); {
ret = s_example_write_file(file_hello, data); ESP_LOGE(TAGSD, "Failed to open counter file");
if (ret != ESP_OK) {
return; return;
} }
index++;
if (f != NULL)
{
const char *file_foo = MOUNT_POINT"/foo.txt"; fprintf(f, "%d", index);
fflush(f);
// Check if destination file exists before renaming fsync(fileno(f)); // Critical: this ensures actual write to disk
struct stat st; fclose(f);
if (stat(file_foo, &st) == 0) { ESP_LOGI(TAGSD, "Index is now %d", index);
// Delete it if it exists
unlink(file_foo);
} }
// Rename original file // === 3. Make folder ===
ESP_LOGI(TAG, "Renaming file %s to %s", file_hello, file_foo); char folder_path[MAX_PATH - 20];
if (rename(file_hello, file_foo) != 0) { snprintf(folder_path, MAX_PATH, MOUNT_POINT "/%d", index);
ESP_LOGE(TAG, "Rename failed"); mkdir(folder_path, 0777); // safe if exists already
return;
}
ret = s_example_read_file(file_foo); // === 4. Create CSV and BIN ===
if (ret != ESP_OK) { char csv_path[MAX_PATH], bin_path[MAX_PATH];
return; snprintf(csv_path, MAX_PATH, "%s/data.csv", folder_path);
} snprintf(bin_path, MAX_PATH, "%s/data.bin", folder_path);
// Format FATFS csvFile = fopen(csv_path, "w");
#ifdef CONFIG_EXAMPLE_FORMAT_SD_CARD sensFile = fopen(bin_path, "wb");
ret = esp_vfs_fat_sdcard_format(mount_point, card); if (csvFile != NULL)
if (ret != ESP_OK) { {
ESP_LOGE(TAG, "Failed to format FATFS (%s)", esp_err_to_name(ret)); fprintf(csvFile,
return; "missionTimer,packetIndex,telemetryIndex,packetType,"
"accX,accY,accZ,gyroX,gyroY,gyroZ,magX,magY,magZ,accTemp,"
"eCO2,tVOC,currentCCS,rawCCS,"
"volts,current,power,rawtemperature,rawhumidity,rawpressure,rawgas,"
"gasValid,heaterStable,gasRange,gasIndex,"
"airTemp,relHumidity,baroPressure,gasResistance,"
"iaqScore,tempScore,humidityScore,gasScore,"
"gpsTime,latitude,longitude,altitude,fixQuality,numSatellites,date,speed,"
"predLatitude,predLongitude,predAltitude,"
"NH3,CO,NO2,UVC,"
"currentServoA,targetServoA,currentServoB,targetServoB,"
"presentDevices\n");
fflush(csvFile);
fsync(fileno(csvFile)); // Critical: this ensures actual write to disk
} }
if (stat(file_foo, &st) == 0) {
ESP_LOGI(TAG, "file still exists");
return;
} else {
ESP_LOGI(TAG, "file doesn't exist, formatting done");
}
#endif // CONFIG_EXAMPLE_FORMAT_SD_CARD
const char *file_nihao = MOUNT_POINT"/nihao.txt";
memset(data, 0, EXAMPLE_MAX_CHAR_SIZE);
snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Nihao", card->cid.name);
ret = s_example_write_file(file_nihao, data);
if (ret != ESP_OK) {
return;
}
//Open file for reading
ret = s_example_read_file(file_nihao);
if (ret != ESP_OK) {
return;
}
// All done, unmount partition and disable SPI peripheral
esp_vfs_fat_sdcard_unmount(mount_point, card);
ESP_LOGI(TAG, "Card unmounted");
//deinitialize the bus after all devices are removed
spi_bus_free(host.slot);
} }

26
main/components/sdcard.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef SDCARD_COMPONENT
#define SDCARD_COMPONENT
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "../hw/buscfg.h"
#define TAGSD "SDSubsys"
#define COUNTER_FILE "cnt.txt"
#define MOUNT_POINT "/sdcard"
void writeFile(FILE *f, const void *data, size_t size);
extern FILE *csvFile;
extern FILE *sensFile;
#define MAX_PATH 80
void initSD(void);
#endif

View File

@@ -9,7 +9,8 @@
#include "esp_vfs_fat.h" #include "esp_vfs_fat.h"
#include "sdmmc_cmd.h" #include "sdmmc_cmd.h"
#define MOUNT_POINT "/canSensors" #define MOUNT_POINT "/sdcard"
#define MAX_LINE_LENGTH 64
#define BLINK_GPIO 2 #define BLINK_GPIO 2
@@ -143,6 +144,9 @@ void init_connected()
void i2c_sensors_task(void *pvParameters) void i2c_sensors_task(void *pvParameters)
{ {
memset(foundDevices, 0, sizeof(foundDevices)); memset(foundDevices, 0, sizeof(foundDevices));
memset(prevDevices, 0, sizeof(prevDevices)); memset(prevDevices, 0, sizeof(prevDevices));

View File

@@ -25,6 +25,7 @@
#include "hw/mcp23018.h" #include "hw/mcp23018.h"
#include "hw/mpu9250.h" #include "hw/mpu9250.h"
#include "hw/buscfg.h" #include "hw/buscfg.h"
#include "components/sdcard.h"
#include "hw/gps.h" #include "hw/gps.h"
@@ -48,6 +49,8 @@ void app_main(void)
// }; // };
// ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &MCPBusCfg, SPI_DMA_DISABLED)); // ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &MCPBusCfg, SPI_DMA_DISABLED));
gpio_pullup_en(HSPI_MISO_GPIO);
gpio_pullup_en(HSPI_SD_CS);
spi_bus_config_t HighSpeedBusCfg = { spi_bus_config_t HighSpeedBusCfg = {
// CONNECTED to LoRa and SD card // CONNECTED to LoRa and SD card
.mosi_io_num = HSPI_MOSI_GPIO, .mosi_io_num = HSPI_MOSI_GPIO,
@@ -64,6 +67,8 @@ void app_main(void)
mcp23018_init(); mcp23018_init();
initSD();
void servoControllerInit(); void servoControllerInit();
ESP_LOGI(TAG, "BEGIN ESP TASKS"); ESP_LOGI(TAG, "BEGIN ESP TASKS");