Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
![]() |
|
As a class assignment, I wired up a Particle Argon to take environment readings using a BME280 over the I2C bus and a SEEED Dust Sensor and Air Quality monitor. Results from a capacitive soil moisture sensor were then used to control a pump over an Optocoupler relay to water a plant. Environment data gets published to OLED display and to an Adafruit Dashboard, with a graphical button that can be clicked to water on demand. Texts are sent out using Zapier to announce watering cycles.
Adding an ESP32 with a camera allowed periodically posting jpeg images of the plant, so that the desired soil moisture level could be controlled using a Dashboard slider after checking in on how the plant is looking. An unexpected hurdle was consistently publishing the image to an Adafruit feed, which required encoding the data taken from the camera's frame buffer to 64-bit, and adjusting the image settings and payload to stay within the 100 kB "history-off" limits for Adafruit feeds. (An Adafruit feed with history enabled limits the published payload to only 1 kB.)
ESP32S3 grab stillframe and MQTT publish main
C/C++/*
* 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++/*
* 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 FileNo preview (download only).
#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
Comments