分享三个例程:使用ESP32做一个实时语音对讲机,MQTT协议实现公网对讲,ESP32-CAM 上传图像数据到网络。并附上例程链接。可以比对着看来学习NOW,MQTT使用方法。想办法把图像和语音代码融合在一起,实现声音和图像同步传输。
2022-08-05 C/C++编程,ESP32/ESP8266 4 条评论 3311 次阅读
最近突发奇想,制作一个数字式的音频通信系统,顺便学习一下I2S与ESP-NOW协议
刚开始查阅资料,发现油管上的已经有人做过了,但是使用的C++,并用了数据流的处理流程,比较晦涩难懂,代码量也很大,属于是炫技的作品,我这篇文章使用C重新写一遍,并且代码量大大降低,也很容易理解,let’s get started!
上一篇博客中已经详细讲解了I2S通信协议,在ESP32中有两个硬件I2S,我们可以通过简单的配置使用它
ESP-NOW是乐鑫的私有协议,相比于传统的七层协议,ESP-NOW只保留了物理层与数据连接层,在其之上封装了ESP-NOW协议,但是这个协议是不开源的,意味着我们看不到这个协议的源码,只能使用封装出来的接口。
使用的硬件:
麦克风:INMP441
功放:MAX98357
主控:ESP32*2
按键与指示灯:板载
使用的软件:
Arduino IDE
ESP32扩展包 2.0.4(最新)
编程思路:
发送:初始化I2S采集后,数据会源源不断的存放在DMAbuf中,按下按键,将数据取出,转换成需要的格式,最终转换成8bit数据发送出去。
接受:在ESP-NOW接受回调函数中,将接收到的数据重新转换为16位, 发送到I2S中的DMA去。注意,在没有接收到消息的是否,需清空dambuf,要不然会重复播放上一帧的音频,发出很大的噪音。
数据流向:I2S采集->原始32bit->16bit->8bit->esp-now发送->接收到数据8bit->转换为16bit->I2S播放
具体配置:
麦克风采集配置
const i2s_config_t rec_i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S| I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 2,
.dma_buf_len = 256,
}
扬声器采集配置:
const i2s_config_t spk_i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
.communication_format =(i2s_comm_format_t)(I2S_COMM_FORMAT_I2S),
.intr_alloc_flags = 0,
.dma_buf_count = 2,
.dma_buf_len = 256,
};
其中,dambuf的两个参数的设定比较有讲究,可以点击这里学习。
硬件连接:
INMP441:
BCK —>>>IO4
WS —>>>IO15
data—>>>IO13
MAX98357:
BCK —>>>IO26
WS —>>>IO22
data—>>>IO25
开源github链接:https://gitlab.ifengyu.com/tianshuaikang/esp32-audio-communication
使用MQTT做一个公网对讲机
上一篇博客中,使用ESP32与ESP-NOW协议做了一个短距离对讲机(https://www.bilibili.com/video/BV1qT411L7WX),发布了一个视频在B站。
评论区中,很多B友希望可以实现无限距离对讲,这样的话需要服务器转发,刚开始我想使用python写一个TCP或者UDP的转发功能,但是考虑到很多小白没有公网服务器,并且也不会使用python,于是我想到了互联网中的MQTT协议,MQTT协议天生为转发而生;MQTT协议是TCP协议的应用,是一个基于客户端-服务器的消息发布/订阅传输协议,MQTT使用的发布/订阅消息模式,提供了一对多的消息分发机制,从而实现与应用程序的解耦。这是一种消息传递模式,消息不是直接从发送器发送到接收器(即点对点),而是由MQTT server(或称为 MQTT Broker)分发的。
我曾经也有一篇文章简单的叙述了MQTT的工作原理与搭建过程(怎样在自己的电脑上搭建MQTT),MQTT可以运行在几乎所有的设备上,例如公网服务器(Linux、Windows系统等)、树莓派、局域网电脑等,当然,如果MQTT服务运行在局域网中,那就只能实现局域网的消息转发。如果你没有公网服务器,可以使用我的MQTT地址,我的MQTT服务器将会免费开放到2025年。我使用的MQTT Broker为EMQ免费开源版,并且修改了cfg文件,将速率与可接入设备限制调高。
如果你只想实现无限对讲,不想关注MQTT,那就看下面的代码吧,代码git链接放在文章最后!
首先需要在Arduino中安装pubsubclient库,这个库是可以运行在ESP上的MQTT库,源码也非常简单,非常值得学习。
代码上基于上个视频的硬件与代码,硬件连接完全相同,软件只进行了通信协议的修改。(重点:)代码中需要修改的只有两个地方,第一个地方是WIFI账号与密码(2.4G),第二个地方是修改角色,发送端或者接收端,具体位于AudioMqtt.h中的SENDER宏定义,你需要将该项设置为1,编译烧录给带有麦克风的设备,再将该项设置为0,编译烧录给带有扬声器的设备。烧录给两个设备,按下发射端的板载按钮说话,你应该就可以听到扬声器中你的声音了。
我们已经了解了MQTT协议的工作原理,我们也可以在电脑上写一段python脚本订阅发布的音频数据并且播放出来,python需要的库为paho-mqtt与pyaudio,代码也在git中,是audiotest.py。
git地址:https://gitlab.ifengyu.com/tianshuaikang/esp32audiomqtt
ESP32-CAM上传图像数据到Python上位机
预防踩坑放在前面:目前安信可官方版的CAM模块已经停售,库存货比较贵,市面上的都是仿制的,质量良莠不齐,给开发带来了很多困扰,经过实际测试,发现山寨货主要有以下几个坑:
1、发热严重,不加散热片长时间运行几乎到了烫手的地步,加上散热片也不会好很多
2、在分辨率大于640*480后,信号变得异常的差,丢包严重,但如果用手捏着天线,数据传输又会稳定很多
第二个原因上网查原因可能是山寨CAM天线附近包了一层地,那么如何避免踩坑?
目前网上的山寨CAM主要有以下两种,位于卡槽右下角的电阻,一种是四个电阻,另一种是六个电阻,建议买四个电阻的(官方板是四个电阻),效果比六个电阻的好很多。
强烈建议外置天线,视频信号穿墙也不是问题,使用外置天线需要修改一个电阻,将原来的电阻拆掉,一坨锡搞过来,买个天线装上就可以了
烧录接线:
一句话讲明白下载:IO0为低电平,复位后为程序下载模式,IO0为高电平,复位后为程序运行模式,所以要进入下载模式,按照这样接线
USB-TTL-------ESP32CAM
TX------------UOR
RX------------UOT
5V------------5V
GND-----------GND
GND-----------IO0
按下小小的复位按钮,就进入下载模式,可以用Arduino烧录程序
下载完成后,将IO0上的线拔掉,复位后就可以运行程序了
程序原理:
初始化ov2640,设定引脚、速率、图片像素、图像格式等参数,另外,板载的SRAM可以设定更大的像素
重点:为什么使用jpeg的图片格式?jpeg是图片的一种有损压缩格式,我们使用网络传输可以节省很多带宽,提升帧率,举个例子,一张320*240像素的图像原始大小为15.36KB,压缩后可能只有2KB。而且OV2640可以直接输出jpeg格式的图片,不用处理器压缩占用时间
我们使用arduino上封装好的库,可以直接获取图片数组,因为ESP32 UDP每个包最大只有1460字节,因此还需要分包发送,我将图片分为1400个字节+1字节的索引,索引代表这个图片的第几个包,如果为0XFF代表这长图片传输完成了,分包代码如下:
uint8_t *P_temp = fb->buf; //暂存指针初始位置
int pic_length = fb->len; //获取图片字节数量
int pic_pack_quantity = pic_length / max_packet_byte; //将图片分包时可以分几个整包
int remine_byte = pic_length % max_packet_byte; //余值,即最后一个包的大小
for (int j = 0; j < pic_pack_quantity; j++) //发送图片信息,这是按分包循环发送,每一次循环发送一个包
{
sendImgAray(fb->buf, max_packet_byte,j); //将图片分包发送
fb->buf+=max_packet_byte; //图片内存指针移动到相应位置
}
sendImgAray(fb->buf, remine_byte,0xFF); //发送最后一个包,剩余的数据
python上位机:
python上位机用UDP协议,修改为当前机器的IP地址和端口号,接受数据后通过判断数据最后一位确定图片是否传输完成,将接收到的数组转换成opencv的img图像,图像就显示出来了,可以通过w、s按钮调整灯光亮度。如果没有图像数据,整个界面是卡死的状态。
代码开源地址:https://gitlab.ifengyu.com/tianshuaikang/espcam2pc.git