Skip to content

Commit 6c62228

Browse files
committed
Merge branch 'master' into jeremypoulter/issue192
2 parents c32a8c0 + dbe4215 commit 6c62228

19 files changed

+811
-14
lines changed

platformio.ini

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ version = -DBUILD_TAG=4.0.1
3333
monitor_speed = 115200
3434
lib_deps =
3535
36-
36+
3737
3838
3939
@@ -42,6 +42,8 @@ lib_deps =
4242
4343
4444
45+
46+
lib_ignore = WebSockets ; ArduinoOcpp: don't compile built-in WS library
4547
extra_scripts = scripts/extra_script.py
4648
debug_flags =
4749
-ggdb
@@ -72,6 +74,7 @@ build_flags =
7274
#-DMBEDTLS_DEBUG_C
7375
-DMG_ENABLE_SNTP=1
7476
-DCS_PLATFORM=CS_P_ESP32
77+
-DAO_CUSTOM_WS ; ArduinoOcpp: don't use built-in WS library
7578
build_partitions = min_spiffs.csv
7679
monitor_flags = --filter=esp32_exception_decoder
7780

@@ -89,6 +92,7 @@ platform = ${common.platform}
8992
board = nodemcu-32s
9093
framework = arduino, espidf
9194
lib_deps = ${common.lib_deps}
95+
lib_ignore = ${common.lib_ignore}
9296
src_build_flags =
9397
${common.version}.dev
9498
${common.src_build_flags}
@@ -111,6 +115,7 @@ framework = arduino, espidf
111115
lib_deps =
112116
${common.lib_deps}
113117
${common.neopixel_lib}
118+
lib_ignore = ${common.lib_ignore}
114119
src_build_flags =
115120
${common.version}.dev
116121
${common.src_build_flags}
@@ -142,6 +147,7 @@ platform = ${common.platform}
142147
board = featheresp32
143148
framework = arduino, espidf
144149
lib_deps = ${common.lib_deps}
150+
lib_ignore = ${common.lib_ignore}
145151
src_build_flags =
146152
${common.version}.dev
147153
${common.src_build_flags}
@@ -165,6 +171,7 @@ platform = ${common.platform}
165171
board = featheresp32
166172
framework = arduino, espidf
167173
lib_deps = ${common.lib_deps}
174+
lib_ignore = ${common.lib_ignore}
168175
src_build_flags =
169176
${common.version}
170177
${common.src_build_flags}
@@ -187,6 +194,7 @@ platform = ${common.platform}
187194
board = featheresp32
188195
framework = arduino, espidf
189196
lib_deps = ${common.lib_deps}
197+
lib_ignore = ${common.lib_ignore}
190198
src_build_flags =
191199
${common.version}.dev
192200
${common.src_build_flags}
@@ -211,6 +219,7 @@ lib_deps =
211219
${common.lib_deps}
212220
${common.neopixel_lib}
213221
adafruit/Adafruit MCP9808 Library @ 1.1.2
222+
lib_ignore = ${common.lib_ignore}
214223
src_build_flags =
215224
${common.version}
216225
${common.src_build_flags}
@@ -239,6 +248,7 @@ platform = ${common.platform}
239248
board = esp32-gateway
240249
framework = arduino, espidf
241250
lib_deps = ${common.lib_deps}
251+
lib_ignore = ${common.lib_ignore}
242252
src_build_flags =
243253
${common.version}
244254
${common.src_build_flags}
@@ -274,6 +284,7 @@ platform = ${common.platform}
274284
board = heltec_wifi_lora_32_V2
275285
framework = arduino, espidf
276286
lib_deps = ${common.lib_deps}
287+
lib_ignore = ${common.lib_ignore}
277288
src_build_flags =
278289
${common.version}.dev
279290
${common.src_build_flags}

src/MongooseOcppSocketClient.cpp

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/*
2+
* Author: Matthias Akstaller
3+
* Created: 2021-04-10
4+
*/
5+
6+
#include "MongooseOcppSocketClient.h"
7+
#include "net_manager.h"
8+
#include "debug.h"
9+
10+
/*** To define a custom CA for OCPP please define the flag OCPP_CUSTOM_CA and set define const char *ocpp_ca with the certificate
11+
*
12+
*** Example:
13+
14+
#define OCPP_CUSTOM_CA
15+
const char *ocpp_ca = "-----BEGIN CERTIFICATE-----\r\n"
16+
"MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\r\n"
17+
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
18+
"DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\r\n"
19+
"PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\r\n"
20+
"Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\r\n"
21+
"AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\r\n"
22+
"rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\r\n"
23+
"OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\r\n"
24+
"xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\r\n"
25+
"7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\r\n"
26+
"aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\r\n"
27+
"HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\r\n"
28+
"SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\r\n"
29+
"ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\r\n"
30+
"AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\r\n"
31+
"R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\r\n"
32+
"JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\r\n"
33+
"Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\r\n"
34+
"-----END CERTIFICATE-----\r\n";
35+
36+
***/
37+
38+
#define WS_UNRESPONSIVE_THRESHOLD_MS 4000UL
39+
40+
#define DEBUG_MSG_INTERVAL 5000UL
41+
42+
#define RECONNECT_AFTER 5000UL //pause interval between two reconnection attempts
43+
44+
//see ArduinoMongoose/src/mongoose.c
45+
#ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS
46+
#define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5
47+
#endif
48+
49+
#define MG_WEBSOCKET_PING_INTERVAL_MS (MG_WEBSOCKET_PING_INTERVAL_SECONDS * 1000UL)
50+
51+
bool ocppIsConnected = false;
52+
53+
MongooseOcppSocketClient::MongooseOcppSocketClient(const String &ws_url) {
54+
this->ws_url = String(ws_url);
55+
56+
}
57+
58+
MongooseOcppSocketClient::~MongooseOcppSocketClient() {
59+
DBUG(F("[MongooseOcppSocketClient] Close and destroy connection to "));
60+
DBUGLN(this->ws_url);
61+
if (nc) {
62+
connection_established = false;
63+
ocppIsConnected = connection_established; //need it static for Wi-Fi dashboard
64+
const char *msg = "socket closed by client";
65+
mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, msg, strlen(msg));
66+
nc = NULL;
67+
}
68+
}
69+
70+
void MongooseOcppSocketClient::mg_event_callback(struct mg_connection *nc, int ev, void *ev_data, void *user_data) {
71+
72+
MongooseOcppSocketClient *instance = NULL;
73+
74+
if (nc->flags & MG_F_IS_WEBSOCKET && nc->flags & MG_F_IS_MongooseOcppSocketClient) {
75+
instance = static_cast<MongooseOcppSocketClient*>(user_data);
76+
} else {
77+
return;
78+
}
79+
80+
if (ev != MG_EV_POLL && ev != MG_EV_SEND) {
81+
instance->last_recv = millis();
82+
}
83+
84+
switch (ev) {
85+
case MG_EV_CONNECT: {
86+
int status = *((int *) ev_data);
87+
if (status != 0) {
88+
DBUG(F("[MongooseOcppSocketClient] Connection to "));
89+
DBUG(instance->ws_url);
90+
DBUG(F(" -- Error: "));
91+
DBUGLN(status);
92+
}
93+
break;
94+
}
95+
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
96+
struct http_message *hm = (struct http_message *) ev_data;
97+
DBUG(F("[MongooseOcppSocketClient] Connection to "));
98+
DBUG(instance->ws_url);
99+
if (hm->resp_code == 101) {
100+
DBUGLN(F(" -- Connected"));
101+
instance->connection_established = true;
102+
ocppIsConnected = instance->connection_established; //need it static for Wi-Fi dashboard
103+
} else {
104+
DBUG(F(" -- Connection failed! HTTP code "));
105+
DBUGLN(hm->resp_code);
106+
/* Connection will be closed after this. */
107+
}
108+
break;
109+
}
110+
case MG_EV_POLL: {
111+
/* Nothing to do here. OCPP engine has own loop-function */
112+
break;
113+
}
114+
case MG_EV_WEBSOCKET_FRAME: {
115+
struct websocket_message *wm = (struct websocket_message *) ev_data;
116+
117+
if (!instance->receiveTXT((const char *) wm->data, wm->size)) { //forward message to OcppEngine
118+
DBUGLN(F("[MongooseOcppSocketClient] Processing WebSocket input event failed!"));
119+
}
120+
break;
121+
}
122+
case MG_EV_CLOSE: {
123+
instance->connection_established = false;
124+
ocppIsConnected = instance->connection_established; //need it static for Wi-Fi dashboard
125+
instance->nc = NULL; //resources will be free'd by Mongoose
126+
DBUG(F("[MongooseOcppSocketClient] Connection to "));
127+
DBUG(instance->ws_url);
128+
DBUGLN(F(" -- Closed"));
129+
break;
130+
}
131+
}
132+
}
133+
134+
void MongooseOcppSocketClient::loop() {
135+
maintainWsConn();
136+
}
137+
138+
void MongooseOcppSocketClient::maintainWsConn() {
139+
140+
if (millis() - last_status_dbg_msg >= DEBUG_MSG_INTERVAL) {
141+
last_status_dbg_msg = millis();
142+
143+
//WS successfully connected?
144+
if (!connection_established) {
145+
DBUGLN(F("[MongooseOcppSocketClient] WS unconnected"));
146+
} else if (millis() - last_recv >= MG_WEBSOCKET_PING_INTERVAL_MS + WS_UNRESPONSIVE_THRESHOLD_MS) {
147+
//WS connected but unresponsive
148+
DBUGLN(F("[MongooseOcppSocketClient] WS unresponsive"));
149+
}
150+
}
151+
152+
if (nc != NULL) { //connection pointer != NULL means that the socket is still open
153+
return;
154+
}
155+
156+
if (!net_is_connected()) {
157+
return;
158+
}
159+
160+
if (ws_url.isEmpty()) {
161+
return;
162+
}
163+
164+
if (millis() - last_reconnection_attempt < RECONNECT_AFTER) {
165+
return;
166+
}
167+
168+
DBUG(F("[MongooseOcppSocketClient] (re-)connect to "));
169+
DBUGLN(this->ws_url);
170+
171+
struct mg_connect_opts opts;
172+
Mongoose.getDefaultOpts(&opts);
173+
174+
#if defined(OCPP_CUSTOM_CA)
175+
opts.ssl_ca_cert = ocpp_ca;
176+
#endif
177+
178+
opts.error_string = &mongoose_error_string;
179+
180+
nc = mg_connect_ws_opt(Mongoose.getMgr(), mg_event_callback, this, opts, this->ws_url.c_str(), "ocpp1.6", NULL);
181+
182+
if (!nc) {
183+
DBUG(F("[MongooseOcppSocketClient] Failed to connect to URL: "));
184+
DBUGLN(this->ws_url);
185+
}
186+
187+
nc->flags |= MG_F_IS_MongooseOcppSocketClient;
188+
189+
last_reconnection_attempt = millis();
190+
}
191+
192+
void MongooseOcppSocketClient::reconnect(const String &ws_url) {
193+
this->ws_url = String(ws_url);
194+
195+
connection_established = false;
196+
ocppIsConnected = connection_established; //need it static for Wi-Fi dashboard
197+
if (nc) {
198+
const char *msg = "socket closed by client";
199+
mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, msg, strlen(msg));
200+
nc = NULL;
201+
}
202+
203+
maintainWsConn();
204+
}
205+
206+
bool MongooseOcppSocketClient::sendTXT(String &out) {
207+
/*
208+
* Check if the EVSE is able to send the data at the moment. This fuzzy check can be useful to
209+
* to diagnose connection problems at upper layers. It gives no guarantee that packages will
210+
* actually be sent successfully.
211+
*/
212+
if (!nc || !connection_established || !net_is_connected())
213+
return false;
214+
215+
if (millis() - last_recv >= MG_WEBSOCKET_PING_INTERVAL_MS + WS_UNRESPONSIVE_THRESHOLD_MS) {
216+
//unresponsive; wait until next WS ping-pong succeeds
217+
return false;
218+
}
219+
220+
if (nc->send_mbuf.len > 0) {
221+
//Something else is already in the queue. This is a strong indicator that the device
222+
//cannot send at the moment
223+
return false;
224+
}
225+
226+
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, out.c_str(), out.length());
227+
228+
return true; //message was handed over to underlying HTTP-layer
229+
}
230+
231+
bool MongooseOcppSocketClient::receiveTXT(const char* msg, size_t len) {
232+
return receiveTXTcallback(msg, len);
233+
}
234+
235+
bool MongooseOcppSocketClient::ocppConnected() {
236+
return ocppIsConnected;
237+
}
238+
239+
bool MongooseOcppSocketClient::isValidUrl(const char *url) {
240+
//URL must start with ws: or wss:
241+
if (url[0] != 'W' && url[0] != 'w')
242+
return false;
243+
if (url[1] != 'S' && url[1] != 's')
244+
return false;
245+
246+
if (url[2] == 'S' && url[2] == 's') {
247+
if (url[3] != ':')
248+
return false;
249+
//else: passed
250+
} else if (url[2] != ':') {
251+
return false;
252+
}
253+
//passed
254+
255+
unsigned int port_i = 0;
256+
struct mg_str scheme, query, fragment;
257+
return mg_parse_uri(mg_mk_str(url), &scheme, NULL, NULL, &port_i, NULL, &query, &fragment) == 0; //mg_parse_uri returns 0 on success, false otherwise
258+
}

0 commit comments

Comments
 (0)