🤔 Intro
Let's continue our journey with Meshtastic by showing you an update of fastmeshapi, a FastAPI-based Meshtastic dashboard.
Each Meshtastic-capable board has its unique characteristics. Since their number is growing, I thought showing some important information directly on the dashboard could be beneficial.
As always, the YouTube video is at the bottom of this post.
📋 Fields
Some notable fields include the name of the board, the Meshtastic support status, hardware revisions, available radio bands, antenna type, chipset, GPIO pins, ports and interfaces.
🖥️ Backend
🗒️ REST API
/v0/boards
endpointAll the board data is fetched from the /v0/boards
endpoint. At the moment, I only added information for a couple of common Heltec boards I own, the v3 and the Wireless Paper. Finding their PDF datasheet was quite easy and it's also very detailed.
🧮 YAML data and classes
All the raw board data you see in this picture is stored in a YAML file. I decided to avoid designing database tables for this scope since all the data is static and limited in size. Modifying the Pydantic classes and the frontend should only be done if there is a real necessity to show new and/or different fields.
Here's a small extract of the YAML file:
boards:
- name: 'HELTEC_V3'
note: "The beginner's device"
links:
- homepage: null
- datasheet: 'https://resource.heltec.cn/download/WiFi_LoRa_32_V3/HTIT-WB32LA_V3.2.pdf'
- hardware_changelog: null
hardware_revisions:
- 'V3.1'
- 'V3.2'
supported_frequencies_mhz:
- 470.0
- 510.0
- 868.0
- 915.0
- 920.0
- 923.0
screen:
type: 'oled'
size_mm:
# Approximate.
base: 27
height: 18
size_inch: 0.95
resolution: '128x64'
buttons:
- 'prg'
- 'rst'
The data is readable and can be easily extended.
Pydantic classes store the board data so that FastAPI can serve it. This information is at first loaded from the YAML file into dictionaries. This is done using the famous PyYAML library.
Pydantic makes field validation available by default, which is very important for FastAPI as well, because we can easily double check if we need to fix how the frontend consumes this data.
Let's see a very small extract of these classes:
# Lots of other stuff here...
class BoardPowerConsumption(BaseModel):
amount_volts: float
lora: Optional[list[BoardPowerConsumptionLora]]
bluetooth_amount: float
wifi: Optional[BoardPowerConsumptionWifi]
sleep_amount: float
deep_sleep_amount: float
# Lots of other stuff here...
class Board(BaseModel):
name: str
note: Optional[str]
links: Optional[
list[
dict[
str, Optional[HttpUrl]
]
]
]
hardware_revisions: list[str]
supported_frequencies_mhz: list[float]
power_consumption_mah: list[BoardPowerConsumption]
# Lots of other stuff here...
@validator('buttons', each_item=True)
def validate_buttons(cls, v):
if not re.match(r'[a-z]{3,}', v):
raise ValueError('Each board button identifier must be of at least length 3, and consist of lowercase letters only.')
return v
Besides the nested classes we can also see a custom validator for the board button names. We can enforce the rules we want, and, in this specific case, a button name must be a 3 letter lowercase string.
As you see, at the YAML and Pydantic class level there is support for lots of fields which are not shown on the dashboard.
🏢 Frontend
As I anticipated in the first post, the frontend of this app is written in Svelte.
✨ Icons
Sveltestrap has a nice vector icon set. Some important icons I would have liked to use in this project are not available. Anyway I figured out alternatives which convey the same meanings. These same icons are also part of the standard Bootstrap package.
🪟 Modals
By using Sveltestrap not only you are provided by convinient icons but also by common patterns such as Modal
, which are these small windows drawn on top of the rest of the content.
To display the fields in a readable manner I used a Table
element.
Icon data is stored in objects: if a board feature is available, its value
field will be set to true
(false
otherwise). The tooltip
field is the text displayed on the icon during the mouse hover action. Finally, the key of each object is the exact name of the icon in the Sveltestrap icon set. So, to organize the interface data, I did like this:
let boardDetailDataInterfaces= {};
function showDetailsBoardInterfaces(interfaces) {
boardDetailDataInterfaces = {
// icon_name: {tooltip_text, value}
'arrow-left-right': {
tooltip: 'Serial',
value: interfaces?.serial ? true : false
},
'wifi': {
tooltip: 'WiFi',
value: interfaces?.wifi ? true : false
},
'bluetooth': {
tooltip: 'Bluetooth',
value: interfaces?.bluetooth ? true : false
},
'map': {
tootlip: 'GPS',
value: interfaces?.gps ? (interfaces.gps.available ? true : false) : false
}
}
}
So, to be able to actually display the icons in the Svelte HTML section we need to unpack the object into key
and value
, and create a div
, Icon
, and Tooltip
.
{#each Object.entries(boardDetailDataInterfaces) as [key, value]}
{#if value.value}
<div id="{key}" class="icon-tooltip"><Icon style="font-size: 2rem;" name="{key}"/><Tooltip target="{key}">{value.tooltip}</Tooltip></div>
{/if}
{/each}
I later used this same Tooltip
pattern for other elements in the project.
🎉 Conclusion
The purpose is to build a persistent, high performance Meshtastic web app that provides a REST API as well.
Let me know in the comments if you already know Meshtastic or if you'd like to try it in the future.
Top comments (0)