「Arduino Mega 2560 LESSON:14 赤外線受信モジュール」の版間の差分
編集の要約なし |
編集の要約なし |
||
| (同じ利用者による、間の1版が非表示) | |||
| 240行目: | 240行目: | ||
IrReceiver.resume(); | IrReceiver.resume(); | ||
} | } | ||
} | |||
</code></pre></div><script></freescript> | |||
ライブラリを使わないIR読み取りコードは以下のとおりです。 | |||
<freescript></script><div class="hljs-wrap"><pre data-label="Arduino Code" class="pre-wrap"> | |||
<code class="cpp">const byte IR_PIN = 2; | |||
// 許容するパルス幅(マイクロ秒) | |||
const unsigned long TIMEOUT_US = 12000; | |||
// 指定したレベルが続いた時間を測定する | |||
// タイムアウトした場合は0を返す | |||
unsigned long measurePulse(byte level) { | |||
unsigned long start = micros(); | |||
while (digitalRead(IR_PIN) == level) { | |||
if (micros() - start > TIMEOUT_US) { | |||
return 0; | |||
} | |||
} | |||
return micros() - start; | |||
} | |||
// NEC信号の読み取り | |||
bool readNEC(uint32_t &data) { | |||
// リーダーMARK(約9000us) | |||
unsigned long mark = measurePulse(LOW); | |||
if (mark < 8000 || mark > 11000) return false; | |||
// リーダーSPACE(約4500us) | |||
unsigned long space = measurePulse(HIGH); | |||
if (space < 3500 || space > 6000) return false; | |||
data = 0; | |||
// NECは32ビット、LSBから送信 | |||
for (byte i = 0; i < 32; i++) { | |||
// データMARK(約560us) | |||
mark = measurePulse(LOW); | |||
if (mark < 300 || mark > 900) return false; | |||
// データSPACE | |||
space = measurePulse(HIGH); | |||
if (space >= 300 && space < 1000) { | |||
// ビット0 | |||
} | |||
else if (space >= 1200 && space < 2200) { | |||
// ビット1 | |||
data |= (uint32_t)1 << i; | |||
} | |||
else { | |||
return false; | |||
} | |||
} | |||
// 最後のMARK(約560us) | |||
mark = measurePulse(LOW); | |||
if (mark < 300 || mark > 900) return false; | |||
return true; | |||
} | |||
void setup() { | |||
pinMode(IR_PIN, INPUT); | |||
Serial.begin(115200); | |||
Serial.println("NEC Receiver Ready"); | |||
} | |||
void loop() { | |||
// 通常はHIGH、受信開始でLOWになる | |||
if (digitalRead(IR_PIN) == LOW) { | |||
uint32_t rawData; | |||
if (readNEC(rawData)) { | |||
Serial.print("RAW: 0x"); | |||
// 8桁の16進数で表示 | |||
for (int i = 7; i >= 0; i--) { | |||
byte nibble = (rawData >> (i * 4)) & 0x0F; | |||
Serial.print(nibble, HEX); | |||
} | |||
Serial.println(); | |||
} | |||
} | |||
} | |||
</code></pre></div><script></freescript> | |||
もっと汎用的な生データを取得するコードはこうだな。 | |||
<freescript></script><div class="hljs-wrap"><pre data-label="Arduino Code" class="pre-wrap"> | |||
<code class="cpp"> | |||
const byte IR_PIN = 11; | |||
// ======================================== | |||
// 設定 | |||
// ======================================== | |||
// true : RAW波形を表示する | |||
// false: 復号結果だけ表示する | |||
const bool SHOW_RAW = true; | |||
// true : 0/1のビット列も表示する | |||
const bool SHOW_BINARY = true; | |||
// true : 1バイトを下位ビットから受信 | |||
// false: 1バイトを上位ビットから受信 | |||
const bool LSB_FIRST = true; | |||
// 無信号150msで受信終了 | |||
const uint32_t END_GAP_US = 150000; | |||
// 無信号15ms以上でフレーム分割 | |||
const uint32_t FRAME_GAP_US = 15000; | |||
// 異常に長いMARKへの対策 | |||
const uint32_t MAX_MARK_US = 500000; | |||
// RAW記録の最大数 | |||
const uint16_t MAX_TIMINGS = 1100; | |||
// HEX復号の最大バイト数 | |||
const uint16_t MAX_BYTES = 80; | |||
// ビット判定が曖昧になる幅(us) | |||
const uint32_t AMBIGUOUS_MARGIN_US = 100; | |||
// ======================================== | |||
// 記録用メモリ | |||
// ======================================== | |||
uint32_t timings[MAX_TIMINGS]; | |||
uint16_t timingCount = 0; | |||
uint8_t decodedBytes[MAX_BYTES]; | |||
unsigned long operationCount = 0; | |||
// ======================================== | |||
// 16進数表示 | |||
// ======================================== | |||
void printHexByte(uint8_t value) { | |||
if (value < 0x10) { | |||
Serial.print('0'); | |||
} | |||
Serial.print(value, HEX); | |||
} | |||
// ======================================== | |||
// 2進数表示 | |||
// ======================================== | |||
void printBinaryByte(uint8_t value) { | |||
for (int8_t i = 7; i >= 0; i--) { | |||
Serial.print((value >> i) & 1); | |||
} | |||
} | |||
// ======================================== | |||
// RAW波形表示 | |||
// ======================================== | |||
void printRaw(uint16_t startIndex, | |||
uint16_t endIndex) { | |||
Serial.println(); | |||
Serial.println("--- RAW TIMING (us) ---"); | |||
uint8_t column = 0; | |||
for (uint16_t i = startIndex; | |||
i < endIndex; i++) { | |||
// 奇数インデックスがSPACE | |||
// 偶数インデックスがMARK | |||
if ((i & 1) == 0) { | |||
Serial.print('+'); | |||
} else { | |||
Serial.print('-'); | |||
} | |||
Serial.print(timings[i]); | |||
Serial.print(' '); | |||
column++; | |||
if (column >= 8) { | |||
Serial.println(); | |||
column = 0; | |||
} | |||
} | |||
Serial.println(); | |||
} | |||
// ======================================== | |||
// パルス距離方式の解析 | |||
// ======================================== | |||
void decodeFrame(uint16_t startIndex, | |||
uint16_t endIndex) { | |||
uint16_t rawCount = endIndex - startIndex; | |||
// 単独MARKの場合 | |||
if (rawCount == 1) { | |||
Serial.println(); | |||
Serial.println("Single MARK / Preamble"); | |||
Serial.print("MARK (us): "); | |||
Serial.println(timings[startIndex]); | |||
return; | |||
} | |||
// 想定する構造: | |||
// Header MARK | |||
// Header SPACE | |||
// Data MARK + SPACE を繰り返す | |||
// Stop MARK | |||
if (rawCount < 5 || | |||
(rawCount & 1) == 0) { | |||
Serial.println(); | |||
Serial.println("Unknown frame structure."); | |||
Serial.println("Use RAW timing data."); | |||
return; | |||
} | |||
uint16_t bitCount = (rawCount - 3) / 2; | |||
Serial.println(); | |||
Serial.println("--- FRAME INFORMATION ---"); | |||
Serial.print("Candidate Bits: "); | |||
Serial.println(bitCount); | |||
Serial.print("Header MARK (us): "); | |||
Serial.println(timings[startIndex]); | |||
Serial.print("Header SPACE (us): "); | |||
Serial.println(timings[startIndex + 1]); | |||
Serial.print("Stop MARK (us): "); | |||
Serial.println(timings[endIndex - 1]); | |||
// ====================================== | |||
// SPACEの最小値・最大値を調査 | |||
// ====================================== | |||
uint32_t minSpace = 0xFFFFFFFFUL; | |||
uint32_t maxSpace = 0; | |||
for (uint16_t i = 0; i < bitCount; i++) { | |||
uint16_t spaceIndex = | |||
startIndex + 3 + i * 2; | |||
uint32_t value = timings[spaceIndex]; | |||
if (value < minSpace) { | |||
minSpace = value; | |||
} | |||
if (value > maxSpace) { | |||
maxSpace = value; | |||
} | |||
} | |||
Serial.print("Data SPACE min (us): "); | |||
Serial.println(minSpace); | |||
Serial.print("Data SPACE max (us): "); | |||
Serial.println(maxSpace); | |||
// ====================================== | |||
// パルス距離方式か簡易判定 | |||
// ====================================== | |||
// 最大SPACEが最小SPACEの | |||
// 1.7倍未満なら2値判定を保留 | |||
// | |||
// 単一種類のビットしか含まれない | |||
// フレームもここでは復号を保留する。 | |||
if (maxSpace <= minSpace || | |||
(uint64_t)maxSpace * 10 < | |||
(uint64_t)minSpace * 17) { | |||
Serial.println(); | |||
Serial.println( | |||
"Pulse-distance decode unavailable." | |||
); | |||
Serial.println( | |||
"Insufficient SPACE separation." | |||
); | |||
Serial.println( | |||
"RAW timing is still available." | |||
); | |||
return; | |||
} | |||
// SPACEの中間値を判定基準にする | |||
uint32_t threshold = | |||
minSpace + (maxSpace - minSpace) / 2; | |||
Serial.print("Auto threshold (us): "); | |||
Serial.println(threshold); | |||
// ====================================== | |||
// ビット復号 | |||
// ====================================== | |||
uint16_t ambiguousCount = 0; | |||
uint16_t byteCount = (bitCount + 7) / 8; | |||
if (byteCount > MAX_BYTES) { | |||
Serial.println( | |||
"ERROR: Decode buffer too small." | |||
); | |||
return; | |||
} | |||
// 復号バッファを初期化 | |||
for (uint16_t i = 0; i < byteCount; i++) { | |||
decodedBytes[i] = 0; | |||
} | |||
if (SHOW_BINARY) { | |||
Serial.println(); | |||
Serial.println("--- BINARY DATA ---"); | |||
Serial.println("(Transmission order)"); | |||
} | |||
for (uint16_t i = 0; i < bitCount; i++) { | |||
uint16_t spaceIndex = | |||
startIndex + 3 + i * 2; | |||
uint32_t space = timings[spaceIndex]; | |||
uint8_t bitValue = | |||
(space > threshold) ? 1 : 0; | |||
// 判定しきい値に近いビットを数える | |||
uint32_t difference = | |||
(space > threshold) | |||
? space - threshold | |||
: threshold - space; | |||
if (difference <= AMBIGUOUS_MARGIN_US) { | |||
ambiguousCount++; | |||
} | |||
// ==================================== | |||
// バイトへ格納 | |||
// ==================================== | |||
uint16_t byteIndex = i / 8; | |||
uint8_t bitPosition = i % 8; | |||
if (!LSB_FIRST) { | |||
bitPosition = 7 - bitPosition; | |||
} | |||
if (bitValue) { | |||
decodedBytes[byteIndex] |= | |||
(uint8_t)(1U << bitPosition); | |||
} | |||
// ==================================== | |||
// 送信順にビットを表示 | |||
// ==================================== | |||
if (SHOW_BINARY) { | |||
Serial.print(bitValue); | |||
if ((i + 1) % 8 == 0) { | |||
Serial.print(' '); | |||
} | |||
if ((i + 1) % 64 == 0) { | |||
Serial.println(); | |||
} | |||
} | |||
} | |||
if (SHOW_BINARY) { | |||
Serial.println(); | |||
} | |||
// ====================================== | |||
// HEX DATA | |||
// ====================================== | |||
Serial.println(); | |||
Serial.println("--- HEX DATA ---"); | |||
for (uint16_t i = 0; i < byteCount; i++) { | |||
printHexByte(decodedBytes[i]); | |||
Serial.print(' '); | |||
if ((i + 1) % 10 == 0) { | |||
Serial.println(); | |||
} | |||
} | |||
Serial.println(); | |||
// ====================================== | |||
// BYTE TABLE | |||
// ====================================== | |||
Serial.println(); | |||
Serial.println("--- BYTE TABLE ---"); | |||
for (uint16_t i = 0; i < byteCount; i++) { | |||
Serial.print("Byte "); | |||
if (i + 1 < 10) { | |||
Serial.print('0'); | |||
} | |||
Serial.print(i + 1); | |||
Serial.print(": 0x"); | |||
printHexByte(decodedBytes[i]); | |||
Serial.print(" BIN: "); | |||
printBinaryByte(decodedBytes[i]); | |||
Serial.println(); | |||
} | |||
// 8ビット未満の端数 | |||
uint8_t remainingBits = bitCount % 8; | |||
if (remainingBits != 0) { | |||
Serial.println(); | |||
Serial.print("Partial last byte: "); | |||
Serial.print(remainingBits); | |||
Serial.println(" valid bits"); | |||
Serial.println( | |||
"Unused positions are padded with 0." | |||
); | |||
} | |||
// ====================================== | |||
// 解析結果 | |||
// ====================================== | |||
Serial.println(); | |||
Serial.println("--- DECODE STATUS ---"); | |||
Serial.println("Method: PULSE DISTANCE"); | |||
Serial.print("Bit order: "); | |||
if (LSB_FIRST) { | |||
Serial.println("LSB FIRST"); | |||
} else { | |||
Serial.println("MSB FIRST"); | |||
} | |||
Serial.print("Decoded bits: "); | |||
Serial.println(bitCount); | |||
Serial.print("Decoded bytes: "); | |||
Serial.println(byteCount); | |||
Serial.print("Ambiguous bits: "); | |||
Serial.println(ambiguousCount); | |||
Serial.println( | |||
"Protocol/checksum not identified." | |||
); | |||
} | |||
// ======================================== | |||
// 1フレームの表示 | |||
// ======================================== | |||
void printFrame(uint16_t startIndex, | |||
uint16_t endIndex, | |||
unsigned int frameNo, | |||
uint32_t gapBefore) { | |||
Serial.println(); | |||
Serial.println("============================"); | |||
Serial.print("Frame No: "); | |||
Serial.println(frameNo); | |||
Serial.print("Gap before (us): "); | |||
Serial.println(gapBefore); | |||
Serial.print("Raw Count: "); | |||
Serial.println(endIndex - startIndex); | |||
// フレーム合計時間 | |||
uint32_t total = 0; | |||
for (uint16_t i = startIndex; | |||
i < endIndex; i++) { | |||
total += timings[i]; | |||
} | |||
Serial.print("Sum (us): "); | |||
Serial.println(total); | |||
// 生波形 | |||
if (SHOW_RAW) { | |||
printRaw(startIndex, endIndex); | |||
} | |||
// 復号 | |||
decodeFrame(startIndex, endIndex); | |||
} | |||
// ======================================== | |||
// 全フレームを表示 | |||
// ======================================== | |||
void printAllFrames(bool overflow, | |||
bool abnormalMark) { | |||
Serial.println(); | |||
Serial.println("############################"); | |||
Serial.print("Operation No: "); | |||
Serial.println(operationCount); | |||
Serial.print("Total timings: "); | |||
Serial.println(timingCount); | |||
if (overflow) { | |||
Serial.println( | |||
"WARNING: BUFFER OVERFLOW!" | |||
); | |||
} | |||
if (abnormalMark) { | |||
Serial.println( | |||
"WARNING: Abnormally long MARK!" | |||
); | |||
} | |||
uint16_t frameStart = 0; | |||
uint32_t gapBefore = 0; | |||
unsigned int frameNo = 1; | |||
for (uint16_t i = 0; i < timingCount; i++) { | |||
// 偶数がMARK、奇数がSPACE | |||
// 15ms以上のSPACEで分割 | |||
if ((i & 1) == 1 && | |||
timings[i] >= FRAME_GAP_US) { | |||
if (i > frameStart) { | |||
printFrame( | |||
frameStart, | |||
i, | |||
frameNo, | |||
gapBefore | |||
); | |||
frameNo++; | |||
} | |||
gapBefore = timings[i]; | |||
frameStart = i + 1; | |||
} | |||
} | |||
// 最終フレーム | |||
if (frameStart < timingCount) { | |||
printFrame( | |||
frameStart, | |||
timingCount, | |||
frameNo, | |||
gapBefore | |||
); | |||
} | |||
Serial.println(); | |||
Serial.println( | |||
"######## END OPERATION ########" | |||
); | |||
} | |||
// ======================================== | |||
// 初期化 | |||
// ======================================== | |||
void setup() { | |||
pinMode(IR_PIN, INPUT); | |||
Serial.begin(115200); | |||
Serial.println( | |||
"Universal IR Multi-Frame Analyzer" | |||
); | |||
Serial.println( | |||
"Generic RAW + HEX Decoder" | |||
); | |||
Serial.println("Arduino Mega2560"); | |||
Serial.println("Ready"); | |||
} | |||
// ======================================== | |||
// メインループ | |||
// ======================================== | |||
void loop() { | |||
// 赤外線信号が来るまで待機 | |||
if (digitalRead(IR_PIN) != LOW) { | |||
return; | |||
} | |||
operationCount++; | |||
timingCount = 0; | |||
bool overflow = false; | |||
bool abnormalMark = false; | |||
byte lastLevel = LOW; | |||
uint32_t lastChange = micros(); | |||
// 受信中はSerial出力しない | |||
while (true) { | |||
byte level = digitalRead(IR_PIN); | |||
uint32_t now = micros(); | |||
uint32_t duration = now - lastChange; | |||
// 信号レベルが変化した | |||
if (level != lastLevel) { | |||
if (timingCount < MAX_TIMINGS) { | |||
timings[timingCount++] = duration; | |||
} else { | |||
overflow = true; | |||
break; | |||
} | |||
lastLevel = level; | |||
lastChange = now; | |||
} | |||
// 150ms以上HIGHなら受信終了 | |||
if (lastLevel == HIGH && | |||
(uint32_t)(now - lastChange) >= END_GAP_US) { | |||
break; | |||
} | |||
// LOWが異常に長い場合 | |||
if (lastLevel == LOW && | |||
(uint32_t)(now - lastChange) >= MAX_MARK_US) { | |||
abnormalMark = true; | |||
break; | |||
} | |||
} | |||
// 全データの受信が完了してから出力 | |||
printAllFrames(overflow, abnormalMark); | |||
} | } | ||
</code></pre></div><script></freescript> | </code></pre></div><script></freescript> | ||
2026年10月23日 (金) 18:49時点における最新版
概要
赤外線受信モジュールを接続するレッスンです。
完成図は以下のとおりです。
KiCADで追加した部品
- Conn_01x03:Generic connector, single row, 01x03, script generated (kicad-library-utils/schlib/autogen/connector)
- +5V
- GND
- PWR_FLAG
KiCADで追加したフットプリント
- Conn_01x03:Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical
- Conn_01x03:Connector_PinSocket_2.54mm:PinSocket_1x03_P2.54mm_Vertical
PCB図は以下のようなもの。
3DPreview図は以下のようなもの。
コードは以下のとおりです。
//www.elegoo.com
//2023.05.05
#include "IRremote.h"
int receiver = 11; // Signal Pin of IR receiver to Arduino Digital Pin 11
/*-----( Declare objects )-----*/
IRrecv irrecv(receiver); // create instance of 'irrecv'
//vairable uses to store the last decodedRawData
uint32_t last_decodedRawData = 0;
/*-----( Function )-----*/
void translateIR() // takes action based on IR code received
{
// 受信したプロトコル名を表示
Serial.print("Protocol: ");
Serial.println(getProtocolString(irrecv.decodedIRData.protocol));
// Check if it is a repeat IR code
if (irrecv.decodedIRData.flags)
{
//set the current decodedRawData to the last decodedRawData
irrecv.decodedIRData.decodedRawData = last_decodedRawData;
Serial.println("REPEAT!");
} else
{
//output the IR code on the serial monitor
Serial.print("IR code:0x");
Serial.println(irrecv.decodedIRData.decodedRawData, HEX);
}
//map the IR code to the remote key
switch (irrecv.decodedIRData.decodedRawData)
{
//標準NECプロトコル
case 0xBA45FF00: Serial.println("POWER"); break;
case 0xB847FF00: Serial.println("FUNC/STOP"); break;
case 0xB946FF00: Serial.println("VOL+"); break;
case 0xBB44FF00: Serial.println("FAST BACK"); break;
case 0xBF40FF00: Serial.println("PAUSE"); break;
case 0xBC43FF00: Serial.println("FAST FORWARD"); break;
case 0xF807FF00: Serial.println("DOWN"); break;
case 0xEA15FF00: Serial.println("VOL-"); break;
case 0xF609FF00: Serial.println("UP"); break;
case 0xE619FF00: Serial.println("EQ"); break;
case 0xF20DFF00: Serial.println("ST/REPT"); break;
case 0xE916FF00: Serial.println("0"); break;
case 0xF30CFF00: Serial.println("1"); break;
case 0xE718FF00: Serial.println("2"); break;
case 0xA15EFF00: Serial.println("3"); break;
case 0xF708FF00: Serial.println("4"); break;
case 0xE31CFF00: Serial.println("5"); break;
case 0xA55AFF00: Serial.println("6"); break;
case 0xBD42FF00: Serial.println("7"); break;
case 0xAD52FF00: Serial.println("8"); break;
case 0xB54AFF00: Serial.println("9"); break;
default:
Serial.println(" other button ");
}// End Case
//store the last decodedRawData
last_decodedRawData = irrecv.decodedIRData.decodedRawData;
delay(500); // Do not get immediate repeat
} //END translateIR
void setup() /*----( SETUP: RUNS ONCE )----*/
{
Serial.begin(9600);
Serial.println("IR Receiver Button Decode");
irrecv.enableIRIn(); // Start the receiver
}/*--(end setup )---*/
void loop() /*----( LOOP: RUNS CONSTANTLY )----*/
{
if (irrecv.decode()) // have we received an IR signal?
{
translateIR();
irrecv.resume(); // receive the next value
}
}/* --(end main loop )-- */
いろいろなリモコンコードを取得するのは以下のコード
#define RAW_BUFFER_LENGTH 750
#include <IRremote.hpp>
const int IR_RECEIVE_PIN = 11;
void setup() {
Serial.begin(115200);
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
Serial.println("IR Remote Analyzer");
Serial.println("TV / Recorder / Air Conditioner");
Serial.println("Toshiba / Daikin signal investigation");
}
void loop() {
if (IrReceiver.decode()) {
Serial.println();
Serial.println("============================");
// 受信バッファが足りなかった場合
if (IrReceiver.decodedIRData.flags &
IRDATA_FLAGS_WAS_OVERFLOW) {
Serial.println("WARNING: Buffer overflow!");
}
// ライブラリによるプロトコル解析
IrReceiver.printIRResultShort(&Serial);
// 受信ビット数
Serial.print("Bits: ");
Serial.println(IrReceiver.decodedIRData.numberOfBits);
// リピート判定
if (IrReceiver.decodedIRData.flags &
IRDATA_FLAGS_IS_REPEAT) {
Serial.println("Repeat signal");
}
// 生のパルスデータ
Serial.println("--- Raw Timing ---");
IrReceiver.printIRResultRawFormatted(&Serial, true);
Serial.println("============================");
IrReceiver.resume();
}
}
ライブラリを使わないIR読み取りコードは以下のとおりです。
const byte IR_PIN = 2;
// 許容するパルス幅(マイクロ秒)
const unsigned long TIMEOUT_US = 12000;
// 指定したレベルが続いた時間を測定する
// タイムアウトした場合は0を返す
unsigned long measurePulse(byte level) {
unsigned long start = micros();
while (digitalRead(IR_PIN) == level) {
if (micros() - start > TIMEOUT_US) {
return 0;
}
}
return micros() - start;
}
// NEC信号の読み取り
bool readNEC(uint32_t &data) {
// リーダーMARK(約9000us)
unsigned long mark = measurePulse(LOW);
if (mark < 8000 || mark > 11000) return false;
// リーダーSPACE(約4500us)
unsigned long space = measurePulse(HIGH);
if (space < 3500 || space > 6000) return false;
data = 0;
// NECは32ビット、LSBから送信
for (byte i = 0; i < 32; i++) {
// データMARK(約560us)
mark = measurePulse(LOW);
if (mark < 300 || mark > 900) return false;
// データSPACE
space = measurePulse(HIGH);
if (space >= 300 && space < 1000) {
// ビット0
}
else if (space >= 1200 && space < 2200) {
// ビット1
data |= (uint32_t)1 << i;
}
else {
return false;
}
}
// 最後のMARK(約560us)
mark = measurePulse(LOW);
if (mark < 300 || mark > 900) return false;
return true;
}
void setup() {
pinMode(IR_PIN, INPUT);
Serial.begin(115200);
Serial.println("NEC Receiver Ready");
}
void loop() {
// 通常はHIGH、受信開始でLOWになる
if (digitalRead(IR_PIN) == LOW) {
uint32_t rawData;
if (readNEC(rawData)) {
Serial.print("RAW: 0x");
// 8桁の16進数で表示
for (int i = 7; i >= 0; i--) {
byte nibble = (rawData >> (i * 4)) & 0x0F;
Serial.print(nibble, HEX);
}
Serial.println();
}
}
}
もっと汎用的な生データを取得するコードはこうだな。
const byte IR_PIN = 11;
// ========================================
// 設定
// ========================================
// true : RAW波形を表示する
// false: 復号結果だけ表示する
const bool SHOW_RAW = true;
// true : 0/1のビット列も表示する
const bool SHOW_BINARY = true;
// true : 1バイトを下位ビットから受信
// false: 1バイトを上位ビットから受信
const bool LSB_FIRST = true;
// 無信号150msで受信終了
const uint32_t END_GAP_US = 150000;
// 無信号15ms以上でフレーム分割
const uint32_t FRAME_GAP_US = 15000;
// 異常に長いMARKへの対策
const uint32_t MAX_MARK_US = 500000;
// RAW記録の最大数
const uint16_t MAX_TIMINGS = 1100;
// HEX復号の最大バイト数
const uint16_t MAX_BYTES = 80;
// ビット判定が曖昧になる幅(us)
const uint32_t AMBIGUOUS_MARGIN_US = 100;
// ========================================
// 記録用メモリ
// ========================================
uint32_t timings[MAX_TIMINGS];
uint16_t timingCount = 0;
uint8_t decodedBytes[MAX_BYTES];
unsigned long operationCount = 0;
// ========================================
// 16進数表示
// ========================================
void printHexByte(uint8_t value) {
if (value < 0x10) {
Serial.print('0');
}
Serial.print(value, HEX);
}
// ========================================
// 2進数表示
// ========================================
void printBinaryByte(uint8_t value) {
for (int8_t i = 7; i >= 0; i--) {
Serial.print((value >> i) & 1);
}
}
// ========================================
// RAW波形表示
// ========================================
void printRaw(uint16_t startIndex,
uint16_t endIndex) {
Serial.println();
Serial.println("--- RAW TIMING (us) ---");
uint8_t column = 0;
for (uint16_t i = startIndex;
i < endIndex; i++) {
// 奇数インデックスがSPACE
// 偶数インデックスがMARK
if ((i & 1) == 0) {
Serial.print('+');
} else {
Serial.print('-');
}
Serial.print(timings[i]);
Serial.print(' ');
column++;
if (column >= 8) {
Serial.println();
column = 0;
}
}
Serial.println();
}
// ========================================
// パルス距離方式の解析
// ========================================
void decodeFrame(uint16_t startIndex,
uint16_t endIndex) {
uint16_t rawCount = endIndex - startIndex;
// 単独MARKの場合
if (rawCount == 1) {
Serial.println();
Serial.println("Single MARK / Preamble");
Serial.print("MARK (us): ");
Serial.println(timings[startIndex]);
return;
}
// 想定する構造:
// Header MARK
// Header SPACE
// Data MARK + SPACE を繰り返す
// Stop MARK
if (rawCount < 5 ||
(rawCount & 1) == 0) {
Serial.println();
Serial.println("Unknown frame structure.");
Serial.println("Use RAW timing data.");
return;
}
uint16_t bitCount = (rawCount - 3) / 2;
Serial.println();
Serial.println("--- FRAME INFORMATION ---");
Serial.print("Candidate Bits: ");
Serial.println(bitCount);
Serial.print("Header MARK (us): ");
Serial.println(timings[startIndex]);
Serial.print("Header SPACE (us): ");
Serial.println(timings[startIndex + 1]);
Serial.print("Stop MARK (us): ");
Serial.println(timings[endIndex - 1]);
// ======================================
// SPACEの最小値・最大値を調査
// ======================================
uint32_t minSpace = 0xFFFFFFFFUL;
uint32_t maxSpace = 0;
for (uint16_t i = 0; i < bitCount; i++) {
uint16_t spaceIndex =
startIndex + 3 + i * 2;
uint32_t value = timings[spaceIndex];
if (value < minSpace) {
minSpace = value;
}
if (value > maxSpace) {
maxSpace = value;
}
}
Serial.print("Data SPACE min (us): ");
Serial.println(minSpace);
Serial.print("Data SPACE max (us): ");
Serial.println(maxSpace);
// ======================================
// パルス距離方式か簡易判定
// ======================================
// 最大SPACEが最小SPACEの
// 1.7倍未満なら2値判定を保留
//
// 単一種類のビットしか含まれない
// フレームもここでは復号を保留する。
if (maxSpace <= minSpace ||
(uint64_t)maxSpace * 10 <
(uint64_t)minSpace * 17) {
Serial.println();
Serial.println(
"Pulse-distance decode unavailable."
);
Serial.println(
"Insufficient SPACE separation."
);
Serial.println(
"RAW timing is still available."
);
return;
}
// SPACEの中間値を判定基準にする
uint32_t threshold =
minSpace + (maxSpace - minSpace) / 2;
Serial.print("Auto threshold (us): ");
Serial.println(threshold);
// ======================================
// ビット復号
// ======================================
uint16_t ambiguousCount = 0;
uint16_t byteCount = (bitCount + 7) / 8;
if (byteCount > MAX_BYTES) {
Serial.println(
"ERROR: Decode buffer too small."
);
return;
}
// 復号バッファを初期化
for (uint16_t i = 0; i < byteCount; i++) {
decodedBytes[i] = 0;
}
if (SHOW_BINARY) {
Serial.println();
Serial.println("--- BINARY DATA ---");
Serial.println("(Transmission order)");
}
for (uint16_t i = 0; i < bitCount; i++) {
uint16_t spaceIndex =
startIndex + 3 + i * 2;
uint32_t space = timings[spaceIndex];
uint8_t bitValue =
(space > threshold) ? 1 : 0;
// 判定しきい値に近いビットを数える
uint32_t difference =
(space > threshold)
? space - threshold
: threshold - space;
if (difference <= AMBIGUOUS_MARGIN_US) {
ambiguousCount++;
}
// ====================================
// バイトへ格納
// ====================================
uint16_t byteIndex = i / 8;
uint8_t bitPosition = i % 8;
if (!LSB_FIRST) {
bitPosition = 7 - bitPosition;
}
if (bitValue) {
decodedBytes[byteIndex] |=
(uint8_t)(1U << bitPosition);
}
// ====================================
// 送信順にビットを表示
// ====================================
if (SHOW_BINARY) {
Serial.print(bitValue);
if ((i + 1) % 8 == 0) {
Serial.print(' ');
}
if ((i + 1) % 64 == 0) {
Serial.println();
}
}
}
if (SHOW_BINARY) {
Serial.println();
}
// ======================================
// HEX DATA
// ======================================
Serial.println();
Serial.println("--- HEX DATA ---");
for (uint16_t i = 0; i < byteCount; i++) {
printHexByte(decodedBytes[i]);
Serial.print(' ');
if ((i + 1) % 10 == 0) {
Serial.println();
}
}
Serial.println();
// ======================================
// BYTE TABLE
// ======================================
Serial.println();
Serial.println("--- BYTE TABLE ---");
for (uint16_t i = 0; i < byteCount; i++) {
Serial.print("Byte ");
if (i + 1 < 10) {
Serial.print('0');
}
Serial.print(i + 1);
Serial.print(": 0x");
printHexByte(decodedBytes[i]);
Serial.print(" BIN: ");
printBinaryByte(decodedBytes[i]);
Serial.println();
}
// 8ビット未満の端数
uint8_t remainingBits = bitCount % 8;
if (remainingBits != 0) {
Serial.println();
Serial.print("Partial last byte: ");
Serial.print(remainingBits);
Serial.println(" valid bits");
Serial.println(
"Unused positions are padded with 0."
);
}
// ======================================
// 解析結果
// ======================================
Serial.println();
Serial.println("--- DECODE STATUS ---");
Serial.println("Method: PULSE DISTANCE");
Serial.print("Bit order: ");
if (LSB_FIRST) {
Serial.println("LSB FIRST");
} else {
Serial.println("MSB FIRST");
}
Serial.print("Decoded bits: ");
Serial.println(bitCount);
Serial.print("Decoded bytes: ");
Serial.println(byteCount);
Serial.print("Ambiguous bits: ");
Serial.println(ambiguousCount);
Serial.println(
"Protocol/checksum not identified."
);
}
// ========================================
// 1フレームの表示
// ========================================
void printFrame(uint16_t startIndex,
uint16_t endIndex,
unsigned int frameNo,
uint32_t gapBefore) {
Serial.println();
Serial.println("============================");
Serial.print("Frame No: ");
Serial.println(frameNo);
Serial.print("Gap before (us): ");
Serial.println(gapBefore);
Serial.print("Raw Count: ");
Serial.println(endIndex - startIndex);
// フレーム合計時間
uint32_t total = 0;
for (uint16_t i = startIndex;
i < endIndex; i++) {
total += timings[i];
}
Serial.print("Sum (us): ");
Serial.println(total);
// 生波形
if (SHOW_RAW) {
printRaw(startIndex, endIndex);
}
// 復号
decodeFrame(startIndex, endIndex);
}
// ========================================
// 全フレームを表示
// ========================================
void printAllFrames(bool overflow,
bool abnormalMark) {
Serial.println();
Serial.println("############################");
Serial.print("Operation No: ");
Serial.println(operationCount);
Serial.print("Total timings: ");
Serial.println(timingCount);
if (overflow) {
Serial.println(
"WARNING: BUFFER OVERFLOW!"
);
}
if (abnormalMark) {
Serial.println(
"WARNING: Abnormally long MARK!"
);
}
uint16_t frameStart = 0;
uint32_t gapBefore = 0;
unsigned int frameNo = 1;
for (uint16_t i = 0; i < timingCount; i++) {
// 偶数がMARK、奇数がSPACE
// 15ms以上のSPACEで分割
if ((i & 1) == 1 &&
timings[i] >= FRAME_GAP_US) {
if (i > frameStart) {
printFrame(
frameStart,
i,
frameNo,
gapBefore
);
frameNo++;
}
gapBefore = timings[i];
frameStart = i + 1;
}
}
// 最終フレーム
if (frameStart < timingCount) {
printFrame(
frameStart,
timingCount,
frameNo,
gapBefore
);
}
Serial.println();
Serial.println(
"######## END OPERATION ########"
);
}
// ========================================
// 初期化
// ========================================
void setup() {
pinMode(IR_PIN, INPUT);
Serial.begin(115200);
Serial.println(
"Universal IR Multi-Frame Analyzer"
);
Serial.println(
"Generic RAW + HEX Decoder"
);
Serial.println("Arduino Mega2560");
Serial.println("Ready");
}
// ========================================
// メインループ
// ========================================
void loop() {
// 赤外線信号が来るまで待機
if (digitalRead(IR_PIN) != LOW) {
return;
}
operationCount++;
timingCount = 0;
bool overflow = false;
bool abnormalMark = false;
byte lastLevel = LOW;
uint32_t lastChange = micros();
// 受信中はSerial出力しない
while (true) {
byte level = digitalRead(IR_PIN);
uint32_t now = micros();
uint32_t duration = now - lastChange;
// 信号レベルが変化した
if (level != lastLevel) {
if (timingCount < MAX_TIMINGS) {
timings[timingCount++] = duration;
} else {
overflow = true;
break;
}
lastLevel = level;
lastChange = now;
}
// 150ms以上HIGHなら受信終了
if (lastLevel == HIGH &&
(uint32_t)(now - lastChange) >= END_GAP_US) {
break;
}
// LOWが異常に長い場合
if (lastLevel == LOW &&
(uint32_t)(now - lastChange) >= MAX_MARK_US) {
abnormalMark = true;
break;
}
}
// 全データの受信が完了してから出力
printAllFrames(overflow, abnormalMark);
}