Having an issue where a float timestamp is sent correctly to the card settings.db file but is truncated on the Transaction request in micropython running on an ESP32. The debug of the request in the REPL shows it correctly as:
,"start_time":1686158220.97,
but immediately after the Transaction it is stored as:
, 'start_time': 1.686158e+09,
losing a very significant amount of precision. I am guessing I can side step by converting to ms integer before sending to the db file but would like to know if I am just using the API incorrectly.
Hey @szygmunt thanks for the question and welcome to the Blues community, we’re glad you’re here!
I’ll take a look into this for you, but can you first provide an example code snippet that I can use to repro the issue?
Thanks. Test code is below. From the notehub interface I sent a test.qi containing:
{"start_time":1686158220.97}
Running the code in micropython on the ESP32 produces the following output:
>>> import bugtest
Opening Notecard...
{"inbound": 10, "outbound": 10, "sync": true, "duration": 60, "req": "hub.set", "body": {"os_family": "ESP32 module with ESP32", "os_platform": "esp32", "os_name": "micropython", "req_interface": "i2c", "agent": "note-python", "req_port": 0, "os_version": "3.4.0; MicroPython v1.20.0 on 2023-04-26"}, "mode": "continuous", "product": "####"}
{}
{"seconds": 600, "mode": "periodic", "req": "card.location.mode"}
{"seconds":600,"mode":"periodic"}
{"file": "test.qi", "req": "note.get"}
{"time":1686241193,"body":{"start_time":1686158220.97}}
+++++RESPONSE BODY+++++>{'time': 1686241193, 'body': {'start_time': 1.686158e+09}}
======>>>>{'start_time': 1.686158e+09}
Start Time: 1.686158e+09 as int 1686158208 as float 1.686158e+09
I was able to sidestep the issue in my code by converting to int first but am confused why the float is not converting correctly. Equipment is a Huzzah32 Feather running MicroPython v1.20.0. Python notecard library was pulled from github recently. Thank you for your help.
import sys, time
import notecard
from machine import I2C,Pin
def NotecardExceptionInfo(exception):
"""Construct a formatted Exception string.
Args:
exception (Exception): An exception object.
Returns:
string: a summary of the exception with line number and details.
"""
name = exception.__class__.__name__
return sys.platform + ": " + name \
+ ": " + ' '.join(map(str, exception.args))
#=========================ENTRY=====================================
productUID = "<<>>"
use_uart = False
try:
i2c_port = I2C(0, scl=Pin(22), sda=Pin(23), freq=400000)
except Exception as exception:
raise Exception("error opening port: "
+ NotecardExceptionInfo(exception))
#===========================+++++===================================
def get_notecard():
print("Opening Notecard...")
try:
card = notecard.OpenI2C(i2c_port, 0, 0, debug=True)
configure_notecard(card)
configure_notecard_gps(card)
return card
except Exception as exception:
raise Exception("error opening notecard: "
+ NotecardExceptionInfo(exception))
def configure_notecard(card):
"""Submit a simple JSON-based request to the Notecard.
Args:
card (object): An instance of the Notecard class
"""
req = {"req": "hub.set"}
req["product"] = productUID
req["mode"] = "continuous"
req["outbound"] = 10
req["inbound"] = 10
req["duration"] = 60
req["sync"] = True
try:
card.Transaction(req)
except Exception as exception:
print("Configure Transaction error: " + NotecardExceptionInfo(exception))
time.sleep(5)
def sync_notecard(card):
"""Submit a simple JSON-based request to the Notecard.
Args:
card (object): An instance of the Notecard class
"""
req = {"req": "hub.sync"}
try:
card.Transaction(req)
except Exception as exception:
print("Sync Transaction error: " + NotecardExceptionInfo(exception))
time.sleep(5)
def configure_notecard_gps(card):
"""Submit a simple JSON-based request to the Notecard.
Args:
card (object): An instance of the Notecard class
"""
req = {"req": "card.location.mode"}
req["mode"] = "periodic"
req["seconds"] = 600
try:
card.Transaction(req)
except Exception as exception:
print("GPS Transaction error: " + NotecardExceptionInfo(exception))
time.sleep(5)
def get_data(card):
try:
req = {"req":"note.get","file":"test.qi"}
rsp = card.Transaction(req)
print(f"+++++RESPONSE BODY+++++>{rsp}")
config = rsp["body"]
print(f"======>>>>{config}")
print(f"Start Time: {config['start_time']} as int {int(config['start_time'])} as float {float(config['start_time'])}")
return config
except KeyError:
print("No notes available")
return None
ltecard = get_notecard()
ltecard = get_data(ltecard)
Has there been any updates that fix this issue? Still having a problem where long floats(timestamps) are truncated unless I convert them to int first. For example, in python the following is sent:
“timestamp”: 1761777578.003553,
But in blues it becomes:
"timestamp": 1761777000
Again, can be worked around by converting to int but keep running into this problem. Is it a known issue? Perhaps my python library is out of date?
Hi @szygmunt
Thanks for waiting for this. I managed to order an ESP32 and looked into this for you. I believe that the issue is actually with how floats are implemented in MicroPython (this may be specific to the ESP32, as here floats have 32 bits of precision).
If you try to run the following code on your ESP32 Huzzah (not using the note-python library), you’ll notice the truncation:
import json
timestamp = 1761777578.003553
print("timestamp:",timestamp)
json_str = json.dumps({"timestamp": timestamp})
print("JSON:", json_str)
parsed = json.loads(json_str)
print("Parsed:", parsed["timestamp"])
print("Match:", parsed["timestamp"] == timestamp)
This is the output from my ESP32:
timestamp: 1761777500.0
1761777500.000000
JSON: {"timestamp": 1761777500.0}
Parsed: 1761777600.0
Match: False
I think the best performing option here would be to continue using integers (i.e. convert your timestamp into microseconds). I am confident it is unrelated to the Notecard or note-python library.
Let us know if that helps!
Thanks,
Alex
For reference, storing and manipulating the timestamp as an Integer:
import json
# Store timestamp as integer microseconds
timestamp_us = 1761777578003553
# For JSON storage - keep as integer
json_str = json.dumps({"timestamp_us": timestamp_us})
parsed = json.loads(json_str)
print("Stored microseconds:", parsed["timestamp_us"])
# When you need seconds, handle integer division:
seconds_int = parsed["timestamp_us"] // 1000000
microseconds_remainder = parsed["timestamp_us"] % 1000000
print(f"Seconds (int): {seconds_int}")
print(f"Microseconds: {microseconds_remainder}")
print(f"Formatted: {seconds_int}.{microseconds_remainder:06d}")
2 Likes
Thank you so much for pointing this out. In hindsight it seems obvious. Will have to double check my float usage to make sure I am not assuming a double anywhere I shouldn’t be.
Just in case someone else runs into this, as an alternative to using ints you can change this line in:
ports/esp32/mpconfigport.h:
// #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE)
which will run in non hardware accelerated double mode which could be a lot slower in some use cases but it does work as expected.
Connected to MicroPython at /dev/cu.usbmodem1101
Use Ctrl-] or Ctrl-x to exit this shell
y on 2025-11-10; FeatherS3 with ESP32-S3
Type “help()” for more information.
import json
timestamp = 1761777578.003553
print(“timestamp:”,timestamp)
timestamp: 1761777578.003553
json_str = json.dumps({“timestamp”: timestamp})
print(“JSON:”, json_str)
JSON: {“timestamp”: 1761777578.003553}
parsed = json.loads(json_str)
print(“Parsed:”, parsed[“timestamp”])
Parsed: 1761777578.003553
print(“Match:”, parsed[“timestamp”] == timestamp)
Match: True
Thanks again for taking the time to dig into this!
2 Likes