· 3 months ago · Jul 04, 2025, 09:50 AM
1/*
2 * ESP32-C3 Supermini Motion Detection with Deep Sleep and Telegram Notifications
3 *
4 * DESCRIPTION:
5 * This sketch implements a battery-efficient motion detection system using an ESP32-C3 Supermini
6 * and MPU6050 accelerometer/gyroscope sensor. The system uses deep sleep to conserve power and
7 * wakes up on motion detection or timer events.
8 *
9 * FUNCTIONALITY:
10 * - Detects motion using MPU6050's Wake-on-Motion (WoM) interrupt feature
11 * - Wakes from deep sleep when motion is detected on GPIO pin 4
12 * - Sends boot count notifications to Telegram bot
13 * - Implements power-saving sleep cycle:
14 * * First 2 wake-ups: Motion detection mode (wake on GPIO interrupt)
15 * * 3rd wake-up: Extended sleep mode (5 minutes timer-based sleep)
16 * * Cycle repeats indefinitely
17 *
18 * HARDWARE REQUIREMENTS:
19 * - ESP32-C3 Supermini board
20 * - MPU6050 sensor module
21 * - LED connected to GPIO 8 (for status indication)
22 * - I2C connections: SDA=GPIO6, SCL=GPIO7
23 * - Interrupt connection: MPU6050 INT pin to GPIO4
24 *
25 * POWER OPTIMIZATION:
26 * - Disables gyroscopes to reduce current consumption (~600µA)
27 * - Uses deep sleep between motion events
28 * - Brownout detector disabled for stable operation
29 * - GPIO wakeup configured for ESP32-C3 (differs from ESP32-WROOM/WROVER)
30 *
31 * NETWORK FEATURES:
32 * - Connects to WiFi network on wake-up
33 * - Sends notifications via Telegram bot
34 * - Includes timeout protection for WiFi connection attempts
35 * - Synchronizes time with NTP servers
36 *
37 * CONFIGURATION:
38 * - Motion sensitivity: Adjustable via setInterrupt() threshold (1-255)
39 * - Sleep duration: 5 minutes (LONG_SLEEP_TIME constant)
40 * - WiFi credentials and Telegram token need to be configured
41 * - Timezone set for Lisbon, Portugal (MYTZ constant)
42 *
43 * OPERATION CYCLE:
44 * 1. Boot and increment boot counter
45 * 2. Send status to Telegram
46 * 3. Configure motion detection (boots 1-2) or timer sleep (boot 3)
47 * 4. Enter deep sleep
48 * 5. Wake on motion or timer, repeat cycle
49 *
50 * AUTHOR: [Your name/organization]
51 * VERSION: 1.0
52 * DATE: [Current date]
53 *
54 * DEPENDENCIES:
55 * - AsyncTelegram2 library
56 * - WiFiClientSecure library
57 * - Wire library (I2C)
58 *
59 * NOTE: This code is specifically configured for ESP32-C3 Supermini.
60 * GPIO wakeup configuration differs from ESP32-WROOM/WROVER modules.
61 */
62
63 /*
64* https://www.jarzebski.pl/arduino/czujniki-i-sensory/3-osiowy-zyroskop-i-akcelerometr-mpu6050.html
65* https://wolles-elektronikkiste.de/mpu6050-beschleunigungssensor-und-gyroskop
66* https://arduino.stackexchange.com/questions/48424/how-to-generate-hardware-interrupt-in-mpu6050-to-wakeup-arduino-from-sleep-mode/48430#48430
67* https://github.com/chris-ault/MotionDetector
68* https://os.mbed.com/teams/Unina_corse/code/Nucleo_rtos_basic_copyProva_Quirino/docs/tip/MPU6050_8h_source.html
69*/
70
71//#include <WiFi.h>
72
73#include "soc/soc.h" // Brownout error fix
74#include "soc/rtc_cntl_reg.h" // Brownout error fix
75
76#include "driver/rtc_io.h"
77
78#include <WiFiClientSecure.h>
79WiFiClientSecure client;
80#include <AsyncTelegram2.h> // https://github.com/cotestatnt/AsyncTelegram2
81AsyncTelegram2 myBot(client);
82const char * network = "xxxxxxxx"; // SSID WiFi network
83const char * pass = "xxxxxxxx"; // Password WiFi network
84const char * token = "xxxxxxxx"; // (superC3mpu6050) Telegram token
85int64_t userid = xxxxxxxx;
86//int64_t userid = -1001336045228; // Canal Oeste
87#define MYTZ "WET0WEST,M3.5.0/1,M10.5.0/2" // POSIX timezone string for Lisbon
88
89#include "Wire.h"
90
91#define MPU6050_ADDR 0x68 // Alternatively set AD0 to HIGH --> Address = 0x69
92#define MPU6050_ACCEL_CONFIG 0x1C // Accelerometer Configuration Register
93#define MPU6050_PWR_MGT_1 0x6B // Power Management 1 Register
94#define MPU6050_INT_PIN_CFG 0x37 // Interrupt Pin / Bypass Enable Configuration Register
95#define MPU6050_INT_ENABLE 0x38 // Interrupt Enable Register
96#define MPU6050_LATCH_INT_EN 0x05 // Latch Enable Bit for Interrupt
97#define MPU6050_ACTL 0x07 // Active-Low Enable Bit
98#define MPU6050_WOM_EN 0x06 // Wake on Motion Enable bit
99#define MPU6050_WOM_THR 0x1F // Wake on Motion Threshold Register
100#define MPU6050_MOT_DUR 0x20 // Motion Detection Duration Register
101#define MPU6050_ACCEL_INTEL_CTRL 0x69 // Accelaration Interrupt Control Register
102#define MPU6050_SIGNAL_PATH_RESET 0x68 // Signal Path Reset Register
103
104//byte interruptPin = 4;
105byte ledPin = 8; // ESP32-C3 Supermini
106volatile bool accEvent = false;
107
108#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
109#define LONG_SLEEP_TIME 5 * 60 /* Time ESP32 will sleep (in seconds) if bootCount = 4 */
110#define WAKEUP_PIN 4 /* GPIO pin used for external wakeup */
111
112RTC_DATA_ATTR int bootCount = 0;
113
114/*
115Method to print the reason by which ESP32
116has been awaken from sleep
117*/
118void print_wakeup_reason(){
119 esp_sleep_wakeup_cause_t wakeup_reason;
120
121 wakeup_reason = esp_sleep_get_wakeup_cause();
122
123 switch(wakeup_reason)
124 {
125 case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
126 case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
127 case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
128 case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
129 case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
130 default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
131 }
132}
133
134void blinkLED(int numBlinks, int blinkInterval) {
135 for (int i = 0; i < numBlinks; i++) {
136 digitalWrite(ledPin, LOW); // Turn on the LED // LOW for ESP32C3
137 delay(blinkInterval);
138 digitalWrite(ledPin, HIGH); // Turn off the LED
139 delay(blinkInterval);
140 }
141}
142
143void sendToTelegram() {
144 Serial.print("Connecting to ");
145 Serial.println(network);
146
147 WiFi.begin(network, pass);
148
149 int wifi_start_counter = 0;
150 while (WiFi.status() != WL_CONNECTED) {
151 Serial.print(".");
152 wifi_start_counter++;
153 if (wifi_start_counter >= 10) {
154 return; // exit sendToTelegram() completely
155 }
156 delay(1000);
157 }
158
159 Serial.println("");
160 Serial.println("WiFi connected! Sending message to Telegram...");
161
162 // Sync time with NTP
163 configTzTime(MYTZ, "time.google.com", "time.windows.com", "pool.ntp.org");
164 client.setCACert(telegram_cert);
165 // Set the Telegram bot properies
166 myBot.setUpdateTime(2000);
167 myBot.setTelegramToken(token);
168
169 // Check if all things are ok
170 Serial.print("\nTest Telegram connection... ");
171 myBot.begin() ? Serial.println("OK") : Serial.println("NOK");
172
173 //// Send a welcome message to user when ready
174 //char welcome_msg[64];
175 //snprintf(welcome_msg, 64, "BOT @%s online.\nTry with /takePhoto command.", myBot.getBotName());
176 //myBot.sendTo(userid, welcome_msg);
177
178 char bootCountMsg[64];
179 snprintf(bootCountMsg, 64, "Boot count is: %d", bootCount);
180 myBot.sendTo(userid, bootCountMsg);
181
182 //const char* message = "/clip";
183 //// Send the message to the Telegram channel
184 //bool sent = myBot.sendTo(userid, message);
185}
186
187void setup() {
188 WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector
189
190 Wire.begin(6, 7);
191
192 Serial.begin(115200);Serial.println("");
193
194 pinMode(ledPin, OUTPUT);
195 blinkLED(3, 100); // Blink the LED 3 times with a 100ms interval
196
197 //Print the wakeup reason for ESP32
198 //print_wakeup_reason();
199
200 //Increment boot number and print it every reboot
201 ++bootCount;
202 Serial.println("Boot number: " + String(bootCount));Serial.println("");
203
204 sendToTelegram();
205
206 // Sleep for an amount of time after more than 3 external interrupts
207 if (bootCount >= 1 && bootCount <= 2) {
208
209 writeRegister(MPU6050_PWR_MGT_1, 0);
210 setInterrupt(1); // set Wake on Motion Interrupt / Sensitivity; 1(highest sensitivity) - 255
211 delay(100);
212
213 // Configure external wakeup on WAKEUP_PIN when bootCount is between 1 and 3
214 // For ESP32 C3 Supermini
215 esp_deep_sleep_enable_gpio_wakeup(1 << WAKEUP_PIN, ESP_GPIO_WAKEUP_GPIO_HIGH);
216
217 /*// For ESP32-WROOM and ESP32-WROVER
218 esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 1); // 1 = High, 0 = Low
219 rtc_gpio_pullup_dis(WAKEUP_PIN);
220 rtc_gpio_pulldown_en(WAKEUP_PIN);*/
221
222 Serial.println("Setup ESP32 to wake up on WAKEUP_PIN trigger");
223
224 } else if (bootCount == 3) {
225
226 // Reset bootCount and set timer wakeup for 2*30 seconds
227 bootCount = 0;
228 esp_sleep_enable_timer_wakeup(LONG_SLEEP_TIME * uS_TO_S_FACTOR);
229 Serial.println("Setup ESP32 to sleep for 10*60 seconds and reset bootCount");
230
231 }
232
233 // Go to sleep now
234 blinkLED(2, 1000); // Blink the LED 2 times with a 100ms interval
235 Serial.println("");
236 Serial.println("Going to sleep now");
237 Serial.flush();
238 esp_deep_sleep_start();
239 Serial.println("This will never be printed");
240
241}
242
243void loop() {
244
245// if(accEvent){
246// digitalWrite(ledPin, HIGH);
247// delay(1000);
248// digitalWrite(ledPin, LOW);
249// accEvent = false;
250// attachInterrupt(digitalPinToInterrupt(interruptPin), motion, RISING);
251// }
252
253}
254
255void setInterrupt(byte threshold){
256//writeRegister(MPU6050_SIGNAL_PATH_RESET, 0b00000111); // not(?) needed
257//writeRegister(MPU6050_INT_PIN_CFG, 1<<MPU6050_ACTL); // 1<<MPU6050_LATCH_INT_EN
258 writeRegister(MPU6050_ACCEL_CONFIG, 0b00000001);
259 writeRegister(MPU6050_WOM_THR, threshold);
260 writeRegister(MPU6050_MOT_DUR, 0b00000001); // set duration (LSB = 1 ms)
261//writeRegister(MPU6050_ACCEL_INTEL_CTRL, 0x15); // not needed (?)
262 writeRegister(MPU6050_INT_ENABLE, 1<<MPU6050_WOM_EN);
263
264 writeRegister(0x6C, 7); // Disable Gyros to lower quiescent current to close to 600uA
265 //writeRegister(0x6C, 0); // Re-enable Gyros
266}
267
268void writeRegister(uint16_t reg, byte value){
269 Wire.beginTransmission(MPU6050_ADDR);
270 Wire.write(reg);
271 Wire.write(value);
272 Wire.endTransmission();
273}
274
275//void motion(){
276// accEvent = true;
277// detachInterrupt(digitalPinToInterrupt(interruptPin));
278//}
279