通过ESP8266模块与JQ8900模块与RTT实现手机远程控制音乐播放

本例程是一个简单的串口通信的交互例程,通过了RT-thread系统来实现。

简要叙述程序思路:
  1. 创建两个进程句柄,serial与serial2。
  2. 调用一系列初始化串口设备的函数,分别是rt_device_find(查找设备),rt_device_set_rx_indicate(设置接收回调函数)。
  3. 启动线程,调用函数并检测接受到的信息
  4. 接收到对应信息后调用函数向JQ8900模块发送命令
  5. 同时保留了按钮操控的效果,通过rt_pin_attach_irq设置按钮中断函数

以下给出源码:

/*
基于RTT的手机通过esp8266操作JQ8900功放模块的一种方式
所用开发板 野火指南者
WiFi模块   ESP8266
功放模块   JQ8900
RTT版本    4.0.2(野火BSP)
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#ifndef KEY0_PIN_NUM
    #define KEY0_PIN_NUM      GET_PIN(A, 0) /* PA0 Key1 pin脚*/
#endif
#ifndef KEY1_PIN_NUM
    #define KEY1_PIN_NUM      GET_PIN(C, 13) /* PC13 Key2 pin脚*/
#endif
#ifndef SAMPLE_UART_NAME
#define SAMPLE_UART_NAME       "uart2"        /* JQ8900所接串口 请先事先在env中打开*/
#endif
#ifndef UART_NAME
#define UART_NAME              "uart3"        /* ESP8266所接串口*/
#endif
static rt_device_t serial;   
static rt_device_t serial2;   
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 句柄默认设置 */
static struct rt_semaphore rx_sem;
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(&rx_sem);
    return RT_EOK;
}
static void Re_music(void *args)
{
    static char buffer1[4]={0xAA,0x05,0x00,0xAF};    //JQ8900上一曲命令
    rt_uint32_t tx_length;
    tx_length=4;
    int i = 0;
    while(i < 4){
    rt_device_write(serial,0,&buffer1[i],tx_length);  //循环发送
    i++;}
    rt_kprintf("jun\n");
}
static void next_music(void *args)
{
    static char buffer1[4]={0xAA,0x06,0x00,0xB0};    //JQ8900下一曲命令
    rt_uint32_t tx_length;
    tx_length=4;
    int i = 0;
    while(i < 4){
    rt_device_write(serial,0,&buffer1[i],tx_length);
    i++;}
    rt_kprintf("bian\n");
}

static void nmsl(void *parameter){
  
      char ch;

    while (1)
    {
        while (rt_device_read(serial2, -1, &ch, 1) != 1)
        {
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
        if(ch == 'a'){         //esp8266检测接受信息,为a则调用函数
          Re_music(0);
        }
        if(ch == 'b'){
          next_music(0);
        }

}
}

static void serial_thread_entry(void *parameter)
{
  //  char ch;
    while (1)
      
    {    
    rt_pin_mode(KEY0_PIN_NUM, PIN_MODE_INPUT_PULLUP);
    rt_pin_attach_irq(KEY0_PIN_NUM, PIN_IRQ_MODE_FALLING, Re_music, RT_NULL);   //设置按钮中断回调函数
    rt_pin_irq_enable(KEY0_PIN_NUM, PIN_IRQ_ENABLE);
    rt_pin_mode(KEY1_PIN_NUM, PIN_MODE_INPUT_PULLUP);
    rt_pin_attach_irq(KEY1_PIN_NUM, PIN_IRQ_MODE_FALLING, next_music, RT_NULL);
    rt_pin_irq_enable(KEY1_PIN_NUM, PIN_IRQ_ENABLE);    
    }
}
static int mdr5(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    char uart_name[RT_NAME_MAX];
    if (argc == 2)
    {
        rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
    }
    else
    {
        rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
    }
    serial = rt_device_find(uart_name);
    serial2 = rt_device_find(UART_NAME);
    if (!serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return RT_ERROR;
    }
    if (!serial2)
    {
        rt_kprintf("find %s failed!\n", UART_NAME);
        return RT_ERROR;
    }
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
    config.baud_rate = BAUD_RATE_9600;    //串口设置为9600
    config.data_bits = DATA_BITS_8;
    config.stop_bits = STOP_BITS_1;
    config.parity = PARITY_NONE;
    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
    rt_device_set_rx_indicate(serial, uart_input);
    
    rt_device_open(serial2, RT_DEVICE_FLAG_INT_RX);
    rt_device_set_rx_indicate(serial2, uart_input);
    rt_thread_t thread1 = rt_thread_create("serial2", nmsl, RT_NULL, 1024, 25, 10);
        if (thread1 != RT_NULL)
    {
        rt_thread_startup(thread1);
    }
    else
    {
        ret = RT_ERROR;
    }
    
    
    rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        ret = RT_ERROR;
    }
    return ret;
}
MSH_CMD_EXPORT(mdr5, uart JUN);

值得注意的是,JQ8900模块的波特率是9600,需要使用rt_device_control函数来设置串口波特率,而esp8266则不用

本程序还设置了一个接受信号量,用来在串口接收到信息之后发送信号量,来表示已经接受到数据。

通过串口方式给ESP8266烧写固件

其中ESP8266的VCC脚为3V3,CH_PD脚为EN脚,GPIO0为IO0脚。

首先按照这个方式将esp8266与开发板连接起来(需要先拔掉串口1和串口3上的总共4个跳帽)

具体连接方式参考

其中3V3口与EN口可接3V3,也可不接

由于野火开发板的esp8266的flash只有512K的大小,所以烧不了安信可的最新固件,只能烧勉强不算太旧的固件,这里给出安信可的V0.9.5.6固件

下载ESP8266一键下载工具       提取码:7z71

将开发板上电后

打开esp8266一键下载工具,切到配置页面

只保留//Flash前的小叉,去掉另外3个小叉,之后再点击//Flash旁的小齿轮,选择到刚刚下载的固件

确认线没有接错之后,切换到操作界面,选择CH340所对应的端口号,点击一键烧写

没有错误的话,将会开始烧写过程,等待进度条跑完,左下角会显示绿色的勾

由于笔者将会使用RTT的AT组件进行调试,如果不使用RTT的话,可以直接打开SScom进行ESP8266的调试工作

输入AT+GMR将会显示固件信息

断电,拔掉所有外接杜邦线,并接回跳帽,重新上电,观察串口传回信息,并在At client中验证版本信息

 \ | /
- RT -     Thread Operating System
 / | \     4.0.2 build Aug 18 2019
 2006 - 2019 Copyright by rt-thread team
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/at.clnt] AT client(V1.3.0) on device uart3 initialize success.
[D/at.dev] the network interface device(esp0) set up status
[D/at.dev] esp8266 device(esp0) initialize start.
msh />[I/SDIO] SD card capacity 15273984 KB.
found part[0], begin: 4194304, size: 14.576GB
[E/at.clnt] Read response buffer failed. The Response buffer size is out of buffer size(512)!
[I/app.card] sd card mount to '/'
[D/at.dev] AT version:0.21.0.0
[D/at.dev] SDK version:0.9.5
[D/at.dev] 
[I/at.dev] esp8266 device(esp0) network initialize successfully.
[E/at.dev] esp8266 device(esp0) prase "AT+CIPSTA?" command resposne data error.
at client
======== Welcome to using RT-Thread AT command client cli ========
Cli will forward your command to server port(uart3). Press 'ESC' to exit.
AT+GMR
AT version:0.21.0.0
SDK version:0.9.5

OK

ESP8266烧写固件结束

ESP8266作为Server的一种简单透传方式|基于RT-thread

/*
通过RTT例子修改
通过ESP8266作为Server,手机作为Client的穿透方式
通过手机控制野火开发板板载RGB灯的程序
*/

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>


#define LED0_PIN    GET_PIN(B, 1)    //定位到野火开发板上两个RGB相关的寄存器上
#define LED0_PINE   GET_PIN(B, 0)
#define SAMPLE_UART_NAME       "uart3"

static rt_device_t serial;              /* 串口设备句柄 */
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 配置参数 */


/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;

/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static void serial_thread_entry(void *parameter)
{
    char ch;

    while (1)
    {
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
        while (rt_device_read(serial, -1, &ch, 1) != 1)
        {
            /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
 //       检测接受缓存区内的字符,与手机发送字符对应,已达到检测开灯的效果
          if(ch == 'a'){
         rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
         rt_pin_mode(LED0_PINE, PIN_MODE_OUTPUT);
          }
          if(ch == '0'){
         rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
         rt_pin_mode(LED0_PINE, PIN_MODE_OUTPUT);
          }
          if(ch == 'u'){
         rt_pin_write(LED0_PIN, PIN_HIGH);
         rt_thread_mdelay(500);
          }
          if(ch == 'n'){
         rt_pin_write(LED0_PIN, PIN_LOW);
         rt_thread_mdelay(500);
          }					
          if(ch == 'b'){
        rt_pin_write(LED0_PINE, PIN_HIGH);
        rt_thread_mdelay(500);
          }			
          if(ch == 'i'){
        rt_pin_write(LED0_PINE, PIN_LOW);
        rt_thread_mdelay(500);
          }									
    }
}

static int mdr1(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    char uart_name[RT_NAME_MAX];

    if (argc == 2)
    {
        rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
    }
    else
    {
        rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
    }

    /* 查找系统中的串口设备 */
    serial = rt_device_find(uart_name);

    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
    if (!serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
    /* 打开设备后才可修改串口配置参数 */
    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial, uart_input);
    /* 发送字符串 */
//    rt_device_write(serial, 0, str, (sizeof(str) - 1));

    /* 创建 serial 线程 */
    rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
    /* 创建成功则启动线程 */
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        ret = RT_ERROR;
    }

    return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mdr1, uart JUN);

以上先给出例程,将其放入RTT的工程中进行编译并烧入开发板

在完成实验之前请确保esp8266与手机在同一局域网下

打开SSCOM,看到RTT的开机信息后输入:at client

在AT界面中输入

  • AT+CIPMUX=1
  • AT+CIPSERVER=1,8081
  • AT+CIFSR
msh />at client
======== Welcome to using RT-Thread AT command client cli ========
Cli will forward your command to server port(uart3). Press 'ESC' to exit.
AT+CIPMUX=1

OK


AT+CIPSERVER=1,8081

OK
AT+CIFSR
+CIFSR:STAIP,"192.168.0.110"
+CIFSR:STAMAC,"cc:50:e3:72:62:2d"

OK

 

 

 

打开手机app 有人网络调试助手

切换到TCP client,选择 增加,在IP地址中填入你esp8266的ip地址

点击增加,显示连接成功

在手机端发送 ‘a’ 字符,可以看到开发板上的RGB灯亮了

再尝试0,u,n,b,i,开发板分别做出了不同反应


分析一下代码,先创建了用于接受消息堵塞的信号量

在创建了一个回调函数<uart_input>,若串口以中断接收模式打开,当串口接收到一个数据产生中断时,就会调用回调函数,并且会把此时缓冲区的数据大小放在 size 参数里,把串口设备句柄放在 dev 参数里供调用者获取。

然后创建了一个用户入口函数<serial_thread_entry>主要用来读取数据后检测并使能GPIO

然后创建了一个线程函数,进行初始化信号量和串口基本环境初始化,成功后启动线程,并调用用户入口函数,来达到预期效果

 

野火指南者通过RT-Thread的AT组件连接板载ESP8266

先按照RTT官网的AT组件连接esp8266的教程,准备连接esp8266

开发板平台:野火 指南者开发版

CPU:STM32F103VE

RT-Thread版本:4.0.2

一、安装并配置ENV

安装env软件的方法请参考RTT的官方教程

安装完env后在野火的RTT项目中右键Con Emu Here

 在命令行中运行menuconfig

 

 进入到控制台界面

-配置串口支持(野火版上默认esp8266接uart3):进入到Hardware Drivers Config –>On-chip Peripheral Drivers –>Enable UART –>Enable UART3

-开启AT组件功能:进入到RT-Thread Components–>Network–>AT commands

AT Client 配置选项介绍如下:

  • Enable debug log output:配置开启调试日志;
  • Enable AT commands client:配置开启 AT 客户端;
  • The muxinum number of supported clients: 配置最大同时支持的客户端数量,该例程使用单客户端连接,配置为 1 即可。
  • Enable BSD Socket API support by AT commands: 配置开启 BSD Socket API 支持。
  • Enable CLI(Command-Line Interface) for AT commands: 配置开启 AT 命令行交互模式。

 

Enable print RAW format AT command communication data: 配置开启收发数据实时打印功能。

-开启SAL组件:
进入到RT-Thread Components–>Network–>Socket abstraction layer

打开Enable socket abstraction layer 和 Enable BSD socket operated by file system API

-开启AT-decive:进入到RT-Thread online packages –> IoT – internet of things –> AT DEVICE

打开后进入Espressif ESP8266

在WIFI ssid与WIFI password中填写开发板要连接的wifi网络

把AT client device name修改成uart3

-配置完成,将光标移向env下方的Save,回车保存后退出到命令行界面

在命令行中输入:pkg –update

再输入:scons –target=mdk5 来重新生成项目

二、编译调试项目

打开项目文件.uvprojx

将项目编译后,通过下载器烧录进野火指南者开发板,打开sscom观察串口内的信息:

 

 \ | /
- RT -     Thread Operating System
 / | \     4.0.2 build Aug 16 2019
 2006 - 2019 Copyright by rt-thread team
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/at.clnt] AT client(V1.3.0) on device uart3 initialize success.
[D/at.dev] the network interface device(esp0) set up status
[D/at.dev] esp8266 device(esp0) initialize start.
[E/at.clnt] Read response buffer failed. The Response buffer size is out of buffer size(256)!
[D/at.clnt] execute command (ATE0) timeout (5000 ticks)!
[I/at.dev] esp8266 device(esp0) initialize retry...
[D/at.clnt] execute command (ATE0) timeout (5000 ticks)!
[I/at.dev] esp8266 device(esp0) initialize retry...
[D/at.clnt] execute command (ATE0) timeout (5000 ticks)!
[I/at.dev] esp8266 device(esp0) initialize retry...
[D/at.clnt] execute command (ATE0) timeout (5000 ticks)!
[I/at.dev] esp8266 device(esp0) initialize retry...
[D/at.clnt] execute command (ATE0) timeout (5000 ticks)!
[I/at.dev] esp8266 device(esp0) initialize retry...
[E/at.dev] esp8266 device(esp0) network initialize failed(-1).

我们发现串口报告的错误信息有两个

一个是Read response buffer failed. 缓冲区内存过小,那我们就直接增大就好了

一个是command (ATE0)运行超时,ATE0是AT指令中的关闭回显,出现这个问题的原因可能是AT组件初始化时间过短

-解决缓冲区内存过小

打开项目文件,定位到AT/at_client.c

将第364行修改成

resp = at_create_resp(512, 0, rt_tick_from_millisecond(300));

即可解决问题

-解决ATE0运行超时

 

打开项目文件,定位到at_device/at_device_esp8266.c

我们在第632行看到了相关的ATE0的代码

AT_SEND_CMD(client, resp, "AT+RST");
/* reset waiting delay */
rt_thread_mdelay(2000);
/* disable echo */
AT_SEND_CMD(client, resp, "ATE0");

将rt_thread_mdelay函数的延迟时间修改成2000ms,让AT+RST有足够时间来运行

即可解决问题

-将项目重新编译后,烧录进开发板,继续观察串口内容
 \ | /
- RT -     Thread Operating System
 / | \     4.0.2 build Aug 16 2019
 2006 - 2019 Copyright by rt-thread team
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/at.clnt] AT client(V1.3.0) on device uart3 initialize success.
[D/at.dev] the network interface device(esp0) set up status
[D/at.dev] esp8266 device(esp0) initialize start.
[D/at.dev] AT version:0.22.0.0(Mar 20 2015 10:04:26)
[D/at.dev] SDK version:1.0.0
[D/at.dev] compile time:Mar 20 2015 11:00:56
[D/at.dev] 
[I/at.dev] esp8266 device(esp0) network initialize successfully.
[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!
[W/at.dev] please check and update device(esp0) firmware to support the "AT+CIPDNS_CUR?" command.
[E/at.clnt] execute command (AT+CIPDOMAIN="link.rt-thread.org") failed!

又发现了两个错误

[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed! [W/at.dev] please check and update device(esp0) firmware to support the “AT+CIPDNS_CUR?” command. [E/at.clnt] execute command (AT+CIPDOMAIN=”link.rt-thread.org”) failed!

这些错误的原因是由于野火板载的ESP8266的固件版本太低造成的,可以通过刷最新的安信可ESP8266固件来解决,本文不做过多展开。

我们在串口中输入 at client 命令,并输入 AT+RST与AT+PING=”www.baidu.com”

来测试AT组件是否安装完善

msh />at client
======== Welcome to using RT-Thread AT command client cli ========
Cli will forward your command to server port(uart3). Press 'ESC' to exit.
AT+RST

OK

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x40100000, len 1320, room 16 
tail 8
chksum 0xb8
load 0x3ffe8000, len 776, room 0 
tail 8
chksum 0xd9
load 0x3ffe8308, len 412, room 0 
tail 12
chksum 0xb9
csum 0xb9

2nd boot version : 1.3(b3)
  SPI Speed      : 40MHz
  SPI Mode       : QIO
  SPI Flash Size : 4Mbit
jump to run user2

sl??
ready

AT+PING="www.baidu.com"
AT+PING="www.baidu.com"
+12

OK

输入输出结果如上,可以看到,AT组件正常运行了!