板實現(xiàn)軟件定義無線電接收)
1. 從一塊二十塊錢的開發(fā)板說起ESP32做SDR這件事到底靠不靠譜第一次在群里看到有人甩出一句“ESP32能當SDR用了”我的反應(yīng)和大多數(shù)人一樣——眩暈癱坐不至于但確實愣了幾秒。ESP32這塊芯片玩嵌入式的人太熟了二十來塊錢一片雙核240MHz帶WiFi和藍牙平時干的是讀傳感器、點燈、驅(qū)動屏幕、跑個小車的活兒。SDR是什么軟件定義無線電傳統(tǒng)印象里那是需要高速ADC、FPGA、動輒幾百兆采樣率的東西跟一個主打低功耗物聯(lián)網(wǎng)的MCU怎么看都不像能扯上關(guān)系。但仔細扒了一圈資料、翻了幾個開源項目之后我發(fā)現(xiàn)這事沒那么玄乎也沒那么簡單。說它“能當SDR”準確的理解應(yīng)該是ESP32可以在極其有限的條件下實現(xiàn)特定頻段、特定帶寬的無線電信號接收與簡單解調(diào)而不是替代一臺RTL-SDR或者Airspy。這個區(qū)別非常關(guān)鍵直接決定了你對這個項目的預(yù)期也決定了你會不會花了兩天時間最后罵罵咧咧地放棄。這篇文章適合幾類人看手里有ESP32開發(fā)板、對無線電感興趣但沒接觸過SDR的嵌入式玩家玩過RTL-SDR、想看看MCU能做到什么程度的無線電愛好者以及單純好奇“這玩意兒到底怎么實現(xiàn)的”的技術(shù)圍觀群眾。我會從原理層面講清楚為什么ESP32能做這件事、它的物理極限在哪里然后給出可復(fù)現(xiàn)的實操路徑最后把我踩過的坑和排查經(jīng)驗整理出來。不管你是剛點亮第一塊ESP32的新手還是已經(jīng)用ESP-IDF寫過不少項目的老手應(yīng)該都能從中找到有用的東西。2. 核心原理拆解ESP32憑什么能碰SDR的邊2.1 先搞清楚SDR到底在干什么要理解ESP32能不能做SDR得先弄明白SDR的本質(zhì)。傳統(tǒng)無線電接收機的信號鏈路是硬件搭出來的天線收到信號經(jīng)過濾波、放大、混頻、檢波最后出來音頻或者數(shù)據(jù)。每一級都是固定的電路想換個頻段就得換硬件。SDR的思路是把盡可能多的處理搬到軟件里天線信號經(jīng)過最少的模擬前端通常就是一個寬帶放大器加一個高速ADC數(shù)字化之后濾波、混頻、解調(diào)全部用代碼算出來。這里面的核心瓶頸是ADC。根據(jù)奈奎斯特采樣定理你要接收中心頻率為f、帶寬為B的信號ADC的采樣率至少要到2B以上實際中還要考慮過采樣和抗混疊。FM廣播帶寬約200kHz那采樣率至少400ksps航空波段AM信號帶寬窄一些大概幾十ksps就夠。所以SDR對ADC的要求取決于你想接收什么信號。RTL-SDR之所以便宜好用是因為它用了R820T2這顆調(diào)諧芯片把射頻信號先降到中頻再用一顆8bit、2.4Msps的ADC采樣通過USB把IQ數(shù)據(jù)送到電腦上處理。關(guān)鍵點在于真正的高速采樣和下變頻是硬件芯片干的電腦只負責后面的數(shù)字信號處理。2.2 ESP32的硬件底子到底夠不夠ESP32的射頻部分本來是為WiFi和藍牙設(shè)計的支持2.4GHz頻段內(nèi)部有射頻收發(fā)鏈路。但問題在于這條鏈路是封閉的你沒法直接拿到原始IQ數(shù)據(jù)。ESP32的ADC是SAR型標稱12位實際有效位數(shù)大概9到10位采樣率理論上最高2Msps但這是在特定條件下的極限值實際用起來受WiFi藍牙共存、DMA帶寬、CPU負載影響很大。那為什么還有人做出來了關(guān)鍵在于思路的轉(zhuǎn)變不去碰2.4GHz的WiFi射頻鏈路而是用ESP32的ADC直接采樣外部輸入的模擬信號。也就是說你需要外接一個簡單的模擬前端——一根天線、一個合適的帶通濾波器、可能再加一級LNA——把信號送到ESP32的ADC引腳上。ESP32負責的是采樣和后續(xù)的數(shù)字信號處理射頻前端的工作交給了外部電路。這個方案的本質(zhì)是ESP32充當了一個低速ADC加DSP處理器的角色。它的采樣率決定了能處理的信號帶寬它的算力決定了能跑多復(fù)雜的解調(diào)算法。根據(jù)社區(qū)里幾個開源項目的實測數(shù)據(jù)ESP32在穩(wěn)定工作的情況下ADC采樣率大概能跑到1Msps左右對應(yīng)理論帶寬500kHz。實際上考慮到抗混疊和算法開銷能有效處理的帶寬大概在100到200kHz之間。2.3 為什么這件事值得折騰你可能會問花這么大勁就為了接收一兩百kHz帶寬的信號有什么意義直接買個RTL-SDR不好嗎這個問題我認真想過。從純功能角度ESP32做SDR確實打不過任何一款入門級SDR接收機。但它有幾個獨特的價值點第一它是一個完全獨立的嵌入式方案不需要連接電腦可以做成一個巴掌大的獨立接收設(shè)備配上屏幕和電池就能戶外使用第二它的成本極低一塊ESP32加上幾個分立元件總成本可能不到三十塊錢第三也是最重要的一點它是一個極好的學習平臺。當你親手用ADC采樣、用代碼實現(xiàn)FFT、用算法解調(diào)出第一個信號的時候你對SDR的理解會發(fā)生質(zhì)的變化這是插上RTL-SDR點開SDR#軟件永遠得不到的體驗。注意ESP32做SDR接收接收頻段取決于你外接的模擬前端。ESP32本身不負責調(diào)諧它只是采樣。你想接收什么頻段就需要對應(yīng)的前端電路把信號搬移到ADC能處理的范圍內(nèi)。3. 實操前的準備工作硬件選型和軟件環(huán)境搭建3.1 硬件清單與選型邏輯動手之前先把東西備齊。我列一個最小系統(tǒng)的清單然后解釋每一項為什么這么選。組件推薦型號選型理由大致成本主控板ESP32-WROOM-32最通用的型號社區(qū)資料多ADC引腳引出方便15-25元天線拉桿天線或自制偶極子根據(jù)目標頻段裁剪長度短波用長線FM用半波振子5-20元帶通濾波器自制LC或成品濾除目標頻段外的強信號防止ADC飽和5-15元低噪放可選MMIC如BGA2801弱信號時提升信噪比強信號時不需要3-10元偏置器自制給LNA供電同時隔離射頻和直流2-5元顯示屏SSD1306 OLED 128x64顯示頻譜和解調(diào)信息I2C接口省引腳8-15元連接線杜邦線、SMA轉(zhuǎn)接線射頻連接盡量短減少損耗和干擾若干關(guān)于ESP32型號的選擇我建議用ESP32-WROOM-32或者ESP32-S3。WROOM-32的好處是資料最全ADC引腳在GPIO32到GPIO39之間其中GPIO34到GPIO39是輸入-only適合做ADC輸入。S3的話算力更強有向量指令可以加速FFT但價格稍貴資料相對少一些。如果你是第一次做這個項目從WROOM-32開始最穩(wěn)妥。天線這塊要重點說一下。很多人隨便拿一根杜邦線就當天線用結(jié)果什么信號都收不到然后懷疑是代碼問題。天線的長度和形式直接決定了你能否有效接收目標頻段的信號。一個簡單的計算公式半波振子長度米等于150除以頻率MHz。比如你想接收100MHz附近的FM廣播半波振子大概1.5米接收短波7MHz那就要20米左右了。實際中受空間限制可以用四分之一波長或者加感線圈來縮短。3.2 軟件環(huán)境搭建從Arduino IDE到ESP-IDF軟件環(huán)境這塊我推薦兩條路線根據(jù)你的經(jīng)驗水平來選。如果你之前主要用Arduino IDE開發(fā)ESP32那繼續(xù)用Arduino框架是最快上手的路徑。需要安裝的東西Arduino IDE 2.x版本、ESP32開發(fā)板支持包建議用2.0.11或2.0.14版本穩(wěn)定性好、以及必要的庫。安裝ESP32支持包的時候國內(nèi)網(wǎng)絡(luò)環(huán)境下載可能比較慢可以找離線包手動安裝。具體操作是在Arduino IDE的“首選項”里填入開發(fā)板管理器地址然后在開發(fā)板管理器里搜索esp32安裝。如果下載速度實在受不了去社區(qū)找對應(yīng)版本的離線包解壓到Arduino的hardware目錄下也能用。如果你追求性能和底層控制那就上ESP-IDF。ESP-IDF是樂鑫官方的開發(fā)框架對ADC、DMA、I2S的控制更直接做高速采樣的時候優(yōu)勢明顯。安裝ESP-IDF推薦用VSCode加ESP-IDF插件的方式比純命令行友好很多。安裝過程中會下載工具鏈和Python環(huán)境同樣可能遇到網(wǎng)絡(luò)慢的問題可以配置國內(nèi)鏡像源加速。安裝完成之后用idf.py create-project創(chuàng)建一個新項目就可以開始了。提示不管用哪個框架都建議先把一個最簡單的ADC采樣程序跑通用串口打印原始數(shù)據(jù)確認硬件連接和基礎(chǔ)功能沒問題再往上疊加SDR相關(guān)的代碼。這樣出問題的時候排查范圍小很多。3.3 采樣率與時鐘配置的關(guān)鍵參數(shù)這是整個項目最核心的技術(shù)點之一。ESP32的ADC時鐘源來自APB時鐘默認80MHz通過分頻器產(chǎn)生ADC的工作時鐘。在Arduino框架下analogRead()函數(shù)的采樣率大概只有幾十ksps遠遠不夠。要跑到1Msps左右必須用底層寄存器操作或者I2S DMA的方式。用I2S DMA采ADC數(shù)據(jù)是社區(qū)里比較成熟的方案。原理是把ADC配置成連續(xù)采樣模式通過I2S外設(shè)的DMA通道把數(shù)據(jù)搬運到內(nèi)存緩沖區(qū)CPU只需要處理緩沖區(qū)里的數(shù)據(jù)不用管采樣時序。這樣可以把采樣率做到比較高同時CPU還有余力做信號處理。具體的配置參數(shù)I2S采樣率設(shè)置為1MHzDMA緩沖區(qū)大小設(shè)為1024或2048個采樣點雙緩沖模式。ADC衰減設(shè)為11dB對應(yīng)輸入范圍0到3.3V左右分辨率設(shè)為12位。實際有效位數(shù)大概9到10位所以信噪比不會太好但對于接收較強的廣播信號來說夠用了。這里有個坑要注意ESP32的ADC在WiFi開啟的時候噪聲會明顯增大。如果你不需要WiFi功能在采樣期間把WiFi關(guān)掉信噪比會好很多。如果確實需要WiFi盡量把采樣和WiFi傳輸在時間上錯開。4. 從采樣到解調(diào)完整信號鏈的實現(xiàn)過程4.1 模擬前端電路的設(shè)計與搭建ESP32的ADC輸入范圍是0到3.3V衰減11dB時而且只能接受正電壓。射頻信號是交流信號有正有負所以需要加一個直流偏置電路把信號抬升到1.65V左右讓交流信號在1.65V上下擺動這樣ADC才能正確采樣。最基本的偏置電路用兩個等值電阻分壓產(chǎn)生1.65V然后通過一個隔直電容把射頻信號耦合進來。但這樣做的缺點是輸入阻抗不夠穩(wěn)定而且沒有濾波。更好的做法是用一個運算放大器搭一個偏置加法電路同時兼做低通抗混疊濾波。運放選帶寬足夠的型號比如NE5532或者OPA2350增益帶寬積至少要到幾十MHz。帶通濾波器根據(jù)你的目標頻段來設(shè)計。以接收FM廣播88到108MHz為例你需要一個中心頻率98MHz、帶寬20MHz左右的帶通濾波器??梢杂肔C集總參數(shù)做但98MHz的頻率下電感電容的值很小手工焊接比較困難。更實際的做法是用一個成品FM帶通濾波器或者用傳輸線結(jié)構(gòu)的濾波器。如果目標頻段更低比如短波或者中波LC濾波器的實現(xiàn)就容易得多。低噪放這塊如果信號比較強比如本地FM廣播可以不加。如果信號弱加一級MMIC放大器會有明顯改善。注意LNA的增益不要太高否則強信號會讓ADC飽和產(chǎn)生嚴重的互調(diào)失真。我一般會加一個可調(diào)衰減器或者用PIN二極管做AGC但這就復(fù)雜了新手可以先不加根據(jù)實際接收效果再決定。4.2 采樣數(shù)據(jù)的預(yù)處理去偏置與數(shù)字濾波ADC采到的原始數(shù)據(jù)是0到4095之間的整數(shù)對應(yīng)0到3.3V。信號的中心在1.65V左右對應(yīng)ADC讀數(shù)大概2048。第一步預(yù)處理就是把直流偏置去掉把數(shù)據(jù)變成以0為中心的有符號數(shù)。這個操作很簡單每個采樣值減去2048就行。接下來是數(shù)字濾波。雖然模擬前端已經(jīng)有抗混疊濾波但ESP32的ADC采樣率是1MHz而實際有用的信號帶寬可能只有100kHz所以還需要數(shù)字低通濾波來進一步抑制帶外噪聲。在ESP32上跑FIR濾波是可行的但要注意算力開銷。一個64階的FIR濾波器在240MHz主頻下處理1Msps的采樣流大概占用不到10%的CPU。如果階數(shù)太高比如256階那CPU占用就會很明顯了。更高效的做法是用CIC濾波器或者簡單的滑動平均?;瑒悠骄举|(zhì)上是一個FIR濾波器系數(shù)全為1實現(xiàn)起來只需要加減法不需要乘法速度極快。比如16點滑動平均可以把采樣率從1MHz降到62.5kHz同時濾除高頻噪聲。降采樣之后的數(shù)據(jù)量小了后續(xù)FFT和解調(diào)的負擔也輕了。實操心得預(yù)處理階段的數(shù)據(jù)類型選擇很重要。用float做運算方便但慢用int16_t做定點運算快但要注意溢出。我建議在濾波和降采樣階段用int32_t累加輸出用int16_tFFT階段再轉(zhuǎn)float。這樣兼顧速度和精度。4.3 FFT頻譜分析與信號識別有了預(yù)處理后的IQ數(shù)據(jù)實際上ESP32做的是實數(shù)采樣嚴格來說只有I路沒有Q路但可以通過希爾伯特變換或者直接對實數(shù)做FFT來獲取頻譜下一步就是做FFT看頻譜。ESP32上跑FFT推薦用樂鑫官方提供的esp-dsp庫里面有針對ESP32優(yōu)化的FFT函數(shù)支持實數(shù)FFT和復(fù)數(shù)FFT速度比手寫的快很多。FFT的點數(shù)選擇要權(quán)衡頻率分辨率和時間分辨率。1024點FFT在62.5kHz采樣率下頻率分辨率是61Hz時間窗口約16ms。對于看FM廣播的頻譜來說這個分辨率足夠了。2048點的話分辨率翻倍但計算量也翻倍。我一般用1024點在ESP32上跑一次大概幾百微秒完全跟得上實時顯示。頻譜出來之后你可以通過找峰值來確定信號的中心頻率。比如在88到108MHz范圍內(nèi)經(jīng)過下變頻后對應(yīng)到基帶的0到20MHz再經(jīng)過采樣和降采樣后對應(yīng)到0到31.25kHz找到幅度最大的幾個峰那就是電臺信號。然后根據(jù)峰值的位置把信號搬移到零頻附近做進一步解調(diào)。4.4 FM解調(diào)的實現(xiàn)細節(jié)FM解調(diào)的核心是求瞬時頻率。對于實數(shù)采樣信號可以先做希爾伯特變換得到解析信號然后求相鄰采樣點的相位差相位差就對應(yīng)瞬時頻率。另一種更簡單的方法是對信號做反正切鑒頻把信號延遲一個采樣周期然后計算原信號和延遲信號的叉積與點積叉積除以點積的反正切就是相位差。在ESP32上實現(xiàn)FM解調(diào)我推薦用第二種方法因為計算量小不需要做希爾伯特變換。具體步驟是對預(yù)處理后的信號x[n]計算x[n]和x[n-1]的叉積cross等于x[n]乘以x[n-1]的共軛的虛部對于實數(shù)信號cross等于x[n]乘以x[n-1]的延遲版本這里需要先構(gòu)造解析信號或者用近似方法點積dot等于x[n]乘以x[n-1]的實部。然后相位差等于atan2(cross, dot)。這個相位差就正比于瞬時頻率偏移也就是解調(diào)出來的音頻信號。解調(diào)出來的音頻信號還需要經(jīng)過低通濾波和去加重。去加重是FM廣播的標準處理時間常數(shù)一般是75微秒美國或50微秒歐洲。去加重的實現(xiàn)就是一個簡單的一階高通濾波器用代碼幾行就能搞定。最后把音頻信號通過DAC輸出或者通過I2S送到外部DAC芯片就能聽到聲音了。ESP32自帶兩個8位DAC引腳GPIO25和GPIO26可以直接輸出音頻。但8位分辨率對于音頻來說有點低會有明顯的量化噪聲。更好的方案是用I2S接一個外部DAC比如MAX98357或者PCM5102音質(zhì)會好很多。如果只是聽個響內(nèi)置DAC也湊合能用。5. 常見問題與排查技巧實錄5.1 采樣數(shù)據(jù)全是噪聲怎么辦這是最常見的問題十有八九是模擬前端的問題。排查步驟按順序來先用示波器或者萬用表確認ADC輸入引腳的直流電平是不是在1.65V左右。如果偏差太大檢查偏置電路的分壓電阻是不是焊錯了或者虛焊。然后確認天線有沒有接好天線和ADC輸入之間的耦合電容是不是合適。如果這些都正常那可能是信號太弱需要加LNA或者換更好的天線。還有一個容易被忽略的點ESP32開發(fā)板上的ADC引腳可能被其他功能占用了。比如GPIO34在某些板子上接了攝像頭或者SD卡。用之前查一下開發(fā)板的原理圖確認引腳是空閑的。另外ADC2的引腳在WiFi開啟時是不能用的只能用ADC1的引腳GPIO32到GPIO39。5.2 頻譜顯示有信號但解調(diào)不出聲音這種情況通常是解調(diào)參數(shù)不對。首先確認你解調(diào)的信號確實是FM信號而不是AM或者數(shù)字信號。FM廣播的信號帶寬大概200kHz在頻譜上看起來是一個比較寬的峰而AM信號是一個窄峰。如果確認是FM檢查鑒頻器的參數(shù)延遲采樣點數(shù)是不是1atan2的計算是不是正確去加重的截止頻率是不是設(shè)對了。另一個可能的原因是采樣率不夠。如果ADC實際采樣率只有500kHz那能處理的信號帶寬只有250kHz對于200kHz帶寬的FM信號來說勉強夠用但邊緣可能會失真??梢栽囍档湍繕诵盘柕膸捯蠡蛘咛岣卟蓸勇省?.3 編譯速度慢和燒錄失敗的處理用Arduino IDE編譯ESP32項目尤其是帶WiFi和藍牙庫的項目編譯時間可能長達幾分鐘。加快編譯速度的方法有幾個關(guān)閉不需要的庫用預(yù)編譯的庫文件升級電腦的CPU和內(nèi)存這個最直接但成本高或者換用PlatformIO。PlatformIO的編譯緩存機制比Arduino IDE好很多第二次編譯同樣的項目會快很多。燒錄失敗的話先檢查USB線是不是數(shù)據(jù)線有些線只能充電不能傳數(shù)據(jù)然后確認開發(fā)板驅(qū)動裝好了。ESP32燒錄的時候需要進入下載模式有些板子自動下載電路做得不好需要手動按住BOOT鍵再按RESET鍵。如果串口一直顯示“等待上電同步”大概率是這個問題。問題現(xiàn)象可能原因排查方法解決方案采樣全是噪聲偏置電壓不對萬用表測ADC引腳電壓調(diào)整分壓電阻確認在1.65V頻譜無信號峰天線未接好檢查天線連接和長度重新裁剪天線確保接觸良好解調(diào)無聲音鑒頻參數(shù)錯誤檢查延遲和atan2計算調(diào)整延遲點數(shù)驗證去加重編譯超時庫太多查看編譯日志移除無用庫換PlatformIO燒錄失敗未進入下載模式觀察串口輸出手動按住BOOT再復(fù)位WiFi開啟后噪聲大ADC2與WiFi沖突確認使用的ADC通道換用ADC1引腳或關(guān)閉WiFi5.4 提升接收效果的幾個實用技巧第一個技巧是給ADC輸入加一個簡單的RC低通濾波器截止頻率設(shè)在采樣率的一半以下。比如采樣率1MHzRC截止頻率設(shè)500kHz。這個濾波器可以濾除高頻噪聲改善信噪比。電阻選1kΩ電容選300pF左右。第二個技巧是用多個ADC引腳做時間交織采樣。ESP32有兩個ADC理論上可以交替采樣把等效采樣率翻倍。但實際中兩個ADC之間的一致性不好需要校準實現(xiàn)起來比較復(fù)雜。新手不建議嘗試。第三個技巧是在FFT之前加窗函數(shù)。不加窗的話頻譜泄漏會比較嚴重弱信號可能被強信號的旁瓣淹沒。漢寧窗或者漢明窗都是不錯的選擇計算量也不大。esp-dsp庫里有現(xiàn)成的窗函數(shù)可以調(diào)用。6. 這個項目的邊界在哪里理性看待ESP32 SDR的能力折騰了這么久我對ESP32做SDR的能力邊界有了比較清晰的認識。它能做的事情接收本地強信號的FM廣播、接收航空波段AM信號、接收氣象衛(wèi)星的APT信號需要合適的天線和前端、做簡單的頻譜監(jiān)測。它不能做的事情接收弱信號、處理寬帶信號、替代專業(yè)SDR接收機、做高精度的信號分析。如果你追求的是實用性想穩(wěn)定接收各種信號那還是買一臺RTL-SDR或者更好的接收機。但如果你追求的是學習價值想真正理解SDR的工作原理那ESP32這個平臺能給你的東西遠超它的價格。當你第一次從自己焊的電路上、從自己寫的代碼里聽到電臺主持人的聲音時那種成就感是插上成品設(shè)備永遠體會不到的。最后分享一個我在調(diào)試過程中總結(jié)的小經(jīng)驗每次修改代碼之后先用一個已知的信號源比如信號發(fā)生器或者另一個ESP32發(fā)射的測試信號驗證接收鏈路是否正常再去接收真實信號。這樣可以快速區(qū)分是代碼問題還是信號問題省去很多瞎猜的時間。另外把關(guān)鍵的中間數(shù)據(jù)通過串口打印出來比如ADC原始值、濾波后的值、FFT的峰值位置比盯著屏幕上的最終結(jié)果瞎調(diào)要高效得多。