Added documentation on firmware hacking & hardware provisioning, started working on the MQTT interface
This commit is contained in:
parent
e0754f1f02
commit
d9ec08e912
@ -28,7 +28,6 @@
|
||||
|
||||
As far as I know, at the time of writing this, the onyl way to change the Ringtone is from the App...
|
||||
|
||||
|
||||
## Compile & flash firmware
|
||||
- Select `PlatformIO: Pick Project Environment` & select your board.
|
||||
- Run `PLatformIO: Build` to compile the firmware.
|
15
README.md
15
README.md
@ -1,28 +1,25 @@
|
||||
# Meshtastic Utilities
|
||||
> Experiments with Meshtastic, MQTT, Lora, & more....
|
||||
|
||||
## WORK-IN-PROGRESS
|
||||
|
||||
## Information
|
||||
This repository serves as a collection of resources created in my journey to learn & utilize [LoRa](https://en.wikipedia.org/wiki/LoRa) based communications with [Meshtastic](https://meshtastic.org).
|
||||
|
||||
The goal here is to create simple & clean modules to interface with the hardware in a way that can be used to expand the possibilities of the devices capabilities.
|
||||
|
||||
The hardware I am experimenting with: [Lilygo T-Deck](https://www.lilygo.cc/products/t-deck), [Lilygo T-Beam](https://www.lilygo.cc/products/t-beam-v1-1-esp32-lora-module), [Heltec Lora 32 v3](https://heltec.org/project/wifi-lora-32-v3/), and [RAK Wireless 4631](https://store.rakwireless.com/products/wisblock-core-modules?variant=42440631419078)
|
||||
|
||||
## Notes to self & developers
|
||||
- The node id formula is: f'!{hex(node_num)[2:]}'
|
||||
## Documentation
|
||||
- [Firmware Hacks & Customization](./FIRMWARE.md)
|
||||
- [Setup Hardware](./SETUP.md)
|
||||
|
||||
## Hardware & Software related Issues
|
||||
|
||||
## Bugs & Issues
|
||||
- T-Deck must have Wifi turned off when going mobile. Upon leaving my house with WiFi still enabled, the UI & connection was EXTREMELY laggy & poor. Couldn't even type well...
|
||||
- T-Deck using a custom MQTT with TLS & auth will cause a reboot loop *(Need to fix this ASAP)*
|
||||
- `event_node` event is called **AS** we are defining the interface, so using `self.interface` in that callback will error.
|
||||
|
||||
## Roadmap
|
||||
- Asyncronous meshtastic interface *(Priority)*
|
||||
- Asyncronous meshtastic interface
|
||||
- MQTT interface with working decryption
|
||||
- Documentation on MQTT bridging for high availability
|
||||
- Create a simple setup script to provision new devices over serial
|
||||
- Bridge for IRC to allow channel messages to relay over Meshtastic & all Meshtastic events to relay into IRC. *(IRC to Meshtastic will require a command like `!mesh <message here>` to avoid overloading the traffic over LoRa)*
|
||||
|
||||
___
|
||||
|
41
SETUP.md
Normal file
41
SETUP.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Setup Hardware
|
||||
|
||||
It is recommended that you provision your hardware using the serial interface over USB by using the [Meshtastic CLI Tool](https://pypi.org/project/meshtastic/). This is very specific because currently, at the time of writing this repository, changes made via the [web interface](https://client.meshtastic.org) do not work sometimes. When you "save" your settings, the device will reboot with the old settings still. I have had zero issues making changes over serial with the CLI interface.
|
||||
|
||||
- `pip install meshtastic` to install the CLI tool
|
||||
- Plug in your device *(Make sure the USB cable you are using allows data transfer & not just power)*
|
||||
- Run the commands below *(Each command will make the device reboot after setting it)*
|
||||
|
||||
###### NAME
|
||||
```
|
||||
meshtastic --set-owner 'CHANGEME' --set-owner-short 'CHNG'
|
||||
```
|
||||
|
||||
**Note:** Short name can only be 4 alphanumeric characters.
|
||||
|
||||
###### LORA
|
||||
```
|
||||
meshtastic --set lora.region US
|
||||
```
|
||||
|
||||
###### CHANNEL
|
||||
```
|
||||
meshtastic --ch-set name "SUPERNETS" --ch-set psk "CHANGEME" --ch-set uplink_enabled true --ch-set downlink_enabled true --ch-index 0
|
||||
```
|
||||
|
||||
###### WIFI
|
||||
```
|
||||
meshtastic --set network.wifi_enabled true --set network.wifi_ssid "CHANGEME" --set network.wifi_psk "CHANGEME"
|
||||
```
|
||||
|
||||
###### MQTT
|
||||
```
|
||||
meshtastic --set mqtt.enabled true --set mqtt.address "CHANGEME" --set mqtt.username "CHANGEME" --set mqtt.password "CHANGEME" --set mqtt.tls_enabled true --set mqtt.root "msh/CHANGEME" --set mqtt.encryption_enabled true
|
||||
```
|
||||
|
||||
###### BLUETOOTH
|
||||
```
|
||||
meshtastic --set bluetooth.enabled true
|
||||
```
|
||||
|
||||
**Note:** Only enable this on devices are not using Wifi *(mobile devices)* because with ESP32 chips, I don't think Wifi & Bluetooth can function side-by-side together.
|
@ -8,9 +8,6 @@ import time
|
||||
|
||||
try:
|
||||
import meshtastic
|
||||
from meshtastic.serial_interface import SerialInterface
|
||||
from meshtastic.util import findPorts
|
||||
from meshtastic.tcp_interface import TCPInterface
|
||||
except ImportError:
|
||||
raise ImportError('meshtastic library not found (pip install meshtastic)')
|
||||
|
||||
@ -24,12 +21,27 @@ except ImportError:
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s | %(levelname)9s | %(funcName)s | %(message)s', datefmt='%Y-%m-%d %I:%M:%S')
|
||||
|
||||
|
||||
def now():
|
||||
'''Returns the current date and time in a formatted string'''
|
||||
|
||||
return time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
|
||||
class MeshtasticClient(object):
|
||||
def __init__(self):
|
||||
def __init__(self, option: str, value: str):
|
||||
self.interface = None
|
||||
self.me = {}
|
||||
self.nodes = {}
|
||||
|
||||
self.interface_option = option
|
||||
self.interface_value = value
|
||||
|
||||
if self.interface_option == 'serial':
|
||||
from meshtastic.serial_interface import SerialInterface as MeshInterface
|
||||
from meshtastic.util import findPorts
|
||||
elif self.interface_option == 'tcp':
|
||||
from meshtastic.tcp_interface import TCPInterface as MeshInterface
|
||||
|
||||
|
||||
def connect(self, option: str, value: str):
|
||||
'''
|
||||
@ -56,16 +68,16 @@ class MeshtasticClient(object):
|
||||
raise SystemExit('Invalid interface option')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f'Failed to connect to the Meshtastic interface: {e}')
|
||||
logging.error('Retrying in 10 seconds...')
|
||||
time.sleep(10)
|
||||
logging.error(f'Failed to connect to the radio: {e}')
|
||||
logging.error('Retrying in 15 seconds...')
|
||||
time.sleep(15)
|
||||
|
||||
else:
|
||||
self.me = self.interface.getMyNodeInfo()
|
||||
break
|
||||
|
||||
|
||||
def sendmsg(self, message: str, destination: int, channelIndex: int = 0):
|
||||
def send(self, message: str):
|
||||
'''
|
||||
Send a message to the Meshtastic interface
|
||||
|
||||
@ -76,7 +88,7 @@ class MeshtasticClient(object):
|
||||
logging.warning('Message exceeds 255 characters')
|
||||
message = message[:255]
|
||||
|
||||
self.interface.sendText(message, destination, wantAck=True, channelIndex=channelIndex) # Do we need wantAck?
|
||||
self.interface.sendText(message)
|
||||
|
||||
logging.info(f'Sent broadcast message: {message}')
|
||||
|
||||
@ -115,7 +127,7 @@ class MeshtasticClient(object):
|
||||
:param interface: Meshtastic interface
|
||||
'''
|
||||
|
||||
logging.info(f'Data update: {packet}')
|
||||
logging.info(f'Data update: {data}')
|
||||
|
||||
|
||||
def event_disconnect(self, interface, topic=pub.AUTO_TOPIC):
|
||||
@ -141,9 +153,12 @@ class MeshtasticClient(object):
|
||||
:param node: Node information
|
||||
'''
|
||||
|
||||
# Node ID Formula = f'!{hex(node_num)[2:]}'
|
||||
|
||||
self.nodes[node['num']] = node
|
||||
|
||||
logging.info(f'Node recieved: {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"]}')
|
||||
print(node)
|
||||
|
||||
|
||||
def event_position(self, packet: dict, interface):
|
||||
@ -155,7 +170,7 @@ class MeshtasticClient(object):
|
||||
'''
|
||||
|
||||
sender = packet['from']
|
||||
msg = packet['decoded']['payload'].hex() # What exactly is contained in this payload?
|
||||
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
|
||||
@ -164,7 +179,7 @@ class MeshtasticClient(object):
|
||||
snr = packet['rxSnr']
|
||||
rssi = packet['rxRssi']
|
||||
|
||||
logging.info(f'Position recieved: {id} - {name}: {longitude}, {latitude}, {altitude}m (SNR: {snr}, RSSI: {rssi}) - {msg}')
|
||||
logging.info(f'{id} - {name}: {longitude}, {latitude}, {altitude}m (SNR: {snr}, RSSI: {rssi}) - {msg}')
|
||||
|
||||
|
||||
def event_text(self, packet: dict, interface):
|
||||
@ -181,7 +196,7 @@ class MeshtasticClient(object):
|
||||
name = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK'
|
||||
target = self.nodes[to]['user']['longName'] if to in self.nodes else 'UNK'
|
||||
|
||||
logging.info(f'Message recieved: {id} {name} -> {target}: {msg}')
|
||||
logging.info(f'{id} {name} -> {target}: {msg}')
|
||||
print(packet)
|
||||
|
||||
|
88
meshmqtt.py
Normal file
88
meshmqtt.py
Normal file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python
|
||||
# Meshtastic MQTT Interface - Developed by Acidvegas in Python (https://git.acid.vegas/meshtastic)
|
||||
|
||||
import base64
|
||||
import random
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
except ImportError:
|
||||
raise ImportError('cryptography library not found (pip install cryptography)')
|
||||
|
||||
try:
|
||||
from meshtastic import mesh_pb2, mqtt_pb2, portnums_pb2, telemetry_pb2
|
||||
except ImportError:
|
||||
raise ImportError('meshtastic library not found (pip install meshtastic)')
|
||||
|
||||
try:
|
||||
import paho.mqtt.client as mqtt
|
||||
except ImportError:
|
||||
raise ImportError('paho-mqtt library not found (pip install paho-mqtt)')
|
||||
|
||||
|
||||
# MQTT Configuration
|
||||
MQTT_BROKER = 'localhost'
|
||||
MQTT_PORT = 1883
|
||||
MQTT_USERNAME = 'username'
|
||||
MQTT_PASSWORD = 'password'
|
||||
MQTT_ROOT_TOPIC = 'msh/US/2/c/'
|
||||
CHANNEL_KEY = 'channel_key'
|
||||
|
||||
|
||||
def on_connect(client, userdata, flags, rc, properties):
|
||||
'''
|
||||
Callback for when the client receives a CONNACK response from the server.
|
||||
|
||||
:param client: The client instance for this callback
|
||||
:param userdata: The private user data as set in Client() or user_data_set()
|
||||
:param flags: Response flags sent by the broker
|
||||
:param rc: The connection result
|
||||
:param properties: The properties returned by the broker
|
||||
'''
|
||||
|
||||
if rc == 0:
|
||||
print('Connected to MQTT broker')
|
||||
|
||||
else:
|
||||
print(f"Failed to connect to MQTT broker with result code {str(rc)}")
|
||||
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
'''
|
||||
Callback for when a PUBLISH message is received from the server.
|
||||
|
||||
:param client: The client instance for this callback
|
||||
:param userdata: The private user data as set in Client() or user_data_set()
|
||||
:param msg: An instance of MQTTMessage. This is a class with members topic, payload, qos, retain.
|
||||
'''
|
||||
|
||||
service_envelope = mqtt_pb2.ServiceEnvelope()
|
||||
|
||||
try:
|
||||
service_envelope.ParseFromString(msg.payload)
|
||||
print(service_envelope)
|
||||
|
||||
message_packet = service_envelope.packet
|
||||
print(message_packet)
|
||||
|
||||
except Exception as e:
|
||||
print(f'error on message: {e}')
|
||||
|
||||
else:
|
||||
if message_packet.HasField('encrypted') and not message_packet.HasField('decoded'): # Do we need to check for both?
|
||||
pass # Need to finish this
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
||||
client.on_connect = on_connect
|
||||
client.username_pw_set(username=MQTT_USERNAME, password=MQTT_PASSWORD)
|
||||
client.connect(MQTT_BROKER, MQTT_PORT, 60)
|
||||
client.on_message = on_message
|
||||
client.subscribe(MQTT_ROOT_TOPIC, 0) # This is the topic that the Meshtastic device is publishing to
|
||||
|
||||
# Keep-alive loop
|
||||
while client.loop() == 0:
|
||||
pass
|
Loading…
Reference in New Issue
Block a user