[Solucionado] Guardar imágenes tomadas por una cámara OV7670 en una memoria SD.

Hola a todos. Estoy en un problema. Tengo el código para tomar las fotografías con una cámara OV7670. Estoy usando un Arduino Nano.

Una parte del código que he modificado se muestra como sigue (lo siguiente es la original):

void setup(){
  Serial.begin(9600);
  arduinoUnoInut();
  camInit();
  setRes();
  setColor();
  wrReg(0x11, 11); //if your images are unclear, change this(0x11, 11); to (0x11, 9); / (0x11,10); / (0x11, 12); / (0x11, 13);
}

void loop(){
  captureImg(320, 240);
}

Lo siguiente son las modificaciones que hice (pero no me sale la compilación aún):

void setup(){
  arduinoUnoInut();
  camInit();
  setRes();
  setColor();
  wrReg(0x11, 11); //if your images are unclear, change this(0x11, 11); to (0x11, 9); / (0x11,10); / (0x11, 12); / (0x11, 13);
  Serial.begin(9600);

  Serial.print("Iniciando SD ...");                                  // Inicializando el micro SD
  if(!SD.begin(4)) {                                                 // Se asigna al pin D9 la transferencia de información del lector de SD
    Serial.println("No se pudo inicializar"); 
    return;
  }
  Serial.println("Inicialización exitosa");
}
  
File imgFile;

void loop(){
  imgFile = SD.open("img", FILE_WRITE);
  if(imgFile) {
    imgFile.write(captureImg(320, 240));
    imgFile.close();
  }
  else {
    Serial.println("Error al abrir el archivo");
    delay(1000);
  }
  delay(1000);
}

La línea que toma las capturas de las imágenes (fotografías) es

captureImg(320, 240);

Mi idea es que esas imágenes se "escriban", es decir, se guarden en un archivo de memoria SD. El problema es que no sé cómo. ¿Alguien me podría ayudar por favor? Gracias por su atención.

El archivo que tiene el código completo original se muestra abajo.

OV7670_Arduino.ino (23.7 KB)

En este enlace se explica un tutorial de cómo hacer el código para poner guardar en una memoria SD las imágenes capturadas por la cámara OV7670. El problema es que no pone el código completo, sino que los parte en diferentes secciones. Además, hay una parte que no entiendo: en la sección "Writing A Bitmap Image File" aparece un script de Python, pero no sé donde escribirlo y donde guardarlo. ¿Me podrían ayudar con este problema por favor?

El script de Python es para tomar una imagen y guardarla en la SD.

Writing A Bitmap Image File

y no leiste bien todo el tutorial pero al final dice

Results
Here are some of the images i obtained. Remember to capture a couple of image to get one fine output. The first one or two images are going to be whitewashed as the camera tries to adjust its exposure, gain etc. The source code is available here.

Y en here tenés el código completo que repito por si no entendiste a este sitio de Github

Sí. Ya vi que cometí un error el no fijarme en el último párrafo. Ya tengo el código completo, el problema es que cuando lo ejecuto no me aparece una invocación al método "ReadOV7670()". Adjunto el código completo:

/*
OV7670 Camera module with SD card module
on Arduino Uno.
 
Author: Hardik Kalasua (hardik.kalasua@gmail.com)
NOTE: Much of the code is based on insights from the
work of the user named ComputerNerd.
NOTE: OV7670 SCCB interface does't work if XCLK not provided.
** Thanks to all the android forums and stack exchange answers. :)
*/


#include <Wire.h>
#include <SD.h>
int CS_Pin = 10;

void Init_YUV422(){
  WriteOV7670(0x12, 0x00);//COM7
  WriteOV7670(0x8C, 0x00);//RGB444
  WriteOV7670(0x04, 0x00);//COM1
  WriteOV7670(0x40, 0xC0);//COM15
  WriteOV7670(0x14, 0x1A);//COM9
  WriteOV7670(0x3D, 0x40);//COM13
  }

void Init_QVGA(){
  WriteOV7670(0x0C, 0x04);//COM3 - Enable Scaling
  WriteOV7670(0x3E, 0x19);//COM14
  WriteOV7670(0x72, 0x11);//
  WriteOV7670(0x73, 0xF1);//
  WriteOV7670(0x17, 0x16);//HSTART
  WriteOV7670(0x18, 0x04);//HSTOP
  WriteOV7670(0x32, 0xA4);//HREF
  WriteOV7670(0x19, 0x02);//VSTART
  WriteOV7670(0x1A, 0x7A);//VSTOP
  WriteOV7670(0x03, 0x0A);//VREF
  }

void Init_OV7670(){
  //Reset All Register Values
  WriteOV7670(0x12,0x80);
  delay(100);
  WriteOV7670(0x3A, 0x04); //TSLB
 
  WriteOV7670(0x13, 0xC0); //COM8
  WriteOV7670(0x00, 0x00); //GAIN
  WriteOV7670(0x10, 0x00); //AECH
  WriteOV7670(0x0D, 0x40); //COM4
  WriteOV7670(0x14, 0x18); //COM9
  WriteOV7670(0x24, 0x95); //AEW
  WriteOV7670(0x25, 0x33); //AEB
  WriteOV7670(0x13, 0xC5); //COM8
  WriteOV7670(0x6A, 0x40); //GGAIN
  WriteOV7670(0x01, 0x40); //BLUE
  WriteOV7670(0x02, 0x60); //RED
  WriteOV7670(0x13, 0xC7); //COM8
  WriteOV7670(0x41, 0x08); //COM16
  WriteOV7670(0x15, 0x20); //COM10 - PCLK does not toggle on HBLANK
  }

void WriteOV7670(byte regID, byte regVal){
  // Slave 7-bit address is 0x21.
  // R/W bit set automatically by Wire functions
  // dont write 0x42 or 0x43 for slave address
  Wire.beginTransmission(0x21);
  // Reset all register values
  Wire.write(regID);  
  Wire.write(regVal);
  Wire.endTransmission();
  delay(1);
  
  }

void ReadOV7670(byte regID){
  // Reading from a register is done in two steps
  // Step 1: Write register address to the slave 
  // from which data is to be read. 
  Wire.beginTransmission(0x21); // 7-bit Slave address
  Wire.write(regID);  // reading from register 0x11
  Wire.endTransmission();

  // Step 2: Read 1 byte from Slave
  Wire.requestFrom(0x21, 1);
  Serial.print("Read request Status:");
  Serial.println(Wire.available());
  Serial.print(regID,HEX);
  Serial.print(":");
  Serial.println(Wire.read(),HEX);
  }

void XCLK_SETUP(void){
  pinMode(9, OUTPUT); //Set pin 9 to output
  
  //Initialize timer 1
  
  //WGM13, WGM12, WGM11 & WGM10 bits SET- Fast PWM mode
  //COM1A0 SET- Toggle OC1A on compare match
  TCCR1A = (1 << COM1A0) | (1 << WGM11) | (1 << WGM10);
  //SET CS10 bit for clock select with no prescaling
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); 
  //Output Compare Register 1A(OCR1A) = 0
  //This will lead to a match on every clock cycle
  //Toggle OC1A output pin on every match instance
  //Therefore, the generated waveform will have half
  //the frequency of the driving clock i.e. 8Mhz
  //OC1A pin- PB1 (alternate functn) pin i.e. Arduino pin 9
  OCR1A = 0;

}

// Two Wire Interface Setup
// Sets the frequency of the SCL line
// Default is 100KHz so we won't use this function
void TWI_SETUP(void){
  //Set prescaler bits in TWI Status Register (TWSR) to 0
  TWSR &= ~3;
  //Set SCL frequency to 100KHz
  //SCLfreq = CPUfreq/(16 + 2(TWBR) - 4^(TWPS))
  //TWBR = 72, TWPS(prescaler) = 0
  TWBR = 72;

}

void OV7670_PINS(void){
  //Setup Data input pins and Interrupt pins
  //DDRC bits 3:0 = 0 =>  bits configured as Data Inputs
  //DDRC 3:0 - A3,A2,A1,A0
  DDRC &= ~15;//low d0-d3 camera
  
  //~(0b11111100) = 0b00000011
  //make DDRD 7:2 = 0 => Inputs
  //d7-d4 as data inputs, d3-INT1 is VSYNC and d2-INT0 is PCLK
  DDRD &= ~252;
}



void QVGA_Image(String title){
  int h,w;
  
  File dataFile = SD.open(title, FILE_WRITE);
  while (!(PIND & 8));//wait for high
  while ((PIND & 8));//wait for low

    h = 240;
  while (h--){
        w = 320;
       byte dataBuffer[320];
    while (w--){
      while ((PIND & 4));   //wait for low
        dataBuffer[319-w] = (PINC & 15) | (PIND & 240);
      while (!(PIND & 4));  //wait for high
      while ((PIND & 4));   //wait for low
      while (!(PIND & 4));  //wait for high
    }
    dataFile.write(dataBuffer,320);

    
  }

    dataFile.close();
    delay(100);
}

void setup(){
 
  noInterrupts(); //Disable all interrupts
  XCLK_SETUP();   //Setup 8MHz clock at pin 11
  OV7670_PINS();  // Setup Data-in and interrupt pins from camera
  delay(1000);
  TWI_SETUP();    // Setup SCL for 100KHz
  interrupts();
  Wire.begin(); 
  
  Init_OV7670();
  Init_QVGA();
  Init_YUV422();
  WriteOV7670(0x11, 0x1F); //Range 00-1F
  noInterrupts();
  Serial.begin(9600);
  pinMode(CS_Pin, OUTPUT);
  SD.begin(CS_Pin);
 
}


void loop(){
  QVGA_Image("0.bmp");
  QVGA_Image("1.bmp");
  QVGA_Image("2.bmp");
  QVGA_Image("3.bmp");
  QVGA_Image("4.bmp");
  QVGA_Image("5.bmp");
  QVGA_Image("6.bmp");
  QVGA_Image("7.bmp");
  QVGA_Image("8.bmp");
  while(1);
}

La parte del código que tiene el método "ReadOV7670()" es la siguiente:

void ReadOV7670(byte regID){
  // Reading from a register is done in two steps
  // Step 1: Write register address to the slave 
  // from which data is to be read. 
  Wire.beginTransmission(0x21); // 7-bit Slave address
  Wire.write(regID);  // reading from register 0x11
  Wire.endTransmission();

  // Step 2: Read 1 byte from Slave
  Wire.requestFrom(0x21, 1);
  Serial.print("Read request Status:");
  Serial.println(Wire.available());
  Serial.print(regID,HEX);
  Serial.print(":");
  Serial.println(Wire.read(),HEX);
  }

El detalle es que no veo una instrucción que invoque a este método en ningún lado, por lo que me doy a entender que daría igual resultado al programa con o sin este método. ¿Alguien me puede ayudar a explicar si el código está bien hecho y me falta algo, o el código del tutorial está incompleto? Recuerdo que el tutorial es éste.

Para empezar tu quieres leer desde la SD no desde el OV7670, asi que eso debe servir para leer registros de la placa, por eso no lo usa.

Muchas gracias por las opiniones surbyte. En efecto, no lo usa. El código es correcto. El problema que tenía era que debí revisar el hardware y la manera en que hacía la conversión. Voy a explicar a continuación como resolví mi problema por si alguien más lo necesita.

El código es correcto, se debe cargar al Arduino. La imagen para conectar los pines es ésta. Hay que conectar correctamente los pines tal y como los muestra este esquema.

Después de ejecutar el programa de Arduino, se escribirán en la memoria SD las imágenes "0.bmp", "1.bmp", "2.bmp", y así sucesivamente. Cada una de éstas imágenes hay que abrirlas con un editor hexadecimal. En mi caso, yo usé el programa "HxD Hex Editor" que viene gratuito en internet. Después, van a aparecer todos los números hexadecimales que componen la imagen capturada por la cámara. Hay que copiar todo en una archivo de bloc de notas recién creado. Después, este archivo de texto, lo cambiamos con la extensión ".csv" para que se ponga en este formato que se mostrará en Excel.
Ya teniendo este archivo, se debe crear en otro archivo de bloc de notas el siguiente código en Python:

import csv
import sys
import binascii
csv.field_size_limit(500*1024*1024)

columnvector = []
with open('data.csv', 'r') as csvfile:
 csvreader = csv.reader(csvfile,delimiter=' ', quotechar='|')
 for row in csvreader:
 columnvector.append(row)


headers = ['42','4D','36','84','03','00','00','00','00','00','36','00','00','00','28','00','00','00',
'40','01','00','00','F0','00','00','00','01','00','18','00','00','00','00','00','00','84','03','00','C5','00',
'00','00','C5','00','00','00','00','00','00','00','00','00','00','00']

hexArray=[]
for i in range(0,76800):
 data = columnvector[0][i]
 hexArray.extend([data,data,data])



with open('test3.bmp', 'wb') as f:
    f.write(binascii.unhexlify(''.join(headers)))
    f.write(binascii.unhexlify(''.join(hexArray)))

Teniendo este código de Python lo guardamos con la extensión ".py". Teniendo ésto, se procederá a ejecutarlo. En mi caso, yo instalé el IDLE Python, por lo que di click derecho en este archivo con el código con la extensión ".py", y después seleccioné la parte que dice: "Edit with IDLE" y le di click.

Después me aparece el código en un entorno de desarrollo. En la pestaña de arriba aparece la sección "Run", se selecciona éste, y después se selecciona "Run Module". Si marca un error, por favor visiten los comentarios de este tutorial. Entonces, al ejecutarse, lo que hay que hacer es abrir nuevamente el archivo que abrieron al principio con el editor hexadecimal. En ese archivo aparecerá la imagen capturada por la cámara.

La imagen que me salió es ésta, eso sí, está en blanco y negro por la configuración comentada en dicho tutorial:

test3.jpg

Evidentemente, me salió con la extensión ".bmp", pero debido a que no me permite este foro subir imágenes con esta extensión, lo convertí a la extensión ".jpg".

test.jpg

test3.jpg

Bueno me alegro que lo hayas resuelto pero mas valoro tu explicación final para quien quiera seguir tus pasos.