Last active
August 29, 2015 13:56
-
-
Save PlumCantaloupe/8939224 to your computer and use it in GitHub Desktop.
A class I have modified/created for use in past projects when needing Windows/MacOSX communication. Used specifically with Teensy which used an extremely high-bandwidth.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/***************** | |
- A slightly modified version of receive_test.c from here: http://www.pjrc.com/teensy/benchmark_usb_serial_receive.html#code | |
- Used in Cinder projects when communicating with Serial | |
- Also tested on both Windows7/8 and Mac OSX platforms | |
TEST USAGE FOLLIOWING: | |
void InvestOttawa_2013App::startSerial() { | |
if (!mSerialOn) { | |
std::cout << "connecting serial ..." << std::endl; | |
//serial init | |
mErrorCount = 0; | |
bool successfulConnection = false; | |
#if defined( CINDER_MSW ) | |
//open correct port (windows as serial port changes on restarts) just brute force through all ports and assume connected to only one arduino | |
int numOfPorts = 10; | |
std::string portName; | |
for (int i=0; i<numOfPorts; i++) { | |
portName = "COM" + boost::lexical_cast<std::string>(i); | |
mSerialPort = C_Serial::open_port_and_set_baud_or_die(portName.c_str(), BAUD, &successfulConnection); | |
if (successfulConnection) { | |
std:cout << "opening serial port " << portName << " was successful!" << std::endl; | |
break; | |
} | |
} | |
#else | |
//mac ports do not change when restarted so we will just stick with define for now | |
mSerialPort = C_Serial::open_port_and_set_baud_or_die(SERIAL_PORT.c_str(), BAUD, &successfulConnection); | |
if (successfulConnection) { | |
std:cout << "opening serial port " << SERIAL_PORT << " was successful!" << std::endl; | |
} | |
#endif | |
mSerialOn = true; | |
} | |
} | |
****************************/ | |
/***** | |
SerialC.h starting now | |
*****/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
//#include <sys/time.h> | |
#include <fcntl.h> | |
//#include <unistd.h> | |
#include <errno.h> | |
#if defined(CINDER_MAC) || defined(LINUX) | |
#include <termios.h> | |
#include <sys/select.h> | |
#define PORTTYPE int | |
#define BAUD B115200 | |
#if defined(LINUX) | |
#include <sys/ioctl.h> | |
#include <linux/serial.h> | |
#endif | |
#elif defined(CINDER_MSW) | |
#include <windows.h> | |
#define PORTTYPE HANDLE | |
#define BAUD 115200 | |
#else | |
#error "You must define the operating system\n" | |
#endif | |
namespace C_Serial { | |
/**********************************/ | |
/* Misc. Functions */ | |
/**********************************/ | |
// Convert an UTF8 string to a wide Unicode String | |
#if defined(CINDER_MSW) | |
static std::wstring utf8_decode(const std::string &str) | |
{ | |
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); | |
std::wstring wstrTo( size_needed, 0 ); | |
MultiByteToWideChar (CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed); | |
return wstrTo; | |
} | |
#endif | |
static void delay(double sec) | |
{ | |
#if defined(CINDER_MAC) || defined(LINUX) | |
usleep(sec * 1000000); | |
#elif defined(CINDER_MSW) | |
Sleep(sec * 1000); | |
#endif | |
} | |
static void die(const char *format, ...) | |
{ | |
va_list args; | |
va_start(args, format); | |
vfprintf(stderr, format, args); | |
exit(1); | |
} | |
static int receive_bytes(PORTTYPE port, char *buf, int len) | |
{ | |
int count=0; | |
#if defined(CINDER_MAC) || defined(LINUX) | |
int r; | |
int retry=0; | |
//char buf[512]; | |
if (len > sizeof(buf) || len < 1) return -1; | |
// non-blocking read mode | |
fcntl(port, F_SETFL, fcntl(port, F_GETFL) | O_NONBLOCK); | |
while (count < len) { | |
r = read(port, buf + count, len - count); | |
//printf("read, r = %d\n", r); | |
if (r < 0 && errno != EAGAIN && errno != EINTR) return -1; | |
else if (r > 0) count += r; | |
else { | |
// no data available right now, must wait | |
fd_set fds; | |
struct timeval t; | |
FD_ZERO(&fds); | |
FD_SET(port, &fds); | |
t.tv_sec = 1; | |
t.tv_usec = 0; | |
r = select(port+1, &fds, NULL, NULL, &t); | |
//printf("select, r = %d\n", r); | |
if (r < 0) return -1; | |
if (r == 0) return count; // timeout | |
} | |
retry++; | |
if (retry > 1000) return -100; // no input | |
} | |
fcntl(port, F_SETFL, fcntl(port, F_GETFL) & ~O_NONBLOCK); | |
#elif defined(CINDER_MSW) | |
COMMTIMEOUTS timeout; | |
DWORD n; | |
BOOL r; | |
int waiting=0; | |
GetCommTimeouts(port, &timeout); | |
timeout.ReadIntervalTimeout = MAXDWORD; // non-blocking | |
timeout.ReadTotalTimeoutMultiplier = 0; | |
timeout.ReadTotalTimeoutConstant = 0; | |
SetCommTimeouts(port, &timeout); | |
while (count < len) { | |
r = ReadFile(port, buf + count, len - count, &n, NULL); | |
if (!r) die("read error\n"); | |
if (n > 0) count += n; | |
else { | |
if (waiting) break; // 1 sec timeout | |
timeout.ReadIntervalTimeout = MAXDWORD; | |
timeout.ReadTotalTimeoutMultiplier = MAXDWORD; | |
timeout.ReadTotalTimeoutConstant = 1000; | |
SetCommTimeouts(port, &timeout); | |
waiting = 1; | |
} | |
} | |
#endif | |
return count; | |
} | |
static int transmit_bytes(PORTTYPE port, const void *data, int len) | |
{ | |
#if defined(CINDER_MAC) || defined(LINUX) | |
return write(port, data, len); | |
#elif defined(CINDER_MSW) | |
DWORD n; | |
BOOL r; | |
r = WriteFile(port, data, len, &n, NULL); | |
if (!r) return 0; | |
return n; | |
#endif | |
} | |
static void close_port(PORTTYPE port) | |
{ | |
#if defined(CINDER_MAC) || defined(LINUX) | |
close(port); | |
#elif defined(CINDER_MSW) | |
CloseHandle(port); | |
#endif | |
} | |
// wait for the Arduino board to boot up, since opening | |
// the board raises DTR, which resets the processor. | |
// as soon as it properly responds, we know it's running | |
static void wait_online(PORTTYPE port) | |
{ | |
char buf[8]; | |
int r; | |
printf("waiting for board to be ready:\n"); | |
while (1) { | |
delay(0.1); | |
printf("."); | |
fflush(stdout); | |
buf[0] = 'x'; | |
r = transmit_bytes(port, buf, 1); | |
if (r != 1) die("unable to write, r = %d\n", r); | |
r = receive_bytes(port, buf, 4); | |
if (r == 4) break; // success, device online | |
} | |
printf("ok\n"); | |
} | |
//int main(int argc, char **argv) | |
//{ | |
// PORTTYPE fd; | |
// | |
// if (argc < 2) die("Usage: latency_test3 <comport>\n"); | |
// fd = open_port_and_set_baud_or_die(argv[1], BAUD); | |
// printf("port %s opened\n", argv[1]); | |
// | |
// wait_online(fd); | |
// do_test_100_times(fd, 1); | |
// do_test_100_times(fd, 2); | |
// do_test_100_times(fd, 12); | |
// do_test_100_times(fd, 30); | |
// do_test_100_times(fd, 62); | |
// do_test_100_times(fd, 71); | |
// do_test_100_times(fd, 128); | |
// do_test_100_times(fd, 500); | |
// do_test_100_times(fd, 1000); | |
// do_test_100_times(fd, 2000); | |
// do_test_100_times(fd, 4000); | |
// do_test_100_times(fd, 8000); | |
// | |
// close_port(fd); | |
// return 0; | |
//} | |
/**********************************/ | |
/* Serial Port Functions */ | |
/**********************************/ | |
static PORTTYPE open_port_and_set_baud_or_die(const char *name, long baud, bool *successfulConnection) | |
{ | |
PORTTYPE fd; | |
#if defined(CINDER_MAC) | |
struct termios tinfo; | |
fd = open(name, O_RDWR | O_NONBLOCK); | |
if (fd < 0) die("unable to open port %s\n", name); | |
if (tcgetattr(fd, &tinfo) < 0) die("unable to get serial parms\n"); | |
if (cfsetspeed(&tinfo, baud) < 0) die("error in cfsetspeed\n"); | |
tinfo.c_cflag |= CLOCAL; | |
if (tcsetattr(fd, TCSANOW, &tinfo) < 0) die("unable to set baud rate\n"); | |
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); | |
#elif defined(LINUX) | |
struct termios tinfo; | |
struct serial_struct kernel_serial_settings; | |
int r; | |
fd = open(name, O_RDWR); | |
if (fd < 0) die("unable to open port %s\n", name); | |
if (tcgetattr(fd, &tinfo) < 0) die("unable to get serial parms\n"); | |
if (cfsetspeed(&tinfo, baud) < 0) die("error in cfsetspeed\n"); | |
if (tcsetattr(fd, TCSANOW, &tinfo) < 0) die("unable to set baud rate\n"); | |
r = ioctl(fd, TIOCGSERIAL, &kernel_serial_settings); | |
if (r >= 0) { | |
kernel_serial_settings.flags |= ASYNC_LOW_LATENCY; | |
r = ioctl(fd, TIOCSSERIAL, &kernel_serial_settings); | |
if (r >= 0) printf("set linux low latency mode\n"); | |
} | |
#elif defined(CINDER_MSW) | |
COMMCONFIG cfg; | |
COMMTIMEOUTS timeout; | |
DWORD n; | |
char portname[256]; | |
int num; | |
if (sscanf(name, "COM%d", &num) == 1) { | |
sprintf(portname, "\\\\.\\COM%d", num); // Microsoft KB115831 | |
} else { | |
strncpy(portname, name, sizeof(portname)-1); | |
//portname[n-1] = 0; | |
portname[sizeof(portname)-1] = 0; | |
} | |
std::wstring lString = utf8_decode(std::string(portname)); | |
fd = CreateFile(lString.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL); | |
if (fd == INVALID_HANDLE_VALUE) { | |
//die("unable to open port %s\n", name); | |
std::cout << "unable to open port " << name << std::endl; | |
*successfulConnection = false; | |
return 0; | |
} | |
GetCommConfig(fd, &cfg, &n); | |
//cfg.dcb.BaudRate = baud; | |
cfg.dcb.BaudRate = 115200; | |
cfg.dcb.fBinary = TRUE; | |
cfg.dcb.fParity = FALSE; | |
cfg.dcb.fOutxCtsFlow = FALSE; | |
cfg.dcb.fOutxDsrFlow = FALSE; | |
cfg.dcb.fOutX = FALSE; | |
cfg.dcb.fInX = FALSE; | |
cfg.dcb.fErrorChar = FALSE; | |
cfg.dcb.fNull = FALSE; | |
cfg.dcb.fRtsControl = RTS_CONTROL_ENABLE; | |
cfg.dcb.fAbortOnError = FALSE; | |
cfg.dcb.ByteSize = 8; | |
cfg.dcb.Parity = NOPARITY; | |
cfg.dcb.StopBits = ONESTOPBIT; | |
cfg.dcb.fDtrControl = DTR_CONTROL_ENABLE; | |
SetCommConfig(fd, &cfg, n); | |
GetCommTimeouts(fd, &timeout); | |
timeout.ReadIntervalTimeout = 0; | |
timeout.ReadTotalTimeoutMultiplier = 0; | |
timeout.ReadTotalTimeoutConstant = 1000; | |
timeout.WriteTotalTimeoutConstant = 0; | |
timeout.WriteTotalTimeoutMultiplier = 0; | |
SetCommTimeouts(fd, &timeout); | |
#endif | |
*successfulConnection = true; | |
return fd; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment