I'm having problems with reading a bitmap file with the SD library. I wrote a small header that reads uncompressed 24-bit bitmap files. It works fine when I try it on Windows 10 MingW with fread() instead of SD.read(). On my UNO it only reads the correct offset address, the other parts are garbage.
For a pure red 1x1 px bitmap I get the following expected output on Windows with MingW:
Fname: ..\rot.bmp
Offset: 00000036
W * H: 1 * 1
Bit# 24
Cmp: 0
ArraySize: 1
---------------------------
RGB: ff0000
Arduino outputs:
Fname: rot.bmp
Offset: 00000036
W * H: 0 * 2304
Bit# 58
Cmp: 0
ArraySize: 8
---------------------------
Error while reading BMP file
bmp.hpp
#ifndef BMP_HEADER
#define BMP_HEADER
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
#include <SD.h>
#include<stdint.h>
#define BYTE uint8_t
#define WORD uint16_t
#define DWORD uint32_t
#define LONG int32_t
#else
#include<stdlib.h>
#include<windows.h>
#endif // defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
#pragma pack(2)
typedef struct
{
BYTE B;
BYTE G;
BYTE R;
} pixel;
typedef struct
{
WORD bfType;
DWORD bfSize;
DWORD bfReserved;
DWORD bfOffset; //offset to byte array
} bfheader;
typedef struct
{
DWORD biSize; //size of this header
LONG biWidth; //image width
LONG biHeight;//image height
WORD biPlanes;
WORD biBitCount; //color depth in bit per pixel
DWORD biCompression; //0 for none(RGB mode)
DWORD biSizeImage; //size of image in byte; may be 0 if biCompression is RGB
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed; //palete stuff
DWORD biClrImportant; //more palete stuff
} biheader;
//returns BGR array of bitmap
//header and biheader are assumed to be allocated upon execution
//returns null if unsuccessful
inline pixel* readBMP(const char* filename, bfheader* header, biheader* infoheader)
{
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
SDLib::File f = SD.open(filename);
if(!f) return NULL;
f.read((uint8_t *)header, sizeof(bfheader)); // read the 14-byte file header
f.read((uint8_t *)infoheader, sizeof(biheader)); //read the 40-byte info header
#else
FILE* f = fopen(filename, "rb");
if(f == NULL) return NULL;
fread(header, sizeof(bfheader), 1, f);
fread(infoheader, sizeof(biheader), 1, f);
#endif //defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
if(infoheader->biCompression != 0 || infoheader->biBitCount != 24) //if the file is not RGB encoded or if its not 24bit
{
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
f.close();
#else
fclose(f);
#endif // defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
return NULL;
}
int size = (int) infoheader->biSizeImage/sizeof(pixel); //get byte size of pixel array
pixel* data = (pixel*) malloc(sizeof(pixel)*size); // allocate
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA)
f.seek(header->bfOffset); //set file cursor to starting address of RGB array
f.read((uint8_t *)data, sizeof(uint8_t)*size); // read the rest of the data at once
f.close();
#else
fseek(f, header->bfOffset, SEEK_SET);
fread(data, sizeof(pixel), size, f);
fclose(f);
#endif //__arm__
return data;
}
#endif //BMP_HEADER
readbmp_test.ino
#include<arduino.h>
#include <SPI.h>
#include "bmp.hpp"
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
if (!SD.begin(4)) {
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
}
void loop()
{
char debug[160];
char rgbdata[8];
const char* const files[] = {"rot.bmp", "gruen.bmp", "blau.bmp"};
int files_len = 3;
bfheader header;
biheader infoheader;
pixel* image;
for(int i=0; i<files_len; i++)
{
image = readBMP(files[i], &header, &infoheader);
Serial.println("----------------------------");
sprintf(debug, "Fname: %s\n"
"Offset: %08lx\n"
"W * H: %ld * %ld\n"
"Bit# %u\n"
"Cmp: %lu\n"
"ArraySize: %lu\n"
"---------------------------\n",
files[i],
header.bfOffset,
infoheader.biWidth, infoheader.biHeight,
infoheader.biBitCount,
infoheader.biCompression,
infoheader.biSizeImage/sizeof(pixel));
Serial.print(debug);
if(image != NULL)
{
for(unsigned j=0; j<infoheader.biSizeImage/sizeof(pixel); j++)
{
sprintf(rgbdata, "%02x %02x %02x", image[j].R, image[j].G, image[j].B);
Serial.println(rgbdata);
}
}else{
sprintf(debug, "Error while reading BMP file\n");
Serial.print(debug);
}
free(image);
}
}
main.cpp //MingW Windows Test
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_MEGA) //arduino compiler wants to run this main?
#else
#include<stdio.h>
#include "bmp.hpp"
int main(const int argc, const char** argv)
{
if(argc < 2) return -1;
bfheader header;
biheader infoheader;
for(int i=1; i<argc; i++)
{
pixel* image = readBMP(argv[i], &header, &infoheader);
if(image != NULL)
{
printf("Fname: %s\n"
"Offset: %08x\n"
"W * H: %d * %d\n"
"Bit# %d\n"
"Cmp: %d\n"
"ArraySize: %d\n"
"---------------------------\n",
argv[i],
header.bfOffset,
infoheader.biWidth, infoheader.biHeight,
infoheader.biBitCount,
infoheader.biCompression,
infoheader.biSizeImage/sizeof(pixel));
for(int j=0; j<infoheader.biSizeImage/sizeof(pixel); j++)
{
printf("RGB: %02x%02x%02x\n", image[j].R, image[j].G, image[j].B);
}
}else{
printf("Error while reading BMP file\n");
return 1;
}
free(image);
}
return 0;
}
#endif
I'm really stuck on this, I actually rewrote it a couple times already. I would really appreciate some help.
file.read
(return value). try to usereadBytes
instead ofread
if the count of read bytes doesn't match the expected sizeSerial.println(*infoheader, HEX);
results in'HardwareSerial::println(biheader&, int)'
sprintf(rgbdata, "%02x %02x %02x", image[j].R, image[j].G, image[j].B); Serial.println(rgbdata);
displays:c5 00 00
dispite the bitmap not containing C5 at all.