1
+ /* !
2
+ * @file ws_networking_pico_v2.h
3
+ *
4
+ * This is a driver for using the Raspberry Pi Pico (RP2040)
5
+ * network interface with Adafruit IO Wippersnapper.
6
+ *
7
+ * Adafruit invests time and resources providing this open source code,
8
+ * please support Adafruit and open-source hardware by purchasing
9
+ * products from Adafruit!
10
+ *
11
+ * Copyright (c) Brent Rubell 2023 for Adafruit Industries.
12
+ *
13
+ * MIT license, all text here must be included in any redistribution.
14
+ *
15
+ */
16
+
17
+ #ifndef WS_NETWORKING_PICO_V2_H
18
+ #define WS_NETWORKING_PICO_V2_H
19
+
20
+ #ifdef ARDUINO_ARCH_RP2040
21
+
22
+ #define PICO_CONNECT_TIMEOUT_MS 20000 /* !< Connection timeout (in ms) */
23
+ #define PICO_CONNECT_RETRY_DELAY_MS 200 /* !< delay time between retries. */
24
+
25
+ #include " Wippersnapper_V2.h"
26
+
27
+ #include " Adafruit_MQTT.h"
28
+ #include " Adafruit_MQTT_Client.h"
29
+ #include " Arduino.h"
30
+ #include < WiFiClient.h>
31
+ #include < WiFiClientSecure.h>
32
+ extern Wippersnapper_V2 WsV2;
33
+
34
+ /* ***************************************************************************/
35
+ /* !
36
+ @brief Class for using the Raspberry Pi Pico network interface.
37
+ */
38
+ /* ***************************************************************************/
39
+ class ws_networking_pico_v2 : public Wippersnapper_V2 {
40
+
41
+ public:
42
+ /* *************************************************************************/
43
+ /* !
44
+ @brief Initializes the WipperSnapper class for RPi Pico.
45
+ */
46
+ /* *************************************************************************/
47
+ ws_networking_pico_v2 () : Wippersnapper_V2() {
48
+ _ssid = 0 ;
49
+ _pass = 0 ;
50
+ }
51
+
52
+ /* *************************************************************************/
53
+ /* !
54
+ @brief Destructor
55
+ */
56
+ /* *************************************************************************/
57
+ ~ws_networking_pico_v2 () {
58
+ if (_mqtt_client_secure)
59
+ delete _mqtt_client_secure;
60
+ if (_mqtt_client_secure)
61
+ delete _mqtt_client_secure;
62
+ }
63
+
64
+ /* *******************************************************/
65
+ /* !
66
+ @brief Sets the WiFi client's ssid and password.
67
+ @param ssid
68
+ WiFi network's SSID.
69
+ @param ssidPassword
70
+ WiFi network's password.
71
+ */
72
+ /* *******************************************************/
73
+ void set_ssid_pass (const char *ssid, const char *ssidPassword) {
74
+ _ssid = ssid;
75
+
76
+ // set the AP password
77
+ // check if ssidPassword was "" in secrets.json
78
+ if ((ssidPassword != NULL ) && (strlen (ssidPassword) == 0 )) {
79
+ _pass = NULL ; // Set as NULL for open networks
80
+ } else {
81
+ _pass = ssidPassword;
82
+ }
83
+ }
84
+
85
+ /* *********************************************************/
86
+ /* !
87
+ @brief Sets the WiFi client's ssid and password.
88
+ */
89
+ /* *********************************************************/
90
+ void set_ssid_pass () {
91
+ _ssid = WsV2._configV2 .network .ssid ;
92
+ _pass = WsV2._configV2 .network .pass ;
93
+ }
94
+
95
+ /* **********************************************************/
96
+ /* !
97
+ @brief Performs a scan of local WiFi networks.
98
+ @returns True if `_network_ssid` is found, False otherwise.
99
+ */
100
+ /* **********************************************************/
101
+ bool check_valid_ssid () {
102
+ // Set WiFi to station mode and disconnect from an AP if it was previously
103
+ // connected
104
+ WiFi.mode (WIFI_STA);
105
+ WiFi.disconnect ();
106
+ delay (100 );
107
+
108
+ // Perform a network scan
109
+ int n = WiFi.scanNetworks ();
110
+ if (n == 0 ) {
111
+ WS_DEBUG_PRINTLN (" ERROR: No WiFi networks found!" );
112
+ return false ;
113
+ }
114
+
115
+ // Was the network within secrets.json found?
116
+ for (int i = 0 ; i < n; ++i) {
117
+ if (strcmp (_ssid, WiFi.SSID (i)) == 0 ) {
118
+ WS_DEBUG_PRINT (" SSID (" );
119
+ WS_DEBUG_PRINT (_ssid);
120
+ WS_DEBUG_PRINT (" ) found! RSSI: " );
121
+ WS_DEBUG_PRINTLN (WiFi.RSSI (i));
122
+ return true ;
123
+ }
124
+ if (WsV2._isWiFiMultiV2 ) {
125
+ // multi network mode
126
+ for (int j = 0 ; j < WS_MAX_ALT_WIFI_NETWORKS; j++) {
127
+ if (strcmp (WsV2._multiNetworksV2 [j].ssid , WiFi.SSID (i)) == 0 ) {
128
+ WS_DEBUG_PRINT (" SSID (" );
129
+ WS_DEBUG_PRINT (WsV2._multiNetworksV2 [j].ssid );
130
+ WS_DEBUG_PRINT (" ) found! RSSI: " );
131
+ WS_DEBUG_PRINTLN (WiFi.RSSI (i));
132
+ return true ;
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ // User-set network not found, print scan results to serial console
139
+ WS_DEBUG_PRINTLN (" ERROR: Your requested WiFi network was not found!" );
140
+ WS_DEBUG_PRINTLN (" WipperSnapper found these WiFi networks: " );
141
+ for (int i = 0 ; i < n; ++i) {
142
+ WS_DEBUG_PRINT (WiFi.SSID (i));
143
+ WS_DEBUG_PRINT (" " );
144
+ WS_DEBUG_PRINT (WiFi.RSSI (i));
145
+ WS_DEBUG_PRINTLN (" dB" );
146
+ }
147
+
148
+ return false ;
149
+ }
150
+
151
+ /* *******************************************************/
152
+ /* !
153
+ @brief Sets the RPi Pico's unique client identifier
154
+ @note On RPi Pico, the UID is the MAC address.
155
+ */
156
+ /* *******************************************************/
157
+ void getMacAddr () {
158
+ uint8_t mac[6 ] = {0 };
159
+ WiFi.macAddress (mac);
160
+ memcpy (WsV2._macAddrV2 , mac, sizeof (mac));
161
+ }
162
+
163
+ /* *******************************************************/
164
+ /* !
165
+ @brief Gets the current network RSSI value
166
+ @return int32_t RSSI value
167
+ */
168
+ /* *******************************************************/
169
+ int32_t getRSSI () { return WiFi.RSSI (); }
170
+
171
+ /* *******************************************************/
172
+ /* !
173
+ @brief Initializes the MQTT client
174
+ @param clientID
175
+ MQTT client identifier
176
+ */
177
+ /* *******************************************************/
178
+ void setupMQTTClient (const char *clientID) {
179
+ if (strcmp (WsV2._configV2 .aio_url , " io.adafruit.com" ) == 0 ||
180
+ strcmp (WsV2._configV2 .aio_url , " io.adafruit.us" ) == 0 ) {
181
+ _mqtt_client_secure = new WiFiClientSecure ();
182
+ _mqtt_client_secure->setCACert (
183
+ strcmp (WsV2._configV2 .aio_url , " io.adafruit.com" ) == 0
184
+ ? _aio_root_ca_prod
185
+ : _aio_root_ca_staging);
186
+ WsV2._mqttV2 = new Adafruit_MQTT_Client (
187
+ _mqtt_client_secure, WsV2._configV2 .aio_url , WsV2._configV2 .io_port , clientID,
188
+ WsV2._configV2 .aio_user , WsV2._configV2 .aio_key );
189
+ } else {
190
+ _mqtt_client_insecure = new WiFiClient ();
191
+ WsV2._mqttV2 = new Adafruit_MQTT_Client (
192
+ _mqtt_client_insecure, WsV2._configV2 .aio_url , WsV2._configV2 .io_port ,
193
+ clientID, WsV2._configV2 .aio_user , WsV2._configV2 .aio_key );
194
+ }
195
+ }
196
+
197
+ /* *******************************************************/
198
+ /* !
199
+ @brief Returns the network status of an RPi Pico.
200
+ @return ws_status_t
201
+ */
202
+ /* *******************************************************/
203
+ ws_status_t networkStatus () {
204
+ switch (WiFi.status ()) {
205
+ case WL_CONNECTED:
206
+ return WS_NET_CONNECTED;
207
+ case WL_CONNECT_FAILED:
208
+ return WS_NET_CONNECT_FAILED;
209
+ case WL_IDLE_STATUS:
210
+ return WS_IDLE;
211
+ default :
212
+ return WS_NET_DISCONNECTED;
213
+ }
214
+ }
215
+
216
+ /* ******************************************************************/
217
+ /* !
218
+ @brief Returns the type of network connection used by Wippersnapper
219
+ @return Pico
220
+ */
221
+ /* ******************************************************************/
222
+ const char *connectionType () { return " Pico" ; }
223
+
224
+ protected:
225
+ const char *_ssid; // /< WiFi SSID
226
+ const char *_pass; // /< WiFi password
227
+ WiFiClient
228
+ *_mqtt_client_insecure; // /< Pointer to an insecure WiFi client object
229
+ WiFiClientSecure
230
+ *_mqtt_client_secure; // /< Pointer to a secure WiFi client object
231
+ WiFiMulti _wifiMulti; // /< WiFiMulti object for multi-network mode
232
+
233
+ const char *_aio_root_ca_staging =
234
+ " -----BEGIN CERTIFICATE-----\n "
235
+ " MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw\n "
236
+ " TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n "
237
+ " cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n "
238
+ " WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n "
239
+ " RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G\n "
240
+ " h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV\n "
241
+ " 6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw\n "
242
+ " gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\n "
243
+ " ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj\n "
244
+ " v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\n "
245
+ " AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\n "
246
+ " BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\n "
247
+ " Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc\n "
248
+ " MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL\n "
249
+ " pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp\n "
250
+ " eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH\n "
251
+ " pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7\n "
252
+ " s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu\n "
253
+ " h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv\n "
254
+ " YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8\n "
255
+ " ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0\n "
256
+ " LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+\n "
257
+ " EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY\n "
258
+ " Ig46v9mFmBvyH04=\n "
259
+ " -----END CERTIFICATE-----\n " ; // /< Root certificate for io.adafruit.us
260
+
261
+ const char *_aio_root_ca_prod =
262
+ " -----BEGIN CERTIFICATE-----\n "
263
+ " MIIEjTCCA3WgAwIBAgIQDQd4KhM/xvmlcpbhMf/ReTANBgkqhkiG9w0BAQsFADBh\n "
264
+ " MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n "
265
+ " d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n "
266
+ " MjAeFw0xNzExMDIxMjIzMzdaFw0yNzExMDIxMjIzMzdaMGAxCzAJBgNVBAYTAlVT\n "
267
+ " MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n "
268
+ " b20xHzAdBgNVBAMTFkdlb1RydXN0IFRMUyBSU0EgQ0EgRzEwggEiMA0GCSqGSIb3\n "
269
+ " DQEBAQUAA4IBDwAwggEKAoIBAQC+F+jsvikKy/65LWEx/TMkCDIuWegh1Ngwvm4Q\n "
270
+ " yISgP7oU5d79eoySG3vOhC3w/3jEMuipoH1fBtp7m0tTpsYbAhch4XA7rfuD6whU\n "
271
+ " gajeErLVxoiWMPkC/DnUvbgi74BJmdBiuGHQSd7LwsuXpTEGG9fYXcbTVN5SATYq\n "
272
+ " DfbexbYxTMwVJWoVb6lrBEgM3gBBqiiAiy800xu1Nq07JdCIQkBsNpFtZbIZhsDS\n "
273
+ " fzlGWP4wEmBQ3O67c+ZXkFr2DcrXBEtHam80Gp2SNhou2U5U7UesDL/xgLK6/0d7\n "
274
+ " 6TnEVMSUVJkZ8VeZr+IUIlvoLrtjLbqugb0T3OYXW+CQU0kBAgMBAAGjggFAMIIB\n "
275
+ " PDAdBgNVHQ4EFgQUlE/UXYvkpOKmgP792PkA76O+AlcwHwYDVR0jBBgwFoAUTiJU\n "
276
+ " IBiV5uNu5g/6+rkS7QYXjzkwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsG\n "
277
+ " AQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMDQGCCsGAQUFBwEB\n "
278
+ " BCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEIGA1Ud\n "
279
+ " HwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEds\n "
280
+ " b2JhbFJvb3RHMi5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEW\n "
281
+ " HGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQELBQADggEB\n "
282
+ " AIIcBDqC6cWpyGUSXAjjAcYwsK4iiGF7KweG97i1RJz1kwZhRoo6orU1JtBYnjzB\n "
283
+ " c4+/sXmnHJk3mlPyL1xuIAt9sMeC7+vreRIF5wFBC0MCN5sbHwhNN1JzKbifNeP5\n "
284
+ " ozpZdQFmkCo+neBiKR6HqIA+LMTMCMMuv2khGGuPHmtDze4GmEGZtYLyF8EQpa5Y\n "
285
+ " jPuV6k2Cr/N3XxFpT3hRpt/3usU/Zb9wfKPtWpoznZ4/44c1p9rzFcZYrWkj3A+7\n "
286
+ " TNBJE0GmP2fhXhP1D/XVfIW/h0yCJGEiV9Glm/uGOa3DXHlmbAcxSyCRraG+ZBkA\n "
287
+ " 7h4SeM6Y8l/7MBRpPCz6l8Y=\n "
288
+ " -----END CERTIFICATE-----\n " ; // /< Root certificate for io.adafruit.com
289
+
290
+ /* *************************************************************************/
291
+ /* !
292
+ @brief Establishes a connection with the wireless network.
293
+ */
294
+ /* *************************************************************************/
295
+ void _connect () {
296
+
297
+ if (WiFi.status () == WL_CONNECTED)
298
+ return ;
299
+
300
+ WiFi.mode (WIFI_STA);
301
+ WsV2.feedWDTV2 ();
302
+ WiFi.setTimeout (20000 );
303
+ WsV2.feedWDTV2 ();
304
+
305
+ if (strlen (_ssid) == 0 ) {
306
+ _statusV2 = WS_SSID_INVALID;
307
+ } else {
308
+ _disconnect ();
309
+ delay (5000 );
310
+ WsV2.feedWDTV2 ();
311
+ if (WsV2._isWiFiMultiV2 ) {
312
+ // multi network mode
313
+ _wifiMulti.clearAPList ();
314
+ // add default network
315
+ _wifiMulti.addAP (_ssid, _pass);
316
+ // add array of alternative networks
317
+ for (int i = 0 ; i < WS_MAX_ALT_WIFI_NETWORKS; i++) {
318
+ _wifiMulti.addAP (WsV2._multiNetworksV2 [i].ssid ,
319
+ WsV2._multiNetworksV2 [i].pass );
320
+ }
321
+ WsV2.feedWDTV2 ();
322
+ if (_wifiMulti.run (10000 ) == WL_CONNECTED) {
323
+ WsV2.feedWDTV2 ();
324
+ _statusV2 = WS_NET_CONNECTED;
325
+ return ;
326
+ }
327
+ WsV2.feedWDTV2 ();
328
+ } else {
329
+ WiFi.begin (_ssid, _pass);
330
+
331
+ // Use the macro to retry the status check until connected / timed out
332
+ int lastResult;
333
+ /* RETRY_FUNCTION_UNTIL_TIMEOUT(
334
+ []() -> int { return WiFi.status(); }, // Function call each cycle
335
+ int, // return type
336
+ lastResult, // return variable (unused here)
337
+ [](int status) { return status == WL_CONNECTED; }, // check
338
+ PICO_CONNECT_TIMEOUT_MS, // timeout interval (ms)
339
+ PICO_CONNECT_RETRY_DELAY_MS); // interval between retries */
340
+
341
+ if (lastResult == WL_CONNECTED) {
342
+ _statusV2 = WS_NET_CONNECTED;
343
+ // wait 2seconds for connection to stabilize
344
+ // WS_DELAY_WITH_WDT(2000);
345
+ return ;
346
+ }
347
+ }
348
+ _statusV2 = WS_NET_DISCONNECTED;
349
+ }
350
+ }
351
+
352
+ /* *************************************************************************/
353
+ /* !
354
+ @brief Disconnects from the wireless network.
355
+ */
356
+ /* *************************************************************************/
357
+ void _disconnect () {
358
+ WsV2.feedWDTV2 ();
359
+ WiFi.disconnect ();
360
+ delay (5000 );
361
+ WsV2.feedWDTV2 ();
362
+ }
363
+ };
364
+
365
+ #endif // ARDUINO_ARCH_RP2040
366
+ #endif // WS_NETWORKING_PICO_V2_H
0 commit comments