Silabs Silicon Labs Configurator (SLC) command line

Wow, this is clearly not used much! but, it does seem to work so far? I’ve only taken a few steps, so this is note taking for me and the rest of the internet!


It talks about pip install -r requirements.txt, but even if you run it in a venv, somewhere it … escapes? and relies on system libraries, or something. You’ll get failures about unable to find jinja2 anyway. (I already had python3-jinja2 installed system wide, but it failed anyway. I had to remake the virtual env with

$ python3 -m venv --system-site-packages .env33
$ . .env33/bin/activate
(.env33) $ pip install -r requirements.txt

Generate a project

Ok, this is described (poorly, IMO) in their UG520: Software Project Generation and Configuration with SLC-CLI, (also linked from the bottom of the readme of the Gecko SDK) which at least is trying to document this process, so props for getting there I guess.

slc generate ~/SimplicityStudio/SDKs/gecko_sdk/app/bluetooth/example/soc_thermometer/soc_thermometer_freertos.slcp -d ~/src/slc-test1-soc-thermo -np --with brd4184a

Ok, that wasn’t so bad. You can use parts instead of brd ids, but that went ok. You’ll need to use “slc configuration …” to set your sdk path separately. I got a failure about “untrusted sdk” first, which was not described in UG520, but it at least gave me the command line to “trust” the sdk. I don’t know what the goals here are, I presume it’s important for secure boot stuff one day?

You can now simply “make” in the output directory. Not bad. Lots of files, lots of files but hey, command line ready to go…

Adding bluetooth configuration

Ok, but what about the UI for GATT config and things? Components I’m not covering (yet), I _think_ you just edit your .slcp in the output directory?) but I needed to know how to generate the bluetooth stuff again.

Bad news, you’re editing xml. Good news, it’s not terribly sucky, as long as you have a decent grasp of how bluetooth works. There’s online “help” for the syntax inside simplicity studio, if you click the “Manual” button

Click “View manual” to get the syntax for the XML

You can even, while you’re playing, do it in simplicity studio, and then rip it out again. Save in the UI, then right click the “gatt_configuration.btconf” and choose edit in text editor and view it as the plain old XML it is underneath. (What’s a namespace? What’s a QName? aintnobodygottime.gif)

Ok, so you edited your project_path/config/btconf/gatt_configuration.btconf and want to regenerate all the things that need regenerating, filling that sweet “autogen” folder right? slc has a temptingly named “btConfig” that sounds like it might do the right thing. So you run it and give it arguments until it’s happy right?


$ slc btConfig generate -contentFolder $(realpath config/btconf) -generationOutput $(realpath autogen)

But how did we get there? Really?

$ slc btConfig generate -contentFolder config/btconfg -generationOutput autogen
Error: No 'gatt' node found in xml!
Error running command. Exit code: 1

Hrm. what? Maybe it wants the full path to the file, even though it says folder?

$ slc btConfig generate -contentFolder config/btconf/gatt_configuration.btconf -generationOutput autogen
Traceback (most recent call last):
  File "", line 167, in <module>
    od = xml_to_dict(args.inputs)
  File "", line 124, in xml_to_dict
    od[x] = GattXmlParser().to_string(x)
  File "/home/karlp/SimplicityStudio/SDKs/gecko_sdk/protocol/bluetooth/bin/gatt/", line 33, in to_string
    tree = ET.parse(xml)
  File "/home/karlp/tools/Silabs_slc_cli/bin/slc-cli/developer/adapter_packs/python/lib/python3.6/xml/etree/", line 1196, in parse
    tree.parse(source, parser)
  File "/home/karlp/tools/Silabs_slc_cli/bin/slc-cli/developer/adapter_packs/python/lib/python3.6/xml/etree/", line 586, in parse
    source = open(source, "rb")
FileNotFoundError: [Errno 2] No such file or directory: 'config/btconf/gatt_configuration.btconf'
Error running command. Exit code: 1

Well, progress, What? that smells like relative path brain damage….

$ slc btConfig generate -contentFolder $(realpath config/btconf/gatt_configuration.btconf) -generationOutput $(realpath autogen)

That… looks ok then? If you were clever, and had already committed the default state to revision control, you could check what it had done. And you’d see that you just lost the SiLabs OTA and health thermometer characteristics that were in the demo originally. If you look in the gatt_configuration.btconf, you also see that they’re not mentioned there! they’re just extra files dropped in the config/btconf directory. Ahhhh, so we don’t want to use the full path to the file, but we need to use our relative path workarounds we learnt later!

And that builds at least. Command line flashing/debugging/etc is for another day at least…. (We’re going to use SSv5 a bit more, we need to _work_ but at least we need to know how some of this might be possible

Simplicity Studio 5 on Fedora 34

UPDATE: You can do the steps below, and it will…. mostly work, but you’ll still run into broken things like, unable to enter text on the project config wizards or the bluetoot gatt configuration. The only _viable_ solution I’ve found really, is just to log out, and log in again choosing “Gnome on Xorg” and just not making wayland part of your daily headaches.

You still need the export GDK_BACKEND=x11 trick, and you still need the ./studio -Djxbrowser.chromium.dir=/home/$USER/.jxbrowser trick.

You also need to make sure you have git-lfs installed, or you will get fun errors like

/home/karlp/Downloads/SimplicityStudio_v5/developer/toolchains/gnu_arm/10.2_2020q4/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld:/home/karlp/SimplicityStudio/SDKs/gecko_sdk//protocol/bluetooth/lib/EFR32BG22/GCC/binapploader.o:1: syntax error

$ cat /home/karlp/SimplicityStudio/SDKs/gecko_sdk//protocol/bluetooth/lib/EFR32BG22/GCC/binapploader.o
oid sha256:59b50942a2f0085fe2c87d0d038b13b8af279c742f0d3013b1d1ad6929070a3f
size 66088

Oops, that doesn’t look like a bootloader binary! This is actually mentioned on as issue 76584, but you wouldn’t find that til later. The tips are to reinstall, but you can also

cd ~/SimplicityStudio/SDKs/gecko_sdk && git lfs fetch && git lfs pull && echo "whew, that took aaaages"

You can _probably_ do tricks with git lfs to just pull the single files you need, and avoid pulling the 800meg or something at 1meg/sec that it took, but this got me a building and functional simplicity studio! wheee…..

Restoring a Kicad 6.99 PCB to Kicad 6.x

Yah, I was using the 5.99 nightlies via a “kicad-nightly” package on fedora, and after Kicad 6.0 came out, this moved (correctly) to the 6.99 branch, which brought in a whole slew of new changes to the project files, which are… not backward compatible. (Which I also understand… but…)

So, what to do? First, edit the “version” in the top of the file to something like 20211014.

Then try and open the file, see how far along you were and how much got munched….

Try and remove strokes

The following VIM regex will strip all the line settings out. :%s/(stroke.*type solid))// If you had customized lines, sorry, you can’t have that in 6.0 anyway. Try re-opening the file again…

Try also :%s/(stroke.*type default))//

Try and remove thermal angles

more vim regexping…. :%s/(thermal_bridge_angle \d*)//g

That was enough for my file. Hopefully this helps someone…

Xiaomi Miijia LYWSD03MMC with pure bluetoothctl

Because software is software, it all gets thrown out every now and again for….. reasons. BlueZ, on linux, has moved away from hciconfig and hcitool and gattttool and so on, and it’s all “bluetoothctl” and “btmgmt” See for some more on that.

Anyway, I got some of these really cheap Xiaomi BTLE temperature+humidity meters. Turns out there’s a custom firmware you can flash to them and all sorts, but really, I was just interested in some basic usage, and also, as I’m getting my toes wet in bluetooth development, working out how to just plain use them as intended! (well, mostly, I’ve no intention of using any Xiaomi cloud apps)

This application claims to use the standard interface, and it’s “documentation” of this interface is and the little bit of code at

def connect():
	#print("Interface: " + str(args.interface))
	p = btle.Peripheral(adress,iface=args.interface)	
	p.writeCharacteristic(0x0038,val,True) #enable notifications of Temperature, Humidity and Battery voltage
	return p

Well, that wasn’t very helpful. Using tools like NrfConnect, I’m expecting things like a service UUID and a characteristic UUID and things. Also, I don’t have gatttool to even try it like this :)

So, how to do it in “modern” bluetoothctl? Here’s the cheatsheet. First, find our meters…

$ bluetoothctl
# menu scan
# clear
# transport le
# back
# scan on
SetDiscoveryFilter success
Discovery started
[CHG] Controller <your hci mac> Discovering: yes
[NEW] Device <a meter mac> LYWSD03MMC
[NEW] Device <other devices> <other ids>
# scan off

Now, we need to connect to it. According to it actually does broadcast the details about once every 10 minutes, which is neat, but you need a key for it, and aintnobodygottimeforthat.gif.

# connect <mac address>
lots and lots of spam like
[NEW] Descriptor (Handle 0x9f94)
	Characteristic User Description
[NEW] Descriptor (Handle 0xa434)
	Client Characteristic Configuration
[NEW] Characteristic (Handle 0xa904)
	Vendor specific
[NEW] Descriptor (Handle 0xb1d4)
	Characteristic User Description
....more lines like this.
[CHG] Device MAC_ADDRESS UUIDs: 00000100-0065-6c62-2e74-6f696d2e696d
[CHG] Device MAC_ADDRESS UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Device MAC_ADDRESS UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Device MAC_ADDRESS UUIDs: 0000180a-0000-1000-8000-00805f9b34fb
[CHG] Device MAC_ADDRESS UUIDs: 0000180f-0000-1000-8000-00805f9b34fb
[CHG] Device MAC_ADDRESS UUIDs: 0000fe95-0000-1000-8000-00805f9b34fb
[CHG] Device MAC_ADDRESS UUIDs: 00010203-0405-0607-0809-0a0b0c0d1912
[CHG] Device MAC_ADDRESS UUIDs: ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6
[CHG] Device MAC_ADDRESS ServicesResolved: yes

The characteristic you need is ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6, under the similar service UUID. So now, we just “subscribe” to notifications from it.

# menu gatt
[LYWSD03MMC]# select-attribute ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6
[LYWSD03MMC:/service0021/char0035]# notify on
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Notifying: yes
Notify started
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Value:
  36 09 2a f6 0b                                   6.*..           
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRRESS/service0021/char0035 Value:
  35 09 2a f6 0b                                   5.*..           
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Value:
  33 09 2a f6 0b                                   3.*..           
[LYWSD03MMC:/service0021/char0035]# notify off

And…. now we can go back to and decode these readings.

>>> x = "2a 09 2a 4e 0c"
>>> struct.unpack("<HbH",binascii.unhexlify((x).replace(" ","")))
(2346, 42, 3150)

With Python, that gives us a temperature of 23.46°C, humidity of 42%, and a battery level of 3.150V.


Originally, I was blind to the subtle differences in the characteristic UUIDs, and thought I had to hand iterate them. The below text at least shows how you can do that, but it’s unnecessary.

Ok, now the nasty bit. We want to get notifications from one of the characteristics on service UUID ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6. But you can’t just “select” this, because, at least as far as I can tell, there’s no way of saying _which_ descriptor you want.

So, first you need to use scroll that big dump of descriptors (or view it again with list-attributes in the gatt menu) to work out the “locally assigned service handle” In my case it’s 0021, but I’ve got zero faith that’s a stable number. You figure it from a line like this…

[NEW] Characteristic (Handle 0xd944)
	Vendor specific

Now, which characteristic is which? Well, thankfully, Xiaomi uses the lovely Characteristic User Description in their descriptors, so you just go through them one by one, “read”ing them, until you get to the right one…

[LYWSD03MMC:]# select-attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035/desc0037 
[LYWSD03MMC:/service0021/char0035/desc0037]# read
Attempting to read /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035/desc0037
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035/desc0037 Value:
  54 65 6d 70 65 72 61 74 75 72 65 20 61 6e 64 20  Temperature and 
  48 75 6d 69 64 69 74 79 00                       Humidity.       
  54 65 6d 70 65 72 61 74 75 72 65 20 61 6e 64 20  Temperature and 
  48 75 6d 69 64 69 74 79 00                       Humidity.       

The sucky bit, is you need to manually select _each_ of the “descNNNN” underneath _each_ of the “charNNNN” under this service. (On my devices, it’s the second characteristic with dual descriptors…)

now, select the characteristic itself. (you can’t get notifications on the descriptor…)

[LYWSD03MMC:/service0021/char0035/desc0037]# select-attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035
[LYWSD03MMC:/service0021/char0035]# notify on
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Notifying: yes
Notify started
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Value:
  2b 09 2a 4e 0c                                   +.*N.           
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Value:
  2a 09 2a 4e 0c                                   *.*N.           
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Value:
  2b 09 2a 4e 0c                                   +.*N.           
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Value:
  2b 09 2a 4e 0c                                   +.*N.           
[CHG] Attribute /org/bluez/hci0/dev_MAC_ADDRESS/service0021/char0035 Value:
  29 09 2a 4e 0c                                   ).*N.           
[LYWSD03MMC:/service0021/char0035]# notify off

FreeRTOS with STM32 Cube

Cube Sucks. But, if you’ve gotta use it, at least it has demos and things right? And you can use FreeRTOS, that’s integrated, right?

Not really. At the time of writing, working with STM32WB, the out of the box FreeRTOS demos will have problems with printf. Or, well, anything that uses a raw malloc() call.

Out of the box, the cube demo code uses FreeRTOS’s heap_4.c for memory management. This is fine. It means that you select a heap size in your FreeRTOSConfig.h file, and the port provides pvMalloc and pvFree that do the right thing. But, if any code, (anywhere!) uses malloc() itself, directly, then…. where does that come from? Well, it ends up in _sbrk() and the out of box implementation in the cube demos is completely busted. They provide you with the same implementation as for the non FreeRTOS demo.

So, looking at a “classic” memory arrangement, with heap starting at the end of BSS/DATA, and stack growing downwards, you have something like this. (_estack and _end are in the default out of box linker scripts from ST) FreeRTOS’s heap_4.c declares “ucHEAP” (you can statically provide it to, if you like) based on your user provided configTOTAL_HEAP_SIZE which then becomes another (somewhat large) chunk of “DATA” space.

The _sbrk() implementation basically checks that malloc() is asking for space that’s above _end and below the current stack pointer. This is fine when you’re not using an RTOS, but! The RTOS tasks have their own stacks, allocated from the RTOS heap! This is good and right, but it means that the out of box sbrk implementation is now absolutely garbage. It will never succeed, as every single tasks’ stack pointer will be below _end.

You have options here. has written extensively, though I think they make it all sound really complicated.

Option 1 – Just don’t call malloc (or… call it early…)

This is a garbage option. You either need to make sure you never call malloc, or make sure that you only ever call malloc before you start the FreeRTOS task scheduler. This is safe, as the memory you allocate here will be in safely in the gap above DATA on the diagram, but you must make all malloc() calls before starting the task scheduler. This requires no changes to the _sbrk() system call implementation

Option 2 – Use newlib completely

This is Nadler’s approach. Switch heap_4 out for heap_3, rewrite your system calls (including _sbrk() to make all of FreeRTOS use standard newlib malloc/free natively. This isn’t a bad option, but it’s fairly messy, isn’t really something the FreeRTOS people like much, and puts a lot of reliance on how well newlib behaves. The upside here is that you only have one memory management mechanism for the entire system here. (I failed to get this working at the time of writing)

Option 3 – Fix _sbrk() only

You can also just fix _sbrk. The upside here is the change is trivial. The downside is that you now have two heaps. One that you statically give to FreeRTOS via the config options for heap_4.c (ucHEAP in the DATA section), and a new one, growing upwards on demand, classic style, above DATA, show in red on this diagram. Remember, that in the FreeRTOS context, all of the space marked in purple and red is untouched, or “wasted”. You want to size your ucHEAP that you leave “enough” for untracked raw malloc users, but still big enough for your polite, well behaved FreeRTOS consumers.

The change itself is straightforward, just stop looking at the stack pointer, and look at the linker script provided pointer to the end of ram.

--- /out-of-box/application/sysmem.c	2021-03-26 10:02:54.045633733 +0000
+++ /fixed/application/sysmem.c	2021-04-07 13:58:27.610330156 +0000
@@ -37,18 +37,19 @@
 caddr_t _sbrk(int incr)
 	extern char end asm("end");
 	static char *heap_end;
 	char *prev_heap_end;
+	extern char _estack;
 	if (heap_end == 0)
 		heap_end = &end;
 	prev_heap_end = heap_end;
-	if (heap_end + incr > stack_ptr)
+	if (heap_end + incr > &_estack)
 		errno = ENOMEM;
 		return (caddr_t) -1;
 	heap_end += incr;

And that’s it. The _estack symbol is already provided in the out of box linker scripts. Risks here are that you are only checking against end of ram. Conceivably, if you ran lots of deeply nested code with lots of malloc() calls before you start the scheduler, you might end up allocating heap space up over your running stack. You’d have to be very tight on memory usage for that to happen though, and if that’s the case, you probably want to be re-evaluating other choices.

Option 4 – Make newlib use FreeRTOS memory

There is another option, which is to make newlib internally use FreeRTOS pvMalloc/pvFree() instead of their own malloc/free. This is the logical inverse of Option 2, and has the same sorts of tradeoffs. It’s probably complicated to get right, but you end up with only one memory management system and one pool.

Autofailing autotools are autohell, as always

“Needed” a new version of something, so build from source. Configure not available, because… autohell, so run “” instead.

karlp@nanopiduo2:~/src/libgpiod$ ./ 
autoreconf: Entering directory `.'
autoreconf: not using Gettext
autoreconf: running: aclocal --force -I m4
aclocal: warning: couldn't open directory 'm4': No such file or directory
autoreconf: tracing
autoreconf: creating directory autostuff
autoreconf: not using Libtool
autoreconf: running: /usr/bin/autoconf --force error: possibly undefined macro: AC_CHECK_HEADERS
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation. error: possibly undefined macro: AC_LIBTOOL_CXX error: Unexpanded AX_ macro found. Please install GNU autoconf-archive. error: possibly undefined macro: AC_LANG_PUSH error: possibly undefined macro: AC_LANG_POP error: Unexpanded AX_ macro found. Please install GNU autoconf-archive.
autoreconf: /usr/bin/autoconf failed with exit status: 1

Whatever, this is “traditional” that you can’t actually use autohell without adding more autohell from somewhere else, but ok, bend the knee, install autoconf-archive and continue….

karlp@nanopiduo2:~/src/libgpiod$ ./ 
autoreconf: Entering directory `.'
autoreconf: not using Gettext
autoreconf: running: aclocal --force -I m4
aclocal: warning: couldn't open directory 'm4': No such file or directory
autoreconf: tracing
autoreconf: not using Libtool
autoreconf: running: /usr/bin/autoconf --force error: possibly undefined macro: AC_CHECK_HEADERS
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation. error: possibly undefined macro: AC_LIBTOOL_CXX error: possibly undefined macro: AC_LANG_PUSH error: possibly undefined macro: AC_LANG_POP
autoreconf: /usr/bin/autoconf failed with exit status: 1

wat? really? that’s it? Thanks for nothing. Google and the accumulated wisdom of years of hate suggest that, as this is a “new” machine, I might not have installed pkg-config. (Given that the whole thing with autohell is finding things, you might think it could give you a better error?) ok, Install that, try again

karlp@nanopiduo2:~/src/libgpiod$ ./ 
autoreconf: Entering directory `.'
autoreconf: not using Gettext
autoreconf: running: aclocal --force -I m4
aclocal: warning: couldn't open directory 'm4': No such file or directory
autoreconf: tracing
autoreconf: not using Libtool
autoreconf: running: /usr/bin/autoconf --force error: possibly undefined macro: AC_LIBTOOL_CXX
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.
autoreconf: /usr/bin/autoconf failed with exit status: 1

Progress, but… still not there. Still absolutely atrociously useless error messages. Now, I’ve read the README, it says to install autoconf-archive, and that’s about it. I have python3-dev and libstdc++-9-dev I’m not sure what else now…

Oh right. libtool, another gross piece of historic nonsense, that mostly just gets in the way, and leaves pretend files around the place.

Now autogen will ~finish, and run configure, happily checking whether I’m running on a 30 year old unpatched sparc solaris, so it applies the right workarounds, but don’t worry…

configure: error: "libgpiod needs linux headers version >= v5.10.0"

I mean, I’m super glad we included all that “portable” autohell bullshit when we’re explicitly tied to linux anyway. And not just any linux. At the time of writing, 5.10.0 was three days old.

At this point, there are now _other_ problems that autotools, but this is just another example of why you should NEVER be using autotools in 2020.

Jenkins agent systemd unit file

So, you try to setup an agent, and instead of getting a nice package or anything, you get told, “run this long java command line app” …. wat. Here’s a (very) basic system unit file to run your agent. You can copy the command lines from what jenkins provides itself.

$ cat /etc/systemd/system/jenkins-agent.service 
Description=My radical Jenkins agent

ExecStart=/usr/bin/java -jar /var/opt/jenkins-agent/agent.jar -jnlpUrl -secret @/var/opt/jenkins-agent/my.jenkins.secret-file -workDir /var/opt/jenkins-agent


(And remember, JDK8 for your agent, until is fixed)

Then, just systemctl enable jenkins-agent and systemctl start jenkins-agent.

Vodafone Huawei R216 teardown

For work reasons, we had a couple of these LTE mobile hotspots deployed permanently. After about two years, they’ve failed, both with puffed up batteries. For no good reason I can think of, the devices don’t work with the battery removed, they just blink constantly. Still, they worked well right up to that point, so, what’s in one? I’ve not tried powering one up again properly, I’m not excited about ordering a spare battery for one, and it’s not (currently) interesting enough to try and provide power to the right place to fake the battery.

You can find the FCC internal photos under QISR216 but it’s blurry. We know it’s from Huawei, but that’s about it. You can also find a GPL compliance tarball that implies it runs android, and has some more details if you’re good at digging into that sort of thing (I’m not really) R216-open resource code.tar.gz

Here’s some photos of it’s guts with a bit of decoding. There’s a few spaces on the PCB for what I suspect is a SD card, and a 5GHz wifi radio, but it’s kinda ugly trawling through old product announcements to see if any of it was ever used. The row of circular pads on the PCB edge are actually available without disassembly, (there’s a row of holes inside the battery compartment that provides access to them), so that’s presumably a “useful” port for something. There’s another set of test pads by the wifi chip.

bcm43241 wifi chipset

The silver circle in the white field is actually a super slim pushbutton!

And now the “real” side

Green is HiSilicon HI6559, a PMIC from reading elsewhere. Red is some Micron flash, and a HiSilicon Hi6921M. You can read a bit more about the part here:

Blue is HiSilicon Hi6361gfc, presumably the LTE modem? Purple is the Qorvo multiband power amplifier

White box? No idea, not sure what would ever go on that footprint. The blank on the left is presumably a microsd socket.

There’s another one of these superslim membrane switches in the top right, between the micro usb and the optional external antenna connectors.

sunxi-fel: nanopi duo2 – latest kernel + openwrt rootfs

So yeah, back on getting all of that friendlyarm nanopi duo2 supported again. Want a newer kernel to get the bluetooth stuff in, but not interested in trying to port openwrt forwards (yet?) Instead, build your own kernel as per: and build openwrt as normal. (well… normal, but add this patch to skip a manual step: )

Now, make sure you _BUILT IN_ any kernel modules you want, because we aint doin no module installation and loading bullshit.

And then just…. fel load it, as per

$ sunxi-fel uboot u-boot-sunxi-with-spl.bin write-with-progress 0x42000000 /home/karlp/src/linux-git/arch/arm/boot/zImage  write-with-progress 0x43300000 /home/karlp/src/openwrt-git/bin/targets/sunxi/cortexa7/openwrt-sunxi-cortexa7-sun8i-h3-nanopi-duo2-iotbox-rootfs.uboot write 0x43000000 ~/src/linux-git/arch/arm/boot/dts/sun8i-h3-nanopi-duo2-iotbox.dtb write 0x43100000 my-fel-bootz.scr
100% [================================================]  5229 kB,  842.6 kB/s 
100% [================================================] 13228 kB,  845.3 kB/s 
$ cat my-fel-bootz.cmd 
setenv bootargs console=ttyS0,115200 console=ttyGS0,115200 earlyprintk root=/dev/ram0
bootz $kernel_addr_r $ramdisk_addr_r $fdt_addr_r

And presto. ramdisk from openwrt rootfs as root, upstream kernel of whatever you like.

Shitty blog post, but saving this for next time so I don’t lose it again.

Actually using FEL to boot openwrt on friendlyarm nanopi duo2

Following on from where we left off, now let’s actually use FEL for the whole process, not just to get uboot loaded. In the last piece, once uboot was loaded, we used uboot+tftp to boot our openwrt ramdisk, but FEL can do it all in one hit, over usb, according to and that’s way more useful on things that might not have an ethernet port onboard.

We basically just take our steps from the last one, and add them as options to the sunxi-fel tool.

First, we trim down our “my.env” file, as now it just needs to boot.

$ cat my-fel.cmd
setenv bootargs console=ttyS0,115200 earlyprintk
bootm $kernel_addr_r - $fdt_addr_r

Now, we convert that to the “scr” file that uboot will run magically.

$ tools/mkimage -C none -A arm -T script -d my-fel.cmd my-fel.scr

Now, use sunxi-fel with all the wonderful paths into the build guts of openwrt.

$ sunxi-fel uboot u-boot-sunxi-with-spl.bin write-with-progress 0x42000000 /home/karlp/src/openwrt-git/bin/targets/sunxi/cortexa7/openwrt-sunxi-cortexa7-sun8i-h3-nanopi-neo-initramfs-kernel.bin write 0x43000000 ~/src/openwrt-git/build_dir/target-arm_cortex-a7+neon-vfpv4_musl_eabi/linux-sunxi_cortexa7/linux-4.14.82/arch/arm/boot/dts/sun8i-h3-nanopi-neo.dtb write 0x43100000 my-fel.scr
100% [================================================] 4013 kB, 770.4 kB/s

Because openwrt’s buildroot builds us a nice kernel+ramdisk image out of the box, (well, if you select “ramdisk” in the image settings) we don’t need to write the uImage and rootfs separately as indicated on the page.

For reference, the openwrt diffconfig to build this is just

$ ./scripts/