Meshtastic events all handled mostly now and outputting to stdout, debugging and testing finished
This commit is contained in:
parent
c244fa63fe
commit
ce9519afda
@ -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)
|
@ -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
|
|
||||||
|
Loading…
Reference in New Issue
Block a user