framework2
698 строк · 16.7 Кб
1#include "ofSerial.h"
2#include "ofUtils.h"
3#include "ofLog.h"
4
5#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
6#include <sys/ioctl.h>
7#include <getopt.h>
8#include <dirent.h>
9#endif
10
11
12#include <fcntl.h>
13#include <errno.h>
14#include <ctype.h>
15#include <algorithm>
16#include <cstring>
17
18#ifdef TARGET_LINUX
19#include <linux/serial.h>
20#endif
21
22using std::vector;
23using std::string;
24
25#ifdef TARGET_WIN32
26
27// needed for serial bus enumeration:
28// 4d36e978-e325-11ce-bfc1-08002be10318}
29DEFINE_GUID (GUID_SERENUM_BUS_ENUMERATOR, 0x4D36E978, 0xE325,
300x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18);
31
32
33void ofSerial::enumerateWin32Ports(){
34if(bPortsEnumerated == true){
35return;
36}
37
38HDEVINFO hDevInfo = nullptr;
39SP_DEVINFO_DATA DeviceInterfaceData;
40DWORD dataType, actualSize = 0;
41
42// Reset Port List
43nPorts = 0;
44// Search device set
45hDevInfo = SetupDiGetClassDevs((struct _GUID *)&GUID_SERENUM_BUS_ENUMERATOR, 0, 0, DIGCF_PRESENT);
46if(hDevInfo){
47int i = 0;
48unsigned char dataBuf[MAX_PATH + 1];
49while(TRUE){
50ZeroMemory(&DeviceInterfaceData, sizeof(DeviceInterfaceData));
51DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
52if(!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInterfaceData)){
53// SetupDiEnumDeviceInfo failed
54break;
55}
56
57if(SetupDiGetDeviceRegistryPropertyA(hDevInfo,
58&DeviceInterfaceData,
59SPDRP_FRIENDLYNAME,
60&dataType,
61dataBuf,
62sizeof(dataBuf),
63&actualSize)){
64
65sprintf(portNamesFriendly[nPorts], "%s", dataBuf);
66portNamesShort[nPorts][0] = 0;
67
68// turn blahblahblah(COM4) into COM4
69
70char * begin = nullptr;
71char * end = nullptr;
72begin = strstr((char *)dataBuf, "(COM");
73
74if(begin){
75begin++; // get rid of the (
76end = strstr(begin, ")");
77if(end){
78*end = 0; // get rid of the )...
79strcpy(portNamesShort[nPorts], begin);
80}
81if(nPorts++ > MAX_SERIAL_PORTS)
82break;
83}
84}
85i++;
86}
87}
88SetupDiDestroyDeviceInfoList(hDevInfo);
89
90bPortsEnumerated = false;
91}
92
93#endif // TARGET_WIN32
94
95
96
97//----------------------------------------------------------------
98ofSerial::ofSerial(){
99
100#ifdef TARGET_WIN32
101
102nPorts = 0;
103bPortsEnumerated = false;
104
105portNamesShort = new char * [MAX_SERIAL_PORTS];
106portNamesFriendly = new char * [MAX_SERIAL_PORTS];
107for(int i = 0; i < MAX_SERIAL_PORTS; i++){
108portNamesShort[i] = new char[10];
109portNamesFriendly[i] = new char[MAX_PATH];
110}
111
112#else
113
114fd = -1;
115
116#endif
117
118bHaveEnumeratedDevices = false;
119bInited = false;
120}
121
122
123//----------------------------------------------------------------
124ofSerial::~ofSerial(){
125close();
126
127#ifdef TARGET_WIN32
128
129nPorts = 0;
130bPortsEnumerated = false;
131
132for(int i = 0; i < MAX_SERIAL_PORTS; i++){
133delete [] portNamesShort[i];
134delete [] portNamesFriendly[i];
135}
136delete [] portNamesShort;
137delete [] portNamesFriendly;
138
139#endif
140
141bInited = false;
142}
143
144//----------------------------------------------------------------
145#if defined( TARGET_OSX )
146static bool isDeviceArduino( ofSerialDeviceInfo & A ){
147//TODO - this should be ofStringInString
148return (strstr(A.getDeviceName().c_str(), "usbserial") != nullptr
149|| strstr(A.getDeviceName().c_str(), "usbmodem") != nullptr);
150}
151#endif
152
153//----------------------------------------------------------------
154void ofSerial::buildDeviceList(){
155deviceType = "serial";
156devices.clear();
157vector <string> prefixMatch;
158
159#ifdef TARGET_OSX
160
161prefixMatch.push_back("cu.");
162prefixMatch.push_back("tty.");
163
164#endif
165
166#ifdef TARGET_LINUX
167
168prefixMatch.push_back("ttyACM");
169prefixMatch.push_back("ttyS");
170prefixMatch.push_back("ttyUSB");
171prefixMatch.push_back("rfc");
172
173#endif
174
175#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
176ofDirectory dir("/dev");
177int deviceCount = 0;
178for(auto & entry: dir){
179std::string deviceName = entry.getFileName();
180
181//we go through the prefixes
182for(auto & prefix: prefixMatch){
183//if the device name is longer than the prefix
184if(deviceName.size() > prefix.size()){
185//do they match ?
186if(deviceName.substr(0, prefix.size()) == prefix.c_str()){
187devices.push_back(ofSerialDeviceInfo("/dev/"+deviceName, deviceName, deviceCount));
188deviceCount++;
189break;
190}
191}
192}
193}
194
195#endif
196
197#ifdef TARGET_WIN32
198
199enumerateWin32Ports();
200ofLogNotice("ofSerial") << "found " << nPorts << " devices";
201for(int i = 0; i < nPorts; i++){
202//NOTE: we give the short port name for both as that is what the user should pass and the short name is more friendly
203devices.push_back(ofSerialDeviceInfo(string(portNamesShort[i]), string(portNamesFriendly[i]), i));
204}
205
206#endif
207
208#if defined( TARGET_OSX )
209//here we sort the device to have the aruino ones first.
210partition(devices.begin(), devices.end(), isDeviceArduino);
211//we are reordering the device ids. too!
212int k = 0;
213for(auto & device: devices){
214device.deviceID = k++;
215}
216#endif
217
218bHaveEnumeratedDevices = true;
219}
220
221
222//----------------------------------------------------------------
223void ofSerial::listDevices(){
224buildDeviceList();
225
226for(auto & device: devices){
227ofLogNotice("ofSerial") << "[" << device.getDeviceID() << "] = "<< device.getDeviceName().c_str();
228}
229}
230
231//----------------------------------------------------------------
232vector <ofSerialDeviceInfo> ofSerial::getDeviceList(){
233buildDeviceList();
234return devices;
235}
236
237//----------------------------------------------------------------
238void ofSerial::enumerateDevices(){
239listDevices();
240}
241
242//----------------------------------------------------------------
243void ofSerial::close(){
244
245#ifdef TARGET_WIN32
246
247if(bInited){
248SetCommTimeouts(hComm, &oldTimeout);
249CloseHandle(hComm);
250hComm = INVALID_HANDLE_VALUE;
251bInited = false;
252}
253
254#else
255
256if(bInited){
257tcsetattr(fd, TCSANOW, &oldoptions);
258::close(fd);
259bInited = false;
260}
261// [CHECK] -- anything else need to be reset?
262
263#endif
264}
265
266//----------------------------------------------------------------
267bool ofSerial::setup(){
268return setup(0, 9600); // the first one, at 9600 is a good choice...
269}
270
271//----------------------------------------------------------------
272bool ofSerial::setup(int deviceNumber, int baud){
273buildDeviceList();
274if(deviceNumber < (int)devices.size()){
275return setup(devices[deviceNumber].devicePath, baud);
276} else {
277ofLogError("ofSerial") << "couldn't find device " << deviceNumber << ", only " << devices.size() << " devices found";
278return false;
279}
280
281}
282
283//----------------------------------------------------------------
284bool ofSerial::setup(string portName, int baud){
285bInited = false;
286
287#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
288
289//lets account for the name being passed in instead of the device path
290if(portName.size() > 5 && portName.substr(0, 5) != "/dev/"){
291portName = "/dev/" + portName;
292}
293
294ofLogNotice("ofSerial") << "opening " << portName << " @ " << baud << " bps";
295fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
296if(fd == -1){
297ofLogError("ofSerial") << "unable to open " << portName;
298return false;
299}
300
301struct termios options;
302tcgetattr(fd, &oldoptions);
303options = oldoptions;
304switch(baud){
305case 300:
306cfsetispeed(&options, B300);
307cfsetospeed(&options, B300);
308break;
309case 1200:
310cfsetispeed(&options, B1200);
311cfsetospeed(&options, B1200);
312break;
313case 2400:
314cfsetispeed(&options, B2400);
315cfsetospeed(&options, B2400);
316break;
317case 4800:
318cfsetispeed(&options, B4800);
319cfsetospeed(&options, B4800);
320break;
321case 9600:
322cfsetispeed(&options, B9600);
323cfsetospeed(&options, B9600);
324break;
325case 14400:
326cfsetispeed(&options, B14400);
327cfsetospeed(&options, B14400);
328break;
329case 19200:
330cfsetispeed(&options, B19200);
331cfsetospeed(&options, B19200);
332break;
333case 28800:
334cfsetispeed(&options, B28800);
335cfsetospeed(&options, B28800);
336break;
337case 38400:
338cfsetispeed(&options, B38400);
339cfsetospeed(&options, B38400);
340break;
341case 57600:
342cfsetispeed(&options, B57600);
343cfsetospeed(&options, B57600);
344break;
345case 115200:
346cfsetispeed(&options, B115200);
347cfsetospeed(&options, B115200);
348break;
349case 230400:
350cfsetispeed(&options, B230400);
351cfsetospeed(&options, B230400);
352break;
353case 12000000:
354cfsetispeed(&options, 12000000);
355cfsetospeed(&options, 12000000);
356break;
357default:
358cfsetispeed(&options, B9600);
359cfsetospeed(&options, B9600);
360ofLogError("ofSerial") << "setup(): cannot set " << baud << " bps, setting to 9600";
361break;
362}
363
364options.c_cflag |= (CLOCAL | CREAD);
365options.c_cflag &= ~PARENB;
366options.c_cflag &= ~CSTOPB;
367options.c_cflag &= ~CSIZE;
368options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK);
369options.c_oflag &= (tcflag_t) ~(OPOST);
370options.c_cflag |= CS8;
371#if defined( TARGET_LINUX )
372options.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off software xon/xoff flow ctrl
373options.c_cflag |= CRTSCTS;
374options.c_lflag &= ~(ICANON | ECHO | ISIG);
375#endif
376tcsetattr(fd, TCSANOW, &options);
377#ifdef TARGET_LINUX
378struct serial_struct kernel_serial_settings;
379if (ioctl(fd, TIOCGSERIAL, &kernel_serial_settings) == 0) {
380kernel_serial_settings.flags |= ASYNC_LOW_LATENCY;
381ioctl(fd, TIOCSSERIAL, &kernel_serial_settings);
382}
383#endif
384bInited = true;
385ofLogNotice("ofSerial") << "opened " << portName << " sucessfully @ " << baud << " bps";
386
387return true;
388
389#elif defined( TARGET_WIN32 )
390
391char pn[sizeof(portName)];
392int num;
393if(sscanf(portName.c_str(), "COM%d", &num) == 1){
394// Microsoft KB115831 a.k.a if COM > COM9 you have to use a different
395// syntax
396sprintf(pn, "\\\\.\\COM%d", num);
397} else {
398strncpy(pn, (const char *)portName.c_str(), sizeof(portName)-1);
399}
400
401// open the serial port:
402// "COM4", etc...
403
404hComm = CreateFileA(pn, GENERIC_READ|GENERIC_WRITE, 0, 0,
405OPEN_EXISTING, 0, 0);
406
407if(hComm == INVALID_HANDLE_VALUE){
408ofLogError("ofSerial") << "setup(): unable to open " << portName;
409return false;
410}
411
412
413// now try the settings:
414COMMCONFIG cfg;
415DWORD cfgSize;
416WCHAR buf[80];
417
418cfgSize = sizeof(cfg);
419GetCommConfig(hComm, &cfg, &cfgSize);
420int bps = baud;
421swprintf(buf, L"baud=%d parity=N data=8 stop=1", bps);
422
423if(!BuildCommDCBW(buf, &cfg.dcb)){
424ofLogError("ofSerial") << "setup(): unable to build comm dcb, (" << buf << ")";
425}
426
427// Set baudrate and bits etc.
428// Note that BuildCommDCB() clears XON/XOFF and hardware control by default
429
430if(!SetCommState(hComm, &cfg.dcb)){
431ofLogError("ofSerial") << "setup(): couldn't set comm state: " << cfg.dcb.BaudRate << " bps, xio " << cfg.dcb.fInX << "/" << cfg.dcb.fOutX;
432}
433//ofLogNotice("ofSerial") << "bps=" << cfg.dcb.BaudRate << ", xio=" << cfg.dcb.fInX << "/" << cfg.dcb.fOutX;
434
435// Set communication timeouts (NT)
436COMMTIMEOUTS tOut;
437GetCommTimeouts(hComm, &oldTimeout);
438tOut = oldTimeout;
439// Make timeout so that:
440// - return immediately with buffered characters
441tOut.ReadIntervalTimeout = MAXDWORD;
442tOut.ReadTotalTimeoutMultiplier = 0;
443tOut.ReadTotalTimeoutConstant = 0;
444SetCommTimeouts(hComm, &tOut);
445
446bInited = true;
447return true;
448
449#else
450
451ofLogError("ofSerial") << "not implemented in this platform";
452return false;
453
454#endif
455}
456
457
458//----------------------------------------------------------------
459long ofSerial::writeBytes(const char * buffer, size_t length){
460if(!bInited){
461ofLogError("ofSerial") << "writeBytes(): serial not inited";
462return OF_SERIAL_ERROR;
463}
464
465#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
466size_t written=0;
467fd_set wfds;
468struct timeval tv;
469while (written < length) {
470auto n = write(fd, buffer + written, length - written);
471if (n < 0 && (errno == EAGAIN || errno == EINTR)) n = 0;
472//printf("Write, n = %d\n", n);
473if (n < 0) return OF_SERIAL_ERROR;
474if (n > 0) {
475written += n;
476} else {
477tv.tv_sec = 10;
478tv.tv_usec = 0;
479FD_ZERO(&wfds);
480FD_SET(fd, &wfds);
481n = select(fd+1, NULL, &wfds, NULL, &tv);
482if (n < 0 && errno == EINTR) n = 1;
483if (n <= 0) return OF_SERIAL_ERROR;
484}
485}
486return written;
487#elif defined(TARGET_WIN32)
488
489DWORD written;
490if(!WriteFile(hComm, buffer, length, &written,0)){
491ofLogError("ofSerial") << "writeBytes(): couldn't write to port";
492return OF_SERIAL_ERROR;
493}
494ofLogVerbose("ofSerial") << "wrote " << (int) written << " bytes";
495return (int)written;
496
497#else
498
499return 0;
500
501#endif
502}
503
504//----------------------------------------------------------------
505long ofSerial::writeBytes(const unsigned char * buffer, size_t length){
506return writeBytes(reinterpret_cast<const char*>(buffer), length);
507}
508
509//----------------------------------------------------------------
510bool ofSerial::writeByte(char singleByte){
511return writeByte(static_cast<unsigned char>(singleByte));
512}
513
514//----------------------------------------------------------------
515long ofSerial::writeBytes(const ofBuffer & buffer){
516return writeBytes(buffer.getData(),buffer.size());
517}
518
519//----------------------------------------------------------------
520long ofSerial::readBytes(char * buffer, size_t length){
521if (!bInited){
522ofLogError("ofSerial") << "readBytes(): serial not inited";
523return OF_SERIAL_ERROR;
524}
525
526#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
527
528auto nRead = read(fd, buffer, length);
529if(nRead < 0){
530if ( errno == EAGAIN )
531return OF_SERIAL_NO_DATA;
532ofLogError("ofSerial") << "readBytes(): couldn't read from port: " << errno << " " << strerror(errno);
533return OF_SERIAL_ERROR;
534}
535return nRead;
536
537#elif defined( TARGET_WIN32 )
538
539DWORD nRead = 0;
540if (!ReadFile(hComm, buffer, length, &nRead, 0)){
541ofLogError("ofSerial") << "readBytes(): couldn't read from port";
542return OF_SERIAL_ERROR;
543}
544return (int)nRead;
545
546#else
547
548ofLogError("ofSerial") << "not defined in this platform";
549return -1;
550
551#endif
552}
553
554//----------------------------------------------------------------
555long ofSerial::readBytes(unsigned char * buffer, size_t length){
556return readBytes(reinterpret_cast<char*>(buffer), length);
557}
558
559//----------------------------------------------------------------
560long ofSerial::readBytes(ofBuffer & buffer, size_t length){
561buffer.allocate(length);
562return readBytes(buffer.getData(),length);
563}
564
565//----------------------------------------------------------------
566bool ofSerial::writeByte(unsigned char singleByte){
567if (!bInited){
568ofLogError("ofSerial") << "writeByte(): serial not inited";
569return false;
570}
571return writeBytes(&singleByte,1) > 0;
572}
573
574//----------------------------------------------------------------
575int ofSerial::readByte(){
576if(!bInited){
577ofLogError("ofSerial") << "readByte(): serial not inited";
578return OF_SERIAL_ERROR;
579}
580
581unsigned char tmpByte = 0;
582
583#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
584
585int nRead = read(fd, &tmpByte, 1);
586if(nRead < 0){
587if ( errno == EAGAIN ){
588return OF_SERIAL_NO_DATA;
589}
590ofLogError("ofSerial") << "readByte(): couldn't read from port: " << errno << " " << strerror(errno);
591return OF_SERIAL_ERROR;
592}
593
594if(nRead == 0){
595return OF_SERIAL_NO_DATA;
596}
597
598#elif defined( TARGET_WIN32 )
599
600DWORD nRead;
601if(!ReadFile(hComm, &tmpByte, 1, &nRead, 0)){
602ofLogError("ofSerial") << "readByte(): couldn't read from port";
603return OF_SERIAL_ERROR;
604}
605
606if(nRead == 0){
607return OF_SERIAL_NO_DATA;
608}
609
610#else
611
612ofLogError("ofSerial") << "not defined in this platform";
613return OF_SERIAL_ERROR;
614
615#endif
616
617return tmpByte;
618}
619
620
621//----------------------------------------------------------------
622void ofSerial::flush(bool flushIn, bool flushOut){
623if(!bInited){
624ofLogError("ofSerial") << "flush(): serial not inited";
625return;
626}
627
628#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
629int flushType = 0;
630if(flushIn && flushOut) flushType = TCIOFLUSH;
631else if(flushIn) flushType = TCIFLUSH;
632else if(flushOut) flushType = TCOFLUSH;
633else return;
634
635tcflush(fd, flushType);
636#elif defined( TARGET_WIN32 )
637
638int flushType = 0;
639if(flushIn && flushOut) flushType = PURGE_TXCLEAR | PURGE_RXCLEAR;
640else if(flushIn) flushType = PURGE_RXCLEAR;
641else if(flushOut) flushType = PURGE_TXCLEAR;
642else return;
643
644PurgeComm(hComm, flushType);
645
646#endif
647}
648
649void ofSerial::drain(){
650if(!bInited){
651ofLogError("ofSerial") << "drain(): serial not inited";
652return;
653}
654
655#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
656
657tcdrain(fd);
658
659#endif
660}
661
662//-------------------------------------------------------------
663int ofSerial::available(){
664if(!bInited){
665ofLogError("ofSerial") << "available(): serial not inited";
666return OF_SERIAL_ERROR;
667}
668
669int numBytes = 0;
670
671#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
672
673ioctl(fd, FIONREAD, &numBytes);
674
675#endif
676
677#ifdef TARGET_WIN32
678
679COMSTAT stat;
680DWORD err;
681if(hComm!=INVALID_HANDLE_VALUE){
682if(!ClearCommError(hComm, &err, &stat)){
683numBytes = 0;
684} else {
685numBytes = stat.cbInQue;
686}
687} else {
688numBytes = 0;
689}
690
691#endif
692
693return numBytes;
694}
695
696bool ofSerial::isInitialized() const{
697return bInited;
698}
699