2 Revize

Autor SHA1 Zpráva Datum
  Thomas Buck 404216198d tweak pc python workflows před 3 měsíci
  Thomas Buck b9bbe1ac01 add experimental influxdb support před 3 měsíci
7 změnil soubory, kde provedl 189 přidání a 7 odebrání
  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 Zobrazit soubor

@@ -36,6 +36,10 @@
36 36
 #define AUTO_LOG_ON_MASS_STORAGE
37 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 43
 #define WATCHDOG_PERIOD_MS 1000
40 44
 #define FLASH_LOCK_TIMEOUT_MS 500
41 45
 

+ 2
- 0
include/wifi.h Zobrazit soubor

@@ -39,4 +39,6 @@ const char *wifi_state(void);
39 39
 
40 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 44
 #endif // __WIFI_H__

+ 1
- 1
src/log.c Zobrazit soubor

@@ -32,7 +32,7 @@
32 32
 static uint8_t log_buff[4096] = {0};
33 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 36
 static volatile bool got_input = false;
37 37
 
38 38
 #ifndef PICOWOTA

+ 115
- 0
src/wifi.c Zobrazit soubor

@@ -19,6 +19,7 @@
19 19
 #include "pico/cyw43_arch.h"
20 20
 #include "lwip/netif.h"
21 21
 #include "lwip/ip4_addr.h"
22
+#include "lwip/tcp.h"
22 23
 #include "dhcpserver.h"
23 24
 
24 25
 #include "config.h"
@@ -53,6 +54,18 @@ static bool enabled_ap = false;
53 54
 static char curr_ssid[WIFI_MAX_NAME_LEN + 1] = {0};
54 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 69
 static void wifi_ap(void) {
57 70
     cyw43_thread_enter();
58 71
 
@@ -224,6 +237,101 @@ const char *wifi_state(void) {
224 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 335
 void wifi_run(void) {
228 336
     cyw43_thread_enter();
229 337
 
@@ -284,5 +392,12 @@ void wifi_run(void) {
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 402
     cyw43_thread_exit();
288 403
 }

+ 61
- 0
src/workflow.c Zobrazit soubor

@@ -26,6 +26,12 @@
26 26
 #include "volcano.h"
27 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 35
 #ifdef WF_CONFIRM_WRITES
30 36
 #define DO_WHILE(x, y) \
31 37
     do {               \
@@ -42,6 +48,36 @@ static uint32_t start_t = 0;
42 48
 static uint16_t start_val = 0;
43 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 81
 static void do_step(void) {
46 82
     switch (mem_data()->wf[wf_i].steps[step].op) {
47 83
     case OP_SET_TEMPERATURE:
@@ -50,6 +86,9 @@ static void do_step(void) {
50 86
         start_val = volcano_get_current_temp();
51 87
         DO_WHILE(volcano_set_target_temp(mem_data()->wf[wf_i].steps[step].val),
52 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 92
         break;
54 93
 
55 94
     case OP_PUMP_TIME:
@@ -58,6 +97,9 @@ static void do_step(void) {
58 97
         start_t = to_ms_since_boot(get_absolute_time());
59 98
         start_val = 0;
60 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 103
         break;
62 104
 
63 105
     case OP_WAIT_TIME:
@@ -219,6 +261,10 @@ void wf_start(uint16_t index) {
219 261
              !(volcano_get_state() & VOLCANO_STATE_HEATER));
220 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 268
     do_step();
223 269
 }
224 270
 
@@ -241,6 +287,14 @@ void wf_run(void) {
241 287
     case OP_WAIT_TEMPERATURE: {
242 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 298
         // volcano does not provide a temperature when cold
245 299
         if (start_val == 0) {
246 300
             start_val = temp;
@@ -265,6 +319,9 @@ void wf_run(void) {
265 319
         if (mem_data()->wf[wf_i].steps[step].op == OP_PUMP_TIME) {
266 320
             DO_WHILE(volcano_set_pump_state(false),
267 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 327
         step++;
@@ -273,6 +330,10 @@ void wf_run(void) {
273 330
             DO_WHILE(volcano_set_heater_state(false),
274 331
                      volcano_get_state() & VOLCANO_STATE_HEATER);
275 332
             debug("workflow finished");
333
+
334
+#ifdef VOLCANO_INFLUX_DB
335
+            influxdb_send("heater", 0);
336
+#endif // VOLCANO_INFLUX_DB
276 337
         } else {
277 338
             do_step();
278 339
         }

+ 3
- 3
test_bleak/flow.py Zobrazit soubor

@@ -62,9 +62,9 @@ async def flow(client):
62 62
     print("Turning on heater")
63 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 69
     print("Notification by pumping three times...")
70 70
     for i in range(0, 3):

+ 3
- 3
test_simplepyble/flow.py Zobrazit soubor

@@ -63,9 +63,9 @@ def flow(client):
63 63
     print("Turning on heater")
64 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 70
     print("Notification by pumping three times...")
71 71
     for i in range(0, 3):

Loading…
Zrušit
Uložit