BLYNK
BLYNK.IO       📲 GETTING STARTED       📗 DOCS       👉 SKETCH BUILDER

Back To Basics Still Struggling To Get Device To Work

I am using the AI Thinker ESP32 Cam along with a two-wire fan, and a microwave sensor. I have created a sketch in Arduino and uploaded it to my camera but cannot get it to come online in Blynk to see if the camera catches what i want it to catch. Any thoughts or suggestions on why it is not working and how to fix it and get it to work would be tremendously helpful. Here is the code:

// Template ID, Device Name and Auth Token are provided by the Blynk.Cloud
// See the Device Info tab, or Template settings
#define BLYNK_TEMPLATE_ID           ""
#define BLYNK_DEVICE_NAME           ""
#define BLYNK_AUTH_TOKEN            ""


// Comment this out to disable prints and save space
#define BLYNK_PRINT Serial


#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include "Arduino.h" // General functionality
#include "esp_camera.h" // Camera
#include <SD.h> // SD Card 
#include "FS.h" // File System
#include "soc/soc.h" // System settings (e.g. brownout)
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
#include <EEPROM.h> // EEPROM flash memory
#include <WiFi.h> // WiFi
#include "time.h" // Time functions
// "ESP Mail Client" by Mobizt, tested with v1.6.4
#include "ESP_Mail_Client.h" // e-Mail

// DEFINES
//#define USE_INCREMENTAL_FILE_NUMBERING //Uses EEPROM to store latest file stored
#define USE_timestamp // Uses Wi-Fi to retrieve current time value
#define SEND_EMAIL // Uses Wi-Fi to email photo attachment
#define TRIGGER_MODE // Photo capture triggered by GPIO pin rising/falling
//#define TIMED_MODE // Photo capture automated according to regular delay

char auth[] = BLYNK_AUTH_TOKEN;

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "";
char pass[] = "";
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
#define SMTP_HOST "smtp.office365.com"
#define SMTP_PORT 25
#define AUTHOR_EMAIL ""
#define AUTHOR_PASSWORD ""

// CONSTANTS
// GPIO Pin 33 is small red LED near to RESET button on the back of the board
const byte ledPin = GPIO_NUM_33;
// GPIO Pin 4 is bright white front-facing LED 
const byte flashPin = GPIO_NUM_4;
// When using TRIGGER_MODE, this pin will be used to initiate photo capture
const byte triggerPin = GPIO_NUM_13;
// Flash strength (0=Off, 255=Max Brightness)
// Setting a low flash value can provide a useful visual indicator of when a photo is being taken
const byte flashPower = 1;
//#ifdef TIMED_MODE
const int timeLapseInterval = 30; // seconds between successive shots in TIMELAPSE mode
//#endif
const int startupDelayMillis = 3000; // time to wait after initialising  camera before taking photo

// GLOBALS
// Keep track of number of pictures taken for incremental file naming
int pictureNumber = 0;
// Full path of filename of the last photo saved
String path;
#ifdef SEND_EMAIL
 // SMTP session used for eMail sending
 SMTPSession smtp;
 // Function fired on email success/failure
 void smtpCallback(SMTP_Status status);

 // Callback function after eMail sending
void smtpCallback(SMTP_Status status) {
 // Print the current status
 Serial.println(status.info());
 // Show details of successful delivery
 if (status.success())   {
   Serial.println("----------------");
   Serial.printf("Message sent success: %d\n", status.completedCount());
   Serial.printf("Message sent failed: %d\n", status.failedCount());
   Serial.println("----------------\n");
   struct tm dt;
   for (size_t i=0; i<smtp.sendingResult.size(); i++) {
     SMTP_Result result = smtp.sendingResult.getItem(i);
     time_t ts = (time_t)result.timestamp;
     localtime_r(&ts, &dt);
     Serial.printf("Message No: %d\n", i + 1);
     Serial.printf("Status: %s\n", result.completed ? "success" : "failed");
     Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec);
     Serial.printf("Recipient: %s\n", result.recipients);
     Serial.printf("Subject: %s\n", result.subject);
   }
   Serial.println("----------------");
 }
 
}
#endif

void sleep() {
 // IMPORTANT - we define pin mode for the trigger pin at the end of setup, because most pins on the ESP32-CAM
 // have dual functions, and may have previously been used by the camera or SD card access. So we overwrite them here
 pinMode(triggerPin, INPUT_PULLDOWN);
 // Ensure the flash stays off while we sleep
 rtc_gpio_hold_en(GPIO_NUM_4);
 // Turn off the LED
 digitalWrite(ledPin, HIGH);
 delay(1000);
 #ifdef TRIGGER_MODE
   // Use this to wakeup when trigger pin goes HIGH
   esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1);
   // Use this to wakeup when trigger pin goes LOW
   //esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0);
 #elif defined(TIMED_MODE)
   // Or, use this to wakeup after a certain amount of time has elapsed (parameter specified in uS, so multiply secs by 1000000)
   //esp_sleep_enable_timer_wakeup(timeLapseInterval * 1000000);
 #endif
 Serial.println("Going to sleep now");
 esp_deep_sleep_start();
 Serial.println("This will never be printed");
}

BlynkTimer timer;

// This function is called every time the Virtual Pin 0 state changes
BLYNK_WRITE(V0)
{
 // Set incoming value from pin V0 to a variable
 int value = param.asInt();

 // Update state
 Blynk.virtualWrite(V1, value);
}

// This function is called every time the device is connected to the Blynk.Cloud
BLYNK_CONNECTED()
{
 // Change Web Link Button message to "Congratulations!"
 Blynk.setProperty(V3, "offImageUrl", "https://static-image.nyc3.cdn.digitaloceanspaces.com/general/fte/congratulations.png");
 Blynk.setProperty(V3, "onImageUrl",  "https://static-image.nyc3.cdn.digitaloceanspaces.com/general/fte/congratulations_pressed.png");
 Blynk.setProperty(V3, "url", "https://docs.blynk.io/en/getting-started/what-do-i-need-to-blynk/how-quickstart-device-was-made");
}

// This function sends Arduino's uptime every second to Virtual Pin 2.
void myTimerEvent()
{
 // You can send any value at any time.
 // Please don't send more that 10 values per second.
 Blynk.virtualWrite(V2, millis() / 1000);
}
// constants won't change. They're used here to set pin numbers:

const int pb = 16;     // the number of the pushbutton pin
const int en = 3;      // the number of the LED pin
const int in1 = 2;

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup()
{
 // Debug console
 // Light up the discrete red LED on the back of the board to show the device is active
 pinMode(ledPin, OUTPUT);
 // It's an active low pin, so we write a LOW value to turn it on
 digitalWrite(ledPin, LOW);

 // CAUTION - We'll disable the brownout detection
 WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

 // Start serial connection for debugging purposes 
 Serial.begin(115200);

 Blynk.begin(auth, ssid, pass);
 // You can also specify server:
 //Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
 //Blynk.begin(auth, ssid, pass, IPAddress(), );

 // Setup a function to be called every second
 timer.setInterval(1000L, myTimerEvent);

  Serial.println("Starting up...");
 // initialize the LED pin as an output:
 pinMode(en, OUTPUT);
 pinMode(in1, OUTPUT);
 // initialize the pushbutton pin as an input:
 // Note: Pullup makes HIGH/LOW state backward from what we typically think.
 // So, HIGH = not pressed, LOW = pressed
 pinMode(pb, INPUT_PULLUP);  

 // Keep en pin on all the time:
 // If fanciness is needed, you can modulate this pin to control the speed of the fan.
 Serial.println("en pin set HIGH");
 digitalWrite(en, HIGH);
  Serial.println(__FILE__ __DATE__);
 
 //Pin definition for CAMERA_MODEL_AI_THINKER
 camera_config_t config;
 config.ledc_channel = LEDC_CHANNEL_0;
 config.ledc_timer = LEDC_TIMER_0;
 config.pin_d0 = 5;
 config.pin_d1 = 18;
 config.pin_d2 = 19;
 config.pin_d3 = 21;
 config.pin_d4 = 36;
 config.pin_d5 = 39;
 config.pin_d6 = 34;
 config.pin_d7 = 35;
 config.pin_xclk = 0;
 config.pin_pclk = 22;
 config.pin_vsync = 25;
 config.pin_href = 23;
 config.pin_sscb_sda = 26;
 config.pin_sscb_scl = 27;
 config.pin_pwdn = 32;
 config.pin_reset = -1;
 config.xclk_freq_hz = 20000000;
 config.pixel_format = PIXFORMAT_JPEG;

 // If the board has additional "pseudo RAM", we can create larger images
 if(psramFound()){
   config.frame_size = FRAMESIZE_UXGA; // UXGA=1600x1200. Alternative values: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
   config.jpeg_quality = 10;
   config.fb_count = 2;
 } else {
   config.frame_size = FRAMESIZE_SVGA;
   config.jpeg_quality = 12;
   config.fb_count = 1;
 }

 // Disable any hold on pin 4 that was placed before ESP32 went to sleep
 rtc_gpio_hold_dis(GPIO_NUM_4);
 // Use PWM channel 7 to control the white on-board LED (flash) connected to GPIO 4
 ledcSetup(7, 5000, 8);
 ledcAttachPin(4, 7);
 // Turn the LED on at specified power
 ledcWrite(7, flashPower);

 // Initialise the camera
 // Short pause helps to ensure the I2C interface has initialised properly before attempting to detect the camera
 delay(250);
 esp_err_t err = esp_camera_init(&config);
 if (err != ESP_OK) {
   Serial.printf("Camera init failed with error 0x%x", err);
   sleep();
 }

 // Image settings
 sensor_t * s = esp_camera_sensor_get();
 // Gain
 s->set_gain_ctrl(s, 1);      // Auto-Gain Control 0 = disable , 1 = enable
 s->set_agc_gain(s, 0);       // Manual Gain 0 to 30
 s->set_gainceiling(s, (gainceiling_t)0);  // 0 to 6
 // Exposure
 s->set_exposure_ctrl(s, 1);  // Auto-Exposure Control 0 = disable , 1 = enable
 s->set_aec_value(s, 300);    // Manual Exposure 0 to 1200
 // Exposure Correction
 s->set_aec2(s, 0);           // Automatic Exposure Correction 0 = disable , 1 = enable
 s->set_ae_level(s, 0);       // Manual Exposure Correction -2 to 2
 // White Balance
 s->set_awb_gain(s, 1);       // Auto White Balance 0 = disable , 1 = enable
 s->set_wb_mode(s, 0);        // White Balance Mode 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
 s->set_whitebal(s, 1);       // White Balance 0 = disable , 1 = enable
 s->set_bpc(s, 0);            // Black Pixel Correction 0 = disable , 1 = enable
 s->set_wpc(s, 1);            // White Pixel Correction 0 = disable , 1 = enable
 s->set_brightness(s, 0);     // Brightness -2 to 2
 s->set_contrast(s, 0);       // Contrast -2 to 2
 s->set_saturation(s, 0);     // Saturation -2 to 2
 s->set_special_effect(s, 0); // (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
 // Additional settings
 s->set_lenc(s, 1);           // Lens correction 0 = disable , 1 = enable
 s->set_hmirror(s, 0);        // Horizontal flip image 0 = disable , 1 = enable
 s->set_vflip(s, 0);          // Vertical flip image 0 = disable , 1 = enable
 s->set_colorbar(s, 0);       // Colour Testbar 0 = disable , 1 = enable
 s->set_raw_gma(s, 1);        // 0 = disable , 1 = enable
 s->set_dcw(s, 1);            // 0 = disable , 1 = enable
 
 // We want to take the picture as soon as possible after the sensor has been triggered, so we'll do that first, before
 // setting up the SD card, Wifi etc.
 // Initialise a framebuffer 
 camera_fb_t *fb = NULL;
 // But... we still need to give the camera a few seconds to adjust the auto-exposure before taking the picture
 // Otherwise you get a green-tinged image as per https://github.com/espressif/esp32-camera/issues/55
 // Two seconds should be enough
 delay(startupDelayMillis);
 // Take picture
 fb = esp_camera_fb_get();
 // Check it was captured ok  
 if(!fb) {
   Serial.println("Camera capture failed");
   sleep();
 }

 // Turn flash off after taking picture
 ledcWrite(7, 0);

 // Build up the string of the filename we'll use to save the file
 path = "/pic";

 // Following section creates filename based on increment value saved in EEPROM
 #ifdef USE_INCREMENTAL_FILE_NUMBERING
   // We only need 2 bytes of EEPROM to hold a single int value, but according to
   // https://arduino-esp8266.readthedocs.io/en/latest/libraries.html#eeprom
   // Minimum reserved size is 4 bytes, so we'll use that
   EEPROM.begin(4);
   // Read the value from the EEPROM cache
   EEPROM.get(0, pictureNumber);
   pictureNumber += 1;
   // Path where new picture will be saved in SD Card
   path += String(pictureNumber) + "_";
   // Update the EEPROM cache
   EEPROM.put(0, pictureNumber);
   // And then actually write the modified cache values back to EEPROM
   EEPROM.commit();
 #endif

 // Connect to Wi-Fi if required
 #if defined(SEND_EMAIL) || defined(USE_timestamp)
   WiFi.mode(WIFI_STA);
   WiFi.setHostname("");
   int connAttempts = 0;
       WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
   while (WiFi.status() != WL_CONNECTED && connAttempts < 10) {
     Serial.print(".");
     delay(500);
     connAttempts++;
      }
   if(WiFi.isConnected()){
           Serial.println("");
     Serial.println("WiFi connected.");
     Serial.println("IP address: ");
     Serial.println(WiFi.localIP());
     Serial.print(" Signal Level: ");
     Serial.println(WiFi.RSSI());
     Serial.println();
   }
   else {
     Serial.println(F("Failed to connect to Wi-Fi"));
     sleep();
   }
 #endif

 #ifdef USE_timestamp
   // Following section creates filename based on timestamp
   const long gmtOffset_sec = 0;
   const int daylightOffset_sec = 0;
   // Synchronise time from specified NTP server - e.g. "pool.ntp.org", "time.windows.com", "time.nist.gov"
   // From https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/Time/SimpleTime/SimpleTime.ino
   configTime(gmtOffset_sec, daylightOffset_sec, "pool.ntp.org");
   struct tm timeinfo;
   if(!getLocalTime(&timeinfo)){
     Serial.println("Failed to obtain time");
     sleep();
   }
   else {
     Serial.print("Current time is ");
     Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
     char timeStringBuff[50]; //50 chars should be enough
     strftime(timeStringBuff, sizeof(timeStringBuff), "%Y%m%d_%H%M%S", &timeinfo);
     path += (String)timeStringBuff;
   }
 #endif

 // Add the file extension
 path += ".jpg";
 
 // Next, we need to start the SD card
 Serial.println("Starting SD Card");
 if(!MailClient.sdBegin(14, 2, 15, 13)) {
   Serial.println("SD Card Mount Failed");
   sleep();
 }

 // Access the file system on the SD card
 fs::FS &fs = SD;
 // Attempt to save the image to the specified path
 File file = fs.open(path.c_str(), FILE_WRITE);
 if(!file){
   Serial.printf("Failed to save to path: %s\n", path.c_str());
   sleep();
 }
 else {
   file.write(fb->buf, fb->len); // payload (image), payload length
   Serial.printf("Saved file to path: %s\n", path.c_str());
 }
 file.close();

 // Now that we've written the file to SD card, we can release the framebuffer memory of the camera
 esp_camera_fb_return(fb);
 // And breathe for a moment...
 delay(1000);

 #ifdef SEND_EMAIL
   // Get verbose output of emailing process
   smtp.debug(1); 
   // Assign the callback function called after sending
   smtp.callback(smtpCallback);
   // Define the session config data which used to store the TCP session configuration
   ESP_Mail_Session session;
   session.server.host_name = SMTP_HOST;
   session.server.port = SMTP_PORT;
   session.login.email = AUTHOR_EMAIL;
   session.login.password = AUTHOR_PASSWORD;
   session.login.user_domain = "mydomain.net";

   // Define the SMTP_Message class variable to hold the config of the eMail itself
   SMTP_Message message;
   //message.enable.chunking = true; // Enable chunked data transfer for large messages if server supported
   message.sender.name = "ESP32-CAM";
   message.sender.email = AUTHOR_EMAIL;
   message.subject = "Motion Detected - ESP32-CAM";
   message.addRecipient("", " @gmail.com");
   //message.addRecipient("name2", "email2");
   //message.addCc("email3");
   //message.addBcc("email4");
   // Set the message content
   message.text.content = "";
   message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64;

   // Now define the attachment properties
   SMTP_Attachment att;
   att.descr.filename = "photo.jpg";
   att.descr.mime = "application/octet-stream"; //binary data
   att.file.path = path.c_str();
   att.file.storage_type = esp_mail_file_storage_type_sd;
   att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64;
   // Add attachment to the message
   message.addAttachment(att);

   // Connect to server with the session config
   Serial.println("Connecting to SMTP");
   if(!smtp.connect(&session)) {
     Serial.println("Couldn't connect");
     sleep();
   }
   // Start sending Email and close the session
   Serial.println("Sending Mail");
   if(!MailClient.sendMail(&smtp, &message)) {
     Serial.println("Error sending Email, " + smtp.errorReason());
     sleep();
   }
 #endif

 // Now that email is sent, we can turn the Wi-Fi off
 WiFi.disconnect(true);
 WiFi.mode(WIFI_OFF);

 // And go to bed until the next time we are triggered to take a photo
 sleep();
 

}

void loop()
{
 Blynk.run();
 timer.run();
 // You can inject your own code or combine it with other sketches.
 // Check other examples on how to communicate with Blynk. Remember
 // to avoid delay() function!
 // read the state of the pushbutton value:
 buttonState = digitalRead(pb);

 // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
 if (buttonState == LOW) {
   // Turn on the L293D's in1 pin (turn the fan on)
   digitalWrite(in1, HIGH);
 } else {
   // Turn off the L293D's in1 pin (turn the fan off)
   digitalWrite(in1, LOW);
 }
}

Difficult to say without seeing the text that is displayed in your serial monitor.

I’d start by doing what you’ve hinted at in the title of the topic and go back to basics by removing all of the unnecessary stuff. I’m particularly concerned by all the junk in your void setup, as it appears to be creating a WiFi instance for Blynk, then because #define SEND_EMAIL is true, it’s creating a separate WiFi instance to do a one-off send image routine.
As these boards can’t have tow concurrent WiFi connections, the image sending code will immediately terminate the Blynk connection.

The use of deep sleep doesn’t help debugging either.

Pete.

Thank you. I am breaking everything back down. I had compiled multiple sketches together that worked individually. I am having trouble combining them.