Categories
Arduino Project Project Ideas Raspberry Pi Pico RTOS Software Development

Projects in the Real World

Testing projects in real world situations can help uncover issues that we wish would have been discovered earlier. One such example was with my Speech Timer Project.

I traveled to Binghamton, NY so I could attend my family reunion and decided to arrive early to attend the Morning Knights Toastmasters meeting in-person. I brought along my Speech Timer to try it out at the meeting. Before the meeting even started, I ran into an issue. The clock connects to the internet to get the time to set the real time clock (RTC). I knew this but thought the clock would still operate as the RTC does not need to be set for the speech timer to work. The configuration is set for my home network so I knew it would not connect to the internet at the meeting location. I pressed the button on the remote to activate the timer function and nothing happened. I ended up tucking the clock back into my backpack. I’ll need to edit the code and run some tests to make absolutely certain that it will work when it cannot connect to the internet.

This lesson shows how important it is to test our projects in a variety of situations. I believe I have tested this scenario before, so I will need to make certain that I can recreate it with the current code base, then move on from there.

Categories
Arduino Project Raspberry Pi Pico Software Development

Code Rewrite

I have started to rewrite the code based on the analysis of the parameters and timings. I have yet to look across the code for the various TPMS sensors to see if they use common code. I suspect that they do, but if they don’t it may make the approach impractical. I plan toย investigate loading the parameters and timing values from files on the SD Card as well. I think the file approach may lead to a more sustainable approach.

The code rewrite starts with a structure, CC1101_Parameters, to hold the timing parameters and CC1101_Settings, to hold the settings for the CC1101 radio. The default values specified in the structures are the most common values for each property.

struct CC1101_Parameters {
  int expectedbitcount = 72;
  int expectedbytecount = 9;
  int expectedfifobytecount = 21;
  int syncbits = 16;
  int cdwidth_min = 5500;
  int cdwidth_max = 11500;
  int shorttiming_min = 35;
  int shorttiming_nom = 50;
  int shorttiming_max = 79;
  int longtiming_min = 80;
  int longtiming_max = 120;
  int synctiming_min = 175;
  int synctiming_max = 1200;
  int endtiming_min = 0;
  int endtiming_max = 500;
};  // CC1101_Parameters

struct CC1101_Settings {
  byte cc1101_defval_iocfg2 = 0x0C;    // GDO2 Output Pin Configuration - Serial out (synchronous)
  byte cc1101_defval_iocfg1 = 0x2E;    // GDO1 Output Pin Configuration - not used
  byte cc1101_defval_iocfg0 = 0x0E;    // GDO0 Output Pin Configuration - Carrier Sense output
  byte cc1101_defval_fifothr = 0x0F;   // RX FIFO and TX FIFO Thresholds - 64 bytes in FIFO
  byte cc1101_defval_sync1 = 0xD5;     // Synchronization word, high byte  11010101 01001111
  byte cc1101_defval_sync0 = 0x4F;     // Synchronization word, low byte
  byte cc1101_defval_pktlen = 0x09;    // Packet Length
  byte cc1101_defval_pktctrl1 = 0x00;  // Packet Automation Control
  byte cc1101_defval_pktctrl0 = 0x12;  // Packet Automation Control - synchronous data
  byte cc1101_defval_addr = 0x00;      // Device Address
  byte cc1101_defval_channr = 0x00;    // Channel Number
  byte cc1101_defval_fsctrl1 = 0x0F;   // Frequency Synthesizer Control
  byte cc1101_defval_fsctrl0 = 0x00;   // Frequency Synthesizer Control
  byte cc1101_defval_freq2 = 0x10;     // Frequency Control Word, High Byte
  byte cc1101_defval_freq1 = 0xB0;     // Frequency Control Word, Middle Byte
  byte cc1101_defval_freq0 = 0x71;     // Frequency Control Word, Low Byte
  byte cc1101_defval_deviatn = 0x40;   // Modem Deviation Setting (+/-25.390625kHz)
  byte cc1101_defval_mdmcfg4 = 0x59;   // Modem Configuration (59 = data rate = 20kHz, RX bw 325kHz)
  byte cc1101_defval_mdmcfg3 = 0x93;   // Modem Configuration (now 93 = data rate = 20kHz)
  byte cc1101_defval_mdmcfg2 = 0x10;   // Modem Configuration (GFSK, No Sync or Manchester coding)
  byte cc1101_defval_mdmcfg1 = 0x21;   // Modem Configuration Channel spacing 100kHz
  byte cc1101_defval_mdmcfg0 = 0xF8;   // Modem Configuration
  byte cc1101_defval_agcctrl2 = 0x87;  // AGC Control
  byte cc1101_defval_agcctrl1 = 0x58;  // AGC Control
  byte cc1101_defval_agcctrl0 = 0x80;  // AGC Control
  byte cc1101_defval_mcsm2 = 0x07;     // Main Radio Control State Machine Configuration
  byte cc1101_defval_mcsm1 = 0x3C;     // Main Radio Control State Machine Configuration
  byte cc1101_defval_mcsm0 = 0x18;     // Main Radio Control State Machine Configuration
  byte cc1101_defval_foccfg = 0x16;    // Frequency Offset Compensation Configuration
  byte cc1101_defval_bscfg = 0x6C;     // Bit Synchronization Configuration
  byte cc1101_defval_worevt1 = 0x87;   // High Byte Event0 Timeout
  byte cc1101_defval_worevt0 = 0x6B;   // Low Byte Event0 Timeout
  byte cc1101_defval_worctrl = 0xFB;   // Wake On Radio Control
  byte cc1101_defval_frend1 = 0x56;    // Front End RX Configuration
  byte cc1101_defval_frend0 = 0x10;    // Front End TX Configuration
  byte cc1101_defval_fscal3 = 0xE9;    // Frequency Synthesizer Calibration
  byte cc1101_defval_fscal2 = 0x2A;    // Frequency Synthesizer Calibration
  byte cc1101_defval_fscal1 = 0x00;    // Frequency Synthesizer Calibration
  byte cc1101_defval_fscal0 = 0x1F;    // Frequency Synthesizer Calibration
  byte cc1101_defval_rcctrl1 = 0x41;   // RC Oscillator Configuration
  byte cc1101_defval_rcctrl0 = 0x00;   // RC Oscillator Configuration
  byte cc1101_defval_fstest = 0x59;    // Frequency Synthesizer Calibration Control
  byte cc1101_defval_ptest = 0x7F;     // Production Test
  byte cc1101_defval_agctest = 0x3F;   // AGC Test
  byte cc1101_defval_test2 = 0x81;     // Various Test Settings
  byte cc1101_defval_test1 = 0x35;     // Various Test Settings
  byte cc1101_defval_test0 = 0x09;     // Various Test Settings
};                                     // CC1101_Settings

The next bit of code makes use of switch case statements to set the values based on the selected configuration.

void cc1101_init_vars() {
  switch (carSettings.freq) {
    case TPMS_Frequencies::UK_433MHz:
      switch (carSettings.tpmsSensorType) {
        case TPMS_Sensors::Citroen:
          cc1101_parameters.expectedbitcount = 88;
          cc1101_parameters.expectedbytecount = 10;
          cc1101_parameters.cdwidth_min = 8000;

          break;
        case TPMS_Sensors::Dacia:
          cc1101_parameters.cdwidth_min = 7000;

          cc1101_settings.cc1101_defval_freq0 = 0x0C;
          cc1101_settings.cc1101_defval_deviatn = 0x41;
          break;
        case TPMS_Sensors::Ford:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 10000;
          break;
        case TPMS_Sensors::Hyundai_i35:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 9000;
          cc1101_parameters.synctiming_min = 140;
          cc1101_parameters.synctiming_max = 160;
          break;
// ...
        case TPMS_Sensors::Zoe:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_min = 7000;

          cc1101_settings.cc1101_defval_freq0 = 0x0C;
          cc1101_settings.cc1101_defval_deviatn = 0x41;
          break;
        default:
          break;
      }
      break;
    case TPMS_Frequencies::US_315MHz:
      switch (carSettings.tpmsSensorType) {
        case TPMS_Sensors::Citroen:
          // UK 433 MHz Only
          break;
        case TPMS_Sensors::Dacia:
          // UK 433 MHz Only
          break;
        case TPMS_Sensors::Ford:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 10000;

          cc1101_settings.cc1101_defval_freq2 = 0x0C;
          cc1101_settings.cc1101_defval_freq1 = 0x1D;
          cc1101_settings.cc1101_defval_freq0 = 0x57;
          break;
        case TPMS_Sensors::Hyundai_i35:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 9000;
          cc1101_parameters.synctiming_min = 140;
          cc1101_parameters.synctiming_max = 160;

          cc1101_settings.cc1101_defval_freq2 = 0x0C;
          cc1101_settings.cc1101_defval_freq1 = 0x1D;
          cc1101_settings.cc1101_defval_freq0 = 0x57;
          break;
// ...
        case TPMS_Sensors::Toyota_TRW_C070:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 9000;
          cc1101_parameters.synctiming_min = 140;
          cc1101_parameters.synctiming_max = 160;

          cc1101_settings.cc1101_defval_freq2 = 0x0C;
          cc1101_settings.cc1101_defval_freq1 = 0x1D;
          cc1101_settings.cc1101_defval_freq0 = 0x57;
          break;
        case TPMS_Sensors::TruckSolar:
          // UK 433 MHz Only
          break;
        case TPMS_Sensors::Zoe:
          // UK 433 MHz Only
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

The next step is to determine if common code can be written for receiving the data from each brand of TPMS sensor.

Categories
Arduino Project Raspberry Pi Pico

Comparing two Objects in C++

I’m in the process of modifying the Tire Pressure Monitoring System code to allow configuration information to be retrieved and saved to an SD card as well as making use of the three buttons that I added to my version. I had an issue with the display not initializing as expected. Upon further investigation, I realized that it was an issue with the comparison of two objects, with one being the SPI bus for the CC1101 radio module and the other being the SPI bus for the display. The code initializes the CC1101 SPI bus by default as the CC1101 SPI is required. The code then should initialize the display SPI bus if it is being used for the display and is not the same as the CC1101 SPI bus.

The SPI busses for the CC1101 and display are different, but the check passed on the display SPI bus initialization because the values are the same. I needed to look at the addresses to see if they are pointing to the same location. I used some code that I found on StackOverflow. When I pulled some of the code to look at the values and addresses, I was left scratching my head as it looked like they had the same value, the addresses they were pointing to were different, but the addresses of the pointers were the same. I scratched my head for a few minutes then it dawned on me that the example code was actually displaying the address of the function parameter and not the address of the pointer. Once I figured that out, I started gaining my sanity back. I decided to add a response to the post with some modified code to show what I was expecting. Hopefully it will help someone later if they run into the same issue.

The StackOverflow post is at https://stackoverflow.com/questions/32914298/print-value-and-address-of-pointer-defined-in-function. Below is the reply that I added.


I had noticed that the address of the pointer was the same for each call of the function given in the examples. It then dawned on me the reason was the address displayed was for the function parameter pointer, not the pointer being passed to the function.

There is nothing wrong with the examples and they work as intended. When I first viewed the code, I thought the intent was to display the address of the pointer that was passed, so this caused some confusion for a few minutes. The results were not matching what I expected. Only when I looked more closely did I realize my mistake. Hopefully this will help someone else.

I modified the code in the examples to provide address of the pointer being passed rather than the address of the parameter. This code was written in an Arduino Sketch.

void pointerFuncA_Original(Print* p, const char* str, int *iptr) {
  p->printf("Value of %s: %d\n", str, *iptr);
  p->printf("Address of %s: %p\n", str, iptr);
  p->printf("Address of pointer to %s: %p\n", str, &iptr);
}

void pointerFuncA(Print* p, const char* str, int **iptr) {
  p->printf("Value of %s: %d\n", str, *(*iptr));
  p->printf("Address of %s: %p\n", str, *iptr);
  p->printf("Address of pointer to %s: %p\n", str, &(*iptr));
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  // Wait for the serial port to connect
  while (!Serial) ;

  int i = 1234;
  int *fooi = &i;
  int j = 5678;
  int *fooj = &j;
  Serial.println("--- Original Function outputs the same address for the pointer passed to the function. ---");
  pointerFuncA_Original(&Serial, "i", fooi);
  Serial.println();
  pointerFuncA_Original(&Serial, "j", fooj);
  Serial.println();

  Serial.println("--- Modified Function Calls showing correct address of the pointer passed to the function. ---");
  pointerFuncA(&Serial, "i", &fooi);
  Serial.println();
  pointerFuncA(&Serial, "j", &fooj);
  Serial.println();

  Serial.println("--- Verify ---");
  Serial.printf("Value of i: %d\n", i);
  Serial.printf("Address of i: %p\n", &i);
  Serial.printf("Address of fooi: %p\n", &fooi);
  Serial.println();
  Serial.printf("Value of j: %d\n", j);
  Serial.printf("Address of j: %p\n", &j);
  Serial.printf("Address of fooj: %p\n", &fooj);
}

void loop() {
  // put your main code here, to run repeatedly:

}

OUTPUT:

--- Original Function Calls same address the pointer sent to the function. ---
Value of i: 1234
Address of i: 0x20041fb8
Address of pointer to i: 0x20041fa4

Value of j: 5678
Address of j: 0x20041fc0
Address of pointer to j: 0x20041fa4

--- Modified Function Calls showing correct address of the pointer sent to the function. ---
Value of i: 1234
Address of i: 0x20041fb8
Address of pointer to i: 0x20041fbc

Value of j: 5678
Address of j: 0x20041fc0
Address of pointer to j: 0x20041fc4

--- Verify ---
Value of i: 1234
Address of i: 0x20041fb8
Address of fooi: 0x20041fbc

Value of j: 5678
Address of j: 0x20041fc0
Address of fooj: 0x20041fc4

The original version displays the address of the pointers to i & j as 0x20041fa4. The modified version and the verification check show the correct addresses of the pointers to i & j that were passed to the function.

Categories
Arduino Microcontroller Project Raspberry Pi Pico

DIY TPMS Project: Arduino-Based Tire Pressure Display

A few months ago, the TPMS light illuminated on the dash of my Toyota Prius. I looked at the tires and they looked fine, so I went to each tire measuring the pressure and they all appeared to be fine. I assumed that the battery in one of the tire sensors had died, but how can I tell for certain?

I found a project on hackster.io titled, “Arduino TPMS Tyre Pressure Display” that led me down a rabbit hole learning about TPMS. I found that there are two different types, indirect and direct. The type in my Prius are direct type, which measure the actually pressure in the tires. I also found that in addition to the tire pressure, they also report the temperature and battery health of the module.

All this information is available, but Toyota does not show this information anywhere in the 2007 Prius. If they did, it would improve safety and better experience in the following situations.

  • Stop running around the car to determine which tire is causing the dash light to illuminate.
  • When on a road trip, know if you need to pull over immediately or if it can wait until you arrive at your destination.
  • Identify slow leaks so they can be taken care of right away.
  • Improve gas milage by allowing the driver to top off the air pressure if it is a bit low, but not low enough to trigger the TPMS dash light to illuminate.

This project aims to help address the issues above and make up for Toyota not including this information on the information panel. The code and information are available in my GitHub repo at https://github.com/richteel/tpms/tree/main.

Categories
Arduino CircuitPython Microcontroller Photography Project Raspberry Pi Pico RTOS

Geotagging Photos

I found a good Instructable, GeoTagging With a Standalone GPS Unit & GeoSetter : 6 Steps – Instructables, to walk through geotagging photos with a standalone GPS device and the GeoSetter application. As a result of reading through the 14 year old Instructable, I landed on a somewhat standard CSV format that GPSBabel understands. As a result, the C++ code was updated in the GitHub repository, richteel/gps_tracker: GPS Tracker and data logger (github.com) to use the ‘Universal csv with field structure in the first line’ format. The first line of the CSV file contains the following headers: utc_d,utc_t,lat,lon,alt,head,speed.

  • utc_d: UTC date
  • utc_t: UTC time
  • lat: Latitude
  • lon: Longitude
  • alt: Elevation in meters
  • head: Heading in degrees
  • speed: Speed in meters per second

See: GPSBabel 1.9.0:Universal csv with field structure in first line (unicsv)

GPSBabel Screenshot
GPSBabel screen showing the ‘Universal csv with field structure in the first line’ format selected

GPS Logger

The GPS Logger that I’m discussing, was one I designed and built. The GPS Logger is Open Source, with the case, PCB, schematic, and code are included in the GitHub repository.โ€‚The code, which is currently being used is the C++ code in the folder, Arduino/gps_tracker. (The CircuitPython code may be updated shortly as well, but for now, it is best to use the C++ code.) GPSBabel is used to translate the CSV file from the GPS Logger into a GPX file, that GeoSetter may be used to geotag photos.

Software Used

First a note on the time setting on the camera. Previously, I would set the date and time to the local date time, however I frequently would forget to update when using the camera, which was problematic when switching between daylight saving time and standard time. I decided this time to simply set to UTC. I’m not certain if this will be a good solution, but we shall see.

The first step is to translate the GPS Logger CSV file into a GPX file, using GPSBabel.

The next steps are done using GeoSetter. My camera produces JPG files, so I have only set the values for JPEG.

GeoSetter File Settings – JPEG
GeoSetter Data Preferences Settings
GeoSetter Map Settings
GeoSetter Misc Settings

Once the settings have been edited, then it is possible to load the GPX file with GPS data and geotag files in a folder. Start by selecting all the photos in the photos panel to the left.

GeoSetter Application with all photos in the photos panel selected

To geotag the selected photos, go to the menu and select, Edit > Synchronize with GPS Data Files…

GeoSetter Menu – Edit > Synchronize with GPS Data Files…

A dialog will open, with options for synchonizing the photos and GPS information. I have selected the “Synchronize with a Directory containing Data Files:” option. Not sure why, but I needed to uncheck the “Request Time Zone by using Webservice” option for the geotagging to work.

GeoSetter – Synchronize with GPS Data Files dialog window

A dialog showing the number of matched files is displayed. If no matches are found, then you will be prompted to try again or cancel. In this example, all 20 images were matched with GPS data, so the “Yes” option is selected.

GeoSetter – Synchronize with GPS Data Files confirmation dialog

An additional confirmation dialog will be displayed asking if you wish to save the assigned track(s) to the current directory. Select the desired option.

The photos will be geotagged and the positions will now be displayed on the map.

GeoSetter showing a map with the locations of the photos

The photos are not saved at this point. You will need to select Edit > Save Changes, from the menu to write the EXIF information to the photos.

Once the photos have been saved, you may view the photos in Windows Photo Viewer to show that the location information is saved with the photos.

Windows default photo viewer showing that the location infromation is saved in the EXIF information of the photo

Alternatively, you may look at the file properties to see the EXIF information.

File Properties – Details, showing GPS data
Categories
Arduino CircuitPython Microcontroller Project Raspberry Pi Pico RTOS

Revisiting the GPS Logger

I’m on vacation/holiday this month, but before I left, I paid the GPS Logger project a visit and rewrote the code in C++, using the Arduino IDE and FreeRTOS to make it a bit more responsive to user input. The result is a nearly polished project that I’m very happy with, but I plan to reorganize the GitHub, project files, at richteel/gps_tracker: GPS Tracker and data logger (github.com), and provide a few different patterns for implementing such a project. While I like using FreeRTOS, it is not for everyone, particularly those just getting started with microcontroller projects. I also wish to revisit CircuitPython and see about implementing the FreeRTOS version of Python. I believe the CircuitPython implementation is still an alpha version so I may not be able to do anything with it yet, but may give it a go.

Here are some photos of the GPS Logger in action.

TeelSys GPS v0.5 C++
Date Time (UTC)
01/06/2024 07:54:29
Local (EST)
01//06/2024 02:54:29
Screen 1 of 6 – Main Time
Lat: 17.9689 N Long: 102.6152 E Alt: 600.72 ft Speed: 1.52 mph Head: 0.0 deg
Screen 2 of 6 – Location Information
Satellites: 5 GPS Fix: Yes Fix Quality: GPS 3D Quality: 3D GPS Enabled: Yes
Screen 3 of 6 – GPS Information
Battery: 3.94 V Battery: 69.94 % Status: Discharging
Screen 4 of 6 – Battery
Card: Present Type: SDHC/SDXC Total: 14.621 Gb Free: 14.620 Gb Files: 11
Screen 5 of 6 – SD Card Information
Memory - Heap Free: 154920 b 65.42% Freq: 133.00 MHz
Screen 6 of 6 – Memory

Why is the GPS Logger needed when photos on the phone are Geotagged or the phone is capable of this function?

While this functionality may be available with apps on the phone, it is not always possible to get tracks off of the phone easily to use with an application such as GeoSetter for photos taken with a dedicated camera, such as the Sony DSC-WX500. I want to be able to use the data gathered from the logger and geotag photos from cameras, which do not have a GPS. I also want to create maps, showing the tracks that we took, while on vacation. For both of these reasons, a dedicated GPS Logger is a good option. The file format of the current version is a CSV file, which may or may not work with the GeoSetter application, but I should be able to convert the data into a format that will be usable.

Categories
Arduino Project Raspberry Pi Pico RTOS

Progress

Well, it has been a productive week. Much progress has been made since getting to the root problem with FreeRTOS and WiFi on the Raspberry Pi Pico.

Like the phoenix rising from the ashes, I’ve been pulling in the code from my previous attempts and have the been piecing them together. Progress has been very quick. Too bad I did not dig into the library earlier, but honestly, I may not have hit on the root cause without some considerable investigating. Thankfully GitHub user, GUVWAF, had submitted a fix for the root cause around the same time I had submitted the pull request, so it was not necessary for me to dig into the libraries much more. I’m very thankful to Earle and Maximilian reviewing my pull request and promptly replying to my comments. Even though my pull request was not needed, I learned a great deal from the effort with Earle and Maximilian’s support.

I’m hopeful that I will be pushing some code to my SpeechTimer GitHub repository some time this week. I may not have all the features hammered out, but hope to be close. I will be working on some of the more difficult aspects, which include using the remote and providing a web interface for controlling the timer. The reason these are the more difficult aspects, is not so much in writing the code, but figuring out the best way to control the timer using the remote.

Update 11 December 2023

The project files are now live on GitHub at https://github.com/richteel/SpeechTimer. There are two things missing so far, but it a minimal usable project. The timer is controllable through the IR Remote. It has three predefined timers for typical Toastmasters meeting activities.

  • 5 to 7 Minutes for Speeches
  • 1 to 2 Minutes for Table Topics
  • 2 to 3 Minutes for Evaluations

There is a fourth timer that may be set using the numbers on the remote and the left and right buttons to change from minimum (left) to maximum (right) times.

The two missing features are the web user interface and the remote unit. I plan to work on the web interface first, but may work on the remote unit. I’m thinking about two different architectures for the web interface and need to decide which one I want to use. One is easy to implement, but it is only available on the same subnet as the timer. The other could control the clock from anywhere, but is a bit more difficult to implement.

Categories
Arduino Project 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 Project 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.

In 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 Project 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.