Showing posts with label blinkylights. Show all posts
Showing posts with label blinkylights. Show all posts

20130206

DIY TinyMarquee: an Attiny24 based scrolling marquee

This project uses charlieplexing and software serial to push the capabilities of the AtTiny24. The source code and design files are up on a github repository.

Other DIY LED marquee projects may be of interest, including a large one hand-routed on protoboard, one made from Christmas lights, a network enabled display, one using ping-pong balls, and one implemented in the rear window of a car.



A terminal stock ticker

The python module ystockquote pulls stock prices and other information from Yahoo finance, "get_price(stockname)". Here is a short python script that downloads and formats stock prices and writes them to a local file.

Bitmap fonts 

We need to translate text into a format that we can send to a scrolling marquee. We send raw pixels, so that we can adjust fonts and graphics without re-loading the firmware. Columns of pixel data are sent as 5-bit integers over serial. I used Gimp to draw a font and this Jython script to convert it into bit-packed integers representing columns of pixels for each letter.*


To test converting text to the bitmap font, the script "scroll" takes the scraped data and scrolls it across a simulated marquee in the terminal ( there is also the short "terminal_marquee" script which scrolls indefinitely, but updates intermittently in the background )

Hardware

The hardware consists of 90 3mm LEDs arranged in a 5x18 grid. These are driven by 10 IO lines of an AtTiny24. The remaining free IO line is used to poll and listen for serial data.

There is also a 10K pull-up resistor on the AtTiny's reset pin, and a 0.1μF decoupling capacitor near the power pins. The surface mount AtTiny bridges one row of the LED pins, a bit unusual but this allows a compact layout with through-hole LEDs. The marquee gets power and data from a USB to TTL serial adapter.


Charlieplexing

Charlieplexing is a way to drive tons of LEDs form only a few pins. Since LEDs only light up when current is passed in one direction, you can place two LEDs for every unique pair of IO lines at your disposal. This lets you drive N*(N-1) LEDs from N IO pins.**

PCB design

While it is possible to wire up the grid of LEDs by hand, I would not recommend it. Instead of going through this tedium, you can design a custom board and get it professionally fabricated. I use the free version of Eagle Cad to design and prepare boards for manufacture. A full Eagle tutorial is beyond the scope of this writeup, but numerous tutorials can be found elsewhere online. The Eagle design files for this project can be found here.




Exporting gerber files for board fabrication

One you have finalized a board, you need to prepare design files for fabrication. PCB designs get exported to so called "Gerber" files, which are like the PDFs of circuit board design. Once you have these files you can send them off to a fab house for production. My favorite tutorial for this is on Hackaday.

For one-off boards, BatchPCB is the go-to place. For small runs, consider Advanced Circuits or Seeed studio's Fusion PCB service. For larger runs (more than 30), depending on board size, Goldphoenix is the place to go. Depending on which service you choose, you should get boards in a few days to five weeks. I used Seeed because it is relatively cheap, and it took about a month to ship to the US.

Part sourcing 

For cheap LEDs I use Ebay. For all other components ( especially the AVR microcontollers ), I source from Mouser or Digikey. For a low cost USB to Serial adaptor, look for "USB To RS232 TTL PL2303HX" on Ebay . These are cheaper than, say, an FTDI cable from Sparkfun, and have worked great for me. I'd hoped to save a few bucks by using charlieplexing and the AtTiny14 in the design -- the total cost of each board, shipping and USB-TTL converter included, from a lot of 10, is about $7.50:
10   boards     $30   
1000 LEDs       $10   
10   AtTiny     $14   
50   Resistors  $ 1   
10   Capacitors $ 1   
10   Headers    $ 1   
10   USB-TTL    $18   
-------------------   
                $75   
       /10      $ 7.50

Cleaning the boards 

After you're finished soldering, remove any solder flux adhered to the board. Apart from being unsightly, flux can be conductive and corrosive and damage the board over time. Hackaday has a good tutorial on this. We filled an old jar with 90% Isopropanol, dunked the boards in there, and shook them around for a while -- it worked wonderfully.

Software serial 

The Attiny24 does not have hardware support for serial ( UART ), so we'll have to make a software implementation. For information about the RS-232 communication protocol, see the wiki. I used polling at twice the serial rate (4800Hz) to monitor incoming serial data, which is works for transmitting five bit packets. Further details about the firmware can be found in the C source file.

Compiling with avr-gcc 

I never remember the commands to compile and upload firmware, so here are the commands for future reference.

#compile source to a file a.o targeting the AtTiny24
avr-gcc -Os -mmcu=attiny24 ./display_serial.c -o a.o 

#extract the text and data sections to make a binary for the AVR
avr-objcopy -j .text -j .data -O binary a.o a.bin 

# check the size ( this should be smaller than the amount of available flash )
du -b ./a.bin

# upload the binary to ( in this case ) the AtTiny24 
avrdude -c avrispmkII -p t24 -B4 -P /dev/ttyUSB1 -U flash:w:a.bin 


_____________________________

*If you don't have Jython or a working JVM installed, it may be easier for you to re-enter the font as text data and write a short conversion routine in python.

**If you're familiar with multiplexing there's a simple way to conceptualize board layout for charlieplexing. When you multiplex an NxN grid of LEDs, you use N IO lines to control power to the (-) ends of the LEDs, and N IO lines to control power to the (+) ends of the LEDs.  To go from multiplexing to charlieplexing, note that microcontroller pins can take on three sates : Low (-), High (+), and Off ("high impedence"). Each IO line can serve as both a (+) line and a (-) line. What happens if we use the same N pins to drive both the (-) and the (+) a multiplexed display? Everything works fine, as long as we use the "off" state to stop current. One problem: there some LEDs along the diagonal of the matrix that have their (+) and (-) driven by the same IO pin -- there is no way to make these light up. But, no worries, since we are laying out our own board, we can just delete these LEDs, or connect their (+) terminals to a reserved IO pin to regain control of them! Charlieplexed PCB design can be relatively simple: lay out a grid of LEDs as if you were to multiplex them, but connect the N anodes to the N cathodes, and either delete the N LEDs that end up being connected to the same IO line at both ends, or wire these up to a separate IO pin to finish things off.


20100902

Drop Day Octahedron

I was digging through the archives and I found some documentation for the CCFL Octahedron project from Drop Day that was never published. We don't have photos of the build process, just a text description and the source code, as well as pictures of the finished project in action. This should be good for inspiration and perhaps source code for a similar project, if not total duplication.

Physical construction

Each edge of the octahedron structure is a 2-foot piece of wooden dowel rod. A hole is drilled perpendicular to each rod at each end. The rods at each vertex are held together with a double loop of fishing wire through these holes. Rods and wire are spraypainted black.

The lights are cold cathode flourescent bulbs, as sold for lighting computer cases. A pair of bulbs are attached to the outside of each edge with cable ties. Each pair is powered through an inverter attached to the inside of the edge. I've left some spare bulbs in the elevator closet in alley 3. A pair of bulbs together with inverter can be purchased online for about $8.

A power cable runs from each inverter to a common vertex where they are bundled together and exit through one side. Each of the twelve cables is dual-conductor 18-gauge (?) speaker wire. One end terminates in a connector accepted by the inverter; the other is permanently soldered to the control board.

The control board is powered through a long three-conductor ribbon cable terminating in a Molex 8981 male connector, which will interface with a standard ATX computer power supply. (To make an ATX power supply turn on without a motherboard present, one must connect the ~PS_ON signal on the motherboard connector, normally green, to ground.)


Electronics

The control board is built around an ATtiny2313 processor, two CD4094BC 8-bit shift registers, and three ULN2068 quad-channel Darlington switch arrays. The processor is clocked by an external 16 MHz crystal. The fuse extended and high bytes are left at factory default. The fuse low byte is programmed to 0xFF: no prescalar, no clock output, external crystal, slow start-up. The shift registers are cascaded to act as a single 16-bit serial-in, parallel-out register. The processor has three outputs to the shift register: Data (pin PB3), Clock (pin PB2), and Latch (pin PB4). A single bit is loaded by setting Data high or low, setting Clock high for 4 processor cycles (250 ns), then bringing Clock low again. This is repeated 16 times, once per bit of a 16-bit word, with the least-significant bit first. After all 16 bits are loaded, the Latch pin is strobed high for 4 cycles, at which point the shift registers will present the 16 bits on their output pins.

12 of these 16 bits are wired to inputs of the Darlington switch arrays. When a switch-array input is at logic high, the corresponding output will sink current to ground, illuminating the corresponding lights. The unused bits of the shift register are (from LSB = 0) bits 6, 7, B, and F.

The power supply to the control board consists of ground, +5 V, and +12 V. The 12 V supply is wired directly to the positive side of each inverter's DC input. The negative side of each inverter's input sinks through a Darlington switch. When all bulbs are on, the system will draw about 5 amps on the 12 V line. It has an inline fuse holder loaded with a 10 amp fuse. The 5 V line is used for powering the logic chips only and should draw minimal current.

.. you see how much effort I'm putting here : screen shot of the monospace text figure

hdr2 is used to program the processor via a standard AVR programmer, such as the AVR ISP mkII. From top to bottom as oriented above, the pins are:

MOSI, MISO, SCK, VCC, ~RESET, GND

hdr1 is currently unused but is connected to the circuit. From top to bottom:

VCC, GND, not connected, PD4, PD5

This could be used to communicate with another board via a 2-wire serial protocol.

The push buttons will short PB5 or PB6 to ground when pressed. The switch will short PD6. Neither is used by the current software.


Software

See code at the end of this post. The software implements a few modes and switches between them. It includes a very simplistic random number generator (which is not random at all because the seed is hard-coded).


Bugs and quirks

Sometimes, especially when starting, the system will go into a bad state such as freezing or strobing synchronously in an unappealing fashion. I seem to have fixed this by adding another filtering capacitor on the 12 V rail, but the problem may return.

Sometimes after switching off the ATX power supply it will not turn back on for a number of minutes. Perhaps a large resistor between power and ground would fix this problem by acting as a stabilization load.

Many aspects of the circuit, such as the use of shift registers, an external crystal, and extra buttons and switches, are historical or arbitrary. Where the speaker wire meets the board it can short against unintended pins of the ULN2068 in ways that are not visually obvious. This can result in all four channels on that chip turning on when only one is activated in software.

You can pulse-width modulate a light channel in software, but it will not dim the light. Instead, it will light up part of the tube from one end, which looks cool and is probably very bad for the bulb.


Replacing the control board

The control board is a crufty prototype and it may be desired to replace it with something nicer: either a better-produced custom board or a totally off-the-shelf controller. Essentially all that is required is to switch on and off with some reasonable speed the sinking of ~ 500 mA current on each of 12 channels. This could be accomplished with any number of devices: Darlington pairs, power FETs, solid-state relays, etc. Optical isolation of the control signal may improve robustness. Many microcontrollers can provide the 12 I/O pins directly, so external shift registers are unnecessary.


Software


Here is the source code associated with the Octahedron. First, build-and-upload.sh, the bash commands used to compile and upload the program to the AVR.


#!/bin/sh -e


# build with avr-gcc suite
avr-g++ -o octahedron.bin -mmcu=attiny2313 -Wall -Winline -save-temps -fverbose-asm -Os octahedron.cpp
avr-size octahedron.bin
avr-objcopy -j .text -j .data -O ihex octahedron.bin octahedron.hex

# upload using AVR ISP mkII
avrdude -p t2313 -P usb -c avrispmkII -U flash:w:octahedron.hex


This is the straight up AVR C++. You'll need an in system programmer to upload it.



#define F_CPU 16000000L

#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

typedef uint8_t byte;

// a shitty "random" number generator
uint16_t xrand() __attribute__ ((noinline));
uint16_t xrand() {
static uint16_t y = 3;
y ^= (y << 13);
y ^= (y >> 9);
return y ^= (y << 7);
}

// delay by an amount known at runtime
void delay_ms(uint16_t ms) {
while (ms-- > 0) _delay_ms(1);
}


// Define pins for the shift registers
#define SREG_PORT PORTB
#define SREG_DDR DDRB
#define SREG_LCH (1 << 4) // latch, high to set
#define SREG_DAT (1 << 3) // data
#define SREG_CLK (1 << 2) // clock on rising edge

// Define pins for the buttons and switch
#define UI_PORT PORTB
#define UI_DDR DDRB
#define UI_PIN PINB
#define UI_BTN_A (1 << 6)
#define UI_BTN_B (1 << 5)

#define UI_BTN_A_DOWN (!(UI_PIN & UI_BTN_A))
#define UI_BTN_B_DOWN (!(UI_PIN & UI_BTN_B))

#define UI_SW_PORT PORTD
#define UI_SW_DDR DDRD
#define UI_SW_PIN PIND
#define UI_SW (1 << 6)
#define UI_SW_EN (DARK_PIN & DARK_SW)

// Init the shift registers
inline void sreg_init() {
SREG_DDR |= (SREG_LCH | SREG_DAT | SREG_CLK);
}

// Optional delay when setting pins for the
// shift registers.
// At 5V they will handle at least 12 MHz.
inline void sreg_delay() {
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
asm volatile ("nop");
}

// Shift a bit into the shift registers.
void sreg_shift_bit(bool x) {
if (x)
SREG_PORT |= SREG_DAT;
else
SREG_PORT &= ~SREG_DAT;

sreg_delay();
SREG_PORT |= SREG_CLK;
sreg_delay();
SREG_PORT &= ~SREG_CLK;
}

// A full shift-register write.
// Shift 16 bits, then latch.
void sreg_write(uint16_t x) {
for (byte i=0; i<16; i++) {
sreg_shift_bit(x & 1);
x >>= 1;
}

sreg_delay();
SREG_PORT |= SREG_LCH;
sreg_delay();
SREG_PORT &= ~SREG_LCH;
}

// The bits corresponding to wired-up channels are:
// FEDC BA98 7654 3210
// XXX XXX XX XXXX
// This function maps 12 contig. bits onto these.
uint16_t lights_to_channels(uint16_t x) {
return (x & 0x003F) // 0000 0011 1111
| ((x & 0x01C0) << 2) // 0001 1100 0000
| ((x & 0x0E00) << 3); // 1110 0000 0000
}

// Set which lights are on according to a 12-bit value.
uint16_t lights = 0;
void set_lights(uint16_t new_lights) {
static uint16_t state = 0;
lights = new_lights;
uint16_t new_state = lights_to_channels(lights);
uint16_t mask = 0;

//if (new_state == state) return;

for (byte i=0; i<16; i++) {
mask = (mask << 1) | 1;
sreg_write((state & (~mask)) | (new_state & mask));
_delay_us(500);
}

state = new_state;
}


/*
inline void ui_init() {
// pull up both buttons and the switch
UI_DDR &= ~(UI_BTN_A | UI_BTN_B);
UI_PORT |= (UI_BTN_A | UI_BTN_B);

UI_SW_DDR &= ~UI_SW;
UI_SW_PORT |= UI_SW;
}

inline void ui_debounce() {
_delay_ms(50);
}
*/

#define L_R1 0x400
#define L_G1 0x002
#define L_B1 0x004

#define L_R2 0x008
#define L_G2 0x040
#define L_B2 0x010

#define L_R3 0x100
#define L_G3 0x020
#define L_B3 0x080

#define L_R4 0x001
#define L_G4 0x200
#define L_B4 0x800

/// RGBRBGGBRGRB
/// BRGR BGGB RBGR
/// 0101 0000 1001 = 0x509
/// 0010 0110 0010 = 0x262
/// 1000 1001 0100 = 0x894

#define ALL_R (L_R1 | L_R2 | L_R3 | L_R4)
#define ALL_G (L_G1 | L_G2 | L_G3 | L_G4)
#define ALL_B (L_B1 | L_B2 | L_B3 | L_B4)

#define ALL_LIGHTS (ALL_R | ALL_B | ALL_G)

const uint16_t tris[8] PROGMEM = {
L_R2 | L_G2 | L_B2,
L_R1 | L_G3 | L_B2,
L_R3 | L_G3 | L_B3,
L_R4 | L_G2 | L_B3,
L_R3 | L_G1 | L_B4,
L_R4 | L_G4 | L_B4,
L_R2 | L_G4 | L_B1,
L_R1 | L_G1 | L_B1
};

const uint16_t squares[3] PROGMEM = {
ALL_R, ALL_G, ALL_B
};

/// modes

uint16_t rand_mask() {
return (1 << (xrand() % 12));
}

void noise_fast() {
uint16_t len = 300 + (xrand() % 300);
for (uint16_t t = 0; t < len; t++) {
set_lights(lights ^ rand_mask());
_delay_ms(60);
}
}

void fade_down() {
while (lights != 0) {
set_lights(lights & (~rand_mask()));
_delay_ms(1);
}
}

void fade_up() {
while (lights != ALL_LIGHTS) {
set_lights(lights | rand_mask());
_delay_ms(1);
}
}

void updown_fast() {
uint16_t len = 20 + (xrand() % 20);
for (uint16_t t = 0; t < len; t++) {
fade_up();
fade_down();
}
}

void rgb() {
uint16_t len = 150 + (xrand() % 100);
for (uint16_t t = 0; t < len; t++) {
for (byte i=0; i<3; i++) {
set_lights(pgm_read_dword(&squares[i]));
_delay_ms(120);
}
}
}

void tri_rand() {
uint16_t len = 300 + (xrand() % 700);
for (uint16_t t = 0; t < len; t++) {
set_lights(pgm_read_dword(&(tris[xrand() % 8])));
_delay_ms(60);
}
}

/* causes reset?
void tri_strobe() {
uint16_t len = 100 + (xrand() % 300);
for (uint16_t t = 0; t < len; t++) {
set_lights(pgm_read_dword(&(tris[xrand() % 8])));
_delay_ms(30);
set_lights(0);
delay_ms(xrand() % 200);
}
}
*/

/*
void test() {
for (byte i=0; i<12; i++) {
set_lights(1 << i);
delay_ms(1000);
}
set_lights(0);
delay_ms(5000);
}*/

int main() {

sreg_init();
//ui_init();

set_lights(0);

for (;;) {
rgb();
updown_fast();
rgb();
noise_fast();
rgb();
tri_rand();
}

}




20100228

Drop-Day 2010 Tech

I figured I'd start writing this up on the return flight from Drop Day, so I'm typing here at an odd, cramped, angle from my flight back from LA to Pittsburgh.
Drop-Day was a fine production, a victory for both hobbyist physical computing, and the forces of democratic freedom. I was impressed with the stark giant white cube dance floor with the, as Biff describes, lovecraftian monolith as a centerpiece. Something about the smaller size and the fog machine made people actually want to dance this year. The sensory rooms were excellent, although some of the code running the party never got off the ground. There is something uniquely appealing about a party that crashes, and requires rewriting of computer code and recompiliation on the fly. In addition to projected visuals ( Kanada, Perceptron, Cortex, Live and recorded video feeds, and other trippy renderings ), we had a few Alumni constructed blinkylights. Keegan completed a most excellent glowing octahedron, Suresh completed a rather nice modification of a commercial lamp, and I constructed several more pairs of goggles. I have spent most of my time travelling ( and very little sleeping ) this weekend, and it was well worth it. However, I doubt I'll be traveling back any time in the next five years. Others travel from much further away (London, Fairbanks) to go to this party, which should give you an idea of how important this party is to Dabney alumni.
RGB controlled diffuse illumination lamp :
Suresh successfully modified a modern style diffuse diffuse illumination lamp for controllable RGB color. He even designed and ordered a custom multi-layer board for the thing. I will try to track him down and see if designs and photographs are available anywhere.
CCFL octahedron :
This project was a wire-frame octahedron, approximately two feet on each edge. An octahedron can be viewed as 3 intersecting squares, once for each of the x, y, z, axes. In this design, each axis was assigned a specific color. The octahedron was constructed using two standard cold cathode fluorescent lighting tubes per edge, driven by black-box driving hardware that is powered by 12V DC. 12V is switched to the various edge drivers using darlington arrays controlled by an AtTiny2123(?), with 12V pulled from a modified desktop computer power supply. The skeleton of the octahedron itself was build by cutting wooden dowels to size, drilling a hole through each end, and joining the ends with zip-ties. The lights and driving hardware was also secured to the skeleton via zip ties. A great effect of the hue rotation on tie-dye style patterns is to cause the location of edges to appear to shift as the color changes and alternatively illuminates different parts of the pattern.
Revised goggles :
The goggles you see in these photographs still use the same old LEDs in ping-pong ball design, stripped down and controlled by an AtTiny13a. I would not recommend this design, as technically the chip is unable to source more than 60mA, where the goggles may require up to 120mA. Offhand the AtMega(4,8,16)8 chips are the only ones I can think of that can source sufficient current, and since they can hold more elaborate programs might be a better choice for future designs. Additionally, although the AVR micro-controllers can function at a range of voltages, the nonlinear V-I curve of the LEDs means that attempts to balance the white-point using resistors must be in the context of a well defined voltage ( preferably a constant 20mA current source, but that takes up board space ). Additionally, I was surprised that the internal resistance of coin-cell Cr2023 batteries limits them to approximately 0.3mA continuous draw. Although the much higher mean current draw of ~20mA for the goggles can be supported, this will cause the battery voltage to drop during operation and the LED white-point to drift. Eventually the voltage falls below the operating voltage of the AtTiny. The coin-cells will recover after about ~30 minutes of rebound. We're still seeing some problems with party-durability but hopefully refining the PCB board design and construction can improve on this. Building the goggles is incredibly annoying and I doubt I shall be constructing any more by the old methods for some time. I'm still a bit baffled as to how someone magically managed to repair solder connections and rebuild the connector on one of the goggles in the middle of the party, but ... thats Dabney house for you.
Laser Spirographs and Monolith-Monitor tower with EL wire :
I don't have good documentation on this at the moment, other than this system crashed a lot during the party, but was still super awesome.
Thanks to everyone who made this happen, it was great to see you all again.