From df390358f295ec9c22804e870677b98781352b23 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Thu, 1 Jan 2015 01:22:11 -0700 Subject: [PATCH] wt5001: Initial implementation The module implements support for the WT5001 serial mp3 player. It was tested on the Grove Serial MP3 Player. Signed-off-by: Jon Trulson Signed-off-by: Zion Orent Signed-off-by: John Van Drasek --- examples/CMakeLists.txt | 3 + examples/javascript/w5001.js | 137 +++++++++ examples/wt5001.cxx | 144 +++++++++ src/wt5001/CMakeLists.txt | 5 + src/wt5001/jsupm_wt5001.i | 25 ++ src/wt5001/pyupm_wt5001.i | 9 + src/wt5001/wt5001.cxx | 568 +++++++++++++++++++++++++++++++++++ src/wt5001/wt5001.h | 339 +++++++++++++++++++++ 8 files changed, 1230 insertions(+) create mode 100644 examples/javascript/w5001.js create mode 100644 examples/wt5001.cxx create mode 100644 src/wt5001/CMakeLists.txt create mode 100644 src/wt5001/jsupm_wt5001.i create mode 100644 src/wt5001/pyupm_wt5001.i create mode 100644 src/wt5001/wt5001.cxx create mode 100644 src/wt5001/wt5001.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index dce3eadb..fa4effb2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -68,6 +68,7 @@ add_executable (groveloudness-example groveloudness.cxx) add_executable (mpr121-example mpr121.cxx) add_executable (ublox6-example ublox6.cxx) add_executable (yg1006-example yg1006.cxx) +add_executable (wt5001-example wt5001.cxx) include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -123,6 +124,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/groveloudness) include_directories (${PROJECT_SOURCE_DIR}/src/mpr121) include_directories (${PROJECT_SOURCE_DIR}/src/ublox6) include_directories (${PROJECT_SOURCE_DIR}/src/yg1006) +include_directories (${PROJECT_SOURCE_DIR}/src/wt5001) target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -194,3 +196,4 @@ target_link_libraries (groveloudness-example groveloudness ${CMAKE_THREAD_LIBS_I target_link_libraries (mpr121-example mpr121 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (ublox6-example ublox6 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (yg1006-example yg1006 ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries (wt5001-example wt5001 ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/javascript/w5001.js b/examples/javascript/w5001.js new file mode 100644 index 00000000..79f13023 --- /dev/null +++ b/examples/javascript/w5001.js @@ -0,0 +1,137 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ +/*global */ +/* +* Author: Zion Orent +* Copyright (c) 2014 Intel Corporation. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +var MP3Player = require('jsupm_wt5001'); + +function printUsage(progname) +{ + console.log("Usage:" + progname + " "); + console.log("Commands:"); + console.log("0 - stop playing"); + console.log("1 - start playing track 1"); + console.log("2 - pause/un-pause playback"); + console.log("3 - next track"); + console.log("4 - previous track"); +} + +// Instantiate a WT5001 serial MP3 player on uart 0 (/dev/ttyS0). This +// works for the galileo G2. + +// The Edison uses a different serial port, /dev/ttyMFD1, so if you +// are using this example on an Edison board, set the environment +// variable WT5001_SERIAL_PORT to specify the proper port before +// running this example, +// eg: 'WT5001_SERIAL_PORT=/dev/ttyMFD1 node w5001.js' + +// This example was tested on the Grove Serial MP3 module. + +var defaultPort = "/dev/ttyS0"; +var port = process.env.WT5001_SERIAL_PORT; +if (port) + defaultPort = port; + +var myMP3Player = new MP3Player.WT5001(0, defaultPort); + +var cmd = -1; +if (process.argv.length > 2) + cmd = parseInt(process.argv[2]); + +if (!myMP3Player.setupTty(MP3Player.int_B9600)) +{ + console.log("Failed to setup tty port parameters"); + process.exit(0); +} + +switch (cmd) +{ + case 0: + myMP3Player.stop(); + break; + + case 1: + myMP3Player.play(MP3Player.WT5001.SD, 1); + break; + + case 2: + myMP3Player.pause(); + break; + + case 3: + myMP3Player.next(); + break; + + case 4: + myMP3Player.previous(); + break; + + default: + // nothing, just output usage, and info below + printUsage(process.argv[1]); + break; +} + +// print out some information +var vol = new MP3Player.uint8Array(0); +myMP3Player.getVolume(vol); +console.log("The current volume is: " + vol.getitem(0)); + +var ps = new MP3Player.uint8Array(0); +myMP3Player.getPlayState(ps); +console.log("The current play state is: " + ps.getitem(0)); + +var numf = new MP3Player.uint16Array(0); +myMP3Player.getNumFiles(MP3Player.WT5001.SD, numf); +console.log("The number of files on the SD card is: " + numf.getitem(0)); + +var curf = new MP3Player.uint16Array(0); +myMP3Player.getCurrentFile(curf); +console.log("The current file is: " + curf.getitem(0)); + +var year = new MP3Player.uint16Array(0); +var month = new MP3Player.uint8Array(0); +var day = new MP3Player.uint8Array(0); +myMP3Player.getDate(year, month, day); +var mp3date = month.getitem(0) + "/"; +mp3date += (day.getitem(0) + "/"); +mp3date += year.getitem(0); +console.log("The device date is: " + mp3date); + +var hour = new MP3Player.uint8Array(0); +var minute = new MP3Player.uint8Array(0); +var second = new MP3Player.uint8Array(0); +myMP3Player.getTime(hour, minute, second); +var mp3time = hour.getitem(0) + ":"; +mp3time += (minute.getitem(0) + ":"); +mp3time += second.getitem(0); +console.log("The device time is: " + mp3time); + +// Print message when exiting +process.on('SIGINT', function() +{ + console.log("Exiting..."); + process.exit(0); +}); diff --git a/examples/wt5001.cxx b/examples/wt5001.cxx new file mode 100644 index 00000000..1b40553b --- /dev/null +++ b/examples/wt5001.cxx @@ -0,0 +1,144 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "wt5001.h" + +using namespace std; + +void printUsage(char *progname) +{ + cout << "Usage:" << progname << " " << endl; + cout << "Commands:" << endl; + cout << "0 - stop playing" << endl; + cout << "1 - start playing track 1" << endl; + cout << "2 - pause/un-pause playback" << endl; + cout << "3 - next track" << endl; + cout << "4 - previous track" << endl; +} + +int main (int argc, char **argv) +{ +//! [Interesting] + // Instantiate a WT5001 serial MP3 player on uart 0 (/dev/ttyS0). This + // works for the galileo G2. + + // The Edison uses a different serial port, /dev/ttyMFD1, so if you + // are using this example on an Edison board, set the environment + // variable WT5001_SERIAL_PORT to specify the proper port before + // running this example, + // eg: 'WT5001_SERIAL_PORT=/dev/ttyMFD1 ./wt5001-example' + + // This example was tested on the Grove Serial MP3 module. + + const char *defaultPort = "/dev/ttyS0"; + char *port = getenv("WT5001_SERIAL_PORT"); + + if (port) + defaultPort = port; + + upm::WT5001* mp3 = new upm::WT5001(0, defaultPort); + + int cmd = -1; + if (argc > 1) + cmd = atoi(argv[1]); + + // make sure port is initialized properly. 9600 baud is the default. + if (!mp3->setupTty(B9600)) + { + cerr << "Failed to setup tty port parameters" << endl; + return 1; + } + + switch (cmd) + { + case 0: + mp3->stop(); + break; + + case 1: + mp3->play(upm::WT5001::SD, 1); + break; + + case 2: + mp3->pause(); + break; + + case 3: + mp3->next(); + break; + + case 4: + mp3->previous(); + break; + + default: + // nothing, just output usage, and info below + printUsage(argv[0]); + break; + } + + // Example: set the date + // mp3->setDate(2015, 1, 1); + + // Example: set the time + // mp3->setTime(12, 30, 30); + + // print out some information + uint8_t vol = 0; + if (mp3->getVolume(&vol)) + cout << "The current volume is: " << int(vol) << endl; + + uint8_t ps = 0; + if (mp3->getPlayState(&ps)) + cout << "The current play state is: " << int(ps) << endl; + + uint16_t numf = 0; + if (mp3->getNumFiles(upm::WT5001::SD, &numf)) + cout << "The number of files on the SD card is: " << int(numf) << endl; + + uint16_t curf = 0; + if (mp3->getCurrentFile(&curf)) + cout << "The current file is: " << int(curf) << endl; + + uint16_t year = 0; + uint8_t month = 0, day = 0; + if (mp3->getDate(&year, &month, &day)) + cout << "The device date is: " << int(month) << "/" << int(day) + << "/" << int(year) << endl; + + uint8_t hour = 0, minute = 0, second = 0; + if (mp3->getTime(&hour, &minute, &second)) + cout << "The device time is: " << int(hour) << ":" << int(minute) + << ":" << int(second) << endl; + +//! [Interesting] + + cout << "Exiting..." << endl; + + delete mp3; + return 0; +} diff --git a/src/wt5001/CMakeLists.txt b/src/wt5001/CMakeLists.txt new file mode 100644 index 00000000..f70c82be --- /dev/null +++ b/src/wt5001/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "wt5001") +set (libdescription "upm grove serial mp3 (wt5001) module") +set (module_src ${libname}.cxx) +set (module_h ${libname}.h) +upm_module_init() diff --git a/src/wt5001/jsupm_wt5001.i b/src/wt5001/jsupm_wt5001.i new file mode 100644 index 00000000..ff60c1b5 --- /dev/null +++ b/src/wt5001/jsupm_wt5001.i @@ -0,0 +1,25 @@ +%module jsupm_wt5001 +%include "../upm.i" +%include "../carrays_uint8_t.i" +%include "../carrays_uint16_t.i" + +%typemap(in) uint8_t * { + void *argp = 0 ; + int res = SWIG_ConvertPtr($input, &argp,SWIGTYPE_p_uint8Array, 0 | 0 ); + $1 = (uint8_t *)(argp); +} + +%typemap(in) uint16_t * { + void *argp = 0 ; + int res = SWIG_ConvertPtr($input, &argp,SWIGTYPE_p_uint16Array, 0 | 0 ); + $1 = (uint16_t *)(argp); +} + + +%{ + #include "wt5001.h" + speed_t int_B9600 = B9600; +%} + +%include "wt5001.h" +speed_t int_B9600 = B9600; diff --git a/src/wt5001/pyupm_wt5001.i b/src/wt5001/pyupm_wt5001.i new file mode 100644 index 00000000..caa684f3 --- /dev/null +++ b/src/wt5001/pyupm_wt5001.i @@ -0,0 +1,9 @@ +%module pyupm_wt5001 +%include "../upm.i" + +%feature("autodoc", "3"); + +%include "wt5001.h" +%{ + #include "wt5001.h" +%} diff --git a/src/wt5001/wt5001.cxx b/src/wt5001/wt5001.cxx new file mode 100644 index 00000000..d595b721 --- /dev/null +++ b/src/wt5001/wt5001.cxx @@ -0,0 +1,568 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "wt5001.h" + +using namespace upm; +using namespace std; + +static const int defaultDelay = 100; // max wait time for read + +WT5001::WT5001(int uart, const char *tty) : + m_ttyFd(-1) +{ + mraa_init(); + + if ( !(m_uart = mraa_uart_init(uart)) ) + { + cerr << __FUNCTION__ << ": mraa_uart_init() failed" << endl; + return; + } + + // now open the tty + if ( (m_ttyFd = open(tty, O_RDWR)) == -1) + { + cerr << __FUNCTION__ << ": open of " << tty << " failed: " + << strerror(errno) << endl; + return; + } +} + +WT5001::~WT5001() +{ + if (m_ttyFd != -1) + close(m_ttyFd); + + mraa_deinit(); +} + +bool WT5001::dataAvailable(unsigned int millis) +{ + if (m_ttyFd == -1) + return false; + + struct timeval timeout; + + // no waiting + timeout.tv_sec = 0; + timeout.tv_usec = millis * 1000; + + int nfds; + fd_set readfds; + + FD_ZERO(&readfds); + + FD_SET(m_ttyFd, &readfds); + + if (select(m_ttyFd + 1, &readfds, NULL, NULL, &timeout) > 0) + return true; // data is ready + else + return false; +} + +int WT5001::readData(char *buffer, size_t len) +{ + if (m_ttyFd == -1) + return(-1); + + if (!dataAvailable(defaultDelay)) + return 0; // timed out + + int rv = read(m_ttyFd, buffer, len); + + if (rv < 0) + cerr << __FUNCTION__ << ": read failed: " << strerror(errno) << endl; + + return rv; +} + +int WT5001::writeData(char *buffer, size_t len) +{ + if (m_ttyFd == -1) + return(-1); + + // first, flush any pending but unread input + tcflush(m_ttyFd, TCIFLUSH); + + int rv = write(m_ttyFd, buffer, len); + + if (rv < 0) + { + cerr << __FUNCTION__ << ": write failed: " << strerror(errno) << endl; + return rv; + } + + tcdrain(m_ttyFd); + + return rv; +} + +bool WT5001::setupTty(speed_t baud) +{ + if (m_ttyFd == -1) + return(false); + + struct termios termio; + + // get current modes + tcgetattr(m_ttyFd, &termio); + + // setup for a 'raw' mode. 81N, no echo or special character + // handling, such as flow control. + cfmakeraw(&termio); + + // set our baud rates + cfsetispeed(&termio, baud); + cfsetospeed(&termio, baud); + + // make it so + if (tcsetattr(m_ttyFd, TCSAFLUSH, &termio) < 0) + { + cerr << __FUNCTION__ << ": tcsetattr failed: " << strerror(errno) << endl; + return false; + } + + return true; +} + +bool WT5001::checkResponse(WT5001_OPCODE_T opcode) +{ + char resp; + char fopcode = (char)opcode; + + int rv = readData(&resp, 1); + + // check for wrong response byte, or timeout + if ((resp != fopcode) || rv == 0 ) + return false; + + return true; +} + +bool WT5001::play(WT5001_PLAYSOURCE_T psrc, uint16_t index) +{ + char pkt[6]; + WT5001_OPCODE_T opcode = PLAY_SD; + + pkt[0] = WT5001_START; + pkt[1] = 0x04; // length + + switch (psrc) // src + { + case SD: + opcode = PLAY_SD; + break; + + case SPI: + opcode = PLAY_SPI; + break; + + case UDISK: + opcode = PLAY_UDISK; + break; + } + + pkt[2] = opcode; + pkt[3] = (index >> 8) & 0xff; // index hi + pkt[4] = index & 0xff; // index lo + pkt[5] = WT5001_END; + + writeData(pkt, 6); + + return checkResponse(opcode); +} + +bool WT5001::stop() +{ + char pkt[4]; + WT5001_OPCODE_T opcode = STOP; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + return checkResponse(opcode); +} + +bool WT5001::next() +{ + char pkt[4]; + WT5001_OPCODE_T opcode = NEXT; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + return checkResponse(opcode); +} + +bool WT5001::previous() +{ + char pkt[4]; + WT5001_OPCODE_T opcode = PREVIOUS; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + return checkResponse(opcode); +} + +bool WT5001::pause() +{ + char pkt[4]; + WT5001_OPCODE_T opcode = PAUSE; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + return checkResponse(opcode); +} + +bool WT5001::setVolume(uint8_t vol) +{ + if (vol > WT5001_MAX_VOLUME) + { + cerr << __FUNCTION__ << ": volume must be between 0 and " + << WT5001_MAX_VOLUME << endl; + return false; + } + + char pkt[5]; + WT5001_OPCODE_T opcode = SET_VOLUME; + + pkt[0] = WT5001_START; + pkt[1] = 0x03; // length + pkt[2] = opcode; + pkt[3] = vol; + pkt[4] = WT5001_END; + + writeData(pkt, 5); + + return checkResponse(opcode); +} + +bool WT5001::queue(uint16_t index) +{ + char pkt[6]; + WT5001_OPCODE_T opcode = QUEUE; + + pkt[0] = WT5001_START; + pkt[1] = 0x04; // length + pkt[2] = opcode; + pkt[3] = (index >> 8) & 0xff; // index hi + pkt[4] = index & 0xff; // index lo + pkt[5] = WT5001_END; + + writeData(pkt, 6); + + return checkResponse(opcode); +} + +bool WT5001::setPlayMode(WT5001_PLAYMODE_T pm) +{ + char pkt[5]; + WT5001_OPCODE_T opcode = PLAY_MODE; + + pkt[0] = WT5001_START; + pkt[1] = 0x03; // length + pkt[2] = opcode; + pkt[3] = pm; + pkt[4] = WT5001_END; + + writeData(pkt, 5); + + return checkResponse(opcode); +} + +bool WT5001::insert(uint16_t index) +{ + char pkt[6]; + WT5001_OPCODE_T opcode = INSERT_SONG; + + pkt[0] = WT5001_START; + pkt[1] = 0x04; // length + pkt[2] = opcode; + pkt[3] = (index >> 8) & 0xff; // index hi + pkt[4] = index & 0xff; // index lo + pkt[5] = WT5001_END; + + writeData(pkt, 6); + + return checkResponse(opcode); +} + +bool WT5001::setDate(uint16_t year, uint8_t month, uint8_t day) +{ + char pkt[8]; + WT5001_OPCODE_T opcode = SET_DATE; + + pkt[0] = WT5001_START; + pkt[1] = 0x06; // length + pkt[2] = opcode; + pkt[3] = (year >> 8) & 0xff; // year hi + pkt[4] = year & 0xff; // year lo + pkt[5] = month; // month + pkt[6] = day; // day + pkt[7] = WT5001_END; + + writeData(pkt, 8); + + return checkResponse(opcode); +} + +bool WT5001::setTime(uint8_t hour, uint8_t minute, uint8_t second) +{ + char pkt[7]; + WT5001_OPCODE_T opcode = SET_TIME; + + pkt[0] = WT5001_START; + pkt[1] = 0x05; // length + pkt[2] = opcode; + pkt[3] = hour; // hour + pkt[4] = minute; // minute + pkt[5] = second; // second + pkt[6] = WT5001_END; + + writeData(pkt, 7); + + return checkResponse(opcode); +} + +bool WT5001::setAlarm(uint8_t hour, uint8_t minute, uint8_t second) +{ + char pkt[7]; + WT5001_OPCODE_T opcode = SET_ALARM; + + pkt[0] = WT5001_START; + pkt[1] = 0x05; // length + pkt[2] = opcode; + pkt[3] = hour; // hour + pkt[4] = minute; // minute + pkt[5] = second; // second + pkt[6] = WT5001_END; + + writeData(pkt, 7); + + return checkResponse(opcode); +} + +bool WT5001::clearAlarm() +{ + char pkt[4]; + WT5001_OPCODE_T opcode = CLEAR_ALARM; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + return checkResponse(opcode); +} + +bool WT5001::getVolume(uint8_t *vol) +{ + char pkt[4]; + WT5001_OPCODE_T opcode = READ_VOLUME; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + if (!checkResponse(opcode)) + return false; + + // there should be a byte waiting for us, the volume + int rv = readData((char *)vol, 1); + if (rv != 1) + return false; + + return true; +} + +bool WT5001::getPlayState(uint8_t *ps) +{ + char pkt[4]; + WT5001_OPCODE_T opcode = READ_PLAY_STATE; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + if (!checkResponse(opcode)) + return false; + + // there should be a byte waiting for us, the play state + int rv = readData((char *)ps, 1); + if (rv != 1) + return false; + + return true; +} + +bool WT5001::getNumFiles(WT5001_PLAYSOURCE_T psrc, uint16_t *numf) +{ + char pkt[4]; + WT5001_OPCODE_T opcode; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + + switch (psrc) // src + { + case SD: + opcode = READ_SD_NUMF; + break; + + case SPI: + opcode = READ_SPI_NUMF; + break; + + case UDISK: + opcode = READ_UDISK_NUMF; + break; + } + + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + if (!checkResponse(opcode)) + return false; + + // read the two byte response, and encode them + char buf[2]; + int rv = readData(buf, 2); + if (rv != 2) + return false; + + *numf = (buf[0] << 8) | buf[1]; + + return true; +} + +bool WT5001::getCurrentFile(uint16_t *curf) +{ + char pkt[4]; + WT5001_OPCODE_T opcode = READ_CUR_FNAME; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + if (!checkResponse(opcode)) + return false; + + // read the two byte response, and encode them + char buf[2]; + int rv = readData(buf, 2); + if (rv != 2) + return false; + + *curf = (buf[0] << 8) | (buf[1] & 0xff); + + return true; +} + +bool WT5001::getDate(uint16_t *year, uint8_t *month, uint8_t *day) +{ + char pkt[4]; + WT5001_OPCODE_T opcode = READ_DATE; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + if (!checkResponse(opcode)) + return false; + + // read the 4 byte response + char buf[4]; + int rv = readData(buf, 4); + if (rv != 4) + return false; + + *year = (buf[0] << 8) | (buf[1] & 0xff); + *month = buf[2]; + *day = buf[3]; + return true; +} + +bool WT5001::getTime(uint8_t *hour, uint8_t *minute, uint8_t *second) +{ + char pkt[4]; + WT5001_OPCODE_T opcode = READ_TIME; + + pkt[0] = WT5001_START; + pkt[1] = 0x02; // length + pkt[2] = opcode; + pkt[3] = WT5001_END; + + writeData(pkt, 4); + + if (!checkResponse(opcode)) + return false; + + // read the 3 byte response + char buf[3]; + int rv = readData(buf, 3); + if (rv != 3) + return false; + + *hour = buf[0]; + *minute = buf[1]; + *second = buf[2]; + return true; +} + diff --git a/src/wt5001/wt5001.h b/src/wt5001/wt5001.h new file mode 100644 index 00000000..bc69bbac --- /dev/null +++ b/src/wt5001/wt5001.h @@ -0,0 +1,339 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2014 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const int WT5001_DEFAULT_UART = 0; +const int WT5001_MAX_VOLUME = 31; + +// protocol start and end codes +const uint8_t WT5001_START = 0x7e; +const uint8_t WT5001_END = 0x7e; + +namespace upm { + + /** + * @brief C++ API for the WT5001 Serial MP3 module + * + * UPM support for the WT5001 Serial MP3 Module. This was tested + * specifically with the Grove Serial MP3 module. + * + * @ingroup grove uart + * @snippet wt5001.cxx Interesting + */ + class WT5001 { + public: + + // WT5001 opcodes + typedef enum { NONE = 0x00, + PLAY_SD = 0xa0, + PLAY_SPI = 0xa1, + PLAY_UDISK = 0xa2, + PAUSE = 0xa3, + STOP = 0xa4, + NEXT = 0xa5, + PREVIOUS = 0xa6, + SET_VOLUME = 0xa7, + QUEUE = 0xa8, + PLAY_MODE = 0xa9, + COPY_SD2FLASH = 0xaa, // not implemented + COPY_UDISK2FLASH = 0xab, // not implemented + INSERT_SONG = 0xac, + SET_DATE = 0xb1, + SET_TIME = 0xb2, + SET_ALARM = 0xb3, + SET_ALARM_DUR = 0xb4, // not implemented + CLEAR_ALARM = 0xb5, + CLEAR_ALARM_DUR = 0xb6, // not implemented + READ_VOLUME = 0xc1, + READ_PLAY_STATE = 0xc2, + READ_SPI_NUMF = 0xc3, + READ_SD_NUMF = 0xc4, + READ_UDISK_NUMF = 0xc5, + READ_CUR_FNAME = 0xc6, + READ_CF_CHAR = 0xc7, // not implemented + READ_DATE = 0xd1, + READ_TIME = 0xd2 + } WT5001_OPCODE_T; + + // play modes + typedef enum { NORMAL = 0x00, + SINGLE_REPEAT = 0x01, + ALL_REPEAT = 0x02, + RANDOM = 0x03 + } WT5001_PLAYMODE_T; + + // music source + typedef enum { SD, + SPI, + UDISK + } WT5001_PLAYSOURCE_T; + + /** + * WT5001 Serial MP3 module constructor + * + * @param uart default uart to use (0 or 1) + * @param tty tty device to use + */ + WT5001(int uart, const char *tty); + + /** + * WT5001 Serial MP3 module Destructor + */ + ~WT5001(); + + /** + * Check to see if there is data available for reading + * + * @param millis number of milliseconds to wait, 0 means no wait. + * @return true if there is data available to be read + */ + bool dataAvailable(unsigned int millis); + + /** + * read any available data into a user-supplied buffer. Note, the + * call will block until data is available to be read. Use + * dataAvailable() to determine whether there is data available + * beforehand, to avoid blocking. + * + * @param buffer the buffer to hold the data read + * @param len the length of the buffer + * @return the number of bytes read + */ + int readData(char *buffer, size_t len); + + /** + * write the data in buffer to the device + * + * @param buffer the buffer to hold the data read + * @param len the length of the buffer + * @return the number of bytes written + */ + int writeData(char *buffer, size_t len); + + /** + * setup the proper tty i/o modes and the baudrate. The default + * baud rate is 9600 (B9600). + * + * @param baud the desired baud rate. + * @return true if successful + */ + bool setupTty(speed_t baud=B9600); + + /** + * Get a command response and return it's validity + * + * @param index opcode to verify + * @return true if successful + */ + bool checkResponse(WT5001_OPCODE_T opcode); + + /** + * play a file, from a source + * + * @param psrc the play source (SD, UDISK, SPI) + * @param index file number to play + * @return true if successful + */ + bool play(WT5001_PLAYSOURCE_T psrc, uint16_t index); + + /** + * stop playing + * + * @return true if successful + */ + bool stop(); + + /** + * pause playback, or resume playback if already paused + * + * @return true if successful + */ + bool pause(); + + /** + * go to next track + * + * @return true if successful + */ + bool next(); + + /** + * go to previous track + * + * @return true if successful + */ + bool previous(); + + /** + * set the volume. Range is between 0-31. 0 means mute. + * + * @return true if successful + */ + bool setVolume(uint8_t vol); + + /** + * queue a track to play next, when current song is finished + * + * @param index file number to queue + * @return true if successful + */ + bool queue(uint16_t index); + + /** + * set the playback mode + * + * @param pm play mode to enable + * @return true if successful + */ + bool setPlayMode(WT5001_PLAYMODE_T pm); + + /** + * insert a track to play immediately, interrupting the current + * track. When the inserted track is finished playing, the + * interrupted track will resume where it was interrupted. + * + * @param index file number to insert + * @return true if successful + */ + bool insert(uint16_t index); + + /** + * set the date of the internal clock + * + * @param year 4 digit year + * @param month the month + * @param day the day + * @return true if successful + */ + bool setDate(uint16_t year, uint8_t month, uint8_t day); + + /** + * set the time of the internal clock + * + * @param hour hour + * @param minute minute + * @param second second + * @return true if successful + */ + bool setTime(uint8_t hour, uint8_t minute, uint8_t second); + + /** + * set the alarm + * + * @param hour hour + * @param minute minute + * @param second second + * @return true if successful + */ + bool setAlarm(uint8_t hour, uint8_t minute, uint8_t second); + + /** + * clear any alarm that has been set + * + * @return true if successful + */ + bool clearAlarm(); + + /** + * get the current volume + * + * @param vol the returned volume + * @return true if successful + */ + bool getVolume(uint8_t *vol); + + /** + * get the current play state. 1 = playing, 2 = stopped, 3 = paused + * + * @param ps the returned play state + * @return true if successful + */ + bool getPlayState(uint8_t *ps); + + /** + * get the number of files present on the source device + * + * @param psrc the storage source + * @param numf the returned number of files + * @return true if successful + */ + bool getNumFiles(WT5001_PLAYSOURCE_T psrc, uint16_t *numf); + + /** + * get the index of the current file + * + * @param curf the index of the current file + * @return true if successful + */ + bool getCurrentFile(uint16_t *curf); + + /** + * get the device date + * + * @param year returned 4 digit year + * @param month returned month + * @param day returned day + * @return true if successful + */ + bool getDate(uint16_t *year, uint8_t *month, uint8_t *day); + + /** + * get the device time + * + * @param hour returned hour + * @param minute returned minute + * @param second returned second + * @return true if successful + */ + bool getTime(uint8_t *hour, uint8_t *minute, uint8_t *second); + + + protected: + int ttyFd() { return m_ttyFd; }; + int setTtyFd(int fd) { m_ttyFd = fd; }; + + private: + mraa_uart_context m_uart; + int m_ttyFd; + }; +} + +