Skip to content
Snippets Groups Projects
main.cpp 10.3 KiB
Newer Older
/*
Snippet content:
Takes pictures with ESP32-CAM, saves images to MicroSD Card.
Measures air temperature and relative humidity from SHT85.

We use the ESP-CAM for the SHT85 readout because of the
limited wire length that can be used for I2C sensors - the
camera sits at the top of the mast next to the SHT85 sensor.

ESP-CAM code partially copied from
https://dronebotworkshop.com/esp32-cam-microsd/

NOTE: To flash the ESP-CAM, pin IO0 has to be connected to GND!
TODO: because of this limitation, it would be nice to implement
over-the-air updates for the camera at one point.
*/

// Include Required Libraries
// I2C: SHT85, RTC
#include "ESPNow.hpp"
#include "NoDataAvailableException.hpp"
#include "RTClib.h" // adafruit/RTClib @^2.1.1
#include "Sht85.hpp"
#include "ram_caching.hpp"
#include <Arduino.h>
#include <Wire.h>

// Camera libraries
#include "driver/rtc_io.h"
#include "esp_camera.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/soc.h"

// MicroSD Libraries
#include "FS.h"
#include "SD_MMC.h"

// Pin definitions for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22

// Pin definitions for I2C (SHT85, RTC)
//  This is different from the pins on the ESP32-C3-DevKit boards!
Marie Schroeder's avatar
Marie Schroeder committed
#define SDA 13
#define SCL 12
// Deep Sleep Settings 
#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  10        /* Time ESP32 will go to sleep (in seconds) */


// Counting files on sd card for saving in the name of each image
int fileCountOnSD = 0; // for counting files

// string for saving the time
char time_string[20];

// number of images to take: the last one is saved
//   this is done because the first few images have a green tint otherwise
int img_number = 3;

RTC_DS3231 rtc;                // I2C address: 0x68
camera_config_t camera_config; // camera configuration parameters

void camera_configESPCamera()
{
	// Configure Camera parameters

	camera_config.ledc_channel = LEDC_CHANNEL_0;
	camera_config.ledc_timer = LEDC_TIMER_0;
	camera_config.pin_d0 = Y2_GPIO_NUM;
	camera_config.pin_d1 = Y3_GPIO_NUM;
	camera_config.pin_d2 = Y4_GPIO_NUM;
	camera_config.pin_d3 = Y5_GPIO_NUM;
	camera_config.pin_d4 = Y6_GPIO_NUM;
	camera_config.pin_d5 = Y7_GPIO_NUM;
	camera_config.pin_d6 = Y8_GPIO_NUM;
	camera_config.pin_d7 = Y9_GPIO_NUM;
	camera_config.pin_xclk = XCLK_GPIO_NUM;
	camera_config.pin_pclk = PCLK_GPIO_NUM;
	camera_config.pin_vsync = VSYNC_GPIO_NUM;
	camera_config.pin_href = HREF_GPIO_NUM;
	camera_config.pin_sccb_sda = SIOD_GPIO_NUM;
	camera_config.pin_sccb_scl = SIOC_GPIO_NUM;
	camera_config.pin_pwdn = PWDN_GPIO_NUM;
	camera_config.pin_reset = RESET_GPIO_NUM;
	camera_config.xclk_freq_hz = 20000000;
	camera_config.pixel_format = PIXFORMAT_JPEG; // Choices are YUV422, GRAYSCALE, RGB565, JPEG

	// Select lower framesize if the camera doesn't support PSRAM
	if (psramFound()) {
		camera_config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
		camera_config.jpeg_quality = 10;           // 10-63 lower number means higher quality
		camera_config.fb_count = 2;
	} else {
		camera_config.frame_size = FRAMESIZE_SVGA;
		camera_config.jpeg_quality = 12;
		camera_config.fb_count = 1;
	}

	// Initialize the Camera
	esp_err_t err = esp_camera_init(&camera_config);

	if (err != ESP_OK) {
		Serial.printf("Camera init failed with error 0x%x", err);
		return;
	}

	// Camera quality adjustments
	sensor_t *s = esp_camera_sensor_get();

	// TODO: this is copied from an online tutorial, it's possible that we don't need this at all!
	// BRIGHTNESS (-2 to 2)
	s->set_brightness(s, 0);
	// CONTRAST (-2 to 2)
	s->set_contrast(s, 0);
	// SATURATION (-2 to 2)
	s->set_saturation(s, 0);
	// SPECIAL EFFECTS (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 -
	// Sepia)
	s->set_special_effect(s, 0);
	// WHITE BALANCE (0 = Disable , 1 = Enable)
	s->set_whitebal(s, 1);
	// AWB GAIN (0 = Disable , 1 = Enable)
	s->set_awb_gain(s, 1);
	// WB MODES (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
	s->set_wb_mode(s, 0);
	// EXPOSURE CONTROLS (0 = Disable , 1 = Enable)
	s->set_exposure_ctrl(s, 1);
	// AEC2 (0 = Disable , 1 = Enable)
	s->set_aec2(s, 0);
	// AE LEVELS (-2 to 2)
	s->set_ae_level(s, 0);
	// AEC VALUES (0 to 1200)
	s->set_aec_value(s, 300);
	// GAIN CONTROLS (0 = Disable , 1 = Enable)
	s->set_gain_ctrl(s, 1);
	// AGC GAIN (0 to 30)
	s->set_agc_gain(s, 0);
	// GAIN CEILING (0 to 6)
	s->set_gainceiling(s, (gainceiling_t)0);
	// BPC (0 = Disable , 1 = Enable)
	s->set_bpc(s, 0);
	// WPC (0 = Disable , 1 = Enable)
	s->set_wpc(s, 1);
	// RAW GMA (0 = Disable , 1 = Enable)
	s->set_raw_gma(s, 1);
	// LENC (0 = Disable , 1 = Enable)
	s->set_lenc(s, 1);
	// HORIZ MIRROR (0 = Disable , 1 = Enable)
	s->set_hmirror(s, 0);
	// VERT FLIP (0 = Disable , 1 = Enable)
	s->set_vflip(s, 0);
	// DCW (0 = Disable , 1 = Enable)
	s->set_dcw(s, 1);
	// COLOR BAR PATTERN (0 = Disable , 1 = Enable)
	s->set_colorbar(s, 0);
}
//SHTSensor sht(SHTSensor::SHT85); // I2C address: 0x44


void initMicroSDCard()
{
	// Start the MicroSD card
	Serial.println("Mounting MicroSD Card");
	if (!SD_MMC.begin("/sdcard", true)) { // the arguments disable the LED when using the SD card - leave them there!
		Serial.println("MicroSD Card Mount Failed");
		return;
	}
	uint8_t cardType = SD_MMC.cardType();
	if (cardType == CARD_NONE) {
		Serial.println("No MicroSD Card found");
		return;
	}
}

void takeNewPhoto(String path)
{
	// Take picture with the camera and save it

	// Take multiple pictures with a short break in between
	//   necessary for the camera to auto-adjust settings
	camera_fb_t *fb;
	for (int i = 1; i <= img_number; i++) {
		// Setup frame buffer
		fb = esp_camera_fb_get();
		// digitalWrite(LEDpin, LOW);  // disable flash LED if needed

		if (!fb) {
			Serial.println("Camera capture failed");
			return;
		} else {
			Serial.println("Camera capture successful");
		}
		// TODO check if this delay is necessary once the SD-card todo below
		//   is resolved. It's possible that it can be at least shortened.
		if (i == img_number) {
			delay(2000);
		}

		esp_camera_fb_return(fb);
		// Without this delay the image is corrupt
		// TODO this delay can possibly also be optimized.
		delay(1500);

		/* TODO:
		The SD card throws the following error when the capture fails:
		E (877528) sdmmc_cmd: sdmmc_write_sectors_dma: sdmmc_send_cmd returned 0x109
		E (877528) diskio_sdmmc: sdmmc_write_blocks failed (265)
		This happens infrequently, say every 10th image or so.
		If this happens, one more new image should be taken and saved
		*/
	}

	// Save picture to microSD card
	fs::FS &fs = SD_MMC;
	File file = fs.open(path.c_str(), FILE_WRITE);
	if (!file) {
		Serial.println("Failed to open file in write mode");
	} else {
		file.write(fb->buf, fb->len); // payload (image), payload length
		Serial.printf("Saved file to path: %s\n", path.c_str());
	}
	// Close the file
	file.close();

	// Return the frame buffer back to the driver for reuse
	esp_camera_fb_return(fb);
}

// Count files on SD card to give a meaningful count  
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("Failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
		fileCountOnSD += 1;

        //Serial.print("  FILE: ");
        //Serial.print(file.name());
        //Serial.print("  SIZE: ");
        //Serial.println(file.size());
        
        file = root.openNextFile();
    }
	Serial.println("File Count on SD: ");
	Serial.println(fileCountOnSD);
 void readRTC()
 {
 	// This is used for a picture timestamp (name of the image)
 	 if (! rtc.begin()) {
 	 	Serial.println("Couldnt find RTC!");
 	 } else {
 	 	Serial.println("Setting the time");
 	 }
 	 DateTime now = rtc.now();
 	 sprintf(time_string, "%04d-%02d-%02dT%02d-%02d-%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(),
 	         now.second());
 	 Serial.println(time_string);

	
 }

	pinMode(LEDpin, OUTPUT);
	// Disable brownout detector
	WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

	// Start Serial
	Serial.begin(115200);

	// Initialize the camera
	Serial.print("Initializing the camera module...");
	camera_configESPCamera();
	Serial.println("Camera OK!");
	espnow_setup();
	sht85Sensor.setup();

	try {
		//	out_data_drs26 data= dr26.readData();
		//	Serial.printf("data circumfrence");
		//	Serial.printf(String( data.circumferenceIncrement).c_str());
		// auto messages = drs26.buildMessages();
		auto messages = sht85Sensor.buildMessages();

		// auto scdMessages = scd30.buildmessages();

		for (const Message &message : messages) {
			if (message.send() != ESP_OK) {
				RtcMemory::store(message.getMessageAsMinifiedJsonString());
			}
			delay(5000);
			if (!was_msg_received()) {
				RtcMemory::store(message.getMessageAsMinifiedJsonString());
			}
		}

	} catch (const NoDataAvailableException &e) {
		std::cerr << e.what() << '\n';
	}

	// Initialize the MicroSD
	Serial.print("Initializing the MicroSD card module... ");
	initMicroSDCard();

	String path = "/Picture_" + String(fileCountOnSD) + ".jpg";
	Serial.printf("Picture file name: %s\n", path.c_str());

	// Take and save a picture
	takeNewPhoto(path);

	// Unmount SD card to free the pins for I2C use
	SD_MMC.end();

	// Delay for specified period
	delay(1000);

	// Deep sleep 
	esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
	Serial.println("Setup ESP32 to sleep for " + String(TIME_TO_SLEEP) + " Seconds");
	Serial.println("Going to sleep now");
  	delay(1000);
  	Serial.flush(); 
	gpio_hold_en((gpio_num_t)LEDpin);
	esp_deep_sleep_start();
}

void loop()
{