Replacing an Arduino UNO With an Adafruit Metro RP2350 (A Case Study)
by BoscoHern in Circuits > Electronics
11 Views, 0 Favorites, 0 Comments
Replacing an Arduino UNO With an Adafruit Metro RP2350 (A Case Study)

Most readers will be familiar with the Arduino UNO. The Adafruit Metro RP2350 is an (almost) drop-in replacement for the Arduino UNO, but with a few differences that need to be accounted for. This Instructable details the hardware and firmware changes that were required to migrate a non-trivial Sand Table application from the Arduino UNO board to the Adafruit Metro RP2350 board.
Supplies
- 3D Printer
- Laser Engraver
- Sand Table - see below
- Arduino IDE
- Adafruit Metro RP2350
- IRLZ44N Mosfet
- 100K Resistor
The Application

A recent Instructable presented the World's First Cycloid Art Table: How I Built This Arduino-Powered Spirograph Machine by NewsonsElectronics. This sand table uses laser cut parts and is driven by an Arduino UNO. I had been looking for an affordable sand table project and this one fit the bill. During the construction of the sand table, I noted a few areas that needed clarification, and some areas for improvement. These are documented on my GitHub page. One of the major issues I noted was that the Arduino UNO is somewhat under powered for this application with it's relatively slow processor and very limited memory size. For example, my updated Arduino program for the sand table used nearly 100% of both flash and RAM, leaving no room for the future addition of shapes or other firmware improvements. In addition, the calculations to generate certain shapes caused some jerky servo motion which was partially due to the slow Arduino UNO.
Adafruit Metro RP2350 to the Rescue

The Adafruit Metro RP2350 is a tremendous improvement in processor speed and memory size over the Arduino UNO. Check out of some of the more important features of each board above.
The Adafruit Metro RP2350 has the same physical size and shape as the Arduino UNO, and has (almost) the same pinout. More on that later. This allows the Metro to interface with the Arduino CNC shield with very few changes. The following sections describe the changes, some required and some optional, that I made to the sand table firmware and hardware. I am quite pleased with the results.
Data Type Changes (optional)
Since the UNO was tight on space, all data types were originally declared as the smallest size that would do the job in the UNO program. In most cases (u)int16_t was used since the UNO compiler uses 16-bit integers as its native integer size. Using the native integer size saves on both memory size and execution time since using a different sized integer requires the compiler to generate and execute extra code.
The RP2530 that is used by the Metro has lots of memory compared to the UNO and its native integer size is 32 bits. Therefore, when using the RP2350, memory size is unimportant, and execution speed becomes the major goal. As a result, almost all integer data types were changed to fast types. For example, uint16_t becomes uint_fast16_t, etc. The _fast_ data types allow the compiler to use an integer size that is as least as large as the specified size, but may be larger if larger integers are faster on the particular machine. In the case of the RP2350, since the native integer size is 32 bits, a (u)int_fast16_t will actually use 32 bits, but the same compiled for the Arduino UNO will use 16 bits.
Timer Handling
The UNO version needs the timer clock rate to be set up before use as follows:
The RP2350 already has its timer running at 1MHz, so doesn't require any further initialization. Thus, the timer initialization code was removed.
Speed Delay Value Changes
In most cases, clock periods and timer dependent UNO code will need to be updated to perform correctly with the RP2350. For example, the sand table Arduino UNO program set the timer clock period to 4 microseconds (uSec) while the default RP2350 timer clock period is 1 uSec. It was decided that the 1uSec RP2350 timer was acceptable and didn't need to be changed. To account for this, all speed delay values were multiplied by 4 in the RP2350 version. For example SPEED_DELAY_MIN_VAL was changed from 25 on the UNO to 100 on the RP2350, etc. Speed increments in HandleRemoteCommands() were also adjusted by a factor of 4.
Interrupt Handling
The UNO uses a simple interrupt setup with ISR(vector) indicating that an interrupt will be handled by the function. For example, the following outline shows how the UNO Interrupt Service Routines ( ISRs) were defined in the sand table application:
The RP2350 handles timer interrupts more normally in that the ISR must be declared before use, and added to the timer list before it can be used as follows:
In order for the interrupt to repeat periodically, the UNO bumps the corresponding OCR1x register value at the start of each ISR as shown above. For the RP2350 ISR to repeat, it returns the delay value from its ISR as a negative value, as shown above, indicating that the next execution should start exactly that many microseconds after the time the current ISR was triggered.
Random Seed Generation
It is difficult to generate a good random number seed on the Arduino UNO. The latest UNO implementation of the sand table firmware used an open analog input and a function - GenerateRandomSeed() - to generate a random seed at startup. The RP2350 has a hardware based true random number generator with a standard function - get_rand_32() - to generate a 32-bit random number. So the use of GenerateRandomSeed() was replaced with get_rand_32() in the RP2350 version.
API Math Function Differences
The Arduino UNO version of the sand table firmware uses the squaref() function to square a float value. This function doesn't exist in the RP2350 compiler. It is replaced with the sq() function that does the same thing. So uses of squaref() were replaced with sq().
Execution Speed Differences
The sand table firmware contains logic in the rotary servo ISR to generate compensation pulses in the in/out axis proportional to the rotary axis movement. Part of this logic uses a short delay to affect a pulse on the in/out servo. In the UNO, a pulse delay of 1 microsecond was used. Due to the slow speed of the UNO processor, this delay plus the surrounding code was sufficient to generate a pulse long enough for the servo driver hardware to handle. However, since the RP2350 is so much faster, it was feared that the generated pulse might be too short. Therefore the pulse delay was changed to 5 microseconds. This was probably not necessary, but was done just in case. I am currently considering using the RP2350 PIO state machines to generate the pulses to control the servo drivers. If this is implemented, the hard wired delays will be unnecessary and will be removed from the ISR code.
GPIO Differences
The Arduino UNO sand table firmware uses D12 as the enable output for the rotation axis. However, the Metro RP2350 uses D12 for HSTX connectivity. The UNO pin that is occupied by D12 is now occupied by D22 on the Metro board. As a result, the RP2350 code was changed to initialize D22 as the rotation axis enable output instead of D12.
Serial Logging
The Arcuino UNO version of the sand table uses the SerialLog.h file provided the ability to selectively log data to the serial port based on the value of user selectable macros. The major problem with the UNO implementation is that the API did not provide a printf() function, and the sprintf() function provided by the UNO API did not handle 64-bit integers or floating point numbers. As a result, many places in the sand table code resorted to clumsy sequences of log generation. For example, the following code appears in the UNO version of the PlotShapeArray() function:
This was necessitated due to the UNO sprintf() function not supporting floats. The RP2350 API provides a fully operational printf() function that can handle all data types. As a result, the SerialLog.h file was replaced with SerialLog2350.h which handles the logs more gracefully. For example, the above 5 log statements were replace with the following in the RP2350 code:
Temporarily Handle Compiler Floating Point Issue
I initially spent about an hour trying to find out why certain shape paths were not being generated correctly in my first try of the RP2350 code. I eventually discovered that some floating point trig operations were sometimes returning incorrect values. After a web search, I came across the RP2350 FPU compiler issue #2429. It looks like this issue has been fixed on a development branch, but as of this writing has not been released to production code yet. Until it is fixed, my workaround is to change all floats to doubles, and change all float function calls to their double equivalent. This naturally makes the code execution slower and larger. I will undo these changes once a good version of the compiler is released. The following is a list of from/to strings that need to be made to revert the code to using floats instead of doubles:
- double => float_t
- cos() => cosf()
- sin() => sinf()
- hypot() => hypotf()
- atan2() => atan2f()
- round() => roundf()
- trunc() => truncf()
- sqrt() => sqrtf()
- pow() => powf()
3.3 Volt Vs 5 Volt Processor Differences
The Arduino UNO is a 5 volt device. The RP350 runs at 3.3 volts. The original sand table design fed the speed and brightness pots from the UNO's 5V source. While the Metro RP2350 has a 5V output pin, it should not be used with the analog inputs (or GPIO pins for that matter). The simple change is to move the 5V pot connection to the CNC shield 3.3V pin (next to the RST pin).
Make MOSFET Handle 3.3V
The sand table used an IRFZ44N MOSFET and PWM output to control the brightness of the LEDs. The IRFZ44N has a Gate Threshold Voltage (VGS) of 2 - 4 volts. This is a little too high for the 3.3V used by the RP2350. The MOSFET would not fully turn on with the RP2350's 3.3 volts. The IRFZ44N was replaced with the IRLZ44N which has a VGS of 1-2 volts. This allows the MOSFET to turn on fully. Further research revealed that it is also a good idea to add a 100K resistor across the gate and source of the MOSFET to keep it from conducting at power-up before setup() runs. A 100K resistor was added as such and found to work great.
Work Around Analog Input Noise
Significant noise was found on the Metro analog inputs. This showed up as LED flicker at lower brightness levels. To compensate for this, the pot readings (analog inputs) were configured with a resolution of 12 bits, then shifted right by 4 bits after reading. This effectively produced an 8 bit resolution, which is fine for the sand table application, but eliminated most of the noise/flicker issue.
The LED PWM output frequency was also increased to 100,000 to help reduce flicker.
The code below reflects these changes.
Account for Physical Differences

The sand table uses a 3D printed bumper to mount the processor to the base of the table. The Metro RP2350 includes an SD card holder and a power switch among other hardware that the UNO doesn't have. These are physically located at the edges of the card. The 3D printed bumper used to mount the card to the sand table was modified in order to allow the use of the SD card and power switch if desired. The "Metro_2350_Bumper" files reflect these changes.
The Arduino UNO uses a USB-B connector while the Metro RP2350 uses a USB-C connector. This required the face plate USB hole to be slightly lowered and re-sized to accommodate a USB-C connector. The face plate USB hole was lowered by 2 mm and its height reduced to 10 mm. The "Faceplate 2350" files reflect the changes.
Complete RP2350 Firmware
The completed .ino file uses only 88,980 bytes of the available 16,769,024 bytes of program space. It also uses only 12,748 bytes of the available 524,288 bytes of RAM. This leaves plenty of space for adding additional shapes and features. The speed is truly impressive. I am extremely happy with the results.
Possible future improvements include:
- Use the RP2350 PIO state machines to generate the pulses to the servo drivers. This will allow for enforcement of strict timing limits when generating in/out compensation pulses.
- Add more shapes. Possibilities include fractals, tessellations, and space fillers similar to those in Sandify.
- Add a Raspberry Pi camera interface that will automatically take pictures (maybe time-lapse too) after each shape completes.
- Possibly use the Metro's RGB NeoPixel to show current status.
- Possibly make use of the Metro's micro SD card interface to save user defined shapes.