5 コミット

作成者 SHA1 メッセージ 日付
  Thomas Buck 52577ab1b1 hook up sml parsing to softserial interface 2ヶ月前
  Thomas Buck cd70b4877a test parsing of an actual sml dump from my meter 2ヶ月前
  Thomas Buck 4585b3c25b lora rx tx test with unofficial lib 2ヶ月前
  Thomas Buck e70faaa5d9 start experimenting with lora 2ヶ月前
  Thomas Buck f587c66e75 add pc displays to ui 2ヶ月前
13個のファイルの変更599行の追加12行の削除
  1. 1
    0
      .gitignore
  2. 1
    1
      compile_commands.json
  3. 8
    5
      include/DebugLog.h
  4. 32
    0
      include/lora.h
  5. 24
    0
      include/smart_meter.h
  6. 1
    0
      include/ui.h
  7. 53
    2
      platformio.ini
  8. 31
    0
      src/DebugLog.cpp
  9. 253
    0
      src/lora.cpp
  10. 33
    2
      src/main.cpp
  11. 8
    0
      src/mqtt.cpp
  12. 141
    0
      src/smart_meter.cpp
  13. 13
    2
      src/ui.cpp

+ 1
- 0
.gitignore ファイルの表示

@@ -1,2 +1,3 @@
1 1
 .pio
2 2
 .cache
3
+.directory

+ 1
- 1
compile_commands.json ファイルの表示

@@ -1 +1 @@
1
-.pio/build/cyd/compile_commands.json
1
+.pio/build/loratx/compile_commands.json

+ 8
- 5
include/DebugLog.h ファイルの表示

@@ -26,21 +26,24 @@ public:
26 26
 #ifdef ENABLE_DEBUGLOG
27 27
     String getBuffer(void);
28 28
 #endif // ENABLE_DEBUGLOG
29
-    
29
+
30 30
     void write(char c);
31 31
     void print(String s);
32 32
     void print(int n);
33
-    
33
+
34 34
     void println(void);
35 35
     void println(String s);
36 36
     void println(int n);
37
-    
37
+
38
+    size_t printf(const char * format, ...)  __attribute__ ((format (printf, 2, 3)));
39
+    size_t printf(const char * format, va_list args);
40
+
38 41
 private:
39 42
     void sendToTargets(String s);
40
-    
43
+
41 44
 #ifdef ENABLE_DEBUGLOG
42 45
     void addToBuffer(String s);
43
-    
46
+
44 47
     CircularBuffer<char, DEBUG_LOG_HISTORY_SIZE> buffer;
45 48
 #endif // ENABLE_DEBUGLOG
46 49
 };

+ 32
- 0
include/lora.h ファイルの表示

@@ -0,0 +1,32 @@
1
+/*
2
+ * lora.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __ESP_ENV_LORA__
15
+#define __ESP_ENV_LORA__
16
+
17
+#ifdef FEATURE_LORA
18
+
19
+void lora_oled_init(void);
20
+void lora_oled_print(String s);
21
+
22
+void lora_init(void);
23
+void lora_run(void);
24
+
25
+#ifdef FEATURE_SML
26
+void lora_sml_send(double SumWh, double T1Wh, double T2Wh,
27
+                   double SumW, double L1W, double L2W, double L3W);
28
+#endif // FEATURE_SML
29
+
30
+#endif // FEATURE_LORA
31
+
32
+#endif // __ESP_ENV_LORA__

+ 24
- 0
include/smart_meter.h ファイルの表示

@@ -0,0 +1,24 @@
1
+/*
2
+ * smart_meter.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __ESP_ENV_SMART_METER__
15
+#define __ESP_ENV_SMART_METER__
16
+
17
+#ifdef FEATURE_SML
18
+
19
+void sml_init(void);
20
+void sml_run(void);
21
+
22
+#endif // FEATURE_SML
23
+
24
+#endif // __ESP_ENV_SMART_METER__

+ 1
- 0
include/ui.h ファイルの表示

@@ -32,6 +32,7 @@ struct ui_status {
32 32
     bool light_box;
33 33
     enum bathroom_light_states bathroom_lights;
34 34
     bool light_sink;
35
+    bool pc_displays;
35 36
 };
36 37
 
37 38
 extern struct ui_status ui_status;

+ 53
- 2
platformio.ini ファイルの表示

@@ -13,8 +13,8 @@ platform = platformio/espressif32@6.4.0
13 13
 board = esp32dev
14 14
 framework = arduino
15 15
 upload_protocol = esptool
16
-upload_port = /dev/ttyUSB1
17
-monitor_port = /dev/ttyUSB1
16
+upload_port = /dev/ttyUSB2
17
+monitor_port = /dev/ttyUSB2
18 18
 monitor_speed = 115200
19 19
 extra_scripts = pre:extra_script.py
20 20
 build_flags =
@@ -36,6 +36,56 @@ lib_deps =
36 36
     https://github.com/Bodmer/TFT_eSPI.git#01483a1043a4915c4e34c928fb48a87d3b38a248
37 37
     https://github.com/PaulStoffregen/XPT2046_Touchscreen.git#5d5120e93ab5d28b3f1db6d754819c354c2da019
38 38
 
39
+[env:loratx]
40
+platform = espressif32
41
+board = heltec_wifi_lora_32_V3
42
+framework = arduino
43
+upload_protocol = esptool
44
+upload_port = /dev/ttyUSB2
45
+monitor_port = /dev/ttyUSB2
46
+monitor_speed = 115200
47
+extra_scripts = pre:extra_script.py
48
+build_flags =
49
+  -DSENSOR_HOSTNAME_PREFIX=\"lora-\"
50
+  "-DNAME_OF_FEATURE=\"Lora Gatway\""
51
+  -DENABLE_WEBSOCKETS
52
+  -DENABLE_DEBUGLOG
53
+  -DENABLE_MQTT
54
+  -DNEW_ESP32_LIB
55
+  -DFEATURE_LORA
56
+  -DFEATURE_SML
57
+  -DFEATURE_DISABLE_WIFI
58
+lib_deps =
59
+    https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
60
+    https://github.com/rlogiacco/CircularBuffer.git#f29cf01b6e8603422f3668d51036ac124f803404
61
+    https://github.com/Links2004/arduinoWebSockets.git#30d5e136665a52880f641ddd7245b3ba05ecd32b
62
+    https://github.com/ropg/heltec_esp32_lora_v3.git#9f281354507849755a597ebcce3721582f3b59b8
63
+    https://github.com/olliiiver/sml_parser.git#f256f6935a6c6cad1a7a7f40ea18ac7d2dd28f0a
64
+    https://github.com/plerup/espsoftwareserial.git#40038df467853f47dde4641769183fa46996decb
65
+
66
+[env:lorarx]
67
+platform = espressif32
68
+board = heltec_wifi_lora_32_V3
69
+framework = arduino
70
+upload_protocol = esptool
71
+upload_port = /dev/ttyUSB2
72
+monitor_port = /dev/ttyUSB2
73
+monitor_speed = 115200
74
+extra_scripts = pre:extra_script.py
75
+build_flags =
76
+  -DSENSOR_HOSTNAME_PREFIX=\"lora-\"
77
+  "-DNAME_OF_FEATURE=\"Lora Gatway\""
78
+  -DENABLE_WEBSOCKETS
79
+  -DENABLE_DEBUGLOG
80
+  -DENABLE_MQTT
81
+  -DNEW_ESP32_LIB
82
+  -DFEATURE_LORA
83
+lib_deps =
84
+    https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
85
+    https://github.com/rlogiacco/CircularBuffer.git#f29cf01b6e8603422f3668d51036ac124f803404
86
+    https://github.com/Links2004/arduinoWebSockets.git#30d5e136665a52880f641ddd7245b3ba05ecd32b
87
+    https://github.com/ropg/heltec_esp32_lora_v3.git#9f281354507849755a597ebcce3721582f3b59b8
88
+
39 89
 [env:esp8266env]
40 90
 platform = espressif8266
41 91
 board = esp01_1m
@@ -205,3 +255,4 @@ lib_deps =
205 255
     Adafruit BME280 Library
206 256
     https://github.com/jandrassy/UnoWiFiDevEdSerial1
207 257
     https://github.com/jandrassy/arduino-library-wifilink
258
+

+ 31
- 0
src/DebugLog.cpp ファイルの表示

@@ -15,6 +15,7 @@
15 15
 
16 16
 #include "config.h"
17 17
 #include "servers.h"
18
+#include "lora.h"
18 19
 #include "DebugLog.h"
19 20
 
20 21
 DebugLog debug;
@@ -40,6 +41,10 @@ void DebugLog::addToBuffer(String s) {
40 41
 void DebugLog::sendToTargets(String s) {
41 42
     Serial.print(s);
42 43
 
44
+#ifdef FEATURE_LORA
45
+    lora_oled_print(s);
46
+#endif // FEATURE_LORA
47
+
43 48
     s = "log:" + s;
44 49
     wifi_send_websocket(s);
45 50
 }
@@ -72,3 +77,29 @@ void DebugLog::println(String s) {
72 77
 void DebugLog::println(int n) {
73 78
     println(String(n));
74 79
 }
80
+
81
+size_t DebugLog::printf(const char *format, va_list args) {
82
+    char line_buff[128];
83
+    int l = vsnprintf((char *)line_buff, sizeof(line_buff), format, args);
84
+
85
+    if (l < 0) {
86
+        // encoding error
87
+        l = snprintf((char *)line_buff, sizeof(line_buff), "%s: encoding error\r\n", __func__);
88
+    } else if (l >= (ssize_t)sizeof(line_buff)) {
89
+        // not enough space for string
90
+        l = snprintf((char *)line_buff, sizeof(line_buff), "%s: message too long (%d)\r\n", __func__, l);
91
+    }
92
+    if ((l > 0) && (l <= (int)sizeof(line_buff))) {
93
+        print(String(line_buff));
94
+    }
95
+
96
+    return (l < 0) ? 0 : l;
97
+}
98
+
99
+size_t DebugLog::printf(const char * format, ...) {
100
+    va_list args;
101
+    va_start(args, format);
102
+    size_t r = printf(format, args);
103
+    va_end(args);
104
+    return r;
105
+}

+ 253
- 0
src/lora.cpp ファイルの表示

@@ -0,0 +1,253 @@
1
+/*
2
+ * lora.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifdef FEATURE_LORA
15
+
16
+#include <Arduino.h>
17
+
18
+// Turns the 'PRG' button into the power button, long press is off
19
+#define HELTEC_POWER_BUTTON
20
+
21
+#include <heltec_unofficial.h>
22
+
23
+#include "config.h"
24
+#include "DebugLog.h"
25
+#include "lora.h"
26
+
27
+// define LORA_TEST_TX to periodically transmit a test message
28
+//#define LORA_TEST_TX
29
+
30
+#define OLED_BAT_INTERVAL (10UL * 1000UL) // in ms
31
+
32
+#ifdef FEATURE_SML
33
+#define LORA_LED_BRIGHTNESS 1 // in percent, 50% brightness is plenty for this LED
34
+#else // FEATURE_SML
35
+#define LORA_LED_BRIGHTNESS 25 // in percent, 50% brightness is plenty for this LED
36
+#endif // FEATURE_SML
37
+
38
+#ifdef LORA_TEST_TX
39
+// Pause between transmited packets in seconds.
40
+// Set to zero to only transmit a packet when pressing the user button
41
+// Will not exceed 1% duty cycle, even if you set a lower value.
42
+#define PAUSE               10
43
+#endif // LORA_TEST_TX
44
+
45
+// Frequency in MHz. Keep the decimal point to designate float.
46
+// Check your own rules and regulations to see what is legal where you are.
47
+#define FREQUENCY           866.3       // for Europe
48
+// #define FREQUENCY           905.2       // for US
49
+
50
+// LoRa bandwidth. Keep the decimal point to designate float.
51
+// Allowed values are 7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0 and 500.0 kHz.
52
+#define BANDWIDTH           250.0
53
+
54
+// Number from 5 to 12. Higher means slower but higher "processor gain",
55
+// meaning (in nutshell) longer range and more robust against interference.
56
+#define SPREADING_FACTOR    9
57
+
58
+// Transmit power in dBm. 0 dBm = 1 mW, enough for tabletop-testing. This value can be
59
+// set anywhere between -9 dBm (0.125 mW) to 22 dBm (158 mW). Note that the maximum ERP
60
+// (which is what your antenna maximally radiates) on the EU ISM band is 25 mW, and that
61
+// transmissting without an antenna can damage your hardware.
62
+// 25mW = 14dBm
63
+#define MAX_TX_POWER        14
64
+#define ANTENNA_GAIN        5
65
+#define TRANSMIT_POWER      (MAX_TX_POWER - ANTENNA_GAIN)
66
+
67
+#define RADIOLIB_xy(action) \
68
+    debug.print(#action); \
69
+    debug.print(" = "); \
70
+    debug.print(state); \
71
+    debug.print(" ("); \
72
+    debug.print(radiolib_result_string(state)); \
73
+    debug.println(")");
74
+
75
+#define RADIOLIB_CHECK(action) do { \
76
+    int state = action; \
77
+    if (state != RADIOLIB_ERR_NONE) { \
78
+        RADIOLIB_xy(action); \
79
+        success = false; \
80
+    } \
81
+} while (false);
82
+
83
+static unsigned long last_bat_time = 0;
84
+static bool use_lora = true;
85
+static unsigned long last_tx = 0, counter = 0, tx_time = 0, minimum_pause = 0;
86
+static volatile bool rx_flag = false;
87
+
88
+void lora_oled_init(void) {
89
+    heltec_setup();
90
+}
91
+
92
+void lora_oled_print(String s) {
93
+    display.print(s);
94
+}
95
+
96
+static void print_bat(void) {
97
+    float vbat = heltec_vbat();
98
+    debug.printf("Vbat: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat));
99
+}
100
+
101
+static void lora_rx(void) {
102
+    rx_flag = true;
103
+}
104
+
105
+void lora_init(void) {
106
+    print_bat();
107
+
108
+    bool success = true;
109
+
110
+    RADIOLIB_CHECK(radio.begin());
111
+    if (!success) {
112
+        use_lora = false;
113
+        return;
114
+    }
115
+
116
+    radio.setDio1Action(lora_rx);
117
+
118
+    debug.printf("Frequency: %.2f MHz\n", FREQUENCY);
119
+    RADIOLIB_CHECK(radio.setFrequency(FREQUENCY));
120
+    if (!success) {
121
+        use_lora = false;
122
+        return;
123
+    }
124
+
125
+    debug.printf("Bandwidth: %.1f kHz\n", BANDWIDTH);
126
+    RADIOLIB_CHECK(radio.setBandwidth(BANDWIDTH));
127
+    if (!success) {
128
+        use_lora = false;
129
+        return;
130
+    }
131
+
132
+    debug.printf("Spreading Factor: %i\n", SPREADING_FACTOR);
133
+    RADIOLIB_CHECK(radio.setSpreadingFactor(SPREADING_FACTOR));
134
+    if (!success) {
135
+        use_lora = false;
136
+        return;
137
+    }
138
+
139
+    debug.printf("TX power: %i dBm\n", TRANSMIT_POWER);
140
+    RADIOLIB_CHECK(radio.setOutputPower(TRANSMIT_POWER));
141
+    if (!success) {
142
+        use_lora = false;
143
+        return;
144
+    }
145
+
146
+    // Start receiving
147
+    RADIOLIB_CHECK(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF));
148
+    if (!success) {
149
+        use_lora = false;
150
+        return;
151
+    }
152
+
153
+    // turn on Ve external 3.3V to power Smart Meter reader
154
+    heltec_ve(true);
155
+}
156
+
157
+static void lora_tx(String data) {
158
+    bool tx_legal = millis() > (last_tx + minimum_pause);
159
+    if (!tx_legal) {
160
+        //debug.printf("Legal limit, wait %i sec.\n", (int)((minimum_pause - (millis() - last_tx)) / 1000) + 1);
161
+        return;
162
+    }
163
+
164
+    debug.printf("TX [%s] ", String(counter).c_str());
165
+    radio.clearDio1Action();
166
+
167
+    heltec_led(LORA_LED_BRIGHTNESS);
168
+
169
+    bool success = true;
170
+    tx_time = millis();
171
+    RADIOLIB_CHECK(radio.transmit(data));
172
+    tx_time = millis() - tx_time;
173
+
174
+    heltec_led(0);
175
+
176
+    if (success) {
177
+        debug.printf("OK (%i ms)\n", (int)tx_time);
178
+    } else {
179
+        debug.println("fail");
180
+    }
181
+
182
+    // Maximum 1% duty cycle
183
+    minimum_pause = tx_time * 100;
184
+    last_tx = millis();
185
+
186
+    radio.setDio1Action(lora_rx);
187
+
188
+    success = true;
189
+    RADIOLIB_CHECK(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF));
190
+    if (!success) {
191
+        use_lora = false;
192
+    }
193
+}
194
+
195
+#ifdef FEATURE_SML
196
+void lora_sml_send(double SumWh, double T1Wh, double T2Wh,
197
+                   double SumW, double L1W, double L2W, double L3W) {
198
+
199
+}
200
+#endif // FEATURE_SML
201
+
202
+void lora_run(void) {
203
+    heltec_loop();
204
+
205
+#ifdef OLED_BAT_INTERVAL
206
+    unsigned long time = millis();
207
+    if ((time - last_bat_time) >= OLED_BAT_INTERVAL) {
208
+        last_bat_time = time;
209
+        print_bat();
210
+    }
211
+#endif
212
+
213
+    if (!use_lora) {
214
+        return;
215
+    }
216
+
217
+    // If a packet was received, display it and the RSSI and SNR
218
+    if (rx_flag) {
219
+        rx_flag = false;
220
+
221
+        bool success = true;
222
+        String data;
223
+        RADIOLIB_CHECK(radio.readData(data));
224
+        if (success) {
225
+            debug.printf("RX [%i]\n", data.length());
226
+            debug.printf("  RSSI: %.2f dBm\n", radio.getRSSI());
227
+            debug.printf("  SNR: %.2f dB\n", radio.getSNR());
228
+        }
229
+
230
+        success = true;
231
+        RADIOLIB_CHECK(radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF));
232
+        if (!success) {
233
+            use_lora = false;
234
+            return;
235
+        }
236
+    }
237
+
238
+#ifdef LORA_TEST_TX
239
+    // Transmit a packet every PAUSE seconds or when the button is pressed
240
+    bool tx_legal = millis() > last_tx + minimum_pause;
241
+    if ((PAUSE && tx_legal && millis() - last_tx > (PAUSE * 1000)) || button.isSingleClick()) {
242
+        // In case of button click, tell user to wait
243
+        if (!tx_legal) {
244
+            debug.printf("Legal limit, wait %i sec.\n", (int)((minimum_pause - (millis() - last_tx)) / 1000) + 1);
245
+            return;
246
+        }
247
+
248
+        lora_tx(String(counter++).c_str());
249
+    }
250
+#endif // LORA_TEST_TX
251
+}
252
+
253
+#endif // FEATURE_LORA

+ 33
- 2
src/main.cpp ファイルの表示

@@ -33,6 +33,8 @@
33 33
 #include "html.h"
34 34
 #include "servers.h"
35 35
 #include "ui.h"
36
+#include "lora.h"
37
+#include "smart_meter.h"
36 38
 
37 39
 unsigned long last_led_blink_time = 0;
38 40
 
@@ -57,6 +59,10 @@ void setup() {
57 59
 
58 60
     Serial.begin(115200);
59 61
 
62
+#ifdef FEATURE_LORA
63
+    lora_oled_init();
64
+#endif // FEATURE_LORA
65
+
60 66
     debug.println(F("Initializing..."));
61 67
 
62 68
     // Blink LED for init
@@ -91,6 +97,18 @@ void setup() {
91 97
     debug.println(F("Sensors"));
92 98
     initSensors();
93 99
 
100
+#ifdef FEATURE_LORA
101
+    debug.println(F("LoRa"));
102
+    lora_init();
103
+#endif // FEATURE_LORA
104
+
105
+#ifdef FEATURE_SML
106
+    debug.println(F("SML"));
107
+    sml_init();
108
+#endif // FEATURE_SML
109
+
110
+#ifndef FEATURE_DISABLE_WIFI
111
+
94 112
     // Build hostname string
95 113
     String hostname = SENSOR_HOSTNAME_PREFIX;
96 114
     hostname += SENSOR_ID;
@@ -196,7 +214,7 @@ void setup() {
196 214
     ui_progress(UI_WIFI_CONNECTED);
197 215
 #endif // FEATURE_UI
198 216
 
199
-#endif
217
+#endif // ARCH
200 218
 
201 219
     debug.println(F("Seeding"));
202 220
     randomSeed(micros());
@@ -210,6 +228,8 @@ void setup() {
210 228
     debug.println(F("Servers"));
211 229
     initServers(hostname);
212 230
 
231
+#endif // FEATURE_DISABLE_WIFI
232
+
213 233
     debug.println(F("Ready! Starting..."));
214 234
 
215 235
 #ifdef FEATURE_UI
@@ -219,15 +239,26 @@ void setup() {
219 239
 }
220 240
 
221 241
 void loop() {
222
-    runServers();
223 242
     runSensors();
243
+
244
+#ifdef FEATURE_SML
245
+    sml_run();
246
+#endif // FEATURE_SML
247
+
248
+#ifndef FEATURE_DISABLE_WIFI
249
+    runServers();
224 250
     runMQTT();
225 251
     runInflux();
252
+#endif // FEATURE_DISABLE_WIFI
226 253
 
227 254
 #ifdef FEATURE_UI
228 255
     ui_run();
229 256
 #endif
230 257
 
258
+#ifdef FEATURE_LORA
259
+    lora_run();
260
+#endif // FEATURE_LORA
261
+
231 262
     // blink heartbeat LED
232 263
     unsigned long time = millis();
233 264
     if ((time - last_led_blink_time) >= LED_BLINK_INTERVAL) {

+ 8
- 0
src/mqtt.cpp ファイルの表示

@@ -178,6 +178,10 @@ static void mqttCallback(char* topic, byte* payload, unsigned int length) {
178 178
         ui_status.light_sink = state ? true : false;
179 179
         prev_status.light_sink = ui_status.light_sink;
180 180
         ui_progress(UI_UPDATE);
181
+    } else if (ts == "livingroom/displays/cmnd/POWER") {
182
+        ui_status.pc_displays = state ? true : false;
183
+        prev_status.pc_displays = ui_status.pc_displays;
184
+        ui_progress(UI_UPDATE);
181 185
     }
182 186
 #endif // FEATURE_UI
183 187
 
@@ -252,6 +256,7 @@ static void mqttReconnect() {
252 256
         mqtt.subscribe("livingroom/amp/cmnd/POWER");
253 257
         mqtt.subscribe("bathroom/force_light");
254 258
         mqtt.subscribe("livingroom/light_sink/cmnd/POWER");
259
+        mqtt.subscribe("livingroom/displays/cmnd/POWER");
255 260
 #endif // FEATURE_UI
256 261
     }
257 262
 }
@@ -329,6 +334,9 @@ void writeMQTT_UI(void) {
329 334
     if (curr_status.light_sink != prev_status.light_sink) {
330 335
         mqttPublish("livingroom/light_sink/cmnd/POWER", curr_status.light_sink ? "on" : "off", true);
331 336
     }
337
+    if (curr_status.pc_displays != prev_status.pc_displays) {
338
+        mqttPublish("livingroom/displays/cmnd/POWER", curr_status.pc_displays ? "on" : "off", true);
339
+    }
332 340
 
333 341
     prev_status = curr_status;
334 342
 }

+ 141
- 0
src/smart_meter.cpp ファイルの表示

@@ -0,0 +1,141 @@
1
+/*
2
+ * smart_meter.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifdef FEATURE_SML
15
+
16
+#include <Arduino.h>
17
+#include <sml.h>
18
+#include <SoftwareSerial.h>
19
+
20
+#include "config.h"
21
+#include "DebugLog.h"
22
+#include "lora.h"
23
+#include "smart_meter.h"
24
+
25
+#define SML_TX 47
26
+#define SML_RX 48
27
+
28
+static EspSoftwareSerial::UART port1, port2;
29
+
30
+static double SumWh = NAN, T1Wh = NAN, T2Wh = NAN;
31
+static double SumW = NAN, L1W = NAN, L2W = NAN, L3W = NAN;
32
+
33
+static void EnergySum(void) {
34
+    smlOBISWh(SumWh);
35
+}
36
+
37
+static void EnergyT1(void) {
38
+    smlOBISWh(T1Wh);
39
+}
40
+
41
+static void EnergyT2(void) {
42
+    smlOBISWh(T2Wh);
43
+}
44
+
45
+static void PowerSum(void) {
46
+    smlOBISW(SumW);
47
+}
48
+
49
+static void PowerL1(void) {
50
+    smlOBISW(L1W);
51
+}
52
+
53
+static void PowerL2(void) {
54
+    smlOBISW(L2W);
55
+}
56
+
57
+static void PowerL3(void) {
58
+    smlOBISW(L3W);
59
+}
60
+
61
+static void init_vars(void) {
62
+    SumWh = NAN;
63
+    T1Wh = NAN;
64
+    T2Wh = NAN;
65
+    SumW = NAN;
66
+    L1W = NAN;
67
+    L2W = NAN;
68
+    L3W = NAN;
69
+}
70
+
71
+typedef struct {
72
+    const unsigned char OBIS[6];
73
+    void (*Handler)();
74
+} OBISHandler;
75
+
76
+static OBISHandler handlers[] = {
77
+    { { 1, 0,  1, 8, 0, 255 }, EnergySum }, // 1-0: 1.8.0*255 (T1 + T2) Wh
78
+    { { 1, 0,  1, 8, 1, 255 }, EnergyT1 },  // 1-0: 1.8.1*255 (T1) Wh
79
+    { { 1, 0,  1, 8, 2, 255 }, EnergyT2 },  // 1-0: 1.8.2*255 (T2) Wh
80
+    { { 1, 0, 16, 7, 0, 255 }, PowerSum },  // 1-0:16.7.0*255 (L1 + L2 + L3) W
81
+    { { 1, 0, 21, 7, 0, 255 }, PowerL1 },   // 1-0:21.7.0*255 (L1) W
82
+    { { 1, 0, 41, 7, 0, 255 }, PowerL2 },   // 1-0:41.7.0*255 (L2) W
83
+    { { 1, 0, 61, 7, 0, 255 }, PowerL3 },   // 1-0:61.7.0*255 (L3) W
84
+};
85
+
86
+void sml_init(void) {
87
+    init_vars();
88
+
89
+    port1.begin(9600, SWSERIAL_8N1, SML_RX, -1, false);
90
+    port2.begin(9600, SWSERIAL_8N1, SML_TX, -1, false);
91
+}
92
+
93
+void sml_run(void) {
94
+    if ((!port1.available()) && (!port2.available())) {
95
+        return;
96
+    }
97
+
98
+    unsigned char c = port1.available() ? port1.read() : port2.read();
99
+    sml_states_t s = smlState(c);
100
+
101
+    if (s == SML_START) {
102
+        init_vars();
103
+    } else if (s == SML_LISTEND) {
104
+        for (unsigned int i = 0; i < (sizeof(handlers) / sizeof(handlers[0])); i++) {
105
+            if (smlOBISCheck(handlers[i].OBIS)) {
106
+                handlers[i].Handler();
107
+            }
108
+        }
109
+    } else if (s == SML_FINAL) {
110
+        debug.println("SML Reading:");
111
+
112
+        if (!isnan(SumWh))
113
+            debug.printf("Sum: %14.3lf Wh\n", SumWh);
114
+
115
+        if (!isnan(T1Wh))
116
+            debug.printf(" T1: %14.3lf Wh\n", T1Wh);
117
+
118
+        if (!isnan(T2Wh))
119
+            debug.printf(" T2: %14.3lf Wh\n", T2Wh);
120
+
121
+        if (!isnan(SumW))
122
+            debug.printf("Sum: %14.3lf W\n", SumW);
123
+
124
+        if (!isnan(L1W))
125
+            debug.printf(" L1: %14.3lf W\n", L1W);
126
+
127
+        if (!isnan(L2W))
128
+            debug.printf(" L2: %14.3lf W\n", L2W);
129
+
130
+        if (!isnan(L3W))
131
+            debug.printf(" L3: %14.3lf W\n", L3W);
132
+
133
+        debug.println();
134
+
135
+#ifdef FEATURE_LORA
136
+        lora_sml_send(SumWh, T1Wh, T2Wh, SumW, L1W, L2W, L3W);
137
+#endif // FEATURE_LORA
138
+    }
139
+}
140
+
141
+#endif // FEATURE_SML

+ 13
- 2
src/ui.cpp ファイルの表示

@@ -154,7 +154,7 @@ static void draw_livingroom1(void) {
154 154
     // 5
155 155
     bool on = ui_status.light_corner || ui_status.light_sink || ui_status.light_workspace
156 156
             || ui_status.light_amp || ui_status.light_bench || ui_status.light_box
157
-            || ui_status.light_kitchen || ui_status.light_pc;
157
+            || ui_status.light_kitchen || ui_status.light_pc || ui_status.pc_displays;
158 158
     draw_button(on ? "All Lights Off" : "Wake Up Lights",
159 159
                 BTNS_OFF_X + BTN_W / 2 + BTN_W + BTN_GAP,
160 160
                 BTNS_OFF_Y + BTN_H / 2 + BTN_H + BTN_GAP,
@@ -206,6 +206,13 @@ static void draw_bathroom(void) {
206 206
                 BTNS_OFF_Y + BTN_H / 2 + BTN_H + BTN_GAP,
207 207
                 ui_status.bathroom_lights == BATH_LIGHT_BIG ? TFT_GREEN : TFT_RED);
208 208
 
209
+    // 3
210
+    // TODO own page?
211
+    draw_button("PC Displays",
212
+                BTNS_OFF_X + BTN_W / 2,
213
+                BTNS_OFF_Y + BTN_H / 2 + (BTN_H + BTN_GAP) * 2,
214
+                ui_status.pc_displays ? TFT_GREEN : TFT_RED);
215
+
209 216
     // 4
210 217
     draw_button("Bath Lights Off",
211 218
                 BTNS_OFF_X + BTN_W / 2 + BTN_W + BTN_GAP,
@@ -585,6 +592,8 @@ void ui_run(void) {
585 592
                 INVERT_BOOL(ui_status.light_sink);
586 593
             } else if (ui_page == UI_LIVINGROOM2) {
587 594
                 INVERT_BOOL(ui_status.light_kitchen);
595
+            } else if (ui_page == UI_BATHROOM) {
596
+                INVERT_BOOL(ui_status.pc_displays);
588 597
             }
589 598
             writeMQTT_UI();
590 599
         } else if ((p.x >= BTNS_OFF_X + BTN_W + BTN_GAP) && (p.x <= BTNS_OFF_X + BTN_W + BTN_GAP + BTN_W) && (p.y >= BTNS_OFF_Y) && (p.y <= BTNS_OFF_Y + BTN_H)) {
@@ -602,7 +611,7 @@ void ui_run(void) {
602 611
             if (ui_page == UI_LIVINGROOM1) {
603 612
                 bool on = ui_status.light_corner || ui_status.light_sink || ui_status.light_workspace
604 613
                         || ui_status.light_amp || ui_status.light_bench || ui_status.light_box
605
-                        || ui_status.light_kitchen || ui_status.light_pc;
614
+                        || ui_status.light_kitchen || ui_status.light_pc || ui_status.pc_displays;
606 615
                 if (on) {
607 616
                     ui_status.light_amp = false;
608 617
                     ui_status.light_kitchen = false;
@@ -612,9 +621,11 @@ void ui_run(void) {
612 621
                     ui_status.light_corner = false;
613 622
                     ui_status.light_box = false;
614 623
                     ui_status.light_sink = false;
624
+                    ui_status.pc_displays = false;
615 625
                 } else {
616 626
                     ui_status.light_corner = true;
617 627
                     ui_status.light_sink = true;
628
+                    ui_status.pc_displays = true;
618 629
                 }
619 630
             } else if (ui_page == UI_LIVINGROOM2) {
620 631
                 INVERT_BOOL(ui_status.light_box);

読み込み中…
キャンセル
保存