Any new and working images for Devterm with CM3?

Why’s that?

something like Trixie and XFCE since there is small amounts of ram on this board.

I have the R-01 (also 1GB RAM; everything works fine except a browser, which is not a problem for me) and I just run Slackware ( Slackware image for DevTerm R01 ) with ratpoison on it. I think the regular Armbian stuff will work on the CM3, though.


uConsole OS directory

Ha, that sort of thing happens, you know, I think not a lot of people love the R-01, I love it.

(This is an extremely good post, thank you.)


uConsole OS image directory

Not to pick on a possible typo in a draft but the R-01 is 64 bits.


Compiling custom keyboard firmware

This might actually come in handy, unexpectedly, as it looks like the PicoCalc uses the same CPU, and I have some dumb PicoCalc stuff to try shortly.


Compiling custom keyboard firmware

I jostled my DevTerm and the keyboard went into the busy-wait loop again. This ate another 15 minutes; I needed a break anyway.

TL;DR: 0.1 keyboard firmware with a delay added to fix the busy-wait race condition: https://media.freespeechextremist.com/rvl/full/22f4e1e35c95803479306abf0fa1e0170d99697cd93308bac5a981394ca10bfd?name=devterm_keyboard.bin . This will be useful to basically no one but people that also use acme on the DevTerm and also can’t get the firmware to compile. That may be just me.

The remainder is an explanation because it was an adventure. Gory details follow. Most hackers’ blood runs cold if they hear a sentence that starts with “Can’t you just⋯” but it’s a little harder to recognize when you are doing it to yourself with “We could just⋯” or “Hey, I’ll just⋯”.

The mouse support on the 0.3 firmware is much nicer and I’d really like to be able to use it, but the changes to the behavior of middle-click (simulating a scroll wheel if you move the mouse while holding it; I use acme and this breaks acme). So I’ve got a .ino with that behavior fixed but I cannot get it to compile. (Same versions of everything listed in the README, etc., balky Arduino IDE refuses to compile the code, I blow away ~/.arduino15 and start from scratch and grab the .) I really like the uConsole and the PicoCalc is fun so far, but I still use the DevTerm literally every day. I still absolutely love this machine.

void setup() {
  USBComposite.setManufacturerString("ClockworkPI");
  USBComposite.setProductString("DevTerm");
  USBComposite.setSerialString(SER_NUM_STR);
  
  dev_term.Keyboard = new HIDKeyboard(HID);
  dev_term.Joystick = new HIDJoystick(HID);
  dev_term.Mouse    = new HIDMouse(HID);
  dev_term.Consumer = new HIDConsumer(HID);

  dev_term.Keyboard->setAdjustForHostCapsLock(false);

  dev_term.state = new State();

  dev_term.Keyboard_state.layer = 0;
  dev_term.Keyboard_state.prev_layer = 0;
  dev_term.Keyboard_state.fn_on = 0;
  dev_term.Keyboard_state.shift = 0;
  
  dev_term._Serial = new  USBCompositeSerial;
  
  HID.begin(*dev_term._Serial,reportDescription, sizeof(reportDescription));

  while(!USBComposite); // All we need to do is add delay(10); here.

  keyboard_init(&dev_term);
  keys_init(&dev_term);
  trackball_init(&dev_term);
  
  dev_term._Serial->println("setup done");

  pinMode(PD2,INPUT);// switch 2 in back 
  
  delay(1000);
}

So, I thought, it’s one call: I should just patch the binary! It’s easy-mode for reverse-engineering because the source is right there: this is essentially open-book! This turns out to have been more of an adventure than intended. (But still less pain than arguing with the Arduino SDK.)

The .bin files in the repo (which, with a small amount of effort, you can extract from the shell scripts in Code/devterm_keyboard/bin from the GitHub - clockworkpi/DevTerm: This code repository offers downloads for the latest images of various DevTerm models, as well as kernel patches, keyboard firmware, the source code for screen and printer drivers, hardware schematics, assembly instructions, and essential technical documents. repo) are, of course, without symbols. So, xxd! Easy! I’ll just find the strings in the binary. ARM uses constant pools, so the strings are probably right next to the code! Not too many strings in there, but you can see “ClockworkPI” and “DevTerm” and “setup done” starting at 0xa8d4, and a few other string constants before that. Since stmduino puts the firmware at 0x8000000, this means that the address in memory. Also it turns out that stmduino is Cortex-M3 and Thumb-only and Thumb is way less like ARM than I expected. I’ve never written Thumb by hand; just ARM. This turns out to be relevant.

This will save you some trouble if you want to dump firmware for stmduino devices:

arm-none-eabi-objdump -b binary -m arm -M force-thumb -D --adjust-vma=0x08000000 devterm_keyboard.ino.bin

ARM vs. Thumb is interesting. I fell in love with the ISA back when the Game Boy Advance (ARM7tdmi) was current-gen; I was somewhat late to the game as the Acorn RISC Machine desktops first shipped around the time of the 80286. (There is a really fascinating series of articles at A history of ARM, part 1: Building the first chip - Ars Technica . I learned ARM assembly from a very opinionated site from a devoted ARM fan, ARM ASSEMBLER PROGRAMMING; tutorial, resources, and examples (which was, back then, heyrick.co.uk, if you have the urge to inspect it in the Wayback Machine). The assembly language was super expressive and wonderful and I was seduced and largely ignored Thumb. Every instruction is the same size, one 32-bit word, so unlike x86 where you start disassembling at the wrong offset and you get some plausible-looking gibberish, you’re always aligned when disassembling ARM. You could also do a lot of fun things, like immediate values in registers could be shifted and then you could do a relative load with an offset and this was how you usually interacted with I/O registers. You could keep registers around for this, you’d set one to the video memory or wherever and do some loads and stores; it turns out that if you’re restricted to Thumb-only, immediate values are so scarce that you chase pointers to pointers to pointers just to get a 32-bit number into a 32-bit register.

So, what you do for convenience and instruction-bumming in ARM mode becomes a constant necessity in Thumb mode: the compiler holds onto those registers for dear life. Consequently, although I was looking for “setup done”, and figured it would be straightforward to find its address in a constant pool, it was loaded relative to r6, which was set up approximately 292 bytes before that string was used, and I spent some time not realizing this.

 8000b36:	4e58      	ldr	r6, [pc, #352]	@ (0x8000c98)
 ⋯
 // dev_term._Serial->println("setup done");
 8000c5a:	6970      	ldr	r0, [r6, #20]
 8000c5c:	4915      	ldr	r1, [pc, #84]	@ (0x8000cb4)
 8000c5e:	f003 fc68 	bl	0x8004532

(If you haven’t read much disassembly, on the left is the address, then the 16-bit Thumb instruction, with a little space for double-wide instructions, then the assembly mnemonic. The addresses after the @s are provided by objdump, but the mapping to lines in the code If you know ARM, it’s probably pretty legible but there are gotchas.)

One of the reasons I like to deal with acme is more or less the same reason people like Jupyter notebooks: it’s really easy to intersperse code and annotations, you run a shell and you can make notes. So I was already sitting with one foot in Plan 9 and one in Linux and was pretty sure there was a hex editor for Plan 9 (never enjoyed xxd -r). Cloning and installing vexed(1) was the easiest part of this entire process: shithub: vexed .

Eventually, spending a lot of time blindly stumbling through objdump and I grepped the disassembly for every occurrence of #1000 and found what I was looking for:

 // delay(1000)
 8000c6a:	f44f 707a 	mov.w	r0, #1000	@ 0x3e8
 8000c6e:	f003 fd91 	bl	0x8004794

The bit that made it stand out was that you can see that the pointer dev_term is used in three functions a few lines above the delay, for keyboard_init, keys_init, and trackball_init.

After some careful reading backwards, I annotated enough of the function to figure out what I had to work with:

 // while(!USBComposite);
*8000c36:	4b1e      	ldr	r3, [pc, #120]	@ (0x8000cb0)
 8000c38:	f897 1074 	ldrb.w	r1, [r7, #116]	@ 0x74
 8000c3c:	681a      	ldr	r2, [r3, #0]
*8000c3e:	b901      	cbnz	r1, 0x8000c42
*8000c40:	e7fe      	b.n	0x8000c40
 8000c42:	7b13      	ldrb	r3, [r2, #12]
 8000c44:	2b05      	cmp	r3, #5
*8000c46:	d1fa      	bne.n	0x8000c3e
 // keyboard_init(&dev_term);
*8000c48:	4813      	ldr	r0, [pc, #76]	@ (0x8000c98)
*8000c4a:	f7ff fba5 	bl	0x8000398
 // keys_init(&dev_term);
*8000c4e:	4812      	ldr	r0, [pc, #72]	@ (0x8000c98)
*8000c50:	f7ff fe9a 	bl	0x8000988
 // trackball_init(&dev_term);
*8000c54:	4810      	ldr	r0, [pc, #64]	@ (0x8000c98)
*8000c56:	f7ff ff1b 	bl	0x8000a90
 // dev_term._Serial->println("setup done");
 8000c5a:	6970      	ldr	r0, [r6, #20]
*8000c5c:	4915      	ldr	r1, [pc, #84]	@ (0x8000cb4)
*8000c5e:	f003 fc68 	bl	0x8004532
 // pinMode(PD2,INPUT)
 8000c62:	2102      	movs	r1, #2
 8000c64:	2030      	movs	r0, #48	@ 0x30
*8000c66:	f003 fc83 	bl	0x8004570
 // delay(1000)
 8000c6a:	f44f 707a 	mov.w	r0, #1000	@ 0x3e8
*8000c6e:	f003 fd91 	bl	0x8004794
 // return
 8000c72:	b00c      	add	sp, #48	@ 0x30
 8000c74:	e8bd 81f0 	ldmia.w	sp!, {r4, r5, r6, r7, r8, pc}

The return was implicit, but have a look at which registers it’s restoring. Incidentally, although the .ino file may have initially looked like friendly C code, there was something more sinister lurking: it’s actually C++. I spent some time scratching my head: the beginning of setup() calls, for example, USBComposite.setManufacturerString("ClockworkPI");, but then there’s while(!USBComposite). It may look like we’re just waiting for a variable to stop being null but that’s actually calling something like explicit operator bool() const somewhere.

Most of the code is pretty straightforward. Entirely too straightforward: -Os is pretty good at Thumb, I guess. To do a delay(10), we’ll need at least six bytes and there doesn’t appear to be anywhere to put them. No space in the constant pool. If we’re patching in-place, we can’t just insert it, because that’ll screw up all of the offsets everywhere, and if you look at that series of instructions a little closer, you’ll notice that of the 24 instructions where we’re operating, 14 of them (marked with asterisks) are PC-relative: not just PC-relative loads from the constant pool, but look at, for example, 8000c3e, which is a conditional relative-jump (cbnz, “compare and branch if non-zero”), but all of the branches are relative. The code looks as simple as we can make it, as compact as it could be, and extremely sensitive to being moved. It is probably satisfying if you’re the author of the compiler, but if you’re trying to find a place to cram some new instructions, it’s nightmarish.

I spent some time trying to figure out 8000c40, which is just an endless loop: it jumps to 8000c40. The instruction right before it, jumps over it unless r1==0 and I don’t know what that actually signifies so why it would choose that time to lock up is a mystery to me. I spent an inordinate amount of time thinking “Maybe I could write some cleverly compressed code, shove it onto the stack, and execute it there”.

A sacrifice has to be made, clearly. Thankfully, we have dev_term._Serial->println("setup done"); and that’s debugging output. If we take that out, we get eight extra bytes, more than the six we need. Then we could just move everything down. So, the first step, we make some space:

 // while(!USBComposite);
 8000c36:	4b1e      	ldr	r3, [pc, #120]	@ (0x8000cb0)
 8000c38:	f897 1074 	ldrb.w	r1, [r7, #116]	@ 0x74
 8000c3c:	681a      	ldr	r2, [r3, #0]
 8000c3e:	b901      	cbnz	r1, 0x8000c42
 8000c40:	e7fe      	b.n	0x8000c40
 8000c42:	7b13      	ldrb	r3, [r2, #12]
 8000c44:	2b05      	cmp	r3, #5
 8000c46:	d1fa      	bne.n	0x8000c3e
 8000c48:	bf00      	nop
 8000c4a:	bf00      	nop
 8000c4c:	bf00      	nop
 8000c4e:	bf00      	nop
 // keyboard_init(&dev_term);
 8000c50:	4811      	ldr	r0, [pc, #68]	@ (0x8000c98)
 8000c52:	f7ff fba1 	bl	0x8000398
 // keys_init(&dev_term);
 8000c56:	4810      	ldr	r0, [pc, #64]	@ (0x8000c98)
 8000c58:	f7ff fe96 	bl	0x8000988
 // trackball_init(&dev_term);
 8000c5c:	480e      	ldr	r0, [pc, #56]	@ (0x8000c98)
 8000c5e:	f7ff ff17 	bl	0x8000a90
 // pinMode(PD2,INPUT)
 8000c62:	2102      	movs	r1, #2
 8000c64:	2030      	movs	r0, #48	@ 0x30
 8000c66:	f003 fc83 	bl	0x8004570
 // delay(1000)
 8000c6a:	f44f 707a 	mov.w	r0, #1000	@ 0x3e8
 8000c6e:	f003 fd91 	bl	0x8004794
 // return
 8000c72:	b00c      	add	sp, #48	@ 0x30
 8000c74:	e8bd 81f0 	ldmia.w	sp!, {r4, r5, r6, r7, r8, pc}

Eight bytes never felt so luxurious. Of course, it was easy enough to adjust the PC-relative ldrs but the branches were and are a huge pain. The offsets for the ldrs were easy enough: those instructions all moved down eight bytes, so subtract eight from the offset. For the encoding (which matters if you’re going to be making the change with a hex editor), ldr is always word-aligned so Thumb doesn’t encode the two lowest bits, and the offset is at the end of the instruction, so you just subtract 2: 4813 becomes 4811. For the bl, though, the encoding is a mess. You have 23 immediate bits (sign plus 22 bits) offset from the PC and split up across the two half-words and shifted some of them are XOR’d with the sign and I do not know why. They are corrected in the above, but at this stage, I actually just wrote #WRONG next to them until the end.

# Some Ruby that does the bl calculation:
def bl_calc bl_addr, target_addr
	p = (bl_addr + 4)
	imm32 = (target_addr - p) >> 1
	imm11 = imm32 & 0x7FF # bits[10:0]
	imm10 = (imm32 >> 11) & 0x3FF # bits[20:11]
	s = (imm32 >> 21) & 1
	i1 = (imm32 >> 20) & 1
	i2 = (imm32 >> 19) & 1
	j1 = (~(i1 ^ s)) & 1
	j2 = (~(i2 ^ s)) & 1
	hi = (0b11110 << 11) | (s << 10) | imm10
	lo = (0b11 << 14) | (j1 << 13) | (1 << 12) | (j2 << 11) | imm11
	'%04x %04x' % [hi, lo]
end

So, where’s the semicolon in that while loop? That is, where can we safely insert the call? That’s complicated, but in a somewhat less tedious way than the encoding of bl.

First it loads a constant from the pool into r3: 8000cb0 has 38 06 00 20, which is little-endian, so the address is 20000638. That’s probably some mmap’d I/O register, so that’s probably the address it checks to see if the USB is ready. So we probably need to make sure that the loop reads from wherever that is. r1 and r2 are both caller-saved registers, which means that if we call delay(10), we’ll need to restore them. For r1, we can just make sure it’s reset every time by making the bne.n point back up to 8000c38, which loads r1 from wherever r7 is pointed. r2 is a bigger problem, but since r4 is callee-saved and none of the instructions below us use it until it gets restored at 8000c74, we can just replace r2 with r4 and instead of figuring out how it’s encoded, it’s a big file, we just find an occurrence of ldr r4, [r3, #0] elsewhere (8001196 has one). I didn’t find any other occurrences of ldrb r3, [r4, #12], but it was easy to find the bit by being lazy and xoring the encodings for ldrb r3,[r2], ldrb r3,[r4], and ldrb r3[r2,#12]: 0x7823^(0x7813^0x7b13) and that yields 7b23. For the bne.n, I had irb open and just read the disassembly file, looked for other bne.n occurrences, then subtracted the destination address from the address of the instruction and looked for 0x16: d1f3.

Finally, we have the code:

 // while(!USBComposite);
 8000c36:	4b1e      	ldr	r3, [pc, #120]	@ (0x8000cb0 = 0x20000638)
 8000c38:	f897 1074 	ldrb.w	r1, [r7, #116]	@ 0x74
 8000c3c:	681c      	ldr	r4, [r3, #0]
 8000c3e:	b901      	cbnz	r1, 0x8000c42
 8000c40:	e7fe      	b.n	0x8000c40
 8000c42:	bf00      	nop
 8000c44:	200a      	movs	r0, #10
 8000c46:	f003 fda5  	bl 0x8004794
 8000c4a:	7b23      	ldrb	r3, [r4, #12]
 8000c4c:	2b05      	cmp	r3, #5
 8000c4e:	d1f3      	bne.n	0x8000c38
 // keyboard_init(&dev_term);
 8000c50:	4811      	ldr	r0, [pc, #68]	@ (0x8000c98)
 8000c52:	f7ff fba1 	bl	0x8000398
 // keys_init(&dev_term);
 8000c56:	4810      	ldr	r0, [pc, #64]	@ (0x8000c98)
 8000c58:	f7ff fe96 	bl	0x8000988
 // trackball_init(&dev_term);
 8000c5c:	480e      	ldr	r0, [pc, #56]	@ (0x8000c98)
 8000c5e:	f7ff ff17 	bl	0x8000a90
 // pinMode(PD2,INPUT)
 8000c62:	2102      	movs	r1, #2
 8000c64:	2030      	movs	r0, #48	@ 0x30
 8000c66:	f003 fc83 	bl	0x8004570
 // delay(1000)
 8000c6a:	f44f 707a 	mov.w	r0, #1000	@ 0x3e8
 8000c6e:	f003 fd91 	bl	0x8004794
 // return
 8000c72:	b00c      	add	sp, #48	@ 0x30
 8000c74:	e8bd 81f0 	ldmia.w	sp!, {r4, r5, r6, r7, r8, pc}

We end up with an extra nop but it doesn’t hurt to have an extra nop in an idle loop in an init function. The modified section summed to 54 bytes, so I tapped it into the hex editor (remember: little-endian!) and then disassembled the result and diffed that against the original assembly to see if it really did look right:

@@ -1345,21 +1345,21 @@
  8000c32:      f001 fc49       bl      0x80024c8
  8000c36:      4b1e            ldr     r3, [pc, #120]  @ (0x8000cb0)
  8000c38:      f897 1074       ldrb.w  r1, [r7, #116]  @ 0x74
- 8000c3c:      681a            ldr     r2, [r3, #0]
+ 8000c3c:      681c            ldr     r4, [r3, #0]
  8000c3e:      b901            cbnz    r1, 0x8000c42
  8000c40:      e7fe            b.n     0x8000c40
- 8000c42:      7b13            ldrb    r3, [r2, #12]
- 8000c44:      2b05            cmp     r3, #5
- 8000c46:      d1fa            bne.n   0x8000c3e
- 8000c48:      4813            ldr     r0, [pc, #76]   @ (0x8000c98)
- 8000c4a:      f7ff fba5       bl      0x8000398
- 8000c4e:      4812            ldr     r0, [pc, #72]   @ (0x8000c98)
- 8000c50:      f7ff fe9a       bl      0x8000988
- 8000c54:      4810            ldr     r0, [pc, #64]   @ (0x8000c98)
- 8000c56:      f7ff ff1b       bl      0x8000a90
- 8000c5a:      6970            ldr     r0, [r6, #20]
- 8000c5c:      4915            ldr     r1, [pc, #84]   @ (0x8000cb4)
- 8000c5e:      f003 fc68       bl      0x8004532
+ 8000c42:      bf00            nop
+ 8000c44:      200a            movs    r0, #10
+ 8000c46:      f003 fda5       bl      0x8004794
+ 8000c4a:      7b23            ldrb    r3, [r4, #12]
+ 8000c4c:      2b05            cmp     r3, #5
+ 8000c4e:      d1f3            bne.n   0x8000c38
+ 8000c50:      4811            ldr     r0, [pc, #68]   @ (0x8000c98)
+ 8000c52:      f7ff fba1       bl      0x8000398
+ 8000c56:      4810            ldr     r0, [pc, #64]   @ (0x8000c98)
+ 8000c58:      f7ff fe96       bl      0x8000988
+ 8000c5c:      480e            ldr     r0, [pc, #56]   @ (0x8000c98)
+ 8000c5e:      f7ff ff17       bl      0x8000a90
  8000c62:      2102            movs    r1, #2
  8000c64:      2030            movs    r0, #48 @ 0x30
  8000c66:      f003 fc83       bl      0x8004570

What do you think of this module?

Ouch. Yeah, I’d be worried if it hit the 90s.

I have kind of a pile of SBC heatsinks, I probably would have cut a hole in the back to fit one if it didn’t come with something to cool it off. The CM-4 can barely get by without a heatsink, the A-06 really needs one. The RK1 (similar CPU, 3588 vs. the A-06’s 3399) has a heatsink with a built-in a fan and case fans and it idles at ambient room temperature, but that wouldn’t work on a machine like the DevTerm (bulky heatsink plus all the fans uses a little more juice).

The R-01 I’ve been running with no heatsink. 40 degrees, never gets past 50 when its single core is fully loaded. I’d be really excited about a beefier RISC-V one; it seems about time for a new CPU board, but the existing ones still do the job fine; I use the A-06 about as much as I use my desktop machine.

I seem to remember reading about upgraded components in later shipments.

Yeah, basically moves the heat to the sheet and also covers the vent slots in the back of the case up to the fan, so it makes a little duct instead of just shooting air in the general direction of the CPU and hoping for the best. I didn’t see pictures of it in anyone’s build (I spent an excessive amount of time looking at pictures before the DevTerm arrived; I was very excited) and it came with separate instructions so I figured that it wasn’t part of the first run.

I think cranked all the way down the A06 is probably friendlier with battery life than the CM4?

I think so, but you can turn off cores in the CM-4 by doing sudo sh -c 'echo 0 > /sys/devices/system/cpu/cpuN/online' (replacing N) for as many cores as you want to turn off, and setting the powersave governor. I kind of wonder why the gear system doesn’t just change governors around instead of setting max frequency; like you can set the CPU governors independently, and that caps all of them at the min frequency, and adjust. There’s a “conservative” governor that is like ondemand or schedutil but it has a delay when changing frequencies so there’s a ramp-up and ramp-down period, but I think (don’t quote me on this, haven’t checked, going from memory) that it’s not included for the CM-4.


What do you think of this module?

like other folks I’ve read comments from here, I too have turned my A06 Devterm into a CM4 device

I have the opposite experience: the CM4 is sluggish and I almost only ever use my A06 (really nice performance) or R01 (runs 6-8 hours and “not fast enough for a browser besides w3m/lynx” turns out to be a benefit). I have the CM4 in my uConsole and it’s fine but the A06 is speedy and works fine for me. (I even sprang for the 8GB CM4.) I ended up putting the CM4 into a module and slotting it into the TPi to use as a NAS.

it generally ran too hot to use them all

I think the big piece of copper and thermal paste came with later orders, or at least mine came with one; mine gets hot when it’s cranked all the way up but I rebuilt everything on-device except qt webengine (because it required more RAM to link than I could muster even after plugging in extra USB drives to use as swap). All the cores maxed out, it got up to 71C, kinda hot but still fine.

It’s possible I never got my scheduler and gear-shifting settings just right

I think what was helpful for me was I keep conky on the side (ratpoison with set padding 280 0 0 0, but I set it to toggle so I can hide conky if I want the rest of the screen), so I can see what’s going on and shift appropriately. I usually keep it cranked all the way down (-s 1 or -s 2) and turn it up when I am compiling something or if I attempt to use Firefox or mplayer or something starts to eat a lot of CPU. It’s on -s 1 right now, so just the ambient X/conky/ssh/drawterm stuff eats 13%, but it still lasts a long time. I tried to attach my .conkyrc but it’s not an allowed file type so here’s a screenshot and a gist The .conkyrc I am using on my DevTerm. · GitHub . Anyway, long way of saying it’s easier to figure out when you need to crank the gears up or down when you have a CPU monitor running.

As far as performance, I did some test compiles, the performance cores are about twice as fast at the same clock rate.


Delivery 90 working days.

Maybe it’s a real photo of a large stack of empty boxes. Anyone can print a label on a box.


Delivery 90 working days.

How should this be understood?

They’re scalpers. This happens with any product that has a supply bottleneck.

Where do these sellers get whole cabinets with finished products?

Same place everyone else does, if they even have them. Sometimes they just have boxes, sometimes they have photos from Twitter or Weibo or whatever.


Compiling custom keyboard firmware

Every time I bump the keyboard and it gets disconnected and I have to pop the front off to do the plug/unplug dance, I play a little more with getting my custom firmware onto the keyboard. (I use acme so I need a proper middle-mouse button, so I am using v0.1, but I want some of the bug fixes from newer version, like yatli’s fix: Keyboard stuck in Bootloader mode - #2 by yatli . Also there are some things I want to play with, hacking the keyboard firmware is fun!)

So I have gotten the Arduino SDK installed, I have gotten my changes in, I think it should work, I know how to get .bin files loaded (I use the programmer from the repo rather than trying to get the Arduino SDK to do it; it would be nice not to have to use the Arduino SDK), and now I’m stuck at the last piece:

In file included from /tmp/arduino_build_81750/sketch/devterm.h:5:0,
Alternatives for USBHID_Types.h:
ResolveLibrary(USBHID_Types.h)
from /tmp/arduino_build_81750/sketch/keyboard.h:9, → candidates:

             from DevTerm/Code/devterm_keyboard/devterm_keyboard.ino:1:

state.h:1:26: fatal error: USBHID_Types.h: No such file or directory
#include
^
compilation terminated.
exit status 1
USBHID_Types.h: No such file or directory

I think this is probably a missing library, something I didn’t click, something I wasn’t supposed to click but did, something I didn’t install; I do not know, though.

I’ve got the settings correct, I believe, or at least what I have matches the wiki ( Compile keyboard bootloader and firmware · clockworkpi/DevTerm Wiki · GitHub ):

settings

I’m sure I’m doing something trivially wrong (or at least I hope the solution is simple and I have zero confidence that I understand what is going on and I really wish I was dealing with a Makefile).


CRUX for DevTerm A06, dev notes

I still have to produce the full image, but I’m still running the working image I built.

There is no major blocker, just I haven’t taken the time to build it and to figure out a better hosting situation. (Hosting it where my website/email/whatever live is fine but the upstream is bad so I’d have to throttle and my main hosting setup has metered bandwidth, so I will probably end up throwing it onto a Frantech slab.) In the mean time, I don’t know how easily people on this forum can get to a Tor server.

The plan is mostly to take take the kernel/bootloader/partition table from the official image, copy the kernel modules into /lib/modules, add all the packages (prebuilt packages available here, will figure out a method for hosting some bigger files: http://s3ldbb3l5eqd6tjsklzmxy6i47i3fim55fpxmgeaa6rvpcllkt4ci4yd.onion/a06/crux/ ), and do test-boots, debug it, etc., then remove the ssh keys (so new ones are created on startup), set the root password to something simple so that you can log in and create your user account. Basic nice stuff.

I have acquired a TuringPi 2 and some of their RK-1 boards, so I don’t need to use the qemu chroot any more: distccd lets them function as a small build farm, and their CPUs are similar enough (CPi’s A-06 is a RK3399, big.LITTLE 4/2, the TuringPi RK-1 uses an RK3588, which is dynamIQ and 4/4) that I actually used the DevTerm packages to build out the CRUX image for the RK-1. (I’m running it on a couple of headless CM-4s also, so I guess it’s also compatible, drivers aside.) I wish I had gotten one of them sooner (they had the same supply chain issues that Clockwork Pi had): it was painful trying to get the stupid qt6 stuff to build, but if I had a few nodes with excessive amounts of RAM, I wouldn’t have needed to do hacks.

It is just regular Linux, so the screenshots have more to do with what I am doing to the system than they do with what the system does, but I did like CPi’s amber setup so much that I cloned it on my R-01 Slackware image (They look orange in screenshots, but the bluish cast of the LCD makes them amber: Slackware image for DevTerm R01 .), and the view is a little more conventional on the A-06: running Firefox to view AwkiAwki ( GitHub - pete/awki: Awki is a lightweight wiki written in awk. These are my patches, original here: http://awkiawki.bogosoft.com/ ), dvtm in urxvt, cool-retro-term (power-hungry, I don’t use it too often), drawterm to edit code on the Plan 9 system (and in front of it, 9front’s vt(1), showing an ssh interface to a Pleroma instance), and trusty old xpdf showing “Coders at Work”.


µConsole's emulation

I like it! I use it all the time. I should probably say that I’m running a kind of minimal environment on the R-01 DevTerm: lightweight UI (ratpoison, conky), some small programs (urxvt, xpdf, lighttpd, w3m), and most of the time it is connected to another machine that is doing more work (drawterm to the Plan 9 systems, ssh to the Linux systems). I run gcc and the go compiler sometimes, I use awk a lot, Ruby once in a while.

All of that stuff that I have up and running, the CPU sits at about 3% used, using about 150MB RAM. Because of that, the batteries last 6-8 hours (depending on how I set the backlight), and this is incredibly useful.


µConsole's emulation

That would be very interesting, yes. The A-06 is very capable of compiling anything, but linking qt6 required an obscene amount of RAM and, after much trial and error, I landed on using qemu-binfmt on my desktop system. Some fiddling was required to get a functional chroot: since I registered qemu as the handler for 64-bit ARM binaries, I had to make sure that qemu-aarch64 was available inside the chroot and all the libraries it linked against.

I think something like that would work fine for a RISC-V CPU, but very interested in how you’ve set yours up. I mostly used the prebuilt packages from slarm64 ([sic], the ARM-64 Slackware project has sort of taken custody of the RISC-V Slackware port), and for anything else that I needed, it was fast enough to compile on-device. (I am not likely to try to run Firefox on it. I really like the D1, I don’t wanna fill its memory with JavaScript.)

For builds now, I actually use a TPi2 RK1; I should probably write that up in the thread ( CRUX for DevTerm A06, dev notes ). I imagine that you could do something analogous by getting one of those Lychee Clusters: LicheePi Cluster 4A - Sipeed Wiki . Very cool gear, but possibly overkill just to speed up your builds.


LCD driver source?

You should be able to find it here: GitHub - yatli/xf86-video-fbturbo: Xorg DDX driver for AllWinner D1


R-01 / toot & exchange client

That is what I am talking about. They could post, or even cross-post the same stuff. They don’t need a moderation team.

I think there definitely would be less anxiety about shipping on these forums if they had a community manager who could speak about delays proactively

To be fair, they appear to have been posting in zh-CN elsewhere, which is fine. I ordered a uConsole the day after the announcement and followed it here, and there were a lot of really hostile people throwing accusations and insults. In CPi’s position, I would probably have stayed away from the place where that was going on.

They’re making these cool devices that appeal to hackers, and the uConsole is enough of a “product” that it attracted a broader audience, people that don’t really understand the engineering process or what “lead time” means or how thin the margins can be, and thus were somewhat less sympathetic.


R-01 / toot & exchange client

the CEO of Mastodon (LOL) blocked me

Ha! Everything I have to say about that is off-topic here.

moderating is a pain

I have spent most of my time on there actively avoiding doing moderation and it is still a pain. Some people sign up just to try to grief the mods, so whatever the rules are, they will come up with something.


R-01 / toot & exchange client

You’d want an organization running a mastodon server to be capable of moderating the service. There’s one staff member here who posts in one thread once a month.

You might need to moderate it if it’s got open registrations, but if the only people with accounts on the instance are CPi staff, you don’t need a moderation team.

It doesn’t have to be something grandiose, that’s the point: if they are on fedi, people with accounts on other fedi servers can follow them.


What should we work on next?

I’m actually really delighted with the R01. I expected it to be fun, but it’s very usable and runs super cool and it’s kind of astonishing how well-supported the chip is: everything compiles cleanly, no strange crashes. I was able to get Slackware installed and it works great for daily use.

A RISC-V board with a little more RAM and a few more cores would be an amazing addition for the DevTerm and the uConsole.


R-01 / toot & exchange client

What I use on my R01 is usually just bloat ( bloat - A web client for Mastadon Network , I have a version with a lot of changes at GitHub - pete/bloat: Mirror of a minimal set of changes to BloatFE / git.debu.gs Git - bloat/summary ). It runs fine on w3m or other super-minimal browsers. I use bloat from mothra: the R-01 runs drawterm ( http://drawterm.9front.org/ ) just fine, so I can connect to the Plan 9 machines.

There’s also sshocial ( Duponin / sshocial · GitLab ) which doesn’t require a browser, but I haven’t used it; the instance I’m on has an ssh interface built in.

I kind of wonder why CPi doesn’t run an instance. It’d be pretty cool to be able to get news there. (I locked my Twitter account and quit using it in 2018, but you can’t even use Nitter to read Twitter accounts any more.) On the other hand, every time I talk about the DevTerm or uConsole on fedi, people tell me it’s not a useful device and that it’s impossible to do work on it, even if I am talking about having worked on it all day.


uPico Expansion Card

You’re missing a space. sudo cp target/release/upico /usr/local/bin.


Screen sleeps/blanks, KB and trackball won’t wake

I finally decided to try another way to wake it up, by plugging in an external mouse and keyboard, and I discovered the system immediately woke up and was responsive.

Check powertop. One of the screens in it has a list of USB devices and whether their power management status puts them to sleep or not. If the internal keyboard/trackball go to sleep (or maybe are put to sleep by the OS/DE combination you are using, so if you are using a DE, check if it does any power management in its settings), but an external keyboard doesn’t, then that might be that the reason your device has trouble waking up.