1 /*
2 * Author: Jon Trulson <jtrulson@ics.com>
3 * Copyright (c) 2014 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 <sstream>
27 #include <string>
28 #include <stdexcept>
29
30 #include "wt5001.h"
31
32 using namespace upm;
33 using namespace std;
34
35 static const int defaultDelay = 100; // max wait time for read
36
WT5001(int uart)37 WT5001::WT5001(int uart)
38 {
39 m_ttyFd = -1;
40
41 if ( !(m_uart = mraa_uart_init(uart)) )
42 {
43 throw std::invalid_argument(std::string(__FUNCTION__) +
44 ": mraa_uart_init() failed");
45 return;
46 }
47
48 // This requires a recent MRAA (1/2015)
49 const char *devPath = mraa_uart_get_dev_path(m_uart);
50
51 if (!devPath)
52 {
53 throw std::runtime_error(std::string(__FUNCTION__) +
54 ": mraa_uart_get_dev_path() failed");
55 return;
56 }
57
58 // now open the tty
59 if ( (m_ttyFd = open(devPath, O_RDWR)) == -1)
60 {
61 throw std::runtime_error(std::string(__FUNCTION__) +
62 ": open of " +
63 string(devPath) + " failed: " +
64 string(strerror(errno)));
65 return;
66 }
67 }
68
~WT5001()69 WT5001::~WT5001()
70 {
71 if (m_ttyFd != -1)
72 close(m_ttyFd);
73
74 mraa_deinit();
75 }
76
dataAvailable(unsigned int millis)77 bool WT5001::dataAvailable(unsigned int millis)
78 {
79 if (m_ttyFd == -1)
80 return false;
81
82 struct timeval timeout;
83
84 // no waiting
85 timeout.tv_sec = 0;
86 timeout.tv_usec = millis * 1000;
87
88 int nfds;
89 fd_set readfds;
90
91 FD_ZERO(&readfds);
92
93 FD_SET(m_ttyFd, &readfds);
94
95 if (select(m_ttyFd + 1, &readfds, NULL, NULL, &timeout) > 0)
96 return true; // data is ready
97 else
98 return false;
99 }
100
readData(char * buffer,int len)101 int WT5001::readData(char *buffer, int len)
102 {
103 if (m_ttyFd == -1)
104 return(-1);
105
106 if (!dataAvailable(defaultDelay))
107 return 0; // timed out
108
109 int rv = read(m_ttyFd, buffer, len);
110
111 if (rv < 0)
112 {
113 throw std::runtime_error(std::string(__FUNCTION__) +
114 ": read() failed: " +
115 string(strerror(errno)));
116 return rv;
117 }
118
119 return rv;
120 }
121
writeData(char * buffer,int len)122 int WT5001::writeData(char *buffer, int len)
123 {
124 if (m_ttyFd == -1)
125 return(-1);
126
127 // first, flush any pending but unread input
128 tcflush(m_ttyFd, TCIFLUSH);
129
130 int rv = write(m_ttyFd, buffer, len);
131
132 if (rv < 0)
133 {
134 throw std::runtime_error(std::string(__FUNCTION__) +
135 ": write() failed: " +
136 string(strerror(errno)));
137 return rv;
138 }
139
140 tcdrain(m_ttyFd);
141
142 return rv;
143 }
144
setupTty(speed_t baud)145 bool WT5001::setupTty(speed_t baud)
146 {
147 if (m_ttyFd == -1)
148 return(false);
149
150 struct termios termio;
151
152 // get current modes
153 tcgetattr(m_ttyFd, &termio);
154
155 // setup for a 'raw' mode. 81N, no echo or special character
156 // handling, such as flow control.
157 cfmakeraw(&termio);
158
159 // set our baud rates
160 cfsetispeed(&termio, baud);
161 cfsetospeed(&termio, baud);
162
163 // make it so
164 if (tcsetattr(m_ttyFd, TCSAFLUSH, &termio) < 0)
165 {
166 throw std::runtime_error(std::string(__FUNCTION__) +
167 ": tcsetattr() failed: " +
168 string(strerror(errno)));
169 return false;
170 }
171
172 return true;
173 }
174
checkResponse(WT5001_OPCODE_T opcode)175 bool WT5001::checkResponse(WT5001_OPCODE_T opcode)
176 {
177 char resp;
178 char fopcode = (char)opcode;
179
180 int rv = readData(&resp, 1);
181
182 // check for wrong response byte, or timeout
183 if ((resp != fopcode) || rv == 0 )
184 return false;
185
186 return true;
187 }
188
play(WT5001_PLAYSOURCE_T psrc,uint16_t index)189 bool WT5001::play(WT5001_PLAYSOURCE_T psrc, uint16_t index)
190 {
191 char pkt[6];
192 WT5001_OPCODE_T opcode = PLAY_SD;
193
194 pkt[0] = WT5001_START;
195 pkt[1] = 0x04; // length
196
197 switch (psrc) // src
198 {
199 case SD:
200 opcode = PLAY_SD;
201 break;
202
203 case SPI:
204 opcode = PLAY_SPI;
205 break;
206
207 case UDISK:
208 opcode = PLAY_UDISK;
209 break;
210 }
211
212 pkt[2] = opcode;
213 pkt[3] = (index >> 8) & 0xff; // index hi
214 pkt[4] = index & 0xff; // index lo
215 pkt[5] = WT5001_END;
216
217 writeData(pkt, 6);
218
219 return checkResponse(opcode);
220 }
221
stop()222 bool WT5001::stop()
223 {
224 char pkt[4];
225 WT5001_OPCODE_T opcode = STOP;
226
227 pkt[0] = WT5001_START;
228 pkt[1] = 0x02; // length
229 pkt[2] = opcode;
230 pkt[3] = WT5001_END;
231
232 writeData(pkt, 4);
233
234 return checkResponse(opcode);
235 }
236
next()237 bool WT5001::next()
238 {
239 char pkt[4];
240 WT5001_OPCODE_T opcode = NEXT;
241
242 pkt[0] = WT5001_START;
243 pkt[1] = 0x02; // length
244 pkt[2] = opcode;
245 pkt[3] = WT5001_END;
246
247 writeData(pkt, 4);
248
249 return checkResponse(opcode);
250 }
251
previous()252 bool WT5001::previous()
253 {
254 char pkt[4];
255 WT5001_OPCODE_T opcode = PREVIOUS;
256
257 pkt[0] = WT5001_START;
258 pkt[1] = 0x02; // length
259 pkt[2] = opcode;
260 pkt[3] = WT5001_END;
261
262 writeData(pkt, 4);
263
264 return checkResponse(opcode);
265 }
266
pause()267 bool WT5001::pause()
268 {
269 char pkt[4];
270 WT5001_OPCODE_T opcode = PAUSE;
271
272 pkt[0] = WT5001_START;
273 pkt[1] = 0x02; // length
274 pkt[2] = opcode;
275 pkt[3] = WT5001_END;
276
277 writeData(pkt, 4);
278
279 return checkResponse(opcode);
280 }
281
setVolume(uint8_t vol)282 bool WT5001::setVolume(uint8_t vol)
283 {
284 if (vol > WT5001_MAX_VOLUME)
285 {
286 // C++11 std::to_string() would be nice, but...
287 std::ostringstream str;
288 str << WT5001_MAX_VOLUME;
289
290 throw std::out_of_range(std::string(__FUNCTION__) +
291 ": angle must be between 0 and " +
292 str.str());
293 return false;
294 }
295
296 char pkt[5];
297 WT5001_OPCODE_T opcode = SET_VOLUME;
298
299 pkt[0] = WT5001_START;
300 pkt[1] = 0x03; // length
301 pkt[2] = opcode;
302 pkt[3] = vol;
303 pkt[4] = WT5001_END;
304
305 writeData(pkt, 5);
306
307 return checkResponse(opcode);
308 }
309
queue(uint16_t index)310 bool WT5001::queue(uint16_t index)
311 {
312 char pkt[6];
313 WT5001_OPCODE_T opcode = QUEUE;
314
315 pkt[0] = WT5001_START;
316 pkt[1] = 0x04; // length
317 pkt[2] = opcode;
318 pkt[3] = (index >> 8) & 0xff; // index hi
319 pkt[4] = index & 0xff; // index lo
320 pkt[5] = WT5001_END;
321
322 writeData(pkt, 6);
323
324 return checkResponse(opcode);
325 }
326
setPlayMode(WT5001_PLAYMODE_T pm)327 bool WT5001::setPlayMode(WT5001_PLAYMODE_T pm)
328 {
329 char pkt[5];
330 WT5001_OPCODE_T opcode = PLAY_MODE;
331
332 pkt[0] = WT5001_START;
333 pkt[1] = 0x03; // length
334 pkt[2] = opcode;
335 pkt[3] = pm;
336 pkt[4] = WT5001_END;
337
338 writeData(pkt, 5);
339
340 return checkResponse(opcode);
341 }
342
insert(uint16_t index)343 bool WT5001::insert(uint16_t index)
344 {
345 char pkt[6];
346 WT5001_OPCODE_T opcode = INSERT_SONG;
347
348 pkt[0] = WT5001_START;
349 pkt[1] = 0x04; // length
350 pkt[2] = opcode;
351 pkt[3] = (index >> 8) & 0xff; // index hi
352 pkt[4] = index & 0xff; // index lo
353 pkt[5] = WT5001_END;
354
355 writeData(pkt, 6);
356
357 return checkResponse(opcode);
358 }
359
setDate(uint16_t year,uint8_t month,uint8_t day)360 bool WT5001::setDate(uint16_t year, uint8_t month, uint8_t day)
361 {
362 char pkt[8];
363 WT5001_OPCODE_T opcode = SET_DATE;
364
365 pkt[0] = WT5001_START;
366 pkt[1] = 0x06; // length
367 pkt[2] = opcode;
368 pkt[3] = (year >> 8) & 0xff; // year hi
369 pkt[4] = year & 0xff; // year lo
370 pkt[5] = month; // month
371 pkt[6] = day; // day
372 pkt[7] = WT5001_END;
373
374 writeData(pkt, 8);
375
376 return checkResponse(opcode);
377 }
378
setTime(uint8_t hour,uint8_t minute,uint8_t second)379 bool WT5001::setTime(uint8_t hour, uint8_t minute, uint8_t second)
380 {
381 char pkt[7];
382 WT5001_OPCODE_T opcode = SET_TIME;
383
384 pkt[0] = WT5001_START;
385 pkt[1] = 0x05; // length
386 pkt[2] = opcode;
387 pkt[3] = hour; // hour
388 pkt[4] = minute; // minute
389 pkt[5] = second; // second
390 pkt[6] = WT5001_END;
391
392 writeData(pkt, 7);
393
394 return checkResponse(opcode);
395 }
396
setAlarm(uint8_t hour,uint8_t minute,uint8_t second)397 bool WT5001::setAlarm(uint8_t hour, uint8_t minute, uint8_t second)
398 {
399 char pkt[7];
400 WT5001_OPCODE_T opcode = SET_ALARM;
401
402 pkt[0] = WT5001_START;
403 pkt[1] = 0x05; // length
404 pkt[2] = opcode;
405 pkt[3] = hour; // hour
406 pkt[4] = minute; // minute
407 pkt[5] = second; // second
408 pkt[6] = WT5001_END;
409
410 writeData(pkt, 7);
411
412 return checkResponse(opcode);
413 }
414
clearAlarm()415 bool WT5001::clearAlarm()
416 {
417 char pkt[4];
418 WT5001_OPCODE_T opcode = CLEAR_ALARM;
419
420 pkt[0] = WT5001_START;
421 pkt[1] = 0x02; // length
422 pkt[2] = opcode;
423 pkt[3] = WT5001_END;
424
425 writeData(pkt, 4);
426
427 return checkResponse(opcode);
428 }
429
getVolume(uint8_t * vol)430 bool WT5001::getVolume(uint8_t *vol)
431 {
432 char pkt[4];
433 WT5001_OPCODE_T opcode = READ_VOLUME;
434
435 pkt[0] = WT5001_START;
436 pkt[1] = 0x02; // length
437 pkt[2] = opcode;
438 pkt[3] = WT5001_END;
439
440 writeData(pkt, 4);
441
442 if (!checkResponse(opcode))
443 return false;
444
445 // there should be a byte waiting for us, the volume
446 int rv = readData((char *)vol, 1);
447 if (rv != 1)
448 return false;
449
450 return true;
451 }
452
getPlayState(uint8_t * ps)453 bool WT5001::getPlayState(uint8_t *ps)
454 {
455 char pkt[4];
456 WT5001_OPCODE_T opcode = READ_PLAY_STATE;
457
458 pkt[0] = WT5001_START;
459 pkt[1] = 0x02; // length
460 pkt[2] = opcode;
461 pkt[3] = WT5001_END;
462
463 writeData(pkt, 4);
464
465 if (!checkResponse(opcode))
466 return false;
467
468 // there should be a byte waiting for us, the play state
469 int rv = readData((char *)ps, 1);
470 if (rv != 1)
471 return false;
472
473 return true;
474 }
475
getNumFiles(WT5001_PLAYSOURCE_T psrc,uint16_t * numf)476 bool WT5001::getNumFiles(WT5001_PLAYSOURCE_T psrc, uint16_t *numf)
477 {
478 char pkt[4];
479 WT5001_OPCODE_T opcode;
480
481 pkt[0] = WT5001_START;
482 pkt[1] = 0x02; // length
483
484 switch (psrc) // src
485 {
486 case SD:
487 opcode = READ_SD_NUMF;
488 break;
489
490 case SPI:
491 opcode = READ_SPI_NUMF;
492 break;
493
494 case UDISK:
495 opcode = READ_UDISK_NUMF;
496 break;
497 }
498
499 pkt[2] = opcode;
500 pkt[3] = WT5001_END;
501
502 writeData(pkt, 4);
503
504 if (!checkResponse(opcode))
505 return false;
506
507 // read the two byte response, and encode them
508 char buf[2];
509 int rv = readData(buf, 2);
510 if (rv != 2)
511 return false;
512
513 *numf = (buf[0] << 8) | buf[1];
514
515 return true;
516 }
517
getCurrentFile(uint16_t * curf)518 bool WT5001::getCurrentFile(uint16_t *curf)
519 {
520 char pkt[4];
521 WT5001_OPCODE_T opcode = READ_CUR_FNAME;
522
523 pkt[0] = WT5001_START;
524 pkt[1] = 0x02; // length
525 pkt[2] = opcode;
526 pkt[3] = WT5001_END;
527
528 writeData(pkt, 4);
529
530 if (!checkResponse(opcode))
531 return false;
532
533 // read the two byte response, and encode them
534 char buf[2];
535 int rv = readData(buf, 2);
536 if (rv != 2)
537 return false;
538
539 *curf = (buf[0] << 8) | (buf[1] & 0xff);
540
541 return true;
542 }
543
getDate(uint16_t * year,uint8_t * month,uint8_t * day)544 bool WT5001::getDate(uint16_t *year, uint8_t *month, uint8_t *day)
545 {
546 char pkt[4];
547 WT5001_OPCODE_T opcode = READ_DATE;
548
549 pkt[0] = WT5001_START;
550 pkt[1] = 0x02; // length
551 pkt[2] = opcode;
552 pkt[3] = WT5001_END;
553
554 writeData(pkt, 4);
555
556 if (!checkResponse(opcode))
557 return false;
558
559 // read the 4 byte response
560 char buf[4];
561 int rv = readData(buf, 4);
562 if (rv != 4)
563 return false;
564
565 *year = (buf[0] << 8) | (buf[1] & 0xff);
566 *month = buf[2];
567 *day = buf[3];
568 return true;
569 }
570
getTime(uint8_t * hour,uint8_t * minute,uint8_t * second)571 bool WT5001::getTime(uint8_t *hour, uint8_t *minute, uint8_t *second)
572 {
573 char pkt[4];
574 WT5001_OPCODE_T opcode = READ_TIME;
575
576 pkt[0] = WT5001_START;
577 pkt[1] = 0x02; // length
578 pkt[2] = opcode;
579 pkt[3] = WT5001_END;
580
581 writeData(pkt, 4);
582
583 if (!checkResponse(opcode))
584 return false;
585
586 // read the 3 byte response
587 char buf[3];
588 int rv = readData(buf, 3);
589 if (rv != 3)
590 return false;
591
592 *hour = buf[0];
593 *minute = buf[1];
594 *second = buf[2];
595 return true;
596 }
597
598