From a10b8266a4482010f3ee41c6ec0afbab1d0638c6 Mon Sep 17 00:00:00 2001
From: amedvedova <alzbetamedvedova@gmail.com>
Date: Fri, 16 Sep 2022 09:21:12 +0200
Subject: [PATCH] Added a code snippet for ESPcam + SHT85 readout

---
 code-snippets/client/ESPcam/.gitignore        |   5 +
 .../client/ESPcam/.vscode/extensions.json     |  10 +
 code-snippets/client/ESPcam/include/README    |  39 +++
 code-snippets/client/ESPcam/platformio.ini    |  17 +
 code-snippets/client/ESPcam/src/main.cpp      | 321 ++++++++++++++++++
 code-snippets/client/ESPcam/test/README       |  11 +
 6 files changed, 403 insertions(+)
 create mode 100644 code-snippets/client/ESPcam/.gitignore
 create mode 100644 code-snippets/client/ESPcam/.vscode/extensions.json
 create mode 100644 code-snippets/client/ESPcam/include/README
 create mode 100644 code-snippets/client/ESPcam/platformio.ini
 create mode 100644 code-snippets/client/ESPcam/src/main.cpp
 create mode 100644 code-snippets/client/ESPcam/test/README

diff --git a/code-snippets/client/ESPcam/.gitignore b/code-snippets/client/ESPcam/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/code-snippets/client/ESPcam/.gitignore
@@ -0,0 +1,5 @@
+.pio
+.vscode/.browse.c_cpp.db*
+.vscode/c_cpp_properties.json
+.vscode/launch.json
+.vscode/ipch
diff --git a/code-snippets/client/ESPcam/.vscode/extensions.json b/code-snippets/client/ESPcam/.vscode/extensions.json
new file mode 100644
index 0000000..080e70d
--- /dev/null
+++ b/code-snippets/client/ESPcam/.vscode/extensions.json
@@ -0,0 +1,10 @@
+{
+    // See http://go.microsoft.com/fwlink/?LinkId=827846
+    // for the documentation about the extensions.json format
+    "recommendations": [
+        "platformio.platformio-ide"
+    ],
+    "unwantedRecommendations": [
+        "ms-vscode.cpptools-extension-pack"
+    ]
+}
diff --git a/code-snippets/client/ESPcam/include/README b/code-snippets/client/ESPcam/include/README
new file mode 100644
index 0000000..194dcd4
--- /dev/null
+++ b/code-snippets/client/ESPcam/include/README
@@ -0,0 +1,39 @@
+
+This directory is intended for project header files.
+
+A header file is a file containing C declarations and macro definitions
+to be shared between several project source files. You request the use of a
+header file in your project source file (C, C++, etc) located in `src` folder
+by including it, with the C preprocessing directive `#include'.
+
+```src/main.c
+
+#include "header.h"
+
+int main (void)
+{
+ ...
+}
+```
+
+Including a header file produces the same results as copying the header file
+into each source file that needs it. Such copying would be time-consuming
+and error-prone. With a header file, the related declarations appear
+in only one place. If they need to be changed, they can be changed in one
+place, and programs that include the header file will automatically use the
+new version when next recompiled. The header file eliminates the labor of
+finding and changing all the copies as well as the risk that a failure to
+find one copy will result in inconsistencies within a program.
+
+In C, the usual convention is to give header files names that end with `.h'.
+It is most portable to use only letters, digits, dashes, and underscores in
+header file names, and at most one dot.
+
+Read more about using header files in official GCC documentation:
+
+* Include Syntax
+* Include Operation
+* Once-Only Headers
+* Computed Includes
+
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
diff --git a/code-snippets/client/ESPcam/platformio.ini b/code-snippets/client/ESPcam/platformio.ini
new file mode 100644
index 0000000..51be137
--- /dev/null
+++ b/code-snippets/client/ESPcam/platformio.ini
@@ -0,0 +1,17 @@
+; PlatformIO Project Configuration File
+;
+;   Build options: build flags, source filter
+;   Upload options: custom upload port, speed and extra flags
+;   Library options: dependencies, extra library storages
+;   Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+[env:esp32cam]
+platform = espressif32
+board = esp32cam
+framework = arduino
+monitor_speed = 115200
+lib_deps = sensirion/arduino-sht@^1.2.2
+           adafruit/RTClib @^2.1.1
\ No newline at end of file
diff --git a/code-snippets/client/ESPcam/src/main.cpp b/code-snippets/client/ESPcam/src/main.cpp
new file mode 100644
index 0000000..ac20e29
--- /dev/null
+++ b/code-snippets/client/ESPcam/src/main.cpp
@@ -0,0 +1,321 @@
+/*
+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 IO1 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 <Arduino.h>
+#include <Wire.h>
+#include "SHTSensor.h"    // sensirion/arduino-sht@^1.2.2
+#include "RTClib.h"       // adafruit/RTClib @^2.1.1
+#include "SPI.h"
+
+// Camera libraries
+#include "esp_camera.h"
+#include "soc/soc.h"
+#include "soc/rtc_cntl_reg.h"
+#include "driver/rtc_io.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!
+#define SDA         12
+#define SCL         13
+
+// LED control
+#define LEDpin 4
+
+// 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;
+
+SHTSensor sht(SHTSensor::SHT85);    // I2C address: 0x44
+RTC_DS3231 rtc;                     // I2C address: 0x68
+camera_config_t config;             // camera configuration parameters
+
+
+
+void configESPCamera() { 
+  // Configure Camera parameters 
+
+  config.ledc_channel = LEDC_CHANNEL_0;
+  config.ledc_timer = LEDC_TIMER_0;
+  config.pin_d0 = Y2_GPIO_NUM;
+  config.pin_d1 = Y3_GPIO_NUM;
+  config.pin_d2 = Y4_GPIO_NUM;
+  config.pin_d3 = Y5_GPIO_NUM;
+  config.pin_d4 = Y6_GPIO_NUM;
+  config.pin_d5 = Y7_GPIO_NUM;
+  config.pin_d6 = Y8_GPIO_NUM;
+  config.pin_d7 = Y9_GPIO_NUM;
+  config.pin_xclk = XCLK_GPIO_NUM;
+  config.pin_pclk = PCLK_GPIO_NUM;
+  config.pin_vsync = VSYNC_GPIO_NUM;
+  config.pin_href = HREF_GPIO_NUM;
+  config.pin_sscb_sda = SIOD_GPIO_NUM;
+  config.pin_sscb_scl = SIOC_GPIO_NUM;
+  config.pin_pwdn = PWDN_GPIO_NUM;
+  config.pin_reset = RESET_GPIO_NUM;
+  config.xclk_freq_hz = 20000000;
+  config.pixel_format = PIXFORMAT_JPEG; // Choices are YUV422, GRAYSCALE, RGB565, JPEG
+
+  // Select lower framesize if the camera doesn't support PSRAM
+  if (psramFound()) {
+    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
+    config.jpeg_quality = 10; //10-63 lower number means higher quality
+    config.fb_count = 2;
+  } else {
+    config.frame_size = FRAMESIZE_SVGA;
+    config.jpeg_quality = 12;
+    config.fb_count = 1;
+  }
+
+  // Initialize the Camera
+  esp_err_t err = esp_camera_init(&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);
+}
+
+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);
+}
+
+
+void readSHT(){
+  // Read data from the initialized SHT85
+  // TODO send via esp now instead of printing
+  if (sht.readSample()) {
+      Serial.print("T:  ");
+      Serial.println(sht.getTemperature(), 2);
+      Serial.print("RH: ");
+      Serial.println(sht.getHumidity(), 2);
+  } else {
+      Serial.print("Error in readSample()\n");
+  }
+}
+
+void readRTC(){
+  // This is used for a picture timestamp (name of the image)
+  rtc.begin();
+  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);
+}
+
+void readI2C(){
+  // Start the I2C bus
+  Wire.begin(SDA, SCL);
+  delay(100);     // let serial console settle
+
+  // initiate the SHT85 sensor
+  Serial.println("");
+  if (sht.init()) {
+      Serial.println("SHT initialization successful");
+  } else {
+      Serial.println("SHT initialization failed");
+  }
+
+  // T + RH reading
+  readSHT();
+  // real-time clock reading
+  readRTC();
+  delay(100);
+
+  // end I2C to free the pins to be used by the SD card
+  Wire.end();
+}
+
+
+void setup() {
+  // control of the LED pin
+  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...");
+  configESPCamera();
+  Serial.println("Camera OK!");
+
+}
+
+void loop() {
+  // initiate I2C, read SHT+RTC, end I2C
+  readI2C();
+
+  // Initialize the MicroSD
+  Serial.print("Initializing the MicroSD card module... ");
+  initMicroSDCard();
+
+  // Path where new image will be saved in MicroSD card
+  String path = "/" + String(time_string) + ".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);
+}
+
+
+
diff --git a/code-snippets/client/ESPcam/test/README b/code-snippets/client/ESPcam/test/README
new file mode 100644
index 0000000..9b1e87b
--- /dev/null
+++ b/code-snippets/client/ESPcam/test/README
@@ -0,0 +1,11 @@
+
+This directory is intended for PlatformIO Test Runner and project tests.
+
+Unit Testing is a software testing method by which individual units of
+source code, sets of one or more MCU program modules together with associated
+control data, usage procedures, and operating procedures, are tested to
+determine whether they are fit for use. Unit testing finds problems early
+in the development cycle.
+
+More information about PlatformIO Unit Testing:
+- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
-- 
GitLab