3 Commits

Author SHA1 Message Date
  Thomas Buck 944f87e56b basic ble notification support 8 months ago
  Thomas Buck 7680476df9 prefetch volcano ble characteristics 8 months ago
  Thomas Buck 11483f370b only auto connect when device is visible within 10s of boot 8 months ago
15 changed files with 320 additions and 45 deletions
  1. 9
    0
      README.md
  2. 8
    1
      include/ble.h
  3. 1
    0
      include/config.h
  4. 3
    0
      include/lcd.h
  5. 3
    0
      include/volcano.h
  6. 221
    15
      src/ble.c
  7. 2
    1
      src/buttons.c
  8. 8
    0
      src/console.c
  9. 2
    2
      src/lcd.c
  10. 0
    7
      src/mem.c
  11. 2
    2
      src/state_crafty.c
  12. 9
    2
      src/state_scan.c
  13. 9
    10
      src/text.c
  14. 37
    0
      src/volcano.c
  15. 6
    5
      src/workflow.c

+ 9
- 0
README.md View File

@@ -1,9 +1,18 @@
1 1
 # Pi Pico Volcano Remote Control Gadget
2 2
 
3
+Supports:
4
+
5
+ * workflows for the Volcano Hybrid
6
+ * basic status and settings for Crafty+
7
+
3 8
 For use with Raspberry Pi Pico W boards with the [Waveshare Pico LCD 1.3](https://www.waveshare.com/wiki/Pico-LCD-1.3) and the [Pimoroni Pico Lipo Shim](https://shop.pimoroni.com/products/pico-lipo-shim).
4 9
 
5 10
 Adapted from the [tinyusb-cdc-example](https://github.com/hathach/tinyusb/blob/master/examples/device/cdc_msc/src/main.c), [adc example](https://github.com/raspberrypi/pico-examples/tree/master/adc/read_vsys), [standalone client example](https://github.com/raspberrypi/pico-examples/blob/master/pico_w/bt/standalone/client.c) and my [Trackball firmware](https://git.xythobuz.de/thomas/Trackball).
6 11
 
12
+`python-test` contains a similar app to the C version in the top level of the repo, but instead written for MicroPython on the Pico W.
13
+Unfortunately I had many performance and space problems with this, so I decided to rewrite it.
14
+`web-app` contains a script to conveniently fetch the original web app JS sources, for "reverse engineering".
15
+
7 16
 ## Quick Start
8 17
 
9 18
 When compiling for the first time, check out the required git submodules.

+ 8
- 1
include/ble.h View File

@@ -54,8 +54,15 @@ void ble_connect(bd_addr_t addr, bd_addr_type_t type);
54 54
 bool ble_is_connected(void);
55 55
 void ble_disconnect(void);
56 56
 
57
+int8_t ble_discover(const uint8_t *service, const uint8_t *characteristic);
58
+
57 59
 int32_t ble_read(const uint8_t *characteristic, uint8_t *buff, uint16_t buff_len);
58 60
 int8_t ble_write(const uint8_t *service, const uint8_t *characteristic,
59
-                 uint8_t *buff, uint16_t buff_len);
61
+                 const uint8_t *buff, uint16_t buff_len);
62
+
63
+int8_t ble_notification_disable(const uint8_t *service, const uint8_t *characteristic);
64
+int8_t ble_notification_enable(const uint8_t *service, const uint8_t *characteristic);
65
+bool ble_notification_ready(void);
66
+uint16_t ble_notification_get(uint8_t *buff, uint16_t buff_len);
60 67
 
61 68
 #endif // __BLE_H__

+ 1
- 0
include/config.h View File

@@ -31,6 +31,7 @@
31 31
 
32 32
 #ifdef MENU_PREFER_VOLCANO
33 33
 #define VOLCANO_AUTO_CONNECT_TIMEOUT_MS 2000
34
+#define VOLCANO_AUTO_CONNECT_WITHIN_MS 10000
34 35
 #endif // MENU_PREFER_VOLCANO
35 36
 
36 37
 #endif // NDEBUG

+ 3
- 0
include/lcd.h View File

@@ -21,6 +21,9 @@
21 21
 
22 22
 #include <stdint.h>
23 23
 
24
+#define LCD_WIDTH 240
25
+#define LCD_HEIGHT 240
26
+
24 27
 #define RGB_565(r, g, b) ( \
25 28
       (((r) >> 3) << 11)   \
26 29
     | (((g) >> 2) << 5)    \

+ 3
- 0
include/volcano.h View File

@@ -22,6 +22,9 @@
22 22
 #include <stdint.h>
23 23
 #include <stdbool.h>
24 24
 
25
+// returns < 0 on error
26
+int8_t volcano_discover_characteristics(void);
27
+
25 28
 // in 1/10th degrees C, or < 0 on error
26 29
 int16_t volcano_get_current_temp(void);
27 30
 int16_t volcano_get_target_temp(void);

+ 221
- 15
src/ble.c View File

@@ -28,10 +28,11 @@
28 28
 #include "util.h"
29 29
 #include "ble.h"
30 30
 
31
-#define BLE_READ_TIMEOUT_MS (2 * 500)
32
-#define BLE_SRVC_TIMEOUT_MS (2 * 500)
33
-#define BLE_CHAR_TIMEOUT_MS (2 * 2000)
34
-#define BLE_WRTE_TIMEOUT_MS (2 * 500)
31
+#define BLE_READ_TIMEOUT_MS (3 * 500)
32
+#define BLE_SRVC_TIMEOUT_MS (3 * 500)
33
+#define BLE_CHAR_TIMEOUT_MS (3 * 2000)
34
+#define BLE_WRTE_TIMEOUT_MS (3 * 500)
35
+#define BLE_NOTY_TIMEOUT_MS (3 * 500)
35 36
 #define BLE_MAX_SCAN_AGE_MS (10 * 1000)
36 37
 #define BLE_MAX_SERVICES 8
37 38
 #define BLE_MAX_CHARACTERISTICS 8
@@ -48,11 +49,14 @@ enum ble_state {
48 49
     TC_W4_CHARACTERISTIC,
49 50
     TC_W4_WRITE,
50 51
     TC_WRITE_COMPLETE,
52
+    TC_W4_NOTIFY_ENABLE,
53
+    TC_NOTIFY_ENABLED,
51 54
 };
52 55
 
53 56
 struct ble_characteristic {
54 57
     bool set;
55 58
     gatt_client_characteristic_t c;
59
+    gatt_client_notification_t n;
56 60
 };
57 61
 
58 62
 struct ble_service {
@@ -319,6 +323,11 @@ static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa
319 323
             state = TC_WRITE_COMPLETE;
320 324
             break;
321 325
 
326
+        case TC_W4_NOTIFY_ENABLE:
327
+            //debug("notify enable complete");
328
+            state = TC_NOTIFY_ENABLED;
329
+            break;
330
+
322 331
         default:
323 332
             debug("gatt query complete in invalid state %d", state);
324 333
             break;
@@ -326,6 +335,22 @@ static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa
326 335
         break;
327 336
     }
328 337
 
338
+    case GATT_EVENT_NOTIFICATION: {
339
+        if ((state != TC_READY) && (state != TC_WRITE_COMPLETE)) {
340
+            debug("gatt notification in invalid state %d", state);
341
+            return;
342
+        }
343
+
344
+        uint16_t value_length = gatt_event_notification_get_value_length(packet);
345
+        const uint8_t *value = gatt_event_notification_get_value(packet);
346
+        if ((read_len + value_length) <= BLE_MAX_VALUE_LEN) {
347
+            memcpy(data_buff + read_len, value, value_length);
348
+            read_len += value_length;
349
+        }
350
+
351
+        break;
352
+    }
353
+
329 354
     default:
330 355
         //debug("unexpected event 0x%02X", hci_event_packet_get_type(packet));
331 356
         break;
@@ -572,20 +597,14 @@ int32_t ble_read(const uint8_t *characteristic, uint8_t *buff, uint16_t buff_len
572 597
 
573 598
     memcpy(buff, data_buff, read_len);
574 599
 
600
+    uint16_t tmp = read_len;
601
+    read_len = 0;
602
+
575 603
     cyw43_thread_exit();
576
-    return read_len;
604
+    return tmp;
577 605
 }
578 606
 
579
-int8_t ble_write(const uint8_t *service, const uint8_t *characteristic,
580
-                  uint8_t *buff, uint16_t buff_len) {
581
-    cyw43_thread_enter();
582
-
583
-    if (state != TC_READY) {
584
-        cyw43_thread_exit();
585
-        debug("invalid state for write (%d)", state);
586
-        return -1;
587
-    }
588
-
607
+static int discover_service(const uint8_t *service) {
589 608
     // check if service has already been discovered
590 609
     int srvc = -1, free_srvc = -1;
591 610
     for (int i = 0; i < BLE_MAX_SERVICES; i++) {
@@ -652,6 +671,10 @@ int8_t ble_write(const uint8_t *service, const uint8_t *characteristic,
652 671
         cyw43_thread_enter();
653 672
     }
654 673
 
674
+    return srvc;
675
+}
676
+
677
+static int discover_characteristic(int srvc, const uint8_t *characteristic) {
655 678
     // check if characteristic has already been discovered
656 679
     int ch = -1, free_ch = -1;
657 680
     for (int i = 0; i < BLE_MAX_CHARACTERISTICS; i++) {
@@ -719,6 +742,31 @@ int8_t ble_write(const uint8_t *service, const uint8_t *characteristic,
719 742
         cyw43_thread_enter();
720 743
     }
721 744
 
745
+    return ch;
746
+}
747
+
748
+int8_t ble_write(const uint8_t *service, const uint8_t *characteristic,
749
+                 const uint8_t *buff, uint16_t buff_len) {
750
+    cyw43_thread_enter();
751
+
752
+    if (state != TC_READY) {
753
+        cyw43_thread_exit();
754
+        debug("invalid state for write (%d)", state);
755
+        return -1;
756
+    }
757
+
758
+    int srvc = discover_service(service);
759
+    if (srvc < 0) {
760
+        debug("error discovering service (%d)", srvc);
761
+        return srvc;
762
+    }
763
+
764
+    int ch = discover_characteristic(srvc, characteristic);
765
+    if (ch < 0) {
766
+        debug("error discovering characteristic (%d)", ch);
767
+        return ch;
768
+    }
769
+
722 770
     if (buff_len > BLE_MAX_VALUE_LEN) {
723 771
         buff_len = BLE_MAX_VALUE_LEN;
724 772
     }
@@ -768,3 +816,161 @@ int8_t ble_write(const uint8_t *service, const uint8_t *characteristic,
768 816
     cyw43_thread_exit();
769 817
     return ret;
770 818
 }
819
+
820
+int8_t ble_discover(const uint8_t *service, const uint8_t *characteristic) {
821
+    cyw43_thread_enter();
822
+
823
+    if (state != TC_READY) {
824
+        cyw43_thread_exit();
825
+        debug("invalid state for discovery (%d)", state);
826
+        return -1;
827
+    }
828
+
829
+    int srvc = discover_service(service);
830
+    if (srvc < 0) {
831
+        debug("error discovering service (%d)", srvc);
832
+        return srvc;
833
+    }
834
+
835
+    int ch = discover_characteristic(srvc, characteristic);
836
+    if (ch < 0) {
837
+        debug("error discovering characteristic (%d)", ch);
838
+        return ch;
839
+    }
840
+
841
+    cyw43_thread_exit();
842
+    return 0;
843
+}
844
+
845
+int8_t ble_notification_disable(const uint8_t *service, const uint8_t *characteristic) {
846
+    cyw43_thread_enter();
847
+
848
+    if (state != TC_READY) {
849
+        cyw43_thread_exit();
850
+        debug("invalid state for notify (%d)", state);
851
+        return -1;
852
+    }
853
+
854
+    int srvc = discover_service(service);
855
+    if (srvc < 0) {
856
+        debug("error discovering service (%d)", srvc);
857
+        return srvc;
858
+    }
859
+
860
+    int ch = discover_characteristic(srvc, characteristic);
861
+    if (ch < 0) {
862
+        debug("error discovering characteristic (%d)", ch);
863
+        return ch;
864
+    }
865
+
866
+    gatt_client_stop_listening_for_characteristic_value_updates(&services[srvc].chars[ch].n);
867
+
868
+    cyw43_thread_exit();
869
+    return 0;
870
+}
871
+
872
+int8_t ble_notification_enable(const uint8_t *service, const uint8_t *characteristic) {
873
+    cyw43_thread_enter();
874
+
875
+    if (state != TC_READY) {
876
+        cyw43_thread_exit();
877
+        debug("invalid state for notify (%d)", state);
878
+        return -1;
879
+    }
880
+
881
+    int srvc = discover_service(service);
882
+    if (srvc < 0) {
883
+        debug("error discovering service (%d)", srvc);
884
+        return srvc;
885
+    }
886
+
887
+    int ch = discover_characteristic(srvc, characteristic);
888
+    if (ch < 0) {
889
+        debug("error discovering characteristic (%d)", ch);
890
+        return ch;
891
+    }
892
+
893
+    gatt_client_listen_for_characteristic_value_updates(&services[srvc].chars[ch].n,
894
+                                                        hci_event_handler,
895
+                                                        connection_handle,
896
+                                                        &services[srvc].chars[ch].c);
897
+
898
+    gatt_client_write_client_characteristic_configuration(hci_event_handler,
899
+                                                          connection_handle,
900
+                                                          &services[srvc].chars[ch].c,
901
+                                                          GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
902
+
903
+    state = TC_W4_NOTIFY_ENABLE;
904
+    cyw43_thread_exit();
905
+
906
+    uint32_t start_time = to_ms_since_boot(get_absolute_time());
907
+    while (1) {
908
+        sleep_ms(1);
909
+        main_loop_hw();
910
+
911
+        uint32_t now = to_ms_since_boot(get_absolute_time());
912
+        if ((now - start_time) >= BLE_NOTY_TIMEOUT_MS) {
913
+            debug("timeout waiting for notify enable");
914
+            cyw43_thread_enter();
915
+            state = TC_READY;
916
+            cyw43_thread_exit();
917
+            return -6;
918
+        }
919
+
920
+        cyw43_thread_enter();
921
+        enum ble_state state_cached = state;
922
+        cyw43_thread_exit();
923
+
924
+        if ((state_cached == TC_NOTIFY_ENABLED) || (state_cached == TC_READY)) {
925
+            break;
926
+        }
927
+    }
928
+
929
+    cyw43_thread_enter();
930
+
931
+    int8_t ret = (state == TC_NOTIFY_ENABLED) ? 0 : -7;
932
+    state = TC_READY;
933
+
934
+    cyw43_thread_exit();
935
+    return ret;
936
+}
937
+
938
+bool ble_notification_ready(void) {
939
+    cyw43_thread_enter();
940
+
941
+    if (state != TC_READY) {
942
+        cyw43_thread_exit();
943
+        debug("invalid state for notify (%d)", state);
944
+        return -1;
945
+    }
946
+
947
+    uint16_t tmp = read_len;
948
+
949
+    cyw43_thread_exit();
950
+    return (tmp > 0);
951
+
952
+}
953
+
954
+uint16_t ble_notification_get(uint8_t *buff, uint16_t buff_len) {
955
+    cyw43_thread_enter();
956
+
957
+    if (state != TC_READY) {
958
+        cyw43_thread_exit();
959
+        debug("invalid state for notify (%d)", state);
960
+        return -1;
961
+    }
962
+
963
+    if (read_len > buff_len) {
964
+        debug("buffer too short (%d < %d)", buff_len, read_len);
965
+        cyw43_thread_exit();
966
+        return -2;
967
+    }
968
+
969
+    memcpy(buff, data_buff, read_len);
970
+
971
+    uint16_t tmp = read_len;
972
+    read_len = 0;
973
+
974
+    cyw43_thread_exit();
975
+    return tmp;
976
+}

+ 2
- 1
src/buttons.c View File

@@ -69,8 +69,9 @@ void buttons_run(void) {
69 69
 
70 70
         if ((now - buttons[i].last_time) > DEBOUNCE_DELAY_MS) {
71 71
             if (state != buttons[i].current_state) {
72
+                //debug("btn %d now %s", i, state ? "pressed" : "released");
73
+
72 74
                 buttons[i].current_state = state;
73
-                debug("btn %d now %s", i, state ? "pressed" : "released");
74 75
                 if (callback) {
75 76
                     callback(i, state);
76 77
                 }

+ 8
- 0
src/console.c View File

@@ -171,6 +171,14 @@ static void cnsl_interpret(const char *line) {
171 171
                         bd_addr_to_str(results[i].addr),
172 172
                         results[i].type, results[i].rssi,
173 173
                         age / 1000.0, results[i].name, info);
174
+
175
+                if (results[i].data_len > 0) {
176
+                    hexdump(results[i].data, results[i].data_len);
177
+                }
178
+
179
+                if (i < (n - 1)) {
180
+                    println();
181
+                }
174 182
             }
175 183
         }
176 184
     } else if (str_startswith(line, "con ")) {

+ 2
- 2
src/lcd.c View File

@@ -34,8 +34,8 @@
34 34
 #define LCD_PIN_RST 12
35 35
 #define LCD_PIN_BL 13
36 36
 
37
-#define ST7789_PICO_COLUMN                             240
38
-#define ST7789_PICO_ROW                                ST7789_PICO_COLUMN
37
+#define ST7789_PICO_COLUMN                             LCD_HEIGHT
38
+#define ST7789_PICO_ROW                                LCD_WIDTH
39 39
 
40 40
 #define ST7789_PICO_ACCESS                            (ST7789_ORDER_PAGE_TOP_TO_BOTTOM | \
41 41
                                                        ST7789_ORDER_COLUMN_LEFT_TO_RIGHT | \

+ 0
- 7
src/mem.c View File

@@ -79,10 +79,8 @@ static uint32_t calc_checksum(const struct mem_contents *data) {
79 79
 
80 80
 void mem_load_defaults(void) {
81 81
     data_ram.data.wf_count = wf_default_count;
82
-    debug("preparing %d default workflows", data_ram.data.wf_count);
83 82
     for (uint16_t i = 0; i < wf_default_count; i++) {
84 83
         data_ram.data.wf[i] = wf_default_data[i];
85
-        debug("\"%s\" by \"%s\"", data_ram.data.wf[i].name, data_ram.data.wf[i].author);
86 84
     }
87 85
 }
88 86
 
@@ -108,11 +106,6 @@ void mem_init(void) {
108 106
     } else {
109 107
         debug("invalid config (0x%02X != 0x%02X)", flash_ptr->version, MEM_VERSION);
110 108
     }
111
-
112
-    debug("found %d workflows", data_ram.data.wf_count);
113
-    for (uint16_t i = 0; i < data_ram.data.wf_count; i++) {
114
-        debug("\"%s\" by \"%s\"", data_ram.data.wf[i].name, data_ram.data.wf[i].author);
115
-    }
116 109
 }
117 110
 
118 111
 static void mem_write_flash(void *param) {

+ 2
- 2
src/state_crafty.c View File

@@ -28,7 +28,7 @@
28 28
 
29 29
 #include "menu.h"
30 30
 
31
-#define CRAFTY_UPDATE_TIME_MS 1000
31
+#define CRAFTY_UPDATE_TIME_MS 3000
32 32
 
33 33
 static bd_addr_t ble_addr = {0};
34 34
 static bd_addr_type_t ble_type = 0;
@@ -115,8 +115,8 @@ void state_crafty_run(void) {
115 115
     static uint32_t last = 0;
116 116
     uint32_t now = to_ms_since_boot(get_absolute_time());
117 117
     if (((now - last) >= CRAFTY_UPDATE_TIME_MS) || (last == 0)) {
118
-        last = now;
119 118
         menu_run(draw, true);
119
+        last = to_ms_since_boot(get_absolute_time());
120 120
     }
121 121
 
122 122
     if (wait_for_disconnect && !ble_is_connected()) {

+ 9
- 2
src/state_scan.c View File

@@ -100,9 +100,16 @@ static void draw(struct menu_state *menu) {
100 100
 #ifdef MENU_PREFER_VOLCANO
101 101
             if (dev == DEV_VOLCANO) {
102 102
                 menu->selection = devs - 1;
103
+
103 104
 #ifdef VOLCANO_AUTO_CONNECT_TIMEOUT_MS
104
-                auto_connect_time = to_ms_since_boot(get_absolute_time());
105
-                auto_connect_idx = i;
105
+#ifdef VOLCANO_AUTO_CONNECT_WITHIN_MS
106
+                if (to_ms_since_boot(get_absolute_time()) <= VOLCANO_AUTO_CONNECT_WITHIN_MS) {
107
+#endif // VOLCANO_AUTO_CONNECT_WITHIN_MS
108
+                    auto_connect_time = to_ms_since_boot(get_absolute_time());
109
+                    auto_connect_idx = i;
110
+#ifdef VOLCANO_AUTO_CONNECT_WITHIN_MS
111
+                }
112
+#endif // VOLCANO_AUTO_CONNECT_WITHIN_MS
106 113
 #endif // VOLCANO_AUTO_CONNECT_TIMEOUT_MS
107 114
             }
108 115
 #endif // MENU_PREFER_VOLCANO

+ 9
- 10
src/text.c View File

@@ -166,16 +166,16 @@ void text_box(const char *s, bool centered) {
166 166
         text_prepare_font(&font);
167 167
     }
168 168
 
169
-    int x = 0;
170
-    int width = 240;
169
+    int x_off = 0;
170
+    int width = LCD_WIDTH;
171 171
 
172
-    int y = 50;
173
-    int height = MENU_MAX_LINES * (20 + 2);
172
+    int y_off = 50;
173
+    int height = (MENU_MAX_LINES * 20) + ((MENU_MAX_LINES - 1) * 2);
174 174
 
175 175
     struct text_conf text = {
176 176
         .text = "",
177
-        .x = x,
178
-        .y = y,
177
+        .x = x_off,
178
+        .y = y_off,
179 179
         .justify = false,
180 180
         .alignment = centered ? MF_ALIGN_CENTER : MF_ALIGN_LEFT,
181 181
         .width = width,
@@ -186,10 +186,9 @@ void text_box(const char *s, bool centered) {
186 186
         .font = &font,
187 187
     };
188 188
 
189
-    lcd_write_rect(x,
190
-                   y,
191
-                   x + width - 1,
192
-                   y + height - 1,
189
+    lcd_write_rect(x_off, y_off,
190
+                   x_off + width - 1,
191
+                   y_off + height - 1,
193 192
                    RGB_565(0x00, 0x00, 0x00));
194 193
 
195 194
     text.text = s;

+ 37
- 0
src/volcano.c View File

@@ -40,6 +40,43 @@ static uint8_t uuid_base2[16] = {
40 40
     0x5a, 0x26, 0x42, 0x49, 0x43, 0x4b, 0x45, 0x4c,
41 41
 };
42 42
 
43
+int8_t volcano_discover_characteristics(void) {
44
+    uuid_base[3] = UUID_WRITE_SRVC;
45
+    int8_t r;
46
+
47
+    uuid_base2[3] = UUID_TARGET_TEMP;
48
+    r = ble_discover(uuid_base, uuid_base2);
49
+    if (r < 0) {
50
+        return r;
51
+    }
52
+
53
+    uuid_base2[3] = UUID_HEATER_ON;
54
+    r = ble_discover(uuid_base, uuid_base2);
55
+    if (r < 0) {
56
+        return r;
57
+    }
58
+
59
+    uuid_base2[3] = UUID_HEATER_OFF;
60
+    r = ble_discover(uuid_base, uuid_base2);
61
+    if (r < 0) {
62
+        return r;
63
+    }
64
+
65
+    uuid_base2[3] = UUID_PUMP_ON;
66
+    r = ble_discover(uuid_base, uuid_base2);
67
+    if (r < 0) {
68
+        return r;
69
+    }
70
+
71
+    uuid_base2[3] = UUID_PUMP_OFF;
72
+    r = ble_discover(uuid_base, uuid_base2);
73
+    if (r < 0) {
74
+        return r;
75
+    }
76
+
77
+    return 0;
78
+}
79
+
43 80
 int16_t volcano_get_current_temp(void) {
44 81
     uuid_base[3] = UUID_CURRENT_TEMP;
45 82
 

+ 6
- 5
src/workflow.c View File

@@ -199,12 +199,13 @@ void wf_start(uint16_t index) {
199 199
     wf_i = index;
200 200
     step = 0;
201 201
 
202
-    // discover characteristics
203
-    volcano_set_pump_state(false);
204
-    volcano_set_heater_state(false);
205
-    volcano_set_target_temp(1850);
206
-
202
+    /*
203
+     * first turn on heater, then do discovery, to save some time.
204
+     * this means we heat for some seconds before changing the setpoint.
205
+     * should not be a problem in practice.
206
+     */
207 207
     volcano_set_heater_state(true);
208
+    volcano_discover_characteristics();
208 209
 
209 210
     do_step();
210 211
 }

Loading…
Cancel
Save