Skip to content

Instantly share code, notes, and snippets.

@b2ox
Last active November 30, 2018 03:03
Show Gist options
  • Save b2ox/97a7a75fb4b8182e805314e1957b9619 to your computer and use it in GitHub Desktop.
Save b2ox/97a7a75fb4b8182e805314e1957b9619 to your computer and use it in GitHub Desktop.
赤外線リモコンを受信してちょっとだけ解析してシリアルにjsonで流すArduino Library
#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;
}
#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
// 赤外線リモコンを受信し、シリアルに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();
}
}
@b2ox
Copy link
Author

b2ox commented Oct 31, 2018

ir_scan2.inoをクラス化してみた

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment