| home | [ writing ] | bicycle repair | travel | software |
This page was last updated 1 August 2009.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Wouldn't it be cool to have a desk lamp that could change color, for example flashing red when you receive a mail? Or that pulls weather reports from the Internet, and translates the forecast temperature to a color? It could add some white flashes if thunderstorms are forecast. Or when your phone rings, it could flash green if it's a friend or red if it's your boss, based on caller ID. The possibilities are endless.
This page shows you to build a lamp that can change color, and can
be controlled by a computer. Simple Python and C programs for your PC
or Mac are provided that let you program color patterns into the lamp.
Implementing the weather forecast and other ideas are then up to you.
Web scraping with wget is easy if you understand a scripting language.
The whole project should cost under 100 euro, half of which is for
the lamp (glass ball, LED module, microcontroller, and odds and ends),
and the other half for the flash programmer if you don't have one.
There are enormously bright LEDs now, bright enough to be used as lamps. They also come with three emitters, red, green, and blue, on a single module, spaced closely enough that the colors mix. One such module is the Z-Power by Seoul Semiconductor. This page shows how to build a lamp using this module that can be programmed to show any color or cycle of colors, as instructed by a PC or Mac over a serial RS232 line. (Or USB, using a cheap USB-to-serial adapter - unfortunately USB cables cannot be longer than about two meters before they become either unreliable or very expensive.)
Building the lamp takes three steps:
The picture to the right shows the Z-Power LED assembly without heat sink. The mounting plate is about the size of a five-cent coin. I got mine at LEDs and more, but there are cheaper sources on the Internet. You should pay around 10 euro.
The circuit consists of the Z-Power LED module, which must be mounted on a heat sink because it can run hot. Each LED is driven by a 8550 transistor connected to an Atmel ATMEGA 8-16PU microcontroller. A MAX-232 chip converts the serial signals of the microcontroller to RS-232 voltages. The circuit runs on 5V; I use a cheap power supply used for external USB hard disk enclosures that can be bought for a few euro. Only use the ground and 5V wires! The 12V wire can destroy the circuit. In the power supply I used, the cable wires did not follow the usual black/red/yellow color scheme for ground/5V/12V, so always check very carefully with a voltmeter!
Those Atmel microcontrollers are amazing. In one small 28-pin DIP package it contains an 8-bit microcontroller running at 1 MHz (up to 8 MHz with a an external crystal), 20 KB Flash memory, RAM, programmer ROM, parallel and serial ports, timers - it's a small computer on a chip that requires no external chips at all. Its parallel output pins can drive normal LEDs directly, but high-power LEDs require a driver transistor. This is a view of the breadboard under the lamp, and the LED assembly on its heat sink inside the lamp. Click here for a larger picture of the breadboard. Note the five-pin connector at the bottom with the rightmost pin marked black (GND); this is the programming interface that the AVR USB programmer plugs into. More on that below.
|
|
Here is the circuit diagram. I used a breadboard with small copper rings around the holes, and thin magnet wire (0.07mm) to wire up the pins. Magnet wire is normally used for winding electromagnets; I like it because it's insulated with a thin yellowish coating that burns off when soldered to a pin. For the connection to the LED assembly I used thicker wires with a conventional green plastic sheath.
You should be sufficiently familiar with soldering and TTL chips to attempt this. Chips in DIP packaging have an indentation at one end; pin 1 is to the left of that mark. Pin count then runs counter-clockwise. I recommend machined sockets, especially for the programmer connector. Sockets with metal tabs tend to become unreliable after a while. For Americans: the rectangular box side of the capacitor symbol is positive, like the curved side of the US symbol. Do not get the polarity wrong; and yes, the top left capacitor really does have its negative side connected to VCC. The MAX-232 chip uses DC-DC converters to generate +/-12V internally.
The speaker shown in the circuit diagram is not currently supported by the firmware. You should omit it. If you do add one, use a piezo-electric buzzer; a small plastic box that contains the piezo disc and a small buzzer chip. It will start beeping when DC power is applied. Do not use a normal speaker here.
The microcontroller must be programmed with firmware that controls the LEDs and watches the serial interface. This is done in three steps:
The AVR programmer connects to the lamp microcontroller with a five-pin connector that is plugged into the lamp microcontroller board's programming connector. Make sure that you follow the pinout in the circuit diagram above: the AVR programmer's connector is color-coded so that gray = GND, brown = SCK, white = MISO, yellow = MOSI, and green = reset. Please check with the programmer's documentation in case they change the connector.
The AVR programmer can remain plugged in all the time during development. You can compile, download the hex file, and watch the firmware run on the lamp microcontroller without swapping cables. This makes for a fast development cycle. The download package contains a Makefile for Linux or the Mac (didn't try it on Windows) that will compile and download if you type make and make load.
The source code and control files are part of the download package at the end of this section. If you just want to take a look, here is the source code.
The code consists of several functions:
See below for some examples to use these to achieve effects. The whole notion of actions is based on the idea that once programmed by the PC or Mac, the lamp can be left to itself to execute complex color patterns without continuous intervention by the PC. A PC could not possibly keep up with the timing demands.
It also periodically calls the recalculate function when the interrupt handler reports a 64-interrupt cycle has completed and new colors are needed; this happens every 10ms.
You can download the firmware here, in gzipped tar format (can be unpacked with tar xzvf ledlamp-firmware.tgz on Linux and MacOS, or most unzippers on Windows with CR/LF expansion disabled):
The tar file includes the C source code, Makefile, and a hex file that can be directly downloaded to the lamp microcontroller. With the hex file, you only need the Tuxgraphics AVR programmer, but you don't need an ACR C compiler - provided you do not plan to modify the firmware. If you do, use make to recompile the source code, and make load to download the modified firmware into the lamp.
After turning on the lamp, it slowly cycles through six colors. But you can control it with your PC or Mac by connecting the serial interface to your PC or Mac. You need to connect the lamp's RxD input to an RS-232 sub-D connector: of the nine pins, you only need to connect pin 2 to the lamp's RxD, and pin 7 to the lamp's GND. You can also connect pin 3 to the lamp's TxD, but the back channel currently just echoes back what the lamp has received on RxD. This is useful for debugging; if you get an echo the lamp firmware is working. This 9-pin connector then goes into your PC's serial connector, or if it doesn't have one (like Macs), into a USB/serial adapter available for some 10 euro.
You can then connect to the lamp with a terminal program such as minicom. Set it to 9600 baud, one stop bit, no parity, no software flow control, no hardware flow control (CTS/DTS). On Linux, the first serial port has the name /dev/ttyS0; if you are using the USB/serial adapter it's /dev/ttyUSB0. Depending on your motherboard wiring, it might be /dev/ttyS1 instead of S0. The Windows names are COM0 or COM1. I don't know under what names the USB/serial adapters show up but I believe it's a COMn for some n in the range between 0 and 15. (Do not change the Atmega firmware to more than 9600 baud, that is not reliable.)
You can now send commands to the lamp. If you have connected the lamp's TxD line, the characters will be echoed to you; it you didn't, you may be typing blindly with no characters appearing on the screen. All commands are lines of text ending in a newline (CR, LF, or both). Blanks and tabs are ignored. All numbers must have an exact length (one, two, four, or six digits); all numbers are hexadecimal. In the descriptions below, the digit 0 is shown in places where a number (0..f) is expected.
Each command begins with the following characters:
No characters may follow the + and - commands. The = command must be followed by zero or more of the following subcontrols that closely follow the action fields described above:
These following table shows the default actions created when the lamp boots, and that cause it to begin cycling through the rainbow. Note how the patterns are staggered: the first uses bits 0 and 1, the next uses bits 2 and 3, and so on. They are all started at the same time with the same speeds, so they stay synchronized. Each action shows its foreground color for two ticks (two consecutive pattern bits are on), so the soft blending to the next tick will show the foreground color for a full tick, and not just reach it very briefly and immediately begin to fade to the next.
| action | command | comment |
|---|---|---|
| 0 | =0 f400000 b000000 p0003 l0a r00 s32 S A | program action 0 foreground color is full red background color is black bit pattern is --------** (red for first two ticks) length of bit pattern is decimal 10 repeat forever speed is decimal 50 * 20ms units, one pattern bit lasts one second blend softly between foreground and background color activate action now |
| 1 | =1 f004000 b000000 p000c l0a r00 s32 S A | program action 1 foreground color is full green see above bit pattern is ------**-- (green for next two ticks) see above see above see above see above see above |
| 2 | =2 f400030 b000000 p0030 l0a r00 s32 S A | program action 2 foreground color is magenta see above bit pattern is ----**---- (magenta for next two ticks) see above see above see above see above see above |
| 3 | =3 f401400 b000000 p00c0 l0a r00 s32 S A | program action 3 foreground color is yellow see above bit pattern is --**------ (yellow for next two ticks) see above see above see above see above see above |
| 4 | =4 f000040 b000000 p0300 l0a r00 s32 S A | program action 4 foreground color is full blue see above bit pattern is **-------- (blue for last two ticks) see above see above see above see above see above |
The commands are shown on multiple lines for clarity; if sent over the serial interface they must be all on one line, and they must be terminated with a newline. Here are some additional preprogrammed actions that are not enabled after booting (the final A is missing):
| action | command | comment |
|---|---|---|
| 5 | =5 f404040 b000500 p02aa l0b o0b r01 s02 H | program action 5: five fast white flashes foreground color is full white background color is dark green bit pattern is *-*-*-*-*- (alternate between white and dark green) length of bit pattern is decimal 11 override lower actions during entire white/dark green flashing run pattern only once speed is 2 * 20ms units, the entire action lasts 11*2*20 = 440ms hard switching between white and dark green, no blending |
| 6 | =6 f400000 b000000 p002a l20 o07 r2e s0a H | program action 6: red triple pulses for five minutes foreground color is full red background color is black bit pattern is ----------*-*-*- (three red pulses, otherwise black) length of bit pattern is decimal 32 (pattern controls only first 16) override lower actions during the red pulses only run pattern decimal 46 times speed is 10 * 20ms units, the entire action lasts 46*32*10*20ms = 5 minutes hard switching between red and black, no blending |
| 7 | =7 f000040 b400000 p5555 l10 o10 r00 s04 H | program action 7: pulse red/blue forever foreground color is full blue background color is full red bit pattern is *-*-*-*-*-*-*-*- (alternate red and blue) length of bit pattern is decimal 16 override lower actions permanently run pattern indefinitely speed is 4 * 20ms units, 1000/(2*4*20) = 6.25 pulses per second soft blending between red and blue |
| 8 | =8 f000000 b000040 p0055 l08 o02 rb9 s02 H | program action 8: blue flashing for one minute foreground color is black background color is full blue bit pattern is *-*-*-*- (alternate black and blue) length of bit pattern is 8 override lower actions for the first of every four flashes run pattern decimal 185 times speed is 2 * 20ms units, action runs for 185*8*2*20ms = 1 minute hard switching between blue and black, no blending |
| 9 | =9 f000000 b400000 p0055 l08 o08 rb9 s02 H | program action 9: red flashing for one minute foreground color is black background color is full red bit pattern is *-*-*-*- (alternate black and red) length of bit pattern is 8 override lower actions for the entire duration run pattern decimal 185 times speed is 2 * 20ms units, action runs for 185*8*2*20ms = 1 minute hard switching between red and black, no blending |
To activate one of the actions 5 through 9, send a command such as +5 followed by a newline. Similarly, to stop a running action, send a command such as -5. The actions a through f are not preprogrammed but can be programmed with a command beginning with =a or similar. All actions can be reprogrammed at any time.
Here is a simple Python script that starts action 5. It assumes that your lamp is attached to an USB port using an USB/serial adapter, and that the script runs on Linux. If this is not the case, please replace /dev/ttyUSB0 with the appropriate device name on your computer. Make sure that you have read and write permissions for the device node; you may have to chmod 666 it as root.
import termios
lamp = open("/dev/ttyUSB0", "w")
[iflag, oflag, cflag, lflag, ispeed, ospeed, cc] = termios.tcgetattr(lamp)
termios.tcsetattr(lamp, termios.TCSANOW,
[iflag, oflag, cflag, lflag, termios.B9600, termios.B9600, cc])
def tell_lamp(command):
lamp.write(command + "\n")
lamp.flush()
tell_lamp("+5")
|
Here is a program written in C that allows sending commands to the lamp from the command line, one command per argument word. Again, replace the device name with one that is appropriate for your system.
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
int fd;
void init()
{
fd = open("/dev/ttyUSB0", O_RDWR);
if (fd < 0) {
perror("/dev/ttyUSB0");
fprintf(stderr, "cannot connect to LED lamp\n");
return;
}
struct termios tc; // 9600 baud, 8n1, no flow control
tcgetattr(fd, &tc);
tc.c_iflag = IGNPAR;
tc.c_oflag = 0;
tc.c_cflag = CS8 | CREAD | CLOCAL;
tc.c_lflag = 0;
cfsetispeed(&tc, B9600);
cfsetospeed(&tc, B9600);
tcsetattr(fd, TCSANOW, &tc);
}
void send(
const char *command) // send this command
{
write(fd, command, strlen(command));
write(fd, "\n", 1);
}
void main(
int argc, // number of command-line options
const char **argv) // each argument word is one command
{
init();
for (int i=1; i < argc; i++)
send(argv[i]);
}
|
Enjoy! And please tell me about your experiences and modifications.
Michael McTernan has also built a similar lamp, and he has added an Ethernet interface too. Tuxgraphics now offers the necessary components for under 10 Euro.
| home | [ writing ] | bicycle repair | travel | software |