Commit f4dfc3eb authored by Peter Müller's avatar Peter Müller

Initial commit

parents
# ATtiny85 RadioHead DHT22 Weather Station
Arduino sketch for using an ATtiny85 microprocessor and a DHT22 humidity/temperature sensor as a battery powered weather station.
The measured values are transmitted using [RadioHead](http://www.airspayce.com/mikem/arduino/RadioHead/) and a 433MHz radio module.
[![breadboard](https://cdn.cryhost.de/arduino/attiny85-radiohead-dht22-weather-sensor-breadboard.png)](https://cdn.cryhost.de/arduino/attiny85-radiohead-dht22-weather-sensor-breadboard.png)
## Power supply
The power supply consists of 3 AA batteries and on DC boost converter.
The boost converter takes 0.6V to nearly 5V and gives a stable output of 5V.
## Power saving
The ATtiny85 is sleeping most of the time in power down mode.
The watchdog wakes the controller up every 8 seconds.
After 7 wake ups the temperature and humidity are measured and transmitted.
Additionally every 30 measurements the battery status is read using the ADC.
## Transmitted RadioHead messages
There are four different types of transmitted messages.
### Start message
This message is transmitted once if the controller starts.
It's one byte containing `0x00`.
### Temperature and humidity
This is a 9 byte message starting with the code `0x01`, followed by 4 byte temperature and 4 byte humidity as float using the little-endian format.
`0x01 t t t t h h h h`, e.g. `0x01 0xcd 0xcc 0xd0 0x41 0xcd 0xcc 0x4e 0x42`
### Battery status
This is a 3 byte message starting with the code `0x02`, followed by one byte with the battery percentage (0 to 100) and one byte with the raw ADC value (0 to 255).
e.g. `0x02 0x64 0xe7`
### Error message
If an error occurred during temperature/humidity measuring this one byte error message containing `0xee` is sent.
## ATtiny85 in ArduinoIDE
To be able to program an ATtiny microprocessor you need to add the following boards-manager URL and install the `attiny` package.
`https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json`
## License
Licensed under GPL Version 2
Copyright (c) 2017 Peter Müller <peter@crycode.de> (https://crycode.de/)
/*
* RadioHead DHT22 Weather Station
*
* Copyright (c) 2017 Peter Müller <peter@crycode.de> (https://crycode.de)
*/
/**************
* config *
**************/
// pin with the LED connected
#define LED_PIN 4
// blink time for the LED
#define LED_TIME 200
// pin of the DHT22 sensor
#define DHT_PIN 1
// the own RadioHead address
#define RH_OWN_ADDR 0xca // 202
// the server RadioHead address
#define RH_SERVER_ADDR 0x01
// RadioHead bitrate in bit/s
#define RH_SPEED 2000
// pins for the radio hardware
#define RH_RX_PIN 5 // not used, set to a non-existens pin
#define RH_TX_PIN 3
#define RH_PTT_PIN 5 // not used, set to a non-existens pin
// time until the watchdog wakes the mc in seconds
#define WATCHDOG_TIME 8 // 1, 2, 4 or 8
// after how many watchdog wakeups we should collect and send the data
#define WATCHDOG_WAKEUPS_TARGET 7 // 8 * 7 = 56 seconds between each data collection
// after how many data collections we should get the battery status
#define BAT_CHECK_INTERVAL 30
// min max values for the ADC to calculate the battery percent
#define BAT_ADC_MIN 40 // ~0,78V
#define BAT_ADC_MAX 225 // ~4,41V
/**************
* end config *
**************/
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#if BAT_ADC_MIN >= BAT_ADC_MAX
#error BAT_ADC_MAX must be greater than BAT_ADC_MIN!
#endif
// DHT22 lib
// not using the current adafruit dht library, because it eats too many memory
#include "dht22.h"
dht22 dht;
// buffer for RadioHead messages
// rh_buf[0] - control byte
// control byte 0x00
// start message
// control byte 0x01
// rh_buf[1-4] - temperature as float
// rh_buf[5-8] - humidity as float
// control byte 0x02
// rh_buf[1] - battery status in %
// rh_buf[2] - battery raw ADC value (0 to 255)
// control byte 0xEE
// error reading temperature/humidity
#define RH_BUF_LEN 9
uint8_t rh_buf[RH_BUF_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// reduce the RadioHead max message length to save memory
#define RH_ASK_MAX_MESSAGE_LEN RH_BUF_LEN
// RadioHead
#include <RH_ASK.h>
#include <RHDatagram.h>
#include <RHReliableDatagram.h> // only needed for some constants
RH_ASK rh_driver(RH_SPEED, RH_RX_PIN, RH_TX_PIN, RH_PTT_PIN);
RHDatagram rh_manager(rh_driver, RH_OWN_ADDR);
// some mcs (e.g atmega328) have WDTCSR instead of WTDCR
#if !defined WDTCR and defined WDTCSR
#define WDTCR WDTCSR
#endif
// header ID for the RadioHead message
uint8_t rh_id = 0;
// counter for the battery check, starting at BAT_CHECK_INTERVAL to transmit the battery status on first loop
uint8_t bat_check_count = BAT_CHECK_INTERVAL;
void enableWatchdog()
{
cli();
// clear the reset flag
MCUSR &= ~(1<<WDRF);
// set WDCE to be able to change/set WDE
WDTCR |= (1<<WDCE) | (1<<WDE);
// set new watchdog timeout prescaler value
#if WATCHDOG_TIME == 1
WDTCR = 1<<WDP1 | 1<<WDP2;
#elif WATCHDOG_TIME == 2
WDTCR = 1<<WDP0 | 1<<WDP1 | 1<<WDP2;
#elif WATCHDOG_TIME == 4
WDTCR = 1<<WDP3;
#elif WATCHDOG_TIME == 8
WDTCR = 1<<WDP0 | 1<<WDP3;
#else
#error WATCHDOG_TIME must be 1, 2, 4 or 8!
#endif
// enable the WD interrupt to get an interrupt instead of a reset
WDTCR |= (1<<WDIE);
sei();
}
// function to go to sleep
void enterSleep(void)
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN); /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
sleep_enable();
/* Now enter sleep mode. */
sleep_mode();
/* The program will continue from here after the WDT timeout*/
sleep_disable(); /* First thing to do is disable sleep. */
}
void setup() {
// setup the LED pin
pinMode(LED_PIN, OUTPUT);
// setup the ADC
ADMUX =
(1 << ADLAR) | // left shift result
(0 << REFS1) | // Sets ref. voltage to VCC, bit 1
(0 << REFS0) | // Sets ref. voltage to VCC, bit 0
(0 << MUX3) | // use ADC1 for input (PB2), MUX bit 3
(0 << MUX2) | // use ADC1 for input (PB2), MUX bit 2
(0 << MUX1) | // use ADC1 for input (PB2), MUX bit 1
(1 << MUX0); // use ADC1 for input (PB2), MUX bit 0
ADCSRA =
(1 << ADEN) | // enable ADC
(1 << ADPS2) | // set prescaler to 64, bit 2
(1 << ADPS1) | // set prescaler to 64, bit 1
(0 << ADPS0); // set prescaler to 64, bit 0
// disable ADC for powersaving
ADCSRA &= ~(1<<ADEN);
// disable analog comperator for powersaving
ACSR |= (1<<ACD);
// init RadioHead
if(!rh_manager.init()){
// init failed, blink 10 times, then go to sleep
for(uint8_t i=0; i<10; i++){
digitalWrite(LED_PIN, HIGH);
_delay_ms(LED_TIME);
digitalWrite(LED_PIN, LOW);
_delay_ms(LED_TIME);
}
enterSleep();
}
// init the DHT22
dht_init(&dht, DHT_PIN);
// send start message
rh_buf[0] = 0x00;
rh_send(1);
// blink 3 times
for(uint8_t i=0; i<3; i++){
digitalWrite(LED_PIN, HIGH);
_delay_ms(LED_TIME);
digitalWrite(LED_PIN, LOW);
_delay_ms(LED_TIME);
}
// enable the watchdog
enableWatchdog();
}
// main loop
void loop() {
// turn on the LED
digitalWrite(LED_PIN, HIGH);
// battery check/status
bat_check_count++;
if(bat_check_count >= BAT_CHECK_INTERVAL){
bat_check();
bat_check_count = 0;
}
// temperature and humidity
float t = 0;
float h = 0;
// read from the sensor and check if it was successfull
if(dht_read_data(&dht, &t, &h) == 1){
// normal message
rh_buf[0] = 0x01;
// copy to buffer
memcpy(&rh_buf[1],&t,4);
memcpy(&rh_buf[5],&h,4);
rh_send(RH_BUF_LEN);
}else{
// error message
rh_buf[0] = 0xee;
rh_send(1);
}
// turn off the LED
digitalWrite(LED_PIN, LOW);
// deep sleep
for(uint8_t i=0;i < WATCHDOG_WAKEUPS_TARGET;i++){
enterSleep();
}
}
// function to read and send the battery status
void bat_check(){
// enable the ADC
ADCSRA |= (1<<ADEN);
// short delay
_delay_ms(10);
ADCSRA |= (1 << ADSC); // start ADC measurement
while ( ADCSRA & (1 << ADSC) ); // wait till conversion complete
int16_t adc = ADCH;
// clear the ADIF bit by writing 1 to it
ADCSRA|=(1<<ADIF);
// disable the ADC
ADCSRA &= ~(1<<ADEN);
// write the raw value into the buffer
rh_buf[2] = adc;
// calc the battery status in percent, respecting min and max
adc = 100 * ( adc - BAT_ADC_MIN ) / (BAT_ADC_MAX - BAT_ADC_MIN);
if(adc > 100){
adc = 100;
}else if(adc < 0){
adc = 0;
}
// write it into the buffer
rh_buf[1] = adc;
// set command byte to 0x02 for battery status
rh_buf[0] = 0x02;
// send 3 bytes from the buffer
rh_send(3);
}
// function to send RadioHead messages from the buffer
void rh_send(uint8_t len){
// set header ID
rh_id++;
rh_manager.setHeaderId(rh_id);
// set/reset flags
rh_manager.setHeaderFlags(RH_FLAGS_NONE, RH_FLAGS_ACK);
// send the data
rh_manager.sendto(rh_buf, len, RH_SERVER_ADDR);
// wait until sending data is done
rh_manager.waitPacketSent();
}
// watchdog ISR
ISR(WDT_vect){
// nothing to do here, just wake up
}
/* Fast DHT Lirary
*
* Copyright (C) 2015 Sergey Denisov.
* Written by Sergey Denisov aka LittleBuster (DenisovS21@gmail.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version 3
* of the Licence, or (at your option) any later version.
*
* Original library written by Adafruit Industries. MIT license.
*
* https://github.com/LittleBuster/avr-dht22
*/
#include "dht22.h"
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define DHT_COUNT 6
#define DHT_MAXTIMINGS 85
void dht_init(struct dht22 *dht, uint8_t pin)
{
dht->pin = pin;
/* Setup the pins! */
DDR_DHT &= ~(1 << dht->pin);
PORT_DHT |= (1 << dht->pin);
}
static uint8_t dht_read(struct dht22 *dht)
{
uint8_t tmp;
uint8_t sum = 0;
uint8_t j = 0, i;
uint8_t last_state = 1;
uint16_t counter = 0;
/*
* Pull the pin 1 and wait 250 milliseconds
*/
PORT_DHT |= (1 << dht->pin);
_delay_ms(250);
dht->data[0] = dht->data[1] = dht->data[2] = dht->data[3] = dht->data[4] = 0;
/* Now pull it low for ~20 milliseconds */
DDR_DHT |= (1 << dht->pin);
PORT_DHT &= ~(1 << dht->pin);
_delay_ms(20);
cli();
PORT_DHT |= (1 << dht->pin);
_delay_us(40);
DDR_DHT &= ~(1 << dht->pin);
/* Read the timings */
for (i = 0; i < DHT_MAXTIMINGS; i++) {
counter = 0;
while (1) {
tmp = ((PIN_DHT & (1 << dht->pin)) >> 1);
_delay_us(3);
if (tmp != last_state)
break;
counter++;
_delay_us(1);
if (counter == 255)
break;
}
last_state = ((PIN_DHT & (1 << dht->pin)) >> 1);
if (counter == 255)
break;
/* Ignore first 3 transitions */
if ((i >= 4) && (i % 2 == 0)) {
/* Shove each bit into the storage bytes */
dht->data[j/8] <<= 1;
if (counter > DHT_COUNT)
dht->data[j/8] |= 1;
j++;
}
}
sei();
sum = dht->data[0] + dht->data[1] + dht->data[2] + dht->data[3];
if ((j >= 40) && (dht->data[4] == (sum & 0xFF)))
return 1;
return 0;
}
uint8_t dht_read_temp(struct dht22 *dht, float *temp)
{
if (dht_read(dht)) {
*temp = dht->data[2] & 0x7F;
*temp *= 256;
*temp += dht->data[3];
*temp /= 10;
if (dht->data[2] & 0x80)
*temp *= -1;
return 1;
}
return 0;
}
uint8_t dht_read_hum(struct dht22 *dht, float *hum)
{
if (dht_read(dht)) {
*hum = dht->data[0];
*hum *= 256;
*hum += dht->data[1];
*hum /= 10;
if (*hum == 0.0f)
return 0;
return 1;
}
return 0;
}
uint8_t dht_read_data(struct dht22 *dht, float *temp, float *hum)
{
if (dht_read(dht)) {
/* Reading temperature */
*temp = dht->data[2] & 0x7F;
*temp *= 256;
*temp += dht->data[3];
*temp /= 10;
if (dht->data[2] & 0x80)
*temp *= -1;
/* Reading humidity */
*hum = dht->data[0];
*hum *= 256;
*hum += dht->data[1];
*hum /= 10;
if (*hum == 0.0f)
return 0;
return 1;
}
return 0;
}
/* struct dht22 AVR Lirary
*
* Copyright (C) 2015 Sergey Denisov.
* Written by Sergey Denisov aka LittleBuster (DenisovS21@gmail.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version 3
* of the Licence, or (at your option) any later version.
*
* Original library written by Adafruit Industries. MIT license.
*
* https://github.com/LittleBuster/avr-dht22
*/
#ifndef __DHT22_H__
#define __DHT22_H__
#include <stdint.h>
/*
* Sensor's port
*/
#define DDR_DHT DDRB
#define PORT_DHT PORTB
#define PIN_DHT PINB
struct dht22 {
uint8_t data[6]; /* data from sensor store here */
uint8_t pin; /* DDR & PORT pin */
};
/**
* Init dht sensor
* @dht: sensor struct
* @pin: PORT & DDR pin
*/
void dht_init(struct dht22 *dht, uint8_t pin);
/**
* Reading temperature from sensor
* @dht: sensor struct
* @temp: out temperature pointer
*
* Returns 1 if succeful reading
* Returns 0 if fail reading
*/
uint8_t dht_read_temp(struct dht22 *dht, float *temp);
/**
* Reading humidity from sensor
* @dht: sensor struct
* @hum: out humidity pointer
*
* Returns 1 if succeful reading
* Returns 0 if fail reading
*/
uint8_t dht_read_hum(struct dht22 *dht, float *hum);
/**
* Reading temperature and humidity from sensor
* @dht: sensor struct
* @temp: out temperature pointer
* @hum: out humidity pointer
*
* Returns 1 if succeful reading
* Returns 0 if fail reading
*
* The fastest function for getting temperature + humidity.
*/
uint8_t dht_read_data(struct dht22 *dht, float *temp, float *hum);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment