Last active
November 30, 2018 03:03
-
-
Save b2ox/97a7a75fb4b8182e805314e1957b9619 to your computer and use it in GitHub Desktop.
赤外線リモコンを受信してちょっとだけ解析してシリアルにjsonで流すArduino Library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include "IRScanner.h" | |
IRScanner::IRScanner(uint8_t pin, uint8_t code_max) | |
{ | |
_pin = pin; | |
_code_max = code_max; | |
_work_max = (_code_max + 1) * 16; | |
_code_array = new uint8_t[_code_max]; | |
_work_array = new int16_t[_work_max]; | |
_T = 0; | |
pinMode(_pin, INPUT); | |
} | |
IRScanner::~IRScanner() | |
{ | |
delete[] _code_array; | |
delete[] _work_array; | |
} | |
int IRScanner::unit() | |
{ | |
return _T; | |
} | |
int IRScanner::unit_delta() | |
{ | |
return _Tmax - _Tmin; | |
} | |
int IRScanner::code_size() | |
{ | |
return _code_size; | |
} | |
// 赤外線信号を読み取って、生データの個数を返す | |
// 負数の場合はエラー | |
int IRScanner::scan() | |
{ | |
boolean mode = HIGH; //呼び出し時点で赤外線モジュール出力はHIGH(=リモコンLEDオフ)と仮定する | |
int i = 0; | |
_Tmin = INT16_MAX; // LOW,HIGH継続時間の最小値の計測用 | |
pinMode(13, OUTPUT); // Arduino上のLEDを受信インジケータとして使う | |
digitalWrite(13, LOW); | |
while (i < _work_max) | |
{ | |
unsigned long st = micros(); | |
unsigned long dur = INT16_MAX; //時間μs | |
// LOW,HIGHが変化するまでループで待機(timeoutを超えたら抜ける) | |
do | |
{ | |
dur = micros() - st; | |
} while (digitalRead(_pin) == mode && dur <= INT16_MAX); | |
if (dur > INT16_MAX) break; | |
if (i == 0) digitalWrite(13, HIGH); // リモコンLEDを受信開始したらArduino上のLEDを点灯 | |
mode = !mode; | |
int d = (int)dur; | |
_work_array[i++] = d; | |
if (d < _Tmin) _Tmin = d; // 最小値を更新 | |
} | |
digitalWrite(13, LOW); // 受信終了したので消灯 | |
_work_size = i - 1; | |
if (_work_size < 18) return -1; // リーダー(2)+8bitデータ(16)くらいはあるはずなので少ないのは読み取りミス | |
// 単位幅にはばらつきがあるので | |
// 最小値の2倍を超えない範囲で最大のものを求め | |
int Tmin2 = _Tmin * 2; | |
_Tmax = _Tmin; | |
for (int j = 1; j <= _work_size; j++) | |
{ | |
int w = _work_array[j]; | |
if (w < Tmin2 && _Tmax < w) _Tmax = w; | |
} | |
if (_Tmax == _Tmin) return -2; // ばらつきが全く無いのは不自然なので読み取りミス | |
// 中央値を10μ単位で四捨五入して単位幅Tとする | |
_T = (int)((10.0f + _Tmin + _Tmax) / 20) * 10; | |
// Tでクォンタイズ(上書きする) | |
for (int j = 1; j <= _work_size; j++) | |
_work_array[j - 1] = (int)(0.5f + (float)_work_array[j] / _T); | |
_work_array[_work_size - 1] = 0; | |
// コード解析 NECとAEHAは 1T+1T:0 / 1T+3T:1 で8bit単位で構成される | |
// リーダー部分(NEC:16T+8T, AEHA:8T+4T)のチェックは省略 | |
byte d = 0; | |
int k = 0; | |
for (int j = 2; j < _work_size; j += 2) | |
{ | |
int a = _work_array[j]; // LED onの時間(Tで量子化) | |
int b = _work_array[j + 1]; // LED offの時間 | |
if (a == 1) | |
{ // (a,b): (1,1)->0, (1,3)->1 | |
if (b == 1 || b == 3) | |
{ | |
d = d << 1; | |
if (b == 3) d += 1; | |
k++; | |
if (k % 8 == 0) | |
{ | |
_code_array[k / 8 - 1] = d; | |
d = 0; | |
} | |
} | |
else | |
a = 0; // 1T+1T or 1T+3T以外は終端とみなす | |
} | |
else if (7 < a && a < 11) | |
{ // 途中にリーダーが入って続きのコードを出力するものがある(東芝 WH-D9Gとか) | |
// 8Tジャストということはないので誤差を許容 | |
_code_array[k / 8] = 0; // ダミーデータとして0を入れておく | |
k += 8; | |
a = 1; | |
} | |
if (a != 1) break; | |
} | |
_code_size = k / 8; | |
// コード解析 ここまで | |
return _work_size; | |
} | |
// 受信データをjson文字列にする | |
String IRScanner::toJsonString() | |
{ | |
String s = String("{\"t\":" + String(_T) + ",\"w\":" + String(_Tmax - _Tmin) + ",\"code\":["); | |
for (int i = 0; i < _code_size; i++) | |
{ | |
if (i > 0 && _code_array[i] == (byte)~_code_array[i - 1]) | |
s.concat("-1"); // 直前値の補数の場合 | |
else | |
s.concat(String(_code_array[i])); | |
s.concat(","); | |
} | |
s.concat("0],\"q\":["); | |
for (int i = 0; i < _work_size - 1; i++) | |
{ | |
s.concat(String(_work_array[i])); | |
s.concat(","); | |
} | |
s.concat("0]}"); | |
return s; | |
} | |
// 受信データを Tus[Tmax-Tmin]code0,code1,...,0 (値は16進数、直前値の補数は'_')形式にする | |
String IRScanner::toString() | |
{ | |
String s = String(String(_T) + "us[" + String(_Tmax - _Tmin) + "]"); | |
for (int i = 0; i < _code_size; i++) | |
{ | |
if (i > 0 && _code_array[i] == (byte)~_code_array[i - 1]) | |
s.concat("_"); // 直前値の補数の場合 | |
else | |
s.concat(String(_code_array[i], HEX)); | |
s.concat(","); | |
} | |
s.concat("0"); | |
return s; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __IRScanner_Library__ | |
#define __IRScanner_Library__ | |
#include <Arduino.h> | |
class IRScanner { | |
public: | |
IRScanner(uint8_t pin, uint8_t code_max); // pin:IRセンサ接続ピン code_max:8bitコードの最大個数 | |
~IRScanner(); | |
int scan(); // スキャンして読み込んだ生データの個数を返す 負数の場合は読み込みエラー | |
// ここから下はscan()実行後でないと無意味 | |
int unit(); // 信号の単位幅を返す | |
int unit_delta(); // 信号の誤差の振れ幅 Tmin < T < Tmax として Tmax - Tmin が返る | |
int code_size(); // 読み取った8bitコードの個数 | |
void putJson(); // シリアルにjson形式で出力 | |
void putData(); // シリアルに Tus[Tmax-Tmin]code0,code1,...,0 (値は16進数、直前値の補数は'_')形式で出力 | |
private: | |
uint8_t _pin; | |
int _code_max, _code_size; | |
uint8_t *_code_array; | |
int16_t *_work_array; | |
int _work_max, _work_size; | |
int _T, _Tmin, _Tmax; | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 赤外線リモコンを受信し、シリアルにjson形式とかで出力する | |
#include <Arduino.h> | |
#include "IRScanner.h" | |
// D11:MOSI を使うとISP用ピンのVcc,MOSI,GNDにまたがって挿すことができる | |
IRScanner IR(11, 16); | |
void setup() { | |
Serial.begin(115200); | |
Serial.println("IRScanner start."); | |
} | |
char mode = 'd'; // 'd' or 'j'のトグル | |
void loop() | |
{ | |
if (Serial.available() > 0) { | |
char c = Serial.read(); | |
if (c == 'd' || c == 'j') { // シリアルに'd','j'が入力されたら表示モードを変更 | |
mode = c; | |
Serial.print("mode change:"); | |
Serial.println(mode); | |
} | |
} | |
if (IR.scan() > 0) { | |
if (mode == 'j') IR.putJson(); else IR.putData(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ir_scan2.inoをクラス化してみた