Difficulties saving 24Bit BMP from ArduCam to SD card

Hi,
I used the code provided by Svarun123 to save a bitmap from ArduCam to the SD card of a Adafruit 96x64 RGB OLED (SSD1331). This works well when writing a 16Bit bitmap to the SD card. However, I want to load the bitmap to the OLED and therefore, I need a 24Bit bimap. I tried to convert the bitmap format during writing to SD card and the captured bitmap can be identified but the color codes are incorrect.

Here is the initialization, the BMP header and the setup:

#include <Wire.h>
#include <ArduCAM.h>
#include <SPI.h>
#include "memorysaver.h"

#include <a_Adafruit_GFX.h>
#include <a_Adafruit_SSD1331.h>
#include <SD.h>
//#include <SPI.h>
#define sclk 13
#define mosi 11
#define oled_cs   5//10 - OLEDCS
#define rst  9
#define dc   8
#define SD_CS 4

// Color definitions
#define BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0  
#define WHITE           0xFFFF

int FilesCreated;

// to draw images from the SD card, we will share the hardware SPI interface
Adafruit_SSD1331 tft = Adafruit_SSD1331(oled_cs, dc, rst);  

// the file itself
File bmpFile;

// information we extract about the bitmap file
int bmpWidth, bmpHeight;
uint8_t bmpDepth, bmpImageoffset;



//This demo can only work on OV2640_MINI_2MP platform.
#if !(defined OV2640_MINI_2MP)
  #error Please select the hardware platform and camera module in the ../libraries/ArduCAM/memorysaver.h file
#endif
#define BMPIMAGE160 54 //320x240
const char bmp_header_160[BMPIMAGE160] PROGMEM =
{
  0x42, 0x4D, 0x36, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 
  0x28, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x01, 0x00, 
  0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// set pin 7 as the slave select for the digital pot:
const int CS = 7;
bool is_header = false;
int mode = 0;
uint8_t start_capture = 0;
  ArduCAM myCAM( OV2640, CS );
uint8_t read_fifo_burst(ArduCAM myCAM);

void setup() {
  Serial.begin(500000);//9600
   
  pinMode(oled_cs, OUTPUT);
  digitalWrite(oled_cs, HIGH);
     
  // initialize the OLED
  tft.begin();
  Serial.println("init");
  tft.fillScreen(BLUE);
  delay(500);
  Serial.print("Initializing SD card...");
  if (!SD.begin(SD_CS)) {
    Serial.println("failed!");
    return;
  }
  Serial.println("SD OK!");


  // put your setup code here, to run once:
  uint8_t vid, pid;
  uint8_t temp;
  Wire.begin();
  Serial.begin(500000);//921600
  Serial.println(F("ACK CMD ArduCAM Start! END"));
  Serial.println("ArduCAM Start!");
  // set the CS as an output:
  pinMode(CS, OUTPUT);            //Konfiguration Pin 7
  digitalWrite(CS, HIGH);         //Pin 7 auf high setzen
  // initialize SPI:
  SPI.begin();
    //Reset the CPLD
  myCAM.write_reg(0x07, 0x80);
  delay(100);
  myCAM.write_reg(0x07, 0x00);
  delay(100);
  while(1){
    //Check if the ArduCAM SPI bus is OK
    myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
    temp = myCAM.read_reg(ARDUCHIP_TEST1);
    if (temp != 0x55){
      Serial.println(F("ACK CMD SPI interface Error! END"));
      Serial.println("SPI interface Error!");
      delay(1000);continue;
    }else{
      Serial.println(F("ACK CMD SPI interface OK. END"));break;
      Serial.println("SPI interface OK.");break;
    }
  }
  
  #if defined (OV2640_MINI_2MP)
    while(1){
      //Check if the camera module type is OV2640
      myCAM.wrSensorReg8_8(0xff, 0x01);
      myCAM.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
      myCAM.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
      if ((vid != 0x26 ) && (( pid != 0x41 ) || ( pid != 0x42 ))){
        Serial.println(F("ACK CMD Can't find OV2640 module! END"));
        Serial.println("Can't find OV2640 module!");
        delay(1000);continue;
      }
      else{
        Serial.println(F("ACK CMD OV2640 detected. END"));break;
        Serial.println("OV2640 detected.");
      } 
    }
  #endif
  //Change to JPEG capture mode and initialize the OV5642 module
//  myCAM.set_format(JPEG);
//  myCAM.InitCAM();
    myCAM.OV2640_set_JPEG_size(OV2640_160x120);
//    Serial.println(F("ACK CMD switch to OV2640_160x120 END"));
    Serial.println("switch to OV2640_160x120");
//    myCAM.OV2640_set_Light_Mode(Auto);temp = 0xff;
//    Serial.println(F("ACK CMD Set to Auto END"));
    temp = 0xff;
    //Format auf Bitmap
    myCAM.set_format(BMP);
    myCAM.InitCAM();
    myCAM.wrSensorReg16_8(0x3818, 0x81);
    myCAM.wrSensorReg16_8(0x3621, 0xA7);
    
  delay(1000);
  myCAM.clear_fifo_flag();
}

void loop() {
    // put your main code here, to run repeatedly:
    uint8_t temp = 0xff, temp_last = 0;
    bool is_header = false;

    myCAM.flush_fifo();
    myCAM.clear_fifo_flag();
    //Start capture
    myCAM.start_capture();
    while (!myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK));
//    captureTime = millis() - currTime;
    delay(50);
//    Serial.println(F("ACK CMD Image capture finished! END")); //TODO: Timing
//    Serial.println(F("ACK CMD Starting image processing... END"));
//    Serial.println("Starting image processing...");
//    currTime = millis();
    //ReadJPEGBurst();
    ReadBMPBurst();
//    processingTime = millis() - currTime;
//    Serial.println(F("ACK CMD Image processing finished! END")); //TODO: Timing
//    Serial.println("Image processing finished!");
    myCAM.clear_fifo_flag();
    delay(2000);
}

Here is the code for writing the bitmap to the SD card

void ReadBMPBurst()
{
//  Serial.println(F("ACK CMD Reading BMP burst... END"));
  Serial.println("Reading BMP burst... ");
  uint32_t length = myCAM.read_fifo_length();
  if (length >= MAX_FIFO_SIZE)
  {
    Serial.println(F("ACK CMD ERROR: Over size! END"));
    myCAM.clear_fifo_flag();
    return;
  }
  if (length == 0)
  {
    Serial.println(F("ACK CMD ERROR: Size is 0! END"));
    myCAM.clear_fifo_flag();
    return;
  }

  myCAM.CS_LOW();
  myCAM.set_fifo_burst();
  File outputFile;

  if (SD_CS >= 0)
  {
    myCAM.CS_HIGH();
    outputFile = SD.open("CAM_" + String(FilesCreated++) + ".bmp", O_WRITE | O_CREAT | O_TRUNC);
    myCAM.CS_LOW();
    myCAM.set_fifo_burst();
    if (!outputFile)
    {
      Serial.println(F("ACK CMD ERROR: Unable to create BMP on SD! END"));
      myCAM.clear_fifo_flag();
      return;
    }
  }

  Serial.println(SD_CS);
  if (SD_CS >= 0)
  {
    byte send1 = 0xFF;
    byte send2 = 0xAA;
    myCAM.CS_HIGH();
//    outputFile.write(&send1, 1);
//    outputFile.write(&send2, 1);
    myCAM.CS_LOW();
    myCAM.set_fifo_burst();
  }
  else
  {
    Serial.write(0xFF);
    Serial.write(0xAA);
  }

/*  Serial.print("BMPIMAGEOFFSET: ");Serial.println(BMPIMAGEOFFSET);
  for (int i = 0; i < BMPIMAGEOFFSET; i++)
  {
    if (SD_CS >= 0)
    {
      byte send = pgm_read_byte(&bmp_header[i]);
      Serial.print("bmpHeader");Serial.print(i);Serial.print(": ");Serial.println(send);
      myCAM.CS_HIGH();
      outputFile.write(&send, 1);
      myCAM.CS_LOW();
      myCAM.set_fifo_burst();
    }
    else
    {
      Serial.write(pgm_read_byte(&bmp_header[i]));
    }
  }*/

  Serial.print("BMPIMAGE160: ");Serial.println(BMPIMAGE160);
  for (int i = 0; i < BMPIMAGE160; i++)
  {
    if (SD_CS >= 0)
    {
      byte send = pgm_read_byte(&bmp_header_160[i]);
//      Serial.print("bmpHeader");Serial.print(i);Serial.print(": ");Serial.println(send);
      myCAM.CS_HIGH();
      outputFile.write(&send, 1);
      myCAM.CS_LOW();
      myCAM.set_fifo_burst();
    }
    else
    {
      Serial.write(pgm_read_byte(&bmp_header_160[i]));
    }
  }
  //SPI.transfer(0x00);

  for (int i = 0; i < 320; i++)
  {
    for (int j = 0; j < 240; j++)
    {
      char VH = SPI.transfer(0x00);
      char VL = SPI.transfer(0x00);
      //R: 5bit, G: 6bit, B: 5bit
//      byte fR = (VH >> 0x0C) & 0xFFFFF ;  
//      byte fG = ((VH & 0x00000FFF) << 0x0C) | ((VL >> 0x14) & 0xFFF);
//      byte fB = VL & 0xFFFFF;
      //R: 5bit, G: 5bit, B: 5bit
      byte fR = (VH >> 0x08) & 0xFFFFF ;  
      byte fG = ((VH & 0x000000FF) << 0x0C) | ((VL >> 0x14) & 0xFFF);
      byte fB = VL & 0xFFFFF;
      
      if (SD_CS >= 0)
      {
        myCAM.CS_HIGH();
        outputFile.write(&fR, 1);
        delayMicroseconds(12);
        outputFile.write(&fG, 1);
        delayMicroseconds(12);
        outputFile.write(&fB, 1);
        delayMicroseconds(12);
/*        outputFile.write(&VL, 1);
        delayMicroseconds(12);
        outputFile.write(&VH, 1);
        delayMicroseconds(12);*/
        myCAM.CS_LOW();
        myCAM.set_fifo_burst();
      }
    }
  }

  if (SD_CS >= 0)
  {
    byte send1 = 0xBB;
    byte send2 = 0xCC;
    myCAM.CS_HIGH();
    outputFile.write(&send1, 1);
    outputFile.write(&send2, 1);
    myCAM.CS_LOW();
    myCAM.set_fifo_burst();
  }
  else
  {
    Serial.write(0xBB);
    Serial.write(0xCC);
  }

  myCAM.CS_HIGH();
  if (SD_CS >= 0)
    outputFile.close();
//  Serial.println(F("ACK CMD BMP burst read! END"));
  Serial.println("BMP burst read!");
  return;
}

This is what the monitor logged:

21:08:40.114 -> ACK CMD ArduCAM Start! END
21:08:40.114 -> ArduCAM Start!
21:08:40.314 -> ACK CMD SPI interface OK. END
21:08:40.314 -> ACK CMD OV2640 detected. END
21:08:40.354 -> switch to OV2640_160x120
21:08:41.834 -> Reading BMP burst... 
21:08:41.874 -> 4
21:08:41.874 -> BMPIMAGE160: 54
21:08:56.474 -> BMP burst read!
21:08:58.594 -> Reading BMP burst... 
21:08:58.634 -> 4
21:08:58.634 -> BMPIMAGE160: 54
21:09:13.234 -> BMP burst read!
21:09:15.394 -> Reading BMP burst... 
21:09:15.434 -> 4
21:09:15.434 -> BMPIMAGE160: 54
21:09:29.994 -> BMP burst read!

This is what I get:
CAM.zip (113,1 KB)

The issue is probably the conversion of VH and VL to fR, fG and fB. But I can't find the reason.

Kind regards
Günter

There are a variety of different BMP header options. Make sure you have the correct header data.

Hi all
I solved the problem
Now the ArduCam bitmap is saced to the SD card of the 96x64 OLED display in 24bit format.

void ReadBMPBurst()
{
  Serial.println(F("ACK CMD Reading BMP burst... END"));
  Serial.println("Reading BMP burst... ");
  uint32_t length = myCAM.read_fifo_length();
  if (length >= MAX_FIFO_SIZE)
  {
    Serial.println(F("ACK CMD ERROR: Over size! END"));
    myCAM.clear_fifo_flag();
    return;
  }
  if (length == 0)
  {
    Serial.println(F("ACK CMD ERROR: Size is 0! END"));
    myCAM.clear_fifo_flag();
    return;
  }

  myCAM.CS_LOW();
  myCAM.set_fifo_burst();
  File outputFile;

  if (SD_CS >= 0)
  {
    myCAM.CS_HIGH();
    outputFile = SD.open("CAM_" + String(FilesCreated++) + ".bmp", O_WRITE | O_CREAT | O_TRUNC);
    myCAM.CS_LOW();
    myCAM.set_fifo_burst();
    if (!outputFile)
    {
      Serial.println(F("ACK CMD ERROR: Unable to create BMP on SD! END"));
      myCAM.clear_fifo_flag();
      return;
    }
  }

  Serial.println(SD_CS);
  if (SD_CS >= 0)
  {
    byte send1 = 0xFF;
    byte send2 = 0xAA;
    myCAM.CS_HIGH();
//    outputFile.write(&send1, 1);
//    outputFile.write(&send2, 1);
    myCAM.CS_LOW();
    myCAM.set_fifo_burst();
  }
  else
  {
    Serial.write(0xFF);
    Serial.write(0xAA);
  }


//  Serial.print("BMPIMAGE160: ");Serial.println(BMPIMAGE160);
  for (int i = 0; i < BMPIMAGE160; i++)
  {
    if (SD_CS >= 0)
    {
      byte send = pgm_read_byte(&bmp_header_160[i]);
//      Serial.print("bmpHeader");Serial.print(i);Serial.print(": ");Serial.println(send);
      myCAM.CS_HIGH();
      outputFile.write(&send, 1);
      myCAM.CS_LOW();
      myCAM.set_fifo_burst();
    }
    else
    {
      Serial.write(pgm_read_byte(&bmp_header_160[i]));
    }
  }
  //SPI.transfer(0x00);

  for (int i = 0; i < 320; i++)
  {
    for (int j = 0; j < 240; j++)
    {
      char VH = SPI.transfer(0x00);
      char VL = SPI.transfer(0x00);
      //R: 5bit, G: 6bit, B: 5bit --> R:8bit, G:8bit, B:8bit
      byte B5 = VL & 0x1F;
      byte G6 = (((VL & 0xE0) >> 5) | ((VH & 0x07) << 3)) & 0x3F;
      byte R5 = (VH >> 3) & 0x1F;
      byte R8 = (R5 * 527 + 23) >>6;
      byte G8 = (G6 * 259 + 33) >>6;
      byte B8 = (B5 * 527 + 23) >>6;
      
      if (SD_CS >= 0)
      {
        myCAM.CS_HIGH();
        outputFile.write(&B8, 1);
        delayMicroseconds(12);
        outputFile.write(&G8, 1);
        delayMicroseconds(12);
        outputFile.write(&R8, 1);
        delayMicroseconds(12);
        myCAM.CS_LOW();
        myCAM.set_fifo_burst();
      }
    }
  }

  if (SD_CS >= 0)
  {
    byte send1 = 0xBB;
    byte send2 = 0xCC;
    myCAM.CS_HIGH();
    outputFile.write(&send1, 1);
    outputFile.write(&send2, 1);
    myCAM.CS_LOW();
    myCAM.set_fifo_burst();
  }
  else
  {
    Serial.write(0xBB);
    Serial.write(0xCC);
  }

  myCAM.CS_HIGH();
  if (SD_CS >= 0)
    outputFile.close();
  Serial.println(F("ACK CMD BMP burst read! END"));
  Serial.println("BMP burst read!");
  return;
}

Kind regards

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