diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 280538cf..d7a223dd 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -266,6 +266,7 @@ if (BACNET_FOUND) endif() add_example (vcap) add_example (ds2413) +add_example (ds18b20) # These are special cases where you specify example binary, source file and module(s) include_directories (${PROJECT_SOURCE_DIR}/src) diff --git a/examples/c++/ds18b20.cxx b/examples/c++/ds18b20.cxx new file mode 100644 index 00000000..ce53d580 --- /dev/null +++ b/examples/c++/ds18b20.cxx @@ -0,0 +1,81 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include + +#include "ds18b20.h" + +using namespace std; + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); + +//! [Interesting] + + cout << "Initializing..." << endl; + + // Instantiate an DS18B20 instance using the default values (uart 0) + upm::DS18B20 sensor; + + // locate and setup our devices + sensor.init(); + + cout << "Found " << sensor.devicesFound() << " device(s)" << endl; + cout << endl; + + // bail if we didn't find anything + if (!sensor.devicesFound()) + return 1; + + // update and print available values every second + while (shouldRun) + { + // update our values for the first sensor + sensor.update(0); + + // we show both C and F for temperature for the first sensor + cout << "Temperature: " << sensor.getTemperature(0) + << " C / " << sensor.getTemperature(0, true) << " F" + << endl; + + sleep(1); + } + + cout << "Exiting..." << endl; + +//! [Interesting] + + return 0; +} diff --git a/examples/javascript/ds18b20.js b/examples/javascript/ds18b20.js new file mode 100644 index 00000000..3c2835fc --- /dev/null +++ b/examples/javascript/ds18b20.js @@ -0,0 +1,70 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* + * Author: Jon Trulson + * 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. + */ + + +var sensorObj = require('jsupm_ds18b20'); + + +/************** Main code **************/ + +console.log("Initializing..."); + +// Instantiate an DS18B20 instance using the default values (uart 0) +var sensor = new sensorObj.DS18B20(0); + +// locate and setup our devices +sensor.init(); + +console.log("Found", sensor.devicesFound(), "device(s)"); +console.log(""); + +if (!sensor.devicesFound()) +{ + process.exit(1); +} + +// update and print available values every second +setInterval(function() +{ + // update our values for the first sensor + sensor.update(0); + + // we show both C and F for temperature for the first sensor + console.log("Temperature:", sensor.getTemperature(0), + "C /", sensor.getTemperature(0, true), "F"); + +}, 1000); + + +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log("Exiting..."); + process.exit(0); +}); diff --git a/examples/python/ds18b20.py b/examples/python/ds18b20.py new file mode 100644 index 00000000..d91d33c8 --- /dev/null +++ b/examples/python/ds18b20.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# Author: Jon Trulson +# 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. + +import time, sys, signal, atexit +import pyupm_ds18b20 as sensorObj + +## Exit handlers ## +# This function stops python from printing a stacktrace when you hit control-C +def SIGINTHandler(signum, frame): + raise SystemExit + +# This function lets you run code on exit +def exitHandler(): + print "Exiting..." + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) +signal.signal(signal.SIGINT, SIGINTHandler) + +print "Initializing..." + +# Instantiate an DS18B20 instance using the default values (uart 0) +sensor = sensorObj.DS18B20(0) + +# locate and setup our devices +sensor.init() + +print "Found", sensor.devicesFound(), "device(s)" +print + +if (not sensor.devicesFound()): + sys.exit(1); + +# update and print available values every second +while (1): + # update our values for the first sensor + sensor.update(0) + + # we show both C and F for temperature for the first sensor + print "Temperature:", sensor.getTemperature(0), "C /", + print sensor.getTemperature(0, True), "F" + + time.sleep(1) diff --git a/src/ds18b20/CMakeLists.txt b/src/ds18b20/CMakeLists.txt new file mode 100644 index 00000000..f8f38a83 --- /dev/null +++ b/src/ds18b20/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "ds18b20") +set (libdescription "upm DS18B20 1-wire Temperature sensor") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/ds18b20/ds18b20.cxx b/src/ds18b20/ds18b20.cxx new file mode 100644 index 00000000..50688ae1 --- /dev/null +++ b/src/ds18b20/ds18b20.cxx @@ -0,0 +1,298 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include + +#include "ds18b20.h" + +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>= _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) + { + 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>= 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= 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); +} diff --git a/src/ds18b20/ds18b20.h b/src/ds18b20/ds18b20.h new file mode 100644 index 00000000..46d7abb2 --- /dev/null +++ b/src/ds18b20/ds18b20.h @@ -0,0 +1,242 @@ +/* + * Author: Jon Trulson + * 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. + */ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define DS18B20_DEFAULT_UART 0 + +namespace upm { + /** + * @brief DS18B20 1-Wire Temperature Sensor + * @defgroup ds18b20 libupm-ds18b20 + * @ingroup maxim uart + */ + + /** + * @library ds18b20 + * @sensor ds18b20 + * @comname DS18B20 1-Wire Temperature Sensor + * @type uart + * @man maxim + * @con uart + * @web https://www.sparkfun.com/products/11050 + * + * @brief API for the DS18B20 1-Wire Temperature Sensor + * + * This driver supports, and was tested with, a DS18B20 with + * external power. + * + * Multiple DS18B20 devices can be connected to this bus. This + * module will identify all such devices connected, and allow you + * to access them using an index starting at 0. + * + * Parasitic power is not currently supported due + * to the very tight 10us limit on switching a GPIO properly to + * supply power during certain operations. For this reason, you + * should use external power for your sensors. + * + * Setting the alarm values (Tl, Th) is also not supported, since + * this is only useful when doing a 1-wire device search looking + * for devices in an alarm state, a capability not yet supported + * in MRAA. In reality, this is trivial to handle yourself in + * your application. + * + * This device requires the use of a UART to provide access to a + * Dallas 1-wire bus, via a new facility supported by MRAA (once + * the relevant PR is accepted), using the UartOW access class. + * It is important to realize that the UART is only being used to + * access and control a Dallas 1-wire compliant bus, it is not + * actually a UART device. + * + * @snippet ds18b20.cxx Interesting + */ + + class DS18B20 { + public: + + // The family code for these devices. We handle all of them that + // are found on the bus. + static const uint8_t DS18B20_FAMILY_CODE = 0x28; + + // commands + typedef enum { + CMD_CONVERT = 0x44, // start a temp conversion + CMD_WRITE_SCRATCHPAD = 0x4e, + CMD_READ_SCRATCHPAD = 0xbe, + CMD_COPY_SCRATCHPAD = 0x48, // copy scratchpad to EEPROM + CMD_RECALL_EEPROM = 0xb8, // copy EEPROM to scratchpad + CMD_READ_POWER_SUPPLY = 0xb4 // parasitically powered? + } CMD_T; + + // config register (scratchpad[4]) + typedef enum { + CFG_RESOLUTION_R0 = 0x20, + CFG_RESOLUTION_R1 = 0x40, + _CFG_RESOLUTION_MASK = 3, + _CFG_RESOLUTION_SHIFT = 5 + + // all other bits reserved and non-writable + } CFG_BITS_T; + + typedef enum { + RESOLUTION_9BITS = 0, // 93.75ms (tconv/8) + RESOLUTION_10BITS = 1, // 187.5 (tconv/4) + RESOLUTION_11BITS = 2, // 375ms (tconv/2) + RESOLUTION_12BITS = 3 // 750ms (tconv) + } RESOLUTIONS_T; + + /** + * DS18B20 object constructor + * + * @param uart Default UART to use (0 or 1). Default is 0. + */ + DS18B20(int uart=DS18B20_DEFAULT_UART); + + /** + * DS18B20 object destructor + */ + ~DS18B20(); + + /** + * This method will search the 1-wire bus and store information on + * each device detected on the bus. If no devices are found, an + * exception is thrown. Once this function completes + * successfully, you can use devicesFound() to determine how many + * devices were detected. This method must be executed first + * before any others below. + */ + void init(); + + /** + * Update our stored temperature for a device. This method must + * be called prior to getTemperature(). + * + * @param index The device index to access (starts at 0). Specify + * -1 to query all detected devices. Default: -1 + */ + void update(int index=-1); + + /** + * Get the current temperature. update() must have been called + * prior to calling this method. + * + * @param index The device index to access (starts at 0). + * @param fahrenheit true to return the temperature in degrees + * fahrenheit, false to return the temperature in degrees celcius. + * The default is false (degrees Celcius). + * @return The last temperature reading in Celcius or Fahrenheit + */ + float getTemperature(int index, bool fahrenheit=false); + + /** + * Set the device resolution for a device. These devices support + * 9, 10, 11, and 12 bits of resolution, with the default from the + * factory at 12 bits. + * + * @param index The device index to access (starts at 0). + * @param res One of the RESOLUTIONS_T values + */ + void setResolution(int index, RESOLUTIONS_T res); + + /** + * Copy the device's scratchpad memory to the EEPROM. This + * includes the configuration byte (resolution). + * + * @param index The device index to access (starts at 0). + */ + void copyScratchPad(int index); + + /** + * Copy the device's EEPROM memory to the scratchpad. This method + * will return when the copy completes. This operation is + * performed by the device automatically on power up, so it is + * rarely needed. + * + * @param index The device index to access (starts at 0). + */ + void recallEEPROM(int index); + + /** + * This method will return the number of DS18B20 devices that were + * found on the bus by init(). + * + * @return number of DS18B20's that were found on the bus + */ + int devicesFound() + { + return m_devicesFound; + } + + /** + * Return an 8 byte string representing the unique device ID + * (1-wire romcode) for a given device index. + * + * @param index The device index to access (starts at 0). + * @return 8 byte string representing the 1-wire device's unique + * romcode. + */ + std::string getId(int index) + { + if (index < 0 || index >= m_devicesFound) + { + throw std::out_of_range(std::string(__FUNCTION__) + + ": device index out of range"); + } + return m_deviceMap[index].id; + } + + protected: + mraa::UartOW m_uart; + + // the total number of devices found + int m_devicesFound; + + // this struct will generate SWIG warnings on build, but as it's not + // exposed outside the class, they can be safely ignored + + // data we need to store for each sensor we are dealing with + typedef struct { + std::string id; // 8-byte romcode id + float temperature; + RESOLUTIONS_T resolution; + } sensor_info_t; + + std::map m_deviceMap; + + private: + // internal utility function to read temperature from a single + // device + float readSingleTemp(int index); + }; +} diff --git a/src/ds18b20/javaupm_ds18b20.i b/src/ds18b20/javaupm_ds18b20.i new file mode 100644 index 00000000..b8ee1b2c --- /dev/null +++ b/src/ds18b20/javaupm_ds18b20.i @@ -0,0 +1,22 @@ +%module javaupm_ds18b20 +%include "../upm.i" +%include "carrays.i" +%include "std_string.i" + +%{ + #include "ds18b20.h" +%} + +%include "ds18b20.h" +%array_class(char, charArray); + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_ds18b20"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/ds18b20/jsupm_ds18b20.i b/src/ds18b20/jsupm_ds18b20.i new file mode 100644 index 00000000..ac4cb95d --- /dev/null +++ b/src/ds18b20/jsupm_ds18b20.i @@ -0,0 +1,11 @@ +%module jsupm_ds18b20 +%include "../upm.i" +%include "carrays.i" +%include "std_string.i" + +%{ + #include "ds18b20.h" +%} + +%include "ds18b20.h" +%array_class(char, charArray); diff --git a/src/ds18b20/pyupm_ds18b20.i b/src/ds18b20/pyupm_ds18b20.i new file mode 100644 index 00000000..0cdd96ea --- /dev/null +++ b/src/ds18b20/pyupm_ds18b20.i @@ -0,0 +1,12 @@ +%module pyupm_ds18b20 +%include "../upm.i" +%include "carrays.i" +%include "std_string.i" + +%feature("autodoc", "3"); + +%{ + #include "ds18b20.h" +%} +%include "ds18b20.h" +%array_class(char, charArray);