总的思路是以openmv 接收蓝牙的指令,如果是自动选项,就在openmv 运行识别红球进行固定距离跟踪的程序;如果是手动选项,openmv就直接把所得到的数据传给STM32,不做其它处理。那这里就需要做两件事情,一个是要接收蓝牙传过来的数据,一个是给STM32输出指令。
通过测试,openmv是有1和3两个串口可用,那就用1接收数据,用3输出数据。(计划很理想,但是有bug)

串口通信

OpenMV本质还是一个单片机,可以通过调用pyb中的UART使用串口通信,注意发送的数据类型为字符串,可以通过json.dumps()进行字符串转换

数据类型为字符串

from pyb import UART

uart = UART(3, 9600)
uart.write('hello')
uart.read(5) # read up to 5 bytes

字符串转换

uart3.write(ujson.dumps(cxy)+'\r\n')

数据打包

def send_data_packet(x, y):#数据打包
    temp = struct.pack("<bbii",                #格式为俩个字符俩个整型
                   0xAA,                       #帧头1
                   0xAE,                       #帧头2
                   int(x), # up sample by 4    #数据1
                   int(y)) # up sample by 4    #数据2
    uart.write(temp)                           #串口发送

接收蓝牙数据

串口接收数据

#串口接收数据
import  time
from pyb import UART
uart = UART(3, 9600,time_out_char=1000)
while(True):
	if uart.any():
	a=uart.readline().decode.strip()
	b=int(a)
	print(b)

接收到a代表的数据,用b输出,decode为字符串,无回车换行,但是一般有回车换行简单些,那就要在输入的时候写上。

IO口通信接收指令

p_out = Pin('P7', Pin.OUT_PP)#设置p_out为输出引脚
        p_out.high()#设置p_out引脚为高
        p_in = Pin('P7', Pin.IN, Pin.PULL_UP)#设置p_in为输入引脚,并开启上拉电阻

串口接收

STM32是以中断的方式接收数据

#stm32串口接收是以中断的方式
void USART2_IRQHandler(void)
{
	
	static uint8_t rebuf[8]={0},i=0;#中断子程序每进入一次,只会接收一个Byte的数据。也就是说接收完一帧数据需要进入8次中断才行。
	
	if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET)#OpenMV_Rx_BUF是一个外部变量用于保存从openmv接收到的数据,中断子程序中,每当进入中断,会首先判断帧头,如果不是 帧头,会直接丢弃,直到等到帧头的到来。
	{
		rebuf[i++]=USART_ReceiveData(USART2);	
		if(rebuf[0]!=0x4000)#帧头
			i=0;
	  if((i==2)&&(rebuf[1]!=0x4000))#判断帧头
			i=0;
		if(i>=7)#代表一帧数据完毕
		{
			memcpy(OpenMV_Rx_BUF,rebuf,i);
			
			i = 0;
 
		}
		USART_ClearFlag(USART2,USART_FLAG_RXNE);
	}

上面if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET)

OpenMV_Rx_BUF是一个外部变量用于保存从openmv接收到的数据,中断子程序中,每当进入中断,会首先判断帧头,如果不是 帧头,会直接丢弃,直到等到帧头的到来。

openmv 中就可以用以下表示

FH = bytearray([0xb3,0xb3])#帧头
 DF = bytearray([0xb4,0xb4])#帧尾  
 uart3.write(ujson.dumps(FH)+'\r\n')
 uart3.write(ujson.dumps(cxy)+'\r\n')
uart3.write(ujson.dumps(DH)+'\r\n')

串口连接

openmv有uart1和uart3两个串口。

opencv 串口通信 openmv串口发送字符串_串口通信


opencv 串口通信 openmv串口发送字符串_json_02

问题

1.蓝牙传递的指令对应的帧头帧尾还没有确定,还需更改。这个是需要用蓝牙模块通过USB转接口插在电脑上,用串口助手看,其中用手机上的SPP蓝牙软件传送,不知我这边是什么问题,线是对的,换手机和电脑都没用,蓝牙模块也是好的,已经给战友邮过去了,等待支援。

战友这里已解决,刚开始和我的问题是一样的,但是他那边多尝试了几遍就好了,但是又出现了输出乱码的情况,把波特率重置了一下就好了,感觉很可能是波特率的问题。

至于帧头帧尾,那不就是中括号的左右吗。。。

opencv 串口通信 openmv串口发送字符串_数据_03

2.这个多次输入信息,不知道语法怎么表示了。

if b=[9]:#手动,这个多次输入信息,不知道语法怎么表示了
     if b =[1]:
     ch=[1]
     uart3.write(ujson.dumps(FH)+'\r\n')
     uart3.write(ujson.dumps(ch)+'\r\n')
     uart3.write(ujson.dumps(DH)+'\r\n')
     print("前进")

用了懒方法,若b不为a,则是手动,最省事的方法就是开始的时候只能输入自动[a]和前进等指令,如果输入的不是自动[a],则根据指令直接传给单片机。

else# b不为a,则是手动,最省事的方法就是开始的时候只能输入自动[a]和前进等指令,如果输入的不是自动[a],则根据指令直接传给单片机
               if b =[1]:
                   ch=[1]
                   
                   uart3.write(ujson.dumps(FH)+'\r\n')
                   uart3.write(ujson.dumps(ch)+'\r\n')
                   uart3.write(ujson.dumps(DF)+'\r\n')
                   print("前进")

当然,还可以用松键检测这种更为严谨的方法。
3.串口连接好,连上电脑,不亮灯。

线的接触问题。

4.可打开串口,但是电脑端接收不到数据。

是这个USB-TTL线和openmv的P4,P5接反了,所以,在这个时候不要怀疑世界,要先重新审视一下自己的装备。(蓝牙模块和USB-TTL线反接也很重要)

opencv 串口通信 openmv串口发送字符串_数据_04


5.python语法错误

这个错误真的是弱爆了。还是平常自主写的太少了,比如

if:

elif:

else:

if a == [b] :

和c语言不一样,一定要加冒号,赋值的时候要是两个等号,还有缩进问题(还是最好在openmv ide里面编写程序,在记事本写的很悲伤)。
如果有语法错误,不知道在哪里,那就一行一行的看,一行一行注释,排除法找,最本质的方法是最有效的。

6.最花时间的一个问题,openmv只能发送数据,不能接收数据。

要是有示波器看看硬件问题还是程序问题就好了。

最后发现是openmv不能完美的接收数据,因为一接收数据,摄像头就不可以用了,是自身固件的问题,怪不得官网推荐用扩展板。
但是除了扩展板,还可以用单片机当中介,IO高低电平等多种方法,我们这边工具不够,再次邮寄。

最终是先拿openmv运行,只是输出数据,再拿esp32的蓝牙接收,

openmv

# Single Color RGB565 Blob Tracking Example
#
# This example shows off single color RGB565 tracking using the OpenMV Cam.

import sensor, image, time, math,pyb

from pyb import UART
from pyb import Pin

import ujson



threshold_index = 0 # 0 for red, 1 for green, 2 for blue

# Color Tracking Thresholds (L Min, L Max, A Min, A Max, B Min, B Max)

thresholds = [(30, 100, 15, 127, 15, 127)]

K=800#计算距离的辅助值

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 5000)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
clock = time.clock()

uart3 = UART(3, 9600) #p4p5
uart1 = UART(1, 9600) #p2p3
uart1= UART(1, 9600, timeout_char=1000)

#蓝牙接收数据
#UART.init(baudrate=9600, bits=8, parity=None, stop=1)


#IO口通信接收指令    
#p_out=Pin('P7',Pin.OUT_PP)#设置p_out为输出引脚
#p_out.high()#设置p_out引脚为高
#p_in=Pin('P7',Pin.IN,Pin.PULL_UP)#设置p_in为输入引脚,并开启上拉电阻
  


while(True):
    p_out1 = Pin('P7', Pin.OUT_PP)#设置p_out为输出引脚
    p_out1.high()#设置p_out引脚为高
    p_in1 = Pin('P7', Pin.IN, Pin.PULL_UP)#设置p_in为输入引脚,并开启上拉电阻
    value1 = p_in1.value()
    p_out2 = Pin('P8', Pin.OUT_PP)#设置p_out为输出引脚
    p_out2.high()#设置p_out引脚为高
    p_in2 = Pin('P8', Pin.IN, Pin.PULL_UP)#设置p_in为输入引脚,并开启上拉电阻
    value2 = p_in2.value()
    
    #供单片机判断接收数据

    FH = bytearray([0xb3,0xb3])#帧头
    AH = bytearray([0x40])
    DF = bytearray([0xb4,0xb4])#帧尾
    HF = bytearray([0x40])
    #手动
    if value1==1 and value2==1:
        ch=[1]
   
        uart3.write(ujson.dumps(FH)+'\r\n')
        uart3.write(ujson.dumps(ch)+'\r\n')
        uart3.write(ujson.dumps(DF)+'\r\n')
        print("前进")
        clock.tick()
    
    elif value1==1 and value2==0:
        ch=[2]
   
        uart3.write(ujson.dumps(FH)+'\r\n')
        uart3.write(ujson.dumps(ch)+'\r\n')
        uart3.write(ujson.dumps(DF)+'\r\n')
        print("后退")
    elif value1==0 and value2==1:
        ch=[4]
   
        uart3.write(ujson.dumps(FH)+'\r\n')
        uart3.write(ujson.dumps(ch)+'\r\n')
        uart3.write(ujson.dumps(DF)+'\r\n')
        print("左转")
    elif value1==0 and value2==0:
        ch=[3]
   
        uart3.write(ujson.dumps(FH)+'\r\n')
        uart3.write(ujson.dumps(ch)+'\r\n')
        uart3.write(ujson.dumps(DF)+'\r\n')
        print("右转")

    else:
        img = sensor.snapshot()
        for blob in img.find_blobs([thresholds[threshold_index]], pixels_threshold=200, area_threshold=200, merge=True):
            # These values depend on the blob not being circular - otherwise they will be shaky.
            img.draw_rectangle(blob.rect())
            img.draw_cross(blob.cx(), blob.cy())
            # Note - the blob rotation is unique to 0-180 only.
            #if b == "[a]": #接收蓝牙指令为自动
            # print(clock.fps())
            # print(blob.cx(),blob.cy())
            if ((blob.cx()-160)<(-10)) :
                # 串口发送左
                cxy=[4]
                    
                uart3.write(ujson.dumps(FH)+'\r\n')
                uart3.write(ujson.dumps(cxy)+'\r\n')
                uart3.write(ujson.dumps(DF)+'\r\n')
                print("左转")
            if ((blob.cx()-160)>10) :
                # 串口发送右
                cxy=[3]
                    
                uart3.write(ujson.dumps(FH)+'\r\n')
                uart3.write(ujson.dumps(cxy)+'\r\n')
                uart3.write(ujson.dumps(DF)+'\r\n')
                print("右转")
            if ((blob.cy()-120)<(-30)) :
                # 串口发送前进
                cxy=[1]
                    
                uart3.write(ujson.dumps(FH)+'\r\n')
                uart3.write(ujson.dumps(cxy)+'\r\n')
                uart3.write(ujson.dumps(DF)+'\r\n')
                print("前进")
            if ((blob.cy()-120)>30) :
                # 串口发送后退
                cxy=[2]
                
                uart3.write(ujson.dumps(FH)+'\r\n')
                uart3.write(ujson.dumps(cxy)+'\r\n')
                uart3.write(ujson.dumps(DF)+'\r\n')
                print("后退")

esp32

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;                                 //定义蓝牙对象
//esp32有三个串口,分别为Serial、Serial1、Serial2,其中Serial为默认串口,可用来下载程序。
HardwareSerial mySerial1(1);                              //定义Serial1对象
HardwareSerial mySerial2(2);                              //定义Serial2对象

unsigned char btrxdata[4];                                //蓝牙接收数组
unsigned char btrxcount=0;                                //蓝牙接收标志位
unsigned char omvrxdata[4];                               //openmv接收数组
unsigned char omvrxcount=0;                               //openmv接收标志位
unsigned char wordmode=0;                                 //工作模式,0为手动模式即仅能通过蓝牙控制,1为自动模式可通过openmv控制 ,二者切换通过蓝牙控制

void setup() {
  pinMode(21, INPUT);                                     //初始化io21,输入模式,用于检测hc05是否有连接。本程序中未使用
  mySerial1.begin(9600,SERIAL_8N1,32,33);                 //初始化Serial1,波特率9600 rx为io32 tx为io33
  mySerial2.begin(9600,SERIAL_8N1,18,19);                 //同上
  Serial.begin(9600);                                     //初始化Serial,仅填波特率即可,也可映射到任意io,参数格式同上
  SerialBT.begin("dog");                                  //初始化蓝牙,蓝牙名字为dog
}

void loop() {

  while(Serial.available())                               //如果串口接收缓冲区有数据,就全部读取
  {
    omvrxdata[omvrxcount++]=Serial.read();
    if(wordmode == 1)                                     //如果是自动模式就转发数据,否则丢弃
    {
      if((omvrxdata[omvrxcount-3]=='[')&&(omvrxdata[omvrxcount-1]==']'))
      {
        mySerial1.write(omvrxdata[0]);
        mySerial1.write(omvrxdata[1]);
        mySerial1.write(omvrxdata[2]);
        omvrxcount = 0;
        memset(omvrxdata,0,sizeof(omvrxdata));            //清空数组
      }
    }
    if(omvrxcount == 3)
    {
      omvrxcount = 0;
      memset(omvrxdata,0,sizeof(omvrxdata));              
    }
  }
   
  while(SerialBT.available())                              //如果蓝牙接收缓冲区有数据,就全部读取
  {
    btrxdata[btrxcount++]=SerialBT.read();
    if((btrxdata[btrxcount-2]==0x0a)&&(btrxdata[btrxcount-1]==']'))
    {
      btrxcount = 0;                                       //检测接收到的数据是否有控制数据 如果有则执行 若无则转发
      memset(btrxdata,0,sizeof(btrxdata));
      wordmode = 1;
    }
    if((btrxdata[btrxcount-2]==0x0b)&&(btrxdata[btrxcount-1]==']'))
    {
      btrxcount = 0;
      memset(btrxdata,0,sizeof(btrxdata));
      wordmode = 0;
    }
    if((btrxdata[btrxcount-2]==0x0c)&&(btrxdata[btrxcount-1]==']'))
    {
      btrxcount = 0;
      memset(btrxdata,0,sizeof(btrxdata));
    }
    if((btrxdata[btrxcount-3]=='[')&&(btrxdata[btrxcount-1]==']'))
    {
      mySerial1.write(btrxdata[0]);
      mySerial1.write(btrxdata[1]);
      mySerial1.write(btrxdata[2]);
      btrxcount = 0;
      memset(btrxdata,0,sizeof(btrxdata)); 
    }
    if(btrxcount == 3)
    {
      btrxcount = 0;
      memset(btrxdata,0,sizeof(btrxdata)); 
    }      
  }
}