Meshtastic events all handled mostly now and outputting to stdout, debugging and testing finished

This commit is contained in:
Dionysus 2024-04-28 04:51:02 -04:00
parent c244fa63fe
commit ce9519afda
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
2 changed files with 99 additions and 45 deletions

View File

@ -1,4 +1,4 @@
# Meshtastic # Meshtastic Utilities
> Experiments with Meshtastic, MQTT, Lora, & more.... > Experiments with Meshtastic, MQTT, Lora, & more....
## WORK-IN-PROGRESS ## WORK-IN-PROGRESS
@ -25,6 +25,7 @@ The goal is to experiment with the possibilities of Python as a means of interfa
## Updates ## Updates
- Threw in an IRC skeleton where the serial controller will interface with. Will have to consider how handle asyncronous comms over serial... - Threw in an IRC skeleton where the serial controller will interface with. Will have to consider how handle asyncronous comms over serial...
- Working reconnection on disconnection! - Working reconnection on disconnection!
- Most events are handled and outputted to stdout, debugging and testing finished
___ ___
###### Mirrors for this repository: [acid.vegas](https://git.acid.vegas/meshtastic) • [SuperNETs](https://git.supernets.org/acidvegas/meshtastic) • [GitHub](https://github.com/acidvegas/meshtastic) • [GitLab](https://gitlab.com/acidvegas/meshtastic) • [Codeberg](https://codeberg.org/acidvegas/meshtastic) ###### Mirrors for this repository: [acid.vegas](https://git.acid.vegas/meshtastic) • [SuperNETs](https://git.supernets.org/acidvegas/meshtastic) • [GitHub](https://github.com/acidvegas/meshtastic) • [GitLab](https://gitlab.com/acidvegas/meshtastic) • [Codeberg](https://codeberg.org/acidvegas/meshtastic)

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# Meshtastic Serial Interface - Developed by Acidvegas in Python (https://git.acid.vegas/meshtastic) # Meshtastic Serial Interface - Developed by Acidvegas in Python (https://git.acid.vegas)
import argparse import argparse
import logging import logging
@ -10,7 +10,7 @@ try:
import meshtastic import meshtastic
from meshtastic.serial_interface import SerialInterface from meshtastic.serial_interface import SerialInterface
from meshtastic.util import findPorts from meshtastic.util import findPorts
from meshtastic.tcp_interface import TCPInterface from meshtastic.tcp_interface import TCPInterface
except ImportError: except ImportError:
raise ImportError('meshtastic library not found (pip install meshtastic)') raise ImportError('meshtastic library not found (pip install meshtastic)')
@ -21,7 +21,7 @@ except ImportError:
# Initialize logging # Initialize logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s | %(levelname)9s | %(funcName)s | %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s | %(levelname)9s | %(funcName)s | %(message)s', datefmt='%Y-%m-%d %I:%M:%S')
def now(): def now():
@ -40,11 +40,11 @@ class MeshtasticClient(object):
def connect(self, option: str, value: str): def connect(self, option: str, value: str):
''' '''
Connect to the Meshtastic interface Connect to the Meshtastic interface
:param option: The interface option to connect to :param option: The interface option to connect to
:param value: The value of the interface option :param value: The value of the interface option
''' '''
while True: while True:
try: try:
if option == 'serial': if option == 'serial':
@ -54,7 +54,7 @@ class MeshtasticClient(object):
else: else:
raise Exception('No serial devices found') raise Exception('No serial devices found')
self.interface = SerialInterface(value) self.interface = SerialInterface(value)
elif option == 'tcp': elif option == 'tcp':
self.interface = TCPInterface(value) self.interface = TCPInterface(value)
@ -91,19 +91,16 @@ class MeshtasticClient(object):
'''Create the Meshtastic callback subscriptions''' '''Create the Meshtastic callback subscriptions'''
pub.subscribe(self.event_connect, 'meshtastic.connection.established') pub.subscribe(self.event_connect, 'meshtastic.connection.established')
pub.subscribe(self.event_data, 'meshtastic.receive.data.portnum')
pub.subscribe(self.event_disconnect, 'meshtastic.connection.lost') pub.subscribe(self.event_disconnect, 'meshtastic.connection.lost')
pub.subscribe(self.event_node, 'meshtastic.node') pub.subscribe(self.event_node, 'meshtastic.node')
pub.subscribe(self.event_packet, 'meshtastic.receive') pub.subscribe(self.event_position, 'meshtastic.receive.position')
pub.subscribe(self.event_text, 'meshtastic.receive.text')
pub.subscribe(self.event_user, 'meshtastic.receive.user')
logging.debug('Listening for Meshtastic events...') logging.debug('Listening for Meshtastic events...')
# The meshtastic.receive topics can be broken down further:
# pub.subscribe(self.on_text, 'meshtastic.receive.text')
# pub.subscribe(self.on_position, 'meshtastic.receive.position')
# pub.subscribe(self.on_user, 'meshtastic.receive.user')
# pub.subscribe(self.on_data, 'meshtastic.receive.data.portnum')
def event_connect(self, interface, topic=pub.AUTO_TOPIC): def event_connect(self, interface, topic=pub.AUTO_TOPIC):
''' '''
Callback function for connection established Callback function for connection established
@ -116,6 +113,17 @@ class MeshtasticClient(object):
logging.info(f'Found a total of {len(self.nodes):,} nodes') logging.info(f'Found a total of {len(self.nodes):,} nodes')
def event_data(self, packet: dict, interface):
'''
Callback function for data updates
:param packet: Data information
:param interface: Meshtastic interface
'''
logging.info(f'Data update: {data}')
def event_disconnect(self, interface, topic=pub.AUTO_TOPIC): def event_disconnect(self, interface, topic=pub.AUTO_TOPIC):
''' '''
Callback function for connection lost Callback function for connection lost
@ -125,15 +133,13 @@ class MeshtasticClient(object):
''' '''
logging.warning('Lost connection to radio!') logging.warning('Lost connection to radio!')
logging.info('Reconnecting in 10 seconds...')
time.sleep(10) time.sleep(10)
# TODO: Consider storing the interface option and value in a class variable since we don't want to reference the args object inside the class # TODO: Consider storing the interface option and value in a class variable since we don't want to reference the args object inside the class
self.connect('serial' if args.serial else 'tcp', args.serial if args.serial else args.tcp) self.connect('serial' if args.serial else 'tcp', args.serial if args.serial else args.tcp)
def event_node(self, node): def event_node(self, node):
''' '''
Callback function for node updates Callback function for node updates
@ -146,30 +152,78 @@ class MeshtasticClient(object):
logging.info(f'Node found: {node["user"]["id"]} - {node["user"]["shortName"].ljust(4)} - {node["user"]["longName"]}') logging.info(f'Node found: {node["user"]["id"]} - {node["user"]["shortName"].ljust(4)} - {node["user"]["longName"]}')
def event_packet(self, packet: dict): def event_position(self, packet: dict, interface):
'''
Callback function for position updates
:param packet: Position information
:param interface: Meshtastic interface
'''
sender = packet['from']
msg = packet['decoded']['payload'].hex()
id = self.nodes[sender]['user']['id'] if sender in self.nodes else '!unk '
name = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK'
longitude = packet['decoded']['position']['longitudeI'] / 1e7
latitude = packet['decoded']['position']['latitudeI'] / 1e7
altitude = packet['decoded']['position']['altitude']
snr = packet['rxSnr']
rssi = packet['rxRssi']
logging.info(f'{id} - {name}: {longitude}, {latitude}, {altitude}m (SNR: {snr}, RSSI: {rssi}) - {msg}')
def event_text(self, packet: dict, interface):
''' '''
Callback function for received packets Callback function for received packets
:param packet: Packet received :param packet: Packet received
''' '''
# Handle incoming text messages sender = packet['from']
if packet['decoded']['portnum'] == 'TEXT_MESSAGE_APP': msg = packet['decoded']['payload'].decode('utf-8')
sender = packet['from'] id = self.nodes[sender]['user']['id'] if sender in self.nodes else '!unk '
msg = packet['decoded']['payload'].decode('utf-8') name = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK'
logging.info(f'{id} - {name}: {msg}')
# Message from self
if sender == self.interface.myInfo.my_node_num:
print(f'{now()} {self.nodes[sender]}: {msg}') # Can do custom formatting here or ignore the message, just an example
# Message from others def event_user(self, packet: dict, interface):
if sender in self.nodes: '''
print(f'{now()} {self.nodes[sender]}: {msg}') Callback function for user updates
# Unknown sender :param user: User information
else: '''
# TODO: Trigger request for node update here
print(f'{now()} UNK: {msg}') '''
{
'from' : 862341900,
'to' : 4294967295,
'decoded' : {
'portnum' : 'NODEINFO_APP',
'payload' : b'\n\t!33664b0c\x12\x08HELLDIVE\x1a\x04H3LL"\x06d\xe83fK\x0c(+8\x03',
'wantResponse' : True,
'user' : {
'id' : '!33664b0c',
'longName' : 'HELLDIVE',
'shortName' : 'H3LL',
'macaddr' : 'ZOgzZksM',
'hwModel' : 'HELTEC_V3',
'role' : 'ROUTER_CLIENT',
'raw' : 'rm this'
}
},
'id' : 1612906268,
'rxTime' : 1714279638,
'rxSnr' : 6.25,
'hopLimit' : 3,
'rxRssi' : -38,
'hopStart' : 3,
'raw' : 'rm this'
}
'''
# Not sure what to do with this yet...
pass
@ -178,11 +232,11 @@ if __name__ == '__main__':
parser.add_argument('--serial', help='Use serial interface') # Typically /dev/ttyUSB0 or /dev/ttyACM0 parser.add_argument('--serial', help='Use serial interface') # Typically /dev/ttyUSB0 or /dev/ttyACM0
parser.add_argument('--tcp', help='Use TCP interface') # Can be an IP address or hostname (meshtastic.local) parser.add_argument('--tcp', help='Use TCP interface') # Can be an IP address or hostname (meshtastic.local)
args = parser.parse_args() args = parser.parse_args()
# Ensure one interface is specified # Ensure one interface is specified
if (not args.serial and not args.tcp) or (args.serial and args.tcp): if (not args.serial and not args.tcp) or (args.serial and args.tcp):
raise SystemExit('Must specify either --serial or --tcp interface') raise SystemExit('Must specify either --serial or --tcp interface')
# Initialize the Meshtastic client # Initialize the Meshtastic client
mesh = MeshtasticClient() mesh = MeshtasticClient()
@ -197,11 +251,10 @@ if __name__ == '__main__':
while True: while True:
time.sleep(60) time.sleep(60)
except KeyboardInterrupt: except KeyboardInterrupt:
pass # Exit the loop on Ctrl+C try:
mesh.interface.close()
except:
pass
finally: finally:
if mesh.interface: logging.info('Connection to radio lost')
try:
mesh.interface.close()
logging.info('Connection to radio interface closed!')
except:
pass