Trasmissione di file immagini tra due arduino

Ciao a tutti sono Nicolò, recentemente ho sviluppato un piccolo cubesat 1U solo via software e prima di mandarlo in stampa sto facendo alcuni test per capire come strutturare il software di questo.

Sto riscontrando un problema nella parte di trasmissione di file in UART tra due arduino.

Il mio obbiettivo è quello di trasmettere un file contenuto in una micro SD a un'altro arduino il quale lo salverà in una micro SD.

Ho usato chatgpt e ho letto sui vari forum come trasmettere i file e sono arrivato a questi codici, il file lo trasmette ma quando lo apro dal pc oltre ad essere senza peso il file non viene riconosciuto.

MASTER - invia il file presente sulla SD

#include <SoftwareSerial.h>
#include <SPI.h>
#include <SD.h>

#define SD_CS 10  // Pin Chip Select della microSD
#define BUFFER_SIZE 64  // Dimensione del buffer

SoftwareSerial Serial1(7, 6);  // TX, RX

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
    while (!Serial);
    delay(500);
    Serial.println("Inizializzazione...");

    if (!SD.begin(SD_CS)) {
        Serial.println("Errore nella microSD!");
        return;
    }

    Serial.println("In attesa del ricevitore...");

    unsigned long startTime = millis();
    bool receivedResponse = false;

    while (millis() - startTime < 10000) {  // Attendi fino a 10 secondi
        while (Serial1.available()) {
            char response = Serial1.read();
            if (response == 'R') {  // Il ricevitore invia 'R' per indicare "READY"
                Serial.println("Ricevitore pronto, avvio trasmissione...");
                receivedResponse = true;
                break;
            }
        }
        if (receivedResponse) break;
    }

    if (!receivedResponse) {
        Serial.println("Errore: nessuna risposta valida dal ricevitore.");
        return;
    }

    sendFile("test.png");
}

void sendFile(const char* filename) {
    File file = SD.open(filename);
    if (!file) {
        Serial.println("Errore nell'apertura del file!");
        return;
    }

    Serial1.println("START");  // Segnala l'inizio della trasmissione
    delay(100);

    uint8_t buffer[BUFFER_SIZE];
    int bytesRead;

    while ((bytesRead = file.read(buffer, BUFFER_SIZE)) > 0) {
        Serial1.write(buffer, bytesRead);
        Serial.write(buffer, bytesRead);
        delay(10);
    }

    Serial1.println("END");  // Segnala la fine del file
    file.close();
    Serial.println("File inviato con successo!");
}

void loop() {}

SALVE - salva il file nella SD

#include <SoftwareSerial.h>
#include <SPI.h>
#include <SD.h>

#define SD_CS 10  // Pin Chip Select della microSD
#define BUFFER_SIZE 64  // Dimensione del buffer

SoftwareSerial Serial1(7,6);

void restart(){};

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
    while (!Serial);
    delay(500);
    Serial.println("Init");

    if (!SD.begin(SD_CS)) {
        Serial.println("Errore nell'inizializzazione della microSD!");
        restart();
    }

    Serial.println("Pronto per ricevere il file...");

    while (true) {  // Continua a inviare "READY" fino a quando non riceve "START"
        Serial1.write('R');  // Invia 'R' per indicare che è pronto
        delay(1000);
        while (Serial1.available()) {
            char received = Serial1.read();
            if (received == 'S') {  // Ricevuto "START"
                Serial.println("Ricezione in corso...");
                receiveFile();
                return;
            }
        }
    }
}

void receiveFile() {
    File file = SD.open("ricevuto.png", FILE_WRITE);
    if (!file) {
        Serial.println("Errore nella creazione del file!");
        return;
    }

    uint8_t buffer[BUFFER_SIZE];
    int bufferIndex = 0;
    bool receiving = true;

    while (receiving) {
        while (Serial1.available()) {
            char receivedChar = Serial1.read();

            buffer[bufferIndex++] = receivedChar;

            if (bufferIndex >= BUFFER_SIZE) {  // Scrive sul file quando il buffer è pieno
                file.write(buffer, BUFFER_SIZE);
                Serial.write(buffer,BUFFER_SIZE);
                bufferIndex = 0;
            }

            /*if(receivedChar == NULL){
              receiving = false;
            }*/
        }
    }
}

void loop() {}

Se qualcuno riesce a darmi una mano o consigliarmi una libreria che posso usare per trasmettere file di ogni genere da arduino lo ringrazio in anticipo!

Quel protocollo mi pare veramente degno di un codice di ChatGPT, ossia come al solito uno spunto ma che va sempre preso con le molle e verificato.

Per prima cosa: di quale Arduino parliamo? Perché SoftwareSerial generalmente è affidabile per velocità fino a 38400, a 115200 baud non l'ho mai visto funzionare correttamente, almeno su UNO R3, e Mega.

In particolare poi, a proposito del protocollo, vedo che il "trasmittente" manda la stringa "START" (seguita da CR e LF, ossia "\r\n") mentre il ricevente inizia a ricevere i dati appena vede il carattere 'S'... Ma tu nella Sd del destinatario hai provato ad aprire il file con un editor esadecimale? Secondo me i primi byte sono "TART\r\n" seguiti dal vero contenuto, col risultato che il file jpg viene "sminchiato".

Consiglio: fino a che non hai sufficiente pratica di programmazione, lascia perdere chatGPT (tranne solo come "spunto" magari per imparare qualcosa), che non impari una mazza.

... e che, nel 90% dei casi, di primo acchito produce codice che è sbagliato :roll_eyes:

Se uno conosce bene la materia se n'accorge e comincia a punzecchiarlo fino a quando non ammette l'errore e, piano piano, si avvicina a qualche cosa di decente, ma altrimenti ... un disastro.

Guglielmo

Ciao, prova a guardare questa libreria SerialTransfer

ciao, ho usato chatgpt solo per avere un idea di come fare e sono partito da quel codice modificandolo per capire come trasmettere il file

si stavo facendo ora delle prove

Ups ... nessuno se ne è accorto (manco io ... mi sono fidato di docdoc :grin:)?

@nikolo02:

Buongiorno e benvenuto nella sezione Italiana del forum,

cortesemente, come prima cosa, leggi attentamente il REGOLAMENTO di detta sezione, (... e, per evitare future possibili discussioni/incomprensioni, prestando molta attenzione al punto 15), dopo di che, come da suddetto regolamento (punto 16.7), fai la tua presentazione NELL'APPOSITA DISCUSSIONE (... quello che vedi in blu è un link, fai click su di esso per raggiungere la discussione) spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.

Grazie,

Guglielmo

Ci sono varii problemi

Senza entrare in troppi dettagli, ma solo ad una seconda occhiata (ma li avevo già visti alla prima):

Usi la seriale software ad una velocità eccessiva, 115200 non andrà mai
Non superare 19200 e ringrazia che vada
Per velocità maggiori passa a una scheda che abbia due seriali HW oppure evita di usare la USB e usa la sola seriale HW che hai
Lo handshaking (ovvero il reciproco riconoscimento e sincronizzazione) tra trasmettitore e ricevitore è molto approssimato ad esempio tu per cominciare la trasmissione trasmetti "START", ma in ricezione ti accontenti di una S maiuscola
E le 4 lettere che avanzano? Vanno a finire nel file?
Non hai un controllo degli errori
Ma soprattutto non chiudi il file alla fine (anzi, come la riconosci la fine?)

Per ispirazione prova a cercare le implementazioni in Arduino dei protocolli Xmodem, Ymodem e Zmodem
Dubito che facciano al caso tuo, a partire dal fatto che trasmettono blocchi troppo lunghi per una povera UNO, ma possono ispirare....

E proposito: specifica che schede stai usando

E perché dovrei farlo?
Se conosco la materia o faccio da solo e non chiedo alle AI oppure chiedo e poi correggo da me
Comunque dopo che ho ottenuto un risultato "certamente" non lo vado a spiegare alla AI, che impara a mie spese e rivende ad altri il mio lavoro

grazie per il consiglio, gli darò un occhiata. come scheda sto usando un arduino nano per fare dei test, e sul satellite sarà presente lo stesso microcontrollore un atmega328p e per prendere le immagini c'è un rp2040 con una camera ov5640. La obc è presente il atmega328p e avrà come sistema operativo freertos perchè dovrà eseguire delle operazioni in remoto. sulla scheda che gestisce le comunicazioni a terra non è presente una microsd quindi i passaggi che dovrà fare l'immagine

dalla microsd collegata all'RP2040 --> alla SD dell'OBC --> tramite la scheda radio a terra

Ehhhh ??? E che c'entra FreeRTOS™? E poi ... FreeRTOS™ su ATMega328P ??? ... scordati di farci cose serie, NON c'è sufficiente SRAM.

Guglielmo

ho fatto una prova, una funzione per tenere il tempo, una per eseguire i comandi che arrivano dal modulo radio e gestire le operazioni in remoto e ce la fa. le altre schede avranno un codice abbastanza semplice se vengono interpellate rispondono. magari posso usare simplecli per gestire le varie funzioni di queste

Ne dubito, lo stack overflow è in agguato, ma ... uomo avvisato mezzo salvato ...

Guglielmo

Cosa è un OBC?

Comunque mi sembra una cosa confusa e sovraprogettata

Mi spieghi?

si certo, la obc (on board computer) è quella scheda all'interno di un satellite che gestisce tutte le operazioni e le comunicazioni con la terra. praticamente decide come muovere il satellite se le batterie sono scariche per orientare i pannelli solari (ma questa parte nel mio sistema la delocalizzo alla EPS eletric power supply)

Il mio:
Mi spiego

Era riferito alla sovraprogettazione del passaggio dati

Mi spieghi perché gli fai fare quel giro dell'oca?

Perché è la comunicazione nominale all'interno del satellite e la ridondata è la i2c.

Quindi per adesso basta considerare due Arduino con due SD connessi da una seriale

Bene

Esatto

Ciao io ho sviluppato un piccolo codice di prova che invia i dati dalla seriale di Arduino minima R4 alla SD, si basa nell'utilizzo dei caratatteri XOFF/XON e con l`ausilio di teraterm si inviano i dati dal PC divisi a pacchetti di 256byte.

/*
*Questo programma testato su Arduino Minima R4 consete di traferire tramite la UART 
*qalsiasi file su SD. Usa teratem File>Sendfile>Binary>block 256/512>delay 1ms
*WorkInProgress 2025-01
*/

#include <SPI.h>
#include <SD.h>

#define CS_SD 10  // Pin Chip Select per la SD
#define BUFFER_SIZE 256  // Buffer da 512 byte per SD
#define XON  0x11  // ASCII DC1
#define XOFF 0x13  // ASCII DC3

File file;
char buffer[BUFFER_SIZE];
int bufferIndex = 0;
bool receiving = false;
unsigned long lastWriteTime = 0;
unsigned long totalBytesWritten = 0;
unsigned long totalBlocksWritten = 0;

void setup() {
    Serial.begin(115200);
    while (!Serial) {}

    Serial.println("Inizializzazione SD...");
    if (!SD.begin(CS_SD)) {
        Serial.println(" Errore SD! Controlla i collegamenti.");
        return;
    }
    Serial.println("SD pronta!");

    if (SD.exists("output.bin")) {
        SD.remove("output.bin");
        Serial.println("File esistente 'output.bin' cancellato.");
    }

    Serial.write(XON);  // Iniziamo il trasferimento
}

void loop() {
    if (Serial.available()) {
        if (!receiving) {
            Serial.write(XOFF); // Ferma il flusso dati
            file = SD.open("output.bin", FILE_WRITE);
            if (!file) {
                Serial.println("Errore apertura file!");
                return;
            }
            Serial.println("Scrittura su SD iniziata...");
            receiving = true;
        }

        while (Serial.available() && bufferIndex < BUFFER_SIZE) {
            buffer[bufferIndex++] = Serial.read();
        }

        if (bufferIndex == BUFFER_SIZE) {
            file.write((uint8_t*)buffer, BUFFER_SIZE);
            totalBytesWritten += BUFFER_SIZE;
            totalBlocksWritten++;
            
            Serial.print(" Scritti ");
            Serial.print(BUFFER_SIZE);
            Serial.print(" byte - Blocco #");
            Serial.println(totalBlocksWritten);

            bufferIndex = 0;
            lastWriteTime = millis();
        }

        Serial.write(XON); //  Riprendiamo la trasmissione
    }

    // Aspetta 1 secondo dall'ultima scrittura prima di chiudere il file
    if (receiving && Serial.available() == 0 && bufferIndex > 0 && millis() - lastWriteTime >= 4000) {
        Serial.print("Ultimo bufferIndex prima della scrittura finale: ");
        Serial.println(bufferIndex);
        
        file.write((uint8_t*)buffer, bufferIndex);
        totalBytesWritten += bufferIndex;
        totalBlocksWritten++;

        Serial.print("Scritti ultimi ");
        Serial.print(bufferIndex);
        Serial.println(" byte!");

        bufferIndex = 0;
        delay(5000);//debug
        file.close();
        Serial.println(" File salvato come 'output.bin'!");
        Serial.print(" Totale blocchi scritti: ");
        Serial.println(totalBlocksWritten);
        Serial.print(" Totale byte scritti: ");
        Serial.println(totalBytesWritten);
        
        receiving = false;
    }
}

Ora sto lavorando ed ho quasi completato una implementazione con SPI-NOR, se qualcuno è interessato potrei condividerlo in uno nuovo post, vi metto qui l`immagine del menu che sto implementando: