我是怎么修好電腦開機(jī)鍵的:ASK/OOK自動(dòng)接收與解碼

本文假裝修理電腦開機(jī)鍵皿伺,其實(shí)是在解決無線電遙控研究的一個(gè)基礎(chǔ)問題:怎樣用廉價(jià)的方案持續(xù)監(jiān)聽ASK/OOK信號(hào)并自動(dòng)解碼员辩。ASK/OOK廣泛應(yīng)用于各種無線電遙控設(shè)備,包括家電鸵鸥、卷簾門奠滑、車門……

背景

我的臺(tái)式機(jī)的開機(jī)鍵確實(shí)是壞了。曾經(jīng)買過一個(gè)嘗試替換妒穴,但因規(guī)格不同裝不上去宋税;還用復(fù)位鍵代替開機(jī)鍵用過一陣子,但復(fù)位鍵太小讼油,按得不爽杰赛。本來有小米的WiFi開關(guān),但還不知如何整合矮台。萬般無奈乏屯,用無線電遙控按鈕加上網(wǎng)絡(luò)喚醒成了最后簡便可行的方案根时。

另一方面,以前文章說過用軟件無線電(比如HackRF + Inspectrum)可以接收ASK/OOK信號(hào)并解碼辰晕,但有幾個(gè)缺點(diǎn):

  1. 成本高蛤迎;
  2. 不能自動(dòng)解碼:如果要解碼大量數(shù)據(jù)(比如研究滾碼),用Inspectrum手動(dòng)解碼是非常煩瑣的伞芹;
  3. 不能用于長期監(jiān)聽:因?yàn)镚NU Radio儲(chǔ)存的是高采樣率的原始信號(hào)忘苛,每秒數(shù)MB。

所以唱较,還是要再找一條路扎唾。

所需材料

  1. ASK接收/解調(diào)模塊:圖左上, 有兩種,都是5元左右南缓;
  2. 樹莓派:圖右上胸遇;
  3. 無線按鈕:圖下, 本來可以用其它無線遙控器代替,但這種比較像按鈕汉形。假貨寶上不多見纸镊,略貴,15元概疆。
圖1:修理電腦開機(jī)鍵所需材料

ASK接收/解調(diào)模塊的作用是將315/433MHz的調(diào)幅信號(hào)解調(diào)逗威。除了電源,它有一個(gè)數(shù)據(jù)腳岔冀,輸出解調(diào)的高低電平凯旭;有的模塊有一個(gè)使能腳,但不接也行(因?yàn)楦?dòng)為高電平使套,即啟用狀態(tài))罐呼。

圖中有一個(gè)模塊沒接天線,其實(shí)也可以收到信號(hào)侦高;也可以用一根20cm左右的導(dǎo)線做天線嫉柴。感覺差別不大。

無線按鈕的作用則相反:將一組數(shù)據(jù)編碼奉呛,然后用315/433MHz調(diào)幅發(fā)射出去计螺。這組數(shù)據(jù)是芯片預(yù)置的,據(jù)說每個(gè)芯片都不一樣瞧壮。編碼方式實(shí)測是脈寬編碼(PWM)登馒,這是無線電遙控器里最常用的。

認(rèn)識(shí)接收模塊

把這兩個(gè)接收模塊加上5V電源馁痴,然后用示波器觀察數(shù)據(jù)引腳。

圖2. 用示波器觀察解調(diào)的信號(hào)

然后發(fā)現(xiàn)杯具了肺孤,怎么總是有“信號(hào)”罗晕?——這济欢,就是傳說中的噪聲。便宜無好貨? 也許小渊。但電路噪聲法褥、空中擁擠的無線電、甚至樹莓派產(chǎn)生的射頻……要完全消除噪聲酬屉,并不是那么容易的半等。所以,保持平常心呐萨。

圖3. 接收模塊的噪聲

再看有信號(hào)時(shí):按一下無線按鈕杀饵。這次倒比較像樣。

圖4. 有信號(hào)輸入時(shí)谬擦,接收模塊的輸出

為了驗(yàn)證其正確性切距,用樹莓派發(fā)射一個(gè)信號(hào)。黃色是發(fā)射的惨远,綠色是接收的谜悟。可以看到波形基本一致北秽,有一丟丟的延時(shí)葡幸。

圖5:發(fā)射和接收的信號(hào)

認(rèn)識(shí)無線按鈕

從圖4可以看出,無線按鈕的信號(hào)是脈寬調(diào)制(PWM)的贺氓,即一對(duì)高低電平的組合表示1bit蔚叨,高電平較寬的代表1,低電平較寬的代表0掠归。圖中的波形可以解讀為 1000 1010 1001 10...

縮小比例缅叠,看看整體。

圖6. 無線按鈕發(fā)射的信號(hào)整體

可以看到虏冻,以一段大約10ms的低電平作為分隔肤粱,我們可以將其作為信號(hào)開始的標(biāo)志,即 start0 + data (+ start0)厨相。

在上一篇文章中领曼,我們建立的遙控信號(hào)的模型是:先有一段較長的高電平作為開始,然后一小段低電平蛮穿,然后是數(shù)據(jù)庶骄,最后有一段低電平作為結(jié)束,即 start1 + start0 + data + stop0践磅。

顯然后者是更合理的单刁。因?yàn)闆]有信號(hào)時(shí),接收模塊的輸出就是低電平府适。這個(gè)無線按鈕有偷工之嫌羔飞。但我們能適應(yīng)肺樟,硬件不夠,軟件來補(bǔ)逻淌。

關(guān)鍵代碼

初始化

接收模塊的驅(qū)動(dòng)能力一般很弱么伯,所以,不要使用上拉或下拉電阻卡儒。

    #  do NOT use pull_up_down=GPIO.PUD_UP nor GPIO.PUD_DOWN
    GPIO.setup(pin_rx, GPIO.IN)

如何采樣

樹莓派有提供 callback(即 add_event_detect) 和 wait_for_edge田柔,用于在每個(gè)電平跳變時(shí)進(jìn)行響應(yīng)/處理。但實(shí)際試了下骨望,效果并不好硬爆。感覺它們更適合于低速的場合,比如檢測傳統(tǒng)的開關(guān)狀態(tài)锦募。

最后摆屯,回歸到最樸素的輪詢式。另一方面也是因?yàn)殡娐分杏性肼暱纺叮词篃o信號(hào)時(shí)虐骑,數(shù)據(jù)腳也一直存在電平的跳變,用callback并不會(huì)更快赎线。

實(shí)測采樣周期設(shè)置為 0.1ms 或 0.05ms 都處理得過來廷没。由于ASK/OOK數(shù)據(jù)頻率通常在1~2KHz,所以 0.05ms相當(dāng)于20KHz的采樣率垂寥,足夠了颠黎。

下面的代碼保證間隔一個(gè)采樣周期進(jìn)行一次采樣,返回采樣時(shí)的電平及當(dāng)時(shí)的時(shí)間滞项。

    def get_sample(self):
        self.sample_time += self.sample_period
        now = time.time()
        wait = self.sample_time - now
        if wait > 0:
          time.sleep(wait)
        b = GPIO.input(self.pin_rx)
        now = time.time()
        return (b, now)

接收一段信號(hào)

根據(jù)前面的分析狭归,有的信號(hào)會(huì)以高電平作為前導(dǎo),有的會(huì)以低電平作為前導(dǎo)文判。所以过椎,下面的代碼等待一段足夠?qū)挼碾娖剑ū热?~10ms)作為信號(hào)的開始,而不管它是高電平還是低電平戏仓。之所以忽略太長的電平疚宇,是因?yàn)闆]有信號(hào)(萬一也沒有噪聲)時(shí),將會(huì)一直是低電平赏殃。

我們記錄起始時(shí)的電平敷待,然后將每個(gè)電平翻轉(zhuǎn)時(shí)對(duì)應(yīng)的時(shí)間記入一個(gè)時(shí)間戳數(shù)組。我們忽略太窄的脈寬仁热,因?yàn)樗ǔJ敲蹋ㄒ惨馕吨@多半是一段噪聲)榜揖;此時(shí),我們直接丟棄這段“信號(hào)”。

直到下一段較寬的電平時(shí)举哟,我們認(rèn)為這段信號(hào)結(jié)束钳幅。將時(shí)間戳數(shù)組和起始電平作為一個(gè)波形對(duì)象返回。這相當(dāng)于這個(gè)信號(hào)的物理層表示炎滞。

太短的“信號(hào)”也將被丟棄,因?yàn)樗ǔR彩窃肼曃芷颍挥幸饬x的數(shù)據(jù)不會(huì)那么短册赛。太長的“信號(hào)”也將被丟棄,因?yàn)檫@也可能是噪聲震嫉,而且它會(huì)使數(shù)組過大森瘪,可能導(dǎo)致程序崩潰。

    def receive(self):
        wave = BitWave()
        ts = wave.timestamp
        b = GPIO.input(self.pin_rx)
        now = time.time()
        ch = GPIO.wait_for_edge(self.pin_rx, GPIO.BOTH, timeout=self.min_gap)
        if ch is not None:
            return None
        b0 = b
        t0 = now
        ts.append(t0)
        ch = GPIO.wait_for_edge(self.pin_rx, GPIO.BOTH, timeout=self.max_gap-self.min_gap)
        b = GPIO.input(self.pin_rx)
        now = time.time()
        if ch is None:
            return None
        if b == b0:
            return None
        wave.startbit = b0
        self.bit = b
        self.sample_time = now
        self.edge_time = now
        ts.append(now)
        min_gap = 1e-3 * self.min_gap
        while True:
            (b, now) = self.get_sample()
            if b == self.bit:
                if now - self.edge_time > min_gap:
                    if b == 0:
                        ts.append(now)
                    return wave if len(ts) > 5 else None
            else:
                if now - self.edge_time < self.sample_period:
                    return None
                self.edge_time = now
                self.bit = b
                ts.append(now)
                if len(ts) > self.max_len:
                    return None
        return wave

PWM解碼

解碼是指票堵,從物理層的波形扼睬,解碼出數(shù)據(jù)鏈路層的比特流。

我們將接收到的波形按PWM來解碼悴势。但要注意窗宇,信號(hào)可能并不是PWM的,而且噪聲可能帶來毛刺或波形寬度的變化特纤。所以军俊,一旦碰到任何不符合預(yù)期的地方,我們毫不留情地返回失敗捧存。

首先我們要排除起始的高/低電平粪躬,最后一個(gè)bit也要排除,因?yàn)樗ǔв薪Y(jié)束時(shí)的低電平昔穴,而使得寬度不同于別的bit镰官。用這一段數(shù)據(jù),我們可以計(jì)算出每個(gè)符號(hào)(bit)的平均時(shí)長吗货,作為周期泳唠。

在解碼的過程中,任何一個(gè)電平寬度超過周期的卿操,都被視為不合法警检。

同時(shí),我們還把每對(duì)電平中較寬的脈寬累加起來害淤,用其平均值/周期作為占空比扇雕。占空比太大也被視為不合法。因?yàn)閷?duì)于1KHz的數(shù)據(jù)窥摄,如果占空比為95%镶奉,意味著窄的電平是0.05ms,這已經(jīng)是我們采樣精度的極限了。而且事實(shí)上哨苛,沒人會(huì)用這么高的占空比鸽凶,通常70%左右比較合理,這樣誤碼率更低建峭。

    def decode(self, wave):
        ts = wave.timestamp
        size = len(ts) - 1
        if size < 17:  # 8 bits at least
            return False
        if wave.startbit == 1:
            self.start1 = ts[1] - ts[0]
            i1 = 2
        else:
            self.start1 = 0
            i1 = 1
        self.start0 = ts[i1] - ts[i1 - 1]
        cbits = size - i1
        if (cbits % 2) > 0:
            return False
        self.bits = BitArray()
        csym = cbits / 2 - 1
        dur = ts[-3] - ts[i1]
        self.period = dur / csym
        self.stop0 = ts[-1] - ts[-3] - self.period
        if self.stop0 < 0:
            return False
        sum1 = 0
        c1 = 0
        for i in range(i1, size - 2):
            w = ts[i + 1] - ts[i]
            if w >= self.period:
                return False
            if w + w > self.period:
                sum1 += w
                c1 += 1
        if (c1 == 0):
            return False
        self.duty = sum1 / c1 / self.period
        if self.duty > 0.9:
            return False

        for i in range(i1, size - 1, 2):
            w = ts[i + 1] - ts[i]
            b = '0b1' if  w + w > self.period else '0b0'
            self.bits.append(b)
        return True

效果

因?yàn)榇a是輪詢式玻侥,所以要關(guān)心一下CPU占用率。在樹莓派3代上亿蒸,采樣周期設(shè)置為0.05ms時(shí)凑兰,用top查看,CPU占用率大約25%边锁;而且改變采樣周期姑食,基本沒有變化。換不同的電路板時(shí)倒有有差別(另一塊電路板在10%以下茅坛,可惜已經(jīng)掛了)音半,可能和噪聲水平有關(guān)系。

其實(shí)在同一個(gè)樹莓派上贡蓖,開兩個(gè)進(jìn)程曹鸠,一個(gè)發(fā)送,一個(gè)接收斥铺,完全沒有壓力物延,收到的數(shù)據(jù)能正確解碼。

至于噪聲仅父,雖然示波器上一直有噪聲電平叛薯,但經(jīng)過層層檢驗(yàn),幾乎沒出現(xiàn)把噪聲解碼為信號(hào)的情況笙纤。

但有信號(hào)時(shí)會(huì)出現(xiàn)誤碼耗溜,表現(xiàn)在:

  1. 如果發(fā)射端比較遠(yuǎn)或信號(hào)比較弱時(shí),有一定的誤碼省容;
  2. 在按一下鍵時(shí)抖拴,通常會(huì)將信號(hào)重復(fù)發(fā)送3~5次,其中可能會(huì)有解碼錯(cuò)誤的腥椒;尤其是最后一個(gè)信號(hào)阿宅。其實(shí)最后一個(gè)信號(hào)可能根本沒有完整發(fā)送。因?yàn)橛械倪b控器在按下鍵時(shí)會(huì)一直發(fā)送笼蛛,松開時(shí)就立即停止發(fā)送洒放。
  3. 經(jīng)過博聯(lián)(Broadlink)RM2這樣的萬能遙控器學(xué)習(xí)后、再發(fā)送的“二手碼”滨砍,誤碼會(huì)更高一些往湿。

下圖是實(shí)際運(yùn)行效果妖异,簡單說明下:

  • [WAV] 表示原始波形信息。44.4ms/0/51 表示總長44.4ms领追,以低電平開始他膳,總共51段,后面是每段電平的時(shí)長绒窑,單位ms棕孙。
  • [PWM] 表示PWM解碼成功,后面是其信息:起始高電平/低電平時(shí)長/結(jié)束低電平時(shí)長些膨、比特周期散罕、占空比、總位數(shù)傀蓉、數(shù)據(jù)。
  • 綠色框表示解碼符合預(yù)期职抡;橙色框表示雖然是正確的PWM碼葬燎,但數(shù)據(jù)有丟失,不同于發(fā)射數(shù)據(jù)缚甩;紅色框則是噪聲了(不能成功解碼)谱净。
圖7:解碼及誤碼情況

另外,對(duì)于按一下鍵擅威,連續(xù)發(fā)射的多個(gè)信號(hào)壕探,并不能全部接收到。因?yàn)榻邮盏揭粋€(gè)信號(hào)后郊丛,程序就會(huì)去解碼李请,而解碼耗費(fèi)的時(shí)間可能會(huì)錯(cuò)過一個(gè)信號(hào)(的起始電平)。用多線程也許能解決這個(gè)問題厉熟。但并沒有多大必要导盅,因?yàn)榘l(fā)射端本來就會(huì)重復(fù)發(fā)送多次同樣的信號(hào),只要能接收到其中一個(gè)完整的就行了揍瑟。

成果

有了這個(gè)自動(dòng)接收/解碼機(jī)白翻,首先解碼了無線電按鈕,據(jù)此觸發(fā)WOL绢片,實(shí)現(xiàn)了按鍵就能開電腦的效果B蒜伞(我容易嗎我)

然后,很容易地解碼了家里的所有無線電遙控器底循,做成字典巢株。一方面,配合發(fā)射模塊熙涤,可以程序控制所有設(shè)備纯续;另一方面随珠,可以監(jiān)視家里的所有無線電活動(dòng)(作為日志嘛)。當(dāng)然猬错,也許會(huì)收到別的信號(hào)(你懂的)窗看。

圖8:家中的無線電活動(dòng)

結(jié)語

用樹莓派+5元錢的ASK接收模塊,即可實(shí)現(xiàn)對(duì)ASK/OOK信號(hào)的自動(dòng)接收和解碼倦炒。相比于軟件無線電套件(HackRF, GNU Radio, Inspectrum)显沈,輕巧低廉。而且在這一領(lǐng)域逢唤,可以說更強(qiáng)大拉讯。實(shí)測用腳本語言就可以滿足數(shù)據(jù)采集和解碼的性能要求,開發(fā)調(diào)試非常方便鳖藕。主要的問題是處理噪聲魔慷,增加一些有效性檢查。

在修好電腦開機(jī)鍵的同時(shí)著恩,順便完成了發(fā)射/接收院尔,解碼/編碼的整套工具集,可以用于ASK/OOK的研究喉誊。

代碼已開源:https://github.com/loblab/rfask

目前支持PWM和曼徹斯特編碼邀摆。

圖9:ASK/OOK工具集
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伍茄,隨后出現(xiàn)的幾起案子栋盹,更是在濱河造成了極大的恐慌,老刑警劉巖敷矫,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件例获,死亡現(xiàn)場離奇詭異,居然都是意外死亡曹仗,警方通過查閱死者的電腦和手機(jī)躏敢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來整葡,“玉大人件余,你說我怎么就攤上這事≡饩樱” “怎么了啼器?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俱萍。 經(jīng)常有香客問我端壳,道長,這世上最難降的妖魔是什么枪蘑? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任损谦,我火速辦了婚禮岖免,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘照捡。我一直安慰自己颅湘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布栗精。 她就那樣靜靜地躺著闯参,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悲立。 梳的紋絲不亂的頭發(fā)上鹿寨,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音薪夕,去河邊找鬼脚草。 笑死,一個(gè)胖子當(dāng)著我的面吹牛原献,可吹牛的內(nèi)容都是我干的馏慨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嚼贡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了同诫?” 一聲冷哼從身側(cè)響起粤策,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎误窖,沒想到半個(gè)月后叮盘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霹俺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年柔吼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丙唧。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡愈魏,死狀恐怖巨坊,靈堂內(nèi)的尸體忽然破棺而出喷斋,到底是詐尸還是另有隱情,我是刑警寧澤狮鸭,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布胡本,位于F島的核電站牌柄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侧甫。R本人自食惡果不足惜珊佣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一蹋宦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咒锻,春花似錦冷冗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至敦捧,卻和暖如春须板,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兢卵。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工习瑰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秽荤。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓甜奄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窃款。 傳聞我的和親對(duì)象是個(gè)殘疾皇子课兄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容