i have problem to subscribe 2 (and more) ESP8266 to one RPi3 broker.
I used:
import paho.mqtt.client as mqtt
import datetime
mqtt_topics = ["esp8266-1", "esp8266-2"]
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
for topic in mqtt_topics:
client.subscribe(topic)
def on_message(client, userdata, msg):
print(datetime.datetime.now())
print(str(msg.topic)+" "+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.connect('localhost', 1883, 60)
try:
for topic in mqtt_topics:
client.on_message = on_message
client.loop_forever()
except KeyboardInterrupt:
print("CTRL-C: Terminating program.")
It works but ony to 1 esp.
If i connect esp8266-1 it checks it values, then i connect esp8266-2 to have its values and esp8266-1 is no longer available (even if i turn of esp8266-2).
How to subscribe both esp8266?
I only can have one of them at time.
The code needs few changes:
import paho.mqtt.client as mqtt
import datetime
mqtt_topics = [("esp8266-1",0), ("esp8266-2",0)]
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe(mqtt_topics)
def on_message(client, userdata, msg):
print(datetime.datetime.now())
print(str(msg.topic)+" "+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect('localhost', 1883, 60)
try:
client.loop_forever()
except KeyboardInterrupt:
print("CTRL-C: Terminating program.")
But if one client works and a second forces the first to disconnect then it sounds like you have the same clientID for both clients. clientIDs need to be unique for all client.
Related
I am trying to get my head around UMQTT.simple. I am looking to handle instances in which my server might disconnect for a reboot. I want to check whether the client is connected, and if not, wait some period and try to reconnect.The guidance seems to be to use client.ping() for this (How to check Micropython umqtt client is connected?).
For the MQTT.paho client I see there is a way to access ping responses in the logs function (see here: http://www.steves-internet-guide.com/mqtt-keep-alive-by-example/). For UMQTT the docs indicate that ping response is handled automatically by wait_msg(): Ping server (response is automatically handled by wait_msg() (https://mpython.readthedocs.io/en/master/library/mPython/umqtt.simple.html). There does not appear to be any analogous logs function mentioned in the UMQTT.simple docs.
This is confounding for a couple of reasons:
If i use client.wait_msg() how do I call client.ping()? client.wait_msg() is a blocking function, so I can't make the ping. The system just disconnects when the keepalive time is reached.
If I call client.check_msg(), and client.ping() intermittently, I can't access the callback. My callback function doesn't have parameters to access pingresponse (params are f(topic, msg) in the docs).
The way I am solving this for now is to set a bunch of try-except calls on my client.connect and then connect-subscribe functions, but its quite verbose. Is this the way to handle or can i take advantage of the pingresponse in UMQTT.simple?
Below is a sample of the code i am running:
#Set broker variables and login credentials
#Connect to the network
#write the subscribe call back
def sub_cb(topic, msg):
print((topic, msg))
#write a function that handles connecting and subscribing
def connect_and_subscribe():
global CLIENT_NAME, BROKER_IP, USER, PASSWORD, TOPIC
client = MQTTClient(client_id=CLIENT_NAME,
server=BROKER_IP,
user=USER,
password=PASSWORD,
keepalive=60)
client.set_callback(sub_cb)
client.connect()
client.subscribe(TOPIC)
print('Connected to MQTT broker at: %s, subscribed to %s topic' % (BROKER_IP, TOPIC))
return(client) #return the client so that i can do stuff with it
client = connect_and_subscribe()
#Check messages
now = time.time()
while True:
try:
client.check_msg()
except OSError as message_error: #except if disconnected and check_msg() fails
if message_error == -1:
time.sleep(30) #wait for reboot
try:
client = connect_and_subscribe() #Try connect again to the server
except OSError as connect_error: #If the server is still down
time.sleep(30) #wait and try again
try:
client = connect_and_subscribe()
except:
quit() #Quite so that i don't get stuck in a loop
time.sleep(0.1)
if time.time() - now > 80: #ping to keepalive (60 * 1.5)
client.ping()
now = time.time() #reset the timer
Not able to figure out what is wrong when I give address as 127.0.0.3(TimeoutError), whereas the following code works fine when the addr is 127.0.0.1(ConnectionRefusedError). Note that in both the cases, the server is not up.
Can anyone elaborate why different errors are being shown up here, and ConnectionResfusedError is quick, whereas the TimeoutError takes time.
import asyncio
async def handle_connection(addr, port, message):
reader, writer = await asyncio.open_connection(addr, port)
print(f'Send: {message!r}')
writer.write(message.encode())
data = await reader.read(100)
print(f'Received: {data.decode()!r}')
print('Close the connection')
writer.close()
if __name__ == "__main__":
addr = '127.0.0.3'
port = 8888
try:
asyncio.run(handle_connection(addr, port, 'request'))
except Exception as err:
print(f'Error: {err}'
I want to connect my KaaIOT cloud and subscribe a topic to show result from terminal. I do not know where to get the topic name to subscribe with. I had read the KaaIOT documentation but still cannot have a clear idea on it. Could someone help me with a sample code for me to reference?
KaaIOT Information
appVersion.name: c184ijqrqa51q5haskp0-v1
appVersion.registeredDate: 2021-03-16T05:59:54.185Z
createdDate: 2021-03-16T05:59:54.186Z
endpointId: fc2c5833-77c5-445a-89a0-9b0e7498c048
model: Raspberry Pi (192.168.0.171)
metadataUpdatedDate: 2021-03-17T09:13:01.809Z
Sample Code
import itertools
import json
import queue
import random
import string
import sys
import time
import paho.mqtt.client as mqtt
KPC_HOST = "mqtt.cloud.kaaiot.com" # Kaa Cloud plain MQTT host
KPC_PORT = 1883 # Kaa Cloud plain MQTT port
APPLICATION_VERSION = "" # Paste your application version
ENDPOINT_TOKEN = "" # Paste your endpoint token
class MetadataClient:
def __init__(self, client):
self.client = client
self.metadata_by_request_id = {}
self.global_request_id = itertools.count()
get_metadata_subscribe_topic = f'kp1/{APPLICATION_VERSION}/epmx/{ENDPOINT_TOKEN}/get/#'
self.client.message_callback_add(get_metadata_subscribe_topic, self.handle_metadata)
def handle_metadata(self, client, userdata, message):
request_id = int(message.topic.split('/')[-2])
if message.topic.split('/')[-1] == 'status' and request_id in self.metadata_by_request_id:
print(f'<--- Received metadata response on topic {message.topic}')
metadata_queue = self.metadata_by_request_id[request_id]
metadata_queue.put_nowait(message.payload)
else:
print(f'<--- Received bad metadata response on topic {message.topic}:\n{str(message.payload.decode("utf-8"))}')
def get_metadata(self):
request_id = next(self.global_request_id)
get_metadata_publish_topic = f'kp1/{APPLICATION_VERSION}/epmx/{ENDPOINT_TOKEN}/get/{request_id}'
metadata_queue = queue.Queue()
self.metadata_by_request_id[request_id] = metadata_queue
print(f'---> Requesting metadata by topic {get_metadata_publish_topic}')
self.client.publish(topic=get_metadata_publish_topic, payload=json.dumps({}))
try:
metadata = metadata_queue.get(True, 5)
del self.metadata_by_request_id[request_id]
return str(metadata.decode("utf-8"))
except queue.Empty:
print('Timed out waiting for metadata response from server')
sys.exit()
def patch_metadata_unconfirmed(self, metadata):
partial_metadata_udpate_publish_topic = f'kp1/{APPLICATION_VERSION}/epmx/{ENDPOINT_TOKEN}/update/keys'
print(f'---> Reporting metadata on topic {partial_metadata_udpate_publish_topic}\nwith payload {metadata}')
self.client.publish(topic=partial_metadata_udpate_publish_topic, payload=metadata)
def main():
# Initiate server connection
print(f'Connecting to Kaa server at {KPC_HOST}:{KPC_PORT} using application version {APPLICATION_VERSION} and endpoint token {ENDPOINT_TOKEN}')
client_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6))
client = mqtt.Client(client_id=client_id)
client.connect(KPC_HOST, KPC_PORT, 60)
client.loop_start()
metadata_client = MetadataClient(client)
# Fetch current endpoint metadata attributes
retrieved_metadata = metadata_client.get_metadata()
print(f'Retrieved metadata from server: {retrieved_metadata}')
# Do a partial endpoint metadata update
metadata_to_report = json.dumps({"model": "BFG 9000", "mac": "00-14-22-01-23-45"})
metadata_client.patch_metadata_unconfirmed(metadata_to_report)
time.sleep(5)
client.disconnect()
if __name__ == '__main__':
main()
I have established a communication with EMQX broker by using MQTT protocol. But I don't know about KaaIoT much but this might help you. As I went through your code, I didn't see the part where you have subscribed to the topic(correct me if I am wrong). You can refer this. I have implemented the sub pub model and below is the subscriber code which runs fine for EMQX broker. You can try it for KaaIoT.
import paho.mqtt.client as mqtt
import time
import logging
def on_connect(client, userdata, flags, rc):
logging.info("Connected flags"+str(flags)+"result code " + str(rc)+ "client1_id")
client.connected_flag=True
def on_message(client, userdata, message):
print("Received message: " ,str(message.payload.decode("utf-8")))
def on_disconnect(client, userdata, rc):
if rc != 0:
print("Unexpected MQTT disconnection. Will auto-reconnect")
client = mqtt.Client('''Your client id string''')
client.connect("mqtt.cloud.kaaiot.com", 1883, 60)
client.subscribe('''Your topic name (mentioned where data is published)''',qos=1)
client.on_connect = on_connect
client.on_message=on_message
client.on_disconnect = on_disconnect
client.loop_forever()
hope you are doing it right these days.
To summarize my problem, I think this is not working becuase I am using a free Twilio account instead of a paid one. But that's just my beginner theory. Now, the issue:
I have tried an official Twilio tutorial (https://www.twilio.com/blog/automating-ngrok-python-twilio-applications-pyngrok, I shared the link in case someone finds it interesting or needs it), which allows us to automate SMS webhook (sms_url) configuration by using Client (twilio) and pyngrok (ngrok).
def start_ngrok():
from twilio.rest import Client
from pyngrok import ngrok
url = ngrok.connect(5000)
print(' * Tunnel URL:', url)
client = Client()
client.incoming_phone_numbers.list(
phone_number=os.environ.get('TWILIO_PHONE_NUMBER'))[0].update(
sms_url=url + '/bot')
I can't explain all the things that I tried in the last 4 days, with no success. I keep getting the same error:
client.incoming_phone_numbers.list(phone_number=os.environ.get('TWILIO_PHONE_NUMBER'))[0].update(sms_url=url + '/bot')
IndexError: list index out of range
Something is not working with the list, it comes empty, although environment variables are working properly. I will work with just one phone_number, so there no need for list, indeed, so I started to change that line to avoid different errors and ended up with this:
def start_ngrok():
from twilio.rest import Client
from pyngrok import ngrok
url = ngrok.connect(5000)
print(' * Tunnel URL:', url)
client = Client()
client.incoming_phone_numbers("my_number").update(sms_url=str(url) + '/bot')
Then I got the final error that I can't solve by my self:
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__init__.py", line 442, in update
payload = self._version.update(method='POST', uri=self._uri, data=data, )
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/twilio/base/version.py", line 106, in update
raise self.exception(method, uri, response, 'Unable to update record')
twilio.base.exceptions.TwilioRestException:
HTTP Error Your request was:
POST /Accounts/my_account_SID/IncomingPhoneNumbers/+my_number.json
Twilio returned the following information:
Unable to update record: The requested resource /2010-04-01/Accounts/my_account_SID/IncomingPhoneNumbers/+my_number.json was not found
More information may be available here:
https://www.twilio.com/docs/errors/20404
I tried all different phone numbers combinations/formats: nothing works.
Thanks for your time reading all this!
Looks like something changed since the blog was written or there was a mistake.
Try the below:
The only difference is adding .public_url to the url object. Also allowed a GET to /bot for testing.
from dotenv import load_dotenv
from flask import Flask, request
from twilio.twiml.messaging_response import MessagingResponse
load_dotenv()
app = Flask(__name__)
#app.route('/bot', methods=['POST','GET'])
def bot():
user = request.values.get('From', '')
resp = MessagingResponse()
resp.message(f'Hello, {user}, thank you for your message!')
return str(resp)
def start_ngrok():
from twilio.rest import Client
from pyngrok import ngrok
url = ngrok.connect(5000)
print('This is',url)
print(' * Tunnel URL:', url)
client = Client()
client.incoming_phone_numbers.list(
phone_number=os.environ.get('TWILIO_PHONE_NUMBER'))[0].update(
sms_url=url.public_url + '/bot')
if __name__ == '__main__':
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
start_ngrok()
app.run(debug=True)
I try to receive simple text values from external MQTT broker topics with IoT Gateway.
For this purpose I simplify the existing script (extensions/mqtt/custom_mqtt_uplink_converter.py):
from thingsboard_gateway.connectors.mqtt.mqtt_uplink_converter import MqttUplinkConverter, log
class CustomMqttUplinkConverter(MqttUplinkConverter):
def __init__(self, config):
self.__config = config.get('converter')
self.dict_result = {}
def convert(self, topic, body):
try:
log.debug("New data received: %s: %s" % (topic,body))
# if topic = '/devices/buzzer/controls/volume' device name will be 'buzzer'.
self.dict_result["deviceName"] = topic.split("/")[2]
# just hardcode this
self.dict_result["deviceType"] = "buzzer"
self.dict_result["telemetry"] = {"data": body}
log.debug("Result: %s" % (self.dict_result))
return self.dict_result
except ...
When I start gateway I see in his log that he successfully connected and read the values:
INFO ... MQTT Broker Connector connected to 10.1.1.2:1883 - successfully.'
DEBUG ... Client <paho.mqtt.client.Client object at 0x7fb42d19dd68>, userdata None, flags {'session present': 0}, extra_params ()'
DEBUG ... <module 'CustomMqttUplinkConverter' from '/var/lib/thingsboard_gateway/extensions/mqtt/custom_mqtt_uplink_converter.py'>'
DEBUG ... Import CustomMqttUplinkConverter from /var/lib/thingsboard_gateway/extensions/mqtt.'
DEBUG ... Converter CustomMqttUplinkConverter for topic /devices/buzzer/controls/volume - found!'
INFO ... Connector "MQTT Broker Connector" subscribe to /devices/buzzer/controls/volume'
DEBUG ... Received data: {}'
DEBUG ... (None,)'
INFO ... "MQTT Broker Connector" subscription success to topic /devices/buzzer/controls/volume, subscription message id = 1'
DEBUG ... New data received: /devices/buzzer/controls/volume: 66'
DEBUG ... Result: {'deviceName': 'buzzer', 'deviceType': 'buzzer', 'telemetry': {'data': 66}}'
But this values are the last values he can read. If I change volume one broker new values will not appear neither in the log nor in TB UI. (I control updates with mosquitto_sub.)
Seems this converter will never called again until gateway restarted. Is it correct behaveour?
How can I make sure that my code is correct if I don't see the result?
Hi I have tried your version of the custom converter, it didn't work, but when I changed
self.dict_result["telemetry"] = {"data": body}
to
self.dict_result["telemetry"] = [{"data": body}]
It sent data correctly.
The gateway requires an array of telemetry of attributes from the converter.