IR Sensor, PanicButton, OneWireTempSensor
[svn42.git] / rf433ctl / DallasTemperature / DallasTemperature.cpp
diff --git a/rf433ctl/DallasTemperature/DallasTemperature.cpp b/rf433ctl/DallasTemperature/DallasTemperature.cpp
new file mode 100644 (file)
index 0000000..5710379
--- /dev/null
@@ -0,0 +1,617 @@
+// This library is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU Lesser General Public\r
+// License as published by the Free Software Foundation; either\r
+// version 2.1 of the License, or (at your option) any later version.\r
+\r
+#include "DallasTemperature.h"\r
+\r
+extern "C" {\r
+  #include "WConstants.h"\r
+}\r
+\r
+DallasTemperature::DallasTemperature(OneWire* _oneWire) \r
+  #if REQUIRESALARMS\r
+  : _AlarmHandler(&defaultAlarmHandler)\r
+  #endif\r
+{\r
+  _wire = _oneWire;\r
+  devices = 0;\r
+  parasite = false;\r
+  conversionDelay = TEMP_9_BIT;\r
+}\r
+\r
+// initialize the bus\r
+void DallasTemperature::begin(void)\r
+{\r
+  DeviceAddress deviceAddress;\r
+\r
+  _wire->reset_search();\r
+\r
+  while (_wire->search(deviceAddress))\r
+  {\r
+    if (validAddress(deviceAddress))\r
+    {      \r
+      if (!parasite && readPowerSupply(deviceAddress)) parasite = true;\r
+\r
+      ScratchPad scratchPad;\r
+\r
+      readScratchPad(deviceAddress, scratchPad);\r
+\r
+      if (deviceAddress[0] == DS18S20MODEL) conversionDelay = TEMP_12_BIT; // 750 ms\r
+      else if (scratchPad[CONFIGURATION] > conversionDelay) conversionDelay = scratchPad[CONFIGURATION];\r
+\r
+      devices++;\r
+    }\r
+  }\r
+}\r
+\r
+// returns the number of devices found on the bus\r
+uint8_t DallasTemperature::getDeviceCount(void)\r
+{\r
+  return devices;\r
+}\r
+\r
+// returns true if address is valid\r
+bool DallasTemperature::validAddress(uint8_t* deviceAddress)\r
+{\r
+  return (_wire->crc8(deviceAddress, 7) == deviceAddress[7]);\r
+}\r
+\r
+// finds an address at a given index on the bus\r
+// returns true if the device was found\r
+bool DallasTemperature::getAddress(uint8_t* deviceAddress, uint8_t index)\r
+{\r
+  uint8_t depth = 0;\r
+\r
+  _wire->reset_search();\r
+\r
+  while (depth <= index && _wire->search(deviceAddress))\r
+  {\r
+    if (depth == index && validAddress(deviceAddress)) return true;\r
+    depth++;\r
+  }\r
+\r
+  return false;\r
+}\r
+\r
+// attempt to determine if the device at the given address is connected to the bus\r
+bool DallasTemperature::isConnected(uint8_t* deviceAddress)\r
+{\r
+  ScratchPad scratchPad;\r
+  return isConnected(deviceAddress, scratchPad);\r
+}\r
+\r
+// attempt to determine if the device at the given address is connected to the bus\r
+// also allows for updating the read scratchpad\r
+bool DallasTemperature::isConnected(uint8_t* deviceAddress, uint8_t* scratchPad)\r
+{\r
+  readScratchPad(deviceAddress, scratchPad);\r
+  return (_wire->crc8(scratchPad, 8) == scratchPad[SCRATCHPAD_CRC]);\r
+}\r
+\r
+// read device's scratch pad\r
+void DallasTemperature::readScratchPad(uint8_t* deviceAddress, uint8_t* scratchPad)\r
+{\r
+  // send the command\r
+  _wire->reset();\r
+  _wire->select(deviceAddress);\r
+  _wire->write(READSCRATCH);\r
+\r
+  // read the response   \r
+\r
+  // byte 0: temperature LSB\r
+  scratchPad[TEMP_LSB] = _wire->read();\r
+\r
+  // byte 1: temperature MSB\r
+  scratchPad[TEMP_MSB] = _wire->read();\r
+\r
+  // byte 2: high alarm temp\r
+  scratchPad[HIGH_ALARM_TEMP] = _wire->read();\r
+\r
+  // byte 3: low alarm temp\r
+  scratchPad[LOW_ALARM_TEMP] = _wire->read();\r
+\r
+  // byte 4:\r
+  // DS18S20: store for crc\r
+  // DS18B20 & DS1822: configuration register\r
+  scratchPad[CONFIGURATION] = _wire->read();\r
+\r
+  // byte 5:\r
+  // internal use & crc\r
+  scratchPad[INTERNAL_BYTE] = _wire->read();\r
+\r
+  // byte 6:\r
+  // DS18S20: COUNT_REMAIN\r
+  // DS18B20 & DS1822: store for crc\r
+  scratchPad[COUNT_REMAIN] = _wire->read();\r
+  \r
+  // byte 7:\r
+  // DS18S20: COUNT_PER_C\r
+  // DS18B20 & DS1822: store for crc\r
+  scratchPad[COUNT_PER_C] = _wire->read();\r
+  \r
+  // byte 8:\r
+  // SCTRACHPAD_CRC\r
+  scratchPad[SCRATCHPAD_CRC] = _wire->read();\r
+\r
+  _wire->reset();\r
+}\r
+\r
+// writes device's scratch pad\r
+void DallasTemperature::writeScratchPad(uint8_t* deviceAddress, const uint8_t* scratchPad)\r
+{\r
+  _wire->reset();\r
+  _wire->select(deviceAddress);\r
+  _wire->write(WRITESCRATCH);\r
+  _wire->write(scratchPad[HIGH_ALARM_TEMP]); // high alarm temp\r
+  _wire->write(scratchPad[LOW_ALARM_TEMP]); // low alarm temp\r
+  // DS18S20 does not use the configuration register\r
+  if (deviceAddress[0] != DS18S20MODEL) _wire->write(scratchPad[CONFIGURATION]); // configuration\r
+  _wire->reset();\r
+  // save the newly written values to eeprom\r
+  _wire->write(COPYSCRATCH, parasite);\r
+  if (parasite) delay(10); // 10ms delay \r
+  _wire->reset();\r
+}\r
+\r
+// reads the device's power requirements\r
+bool DallasTemperature::readPowerSupply(uint8_t* deviceAddress)\r
+{\r
+  bool ret = false;\r
+  _wire->reset();\r
+  _wire->select(deviceAddress);\r
+  _wire->write(READPOWERSUPPLY);\r
+  if (_wire->read_bit() == 0) ret = true;\r
+  _wire->reset();\r
+  return ret;\r
+}\r
+\r
+// returns the current resolution, 9-12\r
+uint8_t DallasTemperature::getResolution(uint8_t* deviceAddress)\r
+{\r
+  if (deviceAddress[0] == DS18S20MODEL) return 9; // this model has a fixed resolution\r
+\r
+  ScratchPad scratchPad;\r
+  readScratchPad(deviceAddress, scratchPad);\r
+  switch (scratchPad[CONFIGURATION])\r
+  {\r
+    case TEMP_12_BIT:\r
+      return 12;\r
+      break;\r
+    case TEMP_11_BIT:\r
+      return 11;\r
+      break;\r
+    case TEMP_10_BIT:\r
+      return 10;\r
+      break;\r
+    case TEMP_9_BIT:\r
+      return 9;\r
+      break;\r
+  }\r
+}\r
+\r
+// set resolution of a device to 9, 10, 11, or 12 bits\r
+void DallasTemperature::setResolution(uint8_t* deviceAddress, uint8_t newResolution)\r
+{\r
+  ScratchPad scratchPad;\r
+  if (isConnected(deviceAddress, scratchPad))\r
+  {\r
+    // DS18S20 has a fixed 9-bit resolution\r
+    if (deviceAddress[0] != DS18S20MODEL)\r
+    {\r
+      switch (newResolution)\r
+      {\r
+        case 12:\r
+          scratchPad[CONFIGURATION] = TEMP_12_BIT;\r
+          break;\r
+        case 11:\r
+          scratchPad[CONFIGURATION] = TEMP_11_BIT;\r
+          break;\r
+        case 10:\r
+          scratchPad[CONFIGURATION] = TEMP_10_BIT;\r
+          break;\r
+        case 9:\r
+        default:\r
+          scratchPad[CONFIGURATION] = TEMP_9_BIT;\r
+          break;\r
+      }\r
+      writeScratchPad(deviceAddress, scratchPad);\r
+    }\r
+  }\r
+} \r
+\r
+// sends command for all devices on the bus to perform a temperature\r
+void DallasTemperature::requestTemperatures(void)\r
+{\r
+  _wire->reset();\r
+  _wire->skip();\r
+  _wire->write(STARTCONVO, parasite);\r
+\r
+  switch (conversionDelay)\r
+  {\r
+    case TEMP_9_BIT:\r
+      delay(94);\r
+      break;\r
+    case TEMP_10_BIT:\r
+      delay(188);\r
+      break;\r
+    case TEMP_11_BIT:\r
+      delay(375);\r
+      break;\r
+    case TEMP_12_BIT:\r
+    default:\r
+      delay(750);\r
+      break;\r
+  }\r
+}\r
+\r
+// sends command for one device to perform a temperature by address\r
+void DallasTemperature::requestTemperaturesByAddress(uint8_t* deviceAddress)\r
+{\r
+  _wire->reset();\r
+  _wire->select(deviceAddress);\r
+  _wire->write(STARTCONVO, parasite);\r
+\r
+  switch (conversionDelay)\r
+  {\r
+    case TEMP_9_BIT:\r
+      delay(94);\r
+      break;\r
+    case TEMP_10_BIT:\r
+      delay(188);\r
+      break;\r
+    case TEMP_11_BIT:\r
+      delay(375);\r
+      break;\r
+    case TEMP_12_BIT:\r
+    default:\r
+      delay(750);\r
+      break;\r
+  }\r
+}\r
+\r
+// sends command for one device to perform a temp conversion by index\r
+void DallasTemperature::requestTemperaturesByIndex(uint8_t deviceIndex)\r
+{\r
+  DeviceAddress deviceAddress;\r
+  getAddress(deviceAddress, deviceIndex);\r
+  requestTemperaturesByAddress(deviceAddress);\r
+}\r
+\r
+\r
+// Fetch temperature for device index\r
+float DallasTemperature::getTempCByIndex(uint8_t deviceIndex)\r
+{\r
+  DeviceAddress deviceAddress;\r
+  getAddress(deviceAddress, deviceIndex);\r
+  return getTempC((uint8_t*)deviceAddress);\r
+}\r
+\r
+// Fetch temperature for device index\r
+float DallasTemperature::getTempFByIndex(uint8_t deviceIndex)\r
+{\r
+  return DallasTemperature::toFahrenheit(getTempCByIndex(deviceIndex));\r
+}\r
+\r
+// reads scratchpad and returns the temperature in degrees C\r
+float DallasTemperature::calculateTemperature(uint8_t* deviceAddress, uint8_t* scratchPad)\r
+{\r
+  int16_t rawTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];\r
+\r
+  switch (deviceAddress[0])\r
+  {\r
+    case DS18B20MODEL:\r
+    case DS1822MODEL:\r
+      switch (scratchPad[CONFIGURATION])\r
+      {\r
+        case TEMP_12_BIT:\r
+          return (float)rawTemperature * 0.0625;\r
+          break;\r
+        case TEMP_11_BIT:\r
+          return (float)(rawTemperature >> 1) * 0.125;\r
+          break;\r
+        case TEMP_10_BIT:\r
+          return (float)(rawTemperature >> 2) * 0.25;\r
+          break;\r
+        case TEMP_9_BIT:\r
+          return (float)(rawTemperature >> 3) * 0.5;\r
+          break;\r
+      }\r
+      break;\r
+    case DS18S20MODEL:\r
+      /*\r
+      \r
+      Resolutions greater than 9 bits can be calculated using the data from \r
+      the temperature, COUNT REMAIN and COUNT PER °C registers in the \r
+      scratchpad. Note that the COUNT PER °C register is hard-wired to 16 \r
+      (10h). After reading the scratchpad, the TEMP_READ value is obtained \r
+      by truncating the 0.5°C bit (bit 0) from the temperature data. The \r
+      extended resolution temperature can then be calculated using the \r
+      following equation:\r
+      \r
+                                       COUNT_PER_C - COUNT_REMAIN\r
+      TEMPERATURE = TEMP_READ - 0.25 + --------------------------\r
+                                               COUNT_PER_C\r
+      */\r
+  \r
+      // Good spot. Thanks Nic Johns for your contribution\r
+      return (float)(rawTemperature >> 1) - 0.25 +((float)(scratchPad[COUNT_PER_C] - scratchPad[COUNT_REMAIN]) / (float)scratchPad[COUNT_PER_C] ); \r
+      break;\r
+  }\r
+}\r
+\r
+// returns temperature in degrees C or DEVICE_DISCONNECTED if the \r
+// device's scratch pad cannot be read successfully.\r
+// the numeric value of DEVICE_DISCONNECTED is defined in \r
+// DallasTemperature.h.  it is a large negative number outside the \r
+// operating range of the device\r
+float DallasTemperature::getTempC(uint8_t* deviceAddress)\r
+{\r
+  // TODO: Multiple devices (up to 64) on the same bus may take some time to negotiate a response\r
+  // What happens in case of collision?\r
+\r
+  ScratchPad scratchPad;\r
+  if (isConnected(deviceAddress, scratchPad)) return calculateTemperature(deviceAddress, scratchPad);\r
+  return DEVICE_DISCONNECTED;\r
+}\r
+\r
+// returns temperature in degrees F\r
+float DallasTemperature::getTempF(uint8_t* deviceAddress)\r
+{\r
+  return toFahrenheit(getTempC(deviceAddress));\r
+}\r
+\r
+// returns true if the bus requires parasite power\r
+bool DallasTemperature::isParasitePowerMode(void)\r
+{\r
+  return parasite;\r
+}\r
+\r
+#if REQUIRESALARMS\r
+\r
+/*\r
+\r
+ALARMS:\r
+\r
+TH and TL Register Format\r
+\r
+BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0\r
+  S    2^6   2^5   2^4   2^3   2^2   2^1   2^0\r
+\r
+Only bits 11 through 4 of the temperature register are used \r
+in the TH and TL comparison since TH and TL are 8-bit \r
+registers. If the measured temperature is lower than or equal \r
+to TL or higher than or equal to TH, an alarm condition exists \r
+and an alarm flag is set inside the DS18B20. This flag is \r
+updated after every temperature measurement; therefore, if the \r
+alarm condition goes away, the flag will be turned off after \r
+the next temperature conversion.\r
+\r
+*/\r
+\r
+// sets the high alarm temperature for a device in degrees celsius\r
+// accepts a float, but the alarm resolution will ignore anything \r
+// after a decimal point.  valid range is -55C - 125C\r
+void DallasTemperature::setHighAlarmTemp(uint8_t* deviceAddress, char celsius)\r
+{\r
+  // make sure the alarm temperature is within the device's range\r
+  if (celsius > 125) celsius = 125;\r
+  else if (celsius < -55) celsius = -55;\r
+  \r
+  ScratchPad scratchPad;\r
+  if (isConnected(deviceAddress, scratchPad))\r
+  { \r
+    scratchPad[HIGH_ALARM_TEMP] = (uint8_t)celsius;\r
+    writeScratchPad(deviceAddress, scratchPad);\r
+  }\r
+}\r
+\r
+// sets the low alarm temperature for a device in degreed celsius\r
+// accepts a float, but the alarm resolution will ignore anything \r
+// after a decimal point.  valid range is -55C - 125C\r
+void DallasTemperature::setLowAlarmTemp(uint8_t* deviceAddress, char celsius)\r
+{\r
+  // make sure the alarm temperature is within the device's range\r
+  if (celsius > 125) celsius = 125;\r
+  else if (celsius < -55) celsius = -55;\r
+\r
+  ScratchPad scratchPad;\r
+  if (isConnected(deviceAddress, scratchPad))\r
+  {\r
+    scratchPad[LOW_ALARM_TEMP] = (uint8_t)celsius;\r
+    writeScratchPad(deviceAddress, scratchPad);\r
+  }\r
+}\r
+\r
+// returns a char with the current high alarm temperature or \r
+// DEVICE_DISCONNECTED for an address\r
+char DallasTemperature::getHighAlarmTemp(uint8_t* deviceAddress)\r
+{\r
+  ScratchPad scratchPad;\r
+  if (isConnected(deviceAddress, scratchPad)) return (char)scratchPad[HIGH_ALARM_TEMP];\r
+  return DEVICE_DISCONNECTED;\r
+}\r
+\r
+// returns a char with the current low alarm temperature or \r
+// DEVICE_DISCONNECTED for an address\r
+char DallasTemperature::getLowAlarmTemp(uint8_t* deviceAddress)\r
+{\r
+  ScratchPad scratchPad;\r
+  if (isConnected(deviceAddress, scratchPad)) return (char)scratchPad[LOW_ALARM_TEMP];\r
+  return DEVICE_DISCONNECTED;\r
+}\r
+\r
+// resets internal variables used for the alarm search\r
+void DallasTemperature::resetAlarmSearch()\r
+{\r
+  alarmSearchJunction = -1;\r
+  alarmSearchExhausted = 0;\r
+  for(uint8_t i = 0; i < 7; i++)\r
+    alarmSearchAddress[i] = 0;\r
+}\r
+\r
+// This is a modified version of the OneWire::search method.  \r
+//\r
+// Also added the OneWire search fix documented here:\r
+// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295\r
+//\r
+// Perform an alarm search. If this function returns a '1' then it has\r
+// enumerated the next device and you may retrieve the ROM from the\r
+// OneWire::address variable. If there are no devices, no further\r
+// devices, or something horrible happens in the middle of the\r
+// enumeration then a 0 is returned.  If a new device is found then\r
+// its address is copied to newAddr.  Use \r
+// DallasTemperature::resetAlarmSearch() to start over.\r
+bool DallasTemperature::alarmSearch(uint8_t* newAddr)\r
+{\r
+  uint8_t i;\r
+  char lastJunction = -1;\r
+  uint8_t done = 1;\r
+      \r
+  if (alarmSearchExhausted) return false;\r
+  if (!_wire->reset()) return false;\r
+\r
+  // send the alarm search command\r
+  _wire->write(0xEC, 0);\r
+      \r
+  for(i = 0; i < 64; i++)\r
+  {\r
+    uint8_t a = _wire->read_bit( );\r
+    uint8_t nota = _wire->read_bit( );\r
+    uint8_t ibyte = i / 8;\r
+    uint8_t ibit = 1 << (i & 7);\r
+    \r
+    // I don't think this should happen, this means nothing responded, but maybe if\r
+    // something vanishes during the search it will come up.\r
+    if (a && nota) return false;\r
+\r
+    if (!a && !nota)\r
+    {\r
+      if (i == alarmSearchJunction)\r
+      {\r
+        // this is our time to decide differently, we went zero last time, go one.\r
+        a = 1;\r
+        alarmSearchJunction = lastJunction;\r
+      }\r
+      else if (i < alarmSearchJunction) \r
+      {\r
+        // take whatever we took last time, look in address\r
+        if (alarmSearchAddress[ibyte] & ibit) a = 1;\r
+        else\r
+        {\r
+          // Only 0s count as pending junctions, we've already exhasuted the 0 side of 1s\r
+          a = 0;\r
+          done = 0;\r
+          lastJunction = i;\r
+        }\r
+      }\r
+      else\r
+      {\r
+        // we are blazing new tree, take the 0\r
+        a = 0;\r
+        alarmSearchJunction = i;\r
+        done = 0;\r
+      }\r
+      // OneWire search fix\r
+      // See: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295\r
+    }\r
+\r
+    if (a) alarmSearchAddress[ibyte] |= ibit;\r
+    else alarmSearchAddress[ibyte] &= ~ibit;\r
+  \r
+    _wire->write_bit(a);\r
+  }\r
+\r
+  if (done) alarmSearchExhausted = 1;\r
+  for (i = 0; i < 8; i++) newAddr[i] = alarmSearchAddress[i];\r
+  return true;  \r
+}\r
+\r
+// returns true if device address has an alarm condition\r
+bool DallasTemperature::hasAlarm(uint8_t* deviceAddress)\r
+{\r
+  ScratchPad scratchPad;\r
+  if (isConnected(deviceAddress, scratchPad))\r
+  {\r
+    float temp = calculateTemperature(deviceAddress, scratchPad);\r
+  \r
+    // check low alarm\r
+    if ((char)temp <= (char)scratchPad[LOW_ALARM_TEMP]) return true;\r
+  \r
+    // check high alarm\r
+    if ((char)temp >= (char)scratchPad[HIGH_ALARM_TEMP]) return true;\r
+  }\r
+\r
+  // no alarm\r
+  return false;\r
+}\r
+\r
+// returns true if any device is reporting an alarm condition on the bus\r
+bool DallasTemperature::hasAlarm(void)\r
+{\r
+  DeviceAddress deviceAddress;\r
+  resetAlarmSearch();\r
+  return alarmSearch(deviceAddress);\r
+}\r
+\r
+// runs the alarm handler for all devices returned by alarmSearch()\r
+void DallasTemperature::processAlarms(void)\r
+{\r
+  resetAlarmSearch();\r
+  DeviceAddress alarmAddr;\r
+\r
+  while (alarmSearch(alarmAddr))\r
+  {\r
+    if (validAddress(alarmAddr))\r
+      _AlarmHandler(alarmAddr);\r
+  }\r
+}\r
+\r
+// sets the alarm handler\r
+void DallasTemperature::setAlarmHandler(AlarmHandler *handler)\r
+{\r
+  _AlarmHandler = handler;\r
+}\r
+\r
+// The default alarm handler\r
+void DallasTemperature::defaultAlarmHandler(uint8_t* deviceAddress)\r
+{\r
+}\r
+\r
+#endif\r
+\r
+// Convert float celsius to fahrenheit\r
+float DallasTemperature::toFahrenheit(float celsius)\r
+{\r
+  return (celsius * 1.8) + 32;\r
+}\r
+\r
+// Convert float fahrenheit to celsius\r
+float DallasTemperature::toCelsius(float fahrenheit)\r
+{\r
+  return (fahrenheit - 32) / 1.8;\r
+}\r
+\r
+#if REQUIRESNEW\r
+\r
+// MnetCS - Allocates memory for DallasTemperature. Allows us to instance a new object\r
+void* DallasTemperature::operator new(unsigned int size) // Implicit NSS obj size\r
+{\r
+  void * p; // void pointer\r
+  p = malloc(size); // Allocate memory\r
+  memset((DallasTemperature*)p,0,size); // Initalise memory\r
+\r
+  //!!! CANT EXPLICITLY CALL CONSTRUCTOR - workaround by using an init() methodR - workaround by using an init() method\r
+  return (DallasTemperature*) p; // Cast blank region to NSS pointer\r
+}\r
+\r
+// MnetCS 2009 -  Unallocates the memory used by this instance\r
+void DallasTemperature::operator delete(void* p)\r
+{\r
+  DallasTemperature* pNss =  (DallasTemperature*) p; // Cast to NSS pointer\r
+  pNss->~DallasTemperature(); // Destruct the object\r
+\r
+  free(p); // Free the memory\r
+}\r
+\r
+#endif\r