Multi-client UART-based GPIO control system for Raspberry Pi Pico, with persistent flash state and automatic UART connection detection.
This project enables a central Pico-based server to detect and communicate with multiple Pico-based clients over UART. Clients expose GPIOs that can be remotely controlled (ON/OFF/TOGGLE) by the server via UART messages. The server saves the state of all connected devices in its internal Flash memory, allowing full device state restoration after power cycles.
Video DEMO here: https://youtu.be/Ub3jZuVrXoA
- Server auto-detects all valid TX/RX pin combinations via UART handshake.
- Each client listens for GPIO control commands and applies them.
- Server sends state updates and stores them in persistent Flash.
-
Automatic UART handshake
-
Scans all Raspberry Pi Pico's UART0 & UART1 pin pairs (up to 5 clients)
-
Remote GPIO control
-
Server tracks all client states, supports:
- ON / OFF Device
- TOGGLE Device
- SAVE active config
- BUILD preset config
- LOAD preset into active config
- 5 PRESET configurations per client
-
Persistent flash memory with CRC32 protection
-
Menu-based USB CLI interface for live control
-
Power Saving For Clients
-
Server powers on and scans UART pin pairs.
-
Client powers on and broadcasts handshake.
-
On success:
- Server saves connection
- Server can control client GPIOs
-
States saved to Flash with CRC32.
-
On reboot, handshake runs again and states are restored automatically.
Client → Server : "Requesting Connection-[TX,RX]"
Server → Client : "[TX,RX]"
Client → Server : "[Connection Accepted]"
Server → Client : "[gpio_number,value]"
Example: "[2,1]" → turn GPIO 2 ON
OR
Server → Client : "[FLAG,FLAG]"
Example: "[WAKE_UP_FLAG_NUMBER,WAKE_UP_FLAG_NUMBER]" → confirm dormant wakeup
- Any Raspberry Pi Pico boards (1 server, 1-5 clients)
- Pico SDK
- CMake
- Arm GCC toolchain
- Make/Ninja or
nmake(depending on platform)
Follow these steps once to set up your environment for all Pico C/C++ projects.
- Download from: Arm GNU Toolchain Downloads
- Install to:
C:\arm-gcc(for example)
- Add to PATH during installation or after
- https://cmake.org/download/
- Add to PATH during installation or after
Best option: use NMake, included with Visual Studio Developer Command Prompt
You don’t need to install anything else if you build from this environment.
Optional alternative (MinGW):
- Install MSYS2: https://www.msys2.org
- Then in MSYS2 terminal:
pacman -S mingw-w64-x86_64-make
git clone -b master https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule update --init5. SET ENVIRONMENT VARIABLE:
set PICO_SDK_PATH=path\to\pico-sdkUbuntu/Debian:
sudo apt update
sudo apt install gcc-arm-none-eabimacOS:
brew tap ArmMbed/homebrew-formulae
brew install arm-none-eabi-gccsudo apt install cmake ninja-build
# or
brew install cmake ninjagit clone -b master https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule update --initSet environment variable:
export PICO_SDK_PATH=/full/path/to/pico-sdkThis project supports multiple build systems using CMake. The -DPICO_BOARD=pico flag is used to select the board version. The CMakeLists.txt files in this project are configured so that the build outputs flash files for both the server and the client separately, each in its own folder.
mkdir build
cd build
# Create separate folders for each board type
mkdir build_pico2_w
cd build_pico2_w
cmake -G "NMake Makefiles" ../.. -DPICO_BOARD=pico2_w
nmake
cd..
mkdir build_pico
cd build_pico
cmake -G "NMake Makefiles" ../.. -DPICO_BOARD=pico
nmakeEach board type now has its own build folder, containing both the client and server firmware ready to be flashed.
mkdir build
cd build
mkdir build_pico
cd build_pico
cmake -G "Ninja" ../.. -DPICO_BOARD=pico
ninjamkdir build
cd build
mkdir build_pico
cd build_pico
cmake -G "Unix Makefiles" ../.. -DPICO_BOARD=pico
makemkdir build
cd build
mkdir build_pico
cd build_pico
cmake -G "MinGW Makefiles" ../.. -DPICO_BOARD=pico
mingw32-make- Hold
BOOTSELwhile plugging in the Pico via USB. - It will mount as a drive.
- Drag the
.uf2file onto it.
picotool load client.uf2/server.uf2Requires:
picotoolinstalled- Pico connected normally (not in BOOTSEL)
Edit types.c, config.h and menu.h to customize settings like:
- UART pin pairs and instances enabled for handshake
- UART baudrate and messages
- Client & Server handshake timeout
- Max GPIOs per client
- Enable / Disable periodic onboard led blink
- Onboard led blink periods
- Flash memory layout
- Console buffer size limit at reconnection
- etc...
- Uses
__not_in_flash_funcfor safe Flash writes - Handshake timeouts are adjustable
Dragos Hategan
This project is licensed under the BSD-3-Clause License.
This open-source project was developed by Dragos Hategan as part of a professional embedded IoT portfolio.
You are free to use, modify, and redistribute this project, including in commercial or private projects, as long as attribution is maintained. If you build upon this codebase or publish derived works, please consider crediting the original author by name or linking back to this repository.
For freelancing, consulting, or collaborations in embedded systems, feel free to reach out via GitHub or LinkedIn.