html控制树莓派小车,用家里旧的红外遥控器控制树莓派小车
0x01 前言
紅外遙控是一種無線、非接觸控制技術,在工業控制、航空航天、家電等領域都得到了廣泛應用。大部分的電視的遙控器,空調遙控器就都是紅外遙控。在baidu上檢索了樹莓派小車的各種控制方案,沒有找到紅外遙控的控制方案。所以本文嘗試使用紅外控制方案對樹莓派小車進行控制。
0x02 所需材料
1. 樹莓派小車。(樹莓派小車的安裝不是本文重點,如果讀者不熟悉小車的安裝,請自行搜索。)
2. 紅外接收頭
型號:VS1838B(價格很便宜,幾毛錢一個。)
3. 家里廢舊的紅外遙控器
0x03 紅外通訊協議學習
遙控器的基帶通信協議很多,大概有幾十種,用的最多的就是 NEC 協議了,下面開始了解下NEC協議。
圖 1 NEC 協議數據格式
NEC 協議的數據格式包括了引導碼、用戶碼、用戶碼(或者用戶碼反碼)、按鍵鍵碼和鍵碼反碼,最后一個停止位。停止位主要起隔離作用,一般不進行判斷,編程時我們也不予理會。其中數據編碼總共是 4 個字節 32 位。第一個字節是用戶碼,第二個字節可能也是用戶碼,或者是用戶碼的反碼,具體由生產商決定,第三個字節就是當前按鍵的鍵數據碼,而第四個字節是鍵數據碼的反碼,可用于對數據的糾錯。
注意: NEC 協議中的每一位數據本身也需要進行編碼,編碼后再進行載波調制。引導碼:9ms 的載波+4.5ms 的空閑。
比特值“0”:560us 的載波+560us 的空閑。
比特值“1”:560us 的載波+1.68ms 的空閑。
紅外接收頭,當收到有載波的信號的時候,會輸出一個低電平,空閑的時候會輸出高電平,我們用邏輯分析儀抓出來一個紅外按鍵通過解碼后的圖形來了解一下,如圖 2 所示。
圖 2? 紅外遙控器按鍵編碼
從圖上可以看出,先是 9ms 載波加 4.5ms 空閑的起始碼,數據碼是低位在前,高位在后,數據碼第一個字節是 8 組 560us 的載波加 560us 的空閑,也就是 0x00,第二個字節是 8 組 560us的載波加 1.68ms 的空閑,可以看出來是 0xFF,這兩個字節就是用戶碼和用戶碼的反碼。按鍵的鍵碼二進制是 0x0C,反碼就是 0xF3,最后跟了一個 560us 載波停止位。對于我們的遙控器來說,不同的按鍵,就是鍵碼和鍵碼反碼的區分,用戶碼是一樣的。這樣我們就可以通過單片機的程序,把當前的按鍵的鍵碼給解析出來。
額外提一句:空調的遙控器比較特殊,用戶碼和鍵盤均存儲數據。
0x04 監聽紅外遙控
VS1838B的3個管腳分別為OUT、GND、VCC。當收到有載波的信號的時候,OUT管腳會輸出一個低電平,空閑的時候會輸出高電平。
本次將樹莓派的18引腳作為紅外接收引腳。
監聽紅外的機能代碼如下:#define IR_INPUT_PIN (18)
#define ERROR (0xFE)
// 請事先破獲遙控器的各個鍵的按鍵碼,然后與小車的前、后、左、右、停的行為相對應。
// 以下5個值需要與遙控器的按鍵碼匹配。
#define IR_CONTROL_KEY_UP (0x01)
#define IR_CONTROL_KEY_DOWN (0x02)
#define IR_CONTROL_KEY_LEFT (0x03)
#define IR_CONTROL_KEY_RIGHT (0x04)
#define IR_CONTROL_KEY_STOP (0x05)
// 信號量
sem_t g_edge_falling, g_edge_rising;
void setup();
void edgeFalling();
void edgeRising();
void GPIO_wait_for_edge(int event);
bool IRStart();
unsigned char getByte();
unsigned char getKey();
void setup()
{
if(-1==wiringPiSetup())
{
std::cerr<
exit(-1);
}
pinMode(IR_INPUT_PIN,INPUT); //配置引腳為輸入
pullUpDnControl(IR_INPUT_PIN,PUD_UP); //引腳上拉到3.3v
//注冊中斷處理函數
if(0>wiringPiISR(IR_INPUT_PIN,INT_EDGE_FALLING,edgeFalling))
{
std::cerr<
exit(-1);
}
if(0>wiringPiISR(IR_INPUT_PIN,INT_EDGE_RISING,edgeRising))
{
std::cerr<
exit(-1);
}
}
void edgeFalling() {
sem_post(&g_edge_falling);
}
void edgeRising() {
sem_post(&g_edge_rising);
}
void GPIO_wait_for_edge(int event){
if(INT_EDGE_FALLING==event){
sem_init(&g_edge_falling, 0, 0);
sem_wait(&g_edge_falling);
}
else if(INT_EDGE_RISING==event) {
sem_init(&g_edge_rising, 0, 0);
sem_wait(&g_edge_rising);
}
}
bool IRStart() {
time_t timeFallingEdge[] = {0,0};
time_t timeRisingEdge = 0;
time_t timeSpan[] = {0, 0};
//GPIO.wait_for_edge(PIN, GPIO.FALLING);
GPIO_wait_for_edge(INT_EDGE_FALLING);
timeFallingEdge[0] = time(NULL);
//GPIO.wait_for_edge(PIN, GPIO.RISING);
GPIO_wait_for_edge(INT_EDGE_RISING);
timeRisingEdge = time(NULL);
//GPIO.wait_for_edge(PIN, GPIO.FALLING);
GPIO_wait_for_edge(INT_EDGE_FALLING);
timeFallingEdge[1] = time(NULL);
timeSpan[0] = timeRisingEdge - timeFallingEdge[0];
timeSpan[1] = timeFallingEdge[1] - timeRisingEdge;
if (timeSpan[0] > 0.0085 && \
timeSpan[0] < 0.0095 && \
timeSpan[1] > 0.004 && \
timeSpan[1] < 0.005){
return true;
}
else {
return false;
}
}
unsigned char getByte() {
unsigned char byte = 0;
time_t timeRisingEdge = 0;
time_t timeFallingEdge = 0;
time_t timeSpan = 0;
for(int i =0; i<8; ++i) {
//GPIO.wait_for_edge(PIN, GPIO.RISING);
GPIO_wait_for_edge(INT_EDGE_RISING);
timeRisingEdge = time(NULL);
//GPIO.wait_for_edge(PIN, GPIO.FALLING);
GPIO_wait_for_edge(INT_EDGE_FALLING);
timeFallingEdge = time(NULL);
timeSpan = timeFallingEdge - timeRisingEdge;
if(timeSpan > 0.0016 && timeSpan < 0.0018) {
byte |= 1 << i;
}
}
return byte;
}
unsigned char getKey() {
unsigned char byte[] = {0x00,0x00,0x00,0x00};
if(false==IRStart()) {
sleep(0.11); // One message frame lasts 108 ms.
return ERROR;
}
else {
for( int i=0;i<4;++i) {
byte[i] = getByte();
}
if((byte[0] + byte[1] == 0xff)&&(byte[2] + byte[3] == 0xff)) {
return byte[2];
}
else {
return ERROR;
}
}
}
void *listenIRThread(void *arg) {
printf("IRM Test Start ...\n");
setup();
while(true) {
unsigned char key = getKey();
if(key != ERROR) {
printf("Get the key: 0x%02x\n",key);
switch(key)
{
case IR_CONTROL_KEY_UP:
{
// 前進
std::cout << "command: CARRUN FORWARD"<< std::endl;
DirectionReq *req = new DirectionReq();
req->setValue(DIRECTION_FORWARD);
ControlManager::instance()->postActionReq(req);
break;
}
case IR_CONTROL_KEY_DOWN:
{
// 后退
std::cout << "command: CARRUN BACK"<< std::endl;
DirectionReq *req = new DirectionReq();
req->setValue(DIRECTION_BACK);
ControlManager::instance()->postActionReq(req);\
break;
}
case IR_CONTROL_KEY_LEFT:
{
// 左轉
std::cout << "command: CARRUN LEFT"<< std::endl;
DirectionReq *req = new DirectionReq();
req->setValue(DIRECTION_LEFT);
ControlManager::instance()->postActionReq(req);
break;
}
case IR_CONTROL_KEY_RIGHT:
{
// 右轉
std::cout << "command: CARRUN RIGHT"<< std::endl;
DirectionReq *req = new DirectionReq();
req->setValue(DIRECTION_RIGHT);
ControlManager::instance()->postActionReq(req);
break;
}
case IR_CONTROL_KEY_STOP:
{
// 停車
std::cout << "command: CARRUN STOP"<< std::endl;
StatusReq *req = new StatusReq();
ControlManager::instance()->postStatusReq(req);
break;
}
default:
break;
}
}
}
}
0x05 控制小車動作
本次使用python語言提供的接口來控制小車的動作:前進、后退、左轉、右轉、停止。
用python控制小車動作的代碼如下:#!/usr/bin/Python
# -*- coding: UTF-8 -*-
#引入gpio的模塊
import RPi.GPIO as GPIO
import time
#設置in1到in4接口
IN1 = 12
IN2 = 16
IN3 = 18
IN4 = 22
#初始化接口
defcar_init():
#設置GPIO模式
GPIO.setmode(GPIO.BOARD)
GPIO.setup(IN1,GPIO.OUT)
GPIO.setup(IN2,GPIO.OUT)
GPIO.setup(IN3,GPIO.OUT)
GPIO.setup(IN4,GPIO.OUT)
#前進的代碼
defcar_forward():
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(0.15)
GPIO.cleanup()
#后退
defcar_back():
GPIO.output(IN1,GPIO.LOW)
GPIO.output(IN2,GPIO.HIGH)
GPIO.output(IN3,GPIO.LOW)
GPIO.output(IN4,GPIO.HIGH)
time.sleep(0.15)
GPIO.cleanup()
#左轉
defcar_left():
GPIO.output(IN1,False)
GPIO.output(IN2,False)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(0.15)
GPIO.cleanup()
#右轉
defcar_right():
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)
GPIO.output(IN3,False)
GPIO.output(IN4,False)
time.sleep(0.15)
GPIO.cleanup()
#停止
defcar_stop():
GPIO.output(IN1,GPIO.LOW)
GPIO.output(IN2,GPIO.LOW)
GPIO.output(IN3,GPIO.LOW)
GPIO.output(IN4,GPIO.LOW)
GPIO.cleanup()
0x06 結束
到此紅外遙控器控制樹莓派小車小車的控制系統就介紹完了。
本文重點講解的是紅外協議的解析部分,而小車的控制策略部分沒有詳細講解,讀者可以參考我之前寫的幾篇小車相關的文章,進行了解。
最后,整套代碼已經發到了百度網盤上。
*本文作者:xutiejun,轉載請注明來自FreeBuf.COM
總結
以上是生活随笔為你收集整理的html控制树莓派小车,用家里旧的红外遥控器控制树莓派小车的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python异步编程——aiohttp
- 下一篇: httpd源码安装-及实现虚拟主机