Sending MQTT topic as bytestring to ESP8266/32 modules?

So the MQTT support in MicroPython for ESP8266 and ESP32 modules is a bit unique. The MQTT topic needs to be 'b'string, i.e. a bytestring per python notation.

I have been successful publishing MQTT messages from MicroPython on an ESP module to NodeRed. I just need to encode the topic as utf-8, and the payload, i.e. message as JSON format string. Of course, I am converting the JSON message to JS object.

But when I publish a topic and message from NodeRed, it never is (apparently) received on MicroPython based ESP module. I am pretty sure I am not formatting the topic right? How should I encode the topic string from the NodeRed side?

If I use MQTT to publish and subscribe in MicroPython on ESPs modules, that works. So I know the MicroPython subscribing code is valid, as well as the MQTT message callback in MicroPython on the ESP module.

This is what I do at the ESP32-S2-Mini end to publish and subscribe to Node-RED via MQTT.
Let's assume it's a node in my garage. I run a number of async functions to collect readings/data that are placed in Python dictionary. When its time to send a report I collect the appropriate values from the Python dictionary and structure a report (making use of json.dumps) to send to Node-RED.

mqtt_broker_prefix = skynet
baseID = garage

publish_message = f'{mqtt_broker_prefix}/{baseID}/message'
print(f'Publish path for messages: {publish_message}')
subscribe_to_directives = f'{mqtt_broker_prefix}/{baseID}/directives/#'

from umqttsimple import MQTTClient
import json

# Loads of other stuff goes in here...

def publish_analog_report():
    publish_topic = f'{mqtt_broker_prefix}/{baseID}/B/reports'
    location = node_details["location"]
    lat = node_details["lat"]
    lon = node_details["lon"]
    map_color = node_details["map_color"]
                
    # Create a dictionary
    json_data = {
        "report": "Analog reading Vin, Vreg",
        "baseID": baseID,
        "nodeID": "B",
        "mode": "Base Station",
        "location": location,
        "lat": lat,
        "lon": lon,
        "map_color": map_color,
        "Vin": Vin,
        "Vreg": Vreg
        }
        # Convert dictionary to JSON string
    #print("send_analog_report ", json_data)
    json_message = json.dumps(json_data)
    mqtt_client.publish(publish_topic, json_message)

#collect analog readings
publish_analog_report()
etc....

In my mqtt_callback I have this...

# MQTT callback function to handle incoming messages
def mqtt_callback(topic, msg):
    print(f'Received message on topic: {topic}')
    
    my_topic = str(topic)
    parts = my_topic.split('/')
    base_part = parts[1]
    node_part = parts[2]
    
    mqtt_directives = dict(json.loads(msg.decode("utf-8")))
    print(f'Recd directive is: {mqtt_directives}')

At the Node-RED end, I use this JavaScript in a function node that connects to a MQTT-Out node.

let baseID = flow.get("baseID");

msg.payload = {"set_named":"closed","state":"0"};

msg.topic = `skynet/${baseID}/directives`;

return msg;
1 Like

Thanks for the example...

So if I read this write, you just set msg.topic to simple text string, nothing special when you publish to a subscriber on the ESP module has established. Humm.... so I have something goofed up since my callback never seems to fire. I was thinking the topic string was malformed or wrong format, hence why my callback never fires.

I am calling check_msg() routinely in a loop, so I know the callback should be getting cycles.

I know I have NodeRed working 100% in that it can publish/subscribe to its-self. So the issue has to be on the ESP side. I can publish from the ESP to NodeRed, so it really comes down to the subscriber not existing correctly, it would seem.

Found the problem... on my publish node in NodeRed I had enabled 'Format JSON string' (in error) and that was goofing up the topic/message ESP was receiving. Turned that off, and ESP callback is firing as expected with the topic received.

1 Like

The other thing to do, unless you have already done it, is to set the output option on the MQTT-In node in Node-RED to this... (as it saves having to use a JSON conversion node in your flow)

Yes, I do that as well.