Skip to content

Commit 39f6537

Browse files
authored
Merge pull request #182 from matth-x/master
Integration of ArduinoOcpp
2 parents 263b15e + ee9bea4 commit 39f6537

File tree

10 files changed

+788
-2
lines changed

10 files changed

+788
-2
lines changed

platformio.ini

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ version = -DBUILD_TAG=4.0.1
3333
monitor_speed = 115200
3434
lib_deps =
3535
36-
36+
37+
https://github.com/matth-x/ArduinoMongoose
3738
3839
3940
@@ -42,6 +43,8 @@ lib_deps =
4243
4344
4445
46+
47+
lib_ignore = WebSockets ; ArduinoOcpp: don't compile built-in WS library
4548
extra_scripts = scripts/extra_script.py
4649
debug_flags =
4750
-ggdb
@@ -72,6 +75,7 @@ build_flags =
7275
#-DMBEDTLS_DEBUG_C
7376
-DMG_ENABLE_SNTP=1
7477
-DCS_PLATFORM=CS_P_ESP32
78+
-DAO_CUSTOM_WS ; ArduinoOcpp: don't use built-in WS library
7579
build_partitions = min_spiffs.csv
7680
monitor_flags = --filter=esp32_exception_decoder
7781

@@ -89,6 +93,7 @@ platform = ${common.platform}
8993
board = nodemcu-32s
9094
framework = arduino, espidf
9195
lib_deps = ${common.lib_deps}
96+
lib_ignore = ${common.lib_ignore}
9297
src_build_flags =
9398
${common.version}.dev
9499
${common.src_build_flags}
@@ -111,6 +116,7 @@ framework = arduino, espidf
111116
lib_deps =
112117
${common.lib_deps}
113118
${common.neopixel_lib}
119+
lib_ignore = ${common.lib_ignore}
114120
src_build_flags =
115121
${common.version}.dev
116122
${common.src_build_flags}
@@ -142,6 +148,7 @@ platform = ${common.platform}
142148
board = featheresp32
143149
framework = arduino, espidf
144150
lib_deps = ${common.lib_deps}
151+
lib_ignore = ${common.lib_ignore}
145152
src_build_flags =
146153
${common.version}.dev
147154
${common.src_build_flags}
@@ -165,6 +172,7 @@ platform = ${common.platform}
165172
board = featheresp32
166173
framework = arduino, espidf
167174
lib_deps = ${common.lib_deps}
175+
lib_ignore = ${common.lib_ignore}
168176
src_build_flags =
169177
${common.version}
170178
${common.src_build_flags}
@@ -187,6 +195,7 @@ platform = ${common.platform}
187195
board = featheresp32
188196
framework = arduino, espidf
189197
lib_deps = ${common.lib_deps}
198+
lib_ignore = ${common.lib_ignore}
190199
src_build_flags =
191200
${common.version}.dev
192201
${common.src_build_flags}
@@ -211,6 +220,7 @@ lib_deps =
211220
${common.lib_deps}
212221
${common.neopixel_lib}
213222
adafruit/Adafruit MCP9808 Library @ 1.1.2
223+
lib_ignore = ${common.lib_ignore}
214224
src_build_flags =
215225
${common.version}
216226
${common.src_build_flags}
@@ -239,6 +249,7 @@ platform = ${common.platform}
239249
board = esp32-gateway
240250
framework = arduino, espidf
241251
lib_deps = ${common.lib_deps}
252+
lib_ignore = ${common.lib_ignore}
242253
src_build_flags =
243254
${common.version}
244255
${common.src_build_flags}
@@ -274,6 +285,7 @@ platform = ${common.platform}
274285
board = heltec_wifi_lora_32_V2
275286
framework = arduino, espidf
276287
lib_deps = ${common.lib_deps}
288+
lib_ignore = ${common.lib_ignore}
277289
src_build_flags =
278290
${common.version}.dev
279291
${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)