mirror of
https://github.com/sascha-hemi/pycom-documentation.git
synced 2026-03-21 10:05:56 +01:00
Added Pymesh chapter
This commit is contained in:
@@ -1,139 +0,0 @@
|
||||
---
|
||||
title: "Pymesh"
|
||||
aliases:
|
||||
- tutorials/lora/lora-mesh.html
|
||||
- tutorials/lora/lora-mesh.md
|
||||
- chapter/tutorials/lora/lora-mesh
|
||||
---
|
||||
|
||||
{{% hint style="info" %}}
|
||||
These API's are currently only available in the latest RC builds.
|
||||
{{% /hint %}}
|
||||
|
||||
The Pymesh LoRa Mesh is implemented using [OpenThread](https://openthread.io/guides/thread-primer).
|
||||
|
||||
To understand the OpenThread terms and overall functionality, these guides are highly recommended:
|
||||
|
||||
* [What is Thread?](https://openthread.io/guides/thread-primer)
|
||||
* [Node Roles and Types](https://openthread.io/guides/thread-primer/node-roles-and-types)
|
||||
* [IPv6 Addressing](https://openthread.io/guides/thread-primer/ipv6-addressing) (especially, RLOC unicast address)
|
||||
|
||||
Important, all the OpenThread CLI commands are accessible using `LoRa.cli("command")`, the complete list of commands is [here](https://github.com/openthread/openthread/blob/c482301ec73b80985445102e4d0a936346172ddb/src/cli/README). Please note some commands, can't execute, as some functionalities are not implemented (ex: Commissioning Role, Joiner Role, DNS).
|
||||
|
||||
This example shows how to:
|
||||
|
||||
* enable LoRa-Mesh network
|
||||
* find neighbors (parent, children, other router direct connections)
|
||||
* send PING to neighbors
|
||||
* open UDP socket for:
|
||||
* listening incoming UDP packets and answering back (ACK)
|
||||
* sending packets to all neighbors
|
||||
* toggle LED as packet/ping is received.
|
||||
|
||||
{{% hint style="info" %}}
|
||||
The LoRa-Mesh socket supports only the following socket methods: `close()`, `bind()`, `sendto()`, and `recvfrom()`.
|
||||
{{% /hint %}}
|
||||
|
||||
**Lora Mesh example**
|
||||
|
||||
* Source: [https://github.com/pycom/pycom-libraries/blob/master/lib/lora\_mesh/main.py](https://github.com/pycom/pycom-libraries/blob/master/lib/lora_mesh/main.py)
|
||||
* `Loramesh` micropython library is available at [https://github.com/pycom/pycom-libraries/blob/master/lib/lora\_mesh/loramesh.py](https://github.com/pycom/pycom-libraries/blob/master/lib/lora_mesh/loramesh.py).
|
||||
|
||||
```python
|
||||
|
||||
from network import LoRa
|
||||
import socket
|
||||
import time
|
||||
import utime
|
||||
import ubinascii
|
||||
import pycom
|
||||
import machine
|
||||
|
||||
from loramesh import Loramesh
|
||||
|
||||
pycom.wifi_on_boot(False)
|
||||
pycom.heartbeat(False)
|
||||
|
||||
lora = LoRa(mode=LoRa.LORA, region=LoRa.EU868, bandwidth=LoRa.BW_125KHZ, sf=7)
|
||||
MAC = str(ubinascii.hexlify(lora.mac()))[2:-1]
|
||||
print("LoRa MAC: %s"%MAC)
|
||||
|
||||
mesh = Loramesh(lora)
|
||||
|
||||
# waiting until it connected to Mesh network
|
||||
while True:
|
||||
mesh.led_state()
|
||||
print("%d: State %s, single %s"%(time.time(), mesh.cli('state'), mesh.cli('singleton')))
|
||||
time.sleep(2)
|
||||
if not mesh.is_connected():
|
||||
continue
|
||||
|
||||
print('Neighbors found: %s'%mesh.neighbors())
|
||||
break
|
||||
|
||||
# create UDP socket
|
||||
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
|
||||
myport = 1234
|
||||
s.bind(myport)
|
||||
|
||||
# handler responisble for receiving packets on UDP Pymesh socket
|
||||
def receive_pack():
|
||||
# listen for incomming packets
|
||||
while True:
|
||||
rcv_data, rcv_addr = s.recvfrom(128)
|
||||
if len(rcv_data) == 0:
|
||||
break
|
||||
rcv_ip = rcv_addr[0]
|
||||
rcv_port = rcv_addr[1]
|
||||
print('Incomming %d bytes from %s (port %d)'%(len(rcv_data), rcv_ip, rcv_port))
|
||||
print(rcv_data)
|
||||
# could send some ACK pack:
|
||||
if rcv_data.startswith("Hello"):
|
||||
try:
|
||||
s.sendto('ACK ' + MAC + ' ' + str(rcv_data)[2:-1], (rcv_ip, rcv_port))
|
||||
except Exception:
|
||||
pass
|
||||
mesh.blink(7, .3)
|
||||
|
||||
pack_num = 1
|
||||
msg = "Hello World! MAC: " + MAC + ", pack: "
|
||||
ip = mesh.ip()
|
||||
mesh.mesh.rx_cb(receive_pack)
|
||||
|
||||
# infinite main loop
|
||||
while True:
|
||||
mesh.led_state()
|
||||
print("%d: State %s, single %s, IP %s"%(time.time(), mesh.cli('state'), mesh.cli('singleton'), mesh.ip()))
|
||||
|
||||
# check if topology changes, maybe RLOC IPv6 changed
|
||||
new_ip = mesh.ip()
|
||||
if ip != new_ip:
|
||||
print("IP changed from: %s to %s"%(ip, new_ip))
|
||||
ip = new_ip
|
||||
|
||||
# update neighbors list
|
||||
neigbors = mesh.neighbors_ip()
|
||||
print("%d neighbors, IPv6 list: %s"%(len(neigbors), neigbors))
|
||||
|
||||
# send PING and UDP packets to all neighbors
|
||||
for neighbor in neigbors:
|
||||
if mesh.ping(neighbor) > 0:
|
||||
print('Ping OK from neighbor %s'%neighbor)
|
||||
mesh.blink(10, .1)
|
||||
else:
|
||||
print('Ping not received from neighbor %s'%neighbor)
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
pack_num = pack_num + 1
|
||||
try:
|
||||
s.sendto(msg + str(pack_num), (neighbor, myport))
|
||||
print('Sent message to %s'%(neighbor))
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(20 + machine.rng()%20)
|
||||
|
||||
# random sleep time
|
||||
time.sleep(30 + machine.rng()%30)
|
||||
|
||||
```
|
||||
@@ -1,172 +0,0 @@
|
||||
---
|
||||
title: "Pymesh Border Router"
|
||||
aliases:
|
||||
- tutorials/lora/pymesh-br.html
|
||||
- tutorials/lora/pymesh-br.md
|
||||
---
|
||||
|
||||
|
||||
{{% hint style="info" %}}
|
||||
These API's are currently only available in the latest RC builds.
|
||||
{{% /hint %}}
|
||||
|
||||
The following script exemplifies the declaration of a Border Router inside Pymesh.
|
||||
|
||||
The Border Router is a role of a Pymesh node that can forward the Pymesh internal data to the Cloud. This is accomplished with the following steps:
|
||||
|
||||
- declare the BR network address prefix, like `2001:dead:beef:cafe::/64`.
|
||||
- in this particular script, as example the node which has LoRa MAC as value `8` will set BR interface.
|
||||
- in a more practical usecase, the BR should be enabled if Wifi/BLE/Cellular connections are setup.
|
||||
|
||||
- all the nodes within the Pymesh will create a random IPv6 unicast address with this prefix.
|
||||
|
||||
- if a node sends data to an IPv6 which is external (like `1:2:3::4`), this UDP datagram will be routed to the BR.
|
||||
|
||||
- BR will receive the UDP datagrams for external with an appended header, which contains:
|
||||
- MAGIC byte: `0xBB`
|
||||
- IPv6 destination as bytearray (16 bytes)
|
||||
- port destination as 2 bytes (1-65535 values).
|
||||
- the IPv6 destination is important so BR could actually route (forward) the UDP datagram content to the right interface (Wifi/BLE/cellular).
|
||||
|
||||
After this script is executed, some example of testing:
|
||||
|
||||
- send data to the cloud
|
||||
|
||||
`eid_socket.sendto("01234567890123456789", ("1::2", 1235))`
|
||||
|
||||
- send data to the EID ipv6 of another Node inside Pymesh
|
||||
|
||||
`eid_socket.sendto("0123456789", ("fdde:ad00:beef:0:4623:91c8:64b2:d9ec", 1235))`
|
||||
|
||||
- send data to the Leader
|
||||
|
||||
`eid_socket.sendto("0123456789", ("fdde:ad00:beef:0:0:ff:fe00:fc00", 1235))`
|
||||
|
||||
- send data to the RLOC of another Node inside Pymesh
|
||||
|
||||
`eid_socket.sendto("0123456789", ("fdde:ad00:beef:0:0:ff:fe00:6800", 1235))`
|
||||
|
||||
|
||||
|
||||
## Border Router example
|
||||
|
||||
- Source: [https://github.com/pycom/pycom-libraries/blob/master/lib/lora_mesh/main_border_router.py](https://github.com/pycom/pycom-libraries/blob/master/lib/lora_mesh/main_border_router.py)
|
||||
- `Loramesh` micropython library is available at [https://github.com/pycom/pycom-libraries/blob/master/lib/lora\_mesh/loramesh.py](https://github.com/pycom/pycom-libraries/blob/master/lib/lora_mesh/loramesh.py).
|
||||
|
||||
```python
|
||||
|
||||
from network import LoRa
|
||||
import ubinascii
|
||||
from loramesh import Loramesh
|
||||
import pycom
|
||||
import time
|
||||
import socket
|
||||
import struct
|
||||
|
||||
BORDER_ROUTER_HEADER_FORMAT = '!BHHHHHHHHH'
|
||||
BORDER_ROUTER_MAGIC_BYTE = 0xBB
|
||||
|
||||
pycom.wifi_on_boot(False)
|
||||
pycom.heartbeat(False)
|
||||
border_router_net = "2001:dead:beef:cafe::/64"
|
||||
|
||||
lora = LoRa(mode=LoRa.LORA, region=LoRa.EU868, bandwidth=LoRa.BW_125KHZ, sf=7)
|
||||
#mesh = lora.Mesh()
|
||||
MAC = str(ubinascii.hexlify(lora.mac()))[2:-1]
|
||||
print("LoRa MAC: %s"%MAC)
|
||||
|
||||
mesh = Loramesh(lora)
|
||||
|
||||
# waiting until it connected to Mesh network
|
||||
while True:
|
||||
mesh.led_state()
|
||||
print("%d: State %s, single %s"%(time.time(), mesh.cli('state'), mesh.cli('singleton')))
|
||||
time.sleep(2)
|
||||
if not mesh.is_connected():
|
||||
continue
|
||||
|
||||
print('Neighbors found: %s'%mesh.neighbors())
|
||||
print('IPs: %s'%mesh.mesh.ipaddr())
|
||||
break
|
||||
|
||||
sockets = []
|
||||
#add BR for a certain MAC address
|
||||
# or in a certain condition (Wifi/BLE/cellular connection to Internet)
|
||||
if int(MAC, 16) == 8:
|
||||
if len(mesh.mesh.border_router()) == 0:
|
||||
mesh.mesh.border_router(border_router_net, 0)
|
||||
print("Set Border Router with prefix %s"%border_router_net)
|
||||
|
||||
# create UDP socket for Border Router interface
|
||||
br_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
|
||||
myport = 1234
|
||||
print("Please wait until BR gets propagated to the Leader ...")
|
||||
while True:
|
||||
ip = mesh.ip(border_router_net)
|
||||
if ip is not None:
|
||||
br_socket.bind((ip, myport))
|
||||
print("Created socked for (%s, %d)"%(ip, myport))
|
||||
break
|
||||
time.sleep(1)
|
||||
sockets.append(br_socket)
|
||||
|
||||
eid_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
|
||||
myport = 1235
|
||||
#ip = mesh.ip()# ipv6 EID
|
||||
ip = "::" # in this case, socket can be bind just on a port, like: eid_socket.bind(myport)
|
||||
if ip is not None:
|
||||
eid_socket.bind((ip, myport))
|
||||
#eid_socket.bind(myport)
|
||||
print("Created socked for (%s, %d)"%(ip, myport))
|
||||
sockets.append(eid_socket)
|
||||
|
||||
# handler responsible for receiving packets on UDP Pymesh socket
|
||||
def receive_pack(sockets):
|
||||
# listen for incoming packets on all sockets
|
||||
while True:
|
||||
is_new_data = False
|
||||
for sock in sockets:
|
||||
# check if data received on all sockets
|
||||
rcv_data, rcv_addr = sock.recvfrom(128)
|
||||
if len(rcv_data) > 0:
|
||||
is_new_data = True
|
||||
break # out of for sock
|
||||
if not is_new_data:
|
||||
break # out of while True
|
||||
rcv_ip = rcv_addr[0]
|
||||
rcv_port = rcv_addr[1]
|
||||
print('Incoming %d bytes from %s (port %d)'%(len(rcv_data), rcv_ip, rcv_port))
|
||||
|
||||
#check if data is for the external of the Pymesh (for The Cloud)
|
||||
if rcv_data[0] == BORDER_ROUTER_MAGIC_BYTE and len(rcv_data) >= struct.calcsize(BORDER_ROUTER_HEADER_FORMAT):
|
||||
br_header = struct.unpack(BORDER_ROUTER_HEADER_FORMAT, rcv_data)
|
||||
print("IP dest: %x:%x:%x:%x:%x:%x:%x:%x (port %d)"%(
|
||||
br_header[1],br_header[2],br_header[3],br_header[4],
|
||||
br_header[5],br_header[6],br_header[7],br_header[8], br_header[9]))
|
||||
rcv_data = rcv_data[struct.calcsize(BORDER_ROUTER_HEADER_FORMAT):]
|
||||
|
||||
print(rcv_data)
|
||||
|
||||
# send some ACK
|
||||
if not rcv_data.startswith("ACK"):
|
||||
print("Sent ACK back")
|
||||
sock.sendto('ACK', (rcv_ip, rcv_port))
|
||||
|
||||
mesh.mesh.rx_cb(receive_pack, sockets)
|
||||
|
||||
print('IPs: %s'%mesh.mesh.ipaddr())
|
||||
print('BRs: %s'%mesh.mesh.border_router())
|
||||
|
||||
"""
|
||||
Example of usage:
|
||||
* send data to the cloud
|
||||
eid_socket.sendto("01234567890123456789", ("1::2", 1235))
|
||||
* send data to the EID ip of another Node inside Pymesh
|
||||
eid_socket.sendto("0123456789", ("fdde:ad00:beef:0:4623:91c8:64b2:d9ec", 1235))
|
||||
* send data to the Leader
|
||||
eid_socket.sendto("0123456789", ("fdde:ad00:beef:0:0:ff:fe00:fc00", 1235))
|
||||
* send data to the RLOC of another Node inside Pymesh
|
||||
eid_socket.sendto("0123456789", ("fdde:ad00:beef:0:0:ff:fe00:6800", 1235))
|
||||
"""
|
||||
|
||||
```
|
||||
Reference in New Issue
Block a user