Categories
Arduino Raspberry Pi Pico RTOS

Raspberry Pi Pico W RTOS and Wi-Fi

It has been a few months since I posted last, but I’ve had a few frustrating months working on the Speech Timer project with a Raspberry Pi Pico W, RTOS, and Wi-Fi. It does not help that I can only work on this project for about 4 to 8 hours a week. This post will go into a few of the issues that I’ve run into and hoping to get the community to help me narrow down the issue.

Starting this project, I used CircuitPython to get the project up and running. It went fairly smoothly, but I ran into some issues that made it not such a good solution. Firstly, the program ran fine most of the time, but after some time, the heap would run out of enough contiguous space and would crash. This issue was still prevalent with garbage collection being called frequently and other optimizations to reduce heap fragmentation. I then turned my attention to C++ as it is possible to create more rock solid code than CircuitPython and the behavior is more predicable. Another reason for looking into C++ is I wanted to use RTOS to help make the user interaction more responsive.

The first problems to show up were with the IRremote library but most of the issues were a result of how the library is built. Once those limitations were mostly understood, it was possible to have the IR Remote working reliably.

The next issue came with the Wi-Fi connection. I still have not sorted it out, but still trying. I can get Wi-Fi working just fine if the FreeRTOS library is not included, but as soon as it is added, the Raspberry Pi Pico W attempts to connect to Wi-Fi, then locks up.

Here is some information on my setup:

  • BOARD: Raspberry Pi Pico W
  • IDE: Arduino IDE 2.2.1
  • CORE: Raspberry Pi Pico W core written by Earle F. Philhower, III
  • PROGRAMMING METHOD: Picoprobe

I made some changes to the WiFiMulti library to allow clearing the AP list and print out some status messages to let me know where things seem to be falling apart.

Below is the modified WiFiMulti.h file.

/*
WiFiMulti.h - Choose best RSSI and connect
Copyright (c) 2022 Earle F. Philhower, III

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Modified by Ivan Grokhotkov, January 2015 - esp8266 support
*/

#pragma once

#include <list>
#include <stdint.h>
#include "wl_definitions.h"

class WiFiMulti {
public:
WiFiMulti();
~WiFiMulti();

bool addAP(const char *ssid, const char *pass = nullptr);

void clearAPList();

uint8_t run(uint32_t to = 10000);

private:
struct _AP {
char *ssid;
char *pass;
};
std::list<struct _AP> _list;
};

Below is the modified WiFiMulti.cpp file.

/*
WiFiMulti.cpp - Choose best RSSI and connect
Copyright (c) 2022 Earle F. Philhower, III

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Modified by Ivan Grokhotkov, January 2015 - esp8266 support
*/

#include "WiFi.h"
#include <string.h>
#include <algorithm>

WiFiMulti::WiFiMulti() {
}

WiFiMulti::~WiFiMulti() {
while (!_list.empty()) {
struct _AP ap = _list.front();
_list.pop_front();
free(ap.ssid);
free(ap.pass);
}
}

bool WiFiMulti::addAP(const char *ssid, const char *pass) {
struct _AP ap;
if (!ssid) {
return false;
}
ap.ssid = strdup(ssid);
if (!ap.ssid) {
return false;
}
if (pass) {
ap.pass = strdup(pass);
if (!ap.pass) {
free(ap.ssid);
return false;
}
} else {
ap.pass = nullptr;
}
DEBUGV("[WIFIMULTI] Adding: '%s' %s' to list\n", ap.ssid, ap.pass);
_list.push_front(ap);
return true;
}

void WiFiMulti::clearAPList() {
while (!_list.empty()) {
struct _AP ap = _list.front();
_list.pop_front();
free(ap.ssid);
free(ap.pass);
}
}


uint8_t WiFiMulti::run(uint32_t to) {
struct _scanAP {
char *ssid;
char *psk;
uint8_t bssid[6];
int rssi;
};
std::list<struct _scanAP> _scanList;

// If we're already connected, don't re-scan/etc.
if (WiFi.status() == WL_CONNECTED) {
return WL_CONNECTED;
}

DEBUGV("[WIFIMULTI] Rescanning to build new list of APs\n");
int cnt = WiFi.scanNetworks();
if (!cnt) {
return WL_DISCONNECTED;
}

// Add all matching ones to the scanList
for (int i = 0; i < cnt; i++) {
for (auto j = _list.begin(); j != _list.end(); j++) {
if (!strcmp(j->ssid, WiFi.SSID(i))) {
_scanAP itm;
itm.ssid = j->ssid;
itm.psk = j->pass;
WiFi.BSSID(i, itm.bssid);
itm.rssi = WiFi.RSSI(i);
_scanList.push_front(itm);
}
}
}
// Sort by RSSI using C++ lambda magic
_scanList.sort([](const struct _scanAP & a, const struct _scanAP & b) {
return a.rssi > b.rssi;
});
for (auto j = _scanList.begin(); j != _scanList.end(); j++) {
DEBUGV("[WIFIMULTI] scanList: SSID: '%s' -- BSSID: '%02X%02X%02X%02X%02X%02X' -- RSSI: %d\n", j->ssid,
j->bssid[0], j->bssid[1], j->bssid[2], j->bssid[3], j->bssid[4], j->bssid[5], j->rssi);
}

// Attempt to connect to each (will be in order of decreasing RSSI)
for (auto j = _scanList.begin(); j != _scanList.end(); j++) {
DEBUGV("[WIFIMULTI] Connecting to: SSID: '%s' -- BSSID: '%02X%02X%02X%02X%02X%02X' -- RSSI: %d\n", j->ssid,
j->bssid[0], j->bssid[1], j->bssid[2], j->bssid[3], j->bssid[4], j->bssid[5], j->rssi);
uint32_t start = millis();
if (j->psk) {
WiFi.begin(j->ssid, j->psk, j->bssid);
} else {
WiFi.beginBSSID(j->ssid, j->bssid);
}
while (!WiFi.connected() && (millis() - start < to)) {
Serial1.print("^");
delay(5);
}
Serial1.print("\n");
if (WiFi.status() == WL_CONNECTED) {
Serial1.println("Connected");
return WL_CONNECTED;
}
}

// Failed at this point...
Serial1.println("Failed to Connect");
return WiFi.status();
}

The Arduino Sketch contains the following files:

  • sketch_dec02a.ino – Main Sketch file
  • Clk_SdCard.h – Header file for SD Card functions
  • Clk_SdCard.cpp – Code file for SD Card functions
  • Clk_Wifi.h – Header file for Wi-Fi functions
  • Clk_Wifi.cpp – Code file for Wi-Fi functions
  • config.h – Structure for configuration file
  • DbgPrint.h – Helper file for Serial statements
  • Defines.h – Pin definitions, programming mode, and debug flag
  • StructsAndEnums.h – Various structures and enumerations

The sketch_dec2a.ino file is shown below. All other files are included in the code.zip file in the “sketch_dec02a” folder. The zip file contains two other folders. The “lib” folder holds the two WiFiMulti library files, that may be copied into the library location. On my Windows machine, the location is “C:\Users\user\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\WiFi\src”. The other folder is “SDCard” and contains a sample config.txt file to be edited with your Wi-Fi configuration information and copied to the micro SD Card.
NOTE: The card reader needs to be connected to the pins defined in Defines.h, or the pin definitions need to be modified to match your setup.

/*****************************************************************************

* FreeRTOS Setup *
*****************************************************************************/
#include <FreeRTOS.h>
#include <task.h>
#include <semphr.h>
#define xPortGetCoreID get_core_num

/*****************************************************************************
* Project Files *
*****************************************************************************/
#include "DbgPrint.h" // Serial helpers
#include "Defines.h" // Pin definitions, programming mode, and debug flag
#include "config.h" // Structures for the configuration file
#include "Clk_SdCard.h" // Higher level SD Card functions
#include "Clk_Wifi.h" // Higher level Wi-Fi functions

/*****************************************************************************
* Other Includes *
*****************************************************************************/
#include <map>

/*****************************************************************************
* GLOBALS *
*****************************************************************************/
// Flags to make certain that required initialization is complete before tasks start
bool setup_complete = false;

// WiFi global settings
char last_ipaddress[16] = "";
WiFiMode_t last_WiFiMode = WIFI_OFF;

// Config global settings
unsigned long configLoadMillis = 0;
unsigned long last_configLoadMillis = 0;

// Module Objects
Clk_SdCard clockSdCard = Clk_SdCard();
Clk_Wifi clockWifi = Clk_Wifi();

/*****************************************************************************
* MAPPINGS *
*****************************************************************************/
// Mapping for string lookup
std::map<WiFiMode_t, const char *> wifiModeName{ { WIFI_OFF, "WiFi Off" }, { WIFI_STA, "Station Mode" }, { WIFI_AP, "Soft-AP Mode" }, { WIFI_AP_STA, "Station + Soft-AP Mode" } };

/*****************************************************************************
* INO FUNCTIONS *
*****************************************************************************/
void debugMessage(const char *message) {
Dbg_printf("%s\t%d\t%d\n", message, xPortGetCoreID(), rp2040.getFreeHeap());
}

/*****************************************************************************
* TASKS *
*****************************************************************************/
// void checkWiFi(void *param) {
void checkWiFi() {
// Make certain that setup has completed
while (!setup_complete) {
vTaskDelay(10 / portTICK_PERIOD_MS);
}

int loopCount = 0;

while (1) {
Dbg_print("."); // Print ... while looping so we know that the loop is running
// If the SD Card was inserted/reinserted, use the new configuration to connect to the Wi-Fi AP
if (configLoadMillis != last_configLoadMillis) {
Dbg_println("START: Restart Wi-Fi with new Config");
clockWifi.begin(&clockSdCard.sdCardConfig);
Dbg_println("Wi-Fi Begin Completed");
last_configLoadMillis = configLoadMillis;
Dbg_println("END: Restart Wi-Fi with new Config");
}

// Set the IP Address property
clockWifi.hasIpAddress();

// If the IP Address has changed, print the IP Addess
if (strcmp(last_ipaddress, clockWifi.ipAddress) != 0) {
Dbg_println("START: Update IP Address");
strcpy(last_ipaddress, clockWifi.ipAddress);
Dbg_printf("IP Address has changed to %s\n", last_ipaddress);
Dbg_println("END: Update IP Address");
}

// If the Wi-Fi Mode has changed, print debug message
if (last_WiFiMode != clockWifi.wifiMode) {
Dbg_println("START: Print Debug Statement");
last_WiFiMode = clockWifi.wifiMode;
Dbg_printf("Wi-Fi has changed to %s\n", wifiModeName[last_WiFiMode]);
Dbg_println("END: Print Debug Statement");
}

loopCount++;
// Print message with stack usage once every ten loops
if (loopCount > 10) {
debugMessage("checkWiFi");
loopCount = 0;
}
vTaskDelay(250 / portTICK_PERIOD_MS);
}
}

/*****************************************************************************
* SETUP *
*****************************************************************************/
void setup() {
Dbg_begin(115200);

// Wait for the serial port to connect
while (DEBUG && !(Serial || Serial1)) {
delay(1); // wait for serial port to connect.
}

// Print 5 blank lines on startup
for (int i = 0; i < 5; i++) {
Dbg_println("");
}

clockSdCard.begin();
// NOTE: The SD Card needs to be inserted with the Config.txt file or the code will
// never attempt to connect to Wi-Fi. In the final version, there will be a task to
// check the SD Card and reload the configuration file.
if (clockSdCard.isCardPresent()) {
configLoadMillis = millis();
}

Dbg_printf("sdcard_init_complete = %s\n", configLoadMillis != last_configLoadMillis ? "true" : "false");

debugMessage("-----------------------------");

debugMessage("END: setup()");
setup_complete = true;
}

/*****************************************************************************
* LOOP *
*****************************************************************************/
void loop() {
// The core written by Earle F. Philhower, III, requires that the Wi-Fi be
// accessed from Core 0, therefore it needs to run in the loop.
checkWiFi();
}

The code typically hangs with the following output.

START: Reading Config File

0. MySSID_One Password1

1. MySSID_Two Password2

2. MySSID_Three Password3

FINISHED: Reading Config File

sdcard_init_complete = true

----------------------------- 0 165684

END: setup() 0 165684

.START: Restart Wi-Fi with new Config

------^^------ WIFI: Added AP MySSID_One ------^^------

------^^------ WIFI: Added AP MySSID_Two ------^^------

------^^------ WIFI: Added AP MySSID_Three ------^^------

Connecting WiFi...

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Once in a great while the code runs properly, but if it failed to connect, the second attempt most likely will hang.

START: Reading Config File

0. MySSID_One Password1

1. MySSID_Two Password2

2. MySSID_Three Password3

FINISHED: Reading Config File

sdcard_init_complete = true

----------------------------- 0 165684

END: setup() 0 165684

.START: Restart Wi-Fi with new Config

------^^------ WIFI: Added AP MySSID_One ------^^------

------^^------ WIFI: Added AP MySSID_Two ------^^------

------^^------ WIFI: Added AP MySSID_Three ------^^------

Connecting WiFi...

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Failed to Connect



Failed to connect to Wi-Fi. Trying again...

------^^------ WIFI: Added AP MySSID_One ------^^------

------^^------ WIFI: Added AP MySSID_Two ------^^------

------^^------ WIFI: Added AP MySSID_Three ------^^------

Connecting WiFi...

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Then once in a while it will connect just fine.

START: Reading Config File

0. MySSID_One Password1

1. MySSID_Two Password2

2. MySSID_Three Password3

FINISHED: Reading Config File

sdcard_init_complete = true

----------------------------- 0 165684

END: setup() 0 165684

.START: Restart Wi-Fi with new Config

------^^------ WIFI: Added AP MySSID_One ------^^------

------^^------ WIFI: Added AP MySSID_Two ------^^------

------^^------ WIFI: Added AP MySSID_Three ------^^------

Connecting WiFi...

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Connected



WiFi connected, SSID: MySSID_One, IP address: 192.168.1.45

Connected in 8015 ms

------^^------ WIFI: Starting Station Mode ------^^------

Wi-Fi Begin Completed

END: Restart Wi-Fi with new Config

START: Update IP Address

IP Address has changed to 192.168.1.45

END: Update IP Address

START: Print Debug Statement

Wi-Fi has changed to Station Mode

END: Print Debug Statement

..........checkWiFi 0 164684

...........checkWiFi 0 164684

...........checkWiFi 0 164684

...........checkWiFi 0 164684

...........checkWiFi 0 164684

...........checkWiFi 0 164684

.......

If you have any thoughts on what may be happening, please comment to let me know. Also check the discussion board on Earle’s core library on GitHub at https://github.com/earlephilhower/arduino-pico/discussions?discussions_q=RTOS. I plan to post a comment there and point to this post.

Thank you in advance for any help on this issue.

UPDATE 3 December 2023

I had found that the delay statement in the WiFIMulti.ccp file was causing the issue with FreeRTOS. I modified it with the blink without delay example and the code worked. I created a pull request for the change and hope it will become part of the code base in the future so hopefully no one else will run into this issue.

UPDATE 4 December 2023

It seems that it was dumb luck yesterday that made me think the issue was with the delay statement. I had created a pull request for the change and fortunately, Earle F. Philhower, III pushed back a bit and stated that it did not make sense that delay would be causing an issue. I was doing some troubleshooting with him and Maximilian Gerhardt, then it appeared that my fix was no longer working. I had forgot that I had modified the fix to handle a rollover in millis, which was really unnecessary. When I removed that segment, the timing loop replacement for delay behaved exactly the same way. I then modified the code to use delay and remove the timing loop and put the if block back in and it worked. It appears that having only a delay in the main loop is the cause of the issue. If anyone has an idea why, please feel free to submit a comment or better yet go to the pull request and add a comment there. The link to the pull request is https://github.com/earlephilhower/arduino-pico/pull/1878.

Original block of code.

while (!WiFi.connected() && (millis() - start < to)) {
    delay(5);
}

My original modification.

while (!WiFi.connected() && (millis() - start < to)) {
	// Replaced delay(5); with the following to prevent
	// failure when using RTOS
	unsigned long tWifiDelayForRtos_start = millis();
	while (millis() - tWifiDelayForRtos_start <= 5) {
		if (millis() < tWifiDelayForRtos_start) {
			// millis wrapped around to zero. Rare to hit this
			// issue, but it will do this every 49 days.
			tWifiDelayForRtos_start = millis();
		}
	}
}

Modification without the if block. Behaved the same as the original code.

while (!WiFi.connected() && (millis() - start < to)) {
	// Replaced delay(5); with the following to prevent
	// failure when using RTOS
	unsigned long tWifiDelayForRtos_start = millis();
	while (millis() - tWifiDelayForRtos_start <= 5) {
		;
	}
}

Modification with delay. Note that all the other code in while loop is unnecessary as it does nothing useful. It is only needed to keep the code running, but why?

while (!WiFi.connected() && (millis() - start < to)) {
	// Replaced delay(5); with the following to prevent
	// failure when using RTOS
	unsigned long tWifiDelayForRtos_start = millis();
	if (millis() < tWifiDelayForRtos_start) {
		// millis wrapped around to zero. Rare to hit this
		// issue, but it will do this every 49 days.
		tWifiDelayForRtos_start = millis();
	}
	delay(5);
}

UPDATE 5 December 2023

Finally got to the bottom of this issue. There was a check-in of new code around the same time as I posted about this issue. The change was in the FreeRTOS code in the file variantHooks.cpp. The call to portENABLE_INTERRUPTS(); was moved from before the call to vTaskPreemptionEnable(nullptr) to after the call. That change addressed the exact problems that I was seeing.

Glad to see that this is finally addressed. I think this may be related to some other issues that I experienced, but not certain. Now I can get back to the project and hopefully wrap it up soon.

If you are interested, the commit for the actual fix is d2461a1.

Categories
Arduino CircuitPython Raspberry Pi Pico Software Development

September Update

Another month has gone by and I am still working on the Speech Timer project. I was able to develop a solution with CircuitPython but was not happy with the end product. Perhaps it would be possible to get to a more stable version but even with all the RAM available on the Raspberry Pi Pico W, I would still run into issues with the heap becoming fragmented and not having enough free space to allocate additional space to server web pages reliably. I tried many of the tips to resolve the issue including calling garbage collection periodically. These changes helped the code run a bit longer but still ran into issues. I decided to rewrite the code in C++, using the Arduino IDE and wanted to be able to debug my code on the Raspberry Pi Pico while it was running as I ran into an issue when I changed the configuration to disable serial debugging output by not calling Serial.begin().

I made a Picoprobe and attempted to debug my code, but things were not as straight forward as they could have been, which resulted in many hours waisted.

I had followed the instructions in the getting-started-with-pico.pdf guide and used the Windows Pico SDK, but could not get debugging to work in Visual Code or the Arduino IDE. I was nearly ready to setup another PC with Linux and give that a go as I had seen folks saw that just works with no issues. I’m running Windows, which I assumed was part of the issue. I saw another Windows user who was very frustrated as well and stating that it seemed they were the only ones testing/using this in Windows. They eventually got it working although they could not figure out the root cause. Unfortunately I too got it working and have no clear indication of what the root issue was but I have an idea that there is an issue with the Windows setup scripts that needs to be addressed. If someone has Visual Code and/or Arduino IDE installed before installing the SDK, they will have issues. This may not be the fault of the SDK, it could be with the Visual Code and Arduino IDE setup but I suspect it is indeed the Pico SDK setup on Windows that is the issue.

Out of frustration, I reinstalled the Windows Pico SDK and checked Visual Code and the Arduino IDE. Magically, Visual Studio code worked but the Arduino IDE still failed. I then uninstalled the Arduino IDE, then restarted the PC. Once the PC restarted, I deleted the C:\Users\<user id>\AppData\Local\Arduino15 and C:\Program Files\Arduino IDE folders to clear out anything that was already configured. I then reinstalled the Arduino IDE and tried again and the Arduino IDE worked too.

There really is a problem with the Pico SDK but it will be difficult to fix unless someone can figure out what changed from the problem install and the fixed install. Unfortunately, I did not capture anything before so I cannot tell what went wrong.

End the end, once debugging was working, I was able to step through my code and found the issue in about 2 minutes. Too bad the debugging fiasco caused several days of wasted time trying to get the debugging working.

Categories
AI Raspberry Pi Pico Web Application Development

Quick Update

Just wanted to drop a quick update as I have not been able to post for the past few weeks. I am working on a couple of projects that I plan to write about shortly.

Speech Timer Clock

I have been working on a speech timer clock for my Toastmasters Club. I took an alpha version to a club meeting to get some feedback and took the feedback and have been making improvements to it. Below is a quick video showing a little of the clock’s operation.

Speech timer showing demo of 1 to 2 minute speech

Artificial Intelligence (AI)

I looked into CodeProject.AI Server and found it very easy to use and useful. I plan to write some examples and corrected examples that are posted on CodeProject website. I have started a GitHub Repository for the examples at https://github.com/richteel/AI_Sample.

Categories
Arduino Raspberry Pi Pico

Raspberry Pi Pico with Arduino IDE

The Raspberry Pi Pico may be programmed in the Arduino IDE. There are three board libraries available but I found that the one written by Earle F. Philhower, III works best. Below are the steps that I took to get the example blink sketch loaded on the Raspberry Pi Pico.

  1. In the Arduino IDE, open the Boards Manager
  2. Type “Pico” in the search box
  3. If the “Arduino Mbed OS RP2040 Boards” is installed, click the “Remove” button to uninstall it
  4. If the  “Raspberry Pi Pico/RP 2040” is not installed, click the “Install” button to install it
Boards manager in the Arduino IDE
  1. Connect the Raspberry Pi Pico to the PC through the USB Port
  2. In the Arduino IDE menu, select the “Raspberry Pi Pico” board by going to Tools > Board > Raspberry Pi RP2040 Boards(3.2.0) (in Sketchbook) > Raspberry Pi Pico
Selecting the Raspberry Pi Pico board
  1. In the Arduino IDE menu, select Tools > Port from the menu
    • If this is the first time connecting the Raspberry Pi Pico to the PC, select UF2 Board
First time programming, select UF2 Board
    • If this is not the first time, then a list of COM Ports are available. Open the Device Manager to see the available COM Ports and determine which one is the Pico board. You may unplug the Pico Board, wait for the Device Manager to refresh with one less COM Port, then plug the Pico board back in. Note, which new COM Port appears, that will be the one to select in the Arduino IDE.
Device Manager showing the Ports
Port, select the correct COM Port
  1. Open the example “Blink” sketch from the menu File > Examples > 01. Basics > Blink
  2. Click the “Upload” button in the Arduino IDE to load the sketch onto the Pico board
    NOTE: You may see several warnings about whitespace. These warnings may be ignored
Successfully uploaded sketch
  1. You should see the LED on the Pico board flashing once the sketch is uploaded

Hopefully, this gets you up and running. It is always a good idea to run the example blink program first when configuring a new board. It lets you know right away if things are working as expected. Once that works, then move onto your code.

Categories
Android Arduino Meshtastic Microcontroller Raspberry Pi Pico

Meshtastic Serial

I wanted to see about connecting a Raspberry Pi Pico to a LillyGo TTGO T-Beam v1.1 device. I noticed that Meshtastic supports serial communications, so I decided to give it a go to see how it worked.

There are several serial modes but the ones that seem the most useful are TXTMSG and PROTO. First attempt will be with the TXTMSG Mode as that seems straight forward. Once the TXTMSG Mode is working, I will look into how to use the PROTO Mode.

Wiring

We need to connect the grounds between the two devices, then connect the transmit (TX) from one to the receive (RX) of the other device. Below is a table showing the connections used in my setup.

T-BeamPico
RX pin 13TX pin 1 (GP0)
TX pin 14RX pin 2 (GP1)
GNDGND
T-Beam and Pico wiring
Wiring between T-Beam and Raspberry Pi Pico

Meshtastic Setup

Meshtastic firmware was installed using the Web Installer at https://flasher.meshtastic.org/. The T-Beam came with Meshtastic preinstalled. You may need to use another method to install the firmware if the Web Installer does not work.

Meshtastic Web Installer
Meshtastic Web Installer

T-Beam TEXTMSG Mode

Once Meshtastic has been installed on the T-Beam device and connected to the Android or Apple application, go to the Module Settings to setup the serial connection on the T-Beam device. The Module Settings is accessed by clicking on the kebab menu (aka three vertical dots menu) and selecting “Module Settings”.

kebab menu
Kebab Menu
Module Settings menu item
Module Settings menu item

Once the Module settings are displayed, scroll down to the “Serial Config” section and set the following items.

  • Serial enabled: turn on
  • RX: Set it to the T-Beam pin number for receive, which is 13 in my setup.
  • TX: Set it to the T-Beam pin number for transmit, which is 14 in my setup.
  • Serial baud rate: May leave it at the default setting or set it to “BAUD_38400”. I think it is best to set it as the default baud rate may change in other versions. I believe I read that it did change in the past.
  • Serial mode: Set it to TEXTMSG
  • Once everything is set, click the “Send” button.
Serial Configuration
Serial Configuration

Pico Arduino Code

The Pico code is written in C++ using the Arduino IDE. It is necessary to configure use the Pico Board provided by Earle F. Philhower, III. First, add the URL, https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json, to the Additional Boards Manager URLs by going to File > Preferences in the menu.

Arduino Preferences Menu Item
Arduino Preferences Menu Item
Arduino IDE Preferences
Arduino IDE Preferences

Click the icon to the left of the “Additional boards manager URLs” entry. Add the URL to the a new line in the textbox and click the “OK” button.

Additional Boards Manager URLs
Additional Boards Manager URLs

Open the boards manager by clicking on the boards manager icon, type “Pico” in the search textbox, and install the board, Raspberry Pi Pico/RP2040 by Earle F. Philhower, III.

Boards Manager
Boards Manager

Once the board is installed, you may select it from the boards dropdown selection in the IDE, when the Pico is connected to the PC.

Pico selected in the boards drop-down list
Raspberry Pi Pico selected in the boards drop-down list
/*
  Sample code to allow the Pico to act as a serial bridge between the PC and the Meshtastic device.

  Data sent to the Pico using the Arduino Serial Monitor, PuTTY, or other terminal software is sent
  to the Meshtastic device over the Pico UART0/Serial1 connection. Any data received from the Meshtastic
  device to the Pico is relayed to the PC over the Pico's serial over USB connection.

  REFERENCES:
    - https://meshtastic.org/docs/settings/moduleconfig/serial
    - https://github.com/earlephilhower/arduino-pico/discussions/210
*/

void setup() {
  // PC to Pico
  Serial.begin(9600);
  // Pico to Meshtastic device
  Serial1.begin(38400);
  while (!Serial)
    ;  // Serial is via USB; wait for enumeration
}

void loop() {
  // If data is received from the Meshtastic device, send it to the PC over the USB connection
  if (Serial1.available()) {
    String receiveMessage = Serial1.readString();
    Serial.print("Message received on Serial1 is:  ");
    Serial.println(receiveMessage);  // Send to serial monitor
  }

  // If data is received from the PC, send it to the Meshtastic Device
  while (Serial.available()) {
    int inByte = Serial.read();
    Serial1.write(inByte);
  }
}

Upload the code to the Raspberry Pi Pico. Once the code is loaded, open the serial monitor and type some text and hit enter. The message will be received on the other node(s).

Sending message from PC
Sending message from PC
Message received on other node
Message received on other node

Sending a message from another node, will be received and shown in the serial terminal.

Sending message from another node
Sending message from another node
Receiving message on PC
Receiving message on PC

Now the simple TEXTMSG is working, we can try to get the PROTO working. The PROTO mode is interesting as it may be possible to configure the Meshtastic device, and query it for additional information. I will look into the PROTO Mode in the near future.

Categories
Psion Raspberry Pi Pico

Re-creation of Psion Organiser II

In 1984, Psion launched the first practical pocket computer, the Psion Organiser I. Psion followed up with the Psion Organiser II.

Andrew Menadue has a project which recreates the Psion Organiser II with the Raspberry Pi Pico and ESP32-WROOM-32. He has several videos on YouTube and some GitHub repositories. Andrew was nice enough to send me a set of boards, which I’m in the process of building.

Bill of Materials

The bill of materials is an Excel document located on GitHub.

Power Supply

Categories
Raspberry Pi Pico

Armachat

I have been working on the Armachat project by Peter Misenko. I rewrote the code to implement functions that Peter had not completed and added a few more. Some of the added features include:

  • Implemented detection of a long keypress
  • Dedicated keys when not in the editor
    • Q – Toggle the keyboard backlight
    • A – Toggle the display backlight
    • V – Increase volume / Long keypress to decrease volume
    • B – Increase display brightness / Long keypress to decrease display brightness
  • Ability to receive messages in all screens including editor
  • Validation of editor input
  • Confirmation prompts

I have been working on an issue with editor text input. The display update procedure takes 400 to 500 milliseconds which causes an issue as keypresses are dropped when typing quickly. Initially it was taking nearly 1 second to update the screen so some improvement has been made but it is still not good enough. I continue to work on improving the screen draw time and may deploy a few things to allow all keys to be detected and handled.

Another area for improvement is the long keypress detection. I noticed that removing the long keypress detection speed up the code enough to catch all key presses however the added functionality that the long keypress provides is too much to ignore.

GitHub locations for Peter’s code and my rewrite.