From: Thomas Pietrzak Date: Tue, 14 Jun 2011 19:13:33 +0000 (+0000) Subject: Tacton library initial import X-Git-Url: https://git.thomaspietrzak.com/?a=commitdiff_plain;h=5fbb9d50ecda8d538b88faa9b0dd74c9439927e6;p=tactonlibrary.git Tacton library initial import git-svn-id: svn+ssh://thomaspietrzak.com/var/svn/rep@28 47cf9a05-e0a8-4ed5-9e9b-101a649bc004 --- 5fbb9d50ecda8d538b88faa9b0dd74c9439927e6 diff --git a/TactonLibrary.sdf b/TactonLibrary.sdf new file mode 100644 index 0000000..9557225 Binary files /dev/null and b/TactonLibrary.sdf differ diff --git a/TactonLibrary.sln b/TactonLibrary.sln new file mode 100644 index 0000000..9cc6d11 --- /dev/null +++ b/TactonLibrary.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TactonPlayer", "TactonPlayer\TactonPlayer.vcxproj", "{6D5C62C0-562D-4B2A-9AAC-806372810CBF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Debug|Win32.ActiveCfg = Debug|Win32 + {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Debug|Win32.Build.0 = Debug|Win32 + {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Release|Win32.ActiveCfg = Release|Win32 + {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TactonLibrary.suo b/TactonLibrary.suo new file mode 100644 index 0000000..4b9a884 Binary files /dev/null and b/TactonLibrary.suo differ diff --git a/TactonPlayer/Serial.cpp b/TactonPlayer/Serial.cpp new file mode 100644 index 0000000..e527263 --- /dev/null +++ b/TactonPlayer/Serial.cpp @@ -0,0 +1,10 @@ +#include "Serial.hpp" + +Serial::Serial(char *) +:_connected(false) +{ +} + +Serial::~Serial() +{ +} diff --git a/TactonPlayer/Serial.hpp b/TactonPlayer/Serial.hpp new file mode 100644 index 0000000..f306af7 --- /dev/null +++ b/TactonPlayer/Serial.hpp @@ -0,0 +1,35 @@ +#ifndef _SERIAL_ +#define _SERIAL_ + +#define ARDUINO_WAIT_TIME 500 + +class Serial +{ + public: + //Initialize Serial communication with the given COM port + Serial(char *portName); + + //Close the connection + //NOTA: for some reason you can't connect again before exiting + //the program and running it again + virtual ~Serial(); + + //Read data in a buffer, if nbChar is greater than the + //maximum number of bytes available, it will return only the + //bytes available. The function return -1 when nothing could + //be read, the number of bytes actually read. + virtual int ReadData(void *buffer, unsigned int nbChar)=0; + + //Writes data from a buffer through the Serial connection + //return true on success. + virtual bool WriteData(void *buffer, unsigned int nbChar)=0; + + //Check if we are actually connected + bool IsConnected() { return _connected; } + + protected: + //Connection status + bool _connected; +}; + +#endif diff --git a/TactonPlayer/SerialWindows.cpp b/TactonPlayer/SerialWindows.cpp new file mode 100644 index 0000000..38806ef --- /dev/null +++ b/TactonPlayer/SerialWindows.cpp @@ -0,0 +1,142 @@ +#include "SerialWindows.hpp" + +#include +using namespace std; + +#ifdef WIN32 +#include +#endif + +SerialWindows::SerialWindows(char *portName) +:Serial(portName) +{ + //Try to connect to the given port throuh CreateFile + _hSerial = CreateFileA(portName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, //FILE_ATTRIBUTE_NORMAL, + NULL); + + //Check if the connection was successfull + if(_hSerial == INVALID_HANDLE_VALUE) + { + //If not success full display an Error + if(GetLastError() == ERROR_FILE_NOT_FOUND) + cerr << "ERROR: Handle was not attached. Reason: " << portName << " not available." << endl; + else + cerr << "ERROR unknown" << endl; + char buffer[256]; + sprintf(buffer, "Port %s does not exist or not reachable", portName); + throw buffer; + } + else + { + /* + COMMTIMEOUTS commTimeouts; + commTimeouts.ReadIntervalTimeout = 1; + commTimeouts.ReadTotalTimeoutMultiplier = 10; + commTimeouts.ReadTotalTimeoutConstant = 100; + commTimeouts.WriteTotalTimeoutMultiplier = 10; + commTimeouts.WriteTotalTimeoutConstant = 100; + if (SetCommTimeouts(_comport, &commTimeouts) == 0) + throw "Cannot set COM port timeouts";*/ + + + //If connected we try to set the comm parameters + DCB dcbSerialParams = {0}; + + //Try to get the current + if (!GetCommState(_hSerial, &dcbSerialParams)) + //If impossible, show an error + cerr << "ERROR: failed to get current serial parameters!" << endl; + else + { + //Define serial connection parameters for the arduino board + dcbSerialParams.BaudRate=CBR_57600; + dcbSerialParams.ByteSize=8; + dcbSerialParams.StopBits=ONESTOPBIT; + dcbSerialParams.Parity=NOPARITY; + + //Set the parameters and check for their proper application + if(!SetCommState(_hSerial, &dcbSerialParams)) + cerr << "ERROR: Could not set Serial Port parameters" << endl; + else + { + //If everything went fine we're connected + _connected = true; + //We wait 2s as the arduino board will be reseting + Sleep(2000); + //SDL_Delay(ARDUINO_WAIT_TIME); + } + } + } +} + +SerialWindows::~SerialWindows() +{ + //Check if we are connected before trying to disconnect + if(_connected) + { + //We're no longer connected + _connected = false; + //Close the serial handler + CloseHandle(_hSerial); + } +} + +int SerialWindows::ReadData(void *buffer, unsigned int nbChar) +{ + //Number of bytes we'll have read + DWORD bytesRead = 0; + //Number of bytes we'll really ask to read + unsigned int toRead = 0; + + //Use the ClearCommError function to get status info on the Serial port + ClearCommError(_hSerial, &_errors, &_status); + + //Check if there is something to read + if(_status.cbInQue > 0) + { + //If there is we check if there is enough data to read the required number + //of characters, if not we'll read only the available characters to prevent + //locking of the application. + if(_status.cbInQue > nbChar) + toRead = nbChar; + else + toRead = _status.cbInQue; + + //Try to read the require number of chars, and return the number of read bytes on success + if(ReadFile(_hSerial, buffer, toRead, &bytesRead, NULL) && bytesRead != 0) + return bytesRead; + } + + //If nothing has been read, or that an error was detected return -1 + return -1; +} + + +bool SerialWindows::WriteData(void *buffer, unsigned int nbChar) +{ + DWORD bytesSend; + + //Try to write the buffer on the Serial port + if(!WriteFile(_hSerial, buffer, nbChar, &bytesSend, 0)) + { + //In case it don't work get comm error and return false + ClearCommError(_hSerial, &_errors, &_status); + + return false; + } + else + { + ClearCommError(_hSerial, &_errors, &_status); +/* if(!FlushFileBuffers(_hSerial)) + cout << "ERROR while flushing" << endl;*/ +/* stringstream s; + s << bytesSend << " SENT" << endl; + OutputDebugString(s.str().c_str());*/ + return true; + } +} diff --git a/TactonPlayer/SerialWindows.hpp b/TactonPlayer/SerialWindows.hpp new file mode 100644 index 0000000..28eb1af --- /dev/null +++ b/TactonPlayer/SerialWindows.hpp @@ -0,0 +1,35 @@ +#ifndef _SERIALWINDOWS_ +#define _SERIALWINDOWS_ + +#include "Serial.hpp" + +#include + +class SerialWindows : public Serial +{ + public: + //Initialize Serial communication with the given COM port + SerialWindows(char *portName); + //Close the connection + //NOTA: for some reason you can't connect again before exiting + //the program and running it again + ~SerialWindows(); + //Read data in a buffer, if nbChar is greater than the + //maximum number of bytes available, it will return only the + //bytes available. The function return -1 when nothing could + //be read, the number of bytes actually read. + int ReadData(void *buffer, unsigned int nbChar); + //Writes data from a buffer through the Serial connection + //return true on success. + bool WriteData(void *buffer, unsigned int nbChar); + + + private: + //Serial comm handler + HANDLE _hSerial; + //Get various information about the connection + COMSTAT _status; + //Keep track of last error + DWORD _errors; +}; +#endif diff --git a/TactonPlayer/Tacton.cpp b/TactonPlayer/Tacton.cpp new file mode 100644 index 0000000..3e38661 --- /dev/null +++ b/TactonPlayer/Tacton.cpp @@ -0,0 +1,109 @@ +#include "Tacton.hpp" + +#include +#include + +Tacton::Tacton(unsigned int nbframes, unsigned char *desc) +:_nbframes(nbframes), _patterns(new unsigned char[nbframes]), _durations(new unsigned int[nbframes]), _frequencies(new unsigned int[nbframes]), _amplitudes(new unsigned char[nbframes]) +{ + for(unsigned int i = 0 ; i < nbframes ; i++) + { + _patterns[i] = desc[6*i]; + _durations[i] = (((unsigned int)(desc[6*i+1])) << 8) | ((unsigned int)(desc[6*i+2])); + _frequencies[i] = (((unsigned int)(desc[6*i+3])) << 8) | ((unsigned int)(desc[6*i+4])); + _amplitudes[i] = desc[6*i+5]; + } +} + +Tacton::Tacton(char *desc) +{ + char *buffer = strdup(desc); + + //read the number of frames + char *token = strtok(buffer, "; ,"); + if (token == NULL) + throw "Invalid description"; + _nbframes = atoi(token); + + _patterns = new unsigned char[_nbframes]; + _durations = new unsigned int[_nbframes]; + _frequencies = new unsigned int[_nbframes]; + _amplitudes = new unsigned char[_nbframes]; + + for (unsigned int i = 0 ; i < _nbframes ; i++) + { + //read the pattern + if ((token = strtok(NULL, "; ,")) == NULL || strlen(token) != 4) + throw "Invalid description"; + unsigned char t1 = token[0] == '1'; + unsigned char t2 = token[1] == '1'; + unsigned char t3 = token[2] == '1'; + unsigned char t4 = token[3] == '1'; + _patterns[i] = (t1 << 3) | (t2 << 2) | (t3 << 1) | t4; + + //read the duration + if ((token = strtok(NULL, "; ,")) == NULL) + throw "Invalid description"; + _durations[i] = atoi(token); + + //read the frequency + if ((token = strtok(NULL, "; ,")) == NULL) + throw "Invalid description"; + _frequencies[i] = atoi(token); + + //read the amplitude + if ((token = strtok(NULL, "; ,")) == NULL) + throw "Invalid description"; + _amplitudes[i] = atoi(token); + } + + free(buffer); +} + +Tacton::Tacton(unsigned int nbframes, unsigned char *patterns, unsigned int *durations, unsigned int *frequencies, unsigned char *amplitudes) +:_nbframes(nbframes), _patterns(new unsigned char[nbframes]), _durations(new unsigned int[nbframes]), _frequencies(new unsigned int[nbframes]), _amplitudes(new unsigned char[nbframes]) +{ + memcpy(_patterns, patterns, nbframes * sizeof(unsigned char)); + memcpy(_durations, durations, nbframes * sizeof(unsigned int)); + memcpy(_frequencies, frequencies, nbframes * sizeof(unsigned int)); + memcpy(_amplitudes, amplitudes, nbframes * sizeof(unsigned char)); +} + +Tacton::Tacton(unsigned char pattern, unsigned int duration, unsigned int frequency, unsigned char amplitude) +:_nbframes(1), _patterns(new unsigned char[1]), _durations(new unsigned int[1]), _frequencies(new unsigned int[1]), _amplitudes(new unsigned char[1]) +{ + _patterns[0] = pattern; + _durations[0] = duration; + _frequencies[0] = frequency; + _amplitudes[0] = amplitude; +} + +Tacton::~Tacton() +{ + delete []_patterns; + delete []_durations; + delete []_frequencies; + delete []_amplitudes; +} + +const unsigned int Tacton::getNbFrames() const +{ + return _nbframes; +} + +void *Tacton::rawCode() const +{ + unsigned char *res = new unsigned char[_nbframes * 6]; + for (unsigned int i = 0 ; i < _nbframes ; i++) + { + res[6*i] = _patterns[i]; + res[6*i+1] = (_durations[i] & 0xff00) >> 8; + res[6*i+2] = _durations[i] & 0xff; + res[6*i+3] = (_frequencies[i] & 0xff00) >> 8; + res[6*i+4] = _frequencies[i] & 0xff; + res[6*i+5] = _amplitudes[i]; + } + return res; +} + + \ No newline at end of file diff --git a/TactonPlayer/Tacton.hpp b/TactonPlayer/Tacton.hpp new file mode 100644 index 0000000..b5162c4 --- /dev/null +++ b/TactonPlayer/Tacton.hpp @@ -0,0 +1,40 @@ +#ifndef _TACTON_ +#define _TACTON_ + +class Tacton +{ + public: + //creates a tacton using the raw data format + //6 bytes per frame : + //1 for the pattern + //2 for the duration + //2 for the frequency + //1 for the amplitude + __declspec(dllexport) Tacton(unsigned int nbframes, unsigned char *desc); + //creates a tacton using the CSV format + //1 field for the number of frames, then 4 fields per frame + //1 for the pattern + //1 for the duration + //1 for the frequency + //1 for the amplitude + __declspec(dllexport) Tacton(char *desc); + __declspec(dllexport) Tacton(unsigned int nbframes, unsigned char *patterns, unsigned int *durations, unsigned int *frequencies, unsigned char *amplitudes); + //create a simple vibration + __declspec(dllexport) Tacton(unsigned char pattern, unsigned int duration, unsigned int frequency, unsigned char amplitude); + __declspec(dllexport) ~Tacton(); + + //returns the number of frames + __declspec(dllexport) const unsigned int getNbFrames() const; + + //returns the raw code (useful to send to the serial port) + __declspec(dllexport) void *rawCode() const; + + private: + unsigned int _nbframes; + unsigned char *_patterns; + unsigned int *_durations; + unsigned int *_frequencies; + unsigned char *_amplitudes; +}; + +#endif diff --git a/TactonPlayer/TactonPlayer.cpp b/TactonPlayer/TactonPlayer.cpp new file mode 100644 index 0000000..acebf80 --- /dev/null +++ b/TactonPlayer/TactonPlayer.cpp @@ -0,0 +1,97 @@ +#include "TactonPlayer.hpp" + +#include "SerialWindows.hpp" + +#include + +TactonPlayer::TactonPlayer(char *port) +:_comport(new SerialWindows(port)) +{ +} + +TactonPlayer::~TactonPlayer() +{ + delete _comport; +} + +void TactonPlayer::start() +{ + _comport->WriteData("S", 1); +} + +void TactonPlayer::stop() +{ + _comport->WriteData("Q", 1); +} + +void TactonPlayer::regist(const Tacton &t) +{ + unsigned int nbframes = t.getNbFrames(); + unsigned char *buffer = new unsigned char[3 + 6 * nbframes]; + buffer[0] = 'N'; + buffer[1] = (nbframes & 0xff00) >> 8; + buffer[2] = nbframes & 0xff; + memcpy(buffer + 3, t.rawCode(), 6 * nbframes); + _comport->WriteData(buffer, 3 + 6 * nbframes); + delete []buffer; +} + +unsigned int TactonPlayer::registFile(char *filename) +{ + unsigned int nb = 0; + + //load icons + FILE *f = fopen(filename, "r"); + if (!f) + throw "Invalid file"; + + char buffer[256]; + while ((fgets(buffer, 256, f)) != NULL) + { + while((buffer[strlen(buffer) - 1] == '\r') || (buffer[strlen(buffer) - 1] == '\n')) + buffer[strlen(buffer) - 1] = '\0'; + try + { + Tacton t(buffer); + regist(t); + nb++; + } + catch(...) + { + } + } + fclose(f); + return nb; +} + +void TactonPlayer::play(unsigned char index) +{ + unsigned char buffer[2]; + buffer[0] = 'T'; + buffer[1] = index; + _comport->WriteData(buffer, 2); +} + +void TactonPlayer::play(const Tacton &t) +{ + unsigned int nbframes = t.getNbFrames(); + unsigned char *buffer = new unsigned char[3 + 6 * nbframes]; + buffer[0] = 'V'; + buffer[1] = (nbframes & 0xff00) >> 8; + buffer[2] = nbframes & 0xff; + memcpy(buffer + 3, t.rawCode(), 6 * nbframes); + _comport->WriteData(buffer, 3 + 6 * nbframes); + delete []buffer; +} + +void TactonPlayer::schedule(unsigned char index, unsigned long timestamp) +{ + unsigned char buffer[6]; + buffer[0] = 'P'; + buffer[1] = index; + buffer[2] = (unsigned char)((timestamp & 0xff000000) >> 24); + buffer[3] = (unsigned char)((timestamp & 0x00ff0000) >> 16); + buffer[4] = (unsigned char)((timestamp & 0x0000ff00) >> 8); + buffer[5] = (unsigned char)(timestamp & 0x000000ff); + _comport->WriteData(buffer, 6); +} \ No newline at end of file diff --git a/TactonPlayer/TactonPlayer.hpp b/TactonPlayer/TactonPlayer.hpp new file mode 100644 index 0000000..48296a9 --- /dev/null +++ b/TactonPlayer/TactonPlayer.hpp @@ -0,0 +1,30 @@ +#include "Tacton.hpp" + +#include "Serial.hpp" + +class TactonPlayer +{ + public: + __declspec(dllexport) TactonPlayer(char *port = "COM5"); + __declspec(dllexport) ~TactonPlayer(); + + //set the timestamp to 0, and watch for scheduled tactons + void __declspec(dllexport) start(); + //stop watching scheduled tactons, and erase registered tactons + void __declspec(dllexport) stop(); + + //register a tacton + void __declspec(dllexport) regist(const Tacton &t); + //register a list of tactons in a file, returns the number of tactons loaded + unsigned int __declspec(dllexport) registFile(char *filename); + + //play a registered tacton + void __declspec(dllexport) play(unsigned char index); + //play a specified tacton + void __declspec(dllexport) play(const Tacton &t); + //schedule the play of a registered tacton + void __declspec(dllexport) schedule(unsigned char index, unsigned long timestamp); + +private: + Serial *_comport; +}; diff --git a/TactonPlayer/TactonPlayer.vcxproj b/TactonPlayer/TactonPlayer.vcxproj new file mode 100644 index 0000000..aa962eb --- /dev/null +++ b/TactonPlayer/TactonPlayer.vcxproj @@ -0,0 +1,93 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {6D5C62C0-562D-4B2A-9AAC-806372810CBF} + Win32Proj + TactonPlayer + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;TACTONPLAYER_EXPORTS;%(PreprocessorDefinitions) + 4996 + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;TACTONPLAYER_EXPORTS;%(PreprocessorDefinitions) + 4996 + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TactonPlayer/TactonPlayer.vcxproj.filters b/TactonPlayer/TactonPlayer.vcxproj.filters new file mode 100644 index 0000000..8f2e3f7 --- /dev/null +++ b/TactonPlayer/TactonPlayer.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file