/* * Author: Jon Trulson <jtrulson@ics.com> * Copyright (c) 2016 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include <iostream> #include <time.h> #include <stdexcept> #include "ds18b20.hpp" using namespace upm; using namespace std; // conversion from celcius to fahrenheit static float c2f(float c) { return (c * (9.0 / 5.0) + 32.0); } DS18B20::DS18B20(int uart) : m_uart(uart) { m_devicesFound = 0; // check basic access to the 1-wire bus (presence detect) mraa::Result rv; if ((rv = m_uart.reset()) != mraa::SUCCESS) { throw std::runtime_error(std::string(__FUNCTION__) + ": reset() failed, no devices on bus?"); } } DS18B20::~DS18B20() { } void DS18B20::init() { // iterate through the bus and build up a list of detected DS18B20 // devices (only) // empty the map, in case this method has already been run once // before m_devicesFound = 0; m_deviceMap.clear(); sensor_info_t sinfo; // defaults sinfo.temperature = 0.0; sinfo.resolution = RESOLUTION_12BITS; // start the search from scratch string id = m_uart.search(true); if (id.empty()) { throw std::runtime_error(std::string(__FUNCTION__) + ": no devices detected on bus"); } while (!id.empty()) { // The first byte (id[0]]) is the device type (family) code. We // are only interested in the family code for these devices. if ((uint8_t)id[0] == DS18B20_FAMILY_CODE) { // we have a winner, add it to our map and continue searching sinfo.id = id; m_deviceMap[m_devicesFound] = sinfo; m_devicesFound++; } // continue search id = m_uart.search(false); } if (!m_devicesFound) { throw std::runtime_error(std::string(__FUNCTION__) + ": no DS18B20 devices found on bus"); } // iterate through the found devices and query their resolutions for (int i=0; i<m_devicesFound; i++) { // read only the first 5 bytes of the scratchpad static const int numScratch = 5; uint8_t scratch[numScratch]; m_uart.command(CMD_READ_SCRATCHPAD, m_deviceMap[i].id); for (int j=0; j<numScratch; j++) scratch[j] = m_uart.readByte(); // config byte, shift the resolution to bit 0 scratch[4] >>= _CFG_RESOLUTION_SHIFT; switch (scratch[4] & _CFG_RESOLUTION_MASK) { case 0: m_deviceMap[i].resolution = RESOLUTION_9BITS; break; case 1: m_deviceMap[i].resolution = RESOLUTION_10BITS; break; case 2: m_deviceMap[i].resolution = RESOLUTION_11BITS; break; case 3: m_deviceMap[i].resolution = RESOLUTION_12BITS; break; } // reset the bus m_uart.reset(); } } void DS18B20::update(int index) { if (index >= m_devicesFound) { throw std::out_of_range(std::string(__FUNCTION__) + ": device index out of range"); } // should we update all of them? bool doAll = (index < 0) ? true : false; if (doAll) { // if we want to update all of them, we will first send the // convert command to all of them, then wait. This will be // faster, timey-wimey wise, then converting, sleeping, and // reading each individual sensor. for (int i=0; i<m_devicesFound; i++) m_uart.command(CMD_CONVERT, m_deviceMap[i].id); } else m_uart.command(CMD_CONVERT, m_deviceMap[index].id); // wait for conversion(s) to finish usleep(750000); // 750ms max if (doAll) { for (int i=0; i<m_devicesFound; i++) m_deviceMap[i].temperature = readSingleTemp(i); } else m_deviceMap[index].temperature = readSingleTemp(index); } // utility function to read temp data from a single sensor float DS18B20::readSingleTemp(int index) { if (index < 0 || index >= m_devicesFound) { throw std::out_of_range(std::string(__FUNCTION__) + ": device index out of range"); } static const int numScratch = 9; uint8_t scratch[numScratch]; // read the 9-byte scratchpad m_uart.command(CMD_READ_SCRATCHPAD, m_deviceMap[index].id); for (int i=0; i<numScratch; i++) scratch[i] = m_uart.readByte(); // validate cksum -- if we get an error, we will warn and simply // return the current (previously read) temperature uint8_t crc = m_uart.crc8(scratch, 8); if (crc != scratch[8]) { cerr << __FUNCTION__ << ": crc check failed for device " << index << ", returning previously measured temperature" << endl; return m_deviceMap[index].temperature; } // check the sign bit(s) bool negative = (scratch[1] & 0x80) ? true : false; // shift everything into position int16_t temp = (scratch[1] << 8) | scratch[0]; // grab the fractional uint8_t frac = temp & 0x0f; // depending on the resolution, some frac bits should be ignored, so // we mask them off. For 12bits, all bits are valid so we leve them // alone. switch (m_deviceMap[index].resolution) { case RESOLUTION_9BITS: frac &= 0x08; break; case RESOLUTION_10BITS: frac &= 0x0c; break; case RESOLUTION_11BITS: frac &= 0x0e; break; } // remove the fractional with extreme prejudice temp >>= 4; // compensate for sign if (negative) temp -= 65536; // 2^^16 // convert return ( float(temp) + (float(frac) * 0.0625) ); } float DS18B20::getTemperature(int index, bool fahrenheit) { if (index < 0 || index >= m_devicesFound) { throw std::out_of_range(std::string(__FUNCTION__) + ": device index out of range"); } if (fahrenheit) return c2f(m_deviceMap[index].temperature); else return m_deviceMap[index].temperature; } void DS18B20::setResolution(int index, RESOLUTIONS_T res) { if (index < 0 || index >= m_devicesFound) { throw std::out_of_range(std::string(__FUNCTION__) + ": device index out of range"); } static const int numScratch = 9; uint8_t scratch[numScratch]; // read the 9-byte scratchpad m_uart.command(CMD_READ_SCRATCHPAD, m_deviceMap[index].id); for (int i=0; i<numScratch; i++) scratch[i] = m_uart.readByte(); // resolution is stored in byte 4 scratch[4] = ((scratch[4] & ~(_CFG_RESOLUTION_MASK << _CFG_RESOLUTION_SHIFT)) | (res << _CFG_RESOLUTION_SHIFT)); // now, write back, we only write 3 bytes (2-4), no cksum. m_uart.command(CMD_WRITE_SCRATCHPAD, m_deviceMap[index].id); for (int i=0; i<3; i++) m_uart.writeByte(scratch[i+2]); } void DS18B20::copyScratchPad(int index) { if (index < 0 || index >= m_devicesFound) { throw std::out_of_range(std::string(__FUNCTION__) + ": device index out of range"); } // issue the command m_uart.command(CMD_COPY_SCRATCHPAD, m_deviceMap[index].id); sleep(1); // to be safe... } void DS18B20::recallEEPROM(int index) { if (index < 0 || index >= m_devicesFound) { throw std::out_of_range(std::string(__FUNCTION__) + ": device index out of range"); } // issue the command m_uart.command(CMD_RECALL_EEPROM, m_deviceMap[index].id); // issue read timeslots until a '1' is read back, indicating completion while (!m_uart.writeBit(1)) usleep(100); }