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