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 e2bac7efde56c09d4851c92985647e339b4462e0
parent 7d10d4e64c61ec8cae0ed16b134e40c3de032d43
Author: acidvegas <acid.vegas@acid.vegas>
Date: Wed, 8 May 2024 17:47:32 -0400

Started parsing all meshtastic events for better logging, added more firmware hacks for heartbeat and for display RAM/PSRAM usage

Diffstat:
MREADME.md | 2++
Mdocs/FIRMWARE.md | 29+++++++++++++++++++++++++++++
Mmeshmqtt.py | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------

3 files changed, 121 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
@@ -29,6 +29,8 @@ The goal here is to create simple & clean modules to interface with the hardware
 - Documentation on MQTT bridging for high availability
 - 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)*
 
+## Notes
+- [Meshtastic PortNum List](https://buf.build/meshtastic/protobufs/docs/main:meshtastic#meshtastic.PortNum)
 ___
 
 ###### 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)
 \ No newline at end of file
diff --git a/docs/FIRMWARE.md b/docs/FIRMWARE.md
@@ -32,6 +32,35 @@ You can use the provided [icon.xbm](../assets/icon.xbm) for a rad GTA:SA fist to
 
 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...
 
+###### Display RAM/PSRAM Usage
+Look for these lines:
+```cpp
+    display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
+    display->drawString(x, y + FONT_HEIGHT_SMALL * 3, "http://meshtastic.local");
+```
+
+And place the follow code AFTER the above lines:
+
+```cpp
+// Display memory usage using the MemGet class
+uint32_t freeHeap = memGet.getFreeHeap();
+uint32_t totalHeap = memGet.getHeapSize();
+uint32_t usedHeap = totalHeap - freeHeap;
+display->drawString(x, y + FONT_HEIGHT_SMALL * 4, "Heap: " + String(usedHeap / 1024) + "/" + String(totalHeap / 1024) + " KB");
+
+// Display PSRAM usage using the MemGet class
+uint32_t freePsram = memGet.getFreePsram();
+uint32_t totalPsram = memGet.getPsramSize();
+uint32_t usedPsram = totalPsram - freePsram;
+display->drawString(x, y + FONT_HEIGHT_SMALL * 5, "PSRAM: " + String(usedPsram / 1024) + "/" + String(totalPsram / 1024) + " KB");
+```
+
+###### Heartbeat for redraw
+- Uncomment the line that says: `#define SHOW_REDRAWS`
+
+This will show a little 1x1 pixel on the top left corner anytime the screen is redraw.
+
+
  ## Compile & flash firmware
  - Select `PlatformIO: Pick Project Environment` & select your board.
  - Run `PLatformIO: Build` to compile the firmware.
diff --git a/meshmqtt.py b/meshmqtt.py
@@ -26,11 +26,21 @@ except ImportError:
 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %I:%M:%S')
 
 
+def clean_dict(dictionary: dict) -> dict:
+	'''
+	Remove empty fields from a dictionary.
+
+	:param dictionary: The dictionary to remove empty fields from
+	'''
+	
+	return {key: value for key, value in dictionary.items() if value}
+
+
 class MeshtasticMQTT(object):
 	def __init__(self):
 		'''Initialize the Meshtastic MQTT client'''
 
-		self.broadcast_id = 4294967295
+		self.broadcast_id = 4294967295 # Our channel ID
 		self.key = None
 
 
@@ -148,6 +158,18 @@ class MeshtasticMQTT(object):
 		if message_packet.HasField('encrypted') and not message_packet.HasField('decoded'):
 			message_packet = self.decrypt_message_packet(message_packet)
 
+			text = {
+				'from'       : getattr(message_packet, 'from'),
+				'to'         : getattr(message_packet, 'to'),
+				'channel'    : getattr(message_packet, 'channel'),
+				'id'         : getattr(message_packet, 'id'),
+				'rx_time'    : getattr(message_packet, 'rx_time'),
+				'hop_limit'  : getattr(message_packet, 'hop_limit'),
+				'priority'   : getattr(message_packet, 'priority'),
+				'hop_start'  : getattr(message_packet, 'hop_start')
+			}
+			logging.info(text)
+
 			if message_packet.decoded.portnum == portnums_pb2.UNKNOWN_APP:
 				logging.warning('Received an unknown app message:')
 				logging.info(message_packet)
@@ -172,14 +194,40 @@ class MeshtasticMQTT(object):
 			elif message_packet.decoded.portnum == portnums_pb2.POSITION_APP:
 				data = mesh_pb2.Position()
 				data.ParseFromString(message_packet.decoded.payload)
+
+				data_dict = {key: value for key, value in data}
+				print(data_dict)
+
 				logging.info('Received position:')
-				logging.info(data)
+				loc = {
+					'lattitude'       : getattr(data, 'latitude_i') / 1e7,
+					'longitude'       : getattr(data, 'longitude_i') / 1e7,
+					'altitude'        : getattr(data, 'altitude') / 1000,
+					'location_source' : getattr(data, 'location_source'),
+					'altitude_source' : getattr(data, 'altitude_source'),
+					'pdop'            : getattr(data, 'PDOP'),
+					'hdop'            : getattr(data, 'HDOP'),
+					'vdop'            : getattr(data, 'VDOP'),
+					'gps_accuracy'    : getattr(data, 'gps_accuracy'),
+					'ground_speed'    : getattr(data, 'ground_speed'),
+					'ground_track'    : getattr(data, 'ground_track'),
+					'fix_quality'     : getattr(data, 'fix_quality'),
+					'fix_type'        : getattr(data, 'fix_type'),
+					'sats_in_view'    : getattr(data, 'sats_in_view'),
+					'sensor_id'       : getattr(data, 'sensor_id'),
+					'next_update'     : getattr(data, 'next_update'),
+					'seq_number'      : getattr(data, 'seq_number'),
+					'precision_bits'  : getattr(data, 'precision_bits')
+				}
+
+				if (loc := clean_dict(loc)):
+					logging.info(loc)
 
 			elif message_packet.decoded.portnum == portnums_pb2.NODEINFO_APP:
 				data = mesh_pb2.NodeInfo()
-				data.ParseFromString(message_packet.decoded.payload)
+				#data.ParseFromString(message_packet.decoded.payload)
 				logging.info('Received node info:')
-				logging.info(data)
+				logging.info(message_packet)
 
 			elif message_packet.decoded.portnum == portnums_pb2.ROUTING_APP:
 				data = mesh_pb2.Routing()
@@ -242,10 +290,9 @@ class MeshtasticMQTT(object):
 				logging.info(data)
 
 			elif message_packet.decoded.portnum == portnums_pb2.STORE_FORWARD_APP:
-				data = mesh_pb2.StoreForward()
-				data.ParseFromString(message_packet.decoded.payload)
 				logging.info('Received store and forward:')
-				logging.info(data)
+				logging.info(message_packet)
+				logging.info(message_packet.decoded.payload)
 
 			elif message_packet.decoded.portnum == portnums_pb2.RANGE_TEST_APP:
 				data = mesh_pb2.RangeTest()
@@ -257,7 +304,34 @@ class MeshtasticMQTT(object):
 				data = telemetry_pb2.Telemetry()
 				data.ParseFromString(message_packet.decoded.payload)
 				logging.info('Received telemetry:')
-				logging.info(data)
+
+				data_dict = {key: value for key, value in data}
+				print(data_dict)
+
+				if getattr(data, 'device_metrics'):
+					text = {
+						'battery_level'       : getattr(data.device_metrics, 'battery_level'),
+						'voltage'             : getattr(data.device_metrics, 'voltage'),
+						'channel_utilization' : getattr(data.device_metrics, 'channel_utilization'),
+						'air_util_tx'         : getattr(data.device_metrics, 'air_util_tx'),
+						'uptime_seconds'      : getattr(data.device_metrics, 'uptime_seconds')
+					}
+					if (text := clean_dict(text)):
+						logging.info(text)
+
+				if getattr(data, 'environment_metrics'):
+					env_metrics = {
+						'barometric_pressure' : getattr(data.environment_metrics, 'barometric_pressure'),
+						'current'             : getattr(data.environment_metrics, 'current'),
+						'distance'            : getattr(data.environment_metrics, 'distance'),
+						'gas_resistance'      : getattr(data.environment_metrics, 'gas_resistance'),
+						'iaq'                 : getattr(data.environment_metrics, 'iaq'),
+						'relative_humidity'   : getattr(data.environment_metrics, 'relative_humidity'),
+						'temperature'         : getattr(data.environment_metrics, 'temperature'),
+						'voltage'             : getattr(data.environment_metrics, 'voltage')
+					}
+					if (env_metrics := clean_dict(env_metrics)):
+						logging.info(env_metrics)
 
 			elif message_packet.decoded.portnum == portnums_pb2.ZPS_APP:
 				data = mesh_pb2.Zps()
@@ -281,7 +355,13 @@ class MeshtasticMQTT(object):
 				neighborInfo = mesh_pb2.NeighborInfo()
 				neighborInfo.ParseFromString(message_packet.decoded.payload)
 				logging.info('Received neighbor info:')
-				logging.info(neighborInfo)
+				info = {
+					'node_id'                      : getattr(neighborInfo, 'node_id'),
+					'last_sent_by_id'              : getattr(neighborInfo, 'last_sent_by_id'),
+					'node_broadcast_interval_secs' : getattr(neighborInfo, 'node_broadcast_interval_secs'),
+					'neighbors'                    : getattr(neighborInfo, 'neighbors')
+				}
+				logging.info(info)
 
 			elif message_packet.decoded.portnum == portnums_pb2.ATAK_PLUGIN:
 				data = mesh_pb2.AtakPlugin()
@@ -312,6 +392,7 @@ class MeshtasticMQTT(object):
 				pos.ParseFromString(message_packet.decoded.payload)
 				logging.info('Received map report:')
 				logging.info(pos)
+
 			else:
 				logging.warning('Received an unencrypted message')
 				logging.info(f'Payload: {message_packet}')