Skip to content

Instantly share code, notes, and snippets.

@PlumCantaloupe
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save PlumCantaloupe/8939224 to your computer and use it in GitHub Desktop.
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.
/*****************
- 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