1 /*
2  * Author: Jon Trulson <jtrulson@ics.com>
3  * Copyright (c) 2015 Intel Corporation.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <iostream>
26 #include <string>
27 #include <stdexcept>
28 
29 #include "mhz16.h"
30 
31 using namespace upm;
32 using namespace std;
33 
34 static const int defaultDelay = 100;     // max wait time for read
35 
MHZ16(int uart)36 MHZ16::MHZ16(int uart)
37 {
38   m_ttyFd = -1;
39 
40   if ( !(m_uart = mraa_uart_init(uart)) )
41     {
42       throw std::invalid_argument(std::string(__FUNCTION__) +
43                                   ": mraa_uart_init() failed");
44       return;
45     }
46 
47   // This requires a recent MRAA (1/2015)
48   const char *devPath = mraa_uart_get_dev_path(m_uart);
49 
50   if (!devPath)
51     {
52       throw std::runtime_error(std::string(__FUNCTION__) +
53                                ": mraa_uart_get_dev_path() failed");
54       return;
55     }
56 
57   // now open the tty
58   if ( (m_ttyFd = open(devPath, O_RDWR)) == -1)
59     {
60       throw std::runtime_error(std::string(__FUNCTION__) +
61                                ": open of " +
62                                string(devPath) + " failed: " +
63                                string(strerror(errno)));
64       return;
65     }
66 }
67 
~MHZ16()68 MHZ16::~MHZ16()
69 {
70   if (m_ttyFd != -1)
71     close(m_ttyFd);
72 
73   mraa_deinit();
74 }
75 
dataAvailable(unsigned int millis)76 bool MHZ16::dataAvailable(unsigned int millis)
77 {
78   if (m_ttyFd == -1)
79     return false;
80 
81   struct timeval timeout;
82 
83   // no waiting
84   timeout.tv_sec = 0;
85   timeout.tv_usec = millis * 1000;
86 
87   int nfds;
88   fd_set readfds;
89 
90   FD_ZERO(&readfds);
91 
92   FD_SET(m_ttyFd, &readfds);
93 
94   if (select(m_ttyFd + 1, &readfds, NULL, NULL, &timeout) > 0)
95     return true;                // data is ready
96   else
97     return false;
98 }
99 
readData(char * buffer,int len)100 int MHZ16::readData(char *buffer, int len)
101 {
102   if (m_ttyFd == -1)
103     return(-1);
104 
105   if (!dataAvailable(defaultDelay))
106     return 0;               // timed out
107 
108   int rv = read(m_ttyFd, buffer, len);
109 
110   if (rv < 0)
111     {
112       throw std::runtime_error(std::string(__FUNCTION__) +
113                                ": read() failed: " +
114                                string(strerror(errno)));
115       return rv;
116     }
117 
118   return rv;
119 }
120 
writeData(char * buffer,int len)121 int MHZ16::writeData(char *buffer, int len)
122 {
123   if (m_ttyFd == -1)
124     return(-1);
125 
126   // first, flush any pending but unread input
127   tcflush(m_ttyFd, TCIFLUSH);
128 
129   int rv = write(m_ttyFd, buffer, len);
130 
131   if (rv < 0)
132     {
133       throw std::runtime_error(std::string(__FUNCTION__) +
134                                ": write() failed: " +
135                                string(strerror(errno)));
136       return rv;
137     }
138 
139   tcdrain(m_ttyFd);
140 
141   return rv;
142 }
143 
setupTty(speed_t baud)144 bool MHZ16::setupTty(speed_t baud)
145 {
146   if (m_ttyFd == -1)
147     return(false);
148 
149   struct termios termio;
150 
151   // get current modes
152   tcgetattr(m_ttyFd, &termio);
153 
154   // setup for a 'raw' mode.  81N, no echo or special character
155   // handling, such as flow control.
156   cfmakeraw(&termio);
157 
158   // set our baud rates
159   cfsetispeed(&termio, baud);
160   cfsetospeed(&termio, baud);
161 
162   // make it so
163   if (tcsetattr(m_ttyFd, TCSAFLUSH, &termio) < 0)
164     {
165       throw std::runtime_error(std::string(__FUNCTION__) +
166                                ": tcsetattr() failed: " +
167                                string(strerror(errno)));
168       return false;
169     }
170 
171   return true;
172 }
173 
verifyPacket(uint8_t * pkt,int len)174 bool MHZ16::verifyPacket(uint8_t *pkt, int len)
175 {
176   if (pkt[0] != 0xff || pkt[1] != 0x86)
177     {
178       throw std::runtime_error(std::string(__FUNCTION__) +
179                                ": invalid packet header received");
180       return false;
181     }
182 
183   return true;
184 }
185 
getData(int * gas,int * temp)186 bool MHZ16::getData(int *gas, int *temp)
187 {
188   // the query command
189   const unsigned char cmd[9] =
190     {0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
191 
192   writeData((char *)cmd, 9);
193 
194   // wait up to one second for a response
195   if (!dataAvailable(1000))
196     {
197       throw std::runtime_error(std::string(__FUNCTION__) +
198                                ": Timed out waiting for response");
199       return false;
200     }
201 
202   // read the packet
203   unsigned char packet[9];
204   int rv;
205 
206   if ((rv = readData((char *)packet, 9)) != 9)
207     {
208       throw std::runtime_error(std::string(__FUNCTION__) +
209                                ": Invalid packet size read");
210       return false;
211     }
212 
213   // will throw an exception if it fails
214   verifyPacket(packet, sizeof(packet));
215 
216   // pull out the data and return it.
217   *gas = (packet[2] << 8) | packet[3];
218   *temp = packet[4] - 40;
219 
220   return true;
221 }
222 
calibrateZeroPoint()223 void MHZ16::calibrateZeroPoint()
224 {
225   // the query command
226   const unsigned char cmd[9] =
227     {0xff, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78};
228 
229   writeData((char *)cmd, 9);
230 
231   // no response
232 }
233 
234