Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ repos:
# Strict but reasonable settings
args: [
"--max-line-length=100",
"--extend-ignore=E203,W503"
"--extend-ignore=E203,E231,W503"
]
# Exclude third-party code but check our core code
exclude: '^(src/pymc_core/hardware/lora/|examples/)'
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ For examples, see the [documentation](https://rightup.github.io/pyMC_core/exampl
### Supported Radios
- **Waveshare SX1262 LoRaWAN/GNSS HAT** - Popular Raspberry Pi LoRa module
- **HackerGadgets uConsole** - All-in-one extension board with LoRa support
- **FrequencyLabs meshadv-mini** - Raspberry Pi hat with E22-900M22S LoRa module

### Requirements
- Raspberry Pi (or compatible SBC)
Expand Down Expand Up @@ -179,7 +180,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
- [Documentation](https://rightup.github.io/pyMC_core/)
- [Issues](https://github.com/rightup/pyMC_Core/issues)
- [Discussions](https://github.com/rightup/pyMC_Core/discussions)
- [Meshcore Discord](https://discord.com/channels/1343693475589263471/1343693475589263474)
- [Meshcore Discord](https://discord.gg/fThwBrRc3Q)

---

Expand Down
44 changes: 44 additions & 0 deletions docs/docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ python examples/send_text_message.py uconsole
Each example script accepts an optional radio type parameter:
- `waveshare` (default) - Waveshare SX1262 HAT
- `uconsole` - HackerGadgets uConsole
- `meshadv-mini` - FrequencyLabs meshadv-mini

You can also run examples directly with command-line arguments:

Expand All @@ -101,11 +102,13 @@ Each example accepts an optional radio type parameter:

- `waveshare` (default): Waveshare LoRaWAN/GNSS HAT configuration
- `uconsole`: HackerGadgets uConsole configuration
- `meshadv-mini`: Frequency Labs Mesh Adv

```bash
# Examples with explicit radio type
python examples/send_flood_advert.py waveshare
python examples/send_flood_advert.py uconsole
python examples/send_flood_advert.py meshadv-mini
```

## Hardware Requirements
Expand All @@ -131,6 +134,14 @@ pyMC_Core supports multiple SX1262-based LoRa radio modules:
- **GPIO Pins**: CS=-1, Reset=25, Busy=24, IRQ=26
- **Additional Setup**: Requires SPI1 overlay and GPS/RTC configuration (see uConsole setup guide)

#### Frequency Labs meshadv-mini
- **Hardware**: FrequencyLabs meshadv-mini Hat
- **Platform**: Raspberry Pi (or compatible single-board computer)
- **Frequency**: 868MHz (EU) or 915MHz (US)
- **TX Power**: Up to 22dBm
- **SPI Bus**: SPI0
- **GPIO Pins**: CS=8, Reset=24, Busy=20, IRQ=16

### Default Pin Configurations

#### Waveshare HAT
Expand All @@ -153,6 +164,16 @@ pyMC_Core supports multiple SX1262-based LoRa radio modules:
- TX Enable: Not used (-1)
- RX Enable: Not used (-1)

#### meshadv-mini (Frequency Labs)
- SPI Bus: 0
- CS ID: 0
- CS Pin: GPIO 8
- Busy Pin: GPIO 20
- Reset Pin: GPIO 24
- IRQ Pin: GPIO 16
- TX Enable: Not used (-1)
- RX Enable: GPIO 12

## Dependencies

> **Important**: On modern Python installations (Ubuntu 22.04+, Debian 12+), you may encounter `externally-managed-environment` errors when installing packages system-wide. Create a virtual environment first:
Expand Down Expand Up @@ -251,6 +272,22 @@ All examples use the SX1262 LoRa radio with the following default settings:
- **TX Enable**: Not used (-1)
- **RX Enable**: Not used (-1)

#### meshadv-mini (Frequency Labs)
- **Radio Type**: SX1262 direct hardware control
- **Frequency**: 869.525MHz (European standard)
- **TX Power**: 22dBm
- **Spreading Factor**: 11
- **Bandwidth**: 250kHz
- **Coding Rate**: 4/5
- **Preamble Length**: 17 symbols
- **SPI Bus**: 0
- **CS Pin**: GPIO 8
- **Reset Pin**: GPIO 24
- **Busy Pin**: GPIO 20
- **IRQ Pin**: GPIO 16
- **TX Enable**: Not used (-1)
- **RX Enable**: GPIO 12

The radio configuration is hardcoded in `common.py` for simplicity and reliability.

## Hardware Setup
Expand All @@ -262,6 +299,13 @@ The radio configuration is hardcoded in `common.py` for simplicity and reliabili
4. Remove old GPIO library if present: `sudo apt remove python3-rpi.gpio`
5. The configuration is pre-set in `common.py` for the Waveshare HAT

### Raspberry Pi with Frequency Labs meshadv-mini
1. Connect Frequency Labs meshadv-mini HAT to Raspberry Pi 40PIN GPIO header
2. Enable SPI interface in Raspberry Pi configuration (raspi-config)
3. Install required GPIO library: `sudo apt install python3-rpi.lgpio`
4. Remove old GPIO library if present: `sudo apt remove python3-rpi.gpio`
5. The configuration is pre-set in `common.py` for the meshadv-mini

### Clockwork uConsole
1. The uConsole has the SX1262 radio pre-integrated
2. Enable SPI1 in `/boot/firmware/config.txt`:
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ await node.start()

## Acknowledgements

- Thanks to [MeshCore](https://github.com/meshcore-dev) for the original C++ implementation.
- Thanks to [MeshCore](https://github.com/meshcore-dev) for the original C++ implementation.
- Appreciation to **@scott_33238**, **@liamcottle**, **@recrof**, and **@cheaporeps** on Discord
for their ongoing help and patience with my questions.
for their ongoing help and patience with my questions.
- Waveshare LoRaRF library, modified to use modern `gpiozero` library (`DigitalInputDevice` and `DigitalOutputDevice`) for all GPIO operations, replacing legacy RPi.GPIO methods for compatibility across all recent Raspberry Pi models (Zero, 3, 4, 5)
- Contributors and third-party libraries (see `pyproject.toml`)

Expand Down
82 changes: 65 additions & 17 deletions docs/docs/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This guide references working examples from the `examples/` directory:
- **`send_channel_message.py`**: Send messages to group channels
- **`ping_repeater_trace.py`**: Network diagnostics using trace packets

All examples use the `common.py` utilities for shared setup and support both Waveshare HAT and uConsole radio configurations.
All examples use the `common.py` utilities for shared setup and support Waveshare HAT, uConsole radio, and meshadv-mini configurations.

### Running Examples

Expand All @@ -45,6 +45,11 @@ python examples/send_text_message.py
python examples/send_flood_advert.py uconsole
python examples/send_direct_advert.py uconsole
python examples/send_text_message.py uconsole

# Run examples with meshadv-mini radio
python examples/send_flood_advert.py meshadv-mini
python examples/send_direct_advert.py meshadv-mini
python examples/send_text_message.py meshadv-mini
```

## Radio Setup
Expand Down Expand Up @@ -135,6 +140,49 @@ radio = SX1262Radio(
radio.begin()
```

### meshadv and meshadv-mini LoRaWAN/GNSS HAT

The meshadv and meshadv-mini are available from FrequencyLabs, and are open source hardware. They're based on the E22-900M22S module. The Meshadv has the same pinout (more or less) as the waveshare module, but the meshadv mini is slightly different, so it needs its own definition file. The documentation for the meshadv and meshadv Mini are available on [github](https://github.com/chrismyers2000)

**Hardware Setup:**
1. Connect the HAT to Raspberry Pi 40PIN GPIO header
2. Ensure SPI interface is enabled in Raspberry Pi configuration
3. Install required GPIO library: `sudo apt install python3-rpi.lgpio`

**Pin Configuration (Raspberry Pi):**
- SPI Bus: SPI0 (MOSI, MISO, SCLK pins)
- CS: GPIO 21
- Reset: GPIO 18
- Busy: GPIO 20
- IRQ (DIO1): GPIO 16
- TXEN: GPIO 6
- RXEN: Connected to DIO2 (not used directly)

```python
from pymc_core.hardware.sx1262_wrapper import SX1262Radio

# meshadv-mini HAT configuration (matches official pinout)
radio = SX1262Radio(
bus_id=0, # SPI bus 0
cs_id=0, # SPI chip select 0
cs_pin=8, # CS pin (GPIO 8)
reset_pin=24, # Reset pin (GPIO 24)
busy_pin=20, # Busy pin (GPIO 20)
irq_pin=16, # Interrupt pin (GPIO 16)
txen_pin=-1, # TX enable not connected
rxen_pin=12, # RX enable pin (GPIO 12)
frequency=910525000, # 910.525 MHz (US standard)
tx_power=22, # 22 dBm
spreading_factor=7, # Spreading factor
bandwidth=62500, # 62.5 kHz
coding_rate=5, # 4/5 coding rate
preamble_length=17 # Preamble length
)

# Initialize the radio
radio.begin()
```

### Alternative Radio Configuration

For custom hardware setups, you can customize the pin configuration:
Expand All @@ -156,22 +204,22 @@ radio.begin()

### Radio Configuration Parameters

| Parameter | Description | Waveshare HAT | uConsole |
|-----------|-------------|---------------|----------|
| `bus_id` | SPI bus ID | 0 | 1 |
| `cs_id` | SPI chip select ID | 0 | 0 |
| `cs_pin` | Chip select GPIO pin | 21 | -1 |
| `reset_pin` | Reset GPIO pin | 18 | 25 |
| `busy_pin` | Busy GPIO pin | 20 | 24 |
| `irq_pin` | Interrupt GPIO pin | 16 | 26 |
| `txen_pin` | TX enable GPIO pin | 6 | -1 |
| `rxen_pin` | RX enable GPIO pin | -1 | -1 |
| `frequency` | Operating frequency in Hz | 869525000 (EU) | 915000000 (US) |
| `tx_power` | Transmit power in dBm | 22 | 22 |
| `spreading_factor` | LoRa spreading factor (7-12) | 11 | 11 |
| `bandwidth` | Bandwidth in Hz | 250000 | 250000 |
| `coding_rate` | Coding rate (5=4/5, 6=4/6, etc.) | 5 | 5 |
| `preamble_length` | Preamble length | 17 | 17 |
| Parameter | Description | Waveshare HAT | meshadv-mini | uConsole |
|--------------------|----------------------------------|----------------|----------------|----------------|
| `bus_id` | SPI bus ID | 0 | 0 | 1 |
| `cs_id` | SPI chip select ID | 0 | 0 | 0 |
| `cs_pin` | Chip select GPIO pin | 21 | 8 | -1 |
| `reset_pin` | Reset GPIO pin | 18 | 24 | 25 |
| `busy_pin` | Busy GPIO pin | 20 | 20 | 24 |
| `irq_pin` | Interrupt GPIO pin | 16 | 16 | 26 |
| `txen_pin` | TX enable GPIO pin | 6 | -1 | -1 |
| `rxen_pin` | RX enable GPIO pin | -1 | 12 | -1 |
| `frequency` | Operating frequency in Hz | 869525000 (EU) | 910525000 (US) | 869525000 (EU) |
| `tx_power` | Transmit power in dBm | 22 | 22 | 22 |
| `spreading_factor` | LoRa spreading factor (7-12) | 11 | 7 | 11 |
| `bandwidth` | Bandwidth in Hz | 250000 | 62500 | 250000 |
| `coding_rate` | Coding rate (5=4/5, 6=4/6, etc.) | 5 | 5 | 5 |
| `preamble_length` | Preamble length | 17 | 17 | 17 |

**Note:** Adjust the `frequency` parameter based on your regional LoRa regulations (868MHz for EU, 915MHz for US, 433MHz for Asia).

Expand Down
27 changes: 21 additions & 6 deletions examples/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,15 @@ def create_radio(radio_type: str = "waveshare") -> LoRaRadio:
"reset_pin": 18,
"busy_pin": 20,
"irq_pin": 16,
"txen_pin": 6, # GPIO 6 for TX enable
"rxen_pin": -1,
"txen_pin": 13, # GPIO 13 for TX enable
"rxen_pin": 12,
Comment on lines +54 to +55
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed waveshare configuration from txen_pin: 6 to txen_pin: 13 and added rxen_pin: 12 (was -1). This is a significant hardware configuration change that may break existing waveshare HAT setups. Ensure this change is documented in release notes and that users are aware they may need to update their hardware configuration.

Copilot uses AI. Check for mistakes.
"frequency": int(869.525 * 1000000), # EU: 869.525 MHz
"tx_power": 22,
"spreading_factor": 11,
"bandwidth": int(250 * 1000),
"coding_rate": 5,
"preamble_length": 17,
"is_waveshare": True,
},
"uconsole": {
"bus_id": 1, # SPI1
Expand All @@ -76,11 +77,27 @@ def create_radio(radio_type: str = "waveshare") -> LoRaRadio:
"coding_rate": 5,
"preamble_length": 17,
},
"meshadv-mini": {
"bus_id": 0,
"cs_id": 0,
"cs_pin": 8,
"reset_pin": 24,
"busy_pin": 20,
"irq_pin": 16,
"txen_pin": -1,
"rxen_pin": 12,
"frequency": int(910.525 * 1000000), # US: 910.525 MHz
"tx_power": 22,
"spreading_factor": 7,
"bandwidth": int(62.5 * 1000),
"coding_rate": 5,
"preamble_length": 17,
},
}

if radio_type not in configs:
raise ValueError(
f"Unknown radio type: {radio_type}. Use 'waveshare' or 'uconsole'"
f"Unknown radio type: {radio_type}. Use 'waveshare' 'meshadv-mini' or 'uconsole'"
)

radio_kwargs = configs[radio_type]
Expand Down Expand Up @@ -119,9 +136,7 @@ def create_mesh_node(
# Create a local identity (this generates a new keypair)
logger.debug("Creating LocalIdentity...")
identity = LocalIdentity()
logger.info(
f"Created identity with public key: {identity.get_public_key().hex()[:16]}..."
)
logger.info(f"Created identity with public key: {identity.get_public_key().hex()[:16]}...")

# Create the SX1262 radio
logger.debug("Creating radio...")
Expand Down
4 changes: 1 addition & 3 deletions examples/ping_repeater_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ def on_trace_response(success: bool, response_text: str, response_data: dict):
trace_handler = mesh_node.dispatcher.trace_handler
if trace_handler:
# Use the target repeater's hash for the callback
repeater_hash_hex = (
"b5d8df576ee9ab9ba4e71dc3ef753c6383f1215306139b0cc3bb2c02136d7f65"
)
repeater_hash_hex = "b5d8df576ee9ab9ba4e71dc3ef753c6383f1215306139b0cc3bb2c02136d7f65"
repeater_pubkey_hash = bytes.fromhex(repeater_hash_hex)
repeater_hash = repeater_pubkey_hash[0]

Expand Down
4 changes: 2 additions & 2 deletions examples/send_flood_advert.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def main(radio_type: str = "waveshare"):
# Parse command line arguments
radio_type = sys.argv[1] if len(sys.argv) > 1 else "waveshare"

if radio_type not in ["waveshare", "uconsole"]:
print("Usage: python send_flood_advert.py [waveshare|uconsole]")
if radio_type not in ["waveshare", "uconsole", "meshadv-mini"]:
print("Usage: python send_flood_advert.py [waveshare|uconsole|meshadv-mini]")
sys.exit(1)

main(radio_type)
5 changes: 1 addition & 4 deletions examples/send_tracked_advert.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ async def simple_repeat_counter(packet, raw_data=None):

try:
# Check if this is an advert packet
if (
hasattr(packet, "get_payload_type")
and packet.get_payload_type() == PAYLOAD_TYPE_ADVERT
):
if hasattr(packet, "get_payload_type") and packet.get_payload_type() == PAYLOAD_TYPE_ADVERT:
repeat_count += 1
print(f"ADVERT REPEAT HEARD #{repeat_count}")
except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "pymc_core"
version = "1.0.0"
version = "1.0.1"
authors = [
{name = "Lloyd Newton", email = "[email protected]"},
]
Expand Down
2 changes: 1 addition & 1 deletion src/pymc_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Clean, simple API for building mesh network applications.
"""

__version__ = "1.0.0"
__version__ = "1.0.1"

# Core mesh functionality
from .node.node import MeshNode
Expand Down
Loading