線遙控器DIY:從編碼到解碼完整實(shí)現(xiàn))
前陣子朋友找我?guī)兔φf(shuō)他家車庫(kù)門的遙控器丟了一個(gè)原廠再配要小幾百。我聽(tīng)完第一反應(yīng)是這活兒用433MHz就能干——一塊幾塊錢的射頻發(fā)射模塊、一塊接收模塊加一塊Arduino板子就能從零寫出一套帶編碼和解碼的無(wú)線遙控方案。433MHz無(wú)線遙控器在市面上太常見(jiàn)了車庫(kù)門、樓道門、遙控開(kāi)關(guān)、電機(jī)控制、農(nóng)業(yè)灌溉設(shè)備絕大多數(shù)都用這個(gè)頻段。而它們底層的實(shí)現(xiàn)邏輯說(shuō)白了就是“編碼”和“解碼”兩個(gè)動(dòng)作發(fā)送端把按鍵狀態(tài)編碼成一串脈沖接收端把這串脈沖解碼還原成指令再驅(qū)動(dòng)繼電器或者電機(jī)。這篇文章我就把這套從編碼到解碼的完整鏈路講透硬件選型、底層原理、發(fā)射和接收代碼都會(huì)貼出來(lái)最后再把我在調(diào)試過(guò)程中踩過(guò)的坑一并列上。不管你是電子愛(ài)好者、嵌入式初學(xué)者還是想給家里舊設(shè)備做個(gè)性化的遙控器這篇應(yīng)該都能直接上手用。1. 為什么是433MHz這個(gè)頻段憑什么成為遙控DIY的首選先說(shuō)說(shuō)頻率選擇的問(wèn)題。433MHz并不是憑空冒出來(lái)的它屬于國(guó)際電信聯(lián)盟劃分的ISM頻段也就是工業(yè)、科學(xué)和醫(yī)療頻段。在大多數(shù)國(guó)家和地區(qū)只要發(fā)射功率控制在法定限制以內(nèi)國(guó)內(nèi)通常參考微功率短距離無(wú)線電發(fā)射設(shè)備的相關(guān)規(guī)定不需要申請(qǐng)專用頻率執(zhí)照就能使用。這意味著業(yè)余愛(ài)好者、個(gè)人DIY項(xiàng)目都可以合法使用這個(gè)頻段不用去跑行政審批流程。這是它成為遙控DIY首選的第一層原因。第二層原因是物理特性。無(wú)線信號(hào)的頻率越低波長(zhǎng)越長(zhǎng)繞射和穿墻能力就越強(qiáng)。433MHz的波長(zhǎng)大約69厘米四分之一波長(zhǎng)天線約為17.3厘米這個(gè)頻率的信號(hào)在穿過(guò)一兩堵磚墻之后衰減還不算太大。相比之下2.4GHz頻段的信號(hào)波長(zhǎng)只有12.5厘米左右穿墻能力明顯弱不少這也是為什么家里的WiFi穿幾堵墻之后就卡。如果你要做的是車庫(kù)門、院內(nèi)遙控設(shè)備這類需要穿墻的場(chǎng)景433MHz就是遠(yuǎn)比2.4G合適的選擇。下面這張表能更直觀地看出三個(gè)常見(jiàn)遙控方案的差異方案頻率穿墻能力傳輸速率模塊成本典型場(chǎng)景紅外遙控約38kHz載波無(wú)必須對(duì)準(zhǔn)低極低電視、空調(diào)等家用電器433MHz射頻433.92MHz較好中低1kbps-10kbps低車庫(kù)門、門禁、遙控開(kāi)關(guān)2.4GHz射頻2.4GHz較差高中低航模、數(shù)據(jù)透?jìng)鞯谌龑釉蚓褪悄K成本。一個(gè)ASK/OOK調(diào)制的433MHz發(fā)射模塊在電商平臺(tái)上通常幾塊錢就能買到接收模塊即便是超外差方案也就十幾塊人民幣。這個(gè)成本比買一個(gè)成品的無(wú)線遙控器還便宜而且完全可編程、可定制。綜合這三點(diǎn)433MHz是DIY無(wú)線遙控項(xiàng)目的最佳平衡點(diǎn)——不折騰頻率授權(quán)穿墻能力夠用成本幾乎可以忽略。順帶提一句市面上大量成品遙控器為了成本和通用性都用了固定的編碼芯片比如PT2262、EV1527這種。它們的好處是開(kāi)箱即用但缺點(diǎn)也很明顯編碼格式固定你沒(méi)法自己定義數(shù)據(jù)內(nèi)容也沒(méi)法做更復(fù)雜的協(xié)議。而我們用單片機(jī)自己去編碼最大的價(jià)值就在于靈活——地址位數(shù)想設(shè)多少設(shè)多少數(shù)據(jù)位想怎么編排怎么編排甚至還可以加滾動(dòng)碼防止重放攻擊這些是成品芯片方案很難做到的。2. 硬件選型發(fā)射接收模塊與主控板的搭配方案2.1 發(fā)射模塊怎么看市面上最常見(jiàn)的433MHz發(fā)射模塊是FS1000A也叫XD-FST或類似的兼容型號(hào)本質(zhì)是一個(gè)基于聲表諧振器的ASK/OOK發(fā)射模塊。它有三個(gè)引腳DATA、VCC、GND工作電壓范圍比較寬常見(jiàn)標(biāo)稱是3.5V到12V典型發(fā)射功率在10dBm到15dBm之間。模塊本身不帶調(diào)制功能你往DATA引腳輸入高電平時(shí)它就發(fā)射載波輸入低電平時(shí)就關(guān)閉載波發(fā)射與否完全由數(shù)字引腳的電位決定。這里有一個(gè)關(guān)鍵參數(shù)要注意發(fā)射模塊的電流消耗。FS1000A在發(fā)射狀態(tài)下電流大概在10到20毫安如果用Arduino的3.3V穩(wěn)壓輸出直接給模塊供電當(dāng)發(fā)射電流拉高時(shí)穩(wěn)壓器可能會(huì)壓降導(dǎo)致實(shí)際供電電壓低于模塊正常工作范圍發(fā)射距離會(huì)明顯縮短。我的習(xí)慣是如果主控和模塊共用一個(gè)電源優(yōu)先用5V穩(wěn)壓給模塊供電3.3V只給單片機(jī)IO使用。如果電池供電鎳氫電池或者鋰電池供電要注意電壓波動(dòng)模塊對(duì)電壓比較敏感。2.2 接收模塊超再生與超外差差的不只是價(jià)格接收模塊是整套系統(tǒng)里最影響體驗(yàn)的元器件主流的433MHz接收方案分兩種超再生和超外差。超再生接收模塊結(jié)構(gòu)簡(jiǎn)單只有幾個(gè)晶體管加阻容元件成本極低市面上一兩塊錢到五六塊錢的就是它。它的缺點(diǎn)是靈敏度一般而且沒(méi)有真正意義上的本振鎖定容易受到鄰道信號(hào)干擾沒(méi)有信號(hào)時(shí)輸出端會(huì)輸出帶噪聲特性的隨機(jī)電平。這意味著你在做解碼時(shí)必須自己處理大量的噪聲脈沖。超外差接收模塊結(jié)構(gòu)復(fù)雜一些內(nèi)部有本振、混頻器、中頻濾波器選頻能力好靈敏度高抗干擾能力強(qiáng)得多價(jià)格通常要十幾塊甚至二十幾塊。我的建議很簡(jiǎn)單只要預(yù)算允許優(yōu)先選超外差接收模塊。在遙控這種對(duì)可靠性要求高的場(chǎng)景里超外差省掉的是你后期排查隨機(jī)亂碼的大量時(shí)間。如果你想先低成本驗(yàn)證方案超再生也不是不能用但解碼程序里一定要做好引導(dǎo)碼檢測(cè)和多次校驗(yàn)否則你會(huì)被噪聲折磨得懷疑人生。兩種模塊的性能對(duì)比如下指標(biāo)超再生接收模塊超外差接收模塊成本低一般幾元中十幾到二十多元抗干擾能力一般強(qiáng)靜噪輸出無(wú)信號(hào)時(shí)輸出噪聲隨機(jī)電平無(wú)信號(hào)時(shí)輸出穩(wěn)定電平頻率穩(wěn)定性一般存在頻率漂移好適合場(chǎng)景低復(fù)雜度驗(yàn)證正式項(xiàng)目、可靠性要求高2.3 主控板與天線的細(xì)節(jié)主控板這塊我用得最多的是Arduino Nano理由很簡(jiǎn)單便宜、引腳足夠、3.3V和5V都有、USB直接燒錄而且它支持外部中斷的引腳剛好是D2和D3做脈沖解碼非常順手。如果你想在這個(gè)項(xiàng)目基礎(chǔ)上再掛點(diǎn)別的傳感器STM32和ESP32也都沒(méi)問(wèn)題但代碼邏輯不用變只是引腳號(hào)要自己對(duì)應(yīng)。天線是經(jīng)常被忽略的部分但它對(duì)發(fā)射距離的影響可能比模塊本身的性能還大。433MHz的四分之一波長(zhǎng)單極天線物理長(zhǎng)度應(yīng)該在17.3厘米左右300除以433再乘0.25單位是米。很多模塊的天線焊盤出廠時(shí)留的空位如果你只是拿一小段杜邦線插上去天線效率會(huì)大打折扣。我實(shí)測(cè)過(guò)同樣的模塊從5厘米短天線換成17厘米垂直導(dǎo)線空曠環(huán)境下接收距離能翻三四倍。如果設(shè)備外殼空間不夠可以用螺旋天線把同長(zhǎng)度的導(dǎo)線繞在圓柱體上收縮物理尺寸電氣性能在短距離應(yīng)用下足夠。3. 從波形到數(shù)據(jù)433MHz無(wú)線通信的底層編碼邏輯3.1 ASK調(diào)制不玄乎433MHz遙控器用的調(diào)制方式是ASK即振幅鍵控Amplitude Shift Keying。它的基本原理就是載波有代表一個(gè)狀態(tài)載波沒(méi)有代表另一個(gè)狀態(tài)。發(fā)射模塊DATA引腳拉高就發(fā)射載波DATA引腳拉低就停止發(fā)射。接收模塊把收到的信號(hào)解調(diào)回來(lái)輸出的就是對(duì)應(yīng)的數(shù)字電平。OOKOn-Off Keying是ASK的一種特例恰好就是這種“有關(guān)斷”的開(kāi)關(guān)式鍵控絕大多數(shù)433MHz遙控模塊用的就是OOK。你可以把這個(gè)過(guò)程理解成人打手電筒發(fā)莫爾斯碼燈亮代表一個(gè)狀態(tài)燈滅代表另一個(gè)狀態(tài)。區(qū)別在于無(wú)線電的開(kāi)關(guān)速度比手快得多微秒級(jí)別的切換完全可以由單片機(jī)精確控制。3.2 脈沖寬度編碼用時(shí)間長(zhǎng)短表示0和1有了載波開(kāi)關(guān)下一步就是如何在“有”和“無(wú)”之間表達(dá)數(shù)據(jù)。433MHz遙控最常見(jiàn)的一種做法叫脈沖寬度編碼也叫脈寬調(diào)制編碼。它的思路是每個(gè)數(shù)據(jù)位的周期里高電平時(shí)間固定用低電平時(shí)間的長(zhǎng)短來(lái)區(qū)分邏輯0和邏輯1。以本文采用的編碼協(xié)議為例具體參數(shù)如下數(shù)據(jù)位高電平時(shí)間低電平時(shí)間總周期邏輯0560微秒560微秒1120微秒邏輯1560微秒1680微秒2240微秒這個(gè)比例跟EV1527芯片的編碼風(fēng)格很接近高電平是一個(gè)固定的短脈沖邏輯0的低電平和高電平等長(zhǎng)邏輯1的低電平大約是邏輯0低電平的三倍長(zhǎng)。接收端只要測(cè)量每個(gè)數(shù)據(jù)位中低電平的持續(xù)時(shí)間就能還原出原始的比特流。因?yàn)楦叩碗娖降膶挾炔町惷黠@所以抗干擾能力比純粹的等周期相位編碼要好一些。3.3 幀結(jié)構(gòu)引導(dǎo)碼、地址碼、數(shù)據(jù)碼的編排單個(gè)比特不能表達(dá)一個(gè)完整的指令一幀數(shù)據(jù)必須要有結(jié)構(gòu)。我的幀結(jié)構(gòu)安排如下引導(dǎo)碼先拉高9000微秒再拉低4500微秒。它的作用是讓接收端的自動(dòng)增益控制電路有時(shí)間穩(wěn)定下來(lái)同時(shí)給解碼程序一個(gè)明確的幀起始標(biāo)志。超再生接收模塊在剛開(kāi)始接收時(shí)輸出很不穩(wěn)定前幾個(gè)毫秒的脈沖不能當(dāng)作有效數(shù)據(jù)引導(dǎo)碼正好用來(lái)跳過(guò)這個(gè)階段。地址碼4位可以表達(dá)16種地址。地址碼是用來(lái)區(qū)分不同遙控器的本質(zhì)上就像門卡上的編號(hào)。同一個(gè)接收器只響應(yīng)預(yù)設(shè)地址的發(fā)射器防止鄰居的遙控器把你的設(shè)備觸發(fā)了。數(shù)據(jù)碼4位可以表達(dá)16種按鍵指令。一個(gè)遙控器如果有4個(gè)按鍵可以用4位數(shù)據(jù)分別映射。幀尾最后一個(gè)數(shù)據(jù)位結(jié)束后的低電平之后再補(bǔ)一個(gè)560微秒的高電平脈沖讓接收端知道一幀結(jié)束了。一幀總共是9個(gè)脈沖引導(dǎo)碼高低各一個(gè)地址4位產(chǎn)生4個(gè)高電平脈沖數(shù)據(jù)4位產(chǎn)生4個(gè)高電平脈沖幀尾一個(gè)總時(shí)長(zhǎng)大約在10毫秒左右非??臁_@里有個(gè)細(xì)節(jié)值得說(shuō)一下為什么地址碼只有4位因?yàn)樵诘统杀緢?chǎng)景下16種排列基本夠用比如同一棟樓的幾個(gè)車庫(kù)門大家用不同的地址就能互相區(qū)分。而且為了安全發(fā)射端會(huì)連續(xù)發(fā)送好幾幀接收端連續(xù)兩次收到相同數(shù)據(jù)才執(zhí)行動(dòng)作這樣誤觸發(fā)的概率已經(jīng)降得很低。如果你想做更嚴(yán)謹(jǐn)?shù)南到y(tǒng)地址位完全可以擴(kuò)展到16位甚至32位代碼上的改動(dòng)只是多讀幾個(gè)bit而已。4. 編碼端實(shí)現(xiàn)用Arduino寫一個(gè)433MHz發(fā)射器4.1 硬件連接發(fā)射端的接線特別簡(jiǎn)單Arduino Nano的D4引腳作為數(shù)據(jù)輸出接到發(fā)射模塊的DATA引腳模塊的VCC接5VGND接GND。注意發(fā)射模塊的DATA引腳是直接控制載波的不要接到PWM引腳上去。引腳分配如下模塊引腳接Arduino引腳DATAD4VCC5VGNDGND4.2 發(fā)送函數(shù)的核心思路編碼端的核心就是精確控制“高電平持續(xù)多久、低電平持續(xù)多久”。在Arduino里digitalWrite()控制電位delayMicroseconds()控制延時(shí)。只要把前面定義好的時(shí)序參數(shù)逐一實(shí)現(xiàn)數(shù)據(jù)就能發(fā)送出去。實(shí)際編碼時(shí)有一個(gè)很容易踩的坑delayMicroseconds()的延時(shí)精度依賴單片機(jī)時(shí)鐘在Arduino Nano16MHz晶振上是微秒級(jí)別的基本夠用。但如果發(fā)送過(guò)程中突然來(lái)了串口中斷或者芯片的其他中斷延時(shí)會(huì)有幾十微秒的抖動(dòng)。對(duì)于560微秒對(duì)1680微秒這種比例差異足夠大的編碼來(lái)說(shuō)幾十微秒的抖動(dòng)不會(huì)造成誤判但如果你把時(shí)序設(shè)計(jì)得太敏感比如高電平和低電平只差100微秒中斷就會(huì)成為隱患。所以編碼設(shè)計(jì)時(shí)1和0的脈寬比例至少要拉大到2倍以上才穩(wěn)妥。4.3 完整發(fā)射代碼const int TX_PIN 4; void setup() { pinMode(TX_PIN, OUTPUT); digitalWrite(TX_PIN, LOW); } // 發(fā)送一個(gè)數(shù)據(jù)位 // bit為1時(shí)高560us 低1680us // bit為0時(shí)高560us 低560us void sendBit(bool bit) { digitalWrite(TX_PIN, HIGH); delayMicroseconds(560); digitalWrite(TX_PIN, LOW); if (bit) { delayMicroseconds(1680); } else { delayMicroseconds(560); } } // 發(fā)送引導(dǎo)碼高9000us 低4500us void sendSync() { digitalWrite(TX_PIN, HIGH); delayMicroseconds(9000); digitalWrite(TX_PIN, LOW); delayMicroseconds(4500); } // 發(fā)送4位數(shù)據(jù) void sendNibble(uint8_t nibbleValue) { for (int i 3; i 0; i--) { sendBit((nibbleValue i) 0x01); } } // 發(fā)送完整一幀引導(dǎo)碼 4位地址 4位數(shù)據(jù) 幀尾 void sendFrame(uint8_t addr, uint8_t data) { sendSync(); sendNibble(addr); sendNibble(data); digitalWrite(TX_PIN, HIGH); delayMicroseconds(560); digitalWrite(TX_PIN, LOW); } void loop() { // 連發(fā)3幀接收端更容易穩(wěn)定解調(diào)出完整數(shù)據(jù) for (int i 0; i 3; i) { sendFrame(0xA, 0x5); // 地址0xA數(shù)據(jù)0x5按下第6個(gè)鍵 delay(30); } delay(1000); }這里有幾個(gè)設(shè)計(jì)細(xì)節(jié)值得說(shuō)明連發(fā)3幀是因?yàn)槌偕邮漳K在同步階段需要時(shí)間建立穩(wěn)定輸出第一幀可能就是亂的。實(shí)測(cè)中發(fā)3到5幀基本上能讓接收端穩(wěn)定解調(diào)。幀與幀之間留了30毫秒的間隔避免相鄰幀的數(shù)據(jù)位互相干擾。地址0xA、數(shù)據(jù)0x5只是示例你可以改成任何需要的值只要發(fā)送端和接收端約定的地址一致就行。4.4 發(fā)送端調(diào)試要點(diǎn)調(diào)試發(fā)射端時(shí)你手頭最好有一個(gè)能顯示波形的工具邏輯分析儀或者示波器都行把接收模塊的DATA輸出接上去看波形。如果沒(méi)有就用一個(gè)最便宜的433MHz接收模塊接Arduino用Serial把解調(diào)后的高低電平寬度打出來(lái)也能判斷發(fā)射端是否有問(wèn)題。最關(guān)鍵的一點(diǎn)是如果你發(fā)現(xiàn)發(fā)射時(shí)模塊附近的MCU程序卡頓或者重啟多半是電源問(wèn)題模塊瞬間拉低母線電壓造成的。這時(shí)在模塊的VCC和GND之間加一個(gè)100微法左右的電解電容通常就能解決。5. 解碼端實(shí)現(xiàn)狀態(tài)機(jī)解析無(wú)線信號(hào)5.1 硬件連接與中斷機(jī)制接收端硬件依然簡(jiǎn)單接收模塊的DATA引腳接Arduino Nano的D2因?yàn)镈2支持外部中斷VCC接5VGND接GND。解碼的最大難點(diǎn)在于信號(hào)是一連串不規(guī)則的方波你沒(méi)法在主循環(huán)里持續(xù)快速采樣——Arduino主循環(huán)跑得再快也扛不住微秒級(jí)脈沖的頻繁變化。正確做法是使用外部中斷給D2配置一個(gè)CHANGE觸發(fā)的外部中斷任何一個(gè)電平跳變上升沿或下降沿都會(huì)打斷主循環(huán)進(jìn)入中斷處理函數(shù)。在中斷處理函數(shù)里用micros()記錄當(dāng)前時(shí)間減去上次跳變的時(shí)間就得到了上一個(gè)狀態(tài)的持續(xù)時(shí)間。這個(gè)持續(xù)時(shí)間配上當(dāng)前電平就構(gòu)成了解析的基本信息。5.2 解碼狀態(tài)機(jī)的設(shè)計(jì)拿到脈沖寬度之后接下來(lái)的問(wèn)題是這些寬度到底是引導(dǎo)碼、邏輯0還是邏輯1這里我用一個(gè)簡(jiǎn)單的狀態(tài)機(jī)來(lái)處理狀態(tài)0等待同步。如果檢測(cè)到高電平寬度超過(guò)7000微秒認(rèn)為引導(dǎo)碼的高電平出現(xiàn)了進(jìn)入狀態(tài)2。狀態(tài)2等待引導(dǎo)碼的低電平結(jié)束。如果低電平寬度超過(guò)4000微秒確認(rèn)同步碼完整進(jìn)入狀態(tài)1同時(shí)對(duì)地址碼和數(shù)據(jù)碼的暫存器清零。狀態(tài)1讀取數(shù)據(jù)位。每個(gè)數(shù)據(jù)位的過(guò)程是先高電平后低電平我們重點(diǎn)觀察低電平的寬度。當(dāng)檢測(cè)到上升沿也就是低電平結(jié)束時(shí)用低電平寬度判斷是0還是1小于1000微秒判為0大于1000微秒判為1。每收滿8位4位地址4位數(shù)據(jù)一幀就算接收完成。為什么狀態(tài)機(jī)要分成“等待同步高電平”和“等待同步低電平”兩步因?yàn)槿绻蛔鲞@一步同步碼那4500微秒的低電平會(huì)被誤當(dāng)成第一個(gè)數(shù)據(jù)位導(dǎo)致整幀錯(cuò)位。這也是新手做解碼最容易犯的錯(cuò)誤。5.3 完整解碼代碼const int RX_PIN 2; // D2支持外部中斷 volatile unsigned long lastTime 0; volatile unsigned long pulseWidth 0; volatile int pulseLevel 0; volatile bool newPulse false; void onPinChange() { unsigned long now micros(); pulseWidth now - lastTime; lastTime now; pulseLevel digitalRead(RX_PIN); newPulse true; } int state 0; // 0:等待同步高電平, 2:等待同步低電平, 1:讀取數(shù)據(jù)位 int bitCount 0; uint8_t addr 0; uint8_t data 0; void setup() { Serial.begin(115200); pinMode(RX_PIN, INPUT); attachInterrupt(digitalPinToInterrupt(RX_PIN), onPinChange, CHANGE); } void loop() { if (newPulse) { newPulse false; if (state 0) { // 檢測(cè)到下降沿并且剛結(jié)束的高電平比較長(zhǎng) if (pulseLevel LOW pulseWidth 7000) { state 2; // 進(jìn)入等待同步低電平 } } else if (state 2) { // 檢測(cè)到上升沿并且剛結(jié)束的低電平也足夠長(zhǎng)確認(rèn)同步碼 if (pulseLevel HIGH pulseWidth 4000) { state 1; bitCount 0; addr 0; data 0; } else { state 0; // 同步碼不符合預(yù)期重新等待 } } else if (state 1) { // 上升沿表示一個(gè)低電平剛結(jié)束此時(shí)低電平寬度決定該位的值 if (pulseLevel HIGH) { bool bit (pulseWidth 1000); // 低電平長(zhǎng) - 邏輯1 if (bitCount 4) { addr (addr 1) | bit; } else if (bitCount 8) { data (data 1) | bit; } bitCount; if (bitCount 8) { // 一幀收完輸出結(jié)果 Serial.print(Addr:); Serial.print(addr, HEX); Serial.print( Data:); Serial.println(data, HEX); state 0; } } } } }5.4 數(shù)據(jù)校驗(yàn)與穩(wěn)定性提升上面這個(gè)代碼能跑通但直接用于正式場(chǎng)合還不夠穩(wěn)。我實(shí)際使用中會(huì)在代碼里加一個(gè)“連續(xù)兩幀相同才執(zhí)行”的機(jī)制。因?yàn)闊o(wú)線環(huán)境里的噪聲是隨機(jī)的偶發(fā)的一幀錯(cuò)誤數(shù)據(jù)完全可能通過(guò)引導(dǎo)碼檢測(cè)最后解析出一個(gè)亂七八糟的地址和數(shù)據(jù)。但如果要求連續(xù)兩次收到的幀完全一致才執(zhí)行動(dòng)作單幀誤碼的概率就被過(guò)濾掉了。實(shí)現(xiàn)方式是在解析完一幀后不立即執(zhí)行而是存到lastAddr和lastData變量里。下一幀解析完成后與上一次比對(duì)一致才輸出有效信號(hào)。這個(gè)改動(dòng)對(duì)代碼量影響不大但可靠性提升非常明顯。另外如果你發(fā)現(xiàn)接收模塊在沒(méi)有信號(hào)時(shí)也會(huì)觸發(fā)大量中斷那是超再生接收模塊的“靜噪噪聲”在作怪程序?qū)用嬷荒苡脿顟B(tài)機(jī)多次校驗(yàn)來(lái)扛無(wú)法完全消除。6. 實(shí)測(cè)中的坑與排錯(cuò)經(jīng)驗(yàn)6.1 發(fā)射距離遠(yuǎn)不如預(yù)期這個(gè)是最常見(jiàn)的問(wèn)題。我一開(kāi)始用FS1000A發(fā)射模塊配超再生接收裸板加一根短短的杜邦線做天線空曠地方實(shí)測(cè)只有七八米。排查下來(lái)有三個(gè)原因天線太短。換了一根17.3厘米的漆包線拉直垂直放置距離立刻到了三四十米。發(fā)射電壓偏低。當(dāng)時(shí)用Arduino的3.3V給模塊供電改成5V后發(fā)射功率上了一個(gè)臺(tái)階。接收模塊太差。超再生模塊在強(qiáng)干擾環(huán)境下的表現(xiàn)確實(shí)不如超外差換超外差接收模塊后同樣的發(fā)射端距離又翻了一倍。經(jīng)驗(yàn)是先查天線再查供電最后再懷疑模塊本身。這個(gè)順序按照“手段簡(jiǎn)單到復(fù)雜”排大多數(shù)情況下前兩個(gè)就能解決。6.2 數(shù)據(jù)顯示亂碼如果你在Serial監(jiān)視器里看到一堆匪夷所思的地址和數(shù)據(jù)大概率不是代碼邏輯問(wèn)題而是信號(hào)質(zhì)量問(wèn)題。把握三個(gè)排查點(diǎn)接收模塊的DATA引腳有沒(méi)有上拉或直接懸空超再生的輸出在沒(méi)有信號(hào)時(shí)是隨機(jī)電平亂碼是這個(gè)模塊的正常表現(xiàn)。處理方式是靠引導(dǎo)碼過(guò)濾而不是硬件上硬消。發(fā)送端和接收端的電壓是否都穩(wěn)定用劣質(zhì)USB供電線模塊一旦拉電流電壓就會(huì)掉信號(hào)波形會(huì)變形。時(shí)鐘誤差。Arduino的晶振確實(shí)有一定偏差如果兩個(gè)板子偏差方向相反幾百微秒的脈寬就可能超出閾值范圍。解決辦法是適當(dāng)放寬判定閾值比如把邏輯1的低電平判定閾值從1000微秒改成1100微秒同時(shí)保證邏輯0的低電平不超過(guò)800微秒。6.3 狀態(tài)機(jī)偶爾卡死當(dāng)接收信號(hào)中斷或者一幀數(shù)據(jù)在傳輸過(guò)程中短掉狀態(tài)機(jī)可能停在“讀取數(shù)據(jù)位”狀態(tài)再也不接收新幀。我的處理辦法是加一個(gè)超時(shí)復(fù)位記錄每次進(jìn)入狀態(tài)1的時(shí)間如果超過(guò)50毫秒還沒(méi)收滿一幀就重置回狀態(tài)0。無(wú)線環(huán)境下幀丟失不可避免程序必須健壯到“一次壞幀不影響下一幀”的程度。6.4 433MHz項(xiàng)目還能往哪個(gè)方向擴(kuò)展這套編解碼框架其實(shí)只是起點(diǎn)。往實(shí)用方向走你可以把發(fā)射端做成一個(gè)小遙控器幾個(gè)按鍵接Arduino每個(gè)按鍵觸發(fā)不同的數(shù)據(jù)碼再配一個(gè)天線塞進(jìn)塑料殼里成本不到20塊。往安全方向走可以擴(kuò)展地址碼到16位或32位讓不同遙控器之間碰撞概率趨近于零也可以加入滾動(dòng)碼機(jī)制即每次發(fā)送的數(shù)據(jù)攜帶一個(gè)遞增計(jì)數(shù)器接收端記錄上一次的計(jì)數(shù)器值只接受比上次大的值這樣錄碼器錄到一次信號(hào)后也沒(méi)辦法重放攻擊。往智能化方向走433MHz接收模塊接上ESP32數(shù)據(jù)解析后通過(guò)MQTT上報(bào)就能把傳統(tǒng)射頻遙控設(shè)備并入家里的自動(dòng)化系統(tǒng)——我最近就在做一個(gè)把老舊車位鎖接入本地智能家居控制的小項(xiàng)目底層就是這套解碼邏輯。433MHz這套編碼解碼項(xiàng)目是我入坑射頻DIY以來(lái)覺(jué)得投資回報(bào)率最高的一個(gè)——硬件成本極低原理不復(fù)雜但能覆蓋從簡(jiǎn)單遙控到智能控制系統(tǒng)的完整鏈路。如果你正準(zhǔn)備上手我的建議是別急著堆功能先按文章里的電路和代碼把一幀數(shù)據(jù)從發(fā)射端送到接收端從Serial監(jiān)視器里看到自己定義的地址和數(shù)據(jù)整整齊齊地打印出來(lái)那種掌控感會(huì)推著你往更復(fù)雜的應(yīng)用走。