[Build] Add compatibility with C++20 (#2040)

* [Build] Add compatibility with C++20

* [Build] ESP32 builds both IDF 4.x and IDF 5.x

* [Build] C++20 Use typedefs to switch compilation (volatile vs atomic where needed)

* [Linter] Fix complaint about spaces :-|

* [Linter] Fix more complaints about spaces :-|

* [Build] esp32dev build restore standard espressif32 platform

* [Build] C++20 Adjust typedefs and code to avoid deprecation warnings

* [Build] esp32devIDF5x build restore Arduino 2.0.14 platform

* [Build] esp32devIDF5x build enable Arduino 3.0.0 alpha1 platform by switching interrupts off/on instead of (missing) low-level IDF calls

* [Build] Remove unneeded duplicate typedefs

* [Linter] Fix complaint about comment line-length :-|

* [Build] Add correct include to get `gpio_intr_enable` and `gpio_intr_disable`, thnx Jason2866!

* Add comment why code was changed

* [Lib] Add latest ESP32 fixes by @s-hadinger, add extra NULL safeguard, update Platform build

* [IDF5] Update platform package to avoid python script issues long solved
This commit is contained in:
Ton Huisman
2025-12-16 21:48:58 +01:00
committed by GitHub
parent f66374d087
commit 95b87cccd2
6 changed files with 53 additions and 31 deletions

View File

@@ -443,7 +443,7 @@ char Hostname[kHostnameLength + 1] = "ir_server"; // Default hostname.
uint16_t *codeArray;
uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number
bool boot = true;
volatile bool lockIr = false; // Primitive locking for gating the IR LED.
atomic_bool lockIr = false; // Primitive locking for gating the IR LED.
uint32_t sendReqCounter = 0;
bool lastSendSucceeded = false; // Store the success status of the last send.
uint32_t lastSendTime = 0;

View File

@@ -18,5 +18,10 @@ board = nodemcuv2
board = d1_mini
[env:esp32dev]
platform = espressif32
platform = espressif32 @ ^6.4.0
board = esp32dev
# Experimental IDF 5.x support
[env:esp32devIDF5x]
platform = https://github.com/tasmota/platform-espressif32/releases/download/2025.12.30/platform-espressif32.zip
board = esp32dev

View File

@@ -21,6 +21,12 @@ extern "C" {
#include "IRremoteESP8266.h"
#include "IRutils.h"
#if defined(ESP32)
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
#include <driver/gpio.h>
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif
#ifdef UNIT_TEST
#undef ICACHE_RAM_ATTR
#define ICACHE_RAM_ATTR
@@ -148,7 +154,7 @@ namespace _IRrecv { // Namespace extension
#if defined(ESP32)
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
#endif // ESP32
volatile irparams_t params;
atomic_irparams_t params;
irparams_t *params_save; // A copy of the interrupt state while decoding.
} // namespace _IRrecv
@@ -219,7 +225,7 @@ static void USE_IRAM_ATTR gpio_intr() {
else
params.rawbuf[rawlen] = (now - start) / kRawTick;
}
params.rawlen++;
params.rawlen = params.rawlen + 1; // C++20 fix
start = now;
@@ -342,9 +348,6 @@ IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize,
/// timers or interrupts used.
IRrecv::~IRrecv(void) {
disableIRIn();
#if defined(ESP32)
if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer.
#endif // ESP32
delete[] params.rawbuf;
if (params_save != NULL) {
delete[] params_save->rawbuf;
@@ -425,6 +428,7 @@ void IRrecv::disableIRIn(void) {
timerAlarmDisable(timer);
timerDetachInterrupt(timer);
timerEnd(timer);
timer = NULL; // Cleanup the ESP32 timeout timer.
#endif // ESP32
detachInterrupt(params.recvpin);
#endif // UNIT_TEST
@@ -467,7 +471,7 @@ void IRrecv::resume(void) {
/// i.e. In kStopState.
/// @param[in] src Pointer to an irparams_t structure to copy from.
/// @param[out] dst Pointer to an irparams_t structure to copy to.
void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) {
void IRrecv::copyIrParams(atomic_irparams_t *src, irparams_t *dst) {
// Typecast src and dst addresses to (char *)
char *csrc = (char *)src; // NOLINT(readability/casting)
char *cdst = (char *)dst; // NOLINT(readability/casting)
@@ -532,8 +536,8 @@ void IRrecv::crudeNoiseFilter(decode_results *results, const uint16_t floor) {
for (uint16_t i = offset + 2; i <= results->rawlen && i < kBufSize; i++)
results->rawbuf[i - 2] = results->rawbuf[i];
if (offset > 1) { // There is a previous pair we can add to.
// Merge this pair into into the previous space.
results->rawbuf[offset - 1] += addition;
// Merge this pair into into the previous space. // C++20 fix applied
results->rawbuf[offset - 1] = results->rawbuf[offset - 1] + addition;
}
results->rawlen -= 2; // Adjust the length.
} else {
@@ -1489,7 +1493,7 @@ bool IRrecv::decodeHash(decode_results *results) {
/// @return A match_result_t structure containing the success (or not), the
/// data value, and how many buffer entries were used.
match_result_t IRrecv::matchData(
volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark,
atomic_uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark,
const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace,
const uint8_t tolerance, const int16_t excess, const bool MSBfirst,
const bool expectlastspace) {
@@ -1549,7 +1553,7 @@ match_result_t IRrecv::matchData(
/// true is Most Significant Bit First Order, false is Least Significant First
/// @param[in] expectlastspace Do we expect a space at the end of the message?
/// @return If successful, how many buffer entries were used. Otherwise 0.
uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
uint16_t IRrecv::matchBytes(atomic_uint16_t *data_ptr, uint8_t *result_ptr,
const uint16_t remaining, const uint16_t nbytes,
const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace,
@@ -1601,7 +1605,7 @@ uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
/// @param[in] MSBfirst Bit order to save the data in. (Def: true)
/// true is Most Significant Bit First Order, false is Least Significant First
/// @return If successful, how many buffer entries were used. Otherwise 0.
uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr,
uint16_t IRrecv::_matchGeneric(atomic_uint16_t *data_ptr,
uint64_t *result_bits_ptr,
uint8_t *result_bytes_ptr,
const bool use_bits,
@@ -1703,7 +1707,7 @@ uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr,
/// @param[in] MSBfirst Bit order to save the data in. (Def: true)
/// true is Most Significant Bit First Order, false is Least Significant First
/// @return If successful, how many buffer entries were used. Otherwise 0.
uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr,
uint16_t IRrecv::matchGeneric(atomic_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,
@@ -1750,7 +1754,7 @@ uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr,
/// @param[in] MSBfirst Bit order to save the data in. (Def: true)
/// true is Most Significant Bit First Order, false is Least Significant First
/// @return If successful, how many buffer entries were used. Otherwise 0.
uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr,
uint16_t IRrecv::matchGeneric(atomic_uint16_t *data_ptr,
uint8_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,
@@ -1797,7 +1801,7 @@ uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr,
/// @return If successful, how many buffer entries were used. Otherwise 0.
/// @note Parameters one + zero add up to the total time for a bit.
/// e.g. mark(one) + space(zero) is a `1`, mark(zero) + space(one) is a `0`.
uint16_t IRrecv::matchGenericConstBitTime(volatile uint16_t *data_ptr,
uint16_t IRrecv::matchGenericConstBitTime(atomic_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,
@@ -1884,7 +1888,7 @@ uint16_t IRrecv::matchGenericConstBitTime(volatile uint16_t *data_ptr,
/// @return If successful, how many buffer entries were used. Otherwise 0.
/// @see https://en.wikipedia.org/wiki/Manchester_code
/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf
uint16_t IRrecv::matchManchester(volatile const uint16_t *data_ptr,
uint16_t IRrecv::matchManchester(atomic_const_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,
@@ -1991,7 +1995,7 @@ uint16_t IRrecv::matchManchester(volatile const uint16_t *data_ptr,
/// @see https://en.wikipedia.org/wiki/Manchester_code
/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf
/// @todo Clean up and optimise this. It is just "get it working code" atm.
uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
uint16_t IRrecv::matchManchesterData(atomic_const_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,
@@ -2112,7 +2116,7 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
#if UNIT_TEST
/// Unit test helper to get access to the params structure.
volatile irparams_t *IRrecv::_getParamsPtr(void) {
atomic_irparams_t *IRrecv::_getParamsPtr(void) {
return &params;
}
#endif // UNIT_TEST

View File

@@ -86,6 +86,8 @@ typedef struct {
uint8_t timeout; // Nr. of milliSeconds before we give up.
} irparams_t;
typedef volatile irparams_t atomic_irparams_t;
/// Results from a data match
typedef struct {
bool success; // Was the match successful?
@@ -111,7 +113,7 @@ class decode_results {
uint8_t state[kStateSizeMax]; // Multi-byte results.
};
uint16_t bits; // Number of bits in decoded value
volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks
atomic_uint16_t *rawbuf; // Raw intervals in .5 us ticks
uint16_t rawlen; // Number of records in rawbuf.
bool overflow;
bool repeat; // Is the result a repeat code?
@@ -171,11 +173,11 @@ class IRrecv {
uint16_t _unknown_threshold;
#endif
#ifdef UNIT_TEST
volatile irparams_t *_getParamsPtr(void);
atomic_irparams_t *_getParamsPtr(void);
#endif // UNIT_TEST
// These are called by decode
uint8_t _validTolerance(const uint8_t percentage);
void copyIrParams(volatile irparams_t *src, irparams_t *dst);
void copyIrParams(atomic_irparams_t *src, irparams_t *dst);
uint16_t compare(const uint16_t oldval, const uint16_t newval);
uint32_t ticksLow(const uint32_t usecs,
const uint8_t tolerance = kUseDefTol,
@@ -186,7 +188,7 @@ class IRrecv {
bool matchAtLeast(const uint32_t measured, const uint32_t desired,
const uint8_t tolerance = kUseDefTol,
const uint16_t delta = 0);
uint16_t _matchGeneric(volatile uint16_t *data_ptr,
uint16_t _matchGeneric(atomic_uint16_t *data_ptr,
uint64_t *result_bits_ptr,
uint8_t *result_ptr,
const bool use_bits,
@@ -204,14 +206,14 @@ class IRrecv {
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits,
match_result_t matchData(atomic_uint16_t *data_ptr, const uint16_t nbits,
const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true,
const bool expectlastspace = true);
uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
uint16_t matchBytes(atomic_uint16_t *data_ptr, uint8_t *result_ptr,
const uint16_t remaining, const uint16_t nbytes,
const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace,
@@ -219,7 +221,7 @@ class IRrecv {
const int16_t excess = kMarkExcess,
const bool MSBfirst = true,
const bool expectlastspace = true);
uint16_t matchGeneric(volatile uint16_t *data_ptr,
uint16_t matchGeneric(atomic_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining, const uint16_t nbits,
const uint16_t hdrmark, const uint32_t hdrspace,
@@ -230,7 +232,8 @@ class IRrecv {
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr,
uint16_t matchGeneric(atomic_uint16_t *data_ptr,
uint8_t *result_ptr,
const uint16_t remaining, const uint16_t nbits,
const uint16_t hdrmark, const uint32_t hdrspace,
const uint16_t onemark, const uint32_t onespace,
@@ -241,7 +244,7 @@ class IRrecv {
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
uint16_t matchGenericConstBitTime(volatile uint16_t *data_ptr,
uint16_t matchGenericConstBitTime(atomic_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,
@@ -255,7 +258,7 @@ class IRrecv {
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
uint16_t matchManchesterData(volatile const uint16_t *data_ptr,
uint16_t matchManchesterData(atomic_const_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,
@@ -265,7 +268,7 @@ class IRrecv {
const int16_t excess = kMarkExcess,
const bool MSBfirst = true,
const bool GEThomas = true);
uint16_t matchManchester(volatile const uint16_t *data_ptr,
uint16_t matchManchester(atomic_const_uint16_t *data_ptr,
uint64_t *result_ptr,
const uint16_t remaining,
const uint16_t nbits,

View File

@@ -51,6 +51,16 @@
#include <iostream>
#include <string>
#endif // UNIT_TEST
#if __cplusplus >= 202002L
#include <atomic>
typedef std::atomic< bool > atomic_bool;
typedef std::atomic<uint32_t> atomic_uint32_t;
#else
typedef volatile bool atomic_bool;
typedef volatile uint32_t atomic_uint32_t;
#endif
typedef volatile uint16_t atomic_uint16_t;
typedef volatile const uint16_t atomic_const_uint16_t;
// Library Version Information
// Major version number (X.x.x)

View File

@@ -48,7 +48,7 @@ TEST(TestIRrecv, DecodeHeapOverflow) {
IRrecv irrecv(1);
irrecv.enableIRIn();
ASSERT_EQ(kRawBuf, irrecv.getBufSize());
volatile irparams_t *params_ptr = irrecv._getParamsPtr();
atomic_irparams_t *params_ptr = irrecv._getParamsPtr();
// replace the buffer with a slightly bigger one to see if we go past the end
// accidentally.
params_ptr->rawbuf = new uint16_t[kRawBuf + 10];