meshtastic

- Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.acid.vegas/-c.git
Log | Files | Refs | Archive | README | LICENSE

commit f92093d889d55971d7db97a11adfc309381fd29b
parent ddb5a4b323593bde6d879e1295f96dc71651927b
Author: acidvegas <acid.vegas@acid.vegas>
Date: Fri, 3 May 2024 13:16:58 -0400

Added T-Deck documentation, parts list, gps install guide, etc. Added MQTT connection script (still working on this)

Diffstat:
MFIRMWARE.md | 12+++++++-----
AT-DECK.md | 24++++++++++++++++++++++++
Mmeshmqtt.py | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------

3 files changed, 154 insertions(+), 66 deletions(-)

diff --git a/FIRMWARE.md b/FIRMWARE.md
@@ -1,32 +1,34 @@
 # Meshtastic Firmware Hacks
 
-### Custom Boot Logo & Message
+## Prerequisite
 - Download & install [PlatformIO](https://platformio.org/platformio-ide)
 
 - `git clone https://github.com/meshtastic/firmware.git`
 
 - `cd firmware && git submodule update --init`
 
+## Customization
+###### Custom Boot Logo
 - Use [XMB Viewer](https://windows87.github.io/xbm-viewer-converter/) to convert an image to XMB
 
 - The data from this goes in `firmware/src/graphics/img/icon.xbm`
 
-### Custom boot message
+###### Custom boot message
 - Navigate to `firmware/src/graphics/Screen.cpp`
 
 - Find & replace `const char *title = "meshtastic.org";` with your custom message.
 
-### Custom screen color
+###### Custom screen color
  - Navigate to `src/graphics/TFTDisplay.cpp`
 
  - Find & replace `#define TFT_MESH COLOR565(0x67, 0xEA, 0x94)` with your custom color.
 
- ### Custom alert sound (for T-Deck & devices with a buzzer)
+ ###### Custom alert sound (for T-Deck & devices with a buzzer)
  - From the mobile app, click the 3 dots on the top right, and select `Radio configuration`
  - Under `Module configuration`, select `External Notification`
 - Scroll down & you will see a `Ringtone` option that takes [RTTTL](https://en.wikipedia.org/wiki/Ring_Tone_Text_Transfer_Language) formatted tones.
 
-As far as I know, at the time of writing this, the onyl way to change the Ringtone is from the App...
+As far as I know, at the time of writing this, the only way to change the Ringtone is from the App. While this is not a "firmware" related thing, I included it in this file because it was difficult to find this setting...
 
  ## Compile & flash firmware
  - Select `PlatformIO: Pick Project Environment` & select your board.
diff --git a/T-DECK.md b/T-DECK.md
@@ -0,0 +1,23 @@
+# LilyGo T-Deck Notes
+
+## Parts
+- [T-Deck](https://www.lilygo.cc/products/t-deck)
+- [Case](https://www.printables.com/model/741124-lilygo-t-deck-case) *(You can 3D print it yourself, or follow the Etsy link on here to buy one)*
+- [Antenna](https://www.amazon.com/dp/B086ZG5WBR)
+- [GPS](https://www.amazon.com/dp/B09LQDG1HY) *(There may be a better 15mm option for the case above..)*
+- [Battery](https://www.amazon.com/dp/B0BG82T39Y) *(The battery you get depends on the size of the case you order, contact me if you need help)*
+
+**WARNING:** Do not power on the device until the antenna is plugged in! Even to flash the firmware, or for testing, make sure your antenna is plugged in or you can fry the radio!
+
+## GPS Installation
+The T-Deck has a grove connector for the GPS. Personally, I do not use a grove connector for mine, I removed the Grove input and soldered directly to the board.
+
+**WARNING**: Be careful taking off the grove! Snip the front points, and then use a soldering iron to loosen the metal on the 4 contact back points. You can VERY easily pull the solder pads right off if you just try to rip the grove connector off without loosening the solder points. If you pull off a solder pad, you're pretty much boned on having a GPS module.
+
+You will see VCC, GND, RX & TX points on both the T-Deck & the GPS. Solder wires to match these points, but switch RX & TX. So do VCC to VCC, GND to GND, and then ensure that RX is soldered to TX, and TX is soldered to RX. It's confusing, I know, but that's China based hardware for you...
+
+## Flashing
+Simply plug in the T-Deck via USB and connect to a computer, then visit the [Meshtastic Web Flasher](https://flasher.meshtastic.org) and select your hardware & firmware version. Your device should show up as a serial device on /dev/ttyUSB0 or /dev/ttyAMC0. If you do not see your device, try adding your user to the dialout group. See [SETUP.md](./SETUP.md) for information on how to setup the device once it is flashed with Meshtastic.
+
+## Issues
+- Turn off WiFi completely if you plan to use this on-the-go. The device has an ESP32 chip which will always preffer WiFi over Bluetooth, so if you leave the house with this thign, it will constantly keep trying to connect to your WiFi & really degrade the performance of the board.
+\ No newline at end of file
diff --git a/meshmqtt.py b/meshmqtt.py
@@ -1,88 +1,151 @@
-#!/usr/bin/env python
-# Meshtastic MQTT Interface - Developed by Acidvegas in Python (https://git.acid.vegas/meshtastic)
+#!/usr/bin/env python3
+# Meshtastic MQTT Interface - Developed by acidvegas in Python (https://acid.vegas/meshtastic)
 
+import argparse
 import base64
-import random
+import logging
 
 try:
-    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-    from cryptography.hazmat.backends           import default_backend
+	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)')
+	raise SystemExit('missing the cryptography module (pip install cryptography)')
 
 try:
-    from meshtastic import mesh_pb2, mqtt_pb2, portnums_pb2, telemetry_pb2
+	from meshtastic import mesh_pb2, mqtt_pb2, portnums_pb2, telemetry_pb2
 except ImportError:
-    raise ImportError('meshtastic library not found (pip install meshtastic)')
+	raise SystemExit('missing the meshtastic module (pip install meshtastic)')
 
 try:
-    import paho.mqtt.client as mqtt
+	import paho.mqtt.client as mqtt
 except ImportError:
-    raise ImportError('paho-mqtt library not found (pip install paho-mqtt)')
+	raise SystemExit('missing the paho-mqtt module (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 process_message(message_packet, text_payload, is_encrypted):
 
+	text = {
+		'message': text_payload,
+		'from': getattr(message_packet, 'from'),
+		'id': getattr(message_packet, 'id'),
+		'to': getattr(message_packet, 'to')
+	}
+	print(text)
 
-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')
+def decode_encrypted(message_packet):
+	'''
+	Decrypt an encrypted message packet.
 
-    else:
-        print(f"Failed to connect to MQTT broker with result code {str(rc)}")
+	:param message_packet: The message packet to decrypt'''
+	try:
+		key_bytes = base64.b64decode(key.encode('ascii'))
 
+		nonce_packet_id = getattr(message_packet, 'id').to_bytes(8, 'little')
+		nonce_from_node = getattr(message_packet, 'from').to_bytes(8, 'little')
+		nonce = nonce_packet_id + nonce_from_node
+
+		cipher = Cipher(algorithms.AES(key_bytes), modes.CTR(nonce), backend=default_backend())
+		decryptor = cipher.decryptor()
+		decrypted_bytes = decryptor.update(getattr(message_packet, 'encrypted')) + decryptor.finalize()
+
+		data = mesh_pb2.Data()
+		data.ParseFromString(decrypted_bytes)
+		message_packet.decoded.CopyFrom(data)
+
+		if message_packet.decoded.portnum == portnums_pb2.TEXT_MESSAGE_APP:
+			text_payload = message_packet.decoded.payload.decode('utf-8')
+			is_encrypted = True
+			process_message(message_packet, text_payload, is_encrypted)
+			print(f'{text_payload}')
 
-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()
+		elif message_packet.decoded.portnum == portnums_pb2.NODEINFO_APP:
+				info = mesh_pb2.User()
+				info.ParseFromString(message_packet.decoded.payload)
+				print(info)
 
-    try:
-        service_envelope.ParseFromString(msg.payload)
-        print(service_envelope)
+		elif message_packet.decoded.portnum == portnums_pb2.POSITION_APP:
+			pos = mesh_pb2.Position()
+			pos.ParseFromString(message_packet.decoded.payload)
+			print(pos)
 
-        message_packet = service_envelope.packet
-        print(message_packet)
+		elif message_packet.decoded.portnum == portnums_pb2.TELEMETRY_APP:
+			env = telemetry_pb2.Telemetry()
+			env.ParseFromString(message_packet.decoded.payload)
+			print(env)
 
-    except Exception as e:
-        print(f'error on message: {e}')
+	except Exception as e:
+		logging.error(f'Failed to decrypt message: {str(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
+
+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:
+		logging.error(f'Failed to connect to MQTT broker: {rc}')
+
+
+def on_message(client, userdata, msg):
+	'''
+	Callback for when a 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
+	'''
+	
+	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:
+		logging.error(f'Failed to parse message: {str(e)}')
+		return
+
+	if message_packet.HasField('encrypted') and not message_packet.HasField('decoded'):
+		decode_encrypted(message_packet)
 
 
 
 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
-\ No newline at end of file
+	parser = argparse.ArgumentParser(description='Mesh MQTT')
+	parser.add_argument('--broker', default='mqtt.meshtastic.org', help='MQTT broker address')
+	parser.add_argument('--port', default=1883, type=int, help='MQTT broker port')
+	parser.add_argument('--root', default='msh/US/2/c', help='Root topic')
+	parser.add_argument('--tls', action='store_true', help='Enable TLS/SSL')
+	parser.add_argument('--username', default='meshdev', help='MQTT username')
+	parser.add_argument('--password', default='large4cats', help='MQTT password')
+	parser.add_argument('--key', default='AQ==', help='Encryption key')
+	args = parser.parse_args()
+
+    # Ensure the key is padded and formatted correctly
+	padded_key   = args.key.ljust(len(args.key) + ((4 - (len(args.key) % 4)) % 4), '=')
+	replaced_key = padded_key.replace('-', '+').replace('_', '/')
+	key          = replaced_key
+
+	broadcast_id = 4294967295
+
+	# client = mqtt.Client(client_id='', clean_session=True, userdata=None)
+	client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
+	client.on_connect = on_connect
+	client.username_pw_set(username=args.username, password=args.password)
+	client.connect(args.broker, args.port, 60)
+	client.on_message = on_message
+	client.subscribe(args.root, 0)
+
+	while client.loop() == 0:
+		pass