Raspberry Pi Developer's Guide: Remote Control



無線遙控器常見的有2種, 一種是家電常用的紅外(IR)遙控模式, 另一種是防盜報警設備、門窗遙控、汽車遙控等等常用的無線電(RF)遙控模式。
無線電的使用距離較遠,應用範圍較廣,從遊戲用遙控賽車到汽機車遙控、防盜保全等應用。

無線遙控的原理就是發射機把控制的電信號先編碼, 然後再調製:
  • 紅外調製, 或者
  • 無線調頻、調幅
轉換成無線信號發送出去。 接收機收到載有資訊的無線電波接收, 放大, 解碼, 得到原先的控制電信號, 把這個電信號再進行功率放大用來驅動相關的電氣元件, 實現無線的遙控。


IR Remote Control


紅外遙控器的原理


關於遙控器


遙控器其核心元器件就是編碼芯片,將需要實現的操作指令例如選台、快進等事先編碼,設備接收後解碼再控制有關部件執行相應的動作。
顯然,接收電路及 CPU 也是與遙控器的編碼一起配套設計的。
編碼是通過載波輸出的,即所有的脈衝信號均調製在載波上,載波頻率通常為 38K。
載波是電信號去驅動紅外發光二極管,將電信號變成光信號發射出去,這就是紅外光,波長范圍在 840nm 到 960nm 之間。
在接收端,需要反過來通過光電二極管將紅外線光信號轉成電信號,經放大、整形、解調等步驟,最後還原成原來的脈衝編碼信號,完成遙控指令的傳遞,這是一個十分複雜的過程。
紅外線發射管通常的發射角度為 30-45 度之間,角度大距離就短,反之亦然。
遙控器在光軸上的遙控距離可以大於 8.5 米,與光軸成 30 度(水平方向)或 15 度(垂直方向)上大於 6.5 米,在一些具體的應用中會充分考慮應用目標,在距離角度之間需要找到某種平衡。

對於遙控器涉及到如下幾個主要問題:
  • 遙控器發出的編碼信號驅動紅外線發射管,必需發出波長範圍在 940nm 左右的的紅外光線
  • 因為紅外線接收器的接收二極管主要對這部分紅外光信號敏感,如果波長范圍不在此列, 顯然無法達到控制之目的。不過,幾乎所有的紅外家電遙控器都遵循這一標準。正因為有這 一物理基礎,多合一遙控器才有可能做成。
  • 遙控器發出一串編碼信號只需要持續數十 ms 的時間
  • 大多數是十多 ms 或一百多 ms 重復一次,一串編碼也就包括十位左右到數十位二進制編碼,換言之,每一位二進制編碼的持 續時間或者說位長不過 2ms 左右,頻率只有 500kz 這個量級,要發射更遠的距離必需通過載波,將這些信號調製到數十 khz,用得最多的是 38khz,大多數普通遙控器的載波頻率是所用的陶瓷振盪器的振盪頻率的 1/12,最常用的陶瓷振盪器是 455khz 規格,故最常用的載波也就是 455khz/12=37.9khz,簡稱 38k 載波。 此外還有 480khz(40k)、440khz(37k)、432khz(36k)等規格,也有 200k 左右的載波,用於高速編碼。 紅外線接收器是一體化的組件,為了更有針對性地接收所需要的編碼,就設計成以載波為中心頻率的帶通濾波器,只容許指定載波的信號通過。顯然這是多合一遙控器應該滿足的第二個物理條件。不過,家用電器多用38k,很多紅外線接收器也能很好地接收頻率相近的 40k 或 36k 的遙控編碼。
  • 一個設備受控,除了滿足上面提到的兩個基本物理條件外,最重要的變化多種多樣的當然應該是遙控器發出一串二進制編碼信號了
  • 這也是不同的遙控器不能相互通用的主要原因。 由於市場上出現成百上千的編碼方式並存,並沒有一個統一的國際標準,只有各芯片廠商事 實上的標準,這也是模擬並替換各種原廠遙控器最大的難點。隨著技術的不斷發展,很多公 司開發家電設備的遙控子系統時還不採用通用的編碼芯片,而是用通用的單片機隨心所欲地 自編一些編碼,這就使通用遙控的問題更加複雜化了。
  • 採用同樣的編碼芯片,也不意味著可以通用,因為還有客戶碼
  • 客戶碼設計的最初本意就是為了不同的設備可以相互區分互不干擾。 最初芯片廠商會從全局考慮給不同的家電廠商安排不同的客戶碼以規範市場,例如錄像機和電視機就用不同的設備碼,給甲廠分配的設備碼 和乙廠分配的設備碼就區分在不同的範圍內。
  • 採用同樣的編碼芯片、同樣的客戶碼下,也不能意味著一定可以通用
  • 因為對命令碼的分配與使用上,仍然是沒有固定的模式可以遵循,遙控器編碼芯片簡單的支持數十種命令碼, 多的上千種,但遙控器往往只有數十個鍵,甚至只有幾個鍵,如何從中選取這數十個鍵,這 些鍵如何分配使用,不同的系統設計師都自搞一套,這樣一來事情就更複雜化了。 設計需考慮的問題是如何“同化”不同遙控器發射信號之間的差異。遙控編碼方式涉及很多方 面,首先是數字 0 和 1 的表示(調寬還是調相,脈寬和占空比);其次是幀結構(引導碼和 結束碼,客戶碼和命令碼長度及發送方式);再次是幀間結構(僅發一次還是反復多次,多 幀交替發送,幀間間隔變化);最後是載波頻率,以 38Khz 居多,也有 40Khz 甚至 200khz 等特殊載波。 設計相應電路和軟件時對上述諸多因素加以分析、歸納,將編碼特點用一串二進制位表示出 來形成設備碼,對應於一個具體的遙控器。同一個設備碼下也就是同一個遙控器不同的按鍵 則用命令碼來表示。代碼型遙控器用軟件的方式對這些統一的編碼進行解釋,驅動一個個命 令碼按指定設備碼格式加以“封裝”,形成所需要的遙控信號,達到控製家電的目的。

紅外遙控器原理


一般的紅外遙控系統是由紅外遙控信號發射器、紅外遙控信號接收器和微控制器及其外圍電路等三部分構成的。
  • 遙控信號發射器用來產生遙控編碼脈衝,驅動紅外發射管輸出紅外遙控信號
  • 遙控接收頭完成對遙控信號的放大、檢波、整形、解調出遙控編碼脈衝
  • 遙控編碼脈衝是一組組串行二進制碼,對於一般的紅外遙控系統,此串行碼輸入到微控制器,由其
    內部 CPU 完成對遙控指令解碼,並執行相應的遙控功能。在紅外遙控系統中,解碼的核心是 CPU。它接收解調出的串行二進制碼,在內部根據本
    系統的遙控信號編碼格式將串行碼對應成遙控器上的按鍵。
顯然,這種在 CPU 內部解碼出的遙控指令是不便我們利用的,而且我們也不需要獲取它。我們只需利用一般紅外遙控系統 中的遙控發射器、遙控接收頭,自行設計解碼電路直接對遙控接收頭解調出的遙控編碼脈衝進行解碼,就可以得到原始的按鍵信息。 1. 紅外遙控編碼 目前應用中的各种红外遙控系統的原理都大同小異,區別只是在於各系統的信號編碼格式不同。 紅外遙控發射器組成了鍵掃描、編碼、發射電路。當按下遙控器上任一按鍵時,TC9012即產生一串脈衝編碼。 遙控編碼脈衝對 40kHz 載波進行脈衝幅度調製(PAM)後便形成遙控信號,經驅動電路由紅外發射管發射出去。 紅外遙控接收頭接收到調製後的遙控信號,經前置放大、限幅放大、帶通濾波、峰值檢波和波形整形,從而解調出與輸入遙控信號反相的遙控脈衝。  一次按鍵動作的遙控編碼信息為 32 位串行二進制碼。對於二進制信號“0”,一個脈衝 佔 1.2ms;對於二進制信號“1”,一個脈衝佔 2.4ms,而每一脈衝內低電平均為 0.6ms。從起 始標誌到 32 位編碼脈衝發完大約需 80ms,此後遙控信號維持高電平。若按鍵未釋放,則從 起始標誌起每隔 108ms 發出 3 個脈衝的重複標誌。 在 32 位的編碼脈衝中,前 16 位碼不隨按鍵的不同而變化,我們稱之為用戶碼。它是為 了表示特定用戶而設置的一個辨識標誌,以區別不同機種和不同用戶發射的遙控信號,防止 誤操作。後 16 位碼隨著按鍵的不同而改變,我們就是要讀取這 16 位按鍵編碼,經解碼得到 按鍵鍵號,轉而執行相應控制動作。 那麼,不同的按鍵編碼脈衝是怎樣和遙控器上不同的按鍵一一對應的呢?我們藉助於邏 輯分析儀記錄下來遙控器上每一個按鍵的編碼脈衝序列,破譯出了各按鍵的編碼。截取 16 位鍵碼的 8 位(比如後 8 位)就可達到識別按鍵的目的。當然,要加強遙控系統的抗干擾能 力,還需接收全 16 位鍵碼甚至 16 位用戶碼加以識別。
2. 紅外遙控解碼 紅外遙控接收頭解調出的編碼是串行二進制碼,包含著遙控器按鍵信息。但它還不便於 CPU 讀取識別,因此需要先對這些串行二進制碼進行解碼。下面所講的紅外遙控信號解碼電 路,它主要包括遙控編碼脈衝串並轉換電路與 PLD 解碼電路。 遙控編碼脈衝的串並轉換如下所述: 紅外遙控接收頭解調出的遙控編碼脈衝經一非門反相後引入計數器 4020 的複位端 (RST),4020 的腳 10(CP)端引入 1MHz 計數脈衝。遙控信號(已反相)中每一正脈衝到來時其高電平對4020 復位,經過0.6ms 遙控信號變為低電平,4020 復位結束,開始以1MHz 的頻率計數,直到下一個正脈衝到來時為止。二進制碼“0”每一脈衝週期低電平時間為0.6ms, 二進制碼“1”每一脈衝週期低電平時間為1.8ms,4020 的Q11 端即可以區分二進制碼“0”或“1” 。每一遙控編碼正脈衝上升沿到來時,若 Q11 端為“1”,說明前一位遙控碼為“1”;若 Q11 端為“0”,說明前一位遙控碼為“0”。 將4020 的Q11 端作為74HCS9S 的串行移位輸入端(SER),便可在每一個遙控編碼脈衝上升沿到來時並在4020 復位之前,將74HC595 中的數據沿Q0 到Q7 方向依次移一位,且4020 的Q11 端數據移入74HC595 的Q0 端。對於一組遙控編碼脈衝,共有 33 次上升沿(包 括起標誌),而 74HC595 僅為 8 位移位寄存器,所以移位的最終結果,只有遙控編碼脈衝 的最後 8 位保留在 74HC595 中。 當一組遙控編碼脈衝(反相後)來到時,其起始標誌的上跳沿觸發了雙單穩 74HC123 的 1B,在 1Q 上產生了一個寬度為 120ms 的正脈衝。 1Q 同時又觸發了74HC123 的2B,在產生一個寬度為80ms 的負脈衝,1Q 和相與後作為鎖存信號送至74HC595 的RCLK 端,即一組遙控編碼脈衝到來80ms 後,產生一個鎖存信號。此時 74HC595 已經移過了一組遙控碼, 芯片中保留的是最後 8 位遙控碼,鎖存信號將這最後 8 位遙控碼鎖存。 3. 紅外遙控信號編碼、發射原理 所有紅外遙控器的輸出都是用編碼後串行數據對 38~40kHz 的方波進行脈衝幅度調製 而產生的。如果直接對已調波進行測量,由於單片機的指令周期是微秒(μs)級,而已調 波的脈寬只有 20 多μs,會產生很大的誤差。因此先要對已調波進行解調,對解調後的波形 進行測量。 用遙控脈衝信號調製 38kHz 方波,然後將已調波放大,驅動紅外發光二極管,就可以得 到遙發射信號。調製可用一個或門實現,38kHz 方波可用 8751 的定時器 T1 產生。有些遙控 器的載頻可能是 40kHz,只須稍微加大發射功率仍然可用 38kHz 載頻使其接收電路動作。 通常,紅外遙控器是將遙控信號(二進制脈衝碼)調製在 38KHz 的載波上,經緩衝放 大後送至紅外發光二極管,轉化為紅外信號發射出去的。二進制脈衝碼的形式有多種,其中 最為常用的是 PWM 碼(脈衝寬度調製碼)和 PPM 碼(脈衝位置調製碼)。前者以寬脈衝 表示 1,窄脈衝表示 0。後者脈衝寬度一樣,但是碼位的寬度不一樣,碼位寬的代表 1,碼位 窄的代表 0。遙控編碼脈衝信號(以 PPM 碼為例)通常由引導碼、系統碼、系統反碼、功 能碼、功能反碼等信號組成。引導碼也叫起始碼,由寬度為9ms 的高電平和寬度為4.5ms 的低電平組成(不同的遙控系統在高低電平的寬度上有一定區別),用來標誌遙控編碼脈衝信號的開始。系統碼也叫識別碼,它用來指示遙控系統的種類,以區別其它遙控系統,防止 各遙控系統的誤動作。功能碼也叫指令碼,它代表了相應的控制功能,接收機中的微控制器 可根據功能碼的數值去完成各種功能操作。系統反碼與功能反碼分別是系統碼與功能碼的反 碼,反碼的加入是為了能在接收端校對傳輸過程中是否產生差錯。為了提高抗干擾性能和降低電源消耗,將上述的遙控編碼脈衝對頻率為38KHz(週期為26.3us)的載波信號進行脈幅調製(PAM),再經緩衝放大後送到紅外發光管,將遙控信號發射出去。 根據遙控信號編碼和發射過程,遙控信號的識別——即解碼過程應是去除 38KHz 載波信 號後識別出二進制脈衝碼中的 0 和 1。遙控信號識別、存儲、還原的硬件電路由 CPU、一體 化紅外接收頭、存儲器、還原調製與紅外發光管驅動電路組成.。一體化紅外接收頭負責紅 外遙控信號的解調。將調製在 38kHz 上的紅外脈衝信號解調並反向後再由單片機進行高電平 與低電平寬度的測量。

DIY萬能遙控器

沒有內置紅外線發射器的iPhone或某些Android手機,則必須另購”音訊紅外配件“,其售價雖然不高,不過以資源回收的:
  • 耳機線
  • 遙控器或滑鼠拆解下來的紅外線LED
來DIY,這樣不花半毛錢就能做出這個配件,而且動手做也好玩多了。
材料:(亦可在電子材料行購得,940nm紅外線發射LED每顆約8元、3.5mm立體插頭每個約9元)
  • IR LED × 2
  • 音頻3.5mm立體插頭 × 1
相關的遙控APP非常多,推薦一款控制碼收集最齊全的 《遙控精靈》→AndroidiOS 其可支援大部份的紅外遙控裝置、Wifi設備、智能插座。安裝後設定電器的品牌,然後依畫面指示來設定正確的控制碼。 至於為何要並聯兩顆腳位顛倒的IR LED? 而LED具有單向導通的整流功能,如果只接一顆IR LED,那麼正弦波就會被切掉一半而只剩半波。因此若並聯兩顆腳位顛倒的IR LED,那麼當其中一顆LED因逆向偏壓而無法送出訊號時,就可以由另一顆腳位顛倒的IR LED來送出被切掉的另一半波形,因此”音訊紅外配件”都是由兩顆IR LED並聯而成。這種設計有一個好處,那就是經由耳機孔所輸出的遙控功率能達到最大值,不過其正弦波的訊號頻率就得設計成一般電視遙控器輸出訊號頻率的一半(2倍週期),這樣全波整流後的訊號才能符合驅動電器的規格。這也是為何要接兩顆紅外LED的原因,因為若只接一顆,頻率就對不上了。

Photo Modules for PCM Remote Control Systems

TSOP 1738

Raspberry Pis, Remotes & IR Receivers!

Testing the IR Sensor

Connect up the sensor like so:
  • Pin 1 is the output so we wire this to a visible LED and resistor
  • Pin 2 is ground
  • Pin 3 is VCC, connect to 5V
The positive (longer) head of the Red LED connects to the +5V pin and the negative (shorter lead) connects through a 200 to 1000 ohm resistor to the first pin on the IR sensor. Now grab any remote control (TV, BluRay etc…) and point it at the detector. While pressing some buttons, you should see the LED blink a few times! That's it - your receiver is working. . . . !

Testing the IR Sensor on the Rasberry Pi


Keyes IR Obstacle Avoidance Module KY-032


Pins:

GND – should be connected to ground
– should be connected to 5V power supply
out – obstacle signal
EN – no use found


"""
read state from obstacle avoidance sensor using callback functions
"""
import RPi.GPIO as GPIO
import time

outPin = 15 # out connected to D15
enPin = 16 # EN connected to D16

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(outPin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

def eventObstacleSensor(e):
 print("Obstacle found!")
 print(e)

GPIO.add_event_detect(outPin, GPIO.FALLING, bouncetime = 200, callback = eventObstacleSensor)

while(True):
 time.sleep(0.1)

RF Remote Control

Super Simple Raspberry Pi 433MHz Home Automation

This will specifically show you how to turn any electrical device on or off using your Pi by transmitting commands to a set of 433MHz remote-controlled power sockets. The software side of this system consists of two very simple Python scripts : One for receiving and recording signals, and one for transmitting these signals back to the wireless power sockets. The actual reception/transmission of the signal relies only on the easy-to-use RPi.GPIO library which, at least for me, came pre-installed with Raspbian. This library can also be imported directly into Python. For this project you will need:
  • A Raspberry Pi. Any model should work, I used a Pi 2 model B starter kit, but perhaps you need the central unit only.
  • A 433MHz transmitter/receiver pair. The ones most commonly used in this type of project seem to be these. Buying a pack of five like the one linked ensures that you have a few spares.
  • XCSOURCE 5 PCS 433Mhz RF Transmitter Module + Receiver Kit:
    • TX Technical: Working voltage: 3V-12V
    • Working temperature: -10℃ to +70℃
    • Resonance mode: sound wave resonance (SAW)
    • Modulation mode: ASK/ OOK
  • A set of 433MHz remote-controlled power sockets. I bought these which I'd highly recommend, but there are countless models available. Just make sure they operate on this frequency!
  • Brennenstuhl 1507453 Remote Control Set 1 3 RCS 1000 Comfort:
    • Remote Control Set 1 + 3 RCS 1000 Comfort
    • The comfortable way for switching electrical equipment on/off by a remote control button
    • Plug receiver socket into the main socket and the appliance you wish to use into the receiver socket
    • Particularly suitable for operating lamps and other electrical items, which are inaccessible
    • Extremely useful for people with mobility difficulties
    • Switching capacity max
    • 1
    • 000 W
    • Childproof socket
    • Works up to a distance of 25 metres
    • Frequency 433,92 MHz
    • Scope of delivery: 1 x 4 channel sender, 3 x remote receivers sockets, 1 x 12 V battery type A23
  • Some circuit-building accessories. I'd recommend using a breadboard and some jumper cables to make the circuit building process as easy as possible.

Step 1: Setting Up the Receiver Unit

Before you can use your Pi to send commands to the remote-controlled sockets, you need to know what specific signals they respond to. Most remote-controlled sockets ship with a handset that can be used to turn specific units on or off. In the case of the ones I bought, the handset has four rows of paired ON/OFF buttons, each of which sends out an ON or OFF signal to a particular socket unit. This brings up a question - how do we know which buttons correspond to which socket? This actually depends on the model you have. One of the main reasons I chose my particular model of socket (linked in the introduction) is that each unit has a small set of switches on the back which can be manually configured to make a particular socket respond to a particular set of ON/OFF buttons on the handset. This also means that you can unplug and move the sockets around the house knowing that a particular unit will always respond to the same ON/OFF signals. Once you have figured out how your sockets interact with the handset, you will need to use your 433MHz receiver unit (pictured above) to 'sniff' the codes being sent out by the handset. Once you have recorded the waveforms of these codes, you can replicate them using Python and send them out using the transmitter unit. The first thing to do here is wire the pins on your receiver to the correct GPIO pins on the Pi. The receiver unit has four pins, but only three of them are needed. I think both of the central pins give the same output, so you only need to connect to one of them (unless you want to stream the received signals to two separate GPIO pins).
The image above pretty much summarises the wiring. Each pin on the receiver can be wired directly to the corresponding pin on the Pi. I use a breadboard and jumper cables to make the process a bit more elegant. Note that you can choose any GPIO data pin to connect to either of the central receiver pins. I used the pin marked as '23' on my Pi header. IMPORTANT: If you connect the pin marked '3v3' in the above image to a higher voltage pin on the Pi (e.g. 5v), you will probably damage the Pi as the GPIO pins cannot tolerate voltages above 3v3. Alternatively, you can power it with 5v and set up a voltage divider to send a safe voltage to the DATA pin. The range of the receiver will not be very large at this voltage, especially if an antenna is not connected. However, you don't need a long range here - as long as the receiver can pick up the signals from the handset when they are held right next to each other, that is all we need.

Step 2: Sniffing the Handset Codes

Now that your receiver is wired up to the Pi, you can start the first exciting stage of this project - the sniff. This involves using the attached Python script to record the signal transmitted by the handset when each button is pressed. The script is very simple, and I'd highly recommend you have a look at it before you run it - after all, the point of this project is that you won't just blindly run someone else's code!

from datetime import datetime
import matplotlib.pyplot as pyplot
import RPi.GPIO as GPIO

RECEIVED_SIGNAL = [[], []]  #[[time of reading], [signal reading]]
MAX_DURATION = 5
RECEIVE_PIN = 23

if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(RECEIVE_PIN, GPIO.IN)
    cumulative_time = 0
    beginning_time = datetime.now()
    print '**Started recording**'
    while cumulative_time < MAX_DURATION:
        time_delta = datetime.now() - beginning_time
        RECEIVED_SIGNAL[0].append(time_delta)
        RECEIVED_SIGNAL[1].append(GPIO.input(RECEIVE_PIN))
        cumulative_time = time_delta.seconds
    print '**Ended recording**'
    print len(RECEIVED_SIGNAL[0]), 'samples recorded'
    GPIO.cleanup()

    print '**Processing results**'
    for i in range(len(RECEIVED_SIGNAL[0])):
        RECEIVED_SIGNAL[0][i] = RECEIVED_SIGNAL[0][i].seconds + RECEIVED_SIGNAL[0][i].microseconds/1000000.0

    print '**Plotting results**'
    pyplot.plot(RECEIVED_SIGNAL[0], RECEIVED_SIGNAL[1])
    pyplot.axis([0, MAX_DURATION, -1, 2])
    pyplot.show()
Before you start this process, you will need to make sure you have the Python libraries needed to run the sniffer script. They are listed at the top of the script:

from datetime import datetime
import matplotlib.pyplot as pyplot
import RPi.GPIO as GPIO
The RPi.GPIO and datetime libraries were included with my Raspbian distribution, but I had to install the matplotlib library as follows:

sudo apt-get install python-matplotlib
This library is a commonly used graph plotting library that is very useful even outside of this project, so installing it definitely can't hurt! Once your libraries are up to date, you are ready to start recording data. Here's how the script works: When it is run (using the command 'python ReceiveRF.py'), it will configure the defined GPIO pin as a data input (pin 23 by default). It will then continually sample the pin and log whether it is receiving a digital 1 or 0. This continues for a set duration (5 seconds by default). When this time limit is reached, the script will stop recording data and will close off the GPIO input. It then performs a little post-processing and plots the received input value against time. Again, if you have questions about what the script is doing, you can probably answer them yourself after looking at how it works. I have tried to make the code as readable and simple as possible. What you need to do is look out for when the script indicates that it has **Started recording**. Once this message appears, you should press and hold one of the buttons on the handset for about a second. Be sure to hold it close to the receiver. Once the script has finished recording, it will use matplotlib to plot a graphical waveform of the signal it has received during the recording interval. Please note, if you are connected to your Pi using an SSH client such as PuTTY, you will also need to open an X11 application to allow the waveform to display. I use xMing for this (and for other things such as remote-desktopping into my Pi). To allow the plot to be displayed, simply start xMing before you run the script and wait for the results to appear. Once your matplotlib window appears, the area of interest within the plot should be pretty obvious. You can use the controls at the bottom of the window to zoom in until you are able to pick out the highs and lows of the signal transmitted by the handset while the button was being held down. See the above image for an example of a complete code. The signal will probably consist of very short pulses separated by similar periods of time where no signal is received. This block of short pulses will probably be followed by a longer period where nothing is received, after which the pattern will repeat. Once you have identified the pattern belonging to a single instance of the code, take a screenshot like that at the top of this page, and continue to the next step to interpret it.

Step 3: Transcribing the Resulting Signal

Now that you have identified the block of periodic highs and lows corresponding to a particular button's signal, you will need a way of storing and interpreting it. In the above signal example, you will notice that there are only two unique patterns that make up the whole signal block. Sometimes you see a short high followed by a long low, and sometimes it's the opposite - a long high followed by a short low. When I was transcribing my signals, I decided to use the following naming convention:

1 = short_on + long_off
0 = long_on + short_off
Look again at the labelled waveform, and you will see what I mean. Once you have identified the equivalent patterns in your signal, all you have to do is count the 1's and 0's to build up the sequence. When transcribed, the above signal can be written as follows:

1111111111111010101011101
Now you just need to repeat this process to record and transcribe the signals corresponding to the other buttons on your handset, and you have completed the first part of the process! Before you can re-send the signals using the transmitter, there is a little more work to do. The timing between the highs and lows corresponding to a 1 or a 0 is very important, and you need to make sure that you know how long a 'short_on' or a 'long_off' actually lasts. For my codes, there were three pieces of timing information I needed to extract in order to replicate the signals:
  • The duration of a 'short' interval, i.e. the beginning of a 1 or the end of a 0.
  • The duration of a 'long' interval, i.e. the end of a 1 or the beginning of a 0.
  • The duration of an 'extended' interval. I noticed that when I held a button down on the handset, there was an 'extended_off' period between each repeated instance of the signal block. This delay is used for synchronisation and has a fixed duration.
To determine these timing values, you can use the zoom function on the matplotlib window to zoom all the way in and place the cursor over the relevant parts of the signal. The cursor location readout at the bottom of the window should allow you to determine how wide each part of the signal is that corresponds to a long, short or extended interval. Note that the x-axis of the plot represents time, and the x component of the cursor readout is in units of seconds. For me, the widths were as follows (in seconds):
  • short_delay = 0.00045
  • long_delay = 0.00090 (twice as long as a 'short')
  • extended_delay = 0.0096

Step 4: Setting Up the Transmitter Unit

Once you have collected your codes and timing data, you can disconnect your receiver unit as you will no longer need it. You can then wire up the transmitter directly to the relevant Pi GPIO pins as shown in the above image. I've found that the pins on the transmitter units are labelled, which makes the process easier. In this case, it is OK to power the unit using the 5v supply from the Pi as the DATA pin will not be sending signals to the Pi, only receiving them. Also, a 5v power supply will provide more transmission range than using the 3v3 supply. Again, you can connect the DATA pin to any appropriate pin on the Pi. I used pin 23 (the same as for the receiver). Another thing I'd recommend doing is adding an antenna to the small hole on the top right of the transmitter. I used a 17cm long piece of straight wire. Some sources recommend a coiled wire of similar length. I'm not sure which is better, but the straight wire provides enough range for me to turn the sockets on/off from any location in my small flat. It is best to solder the antenna, but I just removed some of the plastic from the wire and wrapped the copper through the hole. Once the transmitter is wired up, that's all the hardware setup done! The only thing left to do now is set your sockets up around the house and have a look at the transmitter program.

Step 5: Transmitting Signals Using the Pi

This is where the second Python script comes in. It is designed to be just as simple as the first, if not more so. Again, please download it and look over the code. You will need to edit the script to transmit the correct signals according to the data you recorded in step 3, so now's a good time to have a quick glance at it.

import time
import sys
import RPi.GPIO as GPIO

a_on = '1111111111111010101011101'
a_off = '1111111111111010101010111'
b_on = '1111111111101110101011101'
b_off = '1111111111101110101010111'
c_on = '1111111111101011101011101'
c_off = '1111111111101011101010111'
d_on = '1111111111101010111011101'
d_off = '1111111111101010111010111'
short_delay = 0.00045
long_delay = 0.00090
extended_delay = 0.0096

NUM_ATTEMPTS = 10
TRANSMIT_PIN = 23

def transmit_code(code):
    '''Transmit a chosen code string using the GPIO transmitter'''
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(TRANSMIT_PIN, GPIO.OUT)
    for t in range(NUM_ATTEMPTS):
        for i in code:
            if i == '1':
                GPIO.output(TRANSMIT_PIN, 1)
                time.sleep(short_delay)
                GPIO.output(TRANSMIT_PIN, 0)
                time.sleep(long_delay)
            elif i == '0':
                GPIO.output(TRANSMIT_PIN, 1)
                time.sleep(long_delay)
                GPIO.output(TRANSMIT_PIN, 0)
                time.sleep(short_delay)
            else:
                continue
        GPIO.output(TRANSMIT_PIN, 0)
        time.sleep(extended_delay)
    GPIO.cleanup()

if __name__ == '__main__':
    for argument in sys.argv[1:]:
        exec('transmit_code(' + str(argument) + ')')
The libraries needed to run this script were all pre-installed on my Pi, so no further installation was needed. They are listed at the top of the script:

import time
import sys
import RPi.GPIO as GPIO
Underneath the library imports is the information you will have to edit. Here is how it looks by default (this is the information corresponding to my sockets as determined using step 3):

a_on = '1111111111111010101011101'
a_off = '1111111111111010101010111'
b_on = '1111111111101110101011101'
b_off = '1111111111101110101010111'
c_on = '1111111111101011101011101'
c_off = '1111111111101011101010111'
d_on = '1111111111101010111011101'
d_off = '1111111111101010111010111'
short_delay = 0.00045
long_delay = 0.00090
extended_delay = 0.0096
Here we have eight code strings (two for each pair of on/off buttons on my handset - you may have more or fewer codes) followed by the three pieces of timing information also determined in step 3. Take the time to make sure you have entered this information correctly. Once you're happy with the codes/delays you've entered into the script (you can rename the code string variables if you like), you are pretty much ready to try out the system! Before you do, take a look at the transmit_code() function in the script. This is where the actual interaction with the transmitter occurs. This function expects one of the code strings to be sent in as an argument. It then opens up the defined pin as a GPIO output and loops through every character in the code string. It then turns the transmitter on or off according to the timing information you entered to build up a waveform matching the code string. It sends each code multiple times (10 by default) to reduce the chance of it being missed, and leaves an extended_delay between each code block, just like the handset. To run the script, you can use the following command syntax:

python TransmitRF.py code_1 code_2 ...
You can transmit multiple code strings with a single run of the script. For example, to turn sockets (a) and (b) on and socket (c) off, run the script with the following command:

python TransmitRF.py a_on b_on c_off

Step 6: A Note on Timing Accuracy

As mentioned, the timing between the transmitted on/off pulses is quite important. The TransmitRF.py script uses python's time.sleep() function to build up the waveforms with the correct pulse intervals, but it should be noted that this function is not entirely accurate. The length for which it causes the script to wait before executing the next operation can depend on the processor load at that given instant. That is another reason why TransmitRF.py sends each code multiple times - just in case the time.sleep() function is not able to properly construct a given instance of the code.

from datetime import datetime
import time

def check_sleep(amount):
    start = datetime.now()
    time.sleep(amount)
    end = datetime.now()
    delta = end - start
    return delta.seconds + delta.microseconds/1000000.0

error = 0
for i in range(100):
    error += abs(check_sleep(0.050) - 0.050)
error /= 100
print error

I have personally never had issues with time.sleep() when it comes to sending the codes. I do however know that my time.sleep() tends to have an error of about 0.1ms. I determined this using the attached SleepTest.py script which can be used to give an estimate of how accurate your Pi's time.sleep() function is. For my particular remote-controlled sockets, the shortest delay I needed to implement was 0.45ms. As I said, I haven't had issues with non-responsive sockets, so it seems like 0.45 ± 0.1ms is good enough. There are other methods for ensuring that the delay is more accurate; for example, you could use a dedicated PIC chip to generate the codes, but stuff like that is beyond the scope of this tutorial.

Step 7: Conclusion

This project has presented a method for controlling any electrical appliance using a Raspberry Pi and a set of 433MHz remote-controlled sockets, with a focus on simplicity and transparency. This is the most exciting and flexible project that I have used my Pi for, and there are limitless applications for it. Here are some things I can now do thanks to my Pi:
  • Turn on an electric heater next to my bed half an hour before my alarm goes off.
  • Turn the heater off an hour after I've gone to sleep.
  • Turn my bedside light on when my alarm goes off so that I don't fall back to sleep.
  • and many more...
For most of these tasks, I use the crontab function within Linux. This allows you to set up automatic scheduled tasks to run the TransmitRF.py script at specific times. You can also use the Linux at command to run one-off tasks (which, for me, needed to be installed separately using 'sudo apt-get install at'). For example, to turn my heater on half an hour before my alarm goes off the next morning, all I need to do is type:

at 05:30
python TransmitRF.py c_on
You could also use this project in conjunction with my Dropbox home monitoring system to control appliances over the internet! Thanks for reading, and if you would like to clarify something or share your opinion, please post a comment!

Post a comment





留言

熱門文章