Brokenpipe issue when using TCP connection
I'm using the python API to talk to a Meshtastic instance running on localhost. this is a Raspberry Pi 3B, if that matters.
Every so often, it gets the following error:
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
self.run()
File "/usr/lib/python3.11/threading.py", line 1394, in run
self.function(*self.args, **self.kwargs)
File "/home/pi/meshtastic/lib/python3.11/site-packages/meshtastic/mesh_interface.py", line 1116, in callback
self.sendHeartbeat()
File "/home/pi/meshtastic/lib/python3.11/site-packages/meshtastic/mesh_interface.py", line 1105, in sendHeartbeat
self._sendToRadio(p)
File "/home/pi/meshtastic/lib/python3.11/site-packages/meshtastic/mesh_interface.py", line 1180, in _sendToRadio
self._sendToRadioImpl(toRadio)
File "/home/pi/meshtastic/lib/python3.11/site-packages/meshtastic/stream_interface.py", line 120, in _sendToRadioImpl
self._writeBytes(header + b)
File "/home/pi/meshtastic/lib/python3.11/site-packages/meshtastic/tcp_interface.py", line 79, in _writeBytes
self.socket.send(b)
BrokenPipeError: [Errno 32] Broken pipe
I saw the following article and tried the meshtastic.connection.lost, but that does not get called when this error happens.
https://github.com/meshtastic/python/issues/727
using try/except doesn't help because the error happens in a thread that the Meshtastic python lib kicks off.
the following property, doesn't change either when this error happens:
interface.isConnected
Looking at the python code, I don't see any error handling for when _writeBytes fails.
Any thoughts on how best to handle this error?
Brokenpipe is a common issue. In a more general sense, the meshtastic code needs to built with the same design criteria as all embedded code--a graceful exit from every possible error.
When we stick a solar-powered device high in a tree and turn off WiFi to conserve power, we shouldn't have to climb the tree to retrive the device, open the enclosure and touch the reset button. the device needs to recover, reset, and resume.
I ended up getting around this by doing the following. The code reaches in the interface object and uses socket calls to get the status directly from the network socket. Not the best, but works.
def isSocketConnected(sock):
r, _, _ = select.select([sock], [], [], 0)
if r:
data = sock.recv(1, socket.MSG_PEEK)
if not data:
return False
return True
try:
#this line connects to meshtasticd
interface = meshtastic.tcp_interface.TCPInterface(hostname='localhost')
while True:
time.sleep(2)
#keep an eye on the socket to see if it closed
if isSocketConnected(interface.socket):
None
#print("Socket is connected")
else:
print("Socket is disconnected")
interface.close()
exit(4)
except :
print("meshtastic.tcp_interface.TCPInterface failed")
I am using pub_sub_example2.py as a template, you can add the following lines to your code
First, import os. This will be used to close the program
Then create a function and variable to handle connection loss.
continueFlag = True
def onConnectionLoss(interface):
continueFlag = False
Then subscribe to "meshtastic.connection.lost"
pub.subscribe(onConnectionLoss, "meshtastic.connection.lost")
On your while loop you can use continue_function variable as a flag
while continueFlag:
time.sleep(1)
Full sample
"""Simple program to demo how to use meshtastic library.
To run: python examples/pub_sub_example2.py
"""
import sys
import time
from pubsub import pub
import meshtastic
import meshtastic.tcp_interface
# simple arg check
if len(sys.argv) < 2:
print(f"usage: {sys.argv[0]} host")
sys.exit(1)
continueFlag = True
def onReceive(packet, interface): # pylint: disable=unused-argument
"""called when a packet arrives"""
print(f"Received: {packet}")
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
"""called when we (re)connect to the radio"""
# defaults to broadcast, specify a destination ID if you wish
interface.sendText("hello mesh")
def onConnectionLoss(interface):
continueFlag=False
pub.subscribe(onReceive, "meshtastic.receive")
pub.subscribe(onConnection, "meshtastic.connection.established")
pub.subscribe(onConnectionLoss, "meshtastic.connection.lost")
try:
iface = meshtastic.tcp_interface.TCPInterface(hostname=sys.argv[1])
while continueFlag:
time.sleep(1)
iface.close()
except Exception as ex:
print(f"Error: Could not connect to {sys.argv[1]} {ex}")
sys.exit(1)