Scanner et envoyer des signaux de télécommande infrarouge à l'aide de GPIO de Raspberry Pi J'ai écrit cet article la dernière fois. La télécommande pour les téléviseurs et les plafonniers est simple, et c'est facile car le même signal est toujours émis lorsque vous appuyez sur le même bouton, mais la télécommande pour les climatiseurs est un peu spéciale et non. Dans le cas d'un climatiseur, la télécommande connaît l'état actuel du climatiseur et le fonctionnement lorsque le bouton est enfoncé change en fonction de l'état. Cependant, si vous numérisez sous la forme d'un signal lors de la désactivation du climatiseur, d'un signal lors de l'activation du refroidissement à 28 degrés et d'un signal lors de l'activation du chauffage à 20 degrés, vous pouvez activer / désactiver le refroidissement et le chauffage. Sera. Cependant, comme c'est un gros problème, je voudrais analyser la télécommande afin que la température et la direction du vent puissent être ajustées librement.
Nous utiliserons l'analyse et enverrons les programmes de l'article précédent, donc si vous ne l'avez pas lu, veuillez d'abord le lire.
Il s'agit de la télécommande utilisée pour l'analyse. Le nom du modèle est NH122 205AL Le type qui a un climatiseur à Kirigamine.
Tout d'abord, je vais scanner la télécommande du climatiseur.
pi@raspberrypi:~/IR $ ./scan 20 > heat20
GPIO Pin Number : 20
Scanning begin
Scanning end
Output begin
Output end
pi@raspberrypi:~/IR $ cat heat20
3499 1681
460 1257
462 1256
460 396
463 396
462 400
461 1255
461 399
459 400
461 1256
461 1256
460 400
(Omis)
459 1260
459 400
458 400
458 13283
3470 1680
461 1256
461 1258
460 399
460 399
461 399
459 1256
461 399
460 399
459 1259
460 1255
461 400
(Omis)
458 1264
457 399
459 399
458
Regardez ceci pour déterminer quel format est utilisé. Tout d'abord, comme il n'est pas fabriqué par SONY, il s'agit soit de NEC, soit de Ie Seikyo. Format de communication de la télécommande infrarouge Si vous le comparez avec le site ici, Le temps d'éclairage 3499 de la première ligne, c'est-à-dire que le leader est environ 8 fois le temps d'éclairage 460 lors de l'envoi des données de la deuxième ligne et des lignes suivantes, de sorte que vous pouvez voir qu'il s'agit d'un format coopératif fait maison. De plus, il y a un endroit où le temps d'éclairage est aussi long que 3470 sur le chemin, donc cette télécommande peut envoyer un signal à 2 images. Mais cela ne semble pas se répéter. Cependant, je ne peux pas du tout comprendre même si je regarde beaucoup, alors écrivons un programme qui convertit cela en hexadécimal.
Commencez par convertir le fichier numérisé en hexadécimal. Dans le format coopératif fait maison, après l'envoi du leader, il est envoyé dans l'ordre de 8 bits du LSB du premier octet au MSB. Après cela, les 2ème et 3ème octets viennent dans l'ordre et la trame se termine lorsque la lumière est éteinte pendant les 8 dernières ms ou plus.
Pour le moment, ignorez Trailer et Repeat et concentrez-vous uniquement sur Leader et Data. Le format de la télécommande comporte une unité de modulation T, et les informations envoyées sont déterminées par le temps d'éclairage et le temps d'extinction étant plusieurs fois T. Leader a un temps d'éclairage de 8T et un temps d'éclairage de 4T. Lorsque 0 est transmis, le temps d'éclairage est T et le temps d'éclairage est T. Lorsque 1 est transmis, le temps d'éclairage est T et le temps d'éclairage est 3T. Sera. Donc, sur la base du fait que T est de 350 à 500, si le temps d'éclairage est long, il est déterminé par Leader, si le temps d'éclairage et le temps d'extinction sont à peu près identiques, 0, et si le temps d'extinction est plus long que le temps d'éclairage, il est déterminé par la condition approximative. Faire.
aeha.c
//
// aeha.c
// Copyright © 2018 Hiroki Kawakami. All rights reserved.
//
#include<stdio.h>
#include<stdlib.h>
int main() {
int count, offset = -1;
unsigned int on, off;
unsigned char byte = 0;
while(1) {
count = scanf("%u%u", &on, &off);
if (count < 2 || off == 0) {
break;
}
if (on > 1500) {
if (offset >= 0) printf("\n");
offset = 0;
byte = 0;
} else {
byte |= (off > on * 2) << offset++;
if ((offset & 7) == 0) {
printf("%02X ", byte);
offset = 0;
byte = 0;
}
}
}
printf("\n");
return 0;
}
$ gcc -o aeha aeha.c
Convertir le fichier heat20 créé lors de la numérisation
pi@raspberrypi:~/IR $ cat heat20 | ./aeha
23 CB 26 01 00 20 48 04 30 6A 00 00 00 00 10 00 00 2B
23 CB 26 01 00 20 48 04 30 6A 00 00 00 00 10 00 00 2B
Vous pouvez maintenant voir le signal envoyé par la télécommande en hexadécimal. Vous pouvez voir que cette télécommande envoie deux fois la même trame.
Comme j'ai pu voir le signal en hexadécimal, je scanne le signal de la télécommande dans plusieurs modes et le compare pour l'analyser, mais je le scanne un par un et le convertis en hexadécimal. C'est un problème de comparer. Mais c'est d'accord. Ce n'est pas seulement pour le plaisir que j'ai utilisé les E / S standard pour les E / S de fichiers. Cela vous permet de connecter le programme de numérisation et le programme de conversion hexadécimale directement avec un tube.
$ while true; do ./scan 20 2>/dev/null | ./aeha; done
./scan 20 | ./Je peux connecter le programme de scan et le programme de conversion hexadécimal avec aeha avec un tuyau, mais la sortie stderr du programme de scan est un obstacle, donc 2>/dev/Je le jette comme nul. Et, avec cela seul, le programme se terminera après avoir analysé une fois, donc tant que c'est vrai, le programme est redémarré dans une boucle infinie.
En faisant cela, vous pouvez analyser en continu le signal de la télécommande et le voir en valeur hexadécimale.
Lorsque vous avez terminé, appuyez sur Ctrl + C pour quitter.
### Courir
Tout ce que vous avez à faire est d'exécuter la commande écrite ci-dessus, d'envoyer les signaux de télécommande au capteur les uns après les autres et de comparer les valeurs affichées.
pi@raspberrypi:~/IR $ while true; do ./scan 20 2>/dev/null | ./aeha; done 23 CB 26 01 00 20 48 04 30 40 00 00 00 00 10 00 00 01 23 CB 26 01 00 20 48 04 30 40 00 00 00 00 10 00 00 01 23 CB 26 01 00 20 48 03 30 40 00 00 00 00 10 00 00 00 23 CB 26 01 00 20 48 03 30 40 00 00 00 00 10 00 00 00 23 CB 26 01 00 20 48 02 30 40 00 00 00 00 10 00 00 23 CB 26 01 00 20 48 02 30 40 00 00 00 00 10 00 00 FF 23 CB 26 01 00 20 48 01 30 40 00 00 00 00 10 00 00 FE 23 CB 26 01 00 20 48 01 30 40 00 00 00 00 10 00 00 FE 23 CB 26 01 00 20 48 00 30 80 00 00 00 00 10 00 00 3D 23 CB 26 01 00 20 48 00 30 80 00 00 00 00 10 00 00 3D
Dans de rares cas, il peut ne pas être converti normalement, mais comme cette télécommande envoie deux fois le même signal, il n'y a pas de problème d'analyse.
Ceci est le résultat d'une pression sur le bouton pour abaisser la température de consigne vers le capteur et de l'entrée de cinq signaux allant du chauffage à 20 degrés au chauffage à 16 degrés dans l'ordre.
Je pense que le dernier octet est utilisé pour la détection d'erreur, alors ignorez-le, et vous pouvez voir que la valeur du 7e octet (le 0e octet à l'extrême gauche) a changé.
pi@raspberrypi:~/IR $ while true; do ./scan 20 2>/dev/null | ./aeha; done 23 CB 26 01 00 20 58 0C 36 40 00 00 00 00 10 00 00 1F 23 CB 26 01 00 20 58 0C 36 40 00 00 00 00 10 00 00 1F 23 CB 26 01 00 20 58 0D 36 40 00 00 00 00 10 00 00 20 23 CB 26 01 00 20 58 0D 36 40 00 00 00 00 10 00 00 20 23 CB 26 01 00 20 58 0E 36 40 00 00 00 00 10 00 00 21 23 CB 26 01 00 20 58 0E 36 40 00 00 00 00 10 00 00 21 23 CB 26 01 00 20 58 0F 36 80 00 00 00 00 10 00 00 62 23 CB 26 01 00 20 58 0F 36 80 00 00 00 00 10 00 00 62
De plus, cela est le résultat de la pression sur le bouton pour augmenter la température de consigne du refroidissement de 28 degrés au refroidissement de 31 degrés et de l'entrée de quatre signaux dans l'ordre, et on peut voir que la valeur du 7ème octet change également.
De plus, si vous calculez la valeur du 7e octet, vous pouvez voir qu'elle est de -16 à partir de la température réglée.
Par conséquent, il a été constaté que la température réglée est stockée dans le 7e octet.
Ensuite, de la même manière pour le mode de fonctionnement, la vitesse du vent, la direction du vent, etc., il suffit d'entrer des signaux qui ne changent qu'une seule condition dans l'ordre et de voir le changement de valeur.
### 1 dernier octet
Il y a eu une analyse de la télécommande du climatiseur de National sur le site de référence. La télécommande était au format coopératif fait maison, et le dernier octet était les 8 bits inférieurs de la somme de tous les octets. Quand je l'ai vérifié avec la télécommande que j'analyse maintenant, ce sont les 8 bits inférieurs qui ont été ajoutés comme dans le site de référence.
## Résultats d'analyse
![解析.png](https://qiita-image-store.s3.amazonaws.com/0/242402/8227486d-9575-6147-3c5d-33c8e94b4954.png)
Le signal lorsque la minuterie est réglée n'est pas analysé. Il est plus facile et plus flexible de le contrôler par programme.
Lorsque la zone de vent est de 0, la valeur spécifiée pour les vents gauche et droit est utilisée. Si spécifié ici, l'orientation sera celle spécifiée dans la zone de vent sans utiliser les valeurs de vent gauche et droite.
# Envoyer
Maintenant que l'analyse est terminée, il ne vous reste plus qu'à écrire un programme qui génère des signaux en fonction des résultats de l'analyse. Le programme qui génère le signal a été créé avec Node.js afin qu'il puisse être facilement exploité via le Web plus tard.
## programme
#### **`mbac.js`**
```js
//
// mbac.js
// Copyright © 2018 Hiroki Kawakami. All rights reserved.
//
var bytes = [0x23, 0xcb, 0x26, 0x01, 0x00, 0x00, 0x58, 12, 0x32, 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0];
var aeha = function(T, bytes, repeats, interval) {
var result = "";
var i = 0;
var length = bytes.length;
while (true) {
result += T * 8 + " " + T * 4 + "\n"; // Leader
for (var j = 0; j < length; j++) {
for (var k = 0; k < 8; k++) {
if ((bytes[j] & (1 << k)) != 0) { // 1
result += T + " " + T * 3 + "\n";
} else { // 0
result += T + " " + T + "\n";
}
}
}
if (++i >= repeats) {
result += T;
break;
} else {
result += T + " " + interval + "\n"; // Trailer
}
}
return result;
}
var UpdateCheckByte = function() {
var sum = 0;
for (var i = 0; i < bytes.length - 1; i++) {
sum += bytes[i];
}
bytes[bytes.length - 1] = sum & 0xff;
}
var SetPower = function(power) {
if (power.toLowerCase !== undefined) {
power = power.toLowerCase()
}
if (!power || power == "off" || power == "false") {
bytes[5] = 0x00;
savedState.power = false;
} else {
bytes[5] = 0x20;
savedState.power = true;
}
}
var SetMode = function(mode) { //Régler le mode (cool:Climatisation, heat:chauffage, dry:Déshumidification, wind:Ventilateur)
mode = mode.toLowerCase();
var byte = {"cool": 0x58, "heat": 0x48, "dry": 0x50, "wind": 0x38}[mode];
if (byte === undefined) {
console.log("Mode %s is not defined!", mode);
return false;
}
SetPower(true);
bytes[6] = byte;
savedState.mode = mode;
if (mode == "cool") {
SetTemperature(savedState.coolTemperature);
bytes[8] = (bytes[8] & 0xf0) | 0x6;
} else if (mode == "heat") {
SetTemperature(savedState.heatTemperature);
} else if (mode == "dry") {
SetDryIntensity(savedState.dryIntensity);
}
return true;
}
var SetTemperature = function(temperature) { //Réglez la température de consigne (16~31)
if (temperature < 16 || temperature > 31) {
console.log("Temperature %d is out of range (16 ~ 31).", temperature);
return false;
}
bytes[7] = temperature - 16;
if (savedState.mode == "cool") {
savedState.coolTemperature = temperature;
} else if (savedState.mode == "heat") {
savedState.heatTemperature = temperature;
}
return true;
}
var SetDryIntensity = function(intensity) { //Force de déshumidification (élevée:force, normal:la norme, low:faible)
intensity = intensity.toLowerCase();
var byte = {"high": 0x0, "normal": 0x2, "low": 0x4}[intensity];
if (byte === undefined) {
console.log("Dry intensity %s is not defined!", intensity);
return false;
}
bytes[8] = (bytes[8] & 0xf0) | byte;
savedState.dryIntensity = intensity;
return true;
}
var SetWindHorizontal = function(horizontal) { //Direction du vent gauche et droite (1:le plus à gauche~ 3:Central~ 5:Le plus à droite, 6:rotation)
if (horizontal <= 0 || horizontal > 6) {
console.log("Horizontal wind direction %d is out of range (1 ~ 6).", horizontal);
return false;
}
if (horizontal == 6) {
horizontal = 0xc;
}
bytes[8] = (bytes[8] & 0xf) | (horizontal << 4);
savedState.horizontal = horizontal;
return true;
}
var SetWindVertical = function(vertical) { //Sous amélioration du vent (0:Automatique, 1:Haut~ 5:Bas, 6:rotation)
if (vertical < 0 || vertical > 6) {
console.log("Vertical wind direction %d is out of range (0 ~ 6).", vertical);
return false;
}
if (vertical == 6) {
vertical = 7;
}
bytes[9] = (bytes[8] & 0b11000111) | (vertical << 3);
savedState.vertical = vertical;
return true;
}
var SetWindSpeed = function(speed) { //Vitesse du vent (0:Automatique, 1:faible, 2:Pendant ~, 3:force, 4:puissant)
if (speed < 0 || speed > 4) {
console.log("Wind speed %d is out of range (0 ~ 4).", speed);
return false;
}
var powerful = 0x00;
if (speed == 4) {
speed = 3;
powerful = 0x10;
}
bytes[9] = (bytes[9] & 0b11111000) | speed;
bytes[15] = powerful;
savedState.speed = speed;
return true;
}
var SetWindArea = function(area) { //Zone de vent (aucun:Utilisez les valeurs de vent gauche et droite, whole:L'ensemble, left:Moitié gauche, right:Moitié droite)
var byte = {"none": 0x00, "whole": 0x8, "left": 0x40, "right": 0xc0}[area.toLowerCase()];
if (byte === undefined) {
console.log("Wind area %s is not defined!", area);
return false;
}
bytes[13] = byte;
savedState.area = area;
return true;
}
var fs = require("fs");
var savedState = {
"power": false,
"mode": "cool",
"coolTemperature": 28,
"heatTemperature": 20,
"dryIntensity": "normal",
"horizontal": 5,
"vertical": 0,
"speed": 0,
"area": "none"
};
try {
savedState = JSON.parse(fs.readFileSync("mbac.sav", "utf8"));
} catch(error) {}
try {
if (savedState.mode !== undefined) {
SetMode(savedState.mode);
}
if (savedState.power !== undefined) {
SetPower(savedState.power);
}
if (savedState.horizontal !== undefined) {
SetWindHorizontal(savedState.horizontal);
}
if (savedState.vertical !== undefined) {
SetWindVertical(savedState.vertical);
}
if (savedState.speed !== undefined) {
SetWindSpeed(savedState.speed);
}
if (savedState.area !== undefined) {
SetWindArea(savedState.area);
}
} catch(error) {console.error(error)}
var SaveState = function() {
fs.writeFile('mbac.sav', JSON.stringify(savedState));
}
if (require.main === module) {
var i = 2;
while (i < process.argv.length) {
var key = process.argv[i];
if (key == "-p" || key == "--power") {
SetPower(process.argv[i + 1]);
i += 2;
} else if (key == "-m" || key == "--mode") {
if (!SetMode(process.argv[i + 1])) {
console.log("Invalid value of mode option \"%s\"", process.argv[i + 1]);
return;
}
i += 2;
} else if (key == "-t" || key == "--temperature") {
if (!SetTemperature(process.argv[i + 1])) {
console.log("Invalid value of temperature option \"%s\"", process.argv[i + 1]);
return;
}
i += 2;
} else if (key == "-d" || key == "--dry_intensity") {
if (!SetDryIntensity(process.argv[i + 1])) {
console.log("Invalid value of dry intensity option \"%s\"", process.argv[i + 1]);
return;
}
i += 2;
} else if (key == "-h" || key == "--horizontal") {
if (!SetWindHorizontal(process.argv[i + 1])) {
console.log("Invalid value of wind horizontal option \"%s\"", process.argv[i + 1]);
return;
}
i += 2;
} else if (key == "-v" || key == "--vertical") {
if (!SetWindVertical(process.argv[i + 1])) {
console.log("Invalid value of wind vertical option \"%s\"", process.argv[i + 1]);
return;
}
i += 2;
} else if (key == "-s" || key == "--speed") {
if (!SetWindSpeed(process.argv[i + 1])) {
console.log("Invalid value of wind speed option \"%s\"", process.argv[i + 1]);
return;
}
i += 2;
} else if (key == "-a" || key == "--area") {
if (!SetWindArea(process.argv[i + 1])) {
console.log("Invalid value of wind area option \"%s\"", process.argv[i + 1]);
return;
}
i += 2;
} else {
console.log("Invalid option key \"%s\"", key);
return;
}
}
UpdateCheckByte();
SaveState();
var signal = aeha(430, bytes, 2, 13300);
console.log(signal);
}
Premièrement, bytes est un tableau qui stocke les signaux générés.
La fonction aeha est une fonction qui sort le temps d'éclairage / d'extinction de la LED à partir du tableau d'octets selon le format coopératif fait maison.
La fonction UpdateCheckByte est une fonction qui calcule et définit la valeur de contrôle à la fin du signal.
Après cela, il existe une fonction pour définir différents états du climatiseur.
var fs = require("fs");
À partir de maintenant, ce sera un programme qui restaure l'état du climatiseur précédent et enregistre l'état actuel. En faisant cela, vous pouvez opérer en spécifiant uniquement la pièce que vous souhaitez changer sans spécifier à chaque fois tous les états du climatiseur.
if (require.main === module)Le contenu évalue et exécute les arguments de ligne de commande. Actuellement, je n'ai pas l'intention d'utiliser l'opération à partir de la ligne de commande régulièrement, donc l'implémentation ici est appropriée.
## Courir
[Scanner et envoyer des signaux de télécommande infrarouge à l'aide de GPIO de Raspberry Pi](https://qiita.com/Hiroki_Kawakami/items/3f7d332797d188dc9022)
Le programme d'envoi utilise celui de l'article ci-dessus tel quel. Veuillez le voir pour une utilisation spécifique.
Refroidissement 30 degrés
#### **`node mbac.js -m cool -t 30 | sudo ./send 18`**
Chauffage 20 degrés
node mbac.js -m heat -t 20 | sudo ./send 18
Éteindre
#### **`node mbac.js -p off | sudo ./send 18`**
Vous pouvez également utiliser les options suivantes
Le programme lui-même est simple, mais je ne l'ai pas testé, il peut donc être bogué.
Analyser le signal de la télécommande avec Rasberry Pi 3 (mise à jour du résultat de l'analyse du signal de la télécommande 10/1) Format de communication de la télécommande infrarouge
Recommended Posts