Nick Tolk
Published © MIT

PlantCare_NRT

A Particle Argon waters a plant based on moisture in soil, while a camera attached to an ESP32 posts jpegs to an Adafruit Dashboard.

IntermediateShowcase (no instructions)6 hours57
PlantCare_NRT

Things used in this project

Hardware components

Argon
Particle Argon
×1
Gravity: I2C BME280 Environmental Sensor
DFRobot Gravity: I2C BME280 Environmental Sensor
×1
Grove - Capacitive Moisture Sensor (Corrosion Resistant)
Seeed Studio Grove - Capacitive Moisture Sensor (Corrosion Resistant)
×1
Espressif ESP32 Development Board - Developer Edition
Espressif ESP32 Development Board - Developer Edition
×1
Grove - Dust Sensor(PPD42NS)
Seeed Studio Grove - Dust Sensor(PPD42NS)
×1
Grove - Air quality sensor v1.3
Seeed Studio Grove - Air quality sensor v1.3
×1
Through Hole Resistor, 2.2 kohm
Through Hole Resistor, 2.2 kohm
×1
Resistor 221 ohm
Resistor 221 ohm
×1
General Purpose Transistor PNP
General Purpose Transistor PNP
×1

Software apps and online services

Visual Studio Code Extension for Arduino
Microsoft Visual Studio Code Extension for Arduino
VS Code
Microsoft VS Code

Story

Read more

Schematics

PlantCare breadboard - Fritzing

The Fritzing version of the breadboard layout

PlantCare breadboard - Image

Layout for the Particle Argon with I2C, sensors, and motor control

Plantcare schematic

Schematic view of the PlantCare breadboard

Code

ESP32S3 grab stillframe and MQTT publish main

C/C++
main.cpp file for handling camera grab on ESP32S3 board then publishing to Adafruit dashboard
/*
 * Project frameGrabMQTT
 * Description: Periodically grabs still frames from camera and publishes them to an Adafruit feed
 * Author:      Nick Tolk
 * Date:        17-MAR-2023
 */
#include <Arduino.h>

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

// other camera models listed in "camera_pins.h"
#define CAMERA_MODEL_ESP32S3_EYE
#include "camera_pins.h"
const int ONBOARD_LED = 2;


#include "credentials.h"
const char* mqttTopicSnap = "nicktolk/feeds/plantSnap";
const char* mqttTopicStream = "nicktolk/feeds/plantStream";
const int MQTT_BUFFERSIZE = 50 * 1024;
const int MAX_PUBLISH = 50 * 1024;    // Adafruit limit is 100 kB, not 50

// these are used for image publication to Adafruit dashboard
#include <PubSubClient.h>
#include <base64.h>

bool pin2On = false;  // used for irregular LED flashing while attempting serial connection, as serial is often not up

const int PUBLISH_DELAY = 5*60*1000;  // 5 minutes between publishing images
const int SERIAL_TIMEOUT = 0*1000;   // 0 seconds to wait for serial connection - often absent
int lastTick = 0; // for timing

WiFiClient espClient;
PubSubClient client(espClient);

void mqtt_setup();
void callback(char* topic, byte* payload, unsigned int length);

char strOut[1024];  // for Adafruit text publications (1kB limit when Dashboard control history is on)
String buffer;      // for Adafruit image publication (100kB limit when Dashboard control history is on)

void setup() {
  Serial.begin(9600);
  pinMode(ONBOARD_LED, OUTPUT); 
  while(!Serial.available() && millis() - lastTick < SERIAL_TIMEOUT){
    digitalWrite(ONBOARD_LED, pin2On = !pin2On);
    delay(200 + 200 * (random()%2));              // random on/off delays of either 200 or 400 ms
    Serial.begin(9600);
  }
  Serial.setDebugOutput(true);
  Serial.println("Serial is up!");

  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.frame_size = FRAMESIZE_VGA;          // limited for 100 kB Adafruit limit for publishing
  config.pixel_format = PIXFORMAT_JPEG;
  config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_DRAM;
  config.jpeg_quality = 10;                   // (0-63) higher numbers are lower quality
  config.fb_count = 1;

  // 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();
// adjusted for spefic use in class
  s->set_agc_gain(s, 15);   // (1 - 31)
  s->set_gain_ctrl(s, 1);   // white balance gain control (0 or 1)
  s->set_brightness(s, 2);  // (-2 to 2) set to brightest

  WiFi.begin(ssid, password);
  WiFi.setSleep(false);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
}

// publish a pic, then wait
void loop() {
  mqtt_setup();     // refresh to keep alive
  static camera_fb_t *fb = NULL;
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    client.publish(mqttTopicStream, "Camera capture failed\0");
    return;
  }

// Adafruit insists on 64-bit encoded jpegs
  buffer = base64::encode((uint8_t *)fb->buf, fb->len);
  sprintf(strOut, "Got frame: %d x %d (%d/%d)\0", fb->width, fb->height, fb->len, buffer.length());
  client.publish(mqttTopicStream, strOut);
  Serial.printf("%s\n", strOut);
  if (buffer.length() < MAX_PUBLISH) {
    if (client.publish(mqttTopicSnap, buffer.c_str()))
    {
      Serial.print("Published Image to ");
      Serial.print(mqttTopicSnap);
      Serial.printf("\n");
      client.publish(mqttTopicStream, "Published!\0");
      Serial.printf("Published %d bytes (from %d)\n", buffer.length(), fb->len);
    }
    else
    {
      Serial.println("Error Publishing Image");
      client.publish(mqttTopicStream, "Error publishing...\0");
    }
  } else {
    client.publish(mqttTopicStream, "Over limit - We'll try to publish the next pic\0");
  }

  esp_camera_fb_return(fb);
  buffer.clear();

  delay(PUBLISH_DELAY);     // use a delay here - time since last attempt ended, not started
}

//------------------ MQTT ----------------------------------
void mqtt_setup() {
// Adafruit publication limit is 100kB with Dashboard control history off
// Buffersize is set lower based on successful publish history
  client.setBufferSize((uint16_t)MQTT_BUFFERSIZE);
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  Serial.println("Connecting to MQTT…");
  while (!client.connected()) {        
    String clientId = "ESP32Client-";
    clientId += String(random(0xffff), HEX);
    if (client.connect(clientId.c_str(), mqttUser, mqttPassword )) {
        Serial.println("connected");
    } else {
        Serial.print("failed with state  ");
        Serial.println(client.state());
        delay(2000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {

    Serial.print("Message arrived in topic: ");
    Serial.println(topic);

    String byteRead = "";
    Serial.print("Message: ");
    for (int i = 0; i < length; i++) {
        byteRead += (char)payload[i];
    }    
    Serial.println(byteRead);
}

PlantCare Main

C/C++
.ino file for handing environmental sensors, MQTT publish & subscribe behavior, and watering control
/*
 * Project PlantCare
 * Description: PlantCare reads environmental data using several sensors, displays those, and publishes them to an Adafruit Dashboard.
 *              Based on the reading on a capacitive soil moisuture probe, a relay is used to turn on a pump and water a plant.
 *              Desired moisture is set on start-up to the level detected. This is adjusted on the Dashboard.
 *              Watering events are posted for use in sending out watering announcements using Zapier.
 * Author:      Nick Tolk
 * Date:        17-MAR-2023
 */


#include "credentials.h"
#include "PlantCare.h"

#include <Adafruit_SSD1306.h>
#include <Adafruit_BME280.h>
#include <Grove_Air_quality_Sensor.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h" 
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"

// Sensor declarations
Adafruit_BME280 bme;
static Adafruit_SSD1306 display(OLED_RESET);
AirQualitySensor aqSensor(AQ_PIN);

TCPClient TheClient;

// "plantStream" is also used by a separate application, frameGrabMQTT, which also publishes images to "plantSnap" on the same Dashboard
Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY); 
Adafruit_MQTT_Publish mqttPlantStream = Adafruit_MQTT_Publish(&mqtt,AIO_USERNAME "/feeds/plantStream");     // log for capturing events and dispaying environment data
Adafruit_MQTT_Subscribe mqttMoistTarget = Adafruit_MQTT_Subscribe(&mqtt,AIO_USERNAME "/feeds/moistTarget"); // slider for soil moisture target
Adafruit_MQTT_Publish mqttMoistTargetPub = Adafruit_MQTT_Publish(&mqtt,AIO_USERNAME "/feeds/moistTarget");
Adafruit_MQTT_Subscribe mqttWaterNow = Adafruit_MQTT_Subscribe(&mqtt,AIO_USERNAME "/feeds/waterNow");       // push-button on dashboard for manual watering requests
Adafruit_MQTT_Publish mqttMoist = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/moistCurrent");         // current reading from soil moisture probe, scaled 1-100
Adafruit_MQTT_Publish mqttWaterEvent = Adafruit_MQTT_Publish(&mqtt,AIO_USERNAME "/feeds/waterEvent");       // published to trigger SMS from Zapier

void MQTT_connect();  // this is called AFTER setting subscriptions
bool MQTT_ping();     // for keep-alive

void displayInit();                 // initizes OLED display
void sensorInit();                  // initializes sensors and IO pins
void readAndDisplay(bool publish);  // display current environment conditions to display, and maybe publish via MQTT
void waterNow();                    // briefly activate pump and publish event

bool timeSync = false;  // keep trying to sync time for OLED output until success

char strOut[1024];  // for Adafruit publications (1kB limit when Dashboard control history is on)

void setup() {
  Serial.begin(9600);

  // before waiting for wifi, make sure the pump is off
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, RELAY_OFF);

  WiFi.on();
  WiFi.connect();
  while(WiFi.connecting()) {
    Serial.printf(".");
    delay(100);
  }
  Serial.printf("\n");

// These come BEFORE MQTT_connect()!
  mqtt.subscribe(&mqttMoistTarget);
  mqtt.subscribe(&mqttWaterNow);

  MQTT_connect();
  MQTT_ping();

  Wire.begin();   // init I2C bus

  displayInit();  // white text, size 1, "OLED Awake"

  sensorInit();   // inits and outputs values to OLED display

  Time.zone (-6);             // MST = -7, MDT = -6
  if (Particle.syncTime()){   // Sync time with Particle Cloud
    timeSync = true;
  }

}


void loop() {
// Verify connection
  MQTT_connect();
  MQTT_ping();

// collect dust sensor stuff
  duration = pulseIn(DUST_PIN, LOW);
  lowpulseoccupancy += duration;

// check subscriptions
  Adafruit_MQTT_Subscribe *subscription;
  while((subscription = mqtt.readSubscription(100))){
    if (subscription == &mqttWaterNow) {
      if (atoi((char *)mqttWaterNow.lastread)){
        mqttPlantStream.publish("Watering due to request\n");
        Serial.printf("WATER!\n");
        waterNow();
      }
    }
    if (subscription == &mqttMoistTarget) {
      moistTarget = atof((char *)mqttMoistTarget.lastread);
      Serial.printf("New target: %f\n", moistTarget);
      displayTick = -displayDelay;
      sprintf(strOut, "Setting moisture target to %.1f\0", moistTarget);
      mqttPlantStream.publish(strOut);
    }
  }

// update OLED?
  if ((millis()-displayTick) >= displayDelay)
  {
  // publish too?    
    if ((millis() - publishTick) >= publishDelay){
      readAndDisplay(true);
      publishTick = millis();
    } else {
      readAndDisplay(false);
    }
  // need to water?
    if (moistScaled < moistTarget - waterGap){
      if ((millis() - waterTick) >= waterDelay){
        mqttPlantStream.publish("Giving JJ water because he's thirsty\0");        
        waterNow();
        waterTick = millis();
      }
    }
    displayTick = millis();
  }

}

// Checks sensors and outputs to OLED. May also publish to Aafruit.
void readAndDisplay(bool publish = false){
// retry time sync if needed
  if (!timeSync){
    if (Particle.syncTime()){
      timeSync = true;
    }
  }

// BME280 temp/pressure/relative humidity
  tempC = bme.readTemperature();  // deg C
  pressPA = bme.readPressure();   // pascals
  humidRH = bme.readHumidity();   // %RH

  tempF = map(tempC, 0.0, 100.0, 32.0, 212.0);
  pressHg = pressPA / 133.3223684;

  aqVal = aqSensor.getValue();        // air quality sensor
  moistVal = analogRead(MOIST_PIN);   // capacitive soil moisture sensor
  moistScaled = map((float)moistVal, 3500.0, 2000.0, 0.0, 100.0); // scaling values taken from dry soil and tap water
  if (publish){
    mqttMoist.publish(moistScaled);   // only the scaled moisture value gets its own feed. the rest go to the "plantStream"
  }

// for dust sensor
  ratio = lowpulseoccupancy/(displayDelay*10.0);  // Integer percentage 0=>100
  concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve
  lowpulseoccupancy = 0;

// OLED update
  display.clearDisplay();
  display.setCursor(0, 5);
  sprintf(strOut, "Temp:%.1fF\nRH:%.1f%%\nMoisture:%.0f/%.0f\nAQ:%d\0", 
    tempF, humidRH, moistScaled, moistTarget, aqVal);
  display.printf("%s", strOut);
  display.printf("\n%s\n", Time.timeStr().substring(11 ,19).c_str());  // just time, not date
  display.display();
  
  if (publish){
    mqttPlantStream.publish(strOut); 
  }
}

// starts display with appropriate settings and displays "OLED awake"
void displayInit(){
  display.begin(SSD1306_SWITCHCAPVCC, displayAddress);
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.clearDisplay();
  display.display();

  display.clearDisplay();
  display.setCursor(0, 5);
  display.printf("OLED awake\n");
  display.display();
}

// intialize sensors and report values or failures
void sensorInit(){
  if (!bme.begin (bmeAddress)){
    display.printf("BME280 at address 0x%02X failed\n", bmeAddress);
  } else {
    display.printf("BME:%.1f, %.1f, %.1f\n", bme.readTemperature(), bme.readPressure()/133.3223684, bme.readHumidity());
  }
  display.display();

  if (!aqSensor.init()){
    display.printf("Air quality sensor failed\n");
  } else {
    display.printf("AQ:%d\n", aqSensor.getValue());
  }
  display.display();

  pinMode(DUST_PIN, INPUT);
  display.printf("Dust pulse:%lu\n", pulseIn(DUST_PIN, LOW));
  display.display();

  pinMode(MOIST_PIN, INPUT);
  moistVal = analogRead(MOIST_PIN);
  moistScaled = map((float)moistVal, 3500.0, 2000.0, 0.0, 100.0);
  moistTarget = moistScaled;
  display.printf("Moisture:%.0f/%.0f\n", moistScaled, moistTarget);
  mqttMoistTargetPub.publish(moistScaled);
  display.display();
}

// water briefly and publish "waterEvent"
void waterNow(){
  mqttWaterEvent.publish(1);
  int startWater = millis();
  digitalWrite(RELAY_PIN, RELAY_ON);
  while (millis() - startWater < waterDuration){  }
  digitalWrite(RELAY_PIN, RELAY_OFF);
  mqttWaterEvent.publish(0);
}

// MQTT functions taken from class example
// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;
 
  // Return if already connected.
  if (mqtt.connected()) {
    return;
  }
 
  Serial.print("Connecting to MQTT... ");
 
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.printf("Error Code %s\n",mqtt.connectErrorString(ret));
    Serial.printf("Retrying MQTT connection in 5 seconds...\n");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds and try again
  }
  Serial.printf("MQTT Connected!\n");
}

bool MQTT_ping() {
  static unsigned int last;
  bool pingStatus = 0;

  if ((millis()-last)>120000) {
    Serial.printf("Pinging MQTT \n");
    pingStatus = mqtt.ping();
    if(!pingStatus) {
      Serial.printf("Disconnecting \n");
      mqtt.disconnect();
    }
    last = millis();
  }
  return pingStatus;
}

PlantCare Header

C Header File
.h header file to be used with PlantCare.ino. Contains pin assigments, constants, and some variable declararions
No preview (download only).

Camera pin header

C Header File
Header file containing pin assignments for several target boards
#if defined(CAMERA_MODEL_WROVER_KIT)
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    21
#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      19
#define Y4_GPIO_NUM      18
#define Y3_GPIO_NUM       5
#define Y2_GPIO_NUM       4
#define VSYNC_GPIO_NUM   25
#define HREF_GPIO_NUM    23
#define PCLK_GPIO_NUM    22

#elif defined(CAMERA_MODEL_ESP_EYE)
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM    4
#define SIOD_GPIO_NUM    18
#define SIOC_GPIO_NUM    23

#define Y9_GPIO_NUM      36
#define Y8_GPIO_NUM      37
#define Y7_GPIO_NUM      38
#define Y6_GPIO_NUM      39
#define Y5_GPIO_NUM      35
#define Y4_GPIO_NUM      14
#define Y3_GPIO_NUM      13
#define Y2_GPIO_NUM      34
#define VSYNC_GPIO_NUM   5
#define HREF_GPIO_NUM    27
#define PCLK_GPIO_NUM    25

#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
#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

#elif defined(CAMERA_MODEL_M5STACK_V2_PSRAM)
#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    15
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     22
#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    25
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_M5STACK_WIDE)
#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    15
#define XCLK_GPIO_NUM     27
#define SIOD_GPIO_NUM     22
#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    25
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_M5STACK_ESP32CAM)
#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       17
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_M5STACK_UNITCAM)
#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

#elif defined(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

#elif defined(CAMERA_MODEL_TTGO_T_JOURNAL)
#define PWDN_GPIO_NUM      0
#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       17
#define VSYNC_GPIO_NUM    22
#define HREF_GPIO_NUM     26
#define PCLK_GPIO_NUM     21


#elif defined(CAMERA_MODEL_ESP32_CAM_BOARD)
// The 18 pin header on the board has Y5 and Y3 swapped
#define USE_BOARD_HEADER 0 
#define PWDN_GPIO_NUM    32
#define RESET_GPIO_NUM   33
#define XCLK_GPIO_NUM     4
#define SIOD_GPIO_NUM    18
#define SIOC_GPIO_NUM    23

#define Y9_GPIO_NUM      36
#define Y8_GPIO_NUM      19
#define Y7_GPIO_NUM      21
#define Y6_GPIO_NUM      39
#if USE_BOARD_HEADER
#define Y5_GPIO_NUM      13
#else
#define Y5_GPIO_NUM      35
#endif
#define Y4_GPIO_NUM      14
#if USE_BOARD_HEADER
#define Y3_GPIO_NUM      35
#else
#define Y3_GPIO_NUM      13
#endif
#define Y2_GPIO_NUM      34
#define VSYNC_GPIO_NUM    5
#define HREF_GPIO_NUM    27
#define PCLK_GPIO_NUM    25

#elif defined(CAMERA_MODEL_ESP32S3_CAM_LCD)
#define PWDN_GPIO_NUM     -1
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM     40
#define SIOD_GPIO_NUM     17
#define SIOC_GPIO_NUM     18

#define Y9_GPIO_NUM       39
#define Y8_GPIO_NUM       41
#define Y7_GPIO_NUM       42
#define Y6_GPIO_NUM       12
#define Y5_GPIO_NUM       3
#define Y4_GPIO_NUM       14
#define Y3_GPIO_NUM       47
#define Y2_GPIO_NUM       13
#define VSYNC_GPIO_NUM    21
#define HREF_GPIO_NUM     38
#define PCLK_GPIO_NUM     11

#elif defined(CAMERA_MODEL_ESP32S2_CAM_BOARD)
// The 18 pin header on the board has Y5 and Y3 swapped
#define USE_BOARD_HEADER 0
#define PWDN_GPIO_NUM     1
#define RESET_GPIO_NUM    2
#define XCLK_GPIO_NUM     42
#define SIOD_GPIO_NUM     41
#define SIOC_GPIO_NUM     18

#define Y9_GPIO_NUM       16
#define Y8_GPIO_NUM       39
#define Y7_GPIO_NUM       40
#define Y6_GPIO_NUM       15
#if USE_BOARD_HEADER
#define Y5_GPIO_NUM       12
#else
#define Y5_GPIO_NUM       13
#endif
#define Y4_GPIO_NUM       5
#if USE_BOARD_HEADER
#define Y3_GPIO_NUM       13
#else
#define Y3_GPIO_NUM       12
#endif
#define Y2_GPIO_NUM       14
#define VSYNC_GPIO_NUM    38
#define HREF_GPIO_NUM     4
#define PCLK_GPIO_NUM     3

#elif defined(CAMERA_MODEL_ESP32S3_EYE)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 4
#define SIOC_GPIO_NUM 5

#define Y2_GPIO_NUM 11
#define Y3_GPIO_NUM 9
#define Y4_GPIO_NUM 8
#define Y5_GPIO_NUM 10
#define Y6_GPIO_NUM 12
#define Y7_GPIO_NUM 18
#define Y8_GPIO_NUM 17
#define Y9_GPIO_NUM 16

#define VSYNC_GPIO_NUM 6
#define HREF_GPIO_NUM 7
#define PCLK_GPIO_NUM 13

#else
#error "Camera model not selected"
#endif

Credits

Nick Tolk

Nick Tolk

4 projects • 8 followers

Comments

Add projectSign up / Login