// Santi&be Youtube
// https://www.youtube.com/watch?v=I-HbdRWwMVY
#include <IotWebConf.h>
const char thingName[] = "PUY IOT FIRST";
const char wifiInitialApPassword[] = "111111111";
#include <TridentTD_LineNotify.h>
String LineText;
String string1 = "CAM System เริ่มทำงาน MCU RESET ครั้งที่ ";
#define Line_Token_1 45
#define CONFIG_VERSION "dem2"
// -- Callback method declarations.
void configSaved();
boolean formValidator();
DNSServer dnsServer;
WebServer server(80);
char LINE_TOKEN[Line_Token_1];
IotWebConf iotWebConf(thingName, &dnsServer, &server, wifiInitialApPassword, CONFIG_VERSION);
IotWebConfParameter Token_Line = IotWebConfParameter("Line_token", "Line_token", LINE_TOKEN, Line_Token_1);
const char* host = "www.google.co.th";
int Internet;
String SSID_NAME;
String PASSWORD_NAME;
String IP;
String Sub_M;
String Gate_way;
String SSID_AP;
String PASSWORD_AP;
#include <EEPROM.h>
int MCU_Reset = 0;
int count_MCU_Rset;
int Amount_Mcu_Reset;
int reset_MCU;
//#include <WiFi.h>
#include "esp_camera.h"
#include "esp_system.h"
hw_timer_t *timer = NULL;
void IRAM_ATTR resetModule() {
ets_printf("reboot\n");
esp_restart();
}
// Pin definition for CAMERA_MODEL_AI_THINKER กำหนดการต่อขาให้กับกล้อง รุ่น 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
const int Led_Flash = 4; //ไฟ LED ESP32 CAM GPIO4
const int Led_run = 13;//ใช้ GPIO13 จะเป็นไฟกระพริบแสดงให้ผู้ใช้ทราบว่าขณะนี้กล้องพร้อมทำงานถ้าไฟดับแสดงว่ากล้องไม่พร้อมทำงาน
int Temp_High = 12;//จ่ายไฟ 3.3 หรือ 5Vdc เข้าที่ขา GPIO12 เพื่อทำให้ให้เกิดการ Copture ภาพ + ไฟ Led flash ทำงาน
int Temp_Normal = 2; //จ่ายไฟ 3.3 หรือ 5Vdc เข้าที่ขา GPIO15 เพื่อทำให้ให้เกิดการ Copture ภาพ + ไฟ Led flash ทำงาน
boolean startTimer = false;//ให้การนับเวลามีค่าเป็น เท็จ ก่อน
unsigned long time_now = 0;
int time_capture = 0; //ให้เวลาการ จับภาพ = 0
//------------------------------------------------------------------------------------------------------------
TaskHandle_t Task1;//IoTCONF
TaskHandle_t Task2;//Internet
void setup() {
Serial.begin(115200);
EEPROM.begin(1024);
while (!Serial) {
;
}
pinMode(Led_Flash, OUTPUT);
pinMode(Led_run, OUTPUT);
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
LINE.setToken(LINE_TOKEN);
//แบ่งค่านี้โดย 80 (ใช้ 80 เป็นค่า prescaler)จะได้รับสัญญาณที่มีความถี่ 1 MHz ที่จะเพิ่มเคาน์เตอร์จับเวลา1 000 000 ครั้งต่อวินาที
//แต่ก่อนที่จะเปิดใช้งานตัวจับเวลาเราจำเป็นต้องเชื่อมโยงกับฟังก์ชันการจัดการซึ่งจะดำเนินการเมื่อมีการสร้างการขัดจังหวะ
//สิ่งนี้ทำได้ด้วยการเรียกใช้ฟังก์ชันtimerAttachInterrupt
timer = timerBegin(0, 80, true); //timer 0, div 80Mhz
timerAttachInterrupt(timer, &resetModule, true);
timerAlarmWrite(timer, 20000000, false); //set time in us 15s ตั้งค่าเวลาของเราเป็น 15 วินาที
timerAlarmEnable(timer); //enable interrupt สั่งให้ Interrupt ทำงาน
camera_config_t config; //ตั้งค่า กล้อง มี code มาให้แล้ว
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;//เวลาเป็น 15 วินาที
config.pixel_format = PIXFORMAT_JPEG; //รูปแบบภาพเป็น JPEG
// เริ่มต้นด้วยคุณสมบัติที่สูงเพื่อจัดสรรบัฟเฟอร์ขนาดใหญ่ไว้ล่วงหน้า
if (psramFound()) { //ถ้ากล้องมีภาพเข้ามาจะกำหนดให้แสดงภาพตาม Resolutotion ที่กำหนดไว้
// FRAMESIZE_ +
//QQVGA/160x120 //QQVGA2/128x160 //QCIF/176x144 //HQVGA/240x176
//QVGA/320x240 //CIF/400x296 //VGA/640x480 //SVGA/800x600//XGA/1024x768
//SXGA/1280x1024 //UXGA/1600x1200 //QXGA/2048*1536
config.frame_size = FRAMESIZE_VGA; //Resolutotion แบบ VGA // 640x480
config.jpeg_quality = 10;//คุณภาพของไฟล์ภาพ =10
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_QQVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Init Camera // เริ่มต้นกล้อง
esp_err_t err = esp_camera_init(&config);//
if (err != ESP_OK) {
//การเริ่มต้นกล้องล้มเหลวโดยมีข้อผิดพลาด
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
}// จบ void setup
//-------------------------------------------------------------------------------------
//Task1code IoTCONF
void Task1code( void * pvParameters ) {
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
iotWebConf.addParameter(&Token_Line);
iotWebConf.setConfigSavedCallback(&configSaved);
iotWebConf.setFormValidator(&formValidator);
iotWebConf.getApTimeoutParameter()->visible = true;
// -- Initializing the configuration.
iotWebConf.init();
// -- Set up required URL handlers on the web server.
server.on("/", handleRoot);
server.on("/config", [] { iotWebConf.handleConfig(); });
server.onNotFound([]() {
iotWebConf.handleNotFound();
iotWebConf.handleCaptivePortal();
});
Serial.println("Ready");
Serial.println("Finish");
SSID_NAME = iotWebConf.getWifiSsidParameter()->valueBuffer;
PASSWORD_NAME = iotWebConf.getWifiPasswordParameter()->valueBuffer;
SSID_AP = iotWebConf.getThingNameParameter()->valueBuffer;
PASSWORD_AP = iotWebConf.getApPasswordParameter()->valueBuffer;
count_MCU_Rset = EEPROM.get(150, Amount_Mcu_Reset);
Serial.print("count_MCU_Rset = ");
Serial.println(count_MCU_Rset);
reset_MCU = count_MCU_Rset + 1;
Serial.print("reset_MCU = ");
Serial.println(reset_MCU);
Amount_Mcu_Reset = EEPROM.put(101, reset_MCU);
EEPROM.commit();
Serial.print("Amount_Mcu_Reset = ");
Serial.println(Amount_Mcu_Reset);
count_MCU_Rset = EEPROM.put(150, Amount_Mcu_Reset);
EEPROM.commit();
Serial.print("count_MCU_Rset = ");
Serial.println(count_MCU_Rset);
for (;;) {
if (Internet == 1 && MCU_Reset == 0) {
Serial.println("MCU_Reset = 1");
LineText = string1 + count_MCU_Rset;
LINE.notify(LineText);
delay (200);
MCU_Reset = 1;
}
iotWebConf.doLoop();
delay(500);
}
}
//Task2code INTERNET
void Task2code( void * pvParameters ) {
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for (;;) {
WiFiClient client;
if (client.connect(host, 80))
{
// Serial.println("connected");
Internet = 1;
client.stop();
delay(100);
}
else {
//Serial.println("connection failed!");
Internet = 0;
client.stop();
}
delay(400);
}
}
void loop() {
timerWrite(timer, 0); //reset timer (feed watchdog)สั่ง reset เวลา เพื่อป้องกัน เวลา error จน watch เจอแล้ว Reboot ใหม่ หรือค้าง
long tme = millis();
//----------- กรณี temperature High------------------------------------------------------------------------
if (digitalRead(Temp_High) == 1 && startTimer != true) { //มีไฟ 3.3/5 Vdc เข้ามาที่ขานี้ ให้ตัวแปรนี้มีค่า = 1
Camera_capture();//ไป ฟังก์ชั่น Camera_capture เพื่อทำการจับภาพ
//Serial.println("OK");
startTimer = true; //ให้คำสั่งนี้ ให้คำสั่งเริ่มต้นนับเวลาทำงาน
} else if (digitalRead(Temp_High) == 0) { //ถ้าไม่มีไฟ 3.3/5 Vdc เข้ามา ให้ตัวแปรนี้มีค่า = 0
startTimer = false;
time_capture = 0;
}
//--------------------------------------------------------------------------------------------------------
//----------- กรณี temperature Normal-----------------------------------------------------------------------
if (digitalRead(Temp_Normal ) == 1 && startTimer != true) { //มีไฟ 3.3/5 Vdc เข้ามาที่ขานี้ ให้ตัวแปรนี้มีค่า = 1
Camera_capture1();//ไป ฟังก์ชั่น Camera_capture เพื่อทำการจับภาพ
//Serial.println("OK");
startTimer = true; //ให้คำสั่งนี้ ให้คำสั่งเริ่มต้นนับเวลาทำงาน
} else if (digitalRead(Temp_Normal ) == 0) { //ถ้าไม่มีไฟ 3.3/5 Vdc เข้ามา ให้ตัวแปรนี้มีค่า = 0
startTimer = false;
time_capture = 0;
}
//--------------------------------------------------------------------------------------------------------
//-------- ไฟแสดงสถานะความพร้อมของกล้อง ถ้ากระพริบแสดงว่ากล้องพร้อมทำงาน --------------------
if (millis() > time_now + 1000) {
time_now = millis();
digitalWrite(Led_run, HIGH);
delay(20);
digitalWrite(Led_run, LOW);
}
//-------------------------------------------------------------------------------------
//----------- กรณี temperature High----
tme = millis() - tme;
if (digitalRead(Temp_High) == 1) {
if (++time_capture > 60) {// กรณีที่ขานี้มีค่าเท่ากับ 1 แต่ใช้เวลาเกินกว่า 60 จะให้ Time Capture = 0 และเข้าไป Capture ภาพใหม่ในฟังก์ชั่น Camera Capture
time_capture = 0;
Camera_capture();
Serial.println("Over Time");//และมีข้อความบอกว่าเวลาเกินกำหนด
}
}
//----------- กรณี temperature Normal----
if (digitalRead(Temp_Normal ) == 1) {
if (++time_capture > 60) {// กรณีที่ขานี้มีค่าเท่ากับ 1 แต่ใช้เวลาเกินกว่า 60 จะให้ Time Capture = 0 และเข้าไป Capture ภาพใหม่ในฟังก์ชั่น Camera Capture
time_capture = 0;
Camera_capture1();
Serial.println("Over Time");//และมีข้อความบอกว่าเวลาเกินกำหนด
}
}
Serial.println(digitalRead(Temp_High));
Serial.println(digitalRead(Temp_Normal));
iotWebConf.doLoop();
delay(200);
}//จบ void loop
//-------------------------------------------------------------------------------------
//----------- กรณี temperature High----
void Camera_capture() {//มีไฟ 3.3/5 Vdc เข้ามาที่ขานี้ โปรแกรมสั่งให้มาทำงานที่นี่
//สั่งชุดไฟ LED ของ ESP32 CAM ทำงานแบบกระพริบเหมือนไฟ Flash
digitalWrite(Led_Flash, HIGH);
delay(100);
digitalWrite(Led_Flash, LOW);
delay(100);
digitalWrite(Led_Flash, HIGH);//สั่งไฟติดค้างจนกว่า จนกว่าการถ่ายภาพจะเสร็จ
camera_fb_t * fb = NULL;
delay(200);
// Take Picture with Camera ถ่ายภาพด้วยกล้อง
//รับเฟรม
fb = esp_camera_fb_get();
// ถ้าจับภาพไม่สำเร็จ
if (!fb) {
Serial.println("Camera capture failed");
ESP.restart();
delay(2000);
return;
}
digitalWrite(Led_Flash, LOW);////สั่งไฟดับ
Send_line(fb->buf, fb->len);//ส่งค่าเข้า Line notify โดยนำข้อมูล Buffer ของ fb ซึ่งเป็นข้อมูลภาพและความยาวข้อมูล
esp_camera_fb_return(fb);
// Serial.println("Going to sleep now");
// esp_deep_sleep_start(); //สั่ง esp ไม่ให้ Deep sleep คำสั่งนี้ไม่ใช้ แสดงว่าให้มันทำงานตลอดเวลา
// Serial.println("This will never be printed");
}//End void Camera_capture //กลับไปทำคำสั่งต่อที่ void loop
//-------------------------------------------------------------------------------------
void Send_line(uint8_t *image_data, size_t image_size) {// โค้ดการส่ง LINE โดยการแปลงค่า fb->buf, fb->len เป็น uint8_t *image_data, size_t image_size
LINE.notifyPicture("A", image_data, image_size);
Serial.println("เข้า loop A");
}
//-------------------------------------------------------------------------------------
//----------- กรณี temperature Normal----
void Camera_capture1() {//มีไฟ 3.3/5 Vdc เข้ามาที่ขานี้ โปรแกรมสั่งให้มาทำงานที่นี่
//สั่งชุดไฟ LED ของ ESP32 CAM ทำงานแบบกระพริบเหมือนไฟ Flash
digitalWrite(Led_Flash, HIGH);
delay(100);
digitalWrite(Led_Flash, LOW);
delay(100);
digitalWrite(Led_Flash, HIGH);//สั่งไฟติดค้างจนกว่า จนกว่าการถ่ายภาพจะเสร็จ
camera_fb_t * fb = NULL;
delay(200);
// Take Picture with Camera ถ่ายภาพด้วยกล้อง
//รับเฟรม
fb = esp_camera_fb_get();
// ถ้าจับภาพไม่สำเร็จ
if (!fb) {
Serial.println("Camera capture failed");
return;
}
digitalWrite(Led_Flash, LOW);////สั่งไฟดับ
Send_line1(fb->buf, fb->len);//ส่งค่าเข้า Line notify โดยนำข้อมูล Buffer ของ fb ซึ่งเป็นข้อมูลภาพและความยาวข้อมูล
esp_camera_fb_return(fb);
// Serial.println("Going to sleep now");
// esp_deep_sleep_start(); //สั่ง esp ไม่ให้ Deep sleep คำสั่งนี้ไม่ใช้ แสดงว่าให้มันทำงานตลอดเวลา
// Serial.println("This will never be printed");
}//End void Camera_capture //กลับไปทำคำสั่งต่อที่ void loop
//-------------------------------------------------------------------------------------
void Send_line1(uint8_t *image_data, size_t image_size) {// โค้ดการส่ง LINE โดยการแปลงค่า fb->buf, fb->len เป็น uint8_t *image_data, size_t image_size
LINE.notifyPicture("B", image_data, image_size);
Serial.println("เข้า loop B");
}
//-------------------------------------------------------------------------------------
/**
Handle web requests to "/" path.
*/
void handleRoot()
{
Serial.println(" NOW! CONFIG AP ");
// -- Let IotWebConf test and handle captive portal requests.
if (iotWebConf.handleCaptivePortal())
{
// -- Captive portal request were already served.
return;
}
String s = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/>";
s += "<title>IotWebConf 03 Custom Parameters</title></head><body>ESP32 CAM";
s += "<ul>";
s += "<li>String param value1: ";
s += LINE_TOKEN;
s += "</ul>";
s += "Go to <a href='config'>configure page</a> to change values.";
s += "</body></html>\n";
server.send(200, "text/html", s);
}
void configSaved()
{
Serial.println("Configuration was updated.");
Serial.println(" SAVE! CONFIG AP");
Serial.println(" DISCON. CONFIG!");
SSID_AP = iotWebConf.getThingNameParameter()->valueBuffer;
PASSWORD_AP = iotWebConf.getApPasswordParameter()->valueBuffer;
}
boolean formValidator()
{
Serial.println("Validating form.");
boolean valid = true;
int l = server.arg(Token_Line.getId()).length();
if (l < 3)
{
Token_Line.errorMessage = "Please provide at least 3 characters for this test!";
valid = false;
}
return valid;
}
จุดที่น่าสนใจคือ การย้าย core config AP ไปยัง Core 0 เพื่อให้ทำงานแยกส่วนกัน ดดยไม่มีปัญหา