Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/IRMQTTServer/IRMQTTServer.ino
Expand Up @@ -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;
Expand Down
7 changes: 6 additions & 1 deletion platformio.ini
Expand Up @@ -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/2023.10.12/platform-espressif32.zip
board = esp32dev
64 changes: 46 additions & 18 deletions src/IRrecv.cpp
Expand Up @@ -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>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this hurt or cause an issue if it is always included?
i.e. Can we just include this header all the time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm more inclined to only add includes when really needed, trying to avoid unneeded code being pulled in the build or having unexpected interactions.

#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif

#ifdef UNIT_TEST
#undef ICACHE_RAM_ATTR
#define ICACHE_RAM_ATTR
Expand Down Expand Up @@ -144,7 +150,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

Expand Down Expand Up @@ -215,7 +221,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;

Expand All @@ -242,8 +248,13 @@ static void USE_IRAM_ATTR gpio_intr() {
// @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178
timer->dev->config.alarm_en = 1;
#else // _ESP32_IRRECV_TIMER_HACK
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
timerWrite(timer, 0);
timerStart(timer);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
timerWrite(timer, 0);
timerAlarmEnable(timer);
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif // _ESP32_IRRECV_TIMER_HACK
#endif // ESP32
}
Expand Down Expand Up @@ -333,9 +344,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;
Expand All @@ -359,20 +367,29 @@ void IRrecv::enableIRIn(const bool pullup) {
#if defined(ESP32)
// Initialise the ESP32 timer.
// 80MHz / 80 = 1 uSec granularity.
timer = timerBegin(_timer_num, 80, true);
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
timer = timerBegin(1000000); // 1 MHz
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
timer = timerBegin(_timer_num, 80, true); // 1 MHz : 80 MHz with divider 80
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#ifdef DEBUG
if (timer == NULL) {
DPRINT("FATAL: Unable enable system timer: ");
DPRINTLN((uint16_t)_timer_num);
}
#endif // DEBUG
assert(timer != NULL); // Check we actually got the timer.
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
timerAttachInterrupt(timer, &read_timeout);
timerAlarm(timer, MS_TO_USEC(params.timeout), ONCE, 0);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
// Set the timer so it only fires once, and set it's trigger in uSeconds.
timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE);
// Note: Interrupt needs to be attached before it can be enabled or disabled.
// Note: EDGE (true) is not supported, use LEVEL (false). Ref: #1713
// See: https://github.com/espressif/arduino-esp32/blob/caef4006af491130136b219c1205bdcf8f08bf2b/cores/esp32/esp32-hal-timer.c#L224-L227
timerAttachInterrupt(timer, &read_timeout, false);
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif // ESP32

// Initialise state machine variables
Expand All @@ -398,9 +415,16 @@ void IRrecv::disableIRIn(void) {
os_timer_disarm(&timer);
#endif // ESP8266
#if defined(ESP32)
if (timer == NULL) { return; } // Call only once (defensive)
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
timerDetachInterrupt(timer);
timerEnd(timer);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
timerAlarmDisable(timer);
timerDetachInterrupt(timer);
timerEnd(timer);
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
timer = NULL; // Cleanup the ESP32 timeout timer.
#endif // ESP32
detachInterrupt(params.recvpin);
#endif // UNIT_TEST
Expand All @@ -426,7 +450,11 @@ void IRrecv::resume(void) {
params.rawlen = 0;
params.overflow = false;
#if defined(ESP32)
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
timerStop(timer);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
timerAlarmDisable(timer);
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
gpio_intr_enable((gpio_num_t)params.recvpin);
#endif // ESP32
}
Expand All @@ -437,7 +465,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)
Expand Down Expand Up @@ -502,8 +530,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;
tonhuisman marked this conversation as resolved.
Show resolved Hide resolved
}
results->rawlen -= 2; // Adjust the length.
} else {
Expand Down Expand Up @@ -1449,7 +1477,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) {
Expand Down Expand Up @@ -1509,7 +1537,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,
Expand Down Expand Up @@ -1561,7 +1589,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,
Expand Down Expand Up @@ -1663,7 +1691,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,
Expand Down Expand Up @@ -1710,7 +1738,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,
Expand Down Expand Up @@ -1757,7 +1785,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,
Expand Down Expand Up @@ -1844,7 +1872,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,
Expand Down Expand Up @@ -1951,7 +1979,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,
Expand Down Expand Up @@ -2072,7 +2100,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
Expand Down
25 changes: 14 additions & 11 deletions src/IRrecv.h
Expand Up @@ -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?
Expand All @@ -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?
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -204,22 +206,22 @@ 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,
const uint8_t tolerance = kUseDefTol,
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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions src/IRremoteESP8266.h
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/IRutils.cpp
Expand Up @@ -1397,7 +1397,7 @@ namespace irutils {
/// issue has been found.
uint8_t lowLevelSanityCheck(void) {
const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL;
volatile uint32_t EndianTest = 0x12345678;
atomic_uint32_t EndianTest = 0x12345678;
const uint8_t kBitFieldError = 0b01;
const uint8_t kEndiannessError = 0b10;
uint8_t result = 0;
Expand Down
2 changes: 1 addition & 1 deletion test/IRrecv_test.cpp
Expand Up @@ -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];
Expand Down