Kibitzing on a project involving an Arduino Mega (properly MEGA, but who cares?) with plenty of spare I/O pins led me to slap together a block of LEDs:
Arduino Mega Debugging LEDs
The excessive lead length on the 330 Ω resistors will eventually anchor scope probes syncing on / timing interesting program events.
Not that you have any, but they’re antique HP HDSP-4836 tuning indicators: RRYYGGYYRR. If you were being fussy, you might use 270 Ω resistors on the yellow LEDs to brighten them up.
A simple test program exercises the LEDs:
/*
Debugging LED outputs for Mega board
Ed Nisley - KE4ZNU
Plug the board into the Digital Header pins 34-52 and GND
*/
byte LowLED = 34;
byte HighLED = 52;
byte ThisLED = LowLED;
//-----
void setup() {
pinMode(LED_BUILTIN,OUTPUT);
for (byte p = LowLED; p <= HighLED; p+=2)
pinMode(p, OUTPUT);
// Serial.begin(9600);
}
// -----
void loop() {
digitalWrite(LED_BUILTIN,HIGH);
digitalWrite(ThisLED, HIGH);
delay(100);
digitalWrite(ThisLED, LOW);
// delay(500);
ThisLED = (ThisLED < HighLED) ? (ThisLED + 2) : LowLED;
// Serial.println(ThisLED);
digitalWrite(LED_BUILTIN,LOW);
}
Nothing fancy, but it ought to come in handy at some point.
Obviously, you’ll pick a different keymap name than I did. All the files mentioned below will reside in the new subdirectory, which starts out with only a keymap.c file copied from the default layout.
If you had different hardware, you could specify the driver with a WS2812_DRIVER option.
QMK can also control single-color LEDs with PWM (a.k.a. backlighting), and per-key RGB LEDs (a.k.a. RGB Matrix). These functions, their configuration / controls / data, and their documentation overlap and intermingle to the extent that I spent most of my time figuring out what not to include.
The first two lines describe a single WS2812 RGB LED wired to pin B2 (a.k.a. MOSI) of the Atmel 32U4 microcontroller. The default Reset duration and Byte Order values work for the LED I used
Protip: swapping the order from GRB to RGB is a quick way to discover if the firmware actually writes to the LED, even before you get anything else working: it’ll be red with the proper setting and green with the wrong one.
Dialing the maximum intensity down works well with a bright LED shining directly at your face from a foot away.
Turning on RGBLIGHT_LAYERS is what makes this whole thing happen. The RGBLIGHT_EFFECT_RGB_TEST option enables a simple test animation at the cost of a few hundred bytes of code space; remove that line after everything works.
The last two lines remove the debugging facilities; as always with microcontroller projects, there’s enough room for either your code or the debugger required to get it running, but not both.
With those files set up, the keymap.c file does the heavy lifting:
Undefine LED_LL to enable the test mode, compile, flash, and the LED should cycle red / green / blue forever; you also need the RGB_TEST option in the config.h file.
Define LED_LL and layer lighting should then Just Work™, with the LED glowing:
White for the basic layer with all the letters
Magenta with the Fun key pressed
Cyan with the Esc key pressed
The key map code defines colors for layers that don’t yet exist, but it should get you started.
For convenience, I wadded all three QMK files into a GitHub Gist.
The LED is kinda subtle:
Atreus keyboard – LED installed
As you might expect, figuring all that out took much longer than for you to read about it, but now I have a chance of remembering what I did.
Reattaching the plate to the PCB with only three screws allows marking the hole position on the PCB, which is much easier than pretending to derive the position from first principles:
Atreus keyboard – LED marking
Despite appearances, I traced the hole with a mechanical pencil: black graphite turns shiny silvery gray against matte black soldermask. Also, the PCB trace is off-center, not the hole.
Overlay the neighborhood with Kapton tape to protect the PCB from what comes next:
Snip a WS2812 RGB LED from a strip, stick it in place with eyeballometric alignment over the target, and wire it up:
Having helped grossly over-fund the Atreus Kickstarter earlier this year, a small box arrived pretty much on-time:
Atreus keyboard – overview
I did get the blank keycap set, but have yet to screw up sufficient courage to install them. The caps sit atop the stock Kailh (pronounced, I think, kale) BOX Brown soft tactile switches; they’re clicky, yet not offensively loud.
Removing a dozen screws lets you take it apart, revealing all the electronics on the underside of the PCB:
Atreus keyboard – PCB overview
The central section holds most of the active ingredients:
Of interest is the JTAG header at the front center of the PCB:
Atreus keyboard – JTAG header
I have yet to delve into the code, but I think those signals aren’t involved with the key matrix and one might be available to drive an addressable RGB LED.
For future reference, they’re tucked into the lower left corner of the chip (the mauled format comes from the original PDF):
Atmel 32U4 – JTAG pins
The alternate functions:
SCK = PB1
MOSI = PB2
MISO = PB3
I don’t need exotic lighting, but indicating which key layer is active would be helpful.
Love the key feel, even though I still haven’t hit the B key more than 25% of the time.
There’s not much to it, because the RPi can enable pullup resistors on its digital inputs, whereupon the encoder switches its code bits to common. The third oscilloscope probe to the rear syncs on a trigger output from my knob driver.
I started with the Encoder library from PyPi, but the setup code doesn’t enable the pullup resistors and the interrupt (well, it’s a callback) handler discards the previous encoder state before using it, so the thing can’t work. I kept the overall structure, gutted the code, and rebuilt it around a state table. The code appears at the bottom, but you won’t need it.
Here’s the problem, all in one image:
Knob Encoder – ABT – fast – overview
The top two traces are the A and B encoder bits. The bottom trace is the trigger output from the interrupt handler, which goes high at the start of the handler and low at the end, with a negative blip in the middle when it detects a “no motion” situation: the encoder output hasn’t changed from the last time it was invoked.
Over on the left, where the knob is turning relatively slowly, the first two edges have an interrupt apiece. A detailed view shows them in action (the bottom half enlarge the non-shaded part of the top half):
Knob Encoder – ABT – fast – first IRQs
Notice that each interrupt occurs about 5 ms after the edge causing it!
When the edges occur less than 5 ms apart, the driver can’t keep up. The next four edges produce only three interrupts:
Knob Encoder – ABT – fast – 4 edges 3 IRQ
A closer look at the three interrupts shows all of them produced the “no motion” pulse, because they all sampled the same (incorrect) input bits:
In fact, no matter how many edges occur, you only get three interrupts:
Knob Encoder – ABT – fast – 9 edges 3 IRQ
The groups of interrupts never occur less than 5 ms apart, no matter how many edges they’ve missed. Casual searching suggests the Linux Completely Fair Scheduler has a minimum timeslice / thread runtime around 5 ms, so the encoder may be running at the fastest possible response for a non-real-time Raspberry Pi kernel, at least with a Python handler.
If. I. Turn. The. Knob. Slowly. Then. It. Works. Fine. But. That. Is. Not. Practical. For. My. Purposes.
Nor anybody else’s purposes, really, which leads me to think very few people have ever tried lashing a rotary encoder to a Raspberry Pi.
So, OK, I’ll go with Nearer and Farther focusing buttons.
The same casual searching suggested tweaking the Python thread’s priority / niceness could lock it to a different CPU core and, obviously, writing the knob handler in C / C++ / any other language would improve the situation, but IMO the result doesn’t justify the effort.
My attempt at a Python encoder driver + simple test program as a GitHub Gist:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The voltage (yellow) and current (green, 100 mA/div) waveforms look downright tame compared to some of the other chargers!
I made a cursory attempt to crack the case open, but gave up before doing any permanent damage. Hey, that UL listing (and, presumably, the interior details) means they’re three times the price of those Anonymous chargers!
Start by grabbing opposite corners in a small vise and gently cracking the solvent-bonded joint between the sections:
Anon white charger – case cracking
Pull the base past the molded latches:
Anon white charger – case opened
Behold: components!
Anon white charger – PCB top
On both sides of both PCBs!
Anon white charger – PCB bottom
The top half of both boards, above the isolation cut, handles the line voltage and the lower half handles the 5 V USB output. You’ll note the absence of extra-cost parts like voltage feedback or ahem safety fuses.
Treating the whole regulator as a black box simplifies the schematic:
Anonymous white charger – schematic
The cap bridging the two sides should be a Y capacitor, but it’s an ordinary 1 nF ceramic cap with a generous 1 kV rating. As far as I can tell, having it inject AC line noise directly into the +5 V side of the USB supply is just a bonus.