Esp32 crashing when trying to open file in FreeRTOS task

Hello! I've been trying to debug this for a while, so far with no luck, so I'm hoping some kind person with more experience than I can help me out :slight_smile:. I'm trying to write an esp32 based data logger (it saves IMU data to a file on the attached SD card, sends a WiFi packet to be picked up by another esp, and takes a photo every second, when it's also supposed to write the last second's worth of IMU data to a csv file, both on the SD. I had a sequential version of this that worked, but was very slow, so I decided to try and write a multithreaded FreeRTOS version. I have no prior experience with FreeRTOS, so probably therein lies the problem.

Regardless, when I run the write_all task, the program crashes when trying to open the csv. Here is the output:

16:08:23.259 > Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
16:08:23.261 >
16:08:23.261 > Core  1 register dump:
16:08:23.261 > PC      : 0x4200acba  PS      : 0x00060330  A0      : 0x82005b20  A1      : 0x3fcb1590
16:08:23.261 > A2      : 0x3a37303a  A3      : 0x0000ffff  A4      : 0x3d8bc4bc  A5      : 0x00000000
16:08:23.261 > A6      : 0x00000000  A7      : 0x00000040  A8      : 0x00000000  A9      : 0x00000000
16:08:23.261 > A10     : 0x00000010  A11     : 0x00000010  A12     : 0x00000000  A13     : 0x00000040
16:08:23.261 > A14     : 0x00800000  A15     : 0xffffffff  SAR     : 0x0000000a  EXCCAUSE: 0x0000001c
16:08:23.261 > EXCVADDR: 0x00000008  LBEG    : 0x4202ba14  LEND    : 0x4202ba24  LCOUNT  : 0x00000008
16:08:23.261 >
16:08:23.261 >
16:08:23.261 > Backtrace: 0x4200acb7:0x3fcb1590 0x42005b1d:0x3fcb15b0 0x420060e9:0x3fcb15d0 0x4200625b:0x3fcb15f0 0x420063a9:0x3fcb1610 0x4202a3f9:0x3fcb1640 0x4202a5d0:0x3fcb1660 0x4202b192:0x3fcb1680 0x4202b872:0x3fcb16c0 0x4202bb2a:0x3fcb1700 0x4202d54f:0x3fcb1970 0x4201479f:0x3fcb19a0 0x42074de2:0x3fcb19c0 0x42074e85:0x3fcb19f0 0x420734dc:0x3fcb1a10 0x42073df1:0x3fcb1a40 0x42005772:0x3fcb1ad0 0x42005289:0x3fcb1b00
  #0  0x4200acb7 in spiTransferShortNL at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-spi.c:1223
  #1  0x42005b1d in SPIClass::transfer16(unsigned short) at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src/SPI.cpp:226
  #2  0x420060e9 in sdReadBytes(unsigned char, char*, int) at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/libraries/SD/src/sd_diskio.cpp:221
  #3  0x4200625b in sdReadSector(unsigned char, char*, unsigned long long) at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/libraries/SD/src/sd_diskio.cpp:260
  #4  0x420063a9 in ff_sd_read(unsigned char, unsigned char*, unsigned int, unsigned int) at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/libraries/SD/src/sd_diskio.cpp:643
  #5  0x4202a3f9 in ff_disk_read at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/fatfs/diskio/diskio.c:70
  #6  0x4202a5d0 in move_window at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/fatfs/src/ff.c:1077
      (inlined by) move_window at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/fatfs/src/ff.c:1064
  #7  0x4202b192 in dir_find at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/fatfs/src/ff.c:2450
  #8  0x4202b872 in follow_path at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/fatfs/src/ff.c:3070
  #9  0x4202bb2a in f_open at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/fatfs/src/ff.c:3571
  #10 0x4202d54f in vfs_fat_open at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/fatfs/vfs/vfs_fat.c:313
  #11 0x4201479f in esp_vfs_open at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/vfs/vfs.c:399 (discriminator 3)
  #12 0x42074de2 in _fopen_r at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32s3-elf/src/newlib/newlib/libc/stdio/fopen.c:129
  #13 0x42074e85 in fopen at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32s3-elf/src/newlib/newlib/libc/stdio/fopen.c:168
  #14 0x420734dc in VFSFileImpl::VFSFileImpl(VFSImpl*, char const*, char const*) at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/libraries/FS/src/vfs_api.cpp:299
  #15 0x42073df1 in void __gnu_cxx::new_allocator<VFSFileImpl>::construct<VFSFileImpl, VFSImpl*, char const*&, char const*&>(VFSFileImpl*, VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\ext/new_allocator.h:136
      (inlined by) void std::allocator_traits<std::allocator<VFSFileImpl> >::construct<VFSFileImpl, VFSImpl*, char const*&, char const*&>(std::allocator<VFSFileImpl>&, VFSFileImpl*, VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\bits/alloc_traits.h:475
      (inlined by) std::_Sp_counted_ptr_inplace<VFSFileImpl, std::allocator<VFSFileImpl>, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<VFSImpl*, char const*&, char const*&>(std::allocator<VFSFileImpl>, VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\bits/shared_ptr_base.h:545
      (inlined by) std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<VFSFileImpl, std::allocator<VFSFileImpl>, VFSImpl*, char const*&, char const*&>(VFSFileImpl*&, std::_Sp_alloc_shared_tag<std::allocator<VFSFileImpl> >, VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\bits/shared_ptr_base.h:677
      (inlined by) std::__shared_ptr<VFSFileImpl, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<VFSFileImpl>, VFSImpl*, char const*&, char const*&>(std::_Sp_alloc_shared_tag<std::allocator<VFSFileImpl> >, VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\bits/shared_ptr_base.h:1342
      (inlined by) std::shared_ptr<VFSFileImpl>::shared_ptr<std::allocator<VFSFileImpl>, VFSImpl*, char const*&, char const*&>(std::_Sp_alloc_shared_tag<std::allocator<VFSFileImpl> >, VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\bits/shared_ptr.h:359
      (inlined by) std::shared_ptr<VFSFileImpl> std::allocate_shared<VFSFileImpl, std::allocator<VFSFileImpl>, VFSImpl*, char const*&, char const*&>(std::allocator<VFSFileImpl> const&, VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\bits/shared_ptr.h:706
      (inlined by) std::shared_ptr<VFSFileImpl> std::make_shared<VFSFileImpl, VFSImpl*, char const*&, char const*&>(VFSImpl*&&, char const*&, char const*&) at c:\users\ranat\.platformio\packages\toolchain-xtensa-esp32s3\xtensa-esp32s3-elf\include\c++\8.4.0\bits/shared_ptr.h:722
      (inlined by) VFSImpl::open(char const*, char const*, bool) at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/libraries/FS/src/vfs_api.cpp:47
  #16 0x42005772 in fs::FS::open(char const*, char const*, bool) at C:/Users/ranat/.platformio/packages/framework-arduinoespressif32/libraries/FS/src/FS.cpp:234
  #17 0x42005289 in task_write_all(void*) at src/main.cpp:733

16:08:23.261 >
16:08:23.261 >
16:08:23.261 >
16:08:23.261 >
16:08:23.261 > ELF file SHA256: 8df70dfa3085cc67
16:08:23.261 >
16:08:23.261 > Rebooting...

I understand this error to mean that the program is accessing memory it should not be able to. However, the section of code this error points to is as follows:

void task_write_all(void *pvParameters)
{
  const TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
  char file[FILENAME_SIZE]{};
  char timestamp[TIME_SIZE]{};
  DateTime now;
  File img_file;
  File imu_file;
  
  for(;;)
  {
    Serial.println("started write all");
    taskENTER_CRITICAL(&lock);
    imu_idx_last = imu_idx;
    imu_idx = 0;
    imu_switch = 1 - imu_switch;
    old_time = millis();
    taskEXIT_CRITICAL(&lock);

    if(sd_good)
    {
      now = rtc.now();
      memset(timestamp, '\0', TIME_SIZE);
      memset(file, '\0', FILENAME_SIZE);
      generate_timestamp(timestamp, &now, true);
      sprintf(file, "/image%s.jpg", timestamp);

      camera_fb_t *fb = esp_camera_fb_get();
      if (!fb) 
      {
        Serial.println("Failed to get camera frame buffer");
      }

      Serial.printf("Writing files: %s\n", file);

      img_file = SD.open(file, FILE_WRITE);
      imu_file = SD.open("/imu_data.csv", FILE_APPEND);

      if(!img_file)
      {
        Serial.println("Failed to open image file for writing");
      }
      if(!imu_file)
      {
        Serial.println("Failed to open imu file for writing");
      }
      Serial.println("Files loaded");

      if(img_file.write(fb->buf, fb->len) == fb->len)
      {
        Serial.println("Image write succeeded");
        img_file.close();
      } 
      else 
      {
        Serial.println("Image write failed");
        img_file.close();
      }

      // Release image buffer
      esp_camera_fb_return(fb);
      Serial.println("Image saved");
  
      Serial.println("Writing csv");
      if(imu_switch == 0)
      {
        for(int i=0;i<imu_idx_last;i++)
        {
          imu_file.println(imu_data1[i]);
        }
      }
      else
      {
        for(int i=0;i<imu_idx_last;i++)
        {
          imu_file.println(imu_data0[i]);
        }
      }
      
      
      imu_file.close();
      Serial.println("imu csv saved");
    }
    else
    {
      Serial.println("Unknown SD card failure");
    }

    vTaskDelay( xDelay ); 
  }
  vTaskDelete( NULL );
}

With the specific line in question beingimu_file = SD.open("/imu_data.csv", FILE_APPEND);. imu_file only exists within the context of this task, and this error happens the first time the task is called. I am honestly quite stumped at this point as to the cause of the error, and would really love some help.

Thank you very much in advance!

The trace gives the address of the return point, so the error could be in the previous source line. What does the line

show?

It prints the filename of the image frame I'm capturing. From the specific log, it looked like this: Writing files: /image20250523163956.jpg. The string in file is generated by the function generate_timestamp, which just makes a timestamp like the one shown, adding zeroes behind single digit elements of the timestamp (e.g. the 05 in the shown example)

What SD support are you using? SD.h only supports 8.3 style 11 character names. You need to use SdFat.h for long name support.
https://docs.arduino.cc/libraries/sdfat/

And how much RAM have you allocated for the task ?

Both questions that would have been unnecessary to ask had @ar3dhel1 bothered to post the complete code from the very start.

I'm using SD.h, of which I admittedly have not read the documentation and did not know this, I was just working from examples. Though these filename lengths did work in the sequential version of the program. I've started looking into the documentation of SdFat, thank you :slight_smile:

When I generated this error, 4096. I have since doubled it, which slightly embarrassingly may have been the cause of the problem, I really should have thought of that. I'm not entirely sure, since now I'm just getting this, from what I can tell from debug texts at the same point in the code: #0 0x42013a33 in panic_handler at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/port/panic_handler.c:148 (discriminator 3)

My code is relatively long, and I thought posting a targeted section would improve readability. Apologies if I was incorrect in that, but there's no need to be rude. Here is the entire code. It's not the tidiest I fear, I usually clean up my code properly once I've mostly finished with it.

#include <Arduino.h>

#include "nvs_flash.h"

#include "esp_mac.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "esp_now.h"
#include "esp_camera.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <RTClib.h>
#include <Wire.h>

RTC_DS1307 rtc;

Adafruit_MPU6050 imu;

// CAMERA PINS

#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM     10
#define SIOD_GPIO_NUM     40
#define SIOC_GPIO_NUM     39

#define Y9_GPIO_NUM       48
#define Y8_GPIO_NUM       11
#define Y7_GPIO_NUM       12
#define Y6_GPIO_NUM       14
#define Y5_GPIO_NUM       16
#define Y4_GPIO_NUM       18
#define Y3_GPIO_NUM       17
#define Y2_GPIO_NUM       15
#define VSYNC_GPIO_NUM    38
#define HREF_GPIO_NUM     47
#define PCLK_GPIO_NUM     13

#define LED_GPIO_NUM      21

#define CONFIG_LESS_INTERFERENCE_CHANNEL    11
#define CONFIG_SEND_FREQUENCY               40           // in hz

#define IMU_SIZE                            61
#define TIME_SIZE                           24
#define FILENAME_SIZE                       26

// mac addresses
static const uint8_t MAC_ADDR_SEND[] = {0x1a, 0x00, 0x00, 0x00, 0x00, 0x00};
static const uint8_t MAC_ADDR_RECV[] = {0x1a, 0x01, 0x00, 0x00, 0x00, 0x00};
static const uint8_t MAC_ADDR_BROAD[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

uint8_t pkt_count = 0;

// define tasks
void task_write_all(void *pvParameters);
void task_save_imu(void *pvParameters);
void task_send_csi(void *pvParameters);

static portMUX_TYPE lock = portMUX_INITIALIZER_UNLOCKED;

// global variables
int imu_idx = 0;
int imu_switch = 0;
int imu_idx_last = 0;

char imu_data0[50][IMU_SIZE+TIME_SIZE];
char imu_data1[50][IMU_SIZE+TIME_SIZE];

unsigned long new_time = 0;
unsigned long old_time = 0;

bool wifi_good;
bool rtc_good;
bool sd_good;
bool cam_good;
bool imu_good;

bool report_esp_error(esp_err_t err)
{
  if(err == ESP_OK)
  {
    return false;
  }
  else
  {
    Serial.printf("Error: %s\n", esp_err_to_name(err));
    return true;
  }
}

bool imu_setup()
{
  if (!imu.begin(0x69)) 
  {
    Serial.println("Failed to find IMU");
    return false;
  }
  Serial.println("IMU Found!");

  imu.setAccelerometerRange(MPU6050_RANGE_2_G);
  Serial.println("Accelerometer range set to: +- 2G");
  imu.setGyroRange(MPU6050_RANGE_250_DEG);
  Serial.println("Gyro range set to: +- 250 deg/s");
  imu.setFilterBandwidth(MPU6050_BAND_44_HZ);
  Serial.println("Filter bandwidth set to: 44 Hz");

  return true;
}

bool rtc_setup()
{
  if (!rtc.begin()) 
  {
    Serial.println("Failed to find RTC");
    return false;
  }
  Serial.println("RTC Found!");

  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  old_time = millis();
  return true;
}

bool cam_setup()
{
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size = FRAMESIZE_UXGA;
  config.pixel_format = PIXFORMAT_JPEG; // for streaming
  config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_PSRAM;
  config.jpeg_quality = 12;
  config.fb_count = 1;
  
  // if PSRAM IC present, init with UXGA resolution and higher JPEG quality
  //                      for larger pre-allocated frame buffer.
  if(config.pixel_format == PIXFORMAT_JPEG)
  {
    if(psramFound())
    {
      config.jpeg_quality = 10;
      config.fb_count = 2;
      config.grab_mode = CAMERA_GRAB_LATEST;
    } else 
    {
      // Limit the frame size when PSRAM is not available
      config.frame_size = FRAMESIZE_SVGA;
      config.fb_location = CAMERA_FB_IN_DRAM;
    }
  } 
  else 
  {
    // Best option for face detection/recognition
    config.frame_size = FRAMESIZE_240X240;
#if CONFIG_IDF_TARGET_ESP32S3
    config.fb_count = 2;
#endif
  }

  // camera init
  if (report_esp_error(esp_camera_init(&config))) return false;
  /*esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) 
  {
    Serial.printf("Camera init failed with error 0x%x", err);
    return false;
  }*/
  
  return true;
}

bool sd_setup()
{
  // Initialize SD card
  if(!SD.begin(21))
  {
    Serial.println("Card Mount Failed");
    return false;
  }
  uint8_t cardType = SD.cardType();

  // Determine if the type of SD card is available
  if(cardType == CARD_NONE)
  {
    Serial.println("No SD card attached");
    return false;
  }

  Serial.print("SD Card Type: ");
  if(cardType == CARD_MMC)
  {
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }

  return true;
}

bool wifi_setup()
{
  // NVS SETUP //
  // try to initialise the default NVS partition
  esp_err_t errcode = nvs_flash_init();

  // if necessary, clear the flash and re-initialise
  if (errcode == ESP_ERR_NVS_NO_FREE_PAGES || errcode == ESP_ERR_NVS_NEW_VERSION_FOUND) 
  {
    report_esp_error(nvs_flash_erase());
    errcode = nvs_flash_init();
  }

  // if there is still something wrong, report and abort
  if (report_esp_error(errcode)) return false;

  // WIFI SETUP //
  // setup the default event loop, for handling WiFi events
  if (report_esp_error(esp_event_loop_create_default())) return false;

  // initialise the TCP/IP stack
  if (report_esp_error(esp_netif_init())) return false;

  // initialise the WiFi configuration to default config values
  wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();

  // initialise WiFi resources, and start the WiFi task
  if (report_esp_error(esp_wifi_init(&config))) return false;

  // set the WiFi to run in station mode
  if (report_esp_error(esp_wifi_set_mode(WIFI_MODE_STA))) return false;

  // set the WiFi configuration storage as RAM
  if (report_esp_error(esp_wifi_set_storage(WIFI_STORAGE_RAM))) return false;

  // set the bandwidth to HT40
  if (report_esp_error(esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT40))) return false;

  // start WiFi, following current settings
  if (report_esp_error(esp_wifi_start())) return false;

  // set the ESP-NOW rate
  if (report_esp_error(esp_wifi_config_espnow_rate(WIFI_IF_STA, WIFI_PHY_RATE_MCS0_SGI))) return false;

  // set power saving type to none
  if (report_esp_error(esp_wifi_set_ps(WIFI_PS_NONE))) return false;

  // set the primary and secondary channels
  if (report_esp_error(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_BELOW))) return false;

  // set the MAC address
  if (report_esp_error(esp_wifi_set_mac(WIFI_IF_STA, MAC_ADDR_SEND))) return false;

  // initialise ESP-NOW
  if (report_esp_error(esp_now_init())) return false;

  // set the ESP-NOW primary master key
  if (report_esp_error(esp_now_set_pmk((uint8_t *)"pmk1234567890123"))) return false;

  // define the receiver as an ESP-NOW peer
  esp_now_peer_info_t recv = 
  {
    .channel   = CONFIG_LESS_INTERFERENCE_CHANNEL,
    .ifidx     = WIFI_IF_STA,    
    .encrypt   = false   
  };

  memcpy(recv.peer_addr, MAC_ADDR_BROAD, sizeof(uint8_t[6]));

  // add the peer to the ESP-NOW network
  if (report_esp_error(esp_now_add_peer(&recv))) return false;

  // finally, report on the sending parameters
  Serial.printf("Channel: %d\nFrequency: %d Hz\nMAC address: " MACSTR "\n", CONFIG_LESS_INTERFERENCE_CHANNEL, CONFIG_SEND_FREQUENCY, MAC2STR(MAC_ADDR_SEND));

  return true;
}

void generate_timestamp(char* timestamp, DateTime* now, bool img, unsigned long milli = 0)
{
  if(img)
  {
    if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d", now->year(), now->month(), now->day(), now->hour(), now->minute(), now->second());                           //00000
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d", now->year(), now->month(), now->day(), now->hour(), now->minute(), 0, now->second());                      //00001
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), now->month(), now->day(), now->hour(), 0, now->minute(), 0, now->second());                 //00011
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d", now->year(), now->month(), now->day(), now->hour(), 0, now->minute(), now->second());                      //00010
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), now->month(), now->day(), 0, now->hour(), 0, now->minute(), now->second());                 //00110
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), now->month(), now->day(), 0, now->hour(), 0, now->minute(), 0, now->second());            //00111
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), now->month(), now->day(), 0, now->hour(), now->minute(), 0, now->second());                 //00101
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d", now->year(), now->month(), now->day(), 0, now->hour(), now->minute(), now->second());                      //00100
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), 0, now->hour(), now->minute(), now->second());                 //01100
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), 0, now->hour(), now->minute(), 0, now->second());            //01101
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), 0, now->second());       //01111
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), now->second());            //01110
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), now->hour(), 0, now->minute(), now->second());                 //01010
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), now->hour(), 0, now->minute(), 0, now->second());            //01011
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), now->hour(), now->minute(), 0, now->second());                 //01001
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d", now->year(), now->month(), 0, now->day(), now->hour(), now->minute(), now->second());                      //01000
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), now->hour(), now->minute(), now->second());                 //11000
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), now->hour(), now->minute(), 0, now->second());            //11001
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), now->hour(), 0, now->minute(), 0, now->second());       //11011
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), now->hour(), 0, now->minute(), now->second());            //11010
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), now->second());       //11110
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), 0, now->second());  //11111
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), now->minute(), 0, now->second());       //11101
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), now->minute(), now->second());            //11100
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), 0, now->hour(), now->minute(), now->second());                 //10100
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), 0, now->hour(), now->minute(), 0, now->second());            //10101
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), 0, now->hour(), 0, now->minute(), 0, now->second());       //10111
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), 0, now->hour(), 0, now->minute(), now->second());            //10110
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), now->hour(), 0, now->minute(), now->second());                 //10010
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), now->hour(), 0, now->minute(), 0, now->second());            //10011
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), now->hour(), now->minute(), 0, now->second());                 //10001
    }
    else
    {
      sprintf(timestamp, "%d%d%d%d%d%d%d", now->year(), 0, now->month(), now->day(), now->hour(), now->minute(), now->second());                      //10000
    }
  }
  else
  {
    if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d:%d:%d:%lu", now->year(), now->month(), now->day(), now->hour(), now->minute(), now->second(), milli);                           //00000
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d:%d:%d%d:%lu", now->year(), now->month(), now->day(), now->hour(), now->minute(), 0, now->second(), milli);                      //00001
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d:%d%d:%d%d:%lu", now->year(), now->month(), now->day(), now->hour(), 0, now->minute(), 0, now->second(), milli);                 //00011
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d:%d%d:%d:%lu", now->year(), now->month(), now->day(), now->hour(), 0, now->minute(), now->second(), milli);                      //00010
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d%d:%d%d:%d:%lu", now->year(), now->month(), now->day(), 0, now->hour(), 0, now->minute(), now->second(), milli);                 //00110
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d%d:%d%d:%d%d:%lu", now->year(), now->month(), now->day(), 0, now->hour(), 0, now->minute(), 0, now->second(), milli);            //00111
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d%d:%d:%d%d:%lu", now->year(), now->month(), now->day(), 0, now->hour(), now->minute(), 0, now->second(), milli);                 //00101
    }
    else if(now->month() >= 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d:%d%d:%d:%d:%lu", now->year(), now->month(), now->day(), 0, now->hour(), now->minute(), now->second(), milli);                      //00100
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d%d:%d:%d:%lu", now->year(), now->month(), 0, now->day(), 0, now->hour(), now->minute(), now->second(), milli);                 //01100
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d%d:%d:%d%d:%lu", now->year(), now->month(), 0, now->day(), 0, now->hour(), now->minute(), 0, now->second(), milli);            //01101
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d%d:%d%d:%d%d:%lu", now->year(), now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), 0, now->second(), milli);       //01111
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d%d:%d%d:%d:%lu", now->year(), now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), now->second(), milli);            //01110
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d:%d%d:%d:%lu", now->year(), now->month(), 0, now->day(), now->hour(), 0, now->minute(), now->second(), milli);                 //01010
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d:%d%d:%d%d:%lu", now->year(), now->month(), 0, now->day(), now->hour(), 0, now->minute(), 0, now->second(), milli);            //01011
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d:%d:%d%d:%lu", now->year(), now->month(), 0, now->day(), now->hour(), now->minute(), 0, now->second(), milli);                 //01001
    }
    else if(now->month() >= 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d:%d%d:%d:%d:%d:%lu", now->year(), now->month(), 0, now->day(), now->hour(), now->minute(), now->second(), milli);                      //01000
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d:%d:%d:%lu", now->year(), 0, now->month(), 0, now->day(), now->hour(), now->minute(), now->second(), milli);                 //11000
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d:%d:%d%d:%lu", now->year(), 0, now->month(), 0, now->day(), now->hour(), now->minute(), 0, now->second(), milli);            //11001
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d:%d%d:%d%d:%lu", now->year(), 0, now->month(), 0, now->day(), now->hour(), 0, now->minute(), 0, now->second(), milli);       //11011
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d:%d%d:%d:%lu", now->year(), 0, now->month(), 0, now->day(), now->hour(), 0, now->minute(), now->second(), milli);            //11010
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d%d:%d%d:%d:%lu", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), now->second(), milli);       //11110
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d%d:%d%d:%d%d:%lu", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), 0, now->minute(), 0, now->second(), milli);  //11111
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d%d:%d:%d%d:%lu", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), now->minute(), 0, now->second(), milli);       //11101
    }
    else if(now->month() < 10 && now->day() < 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d%d:%d%d:%d%d:%d:%d:%lu", now->year(), 0, now->month(), 0, now->day(), 0, now->hour(), now->minute(), now->second(), milli);            //11100
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d%d:%d:%d%d:%d:%d:%lu", now->year(), 0, now->month(), now->day(), 0, now->hour(), now->minute(), now->second(), milli);                 //10100
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d:%d%d:%d:%d%d:%lu", now->year(), 0, now->month(), now->day(), 0, now->hour(), now->minute(), 0, now->second(), milli);            //10101
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d:%d%d:%d%d:%d%d:%lu", now->year(), 0, now->month(), now->day(), 0, now->hour(), 0, now->minute(), 0, now->second(), milli);       //10111
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() < 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d%d:%d:%d%d:%d%d:%d:%lu", now->year(), 0, now->month(), now->day(), 0, now->hour(), 0, now->minute(), now->second(), milli);            //10110
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() >= 10)
    {
      sprintf(timestamp, "%d:%d%d:%d:%d:%d%d:%d:%lu", now->year(), 0, now->month(), now->day(), now->hour(), 0, now->minute(), now->second(), milli);                 //10010
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() < 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d:%d:%d%d:%d%d:%lu", now->year(), 0, now->month(), now->day(), now->hour(), 0, now->minute(), 0, now->second(), milli);            //10011
    }
    else if(now->month() < 10 && now->day() >= 10 && now->hour() >= 10 && now->minute() >= 10 && now->second() < 10)
    {
      sprintf(timestamp, "%d:%d%d:%d:%d:%d:%d%d:%lu", now->year(), 0, now->month(), now->day(), now->hour(), now->minute(), 0, now->second(), milli);                 //10001
    }
    else
    {
      sprintf(timestamp, "%d:%d%d:%d:%d:%d:%d:%lu", now->year(), 0, now->month(), now->day(), now->hour(), now->minute(), now->second(), milli);                      //10000
    }
  }
}

void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(115200);

  imu_good = imu_setup();
  rtc_good = rtc_setup();
  cam_good = cam_setup();
  sd_good = sd_setup();
  wifi_good = wifi_setup();

  xTaskCreatePinnedToCore(
    task_send_csi, "Task Send CSI"  // A name just for humans
    ,
    3072  // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
    ,
    NULL  // Task parameter which can modify the task behavior. This must be passed as pointer to void.
    ,
    3  // Priority
    ,
    NULL  // Task handle is not used here - simply pass NULL
    ,
    0
  );
  xTaskCreatePinnedToCore(
    task_save_imu, "Task Save IMU"  // A name just for humans
    ,
    4096  // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
    ,
    NULL  // Task parameter which can modify the task behavior. This must be passed as pointer to void.
    ,
    3  // Priority
    ,
    NULL  // Task handle is not used here - simply pass NULL
    ,
    0
  );
  xTaskCreatePinnedToCore(
    task_write_all, "Task Write All"  // A name just for humans
    ,
    8192  // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
    ,
    NULL  // Task parameter which can modify the task behavior. This must be passed as pointer to void.
    ,
    2  // Priority
    ,
    NULL  // Task handle is not used here - simply pass NULL                       
    ,
    1
  );
}

void loop() 
{
  // put your main code here, to run repeatedly:

}

void task_send_csi(void *pvParameters)
{
  esp_now_peer_info_t recv;
  esp_now_get_peer(MAC_ADDR_BROAD, &recv);
  const TickType_t xDelay = (1000 / CONFIG_SEND_FREQUENCY) / portTICK_PERIOD_MS;
  for(;;)
  {
    // send a packet
    //esp_err_t errcode = ;

    report_esp_error(esp_now_send(recv.peer_addr, &pkt_count, sizeof(uint8_t)));

    /*if(errcode != ESP_OK) 
    {
      Serial.print("ESP-NOW send error: ");
      Serial.println(esp_err_to_name(errcode));                                                                           
    }*/

    // wait the specified time before sending again
    vTaskDelay( xDelay ); 
  }
  vTaskDelete( NULL );
}

void task_save_imu(void *pvParameters)
{
  const TickType_t xDelay = (1000 / CONFIG_SEND_FREQUENCY) / portTICK_PERIOD_MS;
  char line[IMU_SIZE + TIME_SIZE]{};
  //char data[60];
  char timestamp[TIME_SIZE]{};
  unsigned long time;
  sensors_event_t a, g, temp;
  DateTime now;

  for(;;)
  {
    imu.getEvent(&a, &g, &temp);
    now = rtc.now();
    new_time = millis();
    time = new_time - old_time;
    memset(timestamp, '\0', TIME_SIZE);
    generate_timestamp(timestamp, &now, false, time);
    sprintf(line, "%s,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f", timestamp, a.acceleration.x, a.acceleration.y, a.acceleration.z, g.gyro.x, g.gyro.y, g.gyro.z);
    //strcpy(line, timestamp);
    //sprintf(data, ",%.6f,%.6f,%.6f,%.6f,%.6f,%.6f", a.acceleration.x, a.acceleration.y, a.acceleration.z, g.gyro.x, g.gyro.y, g.gyro.z);
    //strcat(line, data);

    if(imu_switch == 0)
    {
      taskENTER_CRITICAL(&lock);
      strcpy(imu_data0[imu_idx], line);
      imu_idx++;
      taskEXIT_CRITICAL(&lock);
    }
    else
    {
      taskENTER_CRITICAL(&lock);
      strcpy(imu_data1[imu_idx], line);
      imu_idx++;
      taskEXIT_CRITICAL(&lock);
    }

    vTaskDelay( xDelay ); 
  }
  vTaskDelete( NULL );
}

void task_write_all(void *pvParameters)
{
  const TickType_t xDelay = 1000 / portTICK_PERIOD_MS;
  char filename[FILENAME_SIZE]{};
  char timestamp[TIME_SIZE]{};
  DateTime now;
  File img_file;
  File imu_file;
  
  for(;;)
  {
    Serial.println("started write all");
    taskENTER_CRITICAL(&lock);
    imu_idx_last = imu_idx;
    imu_idx = 0;
    imu_switch = 1 - imu_switch;
    old_time = millis();
    taskEXIT_CRITICAL(&lock);

    if(sd_good)
    {
      now = rtc.now();
      memset(timestamp, '\0', TIME_SIZE);
      memset(filename, '\0', FILENAME_SIZE);
      generate_timestamp(timestamp, &now, true);
      sprintf(filename, "/image%s.jpg", timestamp);

      camera_fb_t *fb = esp_camera_fb_get();
      if (!fb) 
      {
        Serial.println("Failed to get camera frame buffer");
      }

      Serial.printf("Writing files: %s\n", filename);

      img_file = SD.open(filename, FILE_WRITE);
      //imu_file = SD.open("/imu_data.csv", FILE_APPEND);

      if(!img_file)
      {
        Serial.println("Failed to open image file for writing");
      }
      //if(!imu_file)
      //{
      //  Serial.println("Failed to open imu file for writing");
      //}
      Serial.println("Files loaded");

      if(img_file.write(fb->buf, fb->len) == fb->len)
      {
        Serial.println("Image write succeeded");
      } 
      else 
      {
        Serial.println("Image write failed");
      }
      img_file.close();

      // Release image buffer
      esp_camera_fb_return(fb);
      Serial.println("Image saved");
  
      //Serial.println("Writing csv");
      //if(imu_switch == 0)
      //{
      //  for(int i=0;i<imu_idx_last;i++)
      //  {
      //    imu_file.println(imu_data1[i]);
      //  }
      //}
      //else
      //{
      //  for(int i=0;i<imu_idx_last;i++)
      //  {
      //    imu_file.println(imu_data0[i]);
      //  }
    }
      
      
    //  imu_file.close();
    //  Serial.println("imu csv saved");
    //}
    else
    {
      Serial.println("Unknown SD card failure");
    }

    vTaskDelay( xDelay ); 
  }
  vTaskDelete( NULL );
}