Added some firmware hacking documentation for customization, white-space cleanup, etc

This commit is contained in:
Dionysus 2024-04-29 14:15:38 -04:00
parent 3fabecb8aa
commit e0754f1f02
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
4 changed files with 64 additions and 28 deletions

39
FIRMWARE_HACKS.md Normal file
View File

@ -0,0 +1,39 @@
# Meshtastic Firmware Hacks
### Custom Boot Logo & Message
- Download & install [PlatformIO](https://platformio.org/platformio-ide)
- `git clone https://github.com/meshtastic/firmware.git`
- `cd firmware && git submodule update --init`
- 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
- Navigate to `firmware/src/graphics/Screen.cpp`
- Find & replace `const char *title = "meshtastic.org";` with your custom message.
### 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)
- 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...
## Compile & flash firmware
- Select `PlatformIO: Pick Project Environment` & select your board.
- Run `PLatformIO: Build` to compile the firmware.
- Place device in DFU mode & plug in to the computer
- Do `PlatformIO: Upload` to send the firmware to the device
- Press the RESET button or reboot your device.
See [here](https://meshtastic.org/docs/development/firmware/build/) for more information building & flashing custom firmware.

View File

@ -10,6 +10,8 @@ The goal here is to create simple & clean modules to interface with the hardware
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) 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:]}'
## Hardware & Software related Issues ## Hardware & Software related 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 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...

View File

@ -7,6 +7,7 @@ import logging
import ssl import ssl
import time import time
# EF576MkXA3aEURbCfNn6p0FfZdua4I
# Formatting Control Characters / Color Codes # Formatting Control Characters / Color Codes
bold = '\x02' bold = '\x02'
@ -44,7 +45,7 @@ def color(msg: str, foreground: str, background: str = None) -> str:
:param foreground: The foreground color to use. :param foreground: The foreground color to use.
:param background: The background color to use. :param background: The background color to use.
''' '''
return f'\x03{foreground},{background}{msg}{reset}' if background else f'\x03{foreground}{msg}{reset}' return f'\x03{foreground},{background}{msg}{reset}' if background else f'\x03{foreground}{msg}{reset}'
@ -63,7 +64,7 @@ class Bot():
:param chan: The channel to send the ACTION to. :param chan: The channel to send the ACTION to.
:param msg: The message to send to the channel. :param msg: The message to send to the channel.
''' '''
await self.sendmsg(chan, f'\x01ACTION {msg}\x01') await self.sendmsg(chan, f'\x01ACTION {msg}\x01')
@ -73,7 +74,7 @@ class Bot():
:param data: The raw data to send to the IRC server. (512 bytes max including crlf) :param data: The raw data to send to the IRC server. (512 bytes max including crlf)
''' '''
await self.writer.write(data[:510].encode('utf-8') + b'\r\n') await self.writer.write(data[:510].encode('utf-8') + b'\r\n')
@ -84,13 +85,13 @@ class Bot():
:param target: The target to send the PRIVMSG to. (channel or user) :param target: The target to send the PRIVMSG to. (channel or user)
:param msg: The message to send to the target. :param msg: The message to send to the target.
''' '''
await self.raw(f'PRIVMSG {target} :{msg}') await self.raw(f'PRIVMSG {target} :{msg}')
async def connect(self): async def connect(self):
'''Connect to the IRC server.''' '''Connect to the IRC server.'''
while True: while True:
try: try:
options = { options = {
@ -124,7 +125,7 @@ class Bot():
:param data: The data received from the IRC server. :param data: The data received from the IRC server.
''' '''
parts = data.split() parts = data.split()
ident = parts[0][1:] ident = parts[0][1:]
@ -146,7 +147,7 @@ class Bot():
if len(message) > 255: if len(message) > 255:
await self.sendmsg(target, color('Message exceeds 255 bytes nerd!', red)) await self.sendmsg(target, color('Message exceeds 255 bytes nerd!', red))
# TODO: Send a meshtastic message (We have to ensure our outbounds from IRC don't loop back into IRC) # TODO: Send a meshtastic message (We have to ensure our outbounds from IRC don't loop back into IRC)
self.last = time.time() # Update the last command time if it starts with ! character to prevent command flooding self.last = time.time() # Update the last command time if it starts with ! character to prevent command flooding
@ -156,9 +157,9 @@ class Bot():
:param data: The data received from the IRC server. :param data: The data received from the IRC server.
''' '''
logging.info(data) logging.info(data)
try: try:
parts = data.split() parts = data.split()
@ -207,7 +208,7 @@ if __name__ == '__main__':
parser.add_argument('--ssl', action='store_true', help='Use SSL for the connection.') parser.add_argument('--ssl', action='store_true', help='Use SSL for the connection.')
parser.add_argument('--key', default='', help='The key (password) for the IRC channel, if required.') parser.add_argument('--key', default='', help='The key (password) for the IRC channel, if required.')
args = parser.parse_args() args = parser.parse_args()
if not args.channel.startswith('#'): if not args.channel.startswith('#'):
channel = '#' + args.channel channel = '#' + args.channel
@ -220,4 +221,4 @@ if __name__ == '__main__':
bot = Bot() bot = Bot()
asyncio.run(bot.connect()) asyncio.run(bot.connect())

View File

@ -17,24 +17,18 @@ except ImportError:
try: try:
from pubsub import pub from pubsub import pub
except ImportError: except ImportError:
raise ImportError('pubsub library not found (pip install pypubsub)') # Confirm this Pypi package name... raise ImportError('pubsub library not found (pip install pypubsub)')
# Initialize logging # Initialize logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s | %(levelname)9s | %(funcName)s | %(message)s', datefmt='%Y-%m-%d %I:%M:%S') 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): class MeshtasticClient(object):
def __init__(self): def __init__(self):
self.interface = None # We will define the interface in the connect() function self.interface = None
self.me = {} # We will populate this with the event_connect() callback self.me = {}
self.nodes = {} # Nodes will populate with the event_node() callback self.nodes = {}
def connect(self, option: str, value: str): def connect(self, option: str, value: str):
@ -71,7 +65,7 @@ class MeshtasticClient(object):
break break
def send(self, message: str): def sendmsg(self, message: str, destination: int, channelIndex: int = 0):
''' '''
Send a message to the Meshtastic interface Send a message to the Meshtastic interface
@ -82,7 +76,7 @@ class MeshtasticClient(object):
logging.warning('Message exceeds 255 characters') logging.warning('Message exceeds 255 characters')
message = message[:255] message = message[:255]
self.interface.sendText(message) self.interface.sendText(message, destination, wantAck=True, channelIndex=channelIndex) # Do we need wantAck?
logging.info(f'Sent broadcast message: {message}') logging.info(f'Sent broadcast message: {message}')
@ -121,7 +115,7 @@ class MeshtasticClient(object):
:param interface: Meshtastic interface :param interface: Meshtastic interface
''' '''
logging.info(f'Data update: {data}') logging.info(f'Data update: {packet}')
def event_disconnect(self, interface, topic=pub.AUTO_TOPIC): def event_disconnect(self, interface, topic=pub.AUTO_TOPIC):
@ -149,7 +143,7 @@ class MeshtasticClient(object):
self.nodes[node['num']] = node self.nodes[node['num']] = node
logging.info(f'Node found: {node["user"]["id"]} - {node["user"]["shortName"].ljust(4)} - {node["user"]["longName"]}') logging.info(f'Node recieved: {node["user"]["id"]} - {node["user"]["shortName"].ljust(4)} - {node["user"]["longName"]}')
def event_position(self, packet: dict, interface): def event_position(self, packet: dict, interface):
@ -161,7 +155,7 @@ class MeshtasticClient(object):
''' '''
sender = packet['from'] sender = packet['from']
msg = packet['decoded']['payload'].hex() msg = packet['decoded']['payload'].hex() # What exactly is contained in this payload?
id = self.nodes[sender]['user']['id'] if sender in self.nodes else '!unk ' 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' name = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK'
longitude = packet['decoded']['position']['longitudeI'] / 1e7 longitude = packet['decoded']['position']['longitudeI'] / 1e7
@ -170,7 +164,7 @@ class MeshtasticClient(object):
snr = packet['rxSnr'] snr = packet['rxSnr']
rssi = packet['rxRssi'] rssi = packet['rxRssi']
logging.info(f'{id} - {name}: {longitude}, {latitude}, {altitude}m (SNR: {snr}, RSSI: {rssi}) - {msg}') logging.info(f'Position recieved: {id} - {name}: {longitude}, {latitude}, {altitude}m (SNR: {snr}, RSSI: {rssi}) - {msg}')
def event_text(self, packet: dict, interface): def event_text(self, packet: dict, interface):
@ -187,7 +181,7 @@ class MeshtasticClient(object):
name = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK' 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' target = self.nodes[to]['user']['longName'] if to in self.nodes else 'UNK'
logging.info(f'{id} {name} -> {target}: {msg}') logging.info(f'Message recieved: {id} {name} -> {target}: {msg}')
print(packet) print(packet)