Add Camera to Refrigerator with M5Stack-TimerCamera (Saved on GoogleDrive)

In the Video

Japanese【日本語】

English【英語】

1. Overview

Use M5Stack-TimerCamera to add a camera function to your refrigerator.
The captured image is saved in GoogleDrive, and you can check the image in the refrigerator even when you are away from home.
Using ReedSwitch, image acquisition and GoogleDrive storage are performed with the opening and closing of the door as a trigger.

2. The development environment

Arduino was developed in Italy under the philosophy of “making things easier and easier to understand”.
Currently, it is widely used for learning all over the world, and the library is also substantial.
So, if you want to start electronic work, I think this is the only development environment.
Therefore, I use Arduino at this time.

3. Software [Reed Switch]

The software I made this time is as follows.

//*************************************************************************
//  ReedSwitch Ver2023.2.13
//  Arduino Board : M5Stack-Timer-CAM [M5Stack ver 2.0.6]
//  Written by IT-Taro
//***********************************************************************

const byte LED_PIN  = 2;  // green LED
const byte REED_PIN  = 13;  // REED Input PullUp
bool reedVal = false;
bool preVal = false;

// the setup function runs once when you press reset or power the board
void setup() {
  Serial.begin(115200);
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_PIN, OUTPUT);
  // REED Setting
  pinMode(REED_PIN, INPUT_PULLUP);
}

// the loop function runs over and over again forever
void loop() {
  // Read Reed switch status
  reedVal = digitalRead(REED_PIN);

  // Check Status of Reed Switch
  if (reedVal != preVal) {
    digitalWrite(LED_PIN, reedVal);   // Change LED Status
    Serial.print ( "Reed:" );
    Serial.println ( reedVal );
    preVal = reedVal;
  }
  delay(1);
}

4. Google Setting

Google settings are required to use GoogleCloudAPI. Acquire the client ID, refresh token, etc. to be set in the software. Please see here for a separate post

5. Software [with Save Image to GoogleDrive]

The software I made this time is as follows.

//*************************************************************************
//  M5TimerCAM POST LINE Ver2023.2.14
//  Arduino Board : M5Stack-Timer-CAM [M5Stack ver 2.0.6]
//  Written by IT-Taro
//***********************************************************************

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "esp_camera.h"

// ################ for Battery Use ###############
//#include "battery.h"
//#include "soc/rtc_cntl_reg.h" // for BrouwnoutDetector Disable

//#define BATTERY_ENABLE

// ##################### Line, Wi-Fi settings (Preferences) #####################
String clientId           = "413$$[omission]$$googleusercontent.com"; // $$$ CHANGE REQUIRED $$$
String clientSecret       = "GOC$$[omission]$$mdgijDWEOzAIBl85PxxDQ"; // $$$ CHANGE REQUIRED $$$
String refreshToken       = "1//$$[omission]$$K_OQvtejx2P3V8JG8M-lk"; // $$$ CHANGE REQUIRED $$$
String driveFolder        = "1fp$$[omission]$$Ma9eVQasfsrrg3skub38q"; // Folder ID of GoogleDrive $$$ CHANGE REQUIRED $$$

const char *ssid        = "##### SSID #####"; // $$$ CHANGE REQUIRED $$$
const char *password    = "### PASSWORD ###"; // $$$ CHANGE REQUIRED $$$
// ###################################################################
const char* refreshServer = "oauth2.googleapis.com";
const char* refreshUri    = "/token";
const char* apiServer     = "www.googleapis.com";
const char* apiUri        = "/upload/drive/v3/files?uploadType=multipart";
String accessToken        = "";

// LED Pin Setting
const byte LED_PIN       = 2;   // Green LED
const byte REED_PIN   = 13;  // REED SW Input

// CAMERA_MODEL_M5_UNIT_CAM
#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    15
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     25
#define SIOC_GPIO_NUM     23

#define Y9_GPIO_NUM       19
#define Y8_GPIO_NUM       36
#define Y7_GPIO_NUM       18
#define Y6_GPIO_NUM       39
#define Y5_GPIO_NUM        5
#define Y4_GPIO_NUM       34
#define Y3_GPIO_NUM       35
#define Y2_GPIO_NUM       32
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

// Global Values
WiFiClientSecure httpsClient;
bool ledFlag          = true; // LED Control Flag
int waitingTime      = 30000; // Wait 30 seconds to google response.
bool curReedSw       = false; // Cuurent  Reed SW Value
bool preReedSw       = false; // previous Reed SW Value
camera_fb_t * fb;

// Setup Function
void setup() {
  Serial.begin(115200);

// For Battery Use
#ifdef BATTERY_ENABLE
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable   detector
  bat_init();
  bat_hold_output();
#endif
  //Serial.setDebugOutput(true);
  //Serial.println();

  // Camera Setting
  camera_config_t config;
  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;
  // Image size setting:QVGA(320x240),CIF(400x296),HVGA(480x320),VGA(640x480),SVGA(800x600),XGA(1024x768)
  config.frame_size = FRAMESIZE_XGA;
  config.jpeg_quality = 10;
  config.fb_count = 2;

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  //sensor_t *s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  //s->set_vflip(s, 1);         // flip it back (upside down)
  //s->set_brightness(s, 1);  // up the blightness just a bit
  //s->set_saturation(s, -2); // lower the saturation

  // ####### LED Pin setting #######
  pinMode ( LED_PIN, OUTPUT );

  // ####### Reed Switch #######
  pinMode(REED_PIN, INPUT_PULLUP);

  // ####### Wireless Wi-Fi connection #######
  WiFi.begin ( ssid, password );
  while ( WiFi.status() != WL_CONNECTED ) { // infinite loop until connected
    // LED flashes every second while connected
    ledFlag = !ledFlag;
    digitalWrite(LED_PIN, ledFlag);
    delay ( 1000 );
    Serial.print ( "." );
  }
  // Wi-Fi connection completed (IP address display)
  Serial.print ( "Wi-Fi Connected! IP address: " );
  Serial.println ( WiFi.localIP() );
  Serial.println ( );
  // LED lights when Wi-Fi is connected (Wi-Fi connection status)
  digitalWrite ( LED_PIN, true );

  // ####### HTTPS certificate check setting #######
  // Skip Server certificate check (required since 1.0.5)
  httpsClient.setInsecure();//skip verification
  //httpsClient.setCACert(rootCA);// It is also possible to obtain a root certificate in advance using a web browser and set rootCA

  // ####### NTP setting #######
  configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");

  /*    // ####### Get JPEG picture #######
      Serial.println("Start get JPG");
      getCameraJPEG();
      // ####### get Access Token #######
      Serial.println("Start get AccessToken");
      getAccessToken();
      // ####### Save JPEG to GoogleDrive #######
      Serial.println("Start Post GoogleDrive");
      postGoogleDriveByAPI();*/

  Serial.println("Setup Completed!!!");
}

// Loop Function
void loop() {
  // Read Reed switch status
  curReedSw = digitalRead(REED_PIN);

  // Check Status of Reed Switch
  if (curReedSw != preReedSw ) {
    digitalWrite(LED_PIN, curReedSw);   // Change LED Status
    Serial.print ( "Reed:" );
    Serial.println ( curReedSw );
    preReedSw = curReedSw;

    // IF Sw open, proceed task
    if (curReedSw == HIGH) {
      // After 2 seconds, proceed
      delay(2000);
      // ####### Get JPEG picture #######
      Serial.println("Start get JPG");
      getCameraJPEG();
      // ####### get Access Token #######
      Serial.println("Start get AccessToken");
      getAccessToken();
      // ####### Save JPEG to GoogleDrive #######
      Serial.println("Start Post GoogleDrive");
      postGoogleDriveByAPI();
    }
  }
  delay(100);
}

// Send JPEG by Http POST
void postGoogleDriveByAPI() {

  Serial.println("Connect to " + String(apiServer));
  if (httpsClient.connect(apiServer, 443)) {
    Serial.println("Connection successful");

    // Get Time for save file name
    struct tm timeInfo;
    char preFilename[16];
    getLocalTime(&timeInfo);
    sprintf(preFilename, "%04d%02d%02d_%02d%02d%02d",
          timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
          timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);
    String saveFilename = "esp32_" + String(preFilename) + ".jpg";

    String metadata = "--foo_bar_baz\r\n"
                      "Content-Type: application/json; charset=UTF-8\r\n\r\n"
                      "{\"name\":\"" + saveFilename + "\",\"parents\":[\"" + driveFolder + "\"]}\r\n\r\n"; // parents:save folder
                      //"{\"name\":\"" + saveFilename + "\"}\r\n\r\n"; // parerents is Optional
    String startBoundry = "--foo_bar_baz\r\n"
                          "Content-Type:image/jpeg\r\n\r\n";
    String endBoundry   = "\r\n--foo_bar_baz--";

    unsigned long contentsLength = metadata.length() + startBoundry.length() + fb->len + endBoundry.length();
    String header = "POST " + String(apiUri) + " HTTP/1.1\r\n" +
                    "HOST: " + String(apiServer) + "\r\n" +
                    "Connection: close\r\n" +
                    "content-type: multipart/related; boundary=foo_bar_baz\r\n" +
                    "content-length: " + String(contentsLength) + "\r\n" +
                    "authorization: Bearer " + accessToken + "\r\n\r\n";

    Serial.println("Send JPEG DATA by API");
    httpsClient.print(header);
    httpsClient.print(metadata);
    httpsClient.print(startBoundry);
    // JPEG data is separated into 1000 bytes and POST
    unsigned long dataLength = fb->len;
    uint8_t*      bufAddr    = fb->buf;
    for(unsigned long i = 0; i < dataLength ;i=i+1000) {
      if ( (i + 1000) < dataLength ) {
        httpsClient.write(( bufAddr + i ), 1000);
      } else if (dataLength%1000 != 0) {
        httpsClient.write(( bufAddr + i ), dataLength%1000);
      }
    }
    httpsClient.print(endBoundry);

    Serial.println("Waiting for response.");
    long int StartTime=millis();
    while (!httpsClient.available()) {
      Serial.print(".");
      delay(100);
      if ((StartTime+waitingTime) < millis()) {
        Serial.println();
        Serial.println("No response.");
        break;
      }
    }
    Serial.println();
    while (httpsClient.available()) {
      Serial.print(char(httpsClient.read()));
    }
    /*Serial.println("Recieving Reply");
    while (httpsClient.connected()) {
      String retLine = httpsClient.readStringUntil('\n');
      //Serial.println("retLine:" + retLine);
      int okStartPos = retLine.indexOf("200 OK");
      if (okStartPos >= 0) {
        Serial.println("200 OK");
        break;
      }
    }*/
  } else {
    Serial.println("Connected to " + String(refreshServer) + " failed.");
  }
  httpsClient.stop();
}

// Update access token with refresh token (access token valid time is 1 hour)
void getAccessToken() {
  accessToken = "None";
  // ####### Get Access Token #######
  Serial.println("Connect to " + String(refreshServer));
  if (httpsClient.connect(refreshServer, 443)) {
    Serial.println("Connection successful");

    String body = "client_id="     + clientId     + "&" +
                  "client_secret=" + clientSecret + "&" +
                  "refresh_token=" + refreshToken + "&" +
                  "grant_type=refresh_token";

    // Send Header
    httpsClient.println("POST "  + String(refreshUri) + " HTTP/1.1");
    httpsClient.println("Host: " + String(refreshServer));
    httpsClient.println("content-length: " + (String)body.length());
    httpsClient.println("Content-Type: application/x-www-form-urlencoded");
    httpsClient.println();
    // Send Body
    httpsClient.println(body);

    Serial.println("Recieving Token");
    while (httpsClient.connected()) {
      String retLine = httpsClient.readStringUntil('\n');
      //Serial.println("retLine:" + retLine);
      int tokenStartPos = retLine.indexOf("access_token");
      if (tokenStartPos >= 0) {
        tokenStartPos     = retLine.indexOf("\"", tokenStartPos) + 1;
        tokenStartPos     = retLine.indexOf("\"", tokenStartPos) + 1;
        int tokenEndPos   = retLine.indexOf("\"", tokenStartPos);
        accessToken       = retLine.substring(tokenStartPos, tokenEndPos); 
        Serial.println("AccessToken:"+accessToken);
        break;
      }
    }
  } else {
    Serial.println("Connected to " + String(refreshServer) + " failed.");
  }
  httpsClient.stop();
  if (accessToken == "None") {
    Serial.println("Get AccessToken Failed. Restart ESP32!");
    delay(3000);
    ESP.restart();
  }
}

// Get JPEG image with OV3660
void getCameraJPEG(){
  fb = esp_camera_fb_get();  // Get JPEG image
  if (!fb) {
    Serial.printf("Camera capture failed");
  }
  Serial.printf("JPG: %uB ", (uint32_t)(fb->len));
  Serial.println();
  // Shooting end processing
  esp_camera_fb_return(fb);
}

Document [at youtube]

Comments

Copied title and URL