A while back during my spate of RK3588
reviews, I came across Luckfox and found their development boards intriguing, since they were marketed as being able to run Linux in the same postage stamp form factor as an MCU.
That seemed pretty amaazing, so I reached out to them and got a sample.
Disclaimer: Luckfox provided me with a Luckfox Pico Mini board and a CSI camera (for which I thank them), and this article follows my review policy.
What followed has been a surprisingly pleasant experience, and I’ve been tinkering with the Luckfox Pico Mini on and off for a few weeks now–to be fair, mostly off given several work shenanigans, but that’s beside the point right now…
Hardware
I got the B model, which has a soldered on 128MB SPI NAND flash:
The specs for the version I got are:
- Single Cortex A7 1.2GHz core
- 0.5 TOPS NPU (with int4, int8, int16 support, so it can run OpenCV and simple neural networks, but nothing too fancy)
- 64MB RAM
- USB-C 2.0 host/device connector
- CSI connector (I also got a 3mp camera)
- MicroSD connector
- 17 GPIO pins
An intriguing part of the RV1103 chip that I haven’t explored is that it includes a RISC-V MCU and an integrated signal processor for speeding up camera operations–I intend to play with that at some point, but simply haven’t had the time yet.
If you’ve been following along in the ESP32 and RP2040 space, you’re certainly wondering about price right now, since the overall specs compare pretty favourably with similarly sized ESP32 and RP2040 boards, especially if you consider its secret sauce–i.e., running Linux natively.
Right now it’s selling for $8.99, which is around twice the price of similarly-sized boards, but which still looks like a bargain if you need the extra processing power and the ability to run Linux.
On the flip side, this particular model doesn’t have wireless connectivity, which is a bit of a bummer. But I can see how that would complicate the design and increase the price further.
I’d say we’re about a year away from seeing a Cortex A7 with wireless connectivity at this price point, but don’t hold me to that.
Going In Headfirst
Since the B version of the board has on-board storage and comes pre-flashed with a working 5.10 kernel, I decided to see how far I could go with the board as it comes out of the box.
Connecting
I plugged the Luckfox Pico into one of my Linux machines, and it showed up immediately as a RNDIS
device:
❯ dmesg
[57790.089476] usb 3-2: new high-speed USB device number 3 using xhci_hcd
[57790.220263] usb 3-2: New USB device found, idVendor=2207, idProduct=0019, bcdDevice= 3.10
[57790.220280] usb 3-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[57790.220287] usb 3-2: Product: rk3xxx
[57790.220292] usb 3-2: Manufacturer: rockchip
[57790.220298] usb 3-2: SerialNumber: c800c8d03cffebb8
[57790.333460] usbcore: registered new interface driver cdc_ether
[57790.341712] rndis_host 3-2:1.0 usb0: register 'rndis_host' at usb-0000:04:00.4-2, RNDIS device, ae:5c:b5:d3:ee:1e
[57790.341790] usbcore: registered new interface driver rndis_host
[57790.380103] rndis_host 3-2:1.0 enp4s0f4u2: renamed from usb0
This means there’s none of the usual serial port/FTDI
-style shenanigans (well, when you’re not flashing it, of course). Instead, you have to do some TCP/IP shenanigans.
My host machine auto-generated the enp4s0f4u2
interface, but if you need to flash a number of these and keep inventory, here’s a quick snippet to figure out the right interface from the MAC address without going into dmesg
:
❯ ip addr list | grep -B 1 -A 2 ae:5c:b5:d3:ee:1e
5: enp4s0f4u2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
link/ether ae:5c:b5:d3:ee:1e brd ff:ff:ff:ff:ff:ff
inet6 fe80::f8c1:e2a2:dd8a:c6c1/64 scope link noprefixroute
valid_lft forever preferred_lft forever
As you can see, there’s no assigned IPv4 (only the system-assigned link-local IPv6 on the host side). And the Luckfox Pico does not support RFC 3927, so you don’t get an automatic link-local IPv4 address.
The docs specify the default IP as 172.32.0.93
, so let’s set an address at our end and test the Ethernet over USB link:
❯ sudo ip addr add 172.32.0.1/16 dev enp4s0f4u2
❯ ping 172.32.0.93
PING 172.32.0.93 (172.32.0.93) 56(84) bytes of data.
64 bytes from 172.32.0.93: icmp_seq=1 ttl=64 time=0.754 ms
64 bytes from 172.32.0.93: icmp_seq=2 ttl=64 time=0.580 ms
^C
--- 172.32.0.93 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1008ms
rtt min/avg/max/mdev = 0.580/0.667/0.754/0.087 ms
It feels a bit weird to log in to what is almost an MCU via ssh
, but it works fine:
❯ ssh [email protected]
The authenticity of host '172.32.0.93 (172.32.0.93)' can't be established.
ED25519 key fingerprint is SHA256:VnUEEITBSJ7Y7h+wmk8bTqw47QwdLWudvM0tRgke4Kc.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '172.32.0.93' (ED25519) to the list of known hosts.
[email protected]'s password:
# uname -a
Linux luckfox 5.10.110 #1 Tue Nov 14 18:06:04 CST
2023 armv7l GNU/Linux
# cat /etc/issue
Welcome to luckfox pico
Running Processes
The runtime is, as you’d expect, pretty sparse, and on the base image there aren’t a lot of things besides kernel workers:
# ps -ef
PID USER COMMAND
1 root init
2 root [kthreadd]
4 root [kworker/0:0H]
6 root [mm_percpu_wq]
7 root [ksoftirqd/0]
8 root [kdevtmpfs]
9 root [oom_reaper]
10 root [writeback]
11 root [kcompactd0]
12 root [ksmd]
28 root [kblockd]
29 root [kconsole]
30 root [devfreq_wq]
31 root [watchdogd]
32 root [kworker/0:1-eve]
33 root [rpciod]
34 root [kworker/u3:0]
35 root [xprtiod]
36 root [kswapd0]
37 root [nfsiod]
38 root [irq/60-rockchip]
39 root [irq/61-rockchip]
40 root [kworker/u2:1-ev]
41 root [hwrng]
42 root [spi0]
51 root [kworker/0:3-eve]
52 root [irq/22-rockchip]
53 root [kworker/u2:2-ev]
54 root [ubi_bgt0d]
57 root [ubifs_bgt0_0]
73 root /sbin/syslogd -n
77 root /sbin/klogd -n
88 root /sbin/udevd -d
141 root [ubi_bgt4d]
146 root [ubifs_bgt4_0]
155 root [ubi_bgt5d]
160 root [ubifs_bgt5_0]
182 root [irq/50-ffa70000]
200 root [irq/41-rga2]
202 root [queue_work0]
203 root [queue_work1]
204 root [irq/48-ffa50000]
205 root [irq/49-ffa60000]
206 root [vcodec_thread_0]
208 root [rknpu_power_off]
215 root sshd: /usr/sbin/sshd [listener] 0 of 10-100 startups
246 root [vlog]
247 root [vsys]
248 root [vrga]
250 root [vpss]
251 root [vrgn]
253 root [vmcu]
326 root /usr/bin/adbd
334 root [irq/62-dwc3]
370 root smbd -D
373 root {smbd-notifyd} smbd -D
374 root {smbd-cleanupd} smbd -D
379 root nmbd -D
384 root /sbin/getty -L console 0 vt100
397 root sshd: root@pts/0
399 root -sh
409 root ps -ef
Wait, is that… Samba? Seriously?
# cat /etc/samba/smb.conf
[global]
workgroup = WORKGROUP
server string = luckfox samba server
security = user
passdb backend = smbpasswd
smb passwd file = /etc/samba/smbpasswd
[public]
comment = public share
path = /
read only = no
user = root
create mask = 0755
directory mask = 0755#
Yep. This when you start realizing the Luckfox Pico is an embedded system you can mount over SMB–crazy times.
Running My First Binary
At the time I got this I didn’t have any cross-compilation environments set up, but I did have Go, and I have a fairly long history of building impromptu binaries for weird ARM architectures using, it.
So I thought I’d add to that list:
❯ go mod init luckfox/hello
...a few moments later...
❯ cat hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, Luckfox!")
}
❯ env GOOS=linux GOARCH=arm GOARM=7 go build -o hello.armv7
❯ file hello.armv7
hello.armv7: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, Go BuildID=fMvkSlf1kYWYnV88GhTQ/J1Y1phDV859SF5L4bFxZ/P3d-713IBZ6mHIbEpIub/hZRSRS5aBTm4NBaU8f_g, with debug_info, not stripped
❯ scp hello.armv7 [email protected]:~/
...a few moments later...
# ./hello.armv7
Hello Luckfox!
The resulting binary is 1823294 bytes in size (which is reasonable given that it is fully static), and the other (relatively simplistic) stuff I’ve built since hasn’t yet crossed the 2MB mark. And keep in mind this isn’t TinyGo, it’s the full-fat Go runtime.
A few days later, while rummaging around, I realised that the Luckfox Pico ships with Python 3.11. Not MicroPython, mind you, but, again, the whole thing (or at least a good chunk of it that it hasn’t mattered yet).
I’ve since built some C binaries, and eventually realized I can actually run gdb
locally on the device–which is the kind of frictionless, iterative development approach that I love.
This opens up the opportunity for taking existing Python or C code that would be a pain to port to Arduino or a simpler MCU and just cross-compile and copy across and test it within an hour or so, which is pretty great.
GPIO Control
The Luckfox Pico Mini has the usual set of GPIO pins, overlapping with 4 UARTs, an SPI interface and an I2C one:
The nice thing about having a Linux kernel is that GPIOs are mapped as device folders under /sys/class/gpio
, so literally anything can use them without special libraries.
However, there are shipping libraries to make things easier, and the documentation covers that in detail–there’s a C example in the Luckfox Wiki that demonstrates GPIO access, and matching ones for I2C
, SPI
, etc.
Sadly, there’s no I2S
support on this model–so my initial idea of getting a PCM5102A
DAC to work didn’t pan out.
Building and Flashing New Firmware
I didn’t yet have time to build anything truly complex (I was planning to incorporate the Luckfox Pico into a little audio project, and now I am considering other uses), but since that’s usually something I very much want to do, I investigated how to go about it.
In short, there’s a set of comprehensive docs regarding getting an SDK going on Ubuntu 22.04, and a public repository (not on Github, though) with reference tarballs and patches for various libraries. Given the propensity of Rockchip SDKs to come with assorted binaries, I’d be prepared for a little work, but couldn’t see anything too out of the ordinary.
As to pre-built images, as usual with Chinese companies they’re available via Google Drive download (I’m not a fan of this, but it’s a common practice), and the flashing process is pretty straightforward.
A Nice Surprise
On that note, to my surprise, Luckfox actually has Linux and MacOS instructions for flashing, so the tiny Windows box that I got expressey to deal with Rockchip tools for the RK3588
boards sat this one out.
Connectivity
My original idea was to leverage the Cortex A7 for building a tiny synthesiser with a bit more oomph than the Pi Pico ones that have been making the rounds–but that hasn’t come to pass, and the lack of wireless connectivity prevents me from using it in my home automation projects.
But with four UARTs, you can hook up more MCUs and use the Luckfox Pico’s SD card as a sensor data logger as well–I can see this being used in drones and other applications where you need a bit more processing power than a typical MCU.
What I eventually did was to force route the Luckfox Pico through my Linux machine and play around with little TCP servers on it. This is a bit of a kludge, but it works even if it’s not an elegant solution.
Conclusion
So who is this for? Well, for starters, I’d say the Luckfox Pico is for people who already have a code base that absolutely requires full POSIX compliance or is already running on ARMv7 chips and don’t want to mess with the complexity of porting it to a “plain” MCU.
And, in a nutshell, that’s why I have been tinkering with it–I can get something working, build an ARM binary, and copy it across in seconds–or just let the Luckfox Pico route through my machine and fetch the binary from there, without using any board-specific tools whatsoever. That’s something you can’t do with any other development board this size.
I haven’t yet had the time to play with RKNN inference and the baked in YOLO support–I need a quieter time, and will update this post when I get aroudn to it–but I can see the Luckfox Pico being used as a smart, field-programmable part inspector in manufacturing, or a tiny, self-contained auto-tracking camera (like the OBSbot Tiny I’ve been using).
But given that this is an A7 with a pretty decent FPU, I’d say signal processing appliations would be a great candidate (I’m still stuck on the idea of using it as the core of a tiny synthesizer, even without I2S).