ENTS-tock
Environmental NeTworked Sensor firmware on Tock OS
Loading...
Searching...
No Matches
main.cc
Go to the documentation of this file.
1/* vim: set sw=2 expandtab tw=80: */
2
8#include <libtock-sync/services/alarm.h>
9#include <libtock/kernel/ipc.h>
10#include <libtock/tock.h>
11#include <stdio.h>
12#include <ulog.h>
13
14#include "lorawan.h"
15#include "user_config.h"
16
21#define LOG_LINE_SIZE 240
22
24typedef struct {
26 int bytes;
28 int total;
30 int failed;
32 int meas;
36
38
39// buffer to store measurements
40static uint8_t meas_buffer[256] = {};
41static uint8_t meas_buffer_length = 0;
42
43// buffer for user config
44static uint8_t uc_buffer[256] = {};
45static uint8_t uc_buffer_length = 0;
46
47static uint8_t cmd = 0;
48
49// last pid command
50static int last_pid = 0;
51
52static bool has_data = false;
53
54static bool network_ready = false;
55
57static int timesync_retry_delay_ms = 10000;
58
60static int upload_interval = 60000;
61
63static const int userconfig_timeout_ms = 300 * 1000;
64
72static void ipc_callback(int pid, int len, int buf, void* ud);
73
86static int get_payload(uint8_t* buffer, int size);
87
95void ulog_prefix_handler(ulog_event* ev, char* prefix, size_t prefix_size);
96
97void ulog_prefix_handler(ulog_event* ev, char* prefix, size_t prefix_size) {
98 (void)ev;
99
100 snprintf(prefix, prefix_size, "Core\t");
101}
102
109static void microsd_log_output(ulog_event* ev, void* arg) {
110 (void)arg;
111
112 // Guard against recursion. If anything reached from ControllerMicroSDLog()
113 // ever logs, that log would re-enter this handler and recurse forever.
114 static bool busy = false;
115 if (busy) {
116 return;
117 }
118 busy = true;
119
120 // static to keep 128 bytes off the stack, matching microlog's own examples.
121 // Safe despite being shared state because the busy guard above prevents
122 // re-entry.
123 static char line[LOG_LINE_SIZE];
124 if (ulog_event_to_cstr(ev, line, sizeof(line)) == ULOG_STATUS_OK) {
125 ControllerMicroSDLog(line, "core");
126 }
127
128 busy = false;
129}
130
131int main(void) {
132 // Setup logging level and prefix
133 ulog_output_level_set_all(ULOG_LEVEL_TRACE);
134 ulog_prefix_set_fn(ulog_prefix_handler);
135 ulog_info("=== Core App Initialized ===");
136
137 // Load bytes into userconfig buffer
138 //
139 // Yes I am casting uint8_t to a uint16_t and it could overwrite the data
140 // buffer. I know at the time of writing this that the user config stays
141 // under 256 bytes as defined by protobuf.
142 UserConfigStatus uc_status =
143 UserConfigBytes(uc_buffer, (uint16_t*)&uc_buffer_length);
144 if (uc_status != USERCONFIG_OK) {
145 ulog_error("Could not load user config.");
146 }
147
148 // Option to print bytes to the buffer
149 // printf("uc_buffer[%u]:", uc_buffer_length);
150 // for (uint8_t i = 0; i < uc_buffer_length; i++) {
151 // printf(" %02x", uc_buffer[i]);
152 //}
153 // printf("\n\n");
154
155 // start service after connected
156 ipc_register_service_callback("org.ents.core", ipc_callback, NULL);
157
158 // Print warning when using TEST_USER_CONFIG
159#ifdef TEST_USER_CONFIG
160 ulog_warn("TEST_USER_CONFIG is enabled!\n");
161#endif // TEST_USER_CONFIG
162
163 // Initialize controller interface
165
166 // Setup sd card output
167 ulog_output_id sd_output =
168 ulog_output_add(microsd_log_output, NULL, ULOG_LEVEL_INFO);
169 if (sd_output == ULOG_OUTPUT_INVALID) {
170 // Almost always means ULOG_BUILD_EXTRA_OUTPUTS was not set when microlog
171 // was compiled, see embedded/external/microlog/Makefile.
172 ulog_error("Could not register microSD log output");
173 return 1;
174 }
175
176 // Get update configuration from server
178
179 // Reset esp32 and start webserver
181 UserConfigStart(userconfig_timeout_ms);
182
183 // return codes
184 int ret = 0;
185
186 // Initialize LoRaWAN
187
188 ret = lorawan_init();
189 if (ret < 0) {
190 return ret;
191 }
192
193 ret = lorawan_join();
194 if (ret < 0) {
195 return ret;
196 }
197
198 while (1) {
199 ret = lorawan_timesync();
200 if (ret < 0) {
201 ulog_info("Retrying in %d ms", timesync_retry_delay_ms);
202 libtocksync_alarm_delay_ms(timesync_retry_delay_ms);
203 } else {
204 break;
205 }
206 }
207
208 network_ready = true;
209
210 while (1) {
211 ulog_trace("main loop");
212
213 ret = libtocksync_alarm_yield_for_with_timeout(&has_data, upload_interval);
214 if (ret == 0) {
215 ulog_info("Loop triggered with condition");
216 } else if (ret == -1) {
217 ulog_info("Loop triggered with timeout");
218 } else {
219 ulog_warn("Unknown behavior with yield_for_with_timeout (error: %d)",
220 ret);
221 }
222
223 //
224 // Save data on matched command
225 //
226
227 if (has_data && cmd == 2) {
228 // print out bytes
229 // Get number of bytes in buffer
230 ulog_info("Received %d bytes:", meas_buffer_length);
231 // for (int i=1; i < meas_buffer_length; i++) {
232 // printf("%x ", meas_buffer[i]);
233 // }
234 // printf("\n");
235
236 // store in buffer
237 ret = fifo_put(meas_buffer, meas_buffer_length);
238 if (ret < 0) {
239 ulog_error("Could not store measurement in buffer");
240 }
241 stats.meas++;
242
243 // indicate data has been processed and trigger client
244 has_data = false;
245 ipc_notify_client(last_pid);
246
247 // skip uploads if received ipc call
248 continue;
249 }
250
251 //
252 // Upload data
253 //
254
255 uint16_t meas_in_buffer = fifo_buffer_len();
256
257 if (meas_in_buffer > 1) {
258 // batch into minium of 4 measurements
259 while (meas_in_buffer > 1) {
260 ulog_debug("Buffer has %d measurements", meas_in_buffer);
261
262 // format payload
263 uint8_t buffer[60] = {};
264 int len = get_payload(buffer, sizeof(buffer));
265 if (len != 0) {
266 ulog_debug("Uploading %d bytes", len);
267
268 stats.total++;
269 ret = lorawan_upload(buffer, len);
270 if (ret < 0) {
271 stats.failed++;
272 ulog_error("Could not upload with LoRaWAN (error: %d)", ret);
273 } else {
274 ulog_debug("Uploaded %d bytes with LoRaWAN.");
275
276 stats.bytes += len;
277 }
278 }
279
280 meas_in_buffer = fifo_buffer_len();
281 }
282 } else {
283 stats.total++;
284
285 ulog_debug("Sending heartbeat");
286
287 ret = lorawan_heartbeat();
288 if (ret < 0) {
289 stats.failed++;
290 ulog_error("Error sending heartbeat (error: %d)");
291 } else {
293 ulog_debug("Heartbeat sent");
294 }
295 }
296
297 //
298 // print stats
299 //
300 if (!(stats.total % 6)) {
301 ulog_info(
302 "total uploads: %d failed uploads: %d measurements: %d bytes: "
303 "%d heartbeats: %d",
305 }
306 }
307}
308
309static void ipc_callback(int pid, int len, int buf, void* ud) {
310 (void)len;
311 (void)ud;
312
313 ulog_trace("ipc_callabck");
314
315 uint8_t* buffer = (uint8_t*)buf;
316
317 // payload format
318 cmd = buffer[0];
319 uint8_t* length = buffer + 1;
320 uint8_t* data = buffer + 2;
321
322 // Reply with userconfig when requested
323 if (cmd == 1) {
324 ulog_trace("user config command");
325
326 // copy from user config to data
327 memcpy(data, uc_buffer, uc_buffer_length);
328 *length = uc_buffer_length;
329
330 // trigger client
331 ipc_notify_client(pid);
332
333 // Store measurements into buffer
334 } else if (cmd == 2) {
335 ulog_trace("measurement command");
336
337 // copy data to buffer
338 meas_buffer_length = *length;
339 memcpy(meas_buffer, data, *length);
340
341 // store last pid so it can be triggered
342 // basically hold sensors in an interrupt until we store the measurement
343 last_pid = pid;
344 has_data = true;
345
346 // ready command
347 } else if (cmd == 3) {
348 ulog_trace("ready command");
349
350 buffer[1] = (uint8_t)network_ready;
351
352 // trigger client
353 ipc_notify_client(pid);
354
355 // Catch all other commands
356 } else {
357 ulog_error("IPC command %d not implemented.", buffer[0]);
358 }
359}
360
361static int get_payload(uint8_t* buffer, int size) {
362 ulog_trace("get_payload");
363
364 // return codes
365 int ret = 0;
366
367 int len = 0;
368
369 Metadata meta = {};
370 SensorMeasurement meas[8] = {};
371
372 uint16_t length = fifo_buffer_len();
373
374 uint16_t i = 0;
375 for (i = 0; i < length; i++) {
376 ret = fifo_peek(i, buffer, (uint8_t*)&len);
377 if (ret < 0) {
378 ulog_error("Could not read from buffer (error: %d)", ret);
379 continue;
380 }
381 ulog_debug("Read %d bytes from buffer", len);
382
383 // decode measuremnet
384 ret = DecodeSensorMeasurement(buffer, len, &meas[i]);
385 if (ret < 0) {
386 ulog_error("Could not decode measurement (error %d), malformed data?",
387 ret);
388 // TODO: Removed failing measurement. Can't use drop, need to remove
389 // specific index.
390 continue;
391 }
392
393 ret = RepeatedSensorMeasurementsSize(meta, meas, i + 1, (size_t*)&len);
394 if (ret < 0) {
395 ulog_error("Could not find size of payload (error %d)", ret);
396 continue;
397 }
398 ulog_debug("Size of repeated sensor measurements: %d", len);
399
400 // early stop when length exceeds size of buffer
401 if (len > size) {
402 ulog_debug("Over sensors size limit of %d. Removing last measurement.",
403 size);
404
405 i--;
406 break;
407 }
408
410 }
411
412 ret = EncodeRepeatedSensorMeasurements(meta, meas, i + 1, buffer, size,
413 (size_t*)&len);
414 if (ret < 0) {
415 ulog_error("Could not encode %d repeated measurements (error %d)", i, ret);
416 return 0;
417 }
418
419 // Clear uploaded measurements
420 // ulog_debug("%d measurements to drop");
421 for (uint16_t j = 0; j < i; j++) {
422 ret = fifo_drop();
423 if (ret < 0) {
424 ulog_error("Could not remove measurement index %d from buffer", j);
425 }
426 }
427 return len;
428}
Controller module for stm32.
const char data[]
uint8_t buffer[32]
uint8_t i
Circular buffer as FIFO data structure for buffering measurements.
void ControllerDeviceReset(void)
Resets the controller interface.
Definition controller.c:70
void ControllerInit(void)
Shared initialization for all esp32 modules.
Definition controller.c:14
uint32_t ControllerMicroSDLog(const char *msg, const char *base)
Append a line of plain text to the log file on the microSD card.
Definition microsd.c:155
uint16_t fifo_buffer_len(void)
Get the current number of measurements stored in the buffer.
Definition fifo.c:233
fram_status fifo_put(const uint8_t *data, const uint8_t num_bytes)
Puts a measurement into the circular buffer.
Definition fifo.c:55
fram_status fifo_drop(void)
Drops the next measurement in the buffer.
Definition fifo.c:210
fram_status fifo_peek(size_t idx, uint8_t *data, uint8_t *len)
Peeks at the next measurement in the buffer without removing it.
Definition fifo.c:147
SensorStatus DecodeSensorMeasurement(const uint8_t *data, const size_t len, SensorMeasurement *meas)
Decodes a sensor measurement from a buffer.
Definition sensor.c:259
SensorStatus RepeatedSensorMeasurementsSize(Metadata meta, const SensorMeasurement meas[], size_t count, size_t *size)
Gets the size of multiple repeated sensor measurements when encoded.
Definition sensor.c:191
SensorStatus EncodeRepeatedSensorMeasurements(Metadata meta, const SensorMeasurement meas[], size_t count, uint8_t *buffer, size_t size, size_t *length)
Encodes multiple sensor measurements into a buffer.
Definition sensor.c:166
void PrintSensorMeasurement(const SensorMeasurement *m)
Definition sensor.c:338
UserConfigStatus UserConfigBytes(uint8_t *buffer, uint16_t *length)
Gets encoded bytes from userconfig.
Definition user_config.c:79
UserConfigStatus
Definition user_config.h:51
@ USERCONFIG_OK
Definition user_config.h:52
int lorawan_timesync(void)
Definition lorawan.cc:195
int lorawan_upload(uint8_t *buffer, int length)
Definition lorawan.cc:275
int lorawan_heartbeat(void)
Definition lorawan.cc:281
int lorawan_join(void)
Definition lorawan.cc:82
int lorawan_init(void)
Definition lorawan.cc:39
void ulog_prefix_handler(ulog_event *ev, char *prefix, size_t prefix_size)
Callback to add prefix to ulog messages.
Definition main.cc:97
int main(void)
Definition main.cc:131
#define LOG_LINE_SIZE
Definition main.cc:21
upload_stats stats
Definition main.cc:37
MicroSD interface implementation with the esp32.
Library for sensor measurement encoding/decoding.
Controls the user config webserver.
int total
Definition main.cc:28
int bytes
Definition main.cc:26
int heartbeats
Definition main.cc:34
int failed
Definition main.cc:30
int meas
Definition main.cc:32
void UserConfigUpdateFromServer(void)
void UserConfigStart(uint32_t retry_ms)
Starts the user config webserver.