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