Introduction: Throwduino Basic - Light-Sensing Flashing Throwie With 1 Added Part - Now With Morse Code

Picture of Throwduino Basic - Light-Sensing Flashing Throwie With 1 Added Part - Now With Morse Code

Throwies are great.  They have a minimal parts count - 2 or 3 depending on whether you use a magnet - and produce a great effect.  They are very cheap and easy but not highly efficient:

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

Picture of Parts

You need only 3 essential parts for this project:

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 2x6 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).

Picture of Build the Programming Parasite (micro Shield).

This step will assume that you need to program the ATtiny and are using an Arduino running the Arduino ISP as your programmer.  If you have a pre-programed chip then skip to step 4.  If you are using another programmer then you will need to adapt this slightly.  Refer to the ATtiny datasheet for pin definitions etc.

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

Picture of Program the Chip
This is the original sketch - for Morse Code sketch, see step 7

Having set up your programmer in the last step, load the Throwduino basic sketch to the IDE. It's pasted below and also attached.

Edit - there may be an error in the code:

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

Picture of 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.

Program the chip before you cut it about - see the last two steps!

Remove the ATtiny from the programmer and find pin 1.  Bend Ground pin 4 up above the chip.  Now clip off pins 1, 2, 5, 6 & 7.  If there's any chance that you might want to reprogram the chip then leave enough pin  to solder onto later!

Take the LED and identify the +ve (anode) side.  This usually has the longer lead and is away from the flat-spot on the lens.  Solder pins 3 & 8 (which are the only pins that are still pointing down) to this leg of the LED, up fairly near the lens.  Pin 3 should be closest to the LED lens. It's best to do this so the LED leg runs flush against the underside of the chip.  Clip off the extra length on the pins of the chip.

Once soldered, clip a small section out of the LED leg between pins 3 and 8 to leave a gap (& no electrical connection) between.

Now solder the -ve (cathode) side of the LED (the side with the flat spot) to pin 4 that you bent up above the chip.   Again, make it flush to the chip.  Clip off the pin.

Now add the battery, being careful that the +ve side goes to the +ve of the LED and the under-side of the chip.  Leave space so that the battery doesn't touch any part of the chip, ahtough the closest part to the battery should be pin 8 and that is connected to the battery anyway.

Tape around the battery and around the chip to hold them.

During the first 40 seconds of the throwie's life the light will flash three times and then go out.  This is its sensing phase.  After the flashes it will measure the average light level for the next 40 seconds. 

You might like to put it somewhere dark (e.g. cupped in your hands) for some of this time to push the average down a bit so it only comes on when it's really dark. 


After 40 seconds the LED will flash a few times and then go into "live" mode.  It will now issue its flash sequence only when it senses darkness.  Obviously, if you didn't get the threshold level right first time, just disconnect the battery and start again.

The light sensor can tell day from night and indoors from daylight* but it's not terribly sensitive.

If you are using a magnet, tape it on now.

You now have a Throwduino.  With a fresh battery it should last a couple of weeks at least, although I haven't tested one to exhaustion yet so if you do so, please let me know.

Hope you enjoy them!

Ugi

* "But how?"  I hear you cry - see step 6.

Step 5: Lifetime & Cost

Picture of Lifetime & Cost

There are a number of factors to take into account in assessing the lifetime of our throwie.  Firstly, there is the current drawn by the LED.

Our friends over at Evil Mad Scientist were kind enough to conduct an analysis of the current drawn from a throwie over time.  It can be found here:.  If we are using the standard program, which runs at around 11% duty cycle then we would expect the lifetime to be 9-fold greater (9-18 weeks maybe).

However, we also have the overhead of running a microcontroller.  At 1MHz and 3.3V, this would take around 2mA, which would potentially exhaust a CR2032 in as little as 4 days without any load.  However, by setting the microcontroller to sleep during the "off" time for the LED, we can reduce the current to microamps during the off part of this cycle.  I measured the sleep current to be 4.5uA, which is pretty minimal.  This takes the average current down to around 11%, or 0.2mA.  As the battery discharges, the lower voltage results in a still-lower current draw for the chip.

Measuring the actual current drawn, I am seeing about 4.5mA when the LED is on and 4.5 uA when it's off.  This would give around 18 days from a 220mAh battery, but at 4.5mA, an LED would only last about 2 days and we know then last a week or two.  Only time will tell how long we actually get.

As for the cost, a typical throwie (based on buying parts for 10) might cost:

Battery - 30p
LED - 15p
Magnet 40p

Total - 85p for a lifetime of around 1-2 weeks (42-85p per week).

For a Throwduino:

Battery - 30p
LED - 15p
Magnet - 40p
ATtiny85 (Mouser) - 60p

Total - £1.45 for a lifetime of 2, 4, 10 weeks, who-knows.  (2-4 weeks would be £38-73p per week).

Obviously there is the extra time and the need for tools such as the PC, arduino and soldering iron but there is a good chance that if you are interested in microcontrolled throwies you may have these in any case. 

If you don't want to program your own chips then I can probably find arrange a source of pre-programed ones.

Hopefully the addition of an ATtiny adds value at least in proportion to the additional cost.

Edit - 4 weeks later and the original throwduino is still flashing away.  It survived weeks of constant rain in a totally exposed location but continues undaunted.  I flashed away each night, even through pouring rain.

Further Edit - it lasted around 6 weeks.   About half of that time it was flashing day and night.  I think that may be due to using the higher 2.5V reference voltage, which might have dropped out when the battery became depleted.  Using the 1.1V internal reference, I reckon you could get 8 weeks or more from one of these (because it could flash only at night).

Step 6: Magic

Picture of Magic

The "trick" to this project is that we are using a little known property of LEDs that I discovered here - they can act as photodiodes.  We also rely on the fact that the ATtiny has an Analogue to Digital Converter (ADC) which we can press into use to measure this photodiode effect.

The trick to the 1-pin system is that Analog pin 2 in Arduino Tiny is the same as digital pin 4, so we can use the same LED as a sensor and an output.  You could do the same with any analog pin on an Arduino but it would work our rather expensive!

When we start up the AVR we run the "setup" routine, which measures the voltage generated from light hitting the LED over 40 seconds, using code similar to that used during the "loop" routine.  We measure 128 values and take the average of these as being the darkness threshold.  This means we don't need to know the properties of the LED in advance.  As long as it generates a signal we should be able to use it.

Any time that the power is removed or the chip is reset we will run that threshold sensing routine again.  Therefore if the chip resets during the night for any reason it could result in a threshold that will rarely be crossed.  Equally if it resets in bright sunshine then the light would probably stay on all the time.  Life is fraught with hazard.

We could, possibly, write the threshold value to EEPROM but if we did that then we would only get one shot at making the battery connection.  A further development would be to short two pins (e.g. connect pin 7 (PB2) to VCC) until we are happy with our threshold and then snip that pin to prevent further EEPROM writes.  I may work on an update of that sort.

If you have any cunning ideas then I's love to hear them.

Now, to work no the next version - Throwduino RGB, I expect!

Step 7:

Picture of
Morse Code!

In this step you'll find the Morse Code ... erm.... code for Throwdurino.

The great thing about adding a microcontroller is that we can carry information.  This sketch allows the same throwie to flash out a pre-programmed message in Morse Code.  The initial threshold setting routine and light sensing method are the same as for the original sketch.

Edit - there may be an error in the code:

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.

The timing of the Morse is not quite as fast as it should be - the pauses are much too long really - but I found the strict timing too fast for a novice to decode. Or too heavy on battery use.  I think this is pretty clear to the novice Morse Coder.

To edit the message, find the line below at the top of the sketch:

const char flashSeq[]={"DARK HERE ISNT IT"};  // Morse message.  Edit this.

and simply edit that as you require.

To minimise the length of the sketch, it only recognises capital letters.  Everything else will be treated as a "space".  Don't try to use more than 256 characters or it's likely to crash horribly.

The sketch is attached and pasted below.  It should compile to around 1398 bytes

// Throwduino basic - Morse code edition!
// 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 char flashSeq[]={"DARK HERE ISNT IT"};  // Morse message.  Edit this.
//
// Edit the message with UPPER CASE CHARACTERS ONLY.
// Anything else will come out as a 1s delay.
// After the sequence, we wait for 2s before repeating.

const char flashSeq[]={"DARK HERE ISNT IT"};  // Morse message.  Edit this.
const byte seqLength =(sizeof(flashSeq)-1);

// Morse code encoding
// Stored as one byte per letter.
// High 5 bits are 1 for - and 0 for beginning with LSB of those high-5..
// Low 3 bits give number of flashes (dots + dashes).
// Morse allows for 5 flashes (for numbers) but letters require 4 maximum.
// This format could thus be used for numbers as well but I have not implemented that.

const byte morse[26]={
  B00010010, //a
  B00001100, //b
  B00101100, //c
  B00001011, //d
  B00000001, //e
  B00100100, //f
  B00011011, //g
  B00000100, //h
  B00000010, //i
  B01110100, //j
  B00101011, //k
  B00010100, //l
  B00011010, //m
  B00001010, //n
  B00111011, //o
  B00110100, //p
  B01011100, //q
  B00010011, //r
  B00000011, //s
  B00001001, //t
  B00100011, //u
  B01000100, //v
  B00110011, //w
  B01001100, //x
  B01101100, //y
  B00011100, //z
};
// 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);
  
    pause(4);// was pause(4)
    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){
    flashMorse();
    //  If we were flashing, we now sleep for 4s
    pause(8);
    DDRB|=B00011010; // Set 1,3 and 4 as output
    PORTB&=B11110111; // PB3 low.
  }
  else{
    // If we were not flashing then we sleep for 8s
    pause(9);
    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.
  pause(5);
  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++){ 
   flashDot(70);
}
 
// Pause (sleep)
  pause(5);
  DDRB|=B00011010; // Set 1 and 3 as output
  PORTB&=B11110111; // PB3 low.
}

void pause(byte time){
  DDRB&=B11100000; // set all to input
  setup_watchdog(time); // set up wait period
  system_sleep();// sleep
}


void flashMorse(){
   for(byte rep=0; rep<seqLength; rep++){
     byte index=0;
     if(flashSeq[rep]<65 || flashSeq[rep]>90)index=0;
     else index=flashSeq[rep]-64;
     if(!index) pause(6);
     else {
       byte dotdash = morse[index-1]>>3; // put just sequence part (5 bits) into dotdash
       // Dots and dashes according to sequence encoded in morse[]
       for(byte flashes=0; flashes<((morse[(index-1)]&B111)); flashes++){
         if(dotdash&1)flashDot(160); // if LSB is 1 then display a dash
         else flashDot(40); // otherwise it's a dot
         pause(4); // pause between flashes - 256ms
         dotdash>>=1; // roll along to the next bit
       }
       pause(5); // End of word pause 0.5s
     }
   }
}

// flash the LED for time (<256 ms) and pause for 250ms.
void flashDot(byte time){
   DDRB|=B00011010; // Set 1,3 and 4 as output
   PORTB&=B11110111; // PB3 low.
   PORTB|=B00010000; // PB4 High
   delay(time);
   PORTB&=B11101111; // PB4 low.
   pause(4);  
}

// 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
}
// 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 char flashSeq[]={"DARK HERE ISNT IT"};  // Morse message.  Edit this.
//
// Edit the message with UPPER CASE CHARACTERS ONLY.
// Anything else will come out as a 1s delay.
// After the sequence, we wait for 4s before repeating.

const char flashSeq[]={"DARK HERE ISNT IT"};  // Morse message.  Edit this.
const byte seqLength =(sizeof(flashSeq)-1);

// Morse code encoding
// Stored as one byte per letter.
// High 5 bits are 1 for - and 0 for beginning with LSB of those high-5..
// Low 3 bits give number of flashes (dots + dashes).
//Morse allows for 5 flashes (for numbers) but letters require 4 maximum.
//This format could thus be used for numbers as well but I have not implemented that.
const byte morse[26]={
  B00010010, //a
  B00001100, //b
  B00101100, //c
  B00001011, //d
  B00000001, //e
  B00100100, //f
  B00011011, //g
  B00000100, //h
  B00000010, //i
  B01110100, //j
  B00101011, //k
  B00010100, //l
  B00011010, //m
  B00001010, //n
  B00111011, //o
  B00110100, //p
  B01011100, //q
  B00010011, //r
  B00000011, //s
  B00001001, //t
  B00100011, //u
  B01000100, //v
  B00110011, //w
  B01001100, //x
  B01101100, //y
  B00011100, //z
};
// 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
#include
#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);
  
    pause(4);// was pause(4)
    DDRB|=B00011010; // Set 1, 3 and 4 as output
    PORTB&=B1110000; // Set all low
  }
  threshold=(total>>5); // 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()
    flashMorse();
    //  If we were flashing, we now sleep for 4s
    pause(8);
    DDRB|=B00011010; // Set 1,3 and 4 as output
    PORTB&=B11110111; // PB3 low.
  }
  else{
    // If we were not flashing then we sleep for 8s
    pause(9);
    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.
  pause(5);
  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    flashDot(70);
}
 
// Pause (sleep)
  pause(5);
  DDRB|=B00011010; // Set 1 and 3 as output
  PORTB&=B11110111; // PB3 low.
}

void pause(byte time){
  DDRB&=B11100000; // set all to input
  setup_watchdog(time); // set up wait period
  system_sleep();// sleep
}


void flashMorse(){
   for(byte rep=0; rep      byte index=0;
     if(flashSeq[rep]<65 || flashSeq[rep]>90)index=0;
     else index=flashSeq[rep]-64;
     if(!index) pause(6);
     else {
       byte dotdash = morse[index-1]>>3; // put just sequence part (5 bits) into dotdash
       // Dots and dashes according to sequence encoded in morse[]
       for(byte flashes=0; flashes<((morse[(index-1)]&B111)); flashes++){
         if(dotdash&1)flashDot(160); // if LSB is 1 then display a dash
         else flashDot(40); // otherwise it's a dot
         pause(5); // pause between flashes - 256ms
         dotdash>>=1; // roll along to the next bit
       }
       pause(6); // End of word pause 1 s
     }
   }
}

// flash the LED for time (<256 ms) and pause for 250ms.
void flashDot(byte time){
   DDRB|=B00011010; // Set 1,3 and 4 as output
   PORTB&=B11110111; // PB3 low.
   PORTB|=B00010000; // PB4 High
   delay(time);
   PORTB&=B11101111; // PB4 low.
   pause(4);  
}

// 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<

  MCUSR &= ~(1<
  // start 4-clock-cycle timed sequence
  WDTCR |= (1<   // 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
}

Comments

ljohann made it! (author)2014-04-21

Just a quick note that I had to change the battery in my Morse Egg which is based on the Throwduino but includes a 220 Ohm resistor connected to the LED. That results in 85 days using a single battery.

Ugifer (author)ljohann2014-04-21

Hi Lars - thanks for your comment and picture! Always great to see a project made - I'll send you a 3-month pro by PM.

I guess the 220R just cuts down the current that bit more - 85 days is pretty impressive on a singe cell. And it looks like you used a CR2025, which is lower capacity than the 2032 as well!

Love that the light sensing still works too. When the pin is set to input it should be "high resistance" - drawing minimal current so the resistor should not matter. However it's always great to see what happens in practice. There's usually more going on with these things than I understand at least!

Cheers

Ugi

zzyzzx made it! (author)2017-05-24

I used a red LED so I added a 330 ohm resistor between LED and Ground pin. I'm currently having some problems with the flashing code,

1. It doesn't seem to sense the light at all. I've started it under the lamp, on my table, on the window and inside a dark desk drawer and it sets the same threshold all times. To know the threshold I timed the number of flashes it does after some 40 seconds of restart (~108 seconds which translate to 210 flashes if there's a 250ms delay between each 70ms flash). I made another one which is on a breadboard right now and that flashes for ~55 seconds irrespective of the light levels after starting.

This debugging is in reference to line 58 (three flashes after startup) and line 81 (threshold number of flashes after some 40 seconds)

2. It doesn't flash afterwards. I put a cardboard box over it, used by hand, placed it in shadow and nothing makes it flash after the light levels are set.

3. It restarts with slightest movement or touch. This is probably something wrong with the electrical contact between LED leads and battery but have you found a solution to this yet?

Ugifer (author)zzyzzx2017-05-24

Hi zzyzzx

Thank you for your comment and for trying this project - I'm sorry that it's not fully successful at the moment.

A few things you might check/try:

1) Are you using the specified (ancient) version of Arduino and the compatible Arduin Tiny cores? If the pin assignments are off or the ADC is not working correctly then that would cause problems. If I can find it, I will upload the version of the cores that I used. I'm afraid this is now rather an old project.

This is my best guess - that the pin numberings are somehow different with the core you have used so that it's sensing from a different pin to the one that the LED is connected to. IIRC the "output" pin numberings and the "analogue" pin numberings are different with the core I used and so someone may have "fixed" that in a newer version of the core.

You could even put on 3 LEDs - one between ground and each of pins 2, 3 and 7 (actual chip pin numbering) and see if that makes a difference. If it works like that then you just need to work out what "A" pin corresponds to the correct chip pin.

2) Try it briefly without the resistor - The current that flows to the pin in "high impedance" mode should be so low that the resistor should not make a difference to the measurement. You shouldn't connect a red LED directly but the pulses are short and ATTinys are cheap and tough-as-nails so it might be worth risking without the resistor to see if it makes a difference. It shouldn't but it might be worth trying.

3) Try a different LED - red LEDs should sense red light but there is a possible, if somewhat contrived, situation where if the LED and it was fundamentally a more blue colour but made red by some fluorescent type material then it would not work backwards as a photodiode because only red can get through your tinted lens. This is getting into the realms of the theoretical & fantastical but try switching the LED. In fact, you could try with blue or white, clear lens and no resistor, if you have such an LED in your parts box. If that doesn't work then I think it really must be a code issue.

I would investigate the pin assignments primarily or do a test with a blue clear LED if you have one.

Meanwhile, I'll send you a PM with your prize "Pro" membership - that's not conditional on bug fixing.

Good luck!

Ugi

zatamite made it! (author)2015-11-18

Thanks so much for the writeup. Also Thanks for the well commented code. Learned so much. I have modded your code and have a version that accepts numbers, commas, colons, and dashes. I am going to be using it for a game played campus wide called Firefly wherein students decode morse and are given clues/riddles in the morse code to help complete a task or scavenge hunt. Some of the game involves GeoCashing so... The full stops, dashes, commas and numbers were needed. Heres the Code:

const char flashSeq[]={"CAN YOU CATCH THE FIREFLY. FIND CLUE AT 46.814559, -92.053409" }; // Morse message. Edit this.

const byte seqLength =(sizeof(flashSeq)-1);

// Morse code encoding

// Stored as one byte per letter.

// High 5 bits are 1 for - and 0 for beginning with LSB of those high-5..

// Low 3 bits give number of flashes (dots + dashes).

// Morse allows for 5 flashes (for numbers) but letters require 4 maximum.

// FOr Dashes and dots dont forget to start 3 from the right and pattern the timming from right to left.

const byte morse[43]={

B11111101, // 0

B11110101, // 1

B11100101, // 2

B11000101, // 3

B10000101, // 4

B00000101, // 5

B00001101, // 6

B00011101, // 7

B00111101, // 8

B01111101, // 9

B00000000, // BL These have to be kept otherwise and A will become and H. The B will become a I.. and so on.

B00000000, //BL

B00000000, //BL

B00000000, //BL

B00000000, //BL

B00000000, //BL

B00000000, //BL

B00010010, //A

B00001100, //B

B00101100, //C

B00001011, //D

B00000001, //E

B00100100, //F

B00011011, //G

B00000100, //H

B00000010, //I

B01110100, //J

B00101011, //K

B00010100, //L

B00011010, //M

B00001010, //N

B00111011, //O

B00110100, //P

B01011100, //Q

B00010011, //R

B00000011, //S

B00001001, //T

B00100011, //U

B01000100, //V

B00110011, //W

B01001100, //X

B01101100, //Y

B00011100, //Z

};

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 -

// The Firefly is on

flash(5); // Five flashes - we are up and running.

start_of_code();

// 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);

pause(4);// was pause(4)

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){

flashMorse();

// If we were flashing, we now sleep for 4s

pause(8);

DDRB|=B00011010; // Set 1,3 and 4 as output

PORTB&=B11110111; // PB3 low.

}

else{

// If we were not flashing then we sleep for 8s

pause(9);

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.

pause(5);

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++){

flashDot(70);

}

// Pause (sleep)

pause(5);

DDRB|=B00011010; // Set 1 and 3 as output

PORTB&=B11110111; // PB3 low.

}

void pause(byte time){

DDRB&=B11100000; // set all to input

setup_watchdog(time); // set up wait period

system_sleep();// sleep

}

void flashMorse(){

start_of_code();

for(byte rep=0; rep<seqLength; rep++){

byte index=0;

if(flashSeq[rep] == 44) Comma(); // Function for comma 6 blinks

if(flashSeq[rep] == 45) hyphenDash(); // function for Hyphen 6 blinks

if(flashSeq[rep] == 46) fullStop(); // function for Full Stop/Period 6 blinks

if(flashSeq[rep]<48 || flashSeq[rep]>90)index=0;

else index=flashSeq[rep]-47;

if(!index) pause(6);

else {

byte dotdash = morse[index-1]>>3; // put just sequence part (5 bits) into dotdash

// Dots and dashes according to sequence encoded in morse[]

for(byte flashes=0; flashes<((morse[(index-1)]&B111)); flashes++){

if(dotdash&1)flashDot(1500); // if LSB is 1 then display a dash

else flashDot(60); // otherwise it's a dot

pause(4); // pause between flashes - 256ms

dotdash>>=1; // roll along to the next bit

}

pause(6); // End of word pause 0.5s

}

}

}

// flash the LED for time (<256 ms) and pause for 250ms.

void flashDot(int time){

DDRB|=B00011010; // Set 1,3 and 4 as output

PORTB&=B11110111; // PB3 low.

PORTB|=B00010000; // PB4 High

delay(time);

PORTB&=B11101111; // PB4 low.

pause(4);

}

// 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

}

void start_of_code()

{

pinMode(3, OUTPUT);

digitalWrite(3,HIGH);

delay(4000);

digitalWrite(3,LOW);

delay(4000);

}

void fullStop() // Period Handling

{

flashDot(60);

pause(4);

flashDot(1500);

pause(4);

flashDot(60);

pause(4);

flashDot(1500);

pause(4);

flashDot(60);

pause(4);

flashDot(1500);

pause(1);

}

void hyphenDash() // Hypen Handling

{

flashDot(1500);

pause(4);

flashDot(60);

pause(4);

flashDot(60);

pause(4);

flashDot(60);

pause(4);

flashDot(60);

pause(4);

flashDot(1500);

pause(4);

}

void Comma() // Comma Handling.

{

flashDot(1500);

pause(4);

flashDot(1500);

pause(4);

flashDot(60);

pause(4);

flashDot(60);

pause(4);

flashDot(1500);

pause(4);

flashDot(1500);

pause(4);

}

Ugifer (author)zatamite2015-11-19

Fantastic - always great to see a project used, adapted and enhanced!

Glad you were able to follow the code - it's not the most accessible bit of Arduino because I wanted to make it as fast as possible so we can keep the chip asleep to conserve battery life. That means that there's a lot of direct port access. Your battery life should be pretty enormous on all of those Duracells!

Good luck for your campus project! One of the driving forces behind most of my projects is that I like to make techy thing that interest kids (my own and others) so I'm delighted that you found a student activity that could utilise these.

Since you made and posted a project based on one of mine, you get a free 3-month pro-membership. I'll send you a PM with the code in a mo'.

Thanks for posting - I'd love to hear how the students get on.

Ugi

zatamite (author)Ugifer2015-11-19

Cheers Ugi. And thanks for the membership.

Side note on code changes. I exaggerated the timing mucho. Some of the players will be very young. I tested the timing on 7 year old and this speed seemed to be challenging but not anxiety inducing. + I added another colored led to Pin 2(PB3) so the start of the repeating message would be more obvious.

dad_a_monk (author)2015-07-07

I used to teach a lot of pilot survival classes to private pilots. This could be an awesome survival tool. Using it as a ground to air signal, with programed SOS code. Amazing! Could even make a few to be used as a landing zone marker. Looks even better than the little fireflies that attach to 9 volts. How long do these last with a short code loaded and repeating?

Ugifer (author)dad_a_monk2015-07-07

It will depend a bit on your LEDs but mine last about 6 weeks. Hopefully in a survival situation you should not need that long!

I'm not sure how far away you could see one, however - it's just a normal LED. Quite bright from a few dozen yards but not something that's likely to alert a passing plane.

That said, you could make a version with a lithium ion battery, a transistor and maybe a half-watt LED that would give several nights of pretty bright flashing (maybe a week) for a few pounds.

ljohann (author)2014-01-26

Great tutorial! I soldered mine on a prototype PCB along with a battery holder in a candybox container. When the battery is empty I can easily revive it (if it still sits where I placed it). I also included a 220 Ohm resistor (don't know if this will increase battery time or not). I was curious whether or not the resistor would affect the sensing function, but it works just fine!

Best wishes,

Lars

pandyaketan (author)2013-01-08

You ppl may like to see my Light sensor LED, using only a resistor and an LDR... ;-D

reg
k
-------------------------------------------------------
"May the good belong to all the people in the world.
May the rulers go by the path of justice.
May the best of men and their source always prove to be a blessing.
May all the world rejoice in happiness.
May rain come in time and plentifulness be on Earth.
May this world be free from suffering and the noble ones be free from fears"
---- Vedic blessing


Lord_Vek (author)2012-09-20

Good instructable good use of Arduino, Attiny and the trick to use the LED to measure the light.
I made one Throwduino to see how it works, with the Morse code. I initialized it (connected the battery) in absolute darkness, so it would light only in darkness. But it works continuously, no matter if it is darkness or light. Am I doing anything wrong ?
Also, in the first program:

const byte flashSeq[3]={1,4,3}; // Flash pattern. Edit this.

// If you just want a longer pause, use 0 for each 500ms.

what do you mean by that ? Make an example please. I tried first this code, with 4,4,4, as flashSeq parameters but I got 12 flashes because I didn't knew how to make longer pauses.

Thanks in advance.

Ugifer (author)Lord_Vek2012-09-21

Hi - thanks for your message and for your interest in the project. I haven't met these issues with it before but I will help as much as I can.

I assume by "works continuously" you mean that it flashes the whole time, rather than the light is on continuously. If the latter then it's probably a hardware issue (did you clip the wire between the pins for example?).

The LED light sensing has worked with all the white, blue and blue/green LEDs that I have tested. That does not mean there are no LEDs that won't work but I would be surprised. What LED are you using? Colour? Vf? clear or diffused? It shouldn't matter but I'm just interested.

Are you using the same chip (ATTiny 85) and battery (CR2032)? Do you have all the connections made correctly? I'm happy to look at a picture of your device if you post it (or PM me) to see whether I can spot any issues.

You should not need total darkness but you do need to remember that it senses over 40 seconds, so you have to wait that long before bringing it out into bright light. The first 10 seconds are not used, so you can connect the battery in the light then put it in your cupped hands within the first few seconds and that should be easily enough.

My guess would be that the battery connection broke briefly after you brought it out into the light so it re-sensed in the daylight. Maybe try connecting & tape it up well in the light so you can see what you are doing and then putting it under an upturned coffee cup for a minute after. It takes an average so even if your first few seconds are in day light, some time in darkness will push the average down enough.

With the original sketch, the LED will flash a few times after the sensing phase to tell you what value it has sensed. Might be worth looking at that. If you connect the battery in bright light it should flash out a higher reading than in low light (obviously) so try under both conditions and check that it's measuring the difference.

With regard to the flash sequence, {4,4,4} should give you three groups of four flashes, with a short pause between each group. If you do {4,0,4,0,4} then you should get longer pauses between groups. If you do {1,0,0,0,2,3,0,0,4} then you get: flash, long wait, flash, flash, short wait, flash, flash, flash, fairly long wait, flash, flash, flash, flash.

Hope that's of some help. If you can supply more details I will try to help further.

Cheers

Ugi

Ugifer (author)Ugifer2012-09-21

PS I think I may have made a mistake in the code!

It has worked fine for me, but I think this line:

threshold=(total>>5); // set threshold to average point (divide total by 128).

Should be:

threshold=(total>>7); // set threshold to average point (divide total by 128).

I can't check it just now - I'm at work - but try that and see if it helps.

Ugi

Lord_Vek (author)Ugifer2012-09-21

Update...

Then I saw the PS for the threshold, I made the changes and it now works like a charm!!!

Light / Dark detection - OK.

Pauses - OK.

Thank you very much for the answers and for the nice instructable! Be well!

Ugifer (author)Lord_Vek2012-09-21

Thank you for taking the time to comment - I would not have found the bug without that, because mine have worked so far - must be a difference in the performance of the LEDs.

I'll make a correction to the 'ible.

Cheers

Ugi

PS watch out for the RGB version, which will hopefully hit instructables pretty soon!

Lord_Vek (author)Ugifer2012-09-21

Glad to help. I like very much these easy to make, yet sophisticated and clever instructables like yours. I had my Arduino ready with ISP program into it and a breadboard ready to put an ATtiny, so it only took a quarter of minutes to make the changes, reprogram and test. I also made a PCB, since I didn't want to cut the Attiny pins (and in case something went wrong), but also to be able to put the circuit into a plush toy or something. I 'll try to post it.
One thought I made, with a little more code and a mobile vibration motor, you could have something buzzing into a drawer when it's closed (dark) but idle when someone opens it to look into. I read this in High tech practical jokes for the Evil Genius. Thanks again.

Lord_Vek (author)Lord_Vek2012-09-21

Here they are. The .zip contain the eagle files. You can connect either piranha, or normal Led.

Lord_Vek (author)Lord_Vek2012-09-21

3V Battery connector at the back side, that;s why it is so big.

Lord_Vek (author)Ugifer2012-09-21

I believe the circuit I made is correct, otherwise the Led wouldn't lit, or it wouldn't work at all.
Thanks for the answer. I am using a clear red Led, I also tried with a clear blue, and a green piranha. I don't know Vf for any of them. I will try again, based on your above answer for the initial seconds, and also I will try the pauses with the first code.

Thanks for the answer, my friend.

masynmachien (author)2012-08-31

Hi Ugifer,

This great demonstration on how simple and cheap an ATtiny circuit can be, was an inspiration for my newest Ible: A(T)tiny StarBird.

Thanks!

Ugifer (author)masynmachien2012-08-31

That's fantastic - I love the project and it's always great to know that something you did helped to spark an idea with someone else.

Thanks for your kind comments.

masynmachien (author)2012-07-01

Great!
you got my vote

Ugifer (author)masynmachien2012-07-01

Thank you - much appreciated!

ynze (author)2012-06-27

Nice idea! Would it be cheaper (and maybe easier) to use a 555 timer ic in a PWM-circuit to power the LED, instead of a microcontroller?

A pwm circuit needs a bit more parts than your solution, I expect... Did you consider using a 555?

Ugifer (author)ynze2012-06-27

I came at it rather from the opposite direction - more along the lines of:

"Hey! You can get an ATtiny for 50p! They could be disposable at that price - let's do something fun and not worry about losing the chip"

You could use a 555 for a flashing circuit or a PWM "heatbeat" but I have a feeling they are pretty power-hungry and they don't "sleep" like the AVR does. For this project, whenever the light is off the ATTiny is asleep drawing 4.5 microamps! I don't think you would get the same kind of lifetime out of a 555. Would be fun to see thou'.

Ugifer (author)Ugifer2012-06-27

Oh, and you wouldn't get the dark-detecting effect either without a load more components. The great thing about this is that the ATTiny can read the light level, control a meaningful pulse sequence and use only a vanishing current when it's not active.

ynze (author)Ugifer2012-06-28

You're right 100%! The 555 _is_ power hungry. I'm not very much into microcontrollers, that's why I first thought of the 555. ATTiny seems the very best choice to do this.

blhack (author)2012-06-27

Oh, and sorry for rapid-commenting (but I think this is awesome!)

Soldering things directly to the chip like that is called a "dead bug" configuration.

Maybe that's totally common knowledge, and I'm a dork for not know it, but I laughed hysterically when I heard it A guy at our lab recently was talking to me about an attiny project I was working on and was like "oh, you could deadbug it!"

Me: "Deadbug...uhhwhat?"

Him: "Deadbug!"

And he pulls out a chip with a bunch of components soldered to the bottom.

Him: "See, it looks like a dead bug!"

:)

Ugifer (author)blhack2012-06-27

Yeah - and this one is a poor dead bug with most of its legs pulled off! ;-)

blhack (author)2012-06-27

If you want to do this instructable, and don't have a programmer, check out this awesome project out of MIT's fab lab:

http://fab.cba.mit.edu/content/projects/fabisp/

(It's a programmer built on an attiny44 and some passive components. Totally awesome!)

Also check out THIS project:

http://hlt.media.mit.edu/?p=1229

Which lets you program your attiny directly from your arduino! You don't even need to solder anything. TOTALLY AWESOME!

Ugifer (author)blhack2012-06-27

You are right - I had meant to link to a couple of breadboard programming methods. I'll edit in the morning to link some.

blhack (author)2012-06-27

YEESH! These would be expensive! Do you have an ultra-cheap source on attinys?

(Because if you do, you should share it with your new best friend, blhack)

Ugifer (author)blhack2012-06-27

60p from Mouser is not too expensive for an Attiny, when a throwie costs about that (if you include a magnet). They make up in extended lifetime what they cost in extra investment! The issue for a UK order is the delivery charge but they drop that on orders over £50.

Current cheapest DIP ATTiny45 from Mouser is 43p + VAT (8p) = 51p if you buy 10 or more.

dombeef (author)2012-06-27

Couldn't you technically make them pulse like Apple computers when they sleep? I would think the AVR is powerful enough

blhack (author)dombeef2012-06-27

Yes, you can. The attiny85 can do PWM.

Source: I have done literally that (emulating my mac's light, ha)

Ugifer (author)dombeef2012-06-27

You could do that easily - and with the same hardware I think (PB4 can handle PWM AFAIK) - but I think you would have to keep the AVR awake to do it and that takes several mA overhead. However, if you did a heartbeat then slept for a couple of seconds before the next, you could get a fair lifetime.

About This Instructable

19,401views

89favorites

License:

Bio: Call me Ugi. Special offer! Make something based on one of my instructables and post a picture for a free 3-month pro membership (while stocks ... More »
More by Ugifer:Handheld Pong & Invaders on the cheap (Arduino compatible)Kid's Science Project - Reusable Hand WarmersMake a High-Altitude Balloon Tracker (Arduino)
Add instructable to: