mac 下用python模拟串口
#! /usr/bin/env python #coding=utf-8 import pty import os import select import time def mkpty(): # master1, slave = pty.openpty() slaveName1 = os.ttyname(slave) master2, slave = pty.openpty() slaveName2 = os.ttyname(slave) print '\nslave device names: ', slaveName1, slaveName2 return master1, master2 if __name__ == "__main__": datasend = "jack.hong" global data master1, master2 = mkpty() bl = True while True: rl, wl, el = select.select([master1, master2], [], [], 60) for master in rl: data = "" if master==master1: data = os.read(master, 2048) print "read from master1 %d len data." % len(data), data os.write(master2, datasend) else: #if bl == True: data = os.read(master, 2048) print "read from master2 %d len data." % len(data), data os.write(master1, datasend) bl = False #time.sleep(3)
串口通信代码头文件
#pragma once #include <thread> #include <string> #include <stdio.h> #include <stdlib.h> #ifdef WIN32 #include <windows.h> #else #include <fcntl.h> #include <unistd.h> #include <termios.h> #include <sys/select.h> #include <sys/time.h> #include <sys/types.h> #include <errno.h> #include <sys/stat.h> #endif /* #include <winbase.h> #define NOPARITY 0 #define ODDPARITY 1 #define EVENPARITY 2 #define MARKPARITY 3 #define SPACEPARITY 4 #define ONESTOPBIT 0 #define ONE5STOPBITS 1 #define TWOSTOPBITS 2 */ struct SerialPortInfo{ std::string name; unsigned int baudRate{9600}; unsigned int parity{'e'}; unsigned int dataBits{7}; unsigned int stopBits{1}; }; class BaseCom { public: BaseCom(); virtual ~BaseCom(); public: bool Connect(const SerialPortInfo& portinfo); void Close(); bool IsOpen(); #ifdef WIN32 public: bool Connect(const std::string& portName, unsigned int baudRate = CBR_9600, unsigned int parity = NOPARITY, unsigned int dataBits = 8, unsigned int stopBits = ONESTOPBIT); bool Connect(int port, unsigned int baudRate = CBR_9600, unsigned int parity = NOPARITY, unsigned int dataBits = 8, unsigned int stopBits = ONESTOPBIT); protected: virtual bool OpenPort() = 0; void Init(); bool SetupPort(); void SetComPort(int port); bool SetState(int BaudRate, int ByteSize, int Parity, int StopBits); protected: volatile int _port; volatile HANDLE _com_handle; char _com_str[20]; DCB _dcb; COMMTIMEOUTS _co; #else public: bool Connect(const std::string& portName, unsigned int baudRate = 9600, unsigned int parity = 'N', unsigned int dataBits = 8, unsigned int stopBits = 1); protected: bool openLinuxPort(const SerialPortInfo& portinfo); bool setLinuxPortOpt(const SerialPortInfo& portinfo); protected: int _fdSerial{-1}; bool _isOpen{false}; #endif protected: SerialPortInfo _portinfo; }; //ͬ������ #ifdef WIN32 class SyncCom : public BaseCom { public: SyncCom(); int Read(char *buf, int buf_len); int SendData(const char* buf, int buf_len); int SendData(const char* buf); protected: virtual bool OpenPort(); }; #endif //�첽���� typedef void(*RecvDataCallBack)(const std::string& data, void* context); class ASynCom : public BaseCom { public: ASynCom(); virtual ~ASynCom(); int Read(char* buf, int buf_len); bool SendData(const char* buf, int buf_len); bool SendData(const std::string& buf); void Start(); void Stop(); void SetRecvDataCallBack(RecvDataCallBack callback, void* context); protected: static void OnProcRecvData(void* context); #ifdef WIN32 protected: virtual bool OpenPort(); protected: OVERLAPPED _ro, _wo; // �ص�I/O OVERLAPPED _wait_o; //WaitCommEvent use #else ssize_t readDataTty(int fd, char *rcv_buf, int TimeOut, int Len); ssize_t sendDataTty(int fd, const char *send_buf, int Len); void reInit(); #endif bool _isExit = false; std::thread* _workThread = NULL; RecvDataCallBack _recvDataCallback = NULL; void* _recvDataContext = NULL; }; 串口通信代码CPP文件
#include "SerialPort.h" #include <cassert> BaseCom::BaseCom() { #ifdef WIN32 Init(); #endif } BaseCom::~BaseCom() { Close(); } bool BaseCom::Connect(const SerialPortInfo& portinfo) { _portinfo = portinfo; #ifdef WIN32 return Connect(portinfo.name, portinfo.baudRate, portinfo.parity, portinfo.dataBits, portinfo.stopBits); #else bool result{false}; result = openLinuxPort(_portinfo); if(result) result = setLinuxPortOpt(_portinfo); return result; #endif } bool BaseCom::Connect(const std::string& portName, unsigned int baudRate, unsigned int parity, unsigned int dataBits, unsigned int stopBits) { #ifdef WIN32 int pos = portName.find("COM"); if (pos == std::string::npos){ printf("COM name is error. %s\n", portName.c_str()); return false; } std::string sport = portName.substr(pos + 3, portName.size()); int port = atoi(sport.c_str()); return Connect(port, baudRate, parity, dataBits, stopBits); #else _portinfo.name = portName; _portinfo.baudRate = baudRate; _portinfo.parity = parity; _portinfo.dataBits = dataBits; _portinfo.stopBits = stopBits; return Connect(_portinfo); #endif } #ifdef WIN32 bool BaseCom::Connect(int port, unsigned int baudRate, unsigned int parity, unsigned int dataBits, unsigned int stopBits) { if (port < 1 || port > 1024) return false; SetComPort(port); if (!OpenPort()) return false; if (!SetupPort()) return false; return SetState(baudRate, dataBits, parity, stopBits); } bool BaseCom::SetState(int BaudRate, int ByteSize, int Parity, int StopBits) { if (IsOpen()) { if (!GetCommState(_com_handle, &_dcb)) return false; _dcb.BaudRate = BaudRate; _dcb.ByteSize = ByteSize; _dcb.Parity = Parity; _dcb.StopBits = StopBits; return SetCommState(_com_handle, &_dcb) == TRUE; } return false; } void BaseCom::Init() { memset(_com_str, 0, 20); memset(&_co, 0, sizeof(_co)); memset(&_dcb, 0, sizeof(_dcb)); _dcb.DCBlength = sizeof(_dcb); _com_handle = INVALID_HANDLE_VALUE; } bool BaseCom::SetupPort() { if (!IsOpen()) return false; if (!SetupComm(_com_handle, 8192, 8192)) return false; if (!GetCommTimeouts(_com_handle, &_co)) return false; _co.ReadIntervalTimeout = 0xFFFFFFFF; _co.ReadTotalTimeoutMultiplier = 0; _co.ReadTotalTimeoutConstant = 0; _co.WriteTotalTimeoutMultiplier = 0; _co.WriteTotalTimeoutConstant = 2000; if (!SetCommTimeouts(_com_handle, &_co)) return false; if (!PurgeComm(_com_handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) return false; return true; } void BaseCom::SetComPort(int port) { char p[12]; _port = port; strcpy_s(_com_str, "\\\\.\\COM"); _ltoa_s(_port, p, 10); strcat_s(_com_str, p); } #else bool BaseCom::openLinuxPort(const SerialPortInfo& portinfo) { _fdSerial = open(portinfo.name.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); if (-1 == _fdSerial) { printf("Can't Open %s Serial Port \n", portinfo.name.c_str()); return(false); } else { printf("open %s .....\n", portinfo.name.c_str()); } if(fcntl(_fdSerial, F_SETFL, FNDELAY) < 0)//非阻塞,覆盖前面open的属性 { printf("set %s noblock failed\n", _portinfo.name.c_str()); } else{ printf("set %s noblock succ\n", _portinfo.name.c_str()); } /* int ifcntl = fcntl(_fdSerial, F_SETFL, 0); //阻塞 if (ifcntl < 0) { printf("fcntl %s failed, result is %d!\n", portinfo.name.c_str(), ifcntl); } else { printf("fcntl %s succ, result is %d!\n", portinfo.name.c_str(), ifcntl); } */ if (isatty(STDIN_FILENO) == 0) { printf("%s is not a terminal device\n", portinfo.name.c_str()); } else { printf("%s is a tty success!\n", portinfo.name.c_str()); } printf("%s is open fdSerial = %d\n", portinfo.name.c_str(), _fdSerial); return true; } bool BaseCom::setLinuxPortOpt(const SerialPortInfo& portinfo) { int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300, B38400, B19200, B9600, B4800, B2400, B1200, B300, }; int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400, 1200, 300, }; struct termios newtio, oldtio; if (tcgetattr(_fdSerial, &oldtio) != 0) { printf("getSerial %s attr fail", portinfo.name.c_str()); return false; } bzero(&newtio, sizeof(newtio)); newtio.c_cflag |= CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; switch (portinfo.dataBits) { case 5: newtio.c_cflag |= CS5; break; case 6: newtio.c_cflag |= CS6; break; case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; default: printf("%s bits is not 7 or 8, please check", portinfo.name.c_str()); newtio.c_cflag |= CS8; break; } switch (portinfo.parity) { case 'o': case 'O': //奇校验 newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; newtio.c_iflag |= (INPCK); break; case 'e': case 'E': //偶校验 newtio.c_iflag |= (INPCK | ISTRIP); newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD; break; case 'n': case 'N': //无校验 newtio.c_cflag &= ~PARENB; newtio.c_iflag &= ~INPCK; break; case 's': case 'S': newtio.c_cflag &= ~PARENB; //清除校验位 newtio.c_cflag &= ~CSTOPB; //?????????????? newtio.c_iflag |= INPCK; //disable pairty checking break; default: printf("%s Unsupported parity.\n", _portinfo.name.c_str()); return false; } for(auto i = 0; i < sizeof(speed_arr) / sizeof(int); i++) { if(portinfo.baudRate == name_arr[i]) { tcflush(_fdSerial, TCIOFLUSH); cfsetispeed(&newtio, speed_arr[i]); cfsetospeed(&newtio, speed_arr[i]); /* TCSANOW:立即执行而不等待数据发送或者接受完成。 TCSADRAIN:等待所有数据传递完成后执行。 TCSAFLUSH:Flush input and output buffers and make the change */ if ((tcsetattr(_fdSerial, TCSANOW, &newtio)) != 0) { printf("%s set error", portinfo.name.c_str()); return false; } tcflush(_fdSerial, TCIOFLUSH); break; } } if (portinfo.stopBits == 1) { newtio.c_cflag &= ~CSTOPB; } else if(portinfo.stopBits == 2) { newtio.c_cflag |= CSTOPB; } else { printf("%s Unsupported stopbits error", portinfo.name.c_str()); return false; } newtio.c_cflag |= IXON | IXOFF | IXANY; newtio.c_cflag &= ~CSIZE; newtio.c_oflag &= ~OPOST; newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 0; if( tcflush(_fdSerial, TCIOFLUSH) == -1) { printf(" %s tcflush failed\n", portinfo.name.c_str()); } printf("%s set done!\n", portinfo.name.c_str()); _isOpen = true; return true; } #endif void BaseCom::Close() { #ifdef WIN32 if (IsOpen()) { CloseHandle(_com_handle); _com_handle = INVALID_HANDLE_VALUE; } #else if (_fdSerial > 0) close(_fdSerial); #endif } bool BaseCom::IsOpen() { #ifdef WIN32 return _com_handle != INVALID_HANDLE_VALUE; #else return _isOpen; #endif } #ifdef WIN32 SyncCom::SyncCom() { } bool SyncCom::OpenPort() { if (IsOpen()) Close(); _com_handle = CreateFileA( _com_str, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); return IsOpen(); } int SyncCom::Read(char* buf, int buf_len) { if (!IsOpen()) return 0; buf[0] = '\0'; COMSTAT stat; DWORD error; if (ClearCommError(_com_handle, &error, &stat) && error > 0) { PurgeComm(_com_handle, PURGE_RXABORT | PURGE_RXCLEAR); return 0; } unsigned long r_len = 0; buf_len = min(buf_len - 1, (int)stat.cbInQue); if (!ReadFile(_com_handle, buf, buf_len, &r_len, NULL)) r_len = 0; buf[r_len] = '\0'; return r_len; } int SyncCom::SendData(const char* buf, int buf_len) { if (!IsOpen() || !buf) return 0; DWORD error; if (ClearCommError(_com_handle, &error, NULL) && error > 0) PurgeComm(_com_handle, PURGE_TXABORT | PURGE_TXCLEAR); unsigned long w_len = 0; if (!WriteFile(_com_handle, buf, buf_len, &w_len, NULL)) w_len = 0; return w_len; } int SyncCom::SendData(const char* buf) { return SendData(buf, strlen(buf)); } #endif ASynCom::ASynCom() { #ifdef WIN32 memset(&_ro, 0, sizeof(_ro)); memset(&_wo, 0, sizeof(_wo)); _ro.hEvent = CreateEvent(NULL, true, false, NULL); assert(_ro.hEvent != INVALID_HANDLE_VALUE); _wo.hEvent = CreateEvent(NULL, true, false, NULL); assert(_wo.hEvent != INVALID_HANDLE_VALUE); memset(&_wait_o, 0, sizeof(_wait_o)); _wait_o.hEvent = CreateEvent(NULL, true, false, NULL); assert(_wait_o.hEvent != INVALID_HANDLE_VALUE); #endif } ASynCom::~ASynCom() { Close(); #ifdef WIN32 if (_ro.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_ro.hEvent); if (_wo.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_wo.hEvent); if (_wait_o.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_wait_o.hEvent); #endif } #ifdef WIN32 bool ASynCom::OpenPort() { #ifdef WIN32 if (IsOpen()) Close(); _com_handle = CreateFileA( _com_str, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); return IsOpen(); #endif return false; } #endif int ASynCom::Read(char* buf, int buf_len) { #ifdef WIN32 if (!IsOpen()) return 0; buf[0] = '\0'; COMSTAT stat; DWORD error; if (ClearCommError(_com_handle, &error, &stat) && error > 0) { PurgeComm(_com_handle, PURGE_RXABORT | PURGE_RXCLEAR); return 0; } if (!stat.cbInQue) return 0; unsigned long r_len = 0; buf_len = min((int)(buf_len - 1), (int)stat.cbInQue); if (!ReadFile(_com_handle, buf, buf_len, &r_len, &_ro)) { if (GetLastError() == ERROR_IO_PENDING) { if (!GetOverlappedResult(_com_handle, &_ro, &r_len, false)) { if (GetLastError() != ERROR_IO_INCOMPLETE) r_len = 0; } } else r_len = 0; } buf[r_len] = '\0'; return r_len; #else return (int)readDataTty(_fdSerial, buf, 20, buf_len); //100 没有数据就等待100毫秒 #endif } bool ASynCom::SendData(const char* buf, int buf_len) { #ifdef WIN32 if (!IsOpen()) return false; DWORD error; if (ClearCommError(_com_handle, &error, NULL) && error > 0) PurgeComm(_com_handle, PURGE_TXABORT | PURGE_TXCLEAR); unsigned long w_len = 0, o_len = 0; if (!WriteFile(_com_handle, buf, buf_len, &w_len, &_wo)){ if (GetLastError() != ERROR_IO_PENDING) return false; } return true; #else ssize_t sendlen = sendDataTty(_fdSerial, buf, buf_len); return sendlen != -1; #endif } bool ASynCom::SendData(const std::string& buf) { #ifdef WIN32 return SendData(buf.c_str(), buf.size()); #else ssize_t sendlen = sendDataTty(_fdSerial, buf.c_str(), (int)buf.size()); return sendlen != -1; #endif } void ASynCom::SetRecvDataCallBack(RecvDataCallBack callback, void* context) { _recvDataCallback = callback; _recvDataContext = context; } void ASynCom::Start() { _isExit = false; _workThread = new std::thread(ASynCom::OnProcRecvData, this); } void ASynCom::Stop() { if (!_isExit && _workThread != NULL && _workThread->joinable()){ _isExit = true; #ifdef WIN32 TerminateThread(_workThread->native_handle(), 1); #endif if (_workThread->joinable()){ _workThread->join(); } delete _workThread; _workThread = NULL; } } void ASynCom::OnProcRecvData(void* context) { ASynCom *pcom = (ASynCom *)context; #ifdef WIN32 if (!SetCommMask(pcom->_com_handle, EV_RXCHAR | EV_ERR)) return; COMSTAT stat; DWORD error; DWORD mask; DWORD length; while (!pcom->_isExit){ if (!WaitCommEvent(pcom->_com_handle, &mask, &pcom->_wait_o)) { if (GetLastError() == ERROR_IO_PENDING) { GetOverlappedResult(pcom->_com_handle, &pcom->_wait_o, &length, true); } } if (mask & EV_ERR) ClearCommError(pcom->_com_handle, &error, &stat); if (mask & EV_RXCHAR) { ClearCommError(pcom->_com_handle, &error, &stat); if (stat.cbInQue > 0){ char buf[1024] = { 0 }; int buflen = sizeof(buf); int r = pcom->Read(buf, buflen); if (r > 0){ std::string msg; msg.assign(buf, r); if (pcom->_recvDataCallback != NULL && !msg.empty()){ pcom->_recvDataCallback(msg, pcom->_recvDataContext); } else{ printf("SerialPort recv data:%s\n", msg.c_str()); } } } } } #else char buf[1024] = { 0 }; int ibuflen = sizeof(buf)/ sizeof(char); while (!pcom->_isExit) { memset(buf,0,ibuflen); int r = pcom->Read(buf, ibuflen); if (r > 0) { std::string msg; msg.assign(buf, r); if (pcom->_recvDataCallback != NULL && !msg.empty()) { pcom->_recvDataCallback(msg, pcom->_recvDataContext); } else{ if(!msg.empty()) printf("%s recv data:%s\n", pcom->_portinfo.name.c_str(), msg.c_str()); } } } #endif } #ifndef WIN32 ssize_t ASynCom::readDataTty(int fd, char *rcv_buf, int TimeOut, int Len) { if(!_isOpen) { if(Connect(_portinfo)) { fd = _fdSerial; } else { return -1; } } int retval; fd_set rfds; struct timeval tv; ssize_t ret = 0, pos = 0; tv.tv_sec = TimeOut / 1000; //set the rcv wait time tv.tv_usec = TimeOut % 1000 * 1000; //100000us = 0.1s FD_ZERO(&rfds); FD_SET(fd, &rfds); while (FD_ISSET(fd, &rfds)) { FD_ZERO(&rfds); FD_SET(fd, &rfds); retval = select(fd + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { break; } else if (retval) { ret = read(fd, rcv_buf + pos, Len-pos); if (-1 == ret) { close(_fdSerial); _isOpen = false; break; } if (ret == 0) { break; } pos += ret; if (Len <= pos) { break; } FD_ZERO(&rfds); FD_SET(fd,&rfds); retval = select(fd+1,&rfds,NULL,NULL,&tv); if(retval <= 0) { break; } } else { //printf("%s read data time out", _portinfo.name.c_str()); break; } } return pos; } ssize_t ASynCom::sendDataTty(int fd, const char *send_buf, int Len) { ssize_t ret; if(!_isOpen) { if(Connect(_portinfo)) fd = _fdSerial; else return -1; } std::string sbuf; sbuf.assign(send_buf, Len); int pos = 0, ilen = (int)sbuf.size(); while(pos != ilen) { ret = write(fd, sbuf.c_str()+ pos, ilen-pos); if (ret == -1) { //close(_fdSerial); //_isOpen = false; printf("%s write %s error \n", _portinfo.name.c_str(), sbuf.c_str()); return -1; } pos += ret; } return pos; } #endif
测试程序代码
//
// main.cpp
// SerialPort
//
// Created by soft on 6/3/2017.
// Copyright © 2017 Reginald. All rights reserved.
//
#include <iostream>
#include "SerialPort.h"
static int com1count =0;
static int com2count =0;
static void senddata(ASynCom* com,conststd::string& msg,int* cont)
{
std::string temp;
while (true) {
(*cont)++;
temp = msg + " times is " +std::to_string(*cont);
com->SendData(temp);
temp = "";
usleep(800);
}
}
void dohandleReadData(conststd::string& data,void* context)
{
SerialPortInfo* own =static_cast<SerialPortInfo*>(context);
printf("[%s] %s", own->name.c_str(), data.c_str());
}
int main(int argc,constchar * argv[]) {
SerialPortInfo Com1info;
SerialPortInfo Com2info;
Com1info.name ="/dev/ttys029";
Com2info.name ="/dev/ttys030";
ASynCom Com1;
ASynCom Com2;
Com1.SetRecvDataCallBack(dohandleReadData, &Com1info);
Com2.SetRecvDataCallBack(dohandleReadData, &Com2info);
if (Com1.Connect(Com1info) && Com2.Connect(Com2info))
{
Com1.Start();
Com2.Start();
std::thread tcom1(senddata, &Com1,"com1_AAAAA", &com1count);
std::thread tcom2(senddata, &Com2,"com2_BBBBB", &com2count);
if(tcom1.joinable())
tcom1.join();
if(tcom2.joinable())
tcom2.join();
}
return0;
}
