//---------------------------------------------------------------------------------------------------------------------- // TinyTX - An ATtiny84 and RFM12B Wireless Temperature & Humidity Sensor Node // // Updated to Support Status LED by http://raspberry.tips - Original made by Nathan Chantrell // // Using the DHT22 temperature and humidity sensor // // Licenced under the Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) licence: // http://creativecommons.org/licenses/by-sa/3.0/ // // Requires Arduino IDE with arduino-tiny core: http://code.google.com/p/arduino-tiny/ //---------------------------------------------------------------------------------------------------------------------- #include // https://github.com/nathanchantrell/Arduino-DHT22 #include // https://github.com/jcw/jeelib ISR(WDT_vect) { Sleepy::watchdogEvent(); } // interrupt handler for JeeLabs Sleepy power saving #define myNodeID 16 // RF12 node ID in the range 1-30 #define network 210 // RF12 Network group #define freq RF12_433MHZ // Frequency of RFM12B module #define SENDDELAY 60000 // the Atmega internal sleep is max 60 seconds #define SENDDELAYMULTIPLY 5 // We use a for function to multiply the max sleep SENDDELAY * SENDDELAYMULTIPLY #define USE_ACK // Enable ACKs, comment out to disable #define RETRY_PERIOD 5 // How soon to retry (in seconds) if ACK didn't come in #define RETRY_LIMIT 5 // Maximum number of times to retry #define ACK_TIME 10 // Number of milliseconds to wait for an ack #define DHT22_PIN 10 // DHT sensor is connected on D10/ATtiny pin 13 #define DHT22_POWER 9 // DHT Power pin is connected on D9/ATtiny pin 12 #define LEDpin 8 // LED Pin D8, PA2 - set to 0 to disable LED DHT22 myDHT22(DHT22_PIN); // Setup the DHT //######################################################################################################################## //Data Structure to be sent //######################################################################################################################## typedef struct { int humidity; // Humidity reading int supplyV; // Supply voltage int temp; // Temperature reading } Payload; Payload tinytx; // Wait a few milliseconds for proper ACK #ifdef USE_ACK static byte waitForAck() { MilliTimer ackTimer; while (!ackTimer.poll(ACK_TIME)) { if (rf12_recvDone() && rf12_crc == 0 && rf12_hdr == (RF12_HDR_DST | RF12_HDR_CTL | myNodeID)) return 1; } return 0; } #endif //-------------------------------------------------------------------------------------------------- // LED //------------------------------------------------------------------------------------------------- static void activityLed (byte state, byte time = 0) { if (LEDpin) { pinMode(LEDpin, OUTPUT); if (time == 0) { digitalWrite(LEDpin, state); } else { digitalWrite(LEDpin, state); Sleepy::loseSomeTime(time); digitalWrite(LEDpin, !state); } } } // blink led static void blink (byte pin, byte n = 3) { if (LEDpin) { pinMode(pin, OUTPUT); for (byte i = 0; i < 2 * n; ++i) { Sleepy::loseSomeTime(100); digitalWrite(pin, !digitalRead(pin)); } } } //-------------------------------------------------------------------------------------------------- // Send payload data via RF //------------------------------------------------------------------------------------------------- static void rfwrite(){ #ifdef USE_ACK for (byte i = 0; i <= RETRY_LIMIT; ++i) { // tx and wait for ack up to RETRY_LIMIT times rf12_sleep(-1); // Wake up RF module while (!rf12_canSend()) rf12_recvDone(); rf12_sendStart(RF12_HDR_ACK, &tinytx, sizeof tinytx); rf12_sendWait(2); // Wait for RF to finish sending while in standby mode byte acked = waitForAck(); // Wait for ACK rf12_sleep(0); // Put RF module to sleep if (acked) { return; } // Return if ACK received Sleepy::loseSomeTime(RETRY_PERIOD * 1000); // If no ack received wait and try again } #else rf12_sleep(-1); // Wake up RF module while (!rf12_canSend()) rf12_recvDone(); rf12_sendStart(0, &tinytx, sizeof tinytx); rf12_sendWait(2); // Wait for RF to finish sending while in standby mode rf12_sleep(0); // Put RF module to sleep return; #endif } //-------------------------------------------------------------------------------------------------- // Read current supply voltage //-------------------------------------------------------------------------------------------------- long readVcc() { bitClear(PRR, PRADC); ADCSRA |= bit(ADEN); // Enable the ADC long result; // Read 1.1V reference against Vcc #if defined(__AVR_ATtiny84__) ADMUX = _BV(MUX5) | _BV(MUX0); // For ATtiny84 #else ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // For ATmega328 #endif delay(2); // Wait for Vref to settle ADCSRA |= _BV(ADSC); // Convert while (bit_is_set(ADCSRA,ADSC)); result = ADCL; result |= ADCH<<8; result = 1126400L / result; // Back-calculate Vcc in mV ADCSRA &= ~ bit(ADEN); bitSet(PRR, PRADC); // Disable the ADC to save power return result; } //######################################################################################################################## void setup() { rf12_initialize(myNodeID,freq,network); // Initialize RFM12 with settings defined above rf12_sleep(0); // Put the RFM12 to sleep pinMode(DHT22_POWER, OUTPUT); // set power pin for DHT to output PRR = bit(PRTIM1); // only keep timer 0 going ADCSRA &= ~ bit(ADEN); bitSet(PRR, PRADC); // Disable the ADC to save power activityLed(1,1000); // LED on for 1000ms } void loop() { activityLed(1); // LED on digitalWrite(DHT22_POWER, HIGH); // turn DHT sensor on DHT22_ERROR_t errorCode; Sleepy::loseSomeTime(2000); // Sensor requires minimum 2s warm-up after power-on. errorCode = myDHT22.readData(); // read data from sensor activityLed(0); // LED off if (errorCode == DHT_ERROR_NONE) { // data is good tinytx.temp = (myDHT22.getTemperatureC()*100); // Get temperature reading and convert to integer, reversed at receiving end tinytx.humidity = (myDHT22.getHumidity()*100); // Get humidity reading and convert to integer, reversed at receiving end tinytx.supplyV = readVcc(); // Get supply voltage rfwrite(); // Send data via RF if (LEDpin) { blink(LEDpin, 2); // blink LED } } digitalWrite(DHT22_POWER, LOW); // turn DHT22 off for (byte i = 0; i < SENDDELAYMULTIPLY; ++i) // We need to loop because the max sleep is 60sec Sleepy::loseSomeTime(SENDDELAY); //JeeLabs power save function: enter low power mode for 60 seconds (valid range 16-65000 ms) }