Manually Configured Device - Int turns to float

I've been attempting to establish two-way communications between two manually configured devices and bumped into a couple of issues on the way. The behaviour of the synced variables is not as I expected.

I put together a minimal, one-way test case to illustrate the issue as follows. There are two manually configured Things; one called Producer and the other Consumer, containing integers int_producer and int_consumer respectively. The integers are defined as Read/Write and update on change. They are also synced together. Producer Thing is controlled by a Python script called producer.py and Consumer Thing by consumer.py.

Producer.py increments the value of int_producer every 5 seconds. Consumer.py simply logs any changes to int_consumer to the console.

Both variables are displayed as read/write values on a dashboard.

Both scripts are running on the same Raspberry Pi Zero 2W running Python 3.9.2.

I start by running producer.py and int_producer increments as expected and can be observed in the console and in the dashboard.

I then try to run consumer.py and I get inconsistent behaviour with two possible outcomes.

Fourteen runs out of 20, consumer.py issues an error message in response to a change of value and has to reconnect. Something was expecting an int but got a float. The situation doesn't improve no matter how long I leave the script running. Consumer.py console output is shown below.

Curiously, when failing if int_consumer is display, it's as an integer. See "int_consumer = 8".

pi@RPZ2-001:~/shared/python_dev/arduino_manual $ python consumer.py
21:56:04.364 task: int_consumer created.
21:56:04.365 task: conn_task created.
21:56:04.366 Connecting to Arduino IoT cloud...
21:56:05.889 task: discovery created.
21:56:05.890 task: mqtt_task created.
21:56:05.891 Subscribe: b'/a/d/01f3a0fd-8847-4693-96ac-92c428fb2552/e/i'.
21:56:06.197 task: conn_task complete.
21:56:06.702 Subscribe: b'/a/t/54689861-5cc7-4b5d-9693-261b8787da19/e/i'.
21:56:06.938 Subscribe: b'/a/t/54689861-5cc7-4b5d-9693-261b8787da19/shadow/i'.
21:56:07.424 Device configured via discovery protocol.
21:56:07.526 task: discovery complete.
21:56:09.477 int_consumer = 8
21:56:10.864 task: mqtt_task raised exception: int_consumer set to invalid data type, expected: <class 'int'> got: <class 'float'>.
21:56:10.865 task: conn_task created.
21:56:10.866 Connecting to Arduino IoT cloud...
21:56:11.947 Subscribe: b'/a/t/54689861-5cc7-4b5d-9693-261b8787da19/e/i'.
21:56:12.144 task: mqtt_task created.
21:56:12.195 task: conn_task complete.
21:56:15.877 task: mqtt_task raised exception: int_consumer set to invalid data type, expected: <class 'int'> got: <class 'float'>.
21:56:15.877 task: conn_task created.
21:56:15.878 Connecting to Arduino IoT cloud...
21:56:16.949 Subscribe: b'/a/t/54689861-5cc7-4b5d-9693-261b8787da19/e/i'.
21:56:17.156 task: mqtt_task created.
21:56:17.208 task: conn_task complete.
21:56:20.929 task: mqtt_task raised exception: int_consumer set to invalid data type, expected: <class 'int'> got: <class 'float'>.
21:56:20.930 task: conn_task created.
21:56:20.931 Connecting to Arduino IoT cloud...
21:56:21.914 Subscribe: b'/a/t/54689861-5cc7-4b5d-9693-261b8787da19/e/i'.
21:56:22.090 task: mqtt_task created.
21:56:22.141 task: conn_task complete.
21:56:26.013 task: mqtt_task raised exception: int_consumer set to invalid data type, expected: <class 'int'> got: <class 'float'>.
21:56:26.014 task: conn_task created.

Six runs out of 20, consumer.py behaves more as expected and logs changes to int_consumer to the console, but it has changed to a float.

pi@RPZ2-001:~/shared/python_dev/arduino_manual $ python consumer.py
18:44:35.626 task: int_consumer created.
18:44:35.627 task: conn_task created.
18:44:35.628 Connecting to Arduino IoT cloud...
18:44:36.692 task: discovery created.
18:44:36.693 task: mqtt_task created.
18:44:36.695 Subscribe: b'/a/d/01f3a0fd-8847-4693-96ac-92c428fb2552/e/i'.
18:44:37.026 task: conn_task complete.
18:44:38.146 Subscribe: b'/a/t/54689861-5cc7-4b5d-9693-261b8787da19/e/i'.
18:44:38.365 Subscribe: b'/a/t/54689861-5cc7-4b5d-9693-261b8787da19/shadow/i'.
18:44:38.798 Device configured via discovery protocol.
18:44:38.900 task: discovery complete.
18:44:40.036 int_consumer = 65.0
18:44:40.263 Ignoring cloud initialization for record: int_consumer
18:44:45.078 int_consumer = 66.0
18:44:50.108 int_consumer = 67.0
18:44:55.151 int_consumer = 68.0
18:45:00.180 int_consumer = 69.0

With producer.py running, the values of int_producer appears in its widget, but the int_consumer only updates when I hit the refresh button on the browser.

With producer.py not running, values entered into the int_producer widget have no effect. However, values entered into the int_consumer widget get logged by consumer.py as a float. Further, if I enter a floating point number, say 123.456, into the int_consumer widget, all the fractional digits get logged but not displayed in the widget.

Questions:

Why is int_consumer changing to a float?
Why does a change to float sometimes cause the client to crash and other time not?
Why don't the dashboard value widgets track the changes together?
Why aren't manual changes to int_producer seen by consumer.py when changes typed into int_consumer are?
Why are floating point values accepted by the value widget and passed to consumer.py when the variable is an integer?

The code for the scripts is below should anyone want to chip in.

producer.py

"""
    A simple producer.
    Sends an incrementing integer to an AIoT Cloud variable, int_producer.
"""

import time
import logging
import threading
import sys
sys.path.append("lib")
from arduino_iot_cloud import ArduinoCloudClient

DEVICE_ID  = b"PRODUCR_DEVICE_ID"
SECRET_KEY = b"PRODUCER_SECRET_KEY"

def logging_func():
    logging.basicConfig(
        datefmt="%H:%M:%S",
        format="%(asctime)s.%(msecs)03d %(message)s",
        level=logging.INFO,
    )   

def int_producer_changed(client, value):
    """ This function is executed each time the cloud variable "int_producer" changes """
    int1 = value
    logging.info("int_producer = {}".format(value))

def client_thread_func(client):
    """ This function defines the client thread functionality """
    client.start()

if __name__ == "__main__":

    # Set up stuff here
    logging_func()
    client = ArduinoCloudClient(device_id=DEVICE_ID, username=DEVICE_ID, password=SECRET_KEY)
    client.register("int_producer", value=None, on_write=int_producer_changed)
    client_thread = threading.Thread(target=client_thread_func, args=(client,), daemon=True)
    client_thread.start()
    
    number = 0
    
    while True:
        if client.started:
            number += 1
            client["int_producer"] = number
            logging.info("int_producer set to {}".format(number))
        time.sleep(5)

consumer.py

"""
    A simple consumer.
    Receives updates to a single AIoT Cloud variable, int_consumer.
"""

import time
import logging
import sys
sys.path.append("lib")
from arduino_iot_cloud import ArduinoCloudClient

DEVICE_ID  = b"CONSUMER_DEVICE_ID"
SECRET_KEY = b"CONSUMER_SECRET_KEY"

def logging_func():
    logging.basicConfig(
        datefmt="%H:%M:%S",
        format="%(asctime)s.%(msecs)03d %(message)s",
        level=logging.INFO,
    )   

def int_consumer_changed(client, value):
    """ This function is executed each time the cloud variable "int_consumer" changes """
    logging.info("int_consumer = {}".format(value))

logging_func()
client = ArduinoCloudClient(device_id=DEVICE_ID, username=DEVICE_ID, password=SECRET_KEY)
client.register("int_consumer", value=None, on_write=int_consumer_changed)
client.start()

Thanks for posting.

The team is analyzing the potential issue.

1 Like

Good news @robertics !

The team has fixed the bug.

Could you please try again and check if it works for you?

Hi @dbeamonte_arduino, That seems to have fixed the int-to-float issue. Values from producer.py are arriving at the consumer.py without problems. I also restarted it twenty times and it booted correctly every time. That looks like a goer to me. My thanks to you and The Team.

Device-to-device value changes seem to work perfectly now, however, I still don't quite understand the behaviour of dashboard widgets displaying/controlling synchronised variables. Maybe I just don't understand the intention.

I'd have thought that a value change to one of the variables, either by a device or manual input, should be displayed in the other, albeit after a processing delay. Is that how it should work, or do dashboard widgets work differently for synchronised variables?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.