Thursday, 17 November 2016

Wireless DHT22 Sensor 2.0 using ATtiny85

In my previous post I wrote about Wireless DHT22 Sensor for Arduino Weather Station. I used two Arduino boards. One as a wireless temperature/humidity sensor and the other one as a receiver to display measurements. That was a test project just to see if it's possible and how hard can it be. Now I've decided to make it more cost efficient using ATtiny85 in the transmitter and make the receiver display not only outdoor measurement but indoor as well (including temperature, humidity, atmospheric pressure, time, date and battery level). But this post is only about outdoor part, I've decided to divide all in two posts to give more information and make it easier to understand.

So, here's a new schematic diagram
Test circuit
Test circuit of Wireless DHT22 Sensor using ATtiny85
 

The Wireless DHT22 Sensor itself

And the sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// ATTINY25/45/85 pinout for ARDUINO
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+
#include <avr/sleep.h>                      // sleep library
#include <avr/wdt.h>                        // watchdog library
#define adc_disable() (ADCSRA &= ~(1<<ADEN))// disable ADC (before power-off)
#define adc_enable()  (ADCSRA |=  (1<<ADEN))// re-enable ADC

#include <RCSwitch.h>                       // transmitter/receiver library
RCSwitch mySwitch = RCSwitch();
#define key1  303000                        // key for temperature
#define key2  305000                        // key for humidity
#define key3  307000                        // key for battery voltage

#include <Small_dht.h>
dht DHT;
#define DHT22_PIN 0

byte i = 3;                                 // watchdog counter value
volatile int f_wdt = 1;                     // watchdog state

void setup() {
  MCUSR &= ~(1 << WDRF);                    // reset watchdog status flag
  WDTCR |= (1 << WDCE) | (1 << WDE);
  WDTCR = 1 << WDP0 | 1 << WDP3;            // set watchdog to 8 seconds
  WDTCR |= _BV(WDIE);                       // wnable the WD interrupt (no reset)
  ADMUX = _BV(MUX3) | _BV(MUX2);            // reads internal 1V1 vref against VCC
  pinMode(1, OUTPUT);                       // powering sensor from pin 1
  mySwitch.enableTransmit(2);               // using Pin2 to toggle transmitter
}

void loop() {
  i++;                                      // just add 1 to i and do nothing
  if (i == 4) {                             // until i=4 and do the program
    adc_enable();                           // enable ADC
    digitalWrite(1, HIGH);                  // power up DHT sensor
    delay(2000);                            // wait 2 seconds
    int chk = DHT.read22(DHT22_PIN);        // read sensor
    sendRC(DHT.temperature*10+500+key1);    // transmit temperature
    sendRC(DHT.humidity*10+key2);           // transmit humidity
    sendRC(getVCC() + key3);                // transmit battery value
    digitalWrite(1, LOW);                   // power down DHT sensor
    i = 0;                                  // i=0 to start all over when wake up
  }
  system_sleep();                           // go to sleep
}

void sendRC(unsigned long code) {           // data send function
  mySwitch.send(code, 20);
}

void system_sleep() {                       // sleep function
  adc_disable();                            // disable ADC
  digitalWrite(0, LOW);                     // pulldown all used pins
  digitalWrite(1, LOW);                     // to save more power
  digitalWrite(2, LOW);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);      // selecting sleep mode
  sleep_enable();                           // allow sleep
  sleep_mode();                             // sleep!
  sleep_disable();                          // wake up here
}
ISR(WDT_vect)                               // reset watchdog state function
{
  if (f_wdt == 0) f_wdt = 1;
}

int getVCC() {                              // Returns ADC value on the power pin
  ADCSRA |= _BV(ADSC); // Convert           // alowes to get the battery voltage
  while (bit_is_set(ADCSRA, ADSC));         //without using any additional pin
  byte low = ADCL;
  unsigned int val = (ADCH << 8) | low;
  //discard previous result
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA, ADSC));
  low = ADCL;
  val = (ADCH << 8) | low;
  return val;
  //return ((long)1024 * 1100) / val;      // return value in mV (don't need this)
}

Some major changes to the code ware made since previous version. Had to adopt it for ATtiny85 microcontroller. 
  • <Adafruit_SleepyDog.h>  doesn't work with ATtiny85, so I've created the function
    void system_sleep() that does the job putting the controller to sleep and wake it up using watchdog timer.
  • Instead of standard <DHT.h> library I'm using <Small_dht.h> (download here) which operates the same way but uses a whole 1k less of the controllers memory. Not that 8k is not enough to use standard library, but I prefer to have some spare memory for future improvements.
  • Instead of using analog input with voltage divider to measure battery voltage ATtiny (and ATmega) has a possibility to do this internally, so no external components are recurred and also this allows to save few micro Amps of quiescent current. Function
    int getVCC() does the job. Currently I'm using raw ADC value to transmit, but if you need to get voltage (in mV) use "return ((long)1024 * 1100) / val;" instead of just "return val;".
A few words about how to upload this sketch to your ATtiny85.

First, you've got to have a programmer like this one or use your Ardoino as ISP programmer, see here. Then create a board in boards.txt file. In my case it is situated here ...\arduino-1.6.7\hardware\arduino\avr\boards.txt
There is three ways to do this
  1. The easy one. Arduino IDE already has a board Gemma which is using ATtiny85, but it is using bootloader also. As a result, it has only 5310 bytes of chip memory available, that's not enough for this project. What a waste! So, open the file boards.txt, scroll down, find this:
    and change  the upload maximum size. Done. Restart Arduino IDE and be happy)
  2. What if you would like to use the real Gemma board too? Than you're gonna have to create a new one. Lets call it Gemma2. Just copy/past this piece of text and do the changes like below
    Done. Restart Arduino IDE and be even more happy), because now you have two boards: standard Gemma and Gemma2 with expanded memory (all the memory you can use only uploading using a programmer).
  3. The last one will make you extremely happy). Copy/past this to the boards.txt file:
    ##############################################################

    menu.clock=Clock

    ATtinyX5.name=ATtiny25/45/85
    ATtinyX5.bootloader.tool=arduino:avrdude
    ATtinyX5.bootloader.unlock_bits=0xff
    ATtinyX5.bootloader.lock_bits=0xff
    ATtinyX5.build.core=arduino:arduino
    ATtinyX5.build.board=attiny
    ATtinyX5.upload.tool=arduino:avrdude

    ATtinyX5.menu.cpu.attiny25=ATtiny25
    ATtinyX5.menu.cpu.attiny25.upload.maximum_size=2048
    ATtinyX5.menu.cpu.attiny25.upload.maximum_data_size=128
    ATtinyX5.menu.cpu.attiny25.build.mcu=attiny25
    ATtinyX5.menu.cpu.attiny25.build.variant=tiny8

    ATtinyX5.menu.cpu.attiny45=ATtiny45
    ATtinyX5.menu.cpu.attiny45.upload.maximum_size=4096
    ATtinyX5.menu.cpu.attiny45.upload.maximum_data_size=256
    ATtinyX5.menu.cpu.attiny45.build.mcu=attiny45
    ATtinyX5.menu.cpu.attiny45.build.variant=tiny8

    ATtinyX5.menu.cpu.attiny85=ATtiny85
    ATtinyX5.menu.cpu.attiny85.upload.maximum_size=8192
    ATtinyX5.menu.cpu.attiny85.upload.maximum_data_size=512
    ATtinyX5.menu.cpu.attiny85.build.mcu=attiny85
    ATtinyX5.menu.cpu.attiny85.build.variant=tiny8

    ATtinyX5.menu.clock.internal1=Internal 1 MHz
    ATtinyX5.menu.clock.internal1.bootloader.low_fuses=0x62
    ATtinyX5.menu.clock.internal1.bootloader.high_fuses=0xdf
    ATtinyX5.menu.clock.internal1.bootloader.extended_fuses=0xff
    ATtinyX5.menu.clock.internal1.build.f_cpu=1000000L

    ATtinyX5.menu.clock.internal8=Internal 8 MHz
    ATtinyX5.menu.clock.internal8.bootloader.low_fuses=0xe2
    ATtinyX5.menu.clock.internal8.bootloader.high_fuses=0xdf
    ATtinyX5.menu.clock.internal8.bootloader.extended_fuses=0xff
    ATtinyX5.menu.clock.internal8.build.f_cpu=8000000L

    ATtinyX5.menu.clock.internal16=Internal 16 MHz
    ATtinyX5.menu.clock.internal16.bootloader.low_fuses=0xf1
    ATtinyX5.menu.clock.internal16.bootloader.high_fuses=0xdf
    ATtinyX5.menu.clock.internal16.bootloader.extended_fuses=0xff
    ATtinyX5.menu.clock.internal16.build.f_cpu=16000000L

    ATtinyX5.menu.clock.external8=External 8 MHz
    ATtinyX5.menu.clock.external8.bootloader.low_fuses=0xfe
    ATtinyX5.menu.clock.external8.bootloader.high_fuses=0xdf
    ATtinyX5.menu.clock.external8.bootloader.extended_fuses=0xff
    ATtinyX5.menu.clock.external8.build.f_cpu=8000000L

    ATtinyX5.menu.clock.external16=External 16 MHz
    ATtinyX5.menu.clock.external16.bootloader.low_fuses=0xfe
    ATtinyX5.menu.clock.external16.bootloader.high_fuses=0xdf
    ATtinyX5.menu.clock.external16.bootloader.extended_fuses=0xff
    ATtinyX5.menu.clock.external16.build.f_cpu=16000000L

    ATtinyX5.menu.clock.external20=External 20 MHz
    ATtinyX5.menu.clock.external20.bootloader.low_fuses=0xfe
    ATtinyX5.menu.clock.external20.bootloader.high_fuses=0xdf
    ATtinyX5.menu.clock.external20.bootloader.extended_fuses=0xff
    ATtinyX5.menu.clock.external20.build.f_cpu=20000000L

    ##############################################################
    ATtinyX4.name=ATtiny24/44/84
    ATtinyX4.bootloader.tool=arduino:avrdude
    ATtinyX4.bootloader.unlock_bits=0xff
    ATtinyX4.bootloader.lock_bits=0xff
    ATtinyX4.build.core=arduino:arduino
    ATtinyX4.build.board=attiny
    ATtinyX4.upload.tool=arduino:avrdude

    ATtinyX4.menu.cpu.attiny24=ATtiny24
    ATtinyX4.menu.cpu.attiny24.upload.maximum_size=2048
    ATtinyX4.menu.cpu.attiny24.upload.maximum_data_size=128
    ATtinyX4.menu.cpu.attiny24.build.mcu=attiny24
    ATtinyX4.menu.cpu.attiny24.build.variant=tiny14

    ATtinyX4.menu.cpu.attiny44=ATtiny44
    ATtinyX4.menu.cpu.attiny44.upload.maximum_size=4096
    ATtinyX4.menu.cpu.attiny44.upload.maximum_data_size=256
    ATtinyX4.menu.cpu.attiny44.build.mcu=attiny44
    ATtinyX4.menu.cpu.attiny44.build.variant=tiny14

    ATtinyX4.menu.cpu.attiny84=ATtiny84
    ATtinyX4.menu.cpu.attiny84.upload.maximum_size=8192
    ATtinyX4.menu.cpu.attiny84.upload.maximum_data_size=512
    ATtinyX4.menu.cpu.attiny84.build.mcu=attiny84
    ATtinyX4.menu.cpu.attiny84.build.variant=tiny14

    ATtinyX4.menu.clock.internal1=Internal 1 MHz
    ATtinyX4.menu.clock.internal1.bootloader.low_fuses=0x62
    ATtinyX4.menu.clock.internal1.bootloader.high_fuses=0xdf
    ATtinyX4.menu.clock.internal1.bootloader.extended_fuses=0xff
    ATtinyX4.menu.clock.internal1.build.f_cpu=1000000L

    ATtinyX4.menu.clock.internal8=Internal 8 MHz
    ATtinyX4.menu.clock.internal8.bootloader.low_fuses=0xe2
    ATtinyX4.menu.clock.internal8.bootloader.high_fuses=0xdf
    ATtinyX4.menu.clock.internal8.bootloader.extended_fuses=0xff
    ATtinyX4.menu.clock.internal8.build.f_cpu=8000000L

    ATtinyX4.menu.clock.external8=External 8 MHz
    ATtinyX4.menu.clock.external8.bootloader.low_fuses=0xfe
    ATtinyX4.menu.clock.external8.bootloader.high_fuses=0xdf
    ATtinyX4.menu.clock.external8.bootloader.extended_fuses=0xff
    ATtinyX4.menu.clock.external8.build.f_cpu=8000000L

    ATtinyX4.menu.clock.external16=External 16 MHz
    ATtinyX4.menu.clock.external16.bootloader.low_fuses=0xfe
    ATtinyX4.menu.clock.external16.bootloader.high_fuses=0xdf
    ATtinyX4.menu.clock.external16.bootloader.extended_fuses=0xff
    ATtinyX4.menu.clock.external16.build.f_cpu=16000000L

    ATtinyX4.menu.clock.external20=External 20 MHz
    ATtinyX4.menu.clock.external20.bootloader.low_fuses=0xfe
    ATtinyX4.menu.clock.external20.bootloader.high_fuses=0xdf
    ATtinyX4.menu.clock.external20.bootloader.extended_fuses=0xff
    ATtinyX4.menu.clock.external20.build.f_cpu=20000000L

    And you will get a whole set of ATtiny boards, see screen shots








And there's one more thing to do. Find folder "variants", in my case here  ...\arduino-1.6.7\hardware\arduino\avr\variants, then download and unzip variants for tiny 8 pin and 14 pin controller and place those two folders into the "variants" folder.

Connect the controller like this
And press Ctrl+Shift+U or do this

That would be all for the wireless sensor. On the receiver side you should get something like this

But that is a subject for my next post, which is coming soon! See ya!

P.S. This version of the wireless sensor does the measurements every 32 seconds. To increase the power down period you have to increase the value "i". For more details refer to my previous post

P.P.S The current consumption while power down is less than 7 uA! While measuring - about 10 mA, while transmitting - about 15 mA.

No comments:

Post a Comment