Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jpg photo Serial transmit (again!!!) doesn't work #116

Open
philippedc opened this issue Jan 17, 2025 · 2 comments
Open

jpg photo Serial transmit (again!!!) doesn't work #116

philippedc opened this issue Jan 17, 2025 · 2 comments

Comments

@philippedc
Copy link

philippedc commented Jan 17, 2025

Hi, many thanks for this library and your contribution. There was several threads about transmitting a picture,... I'm trying to get it working with the last library version.
The project is building a camera door bell.
The camera is a ESP32-CAM, the receiver is a Cheap Yellow Display ESP32.
Here is the code for the sender:
`
/*
ESP32-CAM takes photos to save on SPIFFS, and with serial transfert / RS485 wire transfer

From an idea of: http://electroniqueamateur.blogspot.com/2020/07/esp32-cam-gestion-distance-de-la-carte.html
https://gist.github.com/ypelletier/e47f4df32745fbdd7bc6799a42979ba3
RS485 exemple: https://microcontrollerslab.com/rs485-serial-communication-esp32-esp8266-tutorial/

about file transfer: https://github.com/PowerBroker2/SerialTransfer/tree/master
discussion: https://forum.arduino.cc/t/transfer-pictures-from-esp32-cam-to-esp32-via-serial/647488
!!! file transfer v3.1.3 only
IDE version 1.8.16 with Expressif Sytems (ESP32) version 2.0.3
*/

//#define SAVE2SPIFFS // to save photo to SPIFFS

// ===========================
// Enter your WiFi credentials
// ===========================
const char* ssid = "xxxxxx";
const char* password = "yyyyyy";

// wifi
//-------
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
WebServer server(80);

// SDCard
//---------
#include <SPIFFS.h>
#include <FS.h>

// to keep data
//-------------
#include <Preferences.h> // https://randomnerdtutorials.com/esp32-save-data-permanently-preferences/
Preferences pref;

// camera
//----------------
#include "esp_camera.h"
#include "Arduino.h"
#include "soc/soc.h" // Disable brownour problems
#include "soc/rtc_cntl_reg.h" // Disable brownour problems

// file transfer
//----------------
#include "SerialTransfer.h" // https://github.com/PowerBroker2/SerialTransfer/tree/master
SerialTransfer myTransfer;
HardwareSerial Comm(1);

// RS485
//----------------
// note: 2nd Serial with Tx = 15, Rx = 14 to MAX485 Rx & Tx pins
#define REpin 15 // MAX485 RE/DE pins 2 & 3
#define TXpin 14 // MAX485 RO pin 4
#define RXpin 13 // MAX485 DI pin 1
#define RS485Transmit HIGH
#define RS485Receive LOW
#define BAUD_RATE 200000 //962100

#define flashPin 4 // white flash led
#define ledPin 33 // red led
int fileNumber = 0; // jpeg file name index number

//
// SETUP
//_____________________________________________________________________________________________

void setup() {

WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector

// Console
Serial.begin(115200); // Define and start serial monitor
delay(1000);
Serial.println(F("starting..."));

// RS485 Serial communication
Comm.begin(BAUD_RATE, SERIAL_8N1, RXpin, TXpin); // Define and start Comm serial port
myTransfer.begin(Comm);
pinMode(REpin, OUTPUT);
digitalWrite(REpin, LOW);

// camera setup - AI Thinker - ESP32-CAM pins definition
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = 5;
config.pin_d1 = 18;
config.pin_d2 = 19;
config.pin_d3 = 21;
config.pin_d4 = 36;
config.pin_d5 = 39;
config.pin_d6 = 34;
config.pin_d7 = 35;
config.pin_xclk = 0;
config.pin_pclk = 22;
config.pin_vsync = 25;
config.pin_href = 23;
config.pin_sscb_sda = 26;
config.pin_sscb_scl = 27;
config.pin_pwdn = 32;
config.pin_reset = -1;

config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG; // YUV422|GRAYSCALE|RGB565|JPEG
config.frame_size = FRAMESIZE_QVGA;
/* UXGA(1600x1200)
SXGA(1280x1024)
XGA(1024x768)
SVGA(800x600)
VGA(640x480)
CIF(400x296)
QVGA(320x240)
HQVGA(240x176)
QQVGA(160x120)
*/
config.jpeg_quality = 12; // 10-63 ; plus bas = meilleure qualité
config.fb_count = 1; // nombre de frame buffers

esp_err_t err = esp_camera_init(&config);
if(err != ESP_OK) {
Serial.printf("Camera error 0x%x", err);
return;
}
sensor_t * s = esp_camera_sensor_get();

// wifi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) delay(500);
Serial.print(F("IP address: ")); Serial.println(WiFi.localIP());

// web server
server.on("/delete", HTTP_GET, handleDelete);
server.on("/clic", HTTP_GET, handleSave);
server.onNotFound(handleNotFound);
server.begin();
Serial.println(F("Web server started."));

// SPIFFS init
//if(SPIFFS.format()) Serial.println(F("SPIFFS erased successfully."));
if(SPIFFS.begin(true)) Serial.println(F("SPIFFS OK"));
else Serial.println(F("SPIFFS error!"));

// get photo index number
pref.begin("parameters", false);
fileNumber = pref.getUInt("num", 0);

} // end of setup

//
// LOOP
//_____________________________________________________________________________________________

void loop() {
server.handleClient();
}

//============================================================================================
// list of functions
//============================================================================================

//
// html Save - Take a photo and save it to SDCard
//____________________________________________________________________________________________

void handleSave(void) {

camera_fb_t * fb = NULL; // frame buffer

fb = esp_camera_fb_get(); // prise de la photo
if(!fb) {
Serial.println("Failed to take a photo.");
return;
}
uint16_t fbSize = fb->len; // keep size of fb
uint8_t *fbFile = fb->buf; // keep picture frame in file

// save photo to SPIFFS
#ifdef SAVE2SPIFFS
char fileName[20] = ""; // picture file name .jpg
sprintf(fileName, "/%d.jpg", fileNumber++); // file name creation
pref.putUInt("num", fileNumber); // keep the file index number
if(SPIFFS.exists(fileName)) SPIFFS.remove(fileName);
File file = SPIFFS.open(fileName, FILE_WRITE);
if(!file) Serial.println("SPIFFS error!");
else {
//file.write(fb->buf, fb->len);
file.write(fbFile, fbSize);
Serial.printf("Photo saved: %s", fileName);
Serial.print(F(" size= ")); Serial.println(fbSize);
}
file.close();
#endif

// photo file serial transfer
// 1- transfer mode & send fbSize
digitalWrite(REpin, RS485Transmit); // RS485 transmit mode
myTransfer.sendDatum(fbSize); // Send fbSize

// 2- define number of packets to transmit
uint8_t dataLen = MAX_PACKET_SIZE -2; // Reserve two bytes for current file index
uint16_t numPackets = fbSize / dataLen; // the number of packets
if(fbSize % MAX_PACKET_SIZE) numPackets++; // Add an extra transmission if needed

// 3- send all data within the file across multiple packets
for(uint16_t i=0; i<numPackets; i++) { // Send all data within the file across multiple packets
uint16_t fileIndex = i * MAX_PACKET_SIZE; // Determine the current file index

// define data length for the last packet if file length is not an exact multiple of MAX_PACKET_SIZE-2
if((fileIndex + (MAX_PACKET_SIZE -2)) > fbSize) dataLen = fbSize - fileIndex;

// 4- send packets
uint8_t sendSize = myTransfer.txObj(fileIndex); // Stuff the current file index
sendSize = myTransfer.txObj(fbFile[fileIndex], sendSize, dataLen); // Stuff the current file data
myTransfer.sendData(sendSize, 1); // Send the current file index and data

Serial.print(i); Serial.print(F(" packet#")); Serial.print(fileIndex);
Serial.print(F(" len= ")); Serial.print(dataLen); 
Serial.print(F(" sendSize= ")); Serial.println(sendSize);
delay(100);

} // end of for

esp_camera_fb_return(fb);
digitalWrite(REpin, RS485Receive); // RS485 stop transmit mode

// web message
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", "");
WiFiClient client = server.client();
server.sendContent("

Une nouvelle photo a été prise.

");
server.sendContent("

Retour à la liste des fichiers

");
} // end of handleSave()

//
// LoadFromSPIFFS() - operations on sdcard
//____________________________________________________________________________________________

bool LoadFromSPIFFS(String path) {

String dataType = "text/plain";
if(path == "/") PrintDirectory();
else {
if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
else if(path.endsWith(".htm")) dataType = "text/html";
else if(path.endsWith(".css")) dataType = "text/css";
else if(path.endsWith(".js")) dataType = "application/javascript";
else if(path.endsWith(".png")) dataType = "image/png";
else if(path.endsWith(".gif")) dataType = "image/gif";
else if(path.endsWith(".jpg")) dataType = "image/jpeg";
else if(path.endsWith(".ico")) dataType = "image/x-icon";
else if(path.endsWith(".xml")) dataType = "text/xml";
else if(path.endsWith(".pdf")) dataType = "application/pdf";
else if(path.endsWith(".zip")) dataType = "application/zip";

File file = SPIFFS.open(path.c_str());
if(!file) return false;

if(server.hasArg("download")) dataType = "application/octet-stream";
if(server.streamFile(file, dataType) != file.size()) Serial.println("Sent less data than expected!");

file.close();

}
return true;
} // end of LoadFromSPIFFS()

//
// PrintDirectory()
//____________________________________________________________________________________________

void PrintDirectory() {

server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", "");
WiFiClient client = server.client();
server.sendContent("

Prise de photo

");
server.sendContent("

");
server.sendContent("

Contenu de la carte SD

");

File dir = SPIFFS.open("/");
File entry = dir.openNextFile();
while(entry) {
Serial.print(F("\tFILE: ")); Serial.print(entry.name());
Serial.print(F("\tSIZE: ")); Serial.println(entry.size());
String output = " ";
output += entry.name();
// add a delete button
output += "
        <a href = /delete?url=";
output += entry.name();
output += "> [Supprimer]
";
server.sendContent(output);
entry = dir.openNextFile();
}
dir.close();
} // end of PrintDirectory()

//
// html Delete
//____________________________________________________________________________________________

void handleDelete() {

if(server.args() == 0) {
returnFail("Mauvais arguments?");
return;
}
String path = "/" + server.arg(0);
Serial.print(F("deleting: ")); Serial.println(path);

//SPIFFS.remove((char *)path.c_str());
SPIFFS.remove(path);

// delete confirmation
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", "");
WiFiClient client = server.client();
server.sendContent("

Le fichier a été supprimé

");
server.sendContent("

Retour à la liste des fichiers

");
} // end of handleDelete()

//
// html display
//____________________________________________________________________________________________

void handleNotFound() {
if(LoadFromSPIFFS(server.uri())) return;
String message = "Action imprevue\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for(uint8_t i = 0; i < server.args(); i++) {
message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
Serial.print(message);
} // end of handleNotFound()

void returnOK() {
server.send(200, "text/plain", "");
}

void returnFail(String msg) {
server.send(500, "text/plain", msg + "\r\n");
}

`

@philippedc
Copy link
Author

philippedc commented Jan 17, 2025

Here is the receiver:

`
/*
RS485 Serial transmit from ESP32-CAM to ESP32-CYD

about serial2 for CYD: witnessmenow/ESP32-Cheap-Yellow-Display#75
about MAX485 to MAX485: https://microcontrollerslab.com/rs485-serial-communication-esp32-esp8266-tutorial/

the RGB led pins are used for RS485 connexion:
17 TX2, 16 RX2, 4 RE

*/

// parameters
//--------------
const String VERSION = "v1.0"; // code version
#define DEBUG 1 // allow console messages

// ===========================
// Enter your WiFi credentials
// ===========================
const char* ssid = "xxxxx";
const char* password = "yyyyyy";

// wifi
#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);

// TFT display
//----------------
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
#include <TJpg_Decoder.h> // https://github.com/Bodmer/TJpg_Decoder

//#include <LovyanGFX.hpp>
//#include "lgfx_ESP32_2432S028.h"

byte chosenTextFont = 0; // 0 or 2 or 4
const byte textFont[] = { 4, 1, 2, 2, 1, 2 }; // 3 combinaisons font + size
#define backgroundColor TFT_BLACK
#define textColor TFT_WHITE
byte screenRotation = 3;

int16_t dw = 320;
int16_t dh = 240;

// sd card part
#include "SPI.h"
#include "FS.h"
#include "SD.h"

// pinup
//-----------------
// sd card pins: default VSPI pins
#define SD_MOSI 23
#define SD_MISO 19
#define SD_SCK 18
#define SD_CS 5

// file transfer
//----------------
#include "SerialTransfer.h" // https://github.com/PowerBroker2/SerialTransfer/tree/master
SerialTransfer myTransfer;

#define BAUD_RATE 200000
#define RS485Transmit HIGH
#define RS485Receive LOW
#define REpin 4

char imageBuffer[5000];
uint16_t loopCount = 0;
uint16_t byteCount = 0;
uint16_t imageSize = 0;

//
// SETUP
//_____________________________________________________________________________________________

void setup() {

// console
Serial.begin(115200);
delay(1000);

// RS485
Serial2.begin(BAUD_RATE); // RS485 IO: 17 TX2, 16 RX2
myTransfer.begin(Serial2);
pinMode(REpin, OUTPUT);
digitalWrite(REpin, RS485Receive); // init receive

// TFT display
tft.init();
tft.fillScreen(backgroundColor);
tft.setRotation(screenRotation);
//tft.setRotation(0);
/*
Normally strings are printed relative to the top left corner but this can be
changed with the setTextDatum() function. The library has #defines for:

TL_DATUM = 0 = Top left
TC_DATUM = 1 = Top centre
TR_DATUM = 2 = Top right
ML_DATUM = 3 = Middle left
MC_DATUM = 4 = Middle centre
MR_DATUM = 5 = Middle right
BL_DATUM = 6 = Bottom left
BC_DATUM = 7 = Bottom centre
BR_DATUM = 8 = Bottom right

L_BASELINE = 9 = Left character baseline (Line the 'A' character would sit on)
C_BASELINE = 10 = Centre character baseline
R_BASELINE = 11 = Right character baseline
*/

tft.setSwapBytes(true);
tft.setTextColor(textColor);
tft.setTextFont(textFont[chosenTextFont]);
tft.setTextSize(textFont[chosenTextFont +1]);
tft.setTextDatum(MC_DATUM);
tft.drawString("Moniteur Video", 160, 120 -80);
tft.drawString(VERSION, 160, 120 -40);
//tft.drawString("", 160, 120);
//tft.drawString("", 160, 120 +40);
//tft.drawString("", 160, 120 +80);
delay(2000);
tft.fillScreen(TFT_BLUE);

TJpgDec.setJpgScale(1); // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
TJpgDec.setCallback(tft_output); // The decoder must be given the exact name of the rendering function above

dw = tft.width();
dh = tft.height();

// start sd card: must be placed AFTER tft and ts setup
//------------------
// info: https://www.programmingelectronics.com/esp-32-sd-card-test-and-hardware-setup/
//pinMode( SD_CS, OUTPUT );
SPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
//if(!SD.begin(SD_CS)) Serial.println(F("Card Mount Failed"));
// to allow 6 "open files" instead of the default 5
if(!SD.begin(SD_CS, SPI, 4000000, "", 7)) Serial.println(F("Card Mount Failed"));
else {
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE) Serial.println(F("No SD card attached"));
else {
Serial.print(F("SD Card Type: "));
if(cardType == CARD_MMC) Serial.println(F("MMC"));
else if(cardType == CARD_SD) Serial.println(F("SDSC"));
else if(cardType == CARD_SDHC) Serial.println(F("SDHC"));
else Serial.println(F("UNKNOWN"));
Serial.printf("SD Card Size: %lluMB\n", SD.cardSize()/(1024*1024));
ListDir(SD, "/", 0);
}
}

} // end of setup

//
// LOOP
//_____________________________________________________________________________________________

void loop() {
unsigned long tempo = millis();
static unsigned long memoTempo = tempo;

if(myTransfer.available()) {
memoTempo = tempo;
if(!myTransfer.currentPacketID()) {
myTransfer.rxObj(imageSize);
#ifdef DEBUG
Serial.println(imageSize);
#endif
}
else if(myTransfer.currentPacketID() == 1) {
#ifdef DEBUG
Serial.print(++loopCount); Serial.print(": bytesRead: "); Serial.println(myTransfer.bytesRead);
#endif
for(uint16_t i=2; i<myTransfer.bytesRead; i++) {
imageBuffer[byteCount] = (char)myTransfer.packet.rxBuff[i];
byteCount++;
}
} // end of test myTransfer.currentPacketID() == 1
} // end of test myTransfer.available()

if((tempo > (memoTempo + 1000)) && (loopCount > 0)) {

Serial.print(F("byteCount= ")); Serial.println(byteCount);
Serial.print(F("imageSize: ")); Serial.println(imageSize);

// save to sdcard
String path = "/pic.jpg";
if(SD.remove(path)) Serial.println(F("File deleted"));
else Serial.println(F("no file to delete"));
File file = SD.open(path, FILE_WRITE);
if(!file) Serial.println(F("Failed to open file for writing"));
//file.write((unsigned char *)imageBuffer, byteCount);
file.write((unsigned char *)imageBuffer, imageSize);
file.close();
Serial.printf("Record file: %s\n", path);

// read picture from sdcard
//char adresse2[] = "/12.jpg";
TJpgDec.drawSdJpg(0, 0, path);
//tft.drawJpg(imageBuffer, imageSize, 0, 0, dw, dh);
/*
uint16_t w = 0, h = 0;
TJpgDec.getJpgSize(&w, &h, (const uint8_t)*imageBuffer, imageSize);
Serial.print(F("width = ")); Serial.print(w); Serial.print(F(", height = ")); Serial.println(h);

TJpgDec.drawJpg(0, 0, imageBuffer, imageSize);
//tft.pushImage( 0,0,320,240, (uint16_t*)imageBuffer.c_str());

*/
// reset counters
//Serial.print(F("Photo published size: ")); Serial.println(imageSize);
loopCount = 0;
byteCount = 0;
memset(imageBuffer, 0, sizeof(imageBuffer));
ListDir(SD, "/", 0);
} // end of test tempo
} // end of loop

//============================================================================================
// list of functions
//============================================================================================

//
// tft_output() called by TJpgDec.setCallback(tft_output);
//____________________________________________________________________________________________

bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) {

if( y >= tft.height()) return 0; // Stop further decoding as image is running off bottom of screen
tft.pushImage(x, y, w, h, bitmap); // This function will clip the image block rendering automatically at the TFT boundaries
return 1; // Return 1 to decode next block
}

//
// sd card functions
//____________________________________________________________________________________________

void ListDir(fs::FS &fs, const char *dirname, uint8_t levels) {
Serial.printf("Listing directory: %s\n", dirname);

File root = fs.open(dirname);
if(!root) {
Serial.println(F("Error to open directory"));
return;
}
if(!root.isDirectory()) {
Serial.println(F("Not a directory"));
return;
}

File file = root.openNextFile();
while(file) {
if(file.isDirectory()) {
Serial.print(F("\tDIR : "));
Serial.println(file.name());
if(levels >0) ListDir(fs, file.name(), levels -1);
}
else {
Serial.print(F("\tFILE: "));
Serial.print(file.name());
Serial.print(F("\tSIZE: "));
Serial.println(file.size());
}
file = root.openNextFile();
}
} // end of ListDir()

void ReadFile(fs::FS &fs, const char *path) {
Serial.printf("Reading file: %s\n", path);

File file = fs.open(path);
if(!file) {
Serial.println(F("Failed to open file for reading"));
return;
}

Serial.print(F("Read from file: "));
while(file.available()) Serial.write(file.read());
file.close();
} // end of ReadFile()

void WriteFile(fs::FS &fs, const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);

File file = fs.open(path, FILE_WRITE);
if(!file) {
Serial.println(F("Failed to open file for writing"));
return;
}
if(file.print(message)) Serial.println(F("File written"));
else Serial.println(F("Write failed"));
file.close();
} // end of WriteFile()

void AppendFile(fs::FS &fs, const char *path, const char *message) {
Serial.printf("Appending to file: %s\n", path);

File file = fs.open(path, FILE_APPEND);
if(!file) {
Serial.println(F("Failed to open file for appending"));
return;
}
if(file.print(message)) Serial.println(F("Message appended"));
else Serial.println(F("Append failed"));
file.close();
} // end of AppendFIle()

void RenameFile(fs::FS &fs, const char * path1, const char * path2) {
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (fs.rename(path1, path2)) Serial.println(F("File renamed"));
else Serial.println(F("Rename failed"));
}

void DeleteFile(fs::FS &fs, const char * path) {
Serial.printf("Deleting file: %s\n", path);
if (fs.remove(path)) Serial.println(F("File deleted"));
else Serial.println(F("Delete failed"));
}

void CopyFile(fs::FS &fs1, const char * path1, fs::FS &fs2, const char * path2) {
Serial.printf("copying: %s to %s\n", path1, path2);
File file1 = fs1.open(path1, FILE_READ);
if( fs2.open(path2, FILE_READ)) Serial.printf("%s yet exists, delete it first\n", path2);
File file2 = fs2.open(path2, FILE_WRITE);

size_t n;
uint8_t buf[64];
while((n = file1.read(buf, sizeof(buf))) > 0) {
file2.write(buf, n);
}
}

`

@philippedc
Copy link
Author

I cannot receive a viewable jpg photo.
I do not understand what is wrong, it would be possible to compare the jpg file content before transmit and once receive, however I cannot configure the SD card at the same time with Serial transfer...

However if I rename the pic.jpg as pic.txt it looks like a jpg file.
Something strange :
I first transmit the image buff size: myTransfer.sendDatum(fbSize);
Once received, I do not get the same value between fbSize and the number of bytes transmitted during the for(uint16_t i=2; i<myTransfer.bytesRead; i++)
for instance on the console I can get:
byteCount= 3416
imageSize: 3442

I guess here is the problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant