#include "IR_DecoderRaw.h" #include "IR_Encoder.h" #include #include IR_DecoderRaw::IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPair) : encoder(encPair) { setPin(pin); id = addr; prevRise = prevFall = prevPrevFall = prevPrevRise = 0; if (encPair != nullptr) { encPair->decPair = this; } #ifdef IRDEBUG pinMode(wrHigh, OUTPUT); pinMode(wrLow, OUTPUT); pinMode(writeOp, OUTPUT); pinMode(errOut, OUTPUT); pinMode(up, OUTPUT); pinMode(down, OUTPUT); #endif } bool IR_DecoderRaw::isSubOverflow() { noInterrupts(); volatile bool ret = isSubBufferOverflow; interrupts(); return ret; } bool IR_DecoderRaw::availableRaw() { if (isAvailable) { isAvailable = false; return true; } else { return false; } }; //////////////////////////////////// isr /////////////////////////////////////////// volatile uint32_t time_; void IR_DecoderRaw::isr() { noInterrupts(); time_ = micros(); interrupts(); if (time_ < oldTime) { time_ += 1000; } oldTime = time_; FrontStorage edge; edge.dir = port->IDR & mask; edge.time = time_; #if defined(IR_EDGE_TRACE) edgeTracePush(edge.time, edge.dir ? 1u : 0u, isPairSending ? (uint8_t)IR_EDGE_TRACE_F_SKIP_DECODE : 0u); #endif if (isPairSending) { return; } subBuffer.push(edge); } //////////////////////////////////////////////////////////////////////////////////// void IR_DecoderRaw::firstRX() { #if defined(IRDEBUG_SERIAL_PACK) packTraceResetFrame(); #endif errors.reset(); packSize = 0; isBufferOverflow = false; isAvailable = false; bufBitPos = 0; isData = true; i_dataBuffer = 0; nextControlBit = bitPerByte; i_syncBit = 0; isWrongPack = false; isPreamb = true; riseSyncTime = bitTime /* 1100 */; #ifdef IRDEBUG wrCounter = 0; #endif memset(dataBuffer, 0x00, dataByteSizeMax); } void IR_DecoderRaw::listenStart() { if (isReciveRaw && ((micros() - prevRise) > IR_timeout * 2)) { #if defined(IRDEBUG_SERIAL_PACK) packTraceOnTimeoutOrAbort(true); #endif isReciveRaw = false; firstRX(); } } // ---- быстрая проверка конца пакета --------------------------------- inline void IR_DecoderRaw::checkTimeout() { if (!isRecive) return; // уже не принимаем – нечего проверять if (micros() - lastEdgeTime > IR_timeout * 2U) { #if defined(IRDEBUG_SERIAL_PACK) packTraceOnTimeoutOrAbort(false); #endif isRecive = false; // приём завершён msgTypeReceive = 0; // firstRX(); // подготовка к новому пакету lastEdgeTime = micros(); // защита от повторного срабатывания } } // ==================================================================== void IR_DecoderRaw::tick() { // FrontStorage *currentFrontPtr; // noInterrupts(); // currentFrontPtr = subBuffer.pop(); // interrupts(); FrontStorage currentFront; noInterrupts(); listenStart(); FrontStorage *currentFrontPtr; currentFrontPtr = subBuffer.pop(); if (currentFrontPtr == nullptr) { isSubBufferOverflow = false; checkTimeout(); // <--- новое место проверки interrupts(); #if defined(IR_EDGE_TRACE) while (edgeTraceFlushChunk(Serial, 48) > 0) {} #endif return; } // Если данных нет - ничего не делаем currentFront = *currentFrontPtr; interrupts(); // ---------- буфер пуст: фронтов нет, проверяем тайм-аут ---------- // if (currentFrontPtr == nullptr) // { // isSubBufferOverflow = false; // return; // } // // ---------- есть фронт: продолжаем обработку ---------- // FrontStorage currentFront = *currentFrontPtr; lastEdgeTime = currentFront.time; // запоминаем любой фронт //////////////////////////////////////////////////////////////////////////////////////////////////////////// if (currentFront.dir) { // Если __/``` ↑ if (currentFront.time - prevRise > riseTimeMax / 4 || highCount || lowCount) { // комплексный фикс рваной единицы risePeriod = currentFront.time - prevRise; highTime = currentFront.time - prevFall; lowTime = prevFall - prevRise; prevRise = currentFront.time; if ( risePeriod > UINT32_MAX - IR_timeout * 10 || highTime > UINT32_MAX - IR_timeout * 10 || lowTime > UINT32_MAX - IR_timeout * 10 || prevRise > UINT32_MAX - IR_timeout * 10) { #ifdef IRDEBUG errPulse(down, 50); // Serial.print("\n"); // Serial.print("risePeriod: "); // Serial.println(risePeriod); // Serial.print("highTime: "); // Serial.println(highTime); // Serial.print("lowTime: "); // Serial.println(lowTime); // Serial.print("prevRise: "); // Serial.println(prevRise); #endif } } else { errors.other++; } } else { // Если ```\__ ↓ if (currentFront.time - prevFall > riseTimeMin / 4) { prevFall = currentFront.time; } else { errors.other++; } } #ifdef IRDEBUG // goto END; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #endif //---------------------------------------------------------------------------------- #ifdef IRDEBUG digitalWrite(errOut, currentFront.dir); #endif if (currentFront.time > prevRise && currentFront.time - prevRise > IR_timeout * 2 && !isReciveRaw) { // первый #ifdef IRDEBUG errPulse(up, 50); errPulse(down, 50); errPulse(up, 150); errPulse(down, 150); #endif preambFrontCounter = preambFronts - 1U; isPreamb = true; isRecive = true; isReciveRaw = true; isWrongPack = false; #if defined(IRDEBUG_SERIAL_PACK) packTraceResetFrame(); packTraceOpen = true; #endif } //------------------------------------------------------------------------------------------------------- if (preambFrontCounter) { // в преамбуле #ifdef IRDEBUG // Serial.print("risePeriod: "); // Serial.println(risePeriod); #endif if (currentFront.dir && risePeriod < IR_timeout) { // __/``` ↑ и мы в внутри пакета if (risePeriod < riseTimeMin / 2) { // fix рваной единицы preambFrontCounter += 2; errors.other++; #ifdef IRDEBUG errPulse(down, 350); #endif } else { if (freeFrec) { riseSyncTime = (riseSyncTime + risePeriod / 2) / 2; } // tuner } } else { /* riseSyncTime = bitTime; */ } // сброс тюнера preambFrontCounter--; // Serial.print("preambFrontCounter: "); Serial.println(preambFrontCounter); } else { if (isPreamb) { // первый фронт после // gotTune.set(riseSyncTime); isPreamb = false; #ifdef IRDEBUG errPulse(up, 50); errPulse(down, 50); #endif prevRise += risePeriod / 2; // prevRise = currentFront.time + riseTime; goto END; } } if (isPreamb) { goto END; } if (risePeriod > IR_timeout || isBufferOverflow || risePeriod < riseTimeMin || isWrongPack) // ~Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц { goto END; } // определить направление фронта if (currentFront.dir) { // Если __/``` ↑ highCount = 0; lowCount = 0; allCount = 0; bool invertErr = false; // #ifdef IRDEBUG // Serial.print("\n"); // Serial.print("wrCounter: "); // Serial.println(wrCounter++); // Serial.print("risePeriod: "); // Serial.println(risePeriod); // Serial.print("highTime: "); // Serial.println(highTime); // Serial.print("lowTime: "); // Serial.println(lowTime); // #endif if (aroundRise(risePeriod)) { // тактирование есть, сигнал хороший - без ошибок(?) if (highTime > lowTime) { // 1 #ifdef IRDEBUG errPulse(wrHigh, 1); #endif writeToBuffer(HIGH); } else { // 0 #ifdef IRDEBUG errPulse(wrLow, 1); #endif writeToBuffer(LOW); } } else { // пропущены такты! сигнал средний // ошибка пропуска highCount = ceil_div(highTime, riseTime); // предполагаемое колличество HIGH битов lowCount = ceil_div(lowTime, riseTime); // предполагаемое колличество LOW битов allCount = ceil_div(risePeriod, riseTime); // предполагаемое колличество всего битов if (highCount == 0 && highTime > riseTime / 3) { // fix короткой единицы (?)после пропуска нулей(?) highCount++; errors.other++; #ifdef IRDEBUG errPulse(up, 50); #endif } if (lowCount + highCount > allCount) { // fix ошибочных сдвигов if (lowCount > highCount) { // Лишние нули lowCount = allCount - highCount; errors.lowSignal += lowCount; #ifdef IRDEBUG // errPulse(errOut, 3); errPulse(down, 40); errPulse(up, 10); errPulse(down, 40); #endif } else if (lowCount < highCount) { // Лишние единицы highCount = allCount - lowCount; errors.highSignal += highCount; #ifdef IRDEBUG errPulse(down, 10); errPulse(up, 40); errPulse(down, 10); // errPulse(errOut, 4); #endif // неизвестный случай Инверсит след бит или соседние // Очень редко // TODO: Отловить проверить } else if (lowCount == highCount) { #ifdef IRDEBUG errPulse(down, 40); errPulse(up, 40); errPulse(down, 40); #endif invertErr = true; // Serial.print("..."); errors.other += allCount; } // errorCounter += allCount; } // errorCounter += allCount; // errors.other+=allCount; if (lowCount < highCount) { errors.highSignal += highCount; } else { errors.lowSignal += lowCount; } // errPulse(errOut, 1); for (int8_t i = 0; i < lowCount && 8 - i; i++) { // отправка LOW битов, если есть if (i == lowCount - 1 && invertErr) { invertErr = false; writeToBuffer(HIGH, true); #ifdef IRDEBUG errPulse(wrHigh, 1); #endif } else { writeToBuffer(LOW); #ifdef IRDEBUG errPulse(wrLow, 1); #endif } } for (int8_t i = 0; i < highCount && 8 - i; i++) { // отправка HIGH битов, если есть if (i == highCount - 1 && invertErr) { invertErr = false; writeToBuffer(LOW, true); #ifdef IRDEBUG errPulse(wrLow, 1); #endif } else { writeToBuffer(HIGH); #ifdef IRDEBUG errPulse(wrHigh, 1); #endif } } } } else { // Если ```\__ ↓ } //////////////////////////////////////////////////////////////////////////////////////////////////////////// END:; #if defined(IR_EDGE_TRACE) while (edgeTraceFlushChunk(Serial, 48) > 0) {} #endif } void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix) { #if !defined(IRDEBUG_SERIAL_PACK) (void)packTraceInvertFix; #endif if (i_dataBuffer > dataByteSizeMax * 8) { // проверка переполнения isBufferOverflow = true; #if defined(IRDEBUG_SERIAL_PACK) if (packTraceOpen) packTraceEmitErrorFlash(F("ERROR: buffer overflow")); #endif } if (isBufferOverflow || isPreamb || isWrongPack) { isRecive = false; isReciveRaw = false; msgTypeReceive = 0; return; } // Переключение флага, data или syncBit if (bufBitPos == nextControlBit) { nextControlBit += (isData ? syncBits : bitPerByte); // маркер следующего переключения isData = !isData; i_syncBit = 0; // сброс счетчика битов синхронизации err_syncBit = 0; // сброс счетчика ошибок синхронизации } if (isData) { // Запись битов в dataBuffer dataBuffer[(i_dataBuffer / 8)] |= bit << (7 - i_dataBuffer % 8); // Запись в буффер i_dataBuffer++; bufBitPos++; #if defined(IRDEBUG_SERIAL_PACK) if (packTraceInvertFix) { packTracePushChar('`'); packTracePushChar(bit ? '1' : '0'); packTracePushChar('`'); } else packTracePushBit(bit); #endif } else { //********************************* Проверка контрольных sync битов *******************************// ////////////////////// Исправление лишнего нуля /////////////////////// if (i_syncBit == 0) { // Первый бит синхронизации if (bit != (dataBuffer[((i_dataBuffer - 1) / 8)] >> (7 - (i_dataBuffer - 1) % 8) & 1)) { bufBitPos++; i_syncBit++; #if defined(IRDEBUG_SERIAL_PACK) packTracePushBit(bit); #endif } else { i_syncBit = 0; errors.other++; err_syncBit++; const bool fatalSync = (err_syncBit >= syncBits); if (fatalSync) { #if defined(IRDEBUG_SERIAL_PACK) && defined(IRDEBUG_SERIAL_SOFT_REJECT) if (packTraceSoftReject()) { packTraceHadWrongSync = true; packTraceForceEndSyncPhase(); err_syncBit = 0; i_syncBit = 0; isWrongPack = false; } else #endif { isWrongPack = true; #if defined(IRDEBUG_SERIAL_PACK) packTraceEmitErrorFlash(F("ERROR: Wrong sync bit")); #endif } } } } else { // Последующие биты синхронизации bufBitPos++; i_syncBit++; #if defined(IRDEBUG_SERIAL_PACK) packTracePushBit(bit); #endif } isWrongPack = (err_syncBit >= syncBits); } //**************************************************************************************************// #ifdef IRDEBUG bit ? infoPulse(writeOp, 2) : infoPulse(writeOp, 1); #endif if (!isAvailable && isData && !isWrongPack) { if (i_dataBuffer == 8 * msgBytes) { // Ппервый байт packSize = dataBuffer[0] & IR_MASK_MSG_INFO; } // Тип приёма (для isReceive): выставляем сразу после первого байта, ДО проверки «Конец». // Иначе при packSize==1 один и тот же шаг i_dataBuffer==8 одновременно «закрывает» кадр (msgTypeReceive=0) // и снова выставляет msgTypeReceive ниже — флаг залипает, пока не придёт ошибка/другой кадр. if (packSize && (i_dataBuffer == 8)) { msgTypeReceive = (dataBuffer[0] >> 5) | 0b11111000; } if (packSize && (i_dataBuffer == packSize * bitPerByte)) { // Конец packInfo.buffer = dataBuffer; packInfo.crc = crcValue; packInfo.err = errors; packInfo.packSize = packSize; packInfo.rTime = riseSyncTime; isRecive = false; isReciveRaw = false; msgTypeReceive = 0; isAvailable = crcCheck(packSize - crcBytes, crcValue); #ifdef BRUTEFORCE_CHECK { uint16_t packTraceBfByte = 0; uint8_t packTraceBfBit = 0; bool packTraceBfMark = false; if (!isAvailable) // Исправление первого бита // Очень большая затычка... for (size_t i = 0; i < min(uint16_t(packSize - crcBytes * 2U), uint16_t(dataByteSizeMax)); ++i) { for (int j = 0; j < 8; ++j) { // инвертируем бит dataBuffer[i] ^= 1 << j; isAvailable = crcCheck(min(uint16_t(packSize - crcBytes), uint16_t(dataByteSizeMax - 1U)), crcValue); // обратно инвертируем бит в исходное состояние if (isAvailable) { packTraceBfByte = static_cast(i); packTraceBfBit = static_cast(j); packTraceBfMark = true; goto OUT_BRUTEFORCE; } else { dataBuffer[i] ^= 1 << j; } } } OUT_BRUTEFORCE: #if defined(IRDEBUG_SERIAL_PACK) if (packTraceBfMark) packTraceWrapDataBitInBackticks(packTraceBfByte, packTraceBfBit); #endif } #endif #if defined(IRDEBUG_SERIAL_PACK) if (isAvailable) packTraceEmitEndOk(static_cast(packSize)); else packTraceEmitEndBadCrc(static_cast(packSize)); #endif if (!isAvailable && packSize > 0 && packSize <= dataByteSizeMax) { memcpy(rejectBuffer, dataBuffer, packSize); rejectPackSize = static_cast(packSize); isRejectAvailable = true; } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } bool IR_DecoderRaw::crcCheck(uint8_t len, crc_t &crc) { bool crcOK = false; crc = 0; crc = (crc8(dataBuffer, 0, len, poly1) << 8) & ~((crc_t)0xFF); crc |= crc8(dataBuffer, 0, len + 1, poly2) & (crc_t)0xFF; if (dataBuffer[len] == (crc >> 8) & 0xFF && dataBuffer[len + 1] == (crc & 0xFF)) { crcOK = true; } else { crcOK = false; } return crcOK; } bool IR_DecoderRaw::availableReject() { if (!isRejectAvailable) return false; isRejectAvailable = false; return true; } uint16_t IR_DecoderRaw::ceil_div(uint16_t val, uint16_t divider) { int ret = val / divider; if ((val << 4) / divider - (ret << 4) >= 8) ret++; return ret; } #if defined(IR_EDGE_TRACE) void IR_DecoderRaw::edgeTracePush(uint32_t t_us, uint8_t level, uint8_t flags) { const uint16_t cap = static_cast(IR_EDGE_TRACE_CAPACITY); noInterrupts(); const uint16_t w = edgeTrace_w; const uint16_t r = edgeTrace_r; const uint16_t next = static_cast((w + 1u) % cap); if (next == r) { edgeTrace_overflow = true; interrupts(); return; } edgeTrace_buf[w].t_us = t_us; edgeTrace_buf[w].level = level ? 1u : 0u; edgeTrace_buf[w].flags = flags; edgeTrace_w = next; interrupts(); } void IR_DecoderRaw::edgeTraceClear() { noInterrupts(); edgeTrace_w = 0; edgeTrace_r = 0; edgeTrace_overflow = false; interrupts(); } uint16_t IR_DecoderRaw::edgeTracePendingCount() const { noInterrupts(); const uint16_t w = edgeTrace_w; const uint16_t r = edgeTrace_r; interrupts(); const uint16_t cap = static_cast(IR_EDGE_TRACE_CAPACITY); if (w >= r) return static_cast(w - r); return static_cast(cap - r + w); } uint16_t IR_DecoderRaw::edgeTraceFlushChunk(Print &out, uint16_t maxRec) { if (maxRec == 0) maxRec = 48; constexpr uint16_t kStackCap = 64; if (maxRec > kStackCap) maxRec = kStackCap; const uint16_t cap = static_cast(IR_EDGE_TRACE_CAPACITY); noInterrupts(); const uint16_t w = edgeTrace_w; const uint16_t r = edgeTrace_r; uint16_t avail = (w >= r) ? static_cast(w - r) : static_cast(cap - r + w); uint16_t toCopy = (avail > maxRec) ? maxRec : avail; const bool truncated = (avail > toCopy); if (toCopy == 0) { interrupts(); return 0; } uint8_t tmp[kStackCap * 6]; for (uint16_t i = 0; i < toCopy; ++i) { const uint16_t idx = static_cast((r + i) % cap); memcpy(tmp + i * 6u, &edgeTrace_buf[idx], 6u); } edgeTrace_r = static_cast((r + toCopy) % cap); const bool ovf = edgeTrace_overflow; interrupts(); uint8_t meta = 0; if (ovf) meta |= 0x01u; if (truncated) meta |= 0x02u; uint8_t line[3 + kStackCap * 6]; line[0] = meta; line[1] = static_cast(toCopy & 0xFFu); line[2] = static_cast((toCopy >> 8) & 0xFFu); memcpy(line + 3, tmp, toCopy * 6u); out.print(F("\n@IRF1v1:")); static const char hd[] = "0123456789abcdef"; const uint16_t lineLen = static_cast(3u + toCopy * 6u); for (uint16_t i = 0; i < lineLen; ++i) { const uint8_t b = line[i]; out.write(hd[b >> 4]); out.write(hd[b & 0x0Fu]); } out.write('\n'); return toCopy; } #endif // IR_EDGE_TRACE #if defined(IRDEBUG_SERIAL_PACK) struct IrPackTraceSeg { uint8_t isSync; uint8_t nbits; // число логических бит (данные) или символов синхры uint8_t nchars; // сырых символов в b (данные: 8…24 из‑за `0`/`1`) char b[24]; }; namespace { void ptPrintHexU8(uint8_t v) { static const char hd[] = "0123456789ABCDEF"; Serial.print(hd[v >> 4]); Serial.print(hd[v & 0x0Fu]); } /** Тройной пробел перед началом блока: msg→from, from→to, to→data, data→CRC. */ static bool ptRawLeadTriple(uint8_t byteIndex, uint8_t framePs) { if (byteIndex == 1 || byteIndex == 3) return true; if (framePs > 7 && byteIndex == 5) return true; if (framePs >= 7 && byteIndex == static_cast(framePs - 2)) return true; return false; } /** В IR raw: только 0/1 из flex-сегмента (без `), takeBits логических бит после skipLogical. */ static void ptEmitRawFlexSliceSerial(const char *s, uint8_t nbytes, uint8_t skipLogical, uint8_t takeBits) { size_t i = 0; uint8_t logical = 0; uint8_t emitted = 0; while (i < nbytes && emitted < takeBits) { if (s[i] == '`' && i + 2u < nbytes && (s[i + 1] == '0' || s[i + 1] == '1') && s[i + 2] == '`') { if (logical >= skipLogical) { Serial.print(s[i + 1]); ++emitted; } i += 3; ++logical; } else if (s[i] == '0' || s[i] == '1') { if (logical >= skipLogical) { Serial.print(s[i]); ++emitted; } ++i; ++logical; } else break; } } static bool ptTryConsumeFlexDataBit(const char *buf, uint16_t len, uint16_t &pos, IrPackTraceSeg &d) { if (pos + 2 < len && buf[pos] == '`' && (buf[pos + 1] == '0' || buf[pos + 1] == '1') && buf[pos + 2] == '`') { if (d.nchars + 3u > sizeof(d.b)) return false; d.b[d.nchars++] = '`'; d.b[d.nchars++] = buf[pos + 1]; d.b[d.nchars++] = '`'; pos = static_cast(pos + 3u); return true; } if (pos < len && (buf[pos] == '0' || buf[pos] == '1')) { if (d.nchars + 1u > sizeof(d.b)) return false; d.b[d.nchars++] = buf[pos++]; return true; } return false; } } // namespace void IR_DecoderRaw::packTraceResetFrame() { packTraceOpen = false; packTraceHadWrongSync = false; packTraceLen = 0; packTraceBitBuf[0] = '\0'; } void IR_DecoderRaw::packTracePushChar(char c) { if (packTraceLen + 1u < kPackTraceBufCap) { packTraceBitBuf[packTraceLen++] = c; packTraceBitBuf[packTraceLen] = '\0'; } } void IR_DecoderRaw::packTracePushBit(bool bit) { packTracePushChar(bit ? '1' : '0'); } void IR_DecoderRaw::packTraceWrapDataBitInBackticks(uint16_t byteIndex, uint8_t bitInByte) { const uint32_t target = uint32_t(byteIndex) * 8u + bitInByte; uint32_t dbit = 0; uint16_t pos = 0; bool inData = true; uint16_t len = packTraceLen; if (len >= kPackTraceBufCap) len = kPackTraceBufCap - 1u; while (pos < len) { if (inData) { uint8_t bitLen = 0; if (pos + 2 < len && packTraceBitBuf[pos] == '`' && packTraceBitBuf[pos + 2] == '`' && (packTraceBitBuf[pos + 1] == '0' || packTraceBitBuf[pos + 1] == '1')) bitLen = 3; else if (packTraceBitBuf[pos] == '0' || packTraceBitBuf[pos] == '1') bitLen = 1; else return; if (dbit == target) { if (bitLen == 3) return; if (packTraceLen + 2u >= kPackTraceBufCap) return; const uint8_t finalBit = static_cast((dataBuffer[byteIndex] >> (7u - bitInByte)) & 1u); const char ch = finalBit ? '1' : '0'; memmove(packTraceBitBuf + pos + 2, packTraceBitBuf + pos, packTraceLen - pos); packTraceBitBuf[pos] = '`'; packTraceBitBuf[pos + 1] = ch; packTraceBitBuf[pos + 2] = '`'; packTraceLen += 2; if (packTraceLen < kPackTraceBufCap) packTraceBitBuf[packTraceLen] = '\0'; return; } ++dbit; pos = static_cast(pos + bitLen); if ((dbit % 8u) == 0u) inData = false; } else { uint8_t sc = 0; while (pos < len && sc < syncBits) { const char c = packTraceBitBuf[pos]; if (c == '0' || c == '1' || c == '?') { ++pos; ++sc; } else break; } inData = true; } } } bool IR_DecoderRaw::packTraceSoftReject() const { #if defined(IRDEBUG_SERIAL_SOFT_REJECT) return true; #else return false; #endif } void IR_DecoderRaw::packTraceForceEndSyncPhase() { for (uint8_t i = 0; i < syncBits; i++) packTracePushChar('?'); const uint8_t cycLen = bitPerByte + syncBits; const uint16_t cyc = uint16_t(bufBitPos / cycLen); bufBitPos = uint16_t((cyc + 1u) * cycLen); if (bufBitPos == nextControlBit) { nextControlBit += (isData ? syncBits : bitPerByte); isData = !isData; i_syncBit = 0; err_syncBit = 0; } } void IR_DecoderRaw::packTraceEmitHex(uint8_t byteCount) const { Serial.print(F("IR hex:")); for (uint8_t i = 0; i < byteCount && i < dataByteSizeMax; i++) { Serial.print(' '); ptPrintHexU8(dataBuffer[i]); } Serial.println(); } void IR_DecoderRaw::packTraceEmitRawBitsLine(bool endWithNewline) const { Serial.print(F("IR raw: ")); uint16_t len = packTraceLen; if (len >= kPackTraceBufCap) len = kPackTraceBufCap - 1u; const char *buf = packTraceBitBuf; uint16_t pos = 0; uint8_t byteIndex = 0; bool firstSeg = true; const uint8_t framePs = (i_dataBuffer >= 8) ? static_cast(dataBuffer[0] & IR_MASK_MSG_INFO) : 0; while (pos < len) { IrPackTraceSeg d{}; d.isSync = 0; while (pos < len && d.nbits < 8) { const uint16_t posBefore = pos; if (!ptTryConsumeFlexDataBit(buf, len, pos, d)) break; if (pos == posBefore) break; ++d.nbits; } if (!d.nbits) break; if (!firstSeg) { if (ptRawLeadTriple(byteIndex, framePs)) Serial.print(F(" ")); else Serial.print(' '); } firstSeg = false; if (d.nbits < 8) { ptEmitRawFlexSliceSerial(d.b, d.nchars, 0, d.nbits); break; } if (byteIndex == 0u) { ptEmitRawFlexSliceSerial(d.b, d.nchars, 0, 3); Serial.print(' '); ptEmitRawFlexSliceSerial(d.b, d.nchars, 3, 5); } else ptEmitRawFlexSliceSerial(d.b, d.nchars, 0, 8); ++byteIndex; if (pos >= len) break; IrPackTraceSeg s{}; s.isSync = 1; while (pos < len && s.nbits < syncBits) { const char c = buf[pos]; if (c != '0' && c != '1' && c != '?') break; if (s.nchars >= sizeof(s.b)) break; s.b[s.nchars++] = c; ++s.nbits; ++pos; } if (s.nbits) { Serial.print(' '); for (uint8_t i = 0; i < s.nbits; ++i) Serial.print(s.b[i]); } } if (endWithNewline) Serial.println(); } void IR_DecoderRaw::packTraceEmitErrorFlash(const __FlashStringHelper *msg) { Serial.println(); packTraceEmitRawBitsLine(false); Serial.print(F(" => ")); Serial.println(msg); { uint16_t nb = i_dataBuffer / 8u; if (nb > dataByteSizeMax) nb = dataByteSizeMax; packTraceEmitHex(static_cast(nb)); } packTraceResetFrame(); } void IR_DecoderRaw::packTraceEmitEndOk(uint8_t packSize) { Serial.println(); packTraceEmitRawBitsLine(false); Serial.print(F(" => OK: ")); irPackTracePrintOkCommand(dataBuffer, packSize); Serial.println(); packTraceEmitHex(packSize); packTraceResetFrame(); } void IR_DecoderRaw::packTraceEmitEndBadCrc(uint8_t packSize) { Serial.println(); packTraceEmitRawBitsLine(false); Serial.println(F(" => ERROR: Wrong CRC")); packTraceEmitHex(packSize); packTraceResetFrame(); } void IR_DecoderRaw::packTraceOnTimeoutOrAbort(bool fromListenStart) { (void)fromListenStart; if (!packTraceOpen) return; const uint16_t expected = (i_dataBuffer >= 8) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0; uint16_t gotBytes = i_dataBuffer / 8; if (gotBytes > dataByteSizeMax) gotBytes = dataByteSizeMax; Serial.println(); packTraceEmitRawBitsLine(false); Serial.print(F(" => ERROR: TIMEOUT, rx_data_size = ")); Serial.print(expected); Serial.print(F(", but only ")); Serial.print(gotBytes); Serial.println(F(" bytes received")); packTraceEmitHex(static_cast(gotBytes)); packTraceResetFrame(); } __attribute__((weak)) void irPackTracePrintOkCommand(const uint8_t *buf, uint8_t packSize) { (void)buf; (void)packSize; } #endif // IRDEBUG_SERIAL_PACK // IRDEBUG FUNC #ifdef IRDEBUG inline void IR_DecoderRaw::errPulse(uint8_t pin, uint8_t count) { for (size_t i = 0; i < count; i++) { digitalWrite(pin, 1); digitalWrite(pin, 0); } digitalWrite(pin, 0); } inline void IR_DecoderRaw::infoPulse(uint8_t pin, uint8_t count) { for (size_t i = 0; i < count; i++) { digitalWrite(pin, 1); digitalWrite(pin, 0); } } #endif