Just like the good ol' days a tiny Linux install feels refreshingly efficient. @speccy88 dropped a note in the Adafruit forums that they got a Linux install running on the Adafruit Fruit Jam. This is based on Mr-Bossman's Pi Pico2 Linux port.
Within a few days of release I've been able to use many of the peripherals. Which is crazy because the Fruit Jam is a fully loaded board.
- WiFi
- NeoPixels
- microSD card (FAT32) / MBR partitioning
- I2C stemma QT
- Audio ISC DAC (speaker / headphones)
- ADC
- DVI (in progress)
- USB Host (in progress)
This guide will cover use of each of these components in both terminal mode and through a web browser.
The installation process is drag-n-drop of a UF2 image. Utilizing a microSD getting online only require a two line config file.
Running Linux on a microcontroller has several advantages over a full blown Raspberry Pi SBC (Single Board Computer).
- Nearly instant on
- Price
- The RP2350 is as little as $5 for basic boards
- Power Efficiency
- The Fruit Jam idles at 0.3 W
- The Pi Zero idles at 0.8 W
- The Pi 5 idles at 3 W
- Stable Filesystem
- Clean boots, even when power is cut
- No corruption issues
- read-only flash rootfs
- Remote Web Browser access
- Web CGI
- Remote CLI access
- telnet
The installation process is nearly identical to how you would install CircuitPython on the Fruit Jam.
- Make sure microSD card is inseted in the Fruit Jam
- The OS will look for for a microSD and produce errors if not present
- FAT32 format (not exFAT)
- Use MBR not GUID partitioning
- Download Image from the Release Page
- See "Assets"
- Adafruit Fruit Jam RP2350 Linux UF2
- Put the Fruit Jam into Bootloader mode
- Press-and-hold the UF2 button and tap the RESET button
- Then release the UF2 button
- Drag and drop the UF2 image to the RP2350 drive
- Let it copy for a few minutes
Interacting with the Linux install is also quite similar to how you would interact with the REPL console on CircuitPython.
When the Fruit Jam boots with Linux installed, I can see a new USB serial port appear from my Ubuntu Linux Desktop which the Fruit Jam is plugged into.
sklarm@bravo:~$ ls -rlt /dev/ttyACM* crw-rw---- 1 root dialout 166, 0 Jun 13 14:28 /dev/ttyACM0
From a macOS or Linux system, the screen utility works well to connect. On Windows you might use Putty or TeraTerm. After sucessfully running the screen command I am logged into the Fruit Jam as root on its console.
sklarm@bravo:~$ screen /dev/ttyACM0 / #
Now we can run a few commands and poke around this installation.
The kernel is Linux 6.15, RISC-V 32-bit. The RP2350 has four cores two ARM Cortex-M33 and two RISC-V Hazard3. CircuitPython runs on the ARM cores; Linux runs on the RISC-V cores. The really cool part about this is that the RISC-V Hazard3 cores are open hardware design by Luke Wren of Raspberry Pi.
~ # uname -a Linux (none) 6.15.0 #1 Wed Jun 10 06:05:56 UTC 2026 riscv32 GNU/Linux ~ # cat /proc/cpuinfo processor : 0 hart : 0 isa : rv32ima_zaamo_zalrsc mmu : none mvendorid : 0x493 marchid : 0x1b mimpid : 0x86fc4e3f hart isa : rv32ima_zaamo_zalrsc
The device name and boot arguments can also be viewed.
~ # cat /proc/device-tree/model Adafruit Fruit Jam RP2350B Hazard3 ~ # cat /proc/cmdline earlycon=pl011,mmio32,0x40078000 console=ttyAMA0 console=ttyGS0 root=mtd:cramfs rootfstype=cramfs ro
What I would give to have a clear command to clean up the terminal. Later, I found that Ctrl-L works.
We can look at this super slim install in terms of memory and disk usage. The entire system (kernel plus user space memory) fits into the 8 MB of PSRAM and still has a third of it free.
The root file system is around 1 MB.
~ # cat /proc/meminfo MemTotal: 6076 kB MemFree: 3328 kB MemAvailable: 2992 kB [...] ~ # df -h Filesystem Size Used Available Use% Mounted on mtd:cramfs 916.0K 916.0K 0 100% /
The Fruit Jam has a WiFi co-processor (ESP32-C6) that runs NINA-FW. The firmware it ships with is likely out of date. It is recommended that you update to the current release (v3.3.0 at the time of this writing) makes for smoother remote access. Adafruit has a guide that walks you through the NINA-FW upgrade on the Fruit Jam.
Use a FAT32 formatted microSD card and create a fruitjam/wifi.conf on it. The file will contain only two lines your access point SSID and the password need to access it. Do not use double quotes around the name. This will bring up your WiFi automatically on the time your reset the power on the Fruit Jam.
SSID=my-access-point PASSWORD=my-password
A log file is written /tmp/airlift-start.log that has many attributes of the WiFi hardware the most valuable being:
- IP address
- Firmware version
- MAC address
There is a more convenient command that can be used, airliftctl, though it cannot be utilized once the network interface is up.
cat /tmp/airlift-start.log
If you do not have a microSD card with the config file, this option can be used to manually bring up the interface on each boot. The airliftctl tool can be used to see the connect to your WiFi access point. It is a simple one line command using airliftctl.
# airliftctl join <your_wifi> <wifi_passwd>
This is a particularly useful command that you can also pull web content over HTTP using the tcp-get argument.
airliftctl tcp-get example.com
exFAT support is not enabled in the current kernel, but you can mount a FAT32 formatted SD card. I used a 32GB card. When the Fruit Jam boots up the car is available as /mnt/sd. You can also unmount and remount while running.
/ # mount /mnt/sd
[ 3448.822309] FAT-fs (mmcblk0p1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
/ #
/ # ls /mnt/sd
fruitjam test.txt cpu_only_bench.txt fsio_bench.txt
/ # umount /mnt/sd
/ # mount /mnt/sd
[ 3461.850574] FAT-fs (mmcblk0p1): Volume was not properly unmou/ # nted. Some data may be corrupt. Please run fsck.
The fruitjamctl command is going to be our Swiss Army knife here to control the Fruit Jam's LEDs, buttons and power related states. Starting with the command-line version. There is also a web interface to take advantage of these features which is CGI based.
Using the status argument, we can see the LED, USB and button states.
fruitjamctl status
Once the Fruit Jam IP address is obtained, you can type it into your web browser and access a control panel to manipulate various components such as NeoPixels, audio, DVI and sensors. This interface also provide status, scripting, boot control and ADC.
http://<ip_address>/playground
The Status bar shows the state of various interfaces now available.
Neopixels can be set with pre-selected buttons rainbow, warm and clear. Alternatively, each button has a built in color pallet option.
You can play ring tone formats and WAV audio files. The audio will be sent to both the headphone jack and speaker.
Display text through the DVI port.
Disable / Enable power to the USB host ports.
The Berry section has a dropdown menu with many simple scripts to do basic interfacing and sanity checks. Most the berry scripts are representative of the functions already available in other sections such neopixel control or reading the file listing on the mounted SD card.
The last few items in the Web CGI interface are quite useful.
- I2C scanner
- ADC reader for 6 GPIOs or internal temperature (raw / mV)
- Buttons (B1, B2, B3) testing
- BOOTSEL puts board into bootloader mode (very handy)
Enough with the boring sysadmin stuff. It is time to blink some lights.
@speccy88 who made the build hooked us up with an example piece of code to drive the Fruit Jam's five Neopixels. To run it, all you need to do is:
~ # cd /root /root # berry neopixels.be
Berry? Yes, it is a super tiny Python-like scripting language.
Taking a look at @speccy88's neopixel.be script we can see it open /dev/neopixels. Then it clears the LEDs and proceeds to send five set commands, one for each NeoPixel. The first argument is the LED position (0-4) followed by R, G, B values to set it to. No libraries are necessary to update the LEDs.
/root # cat neopixels.be
var dev = "/dev/neopixels"
def send(f, cmd)
print("> " + cmd)
f.write(cmd + "\n")
f.flush()
end
def main()
var f = nil
try
f = open(dev, "r+")
except .. as e, v
print("neopixels.be: cannot open " + dev)
print("neopixels.be: expected commands: clear, set INDEX R G B, write")
print("neopixels.be: add the NeoPixel device/control driver, then run this again.")
return
end
send(f, "clear")
send(f, "set 0 24 0 0")
send(f, "set 1 24 12 0")
send(f, "set 2 0 24 0")
send(f, "set 3 0 0 24")
send(f, "set 4 12 0 24")
send(f, "write")
f.close()
print("neopixels.be: five-pixel test pattern sent")
end
main()
The Fruit Jam has three buttons which can be used as inputs. The are two command-line tools that behave the same way to read button state.
fruitjamctl buttons fruitjam-buttons status
Monitor for button presses by using the daemon option. This will return a timestamp (second since boot) as well as which button was pressed and its associated GPIO.
There is a /mnt/sd/fruitjam/buttons.log file with the history of button presses.
fruitjam-buttons daemon
The fruitjam-i2c command-line utility produces output similar to i2cdetect on frequently used on Pi OS. I hooked up a SHT41 temperature / humidity sensor to the STEMMA QT port. It shows up as address 0x44. There is also a built-in DAC on the Fruit Jam the TLV320 which has address 0x18.
fruitjam-i2c scan
Going to the Fruit Jam IP address using a web browser, you will find an I2C scanner. In my case this shows the same two addresses that we obtained from the command-line option above.
Since we have a temperature / humidity sensor connected. Let's use a quick berry script to pull the data. Login through the serial console (not telnet) and you can paste in the code block below which will write out the Berry script to a file called /mnt/sd/sht41.be. Then you can run the script from the console or via telnet anytime to check the sensors temperature and humidity.
cat <<EOF > /mnt/sd/sht41.be
import i2c
i2c.write(0x44, bytes("FD")) # 0xFD = measure, high precision
i2c.delay(10) # wait ~10 ms for the measurement
var r = i2c.read(0x44, 6) # T_msb,T_lsb,crc, RH_msb,RH_lsb,crc
var c = -45 + 175.0 * ((r[0] << 8) | r[1]) / 65535.0
var f = c * 9.0 / 5.0 + 32.0
var rh = -6 + 125.0 * ((r[3] << 8) | r[4]) / 65535.0
print("SHT41:", c, "C /", f, "F RH", rh, "%")
EOF
berry-run /mnt/sd/sht41.be
Nice. As you can see with 8 lines of code we are reading, legit data from an environmental sensor.
The Fruit Jam has an 8-channel / 12-bit ADC. Five of those channels are accessible through the top female 2x16 header and one more A0 is available through the JST-PH connector. There is also an internal temperature ADC.
Hook up a trimpot using:
- trimpot (left) -> 3v (red wire)
- trimpot (center) -> A1 (yellow wire)
- trimpot (right) -> GND (black wire)
It is easy to read samples from the Fruit Jam ADC1 with the following commands. Averaging samples is as easy as passing a --samples argument.
The data returned is both 12-bit raw and millivolts. I turned the pot in various positions while issuing the later reads to show the full range.
raw: 0 - 4095
millivolts: 0 - 3300
fruitjam-adc 1 fruitjam-adc read 1 --samples 16 # avg 16 samples fruitjam-adc 1 # show avg results
The Web CGI for the Fruit Jam accessed by pointing your web browser at the Fruit Jam's IP address can also read the ADC. The same output as the command-line is presented
There is a dropdown menu to select the different ADC0 - ADC5 options as well as the board's internal temperature sensor.
Audio will play through both the speaker and headphones. when using the RTTTL tools. These are Ring Tone Text Transfer Language. A short hand way to write out notes, duration, octave and tempo.
The max for the Web version is about 13 notes.
The command-line utility allows for longer playback due to a 512 KB buffer. The Tetris song has about 160 notes.
# web version Super Mario Song mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6
# Full Tetris Song fruitjam-rtttl --loud tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6
There is also a fruitjam-wavplay CLI and Web interface. I'm not going to cover it right now as the PCM / I2S driver is not fully implemented. The utility can approximate playback, but voice and audio quality is mostly lost. Feature request opened for real PCM playback.
Grab the source plus its Buildroot submodule
git clone https://github.com/speccy88/fruitjam-linux cd fruitjam-linux git submodule update --init
Select the Fruit Jam board configuration
make -C buildroot BR2_EXTERNAL=$PWD adafruit_fruit_jam_rp2350_defconfig
Big Build including cross-toolchain, kernel, and root filesystem (~1 hour first time)
make -C buildroot
Convert to UF2 - done automatically if picotool was installed before the build
picotool uf2 convert buildroot/output/images/flash-image.bin \
buildroot/output/images/flash-image.uf2 --family rp2350-riscv
Flash the Fruit Jam
sudo picotool load -fu buildroot/output/images/flash-image.uf2
This is only a few days old. @speccy88 ended the announcement with a simple request: "let me know if you try it!". If you have a some feedback, drop a line in the forum thread, or on the GitHub repo.
This page (Linux on Fruit Jam) was last updated on June 21, 2026.