|
@@ -0,0 +1,421 @@
|
|
|
+/*
|
|
|
+ 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 = 0; // 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
|
|
|
+}
|