framework2

Форк
0
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

22
using std::vector;
23
using std::string;
24

25
#ifdef TARGET_WIN32
26

27
// needed for serial bus enumeration:
28
// 4d36e978-e325-11ce-bfc1-08002be10318}
29
DEFINE_GUID (GUID_SERENUM_BUS_ENUMERATOR, 0x4D36E978, 0xE325,
30
0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18);
31

32

33
void ofSerial::enumerateWin32Ports(){
34
	if(bPortsEnumerated == true){
35
		return;
36
	}
37

38
	HDEVINFO hDevInfo = nullptr;
39
	SP_DEVINFO_DATA DeviceInterfaceData;
40
	DWORD dataType, actualSize = 0;
41

42
	// Reset Port List
43
	nPorts = 0;
44
	// Search device set
45
	hDevInfo = SetupDiGetClassDevs((struct _GUID *)&GUID_SERENUM_BUS_ENUMERATOR, 0, 0, DIGCF_PRESENT);
46
	if(hDevInfo){
47
		int i = 0;
48
		unsigned char dataBuf[MAX_PATH + 1];
49
		while(TRUE){
50
			ZeroMemory(&DeviceInterfaceData, sizeof(DeviceInterfaceData));
51
			DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
52
			if(!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInterfaceData)){
53
				// SetupDiEnumDeviceInfo failed
54
				break;
55
			}
56

57
		if(SetupDiGetDeviceRegistryPropertyA(hDevInfo,
58
											 &DeviceInterfaceData,
59
											 SPDRP_FRIENDLYNAME,
60
											 &dataType,
61
											 dataBuf,
62
											 sizeof(dataBuf),
63
											 &actualSize)){
64

65
			 sprintf(portNamesFriendly[nPorts], "%s", dataBuf);
66
			 portNamesShort[nPorts][0] = 0;
67

68
			 // turn blahblahblah(COM4) into COM4
69

70
			 char * begin = nullptr;
71
			 char * end = nullptr;
72
			 begin = strstr((char *)dataBuf, "(COM");
73

74
			 if(begin){
75
				 begin++;	// get rid of the (
76
				 end = strstr(begin, ")");
77
				 if(end){
78
					 *end = 0;   // get rid of the )...
79
					 strcpy(portNamesShort[nPorts], begin);
80
				 }
81
				 if(nPorts++ > MAX_SERIAL_PORTS)
82
					 break;
83
				}
84
			}
85
			i++;
86
		}
87
	}
88
	SetupDiDestroyDeviceInfoList(hDevInfo);
89

90
	bPortsEnumerated = false;
91
}
92

93
#endif  // TARGET_WIN32
94

95

96

97
//----------------------------------------------------------------
98
ofSerial::ofSerial(){
99

100
	#ifdef TARGET_WIN32
101

102
		nPorts = 0;
103
		bPortsEnumerated = false;
104

105
		portNamesShort = new char * [MAX_SERIAL_PORTS];
106
		portNamesFriendly = new char * [MAX_SERIAL_PORTS];
107
		for(int i = 0; i < MAX_SERIAL_PORTS; i++){
108
			portNamesShort[i] = new char[10];
109
			portNamesFriendly[i] = new char[MAX_PATH];
110
		}
111

112
	#else
113

114
	fd = -1;
115

116
	#endif
117

118
	bHaveEnumeratedDevices = false;
119
	bInited = false;
120
}
121

122

123
//----------------------------------------------------------------
124
ofSerial::~ofSerial(){
125
	close();
126

127
	#ifdef TARGET_WIN32
128

129
		nPorts = 0;
130
		bPortsEnumerated = false;
131

132
		for(int i = 0; i < MAX_SERIAL_PORTS; i++){
133
			delete [] portNamesShort[i];
134
			delete [] portNamesFriendly[i];
135
		}
136
		delete [] portNamesShort;
137
		delete [] portNamesFriendly;
138

139
	#endif
140

141
	bInited = false;
142
}
143

144
//----------------------------------------------------------------
145
#if defined( TARGET_OSX )
146
static bool isDeviceArduino( ofSerialDeviceInfo & A ){
147
	//TODO - this should be ofStringInString
148
	return (strstr(A.getDeviceName().c_str(), "usbserial") != nullptr
149
			|| strstr(A.getDeviceName().c_str(), "usbmodem") != nullptr);
150
}
151
#endif
152

153
//----------------------------------------------------------------
154
void ofSerial::buildDeviceList(){
155
	deviceType = "serial";
156
	devices.clear();
157
	vector <string> prefixMatch;
158

159
	#ifdef TARGET_OSX
160

161
		prefixMatch.push_back("cu.");
162
		prefixMatch.push_back("tty.");
163

164
	#endif
165

166
	#ifdef TARGET_LINUX
167

168
		prefixMatch.push_back("ttyACM");
169
		prefixMatch.push_back("ttyS");
170
		prefixMatch.push_back("ttyUSB");
171
		prefixMatch.push_back("rfc");
172

173
	#endif
174

175
	#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
176
		ofDirectory dir("/dev");
177
		int deviceCount = 0;
178
		for(auto & entry: dir){
179
			std::string deviceName = entry.getFileName();
180

181
			//we go through the prefixes
182
			for(auto & prefix: prefixMatch){
183
				//if the device name is longer than the prefix
184
				if(deviceName.size() > prefix.size()){
185
					//do they match ?
186
					if(deviceName.substr(0, prefix.size()) == prefix.c_str()){
187
						devices.push_back(ofSerialDeviceInfo("/dev/"+deviceName, deviceName, deviceCount));
188
						deviceCount++;
189
						break;
190
					}
191
				}
192
			}
193
		}
194

195
	#endif
196

197
	#ifdef TARGET_WIN32
198

199
		enumerateWin32Ports();
200
		ofLogNotice("ofSerial") << "found " << nPorts << " devices";
201
		for(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
203
			devices.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.
210
		partition(devices.begin(), devices.end(), isDeviceArduino);
211
		//we are reordering the device ids. too!
212
		int k = 0;
213
		for(auto & device: devices){
214
			device.deviceID = k++;
215
		}
216
	#endif
217

218
	bHaveEnumeratedDevices = true;
219
}
220

221

222
//----------------------------------------------------------------
223
void ofSerial::listDevices(){
224
	buildDeviceList();
225

226
	for(auto & device: devices){
227
		ofLogNotice("ofSerial") << "[" << device.getDeviceID() << "] = "<< device.getDeviceName().c_str();
228
	}
229
}
230

231
//----------------------------------------------------------------
232
vector <ofSerialDeviceInfo> ofSerial::getDeviceList(){
233
	buildDeviceList();
234
	return devices;
235
}
236

237
//----------------------------------------------------------------
238
void ofSerial::enumerateDevices(){
239
	listDevices();
240
}
241

242
//----------------------------------------------------------------
243
void ofSerial::close(){
244

245
	#ifdef TARGET_WIN32
246

247
		if(bInited){
248
			SetCommTimeouts(hComm, &oldTimeout);
249
			CloseHandle(hComm);
250
			hComm = INVALID_HANDLE_VALUE;
251
			bInited = false;
252
		}
253

254
	#else
255

256
		if(bInited){
257
			tcsetattr(fd, TCSANOW, &oldoptions);
258
			::close(fd);
259
			bInited = false;
260
		}
261
		// [CHECK] -- anything else need to be reset?
262

263
	#endif
264
}
265

266
//----------------------------------------------------------------
267
bool ofSerial::setup(){
268
	return setup(0, 9600); // the first one, at 9600 is a good choice...
269
}
270

271
//----------------------------------------------------------------
272
bool ofSerial::setup(int deviceNumber, int baud){
273
	buildDeviceList();
274
	if(deviceNumber < (int)devices.size()){
275
		return setup(devices[deviceNumber].devicePath, baud);
276
	} else {
277
		ofLogError("ofSerial") << "couldn't find device " << deviceNumber << ", only " << devices.size() << " devices found";
278
		return false;
279
	}
280

281
}
282

283
//----------------------------------------------------------------
284
bool ofSerial::setup(string portName, int baud){
285
	bInited = 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
290
		if(portName.size() > 5 && portName.substr(0, 5) != "/dev/"){
291
			portName = "/dev/" + portName;
292
		}
293

294
		ofLogNotice("ofSerial") << "opening " << portName << " @ " << baud << " bps";
295
		fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
296
		if(fd == -1){
297
			ofLogError("ofSerial") << "unable to open " << portName;
298
			return false;
299
		}
300

301
		struct termios options;
302
		tcgetattr(fd, &oldoptions);
303
		options = oldoptions;
304
		switch(baud){
305
			case 300:
306
			cfsetispeed(&options, B300);
307
				cfsetospeed(&options, B300);
308
				break;
309
		   case 1200:
310
			cfsetispeed(&options, B1200);
311
				cfsetospeed(&options, B1200);
312
				break;
313
		   case 2400:
314
			cfsetispeed(&options, B2400);
315
				cfsetospeed(&options, B2400);
316
				break;
317
		   case 4800:
318
			cfsetispeed(&options, B4800);
319
		cfsetospeed(&options, B4800);
320
		break;
321
		   case 9600:
322
			cfsetispeed(&options, B9600);
323
		cfsetospeed(&options, B9600);
324
		break;
325
		   case 14400:
326
			cfsetispeed(&options, B14400);
327
		cfsetospeed(&options, B14400);
328
		break;
329
		   case 19200:
330
			cfsetispeed(&options, B19200);
331
		cfsetospeed(&options, B19200);
332
		break;
333
		   case 28800:
334
			cfsetispeed(&options, B28800);
335
		cfsetospeed(&options, B28800);
336
		break;
337
		   case 38400:
338
			cfsetispeed(&options, B38400);
339
		cfsetospeed(&options, B38400);
340
		break;
341
		   case 57600:
342
			cfsetispeed(&options, B57600);
343
		cfsetospeed(&options, B57600);
344
		break;
345
		   case 115200:
346
			cfsetispeed(&options, B115200);
347
		cfsetospeed(&options, B115200);
348
		break;
349
		   case 230400:
350
			cfsetispeed(&options, B230400);
351
		cfsetospeed(&options, B230400);
352
		break;
353
		   case 12000000: 
354
			cfsetispeed(&options, 12000000);
355
		cfsetospeed(&options, 12000000);	
356
		break;
357
		   default:
358
			cfsetispeed(&options, B9600);
359
		cfsetospeed(&options, B9600);
360
		ofLogError("ofSerial") << "setup(): cannot set " << baud << " bps, setting to 9600";
361
		break;
362
		}
363

364
		options.c_cflag |= (CLOCAL | CREAD);
365
		options.c_cflag &= ~PARENB;
366
		options.c_cflag &= ~CSTOPB;
367
		options.c_cflag &= ~CSIZE;
368
		options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK);
369
		options.c_oflag &= (tcflag_t) ~(OPOST);
370
		options.c_cflag |= CS8;
371
		#if defined( TARGET_LINUX )
372
            options.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off software xon/xoff flow ctrl
373
			options.c_cflag |= CRTSCTS;
374
			options.c_lflag &= ~(ICANON | ECHO | ISIG);
375
		#endif
376
		tcsetattr(fd, TCSANOW, &options);
377
		#ifdef TARGET_LINUX
378
			struct serial_struct kernel_serial_settings;
379
			if (ioctl(fd, TIOCGSERIAL, &kernel_serial_settings) == 0) {
380
				kernel_serial_settings.flags |= ASYNC_LOW_LATENCY;
381
				ioctl(fd, TIOCSSERIAL, &kernel_serial_settings);
382
			}
383
		#endif
384
		bInited = true;
385
		ofLogNotice("ofSerial") << "opened " << portName << " sucessfully @ " << baud << " bps";
386

387
		return true;
388

389
	#elif defined( TARGET_WIN32 )
390

391
		char pn[sizeof(portName)];
392
		int num;
393
		if(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
396
			sprintf(pn, "\\\\.\\COM%d", num);
397
		} else {
398
			strncpy(pn, (const char *)portName.c_str(), sizeof(portName)-1);
399
		}
400

401
		// open the serial port:
402
		// "COM4", etc...
403

404
		hComm = CreateFileA(pn, GENERIC_READ|GENERIC_WRITE, 0, 0,
405
							OPEN_EXISTING, 0, 0);
406

407
		if(hComm == INVALID_HANDLE_VALUE){
408
			ofLogError("ofSerial") << "setup(): unable to open " << portName;
409
			return false;
410
		}
411

412

413
		// now try the settings:
414
		COMMCONFIG cfg;
415
		DWORD cfgSize;
416
		WCHAR buf[80];
417

418
		cfgSize = sizeof(cfg);
419
		GetCommConfig(hComm, &cfg, &cfgSize);
420
		int bps = baud;
421
		swprintf(buf, L"baud=%d parity=N data=8 stop=1", bps);
422

423
		if(!BuildCommDCBW(buf, &cfg.dcb)){
424
			ofLogError("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

430
		if(!SetCommState(hComm, &cfg.dcb)){
431
			ofLogError("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)
436
		COMMTIMEOUTS tOut;
437
		GetCommTimeouts(hComm, &oldTimeout);
438
		tOut = oldTimeout;
439
		// Make timeout so that:
440
		// - return immediately with buffered characters
441
		tOut.ReadIntervalTimeout = MAXDWORD;
442
		tOut.ReadTotalTimeoutMultiplier = 0;
443
		tOut.ReadTotalTimeoutConstant = 0;
444
		SetCommTimeouts(hComm, &tOut);
445

446
		bInited = true;
447
		return true;
448

449
	#else
450

451
		ofLogError("ofSerial") << "not implemented in this platform";
452
		return false;
453

454
	#endif
455
}
456

457

458
//----------------------------------------------------------------
459
long ofSerial::writeBytes(const char * buffer, size_t length){
460
	if(!bInited){
461
		ofLogError("ofSerial") << "writeBytes(): serial not inited";
462
		return OF_SERIAL_ERROR;
463
	}
464

465
	#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
466
		size_t written=0;
467
		fd_set wfds;
468
		struct timeval tv;
469
		while (written < length) {
470
			auto n = write(fd, buffer + written, length - written);
471
			if (n < 0 && (errno == EAGAIN || errno == EINTR)) n = 0;
472
			//printf("Write, n = %d\n", n);
473
			if (n < 0) return OF_SERIAL_ERROR;
474
			if (n > 0) {
475
				written += n;
476
			} else {
477
				tv.tv_sec = 10;
478
				tv.tv_usec = 0;
479
				FD_ZERO(&wfds);
480
				FD_SET(fd, &wfds);
481
				n = select(fd+1, NULL, &wfds, NULL, &tv);
482
				if (n < 0 && errno == EINTR) n = 1;
483
				if (n <= 0) return OF_SERIAL_ERROR;
484
			}
485
		}
486
		return written;
487
	#elif defined(TARGET_WIN32)
488

489
		DWORD written;
490
		if(!WriteFile(hComm, buffer, length, &written,0)){
491
			 ofLogError("ofSerial") << "writeBytes(): couldn't write to port";
492
			 return OF_SERIAL_ERROR;
493
		}
494
		ofLogVerbose("ofSerial") <<  "wrote " << (int) written << " bytes";
495
		return (int)written;
496

497
	#else
498

499
		return 0;
500

501
	#endif
502
}
503

504
//----------------------------------------------------------------
505
long ofSerial::writeBytes(const unsigned char * buffer, size_t length){
506
	return writeBytes(reinterpret_cast<const char*>(buffer), length);
507
}
508

509
//----------------------------------------------------------------
510
bool ofSerial::writeByte(char singleByte){
511
	return writeByte(static_cast<unsigned char>(singleByte));
512
}
513

514
//----------------------------------------------------------------
515
long ofSerial::writeBytes(const ofBuffer & buffer){
516
	return writeBytes(buffer.getData(),buffer.size());
517
}
518

519
//----------------------------------------------------------------
520
long ofSerial::readBytes(char * buffer, size_t length){
521
	if (!bInited){
522
		ofLogError("ofSerial") << "readBytes(): serial not inited";
523
		return OF_SERIAL_ERROR;
524
	}
525

526
	#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
527

528
		auto nRead = read(fd, buffer, length);
529
		if(nRead < 0){
530
			if ( errno == EAGAIN )
531
				return OF_SERIAL_NO_DATA;
532
			ofLogError("ofSerial") << "readBytes(): couldn't read from port: " << errno << " " << strerror(errno);
533
			return OF_SERIAL_ERROR;
534
		}
535
		return nRead;
536

537
	#elif defined( TARGET_WIN32 )
538

539
		DWORD nRead = 0;
540
		if (!ReadFile(hComm, buffer, length, &nRead, 0)){
541
			ofLogError("ofSerial") << "readBytes(): couldn't read from port";
542
			return OF_SERIAL_ERROR;
543
		}
544
		return (int)nRead;
545

546
	#else
547

548
		ofLogError("ofSerial") << "not defined in this platform";
549
		return -1;
550

551
	#endif
552
}
553

554
//----------------------------------------------------------------
555
long ofSerial::readBytes(unsigned char * buffer, size_t length){
556
	return readBytes(reinterpret_cast<char*>(buffer), length);
557
}
558

559
//----------------------------------------------------------------
560
long ofSerial::readBytes(ofBuffer & buffer, size_t length){
561
	buffer.allocate(length);
562
	return readBytes(buffer.getData(),length);
563
}
564

565
//----------------------------------------------------------------
566
bool ofSerial::writeByte(unsigned char singleByte){
567
	if (!bInited){
568
		ofLogError("ofSerial") << "writeByte(): serial not inited";
569
		return false;
570
	}
571
	return writeBytes(&singleByte,1) > 0;
572
}
573

574
//----------------------------------------------------------------
575
int ofSerial::readByte(){
576
	if(!bInited){
577
		ofLogError("ofSerial") << "readByte(): serial not inited";
578
		return OF_SERIAL_ERROR;
579
	}
580

581
	unsigned char tmpByte = 0;
582

583
	#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
584

585
		int nRead = read(fd, &tmpByte, 1);
586
		if(nRead < 0){
587
			if ( errno == EAGAIN ){
588
				return OF_SERIAL_NO_DATA;
589
			}
590
			ofLogError("ofSerial") << "readByte(): couldn't read from port: " << errno << " " << strerror(errno);
591
			return OF_SERIAL_ERROR;
592
		}
593

594
		if(nRead == 0){
595
			return OF_SERIAL_NO_DATA;
596
		}
597

598
	#elif defined( TARGET_WIN32 )
599

600
		DWORD nRead;
601
		if(!ReadFile(hComm, &tmpByte, 1, &nRead, 0)){
602
			ofLogError("ofSerial") << "readByte(): couldn't read from port";
603
			return OF_SERIAL_ERROR;
604
		}
605
	
606
		if(nRead == 0){
607
			return OF_SERIAL_NO_DATA;
608
		}
609

610
	#else
611

612
		ofLogError("ofSerial") << "not defined in this platform";
613
		return OF_SERIAL_ERROR;
614

615
	#endif
616

617
	return tmpByte;
618
}
619

620

621
//----------------------------------------------------------------
622
void ofSerial::flush(bool flushIn, bool flushOut){
623
	if(!bInited){
624
		ofLogError("ofSerial") << "flush(): serial not inited";
625
		return;
626
	}
627

628
	#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
629
		int flushType = 0;
630
		if(flushIn && flushOut) flushType = TCIOFLUSH;
631
		else if(flushIn) flushType = TCIFLUSH;
632
		else if(flushOut) flushType = TCOFLUSH;
633
		else return;
634

635
		tcflush(fd, flushType);
636
	#elif defined( TARGET_WIN32 )
637

638
		int flushType = 0;
639
		if(flushIn && flushOut) flushType = PURGE_TXCLEAR | PURGE_RXCLEAR;
640
		else if(flushIn) flushType = PURGE_RXCLEAR;
641
		else if(flushOut) flushType = PURGE_TXCLEAR;
642
		else return;
643

644
		PurgeComm(hComm, flushType);
645

646
	#endif
647
}
648

649
void ofSerial::drain(){
650
	if(!bInited){
651
		ofLogError("ofSerial") << "drain(): serial not inited";
652
		return;
653
	}
654

655
	#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
656

657
		tcdrain(fd);
658

659
	#endif
660
}
661

662
//-------------------------------------------------------------
663
int ofSerial::available(){
664
	if(!bInited){
665
		ofLogError("ofSerial") << "available(): serial not inited";
666
		return OF_SERIAL_ERROR;
667
	}
668

669
	int numBytes = 0;
670

671
	#if defined( TARGET_OSX ) || defined( TARGET_LINUX )
672

673
		ioctl(fd, FIONREAD, &numBytes);
674

675
	#endif
676

677
	#ifdef TARGET_WIN32
678

679
		COMSTAT stat;
680
		DWORD err;
681
		if(hComm!=INVALID_HANDLE_VALUE){
682
			if(!ClearCommError(hComm, &err, &stat)){
683
				numBytes = 0;
684
			} else {
685
				numBytes = stat.cbInQue;
686
			}
687
		 } else {
688
			numBytes = 0;
689
		 }
690

691
	#endif
692

693
	return numBytes;
694
}
695

696
bool ofSerial::isInitialized() const{
697
	return bInited;
698
}
699

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.