Cant read an BMP image from SD card using sd.h

Hello everyone,
I need some help, I'm trying to read a .BMP image from an SD card reader but apparently ,its not compatible. I dont know what more i can do to solve this problem:

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

#define SSpin 10
SoftwareSerial mySerial(2, 3);
FPM finger(&mySerial);
File myFile;


void printDirectory(File dir, int numTabs) {
    while (true) {
        File entry = dir.openNextFile();
        if (!entry) {// No more files
            break;
        }
        for (uint8_t i = 0; i < numTabs; i++) {
            Serial.print('\t');
        }
        Serial.print(entry.name());
        if (entry.isDirectory()) {
            Serial.println("/");
            printDirectory(entry, numTabs + 1);
        } else {
            // Files have sizes, directories do not
            Serial.print("\t\t");
            Serial.println(entry.size(), DEC);
        }
        entry.close();
    }
}

void setup() {
    Serial.begin(57600);

    Serial.println("Initializing SD card...");
    if (!SD.begin(SSpin)) {
        Serial.println("Initialization failed!");
        return;
    }
    Serial.println("SD Initialization done.");

    if (!SD.exists("two.bmp")) {
        Serial.println("File not found.");
        return;
    }
    Serial.println("File exists.");

    // Abre el archivo de imagen en la tarjeta SD
    myFile = SD.open("one.png", FILE_READ);
    
    if (!myFile) {
        Serial.println("Error opening file.");
        return;
    }
    Serial.println("File opened successfully.");

    size_t fileSize = myFile.size();
    uint8_t buffer[fileSize];
    myFile.read(buffer, fileSize);
    
    myFile.close();
    
    // Inicializa el sensor de huellas dactilares
    if (!finger.begin(FPM_DEFAULT_PASSWORD, FPM_DEFAULT_ADDRESS, FPM_SYS_PARAMS_LEN)) {
        Serial.println("Did not find fingerprint sensor :(");
        return;
    }
    Serial.println("Fingerprint sensor found!");

    // Procesa la imagen con el sensor de huellas dactilares
    FPMStatus status = finger.uploadImage(buffer, fileSize);
    delete[] buffer; // Libera la memoria del buffer
    if (status != FPMStatus::OK) {
        Serial.println("Failed to upload image.");
        return;
    }
    Serial.println("Image uploaded successfully.");

    // Genera el archivo de caracteres a partir de la imagen
    status = finger.image2Tz(1);
    if (status != FPMStatus::OK) {
        Serial.println("Failed to generate character file.");
        return;
    }
    Serial.println("Character file generated successfully.");

    // Genera una plantilla
    status = finger.generateTemplate();
    if (status != FPMStatus::OK) {
        Serial.println("Failed to generate template.");
        return;
    }
    Serial.println("Template generated successfully.");

    // Guarda la plantilla en el buffer del sensor
    status = finger.storeTemplate(1, 1);
    if (status != FPMStatus::OK) {
        Serial.println("Failed to store template.");
        return;
    }
    Serial.println("Template stored successfully.");

    // Descarga la plantilla para comparación
    status = finger.downloadTemplate(1);
    if (status != FPMStatus::OK) {
        Serial.println("Failed to download template.");
        return;
    }
    Serial.println("Template downloaded successfully.");

    // Compara las huellas dactilares
    uint16_t score;
    status = finger.matchTemplatePair(&score);
    if (status != FPMStatus::OK) {
        Serial.println("Fingerprints do not match.");
        return;
    }
    Serial.print("Fingerprints match with a score of ");
    Serial.println(score);
}

void loop() {
    
}

The main purpose is to read the BMP image from the sd sensor, download this image to the buffer sensor (save in the image buffer from the optical sensor r307), generate a template and match "my image" (the picture that i read from the sd card) and match with the fingerprint that i have in my sensor r307.
The schematic for my circuit is:


r307_fingerprint_module_user_manual.pdf (585,0 KB)
Please I need some help, i dont know what i can do to solve this

I moved your topic to an appropriate forum category @agonzale.

In the future, please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.

This is an important part of responsible forum usage, as explained in the "How to get the best out of this forum" guide. The guide contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

Where are you trying to read a bmp image?
I see where you check if it exists. Is that where it fails?

Edit: Yes it can read a bmp file. I just tried it.

Here is the part that fails:
size_t fileSize = myFile.size(); uint8_t buffer[fileSize]; myFile.read(buffer, fileSize);

How big is the file? What is fileSize?
I have a web server that opens and serves many types of files. I use a 65 byte array (tBuf) to hold the output of the SD, then send to the client. myFile is the file handle.

while(myFile.available()) {
   clientCount = myFile.read(tBuf,64);
   client.write((byte*)tBuf,clientCount);
}

My image with extension .BMP has 126KB and the dimension is 256x288 (i resize the image as my optical sensor r307 need in his image buffer an image in format bmp with 256x288) the problem is the library SD.h, i did some probes and with and bmp image said: error reading the image.

With this 3 lines that you post, what is the purpose?
Additionally, when I need to read other image with PNG format said:
SD init done.
File exists.
File opened successfully.
[+]readPacket timeout.

[+]begin: password verification failed
Did not find fingerprint sensor :frowning:

But i use the connection that i told in the forum and when i tried Adafruit my sensor conexion works well, so i dont know,
any idea?

You can't think the compiler can dynamically allocate that buffer if called this way.

    size_t fileSize = myFile.size();
    uint8_t buffer[fileSize];
    myFile.read(buffer, fileSize);

You need either to pre-allocate a buffer (put it as global to make things easier) with a suitable size to be able to contain the largest file you need to load, or use malloc() to dynamically do it (but such method is pretty hard to be managed at this level so I don't suggest you to do so).

Said that, the schematics shows a UNO (so I suppose you're working with it): how can you think you can fit/allocate 126kB into that poor Arduino UNO RAM? Use a smaller buffer (1k or less) and make a loop where you read the file in chunks (e.g. read one buffer data size, write the buffer to destination, and repeat cycle until the end of file).

Does your Arduino have 128K memory available?
uint8_t buffer[fileSize];

// This loops until the file EOF is reached
while(myFile.available()) {
   // clienCount is the number of bytes received. 64 until the last read.
   clientCount = myFile.read(tBuf,64);
   // this writes the clientCount bytes to the client
   client.write((byte*)tBuf,clientCount);
}

Instead of sending it to the client like in my case, send it to your device 64 bytes at a time.
If it is a serial port, change "client.write" to "Serial.write".

Wow, thanks for the advice, now I can read my BMP image and appears:

Initializing SD card...

SD Initialization done.

File opened successfully.

First few bytes of BMP file: 42 20 0 49 0 6E 0 66 0 6F

The problem is that is in a loop and appears all time the same message.
My uploaded code is:

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

#define SSpin 10
SoftwareSerial mySerial(2, 3);
FPM finger(&mySerial);
File dataFile;

void setupSDcard() {
    Serial.println("Initializing SD card...");
    if (!SD.begin(SSpin)) {
        Serial.println("Initialization failed!");
        while (1);
    }
    Serial.println("SD Initialization done.");
}

void SendFile(String Filename) {
    char temp[20];
    Filename.toCharArray(temp, 20);

    dataFile = SD.open(temp);
    if (dataFile) {
        Serial.println("File opened successfully.");
        ReadTheFile();
        dataFile.close();
    } else {
        Serial.print("Error opening file: ");
        Serial.println(temp);
        delay(1000);
        setupSDcard();
        return;
    }
}

void ReadTheFile() {
    const size_t bufferSize = 64;
    uint8_t buffer[bufferSize];

    if (dataFile.available()) {
        dataFile.read(buffer, 10);
        Serial.print("First few bytes of BMP file: ");
        for (int i = 0; i < 10; i++) {
            Serial.print(buffer[i], HEX);
            Serial.print(" ");
        }
        Serial.println();
    }
    
    // Inicializa el sensor de huellas dactilares
    if (!finger.begin()) {
        Serial.println("Did not find fingerprint sensor :(");
        return;
    }
    Serial.println("Fingerprint sensor found!");

    // Procesa la imagen con el sensor de huellas dactilares en fragmentos
    while (dataFile.available()) {
        size_t bytesRead = dataFile.read(buffer, bufferSize);
        FPMStatus status = finger.uploadImage(buffer, bytesRead);
        if (status != FPMStatus::OK) {
            Serial.println("Failed to upload image fragment.");
            return;
        }
    }
    Serial.println("Image uploaded successfully.");

    // Genera el archivo de caracteres a partir de la imagen
    FPMStatus status = finger.image2Tz(1);
    if (status != FPMStatus::OK) {
        Serial.println("Failed to generate character file.");
        return;
    }
    Serial.println("Character file generated successfully.");

    // Genera una plantilla
    status = finger.generateTemplate();
    if (status != FPMStatus::OK) {
        Serial.println("Failed to generate template.");
        return;
    }
    Serial.println("Template generated successfully.");

    // Guarda la plantilla en el buffer del sensor
    status = finger.storeTemplate(1, 1);
    if (status != FPMStatus::OK) {
        Serial.println("Failed to store template.");
        return;
    }
    Serial.println("Template stored successfully.");

    // Descarga la plantilla para comparación
    status = finger.downloadTemplate(1);
    if (status != FPMStatus::OK) {
        Serial.println("Failed to download template.");
        return;
    }
    Serial.println("Template downloaded successfully.");

    // Compara las huellas dactilares
    uint16_t score;
    status = finger.matchTemplatePair(&score);
    if (status != FPMStatus::OK) {
        Serial.println("Fingerprints do not match.");
        return;
    }
    Serial.print("Fingerprints match with a score of ");
    Serial.println(score);
}

void setup() {
    Serial.begin(57600);
    setupSDcard();
    SendFile("prueba.bmp");
}

void loop() {
}

I use a function that its implemented in a library, taking into account the dataset from my r307 optical sensor, I have the functions:

bool FPM::begin(uint32_t pwd, uint32_t addr, FPMSystemParams * params) 
{
    delay(2000);            /* 500 ms at least according to datasheet */
    
#if (FPM_LOG_LEVEL != FPM_LOG_LEVEL_SILENT)
    printf_begin();
#endif
    
    address = addr;
    password = pwd;
    
    if (!verifyPassword(password)) {
        FPM_LOGLN_ERROR("begin: password verification failed");
        return false;
    }
    
    /* check if the user has supplied fixed parameters manually, 
     * this is needed for some sensors like the R308, which don't support SET_PARAM */
    if (params != NULL) {
        useFixedParams = true;
        memcpy(&sysParams, params, sizeof(FPMSystemParams));
        FPM_LOGLN_VERBOSE("begin: using fixed params");
    }
    else if (readParams() != FPMStatus::OK) {
        FPM_LOGLN_ERROR("begin: read params failed");
        return false;
    }
    
    return true;
}

FPMStatus FPM::uploadImage(uint8_t* buffer, size_t bufferSize) {
    buffer[0] = FPM_IMGUPLOAD;

    memcpy(&buffer[1], buffer, bufferSize);
    
    return writeCommandGetResponse(bufferSize + 1); 
}

FPMStatus FPM::image2Tz(uint8_t slot) 
{
    buffer[0] = FPM_IMAGE2TZ; 
    buffer[1] = slot;
    return writeCommandGetResponse(2);
}

FPMStatus FPM::generateTemplate(void) 
{
    buffer[0] = FPM_REGMODEL;
    return writeCommandGetResponse(1);
}


FPMStatus FPM::storeTemplate(uint16_t id, uint8_t slot) 
{
    buffer[0] = FPM_STORE;
    buffer[1] = slot;
    buffer[2] = id >> 8; buffer[3] = id & 0xFF;
    
    return writeCommandGetResponse(4);
} 
FPMStatus FPM::downloadImage(void) 
{
	buffer[0] = FPM_IMGUPLOAD;
    return writeCommandGetResponse(1);
}
FPMStatus FPM::matchTemplatePair(uint16_t * score) 
{
    buffer[0] = FPM_PAIRMATCH;
    
    writePacket(FPM_COMMANDPACKET, buffer, 1);
    
    FPMStatus confirmCode;
    uint16_t readLen = 0;
    
    FPMStatus status = readAckGetResponse(&confirmCode, &readLen);
    
    if (FPM::isErrorCode(status)) return status;
    if (confirmCode != FPMStatus::OK) return confirmCode;
    if (readLen != 2) return FPMStatus::READ_ERROR;
    
    *score = buffer[1]; 
    *score <<= 8;
    *score |= buffer[2];

    return confirmCode;
}

and I need to first read the BMP image from the SD, then download the image to the sensor image buffer, generate a template and then compare my uploaded image with the image that is stored in my sensor buffer.

i try with the change that i put in my last message but the last step that makes correctly is read first few bytes of bmp files

Well, what's your programming knowledge so far?

I see here:

    const size_t bufferSize = 64;
    uint8_t buffer[bufferSize];

    if (dataFile.available()) {
        dataFile.read(buffer, 10);
        Serial.print("First few bytes of BMP file: ");
        for (int i = 0; i < 10; i++) {
            Serial.print(buffer[i], HEX);
            Serial.print(" ");
        }
        Serial.println();
    }

You here define a 64 bytes buffer but read just the first 10, then do nothing more. What are you expecting this does other than always showing the very first 10 bytes?
you added a "while()" loop, but the first 10 bytes are gone so even if it could do the job, the resulting bitmap is incomplete.

I don't know your finger reader so I can't say if you can "stream" the data in blocks, but you need to review your code (I'd start with just a testing code before trying to go on with this project...) or search around for some other examples of uploading bitmaps to finger reader.

A couple things.
When you read from the SD, it increments the file pointer.
You are reading 10 bytes initially. That increments the file pointer.
When you do the "while", it will start sending at position 10. Reset the pointer to start at the beginning.
datafile.seek(0);
Also note in the example, it uses snprintf() in a do.while loop. This is getting the image. I don't see a function that allows BMP files to be imported, just copied from the sensor.

    do {
        status = finger.getImage();
        
        switch (status) 
        {
            case FPMStatus::OK:
                Serial.println("Image taken.");
                break;
                
            case FPMStatus::NOFINGER:
                Serial.println(".");
                break;
                
            default:
                /* allow retries even when an error happens */
                snprintf(printfBuf, PRINTF_BUF_SZ, "getImage(): error 0x%X", static_cast<uint16_t>(status));
                Serial.println(printfBuf);
                break;
        }
        
        yield();
    }
    while (status != FPMStatus::OK);

Nah, I have another problem, and it's that (as you can see in the uploaded image) I don't have enough space in my image buffer to save the image since it has 70KB.

So maybe it would be easier to download the image stored in my optical sensor and try to compare it with my original image using a Python method or something? I'm not sure...

Thanks for your latest advice; dividing the image to read it worked for me.

But now, I've discovered that it will be impossible to do it this way, as my optical sensor doesn't have enough memory to save the image.

I'll try to download the template from my sensor and later compare the image stored on my PC with the one from the sensor. I don't have any more ideas...

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