2 Commits

Author SHA1 Message Date
  Thomas Buck 404216198d tweak pc python workflows 3 months ago
  Thomas Buck b9bbe1ac01 add experimental influxdb support 3 months ago
7 changed files with 189 additions and 7 deletions
  1. 4
    0
      include/config.h
  2. 2
    0
      include/wifi.h
  3. 1
    1
      src/log.c
  4. 115
    0
      src/wifi.c
  5. 61
    0
      src/workflow.c
  6. 3
    3
      test_bleak/flow.py
  7. 3
    3
      test_simplepyble/flow.py

+ 4
- 0
include/config.h View File

36
 #define AUTO_LOG_ON_MASS_STORAGE
36
 #define AUTO_LOG_ON_MASS_STORAGE
37
 #endif // NDEBUG
37
 #endif // NDEBUG
38
 
38
 
39
+//#define INFLUXDB_HOST "IP_V4_HERE"
40
+//#define INFLUXDB_PORT 8086
41
+//#define INFLUXDB_DATABASE "db_name"
42
+
39
 #define WATCHDOG_PERIOD_MS 1000
43
 #define WATCHDOG_PERIOD_MS 1000
40
 #define FLASH_LOCK_TIMEOUT_MS 500
44
 #define FLASH_LOCK_TIMEOUT_MS 500
41
 
45
 

+ 2
- 0
include/wifi.h View File

39
 
39
 
40
 void wifi_run(void);
40
 void wifi_run(void);
41
 
41
 
42
+void wifi_tcp_send(const char *ip, uint16_t port, const char *packet, unsigned int len);
43
+
42
 #endif // __WIFI_H__
44
 #endif // __WIFI_H__

+ 1
- 1
src/log.c View File

32
 static uint8_t log_buff[4096] = {0};
32
 static uint8_t log_buff[4096] = {0};
33
 static struct ring_buffer log_rb = RB_INIT(log_buff, sizeof(log_buff), 1);
33
 static struct ring_buffer log_rb = RB_INIT(log_buff, sizeof(log_buff), 1);
34
 
34
 
35
-static uint8_t line_buff[256] = {0};
35
+static uint8_t line_buff[512] = {0};
36
 static volatile bool got_input = false;
36
 static volatile bool got_input = false;
37
 
37
 
38
 #ifndef PICOWOTA
38
 #ifndef PICOWOTA

+ 115
- 0
src/wifi.c View File

19
 #include "pico/cyw43_arch.h"
19
 #include "pico/cyw43_arch.h"
20
 #include "lwip/netif.h"
20
 #include "lwip/netif.h"
21
 #include "lwip/ip4_addr.h"
21
 #include "lwip/ip4_addr.h"
22
+#include "lwip/tcp.h"
22
 #include "dhcpserver.h"
23
 #include "dhcpserver.h"
23
 
24
 
24
 #include "config.h"
25
 #include "config.h"
53
 static char curr_ssid[WIFI_MAX_NAME_LEN + 1] = {0};
54
 static char curr_ssid[WIFI_MAX_NAME_LEN + 1] = {0};
54
 static char curr_pass[WIFI_MAX_PASS_LEN + 1] = {0};
55
 static char curr_pass[WIFI_MAX_PASS_LEN + 1] = {0};
55
 
56
 
57
+struct wifi_tcp_cache {
58
+    bool in_use;
59
+    bool clear; // TODO
60
+    ip4_addr_t ip;
61
+    uint16_t port;
62
+    struct tcp_pcb *tpcb;
63
+    char data[512];
64
+    unsigned int len;
65
+};
66
+
67
+static struct wifi_tcp_cache cache[4] = {0};
68
+
56
 static void wifi_ap(void) {
69
 static void wifi_ap(void) {
57
     cyw43_thread_enter();
70
     cyw43_thread_enter();
58
 
71
 
224
     return NULL;
237
     return NULL;
225
 }
238
 }
226
 
239
 
240
+static err_t wifi_tcp_connect(void *arg, struct tcp_pcb *tpcb, err_t err) {
241
+    (void)err;
242
+    int c = (int)arg;
243
+
244
+    err_t e = tcp_write(tpcb, cache[c].data, cache[c].len, TCP_WRITE_FLAG_COPY);
245
+    if (e) {
246
+        debug("tcp_write error %d", e);
247
+        cache[c].clear = true;
248
+        return ERR_OK;
249
+    }
250
+
251
+    e = tcp_output(tpcb);
252
+    if (e) {
253
+        debug("tcp_output error %d", e);
254
+        cache[c].clear = true;
255
+        return ERR_OK;
256
+    }
257
+
258
+    return ERR_OK;
259
+}
260
+
261
+static void wifi_tcp_error(void *arg, err_t err) {
262
+    int c = (int)arg;
263
+    debug("tcp err %d", err);
264
+    cache[c].in_use = false; // lwip already freed the tpcb
265
+}
266
+
267
+static err_t wifi_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
268
+    (void)tpcb;
269
+    (void)err;
270
+    int c = (int)arg;
271
+    if (p == NULL) {
272
+        debug("remote closed conn");
273
+        cache[c].clear = true;
274
+    } else {
275
+        debug("tcp rx %d: %s", pbuf_clen(p), (char *)p->payload);
276
+        cache[c].clear = true;
277
+    }
278
+    return ERR_OK;
279
+}
280
+
281
+static err_t wifi_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) {
282
+    (void)tpcb;
283
+    int c = (int)arg;
284
+    if (len == cache[c].len) {
285
+        debug("tcp tx success");
286
+        cache[c].clear = true;
287
+    }
288
+    return ERR_OK;
289
+}
290
+
291
+void wifi_tcp_send(const char *ip, uint16_t port, const char *packet, unsigned int len) {
292
+    cyw43_arch_lwip_begin();
293
+
294
+    ip4_addr_t val;
295
+    if (!ip4addr_aton(ip, &val)) {
296
+        debug("invalid IP: %s", ip);
297
+        cyw43_arch_lwip_end();
298
+        return;
299
+    }
300
+
301
+    int c = -1;
302
+    for (unsigned int i = 0; i < (sizeof(cache) / sizeof(cache[0])); i++) {
303
+        if (!cache[i].in_use) {
304
+            c = i;
305
+        }
306
+    }
307
+    if (c < 0) {
308
+        debug("no space in cache");
309
+        cyw43_arch_lwip_end();
310
+        return;
311
+    }
312
+
313
+    cache[c].in_use = true;
314
+    cache[c].clear = false;
315
+    cache[c].ip = val;
316
+    cache[c].port = port;
317
+    cache[c].tpcb = tcp_new();
318
+
319
+    if (len > sizeof(cache[c].data)) {
320
+        len = sizeof(cache[c].data);
321
+    }
322
+
323
+    cache[c].len = len;
324
+    memcpy(cache[c].data, packet, len);
325
+
326
+    tcp_arg(cache[c].tpcb, (void *)c);
327
+    tcp_err(cache[c].tpcb, wifi_tcp_error);
328
+    tcp_recv(cache[c].tpcb, wifi_tcp_recv);
329
+    tcp_sent(cache[c].tpcb, wifi_tcp_sent);
330
+    tcp_connect(cache[c].tpcb, &val, port, wifi_tcp_connect);
331
+
332
+    cyw43_arch_lwip_end();
333
+}
334
+
227
 void wifi_run(void) {
335
 void wifi_run(void) {
228
     cyw43_thread_enter();
336
     cyw43_thread_enter();
229
 
337
 
284
         }
392
         }
285
     }
393
     }
286
 
394
 
395
+    for (unsigned int c = 0; c < (sizeof(cache) / sizeof(cache[0])); c++) {
396
+        if (cache[c].in_use && cache[c].clear) {
397
+            tcp_close(cache[c].tpcb);
398
+            cache[c].in_use = false;
399
+        }
400
+    }
401
+
287
     cyw43_thread_exit();
402
     cyw43_thread_exit();
288
 }
403
 }

+ 61
- 0
src/workflow.c View File

26
 #include "volcano.h"
26
 #include "volcano.h"
27
 #include "workflow.h"
27
 #include "workflow.h"
28
 
28
 
29
+#if defined(INFLUXDB_HOST) && defined(INFLUXDB_PORT) && defined(INFLUXDB_DATABASE)
30
+#include "usb_descriptors.h"
31
+#include "wifi.h"
32
+#define VOLCANO_INFLUX_DB
33
+#endif
34
+
29
 #ifdef WF_CONFIRM_WRITES
35
 #ifdef WF_CONFIRM_WRITES
30
 #define DO_WHILE(x, y) \
36
 #define DO_WHILE(x, y) \
31
     do {               \
37
     do {               \
42
 static uint16_t start_val = 0;
48
 static uint16_t start_val = 0;
43
 static uint16_t curr_val = 0;
49
 static uint16_t curr_val = 0;
44
 
50
 
51
+#ifdef VOLCANO_INFLUX_DB
52
+static void influxdb_send(const char *name, double value) {
53
+    if (!wifi_ready()) {
54
+        debug("skip influx, no wifi (\"%s\" %.2lf)", name, value);
55
+        return;
56
+    }
57
+
58
+    char payload[128] = {0};
59
+    size_t len = 0;
60
+    len = snprintf(payload, sizeof(payload) - 1,
61
+                   "volcano,device=%s %s=%lf",
62
+                   string_pico_serial, name, value);
63
+
64
+
65
+    char packet[256] = {0};
66
+    size_t n = 0;
67
+    n = snprintf(packet, sizeof(packet) - 1,
68
+                  "POST /write?db=%s HTTP/1.0\r\n"
69
+                  "Host: %s\r\n"
70
+                  "Content-Length: %d\r\n"
71
+                  "\r\n"
72
+                  "%s",
73
+                  INFLUXDB_DATABASE, INFLUXDB_HOST,
74
+                 len, payload);
75
+
76
+    debug("write influx \"%s\": %.2lf", name, value);
77
+    wifi_tcp_send(INFLUXDB_HOST, INFLUXDB_PORT, packet, n);
78
+}
79
+#endif // VOLCANO_INFLUX_DB
80
+
45
 static void do_step(void) {
81
 static void do_step(void) {
46
     switch (mem_data()->wf[wf_i].steps[step].op) {
82
     switch (mem_data()->wf[wf_i].steps[step].op) {
47
     case OP_SET_TEMPERATURE:
83
     case OP_SET_TEMPERATURE:
50
         start_val = volcano_get_current_temp();
86
         start_val = volcano_get_current_temp();
51
         DO_WHILE(volcano_set_target_temp(mem_data()->wf[wf_i].steps[step].val),
87
         DO_WHILE(volcano_set_target_temp(mem_data()->wf[wf_i].steps[step].val),
52
                  volcano_get_target_temp() != mem_data()->wf[wf_i].steps[step].val);
88
                  volcano_get_target_temp() != mem_data()->wf[wf_i].steps[step].val);
89
+#ifdef VOLCANO_INFLUX_DB
90
+        influxdb_send("target", mem_data()->wf[wf_i].steps[step].val);
91
+#endif // VOLCANO_INFLUX_DB
53
         break;
92
         break;
54
 
93
 
55
     case OP_PUMP_TIME:
94
     case OP_PUMP_TIME:
58
         start_t = to_ms_since_boot(get_absolute_time());
97
         start_t = to_ms_since_boot(get_absolute_time());
59
         start_val = 0;
98
         start_val = 0;
60
         debug("workflow pump %.3f s", mem_data()->wf[wf_i].steps[step].val / 1000.0);
99
         debug("workflow pump %.3f s", mem_data()->wf[wf_i].steps[step].val / 1000.0);
100
+#ifdef VOLCANO_INFLUX_DB
101
+        influxdb_send("pump", 1);
102
+#endif // VOLCANO_INFLUX_DB
61
         break;
103
         break;
62
 
104
 
63
     case OP_WAIT_TIME:
105
     case OP_WAIT_TIME:
219
              !(volcano_get_state() & VOLCANO_STATE_HEATER));
261
              !(volcano_get_state() & VOLCANO_STATE_HEATER));
220
     volcano_discover_characteristics(true, false);
262
     volcano_discover_characteristics(true, false);
221
 
263
 
264
+#ifdef VOLCANO_INFLUX_DB
265
+    influxdb_send("heater", 1);
266
+#endif // VOLCANO_INFLUX_DB
267
+
222
     do_step();
268
     do_step();
223
 }
269
 }
224
 
270
 
241
     case OP_WAIT_TEMPERATURE: {
287
     case OP_WAIT_TEMPERATURE: {
242
         uint16_t temp = volcano_get_current_temp();
288
         uint16_t temp = volcano_get_current_temp();
243
 
289
 
290
+#ifdef VOLCANO_INFLUX_DB
291
+        static uint16_t last_temp = 0;
292
+        if (last_temp != temp) {
293
+            influxdb_send("current", temp);
294
+            last_temp = temp;
295
+        }
296
+#endif // VOLCANO_INFLUX_DB
297
+
244
         // volcano does not provide a temperature when cold
298
         // volcano does not provide a temperature when cold
245
         if (start_val == 0) {
299
         if (start_val == 0) {
246
             start_val = temp;
300
             start_val = temp;
265
         if (mem_data()->wf[wf_i].steps[step].op == OP_PUMP_TIME) {
319
         if (mem_data()->wf[wf_i].steps[step].op == OP_PUMP_TIME) {
266
             DO_WHILE(volcano_set_pump_state(false),
320
             DO_WHILE(volcano_set_pump_state(false),
267
                      volcano_get_state() & VOLCANO_STATE_PUMP);
321
                      volcano_get_state() & VOLCANO_STATE_PUMP);
322
+#ifdef VOLCANO_INFLUX_DB
323
+            influxdb_send("pump", 0);
324
+#endif // VOLCANO_INFLUX_DB
268
         }
325
         }
269
 
326
 
270
         step++;
327
         step++;
273
             DO_WHILE(volcano_set_heater_state(false),
330
             DO_WHILE(volcano_set_heater_state(false),
274
                      volcano_get_state() & VOLCANO_STATE_HEATER);
331
                      volcano_get_state() & VOLCANO_STATE_HEATER);
275
             debug("workflow finished");
332
             debug("workflow finished");
333
+
334
+#ifdef VOLCANO_INFLUX_DB
335
+            influxdb_send("heater", 0);
336
+#endif // VOLCANO_INFLUX_DB
276
         } else {
337
         } else {
277
             do_step();
338
             do_step();
278
         }
339
         }

+ 3
- 3
test_bleak/flow.py View File

62
     print("Turning on heater")
62
     print("Turning on heater")
63
     await set_state(client, (True, False))
63
     await set_state(client, (True, False))
64
 
64
 
65
-    await flow_step(client, 185.0, 10.0, 5.0)
66
-    await flow_step(client, 195.0, 5.0, 20.0)
67
-    await flow_step(client, 205.0, 5.0, 20.0)
65
+    await flow_step(client, 185.0, 10.0, 7.0)
66
+    await flow_step(client, 195.0, 5.0, 23.0)
67
+    await flow_step(client, 205.0, 5.0, 23.0)
68
 
68
 
69
     print("Notification by pumping three times...")
69
     print("Notification by pumping three times...")
70
     for i in range(0, 3):
70
     for i in range(0, 3):

+ 3
- 3
test_simplepyble/flow.py View File

63
     print("Turning on heater")
63
     print("Turning on heater")
64
     set_state(client, (True, False))
64
     set_state(client, (True, False))
65
 
65
 
66
-    flow_step(client, 185.0, 10.0, 5.0)
67
-    flow_step(client, 195.0, 5.0, 20.0)
68
-    flow_step(client, 205.0, 5.0, 20.0)
66
+    flow_step(client, 185.0, 10.0, 7.0)
67
+    flow_step(client, 195.0, 5.0, 23.0)
68
+    flow_step(client, 205.0, 5.0, 23.0)
69
 
69
 
70
     print("Notification by pumping three times...")
70
     print("Notification by pumping three times...")
71
     for i in range(0, 3):
71
     for i in range(0, 3):

Loading…
Cancel
Save