Skip to content

Commit 916c31d

Browse files
authored
Adds iLabs RP2040 Connectivity (LTE/WIFI/BLE) board. (#1936)
1 parent 403c147 commit 916c31d

File tree

8 files changed

+700
-2
lines changed

8 files changed

+700
-2
lines changed

boards.txt

Lines changed: 246 additions & 0 deletions
Large diffs are not rendered by default.

package/package_pico_index.template.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@
140140
{
141141
"name": "iLabs Challenger 2040 UWB"
142142
},
143+
{
144+
"name": "iLabs Connectivity 2040 LTE/WiFi/BLE"
145+
},
143146
{
144147
"name": "iLabs RPICO32"
145148
},
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"build": {
3+
"arduino": {
4+
"earlephilhower": {
5+
"boot2_source": "boot2_w25q080_2_padded_checksum.S",
6+
"usb_vid": "0x2E8A",
7+
"usb_pid": "0x107B"
8+
}
9+
},
10+
"core": "earlephilhower",
11+
"cpu": "cortex-m0plus",
12+
"extra_flags": "-D ARDUINO_CONNECTIVITY_2040_LTE_WIFI_BLE_RP2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500 -DWIFIESPAT2",
13+
"f_cpu": "133000000L",
14+
"hwids": [
15+
[
16+
"0x2E8A",
17+
"0x00C0"
18+
],
19+
[
20+
"0x2E8A",
21+
"0x107B"
22+
]
23+
],
24+
"mcu": "rp2040",
25+
"variant": "connectivity_2040_lte_wifi_ble"
26+
},
27+
"debug": {
28+
"jlink_device": "RP2040_M0_0",
29+
"openocd_target": "rp2040.cfg",
30+
"svd_path": "rp2040.svd"
31+
},
32+
"frameworks": [
33+
"arduino"
34+
],
35+
"name": "Connectivity 2040 LTE/WiFi/BLE",
36+
"upload": {
37+
"maximum_ram_size": 270336,
38+
"maximum_size": 8388608,
39+
"require_upload_port": true,
40+
"native_usb": true,
41+
"use_1200bps_touch": true,
42+
"wait_for_upload_port": false,
43+
"protocol": "picotool",
44+
"protocols": [
45+
"blackmagic",
46+
"cmsis-dap",
47+
"jlink",
48+
"raspberrypi-swd",
49+
"picotool",
50+
"picoprobe",
51+
"pico-debug"
52+
]
53+
},
54+
"url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
55+
"vendor": "iLabs"
56+
}

tools/json/ilabs_rpico32.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
},
1010
"core": "earlephilhower",
1111
"cpu": "cortex-m0plus",
12-
"extra_flags": "-D ARDUINO_ILABS_2040_RPICO32_RP2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250",
12+
"extra_flags": "-D ARDUINO_ILABS_2040_RPICO32_RP2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250 -DWIFIESPAT2",
1313
"f_cpu": "133000000L",
1414
"hwids": [
1515
[

tools/makeboards.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,8 @@ def MakeBoardJSON(name, vendor_name, product_name, vid, pid, pwr, boarddefine, f
407407
MakeBoard("challenger_2040_sdrtc", "iLabs", "Challenger 2040 SD/RTC", "0x2e8a", "0x102d", 250, "CHALLENGER_2040_SDRTC_RP2040", 8, "boot2_w25q080_2_padded_checksum")
408408
MakeBoard("challenger_2040_nfc", "iLabs", "Challenger 2040 NFC", "0x2e8a", "0x1036", 250, "CHALLENGER_2040_NFC_RP2040", 8, "boot2_w25q080_2_padded_checksum")
409409
MakeBoard("challenger_2040_uwb", "iLabs", "Challenger 2040 UWB", "0x2e8a", "0x1052", 500, "CHALLENGER_2040_UWB_RP2040", 8, "boot2_w25q080_2_padded_checksum")
410-
MakeBoard("ilabs_rpico32", "iLabs", "RPICO32", "0x2e8a", "0x1010", 250, "ILABS_2040_RPICO32_RP2040", 8, "boot2_w25q080_2_padded_checksum")
410+
MakeBoard("connectivity_2040_lte_wifi_ble", "iLabs", "Connectivity 2040 LTE/WiFi/BLE", "0x2e8a", "0x107b", 500, "CONNECTIVITY_2040_LTE_WIFI_BLE_RP2040", 8, "boot2_w25q080_2_padded_checksum", ["WIFIESPAT2"])
411+
MakeBoard("ilabs_rpico32", "iLabs", "RPICO32", "0x2e8a", "0x1010", 250, "ILABS_2040_RPICO32_RP2040", 8, "boot2_w25q080_2_padded_checksum", ["WIFIESPAT2"])
411412

412413
# Melopero
413414
MakeBoard("melopero_cookie_rp2040", "Melopero", "Cookie RP2040", "0x2e8a", "0x1011", 250, "MELOPERO_COOKIE_RP2040", 8, "boot2_w25q080_2_padded_checksum")
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/*
2+
UBlox SARA/ESP helper class for the Connectivity RP2040 LTE/WIFI/BLE board
3+
4+
Copyright (c) 2024 P. Oldberg <[email protected]>
5+
6+
This library is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU Lesser General Public
8+
License as published by the Free Software Foundation; either
9+
version 2.1 of the License, or (at your option) any later version.
10+
11+
This library is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
Lesser General Public License for more details.
15+
16+
You should have received a copy of the GNU Lesser General Public
17+
License along with this library; if not, write to the Free Software
18+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
21+
#include <Arduino.h>
22+
#include <Connectivity.h>
23+
24+
iLabsConnectivityClass::iLabsConnectivityClass(HardwareSerial* espSerial, HardwareSerial* modemSerial) {
25+
_espSerial = espSerial;
26+
_modemSerial = modemSerial;
27+
/* SARA pins */
28+
pinMode(PIN_SARA_ON, OUTPUT);
29+
digitalWrite(PIN_SARA_ON, LOW); // Output register must always be low
30+
pinMode(PIN_SARA_ON, INPUT_PULLUP);
31+
32+
pinMode(PIN_SARA_RST, INPUT_PULLUP); // Keep as input for now
33+
34+
pinMode(PIN_SARA_PWR, OUTPUT);
35+
digitalWrite(PIN_SARA_PWR, LOW); // No power to SARA yet
36+
37+
pinMode(PIN_SARA_DTR, OUTPUT);
38+
digitalWrite(PIN_SARA_PWR, HIGH); // Make sure DTR is low on the R412M
39+
40+
modemSerialPortConfigured = false;
41+
42+
/* ESP Pins */
43+
pinMode(PIN_ESP_RST, OUTPUT);
44+
digitalWrite(PIN_ESP_RST, LOW); // Hold ESP in reset
45+
pinMode(PIN_ESP_MODE, OUTPUT);
46+
digitalWrite(PIN_ESP_MODE, HIGH); // Prepare for normal start
47+
espSerialPortConfigured = false;
48+
}
49+
50+
// Do a HW reset by applying a low pulse to the reset line for 1mSec
51+
bool iLabsConnectivityClass::doModemPowerOn() {
52+
bool ret;
53+
digitalWrite(PIN_SARA_PWR, HIGH); // Make sure LDO is on
54+
delay(100); // let the power stabilize
55+
pinMode(PIN_SARA_ON, OUTPUT); // Pull power on control low
56+
delay(150); // For 150mS
57+
pinMode(PIN_SARA_ON, INPUT_PULLUP); // before releasing it again.
58+
delay(1000); // Now wait for 1 second
59+
60+
SARA_SERIAL_PORT.setRTS(PIN_SERIAL2_RTS); // Enable hardware handshaking
61+
SARA_SERIAL_PORT.setCTS(PIN_SERIAL2_CTS);
62+
SARA_SERIAL_PORT.begin(DEFAULT_SARA_BAUDRATE);
63+
modemSerialPortConfigured = true;
64+
ret = isModemAlive(); // Make sure the modem is up and running
65+
66+
delay(250); // Allow for any extra characters
67+
// before flushing the input buffer
68+
while (SARA_SERIAL_PORT.available()) {
69+
SARA_SERIAL_PORT.read();
70+
}
71+
72+
return ret;
73+
}
74+
75+
// Checks to see if the modem responds to the "AT" poll command.
76+
bool iLabsConnectivityClass::isModemAlive(uint32_t timeout) {
77+
SARA_SERIAL_PORT.setTimeout(100);
78+
SARA_SERIAL_PORT.println(F("AT"));
79+
String rdy = SARA_SERIAL_PORT.readStringUntil('\n');
80+
while (!rdy.startsWith(F("OK")) && --timeout) {
81+
SARA_SERIAL_PORT.println(F("AT"));
82+
rdy = SARA_SERIAL_PORT.readStringUntil('\n');
83+
//Serial.println(rdy);
84+
}
85+
SARA_SERIAL_PORT.setTimeout(1000); // Restore serial timeout
86+
if (timeout) {
87+
return true;
88+
}
89+
return false;
90+
}
91+
92+
// Return the current MNO profile
93+
// Returns -1 if the serial port is not yet setup or the number of the current
94+
// MNO profile setting from the modem.
95+
int iLabsConnectivityClass::getModemMNOProfile() {
96+
if (!modemSerialPortConfigured) {
97+
return -1;
98+
}
99+
SARA_SERIAL_PORT.println(F("AT+UMNOPROF?"));
100+
String resp = getModemResponse();
101+
return resp.substring(resp.indexOf("+UMNOPROF: ") + 11).toInt();
102+
}
103+
104+
// Set a new MNO profile
105+
// Returns false if the serial port is not yet setup or if an error
106+
// is detected during the communication with the modem.
107+
// This call is synchronous and will wait for the modem to start after
108+
// the soft restart which takes about 10 seconds.
109+
bool iLabsConnectivityClass::setModemMNOProfile(int profile) {
110+
if (!modemSerialPortConfigured) {
111+
return false;
112+
}
113+
114+
// Disconnect from the network
115+
SARA_SERIAL_PORT.println("AT+COPS=2");
116+
if (!getModemResponse().endsWith("OK")) {
117+
return false;
118+
}
119+
120+
String cmd = "AT+UMNOPROF=" + String(profile) + ",1";
121+
SARA_SERIAL_PORT.println(cmd);
122+
if (!getModemResponse().endsWith("OK")) {
123+
return false;
124+
}
125+
126+
// Restart the modem to apply the new MNO profile
127+
SARA_SERIAL_PORT.println("AT+CFUN=15");
128+
if (!getModemResponse().endsWith("OK")) {
129+
return false;
130+
}
131+
132+
return isModemAlive(15000);
133+
}
134+
135+
// Disable power save features
136+
bool iLabsConnectivityClass::enableModemPS(bool enable) {
137+
if (!modemSerialPortConfigured) {
138+
return false;
139+
}
140+
if (enable) {
141+
SARA_SERIAL_PORT.println(F("AT+CPSMS=1"));
142+
} else {
143+
SARA_SERIAL_PORT.println(F("AT+CPSMS=0"));
144+
}
145+
146+
if (!getModemResponse().endsWith("OK")) {
147+
return false;
148+
}
149+
return true;
150+
}
151+
152+
// Get a response from SARA
153+
// A default serial timeout of 2 seconds allow for reading really slow
154+
// responses which should accommodate most replies. Replies are then trimmed
155+
// from control characters and appended with a tab character as a separator.
156+
//
157+
String iLabsConnectivityClass::getModemResponse(int timeout) {
158+
SARA_SERIAL_PORT.setTimeout(2000); // allow for really slow responses
159+
160+
String resp = SARA_SERIAL_PORT.readStringUntil('\n');
161+
resp.trim();
162+
String acc = resp;
163+
while (resp.indexOf("OK") == -1 && resp.indexOf("ERROR") == -1 && --timeout) {
164+
resp = SARA_SERIAL_PORT.readStringUntil('\n');
165+
resp.trim();
166+
if (resp.length()) {
167+
acc += "\t" + resp;
168+
}
169+
}
170+
return acc;
171+
}
172+
173+
// Do a HW reset by applying a low pulse to the reset line for 1mSec
174+
void iLabsConnectivityClass::doEspHWReset() {
175+
digitalWrite(PIN_ESP_RST, LOW); // Hold ESP in reset
176+
delay(1);
177+
digitalWrite(PIN_ESP_RST, HIGH); // Release ESP reset
178+
}
179+
180+
// Set the mode flag high to indicate normal run operation and do a HW
181+
// reset.
182+
void iLabsConnectivityClass::runEspReset() { // Prepare ESP for normal op
183+
digitalWrite(PIN_ESP_MODE, HIGH); // Prepare for normal start
184+
doEspHWReset();
185+
}
186+
187+
// Set the mode flag low to indicate flash operation and do a HW
188+
// reset.
189+
void iLabsConnectivityClass::flashEspReset() { // Prepare ESP for flashing
190+
digitalWrite(PIN_ESP_MODE, LOW); // Prepare for normal start
191+
doEspHWReset();
192+
}
193+
194+
// Wait for the modem to reply with a "ready" prompt. This can be done
195+
// after a sw or hw reset have been performed to ensure that the AT
196+
// interpreter is up and running.
197+
bool iLabsConnectivityClass::waitForEspReady() {
198+
int timeout = 20; // Aprox max 2 sec
199+
200+
_espSerial->setTimeout(100);
201+
String rdy = _espSerial->readStringUntil('\n');
202+
while (!rdy.startsWith("ready") && timeout--) {
203+
rdy = _espSerial->readStringUntil('\n');
204+
}
205+
_espSerial->setTimeout(1000); // Reset default timeout to 1000
206+
if (timeout) {
207+
return true;
208+
}
209+
return false;
210+
}
211+
212+
// Reset the ESP and wait for the "ready" prompt to be returned.
213+
bool iLabsConnectivityClass::resetEsp() {
214+
runEspReset();
215+
_espSerial->begin(DEFAULT_ESP_BAUDRATE);
216+
return waitForEspReady();
217+
}
218+
219+
// Checks to see if the modem responds to the "AT" poll command.
220+
bool iLabsConnectivityClass::isEspAlive() {
221+
int timeout = 100;
222+
223+
_espSerial->setTimeout(250);
224+
_espSerial->println(F("AT"));
225+
String rdy = _espSerial->readStringUntil('\n');
226+
while (!rdy.startsWith(F("OK")) && timeout--) {
227+
_espSerial->println(F("AT"));
228+
rdy = _espSerial->readStringUntil('\n');
229+
}
230+
_espSerial->setTimeout(1000);
231+
232+
if (timeout) {
233+
return true;
234+
}
235+
return false;
236+
}
237+
238+
// Change the baud rate of the ESP device as well as the local UART.
239+
// No checking is done on the input baud rate so the user must know what
240+
// baud rates are valid. The function ends by checking if the ESP is
241+
// reachable by doing an "AT" poll.
242+
bool iLabsConnectivityClass::changeEspBaudRate(int baud) {
243+
_espSerial->print(F("AT+UART_CUR="));
244+
_espSerial->print(baud);
245+
_espSerial->println(F(",8,1,0,0"));
246+
delay(100);
247+
_espSerial->end();
248+
_espSerial->begin(baud);
249+
return isEspAlive();
250+
}
251+
252+
// This method should be called if the builtin object isn't needed any more
253+
// It basically just releases the UART pins for other use.
254+
void iLabsConnectivityClass::releaseEsp() {
255+
_espSerial->end();
256+
}
257+
258+
// We can assign a new hardware serial port to accommodate the ESP device here.
259+
// The function will release the previously used serial port and assign the
260+
// new port. The ESP will be left in a reset state ready to start normal
261+
// operation when exiting the function.
262+
// This function is useful for when using the PIO serial ports to communicate
263+
// with the ESP instead of the built in hardware serial port.
264+
void iLabsConnectivityClass::setEspSerial(HardwareSerial* serial) {
265+
266+
releaseEsp();
267+
_espSerial = serial;
268+
269+
pinMode(PIN_ESP_RST, OUTPUT);
270+
digitalWrite(PIN_ESP_RST, LOW); // Hold ESP in reset
271+
pinMode(PIN_ESP_MODE, OUTPUT);
272+
digitalWrite(PIN_ESP_MODE, HIGH); // Prepare for normal start
273+
}
274+
275+
// Return the current serial object
276+
HardwareSerial* iLabsConnectivityClass::getEspSerial() {
277+
return _espSerial;
278+
}
279+
280+
iLabsConnectivityClass iLabsConnectivity;

0 commit comments

Comments
 (0)