DUDS
Distributed Update of Data from Something
AM2320.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2018 Jeff Jackowski
9  */
13 //#include <boost/crc.hpp>
14 #include <thread>
15 /* for output to help with CRC bug
16 #include <iostream>
17 #include <iomanip>
18 */
19 
20 namespace duds { namespace hardware { namespace devices { namespace instruments {
21 
22 AM2320::AM2320(std::unique_ptr<duds::hardware::interface::I2c> &c) :
23 com(std::move(c)) {
24  wake.addOutputVector() << (std::int8_t)0;
25  read.addOutputVector() << (std::int8_t)3 << (std::int8_t)0 << (std::int8_t)4;
26  // cannot use a repeated start
27  read.addInputVector(8).bigEndian(true).breakBefore();
28  try {
29  sample();
30  } catch (...) {
31  c = std::move(com);
32  throw;
33  }
34 }
35 
36 /* *
37  * The AM2320 uses a CRC encoding taken from the Modbus protocol. This produces
38  * identical results to the code in the AM2320 datasheet.
39  */
40 //typedef boost::crc_optimal<16, 0x8005, 0xFFFF, 0, true, true> ModbusCrc;
41 
43  try {
44  // normal to fail with I2C nack
45  com->converse(wake);
47  // bother; ignore it
48  }
49  // multiple failures followed by a success is a possibility
50  for (int attempts = 2; attempts >= 0; --attempts) {
51  // short wait
52  std::this_thread::sleep_for(std::chrono::milliseconds(10));
53  // get sample
54  try {
55  com->converse(read);
57  if (!attempts) {
58  throw;
59  }
60  }
61  }
62  // parse input
64  std::uint16_t data[4];
65  ex >> data;
66  /* output to help with CRC bug
67  std::cout << "AM2320: " << std::hex << data[0] << ' ' << data[1] << ' ' <<
68  data[2] << ' ' << data[3] << std::dec << std::endl;
69  */
70  // check header-like start
71  if (data[0] != 0x304) {
73  }
74  // check CRC
75  //ModbusCrc crc;
76  //crc.process_bytes(data, 6);
77  // CRC sent in little endian; everything else is big endian (why?!?!???!)
78  // I gather the CRC is little endian based on the poorly written datasheet.
79  // I could be wrong because the CRC never matches, regardless of edianness.
80  // The datasheet code, Adafruit code, and the Boost template used here, all
81  // produce the same CRC result for the same input. The Adafruit code uses
82  // the same data for input, but only requests two register bytes instead
83  // of four. It seems to match what the datasheet says. I have no idea why
84  // the code here has bad CRCs.
85  // In spite of these troubles, the humidity and temperature data appear
86  // reasonable and change as expected.
87  /*
88  if (crc.checksum() != (((data[3] << 8) & 0xFF00) | ((data[3] >> 8) & 0xFF))) {
89  //DUDS_THROW_EXCEPTION(AM2320CrcError());
90  std::cerr << "AM2320: Bad CRC, correct = " << std::hex << crc.checksum()
91  << std::dec << std::endl;
92  // Many attempts at finding a solution. 0xFFFF was also used as the thrid
93  // parameter. Nothing matched what the device sent.
94  boost::crc_optimal<16, 0x8005, 0, 0, true, false> crc0;
95  boost::crc_optimal<16, 0x8005, 0, 0, false, true> crc1;
96  boost::crc_optimal<16, 0x8005, 0, 0, false, false> crc2;
97  boost::crc_optimal<16, 0xA001, 0, 0, true, true> crc3;
98  boost::crc_optimal<16, 0xA001, 0, 0, true, false> crc4;
99  boost::crc_optimal<16, 0xA001, 0, 0, false, true> crc5;
100  boost::crc_optimal<16, 0xA001, 0, 0, false, false> crc6;
101  crc0.process_bytes(data, 6);
102  crc1.process_bytes(data, 6);
103  crc2.process_bytes(data, 6);
104  crc3.process_bytes(data, 6);
105  crc4.process_bytes(data, 6);
106  crc5.process_bytes(data, 6);
107  crc6.process_bytes(data, 6);
108  std::cerr << "CRC: 0 " << std::hex << crc0.checksum() << ", 1 " <<
109  crc1.checksum() << ", 2 " << crc2.checksum() << ", 3 " <<
110  crc3.checksum() << ", 4 " << crc4.checksum() << ", 5 " <<
111  crc5.checksum() << ", 6 " << crc6.checksum() << std::endl;
112  }
113  */
114  // store data
115  rh = data[1];
116  t = (std::int16_t)data[2];
117 }
118 
120  return duds::data::Quantity((double)rh / 10.0, duds::data::Unit(0));
121 }
122 
124  return duds::data::Quantity(
125  (double)t / 10.0 + 273.15,
127  );
128 }
129 
130 } } } }
A container for a value and a unit to better describe the value.
Definition: Quantity.hpp:37
ConversationVector & addInputVector(std::size_t len)
Creates a new ConversationVector for fixed length input and initializes it with the given length...
Header for ConversationExtractor; includes all other conversation related header files.
std::uint16_t rh
Relative humidity.
Definition: AM2320.hpp:61
bool bigEndian() const
True if this part is flagged as having data in big-endian form.
std::unique_ptr< duds::hardware::interface::I2c > com
The I2C communication interface.
Definition: AM2320.hpp:48
STL namespace.
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
Represents an SI unit, either base or derived.
Definition: Unit.hpp:178
duds::hardware::interface::Conversation wake
Used to awaken the device; needed initially and after 3 or more seconds of not talking to the device...
Definition: AM2320.hpp:53
duds::data::Quantity relHumidity() const
Returns the unitless relative humidity quantity.
Definition: AM2320.cpp:119
Base class for all errors specific to I2C communications and used for very general errors...
Definition: I2cErrors.hpp:20
duds::data::Quantity temperature() const
Returns the temperature in Kelvin.
Definition: AM2320.cpp:123
AM2320(std::unique_ptr< duds::hardware::interface::I2c > &c)
Only address is 0x5C.
Definition: AM2320.cpp:22
An attempt was made to use a device that seems to exist, but the responding device is not the type th...
ConversationVector & addOutputVector()
Creates a new ConversationVector for output and returns it for modification.
duds::hardware::interface::Conversation read
Used to read in sampled data from the device.
Definition: AM2320.hpp:57
void sample()
Reads in the last sample and causes the device to start another sample.
Definition: AM2320.cpp:42
Extracts data from a Conversation without modifying the Conversation or copying from it...
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
constexpr Unit Kelvin(DUDS_UNIT_VALUE(0, 0, 1, 0, 0, 0, 0, 0, 0))