It’s so hard for me to clean up my office. I’ll see a random collection of objects that I’m supposed to be sorting, putting away, and/or throwing in the garbage.. and I can’t help playing with them instead.
This time the objects in question were an old CCFL backlight inverter, a neon flicker-flame bulb, and a linear table from a discarded flatbed scanner.
These flicker-flame bulbs have two metal plates in them. Normally, the 60 Hz AC power charges the neon gas between the two plates. The current isn’t high enough to uniformly light the neon along the entire plates, so you end up with an unstable flickering region caused by the gas non-uniformly ionizing along the surface of the plates. As the 60 Hz power cycles on and off, there are frequent opportunities for the ionization to break down, and for the lamp to re-light with a different pattern of glow discharge.
To make this more interesting, I wrapped a very thin (AWG 32) wire around the bulb as a ground. Now instead of just discharging between the plates, we can observe discharge between the plates and the glass envelope of the bulb. Much more interesting— it looks more like a plasma globe now:
Since the CCFL inverter generates a much higher frequency variety of AC power, the bulb actually doesn’t normally flicker when driven this way. To get the interesting flickering back, but in a more controlled fashion, I hooked the input of the inverter up to a benchtop function generator. And finally, to “plot” the flickering, I used a long exposure and the linear motion from the old flatbed scanner table:
A few years ago, I implemented an S/PDIF encoder object for the Parallax Propeller. When I first wrote this object, I wrote only a very terse blog post on the subject. I rather like the simplicity and effectiveness of this project, so I thought I’d write a more detailed explanation for anyone who’s curious about the gritty details.
The source code is open, under an MIT-style license. If you’re a fellow Propeller fan, it’s pretty easy to use this code to give your next sound project a digital output. If not, read on… perhaps you will be inspired to try exploring digital audio on a different microcontroller platform!
Digital Audio Primer
Starting from the very basics… what is S/PDIF, and why would we even want to generate it directly from a microcontroller?
Microcontroller audio projects are getting more and more popular, especially as legions of Arduino hackers build DIY drum machines, noise makers, 8-bit synthesizers, and so on. Many of these bit-bang low-fi audio in software. Some of them use external analog synthesizers, MP3 decoders, or other support ICs. If you wanted high-fidelity audio, though, your options get more limited. Some microcontrollers (like the Propeller) can perform PWM at a high enough frequency to produce reasonable audio quality. But this is still no match for an external DAC, much less a high-quality external DAC. So, if you’re really trying to produce higher quality audio without a lot of extra fuss or expense, it makes some sense to let someone else do the job.
If you have a hi-fi stereo receiver, you already have an external DAC and a good way to communicate with it. Nearly all consumer audio receivers now include digital audio inputs based on the Sony/Phillips Digital Interconnect Format (S/PDIF) standard. This consumer standard is actually a variant of the professional AES3 standard. Electrically, this is a high speed unidirectional serial link with a clock that runs at a high multiple of the audio sample rate. The physical transport can be a low-voltage signal over 75-ohm coax, or it could be optical. Optical interconnects (with TOSLINK connectors) are especially common, and to transmit these signals all you need is an LED.
For every audio sample. this digital signal transmits all of the bits in the sample, as well as some control information. Also, quite importantly in fact, it transmits the timing of these samples. The DAC synchronizes its conversion cycles to the time-of-arrival of each sample that comes over the digital bitstream. So, the analog timing characteristics inherent in this digital signal can also influence the resulting analog signal.
Sound nerds tend to get fabulously stressed out over jitter and wander and so on— names for different kinds of deviations from optimal bit timings. It’s good to keep in mind that, by nature, S/PDIF is a much more real-timey sort of signal than your average serial data link. But unless you’re the sort of hopeless audiophile who spends more on your amplifier power cables than I’d spend on a car, you probably shouldn’t get too bent out of shape over a few nanoseconds here or there.
S/PDIF Data Format
S/PDIF uses a serial signal clocked at 64x the audio sample rate. So, for 48 kHz audio, we need a serial signal at a whopping 3.072 megabits per second! However, the receiver doesn’t just need the bits, it also needs a clock. Since we only have one electrical or optical signal to work with, both the clock and data have to be recoverable from this one signal. S/PDIF does this using Biphase Mark encoding, which is a close relative of Manchester code. Because of this coding, we actually have to transmit on a clock rate which is 2x the bit rate. So, again for 48 kHz audio, we need a transmit clock of 6.144 MHz.
But wait, why 64x the sample rate? Even if we’re transmitting stereo audio at 24 bits per sample, that’s only 48 bits. Where do the other bits go?
Some of them are used by S/PDIF for synchronization purposes, and some are used for a low-frequency signaling channel which can transmit status words at a rate much slower than the audio sample rate, and some are effectively useless to us, reserved for obsolete or infrequently-used standards. The most primitive grouping of bits understood by S/PDIF is a 32-bit subframe which encodes one sample for one audio channel:
So many bits… what does it all mean???
The preamble identifies the type of subframe, as we’ll see below. It is the only part of the stream which is not biphase-mark encoded. It is the only place where we’ll see a run of three clock cycles with no bit transitions, so this allows the receiver to uniquely identify the preamble within the received bitstream.
Each subframe includes 24 bits of audio data, transmitted LSB-first and biphase-mark encoded. The low 4 bits of this stream may be used for other purposes, depending on which standard you’re reading.
The Valid bit indicates that this subframe contains valid sample data, and it is okay to output. In practice this bit isn’t really useful, since with the advent of Dolby compressed audio over S/PDIF, the receiver has a lot more work to do in order to determine if the data is valid uncompressed audio.
The User and Control bits are both part of a lower-bandwidth serial stream that we’ll see later.
And finally, each subframe has a Parity bit to help detect single-bit errors.
The User and Control bits on each channel collectively form four low-bandwidth serial channels, each running at a rate of one bit per sample. In S/PDIF, the User bit is unused, and the Control bits on each channel transmit a 192-bit Channel Status word. This word is fully transmitted once per block where a block is defined as a group of 192 frames beginning with a Z preamble.
In the professional AES3 protocol, there is a lot of data packed into this status word. But S/PDIF uses it for very little. In fact, only 13 of these bits are used at all, and in practice there isn’t really anything useful in this word. AES3 encodes an exact sample rate here, but in S/PDIF the only indication of sample rate is the clock recovered from the S/PDIF bitstream itself.
Biphase Mark Code
Wow, so far this looks pretty easy. Well, except for the high bit rate, and the picky timing. But what about this biphase mark code?
There are a few different ways to think of biphase mark encoding. If you’re familiar with Frequency Shift Keying (FSK) modulation, it might make sense to think of BMC as a particular form of FSK. A string of ones would be encoded as a square wave at a frequency equal to the original bit rate. A string of zeros would be a square wave at half that frequency. Put another way, you can think of BMC in terms of bit transitions. A zero bit is encoded by a transition followed by a non-transition, whereas a one bit turns into two transitions.
This demystifies BMC a bit… but why do it at all? Well, like any protocol which has to travel over some kind of analog physical media, very low-frequency signals (down to and including DC) can be troublesome. Let’s say we’re using an optical TOSLINK cable to transmit S/PDIF, and we have two theoretical bitstreams. One of them always transmits “one” bits, the other always transmits “zero” bits. The first bitstream means the transmitter’s LED is always on, and the second means the LED is always off. How does the receiver tell these two streams apart?
At first it seems obvious. The “one” stream is brighter than the “zero” stream. But actually, this might not be true. Maybe the first stream has a very dim light or a long cable. Maybe the second stream has a light leak around the receiver. It’s unreliable to rely on any absolute amount of light to discriminate ones from zeros, and in fact it’s not that hard to imagine situations where one system’s zero is brighter than another system’s one.
Similar problems exist in many kinds of analog transmission problems. Radio receivers, for example, need to deal with a very wide range of signal strengths. Unbalanced coaxial cables, such as S/PDIF over copper, can face similar problems. The receiver circuit in each of these cases needs to employ some kind of automatic gain control (AGC). AGC circuits track the average power level of the received signal, and “center” the one/zero discrimination threshold around this value. It’s a simplification, but AGC circuits can also be thought of as high-pass filters, since they subtract the unknown DC bias in the received signal.
Since DC signals are removed by the receiver, we can’t use them to carry any useful data. Those hypothetical all-zero or all-one bitstreams would be a disaster, since the receiver would continuously detect a signal level equal to the average. Any tiny amount of electrical noise would be detected as a one or zero.
This is where BMC helps. We can use a simple SciPy simulation to plot unencoded and encoded bitstreams in the frequency domain:
Now it’s easy to see that BMC is in fact shifting the signal up in the frequency domain. It needs twice the bandwidth now, but the center frequency is now near the bit rate, and we no longer have any signal at DC. Hooray, no more grumpy receiver AGC.
Typically if you were generating an S/PDIF signal, it would be sane to use an FPGA or an ASIC. In silicon. But this article is about breaking the mold and doing it in pure software. Why? For fun, and maybe also to lower the barrier to entry on digital audio. There are a few challenges to overcome, though:
Need to have enough CPU left over to generate the audio signal in the first place
Very high bit rate for a software implementation
Strict bit timing, at an unusual frequency
Biphase mark encoding is not parallelizable
My platform of choice for this project was the multi-core Parallax Propeller, since it’s simple and hobbyist-friendly yet it also has features which directly address these challenges. The XMOS XCore, another parallel microcontroller, would also be a fabulous choice. It may also be possible to implement S/PDIF on a sufficiently fast single-core microcontroller. Unfortunately, an 8-bit µC like the AVR used in the popular Arduino board probably wouldn’t be fast enough.
Edit: Actually, perhaps it would be doable on the Arduino after all… you would just need a less common crystal frequency. To generate an S/PDIF signal with a 32 KHz sample rate, for example, you could run the AVR at 16.384 MHz. The encoded bitstream clock would need to be 4.096 MHz (32k * 64 * 2), and you can program the AVR’s SPI master to transmit at up to half the main oscillator frequency. So you could run the AVR at 8.192 MHz or 16.384 MHz. At the latter frequency, you would have 32 instructions for every 8 bits of encoded bitstream data. That should be enough to do the encoding in an ISR and have a little time left over for applications…
A multi-core microcontroller makes challenge (1) a piece of cake. On the Propeller, one of the eight CPU cores can be dedicated to S/PDIF encoding. The other seven are available for application code, sound streaming or synthesis, and for other I/O devices.
Challenges (2) and (3) can be mitigated if we have a little bit of help from hardware. If we were using traditional bit-banging, and toggling I/O pins in code, a very fast processor would be needed. Even if the encoding and output could be done in four instructions per bit, just the S/PDIF encoding would require a little over 24 MIPS of processing power. Annoyingly, the CPU clock would have to be run at a multiple of the audio bit rate. You would have no way to use a separate clock. But if we had some hardware to shift out bits at the right time, the CPU can spend that time doing other tasks. Many microcontrollers have an SPI port that may be able to do the job. The XCore actually has special-purpose shift register hardware just to help with high-speed I/O tasks like this. And the Propeller has something close enough— a “video generator” that can be configured as a latch and shift register. The Propeller’s video generator can be clocked by a PLL that we program to synthesize the audio bitstream clock.
Challenge (4) means we need at least a small amount of code which runs serially for every bit in the audio bitstream. The problem is similar to calculating parity. Every input bit affects all subsequent output bits. In my implementation, I just use the fastest unrolled loop I can to perform the biphase mark encoding in two instructions per bit. Here’s an excerpt from the meatiest part of the BMC implementation:
' Load the preamble. The preamble is not biphase encoded,
' but it is subject to being inverted if the previous cell
' was a 1. This step is omitted for the second half (second
' 32 cells) of a subframe.
' In biphase encoding, every bit unconditionally begins with
' one transition. We can add these transitions too, in the same
' The masks below select all cells in the biphase register that are
' output after the bit we're currently encoding. Any time we
' XOR the biphase register with the mask, we're creating a
' transition on all future bits. The mask starts at the first
' odd numbered non-preamble bit.
xor biphase, preamble
' To actually biphase encode our input data, we'll insert
' additional transitions every time there's a 1 bit in our input.
' For the first half of the subframe, we're processing 12 bits
' of subframe data. (16, minus the 4-bit preamble)
' The loop is unrolled, since this is very speed-critical. At
' 48 KHz, we have less than three instructions per bit!
rcr subframe, #1 wc ' Extract the next LSB from the subframe
if_nc xor biphase, mask_4 ' Insert a transition only for '1' bits.
rcr subframe, #1 wc
if_nc xor biphase, mask_5
rcr subframe, #1 wc
if_nc xor biphase, mask_6
rcr subframe, #1 wc
if_nc xor biphase, mask_7
rcr subframe, #1 wc
if_nc xor biphase, mask_8
rcr subframe, #1 wc
if_nc xor biphase, mask_9
rcr subframe, #1 wc
if_nc xor biphase, mask_10
rcr subframe, #1 wc
if_nc xor biphase, mask_11
rcr subframe, #1 wc
if_nc xor biphase, mask_12
rcr subframe, #1 wc
if_nc xor biphase, mask_13
rcr subframe, #1 wc
if_nc xor biphase, mask_14
rcr subframe, #1 wc
if_nc xor biphase, mask_15
waitvid palette, biphase ' Output the first half of this subframe
' S/PDIF preambles. These are ordered LSB-first, ready for loading into
' 'biphase' before encoding the rest of a subframe.
' These are the preamble encodings that occur after a '0' bit. After a '1'
' bit, these preambles are inverted.
' All odd-numbered unused bits must be '1', so we can insert the fixed
' transitions in the same operation.
preamble_b long %010101010101010101010101_00010111
preamble_m long %010101010101010101010101_01000111
preamble_w long %010101010101010101010101_00100111
' For speed, we precalculate all XOR masks.
mask_0 long %11111111111111111111111111111110
mask_1 long %11111111111111111111111111111000
mask_2 long %11111111111111111111111111100000
mask_3 long %11111111111111111111111110000000
mask_4 long %11111111111111111111111000000000
mask_5 long %11111111111111111111100000000000
mask_6 long %11111111111111111110000000000000
mask_7 long %11111111111111111000000000000000
mask_8 long %11111111111111100000000000000000
mask_9 long %11111111111110000000000000000000
mask_10 long %11111111111000000000000000000000
mask_11 long %11111111100000000000000000000000
mask_12 long %11111110000000000000000000000000
mask_13 long %11111000000000000000000000000000
mask_14 long %11100000000000000000000000000000
mask_15 long %10000000000000000000000000000000
This code really does most of the work. The waitvid instruction waits until the video generator has buffer space available for another 32-bit word, but the video generator is actually clocking out data continuously, without any gaps. Since the biphase mark encoder’s output for one subframe is 64 bits, we split the subframe into two halves and process them each as above. The first half is special, though, since the preamble is not biphase mark coded.
Edit: I should mention that in most cases it probably makes more sense to use a 4-bit or 8-bit lookup table to do the BMC encoding. This approach seemed to make sense on the Propeller. However, for example, the S/PDIF library by XMOS uses a 4-bit table to do the conversion.
Using the SpdifOut Object
To use this SpdifOut object in your own Propeller project, you’ll need another cog to supply data to the S/PDIF cog. The object can receive sound samples one long at a time from hub memory, or you can set up a FIFO buffer for transferring data in more of a bursty fashion. In fact, transferring samples one-at-a-time is really the same thing as creating a one-entry FIFO buffer.
This is a complete example which plays uncompressed audio from an SD card:
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
SPDIF_PIN = 22
SD_CARD_PIN = 0
sd : &quot;fsrw&quot;
spdif : &quot;spdifOut&quot;
BUFFER_SIZE = 128 ' Must be a power of two
PUB main | f, c
spdif.setBuffer(@bufA, BUFFER_SIZE * 2)
' Wait until the driver is using bufB, then read bufA
repeat until spdif.getCount &amp;amp;amp;amp;amp; BUFFER_SIZE
sd.pread(@bufA, BUFFER_SIZE * 4)
' Now the opposite...
repeat while spdif.getCount &amp;amp;amp;amp;amp; BUFFER_SIZE
sd.pread(@bufB, BUFFER_SIZE * 4)
Since we read from the SD card in large blocks, this code uses a double-buffering scheme. While we’re reading one block from the SD card, the other block is being played by the spdifOut module. To represent these two buffers as a FIFO for spdifOut, we just place them consecutively in memory. We can tell which buffer spdifOut is currently playing by looking at the low bits of its played-sample count.
A note about the WAV header: Any modern S/PDIF receiver will actually mute the received audio for a fraction of a second, while it detects whether the bitstream is using Dolby Digital compression. Older receivers without this feature would be in danger of damaging the speakers or amplifier if anyone mistakenly sent them a compressed bitstream they couldn’t handle. Assuming your receiver has this feature, there’s nothing to worry about. If you do have a receiver which starts playing the very first sample you get, you’ll need to be much more careful about the initial conditions. For example, you won’t want to start the S/PDIF cog until the buffer has some valid data in it.
If you’re writing an audio synthesizer, instead of producing big blocks of data, you’re probably producing samples one-at-a-time. This is a very simple sawtooth-wave synthesizer written in assembly. It uses a single long as its buffer, just enough to hold one signed 16-bit sample for each of the two stereo channels. A little bit of Spin code controls the synthesizer cog’s frequency in order to play a short riff:
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
SPDIF_PIN = 22
BPM = 120 ' Tempo
ATTENUATION = 4 ' Power of two
SAMPLE_FP = $17C6F ' Sample rate, fixed point ($100000000 / 44100)
spdif : &quot;spdifOut&quot;
PUB main | songPtr
count_addr := spdif.getCountAddr
rate_addr := @rate
spdif.setBuffer(buffer_addr := @buffer, 1)
songPtr := @song
repeat while rate := WORD[songPtr] * SAMPLE_FP
songPtr += 2
waitcnt(cnt + clkfreq*60/BPM)
synth rdlong t1, count_addr ' Wait for the sample count to change
cmp t1, spdif_count wz
if_z jmp #synth
mov spdif_count, t1
' This is a sawtooth-wave synthesizer. &quot;rate&quot; determines the
' current tone frequency, and the high bits of &quot;accumulator&quot;
' are used to generate a signed 16-bit audio sample.
rdlong t1, rate_addr ' Load wave rate
add accumulator, t1 ' Update sawtooth wave
mov t1, accumulator ' Chop off low bits...
shr t1, #(16 + ATTENUATION) ' and decrease the output volume some
sub t1, midpoint ' Convert unsigned to signed samples
and t1, cFFFF ' Truncate to 16-bit signed
mov t2, t1
shl t1, #16 ' Copy right channel to left
or t2, t1
wrlong t2, buffer_addr ' Write the next sample now!
count_addr long 0
buffer_addr long 0
rate_addr long 0
spdif_count long 0
accumulator long 0
midpoint long $8000 &amp;amp;amp;amp;gt;&amp;amp;amp;amp;gt; ATTENUATION ' Offset to sample midpoint
cFFFF long $FFFF
t1 res 1
t2 res 1
song word 440, 330, 392, 294, 330, 392, 330, 392, 0
The full source code is available on the Object Exchange or in my repository. I enjoyed giving one of my favorite microcontrollers a new kind of output device, and I’m looking forward to seeing what others come up with for the Propeller as well as for other microcontrollers.
But what if you just want one weight sensor, not four? The balance board starts to look kind of pricey, and who wants to deal with Bluetooth if you don’t have to?
I’ve had a couple ideas for problems that could be solved by a cheap interface to the weight sensor in a common digital bathroom scale. The kind you can get for $15 or so at the local big-box store. For example, I’ve pondered using a grid of bathroom scales as an input device. You could use it to emulate a DDR pad, and there might be new applications that could benefit from analog weight readouts on each square of the pad.
The other idea is really Scott’s doing. He has a kegerator, and naturally we’ve been plotting to fill it full of all manner of sensors, actuators, and RFID readers. We’d like to have a continuous measurement of the keg’s weight, both to have some advance warning that it’s getting low, and to detect when an individual glass of beer has been dispensed. It seemed like if we could use the sensor from a digital bathroom scale, the filtered output would probably be accurate enough to detect the difference in weight from just one beer.
I’ve seen other projects to hack a digital scale. For example, this SD Card Bathroom Scale project uses a microcontroller to ‘read’ the LCD by decoding the signals used to drive it. This was a great idea, and he pulled it off quite well. But it requires a lot of wiring, and you’re still limited to the resolution and functionality of the original scale. I wanted to see if there was a simpler and lower-level way to hack a typical bathroom scale. And, for my particular scale at least, there is.
How a digital scale works
Old-skool analog scales (you know, with the needle that moved) were based on some clever gears and levers that converted pressure on the scale into compression of a big spring, then the spring’s compression into rotational motion that could drive a dial. But digital scales don’t really have any moving parts. They are mechanically designed to distribute your weight evenly to a bar or collection of bars which bend very very slightly under the pressure. Those bars are bonded to an electrical element that also flexes very very slightly, changing its electrical resistance. This is a strain gauge.
Inside the scale I hacked, you can see the two load-bearing bars. Each bar has two strain gauge sensors bonded to it. Measuring the difference in strain between the two sensors can tell you how much the bar is flexing.
There are a lot of notable parts here:
There are two buttons mounted on the scale’s feet. These are connected in parallel. If either one is pressed, the scale will power on.
The vertical bars on the left and right are load cells, the mechanical parts that are designed to bend slightly. Each one has a tiny PCB with two strain gauge sensors. The two sensors share one common wire, so there are three wires going to each load cell.
The clip in the bottom-center originally held a lithium coin cell battery.
And on the main PCB:
A numeric LCD module, bonded to the PCB.
A microcontroller or ASIC of unknown variety. This is the digital chip that runs the scale- it’s a chip-on-board module which is soldered to the back of the main PCB.
Analog front-end. This consists of an LMV321 quad op-amp, a few transistors, and a heaping spoonful of passives. This amplifies and conditions the signal from the strain gauge sensors. I’ll talk about this more below.
Power switching. The analog front-end is very low-power, but to preserve battery life even more, the microcontroller uses a transistor to turn it off entirely when the scale is sleeping.
Hidden features! There’s a “CAL” header. If you short this header, wake up the scale, then un-short it, the scale will go into a factory calibration mode. The LCD displays some kind of raw reading in this mode, and it continuously updates.
This is the only scale I’ve disassembled, so your mileage may vary. If your scale puts everything on a single IC, there may not be a way to hack it without building your own analog front-end. But if you get lucky (older scales may be more likely to use discrete parts) you may see something like this in the scale you disassemble. The separation of functionality into discrete analog and digital circuits makes this model of scale quite fun and easy to hack!
This section will explain the process I used to figure out how this scale works. You might be able to use some of the same procedures on other types of digital scales.
Visual inspection: Noticed the op-amp chip, arrangement of the passives, how the strain gauges were connected, power switching.
Looked up a data sheet for the op-amp chip. Noted the power and output pins.
Used an oscilloscope to probe each of the four op-amp outputs, looking for a signal that seemed plausible for a microcontroller to measure. This would have to be either a fairly high-amplitude DC analog voltage, or some kind of variable-frequency signal.
Found this signal. Output D on pin 14 had three different kinds of waveforms: low when the scale is off, high when the scale is on but not actively measuring weight (microcontroller is doing calculations or updating the LCD?). When weighing, this pin is a pulse train whose duty cycle seems proportional to weight.
Traced this signal back to a pin on the microcontroller.
At this point, it might have been sufficient to interface to the scale by snooping this signal. But the pulse train is not present all of the time, so I’d have to have extra code to ignore the idle periods. And you’d have to simulate button presses to keep the scale awake. So, I wanted to figure out how to keep the scale in measurement mode all the time.
First step: Keep the analog front-end powered on. I traced the V+ pin on the op-amp back to a transistor that the microcontroller uses to power on all analog circuitry. I soldered a shorting jumper across this transistor.
Now when the microcontroller is off, the op-amp output is high rather than low. This means there must be some other signal that the microcontroller is outputting in order to put the analog frontend into measurement mode, or otherwise generate that pulse train.
Probed around the microcontroller with my oscilloscope, looking for interesting signals on the pins nearby where the op-amp signal enters.
Found another pulse train which was active only when the scale was in weighing mode. This signal seemed to be at the same frequency as the op-amp output signal. This one is normally low, but has brief high pulses.
At this point, I thought I had enough information to guess how the circuit worked. Traced where this signal goes, to verify. It is an output from the microcontroller which heads to a transistor in the analog front-end. I didn’t fully reverse engineer this, but I suspect it’s discharging a capacitor that’s used for integrating the analog weight signal.
Made the modifications discribed below, and hooked the analog front-end input and output up to a Propeller microcontroller. Verified my guesses by performing some experiments with the microcontroller.
This documents my understanding of how the analog front-end works in this digital scale. I didn’t reverse engineer the circuit itself, so this is really just a “black box” description based on observing how the circuit behaves.
The circuit’s input is a tiny resistance from four strain gauges. They are arranged as two independent load cells, each with two sensors that act as a differential pair. The front-end amplifies these tiny differences in resistance, and sums them into a single analog signal. For example, if the strain gauges were labeled L1, L2, R1, and R2 (two sensors each for left and right load cells), the analog summation would be:
Total = L1 – L2 + R1 – R2
This total is still an analog signal, and it’s fairly noisy. There would be a lot of different ways to convert this to a digital signal: a standard successive-approximation A/D converter, a voltage to frequency converter, an RC oscillator… This scale uses an approach that’s simple, cheap, and pretty accurate for situations where you’d like to dynamically make tradeoffs between sample rate and accuracy. It uses an integrator and comparator.
In this approach, there is a capacitor which can charge or discharge depending on the state of a signal from the microcontroller. I’ve been calling this the “Reset” signal. When Reset is low, the capacitor slowly charges at a rate which depends on the analog Total above. This is an analog integrator. When Reset is high, the capacitor discharges quickly. This voltage across the capacitor is fed to a comparator, where it’s compared to a reference threshold. If it’s larger than this threshold, you get a logic “1” out, if it’s smaller you get a logic “0”.
This starts to look a lot like a closed-loop feedback system. In fact, you can even think of it as an electronic equivalent to the old mechanical balance. Instead of balancing two physical weights on a lever, you’re balancing two electric currents which are trying to charge or discharge the integration capacitor. If the capacitor voltage is too high, you need to drain it a bit. If it’s too low, you need to let if fill up. If you keep the capacitor voltage oscillating right around the comparator threshold, it’s just like you’ve balanced a mechanical scale. Now you know how heavy the object you’re weighing is, because you know how much time you need to spend discharging the capacitor to keep it balanced.
I find this symmetry between the mechanical and electrical scale pretty elegant. It’s also practical- your accuracy is limited by electrical or mechanical noise in the circuit, and by your microcontroller’s timing resolution. But you can always integrate over longer periods of time in order to trade sample rate for accuracy. If you keep the capacitor balanced for a whole second, and add up how much time you spent discharging the capacitor over that second, you’ve just calculated the average weight with much more precision than you’d have if you measured the length of a single discharge pulse.
Modding the Scale
After figuring out the analog front-end, I really just wanted a way to use that by itself without worrying about the built-in microcontroller or LCD. This turned out to be really easy:
Remove the lithium battery and holder
Solder a jumper wire across the transistor that normally powers off the analog front-end. (This is visible in the photo below, near the red and black power wires.)
Cut three traces near the microcontroller: Reset signal out, comparator signal in, and button signal in. This separates the microcontroller from the analog circuitry, and it also prevents the microcontroller from waking up. It will stay turned off.
Solder a 5-pin ribbon cable to the board: Ground, Power (+3v), Buttons (optional), Reset out, Comparator in.
The wires on the left are power and ground. It’s hard to see here, but the traces corresponding to the three wires on the right were cut, between the wires and the chip-on-board assembly. From top to bottom, those wires are comparator, button, and reset. And that’s it! With that simple mod, the scale is ready to connect directly to any 3.3v microcontroller with two free I/O pins. I used a Propeller, but an AVR should work great for this too. If you’re using a 5V microcontroller, you’ll still want a 3v or 3.3v power supply for the scale. You may also want to limit the voltage on the reset output signal too, though I suspect this isn’t actually necessary.
I tested this mod using a PropRPM board and some jumper wires. All of the other stuff on the PropRPM board is unrelated to this project- no external components are necessary to talk to the scale, just two 3.3v I/O pins.
The software is pretty simple. To measure weight, you need to toggle the “Reset” output on and off in order to keep the integration capacitor balanced around a comparator threshold. Pulling Reset high will quickly drain the capacitor, pulling it low lets the capacitor charge at a rate which depends on the total weight on the scale. You can take more accurate measurements by repeating this for more cycles, or take quicker measurements by repeating it for fewer cycles. On the Propeller, I use the system clock to measure the total amount of time that I spend letting the capacitor charge. Here’s a complete program that prints scale measurements to the serial port:
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
PIN_SCALE_OUTPUT = 3 ' Pulse high to discharge
PIN_SCALE_INPUT = 4 ' Output from integrator
term : "Parallax Serial Terminal"
PRI measure_raw(period) : raw_weight | timeref
raw_weight := 0
' Reset, and wait for input to go high. This is its idle state.
waitpne(0, constant(|< PIN_SCALE_INPUT), INA)
' Turn off the 'reset' output. This starts integrating
' the pressure. When the analog integrator reaches a threshold,
' our input pin goes high. Time this.
timeref := cnt
waitpeq(0, constant(|< PIN_SCALE_INPUT), INA)
raw_weight += cnt - timeref
And that’s all it takes! I’m looking forward to trying this with a larger grid of scales. A powerful microcontroller like the Propeller should be able to simultaneously sample and filter measurements from several scales. This proof-of-concept looks pretty promising for the kegerator project, too. With an integration time of a second or so, it can easily measure weight accurately enough to count glasses of beer 🙂
P.S. I’m pretty sure I just spent more time writing this blog entry than I spent on the hack itself. How silly is that?
The story behind this project is a bit overcomplicated. That’s all below, if you’re interested. I made a video to explain the final product:
This all started when I bought a sewing machine for making fabric RFID tags a little over a year ago, and started using it to make cute plushy objects. Well, as much as I like making plushy objects, I haven’t actually made very many of them since buying the machine. So, my boyfriend has been guilting me into making him a few as gifts for Christmas, birthday, valentine’s day. Naturally, being the great partner I am, this gets me thinking about ways to soup up my sewing machine first 🙂
The original machine did bug me in many ways when working on fine detail work. I’m lazy and uncoordinated with a needle, so I tend to sew almost everything by machine, even when the individual stitches all have to be lined up just right. In fact, when sewing the face parts on my meat and crab plushies, I found myself sewing individual stitches by turning the flywheel by hand.
I don’t know whether more expensive sewing machines are any better, but on my cheap Singer Inspiration 4210 the stock motor is nearly unusable at slow speeds. It stalls easily, makes an awful buzzing noise, maintains an uneven speed, and delivers very little torque.
My first experiments were to instrument the sewing machine with sensors that I could use to estimate the speed and angle of the main shaft. I needed some kind of absolute angle reference, plus some way of counting how far the shaft has traveled. For the angle reference, I found a really handy place to mount a photo-interrupter sensor. This cam is part of the mechanism that advances the machine’s feed dogs. It conveniently moves this metal bar back and forth once per revolution of the shaft.
For sensing the shaft speed accurately, my preferred solution would have been a high-resolution encoder wheel attached to the motor or the main shaft. But that would, at the very least, involve making a rotary encoder disc and attaching it somehow to one of the sewing machine’s moving parts. I wanted to avoid messing with the machine’s moving parts as much as possible, so I looked for non-contact solutions. I noticed that, with a little signal conditioning, an IR reflection sensor works pretty well for counting teeth on the machine’s drive belt. After switching motors, I modified this to sense teeth on the motor’s sprocket instead, but the same signal conditioning circuit works in either case. The signal conditioner is an dual op-amp configured as an amplifier, bandpass filter, and comparator with hysteresis.
Lastly, there’s the most important sensor: the foot pedal! I wanted to keep the original foot pedal feel, but use it as a generic input device for the microcontroller. So, I removed all electronic parts other than the variable resistor itself, and connected that to a 1/8″ jack. With a little modification, the jack fit into the same hole that was originally used for the mains cord.
Induction Motors Suck
This is where the project got a bit out of hand and I started making wrong turns. At first, I was hoping it would be possible to implement smooth closed-loop control by using the original motor and a simple triac-based speed controller for the motor. This seemed straightforward enough. But I didn’t have all of the parts on hand to do a normal optotriac + triac based driver circuit. So I went shopping at HSC. No optotriac, but there were some solid state relays. Alas, after I got them home and looked up the data sheets, it turns out they are the zero-crossing switching variety. Useless for speed control. So, back to the drawing board. No optotriacs on hand. Well, I could move the opto-isolation, putting the microcontroller on a line-powered circuit, and opto-isolating the serial port instead. But that sounded really annoying to debug safely, especially for me without an isolation transformer or variac. But that would require a low-voltage power supply on the mains side of the optoisolator anyway, and if I had to go to that trouble I might as well just use a transistor to switch on the triac…
So I started rummaging through my junk bins again, and I found an old X10 lamp module. This must have a triac in it, I thought. It won’t have an optoisolator, since X10’s cheap circuits never bother with mains isolation. But it might have a power supply and zero-crossing circuit I could use. So I find a handy LM465 schematic online, and I start paring down the circuit into just a bare-bones triac driver, zero crossing detector, and +15V power supply. I got pretty far along on this triac-based speed controller before I switched tactics:
I was experimenting with closed-loop servo control using this triac controller, but the results were pretty uninspiring. With a triac, you can only change speeds as fast as twice your AC power frequency. And a 120 Hz control loop just isn’t tight enough to keep the sewing machine operating smoothly at low speeds. The motor’s lousy torque at these speeds certainly doesn’t help.
I also had lots of trouble with EMI in this design. With all the parts strewn about my desk and electrically isolated but not RF shielded, my microcontroller would randomly reset after the motor ran for a few seconds or so. I first saw this with my trusty Propeller. I tried switching to an AVR Butterfly, half to try and avoid the EMI, half to have its LCD and joystick as a user interface. But, no luck.
I was curious how they solve this problem in industry. Servo motors are pretty common in industrial automation, and surely you must need a fancier way to control the speed of a large motor in order to build a heavy-duty servo. Well, it seems that there are two solutions:
Just use a DC motor instead of AC. This is more expensive, but it’s simple and until recently it was the only option.
Variable-frequency drive. You still use an AC motor, but now you use an AC-to-DC power supply plus a DC-to-AC inverter to control the frequency of the motor’s power supply. With an induction motor, this controls the speed.
VFD seems quite cool, and I was really close to attempting to build a simple VFD controller using parts from a dead UPS. But then my better senses caught up with me…
So, I was about to settle for this mediocrity and finish off my triac-based controller with some buttons and mounting hardware from HSC, when I randomly happened upon the perfect DC motor. I didn’t know it was perfect at the time, but I had a hunch. As soon as I got it home and sat it next to the original motor, I knew.
I was pretty lucky. The speed range was good, the sprocket had the same pitch, and it had plenty of torque at low speeds. The only problem: It’s just barely too large to fit inside the sewing machine cabinet. So, I had to improvise the mounting a bit. I ended up slicing off the bottom panel of the sewing machine, bolting its frame to some scrap pressboard, and mounting the motor using a hacky arrangement of pressboard scraps, screws, and zip ties.
I started out building a simple MOSFET speed controller for the motor, since I’d never used a MOSFET to control an inductive load before. But after spending an hour or two trying to squash transients and convince my FETs to switch at a high enough frequency, I figured I’d had enough pain for one week. I had a spare LMD18200 chip with this project’s name on it.
The LMD18200 is an amazingly versatile and robust little chip. I used it for my laser projector project a while back. It was really overkill for that, but it’s about right for this sewing machine’s beefy new motor. I needed a pretty heavy duty motor drive capacitor too, so I ended up mounting the LMD18200, capacitor, and DC-DC converter in a small metal box for safety reasons. If the capacitor fails, I’d prefer not to have burning hot electrolyte and shrapnel all over my face.
Enough rambling for now. And I guess I should stop hacking my sewing machine, and start sewing cute things 🙂
I did leave plenty of room in this build for later expansion. I’d like to add some kind of user interface for switching sewing modes. Right now I’m using the USB port for that, but a little LCD with a rotary encoder knob would be just swell. I’ve also thought of other ways to automate the sewing machine to make common tasks easier. For example, it would be useful to have a mode that automatically lifts the presser foot between stitches in single-stitch mode. I already found a good place to mount an RC servo motor for that.
And then there’s the idea of building a CNC embroidery machine around this, using Lego Mindstorms to make the cartesian robot…
It seems like a lot of people have been seeing my Flickr photostream and wondering what I must be up to- especially after my photos got linked on hackaday and reddit. Well, I’ve been meaning to write a detailed blog post explaining it all- but I keep running out of time. So I guess a “short” blog post is better than nothing.
The executive summary is that I’ve been working on making it possible to run homebrew games on the DSi. If you’ve used The Homebrew Channel on the Wii, you know what I’m talking about- we’d basically like to have the same thing for the DSi. And as it turns out, the DSi has a lot in common with the Wii.
I haven’t been working on this project alone. I’ve been collaborating with the excellent software and hardware engineers in Team Twiizers, the group responsible for The Homebrew Channel. Bushing has written a couple blog posts about our DSi efforts so far: one on the RAM tracing effort in general, and one with a sample trace.
My role in this project lately has been building hardware and software for a tool which is effectively a low-level CPU debugger for the DSi. It attaches to the RAM bus between the RAM and CPU, it’s capable of tracing all traffic in ‘real-time’ (after slowing down the system clock so the required bandwidth fits in USB 2.0), and just this week I got RAM injection working. The hardware interposes on one of the chip select signals between the CPU and RAM. If an address floats by that we want to patch, the hardware can quickly disable the real RAM chip and drive new data onto the bus as the CPU continues to read. It’s a very effective technique, and my hardware has a pretty capable system for storing and retrieving up to 16 kB of patch data now.
So why is all the complex hardware needed? Well, we hit two big dead-ends:
Several of us managed to run code from within a DSi-enhanced game using modified save files, but most of the interesting hardware on the DSi (like the SD card slot, and the internal flash memory) is disabled when these games are running. The earlier Team Twiizers Classic Word Games hack, WinterMute’s cookhack, and my own cookinject all suffer from this problem.
What we’d really like to do is understand more about how the console’s main menu works, and how software is installed and loaded on the DSi. The best way to do this seemed to be RAM tracing. It required an FPGA and a lot of crazy soldering under a microscope, but now it’s working and we’re well on our way to unlocking the secrets of DSi homebrew.
If you’re interested in following our efforts to make homebrew possible on the DSi, definitely subscribe to Bushing’s blog at hackmii.com. He understands the Wii (and by extension, the DSi) better than I ever will, and he’s great at writing about it.
I have a Flickr set with lots more photos, but these are two of my favorite. The first is a photo of the entire setup as it’s currently configured. The second is a picture of the craziest of the microscopic solder joints in this project. This is where I separated the chip-select signal I mentioned above, giving the FPGA the ability to sit between the CPU and RAM. The trace I’m tapping into is buried under four other PCB layers. For a sense of scale, the orange enameled wire is 32 AWG, or about 0.2mm in diameter. Normal 30 AWG wire-wrapping wire is actually only the third smallest size of wire I’ve been using in this project 🙂
Yesterday I spent some more time reverse engineering Robot Odyssey. This was a great game, and it’s kind of a nostalgic pleasure for me to read and figure out all of this old 16-bit assembly. So far I’ve reverse engineered nearly all of the drawing code, big chunks of the world file format, and most of the code that’s responsible for moving around objects on the screen.
So, I thought I’d try manipulating some of that data. I extended my existing binary patch to add even more code to the game’s per-frame blit function. It sets up the mouse using DOS Int 33h, and lets you manipulate the game’s table of sprite locations by dragging objects with the mouse.
I took a video to show the results. It’s still obviously a hack, but it may actually be kind of useful for assembling circuits more quickly.
The source code is available, but it’s a bit rough. This patch is getting kind of unwieldy in its current state, and I think my next step will be coming up with a better tool for doing these kinds of patches. I’m thinking either:
A pre/post-processor for NASM, which has binary patching oriented features.It would use signatures (byte mask + md5sum) to identify interesting code and data regions in the original binary, and allow you to replace or invoke snippets of code in the original binary using this mechanism. This should make it easier to do very invasive patches which can apply against slightly different versions of a game.
Or, scripting support for DOSBox.This could be a lot like the Lua support in FCEUX which made Super Mario Drag & Drop possible. As much as I like the idea of self-contained binary patches that operate in the same environment the game itself runs in, a Python plugin interface for DOSBox could be extremely powerful. Imagine a real-time level editor, or various kinds of real-time memory comparison tools…
Robot Odyssey is one of the games that I have the fondest childhood memories of. It’s both a high-quality educational game, and a gentle (but very challenging) introduction to digital logic.
There’s a Wikipedia article on the game. There’s also DroidQuest which is a Java-based clone of Robot Odyssey. The DroidQuest site also contains some good info on Robot Odyssey itself, including the only walkthrough I’ve ever seen.
So, I recently got inspired to try playing through Robot Odyssey again. As a kid, I never managed to beat the game. For a long time, it was nearly impossible to run it on a modern machine. It required a 5.25″ disk drive due to the ancient copy protection, it has CGA graphics, assumes you’re using an IBM XT keyboard, and all of the timing is based on the 4.77 MHZ CPU frequency of the original XT.
Thankfully there’s DOSBox, a really high quality emulator that can run old games like this quite faithfully. I started trying to play Robot Odyssey on DOSBox, but there were still two big problems:
Copy Protection.Robot Odyssey checks to make sure you’re running from the original 5.25″ disks, which have a “flaky bit” on them. If the flaky bit isn’t detected, the game will still load but your soldering iron doesn’t work!
Inconsistent speed.DOSBox is really good at slowing down the CPU, but this isn’t an exact science. Some things that were really really slow on the original XT (like writing to the CGA card) are fairly fast on DOSBox, and other things are comparatively too slow. This means you’re constantly futzing with the speed of DOSBox’s CPU emulation, depending on what level you’re in, how many robots are on the screen, etc.
So, I decided to solve these problems (and a few others) by binary patching the game itself. Since there are a bunch of user-tweakable knobs, I figured the best way to distribute this patch was as a Python script which patches the original binaries. You can grab the script from:
If you’re interested in the technical details of how the patch works, the source is pretty well commented. I won’t bore you with that here. This is a list of the patcher’s features:
Disables copy protection.This is necessary to run the game on any modern machine, even assuming you have the original disks.
Installs a frame rate limiter.Instead of adjusting the CPU speed, this is a real and fairly accurate frame rate limiter. You can specify a desired frame rate on the command line when applying the patch. By default it runs at 8 FPS, which feels about right based on memory. (I don’t have an IBM XT handy for checking what the speed is supposed to be…)
Halts the CPU when idle.When the frame rate limiter is sleeping, it yields the CPU. This will help a lot if you’re running the game in a multitasking environment or a virtual machine.
Compatibility with Windows XP’s built-in DOS emulation.You need the “-p” flag for this, and the frame rate limiter won’t be as precise- but the game will be playable just by double-clicking it in Windows!
“Fast” mode.This is an optional feature, enabled with the “-f” flag, which speeds up the game when keyboard input is waiting. This makes it feel a lot more responsive, and makes it faster to navigate around the level. You can also hold down any repeating key as a very simple “turbo” button.
Keyboard compatibility patch.Normally, Robot Odyssey is totally unplayable on any computer without a numeric keypad, including laptops, due to a bug in its keyboard handler. If you enable the “-k” flag, the patcher will rewrite the game’s keyboard mapper to be fully compatible with AT keyboards. This also removes the need to play with Caps Lock on.
Original Robot Odyssey binaries.Make sure the binaries you have aren’t already patched or cracked in any way. I won’t distribute these myself (so don’t ask!) but there are numerous abandonware sites on the web which should have this game. I’m not sure if multiple revisions of this game were produced. This patcher tries to be pretty lenient, but I’ve only tested it with one version. For reference, these are the SHA-1 hashes from my copy of Robot Odyssey:
Something that can run DOS games with CGA graphics! This could be a PC booted into DOS, a Windows machine, DosBOX…
Before you apply the patch, make backup copies of all your game binaries:
micah@carrot:~/robot$ mkdir original
micah@carrot:~/robot$ cp *.EXE original/
micah@carrot:~/robot$ ls original/
GAME.EXE LAB.EXE MENU2.EXE MENU.EXE PLAY.EXE SETUP.EXE TUT.EXE
Now apply the patch to each binary. Each section of the game (Robotropolis, the Innovation Lab, and the Tutorials) have their own separate EXE file, each of which has a separate copy of the game engine. You can use the same or different settings for each.
For example, to patch all binaries with default frame rate, and with the keyboard patch enabled:
micah@carrot:~/robot$ python robot_odyssey_patcher.py original/GAME.EXE GAME.EXE -k
Found copy protection. Disabling...
Found blitter loop. Patching...
Found keyboard mapper. Patching...
Saving comment at 0x1a4d0
micah@carrot:~/robot$ python robot_odyssey_patcher.py original/TUT.EXE TUT.EXE -k
Copy protection not found.
Found blitter loop. Patching...
Found keyboard mapper. Patching...
Saving comment at 0x11380
micah@carrot:~/robot$ python robot_odyssey_patcher.py original/LAB.EXE LAB.EXE -k
Found copy protection. Disabling...
Found blitter loop. Patching...
Found keyboard mapper. Patching...
Saving comment at 0x152a0
Now run PLAY.EXE in Windows, DOSBox, etc. You should see the game running at a steady 8 FPS, and the non-numpad arrow keys should work.
Experiment with the options! The “-h” option gives you a full list of the available setitngs. For example, if you want the game to run a bit faster, you might add “-f -r 10“. This will run the game at 10 FPS, and speed it up when there’s keyboard input. Remember to add “-p” if you’re running in the Windows DOS emulation.
This patcher may also work for games other than Robot Odyssey, which were based on the same engine. For example, Gertrude’s Secrets and Rocky’s Boots. You may have to leave off the “-k” option, since these games don’t necessarily use the same keyboard mapping as Robot Odyssey.
Last time, I posted an ultra-simple “from scratch” RFID reader, which uses no application-specific components: just a Propeller microcontroller and a few passive components. This time, I tried the opposite: building an RFID tag using no application-specific parts.
Well, my solution is full of dirty tricks, but the results aren’t half bad. I used an Atmel AVR microcontroller (the ATtiny85) and a coil. That’s it. You can optionally add a couple of capacitors to improve performance with some types of coils, but with this method it’s possible to build a working RFID tag just by soldering a small inductor to an AVR chip:
The above prototype emulates an EM4102-style tag- a very popular style of low-frequency RFID tag which stores a 40-bit unique ID. I can read my bogus ID value (0x12345678AB) using Parallax’s RFID reader. Below is another prototype, with a larger coil and a couple of capacitors for added range and stability. It is programmed to emulate a HID prox card, a simple FSK-modulated tag with a 44-bit payload. I can read this card successfully with my garage door opener. This one is a little large to conveniently carry around, but a smaller AVR package should help.
So, the shiny electrical tape is beautiful, but how does this thing even work? The power pins on the microcontroller aren’t even connected!
As I said, this makes use of several dirty tricks:
The coil actually powers the AVR through two of its I/O pins. Nearly every chip out there has clamping diodes on its I/O pins, which prevent voltages on that pin from rising above the chip’s supply voltage or sinking below ground. These diodes are useful for arresting static discharge.When you first hold the RFID tag up to a reader, the chip has no power- the supply voltage is zero. When the coil starts to pick up power from the RFID reader, these two I/O pins are presented with a sine wave, a few volts in amplitude. Anywhere that sine wave exceeds the supply voltage, some energy is diverted from the coil to the chip’s supply rails, via the clamping diode. The end result is that the chip is powered, and the coil’s sine wave is truncated. The top and bottom of the sine have been chopped off, and it looks a lot more like a square wave now.
Power filtering using the AVR’s die capacitance. In the smaller prototype, there is no power filtering capacitor at all. In fact, the power is filtered by the internal capacitance of the power planes in the AVR’s silicon die. This isn’t much, but it makes the power supply stable enough that we can execute code even though the supply is pulsing at 125 kHz.
Very low voltage operation. This particular ATtiny85 chip is specified for operation at voltages as low as 2.5v. The extended voltage range version (I didn’t have any of these handy) is specified down to 1.8v. But I’m running these AVRs at barely over 1 volt. At these voltages, the normal AVR clock oscillators don’t work- but I can get away with this because of the next hack…
The coil is the AVR’s clock source. The inductor isn’t just hooked up to any I/O pin: it’s actually connected to the AVR’s clock input. Remember the square-ish wave we’re left with after the clamping diodes suck away some power? That waveform is now our clock input. The microcontroller is executing code at 125 kHz, in lockstep with the RFID reader’s carrier wave.
Firmware? What firmware? At such low speeds, the chip’s firmware looks less like a program, and more like a sequence of I/O operations to perform in sync with each carrier clock cycle. There aren’t a lot of cycles to spare. In the EM4102 protocol, you could potentially do some useful work with the 32 clock cycles you have between each bit. With the HID protocol, though, you need to output an FSK edge as often as once every 4 clock cycles. As a result, the firmware on the RFID tag is extremely dumb. The “source code” is really just a set of fancy assembler macros which convert an RFID tag code into a long sequence of I/O instructions.
The fact that this thing works at all is quite a testament to the robust design of the AVR. The latest AVRFID source is in Subversion, as usual.
Atmel is going to personally revoke my electrical engineering license if they ever find out what’s in that yellow heat-shrink blob, but I now have a shiny new 5 gram version of my helicopter light kit =D
This version has all the same remote-control dimming and strobe capabilities of the heavier Revision A. The only practical drawback is that it isn’t quite as bright.
The above image is part of a histogram generated from Flightaware’s 24-hour time lapse. Click the link for the uncropped version. The original video is a Quicktime wrapper around individual PNG frames. I used mencoder to strip off the Quicktime wrapper, then a small Python script split the video streams into individual frames, decompressed them, and plotted a histogram of each red dot. The script generated a data file which was rendered using a patched version of Fyre 1.0 SVN.