Write large file to CH376 / CH375 (raw image)

Dear experts,
My goal is to capture a picture with an ESP32-CAM and save it via UART (HardawareSerial1) to a CH376 module, on a USB flashdrive.
Using Scott C example, I can write a String-based file without issue, but I need guidance for saving a small RAW image file.
Please find below the code employed in an attempt of saving a picture by entering "A" on the serial terminal. The trouble shall be located around line 126, with the image buffer written over serial. My output files remains desperately 1 Byte big only...

#include "esp_camera.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include <HardwareSerial.h>
HardwareSerial USB(1);

byte computerByte;           //used to store data coming from the computer
byte USB_Byte;               //used to store data coming from the USB stick
int timeOut = 3000;          //TimeOut is 3 seconds. This is the amount of time you wish to wait for a response from the CH376S module.
int LED = 33;                //the red LED is connected to digital pin 33
const int RST = 2;            // Add a reset line or the chip won't connect

#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
  pinMode(RST, OUTPUT);
  digitalWrite(RST, HIGH);
  delay(100);
  digitalWrite(RST, LOW);
  Serial.begin(2000000);                                    // Setup serial communication with the CH376S module (using the default baud rate of 9600)
  USB.begin(57600, SERIAL_8N1, 12, 13);   //, Comm_Txd_pin, Comm_Rxd_pin); // Define and start Comm serial port
  delay(100);
  Serial.println("Serial started...");
  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_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_GRAYSCALE;

  if (psramFound()) {
    config.frame_size = FRAMESIZE_QQVGA; // candidates are QQVGA|QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.fb_count = 1;
  } else {
    Serial.println(F("PSRAM NOT FOUND"));
    return;
  }

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("CAMERA ERROR 0x%x", err);
    return;
  }
  Serial.println("Setting to USB mode...");
  set_USB_Mode(0x06);
}
void loop() {
  if (Serial.available()) {
    computerByte = Serial.read();                      //read any incoming bytes from the Serial monitor, and store this byte in the variable called computerByte
    if (computerByte == 65) {           //A CAPITAL
      Serial.println("Let's take and save a picture");
      takePicture();
    }
  }
  if (USB.available()) {                               // This is here to capture any unexpected data transmitted by the CH376S module
    Serial.print("CH376S has just sent this code:");
    Serial.println(USB.read(), HEX);
  }
}

//END OF LOOP FUNCTION ========================================================================================================================================
// Take a picture and save the buffer to USB drive
void takePicture() {
  camera_fb_t * fb = NULL;
  Serial.print("1 SEC TO PHOTO");
  delay(1000);
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.print("ERROR WHILE TAKING PHOTO");
    return;
  }
  set_USB_Mode(0x06);             //Set to USB Mode
  USBdiskMount();                 //Prepare the USB for reading/writing - you need to mount the USB disk for proper read/write operations.
  setFileName("PIC.BMP");          //Set File name
  if (fileCreate()) {             //Try to create a new file. If file creation is successful

    uint16_t dataLength = fb->len;         // This variable holds the length of the data to be written (in bytes)
    Serial.print("Data Length:");
    Serial.println(dataLength);
    delay(100);
    // This set of commands tells the CH376S module how many bytes to expect from the Arduino.  (defined by the "dataLength" variable)
    USB.write(0x57);
    USB.write(0xAB);
    USB.write(0x3C);
    USB.write((byte) dataLength);
    USB.write((byte) 0x00);
    Serial.println("Data length written to FILE");
    if (waitForResponse("setting data Length")) {    // Wait for an acknowledgement from the CH376S module before trying to send data to it
      Serial.println("Waiting for acknoledgment from CH376...");
      if (getResponseFromUSB() == 0x1E) {            // 0x1E indicates that the USB device is in write mode.
        USB.write(0x57);
        USB.write(0xAB);
        USB.write(0x2D);
        Serial.println("CH376S in write mode...");

              for (size_t i = 0; i < fb->len; i++)
              {
                if (i % 16 == 0) USB.printf("\n%06u\t", i);
                if (fb->buf[i] < 0x10) USB.write('0');
                Serial.println(fb->buf[i], HEX);
                USB.print(fb->buf[i], HEX);
              }

        if (waitForResponse("writing data to file")) { // wait for an acknowledgement from the CH376S module
        }
        Serial.print("Write code (normally FF and 14): ");
        Serial.print(USB.read(), HEX);               // code is normally 0xFF
        Serial.print(",");
        USB.write(0x57);
        USB.write(0xAB);
        USB.write(0x3D);                             // This is used to update the file size. Not sure if this is necessary for successful writing.
        if (waitForResponse("updating file size")) { // wait for an acknowledgement from the CH376S module
        }
        Serial.println(USB.read(), HEX);             //code is normally 0x14
      }
    }
  }

else {
  Serial.println("File could not be created, or it already exists");
}
fileClose(0x01);
esp_camera_fb_return(fb);
}
//=====================================================
//fileCreate()========================================================================================
//the command sequence to create a file
boolean fileCreate() {
  boolean createdFile = false;
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x34);
  if (waitForResponse("creating file")) {     //wait for a response from the CH376S. If file has been created successfully, it will return true.
    if (getResponseFromUSB() == 0x14) {      //CH376S will send 0x14 if this command was successful
      createdFile = true;
    }
  }
  return (createdFile);
}




//set_USB_Mode=====================================================================================
//Make sure that the USB is inserted when using 0x06 as the value in this specific code sequence
void set_USB_Mode (byte value) {
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x15);
  USB.write(value);

  delay(20);

  if (USB.available()) {
    USB_Byte = USB.read();
    //Check to see if the command has been successfully transmitted and acknowledged.
    if (USB_Byte == 0x51) {                               // If true - the CH376S has acknowledged the command.
      Serial.println("set_USB_Mode command acknowledged"); //The CH376S will now check and monitor the USB port
      USB_Byte = USB.read();

      //Check to see if the USB stick is connected or not.
      if (USB_Byte == 0x15) {                           // If true - there is a USB stick connected
        Serial.println("USB is present");
        blinkLED();                                     // If the process was successful, then turn the LED on for 1 second
      } else {
        Serial.print("USB Not present. Error code:");   // If the USB is not connected - it should return an Error code = FFH
        Serial.print(USB_Byte, HEX);
        Serial.println("H");
      }

    } else {
      Serial.print("CH3765 error!   Error code:");
      Serial.print(USB_Byte, HEX);
      Serial.println("H");
    }
  }
  delay(20);
}

//USBdiskMount========================================================================================
//initialise the USB disk and check that it is ready - this process is required if you want to find the manufacturing information of the USB disk
void USBdiskMount() {
  Serial.println("Mounting USB disk");
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x31);

  if (waitForResponse("mounting USB disk")) {     //wait for a response from the CH376S. If CH376S responds, it will be true. If it times out, it will be false.
    if (getResponseFromUSB() == 0x14) {           //CH376S will send 0x14 if this command was successful
      Serial.println(">USB Mounted - OK");
    } else {
      Serial.print(">Failed to Mount USB disk.");
    }
  }
}


//setFileName======================================================================================
//This sets the name of the file to work with
void setFileName(String fileName) {
  Serial.print("Setting filename to:");
  Serial.println(fileName);
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x2F);
  USB.write(0x2F);         // Every filename must have this byte to indicate the start of the file name.
  USB.print(fileName);     // "fileName" is a variable that holds the name of the file.  eg. TEST.TXT
  USB.write((byte)0x00);   // you need to cast as a byte - otherwise it will not compile.  The null byte indicates the end of the file name.
  delay(20);
}

//fileClose=======================================================================================
//closes the file
void fileClose(byte closeCmd) {
  Serial.println("Closing file:");
  USB.write(0x57);
  USB.write(0xAB);
  USB.write(0x36);
  USB.write((byte)closeCmd);                                // closeCmd = 0x00 = close without updating file Size, 0x01 = close and update file Size

  if (waitForResponse("closing file")) {                    // wait for a response from the CH376S.
    byte resp = getResponseFromUSB();
    if (resp == 0x14) {                                    // CH376S will send 0x14 if this command was successful
      Serial.println(">File closed successfully.");
    } else {
      Serial.print(">Failed to close file. Error code:");
      Serial.println(resp, HEX);
    }
  }
}

//waitForResponse===================================================================================
//is used to wait for a response from USB. Returns true when bytes become available, false if it times out.
boolean waitForResponse(String errorMsg) {
  boolean bytesAvailable = true;
  int counter = 0;
  while (!USB.available()) {   //wait for CH376S to verify command
    delay(1);
    counter++;
    if (counter > timeOut) {
      Serial.print("TimeOut waiting for response: Error while: ");
      Serial.println(errorMsg);
      bytesAvailable = false;
      break;
    }
  }
  delay(1);
  return (bytesAvailable);
}

//getResponseFromUSB================================================================================
//is used to get any error codes or messages from the CH376S module (in response to certain commands)
byte getResponseFromUSB() {
  byte response = byte(0x00);
  if (USB.available()) {
    response = USB.read();
  }
  Serial.println("Response from USB: \t");
  Serial.print(response);
  return (response);
}

//blinkLED==========================================================================================
//Turn an LED on for 1 second
void blinkLED() {
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);
}

This piece of code was written after the one from tolgaeng
Please note image structure fb is made of:

    typedef struct {
    uint8_t * buf;       // Pointer to the pixel data 
    size_t len;          // Length of the buffer in bytes 
    size_t width;        // Width of the buffer in pixels 
    size_t height;       // Height of the buffer in pixels 
    pixformat_t format;  // Format of the pixel data 
} camera_fb_t;

For future searches, note the RESET PIN of CH376 HAS to be triggered at startup.
Thank you for your help.

Simple_Jack

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.