· 6 months ago · Mar 25, 2025, 11:00 PM
1/*
2 This sketch configures an ESP32 module to monitor motion using an ADXL345 accelerometer,
3 sends boot count information via Telegram, and manages power consumption through deep sleep modes.
4
5 Key functionalities:
6 - Monitors activity on the Y-axis using the ADXL345 accelerometer, with configurable thresholds.
7 - Connects to WiFi and sends updates via Telegram on each boot, including the boot count.
8 - If no significant activity is detected within a few seconds after ADXL345 setup begins, the ESP32 enters deep sleep mode.
9 - Deep sleep is managed based on the boot count:
10 - Boots 1-2: The ESP32 can wake up on external activity (GPIO14).
11 - Boot 3: The ESP32 sleeps for an extended period (2 minutes), then resets the boot count.
12 - Includes brownout protection and visual feedback (LED) on boot and before entering deep sleep.
13
14 Libraries used:
15 - ADXL345_WE: For controlling and configuring the ADXL345 accelerometer.
16 https://wolles-elektronikkiste.de/en/adxl345-the-universal-accelerometer-part-1 (English)
17 https://github.com/wollewald/ADXL345_WE
18 - AsyncTelegram2: For sending messages through Telegram.
19 https://github.com/cotestatnt/AsyncTelegram2
20
21 Configure WiFi and Telegram token parameters.
22
23 This project is being used to detect rain events. The bottom of a plastic container was cut and the opening, now facing up, covered with cellophane;
24 the adxl345 was glued to the cellophane, which bounces with the drops of rain, triggering the interrupt.
25*/
26
27#include "driver/rtc_io.h"
28
29#include<Wire.h>
30#include<ADXL345_WE.h>
31#define ADXL345_I2CADDR 0x53 // 0x1D if SDO = HIGH
32const int int2Pin = 14;
33volatile bool in_activity = false; // in_activity: either activity or inactivity interrupt occured
34
35#include "soc/soc.h" // Brownout error fix
36#include "soc/rtc_cntl_reg.h" // Brownout error fix
37
38#include <WiFiClientSecure.h>
39WiFiClientSecure client;
40
41#include <AsyncTelegram2.h> // https://github.com/cotestatnt/AsyncTelegram2
42AsyncTelegram2 myBot(client);
43const char * network = "696969"; // SSID WiFi network
44const char * pass = "696969"; // Password WiFi network
45const char * token = "696969"; // (meteoeste) Telegram token
46int64_t userid = 696969;
47//int64_t userid = -696969; //
48#define MYTZ "WET0WEST,M3.5.0/1,M10.5.0/2" // POSIX timezone string for Lisbon
49
50/* There are several ways to create your ADXL345 object:
51 * ADXL345_WE myAcc = ADXL345_WE() -> uses Wire / I2C Address = 0x53
52 * ADXL345_WE myAcc = ADXL345_WE(ADXL345_I2CADDR) -> uses Wire / ADXL345_I2CADDR
53 * ADXL345_WE myAcc = ADXL345_WE(&wire2) -> uses the TwoWire object wire2 / ADXL345_I2CADDR
54 * ADXL345_WE myAcc = ADXL345_WE(&wire2, ADXL345_I2CADDR) -> all together
55 */
56ADXL345_WE myAcc = ADXL345_WE(ADXL345_I2CADDR);
57
58int activity_counter = 0;
59bool goToSleep = false;
60
61#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
62#define LONG_SLEEP_TIME 10 * 60 /* Time ESP32 will sleep (in seconds) if bootCount = 4 */
63#define WAKEUP_GPIO GPIO_NUM_14 /* GPIO pin used for external wakeup */
64
65RTC_DATA_ATTR int bootCount = 0;
66
67byte ledPin = 13; // ESP32-room-32
68
69portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
70
71void IRAM_ATTR in_activityISR() {
72
73 portENTER_CRITICAL(&mux);
74 in_activity = true;
75 portEXIT_CRITICAL(&mux);
76
77}
78
79void sendToTelegram() {
80
81 Serial.print("Connecting to ");
82 Serial.println(network);
83
84 WiFi.begin(network, pass);
85
86 int wifi_start_counter = 0;
87 while (WiFi.status() != WL_CONNECTED) {
88 Serial.print(".");
89 wifi_start_counter++;
90 if (wifi_start_counter >= 10) {
91 //enterDeepSleep();
92 break;
93 }
94 delay(1000);
95 }
96
97 Serial.println("");
98 Serial.println("WiFi connected!");
99
100 // Sync time with NTP
101 configTzTime(MYTZ, "time.google.com", "time.windows.com", "pool.ntp.org");
102 client.setCACert(telegram_cert);
103 // Set the Telegram bot properies
104 myBot.setUpdateTime(2000);
105 myBot.setTelegramToken(token);
106
107 // Check if all things are ok
108 Serial.print("\nTest Telegram connection... ");
109 myBot.begin() ? Serial.println("OK") : Serial.println("NOK");
110
111 // Send a welcome message to user when ready
112 // char welcome_msg[64];
113 // snprintf(welcome_msg, 64, "BOT @%s online.\nTry with /takePhoto command.", myBot.getBotName());
114 // myBot.sendTo(userid, welcome_msg);
115
116 char bootCountMsg[64];
117 snprintf(bootCountMsg, 64, "Boot count is: %d", bootCount);
118 myBot.sendTo(userid, bootCountMsg);
119
120 // const char* message = "/clip";
121 // // Send the message to the Telegram channel
122 // bool sent = myBot.sendTo(userid, message);
123
124}
125
126void blinkLED(int numBlinks, int blinkInterval) {
127 for (int i = 0; i < numBlinks; i++) {
128 digitalWrite(ledPin, HIGH); // Turn on the LED
129 delay(blinkInterval);
130 digitalWrite(ledPin, LOW); // Turn off the LED
131 delay(blinkInterval);
132 }
133}
134
135void setup() {
136 WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector
137
138 Wire.begin(33, 32);
139 Serial.begin(9600); Serial.println();
140 pinMode(int2Pin, INPUT);
141 pinMode(ledPin, OUTPUT);
142
143 blinkLED(3, 200); // Blink the LED 3 times with a 200ms interval to indicate wake up
144
145 Serial.println("ADXL345_Sketch - Activity and Inactivity Interrupts");
146 Serial.println();
147
148 //Increment boot number and print it every reboot
149 ++bootCount;
150 Serial.println("Boot number: " + String(bootCount));
151 Serial.println();
152
153 sendToTelegram();
154
155 if (!myAcc.init()) {
156 Serial.println("ADXL345 not connected!");
157 }
158
159/* Choose the data rate Hz
160 ADXL345_DATA_RATE_3200 3200
161 ADXL345_DATA_RATE_1600 1600
162 ADXL345_DATA_RATE_800 800
163 ADXL345_DATA_RATE_400 400
164 ADXL345_DATA_RATE_200 200
165 ADXL345_DATA_RATE_100 100
166 ADXL345_DATA_RATE_50 50
167 ADXL345_DATA_RATE_25 25
168 ADXL345_DATA_RATE_12_5 12.5
169 ADXL345_DATA_RATE_6_25 6.25
170 ADXL345_DATA_RATE_3_13 3.13
171 ADXL345_DATA_RATE_1_56 1.56
172 ADXL345_DATA_RATE_0_78 0.78
173 ADXL345_DATA_RATE_0_39 0.39
174 ADXL345_DATA_RATE_0_20 0.20
175 ADXL345_DATA_RATE_0_10 0.10
176*/
177 myAcc.setDataRate(ADXL345_DATA_RATE_200);
178 Serial.print("Data rate: ");
179 Serial.print(myAcc.getDataRateAsString());
180
181/* Choose the measurement range
182 ADXL345_RANGE_16G 16g
183 ADXL345_RANGE_8G 8g
184 ADXL345_RANGE_4G 4g
185 ADXL345_RANGE_2G 2g
186*/
187 myAcc.setRange(ADXL345_RANGE_2G);
188 Serial.print(" / g-Range: ");
189 Serial.println(myAcc.getRangeAsString());
190 Serial.println();
191
192 attachInterrupt(digitalPinToInterrupt(int2Pin), in_activityISR, RISING);
193
194/* Three parameters have to be set for activity:
195 1. DC / AC Mode:
196 ADXL345_DC_MODE - Threshold is the defined one (parameter 3)
197 ADXL345_AC_MODE - Threshold = starting acceleration + defined threshold
198 2. Axes, that are considered:
199 ADXL345_000 - no axis (which makes no sense)
200 ADXL345_00Z - z
201 ADXL345_0Y0 - y
202 ADXL345_0YZ - y,z
203 ADXL345_X00 - x
204 ADXL345_X0Z - x,z
205 ADXL345_XY0 - x,y
206 ADXL345_XYZ - all axes
207 3. Threshold in g
208*/
209 myAcc.setActivityParameters(ADXL345_AC_MODE, ADXL345_0Y0, 0.1);
210
211/* You can choose the following interrupts:
212 Variable name: Triggered, if:
213 ADXL345_OVERRUN - new data replaces unread data
214 ADXL345_WATERMARK - the number of samples in FIFO equals the number defined in FIFO_CTL
215 ADXL345_FREEFALL - acceleration values of all axes are below the threshold defined in THRESH_FF
216 ADXL345_INACTIVITY - acc. value of all included axes are < THRESH_INACT for period > TIME_INACT
217 ADXL345_ACTIVITY - acc. value of included axes are > THRESH_ACT
218 ADXL345_DOUBLE_TAP - double tap detected on one incl. axis and various defined conditions are met
219 ADXL345_SINGLE_TAP - single tap detected on one incl. axis and various defined conditions are met
220 ADXL345_DATA_READY - new data available
221
222 Assign the interrupts to INT1 (INT_PIN_1) or INT2 (INT_PIN_2). Data ready, watermark and overrun are
223 always enabled. You can only change the assignment of these which is INT1 by default.
224
225 You can delete interrupts with deleteInterrupt(type);
226*/
227 myAcc.setInterrupt(ADXL345_ACTIVITY, INT_PIN_2);
228 myAcc.readAndClearInterrupts();
229}
230
231/* In the main loop some checks are done:
232 getActTapStatus() returns which axes are responsible for activity interrupt as byte (code in library)
233 getActTapStatusAsString() returns the axes that caused the interrupt as string
234 readAndClearInterrupts(); returns the interrupt type as byte (code in library)
235 checkInterrupt(intSource, type) returns if intSource is type as bool
236*/
237
238void loop() {
239 if ((millis() % 1000) == 1) {
240 xyzFloat g = myAcc.getGValues();
241 Serial.print("g-x = ");
242 Serial.print(g.x);
243 Serial.print(" | g-y = ");
244 Serial.print(g.y);
245 Serial.print(" | g-z = ");
246 Serial.print(g.z);
247 Serial.print(" | act-cnt = ");
248 ++activity_counter;
249 Serial.println(activity_counter);
250 }
251
252 if(in_activity == true) {
253 ++activity_counter;
254 //byte actTapSource = myAcc.getActTapStatus();
255 //Serial.println(actTapSource, BIN);
256 String axes = myAcc.getActTapStatusAsString();
257 byte intSource = myAcc.readAndClearInterrupts();
258
259 if(myAcc.checkInterrupt(intSource, ADXL345_ACTIVITY)){
260 Serial.print("Activity at: ");
261 Serial.println(axes);
262 }
263
264 delay(1000);
265 myAcc.readAndClearInterrupts();
266 in_activity = false;
267 }
268
269 if(activity_counter >= 20) goToSleep = true;
270
271 if(goToSleep == true) {
272 Serial.println("goToSleep == true");
273 Serial.println();
274
275 if (bootCount >= 1 && bootCount <= 2) {
276
277 // Configure external wakeup on GPIO14
278 esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1); // 1 = High, 0 = Low
279 rtc_gpio_pullup_dis(WAKEUP_GPIO);
280 rtc_gpio_pulldown_en(WAKEUP_GPIO);
281 Serial.println("Setup ESP32 to wake up on GPIO14 trigger");
282
283 } else if (bootCount == 3) {
284
285 // Sleep for 10 minutes instead of sending more than 3 consecutive notifications
286 bootCount = 0;
287 esp_sleep_enable_timer_wakeup(LONG_SLEEP_TIME * uS_TO_S_FACTOR);
288 Serial.println("Setup ESP32 to sleep for 10 * 60 seconds and reset bootCount");
289
290 }
291
292 // Go to sleep now
293 blinkLED(2, 1000); // Blink the LED 3 times with a 200ms interval to indicate wake up
294 Serial.println("Going to sleep now");
295 Serial.flush();
296 esp_deep_sleep_start();
297 Serial.println("This will never be printed");
298 Serial.println();
299 }
300
301}
302