--- /dev/null
+// 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