diff --git a/docs/images/am2315.jpeg b/docs/images/am2315.jpeg new file mode 100644 index 00000000..f13efe82 Binary files /dev/null and b/docs/images/am2315.jpeg differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b9e77baf..6d0aa2f0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -47,6 +47,7 @@ add_executable (tsl2561-example tsl2561.cxx) add_executable (htu21d-example htu21d.cxx) add_executable (mpl3115a2-example mpl3115a2.cxx) add_executable (ldt0028-example ldt0028.cxx) +add_executable (am2315-example am2315.cxx) include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -83,6 +84,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/tsl2561) include_directories (${PROJECT_SOURCE_DIR}/src/htu21d) include_directories (${PROJECT_SOURCE_DIR}/src/mpl3115a2) include_directories (${PROJECT_SOURCE_DIR}/src/ldt0028) +include_directories (${PROJECT_SOURCE_DIR}/src/am2315) target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -133,3 +135,4 @@ target_link_libraries (tsl2561-example tsl2561 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (htu21d-example htu21d ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (mpl3115a2-example mpl3115a2 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (ldt0028-example ldt0028 ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (am2315-example am2315 ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/am2315.cxx b/examples/am2315.cxx new file mode 100755 index 00000000..4b95e1c7 --- /dev/null +++ b/examples/am2315.cxx @@ -0,0 +1,75 @@ +/* + * Author: William Penner + * Copyright (c) 2014 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 "am2315.h" + +volatile int doWork = 0; + +upm::AM2315 *sensor = NULL; + +void +sig_handler(int signo) +{ + if (signo == SIGINT) { + printf("\nCtrl-C received.\n"); + doWork = 1; + } +} + +int +main(int argc, char **argv) +{ + // Register signal handler + signal(SIGINT, sig_handler); + + //! [Interesting] + float humidity = 0.0; + float temperature = 0.0; + + sensor = new upm::AM2315(0, AM2315_I2C_ADDRESS); + + sensor->testSensor(); + + while (!doWork) { + humidity = sensor->getHumidity(); + temperature = sensor->getTemperature(); + + std::cout << "humidity value = " << + humidity << + ", temperature value = " << + temperature << std::endl; + usleep (500000); + } + //! [Interesting] + + std::cout << "exiting application" << std::endl; + + delete sensor; + + return 0; +} diff --git a/src/am2315/CMakeLists.txt b/src/am2315/CMakeLists.txt new file mode 100644 index 00000000..5931421c --- /dev/null +++ b/src/am2315/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "am2315") +set (libdescription "libupm Humidity Sensor") +set (module_src ${libname}.cpp) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/am2315/am2315.cpp b/src/am2315/am2315.cpp new file mode 100644 index 00000000..9cea295b --- /dev/null +++ b/src/am2315/am2315.cpp @@ -0,0 +1,368 @@ +/* + * Author: William Penner + * Copyright (c) 2014 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 +#include +#include + +#include "am2315.h" + +using namespace upm; + +char g_name[] = AM2315_NAME; + +AM2315::AM2315(int bus, int devAddr) { + m_temperature = 0; + m_humidity = 0; + m_last_time = 0; + + m_name = g_name; + + m_controlAddr = devAddr; + m_bus = bus; + + initialize_priority(); + + m_i2ControlCtx = mraa_i2c_init(m_bus); + + mraa_result_t ret = mraa_i2c_address(m_i2ControlCtx, m_controlAddr); + if (ret != MRAA_SUCCESS) { + fprintf(stderr, "%s: Error accessing i2c bus\n", m_name); + } + m_model = i2cReadReg_16(AM2315_MODEL); + m_version = i2cReadReg_8(AM2315_VERSION); + m_id = i2cReadReg_32(AM2315_ID); + + fprintf(stdout,"%s: Model: 0x%04x Version: 0x%02x ID: 0x%08x\n", + m_name, m_model, m_version, m_id ); +} + +AM2315::~AM2315() { + mraa_i2c_stop(m_i2ControlCtx); +} + +void +AM2315::update_values(void) +{ + time_t ctime = time(NULL); + if ((ctime - m_last_time) >= AM2315_SAMPLE) { + uint32_t uival = i2cReadReg_32(AM2315_HUMIDITY); + m_humidity = uival >> 16; + m_temperature = uival & 0xffff; + m_last_time = ctime; + } + else { + // In case the time is changed - backwards + if (ctime < m_last_time) + m_last_time = ctime; + } +} + +float +AM2315::getTemperature(void) +{ + update_values(); + return (float)m_temperature / 10; +} + +float +AM2315::getTemperatureF(void) +{ + return getTemperature() * 9 / 5 + 32; +} + +float +AM2315::getHumidity(void) +{ + update_values(); + return (float)m_humidity / 10; +} + +/* + * Test function: when reading the AM2315 many times rapidly should + * result in a temperature increase. This test will verify that the + * value is changing from read to read + */ + +int +AM2315::testSensor(void) +{ + int i; + int iError = 0; + float fTemp, fHum; + float fTempMax, fTempMin; + float fHumMax, fHumMin; + + fprintf(stdout, "%s: Executing Sensor Test\n", m_name ); + + fHum = getHumidity(); + fTemp = getTemperature(); + fTempMax = fTempMin = fTemp; + fHumMax = fHumMin = fHum; + + // Then sample the sensor a few times + for (i=0; i < 10; i++) { + fHum = getHumidity(); + fTemp = getTemperature(); + if (fHum < fHumMin) fHumMin = fHum; + if (fHum > fHumMax) fHumMax = fHum; + if (fTemp < fTempMin) fTempMin = fTemp; + if (fTemp > fTempMax) fTempMax = fTemp; + usleep(50000); + } + + // Now check the results + if (fHumMin == fHumMax && fTempMin == fTempMax) { + fprintf(stdout, "%s: Humidity/Temp reading was unchanged - warning\n", + m_name ); + iError++; + } + if (iError == 0) { + fprintf(stdout, "%s: Device appears functional\n", m_name ); + } + + fprintf(stdout, "%s: Test complete\n", m_name ); + + return iError; +} + +/* + * Functions to alter the thread priority while reading the sensor + * to ensure we can get good data + */ + +void +AM2315::initialize_priority(void) +{ + m_use_priority = true; + this_thread = pthread_self(); + + base_policy = 0; + int ret = pthread_getschedparam(this_thread, &base_policy, &base_params); + if (ret != 0) { + fprintf(stdout, "%s: Error accessing pthread scheduling.\n", m_name); + return; + } +} + +void +AM2315::high_priority(void) +{ + if (! m_use_priority) + return; + + struct sched_param params; + params.sched_priority = sched_get_priority_max(SCHED_FIFO); + int ret = pthread_setschedparam(this_thread, SCHED_FIFO, ¶ms); + if (ret != 0) { + fprintf(stdout,"%s: Unable to set thread priority.\n", m_name); + m_use_priority = false; + return; + } + int policy = 0; + ret = pthread_getschedparam(this_thread, &policy, ¶ms); + if (ret != 0) { + fprintf(stdout,"%s: Unable to get thread priority.\n", m_name); + m_use_priority = false; + return; + } + if(policy != SCHED_FIFO) { + fprintf(stdout,"%s: Unable to change thread priority.\n", m_name); + m_use_priority = false; + } +} + +void +AM2315::normal_priority(void) +{ + if (! m_use_priority) + return; + + int ret = pthread_setschedparam(this_thread, base_policy, &base_params); + if (ret != 0) { + fprintf(stdout,"%s: Unable to set thread priority.\n", m_name); + } +} + +uint16_t +AM2315::crc16(uint8_t* ptr, uint8_t len) +{ + uint16_t crc = 0xffff; + uint8_t i; + + while(len--) { + crc ^= *ptr++; + for (i=0; i < 8; i++) { + if (crc & 0x01) { + crc >>= 1; + crc ^= 0xA001; + } + else { + crc >>= 1; + } + } + } + return crc; +} + +/* + * Functions to read and write data to the i2c device in the + * special format used by the device. This is using i2c to + * interface to a controller that the AOSONG AM2315 uses to + * perform the measurements and manage other registers. + */ +int +AM2315::i2cWriteReg(uint8_t reg, uint8_t* data, uint8_t ilen) +{ + uint8_t tdata[16] = { AM2315_WRITE, reg, ilen }; + mraa_result_t error; + + for (int i=0; i < ilen; i++) { + tdata[i+3] = data[i]; + } + uint16_t crc = crc16(tdata, ilen+3); + // CRC is sent out backwards from other registers (low, high) + tdata[ilen+3] = crc; + tdata[ilen+4] = (crc >> 8); + + mraa_result_t ret = mraa_i2c_address(m_i2ControlCtx, m_controlAddr); + int iLoops = 5; + high_priority(); + do { + error = mraa_i2c_write(m_i2ControlCtx, tdata, ilen+5); + usleep(800); + } while(error != MRAA_SUCCESS && --iLoops); + normal_priority(); + + if (error != MRAA_SUCCESS) { + fprintf(stdout, "%s: Error, timeout writing sensor.\n", m_name); + return -1; + } + crc = crc16(tdata,3); + mraa_i2c_read(m_i2ControlCtx, tdata, 5); + if ((tdata[0] != AM2315_WRITE) || + (tdata[1] != reg) || + (tdata[2] != ilen) || + (tdata[3] != (crc & 0xff)) || + (tdata[4] != (crc >> 8))) { + fprintf(stdout, "%s: CRC error during write verification\n", m_name); + return -1; + } + return 0; +} + + +// TODO: Need to patch up function to return only the data that +// is needed and not require the various functions that call this +// to send it enough buffer to cover the function + +uint8_t +AM2315::i2cReadReg(int reg, uint8_t* data, int ilen) +{ + uint8_t tdata[16] = { AM2315_READ, reg, ilen }; + + mraa_result_t ret = mraa_i2c_address(m_i2ControlCtx, m_controlAddr); + int iLoops = 5; + high_priority(); + do { + ret = mraa_i2c_write(m_i2ControlCtx, tdata, 3); + usleep(800); + } while(ret != MRAA_SUCCESS && --iLoops); + if (ret != MRAA_SUCCESS) { + fprintf(stdout, "%s: Error, timeout reading sensor.\n", m_name); + normal_priority(); + return -1; + } + usleep(5000); + mraa_i2c_read(m_i2ControlCtx, tdata, ilen+4); + normal_priority(); + + uint16_t crc = crc16(tdata, ilen+2); + if ((tdata[0] != AM2315_READ) || + (tdata[1] != ilen) || + (tdata[ilen+2] != (crc & 0xff)) || + (tdata[ilen+3] != (crc >> 8))) { + fprintf(stdout, "%s: Read crc failed.\n", m_name); + } + for (int i=0; i < ilen; i++) + data[i] = tdata[i+2]; + + return 0; +} + +/* + * Functions to set up the reads and writes to simplify the process of + * formatting data as needed by the microcontroller + */ + +int +AM2315::i2cWriteReg_32(int reg, uint32_t ival) { + uint8_t data[4]; + data[0] = ival >> 24; + data[1] = ival >> 16; + data[1] = ival >> 8; + data[1] = ival & 0xff; + return i2cWriteReg(reg, data, 4); +} + +int +AM2315::i2cWriteReg_16(int reg, uint16_t ival) { + uint8_t data[2]; + data[0] = ival & 0xff; + data[1] = ival >> 8; + return i2cWriteReg(reg, data, 2); +} + +int +AM2315::i2cWriteReg_8(int reg, uint8_t ival) { + uint8_t data[2]; + data[0] = ival & 0xff; + data[1] = ival >> 8; + return i2cWriteReg(reg, data, 2); +} + +uint32_t +AM2315::i2cReadReg_32 (int reg) { + uint8_t data[4]; + i2cReadReg(reg, data, 4); + return ((((((uint32_t)data[0] << 8) | data[1]) << 8) | + data[2]) << 8) | data[3]; +} + +uint16_t +AM2315::i2cReadReg_16 (int reg) { + uint8_t data[2]; + i2cReadReg(reg, data, 2); + return ((int16_t)data[0] << 8) | (uint16_t)data[1]; +} + +uint8_t +AM2315::i2cReadReg_8 (int reg) { + uint8_t data[1]; + i2cReadReg(reg, data, 1); + return data[0]; +} + diff --git a/src/am2315/am2315.h b/src/am2315/am2315.h new file mode 100644 index 00000000..d4aea832 --- /dev/null +++ b/src/am2315/am2315.h @@ -0,0 +1,195 @@ +/* + * Author: William Penner + * Copyright (c) 2014 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 + +#define AM2315_NAME "am2315" +#define AM2315_I2C_ADDRESS 0x5c + +#define AM2315_READ 0x03 +#define AM2315_WRITE 0x10 + +/* AM2315 Commands */ +#define AM2315_HUMIDITY 0x00 +#define AM2315_TEMP 0x02 +#define AM2315_MODEL 0x08 +#define AM2315_VERSION 0x0A +#define AM2315_ID 0x0B +#define AM2315_STATUS 0x0F +#define AM2315_USER_A 0x10 +#define AM2315_USER_B 0x12 + +#define AM2315_SAMPLE 2 + +namespace upm { + +/** + * @brief AM2315 humidity sensor library + * @defgroup htu21d libupm-htu21 + */ + +/** + * @brief C++ API for AM2315 chip (Atmospheric Pressure Sensor) + * + * Measurement Specialties [AM2315] + * (http://www.aosong.com/asp_bin/Products/en/AM2315.pdf) + * is a digital humidity sensor with temperature output. + * RH will report between 0 and 100% and temperature range is + * -40 to +125 degC. + * The sampling period of this sensor is 2 seconds. Reads occurring + * more often than that will return cached data. + * + * @ingroup am2315 i2c + * @snippet am2315.cxx Interesting + * @image html am2315.jpeg + */ +class AM2315 { + public: + /** + * Instanciates a AM2315 object + * + * @param bus number of used bus + * @param devAddr address of used i2c device + * @param mode AM2315 oversampling + */ + AM2315 (int bus, int devAddr=AM2315_I2C_ADDRESS); + + /** + * AM2315 object destructor, basicaly it close i2c connection. + */ + ~AM2315 (); + + /** + * Get the current measured humidity [RH] + * Data is updated every 2 seconds - accesses more often than + * that will return cached data + */ + float getHumidity(void); + + /** + * Get the humidity cell temperature [degC] + * Data is updated every 2 seconds - accesses more often than + * that will return cached data + */ + float getTemperature(void); + + /** + * Get the humidity cell temperature [degF] + * Data is updated every 2 seconds - accesses more often than + * that will return cached data + */ + float getTemperatureF(void); + + /** + * Function intended to test the device and verify it + * is correctly operating. + * + */ + int testSensor(void); + + /** + * Write four byte (32b) register + * + * Note: These access routines are not the normal accesses to an i2c + * device. The AM2315 contains a microcontroller that manages the + * actual readings. These handlers then make requests over i2c using + * a protocol defined by the AM2315. + * + * @param reg address of a register + * @param ival 32b value + */ + int i2cWriteReg_32(int reg, uint32_t ival); + + /** + * Write two byte (16b) register + * + * @param reg address of a register + * @param ival 16b value + */ + int i2cWriteReg_16(int reg, uint16_t ival); + + /** + * Write one byte (8b) register + * + * @param reg address of a register + * @param ival 8b value + */ + int i2cWriteReg_8(int reg, uint8_t ival); + + /** + * Read four bytes register + * + * @param reg address of a register + */ + uint32_t i2cReadReg_32 (int reg); + + /** + * Read two bytes register + * + * @param reg address of a register + */ + uint16_t i2cReadReg_16 (int reg); + + /** + * Read one byte register + * + * @param reg address of a register + */ + uint8_t i2cReadReg_8 (int reg); + + private: + + char* m_name; + + int m_controlAddr; + int m_bus; + mraa_i2c_context m_i2ControlCtx; + + void update_values(void); + uint8_t i2cReadReg(int reg, uint8_t* data, int ilen); + int i2cWriteReg(uint8_t reg, uint8_t* data, uint8_t ilen); + uint16_t crc16(uint8_t* ptr, uint8_t len); + void initialize_priority(void); + void high_priority(void); + void normal_priority(void); + + int32_t m_temperature; + int32_t m_humidity; + + uint16_t m_model; + uint16_t m_version; + uint32_t m_id; + + time_t m_last_time; + + bool m_use_priority; + struct sched_param base_params; + int base_policy; + pthread_t this_thread; +}; + +} diff --git a/src/am2315/jsupm_am2315.i b/src/am2315/jsupm_am2315.i new file mode 100644 index 00000000..f6baf25a --- /dev/null +++ b/src/am2315/jsupm_am2315.i @@ -0,0 +1,8 @@ +%module jsupm_am2315 +%include "../upm.i" + +%{ + #include "am2315.h" +%} + +%include "am2315.h" diff --git a/src/am2315/pyupm_am2315.i b/src/am2315/pyupm_am2315.i new file mode 100644 index 00000000..a0f0ad7a --- /dev/null +++ b/src/am2315/pyupm_am2315.i @@ -0,0 +1,13 @@ +%module pyupm_am2315 +%include "../upm.i" + +%feature("autodoc", "3"); + +#ifdef DOXYGEN +%include "am2315_doc.i" +#endif + +%include "am2315.h" +%{ + #include "am2315.h" +%}