利用开发板GPIO模拟出方波,转换成遥控器信号。
一、硬件(R4电阻改成300多欧,太大了遥控头功率低)
二、控制代码testIR.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <asm-generic/fcntl.h>
#include <asm-generic/ioctl.h>
#define PANEL_NAME "/dev/gxpanel0"
#define _PANEL 'P'
#define PANEL_GPIO_HIGH _IOW(_PANEL, 0x0A, uint32_t)
#define PANEL_GPIO_LOW _IOW(_PANEL, 0x0B, uint32_t)
uint32_t pin = 68;
int fd;
//k=100000 370HZ, k=1000 34KHZ, k=895 38KHZ
#define K38(V) do{volatile unsigned int k;for(k=0;k<V;k++)__asm__ volatile ("nop");}while(0)
#define HH(t) \
do { \
int i = t; \
do { \
ioctl(fd, PANEL_GPIO_HIGH, &pin);K38(894); \
ioctl(fd, PANEL_GPIO_LOW, &pin); K38(894); \
} while(--i > 0); \
} while(0)
#define LL(t) \
do { \
int i = t; \
ioctl(fd, PANEL_GPIO_LOW, &pin); \
do { K38(1923); } while(--i > 0); \
} while(0)
#define HH9000 HH(342) //38000*0.009
#define LL4500 LL(171) //38000*0.0045
#define HH560 HH(21) //38000*0.000560
#define LL560 LL(21) //38000*0.000560
#define LL1680 LL(64) //38000*0.001680
void Irda_SendBytes(uint32_t user, uint32_t key)
{
uint8_t v;
uint32_t data;
if (key == -1)
data = user;
else
data = ((~key & 0xFF) << 24) | ((key & 0xFF) << 16) |
((~user & 0xFF) << 8) | (user & 0xFF);
printf("==>Irda_SendBytes:%X\n", data);
HH9000;
LL4500;
for(v = 0; v <= 32; v++) {
HH560;
if(data & 0x80000000) {
LL560;
} else {
LL1680;
}
data <<= 1;
}
LL560;
}
long long getMilliseconds(struct timespec start, struct timespec end) {
long long startMs = (long long)(start.tv_sec * 1000) + (long long)(start.tv_nsec / 1000000);
long long endMs = (long long)(end.tv_sec * 1000) + (long long)(end.tv_nsec / 1000000);
return endMs - startMs;
}
void Adj38K()
{
struct timespec startTime, endTime;
clock_gettime(CLOCK_MONOTONIC, &startTime);
//HH(2280000); // need 60s, 38000*60=2280000
LL(2280000); //need 60s, 38000*60=2280000
clock_gettime(CLOCK_MONOTONIC, &endTime);
long long elapsedTime = getMilliseconds(startTime, endTime);
printf("timespec(ms):%lld\n", elapsedTime);
exit(0);
}
int main(int argc, char **argv)
{
uint32_t arg1, arg2;
if (argc > 1) {
char *p = argv[1];
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
arg1 = strtoul(p, NULL, 16);
else
arg1 = strtoul(p, NULL, 10);
}
if (argc > 2) {
char *p = argv[2];
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
arg2 = strtoul(p, NULL, 16);
else
arg1 = strtoul(p, NULL, 10);
}
fd = open(PANEL_NAME, O_RDWR);
if( fd < 0 ) {
printf("Error: panel fd:%d\n", fd);
return;
}
//Adj38K(); //校准38K方波
if (argc == 2) {
if (arg1 == 0)
ioctl(fd, PANEL_GPIO_LOW, &pin);
else if (arg1 == 1)
ioctl(fd, PANEL_GPIO_HIGH, &pin);
else
Irda_SendBytes(arg1, -1);
} else if (argc == 3) {
Irda_SendBytes(arg1, arg2);
} else {
/*do {
Irda_SendBytes(0x11, 0x22);
usleep(1000000);
} while(1);*/
}
close(fd);
}
生成可执行代码,其中/dev/panel0设备自己开发,就是简单的控制GPIO高低点位
arm-linux-gnueabihf-gcc -D LINUX_OS -g -O0 testIR.c -o testIR
修改连接板子的ttyUSB0权限
sudo chmod 666 /dev/ttyUSB0
测试,在串口终端里运行
echo -e "/testIR 0xBB444FB0" > /dev/ttyUSB0
三、网页发送遥控器信号
在开发板的开发电脑上安装lighttpd
sudo apt install lighttpd
配置好web目录,启动 lighttpd
安装CGI模块
sudo apt-get install lighttpd-mod-cgi
sudo lighttpd-enable-mod cgi
sudo service lighttpd restart
修改CGI配置/etc/lighttpd/conf-enabled/10-cgi.conf
# /usr/share/doc/lighttpd/cgi.txt
server.modules += ( "mod_cgi" )
$HTTP["url"] =~ "^/execute-script/" {
cgi.assign = ( ".py" => "/usr/bin/python" )
}
重启lighttpd
sudo service lighttpd restart
添加CGI脚本execute-script/script.py
这是测试脚本,把测试脚本改成执行命令echo -e “/testIR 0xBB444FB0” > /dev/ttyUSB0,就可以达到网页控制遥控器信号
#!/usr/bin/python
import cgi
import os
form = cgi.FieldStorage()
print("Content-type: text/html\n\n")
print("<html><body>")
print("<h1>POST Data:</h1>")
print("<ul>")
for key in form.keys():
print("<li>{0}:{1}</li>".format(key, form[key].value))
os.system("echo -e '' > /dev/ttyUSB0")
os.system("echo -e '/testIR "+form[key].value+"' > /dev/ttyUSB0")
print("</ul>")
print("</body></html>")
网页使用CGI
测试代码,使用POST发送到CGI
<!DOCTYPE html>
<html>
<head>
<title>遥控器</title>
<meta charset="utf-8">
<style>
.button {
position: absolute;
width: 90px;
height: 30px;
background-color: #ccc;
border-radius: 12px;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.button.active {
background-color: #ff0000;
}
</style>
</head>
<body>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40c43b')" style="left: 10px; top: 10px;">Power</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f402ed1')" style="left: 310px; top: 10px;">Mute</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f406c93')" style="left: 10px; top: 50px;">►||</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40ec13')" style="left: 110px; top: 50px;">USB/■</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f406699')" style="left: 210px; top: 50px;">Rec</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f404cb3')" style="left: 310px; top: 50px;">Info</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f401ce3')" style="left: 10px; top: 90px;">|◄◄</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f409c63')" style="left: 110px; top: 90px;">►►|</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f405ca3')" style="left: 210px; top: 90px;">◄◄</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40dc23')" style="left: 310px; top: 90px;">►►</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4006f9')" style="left: 30px; top: 130px;">Fav</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4026d9')" style="left: 290px; top: 130px;">Sat</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40847b')" style="left: 160px; top: 150px;">▲</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4024db')" style="left: 60px; top: 190px;">◄</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40a45b')" style="left: 160px; top: 190px;">OK</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40e41b')" style="left: 260px; top: 190px;">►</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40946b')" style="left: 160px; top: 230px;">▼</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40d42b')" style="left: 30px; top: 250px;">Menu</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4014eb')" style="left: 290px; top: 250px;">Exit</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4046b9')" style="left: 30px; top: 290px;">SUBTITLE</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f403ec1')" style="left: 160px; top: 290px;">RECALL</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40f40b')" style="left: 290px; top: 290px;">PAGE+</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4044bb')" style="left: 30px; top: 330px;">TXT</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f408c73')" style="left: 160px; top: 330px;">AUDIO</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40cc33')" style="left: 290px; top: 330px;">PAGE-</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f408e71')" style="left: 30px; top: 370px;">1</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f409e61')" style="left: 160px; top: 370px;">2</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f400ef1')" style="left: 290px; top: 370px;">3</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40b649')" style="left: 30px; top: 410px;">4</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f401ee1')" style="left: 160px; top: 410px;">5</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4036c9')" style="left: 290px; top: 410px;">6</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f409669')" style="left: 30px; top: 450px;">7</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40be41')" style="left: 160px; top: 450px;">8</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4016e9')" style="left: 290px; top: 450px;">9</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f4004fb')" style="left: 30px; top: 500px;">EPG</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f40a659')" style="left: 160px; top: 500px;">0</div>
<div class="button" onmousedown="toggleButton(this)" onmouseup="sendButton(event, '0x7f406e91')" style="left: 290px; top: 500px;">TV/RADIO</div>
<script>
function toggleButton(button) {
button.classList.toggle('active');
}
function sendButton(event, keyValue) {
const button = event.target;
const key = keyValue
fetch('execute-script/script.py', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: "key="+key
})
.then(response => {
button.classList.remove('active'); //恢复按键的原始颜色
})
.catch(error => {
button.classList.remove('active'); //恢复按键的原始颜色
});
}
</script>
</body>
</html>
效果图
附:美的遥控器资料
#define MIDEA_HDR_MARK 4500
#define MIDEA_HDR_SPACE 4500
#define MIDEA_BIT_MARK 560
#define MIDEA_ONE_SPACE 1680
#define MIDEA_ZERO_SPACE 560
#define MIDEA_STOP_SPACE 4500
void IRsend::sendMIDEA(unsigned long data, int nbits) {
unsigned long address = 0xB24D;
enableIROut(38);
//leader code
mark(MIDEA_HDR_MARK);
space(MIDEA_HDR_SPACE);
//address code
for (int i = 0; i < 16; i++) {
if (address & 0x8000) {
mark(MIDEA_BIT_MARK);
space(MIDEA_ONE_SPACE);
} else {
mark(MIDEA_BIT_MARK);
space(MIDEA_ZERO_SPACE);
}
address <<= 1;
}
//data code
for (int i = 0; i < nbits; i++) {
if (data & 0x80000000) {
mark(MIDEA_BIT_MARK);
space(MIDEA_ONE_SPACE);
} else {
mark(MIDEA_BIT_MARK);
space(MIDEA_ZERO_SPACE);
}
data <<= 1;
}
//stop code
mark(MIDEA_BIT_MARK);
space(MIDEA_STOP_SPACE);
}
修改完后,保存即可。
可以通过如下例子调用:
#include <IRremote.h>
IRsend irsend;
void setup()
{
Serial.begin(9600);
}
void loop() {
irsend.sendMIDEA(0x7B84E01F, 32);
irsend.sendMIDEA(0x7B84E01F, 32);
delay(5000);
}
注: 上面的例子有两行相同的 irsend.sendMIDEA(0x7B84E01F, 32); 这个是因为美的空调(R51D)协议 一次按键重复发两次,所以要执行两次。
关闭 0x7B84E01F
摆风 0x6B94E01F
强劲 0xF50AA25D
清新 0xF50AA35C
数显 0xF50AA55A
风向 0x1FE044BB
开:根据温度不同数据码不同,下面一一列表:
17度 0x1FE008F7
18度 0x1FE018E7
19度 0x1FE038C7
20度 0x1FE028D7
21度 0x1FE06897
22度 0x1FE07887
23度 0x1FE058A7
24度 0x1FE048B7
25度 0x1FE0C837
26度 0x1FE0D827
27度 0x1FE09867
28度 0x1FE08877
29度 0x1FE0A857
30度 0x1FE0B847