So having made a battery powered version of this for atmega328 i wanted to minaturise it.
This is a little sensor box sketch for an attiny85 or similar. Sending temperature and humidity data to pimatic every minute and sleeping until movement is detected.
It uses the external interrupt features and the watchdog timer. I have measured power draw with my cheapo multimeter and it showed 0.12ma draw whilst the device is sleeping, and about 9ma whilst its awake, for about 3 seconds to report data.
With a very quick rough calculation, I estimate a 3000mah 18650 battery will last maybe 2 years!
your attiny bootloader and fuses must be set to 8mhz otherwise pimatic doesn’t understand the dht stuff. I hope to make it work at 1mhz next.
I have changed my sketch to work better with a DHT22 sensor as I was getting lots of zero readings from it. So below there are two sketches. The original, which will work with a DHT11, and then the DHT22 version. The DHT22 version also uses a different library, which you can get from here > https://github.com/markruys/arduino-DHT
Both sketches will now check the battery level and blink the led if the level is below 25%. They now require the following library to do this https://github.com/sweebee/Arduino-home-automation/tree/master/libraries/readVcc
Be sure to set your max and min voltages in the sketch
DHT11 VERSION
#include <readVcc.h>
#include <NewRemoteTransmitter.h>
#include <RemoteTransmitter.h>
#include <avr/sleep.h>
#include "DHT.h" //include the library to work with the DHT
KaKuTransmitter kaKuTransmitter(3); //Set the number your RF Tx is connected to (PB3 here)
int ledPin = 1; //Led pin
int MIN_V = 3200; // empty voltage (0%)
int MAX_V = 3700; // full voltage (100%)
const int unit = 2; //Change the unit number here for multiple sensors
//The values below have been identified by the documentation on weather1
#define SIGNAL_BREAK 456 //
#define SIGNAL_SHORT 1990 //
#define SIGNAL_LONG 3940 // DHT Stuff, RF protocol
#define SIGNAL_STOP 9236 //
#define REPEATS 7 //
#define RFPIN 3 //Pin that the RF transmitter is connected to
#define DHTPIN 4 //Pin that the DHT data is connected to
#define DHTTYPE DHT11 //DHT11 or DHT21 (AM2301) or DHT22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);
int MODULE_ID = (unit); //Used to identify the module in pimatic (1-255)
int CHANNEL = 1; //Multiple channels can be used (can be 1 - 4)
int TEMP_UNIT = 'C'; //may be set to F
int ID = 123; // KAKU address
const byte inPin = PCINT2; // pin 3 | PIR sensor pin
volatile boolean input = LOW;
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
NewRemoteTransmitter transmitter(ID, RFPIN, 260, 3); // Set-up transmitter
/**
This is a template of the binary message that will be send encoded
by pulses of the RF transmitter
Here is what the bits mean
0101 | 11010000 | 00 | 00 | 000100001001 | 00111101
? ID BT CH Temp. Humid.
The temperature in this example is 26.5° (but sended at 265) and will
be decoded in Pimatic again to the correct value.
The value of the relative humidity is 65%
*/
byte message[] = {
0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1
};
/*
@setMessage(int val, int startPos, int endPos)
convert an integer to binary representation and
write the output to the locations specified in the
message[]
*/
void setMessage(int val, int startPos, int endPos) {
int result[12];
int theValue = val;
for (byte i = 0, j = 11; i < 12; ++i, j--) {
result[j] = theValue & (1 << i) ? 1 : 0;
}
for (byte i = endPos, j = 11; i > startPos - 1; i--, j--) {
message[i] = result[j];
}
}
int watchdog_counter = 0;
//****************************************************************
// 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);
}
/*
@getDHTvalues()
wait for two seconds to read the values of the DHT and
replace their counterparts in the message[]
*/
void getDHTvalues() {
delay(2000); //DHT needs 2 seconds to get started
int h = (int) dht.readHumidity();
int t;
if (TEMP_UNIT == 'C') {
t = (int) (dht.readTemperature() * 10);
} else {
t = (int) (dht.readTemperature(true) * 10);
}
//if the values could not be red
if (isnan(h) || isnan(t)) {
setMessage(0, 16, 27); //Temperature
setMessage(0, 28, 35); //Humidity
} else {
setMessage(t, 16, 27); //Temperature
setMessage(h, 28, 35); //Humidity
}
}
/*
@sendMessage()
execute the getDHTvalues() method to get some input in the
message[] and then send the message X times using the sendBit() method
followed by the terminator pulse
*/
void sendMessage() {
getDHTvalues();
for (byte i = 0; i < REPEATS; i++) {
digitalWrite(ledPin, HIGH); //LED PIN
for (byte i = 0; i < sizeof(message); i++) {
sendBit(message[i]);
}
digitalWrite(ledPin, LOW);
sendTerminator();
}
}
/*
@sendBit(byte b)
takes a given byte which is either 1 or 0 and then sends it
over the air using the breaks necessary for pimatic to recognize
the profile of the a weather1 station
*/
void sendBit(byte b) {
if (b == 0) {
digitalWrite(RFPIN, HIGH);
delayMicroseconds(SIGNAL_BREAK);
digitalWrite(RFPIN, LOW);
delayMicroseconds(SIGNAL_SHORT);
}
else {
digitalWrite(RFPIN, HIGH);
delayMicroseconds(SIGNAL_BREAK);
digitalWrite(RFPIN, LOW);
delayMicroseconds(SIGNAL_LONG);
}
}
/*
@sendTerminator()
This method sends Arnold Schwarzenegger back in time. Just kidding,
actually it does deliver the long break to show the end of the
message[]
*/
void sendTerminator() {
digitalWrite(RFPIN, HIGH);
delayMicroseconds(SIGNAL_BREAK);
digitalWrite(RFPIN, LOW);
delayMicroseconds(SIGNAL_STOP);
digitalWrite(RFPIN, HIGH);
digitalWrite(RFPIN, LOW);
}
//***************************************************************
//External interrupt here
ISR(PCINT0_vect) {
digitalWrite(ledPin, HIGH);
transmitter.sendUnit((unit), true);
digitalWrite(ledPin, LOW);
}
}
//****************************************************************
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
watchdog_counter++;
}
//****************************************************************
// 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
}
void blinkled() {
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
}
void setup() {
pinMode(DHTPIN, INPUT);
pinMode(RFPIN, OUTPUT);
pinMode(inPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT); //LED Pin
digitalWrite(inPin, LOW);
setMessage(MODULE_ID, 4, 11);
setMessage(CHANNEL - 1, 14, 15);
dht.begin();
sbi(GIMSK, PCIE);
sbi(PCMSK, PCINT2);
setup_watchdog(6);
}
void loop() {
if (watchdog_counter == 60) { //Set how long between wakeups here, in seconds
watchdog_counter = 0;
sendMessage();
delay(10);
system_sleep();
sei();
}
// Measure battery
float batteryV = readVcc();
int batteryPcnt = (((batteryV - MIN_V) / (MAX_V - MIN_V)) * 100 );
if (batteryPcnt > 100) {
batteryPcnt = 100;
}
if (batteryPcnt < 25) {
blinkled();
}
system_sleep();
sei();
}
DHT22 VERSION and library https://github.com/markruys/arduino-DHT
#include <readVcc.h>
#include <DHT.h>
#include <NewRemoteTransmitter.h>
#include <RemoteTransmitter.h>
#include <avr/sleep.h>
DHT dht;
KaKuTransmitter kaKuTransmitter(3); //Set the number your RF Tx is connected to (PB3 here)
int ledPin = 1; //Led pin
int MIN_V = 3200; // empty voltage (0%)
int MAX_V = 3700; // full voltage (100%)
const int unit = 1; //Change the unit number here for multple sensors
//The values below have been identified by the documentation on weather1
#define SIGNAL_BREAK 456 //
#define SIGNAL_SHORT 1990 //
#define SIGNAL_LONG 3940 // DHT Stuff, RF protocol
#define SIGNAL_STOP 9236 //
#define REPEATS 7 //
#define RFPIN 3 //Pin that the RF transmitter is connected to
#define DHTPIN 4 //Pin that the DHT data is connected to
#define DHT22_PIN (DHTPIN);
int MODULE_ID = (unit); //Used to identify the module in pimatic (1-255)
int CHANNEL = 1; //Multiple channels can be used (can be 1 - 4)
int TEMP_UNIT = 'C'; //may be set to F
int ID = 123; // KAKU address
const byte inPin = PCINT2; // pin 3 | PIR sensor pin
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
NewRemoteTransmitter transmitter(ID, RFPIN, 260, 3); // Set-up transmitter
/**
This is a template of the binary message that will be send encoded
by pulses of the RF transmitter
Here is what the bits mean
0101 | 11010000 | 00 | 00 | 000100001001 | 00111101
? ID BT CH Temp. Humid.
The temperature in this example is 26.5° (but sended at 265) and will
be decoded in Pimatic again to the correct value.
The value of the relative humidity is 65%
*/
byte message[] = {
0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1
};
/*
@setMessage(int val, int startPos, int endPos)
convert an integer to binary representation and
write the output to the locations specified in the
message[]
*/
void setMessage(int val, int startPos, int endPos) {
int result[12];
int theValue = val;
for (byte i = 0, j = 11; i < 12; ++i, j--) {
result[j] = theValue & (1 << i) ? 1 : 0;
}
for (byte i = endPos, j = 11; i > startPos - 1; i--, j--) {
message[i] = result[j];
}
}
int watchdog_counter = 0;
//****************************************************************
// 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);
}
/*
@getDHTvalues()
wait for two seconds to read the values of the DHT and
replace their counterparts in the message[]
*/
void getDHTvalues() {
delay(2000); //DHT needs 2 seconds to get started
int h = (int) dht.getHumidity();
int t = (int) (dht.getTemperature() * 10);
//if the values could not be red
if (isnan(h) || isnan(t)) {
setMessage(0, 16, 27); //Temperature
setMessage(0, 28, 35); //Humidity
} else {
setMessage(t, 16, 27); //Temperature
setMessage(h, 28, 35); //Humidity
}
}
/*
@sendMessage()
execute the getDHTvalues() method to get some input in the
message[] and then send the message X times using the sendBit() method
followed by the terminator pulse
*/
void sendMessage() {
getDHTvalues();
for (byte i = 0; i < REPEATS; i++) {
digitalWrite(ledPin, HIGH); //LED PIN
for (byte i = 0; i < sizeof(message); i++) {
sendBit(message[i]);
}
digitalWrite(ledPin, LOW);
sendTerminator();
}
}
/*
@sendBit(byte b)
takes a given byte which is either 1 or 0 and then sends it
over the air using the breaks necessary for pimatic to recognize
the profile of the a weather1 station
*/
void sendBit(byte b) {
if (b == 0) {
digitalWrite(RFPIN, HIGH);
delayMicroseconds(SIGNAL_BREAK);
digitalWrite(RFPIN, LOW);
delayMicroseconds(SIGNAL_SHORT);
}
else {
digitalWrite(RFPIN, HIGH);
delayMicroseconds(SIGNAL_BREAK);
digitalWrite(RFPIN, LOW);
delayMicroseconds(SIGNAL_LONG);
}
}
/*
@sendTerminator()
This method sends Arnold Schwarzenegger back in time. Just kidding,
actually it does deliver the long break to show the end of the
message[]
*/
void sendTerminator() {
digitalWrite(RFPIN, HIGH);
delayMicroseconds(SIGNAL_BREAK);
digitalWrite(RFPIN, LOW);
delayMicroseconds(SIGNAL_STOP);
digitalWrite(RFPIN, HIGH);
digitalWrite(RFPIN, LOW);
}
//***************************************************************
//External interrupt here
ISR(PCINT0_vect) {
digitalWrite(ledPin, HIGH);
transmitter.sendUnit((unit), true);
digitalWrite(ledPin, LOW);
}
//****************************************************************
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
watchdog_counter++;
}
//****************************************************************
// 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
}
void blinkled() {
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
delay(300);
digitalWrite(ledPin, LOW);
}
void setup() {
pinMode(DHTPIN, INPUT);
pinMode(RFPIN, OUTPUT);
pinMode(inPin, INPUT);
pinMode(ledPin, OUTPUT); //LED Pin
digitalWrite(inPin, LOW);
setMessage(MODULE_ID, 4, 11);
setMessage(CHANNEL - 1, 14, 15);
dht.setup(4);
sbi(GIMSK, PCIE);
sbi(PCMSK, PCINT2);
setup_watchdog(6);
}
void loop() {
if (watchdog_counter == 60) { //Set how long between wakeups here, in seconds
watchdog_counter = 0;
sendMessage();
delay(10);
}
// Measure battery
float batteryV = readVcc();
int batteryPcnt = (((batteryV - MIN_V) / (MAX_V - MIN_V)) * 100 );
if (batteryPcnt > 100) {
batteryPcnt = 100;
}
if (batteryPcnt < 25) {
blinkled();
}
system_sleep();
sei();
}
and my config.json looks like this
{
"id": "dhttemp",
"name": "Temp + Hum sensor",
"class": "HomeduinoRFTemperature",
"protocols": [
{
"name": "weather1",
"options": {
"id": 1,
"channel": 1
}
}
]
},
{
"id": "homeduino-pir",
"name": "PIR",
"class": "HomeduinoRFPir",
"protocols": [
{
"name": "pir5",
"options": {
"unit": 123,
"id": 1
}
}
],
"resetTime": 6000
},
Once again, I apologise if the code seems messy. This is all completely new to me. All i know is it works!