其實前陣子都在幫人做PC端控制PLC與接收一些電壓電流參數的軟體,只是想到還沒測試過Arduino的Serial Library,所以前幾天有空就先做Linux上的,其實Windows取得COM Port也不難API Call一下就好了,主要想幫Arduino與PIC寫類似一些賣Hexapod套件附的動作編輯軟體,除了動作編輯與除存我還要用OpenGL寫個簡單的動畫模擬在螢幕中央以及支援Webcam(前述部份很簡單),還要有正逆向運動學的計算以及一些校準(這部份不熟)。
要讓PC端連接一些設備可以用UART或是USB的方式傳輸,只要搞懂Spec文件就可以了,Linux下通過Serial Port可以跟Arduino、PIC等等的單晶片溝通,Linux下可以用linudev取得可用的Serial Port設備的Node Path,然後就可以直接當作檔案Open與Close,只是跟讀寫一般純文字文件不同的是Open之後必須要設定Baudrate與Parity,然後用Write與Read 就可以接收資料了,而Arduino的Serial Port接收資料必須要Delay 1ms左右的時間,否則速度太快會接收錯誤資料。
說明:PC端的MAX與Arduino的MAX都是表示Servo的數量,比如設定為12時就會有12條Slider(建議在自己Layout)可以控制12個Servo,然後PC端與Arduino的MAX數量要一樣。
wxarduino.h
#ifndef __WX_ARDUINO__
#define __WX_ARDUINO__
#include <wx/wx.h>
#include <wx/spinctrl.h>
#include <libudev.h>
#include "serialport.h"
#include "connectargsdlg.h"
class App:public wxApp
{
public:
bool OnInit();
};
class Frame:public wxFrame
{
public:
Frame(const wxString&);
void OnSendData(wxTimerEvent&);
void OnConnectDevice(wxCommandEvent&);
void OnReleaseDevice(wxCommandEvent&);
void OnExit(wxCommandEvent&);
private:
enum{
ID_SEND_DATA = 100,
ID_CONNECT_DEVICE,
ID_RELEASE_DEVICE
};
static const unsigned int MAX = 2;
wxMenu *device_path;
wxTimer timer;
SerialPort serial;
wxSlider *servos[MAX];
wxBoxSizer *box[MAX];
wxStaticText *servos_text[MAX];
unsigned char data[MAX];
DECLARE_EVENT_TABLE()
};
#endif
wxarduino.cpp
#include "wxarduino.h"
IMPLEMENT_APP(App)
DECLARE_APP(App)
bool App::OnInit()
{
wxFrame *frame = new Frame(wxT("wxArduino"));
frame->Show(true);
return true;
}
BEGIN_EVENT_TABLE(Frame,wxFrame)
EVT_MENU(wxID_EXIT,Frame::OnExit)
EVT_MENU(ID_CONNECT_DEVICE,Frame::OnConnectDevice)
EVT_MENU(ID_RELEASE_DEVICE,Frame::OnReleaseDevice)
EVT_TIMER(ID_SEND_DATA,Frame::OnSendData)
END_EVENT_TABLE()
Frame::Frame(const wxString &title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxDefaultSize,wxMINIMIZE_BOX | wxCLOSE_BOX | wxCAPTION | wxSYSTEM_MENU),timer(this,ID_SEND_DATA)
{
timer.Stop();
wxMenu *file = new wxMenu;
file->Append(wxID_EXIT,wxT("E&xit\tAlt-e"),wxT("Exit"));
wxMenu *tools = new wxMenu;
tools->Append(ID_CONNECT_DEVICE,wxT("C&onnect Device\tAlt-c"),wxT("Connect Device"));
tools->Append(ID_RELEASE_DEVICE,wxT("R&elease Device\tAlt-r"),wxT("Release Device"));
wxMenuBar *bar = new wxMenuBar;
bar->Append(file,wxT("File"));
bar->Append(tools,wxT("Tools"));
SetMenuBar(bar);
wxBoxSizer *top = new wxBoxSizer(wxVERTICAL);
this->SetSizer(top);
for(int i = 0;i < MAX;++i){
box[i] = new wxBoxSizer(wxHORIZONTAL);
top->Add(box[i],0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);
wxString text;
text.Printf("Servo %d:",i + 1);
servos_text[i] = new wxStaticText(this,wxID_STATIC,text,wxDefaultPosition,wxDefaultSize,0);
box[i]->Add(servos_text[i],0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);
servos[i] = new wxSlider(this,wxID_ANY,90,0,180,wxDefaultPosition,wxSize(200,-1),wxSL_HORIZONTAL | wxSL_AUTOTICKS | wxSL_LABELS);
box[i]->Add(servos[i],0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);
}
CreateStatusBar(2);
SetStatusText(wxT("wxArduino"));
top->Fit(this);
top->SetSizeHints(this);
}
void Frame::OnSendData(wxTimerEvent &event)
{
for(int i = 0;i < MAX;++i){
data[i] = servos[i]->GetValue();
}
serial.Write(data,MAX);
}
void Frame::OnConnectDevice(wxCommandEvent &event)
{
ConnectArgsDialog dlg(this,wxID_ANY,wxT("Connect"),wxDefaultPosition,wxDefaultSize,wxDEFAULT_DIALOG_STYLE);
if(dlg.ShowModal() == wxID_OK){
serial.Open(dlg.GetDevicePath().mb_str());
serial.SetBaudRate(wxAtoi(dlg.GetBaudRate()));
serial.SetParity(8,1,'N');
timer.Start(30);
}
}
void Frame::OnReleaseDevice(wxCommandEvent &event)
{
serial.Close();
timer.Stop();
}
void Frame::OnExit(wxCommandEvent &event)
{
timer.Stop();
Close();
}
connectargsalg.h
#ifndef __CONNECT_ARGS_DIALOG__
#define __CONNECT_ARGS_DIALOG__
#include <wx/wx.h>
#include <libudev.h>
class ConnectArgsDialog:public wxDialog
{
public:
ConnectArgsDialog();
ConnectArgsDialog(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxString &caption = wxT("Connect Args"),
const wxPoint &pos = wxDefaultPosition,
const wxSize &size = wxDefaultSize,
long style = wxCAPTION | wxRESIZE_BORDER | wxSYSTEM_MENU);
bool Create(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxString &caption = wxT("Connect Args"),
const wxPoint &pos = wxDefaultPosition,
const wxSize &size = wxDefaultSize,
long style = wxCAPTION | wxRESIZE_BORDER | wxSYSTEM_MENU);
void Init();
void CreateControls();
bool TransferDataToWindow();
bool TransferDataFromWindow();
wxString GetDevicePath(){return device_path->GetString(device_path->GetCurrentSelection());}
wxString GetBaudRate(){return baud_rate->GetString(baud_rate->GetCurrentSelection());}
private:
wxChoice *device_path;
wxChoice *baud_rate;
DECLARE_CLASS(ConnectArgsDialog)
};
#endif
connectargsdlg.cpp
#include "connectargsdlg.h"
IMPLEMENT_CLASS(ConnectArgsDialog,wxDialog)
ConnectArgsDialog::ConnectArgsDialog()
{
Init();
}
ConnectArgsDialog::ConnectArgsDialog(wxWindow *parent,wxWindowID id,const wxString &caption,const wxPoint &pos,const wxSize &size,long style)
{
Create(parent,id,caption,pos,size,style);
Init();
}
inline void ConnectArgsDialog::Init()
{
}
bool ConnectArgsDialog::Create(wxWindow *parent,wxWindowID id,const wxString &caption,const wxPoint &pos,const wxSize &size,long style)
{
SetExtraStyle(wxWS_EX_BLOCK_EVENTS | wxDIALOG_EX_CONTEXTHELP);
if(!wxDialog::Create(parent,id,caption,pos,size,style)){
return false;
}
CreateControls();
GetSizer()->Fit(this);
GetSizer()->SetSizeHints(this);
Center();
return true;
}
void ConnectArgsDialog::CreateControls()
{
wxBoxSizer *top = new wxBoxSizer(wxVERTICAL);
this->SetSizer(top);
wxBoxSizer *box = new wxBoxSizer(wxHORIZONTAL);
top->Add(box,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);
wxArrayString paths;
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices,*dev_list_entry;
struct udev_device *dev;
udev = udev_new();
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate,"tty");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry,devices){
const char *path;
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev,path);
paths.Add(wxT(udev_device_get_devnode(dev)));
dev = udev_device_get_parent_with_subsystem_devtype(dev,"usb","usb_device");
if(!dev){
paths.RemoveAt(paths.GetCount() - 1);
break;
}
udev_device_unref(dev);
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
device_path = new wxChoice(this,wxID_ANY,wxDefaultPosition,wxDefaultSize,paths);
box->Add(device_path,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);
wxArrayString speed;
speed.Add(wxT("300"));
speed.Add(wxT("1200"));
speed.Add(wxT("2400"));
speed.Add(wxT("4800"));
speed.Add(wxT("9600"));
speed.Add(wxT("19200"));
speed.Add(wxT("38400"));
baud_rate = new wxChoice(this,wxID_ANY,wxDefaultPosition,wxDefaultSize,speed);
box->Add(baud_rate,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);
wxBoxSizer *ResetOkCancelBox = new wxBoxSizer(wxHORIZONTAL);
top->Add(ResetOkCancelBox,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);
wxButton *ok = new wxButton(this,wxID_OK,wxT("&Ok"),wxDefaultPosition,wxDefaultSize,0);
ResetOkCancelBox->Add(ok,0,wxALIGN_CENTER_VERTICAL | wxALL,5);
wxButton *cancel = new wxButton(this,wxID_CANCEL,wxT("&Cancel"),wxDefaultPosition,wxDefaultSize,0);
ResetOkCancelBox->Add(cancel,0,wxALIGN_CENTER_VERTICAL | wxALL,5);
}
bool ConnectArgsDialog::TransferDataToWindow()
{
return true;
}
bool ConnectArgsDialog::TransferDataFromWindow()
{
return true;
}
serialport.h
#ifndef __SERIAL_PORT__
#define __SERIAL_PORT__
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <error.h>
class SerialPort
{
public:
SerialPort();
SerialPort(const char*,int,int,int,int);
~SerialPort();
bool Open(const char*);
bool SetBaudRate(int);
bool SetParity(int,int,int);
void Close();
void Write(unsigned char*,int);
void Read(unsigned char*,int);
private:
const char *path;
int device;
int baud_rate;
int databits;
int stopbits;
int parity;
};
#endif
serialport.cpp
#include "serialport.h"
SerialPort::SerialPort()
{
}
SerialPort::SerialPort(const char *path = "",int baud_rate = 9600,int databits = 8,int stopbits = 1,int parity = 'N')
{
this->path = path;
this->baud_rate = baud_rate;
this->databits = databits;
this->stopbits = stopbits;
this->parity = parity;
}
SerialPort::~SerialPort()
{
Close();
}
bool SerialPort::Open(const char *path = "")
{
if(device){
close(device);
}
device = open(path,O_RDWR);
if(device == -1){
return false;
}
return true;
}
bool SerialPort::SetBaudRate(int baud_rate = 9600)
{
const int speed_arr[] = {B38400,B19200,B9600,B4800,B2400,B1200,B300};
const int name_arr[] = {38400,19200,9600,4800,2400,1200,300};
struct termios options;
tcgetattr(device,&options);
for(int i = 0; i < sizeof(speed_arr) / sizeof(int);++i){
if(baud_rate == name_arr[i]){
tcflush(device,TCIOFLUSH);
cfsetispeed(&options,speed_arr[i]);
cfsetospeed(&options,speed_arr[i]);
tcsetattr(device,TCSANOW,&options);
}
if(tcflush(device,TCIOFLUSH) != 0){
return false;
}
}
return true;
}
bool SerialPort::SetParity(int databits = 8,int stopbits = 1,int parity = 'N')
{
struct termios options;
if(tcgetattr(device,&options) != 0){
return false;
}
options.c_cflag &= ~CSIZE;
switch(databits)
{
case 7:
{
options.c_cflag |= CS7;
break;
}
case 8:
{
options.c_cflag |= CS8;
break;
}
default:
{
return false;
}
}
switch(parity)
{
case 'n':case 'N':
{
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
break;
}
case 'o':case 'O':
{
options.c_cflag |= (PARODD | PARENB);
options.c_iflag |= INPCK;
break;
}
case 'e':case 'E':
{
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_iflag |= INPCK;
break;
}
case 's':case 'S':
{
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
}
default:
{
return false;
}
}
switch(stopbits)
{
case 1:
{
options.c_cflag &= ~CSTOPB;
break;
}
case 2:
{
options.c_cflag |= CSTOPB;
break;
}
default:
{
return false;
}
}
if(parity != 'n' || parity != 'N'){
options.c_iflag |= INPCK;
}
options.c_cc[VTIME] = 150;
options.c_cc[VMIN] = 0;
tcflush(device,TCIFLUSH);
if(tcsetattr(device,TCSANOW,&options) != 0){
return false;
}
return true;
}
void SerialPort::Close()
{
if(device){
close(device);
}
}
void SerialPort::Write(unsigned char *data,int length)
{
write(device,data,length);
}
void SerialPort::Read(unsigned char *data,int length)
{
read(device,data,length);
}
g++ -o2 -o wxArduino wxarduino.cpp connectargsdlg.cpp serialport.cpp -ludev `wx-config --cxxflags --libs`
servo.pde
#include <Servo.h>
const unsigned int MAX = 2;
Servo servos[MAX];
void setup() {
Serial.begin(9600);
for(int i = 0;i < MAX;++i){
servos[i].attach(i + 3);
servos[i].write(90);
}
}
void loop() {
for(int i = 0;Serial.available() > 0;++i){
delay(1); //速度小於1ms接收資料會錯誤
int angle = Serial.read();
servos[i].write(angle);
}
}
沒有留言:
張貼留言