5 Commits

Author SHA1 Message Date
  Thomas Buck 52577ab1b1 hook up sml parsing to softserial interface 2 months ago
  Thomas Buck cd70b4877a test parsing of an actual sml dump from my meter 2 months ago
  Thomas Buck 4585b3c25b lora rx tx test with unofficial lib 2 months ago
  Thomas Buck e70faaa5d9 start experimenting with lora 2 months ago
  Thomas Buck f587c66e75 add pc displays to ui 2 months ago
13 changed files with 599 additions and 12 deletions
  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 View File

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

+ 1
- 1
compile_commands.json View File

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

+ 8
- 5
include/DebugLog.h View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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);

Loading…
Cancel
Save