Skip to content

Instantly share code, notes, and snippets.

@abfo
Last active April 17, 2022 23:45
Show Gist options
  • Save abfo/4e8c12f48d65e8d8e6113172c9729428 to your computer and use it in GitHub Desktop.
Save abfo/4e8c12f48d65e8d8e6113172c9729428 to your computer and use it in GitHub Desktop.
#include <Wire.h>
#include <ArduCAM.h>
#include <SPI.h>
#include <SD.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include "memorysaver.h"
//#define USE_SERIAL
const int CS = 10; // chip select pin for camera
const int SD_CS = 7; // chip select pin for SD reader
const int MaxWait = 100; // how long to wait during initialization
ArduCAM myCAM(OV5642, CS);
int filenum = 1;
uint8_t read_fifo_burst(ArduCAM myCAM);
void setup() {
uint8_t temp;
uint8_t vid, pid;
char fnbuf[15];
int w = 0;
// configure minimal power usage
lowPowerSetup();
// internal LED on during setup
digitalWrite(LED_BUILTIN, HIGH);
// start SPI bus
Wire.begin();
// serial for debug messages, comment USE_SERIAL out above for release
#if defined USE_SERIAL
Serial.begin(115200);
while(!Serial) {}
sMessage(F("Serial connected."));
#endif
// set camera chip select pin HIGH
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
// start SPI
SPI.begin();
delay(100);
// reset CPLD
myCAM.write_reg(0x07, 0x80);
delay(100);
myCAM.write_reg(0x07, 0x00);
delay(100);
// wait for ArduCAM SPI
w = 0;
while(w < MaxWait) {
myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
temp = myCAM.read_reg(ARDUCHIP_TEST1);
if (temp != 0x55){
sMessage(F("Waiting for ArduCAM on SPI bus."));
delayWithFlash();
} else {
sMessage(F("Found ArduCAM on SPI bus."));
break;
}
w++;
if (w >= MaxWait) {
sMessage(F("Waited too long, restarting."));
delay(100);
reset();
}
}
// detect ArduCAM
w = 0;
while(w < MaxWait) {
myCAM.wrSensorReg16_8(0xff, 0x01);
myCAM.rdSensorReg16_8(OV5642_CHIPID_HIGH, &vid);
myCAM.rdSensorReg16_8(OV5642_CHIPID_LOW, &pid);
if((vid != 0x56) || (pid != 0x42)){
sMessage(F("Waiting to detect ArduCAM as OV5642."));
delayWithFlash();
} else{
sMessage(F("Detected ArduCAM as OV5642."));
break;
}
w++;
if (w >= MaxWait) {
sMessage(F("Waited too long, restarting."));
delay(100);
reset();
}
}
sMessage(F("Initializing ArduCAM OV5642."));
myCAM.set_format(JPEG);
myCAM.InitCAM();
myCAM.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);
sMessage(F("Size 2592x1944"));
myCAM.OV5642_set_JPEG_size(OV5642_2592x1944);
delay(1000);
sMessage(F("Advanced AWB"));
myCAM.OV5642_set_Light_Mode(Advanced_AWB);
delay(250);
sMessage(F("High Quality Compression"));
myCAM.OV5642_set_Compress_quality(high_quality);
delay(250);
sMessage(F("Saturation +1"));
myCAM.OV5642_set_Color_Saturation(Saturation1);
delay(250);
sMessage(F("Brightness 0"));
myCAM.OV5642_set_Brightness(Brightness0);
delay(250);
sMessage(F("Contrast +1"));
myCAM.OV5642_set_Contrast(Contrast1);
delay(250);
sMessage(F("Hue 0"));
myCAM.OV5642_set_hue(degree_0);
delay(250);
sMessage(F("Special Effects Normal"));
myCAM.OV5642_set_Special_effects(Normal);
delay(250);
sMessage(F("Exposure Default"));
myCAM.OV5642_set_Exposure_level(Exposure_default);
delay(250);
sMessage(F("Sharpness Auto + 1"));
myCAM.OV5642_set_Sharpness(Auto_Sharpness1);
delay(250);
myCAM.flush_fifo();
myCAM.clear_fifo_flag();
myCAM.write_reg(ARDUCHIP_FRAMES,0x00);
//Initialize SD Card
w = 0;
sMessage(F("Waiting for SD Card"));
while(!SD.begin(SD_CS)){
//Serial.println(F("SD Card Error!"));
delayWithFlash();
w++;
if (w >= MaxWait) {
sMessage(F("Waited too long, restarting."));
delay(100);
reset();
}
}
sMessage(F("SD Card Detected, finding first filename"));
// figure out first filename
sprintf(fnbuf, "%08d.jpg", filenum);
while(SD.exists(fnbuf))
{
filenum += 1000;
sprintf(fnbuf, "%08d.jpg", filenum);
}
sMessage(F("Setup complete!"));
digitalWrite(LED_BUILTIN, LOW);
}
void loop() {
char fnbuf[15];
byte buf[256];
static int i = 0;
int sleepCount = 2;
int s = 0;
uint8_t temp = 0,temp_last = 0;
uint32_t length = 0;
bool is_header = false;
File outFile;
// take a photo
sMessage(F("Waking camera and capturing..."));
myCAM.clear_bit(ARDUCHIP_GPIO, GPIO_PWDN_MASK);
delay(800);
//Flush the FIFO
myCAM.flush_fifo();
//Clear the capture done flag
myCAM.clear_fifo_flag();
//Start capture
myCAM.start_capture();
// wait for capture
while(!myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK));
sMessage(F("Capture complete"));
length = myCAM.read_fifo_length();
if (length > 200000)
{
// Large file, short sleep so we capture quickly
sleepCount = 3;
}
else
{
// Small file, assume night time so sleep longer to save power
sleepCount = 200;
}
// Construct a file name
sprintf(fnbuf, "%08d.jpg", filenum);
//Open the new file
outFile = SD.open(fnbuf, O_WRITE | O_CREAT | O_TRUNC);
filenum++;
// copy image data to file
myCAM.CS_LOW();
myCAM.set_fifo_burst();
while ( length-- )
{
temp_last = temp;
temp = SPI.transfer(0x00);
//Read JPEG data from FIFO
if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
{
buf[i++] = temp; //save the last 0XD9
//Write the remain bytes in the buffer
myCAM.CS_HIGH();
outFile.write(buf, i);
//Close the file
outFile.close();
sMessage(F("Image Saved to SD"));
is_header = false;
i = 0;
}
if (is_header == true)
{
//Write image data to buffer if not full
if (i < 256)
buf[i++] = temp;
else
{
//Write 256 bytes image data to file
myCAM.CS_HIGH();
outFile.write(buf, 256);
i = 0;
buf[i++] = temp;
myCAM.CS_LOW();
myCAM.set_fifo_burst();
}
}
else if ((temp == 0xD8) & (temp_last == 0xFF))
{
is_header = true;
buf[i++] = temp_last;
buf[i++] = temp;
}
}
// sleep for a while...
sMessage(F("Going to sleep..."));
myCAM.set_bit(ARDUCHIP_GPIO, GPIO_PWDN_MASK);
delay(1000);
for(s = 0; s < sleepCount; s++)
{
// seems to be around 9 seconds per sleep
goToSleep();
}
}
void sMessage(const __FlashStringHelper* m) {
#if defined USE_SERIAL
Serial.println(m);
#endif
}
void delayWithFlash() {
digitalWrite(LED_BUILTIN, LOW);
delay(500);
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
}
void lowPowerSetup () {
// working from https://forum.arduino.cc/t/power-saving-techniques-on-a-stock-mega-2560/425563
pinMode(A0, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
pinMode(A3, OUTPUT);
pinMode(A4, OUTPUT);
pinMode(A5, OUTPUT);
pinMode(A6, OUTPUT);
pinMode(A7, OUTPUT);
pinMode(A8, OUTPUT);
pinMode(A9, OUTPUT);
pinMode(A10, OUTPUT);
pinMode(A11, OUTPUT);
pinMode(A12, OUTPUT);
pinMode(A13, OUTPUT);
pinMode(A14, OUTPUT);
pinMode(A15, OUTPUT);
digitalWrite(A0, LOW);
digitalWrite(A1, LOW);
digitalWrite(A2, LOW);
digitalWrite(A3, LOW);
digitalWrite(A4, LOW);
digitalWrite(A5, LOW);
digitalWrite(A6, LOW);
digitalWrite(A7, LOW);
digitalWrite(A8, LOW);
digitalWrite(A9, LOW);
digitalWrite(A10, LOW);
digitalWrite(A11, LOW);
digitalWrite(A12, LOW);
digitalWrite(A13, LOW);
digitalWrite(A14, LOW);
digitalWrite(A15, LOW);
for (int i = 0; i <= 53; i++) {
if (i == CS) { continue; }
if (i == SD_CS) { continue; }
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
power_adc_disable();
//power_spi_disable();
//power_usart0_disable();
power_usart2_disable();
power_timer1_disable();
power_timer2_disable();
power_timer3_disable();
power_timer4_disable();
power_timer5_disable();
//power_twi_disable();
}
void goToSleep() {
/*** Setup the Watch Dog Timer ***/
/* Clear the reset flag. */
MCUSR &= ~(1<<WDRF);
/* set new watchdog timeout prescaler value */
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = 1<<WDP0 | 0<<WDP1 | 0<<WDP2 | 1<<WDP3;
WDTCSR |= _BV(WDIE);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
sleep_disable();
}
ISR(WDT_vect) {
// Dummy watchdog timer handler to prevent reset
}
void reset() { asm volatile ("jmp 0"); }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment