Browse Source

initial version

FloKra 10 months ago
commit
0991ba73d2
1 changed files with 421 additions and 0 deletions
  1. 421 0
      TeichTemp/TeichTemp.ino

+ 421 - 0
TeichTemp/TeichTemp.ino

@@ -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
+}