123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- /*
- battery powered 433MHz RF transmitter for a DS18B20 sensor
- */
- //#define DEBUGMODE
- #define BAUDRATE 115200
- #include <avr/wdt.h>
- #include <avr/sleep.h>
- #include <avr/power.h>
- #include <OneWire.h>
- #include <DallasTemperature.h>
- #include <RCSwitch.h>
- #define RF_TRANSMITTER_PIN 10
- #define RF_TRANSMITTER_REPEATS 5
- #define RF_TRANSMITTER_SEND_BITS 24 // 24 is maximum, more than that has weird malfunction in RCSwitch library
- #define ADDR_BITS 12
- #define ONE_WIRE_BUS 2
- #define ONE_WIRE_RESOLUTION 11
- // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
- OneWire oneWire(ONE_WIRE_BUS);
- // Pass our oneWire reference to Dallas Temperature.
- DallasTemperature sensors(&oneWire);
- // arrays to hold device address
- DeviceAddress owSensor1;
- RCSwitch mySwitch = RCSwitch();
- const unsigned int sendDataInterval = 32; // WDT will wake the MCU every 8 s - 37 * 8 = 296s =~ 5m
- const unsigned long txBaseCode = 0x631; // we are sending 24 bit codes, the 12 MSB are the device address and defined here
- // 12 bits LSB contains the actual data
- const long InternalReferenceVoltage = 1083L; // <<<<<<<<<< Change this to the reading from your internal voltage reference
- const int battWarningBelow = 330; // 1.1V pro Zelle bei 3x AA
- int battVolts;
- bool battWarning = false;
- unsigned int wakeupCounter = 0;
- void setup() {
- //#ifdef DEBUGMODE
- Serial.begin(BAUDRATE);
- Serial.println(F("DS18B20 RF Transmitter 433MHz v1"));
- //#endif
- // Transmitter is connected to Arduino Pin #10
- //mySwitch.enableTransmit(RF_TRANSMITTER_PIN);
- // Optional set protocol (default is 1, will work for most outlets)
- // mySwitch.setProtocol(2);
- // Optional set pulse length.
- // mySwitch.setPulseLength(320);
- // Optional set number of transmission repetitions.
- mySwitch.setRepeatTransmit(RF_TRANSMITTER_REPEATS);
- disableADC();
- power_spi_disable();
- power_twi_disable();
- // OneWire - locate devices on the bus
- sensors.begin();
- Serial.print(F("OneWire - scanning... "));
- Serial.print(F("Found "));
- Serial.print(sensors.getDeviceCount(), DEC);
- Serial.println(" devices.");
- // report parasite power requirements
- // Serial.print(F("Parasite power is: "));
- // if (sensors.isParasitePowerMode()) Serial.println("ON");
- // else Serial.println("OFF");
- // Assign address manually. The addresses below will need to be changed
- // to valid device addresses on your bus. Device address can be retrieved
- // by using either oneWire.search(deviceAddress) or individually via
- // sensors.getAddress(deviceAddress, index)
- // Note that you will need to use your specific address here
- //insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
- // Method 1:
- // Search for devices on the bus and assign based on an index. Ideally,
- // you would do this to initially discover addresses on the bus and then
- // use those addresses and manually assign them (see above) once you know
- // the devices on your bus (and assuming they don't change).
- if (!sensors.getAddress(owSensor1, 0)) Serial.println(F("ERROR: Unable to find address for Device 0"));
- // method 2: search()
- // search() looks for the next device. Returns 1 if a new address has been
- // returned. A zero might mean that the bus is shorted, there are no devices,
- // or you have already retrieved all of them. It might be a good idea to
- // check the CRC to make sure you didn't get garbage. The order is
- // deterministic. You will always get the same devices in the same order
- //
- // Must be called before search()
- //oneWire.reset_search();
- // assigns the first address found to insideThermometer
- //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
- // show the addresses we found on the bus
- Serial.print(F("Device 0 Address: "));
- printAddress(owSensor1);
- Serial.println();
- // set the resolution
- sensors.setResolution(owSensor1, ONE_WIRE_RESOLUTION);
- // IMPORTANT - do not wait for conversion (done by simple delay in the library)
- // but instead put MCU to power down and wake up after some time by WDT interrupt to read the data
- sensors.setWaitForConversion(false);
- Serial.print(F("Device 0 Resolution: "));
- Serial.print(sensors.getResolution(owSensor1), DEC);
- Serial.println();
- readSensorsAndTransmitValue(true);
- Serial.println(F("Going to deep sleep now."));
- Serial.println(F("Will wake up every 8s by WDT interrupt."));
- Serial.print(F("Send data interval: "));
- Serial.print((sendDataInterval * 8) +8);
- Serial.println("s");
-
- #ifndef DEBUGMODE
- Serial.println(F("INFO: serial logging disabled from now to save battery"));
- #endif
- delay(50);
- #ifndef DEBUGMODE
- Serial.end();
- power_usart0_disable();
- #endif
- watchdogSetup();
- sleepNow();
- }
- void loop() {
- wdt_reset();
- #ifdef DEBUGMODE
- Serial.print(F("wake up #"));
- Serial.print(wakeupCounter);
- Serial.println();
- delay(50);
- #endif
- if (wakeupCounter >= sendDataInterval) {
- wakeupCounter = 0;
- #ifdef DEBUGMODE
- readSensorsAndTransmitValue(false);
- #else
- readSensorsAndTransmitValue(true);
- #endif
- }
- #ifdef DEBUGMODE
- delay(50);
- #endif
- wakeupCounter++;
- watchdogSetup();
- sleepNow();
- }
- void readSensorsAndTransmitValue(bool _printOnSerial) {
- unsigned long txCode;
- #ifdef DEBUGMODE
- Serial.print(F("Requesting temperatures..."));
- Serial.println();
- delay(50);
- #endif
- sensors.requestTemperatures(); // Send the command to get temperatures
- // now wait for the conversion to be finished
- // -> put MCU to sleep for some time (wake up using WDT interrupt)
- watchdogSetup_oneWireDelay();
- sleepNow();
- #ifdef DEBUGMODE
- Serial.print(F("getting sensor data..."));
- Serial.println();
- #endif
- if (_printOnSerial) printTemperature(owSensor1);
- // get Vcc voltage via "bandgap" method
- enableADC();
- // https://forum.arduino.cc/t/measurement-of-bandgap-voltage/38215/8
- for (int i = 0; i <= 3; i++) battVolts = getBandgap(); // >3 readings seem required for stable value?
- disableADC();
- if (_printOnSerial) {
- Serial.print(F("Vcc="));
- Serial.print(battVolts);
- Serial.print(F(" mV, VccLowBatt="));
- Serial.print(battWarningBelow);
- Serial.print(F(" mV ==> BATT "));
- }
- if (battVolts <= battWarningBelow) {
- battWarning = true;
- if (_printOnSerial) {
- Serial.print(F("LOW"));
- Serial.println();
- }
- }
- else {
- battWarning = false;
- if (_printOnSerial) {
- Serial.print(F("OK"));
- Serial.println();
- }
- }
- // code consists of 12 MSB bits for device address
- // and 12 LSB bits with the data
- uint16_t tempValueRAW;
- tempValueRAW = sensors.getTemp(owSensor1);
- txCode = txBaseCode << ADDR_BITS;
- tempValueRAW = tempValueRAW >> 2; // we dont need 12 bit resolution but we need the negative number marker included in 12 bits, so reduce resolution to 10 bit
- tempValueRAW = tempValueRAW & 0xFFF; // 0000111111111111 -> library fills up all 16 MSB bits with 1 when negative number. remove all but 12 LSB bits
- // (ensure that the address part will not be altered)
- #ifdef DEBUGMODE
- Serial.print("T_RAW = ");
- Serial.print(tempValueRAW, HEX);
- Serial.print("h = ");
- Serial.print(tempValueRAW);
- Serial.print(" = ");
- Serial.print(tempValueRAW, BIN);
- Serial.println("b");
- #endif
- txCode = txCode + tempValueRAW;
- #ifdef DEBUGMODE
- Serial.print("TXCODE: ");
- Serial.print(txCode, HEX);
- Serial.println();
- #endif
- mySwitch.enableTransmit(RF_TRANSMITTER_PIN);
- delay(10);
- mySwitch.send(txCode, RF_TRANSMITTER_SEND_BITS);
- delay(10);
- ////battWarning = true;
- txCode = txBaseCode << ADDR_BITS;
- if (battWarning) {
- txCode = txCode + 0xFFE; // send 12 high bits => battery low warning
- }
- else {
- txCode = txCode + 0xFFF; // send 12 high bits => battery low warning
- }
- mySwitch.send(txCode, RF_TRANSMITTER_SEND_BITS);
- delay(10);
- mySwitch.disableTransmit();
- }
- void sleepNow(void) {
- set_sleep_mode(SLEEP_MODE_PWR_DOWN); // choose power down mode
- // sleep_bod_disable(); // optional brown-out detection switch off
- wdt_reset();
- sleep_mode(); // sleep now!
- }
- void watchdogSetup(void) {
- cli();
- wdt_reset();
- WDTCSR |= (1 << WDCE) | (1 << WDE);
- //WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP3) | (1 << WDP0); // 8s / interrupt, no system reset
- WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP3) | (0 << WDP2) | (0 << WDP1) | (1 << WDP0); // 1s / interrupt, no system reset
- sei();
- }
- void watchdogSetup_oneWireDelay(void) {
- cli();
- wdt_reset();
- WDTCSR |= (1 << WDCE) | (1 << WDE);
- //WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP3) | (1 << WDP0); // 8s / interrupt, no system reset
- // Dallas DS10B20 - needed delay vs resolution
- // 9 bit: 94 ms
- // 10 bit: 188 ms
- // 11 bit: 375 ms
- // 12 bit: 750 ms
- #if ONE_WIRE_RESOLUTION == 9
- // 9 bit: 94 ms ==> 0.125s
- WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (0 << WDP2) | (1 << WDP1) | (1 << WDP0); // 0.125s / interrupt, no system reset
- #elif ONE_WIRE_RESOLUTION == 10
- // 10 bit: 188 ms ==> 0.25s
- WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (1 << WDP2) | (0 << WDP1) | (0 << WDP0); // 0.25s / interrupt, no system reset
- #elif ONE_WIRE_RESOLUTION == 11
- // 11 bit: 375 ms ==> 0.5s
- WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (1 << WDP2) | (0 << WDP1) | (1 << WDP0); // 0.1s / interrupt, no system reset
- #else
- // 12 bit: 750 ms ==> 1s
- WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (1 << WDP2) | (1 << WDP1) | (0 << WDP0); // 1s / interrupt, no system reset
- #endif
- sei();
- }
- ISR(WDT_vect) { //put in additional code here
- }
- // function to print a device address
- void printAddress(DeviceAddress deviceAddress)
- {
- for (uint8_t i = 0; i < 8; i++)
- {
- if (deviceAddress[i] < 16) Serial.print("0");
- Serial.print(deviceAddress[i], HEX);
- }
- }
- // function to print the temperature for a device
- void printTemperature(DeviceAddress deviceAddress)
- {
- // method 1 - slower
- //Serial.print("Temp C: ");
- //Serial.print(sensors.getTempC(deviceAddress));
- //Serial.print(" Temp F: ");
- //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit
- // method 2 - faster
- float tempC = sensors.getTempC(deviceAddress);
- if (tempC == DEVICE_DISCONNECTED_C)
- {
- Serial.println(F("ERROR: Could not read temperature data"));
- return;
- }
- Serial.print(F("Temp: "));
- Serial.print(tempC);
- Serial.print(F(" °C"));
- //Serial.print(" Temp F: ");
- //Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
- Serial.println();
- }
- // https://forum.arduino.cc/t/measurement-of-bandgap-voltage/38215/8
- int getBandgap(void)
- {
- //const long InternalReferenceVoltage = 1050L; // Adust this value to your specific internal BG voltage x1000
- // REFS1 REFS0 --> 0 1, AVcc internal ref.
- // MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG)
- ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0);
- // Start a conversion
- ADCSRA |= _BV( ADSC );
- // Wait for it to complete
- while ( ( (ADCSRA & (1 << ADSC)) != 0 ) );
- // Scale the value
- int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L;
- return results;
- }
- void enableADC() {
- // enable ADC
- //power_adc_enable(); // needed for bandgap Vcc measuring later - replaced by following line as power_adc_disable() did not seem to work
- ADCSRA = 0x80; // -> enable ADC, set MSB of ADCSRA register to 1
- }
- void disableADC() {
- // disable ADC
- //power_adc_disable(); // does not really work - AVR draws 0.24 mA in power down mode
- ADCSRA = 0; // this works better - draws only a few µA in power down mode
- }
|