diff --git a/libraries/printHelpers/.arduino-ci.yml b/libraries/printHelpers/.arduino-ci.yml index ff5659b9..3e496624 100644 --- a/libraries/printHelpers/.arduino-ci.yml +++ b/libraries/printHelpers/.arduino-ci.yml @@ -2,6 +2,10 @@ compile: # Choosing to run compilation tests on 2 different Arduino platforms platforms: - uno - - leonardo - due - zero + - leonardo + - m4 + - esp32 + - esp8266 + - mega2560 diff --git a/libraries/printHelpers/.github/workflows/arduino_test_runner.yml b/libraries/printHelpers/.github/workflows/arduino_test_runner.yml index 476456bb..096b975b 100644 --- a/libraries/printHelpers/.github/workflows/arduino_test_runner.yml +++ b/libraries/printHelpers/.github/workflows/arduino_test_runner.yml @@ -4,10 +4,14 @@ name: Arduino CI on: [push, pull_request] jobs: - arduino_ci: + runTest: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: Arduino-CI/action@master - # Arduino-CI/action@v0.1.1 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + gem install arduino_ci + arduino_ci.rb diff --git a/libraries/printHelpers/README.md b/libraries/printHelpers/README.md index b1f926f3..22c8305b 100644 --- a/libraries/printHelpers/README.md +++ b/libraries/printHelpers/README.md @@ -1,10 +1,11 @@ [![Arduino CI](https://github.com/RobTillaart/printHelpers/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) +[![Arduino-lint](https://github.com/RobTillaart/printHelpers/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/printHelpers/actions/workflows/arduino-lint.yml) +[![JSON check](https://github.com/RobTillaart/printHelpers/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/printHelpers/actions/workflows/jsoncheck.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/printHelpers/blob/master/LICENSE) [![GitHub release](https://img.shields.io/github/release/RobTillaart/printHelpers.svg?maxAge=3600)](https://github.com/RobTillaart/printHelpers/releases) - # printHelpers Arduino library to help formatting data for printing @@ -17,19 +18,22 @@ data in a way not possible in the standard print library of the Arduino. The following functions are implemented: -- **print64(int64_t, base)** converts a 64 bit integer number to a char array. +- **char \* print64(int64_t value, uint8_t base)** converts a 64 bit integer +number to a char array. The plus sign is not printed, neither are leading zero's. Base 10 (DEC) and 16 (HEX) are supported and other bases up to 36 can be used. Note that negative numbers will always get a minus sign for any base. Cast the number to uint64_t to suppress the sign. -- **print64(uint64_t, base)** converts a unsigned 64 bit int number to a char array. +- **char \* print64(uint64_t value, uint8_t base)** converts a unsigned 64 bit +int number to a char array. No sign is printed, neither are leading zero's. Base 10 (DEC) and 16 (HEX) are supported and bases up to 36 can be used. ---- -- **sci(double, decimals)** converts a float or double to a char array. +- **char \* sci(double value, uint8_t decimals)** converts a float or double to a +char array. E.g. print(sci(f, 4)) ==> results in "6.7407E+21". The existing Arduino print library only supports printing of floats and doubles up toabout 4E9 while the range of floats goes up to ~1E38. @@ -38,15 +42,17 @@ support down to about 1E-38 (subnormal even to 1E-45). Exisiting library functions **dtostrf()** has no scientific notation and **dtostre()** (AVR) is limited to 7 decimals. -- **eng(double, decimals)** converts a float or double to a char array. +- **char \* eng(double value, uint8_t decimals)** converts a float or double to a +char array. E.g. print(eng(f, 4)) ==> results in "6.7407E+21". Note the exponent created by **eng()** is always a multiple of 3. -- **scieng(double, decimals, expmultiple)** converts a float or double to a char array. +- **char \* scieng(double value, uint8_t decimals, uint8_t exponentMultiple)** converts a +float or double to a char array. **sci()** and **eng()** use the same underlying function called **scieng()** as the initial code for converting was almost identical. Although not intended to be used directly, one can use it. -The last parameter expmultiple defines where the exponent is a multiple of. +The last parameter **exponentMultiple** defines where the exponent is a multiple of. For the **sci()** function this is 1, for the **eng()** function this is 3. The **scieng()** function works for multiples from 1..9 for the exponent. The usability of other values than 1 and 3 are not known. @@ -55,7 +61,7 @@ mantissa. ---- -- **toBytes(val, decimals = 2)** makes from a big number +- **char \* toBytes(double value, uint8_t decimals = 2)** makes from a big number representing an amount of bytes a shorter string usable for displaying. The number of decimals is max 3 Example 3.292.528 ==> "3.140MB" @@ -68,7 +74,7 @@ List of prefixes: treda Byte == TDB uses 2 chars to indicate the magnitude so that would take extra memory of more complex code. As it is seldom used, "official" support stops with UDA. -Should be big enough for some time. +Should be big enough for some time. To have some support the code uses lowercase for the next 8 levels: treda sorta rinta quexa pepta ocha nena minga luma (1024\^21 ~~ 10\^63) @@ -79,37 +85,45 @@ More formatting functions might be added in the future. ## Shared buffer -The implementation of the function all use a shared buffer to hold the generated string. -This is done to reduce the memory overhead of embedding static buffers. **Note this is not thread safe!** -In a coming release the functions will be able to pass a buffer to them to become more thread safe. +The implementation of the function all use a shared buffer to hold the +generated string. +This is done to reduce the memory overhead of embedding static buffers. +**Note this is not thread safe!** +In a coming release the functions will be able to pass a buffer to them +to become more thread safe. -The size of this shared buffer is default 66 to be able to print a 64 bit integer in base 2. -To save memory one can change this buffersize in the code or compile time by changing **PRINTBUFFERSIZE**. +The size of this shared buffer is default 66 to be able to print a 64 bit +integer in base 2. +To save memory one can change this buffer size in the code or compile time +by changing **PRINTBUFFERSIZE**. Be aware that **sci()** and **eng()** use the same buffer. These need about 10 bytes plus one bytes for every decimal used. So for floats one need 15-20 bytes max, for doubles one need up to 30 bytes max. In practice a size of 22 will work for most applications. -| PRINTBUFFERSIZE | BASE SUPPORTED | nr. decimals | -|:----:|:----:|:----:| -| 66 | 02 - 36 | 0 - 50 |(default) -| 34 | 04 - 36 | 0 - 20 | -| 24 | 08 - 36 | 0 - 14 | -| 22 | 10 - 36 | 0 - 12 | -| 18 | 16 - 36 | 0 - 07 | +| PRINTBUFFERSIZE | BASE SUPPORTED | nr. decimals | Notes | +|:---------------:|:--------------:|:------------:|:----------| +| 66 | 02 - 36 | 0 - 50 | (default) | +| 34 | 04 - 36 | 0 - 20 | +| 24 | 08 - 36 | 0 - 14 | +| 22 | 10 - 36 | 0 - 12 | +| 18 | 16 - 36 | 0 - 07 | When functions are added, the recommended minimum size might increase. -## Future - -- Investigate the precision of **sci()** and **eng()**. -- Investigate performance of **sci()** and **eng()**. -- Add option to pass char buffer as parameter (improve threadsafe) -- Add more print helpers. - - ## Operation See examples + +## Future + +- Investigate the precision of **sci()** and **eng()**. +- Investigate performance of **sci()** and **eng()**. +- Investigate performance (local variables iso modifying parameters) +- Add option to pass char buffer as parameter (improve threadsafe) +- Add more print helpers. +- improve readability of the code (even more) +- check TODO's in the code +- diff --git a/libraries/printHelpers/examples/sci_test/sci_test.ino b/libraries/printHelpers/examples/sci_test/sci_test.ino index e033d7d0..0a1e4858 100644 --- a/libraries/printHelpers/examples/sci_test/sci_test.ino +++ b/libraries/printHelpers/examples/sci_test/sci_test.ino @@ -1,7 +1,7 @@ // // FILE: sci_test.ino // AUTHOR: Rob Tillaart -// VERSION: 0.0.1 +// VERSION: 0.1.0 // PURPOSE: test different values with sci function @@ -153,6 +153,7 @@ void test5() Serial.println(); } + void test6() { if (sizeof(double) >= 8) @@ -195,3 +196,4 @@ void test6() // -- END OF FILE -- + diff --git a/libraries/printHelpers/keywords.txt b/libraries/printHelpers/keywords.txt index f35288f8..5b7c515b 100644 --- a/libraries/printHelpers/keywords.txt +++ b/libraries/printHelpers/keywords.txt @@ -1,13 +1,19 @@ -# Syntax Coloring Map for printHelpers +# Syntax Colouring Map for printHelpers + +# Data types (KEYWORD1) -# Datatypes (KEYWORD1) # Methods and Functions (KEYWORD2) print64 KEYWORD2 + sci KEYWORD2 eng KEYWORD2 scieng KEYWORD2 + toBytes KEYWORD2 -# Constants (LITERAL1) + +# Constants (LITERAL1) +PRINTHELPERS_VERSION LITERAL1 +PRINTBUFFERSIZE LITERAL1 diff --git a/libraries/printHelpers/library.json b/libraries/printHelpers/library.json index 0822b3ca..35ef1f51 100644 --- a/libraries/printHelpers/library.json +++ b/libraries/printHelpers/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/printHelpers" }, - "version": "0.2.1", + "version": "0.2.2", "license": "MIT", "frameworks": "arduino", "platforms": "*" diff --git a/libraries/printHelpers/library.properties b/libraries/printHelpers/library.properties index 19313bc5..1ea274dd 100644 --- a/libraries/printHelpers/library.properties +++ b/libraries/printHelpers/library.properties @@ -1,5 +1,5 @@ name=printHelpers -version=0.2.1 +version=0.2.2 author=Rob Tillaart maintainer=Rob Tillaart sentence=Arduino library to help formatting data for printing. 64 bit integers (base 10 and 16). Engineering and scientific notation. diff --git a/libraries/printHelpers/printHelpers.h b/libraries/printHelpers/printHelpers.h index 777d1fbe..46eb9226 100644 --- a/libraries/printHelpers/printHelpers.h +++ b/libraries/printHelpers/printHelpers.h @@ -3,7 +3,7 @@ // FILE: printHelpers.h // AUTHOR: Rob Tillaart // DATE: 2018-01-21 -// VERSION: 0.2.1 +// VERSION: 0.2.2 // PUPROSE: Arduino library to help formatting for printing. // URL: https://github.com/RobTillaart/printHelpers @@ -12,7 +12,7 @@ #include "stdlib.h" -#define PRINTHELPERS_VERSION (F("0.2.1")) +#define PRINTHELPERS_VERSION (F("0.2.2")) // 24 is a pretty safe minimum @@ -38,29 +38,29 @@ char __printbuffer[PRINTBUFFERSIZE]; // buffer size 24 will work for base 8 -36 // buffer size 22 will work for base 10 - 36 -char * print64(int64_t n, uint8_t base = 10) +char * print64(int64_t value, uint8_t base = 10) { - char * buf = __printbuffer; + char * buffer = __printbuffer; uint8_t i = 0; uint8_t j = 0; - buf[0] = 0; + buffer[0] = 0; // small base need bigger buffer - if ((base < 10) && (PRINTBUFFERSIZE <= 22)) return buf; + if ((base < 10) && (PRINTBUFFERSIZE <= 22)) return buffer; // handle special case - if (n == 0) + if (value == 0) { - buf[0] = '0'; - buf[1] = 0; - return buf; + buffer[0] = '0'; + buffer[1] = 0; + return buffer; } // PREFIX NEGATIVE // handle negative values (for all bases for now) - if ((n < 0) && (base != 16)) + if ((value < 0) && (base != 16)) { - n = -n; - buf[0] = '-'; + value = -value; + buffer[0] = '-'; i++; j++; } @@ -68,71 +68,71 @@ char * print64(int64_t n, uint8_t base = 10) // PREFIX HEX if (base == 16) { - buf[0] = '0'; - buf[1] = 'x'; - buf[2] = 0; + buffer[0] = '0'; + buffer[1] = 'x'; + buffer[2] = 0; i = 2; j = 2; } // create one digit per loop - while (n > 0) + while (value > 0) { - int64_t t = n / base; - uint8_t p = n - t * base; - buf[i++] = (p < 10) ? '0' + p : ('A' - 10) + p; - n = t; + int64_t temp = value / base; + uint8_t digit = value - temp * base; + buffer[i++] = (digit < 10) ? '0' + digit : ('A' - 10) + digit; + value = temp; } - buf[i] = 0; + buffer[i] = 0; // reverse buffer --i; while ( i > j) { - uint8_t t = buf[i]; - buf[i] = buf[j]; - buf[j] = t; + uint8_t temp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = temp; i--; j++; } - return buf; + return buffer; } -char * print64(uint64_t n, uint8_t base = 10) +char * print64(uint64_t value, uint8_t base = 10) { - char * buf = __printbuffer; + char * buffer = __printbuffer; uint8_t i = 0; uint8_t j = 0; - buf[0] = 0; + buffer[0] = 0; // small base need bigger buffer - if ((base < 10) && (PRINTBUFFERSIZE <= 22)) return buf; + if ((base < 10) && (PRINTBUFFERSIZE <= 22)) return buffer; // handle special case - if (n == 0) + if (value == 0) { - buf[0] = '0'; - buf[1] = 0; - return buf; + buffer[0] = '0'; + buffer[1] = 0; + return buffer; } - // create one digit per loop - while (n > 0) + // create one digit per iteration + while (value > 0) { - uint64_t t = n / base; - uint8_t p = n - t * base; - buf[i++] = (p < 10) ? '0' + p : ('A' - 10) + p; - n = t; + uint64_t temp = value / base; + uint8_t digit = value - temp * base; + buffer[i++] = (digit < 10) ? '0' + digit : ('A' - 10) + digit; + value = temp; } - buf[i] = 0; - // reverse buf + buffer[i] = 0; + // reverse buffer --i; while (i > j) { - uint8_t t = buf[i]; - buf[i] = buf[j]; - buf[j] = t; + uint8_t temp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = temp; i--; j++; } - return buf; + return buffer; } @@ -143,9 +143,10 @@ char * print64(uint64_t n, uint8_t base = 10) // typical buffer size for 8 byte double is 22 bytes // 15 bytes mantissa, sign dot E-xxx -char * scieng(double number, uint8_t digits, uint8_t em) +// em = exponentMultiple. +char * scieng(double value, uint8_t decimals, uint8_t em) { - char * buf = __printbuffer; + char * buffer = __printbuffer; int exponent = 0; int pos = 0; double e1 = 10; @@ -158,107 +159,107 @@ char * scieng(double number, uint8_t digits, uint8_t em) // Handling these costs 13 bytes RAM // shorten them with N, I, -I ? - if (isnan(number)) + if (isnan(value)) { - strcpy(buf, "nan"); - return buf; + strcpy(buffer, "nan"); + return buffer; } - if (isinf(number)) + if (isinf(value)) { - if (number < 0) strcpy(buf, "-inf"); - strcpy(buf, "inf"); - return buf; + if (value < 0) strcpy(buffer, "-inf"); + strcpy(buffer, "inf"); + return buffer; } // Handle negative numbers - if (number < 0.0) + if (value < 0.0) { - buf[pos++] = '-'; - number = -number; + buffer[pos++] = '-'; + value = -value; } // Scale exponent to multiple of em // TODO: can we remove loop to reduce rounding errors - while (number >= e1) + while (value >= e1) { - number *= e2; + value *= e2; exponent += em; } // TODO: can we remove loop to reduce rounding errors - while (number < 1 && number != 0.0) + while (value < 1 && value != 0.0) { - number *= e1; + value *= e1; exponent -= em; } // Round correctly so that print(1.999, 2) prints as "2.00" double rounding = 0.5; // TODO: can we remove loop to reduce rounding errors - for (uint8_t i = 0; i < digits; ++i) + for (uint8_t i = 0; i < decimals; ++i) { rounding *= 0.1; } - number += rounding; - if (number >= e1) + value += rounding; + if (value >= e1) { exponent += em; - number *= e2; + value *= e2; } // Split whole part and remainder - uint32_t d = (uint32_t)number; - double remainder = number - d; + uint32_t d = (uint32_t)value; + double remainder = value - d; // print whole part - itoa(d, &buf[pos], 10); - pos = strlen(buf); + itoa(d, &buffer[pos], 10); + pos = strlen(buffer); // print remainder part - if (digits > 0) + if (decimals > 0) { - buf[pos++] = '.'; // decimal point + buffer[pos++] = '.'; // decimal point } - // Extract digits from the remainder one at a time + // Extract decimals from the remainder one at a time // to prevent missing leading zero's // TODO: can we remove loop to reduce rounding errors - while (digits-- > 0) + while (decimals-- > 0) { remainder *= 10; d = (uint8_t)remainder; - buf[pos++] = d + '0'; + buffer[pos++] = d + '0'; remainder -= d; } // print exponent - buf[pos++] = 'E'; + buffer[pos++] = 'E'; if (exponent < 0) { - buf[pos++] = '-'; + buffer[pos++] = '-'; exponent = -exponent; } - else buf[pos++] = '+'; + else buffer[pos++] = '+'; - itoa(exponent, &buf[pos], 10); - return buf; + itoa(exponent, &buffer[pos], 10); + return buffer; } -char * eng(double number, uint8_t digits) +char * eng(double value, uint8_t decimals) { - return scieng(number, digits, 3); + return scieng(value, decimals, 3); } -char * sci(double number, uint8_t digits) +char * sci(double value, uint8_t decimals) { - return scieng(number, digits, 1); + return scieng(value, decimals, 1); } -void sci(Stream &str, double f, uint8_t digits) +void sci(Stream &str, double value, uint8_t decimals) { - str.print(sci(f, digits)); + str.print(sci(value, decimals)); } //////////////////////////////////////////////////////////// @@ -271,60 +272,60 @@ void sci(Stream &str, double f, uint8_t digits) // zetta yotta xona weka vunda uda (1024^12) // // (treda Byte == TDB is the next one and it is 2 char -// so codewise difficult and as it is seldom used, support stops there. +// so code wise difficult and as it is seldom used, support stops there. // // To have some support the code uses lowercase for the next 8 levels // treda sorta rinta quexa pepta ocha nena minga luma (1024 ^21 ~~ 10^63) -char * toBytes(double val, uint8_t decimals = 2) +char * toBytes(double value, uint8_t decimals = 2) { - static char buf[12]; - char t[] = " KMGTPEZYXWVUtsrqponml"; - uint8_t i = 0; - if (isinf(val)) + static char buffer[12]; + char t[] = " KMGTPEZYXWVUtsrqponml"; + uint8_t i = 0; // i is index of the array == powers of 1024. + if (isinf(value)) { - strcpy(buf, ""); - return buf; + strcpy(buffer, ""); + return buffer; } - while(val >= 1024) + while(value >= 1024) { - val /= 1024; + value /= 1024; i++; } if (i == 0) decimals = 0; if (decimals > 3) decimals = 3; - // WHOLE PART - int iv = val; - itoa(iv, &buf[0], 10); + // WHOLE PART iv + int integerPart = value; + itoa(integerPart, &buffer[0], 10); // DECIMALS - val -= iv; - uint8_t pos = strlen(buf); + value -= integerPart; + uint8_t pos = strlen(buffer); if (decimals > 0) { - buf[pos++] = '.'; + buffer[pos++] = '.'; while (decimals-- > 0) { - val = val * 10; - buf[pos++] = '0' + int(val); - val -= int(val); + value = value * 10; + buffer[pos++] = '0' + int(value); + value -= int(value); } } // UNITS if (i <= strlen(t)) { - if (i > 0) buf[pos++] = ' '; - buf[pos++] = t[i]; - buf[pos++] = 'B'; - buf[pos] = 0; + if (i > 0) buffer[pos++] = ' '; + buffer[pos++] = t[i]; + buffer[pos++] = 'B'; + buffer[pos] = 0; } else { // TODO e.g. E99 B } - return buf; + return buffer; } diff --git a/libraries/printHelpers/test/unit_test_001.cpp b/libraries/printHelpers/test/unit_test_001.cpp index 5f0ac7c0..d1673e43 100644 --- a/libraries/printHelpers/test/unit_test_001.cpp +++ b/libraries/printHelpers/test/unit_test_001.cpp @@ -48,7 +48,7 @@ unittest_teardown() unittest(test_sci) { - fprintf(stderr, "VERSION: %s\n", PRINTHELPERS_VERSION); + fprintf(stderr, "VERSION: %s\n", (char *) PRINTHELPERS_VERSION); fprintf(stderr, "PRINTBUFFERSIZE: %d\n", PRINTBUFFERSIZE); fprintf(stderr, "%s\n", sci(PI * 1000, 6)); @@ -66,7 +66,7 @@ unittest(test_sci) unittest(test_eng) { - fprintf(stderr, "VERSION: %s\n", PRINTHELPERS_VERSION); + fprintf(stderr, "VERSION: %s\n", (char *) PRINTHELPERS_VERSION); fprintf(stderr, "PRINTBUFFERSIZE: %d\n", PRINTBUFFERSIZE); int32_t value32 = 1UL << 25; @@ -87,7 +87,7 @@ unittest(test_eng) unittest(test_print64) { - fprintf(stderr, "VERSION: %s\n", PRINTHELPERS_VERSION); + fprintf(stderr, "VERSION: %s\n", (char *) PRINTHELPERS_VERSION); fprintf(stderr, "PRINTBUFFERSIZE: %d\n", PRINTBUFFERSIZE); int64_t value64 = 1ULL << 35; @@ -103,7 +103,7 @@ unittest(test_print64) unittest(test_toBytes) { - fprintf(stderr, "VERSION: %s\n", PRINTHELPERS_VERSION); + fprintf(stderr, "VERSION: %s\n", (char *) PRINTHELPERS_VERSION); for (int i = 0; i < 30; i++) {