1) They are on all the time, so on average they waste half of their energy shining during the day.
2) A continuous light is not as visible as a flashing light. An interesting flash-pattern adds to the intrigue and can even convey information.
3) Their Duty cycle is too high – a duty cycle of 10-20% is more than sufficient to be both visible and much more efficient.
This is the first of what I hope to be 2-3 versions of microcontroller projects designed to be so cheap that they can be used as throwies. The plan is to make them more efficient and use that gain to provide a more exciting effect.
This is the “basic” version – it requires only one additional part, three blobs of solder and a few snips. And by magic* it can address all of the issues above. It’s programmable using the Arduino IDE, hence “Throwduino”.
On a per-hour basis it should even work out cheaper than a normal throwie. The cost analysis is in step 5.
Edit – Morse Code sketch now available – see last step.
– Now you can throw your own message of choice!
* not actually magic – see step 6.
Step 1: Parts
An ultrabright blue, blue-green or white LED*
A CR2032 battery
An ATtiny25/45/85 AVR microcontroller.
Optionally, a rare earth magnet.
You will also need these tools:
Soldering iron & solder
Wire cutters
Tape
If you want to do this and don’t want to bother programing the chip yourself, leave a message and I’ll see if I can arrange a source of pre-programed chips.
Assuming that you need to program the ATtiny you will need:
Parts (unless you have a programing shield for ATTinys):
A DIP8 socket
6-pin 0.1″ male header (or 4-pin if you use jumper leads below)
About 2×6 inches of essentially any insulated wire or 1-2 jumper leads (ideally two colours)
Or (alternative parts):
Solderless breadboard
Jumper wres
Tools:
An Arduino (an ISP programmer would also work)
PC with Arduino IDE & Arduino Tiny cores (more on this later)
Hot glue or epoxy advisable
* You could use an Red, Orange, Yellow or Yellow-Green LED but you would need to use a resistor or you would risk a brown-out on the controller and will get a very short lifetime. This adds 30% to the parts count but almost nothing to the price. 300-500 Ohms is not a bad place to start if you go this route. Maybe a little less for Yellow-Green.
Step 2: Build the programming parasite (micro shield).
If you already have a ATtiny programing shield or can hack your EMSL ATMega one like this: then you don’t need to do this step – go on to the next step.
Equally, if you would rather program your ATTiny on a breadboard then there are plenty of tutorials for that, such as this or this. Note that these use the MIT ATtiny core which I have not tried in this project. However, you can use the same hardware setup with the Arduino Tiny cores.
Since we will be cutting the chip around, it will be a lot easier if we program it before we make the “throwie”. To do this we will build the smallest programing board ever conceived* so that we can attach the chip to our Arduino for programing. It’s a bit like a micro shield or perhaps an arduino parasite!
Firstly, take the 6-pin header and break it into a 4 and a 2. Identify pin 1 of the DIP-8 socket.
Now bend out pins 5-7 of the DIP-8 socket and solder the 4-pin header to bent pins 5-7 so that one pin of the header over-hangs past pin 5 of the socket.
Run a short wire from pin 1 of the socket to the over-hanging pin of the header.
Now connect the two remaining header pins to the VCC (pin 8) and Gnd (pin 4) connections of the DIP socket using around 4-6″ wires. Using red for VCC and black for Gnd will help avoid chip-frying mistakes. If you have red/black jumper leads, just solder those to pins 8 and 4.
A spot of hot-glue or 5-minute epoxy to hold things tight would be a good plan at this point.
To use the programmer, insert your ATtiny into the DIP-8 (getting it the right way around – see picture 1) and plug the 4-pin header into your arduino at pins 10-13, so that the over-hanging pin of the programmer goes to pin D10. The two remaining pins on their fly-lead go into +5V and Gnd on the Arduino. Make sure you get them the right way around too! This is why coloured wires are a good plan for this.
You are now all rigged up for using your Arduino as an ISP for your ATtiny.
You may need to add a 10uf capacitor between Gnd and reset to avoid auto-reset issues.
All you need to do now is load the Arduino with the ISP sketch and install the Arduino Tiny cores (if you don’t have that already). They can be found, along with instructions for installation, here.
If you want to watch what the ISP is doing, you can add LEDs and resistors between ground and each of pins 9, 8 and 7. They represent the programmer “heartbeat”, error light and programing light respectively. In practice the flicker on the pin-13 LED tells you that it’s working.
* This may be a slight exaggeration but it can’t be very much so.
Step 3: Program the Chip
Having set up your programmer in the last step, load the Throwduino basic sketch to the IDE. It’s pasted below and also attached.
threshold=(total>>5); // set threshold to average point (divide total by 128).
should probably be:
threshold=(total>>7); // set threshold to average point (divide total by 128).
It has worked for me as it was but this could cause problems with the light sensing. I will check and correct if needed.
If you would like to change the flash-sequence of your thowie then now is the time. Make sure that your code will fit in the memory of the chip you have. It’s not likely to be an issue for anything bigger than an ATtiny25. The sketch below takes not much more than 1K. The code below displays the well-known “1-4-3” flash sequence. This may, of course, not be your thing, so hack-away.
The flash sequence is held in the array and simply holds a list of flash numbers. These are played with a short pause between each, followed by a 2-second pause before repeating. For example, for:
The throwie will flash once, pause, flash 4 times, pause, flash 3 times, pause for 2 seconds & repeat.
Once the sketch is ready, select ATtiny85 1Mz (or whichever ATtiny you are using) from the boards list in the IDE. If you don’t select the correct board you may get a compile error (XXXX not defined in this scope). If you get this, check you have selected an ATtinyX5.
Hit “upload” in the IDE. The Arduino Tiny uses the Arduino ISP by default. You should see the pin13 LED on your Arduino flicker as the sketch is transferred.
How does the sketch work?
We have a few functions defined that do useful things and you might want to use in hacking:
void flash(byte) – flashes the LED “byte” times.
int lightLevel() – returns an int containing the light level measurement from the LED against the 2.56v internal ref.
void setup_watchdog(int) – sets the watchdog timer to value “int”. There are 10 settings from 0 to 9 corresponding to 16, 32, 64, 128, 250, 500, 1000, 2000, 4000 & 8000 ms.
void system_sleep() – puts the system to sleep for the time set in the watchdog setup.
To start we poke at a few registers to set things up and minimise wasted power. Then in setup we flash 3 times and take 148 measurements of the light level. We throw away the first 20 and average the remaining 128. This sets the threshold by which we will judge when it is dark. We flash the LED that threshold number before carrying on. This is more for debugging than anything useful.
In the main loop we measure the light level and compare to the threshold value. If its under the value then we flash the LED by our pre-set pattern. If the light level is high then we sleep for 8 seconds (the longest that we can) before testing again.
My thanks to InsideGadgets for this article which formed the starting point for the sleep code. It’s worth noting if you are using this that you need to reset the interrupt enable flag each time the WDT times out or the chip will reset next time.
Sketch (this should compile to around 1262 bytes):
// Throwduino basic
// Ugi 2012
// MIT license
// Written to run on ATtiny25/45/85 using Arduino Tiny core – http://code.google.com/p/arduino-tiny/
// Will not compile for other Arduinos (ATMega168, ATMega328 etc)
// Make sure you select the correct board before you compile.
// For Ref:
// ADC Pins:
// ADC1 = PB2
// ADC2 = PB4
// ADC3 = PB3
// If you just want to change the flash pattern, do it here.
// default is:
//
// const byte flashSeq[3]={1,4,3}; // Flash pattern
//
// This gives 1 flash, pause, four flashes, pause, three flashes, pause.
// If you just want a longer pause, use 0 for each 500ms.
// After the sequence, we wait for 2s before repeating.
const byte flashSeq[3]={1,4,3}; // Flash pattern. Edit this.
// how many entries in the flash pattern.
const byte seqLength =sizeof(flashSeq);
// Now let’s make a throwie for that sequence….
const int LEDpin=4; // Although this is in a constant, there is a lot of direct port access
//I wouldn’t try to change this without going throu’ the whole code.
unsigned int threshold = 10; // We’ll set this properly during setup
// This sets up some libs and definitions for use with the sleep timer
#include <avr/sleep.h>
#include <avr/wdt.h>
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
ISR(WDT_vect){ WDTCR|=B01000000;} // reset Watchdog Timer to Interrupt mode each time.
// Mess about with some registers to turn stuff off & set references.
void setup(){
ADCSRB&=B10111111; // disable comparitor
ACSR |=B10000000; // turn off power to comparitor
ADCSRA |=B10000000;// turn on ADC
DDRB&=B11100000; // set all to input
DDRB|=B00011010; // Set 1,3 and 4 as output
PORTB&=B11110111; // set PB3 low – this was for the testing phase. Can probably be dropped now.
PRR&=B11111110; // clear ADC disable bit in Power Reduction Reg
analogReference(6); // internal 2.56v reference with no bypass – INTERNAL2v56NB –
// Show we are awake:
flash(3); // Three flashes – we are up and running.
// Set the threshold for light sensing during first 40 seconds.
// We take 148 measurements, bin the first 20 and then average the next 128.
unsigned int total=0;
for (byte rep=0; rep<148; rep++){
int value=lightLevel();
// int value=10;
if(rep>19){
total+=value; // tatke total of 128 readings
}
//flash(value);
DDRB&=B11100000; // set all to input
setup_watchdog(4);
system_sleep();// sleep for quarter second
DDRB|=B00011010; // Set 1, 3 and 4 as output
PORTB&=B1110000; // Set all low
}
threshold=(total>>7); // set threshold to average point (divide total by 128).
flash(threshold); // This is mostly for debugging purposes.
}
//Main Loop
// We measure the light level. If it’s low then we flash our sequence then sleep for 2s.
// If light level high, we sleep for 8s.
void loop(){
analogReference(6); // internal 2.56v reference with no bypass
// Measure light level and if it’s low enough, display our flash sequence
if (lightLevel()<threshold){
for (byte sequence=0; sequence<seqLength; sequence++)
flash(flashSeq[sequence]);
// If we were flashing, we now sleep for 2s
DDRB&=B11100000; // set all to input
setup_watchdog(7);
system_sleep();// sleep for two seconds
DDRB|=B00011010; // Set 1,3 and 4 as output
PORTB&=B11110111; // PB3 low.
}
else{
// If we were not flashing then we sleep for 8s
DDRB&=B11100000; // set all to input
setup_watchdog(9);
system_sleep();// sleep for two seconds
DDRB|=B00011010; // Set 1,3 and 4 as output
PORTB&=B11110111; // PB3 low.
}
}// end main loop
// Measure light level by looking at voltage generated by LED connected to D4 (A2) on pin 3 of the chip.
// Set Output and low to start to discharge any charge buildup – not sure if we need this
int lightLevel(){
pinMode(LEDpin, OUTPUT);
digitalWrite (LEDpin,LOW);
delayMicroseconds(50);
pinMode(LEDpin, INPUT);
delayMicroseconds(100); // let it stabilise as an input again
unsigned int value = analogRead(A2);
return value;
}
// Flash routine – 70ms on, 250 ms off
// Repeat certain number of times and then pause 500ms
void flash(byte No){
pinMode(LEDpin, OUTPUT);
if(!No){// if we receive a zero, just pause.
DDRB&=B11100000; // set all to input
setup_watchdog(5);
system_sleep();// sleep for 500 ms
DDRB|=B00011010; // Set 1,3 and 4 as output
PORTB&=B11110111; // PB3 low.
}
// If we had a non-zero number of flashes, we’ll make them now
for(byte rep=0; rep<No; rep++){
DDRB|=B00011010; // Set 1,3 and 4 as output
PORTB&=B11110111; // PB3 low.
PORTB|=B00010000; // PB4 High
//digitalWrite (LEDpin,HIGH);
delay(70);
PORTB&=B11101111; // PB4 low.
DDRB&=B11100000; // set all to input
//digitalWrite(LEDpin,LOW);
setup_watchdog(4); // WDT 3=128ms
system_sleep();
}
// Pause (sleep)
DDRB&=B11100000; // set all to input
setup_watchdog(5); // WDT 5 = 500ms
system_sleep();// sleep
DDRB|=B00011010; // Set 1 and 3 as output
PORTB&=B11110111; // PB3 low.
}
// Watchdog timer setup
// This is specific to the ATTiny85 (+ tiny45, & tiny25) and won’t compile for ATMega328 etc.
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int period) {
byte value;
if (period > 9) period=9;
value=period & B111;
if (period > 7) value|= (1<<5);
value|= (1<<WDCE);
MCUSR &= ~(1<<WDRF);
// start 4-clock-cycle timed sequence
WDTCR |= (1<<WDCE) | (1<<WDE);
// set new watchdog timeout value
WDTCR = value;
WDTCR |= _BV(WDIE);
}
// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {
cbi(ADCSRA,ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable();
sleep_mode(); // System sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
sbi(ADCSRA,ADEN); // switch Analog to Digitalconverter ON
PRR&=B11111110; // clear ADC disable bit in Power Reduction Reg
}
Step 4: Make up the Throwie
Making up a Throwduino is only slightly more complex than a normal throwie, although you will probably need a soldering iron to make a decent job of it. Once you have made a couple they take less time to make than your soldering iron will take to warm up.
For more detail: Throwduino Basic – Light-Sensing Flashing Throwie