基于ESP32-S2-Mini-1模块制作的FM/网络收音机
该项目基于ESP32-S2模块的物联网/音频处理平台,搭载Arduino开发环境,协同Python socket库,实现了网络收音机和FM收音机的必要功能
标签
Arduino
ESP32
Python
收音机
2022寒假在家练
通信
Congo_red
更新2022-03-01
华北电力大学
2504

项目描述

1.1项目介绍

       此项目是以基于ESP32-S2模块的物联网/音频处理平台,搭载Arduino开发环境,协同Python socket库,实现了网络收音机和FM收音机的以下基本功能:

1可以通过WiFi接收网络上的电台,也可以通过FM模块接收空中的电台,并可以通过按键进行切换、选台

2在OLED显示屏上显示网络电台的IP地址、节目名字等相关信息或FM信号的频段

3系统能够自动校时,开机后自动调节到准确的时间(年、月、日、时、分、秒)

4可以通过FM接收电台信号,并播放出来

5通过OLED显示电台的频率

1.2项目设计思路

       项目设计思路是从实现的目标出发,首先考虑FM收音机部分,对ESP32-S2学习板上的RDA5807模块相应的技术规格进行了解,包括实现原理和音频信号处理的过程,再将其在OLED显示屏上显示,其中需要考虑校时部分,因此需要解决网络连接部分;其次是网络收音机部分,参考多种案例,选择以音频网络服务器作为音频源来源,还是以本地服务器作为音频源。通过预想所要实现的目标,评估ESP32-S2的配置,选用Arduino作为开发环境与烧写软件。

FiLknTPlV5MU7ZM3GlJN6qWZoG5p

硬件介绍

1.2.1核心控制器介绍

      本平台使用了乐鑫公司的ESP32-S2-Mini-1模块,ESP32-S2-MINI-1是一颗通用型Wi-Fi MCU模组,采用PCB板载天线,模组配置了4MB SPI flash,采用的是 ESP32-S2FN4 芯片。

1.2.2收音机模块介绍

      此项目采用的RDA5807收音机模块,RDA5807是一颗调频广播单芯片接收调谐芯片。只需要外部非常少的元器件,便可以组成一个完整的调频广播接收机。这款芯片工作电压范围2.7~3.3V。

FsoXK00pZp2C6x-QWLhs989B6FPN

1.4功能介绍

1.4.1网络连接

      无论是FM功能实现方面,还是网络收音机方面,还是校时功能,都需要网络环境,于是项目首先解决网络连接问题。本项目采用Arduino中的WIFI库实现WiFi的连接。

代码如下:

WiFiClient client;//声明一个与客户端对象,用于与服务器连接

const char *admin = "chhzqx";//指定WIFI用户名

const char *password = "821542zc";//指定WIFI密码

String Initial_WiFi(){

  int times = 0;

  WiFi.mode(WIFI_STA);//将wifi模块调成STA模式,(可能默认是它)

  WiFi.begin(admin, password);

  while(WiFi.status() != WL_CONNECTED){ //未连接上则一直循环

    u8g2.clearBuffer();

    u8g2.setFont(u8g2_font_ncenB14_tf);//设置字体信息

    u8g2.setCursor(0,32);

    u8g2.print("Connecting...");

    u8g2.sendBuffer();

    delay(400);

    times++;

    if(times >=20) break;//连接超时则跳出循环开机

  }

  return WiFi.localIP().toString(); //连接上

1.4.2时间校准

      校时部分采用了一个通用的时间同步服务,选择连接时间服务器IP :cn.pool.ntp.org,再通过代码获取时间。

代码如下:

const char *ntpServer = "pool.ntp.org";//一个公共获取时间的服务器

const long  gmtOffset_sec = 3600*8; //时区

const int   daylightOffset_sec = 3600; //夏令时

configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);//初始化时间获取信息

1.4.3 FM收音机部分

      通过查询并学习多种案例,选择在arduino中采用RDA5807收音机模块库<RDA5807.h>来完成FM收音部分,

首先也是最容易忽视的一步,通过原理图可知:FM和ESP32板输入的选择由RS2101XC6的模拟开关决定,IN输入信号为low则输出的是No引脚上的信号;IN输入信

FhX0hW6gwcwKEZ_39aotwlpd6oMi

号为High,则输出是NC引脚上的信号。因此若要使用FM模块,则需将1号对应AUDIO_SEL的电平调高。其次,也是观察电路图,和音频放大器的技术规格说明,只有当1号对应引脚SHUTDOWN为高电平时才能将输入的音频信号处理输出给扬声器输出。

FtXw7DBvoVPm44fCzzZoZ8Jz6UBy

      在调好高低电平后就可以使用<RDA5807.h>来进行编程了。

我国可收电台频率范围为87.5MHZ-108MHZ,于是选定8750作为FM初始频率,在进行遍历,到10800不停的搜索电台信号,如果有信号且Rssi大于35,则将频率计入数组中作为可用频道,设定最多10个频道,利用自定义freq_update函数将搜到的频道再进行信号排序,信号高的往前排。

代码如下:

int freq_update(int freq, int rssi){

  while(radio.isStereo()){

    radio.setFrequencyUp();

    delay(100);

    if(rssi< radio.getRssi()){

      freq= radio.getFrequency();

      rssi= radio.getRssi();//如果这个台的频道信号有更高的则替换

    }

  }

  return freq;//返回此台最好的频率值

} int freq_update(int freq, int rssi){

  while(radio.isStereo()){

    radio.setFrequencyUp();

    delay(100);

    if(rssi< radio.getRssi()){

      freq= radio.getFrequency();

      rssi= radio.getRssi();//如果这个台的频道信号有更高的则替换

    }

  }

  return freq;//返回此台最好的频率值

}

int i= 1;//步长变量

  int nation=0;//台数

  int freq;

  radio.setup();

  radio.setVolume(6);

  radio.setFrequency(8750);

  radio.setRDS(true);//初始化收音机

  for(i; i<=210; i++){

if(radio.isStereo() && radio.getRssi()>35) nations[nation++]= freq_update(radio.getFrequency(), radio.getRssi());//如果搜索到的频率有台且信号大于40,则将此台对应频率优化后记入数组

    radio.setFrequencyUp();//提高频率进行搜索

    delay(100);

    if(nation>9) break;//如果台数超过10,则搜索结束*/

Serial.println(radio.getFrequency());

1.4.4 网络收音机部分

在已经实现了网络正常连接的情况下,我是有直接访问一些含音频的服务器,然后通过解码库解码播放的思路,但是通过案例以及自己的实际试验,确实在Arduino环境下调用I2S解码库会报错,因此也是选择直接降低ESP32任务量,建立服务器,将本地已经解码好的wav格式进行数据传输,通过DAC输出的方式输出给扬声器,实现播放音乐。整个过程设计有:

1.服务器方面,采用TCP协议利用socket服务的方式进行数据传输。

2.音频处理方面,采用QQ音乐上的音频转码功能实现MP3转wav;再通过python处理降低音频文件的采样率,同时由于开发板仅有一个声道,所以也需要截取源声音的一个声道;最重要的一点在于量化位数的改变。由于DAC只是一个8位的输出,每次采样都是使用16位的数来存取声音,所以需要将16位的声音映射到8位的声音上,这样不可避免的影响了音质。

3.使用方面,将多个音乐转换后的音频文件放入python列表,通过编程实现播放歌曲结束自动播放下一曲,和可手动播放下一曲的功能。

1.4.5 按键功能部分

      FM收音机部分,设置“切换模式”,“提高音量”,“降低音量”,“换台”四个功能。

相关代码如下:

if(digitalRead(button1_pin)==LOW){

    delay(80);//防抖

    if(digitalRead(button1_pin)==LOW){

      if(Mode==1){

        client.write('q');

        client.stop();//与服务器断开连接

        write_num=0;//重新赋值为零

        read_num=0;//为下一次在进入网络声音机模式做准备

        Index=0;

        isFinish=0;

        music=0;

        timer.detach();}//若由网络模式切回其他模式则关掉客户端和定时器

      Mode = (Mode+1)%3;//利用取余实现0-1-2-0的模式切换

      if(Mode==1){

        isconnect = connectWebserver();//连接服务器

        }

  }

  }//按键1为切换模式

  if(Mode == 0){

    if(digitalRead(button2_pin)==LOW){

      delay(100);//防抖

      radio.setVolumeUp();//按键2为调高音量

    }

     if(digitalRead(button3_pin)==LOW){

      delay(100);//防抖

      radio.setVolumeDown();//按键3为减小音量

    }

     if(digitalRead(button4_pin)==LOW){

      delay(100);//防抖

      num++;

      if(num>9)num=0;//如果台数达最大则重新到1

radio.setFrequency(nations[num]);//设定指定的频率收听广播

    }

  }

    网络收音机部分,设置“切换模式”,“下一曲“”两个功能。

相关代码如下:

if(Mode == 1){

    if(digitalRead(button4_pin)==LOW || isEnd==1){//按第四个按钮或者播放完一首则下一曲

      delay(100);//防抖

      client.write('n');//向服务器发送下一曲的指令

      write_num=0;//重新赋值为零

      read_num=0;

      Index=0;

      isFinish=0;

      music = (music+1)% musics;//下一首

    }

1.4.6 屏幕显示部分

      原本我采用的是典型的OLED驱动<Adafruit_SSD1306.h>,使用它的几个实例后都没有问题,但是一旦将其用于与收音机的信息显示就出现黑屏的问题,多次尝试无果,于是采用u8g2库来驱动OLED屏。

       1开机界面 开机的时候即启动搜台函数,并显示已搜到的台数;搜寻完毕后开始连接WIFI,最后进行一个欢迎界面。

效果如图:

FgQSCnfVE8K0l6zFLzWo_3gQ9U3qFgaRbcUx9JRwGXMwEOq-ifSE2IpT

       2FM收音机界面 屏幕依次显示时间,模式状态,频率,网络IP,音量与台数信息。

效果如图:

FlwqE0cGuuqT8fZpN7i6HV6fsGgR

       3网络收音机界面 屏幕依次显示时间,模式状态,当前歌曲数和服务器端口。

效果如图:

FsCXF3dcV7T8uhcLW6NplHj8bQGt

       4信息界面 屏幕依次显示时间,模式状态,WiFi连接信息

效果如图:

FmdyfWrlErpLcinOHy1JZRysqquh

部分代码如下:

{u8g2.clearBuffer();

  u8g2.setFont(u8g2_font_ncenB14_tf);//设置字体信息

  u8g2.setFontDirection(0);   

  u8g2.setCursor(20, 32);//定位打字位置

  u8g2.print("Welcome");

  u8g2.setFont(u8g2_font_ncenB08_tf);//设置字体信息

  u8g2.setCursor(40, 50);

  u8g2.print("By Congo Red");

  u8g2.sendBuffer();}

{u8g2.clearBuffer();

    u8g2.setFont(u8g2_font_ncenB08_tf);//设置字体信息

    u8g2.setFontDirection(0);

    u8g2.setCursor(0, 10);

    u8g2.print(&timeinfo, "%Y/%m/%d  ");//年月日

    u8g2.print(&timeinfo, "  %H:%M:%S");//时分秒

    u8g2.setCursor(0, 25);

    u8g2.print("MODE : FM");

    u8g2.setCursor(0, 40);

    sprintf(str,"Freq: %.1fMHZ", Message_FM/100.0);

    u8g2.print(str);

    u8g2.setCursor(0, 55);

    sprintf(str, "Vol: %02d/%d Num: %02d/%d",  Message_vol, 15, num+1, 10);

    u8g2.print(str);

    u8g2.sendBuffer();

    delay(10);

  }
{

u8g2.clearBuffer();

    u8g2.setFont(u8g2_font_ncenB08_tf);//设置字体信息

    u8g2.setFontDirection(0);

    u8g2.setCursor(0, 10);

    u8g2.print(&timeinfo, "%Y/%m/%d  ");//年月日

    u8g2.print(&timeinfo, "  %H:%M:%S");//时分秒

    u8g2.setCursor(0, 25);

    u8g2.print("MODE : FM");

    u8g2.setCursor(0, 40);

    sprintf(str,"Freq: %.1fMHZ", Message_FM/100.0);

    u8g2.print(str);

    u8g2.setCursor(0, 55);

    sprintf(str, "Vol: %02d/%d Num: %02d/%d",  Message_vol, 15, num+1, 10);

    u8g2.print(str);

    u8g2.sendBuffer();

    delay(10);

  }
  • 项目过程中遇到的难题及解决办法

1.作为一个有一些编程基础的单片机小白,过程中遇到的最大难题是思路不清,不知从何下手,拿到一个板子怎么阅读参考资料,电路图怎么分析。

解决办法:在b站搜寻观看利用Arduino实现的项目基础视频,学习有关开发板的基础知识;进而结合电子森林和乐鑫官网提供的案例,程序等逐渐深入学习,启发思路。

2.屏幕显示这块算是开始学习时的一个较大的绊脚石,尽管通过视频案例等搞清楚驱动原理后,一直采用<Adafruit_SSD1306.h>驱动库尝试设计项目。然而发现在与网络相关代码一起运行时,屏幕老是出现黑屏情况。

解决办法:再仔细检查代码无问题然而仍然黑屏后,我还是选择了采用u8g2来实现OLED的驱动。

3.网络收音机这部分一个大的问题就是通信相关知识的匮乏,在连接到WIFI的情况下不知道怎么从远程获取音频数据,无从下手。

解决办法:用过网络简单学习通信知识,包括服务器,客户端,网络协议等,此外也是结合案例选择本地服务器的方向,通过自建服务器,利用TCP协议下的socket实现数据传输。

4.在实现多首音乐的自动循环功能屡屡遇挫。在服务器将一首音乐的数据传输到数据缓冲区后,当客户端发出请求数据,服务器并发送1024bytes后,这里就短暂出现客户端检测不到数据,而发生“跳曲”的问题。

解决办法:通过编写代码,引入判定变量,在未检测到数据前不进行下一曲指令。代码如下:

 

if(num_>0 && num_ <1024){

      isEnd = 1;//如果读取的是最后一段,不为1024整,则表示一首播放完毕

      num_ = 1024;

    }

   else{

      isEnd = 0;//解决播放完后再读取数据时available第一次未识别问题

   }

}
  • 未来的计划或建议

作为一个单片机的小白,此次寒假一起练的项目带我真正的入门了单片机,同时学习了硬件和软件的知识,尤其是通信方面的知识,未来我计划用此开发板尝试其他的几个项目,充分利用的单片机模块,继续提升自己。

附件下载
Project.rar
团队介绍
团队成员
Congo_red
单片机入门小白
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号