|
@@ -15,11 +15,11 @@
|
15
|
15
|
|
16
|
16
|
#include <Arduino.h>
|
17
|
17
|
|
18
|
|
-// Turns the 'PRG' button into the power button, long press is off
|
19
|
|
-// this also increases deep sleep power usage!
|
20
|
|
-#ifndef FEATURE_SML
|
|
18
|
+#ifdef FEATURE_SML
|
|
19
|
+#define HELTEC_NO_DISPLAY
|
|
20
|
+#else // FEATURE_SML
|
21
|
21
|
#define HELTEC_POWER_BUTTON
|
22
|
|
-#endif // ! FEATURE_SML
|
|
22
|
+#endif // FEATURE_SML
|
23
|
23
|
|
24
|
24
|
#include <heltec_unofficial.h>
|
25
|
25
|
|
|
@@ -33,11 +33,9 @@
|
33
|
33
|
|
34
|
34
|
#ifdef FEATURE_SML
|
35
|
35
|
#define LORA_LED_BRIGHTNESS 0 // in percent, 50% brightness is plenty for this LED
|
36
|
|
-#define OLED_BAT_INTERVAL (2UL * 60UL * 1000UL) // in ms
|
37
|
|
-#define FORCE_BAT_SEND_AT_OLED_INTERVAL
|
38
|
|
-#define DEEP_SLEEP_TIMEOUT_MS (1UL * 60UL * 1000UL) // gather data for 1min
|
39
|
|
-#define DEEP_SLEEP_ABORT_NO_DATA_MS (20UL * 1000UL) // if no data appears, abort after 20s
|
40
|
|
-#define DEEP_SLEEP_DURATION_S (5UL * 60UL) // then sleep for 5min
|
|
36
|
+#define DEEP_SLEEP_DURATION_S 60
|
|
37
|
+#define DEEP_SLEEP_TIMEOUT_MS (30UL * 1000UL)
|
|
38
|
+#define DEEP_SLEEP_ABORT_NO_DATA_MS DEEP_SLEEP_TIMEOUT_MS
|
41
|
39
|
#else // FEATURE_SML
|
42
|
40
|
#define LORA_LED_BRIGHTNESS 25 // in percent, 50% brightness is plenty for this LED
|
43
|
41
|
#endif // FEATURE_SML
|
|
@@ -86,6 +84,10 @@ static unsigned long last_tx = 0, last_rx = 0;
|
86
|
84
|
static unsigned long tx_time = 0, minimum_pause = 0;
|
87
|
85
|
static volatile bool rx_flag = false;
|
88
|
86
|
|
|
87
|
+#ifndef LORA_KEEP_SENDING_CACHE
|
|
88
|
+RTC_DATA_ATTR static uint8_t last_tx_msg = LORA_SML_HELLO;
|
|
89
|
+#endif // ! LORA_KEEP_SENDING_CACHE
|
|
90
|
+
|
89
|
91
|
#ifdef FEATURE_SML
|
90
|
92
|
|
91
|
93
|
struct sml_cache {
|
|
@@ -103,7 +105,9 @@ void lora_oled_init(void) {
|
103
|
105
|
}
|
104
|
106
|
|
105
|
107
|
void lora_oled_print(String s) {
|
|
108
|
+#ifndef FEATURE_SML
|
106
|
109
|
display.print(s);
|
|
110
|
+#endif // ! FEATURE_SML
|
107
|
111
|
}
|
108
|
112
|
|
109
|
113
|
static void print_bat(void) {
|
|
@@ -120,6 +124,7 @@ double lora_get_mangled_bat(void) {
|
120
|
124
|
return *((double *)data);
|
121
|
125
|
}
|
122
|
126
|
|
|
127
|
+#ifdef LORA_CLIENT_CHECKSUM
|
123
|
128
|
// adapted from "Hacker's Delight"
|
124
|
129
|
static uint32_t calc_checksum(const uint8_t *data, size_t len) {
|
125
|
130
|
uint32_t c = 0xFFFFFFFF;
|
|
@@ -133,6 +138,7 @@ static uint32_t calc_checksum(const uint8_t *data, size_t len) {
|
133
|
138
|
|
134
|
139
|
return ~c;
|
135
|
140
|
}
|
|
141
|
+#endif // LORA_CLIENT_CHECKSUM
|
136
|
142
|
|
137
|
143
|
static void lora_rx(void) {
|
138
|
144
|
rx_flag = true;
|
|
@@ -147,7 +153,10 @@ static bool lora_tx(enum lora_sml_type type, double value) {
|
147
|
153
|
struct lora_sml_msg msg;
|
148
|
154
|
msg.type = type;
|
149
|
155
|
msg.value = value;
|
|
156
|
+
|
|
157
|
+#ifdef LORA_CLIENT_CHECKSUM
|
150
|
158
|
msg.checksum = calc_checksum((uint8_t *)&msg, offsetof(struct lora_sml_msg, checksum));
|
|
159
|
+#endif // LORA_CLIENT_CHECKSUM
|
151
|
160
|
|
152
|
161
|
uint8_t *data = (uint8_t *)&msg;
|
153
|
162
|
const size_t len = sizeof(struct lora_sml_msg);
|
|
@@ -158,7 +167,7 @@ static bool lora_tx(enum lora_sml_type type, double value) {
|
158
|
167
|
for (size_t i = 0; i < len; i++) {
|
159
|
168
|
data[i] ^= LORA_XOR_KEY[i];
|
160
|
169
|
}
|
161
|
|
-#endif
|
|
170
|
+#endif // LORA_XOR_KEY
|
162
|
171
|
|
163
|
172
|
radio.clearDio1Action();
|
164
|
173
|
|
|
@@ -197,10 +206,8 @@ static bool lora_tx(enum lora_sml_type type, double value) {
|
197
|
206
|
}
|
198
|
207
|
|
199
|
208
|
#ifdef FEATURE_SML
|
200
|
|
-static bool lora_sml_cache_send(enum lora_sml_type msg) {
|
201
|
|
- return lora_tx(msg, cache[msg].value);
|
202
|
|
-}
|
203
|
209
|
|
|
210
|
+#ifdef LORA_KEEP_SENDING_CACHE
|
204
|
211
|
static void lora_sml_handle_cache(void) {
|
205
|
212
|
// find smallest message counter that is ready
|
206
|
213
|
unsigned long min_counter = ULONG_MAX;
|
|
@@ -213,7 +220,8 @@ static void lora_sml_handle_cache(void) {
|
213
|
220
|
// try to transmit next value with lowest counter
|
214
|
221
|
for (int i = 0; i < LORA_SML_NUM_MESSAGES; i++) {
|
215
|
222
|
if (cache[i].ready && (cache[i].counter == min_counter)) {
|
216
|
|
- if (lora_sml_cache_send((enum lora_sml_type)i)) {
|
|
223
|
+ enum lora_sml_type msg = (enum lora_sml_type)i;
|
|
224
|
+ if (lora_tx(msg, cache[msg].value)) {
|
217
|
225
|
if (cache[i].has_next) {
|
218
|
226
|
cache[i].has_next = false;
|
219
|
227
|
cache[i].value = cache[i].next_value;
|
|
@@ -225,6 +233,7 @@ static void lora_sml_handle_cache(void) {
|
225
|
233
|
}
|
226
|
234
|
}
|
227
|
235
|
}
|
|
236
|
+#endif // LORA_KEEP_SENDING_CACHE
|
228
|
237
|
|
229
|
238
|
void lora_sml_send(enum lora_sml_type msg, double value, unsigned long counter) {
|
230
|
239
|
if (cache[msg].ready) {
|
|
@@ -239,6 +248,32 @@ void lora_sml_send(enum lora_sml_type msg, double value, unsigned long counter)
|
239
|
248
|
cache[msg].counter = counter;
|
240
|
249
|
}
|
241
|
250
|
}
|
|
251
|
+
|
|
252
|
+void lora_sml_done(void) {
|
|
253
|
+#ifndef LORA_KEEP_SENDING_CACHE
|
|
254
|
+ // turn off Ve external 3.3V to Smart Meter reader
|
|
255
|
+ heltec_ve(false);
|
|
256
|
+
|
|
257
|
+ // select next message from cache
|
|
258
|
+ uint8_t n = 0;
|
|
259
|
+ do {
|
|
260
|
+ last_tx_msg++;
|
|
261
|
+ n++;
|
|
262
|
+ if (last_tx_msg >= LORA_SML_NUM_MESSAGES) {
|
|
263
|
+ last_tx_msg = 0;
|
|
264
|
+ }
|
|
265
|
+ } while ((!cache[last_tx_msg].ready) && (n < LORA_SML_NUM_MESSAGES + 1));
|
|
266
|
+
|
|
267
|
+ // transmit it
|
|
268
|
+ if (cache[last_tx_msg].ready) {
|
|
269
|
+ enum lora_sml_type msg = (enum lora_sml_type)last_tx_msg;
|
|
270
|
+ lora_tx(msg, cache[msg].value);
|
|
271
|
+ }
|
|
272
|
+
|
|
273
|
+ heltec_deep_sleep(DEEP_SLEEP_DURATION_S < (minimum_pause / 1000) ? (minimum_pause / 1000) : DEEP_SLEEP_DURATION_S);
|
|
274
|
+#endif // ! LORA_KEEP_SENDING_CACHE
|
|
275
|
+}
|
|
276
|
+
|
242
|
277
|
#endif // FEATURE_SML
|
243
|
278
|
|
244
|
279
|
void lora_init(void) {
|
|
@@ -251,9 +286,9 @@ void lora_init(void) {
|
251
|
286
|
cache[i].counter = 0;
|
252
|
287
|
cache[i].next_counter = 0;
|
253
|
288
|
}
|
254
|
|
-#endif // FEATURE_SML
|
255
|
289
|
|
256
|
|
- print_bat();
|
|
290
|
+ //print_bat();
|
|
291
|
+#endif // FEATURE_SML
|
257
|
292
|
|
258
|
293
|
bool success = true;
|
259
|
294
|
|
|
@@ -305,9 +340,6 @@ void lora_init(void) {
|
305
|
340
|
#ifdef FEATURE_SML
|
306
|
341
|
// turn on Ve external 3.3V to power Smart Meter reader
|
307
|
342
|
heltec_ve(true);
|
308
|
|
-
|
309
|
|
- // send hello msg after boot
|
310
|
|
- lora_sml_send(LORA_SML_HELLO, heltec_temperature(), 0);
|
311
|
343
|
#endif // FEATURE_SML
|
312
|
344
|
}
|
313
|
345
|
|
|
@@ -331,7 +363,7 @@ void lora_run(void) {
|
331
|
363
|
bool got_sml = sml_data_received();
|
332
|
364
|
if ((got_sml && (time >= DEEP_SLEEP_TIMEOUT_MS))
|
333
|
365
|
|| ((!got_sml) && (time >= DEEP_SLEEP_ABORT_NO_DATA_MS))) {
|
334
|
|
- heltec_deep_sleep(DEEP_SLEEP_DURATION_S);
|
|
366
|
+ heltec_deep_sleep(DEEP_SLEEP_DURATION_S < (minimum_pause / 1000) ? (minimum_pause / 1000) : DEEP_SLEEP_DURATION_S);
|
335
|
367
|
}
|
336
|
368
|
#endif // DEEP_SLEEP_TIMEOUT_MS && DEEP_SLEEP_DURATION_S
|
337
|
369
|
|
|
@@ -376,11 +408,14 @@ void lora_run(void) {
|
376
|
408
|
#endif
|
377
|
409
|
|
378
|
410
|
struct lora_sml_msg *msg = (struct lora_sml_msg *)data;
|
|
411
|
+
|
|
412
|
+#ifdef LORA_CLIENT_CHECKSUM
|
379
|
413
|
uint32_t checksum = calc_checksum(data, offsetof(struct lora_sml_msg, checksum));
|
380
|
414
|
if (checksum != msg->checksum) {
|
381
|
415
|
debug.printf(" CRC: 0x%08X != 0x%08X\n", msg->checksum, checksum);
|
382
|
416
|
} else {
|
383
|
417
|
debug.printf(" CRC: OK 0x%08X\n", checksum);
|
|
418
|
+#endif // LORA_CLIENT_CHECKSUM
|
384
|
419
|
|
385
|
420
|
#ifdef ENABLE_INFLUXDB_LOGGING
|
386
|
421
|
if (data[0] == LORA_SML_BAT_V) {
|
|
@@ -438,7 +473,10 @@ void lora_run(void) {
|
438
|
473
|
writeSensorDatum("environment", "sml", SENSOR_LOCATION, key, msg->value);
|
439
|
474
|
}
|
440
|
475
|
#endif // ENABLE_INFLUXDB_LOGGING
|
|
476
|
+
|
|
477
|
+#ifdef LORA_CLIENT_CHECKSUM
|
441
|
478
|
}
|
|
479
|
+#endif // LORA_CLIENT_CHECKSUM
|
442
|
480
|
}
|
443
|
481
|
|
444
|
482
|
#ifndef FEATURE_SML
|
|
@@ -451,10 +489,11 @@ void lora_run(void) {
|
451
|
489
|
#endif // ! FEATURE_SML
|
452
|
490
|
}
|
453
|
491
|
|
454
|
|
-#ifdef FEATURE_SML
|
|
492
|
+#ifdef LORA_KEEP_SENDING_CACHE
|
455
|
493
|
lora_sml_handle_cache();
|
456
|
|
-#endif // FEATURE_SML
|
|
494
|
+#endif // LORA_KEEP_SENDING_CACHE
|
457
|
495
|
|
|
496
|
+#ifndef FEATURE_SML
|
458
|
497
|
if (button.isSingleClick()) {
|
459
|
498
|
// In case of button click, tell user to wait
|
460
|
499
|
bool tx_legal = millis() > last_tx + minimum_pause;
|
|
@@ -463,13 +502,9 @@ void lora_run(void) {
|
463
|
502
|
return;
|
464
|
503
|
}
|
465
|
504
|
|
466
|
|
- // send test hello message on lorarx target, or battery state on loratx target
|
467
|
|
-#ifdef FEATURE_SML
|
468
|
|
- lora_sml_send(LORA_SML_BAT_V, lora_get_mangled_bat(), 0);
|
469
|
|
-#else // FEATURE_SML
|
470
|
505
|
lora_tx(LORA_SML_HELLO, heltec_temperature());
|
471
|
|
-#endif // FEATURE_SML
|
472
|
506
|
}
|
|
507
|
+#endif // ! FEATURE_SML
|
473
|
508
|
}
|
474
|
509
|
|
475
|
510
|
#endif // FEATURE_LORA
|