Home | History | Annotate | Download | only in am2315
      1 /*
      2  * Author: William Penner <william.penner (at) intel.com>
      3  * Copyright (c) 2014 Intel Corporation.
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a copy
      6  * of this software and associated documentation files (the "Software"), to deal
      7  * in the Software without restriction, including without limitation the rights
      8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9  * copies of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice shall be included in
     13  * all copies or substantial portions of the Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     21  * THE SOFTWARE.
     22  */
     23 
     24 #include <iostream>
     25 #include <string>
     26 #include <stdexcept>
     27 #include <unistd.h>
     28 #include <stdlib.h>
     29 #include <pthread.h>
     30 #include <sched.h>
     31 #include <time.h>
     32 
     33 #include "am2315.h"
     34 
     35 using namespace upm;
     36 
     37 char g_name[] = AM2315_NAME;
     38 
     39 AM2315::AM2315(int bus, int devAddr) {
     40     m_temperature = 0;
     41     m_humidity    = 0;
     42     m_last_time = 0;
     43 
     44     m_name = g_name;
     45 
     46     m_controlAddr = devAddr;
     47     m_bus = bus;
     48 
     49     m_base_priority = sched_getscheduler(0);
     50 
     51     if ( !(m_i2ControlCtx = mraa_i2c_init(m_bus)) )
     52       {
     53         throw std::invalid_argument(std::string(__FUNCTION__) +
     54                                     ": mraa_i2c_init() failed");
     55         return;
     56       }
     57 
     58     mraa_result_t ret = mraa_i2c_address(m_i2ControlCtx, m_controlAddr);
     59     if (ret != MRAA_SUCCESS) {
     60         throw std::invalid_argument(std::string(__FUNCTION__) +
     61                                     ": mraa_i2c_address() failed");
     62         return;
     63     }
     64     m_model = i2cReadReg_16(AM2315_MODEL);
     65     m_version = i2cReadReg_8(AM2315_VERSION);
     66     m_id = i2cReadReg_32(AM2315_ID);
     67 
     68     fprintf(stdout,"%s: Model: 0x%04x Version: 0x%02x ID: 0x%08x\n",
     69             m_name, m_model, m_version, m_id );
     70 }
     71 
     72 AM2315::~AM2315() {
     73     mraa_i2c_stop(m_i2ControlCtx);
     74 }
     75 
     76 void
     77 AM2315::update_values(void)
     78 {
     79     time_t ctime = time(NULL);
     80     if ((ctime - m_last_time) >= AM2315_SAMPLE) {
     81         uint32_t uival = i2cReadReg_32(AM2315_HUMIDITY);
     82         m_humidity = uival >> 16;
     83         m_temperature = uival & 0xffff;
     84         m_last_time = ctime;
     85     }
     86     else {
     87         // In case the time is changed - backwards
     88         if (ctime < m_last_time)
     89             m_last_time = ctime;
     90     }
     91 }
     92 
     93 float
     94 AM2315::getTemperature(void)
     95 {
     96     update_values();
     97     return (float)m_temperature / 10;
     98 }
     99 
    100 float
    101 AM2315::getTemperatureF(void)
    102 {
    103     return getTemperature() * 9 / 5 + 32;
    104 }
    105 
    106 float
    107 AM2315::getHumidity(void)
    108 {
    109     update_values();
    110     return (float)m_humidity / 10;
    111 }
    112 
    113 /*
    114  * Test function: when reading the AM2315 many times rapidly should
    115  * result in a temperature increase.  This test will verify that the
    116  * value is changing from read to read
    117  */
    118 
    119 int
    120 AM2315::testSensor(void)
    121 {
    122     int i;
    123     int iError = 0;
    124     float fTemp, fHum;
    125     float fTempMax, fTempMin;
    126     float fHumMax, fHumMin;
    127 
    128     fprintf(stdout, "%s: Executing Sensor Test\n", m_name );
    129 
    130     fHum  = getHumidity();
    131     fTemp = getTemperature();
    132     fTempMax = fTempMin = fTemp;
    133     fHumMax  = fHumMin  = fHum;
    134 
    135     // Then sample the sensor a few times
    136     for (i=0; i < 10; i++) {
    137         fHum  = getHumidity();
    138         fTemp = getTemperature();
    139         if (fHum  < fHumMin)  fHumMin  = fHum;
    140         if (fHum  > fHumMax)  fHumMax  = fHum;
    141         if (fTemp < fTempMin) fTempMin = fTemp;
    142         if (fTemp > fTempMax) fTempMax = fTemp;
    143         usleep(50000);
    144     }
    145 
    146     // Now check the results
    147     if (fHumMin == fHumMax && fTempMin == fTempMax) {
    148         fprintf(stdout, "%s:  Humidity/Temp reading was unchanged - warning\n",
    149                 m_name );
    150         iError++;
    151     }
    152     if (iError == 0) {
    153         fprintf(stdout, "%s:  Device appears functional\n", m_name );
    154     }
    155 
    156     fprintf(stdout, "%s: Test complete\n", m_name );
    157 
    158     return iError;
    159 }
    160 
    161 uint16_t
    162 AM2315::crc16(uint8_t* ptr, uint8_t len)
    163 {
    164     uint16_t crc = 0xffff;
    165     uint8_t i;
    166 
    167     while(len--) {
    168         crc ^= *ptr++;
    169         for (i=0; i < 8; i++) {
    170             if (crc & 0x01) {
    171                 crc >>= 1;
    172                 crc ^= 0xA001;
    173             }
    174             else {
    175                 crc >>= 1;
    176             }
    177         }
    178     }
    179     return crc;
    180 }
    181 
    182 /*
    183  * Functions to read and write data to the i2c device in the
    184  * special format used by the device.  This is using i2c to
    185  * interface to a controller that the AOSONG AM2315 uses to
    186  * perform the measurements and manage other registers.
    187  */
    188 int
    189 AM2315::i2cWriteReg(uint8_t reg, uint8_t* data, uint8_t ilen)
    190 {
    191     uint8_t tdata[16] = { AM2315_WRITE, reg, ilen };
    192     mraa_result_t error;
    193 
    194     for (int i=0; i < ilen; i++) {
    195         tdata[i+3] = data[i];
    196     }
    197     uint16_t crc = crc16(tdata, ilen+3);
    198     // CRC is sent out backwards from other registers (low, high)
    199     tdata[ilen+3] = crc;
    200     tdata[ilen+4] = (crc >> 8);
    201 
    202     mraa_result_t ret = mraa_i2c_address(m_i2ControlCtx, m_controlAddr);
    203     int iLoops = 5;
    204     mraa_set_priority(HIGH_PRIORITY);
    205     do {
    206         error = mraa_i2c_write(m_i2ControlCtx, tdata, ilen+5);
    207         usleep(800);
    208     } while(error != MRAA_SUCCESS && --iLoops);
    209     mraa_set_priority(m_base_priority);
    210 
    211     if (error != MRAA_SUCCESS) {
    212         fprintf(stdout, "%s: Error, timeout writing sensor.\n", m_name);
    213         return -1;
    214     }
    215     crc = crc16(tdata,3);
    216     mraa_i2c_read(m_i2ControlCtx, tdata, 5);
    217     if ((tdata[0] != AM2315_WRITE) ||
    218         (tdata[1] != reg)          ||
    219         (tdata[2] != ilen)         ||
    220         (tdata[3] != (crc & 0xff)) ||
    221         (tdata[4] != (crc >> 8))) {
    222         fprintf(stdout, "%s: CRC error during write verification\n", m_name);
    223         return -1;
    224     }
    225     return 0;
    226 }
    227 
    228 
    229 // TODO: Need to patch up function to return only the data that
    230 // is needed and not require the various functions that call this
    231 // to send it enough buffer to cover the function
    232 
    233 uint8_t
    234 AM2315::i2cReadReg(int reg, uint8_t* data, int ilen)
    235 {
    236     uint8_t tdata[16] = { AM2315_READ, reg, ilen };
    237 
    238     mraa_result_t ret = mraa_i2c_address(m_i2ControlCtx, m_controlAddr);
    239     int iLoops = 5;
    240     mraa_set_priority(HIGH_PRIORITY);
    241     do {
    242         ret = mraa_i2c_write(m_i2ControlCtx, tdata, 3);
    243         usleep(800);
    244     } while(ret != MRAA_SUCCESS && --iLoops);
    245     if (ret != MRAA_SUCCESS) {
    246         fprintf(stdout, "%s: Error, timeout reading sensor.\n", m_name);
    247         mraa_set_priority(m_base_priority);
    248         return -1;
    249     }
    250     usleep(5000);
    251     mraa_i2c_read(m_i2ControlCtx, tdata, ilen+4);
    252     mraa_set_priority(m_base_priority);
    253 
    254     uint16_t crc = crc16(tdata, ilen+2);
    255     if ((tdata[0] != AM2315_READ)  ||
    256         (tdata[1] != ilen)         ||
    257         (tdata[ilen+2] != (crc & 0xff)) ||
    258         (tdata[ilen+3] != (crc >> 8))) {
    259         fprintf(stdout, "%s: Read crc failed.\n", m_name);
    260     }
    261     for (int i=0; i < ilen; i++)
    262         data[i] = tdata[i+2];
    263 
    264     return 0;
    265 }
    266 
    267 /*
    268  * Functions to set up the reads and writes to simplify the process of
    269  * formatting data as needed by the microcontroller
    270  */
    271 
    272 int
    273 AM2315::i2cWriteReg_32(int reg, uint32_t ival) {
    274     uint8_t data[4];
    275     data[0] = ival >> 24;
    276     data[1] = ival >> 16;
    277     data[1] = ival >>  8;
    278     data[1] = ival & 0xff;
    279     return i2cWriteReg(reg, data, 4);
    280 }
    281 
    282 int
    283 AM2315::i2cWriteReg_16(int reg, uint16_t ival) {
    284     uint8_t data[2];
    285     data[0] = ival & 0xff;
    286     data[1] = ival >> 8;
    287     return i2cWriteReg(reg, data, 2);
    288 }
    289 
    290 int
    291 AM2315::i2cWriteReg_8(int reg, uint8_t ival) {
    292     uint8_t data[2];
    293     data[0] = ival & 0xff;
    294     data[1] = ival >> 8;
    295     return i2cWriteReg(reg, data, 2);
    296 }
    297 
    298 uint32_t
    299 AM2315::i2cReadReg_32 (int reg) {
    300     uint8_t data[4];
    301     i2cReadReg(reg, data, 4);
    302     return ((((((uint32_t)data[0] << 8) | data[1]) << 8) |
    303             data[2]) << 8) | data[3];
    304 }
    305 
    306 uint16_t
    307 AM2315::i2cReadReg_16 (int reg) {
    308     uint8_t data[2];
    309     i2cReadReg(reg, data, 2);
    310     return ((int16_t)data[0] << 8) | (uint16_t)data[1];
    311 }
    312 
    313 uint8_t
    314 AM2315::i2cReadReg_8 (int reg) {
    315     uint8_t data[1];
    316     i2cReadReg(reg, data, 1);
    317     return data[0];
    318 }
    319 
    320