├── LICENSE.md
├── README.md
├── VE.Direct2MQTT
├── VE.Direct2MQTT.ino
├── VEDirect.h
├── config.h
├── vedirectEEPROM.h
├── vedirectMQTT.h
├── vedirectONEWIRE.h
├── vedirectOTA.h
├── vedirectSerial.h
└── vedirectWiFi.h
└── pictures
├── MyVEDirectHardware.jpg
└── esp32.gif
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Ralf Lehmann
4 |
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Victron is building wonderful devices for Solar systems.
3 | Some of the devices have a VE.Direct interface and Victron disclosed the protocol.
4 |
5 | - See also: https://www.victronenergy.com/live/vedirect_protocol:faq
6 |
7 | I do own 2 MPTT75/15 Solar charger and all that I was missing was a MQTT interface for monitoring.
8 | I do NOT plan or intend to augment or replace the, in my eyes, perfect bluetooth app.
9 |
10 | So here it is
11 |
12 | # VE.Direct2MQTT
13 |
14 | VE.Direct2MQTT is using an ESP32 developers board and the Arduino IDE to send all ASCII data coming from a VE.Direct device to a MQTT server.
15 |
16 | With the help of the MQTT server you can integrate the monitoring data to virtually any Home Automation System.
17 |
18 | ## Features
19 | - Listen to VE.Direct messages and publish a block (consisting of several key-value pairs) to a MQTT broker
Every key from the device will be appended to the MQTT_PREFIX and build a topic. e.g. MQTT_PREFIX="/MPPT"; Topic /MPPT/V will contain the Battery Voltage
so please see the VE.Direct protocol for the meaning of topics
20 | - SSL enabled
If you are sending messages over the internet or using User/Passwords over the Internet you have to use SSL.
If you are using a locally protected network you can disable the Usage of SSL in the config file
21 | - Have several WiFi SSID's to connect to, in case one or the other is not reachable from your position
22 | - Have sveral MQTT Servers in case one is down.
The system will only be bound to one MQTT server at a time
23 | - Have several OneWire temperature sensors
So you can see the temperature of e.g. the MPPT Solracharger or the batteries or your inverter, ...
24 | - Timing parameters can be changed via MQTT
E.g. you can set that VE.Direct blocks are only transmitted every 10 seconds. The last received block will be transmitted, all other blocks are lost
25 | - OTA (Over The Air Update)
If you have a webserver where you can put binary files on and run php scripts you can use that server to install new VE.Direct2MQTT software on your ESP32
Please make sure that you use SSL and User/Password
26 | - One config file to enable/disable features and configure serial port or MQTT Topics
27 |
28 |
29 | ## Limitations
30 | - VE.Direct2MQTT is only listening to messages of the VE.Direct device
It understands only the "ASCII" part of the protocol that is only good to receive a set of values. You can't request any special data or change any parameters of the VE.Direct device.
This is intentionally because this is monitoring only
31 | - If you transmit a block only every 10 seconds, 9 previous blocks will be lost, because VE.Direct devices publish every second
This is normally not a problem.
32 | - During sending OneWire Data or OTA checking, no VE.Direct blocks will be sent.
This is a limitation of the ESP32, having several tasks tranmitting in parallel caused crashes of the device
33 |
34 | ## Hardware Installation
35 | 
36 |
37 | ## Software configuration
38 | 
39 |
40 | ## Debugging
41 | 
42 |
43 | ## Disclaimer
44 | I WILL NOT BE HELD LIABLE FOR ANY DAMAGE THAT YOU DO TO YOU OR ONE OF YOUR DEVICES.
45 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/VE.Direct2MQTT.ino:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct to MQTT Gateway using a ESP32 Board
3 | Collect Data from VE.Direct device like Victron MPPT 75/15 and send it
4 | to a MQTT gateway. From there you can integrate the data into any
5 | Home Automation Software like ioBroker annd make graphs.
6 |
7 | The ESP32 will read data from the VE.Direct interface and transmit the
8 | data via WiFi to a MQTT broker
9 |
10 | GITHUB Link
11 |
12 | MIT License
13 |
14 | Copyright (c) 2020 Ralf Lehmann
15 |
16 |
17 | Permission is hereby granted, free of charge, to any person obtaining a copy
18 | of this software and associated documentation files (the "Software"), to deal
19 | in the Software without restriction, including without limitation the rights
20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 | copies of the Software, and to permit persons to whom the Software is
22 | furnished to do so, subject to the following conditions:
23 |
24 | The above copyright notice and this permission notice shall be included in all
25 | copies or substantial portions of the Software.
26 |
27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 | SOFTWARE.
34 | */
35 |
36 |
37 | /*
38 | All configuration comes from config.h
39 | So please see there for WiFi, MQTT and OTA configuration
40 | */
41 | #include "config.h"
42 |
43 | time_t last_boot;
44 |
45 | #include "TimeLib.h"
46 | #include "vedirectEEPROM.h"
47 | mEEPROM pref;
48 | #include "VEDirect.h"
49 | #include "vedirectWiFi.h"
50 | #include "vedirectMQTT.h"
51 | #ifdef USE_ONEWIRE
52 | #include "vedirectONEWIRE.h"
53 | #endif
54 | #ifdef USE_OTA
55 | #include "vedirectOTA.h"
56 | #endif
57 |
58 |
59 |
60 | VEDirect ve;
61 | time_t last_vedirect;
62 |
63 |
64 | void setup() {
65 | Serial.begin(115200);
66 | // fetch previously stored parameters from EEPROM
67 | VE_WAIT_TIME = pref.getInt("VE_WAIT_TIME", VE_WAIT_TIME);
68 | #ifdef USE_OTA
69 | OTA_WAIT_TIME = pref.getInt("OTA_WAIT_TIME", OTA_WAIT_TIME);
70 | #endif
71 | #ifdef USE_ONEWIRE
72 | OW_WAIT_TIME = pref.getInt("OW_WAIT_TIME", OW_WAIT_TIME);
73 | #endif
74 | if ( startWiFi()) {
75 | setClock();
76 | last_boot = time(nullptr);
77 | if ( startMQTT()) {
78 | ve.begin();
79 | // looking good; moving to loop
80 | return;
81 | }
82 | }
83 | // oh oh, we did not get WiFi or MQTT, that is bad; we can't continue
84 | // wait a while and reboot to try again
85 | delay(20000);
86 | ESP.restart();
87 | }
88 |
89 | void loop() {
90 | VEDirectBlock_t block;
91 | time_t t = time(nullptr);
92 | #ifdef USE_ONEWIRE
93 | if ( abs(t - last_ow) >= OW_WAIT_TIME) {
94 | if ( checkWiFi()) {
95 | sendOneWireMQTT();
96 | last_ow = t;
97 | sendOPInfo();
98 | }
99 | }
100 | #endif
101 | #ifdef USE_OTA
102 | if ( abs(t - last_ota) >= OTA_WAIT_TIME) {
103 | if ( checkWiFi()) {
104 | startOTA(SKETCH_NAME);
105 | last_ota = t;
106 | }
107 | }
108 | #endif
109 | if ( abs( t - last_vedirect) >= VE_WAIT_TIME) {
110 | if ( ve.getNewestBlock(&block)) {
111 | last_vedirect = t;
112 | log_i("New block arrived; Value count: %d, serial %d", block.kvCount, block.serial);
113 | if ( checkWiFi()) {
114 | sendASCII2MQTT(&block);
115 | // sendOPInfo();
116 | }
117 | }
118 | }
119 | if ( ! espMQTT.loop()){
120 | log_i("MQTT connection lost restart");
121 | startMQTT();
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/VEDirect.h:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct Protocol.
3 |
4 | GITHUB Link
5 |
6 | MIT License
7 |
8 | Copyright (c) 2020 Ralf Lehmann
9 |
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | SOFTWARE.
28 | */
29 |
30 | #ifndef VEDIRECT_H
31 | #define VEDIRECT_H
32 |
33 | #include
34 | #include "vedirectSerial.h"
35 |
36 | typedef struct VEDirectKeyValue_t {
37 | String key;
38 | String value;
39 | };
40 |
41 | typedef struct VEDirectBlock_t {
42 | VEDirectKeyValue_t b[MAX_KEY_VALUE_COUNT];
43 | int kvCount;
44 | int serial;
45 | };
46 |
47 |
48 |
49 |
50 | class VEDirect {
51 | public:
52 | void begin();
53 | boolean addToASCIIBlock(String s);
54 | boolean getNewestBlock(VEDirectBlock_t *b);
55 |
56 |
57 | private:
58 |
59 | int _serial = 0; // Serial number of the block
60 | boolean _endOfASCIIBlock(StringSplitter *s);
61 | void _increaseNewestBlock();
62 | int _getNewestBlock();
63 | VEDirectBlock_t block[MAX_BLOCK_COUNT];
64 | portMUX_TYPE _new_block_mutex = portMUX_INITIALIZER_UNLOCKED; // mutex to protect _newest_block
65 | volatile int _newest_block = -1; // newest complete block; ready for consumption
66 | volatile boolean _has_new_block = false;
67 | int _incoming_block = 0; // block currently filled; candidate for next newest block
68 | int _incoming_keyValueCount = 0;
69 | TaskHandle_t tHandle = NULL;
70 |
71 | };
72 |
73 | /*
74 | This task will collect the data from Serial1 and place them in a block buffer
75 | The task is independant from the main task so it should not loose data because
76 | of MQTT reconnects or other time consuming duties in the main task
77 | */
78 | void serialTask(void * pointer) {
79 | VEDirect *ve = (VEDirect *) pointer;
80 | String data;
81 | startVEDirectSerial();
82 | while ( true ) {
83 | if ( Serial1.available()) {
84 | // char s = Serial1.read();
85 | // log_d("Read: %c:%d", s, s);
86 | // if ( s == '\n') {
87 | // log_d("received start");
88 | // begin of a datafield or frame
89 | data = Serial1.readStringUntil('\n'); // read label and value
90 | //log_d("Received Data: \"%s\"", data.c_str());
91 | if ( data.length() > 0) {
92 | data.replace("\r\n", ""); // Strip carriage return newline; not part of the data
93 | ve->addToASCIIBlock(data);
94 | log_d("Stack free: %5d", uxTaskGetStackHighWaterMark(NULL));
95 | }
96 | //}
97 | } else {
98 | // no serial data available; have a nap
99 | delay(1);
100 | }
101 | }
102 | }
103 |
104 |
105 |
106 |
107 | void VEDirect::begin() {
108 | _newest_block = -1;
109 | _incoming_block = _incoming_keyValueCount = 0;
110 | // create a task to handle the serial input
111 | xTaskCreate(
112 | serialTask, /* Task function. */
113 | "serialTask", /* String with name of task. */
114 | 10000, /* Stack size in bytes. */
115 | this, /* Parameter passed as input of the task */
116 | 1, /* Priority of the task. */
117 | &tHandle); /* Task handle. */
118 | }
119 |
120 | void VEDirect::_increaseNewestBlock() {
121 | taskENTER_CRITICAL(&_new_block_mutex);
122 | _newest_block++;
123 | _newest_block %= MAX_BLOCK_COUNT;
124 | taskEXIT_CRITICAL(&_new_block_mutex);
125 | _has_new_block = true;
126 | }
127 |
128 | int VEDirect::_getNewestBlock() {
129 | int n;
130 | taskENTER_CRITICAL(&_new_block_mutex);
131 | n = _newest_block;
132 | taskEXIT_CRITICAL(&_new_block_mutex);
133 | return n;
134 | }
135 |
136 | boolean VEDirect::_endOfASCIIBlock(StringSplitter *s) {
137 | if ( s->getItemAtIndex(0).equals("Checksum")) {
138 | // To Do: checksum test
139 | return true;
140 | }
141 | return false;
142 | }
143 |
144 | boolean VEDirect::addToASCIIBlock(String s) {
145 | StringSplitter sp = StringSplitter(s, '\t', 2);
146 | String historical = "";
147 | if ( sp.getItemCount() == 2) { // sometime checksum has historical data attached
148 | log_d("Received Key/value: \"%s\":\"%s\"", sp.getItemAtIndex(0).c_str(), sp.getItemAtIndex(1).c_str());
149 | if ( _incoming_keyValueCount < MAX_KEY_VALUE_COUNT) {
150 | block[_incoming_block].b[_incoming_keyValueCount].key = sp.getItemAtIndex(0);
151 | if ( sp.getItemAtIndex(0).equals("Checksum")) {
152 | char s[10];
153 | sprintf(s, "%02x", sp.getItemAtIndex(1).charAt(0));
154 | block[_incoming_block].b[_incoming_keyValueCount].value = String(s);
155 | historical = sp.getItemAtIndex(1).substring(1);
156 | } else {
157 | block[_incoming_block].b[_incoming_keyValueCount].value = sp.getItemAtIndex(1);
158 | }
159 | block[_incoming_block].kvCount = ++_incoming_keyValueCount;
160 | if ( historical.length() > 1) {
161 | // historical data
162 | block[_incoming_block].b[_incoming_keyValueCount].key = "Historical";
163 | block[_incoming_block].b[_incoming_keyValueCount].value = historical;
164 | log_d("Historical data:\"%s\"", historical.c_str());
165 | block[_incoming_block].kvCount = ++_incoming_keyValueCount;
166 | }
167 | } else {
168 | // buffer full but not end of frame
169 | // so this is not a good frame -> delete frame
170 | _incoming_keyValueCount = 0;
171 | return false; // buffer full
172 | }
173 | if ( _endOfASCIIBlock(&sp)) {
174 | // good frame; increase newest frame pointer
175 | block[_incoming_block].serial = _serial++;
176 | _increaseNewestBlock();
177 | _incoming_keyValueCount = 0;
178 | _incoming_block++;
179 | _incoming_block %= MAX_BLOCK_COUNT;
180 | return true;
181 | }
182 | // not the end of a frame yet; continue
183 | return false;
184 | } else {
185 | log_e("Receviced Data not correct: \"%s\"", s.c_str());
186 | //delete splitter;
187 | return false;
188 | }
189 | }
190 |
191 | boolean VEDirect::getNewestBlock(VEDirectBlock_t *b) {
192 | if ( ! _has_new_block) {
193 | return false;
194 | }
195 | _has_new_block = false;
196 | int block_num = _getNewestBlock();
197 | int kv_count = block[block_num].kvCount;
198 | b->kvCount = kv_count;
199 | int ser = block[block_num].serial;
200 | b->serial = ser;
201 | for (int i = 0; i < kv_count; i++) {
202 | b->b[i] = block[block_num].b[i];
203 | }
204 | return true;
205 | }
206 |
207 |
208 |
209 | #endif
210 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/config.h:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct config file.
3 | Cconfiguration parameters for
4 | VE.Direct2MQTT gateway
5 |
6 | GITHUB Link
7 |
8 | MIT License
9 |
10 | Copyright (c) 2020 Ralf Lehmann
11 |
12 |
13 | Permission is hereby granted, free of charge, to any person obtaining a copy
14 | of this software and associated documentation files (the "Software"), to deal
15 | in the Software without restriction, including without limitation the rights
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | copies of the Software, and to permit persons to whom the Software is
18 | furnished to do so, subject to the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be included in all
21 | copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 | SOFTWARE.
30 | */
31 |
32 | /*
33 | Defines that activate features like OTA (over the air update)
34 | or Acitve/Passive mode
35 | */
36 | // use SSL to connect to MQTT Server or OTA Server
37 | // it is strongly recommended to use SSL if you send any password over the net
38 | // connectiong to MQTT might need a password; the same for OTA
39 | // if you do not have SSL activated on your servers rename it to NO_USE_SSL
40 | #define NO_USE_SSL
41 |
42 | // Activate Over The Air Update of firmware
43 | // rename to NO_USE_OTA if you do not have a webserver that can server new firmware
44 | #define USE_OTA
45 |
46 | //
47 | // Use OneWire temperature sensors
48 | //
49 | #define USE_ONEWIRE
50 |
51 | #ifdef USE_ONEWIRE
52 | #define ONEWIRE_PIN 22
53 | /*
54 | define the wait time between 2 attempts to send one wire data
55 | 300000 = every 5 minutes
56 | */
57 | int OW_WAIT_TIME = 10; // in s
58 | time_t last_ow;
59 | #endif
60 |
61 |
62 |
63 | #ifdef USE_SSL
64 | /*
65 | SSL certificate
66 | This is good for all let's encrypt certificates for MQTT or OTA servers
67 | */
68 | /*
69 | This is lets-encrypt-x3-cross-signed.pem
70 | */
71 | const char* rootCACertificate = \
72 | "-----BEGIN CERTIFICATE-----\n" \
73 | "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
74 | "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
75 | "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
76 | "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
77 | "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
78 | "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
79 | "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
80 | "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
81 | "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
82 | "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
83 | "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
84 | "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
85 | "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
86 | "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
87 | "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
88 | "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
89 | "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
90 | "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
91 | "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
92 | "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
93 | "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
94 | "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
95 | "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
96 | "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
97 | "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
98 | "-----END CERTIFICATE-----\n";
99 |
100 | #endif
101 |
102 |
103 | /*
104 | WiFi parameters
105 | */
106 |
107 | // WiFi SSID'S and passwords
108 | // the strongest WiFi station will be used
109 | const char* ssid[] = {"SSID1", "SSID2", "SSID3"};
110 | const char* pw[] = {"PW_SSID1", "PW_SSID2", "PW_SSID3"};
111 |
112 | /*
113 | MQTT parameters
114 | you can have more than one MQTT server, the first one that answers will have the connection
115 | it is strongly recommended to use SSL if you send a username and password over the internet
116 | ATTENTION: use a unique client id to connect to MQTT or you will be kicked out by another device
117 | using your id
118 | */
119 | #define MQTT_MAX_RETRIES 3 // maximum retires to reach a MQTT broker
120 | const char* mqtt_server[] = {"IP_MQTT1", "IP_MQTT2"};
121 | // no SSL ports
122 | const uint16_t mqtt_port[] = {1883, 1883};
123 | // SSL ports
124 | //const uint16_t mqtt_port[] = {8883, 8883};
125 | const char* mqtt_clientID[] = {"clientID", "clientID"};
126 | const char* mqtt_username[] = {"mqttUserName", "mqttUserName"};
127 | const char* mqtt_pw[] = {"mqttUserPW", "mqttUserPW"};
128 | int mqtt_server_count = sizeof(mqtt_server) / sizeof(mqtt_server[0]);
129 |
130 | // this is the MQTT prefix; below that we use the string from VE.Direct
131 | // e.g. /MPPT75-15/PID for Product ID
132 | String MQTT_PREFIX = "/MPPT75-15NOSSL";
133 | String MQTT_PARAMETER = "/MPPT75-15NOSSL/Parameter";
134 | #ifdef USE_ONEWIRE
135 | String MQTT_ONEWIRE = "/MPPT75-15NOSSL/OneWire";
136 | #endif
137 |
138 |
139 | #ifdef USE_OTA
140 | /*
141 | the binary file to look for
142 | */
143 | #define SKETCH_NAME "VE.Direct2MQTT.ino.esp32.bin" // sketch name and binary
144 | /*
145 | the URL of he update
146 | Note: the php script file.php returns a 302 "not modified" if the checksum of the current sketch and the binary
147 | is the same
148 | URL:
149 | http://username:pw@servername/bin/file.php?
150 | */
151 | const char* ota_server_string[] = {"http://user:pw@serverIP/bin/file.php?", "http://user:pw@serverName/bin/file.php?"};
152 | int ota_server_count = sizeof(ota_server_string) / sizeof(ota_server_string[0]);
153 |
154 | /*
155 | define the wait time between 2 attempts to update the firmware
156 | 300000 = every 5 minutes
157 | */
158 | int OTA_WAIT_TIME = 300; // in s
159 | time_t last_ota;
160 | #endif
161 |
162 | /*
163 | Software serial parameter
164 | These are the pins for the VE.Direct connection
165 | WARNING: if your VE.Direct device uses 5V please use a 1kOhm/2kOhm divider for the receive line
166 | The sending line does not need any modification. The ESP uses 3.3V and that's it. A 5V device
167 | should be able to read that voltage as input
168 | */
169 | #ifndef VEDIRECT_RX
170 | #define VEDIRECT_RX 33 // connected to TX of the VE.Direct device; ATTENTION divider may be needed, see abowe
171 | #endif
172 | #ifndef VEDIRECT_TX
173 | #define VEDIRECT_TX 32 // connected to RX of the VE:DIRECT device
174 | #endif
175 |
176 | /*
177 | Depending on the DE.Direct device there will be several Key/Value pairs;
178 | Define the maximum count of key/value pairs
179 | */
180 | #define MAX_KEY_VALUE_COUNT 30
181 |
182 | /*
183 | Number of Key-Value blocks we can buffer
184 | MQTT may be slower than one second, especially when we have to reconnect
185 | this is the number of buffers we can keep
186 | */
187 | #define MAX_BLOCK_COUNT 5
188 |
189 | /*
190 | Wait time in Loop
191 | this determines how many frames are send to MQTT
192 | if wait time is e.g. 10 minutes, we will send only every 10 minutes to MQTT
193 | Note: only the last incoming block will be send; all previous blocks will be discarded
194 | Wait time is in seconds
195 | Waittime of 1 or 0 means every received packet will be transmitted to MQTT
196 | Packets during OTA or OneWire will be discarded
197 | */
198 | int VE_WAIT_TIME = 1; // in s
199 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/vedirectEEPROM.h:
--------------------------------------------------------------------------------
1 | /*
2 | Store alarms and preferences in NVRAM
3 | for Bewässerung
4 |
5 | Ralf Lehmann
6 | 12.2019
7 | */
8 |
9 | #ifndef VEDIRECTEEPROM_H
10 | #define VEDIRECTEEPROM_H
11 |
12 |
13 | // Constants
14 | // no key larger than 15 chars
15 |
16 |
17 |
18 | #include
19 | #define PREF_NAME_SPACE "VE2MQTT" //
20 |
21 | class mEEPROM {
22 | public:
23 | mEEPROM();
24 | String getString(String key, String devault_value);
25 | String getString(int key, String devault_value);
26 | boolean setString(String key, String value);
27 | boolean setString(int key, String value);
28 | int32_t getInt(int key, int default_value);
29 | int32_t getInt(String key, int default_value);
30 | boolean setInt(int key, int32_t value);
31 | boolean setInt(String key, int32_t value);
32 |
33 |
34 | private:
35 | Preferences _preferences;
36 | };
37 |
38 | mEEPROM::mEEPROM() {
39 | // Open Preferences with my-app namespace. Each application module, library, etc
40 | // has to use a namespace name to prevent key name collisions. We will open storage in
41 | // RW-mode (second parameter has to be false).
42 | // Note: Namespace name is limited to 15 chars.
43 |
44 | }
45 |
46 | int32_t mEEPROM::getInt(String key, int default_value = 0) {
47 | _preferences.begin(PREF_NAME_SPACE, false);
48 | int32_t ret = _preferences.getInt(key.c_str(), default_value);
49 | log_d("PrefGetInt; \'%s\' = \'%d\'", key.c_str(), ret);
50 | _preferences.end();
51 | return ret;
52 | }
53 |
54 | boolean mEEPROM::setInt(String key, int32_t value) {
55 | _preferences.begin(PREF_NAME_SPACE, false);
56 | _preferences.putInt(key.c_str(), value);
57 | log_d("PrefPutInt; \'%s\' = \'%d\'", key.c_str(), value);
58 | _preferences.end();
59 | return true;
60 | }
61 |
62 | int32_t mEEPROM::getInt(int key, int default_value = 0) {
63 | return mEEPROM::getInt(String(key), default_value);
64 | }
65 |
66 | boolean mEEPROM::setInt(int key, int32_t value = 0) {
67 | return mEEPROM::setInt(String(key), value);
68 | }
69 |
70 | String mEEPROM::getString(int key, String default_value = ""){
71 | return getString(String(key), default_value);
72 | }
73 |
74 | String mEEPROM::getString(String key, String default_value = "") {
75 | _preferences.begin(PREF_NAME_SPACE, false);
76 | String ret = _preferences.getString(key.c_str(), default_value.c_str());
77 | log_d("PrefGetStr: \'%s\' = \'%s\'", key.c_str(), ret.c_str());
78 | _preferences.end();
79 | return ret;
80 | }
81 |
82 | boolean mEEPROM::setString(int key, String value){
83 | return setString(String(key), value);
84 | }
85 |
86 | boolean mEEPROM::setString(String key, String value) {
87 | _preferences.begin(PREF_NAME_SPACE, false);
88 | _preferences.putString(key.c_str(), value.c_str());
89 | log_d("PrefSetStr: \'%s\' = \'%s\'", key.c_str(), value.c_str());
90 | _preferences.end();
91 | return true;
92 | }
93 |
94 |
95 | #endif
96 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/vedirectMQTT.h:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct MQTT code.
3 |
4 | GITHUB Link
5 |
6 | MIT License
7 |
8 | Copyright (c) 2020 Ralf Lehmann
9 |
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | SOFTWARE.
28 | */
29 |
30 | #ifndef vedirectMQTT_H
31 | #define vedirectMQTT_H
32 |
33 | #include
34 | #include "PubSubClient.h"
35 |
36 | volatile boolean mqtt_param_rec = false; // we received a parameter via MQTT; remove it or it will be received over and over again
37 |
38 | PubSubClient espMQTT(espClient);
39 |
40 | boolean reconnect(const char* server, uint16_t port, const char* id, const char* user, const char* pw) {
41 | espMQTT.setServer(server, port);
42 | // Loop until we're reconnected
43 | int j = MQTT_MAX_RETRIES;
44 | while (!espMQTT.connected() && j-- > 0) {
45 | log_i("Attempting MQTT connection to server: %s, try: %d", server, j);
46 | // Attempt to connect
47 | if (espMQTT.connect(id, user, pw)) {
48 | log_i("connected");
49 | log_i("Subscribing to: %s", MQTT_PARAMETER.c_str());
50 | espMQTT.subscribe(MQTT_PARAMETER.c_str(), 1);
51 | return true;
52 | } else {
53 | log_e("failed, rc= %d", espMQTT.state());
54 | log_e(" try again in 5 seconds");
55 | // Wait 5 seconds before retrying
56 | delay(5000);
57 | }
58 | }
59 | return false;
60 | }
61 |
62 | //
63 | // Callback function; Receiver
64 | // currently only as dummy
65 | //
66 | void callback(const char* topic, byte * payload, unsigned int length) {
67 | mqtt_param_rec = false; // did we receive a parameter?
68 | char s[length + 1]; strncpy(s, (char *)payload, length); s[length] = '\0';
69 | log_d("MQTT callback received: %s - \"%s\"", topic, s);
70 | String t = String(topic);
71 | if ( t.equals(MQTT_PARAMETER)) {
72 | log_d("MQTT received new parameters");
73 |
74 | const int capacity = JSON_OBJECT_SIZE(10); // no clue how much data is coming in
75 | StaticJsonDocument doc;
76 | DeserializationError err = deserializeJson(doc, s, length);
77 | if ( err ) {
78 | log_d("ERROR: deserializing MQTT message: %s", err.c_str());
79 | return;
80 | }
81 | JsonObject obj = doc.as();
82 | #ifdef DEBUG
83 | for ( JsonPair kv : obj) {
84 | log_d("key: %s, Value %s", kv.key().c_str(), kv.value().as().c_str());
85 | }
86 | #endif
87 |
88 | JsonVariant sleeptime = obj["VE_WAIT_TIME"];
89 | if ( sleeptime ) {
90 | mqtt_param_rec = true; // this will cause the parameter to be erased from queue
91 | int st = sleeptime.as();
92 | log_d("Found member VEDIRECT_WAIT_TIME: %d", st);
93 | if ( VE_WAIT_TIME != st) {
94 | VE_WAIT_TIME = st;
95 | // store in EEPROM
96 | pref.setInt("VE_WAIT_TIME", st);
97 | }
98 | }
99 |
100 | #ifdef USE_OTA
101 | JsonVariant ota_sleeptime = obj["OTA_WAIT_TIME"];
102 | if ( ota_sleeptime ) {
103 | mqtt_param_rec = true; // this will cause the parameter to be erased from queue
104 | int st = ota_sleeptime.as();
105 | log_d("Found member OTA_WAIT_TIME: %d", st);
106 | if ( OTA_WAIT_TIME != st) {
107 | OTA_WAIT_TIME = st;
108 | // store in EEPROM
109 | pref.setInt("OTA_WAIT_TIME", st);
110 | }
111 | }
112 | #endif
113 | #ifdef USE_ONEWIRE
114 | JsonVariant ow_sleeptime = obj["OW_WAIT_TIME"];
115 | if ( ow_sleeptime ) {
116 | mqtt_param_rec = true; // this will cause the parameter to be erased from queue
117 | int st = ow_sleeptime.as();
118 | log_d("Found member OW_WAIT_TIME: %d", st);
119 | if ( OW_WAIT_TIME != st) {
120 | OW_WAIT_TIME = st;
121 | // store in EEPROM
122 | pref.setInt("OW_WAIT_TIME", st);
123 | }
124 | }
125 | #endif
126 | }
127 | }
128 |
129 | //
130 | // Startup
131 | //
132 | boolean startMQTT() {
133 | #ifdef USE_SSL
134 | espClient.setCACert(rootCACertificate);
135 | #endif
136 | // receive parameter via MQTT
137 | log_d("MQTT callback setting");
138 | espMQTT.setCallback(callback);
139 | for ( int i = 0; i < mqtt_server_count; i++) {
140 | if (! espMQTT.connected()) {
141 | log_d("Connecting to MQTT server: %s port: %d", mqtt_server[i], mqtt_port[i]);
142 | if ( reconnect(mqtt_server[i], mqtt_port[i], mqtt_clientID[i], mqtt_username[i], mqtt_pw[i])) {
143 | return true; // done got the server
144 | }
145 | }
146 | }
147 | return false; // no server available
148 | }
149 |
150 | //
151 | // end MQTT
152 | //
153 | boolean endMQTT() {
154 | espMQTT.loop();
155 | log_d("MQTT disconnect");
156 | espMQTT.disconnect();
157 | return true;
158 | }
159 |
160 | //
161 | // Send ASCII data from passive mode to MQTT
162 | //
163 | boolean sendASCII2MQTT(VEDirectBlock_t * block) {
164 | for (int i = 0; i < block->kvCount; i++) {
165 | String key = block->b[i].key;
166 | String value = block->b[i].value;
167 | String topic = MQTT_PREFIX + "/" + key;
168 | if ( !espMQTT.connected()) {
169 | startMQTT();
170 | }
171 | espMQTT.loop();
172 | topic.replace("#", ""); // # in a topic is a no go for MQTT
173 | value.replace("\r\n", "");
174 | if ( espMQTT.publish(topic.c_str(), value.c_str())) {
175 | log_i("MQTT message sent succesfully: %s: \"%s\"", topic.c_str(), value.c_str());
176 | } else {
177 | log_e("Sending MQTT message failed: %s: %s", topic.c_str(), value.c_str());
178 | }
179 |
180 | if ( mqtt_param_rec ) {
181 | // avoid loops by sending only if we received a valid parameter
182 | log_i("Removing parameter from Queue: %s", MQTT_PARAMETER.c_str());
183 | espMQTT.publish(MQTT_PARAMETER.c_str(), "", true);
184 | }
185 | }
186 | return true;
187 | }
188 |
189 | //
190 | // send operation info
191 | //
192 | boolean sendOPInfo() {
193 | if ( !espMQTT.connected()) {
194 | startMQTT();
195 | }
196 | espMQTT.loop();
197 | String topic = MQTT_PREFIX + "/" + "UTCBootTime";
198 | if ( espMQTT.publish(topic.c_str(), asctime(localtime(&last_boot)))) {
199 | log_i("MQTT message sent succesfully: %s: \"%s\"", topic.c_str(), asctime(localtime(&last_boot)));
200 | } else {
201 | log_e("Sending MQTT message failed: %s: %s", topic.c_str(), asctime(localtime(&last_boot)));
202 | }
203 | return true;
204 | }
205 |
206 |
207 |
208 | #endif
209 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/vedirectONEWIRE.h:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct OneWire temperature sensors code.
3 |
4 | GITHUB Link
5 |
6 | MIT License
7 |
8 | Copyright (c) 2020 Ralf Lehmann
9 |
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | SOFTWARE.
28 | */
29 |
30 | #ifndef VEDIRECTONEWIRE_H
31 | #define VEDIRECTONEWIRE_H
32 |
33 | #include
34 | #include
35 |
36 | #define MAX_DS18SENSORS 3
37 | #define MAX_MEASSUREMENTS 3
38 |
39 | OneWire oneWire(ONEWIRE_PIN);
40 | DallasTemperature sensors(&oneWire);
41 | int deviceCount = 0;
42 | boolean onewire_good_values = false;
43 |
44 | String addr2String(DeviceAddress addr) {
45 | String s;
46 | for (int i = 0; i < 7; i++) {
47 | s += String(addr[i]) + ":";
48 | }
49 | return s += addr[7];
50 | }
51 |
52 | boolean meassureOneWire() {
53 | log_d("Start");
54 | int m_count = MAX_MEASSUREMENTS;
55 | do {
56 | m_count --;
57 | onewire_good_values = true;
58 | sensors.begin();
59 | sensors.setWaitForConversion(true);
60 | delay(1000);
61 | sensors.requestTemperatures();
62 | delay(1000);
63 | deviceCount = sensors.getDeviceCount();
64 | log_d("Found %d devices", deviceCount);
65 | for (int i = 0; i < deviceCount; i++) {
66 | float temp = sensors.getTempCByIndex(i);
67 | if ( temp > 200 || temp < -80 ) {
68 | onewire_good_values = false;
69 | }
70 | }
71 | } while (m_count >= 0 && !onewire_good_values);
72 | log_d("End");
73 | return true;
74 | }
75 |
76 | boolean sendOneWireMQTT() {
77 | log_d("Start");
78 | meassureOneWire();
79 |
80 | log_d("Found %d One wire temp sensors", deviceCount);
81 | float c[deviceCount];
82 |
83 | if ( !onewire_good_values || deviceCount <= 0) {
84 | log_d("No ONE Wire temp sensors found; END");
85 | return false;
86 | }
87 |
88 | for (int i = 0; i < deviceCount; i++) {
89 | DeviceAddress addr;
90 | sensors.getAddress((uint8_t *)&addr, (uint8_t) i);
91 | c[i] = sensors.getTempCByIndex(i);
92 | log_d("DS18: %s, Temp: %f", addr2String(addr).c_str(), c[i]);
93 | }
94 |
95 | if ( !espMQTT.connected()) {
96 | startMQTT();
97 | }
98 |
99 | StaticJsonDocument<300> doc;
100 | // Add values in the document
101 | //
102 | int count = deviceCount > MAX_DS18SENSORS ? MAX_DS18SENSORS : deviceCount;
103 | log_d("Sending: %d Devices", count);
104 | for (int i = 0; i < count; i++) {
105 | DeviceAddress addr;
106 | sensors.getAddress((uint8_t *)&addr, (uint8_t) i);
107 | String s = addr2String(addr);
108 | doc[s] = c[i];
109 | } char s[300];
110 | serializeJson(doc, s);
111 | if ( espMQTT.publish(MQTT_ONEWIRE.c_str(), s)) {
112 | log_d("Sending OneWire Data: %s - OK", s);
113 | } else {
114 | log_d("Sending OneWire %s Data: %s - ERROR", MQTT_ONEWIRE.c_str(), s);
115 | }
116 | espMQTT.loop();
117 | if ( mqtt_param_rec ) {
118 | // avoid loops by sending only if we received a valid parameter
119 | log_i("Removing parameter from Queue: %s", MQTT_PARAMETER.c_str());
120 | espMQTT.publish(MQTT_PARAMETER.c_str(), "", true);
121 | }
122 | log_d("End");
123 | return true;
124 | }
125 |
126 |
127 | #endif
128 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/vedirectOTA.h:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct Over The Air update code.
3 |
4 | GITHUB Link
5 |
6 | MIT License
7 |
8 | Copyright (c) 2020 Ralf Lehmann
9 |
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | SOFTWARE.
28 | */
29 |
30 | #ifndef VEDIRECTOTA_H
31 | #define VEDIRECTOTA_H
32 |
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 |
39 |
40 | //// Utility to extract header value from headers
41 | //String getHeaderValue(String header, String headerName) {
42 | // return header.substring(strlen(headerName.c_str()));
43 | //}
44 |
45 | //void printSSLError() {
46 | // char s[100];
47 | // int e = espClient.lastError(s, 100);
48 | // D("SSL Error: " + String(e) + " "); Dln(s);
49 | // // int err;
50 | // // br_ssl_client_context sc;
51 | // // err = br_ssl_engine_last_error(&sc.eng);
52 | //}
53 |
54 | boolean splitURL(String * result, String url) {
55 | int col_pos = url.indexOf(':');
56 | if ( col_pos < 0 ) {
57 | return false;
58 | }
59 | result[0] = url.substring(0, col_pos);
60 | // Dln("Proto: " + result[0]);
61 | if ( url.charAt(col_pos + 1) != '/' || url.charAt(col_pos + 2) != '/') {
62 | return false;
63 | }
64 | String rest = url.substring(col_pos + 3 ); // strip // immediately
65 | // Dln("Rest:" + rest);
66 | // check if we have a user name and password
67 | int at_pos = rest.indexOf('@');
68 | if ( at_pos > 0 ) {
69 | // process user:password
70 | result[3] = rest.substring(0, at_pos);
71 | rest = rest.substring(at_pos + 1);
72 | }
73 | int slash_pos = rest.indexOf('/');
74 | if ( slash_pos < 0 ) {
75 | return false;
76 | }
77 | result[1] = rest.substring(0, slash_pos);
78 | result[2] = rest.substring(slash_pos);
79 |
80 | return true;
81 | }
82 |
83 |
84 |
85 | //
86 | // make sure that there is plenty of memory, otherwise SSL will fail
87 | // because bin files contain passwords SSL is needed
88 | // e.g. stop BLE and release the memory
89 | //
90 | bool startOTA(String sketch_name) {
91 | log_d("Free Mem: %d", heap_caps_get_free_size(MALLOC_CAP_8BIT));
92 | log_d("Starting OTA with sketch: %s", sketch_name.c_str());
93 |
94 | // The line below is optional. It can be used to blink the LED on the board during flashing
95 | // The LED will be on during download of one buffer of data from the network. The LED will
96 | // be off during writing that buffer to flash
97 | // On a good connection the LED should flash regularly. On a bad connection the LED will be
98 | // on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
99 | // value is used to put the LED on. If the LED is on with HIGH, that value should be passed
100 | // httpUpdate.setLedPin(LED_BUILTIN, HIGH);
101 |
102 | for (int i = 0; i < ota_server_count; i++) {
103 | t_httpUpdate_return ret = httpUpdate.update(espClient, String(ota_server_string[i]) + sketch_name);
104 | // Or:
105 | //t_httpUpdate_return ret = httpUpdate.update(client, "server", 443, "file.bin");
106 |
107 | switch (ret) {
108 | case HTTP_UPDATE_FAILED:
109 | log_d("HTTP_UPDATE_FAILED Error %d - %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
110 | break;
111 |
112 | case HTTP_UPDATE_NO_UPDATES:
113 | log_d("HTTP_UPDATE_NO_UPDATES");
114 | return true;
115 | break;
116 |
117 | case HTTP_UPDATE_OK:
118 | log_d("HTTP_UPDATE_OK");
119 | return true;
120 | break;
121 | }
122 | }
123 |
124 | return true;
125 | }
126 |
127 |
128 | #endif
129 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/vedirectSerial.h:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct Serial code.
3 |
4 | GITHUB Link
5 |
6 | MIT License
7 |
8 | Copyright (c) 2020 Ralf Lehmann
9 |
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | SOFTWARE.
28 | */
29 |
30 | #ifndef VEDIRECTSERIAL_H
31 | #define VEDIRECTSERIAL_H
32 |
33 | void startVEDirectSerial() {
34 | Serial1.begin(19200, SERIAL_8N1, VEDIRECT_RX, VEDIRECT_TX);
35 | }
36 |
37 | #endif
38 |
--------------------------------------------------------------------------------
/VE.Direct2MQTT/vedirectWiFi.h:
--------------------------------------------------------------------------------
1 | /*
2 | VE.Direct WiFi code.
3 |
4 | GITHUB Link
5 |
6 | MIT License
7 |
8 | Copyright (c) 2020 Ralf Lehmann
9 |
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | SOFTWARE.
28 | */
29 |
30 | #ifndef VERDIRECTWIFI_H
31 | #define VERDIRECTWIFI_H
32 |
33 | #include
34 | #include
35 |
36 | #ifdef USE_SSL
37 | #include
38 | #else
39 | #include
40 | #endif
41 |
42 | WiFiMulti WiFiMulti;
43 | #ifdef USE_SSL
44 | WiFiClientSecure espClient;
45 | #else
46 | WiFiClient espClient;
47 | #endif
48 |
49 | int ssid_count = sizeof(ssid) / sizeof(ssid[0]);
50 |
51 | // Set time via NTP, as required for x.509 validation
52 | void setClock() {
53 | configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
54 |
55 | log_d("Waiting for NTP time sync: ");
56 | time_t now = time(nullptr);
57 | while (now < 8 * 3600 * 2) {
58 | yield();
59 | delay(500);
60 | now = time(nullptr);
61 | }
62 |
63 | log_d("");
64 | struct tm timeinfo;
65 | gmtime_r(&now, &timeinfo);
66 | log_d("NTP time %s", asctime(&timeinfo));
67 | }
68 |
69 | boolean startWiFi() {
70 | log_d("Number of ssid's: %d", ssid_count);
71 | // add all ssid's to WiFiMulti
72 | for (int i = 0; i < ssid_count; i++) {
73 | WiFiMulti.addAP(ssid[i], pw[i]);
74 | }
75 | if ((WiFiMulti.run() == WL_CONNECTED)) {
76 | log_i("WiFi connected");
77 |
78 | #ifdef USE_SSL
79 | //WiFiClientSecure client;
80 | espClient.setCACert(rootCACertificate);
81 | #endif
82 |
83 | // Reading data over SSL may be slow, use an adequate timeout
84 | espClient.setTimeout(24000 / 1000); // timeout argument is defined in seconds for setTimeout
85 | return true;
86 | }
87 | log_e("WiFi could not be started");
88 | return false;
89 | }
90 |
91 | // check if WiFi is still connected
92 | // if not, try to reconnect
93 | boolean checkWiFi(){
94 | if ( WiFi.status() != WL_CONNECTED ){
95 | return startWiFi();
96 | }
97 | return true;
98 | }
99 |
100 | boolean endWiFi() {
101 | log_d("Stopping WiFi ... ");
102 | boolean r = WiFi.disconnect();
103 | delay(1000);
104 | if ( WiFi.status() == WL_CONNECTED) {
105 | log_e("ERROR: unable to disconnect");
106 | return false;
107 | }
108 | log_i("disconnected successfully");
109 | return true;
110 | }
111 |
112 | #endif
113 |
--------------------------------------------------------------------------------
/pictures/MyVEDirectHardware.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RalfJL/VE.Direct2MQTT/ddc6ef74595b5781b02319c9dba2e744061a3a67/pictures/MyVEDirectHardware.jpg
--------------------------------------------------------------------------------
/pictures/esp32.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RalfJL/VE.Direct2MQTT/ddc6ef74595b5781b02319c9dba2e744061a3a67/pictures/esp32.gif
--------------------------------------------------------------------------------