Go Down

Topic: Cable locker intruder alarm (a very simple setup) (Read 3028 times) previous topic - next topic

Erni

You could take a look at Jack Christensens sleep code for the ATtiny85.

http://forum.arduino.cc/index.php?topic=167467.0

I have used it with this core without issues:

https://code.google.com/p/arduino-tiny/


Peter_I

#16
Feb 06, 2014, 08:01 pm Last Edit: Feb 06, 2014, 08:23 pm by Peter_I Reason: 1
Success!
Or at least not total failure
(I changed so many parameters, I'm not quite sure what made a difference  :smiley-red: )

Now it will compile, but still not fall asleep.
(This starts to remind me, of when the kids were small!)

I uninstalled the core, and reinstalled the one Erni linked to.
Then I transplanted major parts of the code from here:

(I tested it on a tiny with an LED. It goes to 5 microamps when sleeping!)
http://www.insidegadgets.com/2011/02/05/reduce-attiny-power-consumption-by-sleeping-with-the-watchdog-timer/
Code: [Select]
/*
* Watchdog Sleep Example
* Demonstrate the Watchdog and Sleep Functions
* LED on digital pin 0
*
* KHM 2008 / Lab3/  Martin Nawrath nawrath@khm.de
* Kunsthochschule fuer Medien Koeln
* Academy of Media Arts Cologne
*
* Modified on 5 Feb 2011 by InsideGadgets (www.insidegadgets.com)
* to suit the ATtiny85 and removed the cbi( MCUCR,SE ) section
* in setup() to match the Atmel datasheet recommendations
*/

#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

int pinLed = 0;
volatile boolean f_wdt = 1;

void setup(){
 pinMode(pinLed,OUTPUT);
 setup_watchdog(8); // approximately 4 seconds sleep
}

void loop(){
 if (f_wdt==1) {  // wait for timed out watchdog / flag is set when a watchdog timeout occurs
   f_wdt=0;       // reset flag

   digitalWrite(pinLed,HIGH);  // let led blink
   delay(1000);
   digitalWrite(pinLed,LOW);

   pinMode(pinLed,INPUT); // set all used port to intput to save power
   system_sleep();
   pinMode(pinLed,OUTPUT); // set all ports into state before sleep
 }
}

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

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

 byte bb;
 int ww;
 if (ii > 9 ) ii=9;
 bb=ii & 7;
 if (ii > 7) bb|= (1<<5);
 bb|= (1<<WDCE);
 ww=bb;

 MCUSR &= ~(1<<WDRF);
 // start timed sequence
 WDTCR |= (1<<WDCE) | (1<<WDE);
 // set new watchdog timeout value
 WDTCR = bb;
 WDTCR |= _BV(WDIE);
}
 
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
 f_wdt=1;  // set global flag
}



And it ended up looking like this:

Compiling, but not going to sleep like a good little Tiny should!


Code: [Select]
/*
 Cable storage locker alarm
 
 ATTINY VERSION 5
 With power saving according to http://www.insidegadgets.com/2011/02/05/reduce-attiny-power-consumption-by-sleeping-with-the-watchdog-timer/
Code transplanted shamelessly from the "ATtiny85_watchdog_example"

Callibration is based on
http://arduino.cc/en/Tutorial/Calibration
David A Mellis and Tom Igoe


By Peter_I 2014

The circuit:
* LDR attached to analog input 1 and 5V with 10k pull down to GND
* Siren attached digital pin 0
* Piezo beeper attached to pin 1 (PWM)
(If used on Arduino, adjust pins accordingly)

Only sensorMax is interesting, and an "intrusion" is considered to be when
"sensorMax + treshold" is exceeded.
"Intrusion" results in short beeps, 2 sec delay followed by the siren.
The short beeps will remind me to switch off the alarm when I open the locker.

*/

#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


//The constants:
const int sensorPin = A1;     // pin that the sensor is attached to
const int sirenPin = 0;        // pin that the LED is attached to, 0 on ATTiny
const int beepPin = 1;        // piezobeeper on PWM pin
const int treshold = 100;      // treshold value
const int beepLength = 1000;     // A long beep, max 1500ms
const int shortBeep = 100;      // and a shorter beep
const int sirenTime = 30000; // How long to wail


// variables:
int sensorValue = 0;         // the sensor value
int sensorMax = 0;           // maximum sensor value
long previousMillis = 0;        // for timing
unsigned long currentMillis = 0; // for timing

volatile boolean f_wdt = 1;



void setup()
{
setup_watchdog(8); // approximately 4 seconds sleep
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
// Serial.begin(9600);    // comm, disable on ATTiny
// turn on LED to signal the start of the calibration period:
pinMode(sirenPin, OUTPUT);
pinMode (beepPin, OUTPUT);

analogWrite(beepPin, 128);  //3 beeps to give time to close door
delay (beepLength);
analogWrite(beepPin, 0);
delay (beepLength);
analogWrite(beepPin, 128);
delay (beepLength);
analogWrite(beepPin, 0);
delay (beepLength);
analogWrite(beepPin, 128);
delay (beepLength);
analogWrite(beepPin, 0);
// calibrate during the next five seconds
while (millis() < 10000)
{
sensorValue = analogRead(sensorPin);
// record the maximum sensor value
if (sensorValue > sensorMax)
{
sensorMax = sensorValue;
//      Serial.print("sensorMax=   ");
//      Serial.println(sensorMax);
}
}
// signal the end of the calibration period
analogWrite(beepPin, 128);  //Long beep to signal "ARMED"
delay (2*beepLength);
analogWrite(beepPin, 0);
}

void loop()
{
 if (f_wdt==1) // wait for timed out watchdog / flag is set when a watchdog timeout occurs
 {  
   f_wdt=0;       // reset flag
sensorValue = analogRead(sensorPin);  // read the sensor
if (sensorValue > treshold + sensorMax)  //is there light from an open door?
{
//   Serial.println(sensorValue);
//3 quick beeps to signal "Alarm tripped!"
analogWrite(beepPin, 128);  
delay (shortBeep);
analogWrite(beepPin, 0);
delay (2*shortBeep);
analogWrite(beepPin, 128);  
delay (shortBeep);
analogWrite(beepPin, 0);
delay (2*shortBeep);
analogWrite(beepPin, 128);  
delay (shortBeep);
analogWrite(beepPin, 0);
delay (2*shortBeep);
// end of beepery. Now, you have 2 more seconds to switch off, or I'll scream
delay (2000);        // said 2 seconds
digitalWrite (sirenPin, HIGH); // You didn't make it:  Wheeeeeeeeeeeeeeeeeeeeeeeeeee!
delay (sirenTime);        
}   // end of "is there an intruder?" if loop
else
digitalWrite (sirenPin, LOW);      // off
delay (1);

 } // end of watchdog loop

}  //end of void loop

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

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

 byte bb;
 int ww;
 if (ii > 9 ) ii=9;
 bb=ii & 7;
 if (ii > 7) bb|= (1<<5);
 bb|= (1<<WDCE);
 ww=bb;

 MCUSR &= ~(1<<WDRF);
 // start timed sequence
 WDTCR |= (1<<WDCE) | (1<<WDE);
 // set new watchdog timeout value
 WDTCR = bb;
 WDTCR |= _BV(WDIE);
}
 
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
 f_wdt=1;  // set global flag
}
"Nothing is foolproof to a sufficiently talented fool"

Coding Badly


Peter_I

#18
Feb 08, 2014, 03:47 pm Last Edit: Feb 08, 2014, 04:31 pm by Peter_I Reason: 1

Your code never calls system_sleep.


Ouch, that was embarrassing!
:smiley-red:
And it did the trick!
Wohoooooooooo!
Now it obediently goes to sleep, and wakes up once every second to see if there is anything to scream about.
It measures so quickly, that my multimeter can't read how much current it draws. It just flickers a bit when measuring.

Currents during use (according to my cheap multimeter):

Starting and beeping: 19 mA
Calibrating: 7-8 mA
Idle in light: 0.3 mA  (It will draw some current in light because of the LDR)
Activated, feeding power to transistor: 11 mA
Activated, siren screaming: 80 mA
Idle in total darkness: 8 microAmperes !!!

That is far better than anything I had dared hope for!
I do not have the means to measure the average power drawn, but I feel confident, that the short proportion of the time spent measuring, and reducing the idle current with a factor of 1000, should give me at least a year of battery life, instead of just a week.


....And I have learned the hard way, that the sleep code is different for different AVRs.
The code that will compile for the Uno, will not compile on the Attiny85 (and the other way around).
Choosing the right board when developing the code is important.

I have not checked, if that would have solved my previous problems (but it is not unlikely that it would).

The code, that could use a bit of cleaning up, so far:
Code: [Select]
/*
 Cable storage locker alarm
 
 ATTINY VERSION 7
 With power saving according to http://www.insidegadgets.com/2011/02/05/reduce-attiny-power-consumption-by-sleeping-with-the-watchdog-timer/
Code transplanted shamelessly from the "ATtiny85_watchdog_example"

Callibration is based on
http://arduino.cc/en/Tutorial/Calibration
David A Mellis and Tom Igoe


By Peter_I 2014

The circuit:
* LDR attached to analog input 1 and 5V with 10k pull down to GND
* Siren attached digital pin 0
* Piezo beeper attached to pin 1 (PWM)
(This code is tested on an Attiny85 @ 8 mHz with internal oscillator and BOD disabled,
it will not compile on an Arduino Uno, because the sleep code for the
Atmega328 is different)

Only sensorMax is interesting, and an "intrusion" is considered to be when
"sensorMax + treshold" is exceeded.
"Intrusion" results in short beeps, 2 sec delay followed by the siren.
The short beeps will remind me to switch off the alarm when I open the locker, and give me time
to switch off the alarm.

*/

#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


//The constants:
const int sensorPin = A1;     // pin that the sensor is attached to
const int sirenPin = 0;        // pin that the LED is attached to, 0 on ATTiny
const int beepPin = 1;        // piezobeeper on PWM pin
const int treshold = 100;      // treshold value
const int beepLength = 1000;     // A long beep, max 1500ms
const int shortBeep = 100;      // and a shorter beep
const int sirenTime = 30000; // How long to wail


// variables:
int sensorValue = 0;         // the sensor value
int sensorMax = 0;           // maximum sensor value
long previousMillis = 0;        // for timing
unsigned long currentMillis = 0; // for timing

volatile boolean f_wdt = 1;



void setup()
{
setup_watchdog(6); // approximately 1 second sleep
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
// Serial.begin(9600);    // comm, disable on ATTiny
// turn on LED to signal the start of the calibration period:
pinMode(sirenPin, OUTPUT);
pinMode (beepPin, OUTPUT);

analogWrite(beepPin, 128);  //3 beeps to give time to close door
delay (beepLength);
analogWrite(beepPin, 0);
delay (beepLength);
analogWrite(beepPin, 128);
delay (beepLength);
analogWrite(beepPin, 0);
delay (beepLength);
analogWrite(beepPin, 128);
delay (beepLength);
analogWrite(beepPin, 0);
// calibrate during the next five seconds
while (millis() < 10000)
{
sensorValue = analogRead(sensorPin);
// record the maximum sensor value
if (sensorValue > sensorMax)
{
sensorMax = sensorValue;
//      Serial.print("sensorMax=   ");
//      Serial.println(sensorMax);
}
}
// signal the end of the calibration period
analogWrite(beepPin, 128);  //Long beep to signal "ARMED"
delay (2*beepLength);
analogWrite(beepPin, 0);
}

void loop()
{
 if (f_wdt==1) // wait for timed out watchdog / flag is set when a watchdog timeout occurs
 {  
   f_wdt=0;       // reset flag
delay (1);
sensorValue = analogRead(sensorPin);  // read the sensor
if (sensorValue > treshold + sensorMax)  //is there light from an open door?
{
//   Serial.println(sensorValue);
//3 quick beeps to signal "Alarm tripped!"
analogWrite(beepPin, 128);  
delay (shortBeep);
analogWrite(beepPin, 0);
delay (2*shortBeep);
analogWrite(beepPin, 128);  
delay (shortBeep);
analogWrite(beepPin, 0);
delay (2*shortBeep);
analogWrite(beepPin, 128);  
delay (shortBeep);
analogWrite(beepPin, 0);
delay (2*shortBeep);
// end of beepery. Now, you have 2 more seconds to switch off, or I'll scream
delay (2000);        // said 2 seconds
digitalWrite (sirenPin, HIGH); // You didn't make it:  Wheeeeeeeeeeeeeeeeeeeeeeeeeee!
delay (sirenTime);        
}   // end of "is there an intruder?" if loop
else
{digitalWrite (sirenPin, LOW);      // off
   delay (1);
   
   system_sleep(); //Thanks to Coding Badly for reminding me of this line!
   
}  // end of else
 } // end of watchdog loop

}  //end of void loop

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

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

 byte bb;
 int ww;
 if (ii > 9 ) ii=9;
 bb=ii & 7;
 if (ii > 7) bb|= (1<<5);
 bb|= (1<<WDCE);
 ww=bb;

 MCUSR &= ~(1<<WDRF);
 // start timed sequence
 WDTCR |= (1<<WDCE) | (1<<WDE);
 // set new watchdog timeout value
 WDTCR = bb;
 WDTCR |= _BV(WDIE);
}
 
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
 f_wdt=1;  // set global flag
}
"Nothing is foolproof to a sufficiently talented fool"

Peter_I

I popped by a friend who is the owner of a magnificent oscilloscope.

"Waking up, doing the measurement and going back to sleep " takes 2.1 ms.

Doing that once a second must mean, that the power consumption is reduced by a factor 500 relative to being constantly on.


I could bring the time lower, since I've given it a delay of 1 ms before measuring (to let it stabilize) and measuring once a second, is far more often than needed. Every second or every fourth second should be plenty.

But my back-of-a-napkin estimate tells me, that from a 2000 mA/h battery pack, that should give me something like 10 years of stand by. The batteries are likely to die from age or boredom before then.

I'll measure the batteries in a couple of months, and see how they are.

(And should some cable pilfering student get a shock during pilferous activities, I'll tell it here too  :smiley-mr-green: )
"Nothing is foolproof to a sufficiently talented fool"

Peter_I

Oh joy!

It took less than an hour between setting it up today, and catching my suspected student red-handed!

The effect was great, in his own words, he was close to being caught brown-undied, rather than red-handed.

(But the rascal still had the nerves to grab the extension cord he came for!)


I might have to attach a really mean siren!
"Nothing is foolproof to a sufficiently talented fool"

Go Up