- C++ 56.7%
- C 41.6%
- Python 1.5%
- Linker Script 0.2%
Meshtastic Bridge
Configuration
The bridge is configured using the config.json file on the device's file system. Some settings can be changed using remote admin from another node.
Any properties in the config.json file that are ommitted will be set to their default values. ESP32 hardware allows for the config file to be uploaded directly from the IDE using pio run --target uploadfs --environment esp32c6.
{
"usePst1": true,
"mPst1": 0,
"bw1": 0.0,
"sf1": 0,
"cr1": 0,
"hops1": 3,
"clip1": 7,
"tx1": true,
"pwr1": 17,
"fs1": 0,
"bst1": true,
"fq1": 0.0,
"rpt1": false,
"usePst2": true,
"mPst2": 6,
"bw2": 0.0,
"sf2": 0,
"cr2": 0,
"hops2": 3,
"clip2": 7,
"tx2": true,
"pwr2": 17,
"fs2": 0,
"bst2": true,
"fq2": 0.0,
"admK": "",
"chN1": "",
"chK1": "",
"chN2": "",
"chK2": "",
"bstHop": 0,
"id1": null,
"id2": null,
"ln1": "",
"sn1": "",
"ln2": "",
"sn2": "",
"rpt2": false,
"lat": 0,
"lon": 0,
"alt": 0,
"adcF": 1.0,
"nodeInfoInt": 3600,
"nodeInfoEn": true,
"tmyInt": 3600,
"tmyEn": true,
"pTmyInt": 3600,
"pTmyEn": true,
"posInt": 3600,
"posEn": true,
"ngbIInt": 3600,
"ngbIEn": true,
"rlThr": 20,
"rlCap": 10,
"rlRef": 360,
"lowBatCut": 3400,
"lowBatRes": 3600,
"dsSec": 1800,
"lsEn": true,
"lsWake": 1000,
"lsSec": 30
}
| Setting | JSON property | Exposed in app | Default value | Applies to both nodes | Notes |
|---|---|---|---|---|---|
| Use modem preset defaults for radio 1 | usePst1 | x | true | ||
| Modem preset for radio 1 | mPst1 | x | LONG_FAST | see meshtastic_Config_LoRaConfig_ModemPreset |
|
| Bandwidth for radio 1 | bw1 | x | 0.0 | only applies if usePst1 is false | |
| Spreading factor for radio 1 | sf1 | x | 0 | only applies if usePst1 is false | |
| Coding rate for radio 1 | cr1 | x | 0 | only applies if usePst1 is false | |
| Hops for packets sent by this device for radio 1 | hops1 | x | 3 | ||
| Clip Hops for packets bridged from radio 1 | clip1 | --- | 7 | limits hops to this value for bridged packets. Leave at 7 to disable clipping | |
| Bridge packets received by radio 1 | bridge1 | --- | true | ||
| Tx enabled radio 1 | tx1 | x | true | ||
| Power for radio 1 | pwr1 | x | 17 | -9 - 22. Note that E22 900M30S radios have a built-in amplifier that pushes 22dBm to ~30dBm | |
| Frequency slot for radio 1 | fs1 | x | 0 | 0 to use default slot based on preset (mPst) | |
| Boosted rx gain for radio 1 | bst1 | x | true | ||
| Override frequency for radio 1 | fq1 | x | 0.0 | overrides default channel frequency (see fs). Set to 0.0 to disable. | |
| Repeat on Radio 1 | rpt1 | --- | false | repeat packets heard on radio 1 on radio 1 | |
| Use modem preset defaults for radio 2 | usePst2 | x | true | ||
| Modem preset for radio 2 | mPst2 | x | SHORT_FAST | see meshtastic_Config_LoRaConfig_ModemPreset |
|
| Bandwidth for radio 2 | bw2 | x | 0.0 | only applies if usePst2 is false | |
| Spreading factor for radio 2 | sf2 | x | 0 | only applies if usePst1 is false | |
| Coding rate for radio 2 | cr2 | x | 0 | only applies if usePst1 is false | |
| Hops for packets sent by this device for radio 2 | hops2 | x | 3 | ||
| Clip Hops for packets bridged from radio 2 | clip2 | --- | 7 | limits hops to this value for bridged packets. Leave at 7 to disable clipping | |
| Bridge packets received by radio 2 | bridge2 | --- | true | ||
| Tx enabled radio 2 | tx2 | x | true | ||
| Power for radio 2 | pwr2 | x | 17 | -9 - 22. Note that E22 900M30S radios have a built-in amplifier that pushes 22dBm to ~30dBm | |
| Frequency slot for radio 2 | fs2 | x | 0 | 0 to use default slot based on preset (mPst) | |
| Boosted rx gain for radio 2 | bst2 | x | true | ||
| Override frequency for radio 2 | fq2 | x | 0.0 | overrides default channel frequency (see fs). Set to 0.0 to disable. | |
| Repeat on Radio 2 | rpt2 | --- | false | repeat packets heard on radio 2 on radio 2 | |
| Admin channel key | admK | x | yes | key for admin channel (if legacy admin is enabled). Must be 16 or 32 bytes and base64 encoded | |
| Legacy admin enabled | lgcAdm | x | true | yes | enable remote configuration via admin channel (not requiring PKI encryption) |
| Additional channel 1 name | chN1 | x | yes | configured additional channels bypass rate limiting | |
| Additional channel 1 key | chK1 | x | yes | ||
| Additional channel 2 name | chN2 | x | yes | configured additional channels bypass rate limiting | |
| Additional channel 2 key | chK2 | x | yes | ||
| PKI private key | priK | x | random | yes | public key for PKI encryption. Must be 32 bytes and base64 encoded |
| PKI public key | pubK | x | derived | yes | |
| PKI public key of node to have remote admin access | admK1 | x | yes | ||
| PKI public key of node to have remote admin access | admK2 | x | yes | ||
| PKI public key of node to have remote admin access | admK3 | x | yes | ||
| Boost Hops by | bstHop | --- | 0 | yes | add x hops (up to 7 max) to bridged packets |
| Node Id radio 1 | id1 | --- | mac | mac derived by default, can be overwritten in config | |
| Node Id radio 2 | id2 | --- | mac | mac derived +1 by default, can be overwritten in config | |
| Long name radio 1 | ln1 | x | |||
| Short name radio 1 | sn1 | x | |||
| Long name radio 2 | ln2 | x | |||
| Short name radio 2 | sn2 | x | |||
| Latitude 1e-7 | lat | --- | xxx | yes | x 10 000 000 (e.g. use -410000000 to represent -41.0) |
| Longitude 1e-7 | lon | --- | xxx | yes | x 10 000 000 (e.g. use 1740000000 to represent 174.0). Node 2 is shifted 250 to east of node 1 |
| Altitude (m) | alt | --- | 0 | yes | |
| ADC ajustment | adcF | x | 1.0 | yes | |
| NodeInfo interval | nodeInfoInt | x | 3600 | yes | |
| NodeInfo enabled | nodeInfoEn | x | true | yes | set interval to 0 to disable |
| Device telemetry interval | tmyInt | x | 3600 | yes | |
| Device telemetry enabled | tmyEn | x | true | yes | set interval to 0 to disable |
| Power metrics telemetry interval | pTmyInt | x | 3600 | yes | only if INA219 detected on I2C |
| Power metrics telemetry enabled | pTmyEn | x | false | yes | |
| Position interval | posInt | x | 3600 | yes | |
| Position enabled | posEn | x | false | yes | set interval to 0 to disable |
| NeighbourInfo interval | ngbIInt | x | 3600 | yes | |
| NeighbourInfo enabled | ngbIEn | x | true | yes | |
| ChUtil threshold (%) from which to apply rate limiting | rlThr | --- | 20 | yes | |
| Rate limiter bucket capacity | rlCap | --- | 10 | yes | number of requests each node is allowed per period when rate limiting |
| Rate limiter token refill period (s) | rlRef | --- | 360 | yes | period after which all nodes receive their full allowance again |
| Low battery cutoff voltage (mV) | lowBatCut | --- | 3400 | yes | bridge will go into deep sleep below this voltage |
| Low battery resume voltage (mV) | lowBatRes | --- | 3600 | yes | bridge will wake up from deep sleep above this voltage. Ensure this is above the Low battery cutoff voltage |
| Low battery deep sleep seconds | dsSec | --- | 1800 | yes | interval to check battery voltage when in deep sleep |
| Light sleep enabled | lsEn | x | true | yes | should be on when running on battery. Wifi enabled will override this. |
| Light sleep minimum wake time (ms) | lsWake | x | 1000 | yes | |
| Light sleep duration (s) | lsSec | x | 30 | yes | maximum light sleep duration |
| Enable WiFi (ESP32 only) | wifi | x | false | yes | Bridge will either use configured WiFi, or if not connected start it's own AP |
| Enable bluetooth (nFR52840 only) | bt | x | true | yes | |
| Bluetooth pairing PIN | btPin | x | 671385 | yes | default can be set in extra.ini file (see) |
| Enable LEDs (on ESP32 TX LEDs cannot be disabled) | ledEn | x | true | yes | |
| Timezone | tz | x | NZST-12NZDT,M9.5.0,M4.1.0/3 | yes | |
| Log level | log | --- | 3 (Info) | yes | 1: Error, 2: Warn, 3: Info, 4: Debug, 5: Verbose |
| Firmware version | version | --- | yes | read-only, only available via admin channel/DM |
E22 900M30S radio module (on ESP32 bridge)
- Tested TX levels for E22 900M30S radio modules
- Some reverse engineering of the E22 900M30S radio modules
Remote admin
The bridge can be remotely configured using the meshtatic app from another node, either via PKI or legacy admin.
Additionally all above settings can be queried and updated using text messages on the legace admin channel or PKI encrypted direct messages.
When using the legacy admin channel the target needs to be identified by nodeId or shortName, e.g. sending one of the following messages:
{"to":1234567890,"get":"tx1"}{"to":B0SF,"get":"tx1"}
When using PKI encrypted DMs there is no need for the to property:
{"get":"tx1"}
To query a setting use {"get":"<property name>"} and the bridge replies with the value: {"tx1":true}.
To change a setting use {"<property name>":<new value>} with value being the correct json type, e.g.
true/false(boolean){"tx1":true}"text"(strings){"sn1":"B0SF"}1.234/17(numbers){"pwr1":22}
The bridge will reply depending on success of the change:
- value was successfully changed
OK, tx1=>true - value was already as requested
OK, tx1=>: unchanged - value was an invalid type
OK, tx1=>: invalid type(e.g. when using"true"instead oftrue) - if the property name was invalid:
Invalid config name
Hardware revisions
ESP32-C6 (initial version)
Power consumption:
| mode | clk | mA | note |
|---|---|---|---|
| running | 10MHz | 37mA | |
| max - both Tx@22dBm | 10MHz | 485mA | |
| light sleep | 24mA | ||
| WiFi | 80MHz | 62mA | |
| deep sleep | 6.6mA | issue with Rx Amps always on |
nRF52840
This version uses an nRF52840 MCU and 2 SX1262 modules.
Power sources:
- USB-C, 5V
- 1S LiPo, max 5V. 3.3V undervoltage protection
- Solar charge controller (BQ24074RGT) (4.35-10.2V @ max 1.5A)
- optional thermistor to drive LiPo charge controller
- internal DC-DC regulator (TLV62569PDRLR), max 5.5V@2V
Note: when no battery is connected the charge controller will connect any input directly to the internal DC-DC controller, and care needs to be taken to limit the solar input to 5.5V max.
NOTE: v0.1 silk screen has S and B swapped on the JST2.0 battery/solar connectors.
SX1262 configuration:
- DC-DC voltage regulator
- DIO3 driven TCXO (set to 1.8V)
- DIO2 for Rx/Tx switch
Pinout:
| nRF | Arduino | function | note |
|---|---|---|---|
| P0.01 | D 0 | USR_BTN | on board push button, also available on JST connector |
| P0.02 | D 1 A0 | V_BAT_ADC | battery voltage gated by VBAT_EN and reduced by 1k5/3k3 divider |
| P0.04 | D 2 | RX_LED_2 | radio 2 RX led, gated by LED_EN |
| P0.06 | D 3 | NSS_1 | SPI CS for radio 1 |
| P0.08 | D 4 | DIO1_1 | radio 1 DIO1 GPIO |
| P0.09 | D 5 | TxD | TxD of serial port, receiving log messages @ 115200 8N1. Can be used as GPIO |
| P0.10 | D 6 | RxD | RxD of serial port. Can be used as GPIO |
| P0.12 | D 7 | SX_RST_1 | /RST of radio 1 |
| P0.13 | D 8 | NSS_2 | SPI CS for radio 2 |
| P0.15 | D 9 | MISO | SPI MISO |
| P0.17 | D10 | MOSI | SPI MOSI |
| P0.20 | D11 | SCK | SPI CLK |
| P0.22 | D12 | SX_RST_2 | /RST of radio 1 |
| P0.24 | D13 | BUSY_2 | radio 2 busy signal |
| P0.26 | D14 | RX_LED_1 | radio 1 RX led, gated by LED_EN |
| P0.31 | D15 | LED_EN | allows disabling of Tx/Rx LEDs |
| P1.00 | D16 | DIO1_2 | radio 2 DIO1 GPIO |
| P1.02 | D17 | SCL | I2C port SCL. Can be used as GPIO |
| P1.04 | D18 | SDA | I2C port SDA. Can be used as GPIO |
| P1.06 | D19 | I2C_EN | switches GND on I2C port |
| P1.09 | D20 | BUSY_1 | radio 2 busy signal |
| P1.10 | D21 | VBAT_EN | switches battery voltge to the ADC voltage divider |
| SWDCLK | -- | SWDCLK | SWD debug port, use for initial flashing |
| SWDIO | -- | SWDIO | SWD debug port, use for initial flashing |
Initial flashing:
Use nanoDAP or similar SWD adapter and flash nRF's softdevice and the bootloader:
pyocd flash -t nrf52840 "s140_nrf52_6.1.1_softdevice.hex"sourcepyocd flash -t nrf52840 "meshhopper_bootloader-0.9.2_s140_6.1.1.hex"source
Once the softdevice and bootloader have been flashed, the bridge can be set in debug mode by double-clicking the reset button (or holding it during power on), connecting via USB and dropping a UF2 file in the drive.
A UF2 version of the bootloader is also available and can be flashed via USB.