ESP32S2小項目-FM-網路時鐘/電台-Arduino開發環境

ESP32S2小項目,FM,網路時鐘/電台,Arduino開發環境

效果展示

@

開機動畫:

網路時鐘:

FM模組:

網路收音:

硬體平台

ESP32是樂鑫的一塊國產WiFi晶片。

ESP32-S2-MINI-1採用PCB板載天線,模組配置了4MB SPI flash,32 位LX7 單核處理器,工作頻率高達 240 MHz。43 個 GPIO 口,14 個電容式感測 IO,支援 SPI、I2C、I2S、UART、ADC/DAC 和 PWM 等各種標準外設,支援 LCD 介面(8-bit 並口 RGB、8080、6800 介面),支援 8-/16-bit DVP 影像感測器介面,最高時鐘頻率支援到 40 MHz ,支援全速 USB OTG。

硬核學堂在ESP32-S2-MINI-1的基礎上,擴展了麥克風輸入、按鍵輸入、紅外輸入、FM收音機模組、12864OLED螢幕幕輸出、揚聲器(耳機介面)輸出。

具體資訊請參考下面網址:
電子森林基於ESP32-S2模組的物聯網/音頻訊號處理平台

開發平台

可選擇平台有:esp-idf、Arduino,CicruitPython,因為前段時間剛用Arduino平台玩過ESP8266模組,所以這裡選擇Arduino平台進行開發(我才不會告訴你們,我是因為esp-idf編譯工具鏈沒配置好)。

簡單環境配置

第一步:首選項中開發板管理網址:

//dl.espressif.com/dl/package_esp32_index.json

第二步:開發板管理器,找ESP32,下載

第三步:換開發板

因為下載太慢,一般會直接用別人下載好的ESP32的包,但問題是相當的多,給大家放一個我的環境配置歷程圖:

手動配置

最後把我成功試驗過的一種方法告訴大家:

首先,下載解壓我這個包

–來自百度網盤超級會員V4的分享
hi,這是我用百度網盤分享的內容~複製這段內容打開「百度網盤」APP即可獲取
鏈接:[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-rQh7p6CG-1643251689534)(file:///C:\Users\lenovo\AppData\Roaming\Tencent\QQTempSys[5UQ[BL(6~BS2JV6W}N6[%S.png)]//pan.baidu.com/s/1Pcb6X0YfKsrcxr-ZpKr4wA
提取碼:qw35

然後,解壓到自己的arduino安裝路徑里替換掉它

關掉,再打開Arduino就能看到開發板了。

以後大家想配置任何開發板的環境,只要把它的包放到這個路徑里就好,親測有效。

萬事俱備,接下來就是進行軟體開發了。

程式簡述

程式碼邏輯:

邏輯不算複雜。

連接WiFi:

//wifi用戶名與密碼
char* ssid     = "iron2222";
char* password = "*******9009"; 
//啟動WiFi服務,這裡是客戶端(放在set up函數里)
  WiFi.begin(ssid, password);                  
  Serial.print("Connecting to ");              // 串口監視器輸出網路連接資訊
  Serial.print(ssid); Serial.println(" ...");  // 告知用戶正在嘗試WiFi連接
  int i = 0;                                   // 這一段程式語句用於檢查WiFi是否連接成功
  while (WiFi.status() != WL_CONNECTED) {      // WiFi.status()函數的返回值是由WiFi連接狀態所決定的。 
    delay(1000);                               // 如果WiFi連接成功則返回值為WL_CONNECTED                       
    Serial.print(i++); Serial.print(' ');      // 此處通過While循環讓NodeMCU每隔一秒鐘檢查一次WiFi.status()函數返回值
  }                                            // 同時NodeMCU將通過串口監視器輸出連接時長讀秒。
                                               // 這個讀秒是通過變數i每隔一秒自加1來實現的。                                           
  Serial.println("");                          // WiFi連接成功後
  Serial.println("Connection established!");   // 將通過串口監視器輸出"連接成功"資訊。
  Serial.print("IP address:    ");             // 同時還將輸出IP地址。這一功能是通過調用
  Serial.println(WiFi.localIP());              // WiFi.localIP()函數來實現的。該函數的返回值即NodeMCU的IP地址。
  ipaddress = WiFi.localIP().toString();

網路時鐘:

//網路時鐘設置
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;
 // 從網路時間伺服器上獲取並設置時間,獲取成功後晶片會使用RTC時鐘保持時間的更新(放在set up函數里)
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  printLocalTime();
//網路時鐘顯示函數
void timer_show() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo))
    {
        u8g2.clearBuffer();
        u8g2.setFontDirection(0);
        u8g2.setFont(u8g2_font_ncenB08_tr);
        u8g2.setCursor(0, 40);
        u8g2.print("Failed to obtain time");
        u8g2.setCursor(2,55);
        u8g2.print(ipaddress);
        u8g2.sendBuffer();
        return;
    }
  u8g2.clearBuffer();
  u8g2.setFontDirection(0);
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.setCursor(20, 15);
  u8g2.print(&timeinfo, "%H:%M:%S");
  u8g2.setCursor(15, 35);
  u8g2.print(&timeinfo, "%Y/%m/%d");
  u8g2.setCursor(2,55);
  u8g2.print(ipaddress);
  u8g2.sendBuffer();
}

FM收音機:

因為FM模組是通過I2C進行數據傳輸的,所以要打開相應的配置。

//I2C引腳設置
#define ESP32_I2C_SDA 5
#define ESP32_I2C_SCL 4
#define MAX_DELAY_RDS 40   // 40ms - polling method
long rds_elapsed = millis();
RDA5807 rx;
 // I2C相關設置(放在set up函數里)
  Wire.begin(ESP32_I2C_SDA, ESP32_I2C_SCL);
  rx.setup();
  rx.setVolume(0);//音量大小設置,可調
  delay(500);
  //FM相關設置
  rx.setFrequency(10650); //開啟是頻率,可調
  //使能SDR
  rx.setRDS(true);
//FM模式顯示函數
void FM_show() {
  char str[64] = {0};
  u8g2.firstPage();
  u8g2.clearBuffer();         //清除內部緩衝區
  u8g2.setFontDirection(0);   //設置字體方向
  
  u8g2.setFont(u8g2_font_ncenB14_tr);     //設置字體
  u8g2.setCursor(15, 20);
  u8g2.print("FM Radio");

  u8g2.setFont(u8g2_font_ncenB10_tf);  
  u8g2.setCursor(15, 40);
  sprintf(str, "%u MHz",rx.getFrequency() );
  u8g2.print(str);
  u8g2.setCursor(15, 55);
  sprintf(str, "Vol: %2.2u",rx.getVolume());
  u8g2.print(str);
  
  u8g2.sendBuffer();
  delay(1000);
}

網路收音:

這裡其實可以重點講解一下,因為涉及到了DAC。關於什麼是ADC/DAC大家可以看這篇博文:

部落格園//www.cnblogs.com/iron2222/p/15833426.html

CSDN//iron2222.blog.csdn.net/article/details/122636386?spm=1001.2014.3001.5502

整個webradio的實現過程可以劃分為三部分

  • 獲取音頻流,程式使用的 http 協議從一個伺服器上面獲取的音頻數據,並將整個數據存放到一個 buffer 中。
  • 對音頻流進行解碼,當 buffer 中有一定的數據後(可以通過宏進行調整),開啟解碼執行緒。解碼執行緒會從這個 buffer 中取出數據,然後調用解碼庫,將音頻流解碼為可直接輸出的數字訊號。
  • 將解碼後的數據通過 DAC 輸出,解碼執行緒每解完一幀數據後,將它通過 I2C 驅動程式直接送給 DAC。

這裡面存在一個同步的問題,即從伺服器上面獲取音頻流與對音頻流進行解碼的同步。如果獲取的音頻流過快,超過了解碼的速度,則它可能將 buffer 撐爆,因此會丟失部分數據;如果解碼的速度過快,超過了獲取音頻的速度,則它可能將 buffer 消耗的乾乾淨淨,從而也會出現聲音卡頓問題。這需要進行權衡。

但是在S2中無法實現在線音頻流的解碼,所以利用ESP8266作為網路伺服器用socketTCP傳輸搭建了了獨立的網路音頻庫,上面看到的那個黑色的模組。

//網路收音
uint16_t num=0;
#define WEBSERVERIP "192.168.43.212"
#define WEBSERVERPORT 3999
uint8_t netbuf[3][1024];      //網路數據緩衝區
uint16_t writep = 0;          //寫入數量
uint16_t readp = 0;           //讀取數量
WiFiClient client;            //聲明一個客戶端對象,用於與伺服器進行連接
bool connstat = false;        //連接狀態
bool iswaitecho = false;      //是否等待伺服器回應
Ticker flipper;               //時間中斷
uint16_t m_offset = 0;

void onTimer(void) {
  if(readp<=writep)  dacWrite(17, netbuf[readp % 3][m_offset++]);      //播放一次聲音
  if (m_offset >= 1024) {
    m_offset = 0;
    readp++;      //讀取完成一個緩衝區
  }
}
bool connNetMusic() {
  uint8_t i = 0;
  while (i < 5) { //最多連接5次
    if (client.connect(WEBSERVERIP, WEBSERVERPORT)) {
      connstat = true;
      Serial.println("連接成功");
      return true;
    } else {
      Serial.println("訪問失敗");
      client.stop(); //關閉客戶端
    }
    i++;
    delay(100);
  }
  return false;
}

void playMusic() {
        digitalWrite(41, HIGH);digitalWrite(42, HIGH);
        if (connstat == true){

            if (iswaitecho == false && (writep - readp) < 2) {
                client.write('n');          //申請一個緩衝片
                iswaitecho = true;
            }
            if (client.available()) { //如果有數據可讀取
                num = client.read(netbuf[writep % 3], 1024);
                if (writep == 0 && readp == 0) {
                    flipper.attach(0, onTimer);     //開啟定時中斷 就是每秒中斷20000次
                }
                writep++;
                iswaitecho = false;
            }
        }
}

以上便是程式碼的各個模組配置,完整程式碼,可以去我的gitee庫上下載,最終程式文件夾名字是arduino_final_test,其他文件夾是我對各個模組的功能測試,大家都可以拿來玩玩,一步一步的完成這個項目。

碼雲//gitee.com/iron2222/esp32

結語

這個項目還有很多可以改善的的地方,比如FM的自動搜索較好頻道功能,以及網路電台的連接等。

下一步我會使用剛搭建好的esp-idf平台重新開發,arduino平台開發起來有些不是那麼的得心應手,祝我bug少少!!!

祝大家有一個愉快的假期與春節哦!

Tags: