Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
1e5d4ba
Add transport key to packet and utilities for mesh packet calcs
rightup Nov 11, 2025
914c09e
Update src/pymc_core/protocol/transport_keys.py
rightup Nov 22, 2025
b0dce21
Update src/pymc_core/protocol/transport_keys.py
rightup Nov 22, 2025
4c353b0
Update src/pymc_core/protocol/packet.py
rightup Nov 22, 2025
a191c78
Initial plan
Copilot Nov 22, 2025
dde62c6
Merge pull request #16 from rightup/feat/trans-code
rightup Nov 22, 2025
28134ff
Export transport key functions in public API
Copilot Nov 22, 2025
862c2d1
Merge pull request #17 from rightup/copilot/sub-pr-16
rightup Nov 22, 2025
7980c7a
Bump version to 1.0.6 in pyproject.toml and __init__.py; update test …
rightup Nov 22, 2025
ebeb754
Add upper-case hex string representation for packet hash and correspo…
ppicazo Nov 24, 2025
08f37bd
Update get_packet_hash_hex method to allow None as a length parameter…
ppicazo Nov 24, 2025
6f818d9
Implement control packet handling for mesh node discovery
rightup Nov 26, 2025
daef849
Merge pull request #18 from ppicazo/feature/uppercase-packet-hash-str…
rightup Nov 26, 2025
bfc1d85
add control responder example
rightup Nov 27, 2025
ceb04a1
Add support for non-retransmission flag in Packet class
rightup Nov 27, 2025
fad81f3
Change default node type to REPEATER in discovery responder example
rightup Nov 27, 2025
9919e86
Update src/pymc_core/protocol/packet_builder.py
rightup Nov 27, 2025
36dcd1c
Update examples/discover_nodes.py
rightup Nov 27, 2025
61b9a11
Update src/pymc_core/node/handlers/control.py
rightup Nov 27, 2025
dba313e
Update examples/discover_nodes.py
rightup Nov 27, 2025
0df9ceb
Update src/pymc_core/protocol/packet_builder.py
rightup Nov 27, 2025
7cfebd4
Merge pull request #19 from rightup/feat/controlPacket
rightup Nov 27, 2025
e3e933f
Enhance AdvertHandler to validate and extract advert components, ensu…
ppicazo Nov 28, 2025
c07047f
Update src/pymc_core/protocol/packet_builder.py
rightup Nov 28, 2025
c43656f
Merge pull request #20 from ppicazo/rejection_hurts
rightup Nov 28, 2025
0d26da3
Bump version to 1.0.7 and update tests accordingly
rightup Nov 28, 2025
e635aa9
Refactor handlers to improve packet processing and error handling; up…
rightup Dec 1, 2025
564eeb0
Add drop_reason attribute to Packet class
rightup Dec 2, 2025
61fdd3d
setTxPower method to apply PA-safety protection and refine power conf…
rightup Dec 2, 2025
129352c
setTxPower method to simplify power configuration and remove outdated…
rightup Dec 3, 2025
ff7e545
power configuration in SX126x class for improved PA selection and eff…
rightup Dec 3, 2025
5a4f2fa
Update TX power configuration during initialization to use optimized …
rightup Dec 3, 2025
d1449cb
Refactor PA configuration in SX126x class for optimized power setting…
rightup Dec 4, 2025
bcb895c
Power configuration in SX126x class to use fixed PA settings for all …
rightup Dec 4, 2025
83823c4
Add OCP configuration for high power in SX126x class to match RadioLi…
rightup Dec 4, 2025
cb4abe2
disable RF switch during setup
rightup Dec 4, 2025
b027424
Enhance RX gain sensitivity
rightup Dec 4, 2025
4d3694d
Align TXEN/RXEN pin control timing with RadioLib; remove unnecessary …
rightup Dec 4, 2025
aa41d63
Fix antenna resistance in radio setup for improved performance
rightup Dec 4, 2025
7b024d9
Adjust dio3_tcxo_voltage to 1.7 for testing
rightup Dec 4, 2025
41500e8
Update dio3_tcxo_voltage to 1.9 for testing
rightup Dec 4, 2025
442170a
Update dio3_tcxo_voltage to 2.2
rightup Dec 4, 2025
4e63676
Update dio3_tcxo_voltage to 1.8 for consistency with previous settings
rightup Dec 4, 2025
d3738d0
test ramp 40us
rightup Dec 5, 2025
acb9859
Fix ADVERT_FLAG_IS_ROOM_SERVER value to 0x03 for correct bitfield rep…
rightup Dec 7, 2025
4f73cb1
Refactor GPIOPinManager to use python-periphery for GPIO management a…
rightup Dec 8, 2025
9e8c8ec
Handle GPIO read exceptions in input pin monitoring
rightup Dec 8, 2025
8c23b31
Improve GPIO input handling by removing redundant read exception hand…
rightup Dec 8, 2025
709b509
Ensure IRQ status is always cleared to prevent interrupt storms
rightup Dec 8, 2025
97abb99
Improve GPIO input handling by ensuring GPIO read is performed during…
rightup Dec 8, 2025
c49a5ce
Fix IRQ status clearing logic to handle zero status correctly
rightup Dec 8, 2025
e4231eb
Improve edge event handling by reading pin state before invoking call…
rightup Dec 8, 2025
5b04408
Refine interrupt handling by improving logging conditions for unexpec…
rightup Dec 8, 2025
122b634
Increase edge event polling timeout to reduce CPU usage during monito…
rightup Dec 8, 2025
9a413ac
Refine RX interrupt mask to include only actionable interrupts
rightup Dec 8, 2025
392f5d7
Enhance interrupt logging to diagnose spurious triggers and simplify …
rightup Dec 8, 2025
477ee5e
Remove spurious interrupt logging
rightup Dec 8, 2025
eca88a0
Refactor interrupt handling to defer IRQ status clearing to the backg…
rightup Dec 8, 2025
82e6a36
change RX interrupt handling to store IRQ status for background proce…
rightup Dec 8, 2025
5fbbc1c
Update node type extraction in advert flag handling and support for s…
rightup Dec 8, 2025
9edc25c
Suppress warning for cleared TX_TIMEOUT interrupts in interrupt handler
rightup Dec 8, 2025
a0e6f13
Remove warning log for CAD interrupt when CAD_DONE flag is not set
rightup Dec 9, 2025
adfc17a
Update src/pymc_core/protocol/packet.py
rightup Dec 9, 2025
588c79c
Update src/pymc_core/hardware/gpio_manager.py
rightup Dec 9, 2025
415d882
Remove backup GPIO manager implementation
rightup Dec 9, 2025
647fb7a
Merge pull request #23 from rightup/feat/valid-packets-checks
rightup Dec 9, 2025
7616627
Enhance GPIO error handling with detailed messages for busy and permi…
rightup Dec 10, 2025
2f2c28d
Further checks for advert signature verification with detailed loggin…
rightup Dec 10, 2025
b13d5e6
Add delay after interrupt callback to improve CPU efficiency
rightup Dec 10, 2025
50b322f
event handling in GPIOPinManager to reduce CPU usage and improve cal…
rightup Dec 10, 2025
a24ba13
Add delay in interrupt handling to prevent radio lockups during heavy…
rightup Dec 10, 2025
b477e9a
Remove unnecessary 1ms delay in interrupt handling to improve respons…
rightup Dec 10, 2025
d3e6458
Implement health check for radio in dispatcher maintenance loop
rightup Dec 10, 2025
abb6459
Add transmission lock to Dispatcher to prevent concurrent packet sends
rightup Dec 10, 2025
2738294
edge event handling in GPIOPinManager to improve callback execution o…
rightup Dec 11, 2025
ee2e9d6
Refactor edge event handling in GPIOPinManager to simplify callback e…
rightup Dec 11, 2025
dd79b3f
Improve edge event handling in GPIOPinManager to prevent repeated tri…
rightup Dec 11, 2025
dd9be49
Refactor GPIO management in SX126x and sx1262_wrapper to ensure centr…
rightup Dec 11, 2025
7d80e8a
Enhance GPIO pin setup in SX126x to ensure initialization only when n…
rightup Dec 11, 2025
51952ab
Refactor RX interrupt handling in SX1262Radio to improve logging and …
rightup Dec 11, 2025
4939adf
RX interrupt handling in SX1262Radio to utilize LoRaRF's request met…
rightup Dec 11, 2025
98e4916
Improve RX task startup logging and enhance CAD recovery process in S…
rightup Dec 12, 2025
883596b
Fix CAD IRQ handling to read detection status before clearing IRQ flags
rightup Dec 12, 2025
afdc22c
Enhance CAD handling in SX1262Radio by storing results from the inter…
rightup Dec 12, 2025
dc56135
tidy up comments
rightup Dec 12, 2025
8ed3c74
change timeout for large payloads DRO
rightup Dec 12, 2025
cd45c07
change interrupt handling in SX1262Radio by implementing a lightweigh…
rightup Dec 12, 2025
41184cd
improve logging for early interrupts
rightup Dec 12, 2025
3c0a78d
Update src/pymc_core/hardware/sx1262_wrapper.py
rightup Dec 13, 2025
4758ed0
Merge pull request #24 from rightup/fix/timing
rightup Dec 13, 2025
b0a159d
fix AI error ;-/
rightup Dec 13, 2025
ba55e57
Fix SNR by implementing SNR and RSSI conversion utilities.
ppicazo Dec 14, 2025
6aeed80
Merge pull request #25 from ppicazo/ppicazo/snr-fix
rightup Dec 14, 2025
2ea5cb8
Add login server example and server-side handler for ANON_REQ authent…
rightup Dec 15, 2025
ac89e75
Refactor login server to delegate authentication logic to application…
rightup Dec 15, 2025
2b04223
login server functionality by adding hardcoded identity support for t…
rightup Dec 15, 2025
f1935c7
Update routing type to 'direct' for login responses; initialize packe…
rightup Dec 15, 2025
f4fd275
Change routing type to 'flood' for direct requests in LoginServerHandler
rightup Dec 16, 2025
f4442c6
Add txt_type handling to ACK responses in TextMessageHandler
rightup Dec 17, 2025
b552cc0
Update AES key derivation to use first 16 bytes of secret for compati…
rightup Dec 17, 2025
6ab47f0
Implement ProtocolRequestHandler for processing authenticated client …
rightup Dec 17, 2025
f2f1862
LoginServerHandler to support room server format in authentication r…
rightup Dec 17, 2025
c121934
Add debug logging for plaintext details in LoginServerHandler
rightup Dec 18, 2025
cfcfedd
LoginServerHandler to support room server format in ANON_REQ authenti…
rightup Dec 18, 2025
ef5f0de
Add debug logging for plaintext processing in LoginServerHandler
rightup Dec 18, 2025
dba8b7d
Refactor null terminator handling in LoginServerHandler for improved …
rightup Dec 18, 2025
7b5587e
Add delays for hardware stabilization during radio setup and operations
rightup Dec 18, 2025
b436a83
add send metadata
rightup Dec 21, 2025
abf27ca
Add _tx_metadata field to Packet class for enhanced metadata handling
rightup Dec 21, 2025
b630426
Merge pull request #28 from rightup/feat/anon-req
rightup Dec 30, 2025
b6a7ba2
Bump version to 1.0.6 and update related tests
rightup Dec 30, 2025
91f916d
Refactor GPIO management error handling and improve AdvertHandler tests
rightup Dec 30, 2025
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
186 changes: 186 additions & 0 deletions examples/discover_nodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/env python3
"""
Minimal example: Discover nearby mesh nodes.

This example demonstrates how to broadcast a discovery request
and collect responses from nearby repeaters and nodes in the mesh network.

The discovery request is sent as a zero-hop broadcast, and nearby nodes
will respond with their public key and signal strength information.

Features:
- Asynchronous callback-based response collection
- Configurable discovery filter (node types to discover)
- Signal strength data (SNR and RSSI) for each discovered node
- Automatic timeout after specified duration
"""

import asyncio
import random
import time

from common import create_mesh_node

from pymc_core.protocol.packet_builder import PacketBuilder

# ADV_TYPE_REPEATER = 2, so filter mask is (1 << 2) = 0x04
FILTER_REPEATERS = 0x04 # Bit 2 set for repeater node type


async def discover_nodes(
radio_type: str = "waveshare",
serial_port: str = "/dev/ttyUSB0",
timeout: float = 5.0,
filter_mask: int = FILTER_REPEATERS,
):
"""
Discover nearby mesh nodes using control packets.

Args:
radio_type: Radio hardware type ("waveshare", "uconsole", etc.)
serial_port: Serial port for KISS TNC
timeout: How long to wait for responses (seconds)
filter_mask: Node types to discover (bitmask of ADV_TYPE values, e.g., ADV_TYPE_REPEATER = 2, so mask = 0x04 for repeaters)
"""
mesh_node, identity = create_mesh_node("DiscoveryNode", radio_type, serial_port)

# Dictionary to store discovered nodes
discovered_nodes = {}

# Create callback to collect discovery responses
def on_discovery_response(response_data: dict):
"""Handle discovery response callback."""
tag = response_data.get("tag", 0)
node_type = response_data.get("node_type", 0)
inbound_snr = response_data.get("inbound_snr", 0.0) # Their RX of our request
response_snr = response_data.get("response_snr", 0.0) # Our RX of their response
rssi = response_data.get("rssi", 0)
pub_key = response_data.get("pub_key", "")
timestamp = response_data.get("timestamp", 0)

# Get node type name
node_type_names = {1: "Chat Node", 2: "Repeater", 3: "Room Server"}
node_type_name = node_type_names.get(node_type, f"Unknown({node_type})")

# Store node info
node_id = pub_key[:16] # Use first 8 bytes as ID
if node_id not in discovered_nodes:
discovered_nodes[node_id] = {
"pub_key": pub_key,
"node_type": node_type_name,
"inbound_snr": inbound_snr,
"response_snr": response_snr,
"rssi": rssi,
"timestamp": timestamp,
}

print(
f"✓ Discovered {node_type_name}: {node_id}... "
f"(TX→RX SNR: {inbound_snr:+.1f}dB, RX←TX SNR: {response_snr:+.1f}dB, "
f"RSSI: {rssi}dBm)"
)

# Get the control handler and set up callback
control_handler = mesh_node.dispatcher.control_handler
if not control_handler:
print("Error: Control handler not available")
return

# Generate random tag for this discovery request
discovery_tag = random.randint(0, 0xFFFFFFFF)

# Set up callback for responses matching this tag
control_handler.set_response_callback(discovery_tag, on_discovery_response)

# Create discovery request packet
# filter_mask: 0x04 = bit 2 set (1 << ADV_TYPE_REPEATER where ADV_TYPE_REPEATER=2)
# since: 0 = discover all nodes regardless of modification time
pkt = PacketBuilder.create_discovery_request(
tag=discovery_tag, filter_mask=filter_mask, since=0, prefix_only=False
)

print(f"Sending discovery request (tag: 0x{discovery_tag:08X})...")
print(f"Filter mask: 0x{filter_mask:02X} (node types to discover)")
print(f"Waiting {timeout} seconds for responses...\n")

# Send as zero-hop broadcast (no routing path)
success = await mesh_node.dispatcher.send_packet(pkt, wait_for_ack=False)

if success:
print("Discovery request sent successfully")

# Wait for responses
start_time = time.time()
while time.time() - start_time < timeout:
await asyncio.sleep(0.1)

# Display results
print(f"\n{'='*60}")
print(f"Discovery complete - found {len(discovered_nodes)} node(s)")
print(f"{'='*60}\n")

if discovered_nodes:
for node_id, info in discovered_nodes.items():
print(f"Node: {node_id}...")
print(f" Type: {info['node_type']}")
print(f" TX→RX SNR: {info['inbound_snr']:+.1f} dB (our request at their end)")
print(f" RX←TX SNR: {info['response_snr']:+.1f} dB (their response at our end)")
print(f" RSSI: {info['rssi']} dBm")
print(f" Public Key: {info['pub_key']}")
print()
else:
print("No nodes discovered.")
print("This could mean:")
print(" - No nodes are within range")
print(" - No nodes match the filter criteria")
print(" - Radio configuration mismatch")

else:
print("Failed to send discovery request")

# Clean up callback
control_handler.clear_response_callback(discovery_tag)


def main():
"""Main function for running the discovery example."""
import argparse

parser = argparse.ArgumentParser(description="Discover nearby mesh nodes")
parser.add_argument(
"--radio-type",
choices=["waveshare", "uconsole", "meshadv-mini", "kiss-tnc"],
default="waveshare",
help="Radio hardware type (default: waveshare)",
)
parser.add_argument(
"--serial-port",
default="/dev/ttyUSB0",
help="Serial port for KISS TNC (default: /dev/ttyUSB0)",
)
parser.add_argument(
"--timeout",
type=float,
default=5.0,
help="Discovery timeout in seconds (default: 5.0)",
)
parser.add_argument(
"--filter",
type=lambda x: int(x, 0),
default=FILTER_REPEATERS,
help="Node type filter mask (default: 0x04 for repeaters, bit position = node type)",
)

args = parser.parse_args()

print(f"Using {args.radio_type} radio configuration")
if args.radio_type == "kiss-tnc":
print(f"Serial port: {args.serial_port}")

asyncio.run(
discover_nodes(args.radio_type, args.serial_port, args.timeout, args.filter)
)


if __name__ == "__main__":
main()
Loading