TeichTemp.ino 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. battery powered 433MHz RF transmitter for a DS18B20 sensor
  3. */
  4. //#define DEBUGMODE
  5. #define BAUDRATE 115200
  6. #include <avr/wdt.h>
  7. #include <avr/sleep.h>
  8. #include <avr/power.h>
  9. #include <OneWire.h>
  10. #include <DallasTemperature.h>
  11. #include <RCSwitch.h>
  12. #define RF_TRANSMITTER_PIN 10
  13. #define RF_TRANSMITTER_REPEATS 5
  14. #define RF_TRANSMITTER_SEND_BITS 24 // 24 is maximum, more than that has weird malfunction in RCSwitch library
  15. #define ADDR_BITS 12
  16. #define ONE_WIRE_BUS 2
  17. #define ONE_WIRE_RESOLUTION 11
  18. // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
  19. OneWire oneWire(ONE_WIRE_BUS);
  20. // Pass our oneWire reference to Dallas Temperature.
  21. DallasTemperature sensors(&oneWire);
  22. // arrays to hold device address
  23. DeviceAddress owSensor1;
  24. RCSwitch mySwitch = RCSwitch();
  25. const unsigned int sendDataInterval = 32; // WDT will wake the MCU every 8 s - 37 * 8 = 296s =~ 5m
  26. const unsigned long txBaseCode = 0x631; // we are sending 24 bit codes, the 12 MSB are the device address and defined here
  27. // 12 bits LSB contains the actual data
  28. const long InternalReferenceVoltage = 1083L; // <<<<<<<<<< Change this to the reading from your internal voltage reference
  29. const int battWarningBelow = 330; // 1.1V pro Zelle bei 3x AA
  30. int battVolts;
  31. bool battWarning = false;
  32. unsigned int wakeupCounter = 0;
  33. void setup() {
  34. //#ifdef DEBUGMODE
  35. Serial.begin(BAUDRATE);
  36. Serial.println(F("DS18B20 RF Transmitter 433MHz v1"));
  37. //#endif
  38. // Transmitter is connected to Arduino Pin #10
  39. //mySwitch.enableTransmit(RF_TRANSMITTER_PIN);
  40. // Optional set protocol (default is 1, will work for most outlets)
  41. // mySwitch.setProtocol(2);
  42. // Optional set pulse length.
  43. // mySwitch.setPulseLength(320);
  44. // Optional set number of transmission repetitions.
  45. mySwitch.setRepeatTransmit(RF_TRANSMITTER_REPEATS);
  46. disableADC();
  47. power_spi_disable();
  48. power_twi_disable();
  49. // OneWire - locate devices on the bus
  50. sensors.begin();
  51. Serial.print(F("OneWire - scanning... "));
  52. Serial.print(F("Found "));
  53. Serial.print(sensors.getDeviceCount(), DEC);
  54. Serial.println(" devices.");
  55. // report parasite power requirements
  56. // Serial.print(F("Parasite power is: "));
  57. // if (sensors.isParasitePowerMode()) Serial.println("ON");
  58. // else Serial.println("OFF");
  59. // Assign address manually. The addresses below will need to be changed
  60. // to valid device addresses on your bus. Device address can be retrieved
  61. // by using either oneWire.search(deviceAddress) or individually via
  62. // sensors.getAddress(deviceAddress, index)
  63. // Note that you will need to use your specific address here
  64. //insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
  65. // Method 1:
  66. // Search for devices on the bus and assign based on an index. Ideally,
  67. // you would do this to initially discover addresses on the bus and then
  68. // use those addresses and manually assign them (see above) once you know
  69. // the devices on your bus (and assuming they don't change).
  70. if (!sensors.getAddress(owSensor1, 0)) Serial.println(F("ERROR: Unable to find address for Device 0"));
  71. // method 2: search()
  72. // search() looks for the next device. Returns 1 if a new address has been
  73. // returned. A zero might mean that the bus is shorted, there are no devices,
  74. // or you have already retrieved all of them. It might be a good idea to
  75. // check the CRC to make sure you didn't get garbage. The order is
  76. // deterministic. You will always get the same devices in the same order
  77. //
  78. // Must be called before search()
  79. //oneWire.reset_search();
  80. // assigns the first address found to insideThermometer
  81. //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  82. // show the addresses we found on the bus
  83. Serial.print(F("Device 0 Address: "));
  84. printAddress(owSensor1);
  85. Serial.println();
  86. // set the resolution
  87. sensors.setResolution(owSensor1, ONE_WIRE_RESOLUTION);
  88. // IMPORTANT - do not wait for conversion (done by simple delay in the library)
  89. // but instead put MCU to power down and wake up after some time by WDT interrupt to read the data
  90. sensors.setWaitForConversion(false);
  91. Serial.print(F("Device 0 Resolution: "));
  92. Serial.print(sensors.getResolution(owSensor1), DEC);
  93. Serial.println();
  94. readSensorsAndTransmitValue(true);
  95. Serial.println(F("Going to deep sleep now."));
  96. Serial.println(F("Will wake up every 8s by WDT interrupt."));
  97. Serial.print(F("Send data interval: "));
  98. Serial.print((sendDataInterval * 8) +8);
  99. Serial.println("s");
  100. #ifndef DEBUGMODE
  101. Serial.println(F("INFO: serial logging disabled from now to save battery"));
  102. #endif
  103. delay(50);
  104. #ifndef DEBUGMODE
  105. Serial.end();
  106. power_usart0_disable();
  107. #endif
  108. watchdogSetup();
  109. sleepNow();
  110. }
  111. void loop() {
  112. wdt_reset();
  113. #ifdef DEBUGMODE
  114. Serial.print(F("wake up #"));
  115. Serial.print(wakeupCounter);
  116. Serial.println();
  117. delay(50);
  118. #endif
  119. if (wakeupCounter >= sendDataInterval) {
  120. wakeupCounter = 0;
  121. #ifdef DEBUGMODE
  122. readSensorsAndTransmitValue(false);
  123. #else
  124. readSensorsAndTransmitValue(true);
  125. #endif
  126. }
  127. #ifdef DEBUGMODE
  128. delay(50);
  129. #endif
  130. wakeupCounter++;
  131. watchdogSetup();
  132. sleepNow();
  133. }
  134. void readSensorsAndTransmitValue(bool _printOnSerial) {
  135. unsigned long txCode;
  136. #ifdef DEBUGMODE
  137. Serial.print(F("Requesting temperatures..."));
  138. Serial.println();
  139. delay(50);
  140. #endif
  141. sensors.requestTemperatures(); // Send the command to get temperatures
  142. // now wait for the conversion to be finished
  143. // -> put MCU to sleep for some time (wake up using WDT interrupt)
  144. watchdogSetup_oneWireDelay();
  145. sleepNow();
  146. #ifdef DEBUGMODE
  147. Serial.print(F("getting sensor data..."));
  148. Serial.println();
  149. #endif
  150. if (_printOnSerial) printTemperature(owSensor1);
  151. // get Vcc voltage via "bandgap" method
  152. enableADC();
  153. // https://forum.arduino.cc/t/measurement-of-bandgap-voltage/38215/8
  154. for (int i = 0; i <= 3; i++) battVolts = getBandgap(); // >3 readings seem required for stable value?
  155. disableADC();
  156. if (_printOnSerial) {
  157. Serial.print(F("Vcc="));
  158. Serial.print(battVolts);
  159. Serial.print(F(" mV, VccLowBatt="));
  160. Serial.print(battWarningBelow);
  161. Serial.print(F(" mV ==> BATT "));
  162. }
  163. if (battVolts <= battWarningBelow) {
  164. battWarning = true;
  165. if (_printOnSerial) {
  166. Serial.print(F("LOW"));
  167. Serial.println();
  168. }
  169. }
  170. else {
  171. battWarning = false;
  172. if (_printOnSerial) {
  173. Serial.print(F("OK"));
  174. Serial.println();
  175. }
  176. }
  177. // code consists of 12 MSB bits for device address
  178. // and 12 LSB bits with the data
  179. uint16_t tempValueRAW;
  180. tempValueRAW = sensors.getTemp(owSensor1);
  181. txCode = txBaseCode << ADDR_BITS;
  182. 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
  183. tempValueRAW = tempValueRAW & 0xFFF; // 0000111111111111 -> library fills up all 16 MSB bits with 1 when negative number. remove all but 12 LSB bits
  184. // (ensure that the address part will not be altered)
  185. #ifdef DEBUGMODE
  186. Serial.print("T_RAW = ");
  187. Serial.print(tempValueRAW, HEX);
  188. Serial.print("h = ");
  189. Serial.print(tempValueRAW);
  190. Serial.print(" = ");
  191. Serial.print(tempValueRAW, BIN);
  192. Serial.println("b");
  193. #endif
  194. txCode = txCode + tempValueRAW;
  195. #ifdef DEBUGMODE
  196. Serial.print("TXCODE: ");
  197. Serial.print(txCode, HEX);
  198. Serial.println();
  199. #endif
  200. mySwitch.enableTransmit(RF_TRANSMITTER_PIN);
  201. delay(10);
  202. mySwitch.send(txCode, RF_TRANSMITTER_SEND_BITS);
  203. delay(10);
  204. ////battWarning = true;
  205. txCode = txBaseCode << ADDR_BITS;
  206. if (battWarning) {
  207. txCode = txCode + 0xFFE; // send 12 high bits => battery low warning
  208. }
  209. else {
  210. txCode = txCode + 0xFFF; // send 12 high bits => battery low warning
  211. }
  212. mySwitch.send(txCode, RF_TRANSMITTER_SEND_BITS);
  213. delay(10);
  214. mySwitch.disableTransmit();
  215. }
  216. void sleepNow(void) {
  217. set_sleep_mode(SLEEP_MODE_PWR_DOWN); // choose power down mode
  218. // sleep_bod_disable(); // optional brown-out detection switch off
  219. wdt_reset();
  220. sleep_mode(); // sleep now!
  221. }
  222. void watchdogSetup(void) {
  223. cli();
  224. wdt_reset();
  225. WDTCSR |= (1 << WDCE) | (1 << WDE);
  226. //WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP3) | (1 << WDP0); // 8s / interrupt, no system reset
  227. WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP3) | (0 << WDP2) | (0 << WDP1) | (1 << WDP0); // 1s / interrupt, no system reset
  228. sei();
  229. }
  230. void watchdogSetup_oneWireDelay(void) {
  231. cli();
  232. wdt_reset();
  233. WDTCSR |= (1 << WDCE) | (1 << WDE);
  234. //WDTCSR = (1 << WDIE) | (0 << WDE) | (1 << WDP3) | (1 << WDP0); // 8s / interrupt, no system reset
  235. // Dallas DS10B20 - needed delay vs resolution
  236. // 9 bit: 94 ms
  237. // 10 bit: 188 ms
  238. // 11 bit: 375 ms
  239. // 12 bit: 750 ms
  240. #if ONE_WIRE_RESOLUTION == 9
  241. // 9 bit: 94 ms ==> 0.125s
  242. WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (0 << WDP2) | (1 << WDP1) | (1 << WDP0); // 0.125s / interrupt, no system reset
  243. #elif ONE_WIRE_RESOLUTION == 10
  244. // 10 bit: 188 ms ==> 0.25s
  245. WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (1 << WDP2) | (0 << WDP1) | (0 << WDP0); // 0.25s / interrupt, no system reset
  246. #elif ONE_WIRE_RESOLUTION == 11
  247. // 11 bit: 375 ms ==> 0.5s
  248. WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (1 << WDP2) | (0 << WDP1) | (1 << WDP0); // 0.1s / interrupt, no system reset
  249. #else
  250. // 12 bit: 750 ms ==> 1s
  251. WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (1 << WDP2) | (1 << WDP1) | (0 << WDP0); // 1s / interrupt, no system reset
  252. #endif
  253. sei();
  254. }
  255. ISR(WDT_vect) { //put in additional code here
  256. }
  257. // function to print a device address
  258. void printAddress(DeviceAddress deviceAddress)
  259. {
  260. for (uint8_t i = 0; i < 8; i++)
  261. {
  262. if (deviceAddress[i] < 16) Serial.print("0");
  263. Serial.print(deviceAddress[i], HEX);
  264. }
  265. }
  266. // function to print the temperature for a device
  267. void printTemperature(DeviceAddress deviceAddress)
  268. {
  269. // method 1 - slower
  270. //Serial.print("Temp C: ");
  271. //Serial.print(sensors.getTempC(deviceAddress));
  272. //Serial.print(" Temp F: ");
  273. //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit
  274. // method 2 - faster
  275. float tempC = sensors.getTempC(deviceAddress);
  276. if (tempC == DEVICE_DISCONNECTED_C)
  277. {
  278. Serial.println(F("ERROR: Could not read temperature data"));
  279. return;
  280. }
  281. Serial.print(F("Temp: "));
  282. Serial.print(tempC);
  283. Serial.print(F(" °C"));
  284. //Serial.print(" Temp F: ");
  285. //Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
  286. Serial.println();
  287. }
  288. // https://forum.arduino.cc/t/measurement-of-bandgap-voltage/38215/8
  289. int getBandgap(void)
  290. {
  291. //const long InternalReferenceVoltage = 1050L; // Adust this value to your specific internal BG voltage x1000
  292. // REFS1 REFS0 --> 0 1, AVcc internal ref.
  293. // MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG)
  294. ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0);
  295. // Start a conversion
  296. ADCSRA |= _BV( ADSC );
  297. // Wait for it to complete
  298. while ( ( (ADCSRA & (1 << ADSC)) != 0 ) );
  299. // Scale the value
  300. int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L;
  301. return results;
  302. }
  303. void enableADC() {
  304. // enable ADC
  305. //power_adc_enable(); // needed for bandgap Vcc measuring later - replaced by following line as power_adc_disable() did not seem to work
  306. ADCSRA = 0x80; // -> enable ADC, set MSB of ADCSRA register to 1
  307. }
  308. void disableADC() {
  309. // disable ADC
  310. //power_adc_disable(); // does not really work - AVR draws 0.24 mA in power down mode
  311. ADCSRA = 0; // this works better - draws only a few µA in power down mode
  312. }