[JAVA] Versuchen Sie, die Fernbedienung der Mitsubishi-Klimaanlage mit Raspberry Pi zu scannen, zu analysieren und zu übertragen

Scannen und Senden von Infrarot-Fernbedienungssignalen mit GPIO von Raspberry Pi Ich habe diesen Artikel das letzte Mal geschrieben. Die Fernbedienung für Fernseher und Deckenleuchten ist einfach und einfach, da immer das gleiche Signal ausgegeben wird, wenn Sie dieselbe Taste drücken. Die Fernbedienung für Klimaanlagen ist jedoch etwas Besonderes und nicht so. Bei einer Klimaanlage kennt die Fernbedienung den aktuellen Zustand der Klimaanlage, und die Bedienung beim Drücken der Taste ändert sich je nach Zustand. Wenn Sie jedoch beim Ausschalten der Klimaanlage in Form eines Signals scannen, beim Ausschalten bei 28 Grad ein Signal und beim Einschalten der Heizung bei 20 Grad ein Signal, können Sie das Kühlen und Heizen ein- und ausschalten. Wird sein. Da es sich jedoch um eine große Sache handelt, möchte ich die Fernbedienung analysieren, damit die eingestellte Temperatur und Windrichtung frei eingestellt werden können.

Wir werden den Scan verwenden und Programme aus dem vorherigen Artikel senden. Wenn Sie ihn also nicht gelesen haben, lesen Sie ihn bitte zuerst.

Fernbedienung

Remote.png

Dies ist die für die Analyse verwendete Fernbedienung. Der Modellname lautet NH122 205AL Der Typ, der eine Klimaanlage in Kirigamine hat.

Scan

Zunächst werde ich die Fernbedienung der Klimaanlage scannen.

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
(Weggelassen)
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
(Weggelassen)
458 	1264
457 	399
459 	399
458 	

Sehen Sie sich dies an, um festzustellen, welches Format verwendet wird. Erstens, da es nicht von SONY hergestellt wird, ist es entweder NEC oder Ie Seikyo. Kommunikationsformat der Infrarot-Fernbedienung Wenn Sie es mit der Website hier vergleichen, Die Beleuchtungszeit 3499 der ersten Zeile, dh der Leiter, beträgt ungefähr das Achtfache der Beleuchtungszeit 460, wenn die Daten der zweiten und der nachfolgenden Zeilen gesendet werden, sodass Sie sehen können, dass es sich um ein selbst erstelltes kooperatives Format handelt. Es gibt auch einen Ort, an dem die Beleuchtungszeit bis zu 3470 beträgt, sodass diese Fernbedienung möglicherweise ein 2-Frame-Signal sendet. Aber es scheint sich nicht zu wiederholen. Ich kann es jedoch überhaupt nicht verstehen, selbst wenn ich mir so viel anschaue. Schreiben wir also ein Programm, das dies in hexadezimal konvertiert.

Analyse

In hexadezimal konvertieren

Konvertieren Sie zunächst die gescannte Datei in hexadezimal. Im hausgemachten kooperativen Format wird der Leiter nach dem Senden in 8-Bit-Reihenfolge vom LSB des ersten Bytes an das MSB gesendet. Danach werden das 2. und 3. Byte der Reihe nach angezeigt, und der Frame endet, wenn das Licht für die letzten 8 ms oder länger ausgeschaltet wird.

Ignorieren Sie vorerst Trailer und Repeat und konzentrieren Sie sich nur auf Leader und Data. Das Format der Fernbedienung hat eine Modulationseinheit T, und die gesendeten Informationen werden durch die Beleuchtungszeit und die Löschzeit bestimmt, die mehrmals T beträgt. Leader hat eine Beleuchtungszeit von 8T und eine Beleuchtungszeit von 4T. Wenn 0 übertragen wird, ist die Beleuchtungszeit T und die Beleuchtungszeit T. Wenn 1 gesendet wird, beträgt die Beleuchtungszeit T und die Beleuchtungszeit 3T. Wird sein. Basierend auf der Tatsache, dass T 350 bis 500 beträgt, wird bei langer Beleuchtungszeit von Leader bestimmt, ob die Beleuchtungszeit und die Löschzeit ungefähr gleich 0 sind, und wenn die Löschzeit länger als die Beleuchtungszeit ist, wird dies durch den rauen Zustand bestimmt. Machen.

Programm

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;
}

kompilieren

$ gcc -o aeha aeha.c

Lauf

Konvertieren Sie die beim Scannen erstellte heat20-Datei

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 

Jetzt können Sie das von der Fernbedienung gesendete Signal hexadezimal sehen. Sie können sehen, dass diese Fernbedienung zweimal denselben Frame sendet.

Analyse

Da ich das Signal hexadezimal sehen konnte, scanne ich das Signal der Fernbedienung in verschiedenen Modi und vergleiche es, um es zu analysieren. Ich scanne es jedoch nacheinander und konvertiere es in hexadezimal. Es ist mühsam zu vergleichen. Aber es ist okay. Es ist nicht nur zum Spaß, dass ich Standard-E / A für Datei-E / A verwendet habe. Auf diese Weise können Sie das Scanprogramm und das hexadezimale Konvertierungsprogramm direkt mit einer Pipe verbinden.

Befehl

$ while true; do ./scan 20 2>/dev/null | ./aeha; done

./scan 20 | ./Ich kann das Scan-Programm und das hexadezimale Konvertierungsprogramm mit aeha mit einer Pipe verbinden, aber die stderr-Ausgabe des Scan-Programms ist ein Hindernis, also 2>/dev/Ich werfe es als null weg. Und allein damit endet das Programm nach einmaligem Scannen. Während dies zutrifft, wird das Programm in einer Endlosschleife neu gestartet.


 Auf diese Weise können Sie das Fernbedienungssignal kontinuierlich scannen und als Hexadezimalwert anzeigen.
 Wenn Sie fertig sind, drücken Sie Strg + C, um den Vorgang zu beenden.

### Lauf

 Sie müssen lediglich den oben beschriebenen Befehl ausführen, nacheinander Fernbedienungssignale an den Sensor senden und die angezeigten Werte vergleichen.

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


 In seltenen Fällen wird es möglicherweise nicht normal konvertiert, aber da diese Fernbedienung zweimal dasselbe Signal sendet, gibt es kein Problem bei der Analyse.
 Dies ist das Ergebnis des Drückens der Taste, um die eingestellte Temperatur in Richtung des Sensors zu senken und fünf Signale von der Erwärmung um 20 Grad auf die Erwärmung um 16 Grad der Reihe nach einzugeben.

 Ich denke, dass das letzte 1 Byte zur Fehlererkennung verwendet wird. Ignorieren Sie es also und Sie können sehen, dass sich der Wert des 7. Bytes (das 0. Byte ganz links) geändert hat.

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


 Darüber hinaus ist dies das Ergebnis des Drückens der Taste, um die eingestellte Temperatur von 28 Grad auf 31 Grad zu erhöhen und vier Signale der Reihe nach einzugeben, und es ist ersichtlich, dass sich auch der Wert des 7. Bytes ändert.

 Wenn Sie den Wert des 7. Bytes berechnen, können Sie sehen, dass er von der eingestellten Temperatur -16 ist.
 Daher wurde festgestellt, dass die eingestellte Temperatur im 7. Byte gespeichert ist.

 Fügen Sie danach auf die gleiche Weise für den Betriebsmodus, die Windgeschwindigkeit, die Windrichtung usw. einfach Signale hinzu, die nur eine Bedingung in der angegebenen Reihenfolge ändern, und sehen Sie die Wertänderung.

### Letzte 1 Byte

 Auf dem Referenzstandort wurde die Fernbedienung der Klimaanlage von National analysiert. Die Fernbedienung hatte das hausgemachte kooperative Format, und das letzte 1 Byte waren die unteren 8 Bits der Summe aller Bytes. Als ich es mit der Fernbedienung überprüfte, die ich gerade analysiere, wurden die unteren 8 Bits wie auf der Referenzstelle hinzugefügt.

## Analyseergebnis

 ![解析.png](https://qiita-image-store.s3.amazonaws.com/0/242402/8227486d-9575-6147-3c5d-33c8e94b4954.png)

 Das Signal beim Einstellen des Timers wird nicht analysiert. Es ist einfacher und flexibler, es programmgesteuert zu steuern.
 Wenn der Windbereich 0 ist, wird der für den linken und rechten Wind angegebene Wert verwendet. Wenn hier angegeben, ist die Ausrichtung die im Windbereich angegebene, ohne die linken und rechten Windwerte zu verwenden.

# Senden

 Nachdem die Analyse abgeschlossen ist, müssen Sie nur noch ein Programm schreiben, das Signale gemäß den Analyseergebnissen generiert. Das Programm, das das Signal generiert, wurde mit Node.js erstellt, damit es später problemlos über das Web bedient werden kann.

## Programm


#### **`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) { //Modus einstellen (cool:Klimaanlage, heat:Heizung, dry:Entfeuchtung, wind:Gebläse)
    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) { //Stellen Sie die eingestellte Temperatur ein (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) { //Entfeuchtungsstärke (hoch:Stärke, normal:Standard, low:schwach)
    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) { //Windrichtung links und rechts (1:ganz links~ 3:Zentral~ 5:Ganz rechts, 6:Drehung)
    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) { //Bei Windverbesserung (0:Automatisch, 1:oben~ 5:Unterseite, 6:Drehung)
    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) { //Windgeschwindigkeit (0:Automatisch, 1:schwach, 2:Während ~, 3:Stärke, 4:mächtig)
    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) { //Windbereich (keiner:Verwenden Sie die linken und rechten Windwerte, whole:Das ganze, left:Linke Hälfte, right:Rechte Hälfte)
    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);
}

Erstens ist Bytes ein Array, das die erzeugten Signale speichert. Die aeha-Funktion ist eine Funktion, die die Beleuchtungs- / Löschzeit der LED aus dem Byte-Array gemäß dem selbst erstellten kooperativen Format ausgibt. Die UpdateCheckByte-Funktion ist eine Funktion, die den Prüfwert am Ende des Signals berechnet und festlegt. Danach gibt es eine Funktion zum Einstellen verschiedener Zustände der Klimaanlage. var fs = require("fs");Von nun an wird es ein Programm sein, das den vorherigen Zustand der Klimaanlage wiederherstellt und den aktuellen Zustand speichert. Auf diese Weise können Sie nur das Teil angeben, das Sie ändern möchten, ohne jedes Mal alle Zustände der Klimaanlage anzugeben.

if (require.main === module)Der Inhalt wertet Befehlszeilenargumente aus und führt sie aus. Derzeit beabsichtige ich nicht, die Operation regelmäßig über die Befehlszeile zu verwenden, daher ist die Implementierung hier angemessen.



## Lauf

 [Scannen und Senden von Infrarot-Fernbedienungssignalen mit GPIO von Raspberry Pi](https://qiita.com/Hiroki_Kawakami/items/3f7d332797d188dc9022)
 Das Sendeprogramm verwendet das im obigen Artikel beschriebene. Bitte beachten Sie dies für die spezifische Verwendung.

 Kühlung 30 Grad

#### **`node mbac.js -m cool -t 30 | sudo ./send 18`**

Heizung 20 Grad

node mbac.js -m heat -t 20 | sudo ./send 18



 Ausschalten

#### **`node mbac.js -p off | sudo ./send 18`**

Sie können auch mit den folgenden Optionen arbeiten

オプション.png

Das Programm selbst ist einfach, aber ich habe es nicht getestet, so dass es möglicherweise fehlerhaft ist.

Referenzseite

Analysieren Sie das Fernbedienungssignal mit Rasberry Pi 3 (Aktualisierung der Ergebnisse der 10/1-Fernsteuerungssignalanalyse) Kommunikationsformat der Infrarot-Fernbedienung

Recommended Posts

Versuchen Sie, die Fernbedienung der Mitsubishi-Klimaanlage mit Raspberry Pi zu scannen, zu analysieren und zu übertragen
Fügen Sie nun "InfluxDB + Telegraf + Chronograf" in CentOS8 ein und versuchen Sie, die Temperatur mehrerer Raspberry pi4 zu steuern.
Erstellen Sie mit Raspberry Pi und Docker Compose im Handumdrehen ein NAS mit DLNA-Funktion
Stellen Sie die Sensorinformationen von Raspberry Pi in Java grafisch dar und überprüfen Sie sie mit einem Webbrowser
Versuchen Sie es mit der Wii-Fernbedienung in Java
Stellen Sie die Sensorinformationen von Raspberry Pi grafisch dar und bereiten Sie eine Umgebung vor, die mit einem Webbrowser überprüft werden kann