「Arduino Mega 2560 LESSON:14 赤外線受信モジュール」の版間の差分

提供:yonewiki
編集の要約なし
編集の要約なし
 
(同じ利用者による、間の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時点における最新版

Arduino Mega 2560に戻る。

概要

 赤外線受信モジュールを接続するレッスンです。


 完成図は以下のとおりです。


完成図


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図は以下のようなもの。


PCB図


 3DPreview図は以下のようなもの。


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

 

Arduino Mega 2560に戻る。