path: root/README.md
diff options
Diffstat (limited to 'README.md')
1 files changed, 140 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8f7b773
--- /dev/null
+++ b/README.md
@@ -0,0 +1,140 @@
+# Traffic light toy
+Children love playing with car and vehicle toys, and this simple toy-sized
+traffic light adds some extra fun.
+## Photos
+[![Green traffic light](images/traffic-light-green-small.jpeg)](images/traffic-light-green.jpeg) [![Red traffic light light](images/traffic-light-red-small.jpeg)](images/traffic-light-red.jpeg) [![Traffic light seen from the back](images/traffic-light-back-small.jpeg)](images/traffic-light-back.jpeg)
+## Hardware
+The initial version of this traffic light used three different LEDs: green,
+orange and red. However, finding LEDs with sufficiently discernible color
+difference between orange and red, and achieving uniform brightness across
+these LEDs by adjusting the serial resistors, proved to be very difficult. This
+first version, while functional, was not that great. The version presented
+here, uses 8mm Neopixel LEDs, allowing for easy adjustment of color and
+brightness through software, even if in practice the three LEDs consistently
+display the same color: red for the bottom one, orange for the middle one, and
+red for the top one. This change also simplifies the bill of material, as there
+are just 3 identical LEDs. This also simplifies the schematic, as they are
+daisy-chained, allowing the three of them to be controlled with a single MCU
+pin (well, in practice two as explained below) instead of three.
+The board was designed using [KiCad](https://www.kicad.org/) 6.0. The
+[schematic](images/schematic.png) is simple with very few components, as anyway
+space is quite constrained on the PCB. For this reason, and due to the 5 V
+power supply required by the Neopixels LEDs, I opted for an ATtiny9 MCU, which
+operates directly at 5 V. It has limited resources both in terms of RAM, Flash
+and pin counts, but suits the design well. It only has 6 pins, with two are
+allocated for the power supply, leaving 4 GPIO pins available. One is reserved
+for the reset signal of the programming interface, another is used by the push
+button, and one more controls the Neopixel LEDs. The last pin is used to
+provide power to the Neopixels LEDs, as unfortunately their idle current of 1
+mA would quickly drain the battery if left powered. Indeed there is no ON/OFF
+switch on this device (as kids will forget to switch it off), instead the MCU
+enters a power-down after some time, typically consuming less than a micro-amp.
+The push button is then used to wake-up the MCU when the traffic light needs to
+be switched on. To avoid exceeding the maximum current per GPIO pin of 40mA,
+the brightness of the LEDs and the number of simultaneously lit LEDs must be
+limited, but this is not an issue for a traffic light usage. Given the low pin
+count of the MCU, the Neopixel and the push button pins are multiplexed with
+the programming interface. In practice, it means the push button should not be
+used during programming, although it should only result in programming failure,
+as programmers should be protected against short circuits. The five pins
+required to program the MCU are accessible as pads on the PCB, allowing them to
+be temporarily soldered for programming purposes. Finally, to provide the 5 V
+required by the MCU and the LEDs, a TPS61222 boost converter is used. It
+operates down to 0.7V and has a quiescent current of only 5.5 ยต, providing a
+few months of usage on a pair of NiMH batteries.
+All the components from the [bill of material](BOM.txt) should be easily
+available from many distributors. Soldering the components onto the PCB should
+be relatively straightforward for someone used to SMD components. As seen on
+the photos, the battery holder is used as a stand for the traffic light,
+ensuring its stability, and the PCB is attached through an angle bracket and a
+pair of M3 screws.
+## Software
+The software for the MCU is quite simple, mostly written in C (with the
+exception of the timing-critical routine for controlling Neopixel LEDs) and
+organized around a single source file. The entire code occupies a bit less than
+two-thirds of the 1-kilobyte flash memory. The RAM is not used as everything
+fits in the CPU registers, thanks to the 32 registers of the AVR CPU.
+The main part of the code consists mostly in a state machine, defined by the
+`states_desc` table. It describes all possible states of a traffic light. For
+each state, the brightness of each of the 3 RGB LEDs inside each of the 3
+Neopixel LEDs is defined. Additionally, for each state, the following are
+- Duration of the state
+- Next state at the end of the duration
+- Next state in case of a short button press
+- Next state in case of a medium button press
+- Next state in case of a long button press
+Button presses shorter than 10 ms are ignored for debouncing purposes, and
+longer ones are categorized as follows:
+- Short: >= 10 ms and < 500 ms
+- Medium: >= 500 ms and < 1500 ms
+- Long: > 1500 ms
+By default, the state machine is configured to execute the standard traffic
+light sequence observed in France and many other countries: green, orange, red,
+and back to green. The code includes comments to support the use of orange and
+red together between the red and green states, as seen in some countries. A
+short button push advances to the next state without waiting for the full
+duration. A medium push switches the traffic light to blinking orange, and
+another medium push restores the normal sequence. Finally, a long push turns
+off the traffic light, using the special state `state_off`, which disables the
+power supply pin of the LEDs, enables interrupt on the button pin, and puts the
+MCU in a power-down state.
+The signal to control the Neopixel LEDs is generated through bit-banging, using
+an inline assembly-based routine. As [nicely explained by Josh
+the timing is not as critical as one might expect from reading the datasheet,
+only certain aspects of the timing are crucial. This trick enables the use of
+the internal RC oscillator running at only 8 MHz.
+A makefile is provided to compile the code using [AVR
+GCC](https://gcc.gnu.org/wiki/avr-gcc) and [AVR
+ $ make
+ avr-gcc -MMD -g -Wall -Wextra -Os -fshort-enums -mmcu=attiny9 -c -o main.o main.c
+ avr-gcc -MMD -g -Wall -Wextra -Os -fshort-enums -mmcu=attiny9 -Wl,-Map,traffic-light.map,--relax -fwhole-program -o traffic-light.elf main.o
+ avr-objdump -h -S traffic-light.elf > traffic-light.lst
+ avr-objcopy -j .text -j .data -j .rodata -O ihex traffic-light.elf traffic-light.hex
+ AVR Memory Usage
+ ----------------
+ Device: attiny9
+ Program: 594 bytes (58.0% Full)
+ (.text + .data + .bootloader)
+ Data: 0 bytes (0.0% Full)
+ (.data + .bss + .noinit)
+To program the device using an AVRISP MKII programmer and the
+[avrdude](https://github.com/avrdudes/avrdude) software, simply execute the
+`make flash` command. Admittedly, the makefile has only been tested on Debian
+GNU/Linux, so it might require some adjustments, especially when using a
+different programmer.
+## License
+The contents of this repository, with the exception the software, is released
+under the [Creative Commons Attribution-ShareAlike 4.0 International License
+(CC BY-SA 4.0)](LICENSE). The software is released under the [terms of the GNU
+GPL, version 2](software/COPYING).