小R科技-WIFI机器人网·机器人创意工作室

标题: arduino下位机通信协议(完全兼容论坛的上位机程序) [打印本页]

作者: jackzeng    时间: 2012-7-17 15:49
标题: arduino下位机通信协议(完全兼容论坛的上位机程序)
本帖最后由 jackzeng 于 2012-7-17 15:57 编辑

arduino专区的人气太差了,所以发布到这里,请谅解!
前段论坛有人说要发布arduino的控制协议,我是日盼夜盼盼了一个月也没见有人发布。求人不如求己,仿照liuviking的下位机程序,自己写了个arduino的通信协议。经测试已经能完全和论坛发布的上位机程序兼容,只是我还没解决单片机数据回传到电脑的问题,所以雷达的功能还未能实现,希望有解决方法的朋友能和我分享。本人是英语专业的,只能把程序当成英语来看,没系统学习过单片机知识,所以程序中不可避免有漏洞或者缺点,还请各位指正完善。此外,我还把论坛里的单字符控制上位机程序的视频获取部分换成了mjpeg流的方式,避免snapshot频繁向路由发送请求,如果需要此程序的朋友留下qq号码,我传给你。此外感谢liuviking同学的耐心解释和帮助!

#include <Servo.h>
#define BAUDRATE 9600
//DEFINE MOTO PINS
#define ENA  5    //MOTO_A PMW
#define IN1  3
#define IN2  4
#define ENB  6    //MOTO_B PMW
#define IN3  7
#define IN4  8
//DEFINE SERVO
Servo servo_X;
Servo servo_Y;
//CAR MOVEMENTS
void carGoFwd()
{
  digitalWrite(IN1,HIGH);
  digitalWrite(IN2,LOW);
  digitalWrite(IN3,HIGH);
  digitalWrite(IN4,LOW);
  Serial.print("forward");
  delay(5);
}
void carGoBwd()
{
  digitalWrite(IN1,LOW);
  digitalWrite(IN2,HIGH);
  digitalWrite(IN3,LOW);
  digitalWrite(IN4,HIGH);
  Serial.print("Backward");
  delay(5);
}
void carTurnL()
{
  digitalWrite(IN1,HIGH);
  digitalWrite(IN2,LOW);
  digitalWrite(IN3,LOW);
  digitalWrite(IN4,HIGH);
  Serial.print("TurnL");
  delay(5);
}
void carTurnR()
{
  digitalWrite(IN1,LOW);
  digitalWrite(IN2,HIGH);
  digitalWrite(IN3,HIGH);
  digitalWrite(IN4,LOW);
  Serial.print("TurnR");
  delay(5);
}
void carStop()
{
  digitalWrite(IN1,LOW);
  digitalWrite(IN2,LOW);
  digitalWrite(IN3,LOW);
  digitalWrite(IN4,LOW);
  Serial.print("carStop");
  delay(5);
}
//CAR SPEED
void CHNSpeed(int wheelDIR, int wheelSpeed)
{
  int pwm = 100+(wheelSpeed+1)*15;
  if(wheelDIR == 0X01) //LEFT WHEEL
  {
    analogWrite(ENA,pwm);
  }
  else if(wheelDIR == 0X02) //RIGHT WHEEL
  {
    analogWrite(ENB,pwm);
  }
}
//SERVO TURN
void servoSet(int servoNum, int pos)
{
  if(pos > 180) pos = 160;
  else if(pos < 0) pos = 0;
  switch(servoNum)
  {
    case 0X01:
    servo_X.write(pos);
    Serial.print("X");
    Serial.print(pos);
    break;
    case 0X02:
    servo_Y.write(pos);
    Serial.print("Y");
    Serial.print(pos);
    break;
  }
}
void setup()
{
  Serial.begin(BAUDRATE);//SERIAL INIT
  servo_X.attach(10,800,2300);//DEFINE SERVO PINS
  servo_Y.attach(11,1200,2000);
  servo_X.writeMicroseconds(1550);//RESET SERVO POS TO 90 DEGREE
  servo_Y.writeMicroseconds(1600);
  analogWrite(ENA,150);//INIT MOTOSPEED
  analogWrite(ENB,150);
  pinMode(IN1, OUTPUT);//DEFINE MOTO PINS MODE
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
}
//UART PROTOCOL///////////////////////
#define UART_FLAG 0XFF
//Moto////////////////////////////////
//PROTO: FLAG DEV DIRECTION EMPTY FLAG
#define MOTO 0X00
#define FORWARD 0X01 //MOTO COMMAND
#define BACKWARD 0X02
#define TURNLEFT 0X03
#define TURNRIGHT 0X04
#define CARSTOP 0X05
//Servo///////////////////////////////
//PROTO: FLAG DEV SERVONUM POS FLAG
#define SERVO 0X01
//Moto Speed//////////////////////////
//PROTO: FLAG DEV MOTOSIDE SPEED FLAG
#define MOTOSPEED 0X02
//////////////////////////////////////
int n = 1;
int flagCount = 0;
int tempData = 0;
int UARTReveived = 0;
int rxData[5];
void loop()
{
  if(Serial.available())
  {
    tempData = Serial.read();
    delay(3);
    if(tempData == UART_FLAG && flagCount < 2)
    {
      rxData[0] = tempData;
      flagCount++;
    }
    else
    {
      rxData[n] = tempData;
      n++;
    }
    if(flagCount == 2)
    {
      rxData[4] == UART_FLAG;
      UARTReveived = 1;
      n = 1;
      flagCount = 0;
      tempData = 0;
      Serial.flush();
    }
  }
  if(UARTReveived == 1)
  {
    if(rxData[1] == MOTO)
    {
       switch(rxData[2])
       {
         case FORWARD:
         carGoFwd();
         break;
         case BACKWARD:
         carGoBwd();
         break;
         case TURNLEFT:
         carTurnL();
         break;
         case TURNRIGHT:
         carTurnR();
         break;
         case CARSTOP:
         carStop();
         break;
       }
       UARTReveived = 0;
    }
    else if(rxData[1] == SERVO)
    {
      servoSet(rxData[2], rxData[3]);
      UARTReveived = 0;
    }
    else if(rxData[1] == MOTOSPEED)
    {
      CHNSpeed(rxData[2], rxData[3]);
      UARTReveived = 0;
    }
  }
}

解释:
1、我的小车是2舵机的,舵机的角度和arduino舵机库的频宽不同,所以我在setup()里面初始化舵机时做了调整,如果没有这个需要的话请删掉!如果遇到舵机角度范围的问题(比如说转到170°+会卡住),可以参考我的方法解决!我稍后再放出调试舵机的下位机程序。
2、我的小车是用4个6V370减速电机,同一边的两个电机串联,开始时用降压模块降到6V供电,但是发现降压模块的输出功率不够,只好直接12V供电,所以初始化时用pwm把电压调低。如果没这个需要求删除相关语句。调速的时候我把pwm的下限设在100,怕太低了电机停转(这样做有个缺点就是调速不是很明显),如果电机参数不同请更改。
3、调试的时候再每个动作后面加了一个Serial.print,如果不需要调试的话请删掉!
3、arduino的ide不能输入中文,所以注释都是用英文的,看不懂的同学我稍后再改成中文。


作者: liujajia    时间: 2012-7-17 18:40
有做arduino小车的打算,先顶一下
作者: liuviking    时间: 2012-7-17 20:28
很不错!肯动手才能从玩中学到东西。
作者: ARENUS    时间: 2012-9-10 21:44
楼主的小车是怎么也wifi通信的?求指点
作者: 东爱之都    时间: 2012-9-11 20:59
小白虽然看不懂,,,,但是还是顶一下。。。。
作者: jackzeng    时间: 2012-9-12 13:05
ARENUS 发表于 2012-9-10 21:44
楼主的小车是怎么也wifi通信的?求指点

路由的ttl口连接arduino的rx和tx和gnd就可以了,如果是用群主的wifi模块就可以直接通信了,如果是rg100或者db120可能要加个电平转换模块
作者: questfish    时间: 2012-9-18 16:14
用楼主的程序 路由的ttl口连接arduino的rx和tx和gnd 没有接收到数据  求解 求解
作者: jackzeng    时间: 2012-9-23 12:54
questfish 发表于 2012-9-18 16:14
用楼主的程序 路由的ttl口连接arduino的rx和tx和gnd 没有接收到数据  求解 求解

arduino的5VTTL和路由的3.3VTTL理论上可以直接通信。但理论是理论,我试过3块arduino板和2个路由,都不能直接通信。淘宝上有现成的tt转换模块(这个最简单,15元搞定,接线简单),有电路基础的话也可以用三极管自己焊接或者用光耦隔离转换。
作者: jackzeng    时间: 2012-9-23 13:05
还有一种方法:
用一个max232加上四个104的瓷片电容,做成串口通讯转换,将max232的7和8脚短接(短路,,,连在一起)7脚和8脚是连接pc的串口2脚和3脚。(短接)-----9脚和10脚是TTL输入输出。把路由的TTL输出在max232的9脚或10脚,(呵呵那个脚自己查)过一次。就是发进232在发出来,在连接到arduino就行了。
作者: 雨淋沐风    时间: 2012-10-5 10:54
#define BAUDRATE 9600
这个波特率的设置要和路由器的一致是吗,我用的DB120设置的115200,
但是我用usb转ttl连电脑,用串口调试助手没有收到如何信息,也发不出去,用的版主的那个上位机程序
作者: jackzeng    时间: 2012-10-6 20:03
雨淋沐风 发表于 2012-10-5 10:54
#define BAUDRATE 9600
这个波特率的设置要和路由器的一致是吗,我用的DB120设置的115200,
但是我用usb转 ...

波特率肯定要和路由一致!你用ttl转接器是连接路由和电脑还是连接单片机和电脑?如果是路由连接电脑无法通信的话可能是ser2net没有安装或者没有启动,或者是你的串口调试助手的波特率和路由不一致,另外路由要转发数据到电脑的话要改一个文件的(具体找论坛的旧帖)。如果是电脑连接单片机话是不用上位机程序的,直接串口助手发送接收就可以了!
作者: 雨淋沐风    时间: 2012-10-6 20:26
jackzeng 发表于 2012-10-6 20:03
波特率肯定要和路由一致!你用ttl转接器是连接路由和电脑还是连接单片机和电脑?如果是路由连接电脑无法通 ...

怎么看ser2net有没有安装好或启动
作者: 雨淋沐风    时间: 2012-10-6 20:29
雨淋沐风 发表于 2012-10-6 20:26
怎么看ser2net有没有安装好或启动

#!/bin/sh /etc/rc.commonSTART=80SSD=start-stop-daemonNAME=wifirobot
#video_mod=gspca_zc3xxvideo_mod=gspca_ov519  
ser_name=/dev/ttyS0           
ser_speed=9600              
我这样改的
作者: 雨淋沐风    时间: 2012-10-6 20:29
jackzeng 发表于 2012-10-6 20:03
波特率肯定要和路由一致!你用ttl转接器是连接路由和电脑还是连接单片机和电脑?如果是路由连接电脑无法通 ...

#!/bin/sh /etc/rc.commonSTART=80SSD=start-stop-daemonNAME=wifirobot
#video_mod=gspca_zc3xxvideo_mod=gspca_ov519  
ser_name=/dev/ttyS0           
ser_speed=9600              
我这样改的
作者: jackzeng    时间: 2012-10-6 20:31
雨淋沐风 发表于 2012-10-6 20:26
怎么看ser2net有没有安装好或启动

我没遇到过这个问题,没法给你解答(论坛里有很多相关的教程,应该有答案),正常情况下刷论坛的固件应该不会遇到这个问题才对,除非你用的不是论坛的固件,另外你的路由的波特率怎么会是115200呢?是不是刷的不是论坛的固件?
作者: jackzeng    时间: 2012-10-6 20:37
jackzeng 发表于 2012-10-6 20:31
我没遇到过这个问题,没法给你解答(论坛里有很多相关的教程,应该有答案),正常情况下刷论坛的固件应该 ...

改了的话串口调试助手的波特率也要改成115200,不然接收到会是乱码。我觉得很有可能是你的ttl转串口模块有问题!
作者: 雨淋沐风    时间: 2012-10-6 20:40
jackzeng 发表于 2012-10-6 20:31
我没遇到过这个问题,没法给你解答(论坛里有很多相关的教程,应该有答案),正常情况下刷论坛的固件应该 ...

我看的:db120刷机详解 [复制链接] ,和他的步骤一模一样,结果也显示的一模一样,就是把他的:
{
修改配置文件输入命令编辑wifirobot配置文件
vi /etc/init.d/wifirobot
用键盘方向键移到需要编辑的地方,输入小写a进入编辑状态,用数字键盘输入115200,删除键可以删除,编辑完后,按ESC退出编辑状态,将键盘切换为大写英文,连续输入两个大写Z即可保存退出!
#!/bin/sh /etc/rc.commonSTART=80SSD=start-stop-daemonNAME=wifirobot
#video_mod=gspca_zc3xxvideo_mod=gspca_ov519    (这两个是摄像头驱动,有#的就是被屏蔽的,如果大家手上的301摄像头有花屏现象,可以屏蔽另外一个换个驱动试试)
ser_name=/dev/ttyS0              (路由TTL口设置,一般都是ttyS0)
ser_speed=115200                 (TTL波特率设置 配论坛的板子115200也行 要是自己做的51 晶振11.0592的就改成9600)
}

里面的115200改成9600


作者: 雨淋沐风    时间: 2012-10-6 20:44
本帖最后由 雨淋沐风 于 2012-10-6 20:45 编辑
jackzeng 发表于 2012-10-6 20:37
改了的话串口调试助手的波特率也要改成115200,不然接收到会是乱码。我觉得很有可能是你的ttl转串口模块有 ...

乱吗就好了,板子检查了,可以收到同是3.3v ttl输出的GPS信号
作者: kulong223    时间: 2012-11-7 12:59
我的情况和楼住一样,但看论坛上很多人正好相反,都是arduino可以直连DB120不能直连703N.
我想问一下论坛WIFI板或者说703N  TTL电压是2.7V,连上arduino直接能用,是否有烧坏的风险?
我测试两天没问题,最近WIFI板就砖了,电源长亮其他反应一点没有.
作者: roouu    时间: 2013-3-13 19:09
good 楼主辛苦了
作者: 潇洒哥    时间: 2013-6-30 18:54
还有一种方法:
用一个max232加上四个104的瓷片电容,做成串口通讯转换,将max232的7和8脚短接(短路,,,连在一起)7脚和8脚是连接pc的串口2脚和3脚。

这个淘宝上有:地址http://item.taobao.com/item.htm? ... &_u=ujtnj66ee50
作者: 潇洒哥    时间: 2013-7-14 07:06
上个图,这是max232的原理和引脚图,我自己用max232和几个105焊接了一个板子,目前还没连接成功
我用的是db120和arduino uno 小车都做好了,摄像头也调通了,剩下的就是搞定电机驱动板和下位机了

作者: jackzeng    时间: 2013-8-22 22:30
好久没有来逛论坛了,发现有人对我的程序有疑问,特此添加一些注释。
int n = 1;
int flagCount = 0;  '接收到数据包FF的数量,如果数值等于0表示接收到数据包头,等于2表示接收到数据包尾
int tempData = 0; ' 存放读取到的缓冲区的第一个字节
int UARTReveived = 0; 接收到数据包的结尾,也就是第二个FF的时候该变量的值为1,这时表示数据包已经接收完整,准备执行命令;执行完后该值变为0
int rxData[5];
void loop()
{
  if(Serial.available())
  {
    tempData = Serial.read();
    delay(3);
    if(tempData == UART_FLAG && flagCount < 2) ' flagCount < 2成立时有两种情况:flagCount == 0或者 flagCount == 1。等于0时表示之前没有接收过FF,这次接收到的FF是数据包的开头。等于1则表示之前已经接受过一次FF,这次接收到的第二个FF是数据包的结尾。假如发送的是“FF000100FF”,那第一次检测到FF(也就是tempData == UART_FLAG )时flagCount == 0 ,所以条件为真,执行大括号的内容。注意一个字节是十六进制的0xFF,而不是F!    {
      rxData[0] = tempData; '把接收到的FF存放到数组 rxData的第一位。这里其实有一点逻辑错误!!当接收到数据包的结尾,也就是flagCount == 1 时,也会往数组的第一位写进FF,但这完全不会影响到程序的执行。
      flagCount++; ' 每接收到第一个FF,这个值就会加1。
    }
    else ' 如果上述条件不成立则表示正在接收数据包中
    {
      rxData[n] = tempData;
      n++;
    }
    if(flagCount == 2) '当接收到第二个FF时,这个条件就会成立,表示数据包接收完毕
    {
      rxData[4] == UART_FLAG;
      UARTReveived = 1;
      n = 1;
      flagCount = 0;
      tempData = 0;
      Serial.flush();
    }
  }
  if(UARTReveived == 1)
  {
    if(rxData[1] == MOTO)
    {
       switch(rxData[2])
       {
         case FORWARD:
         carGoFwd();
         break;
         case BACKWARD:
         carGoBwd();
         break;
         case TURNLEFT:
         carTurnL();
         break;
         case TURNRIGHT:
         carTurnR();
         break;
         case CARSTOP:
         carStop();
         break;
       }
       UARTReveived = 0;
    }
    else if(rxData[1] == SERVO)
    {
      servoSet(rxData[2], rxData[3]);
      UARTReveived = 0;
    }
    else if(rxData[1] == MOTOSPEED)
    {
      CHNSpeed(rxData[2], rxData[3]);
      UARTReveived = 0;
    }
  }
}
作者: boyboysk    时间: 2013-8-31 21:23
serial.read()每次只能读取一个字节也就是一个字符,如果用UNO的IDE进行调试,发送“FF00", rxdata[0]=F,rxdata[1]=F,rxdata[2]=0,rxdata[3]=0,这样就无法接受命令,为什么楼主说是16进制的FF放在rxdata[0]中呢?
作者: ylsmwj    时间: 2013-9-1 09:42
boyboysk 发表于 2013-8-31 21:23
serial.read()每次只能读取一个字节也就是一个字符,如果用UNO的IDE进行调试,发送“FF00", rxdata[0]=F,r ...


兄弟读帖不仔细哦,注意看你楼上的帖子有这句话“注意一个字节是十六进制的0xFF
我当初也是和你一样纠结于同样的问题,读了楼主的解释后,豁然开朗

作者: 路过081001    时间: 2013-9-12 14:44
注意一个字节是十六进制的0xFF
作者: irobot    时间: 2013-9-12 19:59
谢谢,非常实用
作者: 詹波波    时间: 2015-10-24 10:42
正想看看通讯协议那一块  楼主这个很不错
作者: 詹波波    时间: 2015-10-25 18:28
楼主很棒 终于搞通这个协议了  但是舵机的那部分有一点问题 好像完全没起作用 (串口是可以打印出角度的)
作者: dfl123    时间: 2017-7-17 16:08
你好 请问这个程序匹配的是哪个上位机啊 是带方向盘的那个还是只有上下左右的那个啊




欢迎光临 小R科技-WIFI机器人网·机器人创意工作室 (http://www.wifi-robots.com/) Powered by Discuz! X3.4