Skip to content

跳頻不是加密:ExpressLRS 原始碼與台灣的頻道數功率上限

2026年8月8日 1 分鐘
TL;DR ExpressLRS 的跳頻順序由一句綁定口令經 MD5 推出 UID、再餵給一個線性同餘產生器算出來,完全可複現(我移植成 Python 跟原始 C 碼逐位元對過),而鏈路本身沒有任何加密,只有 14 bit CRC。台灣 LP0002 又讓頻道數變成功率上限:2.4 GHz 跳頻用滿 75 個頻道可以發 1 W,不足只能發 0.125 W。

🌏 English version

規格表那篇拆過《遙控無人機資安檢測規範》怎麼定義「系列產品」——飛控、通訊及衛星定位晶片之模組均為相同者。三個模組裡,飛控在上一篇拆完了。這篇拆第二個:通訊。

反制那篇的核心命題是「電戰需要有訊號才能攻擊」。那篇沒說的是:那個訊號長什麼樣。這篇補上。

方法跟上一篇一樣——ExpressLRS 是開源的(GPLv3,GitHub 上),所以不需要示波器、不需要頻譜分析儀,也不需要買遙控器。所有數字取自 2026-07-31 的 HEAD,commit 5909f77

一、跳頻順序完全由一句口令決定

ExpressLRS 的使用者只設定一樣東西:綁定口令(binding phrase)。發射端和接收端輸入同一句話就配對成功。那句話做了什麼,原始碼裡是完整的四步。

第一步,口令變成 6 個位元組。 src/python/binary_configurator.py 第 43 行:

uid = hashlib.md5(('-DMY_BINDING_PHRASE="' + phrase + '"').encode()).digest()[0:6]

第二步,UID 變成亂數種子。 src/lib/OTA/OTA.cpp 第 41 行:

uint32_t OtaGetUidSeed()
{
    return ((uint32_t)UID[2] << 24) + ((uint32_t)UID[3] << 16) +
           ((uint32_t)UID[4] << 8) + (UID[5]^OTA_VERSION_ID);
}

第三步,種子餵進一個線性同餘產生器。 src/lib/FHSS/random.cpp 整支就這麼短:

uint16_t rng(void) {
    const uint32_t m = 2147483648;
    const uint32_t a = 214013;
    const uint32_t c = 2531011;
    seed = (a * seed + c) % m;
    return seed >> 16;
}

第四步,用它洗牌出 256 格的跳頻序列。 src/lib/FHSS/FHSS.cpp 的註解直接寫了四個要求:每 n 跳出現一次同步頻道、同一區塊內不重複、每個頻道出現次數盡量均等、偽隨機。

這條鏈完全確定性。為了確定我讀對了,我把它移植成 Python,再把原始 C 碼裡的那兩支函式逐字複製成一個獨立的 C 程式,兩邊餵同一個種子,比對輸出:

$ diff <(python3 hop.py) <(./xcheck)
(無輸出)

七種頻道數(3、4、8、13、20、40、80)全部逐位元相同。所以下面的數字不是我推測的,是這套演算法真的會產生的東西:

口令 "taipei-2026" / FCC915
  UID  c8:fd:28:08:da:63   種子 0x2808da67   同步頻道 20
  前 16 跳(頻道)  [20, 0, 3, 37, 8, 7, 16, 34, 2, 6, 14, 5, 15, 13, 30, 33]
  前 6 跳(MHz)    [915.5, 903.5, 905.3, 925.7, 908.3, 907.7]

口令 "taipei-2027" / FCC915
  UID  49:b6:e8:ba:bf:8c   種子 0xe8babf88   同步頻道 20
  前 16 跳(頻道)  [20, 13, 24, 10, 19, 27, 15, 2, 37, 16, 29, 31, 1, 4, 34, 32]

口令差一個字元,整條序列完全不同——這是 MD5 該有的行為。而同一句口令在 900 MHz 和 2.4 GHz 得到的是同一個種子(種子的計算不看頻段),只是套用在不同大小的頻道表上。

二、所以「綁定」不是加密

這件事值得講清楚,因為中文討論裡常常混在一起。

ExpressLRS 的鏈路上沒有任何加密。我在 src/libsrc/src 底下找過,跟 RF 封包有關的只有一支 14 bit 的 CRC(多項式 0x2E57,初始值同樣來自 UID)。綁定口令做的是三件事:決定跳頻序列、決定 CRC 初始值、讓接收端能濾掉不是給自己的封包。

它解決的是「隔壁那台也在飛」,不是「有人想打你」。 這兩個問題長得像但完全不同:

  • 多台同時飛:不同口令 → 不同跳頻序列 → 碰撞機率低,偶爾撞到一格就被 CRC 濾掉。跳頻在這裡非常有效。
  • 有人蓄意干擾:對方如果不在乎頻譜效率,直接把整個 2400–2483.5 MHz 洗一遍就好,跳到哪一格都一樣。跳頻在這裡幫不上忙。

這正好接上反制那篇沒有展開的另一半。電戰之所以還是能對付大多數消費級與商規無人機,不是因為它破解了跳頻,是因為它根本不需要破解——寬頻壓制不挑頻道。而光纖導引之所以無解,是因為那條鏈路上根本沒有電磁訊號可壓。中間那一大片——加密鏈路、認知式跳頻、方向性天線——才是真正的攻防場。

要說得公道:ExpressLRS 是遙控鏈路,它的設計目標是低延遲與高可靠,不是抗蓄意攻擊,專案也沒有這樣宣稱過。把它當成安全機制是使用者的誤解,不是它的缺陷。但如果你在做要送資安檢測的產品,「通訊模組」這一格要交代的正是這件事。

三、速率換靈敏度:代價在原始碼裡標好了

src/src/common.cpp 有兩張並排的表,一張是調變參數,一張叫 ExpressLRS_AirRateRFperf,第二個欄位就是接收靈敏度(dBm)。把它們攤開:

2.4 GHz(SX1280)

模式封包間隔每幾包換頻道停留/頻道靈敏度
FLRC 1000Hz1000 µs22.0 ms−104 dBm
LoRa 500Hz2000 µs48.0 ms−105 dBm
LoRa 250Hz4000 µs416.0 ms−108 dBm
LoRa 150Hz6666 µs426.7 ms−112 dBm
LoRa 50Hz20000 µs240.0 ms−115 dBm

900 MHz(SX127x)

模式靈敏度
LoRa 200Hz−112 dBm
LoRa 100Hz−117 dBm
LoRa 50Hz−120 dBm
LoRa 25Hz−123 dBm

同一顆晶片上,速率從 500 Hz 降到 50 Hz 換來 10 dB;900 MHz 那邊從 200 Hz 降到 25 Hz 換來 11 dB。而跨頻段最極端的兩格——2.4 GHz 500 Hz 的 −105 dBm 對上 900 MHz 25 Hz 的 −123 dBm——差 18 dB。自由空間裡每 6 dB 大約是距離兩倍,所以那是大約八倍的距離差,代價是操控更新率從每秒 500 次掉到每秒 25 次。

「長距離就用 900 MHz 低速率」這句話常被講成經驗法則。它其實是一個可以在表格裡讀出來的工程交換,而且交換的兩端都有數字。

四、台灣:頻道數決定了你能合法發多大功率

現在把 NCC 的《低功率射頻器材技術規範》(LP0002,109 年 7 月 1 日版)攤在旁邊看。

§4.10.1.2 峰值傳導輸出功率,操作於 2400 MHz~2483.5 MHz 者:

(A) 至少使用 75 個跳頻頻道之跳頻系統:1 W(含)以下。 (B) 除(A)外,依 4.10.1.6 (1)(A)(a) 之跳頻系統:0.125 W(含)以下

而 §4.10.1.6 規定跳頻系統「須使用至少 15 個無重疊的頻道」。

所以 2.4 GHz 的跳頻器材有兩級:15~74 個頻道,功率天花板 125 mW;75 個以上,天花板 1 W。 差 9 dB。

ExpressLRS 的 ISM2G4 網域用幾個頻道?src/lib/FHSS/FHSS.cpp

{"ISM2G4", 2400400000, 2479400000, 80, 2440000000}

80 個。 剛好跨過那道 75 的門檻。

我不知道這是不是刻意設計的(FCC 的規則也有同一道 75 頻道門檻,所以更可能是照著美規來的),但結果一樣:頻道數在這裡不只是抗干擾參數,它是功率上限的開關。 少用 5 個頻道,合法功率就從 1 W 掉到 125 mW,換算成距離大約是三分之一。

同一節還有停留時間限制:「在 0.4 秒乘以跳頻頻道數之週期內,任一頻率占用之平均時間應小於或等於 0.4 秒」。上面那張表最慢的一檔停留 40 ms,離 400 ms 還有十倍餘裕。這一項不會是瓶頸。

(順帶一提,PowerLevels_e 這個列舉一路開到 PWR_2000mW。2 W 高過 LP0002 給 2.4 GHz 的 1 W 上限——韌體提供的檔位跟你所在地合法的檔位是兩件事,這件事使用者要自己負責。)

五、那條名字叫「航空模型飛機遙控器」的條文,管不到今天的遙控器

LP0002 §5.3 有一整節叫「無線電遙控器」,§5.3.1 是「模型玩具無線電遙控器」,括號裡明寫 aircraft device。看起來這就是無人機遙控器該用的條文。

打開來看它給的頻率:

(1) 下列頻率可供任何形式之遙控器使用:26.995 MHz、27.045 MHz……27.245 MHz。 (2) 下列頻段僅限航空模型飛機遙控器使用:72.00 MHz~72.99 MHz,頻道間隔:20 kHz。 (3) 下列頻段僅限地表模型遙控器使用:75.41 MHz~75.99 MHz。

有效輻射功率 0.75 W,頻帶寬度 8 kHz 以內。然後是 §5.3.1.7 限制事項的第一條:

(1) 限單向控制。

這是 1990 年代水晶振盪器遙控器的規格。限單向控制這五個字,直接把所有會回傳電量、RSSI、GPS 的現代遙控器排除在外——而回傳遙測正是 ExpressLRS 每 16 到 128 包就做一次的事(2.4 GHz 那張表的 TLMinterval 欄位從 TLM_RATIO_1_16TLM_RATIO_1_128)。

所以今天在台灣賣的 2.4 GHz 遙控器,走的不是這條,是 §4.10「工作頻率為 2400 MHz~2483.5 MHz……採用跳頻或數位調變」——一條泛用的展頻條文,跟藍牙、Wi-Fi、無線滑鼠共用。

這是這個系列第四次遇到同一種形狀:名字叫某某的條文,管的不是某某。 無人機專章沒有隱私條款資安規範定義「系列」用的是硬體模組不是韌體題庫的氣象科目考的是有人機航空氣象學,現在是「航空模型飛機遙控器」只給 72 MHz 且限單向。查法規時,先確認條文的名字和它的內容有沒有對上,比找到那條更重要。

不過 LP0002 有一條把兩套制度縫起來了。§3.8.3:

供遙控無人機或類似器材遙控之使用,應符合目的事業主管機關有關遙控無人機之管理規定。

行政院公報的草案總說明寫明了這條為什麼改:「3.8.3 依民用航空法之用詞定義,將『模型航空器』修正為『遙控無人機』,並刪除多餘文字。」NCC 在 2020 年主動把用詞改成跟民航法一致。 頻率主管機關指向航空主管機關,這是明文的。

六、900 MHz 在台灣沒有 ExpressLRS 的位置

ExpressLRS 的次 GHz 網域有八個,其中最常用的 FCC915 涵蓋 903.5–926.9 MHz、40 個頻道。

台灣這一段開了多少?依 NCC 審驗一致性會議的紀錄,交通部於 106 年 2 月 22 日以交郵字第 10650017402 號公告修正《中華民國無線電頻率分配表》,新增 920~925 MHz 供低功率物聯網設備在次要條件下使用

920–925 只有 5 MHz,而且是「次要」——次要使用者必須忍受主要使用者的干擾,且不得干擾主要使用者。ExpressLRS 的任何一個次 GHz 網域都放不進這 5 MHz 裡。

(433 MHz 那邊,同一份會議紀錄提到「依低功率射頻電機技術規範 3.4.2 規定,433MHz 頻段可運用以傳送控制訊號」。ExpressLRS 有 EU433AU433US433 三個網域,但 §3.4.2 的實際限制值我沒查到全文,不做判斷。)

實務上的結論很直接:在台灣,2.4 GHz 是有明確條文可循的那條路,次 GHz 不是。

七、Listen Before Talk:歐盟的規則寫進了程式碼

順帶一個有意思的觀察。src/lib/LBT 整支被 #if defined(Regulatory_Domain_EU_CE_2400) 包起來——只有歐盟版本會編譯進去

裡面的門檻不是隨便訂的,註解直接寫了公式來源:

// Calculated from EN 300 328, adjusted for 800kHz BW for sx1280
// TL = -70 dBm/MHz + 10*log10(0.8MHz) + 10 × log10 (100 mW / Pout)

於是門檻隨發射功率變動:10 mW 時 −61 dBm、25 mW 時 −65、50 mW 時 −68、100 mW 時 −71。發得越大聲,就要對別人越敏感。

還有一個工程細節值得看:讀 RSSI 之前必須等多久,是隨展頻因子變的——SX1280 上 SF5 等 100 µs、SF6 141 µs、SF7 218 µs、SF8 實測 480 µs。註解寫著這是「TX→RX 切換時間 60 µs 加上一個 LoRa 符元時間」,而且 SF8 那格特別註明是量出來的不是算出來的。

法規條文變成程式裡一個帶單位的常數,這件事本身就是「合規」最具體的樣子。 上一篇談 BSD-3 與 GPLv3 是授權層的合規;這裡是射頻層的。兩者都在原始碼裡看得見。

這篇沒有回答的

  • 沒有量測。 全部是讀原始碼與法規條文。真實的發射功率、雜散、實際抗干擾表現要靠頻譜分析儀與電波暗室,那是檢測機構的事。
  • 沒有碰圖傳。 遙控鏈路(RC link)和影像回傳(VTX)是兩套不同的東西,頻段、功率、法規都不同。5.8 GHz 圖傳在台灣的狀況值得單獨寫。
  • 沒有處理其他遙控協定。 TBS Crossfire、FrSky ACCST/ACCESS、Spektrum DSMX 都是封閉的;DJI 的 OcuSync 更是。我只能讀開源的那一支,所以這篇的結論不能直接推廣到全部。
  • 沒有問檢測機構。 上面對 LP0002 的解讀是條文的字面閱讀。同一塊模組實務上怎麼認定、換韌體要不要重測,那要問受委託的檢測機構。

參考資料

一手:原始碼(ExpressLRS 5909f77,2026-07-31 取得)

  • ExpressLRS/ExpressLRS — GitHub(GPLv3;src/lib/FHSS/FHSS.cpp 的九個 regulatory domain 表與序列建構、src/lib/FHSS/random.cpp 的線性同餘產生器、src/lib/OTA/OTA.cppOtaGetUidSeed()src/python/binary_configurator.py 的綁定口令 MD5、src/src/common.cpp 的速率與靈敏度表、src/lib/LBT/LBT.cpp 的 EN 300 328 門檻、src/lib/POWERMGNT/POWERMGNT.h 的功率檔位)
  • FHSS.cpp — ExpressLRS(跳頻序列四項要求的原始註解)
  • common.cpp — ExpressLRSExpressLRS_AirRateConfigExpressLRS_AirRateRFperf,本文靈敏度與停留時間全部由這兩張表算出)

一手:台灣法規

站內