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

Better conversions of uint64_t's to strings. #318

Merged
merged 3 commits into from
Oct 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 7 additions & 15 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
#include <ESP8266mDNS.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <IRutils.h>
#ifdef MQTT_ENABLE
// --------------------------------------------------------------------
// * * * IMPORTANT * * *
Expand All @@ -129,7 +130,7 @@
// Set if your MQTT server requires a Username & Password to connect.
const char* mqtt_user = "";
const char* mqtt_password = "";
#define MQTT_RECONNECT_TIME 5000 // Delay between reconnect tries.
#define MQTT_RECONNECT_TIME 5000 // Delay(ms) between reconnect tries.

#define MQTTprefix "ir_server"
#define MQTTack MQTTprefix "/sent" // Topic we send back acknowledgements on
Expand Down Expand Up @@ -710,24 +711,15 @@ void sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
#endif
break;
default:
debug("Code: 0x" +
String((uint32_t) (code >> 32), 16) +
String((uint32_t) (code & UINT32_MAX), 16));
debug("Code: 0x" + uint64ToString(code, 16));
debug("Bits: " + String(bits));
debug("Repeats: " + String(repeat));

#ifdef MQTT_ENABLE
if (code >> 32) // Are we dealing with a value larger than UINT32_MAX?
mqtt_client.publish(MQTTack, (String(ir_type) + "," +
String((uint32_t) (code >> 32), 16) +
String((uint32_t) (code & UINT32_MAX), 16)
+ "," + String(bits) + "," +
String(repeat)).c_str());
else
mqtt_client.publish(MQTTack, (String(ir_type) + "," +
String((uint32_t) (code & UINT32_MAX), 16)
+ "," + String(bits) + "," +
String(repeat)).c_str());
mqtt_client.publish(MQTTack, (String(ir_type) + "," +
uint64ToString(code, 16)
+ "," + String(bits) + "," +
String(repeat)).c_str());
#endif
}
}
Expand Down
51 changes: 37 additions & 14 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
#ifndef UNIT_TEST
#include <Arduino.h>
#endif

#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRrecv.h"

// Reverse the order of the requested least significant nr. of bits.
Expand All @@ -30,30 +34,49 @@ uint64_t reverseBits(uint64_t input, uint16_t nbits) {
return (input << nbits) | output;
}

// Print a uint64_t/unsigned long long to the Serial port
// Serial.print() can't handle printing long longs. (uint64_t)
// Convert a uint64_t (unsigned long long) to a string.
// Arduino String/toInt/Serial.print() can't handle printing 64 bit values.
//
// Args:
// input: The value to print
// base: The output base.
// base: The output base.
// Returns:
// A string representation of the integer.
// Note: Based on Arduino's Print::printNumber()
void serialPrintUint64(uint64_t input, uint8_t base) {
char buf[8 * sizeof(input) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];

*str = '\0';

// prevent crash if called with base == 1
#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist.
String uint64ToString(uint64_t input, uint8_t base) {
String result = "";
#else
std::string uint64ToString(uint64_t input, uint8_t base) {
std::string result = "";
#endif
// prevent issues if called with base <= 1
if (base < 2) base = 10;
// Check we have a base that we can actually print.
// i.e. [0-9A-Z] == 36
if (base > 36) base = 10;

do {
char c = input % base;
input /= base;

*--str = c < 10 ? c + '0' : c + 'A' - 10;
if (c < 10)
c +='0';
else
c += 'A' - 10;
result = c + result;
} while (input);
return result;
}

#ifndef UNIT_TEST
Serial.print(str);
#endif
#ifdef ARDUINO
// Print a uint64_t/unsigned long long to the Serial port
// Serial.print() can't handle printing long longs. (uint64_t)
//
// Args:
// input: The value to print
// base: The output base.
void serialPrintUint64(uint64_t input, uint8_t base) {
Serial.print(uint64ToString(input, base));
}
#endif
13 changes: 12 additions & 1 deletion src/IRutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@

// Copyright 2017 David Conran

#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef ARDUINO
#include <string>
#endif

uint64_t reverseBits(uint64_t input, uint16_t nbits);
void serialPrintUint64(uint64_t input, uint8_t base);
#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist.
String uint64ToString(uint64_t input, uint8_t base = 10);
#else
std::string uint64ToString(uint64_t input, uint8_t base = 10);
#endif
void serialPrintUint64(uint64_t input, uint8_t base = 10);

#endif // IRUTILS_H_
56 changes: 56 additions & 0 deletions test/IRutils_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2017 David Conran

#include "IRutils.h"
#include <stdint.h>
#include "gtest/gtest.h"

// Tests reverseBits().
Expand Down Expand Up @@ -33,3 +34,58 @@ TEST(ReverseBitsTest, LessBitsReversedThanInputHasSet) {
EXPECT_EQ(0xF5, reverseBits(0xFA, 4));
EXPECT_EQ(0x12345678FFFF0000, reverseBits(0x123456780000FFFF, 32));
}

// Tests for uint64ToString()

TEST(TestUint64ToString, TrivialCases) {
EXPECT_EQ("0", uint64ToString(0)); // Default base (10)
EXPECT_EQ("0", uint64ToString(0, 2)); // Base-2
EXPECT_EQ("0", uint64ToString(0, 8)); // Base-8
EXPECT_EQ("0", uint64ToString(0, 10)); // Base-10
EXPECT_EQ("0", uint64ToString(0, 16)); // Base-16

EXPECT_EQ("1", uint64ToString(1, 2)); // Base-2
EXPECT_EQ("2", uint64ToString(2, 8)); // Base-8
EXPECT_EQ("3", uint64ToString(3, 10)); // Base-10
EXPECT_EQ("4", uint64ToString(4, 16)); // Base-16
}

TEST(TestUint64ToString, NormalUse) {
EXPECT_EQ("12345", uint64ToString(12345));
EXPECT_EQ("100", uint64ToString(4, 2));
EXPECT_EQ("3039", uint64ToString(12345, 16));
EXPECT_EQ("123456", uint64ToString(123456));
EXPECT_EQ("1E240", uint64ToString(123456, 16));
EXPECT_EQ("FEEDDEADBEEF", uint64ToString(0xfeeddeadbeef, 16));
}

TEST(TestUint64ToString, Max64Bit) {
EXPECT_EQ("18446744073709551615", uint64ToString(UINT64_MAX)); // Default
EXPECT_EQ("1111111111111111111111111111111111111111111111111111111111111111",
uint64ToString(UINT64_MAX, 2)); // Base-2
EXPECT_EQ("1777777777777777777777", uint64ToString(UINT64_MAX, 8)); // Base-8
EXPECT_EQ("18446744073709551615", uint64ToString(UINT64_MAX, 10)); // Base-10
EXPECT_EQ("FFFFFFFFFFFFFFFF", uint64ToString(UINT64_MAX, 16)); // Base-16
}

TEST(TestUint64ToString, Max32Bit) {
EXPECT_EQ("4294967295", uint64ToString(UINT32_MAX)); // Default
EXPECT_EQ("37777777777", uint64ToString(UINT32_MAX, 8)); // Base-8
EXPECT_EQ("4294967295", uint64ToString(UINT32_MAX, 10)); // Base-10
EXPECT_EQ("FFFFFFFF", uint64ToString(UINT32_MAX, 16)); // Base-16
}

TEST(TestUint64ToString, InterestingCases) {
// Previous hacky-code didn't handle leading zeros in the lower 32 bits.
EXPECT_EQ("100000000", uint64ToString(0x100000000, 16));
EXPECT_EQ("100000001", uint64ToString(0x100000001, 16));
}

TEST(TestUint64ToString, SillyBases) {
// If we are given a silly base, we should defer to Base-10.
EXPECT_EQ("12345", uint64ToString(12345, 0)); // Super silly, makes no sense.
EXPECT_EQ("12345", uint64ToString(12345, 1)); // We don't do unary.
EXPECT_EQ("12345", uint64ToString(12345, 100)); // We can't print base-100.
EXPECT_EQ("12345", uint64ToString(12345, 37)); // Base-37 is one to far.
EXPECT_EQ("9IX", uint64ToString(12345, 36)); // But we *can* do base-36.
}