Skip to content

Commit

Permalink
Add low level bit formating sanity checks.
Browse files Browse the repository at this point in the history
* Pave the way for us being able to use Bit Fields reliably. (They are not fully portable)
* Add `lowLevelSanityCheck()` function to do bit field and endianness checks.
* Add a unit test for it. i.e. capture problems in unit tests.
* Add checks for it in `IRrecvDumpV[2|3]` and `IRMQTTServer` so we can catch it at run time.
* Bump IRMQTTServer version number.

Pre-work for #1210
  • Loading branch information
crankyoldgit committed Aug 7, 2020
1 parent 857a9ee commit 993f537
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 1 deletion.
2 changes: 1 addition & 1 deletion examples/IRMQTTServer/IRMQTTServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ const uint16_t kJsonAcStateMaxSize = 1024; // Bytes
// ----------------- End of User Configuration Section -------------------------

// Constants
#define _MY_VERSION_ "v1.5.0"
#define _MY_VERSION_ "v1.5.1"

const uint8_t kRebootTime = 15; // Seconds
const uint8_t kQuickDisplayTime = 2; // Seconds
Expand Down
5 changes: 5 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ using irutils::msToString;
#endif // REPORT_VCC

// Globals
uint8_t _sanity = 0;
#if defined(ESP8266)
ESP8266WebServer server(kHttpPort);
#endif // ESP8266
Expand Down Expand Up @@ -1260,6 +1261,7 @@ void handleInfo(void) {
"ESP32 SDK Version: " + ESP.getSdkVersion() + "<br>"
#endif // ESP32
"Cpu Freq: " + String(ESP.getCpuFreqMHz()) + "MHz<br>"
"Sanity Check: " + String((_sanity == 0) ? "Ok" : "FAILED") + "<br>"
"IR Send GPIO(s): " + listOfTxGpios() + "<br>"
+ irutils::addBoolToString(kInvertTxOutput,
"Inverting GPIO output", false) + "<br>"
Expand Down Expand Up @@ -2081,6 +2083,9 @@ void init_vars(void) {
}

void setup(void) {
// Perform a low level sanity checks that the compiler performs bit field
// packing as we expect and Endianness is as we expect.
_sanity = irutils::lowLevelSanityCheck();
#if DEBUG
if (!isSerialGpioUsedByIr()) {
#if defined(ESP8266)
Expand Down
4 changes: 4 additions & 0 deletions examples/IRrecvDumpV2/IRrecvDumpV2.ino
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ void setup() {
#endif // ESP8266
while (!Serial) // Wait for the serial connection to be establised.
delay(50);
// Perform a low level sanity checks that the compiler performs bit field
// packing as we expect and Endianness is as we expect.
assert(irutils::lowLevelSanityCheck());

Serial.printf("\n" D_STR_IRRECVDUMP_STARTUP "\n", kRecvPin);
#if DECODE_HASH
// Ignore messages with less than minimum on or off pulses.
Expand Down
4 changes: 4 additions & 0 deletions examples/IRrecvDumpV3/IRrecvDumpV3.ino
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ void setup() {
#endif // ESP8266
while (!Serial) // Wait for the serial connection to be establised.
delay(50);
// Perform a low level sanity checks that the compiler performs bit field
// packing as we expect and Endianness is as we expect.
assert(irutils::lowLevelSanityCheck());

Serial.printf("\n" D_STR_IRRECVDUMP_STARTUP "\n", kRecvPin);
OTAinit(); // setup OTA handlers and show IP
#if DECODE_HASH
Expand Down
53 changes: 53 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,4 +947,57 @@ namespace irutils {
}
return true;
}

/// Perform a low lovel bit manipulation sanity check for the given cpu
/// architecture and the compiler operation. Calls to this should return
/// 0 if everything is as expected, anything else means the library won't work
/// as expected.
/// @return A bit mask value of potential issues.
/// 0: (e.g. 0b00000000) Everything appears okay.
/// 0th bit set: (0b1) Unexpected bit field/packing encountered.
/// Try a different compiler.
/// 1st bit set: (0b10) Unexpected Endianness. Try a different compiler flag
/// or use a CPU different architecture.
/// e.g. A result of 3 (0b11) would mean both a bit field and an Endianness
/// issue has been found.
uint8_t lowLevelSanityCheck(void) {
const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL;
volatile uint32_t EndianTest = 0x12345678;
const uint8_t kBitFieldError = 0b01;
const uint8_t kEndiannessError = 0b10;
uint8_t result = 0;
union bitpackdata {
struct {
uint64_t lowestbit:1; // 0th bit
uint64_t next7bits:7; // 1-7th bits
uint64_t _unused_1:20; // 8-27th bits
// Cross the 32 bit boundary.
uint64_t crossbits:16; // 28-43rd bits
uint64_t _usused_2:18; // 44-61st bits
uint64_t highest2bits:2; // 62-63rd bits
};
uint64_t all;
};

bitpackdata data;
data.lowestbit = true;
data.next7bits = 0b0011100; // 0x1C
data._unused_1 = 0;
data.crossbits = 0x1234;
data._usused_2 = 0;
data.highest2bits = 0b10; // 2

if (data.all != kExpectedBitFieldResult) result |= kBitFieldError;
// Check that we are using Little Endian for integers
#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN)
if (BYTE_ORDER != LITTLE_ENDIAN) result |= kEndiannessError;
#endif
#if defined(__IEEE_BIG_ENDIAN) || defined(__IEEE_BYTES_BIG_ENDIAN)
result |= kEndiannessError;
#endif
// Brute force check for little endian.
if (*((uint8_t*)(&EndianTest)) != 0x78) // NOLINT(readability/casting)
result |= kEndiannessError;
return result;
}
} // namespace irutils
1 change: 1 addition & 0 deletions src/IRutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,6 @@ namespace irutils {
const uint64_t data);
uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length);
bool checkInvertedBytePairs(const uint8_t * const ptr, const uint16_t length);
uint8_t lowLevelSanityCheck(void);
} // namespace irutils
#endif // IRUTILS_H_
4 changes: 4 additions & 0 deletions test/IRutils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,3 +776,7 @@ TEST(TestUtils, InvertedBytePairs) {
ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 6));
EXPECT_STATE_EQ(correct, wrong, 6 * 8);
}

TEST(TestUtils, lowLevelSanityCheck) {
ASSERT_EQ(0, irutils::lowLevelSanityCheck());
}

0 comments on commit 993f537

Please sign in to comment.