From d4a641ff8af1b413c8fb96a670282b799be1f6e8 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sun, 13 Sep 2026 17:24:56 -0700 Subject: [PATCH] Keep STM32 text conversion compatible with current Arduino cores --- .github/workflows/run-unit-tests.yml | 1 + src/helpers/TxtDataHelpers.cpp | 6 ++++ test/test_stm32_float_conversion.py | 49 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 test/test_stm32_float_conversion.py diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index 65de6abe..e3ba8c5d 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -94,6 +94,7 @@ jobs: run: | python3 -B test/test_companion_usb_default.py python3 -B test/test_esp32_usb_serial_hygiene.py + python3 -B test/test_stm32_float_conversion.py - name: Verify message reader buttons, touch targets, and footer layouts working-directory: test diff --git a/src/helpers/TxtDataHelpers.cpp b/src/helpers/TxtDataHelpers.cpp index d3063a9e..68b4a710 100644 --- a/src/helpers/TxtDataHelpers.cpp +++ b/src/helpers/TxtDataHelpers.cpp @@ -118,7 +118,13 @@ static void _ftoa(float f, char *p, int *status) *p++ = '0'; else { +#if defined(STM32_PLATFORM) + // STM32 Arduino exposes utoa, but recent cores no longer declare ltoa. + // The sign was emitted above and this magnitude fits a 32-bit unsigned int. + utoa(static_cast(int_part), p, 10); +#else ltoa(int_part, p, 10); +#endif while (*p) p++; } diff --git a/test/test_stm32_float_conversion.py b/test/test_stm32_float_conversion.py new file mode 100644 index 00000000..ee3ff7d8 --- /dev/null +++ b/test/test_stm32_float_conversion.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Compile the real text helpers with STM32's utoa-only Arduino API.""" +from pathlib import Path +import subprocess +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] + + +class Stm32FloatConversionTest(unittest.TestCase): + def test_signed_values_and_limits_without_ltoa(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / "Arduino.h").write_text(""" +#pragma once +#include +#include +inline char* utoa(unsigned int value, char* output, int base) { + if (base != 10) abort(); + sprintf(output, "%u", value); + return output; +} +""") + (root / "test.cpp").write_text(""" +#include +#include +#include "helpers/TxtDataHelpers.h" +int main() { + assert(strcmp(StrHelper::ftoa(0), "0.0") == 0); + assert(strcmp(StrHelper::ftoa(1), "1.0") == 0); + assert(strcmp(StrHelper::ftoa(-2.5f), "-2.5") == 0); + assert(strcmp(StrHelper::ftoa(62.5f), "62.5") == 0); + assert(strcmp(StrHelper::ftoa(1.125f), "1.125") == 0); + assert(strcmp(StrHelper::ftoa(2147483520.0f), "2147483520.0") == 0); + assert(strcmp(StrHelper::ftoa(-2147483520.0f), "-2147483520.0") == 0); + assert(strcmp(StrHelper::ftoa(2147483648.0f), "0") == 0); + assert(strcmp(StrHelper::ftoa(1e-12f), "0") == 0); +} +""") + binary = root / "test" + subprocess.run(["g++", "-std=c++17", "-DSTM32_PLATFORM", "-I", str(root), + "-I", str(ROOT / "src"), str(root / "test.cpp"), + str(ROOT / "src/helpers/TxtDataHelpers.cpp"), "-o", str(binary)], check=True) + subprocess.run([str(binary)], check=True) + + +if __name__ == "__main__": + unittest.main()