基于MAX7800羽毛板语音控制ESP8266小车
基于MAX7800羽毛板语音远程控制ESP8266小车 采用现成的KWS20关键词进行的运动控制小车
标签
Funpack活动
串口
MQTT
远程遥控小车
2345vor
更新2023-03-27
江汉大学
1563

 目录

1. 项目介绍

2. 项目设计思路

3. 主线任务

     3.1 远程遥控ESP8266小车——主线1

      3.2 MAX7800实现KWS20 demo演示——主线2

      3.3 ESP8266-NodeMCU软硬串口通讯——主线3

      3.4 MAX7800与ESP8266mcu串口通讯点灯——主线4

      3.5 MAX7800与ESP8266mcu通讯关键字控制——主线5

4. 副线任务

       4.1 搭建window下配置Maxim SDK环境——副线1

       4.2 MAX78000基础案例演示——副线2

5. 具体实验

       5.1 前期构思

              5.1.1 语音匹配

              5.1.2 操作码匹配

        5.2 硬件结构

              5.2.1 原理图

              5.2.2 实物连接

        5.3 软件设计

              5.3.1 接收机

              5.3.2 发射机

6.  实验效果

7. 总结


 

1. 项目介绍

FtWVS9Ybxi27Sv6QiEqIHfyD8LUz

我从小就喜欢车类玩具,小时候买了5块钱四驱赛车好几个,现在做的算是对童年时代的一种跨越吧!

 

随着时代的变化,目前市面上大多数是摇杆手柄遥控小车,淘宝一搜一堆,这种摇杆控制的我也做过灵敏度还可以,现在尝试做基于MAX7800羽毛板语音控制ESP8266小车
采用MAX7800现成的KWS20关键词,['up', 'down', 'left', 'right', 'stop', 'go', 'yes', 'no', 'on', 'off', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'zero'],进行语音关键字识别远程控制小车。

项目源码在gitcode托管

原理图源码见:嘉立创EDA(标准版) - 免费、易用、强大的在线电路设计软件

演示视频:基于MAX7800羽毛板语音控制ESP8266小车_哔哩哔哩_bilibili

 

 2. 项目设计思路

 

搭建环境《——》学习基本例程《——》熟悉基本编译下载调试功能《——》重点解析参考目标案例《——》各个模块功能调试《——》整体联调维护相关功能《——》演示总结

Fj_-XouP19gX_cqX0D24x6YVChmz

 系统设计图

 

我们开始主线任务和副线任务吧!🌻🌻🌻

FoNyrmBBWH2eoCDDDeL4LeWf144r

3. 主线任务

主线任务围绕语音关键字识别远程控制三部分展开!🥳🥳🥳

3.1 远程遥控ESP8266小车——主线1

一步一个脚印,MQTT的ESP遥控小车从原理到实现,涉及硬件电路,程序设计,然后也探讨了个人见解,希望把两个归中传感器数值传递过去,减少实验步骤,提高速成感和成就感!🌼🌻🎏🛹🏃‍♂️

见网址:基于然也物联MQTT的ESP遥控小车

见项目进度主线1

  • 发射机

Fq_FCEshQ-xq4YFfFawgEn9I4WWD

  • 接收机

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==FjbZ-eP19mF4bVMbOlJ83R3Sb-p3

  • 最后效果

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==Fs5w7i-mrjibt_d7r_3xYpaMc203

3.2 MAX7800实现KWS20 demo演示——主线2

 

Keyword Spotting Demo 软件演示了如何使用MAX78000 EVKIT 识别大量关键字。
KWS20 演示软件使用第二版 Google 语音命令数据集此演示使用了完整数据集中的以下 20 个关键字子集:

[‘up’, ‘down’, ‘left’, ‘right’, ‘stop’, ‘go’, ‘yes’, ‘no’, ‘on’, ‘off’, ‘one’, ‘two’, ‘three’, ‘four’, ‘five’, ‘six’, ‘seven’, ‘eight’, ‘nine’, ‘zero’]
其余关键字和无法识别的词属于“Unknown”类别,理解语音识别原理和相关神经网络代码调用。

见网址:【MAX7800实现KWS20 demo演示】_2345VOR的博客-CSDN博客_ai8x 训练

见项目进度主线2

FsWV7S5bNR7Kh4vrELoUCNdoWMfZwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

3.3 ESP8266-NodeMCU软硬串口通讯——主线3

本次采用ESP8266node MCU开发板,利用自身软硬件串口通讯,采用关键字符串实现开灯功能🛹🛹🛹

见网址:ESP8266-NodeMCU软硬串口通讯

见项目进度主线3

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==Fh9Kb-HQR-94DRypHrIup1iptxCe

3.4 MAX7800与ESP8266mcu串口通讯点灯——主线4

前期搭好MAX7800 的eclipse和ESP82666的Arduino开发环境,现在开始慢慢实现这两者的通讯,目前MAX7800 羽毛板可以发送字符串,但是ESP8266无法连续接收,因此采用简单的单个字符的串口通讯实现点灯功能。

见网址: MAX7800与ESP8266mcu串口通讯点灯

见项目进度主线4

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==FqMTzxQPno-TdJ8f4by6RvM_chBZ

3.5 MAX7800与ESP8266mcu通讯关键字控制——主线5

前期搭好MAX7800 的eclipse和ESP82666的Arduino开发环境,现在开始慢慢实现这两者的通讯,目前MAX7800 羽毛板可以发送字符串,但是ESP8266无法连续接收,因此采用简单的单个字符的串口通讯实现点灯功能,现在可以与esp8266实现KWS20关键字识别控制串口输出字符操作数。

见网址:MAX7800与ESP8266mcu通讯关键字控制

见项目进度主线5

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==FtqqlquNscW9tq8BlAV9vQ4dK_l-

4. 副线任务

副线任务围绕MAX7800基础案例开发,熟悉基本语法、相关外设驱动部分展开!🥳🥳🥳

4.1 搭建window下配置Maxim SDK环境——副线1

下面介绍如何在Windows下搭建Maxim SDK开发环境,我们就可以点灯,hello world啦!

见网址:【window下配置Maxim SDK环境】_2345VOR的博客-CSDN博客_maxim sdk

见项目进度副线1

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==FtyxRRnlDgLs9FKlJnQ9Z7_84wPw

4.2 MAX78000基础案例演示——副线2

挑选ADC、GPIO、UART三个幸运的例程进行学习演示,总结项目编译下载调试开发功能,熟悉语言规范。

见网址:【MAX78000基础案例演示】_2345VOR的博客-CSDN博客

见项目进度副线2

FiSnbfRKNg7nCax8Rukc_A5ODM1zwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

5. 具体实验

改善小车硬件结构,使羽毛板嵌入进去,设计控制逻辑程序,完成语音识别、串口驱动、关键字符解码、MQTT发送接收、设备驱动执行,程序控制流程图如下

FgsPbz9Np611hLNiGPbRiRQxc9_xwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

5.1 前期构思

下面是语音关键字控制小车状态定义,信息传递过程如下,首先从MAX7800语音关键字——串口2传输特定字符——ESP8266T接收操作码并解析成小车状态采用MQTT发送——ESP8266R采用MQTT接收小车状态信息并操作对应设备驱动

5.1.1 语音匹配

  1. ['up', 'down']小车左右手部舵机状态
  2. ['left', 'right']小车左右转方向控制
  3. ['stop', 'go']小车状态控制
  4. ['yes', 'no']小车前进后退方向控制
  5. ['on', 'off']车灯控制状态
  6. ['one', 'two', 'three', 'zero']速度大小控制
  7. ['four', 'five', 'six', 'seven', 'eight', 'nine', 'unknow']无效关键字,保持小车原有状态

以上20个关键字可用15个字符MAX7800负责识别定义,然后发送到esp8266接收对应15个操作码。下图这是我通过MAX7800与esp8266串口通讯找到的,解析字符得到从225到339的15个操作码。

FphlcHv_NL1FjGHmZ0JbMvMkrfL_

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==​亲笔记录

关键字     up     down    left   right    stop    go    yes   no     on     off     one    two    three   zero others
字符     CI    DFN   GO    RPX   SY     TV    U    h    ac    fnld    2    3   4 go 5
操作数     225   226     227   228    229    230   231    232    233    234   236   237    238     235   239


关键代码展示

/* Set of detected words */
const char keywords[NUM_OUTPUTS][10] = { "UP",    "DOWN", "LEFT",   "RIGHT", "STOP",  "GO",
                                         "YES",   "NO",   "ON",     "OFF",   "ONE",   "TWO",
                                         "THREE", "FOUR", "FIVE",   "SIX",   "SEVEN", "EIGHT",
                                         "NINE",  "ZERO", "Unknown" };
const uint8_t TestTxData[BUFF1_SIZE]="CDGRSTUhaf234555555g5";

  5.1.2 操作码匹配

  • 横坐标依次是小车关键状态

小车左右转方向,小车油门大小(有方向),左边舵机手,右边舵机手,车灯

  • 纵坐标依次是语音关键字

['up', 'down', 'left', 'right', 'stop', 'go', 'yes', 'no', 'on', 'off', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'zero']

 

下面给出小车关键状态与语音关键字一一对应关系!!!

  1. 横坐标匹配遥控小车状态(左右方向,前后方向,左边手部舵机,右边手部舵机,车灯)
  2. 纵坐标匹配语音识别关键字(简化为15组)

FrPJN_UzBPZ7IS-WSy17zz2VKud0wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

关键代码展示

int A[4]={400,1850,3400,4200},B[6]={600,1850,3700,4200,3400,4090},C[4]={90,120,180,200},D[4]={90,60,0,200},E[4]={0,1,5,10};//小车五组状态表
int action[2][5] = {
  { A[3], B[3], C[3], D[3], E[3] },
  { A[1], B[1], C[1], D[1], E[0]}
};//发送时参数比照
int mylist[15][5] = {
  { A[3], B[3], C[2], D[2], E[3] },//up
  { A[3], B[3], C[0], D[0], E[3] },//down
  { A[0], B[3], C[1], D[2], E[3] },//left
  { A[2], B[3], C[2], D[1], E[3] },//right
  { A[1], B[1], C[1], D[1], E[0] },//stop
  { A[3], B[2], C[3], D[3], E[1] },//go
  { A[3], B[1], C[3], D[3], E[3] },//yes
  { A[3], B[0], C[3], D[3], E[3] },//no
  { A[3], B[3], C[3], D[3], E[1] },//on
  { A[3], B[3], C[3], D[3], E[0] },//off
  { A[3], B[1], C[3], D[3], E[3] },//zero
  { A[3], B[4], C[3], D[3], E[3] },//1
  { A[3], B[2], C[3], D[3], E[3] },//2
  { A[3], B[5], C[3], D[3], E[3] },//3
  { A[3], B[3], C[3], D[3], E[3] }//unknow
};//语音关键字对应的操作码匹配小车五组状态

5.2 硬件结构

此部分搭建系统的原理图和实物连接

原理图源码见:嘉立创EDA(标准版) - 免费、易用、强大的在线电路设计软件

5.2.1 原理图

  • 发射机

Fho3KfAKt-FfLGnp8o6T85NEigKcwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 物料说明:

MAX7800主控,语音识别20个关键单词,发送特定字符到esp8266,并SD卡记录,显示屏显示(没有设计TFT显示屏接线)

Fgh6gUnX4dkv9agLtC_eML2Ca1t3wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

ESP8266T中转器,接收MAX7800特定字符,匹配对应操作码关联小车状态参数,采用然也物联网平台使用MQTT远程传输小车状态参数指令

FlvZ01K4vo2SfVfYaq2ipN7CoVCTwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

LED1显示器,通过PWM驱动显示传输时的小车速度,采用亮暗程度表达

Fs4rC2LCWnVLRxZCqm1Vl6pbCaJxwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

电源:可采用MAX7800连接USB数据线供电,MAX7800会通过3.3V给ESP8266T供电

  • 接收机

FrC8EIdu5fdGCcVAIRCBG_teH8_NwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 物料说明:

ESP8266主控,采用然也物联网平台使用MQTT远程接收小车状态参数指令,下发操作码关联小车状态参数,驱动电机舵机LED

FlvZ01K4vo2SfVfYaq2ipN7CoVCTwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

L293D拓展驱动板,采用ESP12E 电机拓展板与 NodeMCU ESP8266 Amica 板兼容,扩展板直接放在微控制器上。电机的电源连接到 VM/GND 接线盒,电路板的电源连接到 VIN/GND 接线盒。如果电机的电源与 NodeMCU 的电源相同(<10V 最大值),则可以使用桥连接 VIN 和 VM 引脚。电机连接到接线端子 A+、A-、B+、B-。

Fg5VcAE35K83EhG5l04de-YQUjMQwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

电机R:采用TT马达减速电机,连接右边坦克链条履带结构

FpCIbam9YPHUVrp-wpdlY82l4NEVwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

电机L:采用TT马达减速电机,连接左边坦克链条履带结构

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

Fsq1KfKuTK-83acxs5O9XuFCRpZk

舵机R:采用常见的90舵机作为右手臂

 wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

Fo_L2C0GQggJLNv6pneR6YxF8ofC

舵机L:采用常见的90舵机作为右手臂

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==FvEiA2x6KClz-A-ffJPvZAqqZYIq

 LED2:作为车灯,电机运行时亮,可单独关键词“on,off”控制

Fg9p9okwfe97qGGJl42V0z9SQIXMwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 电源:采用2S航模电池作为接收机的能量供应

FtSYVp37GU2_PyCHyPPMrO4U6HMUwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

 

5.2.2 实物连接

  • 发射机

FsIU2JSegdfvrOBoqZL0eroPWUmGwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

  • 接收机

小车后视图

FgeUS7WPPFYaHDI6x7bPhj3Iw3wRwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

小车前视图

 wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==FohU39OtdTTkFMBze_ElQHNGvbbw

 

小车侧视图

 wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==FsXh33KMFymfaQd_9bFGHyXvaAdJ

 5.3 软件程序设计

本项目涉及两个平台的开发,第一个是MAX7800在Eclipse MaximSDK开发,第二个是ESP8266在Arduino IDE2.0开发,具体参照副线1项目环境搭建!

项目工程在gitcode托管,目前有两版,第一版可以简单测试串口,发行版可以正常控制

FmO-9ybzlwq-pxizG1CXBUzeqr7c

 

5.3.1 接收机

  • max7800程序部分main.c,详细见工程
//字符数组定义
const char keywords[NUM_OUTPUTS][10] = { "UP",    "DOWN", "LEFT",   "RIGHT", "STOP",  "GO",
                                         "YES",   "NO",   "ON",     "OFF",   "ONE",   "TWO",
                                         "THREE", "FOUR", "FIVE",   "SIX",   "SEVEN", "EIGHT",
                                         "NINE",  "ZERO", "Unknown" };
const uint8_t TestTxData[BUFF1_SIZE]="CDGRSTUhaf234555555g5";



//置信度大于90采用串口2发送
/* output char*/
                if(probability>90){
					*firstName = TestTxData[out_class];
					writemsg( firstName, 1);//发送串口2字符指令
					printf("\n count : %d: %d: %s\n", count++,count%62,firstName);
					/*   */
                }
  • esp8266T程序
// #include <WiFi.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// #define joyX 34
// #define joyY 35
// int Ym, Fx, R, L;
// 设置wifi接入信息(遥控器要连接的wifi)
// const char* ssid = "J09 502";
// const char* password = "qwertyuiop111";
// const char* ssid = "vor";
// const char* password = "vor980501";
const char* ssid = "XY-031026";
const char* password = "12345678";
String topicString = "minivorCar214923790t7";  //输入自己的特定主题名,能保证别人不和你重复就行

const char* mqttServer = "test.ranye-iot.net";
// const char* mqttServer = "8.142.157.58";
// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

/**mixly**/
#include <SoftwareSerial.h>
#define timer1 300
volatile int item;
volatile byte item1;
volatile int item2;
volatile byte item3;
volatile int operators;
String strings;
int A[4]={400,1850,3400,4200},B[6]={600,1850,3700,4200,3400,4090},C[4]={90,120,180,200},D[4]={90,60,0,200},E[4]={0,1,5,10};
int action[2][5] = {
  { A[3], B[3], C[3], D[3], E[3] },
  { A[1], B[1], C[1], D[1], E[0]}
};
int mylist[15][5] = {
  { A[3], B[3], C[2], D[2], E[3] },//up
  { A[3], B[3], C[0], D[0], E[3] },//down
  { A[0], B[3], C[1], D[2], E[3] },//left
  { A[2], B[3], C[2], D[1], E[3] },//right
  { A[1], B[1], C[1], D[1], E[0] },//stop
  { A[3], B[2], C[3], D[3], E[1] },//go
  { A[3], B[1], C[3], D[3], E[3] },//yes
  { A[3], B[0], C[3], D[3], E[3] },//no
  { A[3], B[3], C[3], D[3], E[1] },//on
  { A[3], B[3], C[3], D[3], E[0] },//off
  { A[3], B[1], C[3], D[3], E[3] },//zero
  { A[3], B[4], C[3], D[3], E[3] },//1
  { A[3], B[2], C[3], D[3], E[3] },//2
  { A[3], B[5], C[3], D[3], E[3] },//3
  { A[3], B[3], C[3], D[3], E[3] }//unknow
};
SoftwareSerial mySerial(0, 5);
/******/

void setup() {
  /**mixly**/
  item = 0;
  item1 = ' ';
  item2 = 0;
  item3 = ' ';
  operators = 4;//stop
  strings = "hello";
  Serial.begin(115200);
  mySerial.begin(115200);
  pinMode(4, OUTPUT);
  //v1: 串口读取
  //v2: 串口周期性点灯
  //v3: 添加语音串口通讯点灯
  //v4: 添加二维数组语音控制
  digitalWrite(4, LOW);
  Serial.println(strings);
  mySerial.println(strings);
  /******/
  // pinMode(joyX, INPUT);
  // pinMode(joyY, INPUT);
  WiFi.mode(WIFI_STA);                     //设置ESP8266工作模式为无线终端模式
  connectWifi();                           // 连接WiFi
  mqttClient.setServer(mqttServer, 1883);  // 设置MQTT服务器和端口号
  connectMQTTServer();                     // 连接MQTT服务器
}
int count = 0;
void loop() {
  if (mqttClient.connected()) {  // 如果开发板成功连接服务器
    pubMQTTmsg();
    //count = 0;
    mqttClient.loop();    // 保持客户端心跳
  } else {                // 如果开发板未能成功连接服务器
    connectMQTTServer();  // 则尝试连接服务器
  }
  delay(10);
}

void connectMQTTServer() {
  // 根据ESP32的MAC地址生成客户端ID(避免与其它ESP32的客户端ID重名)
  String clientId = "esp8266-" + WiFi.macAddress();
  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str())) {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address: ");
    Serial.println(mqttServer);
    Serial.println("ClientId:");
    Serial.println(clientId);
  } else {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(3000);
  }
}

void pubMQTTmsg() {
  static int value;  // 客户端发布信息用数字

  char publishTopic[topicString.length() + 1];  // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
  strcpy(publishTopic, topicString.c_str());

  // Ym = analogRead(joyY);  //读取油门信息
  // Fx = analogRead(joyX);  //读取方向信息
  // // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
  // String messageString = "A" + String(Ym) + "B" + String(Fx) + "C" + String(R) + "D" + String(L) + "E";
/**mixly**/
  procedure2();
  procedure();
  procedure4(operators);
  if (item > 234 && item < 239) {
    // procedure3();
    // delay(100);
    item = (map(item - 235, 0, 3, 0, 255));
    analogWrite(4, item);
  }
  if (item == 229) {
    digitalWrite(4, LOW);
  }
  String messageString = strings;
/**mixly**/
  char publishMsg[messageString.length() + 1];
  strcpy(publishMsg, messageString.c_str());
  // 实现ESP32向主题发布信息
  if (mqttClient.publish(publishTopic, publishMsg)) {
    //Serial.println("Publish Topic:");Serial.println(publishTopic);
    //Serial.println("Publish message:");
    Serial.println(publishMsg);
  } else {
    Serial.println("Message Publish Failed.");
  }
  delay(timer1);
}
void connectWifi() {
  WiFi.begin(ssid, password);
  //等待WiFi连接,成功连接后输出成功信息
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}

void procedure() {
  if (item3 != item1) {
    Serial.print("item:  ");
    Serial.println(item);
    Serial.print("item1:  ");
    Serial.println(item1);
    item3 = item1;
  }
}

void procedure2() {
  if (Serial.available() > 0) {
    item1 = Serial.read();
  }
  if (mySerial.available() > 0) {
    item1 = mySerial.read();
    item = String(item1).toInt();
    operators = item - 225;
  }
  if (item == 0) {
    item = item2;
  }
  item2 = item;
}

void procedure3() {
  Serial.println("ok");
  mySerial.println("ok");
}

void procedure4(int x) {
  for (int i = 0; i <= 4; i = i + (1)) {
    if (mylist[x][i] < action[0][i]) {
      action[1][i] = mylist[x][i];
    }
  }
  strings = String("A") + String(3700-action[1][0]) + String("B") + String(action[1][1]) + String("C") + String(action[1][2]) + String("D") + String(action[1][3]) + String("E") + String(action[1][4]) + String("F");
  // strings0 = String("A") + String(A[1]) + String("B") + String(B[1]) + String("C") + String(C[1]) + String("D") + String(D[1]) + String("E") + String(E[0]) + String("F");

  // Serial.print("massage:  ");
  // Serial.println(strings);
}

5.3.2 发射机

  • esp8266R程序
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>
#include <espnow.h>
#include <Servo.h>  // 调用Servo库
Servo servo_R;      // 定义Servo对象来控制
Servo servo_L;      // 定义Servo对象来控制
Ticker ticker;
/*
 		Board pin | NodeMCU GPIO | 	Arduino IDE
 					A- 										1 												5 or D1
 					A+ 										3 												0 or D3
 					B- 										2 												4 or D2
 					B+ 										4 												2 or D4
*/
#define IN_1 D1
#define IN_2 D3
#define IN_3 D2
#define IN_4 D4
#define LED D7
// #define servopinr D5
// #define servopinl D6

//↑↑↑以上4个IO口可以输出PWM波,实现全比例控制速度


// 设置wifi接入信息(根据小车要连接的WiFi信息进行修改)
// const char* ssid = "J09 502";
// const char* password = "qwertyuiop111";
// const char* ssid = "vor";
// const char* password = "vor980501";
const char* ssid = "XY-031026";
const char* password = "12345678";
String topicString = "minivorCar214923790t7";  //与esp32代码中的该部分相同
int YmMedian = 1816;                          //油门中位值
int FxMedian = 1811;                          //方向中位值


const char* mqttServer = "test.ranye-iot.net";
// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

int Ym, Fx, LeCar;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
  pinMode(IN_1, OUTPUT);
  pinMode(IN_2, OUTPUT);
  pinMode(IN_3, OUTPUT);
  pinMode(IN_4, OUTPUT);
  pinMode(LED, OUTPUT);
  // pinMode(servopinr, OUTPUT);  //设定舵机接口为输出接口
  // pinMode(servopinl, OUTPUT);  //设定舵机接口为输出接口
  while (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
  }
  
  servo_R.attach(D5,500,2500);                       // 控制线连接数字D5
  servo_L.attach(D6,500,2500);                       // 控制线连接数字D6
  goStop();
  Serial.begin(115200);                     // 启动串口通讯(波特率为9600,波特率可以理解为串口传输数据的速度)
  WiFi.mode(WIFI_STA);                      //设置ESP8266工作模式为无线终端模式(8266像手机一样连接到wifi的模式)
  connectWifi();                            // 连接WiFi
  mqttClient.setServer(mqttServer, 1883);   // 设置MQTT服务器和端口号
  mqttClient.setCallback(receiveCallback);  // 设置MQTT订阅回调函数
  connectMQTTserver();                      // 连接MQTT服务器
  ticker.attach(0.3, touch);
}

void loop() {
  if (mqttClient.connected()) {  // 如果开发板成功连接服务器
    mqttClient.loop();           // 处理信息以及心跳
  } else {                       // 如果开发板未能成功连接服务器
    goStop();
    connectMQTTserver();  // 则尝试连接服务器
  }
}


// 连接MQTT服务器并订阅信息
void connectMQTTserver() {
  // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
  String clientId = "esp8266-" + WiFi.macAddress();

  // 连接MQTT服务器
  if (mqttClient.connect(clientId.c_str())) {
    Serial.println("MQTT Server Connected.");
    Serial.println("Server Address:");
    Serial.println(mqttServer);
    Serial.println("ClientId: ");
    Serial.println(clientId);
    subscribeTopic();  // 订阅指定主题
  } else {
    Serial.print("MQTT Server Connect Failed. Client State:");
    Serial.println(mqttClient.state());
    delay(5000);
  }
}


// 收到信息后的回调函数
// char Ym0[5], Fx0[5];  //储存接受到的油门和方向数据
// int Alocation, Blocation, Clocation, Ym1, Fx1;
char Ym0[5], Fx0[5], R0[5], L0[5], M0[5];                                                    //储存接受到的油门和方向数据
int Alocation, Blocation, Clocation, Dlocation, Elocation, Flocation, Ym1=1850, Fx1=1850, R1=90, L1=90, M1=0;  //定义解析标记位
// 收到信息后的回调函数
//A1827B1835C180D180E0F
void receiveCallback(char* topic, byte* payload, unsigned int length) {
  if ((char)payload[0] == 'A')  //检测esp8266是否联网
  {
    LeCar = 1;
  } else {
    LeCar = 0;
  }

  int j = 0, k = 0, l = 0, n = 0, o = 0;
  for (int i = 1; i < 6; i++) {
    if ((char)payload[i] != 'B') {
      Ym0[j] = (char)payload[i];
      j += 1;
    } else {
      Blocation = i;
      break;
    }
  }
  for (int i = Blocation + 1; i < 11; i++) {
    if ((char)payload[i] != 'C') {
      Fx0[k] = (char)payload[i];
      k += 1;
    } else {
      Clocation = i;
      break;
    }
  }
  for (int i = Clocation + 1; i < 15; i++) {
    if ((char)payload[i] != 'D') {
      R0[l] = (char)payload[i];
      l += 1;
    } else {
      Dlocation = i;
      break;
    }
  }
  for (int i = Dlocation + 1; i < 19; i++) {
    if ((char)payload[i] != 'E') {
      L0[n] = (char)payload[i];
      n += 1;
    } else {
      Elocation = i;
      break;
    }
  }
  for (int i = Elocation + 1; i < length; i++) {
    if ((char)payload[i] != 'E') {
      M0[o] = (char)payload[i];
      o += 1;
    } else {
      Flocation = i;
      break;
    }
  }
  // Ym1 = String(Ym0).toInt();
  // Fx1 = String(Fx0).toInt();
  Fx1 = String(Ym0).toInt();
  Ym1 = String(Fx0).toInt();
  R1 = String(R0).toInt();
  L1 = String(L0).toInt();
  M1 = String(M0).toInt();
  //Ym1 = map(Ym1,YmMedian+50, 4095, 0, 255);
  //  Serial.print("Ym=");
  //  Serial.print(Ym1);
  //  Serial.print(",FX=");
  //  Serial.println(Fx0);

  for (int i = 0; i < 5; i++) {
    Ym0[i] = '\0';
    Fx0[i] = '\0';
    R0[i] = '\0';
    L0[i] = '\0';
    M0[i] = '\0';
  }
}

// 订阅指定主题
void subscribeTopic() {
  char subTopic[topicString.length() + 1];
  strcpy(subTopic, topicString.c_str());
  if (mqttClient.subscribe(subTopic)) {
    Serial.println("Subscrib Topic:");
    Serial.println(subTopic);
  } else {
    Serial.print("Subscribe Fail...");
  }
}

void connectWifi() {
  WiFi.begin(ssid, password);
  //等待WiFi连接,成功连接后输出成功信息
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi Connected!");
  Serial.println("");
}
int Speedy1, Speedy2, Speedx1, Speedx2, direction;
void touch() {
  //    Serial.print(Ym1);
  //     Serial.print(",");
  //     int a=YmMedian+50;
  //     Serial.println(a);
  if (Ym1 > YmMedian + 50) {
    Ym = map(Ym1, YmMedian + 50, 4095, 0, 255);
    Speedy1 = Ym;
    direction = 0;
    //   Serial.print("Speedy1:");
    //   Serial.println(Speedy1);
    Speedy2 = 0;
  } else if (Ym1 < YmMedian - 50) {
    Ym = 255 - map(Ym1, 0, YmMedian - 50, 0, 255);
    Speedy1 = 0;
    direction = 1;
    Speedy2 = Ym;
    //    Serial.print("Speedy2:");
    //    Serial.println(Speedy2);
  } else if (Fx1 > FxMedian + 50) {
    Fx = map(Fx1, FxMedian + 50, 4095, 0, 255);
    Speedx1 = Fx;
    //   Serial.print("Speedx1:");
    //   Serial.println(Speedx1);
    Speedx2 = 0;
  } else if (Fx1 < FxMedian - 50) {
    Fx = 255 - map(Fx1, 0, FxMedian - 50, 0, 255);
    Speedx1 = 0;
    Speedx2 = Fx;
    //    Serial.print("Speedx2:");
    //    Serial.println(Speedx2);
  } else if (YmMedian - 50 < Ym1 < YmMedian + 50 && FxMedian - 50 < Fx1 < FxMedian + 50) {
    Speedy1 = 0;
    Speedy2 = 0;
    Speedx1 = 0;
    Speedx2 = 0;
    Ym = 0;
    Fx = 0;
  }
  if (LeCar == 1)
    GoCar();
  else
    goStop();
}


void GoCar() {
  analogWrite(IN_1, Speedy1 + Speedx1 + Speedy2);
  digitalWrite(IN_2, direction);
  analogWrite(IN_3, Speedy1 + Speedx2 + Speedy2);
  digitalWrite(IN_4, direction);
  Serial.println("IN_1: " + String(Speedy1 + Speedx1 + Speedy2) + "IN_3: " + String(Speedy1 + Speedx2 + Speedy2));
  Serial.println("A" + String(Ym1) + "B" + String(Fx1) + "C" + String(R1) + "D" + String(L1) + "E"+ String(M1) + "F");
  servo_R.write(R1);             	// 舵机角度写入
  servo_L.write(L1);             	// 舵机角度写入
  digitalWrite(LED,M1);
}



void goStop() {
  digitalWrite(IN_1, LOW);
  digitalWrite(IN_2, LOW);
  digitalWrite(IN_3, LOW);
  digitalWrite(IN_4, LOW);
  digitalWrite(LED, LOW);
  servo_R.write(90);             	// 舵机角度写入
  servo_L.write(90);             	// 舵机角度写入
  // delay(100);
}

6.  实验效果

首先是搭建接收机和发射机硬件结构,然后下载程序,检查设备接线,测试15组关键字功能

  • max7800串口打印效果
21:05:31.663 -> ***** Init *****
21:05:31.664 -> pChunkBuff: 128
21:05:31.665 -> pPreambleCircBuffer: 3840
21:05:31.668 -> pAI85Buffer: 16384
21:05:31.737 -> Error opening SD card: FR_NOT_READY
21:05:31.741 -> *** !!!SD ERROR (mounting) !!! ***
21:05:31.744 -> 
21:05:31.744 -> *** I2S & Mic Init ***
21:05:33.741 -> 
21:05:33.741 -> *** READY ***
21:05:42.895 -> 136448 Word starts from index: 132480, avg:499 > 350 
21:05:43.371 -> 144000: Word ends, Appends 4992 zeros 
21:05:43.375 -> 144000: Starts CNN: 1
21:05:43.376 -> 
21:05:43.378 -> 144000: Completes CNN: 1
21:05:43.381 -> CNN Time: 1843 us
21:05:43.382 -> Min: -128,   Max: 127 
21:05:43.384 -> ----------------------------------------- 
21:05:43.388 -> Detected word: ON (96.5%)
21:05:43.391 ->  count : 0: 1: a
21:05:43.392 -> 
21:05:43.392 -> ----------------------------------------- 
21:05:43.454 -> Error opening SD card: FR_NOT_READY
21:05:43.455 -> *** !!!SD ERROR!!! ***
21:05:43.457 -> 
21:05:43.457 -> 
21:05:43.457 -> *** READY ***
21:05:47.081 -> 201984 Word starts from index: 198016, avg:407 > 350 
21:05:47.502 -> 208640: Word ends, Appends 5888 zeros 
21:05:47.507 -> 208640: Starts CNN: 2
21:05:47.511 -> 208640: Completes CNN: 2
21:05:47.513 -> CNN Time: 1843 us
21:05:47.518 -> Min: -115,   Max: 110 
21:05:47.518 -> ----------------------------------------- 
21:05:47.520 -> Detected word: OFF (100.0%)
21:05:47.523 ->  count : 1: 2: f
21:05:47.524 -> 
21:05:47.524 -> ----------------------------------------- 
21:05:47.583 -> Error opening SD card: FR_NOT_READY
21:05:47.586 -> *** !!!SD ERROR!!! ***
21:05:47.588 -> 
21:05:47.589 -> 
21:05:47.589 -> *** READY ***
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

FmvjxWYAAOCSJiEh999zVzdFD9x_

  • esp8266T串口打印效果
21:05:02.727 -> rll��|�l�|�$�#|����r�c�"�p�N�lNn���bp��ls$rlp�N��$��#n�|���b��nn�$��l �Nn�{lor���o{r� p�o�r������bN��o�c��no��d �Nn�lor���Nrl`r��or$`����l$`��N�dhello
21:05:03.814 -> .....
21:05:08.553 -> WiFi Connected!
21:05:08.553 -> 
21:05:08.740 -> MQTT Server Connected.
21:05:08.740 -> Server Address: 
21:05:08.740 -> test.ranye-iot.net
21:05:08.740 -> ClientId:
21:05:08.740 -> esp8266-C8:2B:96:08:73:AD
21:05:08.740 -> A1850B1850C120D60E0F
21:05:09.051 -> A1850B1850C120D60E0F
21:05:09.361 -> A1850B1850C120D60E0F
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 Fj7L7RqDRdRj_te8AzkjM8xJOg7XwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

  • esp8266R串口打印效果

 

21:09:05.523 -> sdlܟ<�l�<�l�c|����r�#�c��gn�dog���c8��lrd;lx�o��d��#g�|�Ǐ#��ng�$��l �'o�dgs���ors�`p�'�s�ܜ���co�<�c��'o��d`�'o�dgs���or$`r��gsl`�����d`��g�d........
21:09:14.394 -> WiFi Connected!
21:09:14.394 -> 
21:09:14.797 -> MQTT Server Connected.
21:09:14.797 -> Server Address:
21:09:14.797 -> test.ranye-iot.net
21:09:14.797 -> ClientId: 
21:09:14.797 -> esp8266-48:55:19:16:40:C1
21:09:14.797 -> Subscrib Topic:
21:09:14.797 -> minivorCar214923790t7
21:09:18.079 -> IN_1: 0IN_3: 0
21:09:18.079 -> A1850B1850C120D60E0F
21:09:18.388 -> IN_1: 0IN_3: 0
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

 FpRy6rJ4dz6PHkI7fSZEPEGcmJgtwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

演示视频:基于MAX7800羽毛板语音控制ESP8266小车_哔哩哔哩_bilibili

FpxVR-WbKHXxQCQ4swB1nvGdXqrl

 

7. 总结

  • 这是我第二次参加嵌入式相关的网上比赛活动,第一次是RT-Thread的【基于RT-Thread+RA6M4的智能鱼缸系统设计之鱼我所欲也】活动,作品是022年暑假做的获得第六名,还是比较开心!
  • 这次最大的收获是MAX7800与ESP8266串口通讯,本来单个字符测试时是可以正常通讯接收,但是移植到KWS20 demo上就不行,所以我另辟蹊径一个一个字符扫描试验测试出15个操作码,刚好把20个关键字简化成15个操作码进行通讯!
  • 这次最大的遗憾是语音控制小车受环境噪音影响较大,我都把识别率卡在90%以上但还是户外效果比较差,室内好一些,如果WiFi网络好大概有0.5s延时,大家也可以尝试二次识别确定的方法减少影响。
  • 非常感谢硬禾官方组织的这场MAX7800设计大赛,大家都为这个国内嵌入式生态出一份力,只要努力认真做了都会有所收获,期盼这些作品在将来某一天为构建美好未来贡献一份微博之力!

虽然这次比赛初期我没有进行kws20 demo的训练试验,但是勇敢的我说是站在巨人的肩膀上,有点小遗憾我的时间和电脑都比较拉胯,下次争取做一些训练,跑一跑模型!🛹🛹🛹我们每天都一点点结合联动丰富生活,从而实现对外部世界进行充分的感知,尽最大努力认识这个有机与无机的环境,科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
🥳🥳🥳再次非常感谢硬禾官方支持和胡同学,乔大哥等等🥳🥳🥳期待这一次的成绩哟!

参考文献

 

附件下载
SCH_基于MAX7800羽毛板语音控制ESP8266小车_2022-12-27.json
立创EDA电路原理图
团队介绍
本团队基于MAX7800羽毛板语音控制ESP8266小车开发
团队成员
2345vor
“长风破浪会有时,直挂云帆济沧海”。珍惜身边的一切,慢慢去积累自己所需要的,把握好现在就是把握未来,在接下来的未来不断奉献自己服务社会。我始终坚信:尊重现实,无所畏惧将超乎一切!
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号