mirror of
https://github.com/sascha-hemi/pycom-documentation.git
synced 2026-03-21 12:05:39 +01:00
Added Pymesh library
This commit is contained in:
43
config.toml
43
config.toml
@@ -1272,39 +1272,60 @@ theme = "doc-theme"
|
||||
weight = 95
|
||||
|
||||
[[menu.main]]
|
||||
name = "Pymesh Library API"
|
||||
url = "/pymesh/library"
|
||||
identifier = "pymesh@library"
|
||||
name = "Pymesh LICENCE"
|
||||
url = "/pymesh/licence"
|
||||
identifier = "pymesh@licence"
|
||||
parent = "pymesh"
|
||||
weight = 10
|
||||
|
||||
[[menu.main]]
|
||||
name = "Micropython API"
|
||||
url = "/firmwareapi/pycom/network/lora/pymesh"
|
||||
identifier = "pymesh@api"
|
||||
name = "Simple Example"
|
||||
url = "/pymesh/simple-example"
|
||||
identifier = "pymesh@simple-example"
|
||||
parent = "pymesh"
|
||||
weight = 20
|
||||
|
||||
[[menu.main]]
|
||||
name = "Simple Example"
|
||||
url = "/pymesh/lora-mesh"
|
||||
identifier = "pymesh@lora-mesh"
|
||||
name = "Pymesh Library API"
|
||||
url = "/pymesh/lib-api"
|
||||
identifier = "pymesh@lib-api"
|
||||
parent = "pymesh"
|
||||
weight = 30
|
||||
|
||||
[[menu.main]]
|
||||
name = "Pymesh Library CLI"
|
||||
url = "/pymesh/lib-cli"
|
||||
identifier = "pymesh@lib-cli"
|
||||
parent = "pymesh"
|
||||
weight = 40
|
||||
|
||||
[[menu.main]]
|
||||
name = "Pymesh Library BLE RPC"
|
||||
url = "/pymesh/lib-ble-rpc"
|
||||
identifier = "pymesh@lib-ble-rpc"
|
||||
parent = "pymesh"
|
||||
weight = 50
|
||||
|
||||
[[menu.main]]
|
||||
name = "Border Router Examplee"
|
||||
url = "/pymesh/pymesh-br"
|
||||
identifier = "pymesh@pymesh-br"
|
||||
parent = "pymesh"
|
||||
weight = 40
|
||||
weight = 60
|
||||
|
||||
[[menu.main]]
|
||||
name = "Advanced Security Example"
|
||||
url = "/pymesh/security"
|
||||
identifier = "pymesh@security"
|
||||
parent = "pymesh"
|
||||
weight = 50
|
||||
weight = 70
|
||||
|
||||
[[menu.main]]
|
||||
name = "Micropython API"
|
||||
url = "/firmwareapi/pycom/network/lora/pymesh"
|
||||
identifier = "pymesh@api"
|
||||
parent = "pymesh"
|
||||
weight = 90
|
||||
|
||||
# *** Documentation Notes
|
||||
[[menu.main]]
|
||||
|
||||
@@ -12,11 +12,12 @@ Pymesh is the LoRa full-mesh network technology.
|
||||
|
||||
A Mesh network acts like a net, this means that any node within the network can connect with any other node.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
Mesh networks essentially get rid of gateways, which decentralises the network's infrastructure. This then means that the network becomes flexible, so it can do many wonderful things – such as generate, change and fix itself. The success of the Mesh network is down to its parts, as any node within the network will automatically connect to the best radio-link available.
|
||||
|
||||
Pymesh solution works on all of our LoRa supporting development boards, the LoPy4 and FiPy as well as on our OEM modules, L01 and L04.
|
||||
|
||||
_**Note: For obtaining the Pymesh firmware please follow the steps from [Pymesh LICENCE page](/pymesh/licence).**_
|
||||
|
||||
## What does Pymesh offer you?
|
||||
|
||||
* Ad-hoc communication network over raw-LoRa radio
|
||||
@@ -29,7 +30,11 @@ Pymesh solution works on all of our LoRa supporting development boards, the LoPy
|
||||
|
||||
## Let's get started!
|
||||
|
||||
* [Pymesh Micropython API](/firmwareapi/pycom/network/lora/pymesh)
|
||||
* [Simple Example](/pymesh/lora-mesh)
|
||||
* [Border Router Example](/pymesh/pymesh-br)
|
||||
* [Pymesh LICENCE](/pymesh/licence)
|
||||
* [Simple Example](/pymesh/simple-example)
|
||||
* [Pymesh library API](/pymesh/lib-api)
|
||||
* [Pymesh library CLI](/pymesh/lib-cli)
|
||||
* [Pymesh library BLE RPC](/pymesh/lib-ble-rpc)
|
||||
* [Border Router](/pymesh/pymesh-br)
|
||||
* [Advanced Security Example](/pymesh/security)
|
||||
* [Pymesh Micropython API](/firmwareapi/pycom/network/lora/pymesh)
|
||||
|
||||
67
content/pymesh/lib-api.md
Normal file
67
content/pymesh/lib-api.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: "Pymesh Library API"
|
||||
aliases:
|
||||
- pymesh/lib-api
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This Micropython library is included as frozen scripts in the Pymesh firmware release.
|
||||
|
||||
Code is open-sourced in [pycom-libraries repository](https://github.com/pycom/pycom-libraries/tree/master/lib/pymesh).
|
||||
|
||||
It is easily customisable and contributions are highly recommend using [Github PRs](https://github.com/pycom/pycom-libraries/pulls).
|
||||
|
||||
### Main features
|
||||
|
||||
* Start Pymesh over LoRa on 863Mhz, bandwidth 250kHz, spreading-factor 7 (please check `pymesh_config.py` defaults for exact values).
|
||||
* Pymesh parameters are automatically saved in NVM, so in the next restart/deepsleep, the node will try to maintain its IP addresses and connections with neighbour nodes.
|
||||
* Start Bluetooth LE server with name `PyGo <LoRa MAC>`
|
||||
* BLE is used with an RPC protocol, presented int [Pymesh library BLE RPC](/pymesh/lib-ble-rpc)
|
||||
* Internal CLI for controlling/triggering Pymesh features, as explained in [Pymesh library CLI](/pymesh/lib-cli).
|
||||
|
||||
### Color coding LED
|
||||
|
||||
The LED color represents the state of the node in the Mesh network.
|
||||
|
||||
```
|
||||
|
||||
Blinking - Send/Receive packet
|
||||
Magenta - LEADER
|
||||
Green - ROUTER
|
||||
White - CHILD,
|
||||
Red - Searching / Detached from any Pymesh
|
||||
Cyan - SINGLE LEADER (no other Router in the same Pymesh)
|
||||
```
|
||||
|
||||
## Example of usage
|
||||
|
||||
A simple example of usage is in the [main.py](https://github.com/pycom/pycom-libraries/blob/master/lib/pymesh/main.py).
|
||||
|
||||
## Specifications
|
||||
|
||||
It can be easily customised, by modifying any file from [/lib folder](https://github.com/pycom/pycom-libraries/tree/master/lib/pymesh/lib) and flashing it to the board. Automatically the uploaded file will be executed instead of the _frozen_ one, already embedded into the binary firmware.
|
||||
|
||||
### Structure
|
||||
|
||||
* `pymesh.py`
|
||||
* contains all the methods accessible from `main.py`
|
||||
* `cli.py`
|
||||
* [Pymesh library CLI](/pymesh/lib-cli)
|
||||
* BLE services
|
||||
* `ble_rpc.py`
|
||||
* [Pymesh library BLE RPC](/pymesh/lib-ble-rpc)
|
||||
* `ble_services.py`
|
||||
* setting BLE server
|
||||
* auxiliary files:
|
||||
* `pymesh-debug.py`
|
||||
* debugging on levels, allowing dynamically changing debug level
|
||||
* `pymesh-config.py`
|
||||
* reading/writing Pymesh configuration file
|
||||
* `gps.py`
|
||||
* maintaining location coordinates, as an extension `Pytrack` GPS can be used
|
||||
* Mesh internals
|
||||
* `mesh-interface.py`
|
||||
* methods to inquire Mesh-internal parameters
|
||||
* `mesh-internal.py` and `loramesh.py`
|
||||
* 2 layers of internal mesh maintenance
|
||||
99
content/pymesh/lib-ble-rpc.md
Normal file
99
content/pymesh/lib-ble-rpc.md
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: "Pymesh Library Bluetooth LE RPC"
|
||||
aliases:
|
||||
- pymesh/simple-example
|
||||
---
|
||||
|
||||
## RPC protocol
|
||||
|
||||
It is implemented in [ble_rpc.py](https://github.com/pycom/pycom-libraries/blob/master/lib/pymesh/lib/ble_rpc.py).
|
||||
|
||||
In the class `RPCHandler` methods can be added to expand RPC set.
|
||||
|
||||
The internal RPC are calling methods from file `mesh_interface.py`.
|
||||
|
||||
## Methods
|
||||
|
||||
#### mesh_is_connected()
|
||||
|
||||
This returns `True` if Node is connected to a Pymesh.
|
||||
|
||||
#### mesh_ip()
|
||||
|
||||
This returns the IP RLOC16 in a string.
|
||||
|
||||
#### set_gps(latitude, longitude)
|
||||
|
||||
This sets the location coordinates.
|
||||
|
||||
#### get_mesh_mac_list()
|
||||
|
||||
This returns list of distinct MAC address that are in this mesh network, for example `[mac1, mac2, mac 3]`.`
|
||||
|
||||
#### get_mesh_pairs()
|
||||
|
||||
This returns list of pairs that is a mesh connection, as shown bellow:
|
||||
|
||||
```
|
||||
[
|
||||
('mac1', 'mac2', rssi),
|
||||
('mac1', 'mac3', rssi),
|
||||
#...
|
||||
]
|
||||
```
|
||||
|
||||
#### get_node_info(mac_id = ' ')
|
||||
|
||||
This returns the node data for a specified mac address, or own data id `mac_id` is not specified. Node data is dictionary with the following structure:
|
||||
```
|
||||
{
|
||||
'ip': 4c00, # last 2bytes from the ip v6 RLOC16 address
|
||||
'r': 3, # not_connected:0 | child:1 | leader:2 | router:3
|
||||
'a': 100, # age[sec], time since last info about this node
|
||||
'nn' : 20 # neighbours number
|
||||
'nei': { # neighbours enumerated, if any
|
||||
(mac, ip, role, rssi, age),
|
||||
(mac, ip, role, rssi, age)
|
||||
}
|
||||
'l': { # location, if available
|
||||
'lng': 7,
|
||||
'lat': 20,
|
||||
},
|
||||
'b' : { # BLE infos
|
||||
'a': 100 # age, seconds since last ping with that device, None if properly disconnected
|
||||
'id': '<UUID>' # 16byte
|
||||
'n': '', # name, max. 16 chars
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### send_message(data)
|
||||
|
||||
This sends a message with another node. It return True if there is buffer to store it (to be sent).
|
||||
|
||||
`data` is a dictionary with the following structure:
|
||||
```
|
||||
{
|
||||
'to': 0x5,
|
||||
'b': 'text',
|
||||
'id': 12345,
|
||||
'ts': 123123123,
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### send_message_was_sent(mac, msg_id)
|
||||
|
||||
This checks if acknowledge was received for the specified `mac` and `msg_id`. It returns `True` if message was delivered.
|
||||
|
||||
#### receive_message()
|
||||
|
||||
This returns the received messages, in a dictionary with the next structure:
|
||||
```
|
||||
{
|
||||
'b': 'text',
|
||||
'from': 'ble_device_id',
|
||||
'ts': 123123123,
|
||||
'id': '<uuid>',
|
||||
}
|
||||
```
|
||||
210
content/pymesh/lib-cli.md
Normal file
210
content/pymesh/lib-cli.md
Normal file
@@ -0,0 +1,210 @@
|
||||
---
|
||||
title: "Pymesh Library CLI"
|
||||
aliases:
|
||||
- pymesh/simple-example
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Pymesh micropython library is included as frozen in the Pymesh firmware releases.
|
||||
|
||||
Instead of REPL, a specific Pymesh CLI is interpreting commands and it is recognised, by starting with `>`.
|
||||
|
||||
The CLI is executed on a separate thread inside the Pymesh library.
|
||||
|
||||
For example:
|
||||
```
|
||||
>mml
|
||||
mesh_mac_list [1, 6, 2]
|
||||
```
|
||||
|
||||
## Internal CLI
|
||||
|
||||
```
|
||||
List of available commands
|
||||
ip - display current IPv6 unicast addresses
|
||||
mac - set or display the current LoRa MAC address
|
||||
self - display all info about current node
|
||||
mml - display the Mesh Mac List (MAC of all nodes inside this Mesh), also inquires Leader
|
||||
mp - display the Mesh Pairs (Pairs of all nodes connections), also inquires Leader
|
||||
s - send message
|
||||
ws - verifies if message sent was acknowledged
|
||||
rm - verifies if any message was received
|
||||
sleep - deep-sleep
|
||||
br - enable/disable or display the current Border Router functionality
|
||||
brs - send packet for Mesh-external, to BR, if any
|
||||
rst - reset NOW, including NVM Pymesh IPv6
|
||||
buf - display buffer info
|
||||
ot - sends command to openthread internal CLI
|
||||
debug - set debug level
|
||||
config - print config file contents
|
||||
```
|
||||
|
||||
### Debug commands
|
||||
|
||||
```
|
||||
> debug
|
||||
5
|
||||
```
|
||||
This sets the debug level. Possible values are:
|
||||
```
|
||||
# recommended debug levels, from the most verbose to off
|
||||
DEBUG_DEBG = const(5)
|
||||
DEBUG_INFO = const(4)
|
||||
DEBUG_NOTE = const(3)
|
||||
DEBUG_WARN = const(2)
|
||||
DEBUG_CRIT = const(1)
|
||||
DEBUG_NONE = const(0)
|
||||
```
|
||||
|
||||
```
|
||||
>rst
|
||||
Mesh Reset NVM settings ...
|
||||
deepsleep_init
|
||||
>deepsleep_now
|
||||
Cleanup code, all Alarms cb should be stopped
|
||||
Going to deepsleep for 1 seconds
|
||||
```
|
||||
This resets the Pymesh parameters saved in NVM, and resets the Node.
|
||||
|
||||
```
|
||||
>sleep
|
||||
(time[sec])<10
|
||||
deepsleep_init
|
||||
>deepsleep_now
|
||||
Cleanup code, all Alarms cb should be stopped
|
||||
Going to deepsleep for 10 seconds
|
||||
```
|
||||
This puts the device in deepsleep for designated number of seconds.
|
||||
|
||||
```
|
||||
>buf
|
||||
Buffer info: total: 200
|
||||
free: 200
|
||||
6lo send: 0 0
|
||||
6lo reas: 0 0
|
||||
ip6: 0 0
|
||||
mpl: 0 0
|
||||
mle: 0 0
|
||||
arp: 0 0
|
||||
coap: 0 0
|
||||
coap secure: 0 0
|
||||
application coap: 0 0
|
||||
```
|
||||
This displays the number of buffers for each layer of the Pymesh.
|
||||
|
||||
```
|
||||
>ot
|
||||
(openthread cli)<leaderdata
|
||||
Partition ID: 1494439163
|
||||
Weighting: 64
|
||||
Data Version: 66
|
||||
Stable Data Version: 108
|
||||
Leader Router ID: 46
|
||||
```
|
||||
This executes an Openthread CLI command. The list of CLI commands is [here](https://github.com/openthread/openthread/tree/master/src/cli), please scroll down on github for the Openthread command list.
|
||||
|
||||
### Commands showing Pymesh internal data
|
||||
|
||||
```
|
||||
>mac
|
||||
1
|
||||
```
|
||||
This shows LoRa MAC, this address is used as unique identifier in the Pymesh. Bellow there's a section on how to set MAC specific MAC address (useful for debugging, the MAC could be consecutive small numbers like `0x1`, `0x2`, `...`)
|
||||
|
||||
```
|
||||
>mml
|
||||
mesh_mac_list [1, 6, 2]
|
||||
```
|
||||
This shows the list of all MAC Nodes attached to Pymesh. It inquires Leader, which centralises this data.
|
||||
|
||||
```
|
||||
>mp
|
||||
Send pack: 0xF3 to IP fdde:ad00:beef:0:0:ff:fe00:fc00
|
||||
last_mesh_pairs [[2, 6, -87], [1, 6, -77]]
|
||||
```
|
||||
This shows Mesh Pairs list, with each direct connected nodes (by their MAC address) and the averaged RSSI value.
|
||||
|
||||
```
|
||||
>mni
|
||||
last_mesh_node_info {1: {"ip": 2048, "l": {"lng": 5.45313, "lat": 51.45}, "a": 10, "r": 3, "nn": 1, "nei": [[6, 55296, 3, -76, 23]]}, 6: {"ip": 55296, "l": {"lng": 5.45313, "lat": 51.45}, "a": 7, "r": 3, "nn": 2, "nei": [[2, 50176, 3, -89, 28], [1, 2048, 3, -77, 23]]}, 2: {"ip": 50176, "l": {"lng": 5.45313, "lat": 51.45}, "a": 7, "r": 3, "nn": 1, "nei": [[6, 55296, 3, -86, 25]]}}
|
||||
```
|
||||
This shows the properties for all the nodes in this Pymesh, together with its neighbours.
|
||||
|
||||
```
|
||||
>s
|
||||
(to)<1
|
||||
(txt)<Hello World!
|
||||
1945688: Send Msg ---------------------->>>>>>>>
|
||||
Added new message for 1: Hello World!
|
||||
Send pack: 0x10 to IP fdde:ad00:beef:0::1
|
||||
True
|
||||
>Incoming 13 bytes from fdde:ad00:beef:0:f67b:3d1e:f07:8341 (port 1234):
|
||||
PACK_MESSAGE_ACK received
|
||||
1945883 ================= ACK RECEIVED :) :) :)
|
||||
```
|
||||
This sends text messages to another Node inside Pymesh. The messaging waits for acknowledgement (ACK message type) from the destination node.
|
||||
|
||||
```
|
||||
>ws
|
||||
(to)<1
|
||||
ACK? mac 1, id 12345 => 1
|
||||
True
|
||||
```
|
||||
This shows if a message was acknowledged by the destination Node.
|
||||
|
||||
```
|
||||
>rm
|
||||
{'b': (b'Hello World!',), 'id': 12345, 'ts': 3301, 'from': 6}
|
||||
```
|
||||
This shows the received messages.
|
||||
|
||||
### Auxiliary commands
|
||||
```
|
||||
>gps
|
||||
(lat [Enter for read])<
|
||||
Gps: (51.45, 5.45313)
|
||||
|
||||
> gps
|
||||
(lat [Enter for read])<11.234
|
||||
(lon)<23.45
|
||||
Gps: (11.234, 23.45)
|
||||
```
|
||||
|
||||
This reads the GPS coordinates or sets them to known values.
|
||||
|
||||
```
|
||||
>config
|
||||
{'ble_api': True, 'MAC': 6, 'autostart': True, 'debug': 5, 'LoRa': {'sf': 7, 'region': 5, 'freq': 863000000, 'bandwidth': 2}, 'ble_name_prefix': 'PyGo ', 'Pymesh': {'key': '112233'}}
|
||||
```
|
||||
This displays the configuration, stored on `/flash/pymesh_config.json`.
|
||||
|
||||
### Border Router specific
|
||||
|
||||
```
|
||||
>br
|
||||
(state 0=Disable, 1=Enable, 2=Display [Default Display])<
|
||||
Border Router state: []
|
||||
|
||||
>br
|
||||
(state 0=Disable, 1=Enable, 2=Display [Default Display])<1
|
||||
(priority -1=Low, 0=Normal or 1=High [Default Normal])<
|
||||
State: True BR: []
|
||||
Force add BR
|
||||
BR: [(net='2001:cafe:cafe:cafe::/64', preference=0)]
|
||||
|
||||
>br
|
||||
(state 0=Disable, 1=Enable, 2=Display [Default Display])<
|
||||
Border Router state: [(net='2001:cafe:cafe:cafe::/64', preference=0)]
|
||||
```
|
||||
This enables/disables and shows the state of the current node as Border Router.
|
||||
|
||||
```
|
||||
>brs
|
||||
brs
|
||||
(message<)
|
||||
(IP destination, Mesh-external [Default: 1:2:3::4])<
|
||||
(port destination [Default: 5555])<
|
||||
Send BR message: {'ip': '1:2:3::4', 'b': '', 'port': 5555}
|
||||
```
|
||||
This sends a packet to an IP address external of the Pymesh. This packet will be received by the designated Border Router and it can be further forwarded to another network interface, like: BLE, Wifi, Sigfox or Cellular (Fipy only).
|
||||
@@ -1,72 +0,0 @@
|
||||
---
|
||||
title: "Pymesh Library"
|
||||
aliases:
|
||||
- pymesh/library
|
||||
---
|
||||
|
||||
## What is Pymesh micropython library?
|
||||
|
||||
Pymesh micropython library is a set of scripts included (as frozen) in the Pymesh firmware binary release (Not yet released).
|
||||
|
||||
[Open-source on github](https://github.com/pycom/pycom-libraries/tree/master/lib/pymesh)
|
||||
|
||||
It allows users to use Pymesh in a few lines of code, as shown in the following code snippet.
|
||||
|
||||
```python
|
||||
|
||||
import pycom
|
||||
import time
|
||||
|
||||
from _pymesh_config import PymeshConfig
|
||||
from _pymesh import Pymesh
|
||||
|
||||
# stop LED heartbeat, because it will be used to indicate current Node role
|
||||
pycom.heartbeat(False)
|
||||
|
||||
# read config file, or set default values
|
||||
pymesh_config = PymeshConfig.read_config()
|
||||
|
||||
#initialize Pymesh
|
||||
pymesh = Pymesh(pymesh_config, new_message_cb)
|
||||
|
||||
while not pymesh.is_connected():
|
||||
print(pymesh.status_str())
|
||||
time.sleep(3)
|
||||
|
||||
# send message to the Node having MAC address 6
|
||||
pymesh.send_mess(6, "Hello World")
|
||||
|
||||
def new_message_cb(rcv_ip, rcv_port, rcv_data):
|
||||
''' callback triggered when a new packet arrived '''
|
||||
print('Incoming %d bytes from %s (port %d):' %
|
||||
(len(rcv_data), rcv_ip, rcv_port))
|
||||
print(rcv_data)
|
||||
|
||||
# user code to be inserted, to send packet to the designated Mesh-external interface
|
||||
# ...
|
||||
return
|
||||
|
||||
######################################################################################
|
||||
# Adding current node as Border Router, with a normal priority and a message handler callback
|
||||
pymesh.br_set(PymeshConfig.BR_PRIORITY_NORM, new_br_message_cb)
|
||||
|
||||
# remove Border Router function from current node
|
||||
#pymesh.br_remove()
|
||||
|
||||
# send data for Mesh-external, basically to the BR
|
||||
ip = "1:2:3::4"
|
||||
port = 5555
|
||||
pymesh.send_mess_external(ip, port, "Hello World")
|
||||
|
||||
def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port):
|
||||
''' callback triggered when a new packet arrived for the current Border Router,
|
||||
having destination an IP which is external from Mesh '''
|
||||
print('Incoming %d bytes from %s (port %d), to external IPv6 %s (port %d)' %
|
||||
(len(rcv_data), rcv_ip, rcv_port, dest_ip, dest_port))
|
||||
print(rcv_data)
|
||||
|
||||
# user code to be inserted, to send packet to the designated Mesh-external interface
|
||||
# ...
|
||||
return
|
||||
|
||||
```
|
||||
48
content/pymesh/licence.md
Normal file
48
content/pymesh/licence.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: "Pymesh Library CLI"
|
||||
aliases:
|
||||
- pymesh/simple-example
|
||||
---
|
||||
|
||||
## Licensing process
|
||||
|
||||
In order to receive access to the Pymesh firmware releases (for Lopy4, Fipy, L01 or L04), the next process should be followed:
|
||||
<!-- <a href="/gitbook/assets/specsheets/Pycom_002_Specsheets_LoPy4_v2.pdf" target="\_blank"> the Pymesh LICENCE PDF document </a> -->
|
||||
|
||||
1. Complete the Pymesh LICENCE PDF document(not yet available), sign it and send us by [this email](mailto:catalin@pycom.io?subject=[Pymesh_LICENCE]).
|
||||
1. You will receive on email an archive containing the images for all boards.
|
||||
1. Extract the corresponding image, for example Lopy4.tar.gz, and upload the firmware to your board, using the [Pycom Firmware Update Tool](https://pycom.io/downloads/), like in the following image:
|
||||
<img src="/gitbook/assets/pymesh/pymesh_firmware_update.png" alt="Pymesh Firmware Update" width="500"/>
|
||||
|
||||
## Test Pymesh firmware loading
|
||||
|
||||
### Method 1
|
||||
|
||||
The simplest method to check if Pymesh class is successfully installed, just try this code directly in REPL:
|
||||
|
||||
```python
|
||||
>>> from network import LoRa
|
||||
>>> lora = LoRa(mode=LoRa.LORA)
|
||||
>>> mesh = lora.Mesh()
|
||||
```
|
||||
|
||||
### Method 2
|
||||
|
||||
Upload the `main.py` from the [Simple Example](/pymesh/simple-example).
|
||||
|
||||
## FAQ
|
||||
|
||||
Q: **I've received an error, such as `(LoadProhibited). Exception was unhandled.`, what should I do?**
|
||||
|
||||
A: In some cases, the NVM partition needs to be formatted. For this a format of whole Flash Memory should be performed.
|
||||
|
||||
This can be done using the cli version of the Firmware Update Tool, so please navigate where this app was installed (search for pycom-fwtool-cli executable) and execute:
|
||||
```
|
||||
pycom-fwtool-cli -p <PORT> erase_all
|
||||
```
|
||||
|
||||
`<PORT>` should be replaced with the actual USB COM port, for example:
|
||||
|
||||
* on Windows `COM10`
|
||||
* on Linux `/dev/ttyACM0`
|
||||
* on MacOS ``/dev/tty.usbmodemPy8eaa911`
|
||||
@@ -5,168 +5,63 @@ aliases:
|
||||
- tutorials/lora/pymesh-br.md
|
||||
---
|
||||
|
||||
<img src="/gitbook/assets/pymesh/pymesh_roles.png" alt="Pymesh" width="500"/>
|
||||
|
||||
{{% hint style="info" %}}
|
||||
These API's are currently only available in the latest RC builds.
|
||||
{{% /hint %}}
|
||||
## Overview
|
||||
|
||||
The following script exemplifies the declaration of a Border Router inside Pymesh.
|
||||
The Border Router role, of a Node inside Pymesh, takes the traffic from the Pymesh and forwards it to the Cloud (Pymesh-external).
|
||||
|
||||
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:
|
||||
Several things must be accomplished:
|
||||
|
||||
- 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.
|
||||
* The BR node should have connection with the Cloud, using another network interface (than LoRa-Pymesh), for example: BLE, Wifi or Cellular.
|
||||
* The node has to be declared as BR, so all the other nodes send packets to it, with destination Cloud.
|
||||
* Dynamically multiple nodes can be BR, each of them having different priorities levels (Normal, High or Low)
|
||||
* The BR with the smallest routing cost is chosen.
|
||||
* If multiple BR have the same routing cost, the priority level is used.
|
||||
|
||||
- all the nodes within the Pymesh will create a random IPv6 unicast address with this prefix.
|
||||
## Border Router using CLI
|
||||
|
||||
- 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.
|
||||
As explained in [Pymesh CLI - Border Router section](pymesh/lib-cli/#border-router-specific), using commands `br` and `brs`, a simple testing scenario can be easily implemented.
|
||||
|
||||
- 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))
|
||||
"""
|
||||
## Border Router using Pymesh API
|
||||
|
||||
```
|
||||
def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port):
|
||||
''' callback triggered when a new packet arrived for the current Border Router,
|
||||
having destination an IP which is external from Mesh '''
|
||||
print('Incoming %d bytes from %s (port %d), to external IPv6 %s (port %d)' %
|
||||
(len(rcv_data), rcv_ip, rcv_port, dest_ip, dest_port))
|
||||
print(rcv_data)
|
||||
|
||||
# user code to be inserted, to send packet to the designated Mesh-external interface
|
||||
# ...
|
||||
return
|
||||
|
||||
add current node as Border Router, with a priority and a message handler callback
|
||||
pymesh.br_set(PymeshConfig.BR_PRIORITY_NORM, new_br_message_cb)
|
||||
|
||||
remove Border Router function from current node
|
||||
pymesh.br_remove()
|
||||
|
||||
send data for Mesh-external, basically to the BR
|
||||
ip = "1:2:3::4"
|
||||
port = 5555
|
||||
pymesh.send_mess_external(ip, port, "Hello World")
|
||||
|
||||
```
|
||||
|
||||
## Technical deep dive
|
||||
|
||||
Internally, the Border Router mechanism is implemented with the following steps:
|
||||
|
||||
* declare the BR network address prefix, like `2001:dead:beef:cafe::/64`
|
||||
* this piece of information is sent to the `Leader` and the dataset will increase version (it will be updated)
|
||||
* the dataset is propagated to all Pymesh nodes
|
||||
* all the nodes will be assigned a random IPv6 unicast address with this network prefix (like `2001:dead:beef:cafe:1234:5678:9ABC:DEF0`)
|
||||
* if a node sends data to an IPv6 which is external (it doesn't have prefix from Pymesh already existent networks, like `1:2:3::4`), this UDP datagram will be routed to the BR
|
||||
* this UDP packet will have as source the random IPv6 from BR network address
|
||||
* 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).
|
||||
|
||||
146
content/pymesh/simple-example.md
Normal file
146
content/pymesh/simple-example.md
Normal file
@@ -0,0 +1,146 @@
|
||||
---
|
||||
title: "Pymesh Library"
|
||||
aliases:
|
||||
- pymesh/simple-example
|
||||
---
|
||||
|
||||
## What is Pymesh micropython library?
|
||||
|
||||
Pymesh micropython library is a set of scripts included (as frozen) in the Pymesh firmware binary release (not yet released).
|
||||
|
||||
[Open-source on github](https://github.com/pycom/pycom-libraries/tree/master/lib/pymesh)
|
||||
|
||||
It allows users to use Pymesh in a few lines of code, as shown in the following code snippet.
|
||||
|
||||
```python
|
||||
|
||||
import pycom
|
||||
import time
|
||||
|
||||
try:
|
||||
from pymesh_config import PymeshConfig
|
||||
except:
|
||||
from _pymesh_config import PymeshConfig
|
||||
|
||||
try:
|
||||
from pymesh import Pymesh
|
||||
except:
|
||||
from _pymesh import Pymesh
|
||||
|
||||
def new_message_cb(rcv_ip, rcv_port, rcv_data):
|
||||
''' callback triggered when a new packet arrived '''
|
||||
print('Incoming %d bytes from %s (port %d):' %
|
||||
(len(rcv_data), rcv_ip, rcv_port))
|
||||
print(rcv_data)
|
||||
|
||||
# user code to be inserted, to send packet to the designated Mesh-external interface
|
||||
for _ in range(3):
|
||||
pycom.rgbled(0x888888)
|
||||
time.sleep(.2)
|
||||
pycom.rgbled(0)
|
||||
time.sleep(.1)
|
||||
return
|
||||
|
||||
|
||||
pycom.heartbeat(False)
|
||||
|
||||
# read config file, or set default values
|
||||
pymesh_config = PymeshConfig.read_config()
|
||||
|
||||
#initialize Pymesh
|
||||
pymesh = Pymesh(pymesh_config, new_message_cb)
|
||||
|
||||
mac = pymesh.mac()
|
||||
# based on LoRa MAC address, some nodes could be forced to be
|
||||
# sleep-end-devices (always Child) or to have increased Leader priority
|
||||
# if mac > 10:
|
||||
# pymesh.end_device(True)
|
||||
# elif mac == 5:
|
||||
# pymesh.leader_priority(255)
|
||||
|
||||
while not pymesh.is_connected():
|
||||
print(pymesh.status_str())
|
||||
time.sleep(3)
|
||||
|
||||
# send message to the Node having MAC address 5
|
||||
pymesh.send_mess(5, "Hello World")
|
||||
|
||||
# def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port):
|
||||
# ''' callback triggered when a new packet arrived for the current Border Router,
|
||||
# having destination an IP which is external from Mesh '''
|
||||
# print('Incoming %d bytes from %s (port %d), to external IPv6 %s (port %d)' %
|
||||
# (len(rcv_data), rcv_ip, rcv_port, dest_ip, dest_port))
|
||||
# print(rcv_data)
|
||||
|
||||
# # user code to be inserted, to send packet to the designated Mesh-external interface
|
||||
# # ...
|
||||
# return
|
||||
|
||||
# add current node as Border Router, with a priority and a message handler callback
|
||||
# pymesh.br_set(PymeshConfig.BR_PRIORITY_NORM, new_br_message_cb)
|
||||
|
||||
# remove Border Router function from current node
|
||||
#pymesh.br_remove()
|
||||
|
||||
# send data for Mesh-external, basically to the BR
|
||||
# ip = "1:2:3::4"
|
||||
# port = 5555
|
||||
# pymesh.send_mess_external(ip, port, "Hello World")
|
||||
|
||||
print("done Pymesh init, forever loop, exit/stop with Ctrl+C multiple times")
|
||||
# set BR with callback
|
||||
|
||||
while True:
|
||||
time.sleep(3)
|
||||
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
An example of output is bellow.
|
||||
|
||||
At any point pressing `Enter` will try to execute CLI commands, more details are presented in [Pymesh library CLI](/pymesh/lib-cli).
|
||||
|
||||
```
|
||||
|
||||
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
|
||||
configsip: 0, SPIWP:0xee
|
||||
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
|
||||
mode:DIO, clock div:1
|
||||
load:0x3fff8020,len:8
|
||||
load:0x3fff8028,len:2164
|
||||
load:0x4009fa00,len:19944
|
||||
entry 0x400a05e8
|
||||
MAC ok 11
|
||||
Settings: {'ble_api': True, 'MAC': 11, 'autostart': True, 'debug': 5, 'LoRa': {'sf': 7, 'region': 5, 'freq': 863000000,'bandwidth': 2}, 'ble_name_prefix': 'PyGo ', 'Pymesh': {'key': '112233'}}
|
||||
LoRa MAC: 0xb, short: 11
|
||||
OT stack: 6412
|
||||
Statistics file ok?!
|
||||
============ MESH THREAD >>>>>>>>>>>
|
||||
3: MAC 0xb(11), State Detached, Single True
|
||||
['fdde:ad00:beef:0:0:0:0:b', 'fdde:ad00:beef:0:0:ff:fe00:c400', 'fdde:ad00:beef:0:2c1a:56c0:ebbd:d46', 'fe80:0:0:0:200:0:0:b']
|
||||
>>>>>>>>>>> DONE MESH THREAD ============ 40
|
||||
|
||||
BLE name: PyGo 11
|
||||
Role 3, Single False, IPv6: ['fdde:ad00:beef:0:0:ff:fe00:c400', 'fdde:ad00:beef:0:0:0:0:b', 'fdde:ad00:beef:0:2c1a:56c0:ebbd:d46', 'fe80:0:0:0:200:0:0:b']
|
||||
>Role 3, Single False, IPv6: ['fdde:ad00:beef:0:0:ff:fe00:c400', 'fdde:ad00:beef:0:0:0:0:b', 'fdde:ad00:beef:0:2c1a:56c0:ebbd:d46', 'fe80:0:0:0:200:0:0:b']
|
||||
Role 3, Single False, IPv6: ['fdde:ad00:beef:0:0:ff:fe00:c400', 'fdde:ad00:beef:0:0:0:0:b', 'fdde:ad00:beef:0:2c1a:56c0:ebbd:d46', 'fe80:0:0:0:200:0:0:b']
|
||||
Role 3, Single False, IPv6: ['fdde:ad00:beef:0:0:ff:fe00:c400', 'fdde:ad00:beef:0:0:0:0:b', 'fdde:ad00:beef:0:2c1a:56c0:ebbd:d46', 'fe80:0:0:0:200:0:0:b']
|
||||
============ MESH THREAD >>>>>>>>>>>
|
||||
13: MAC 0xb(11), State Router, Single False
|
||||
['fdde:ad00:beef:0:0:ff:fe00:c400', 'fdde:ad00:beef:0:0:0:0:b', 'fdde:ad00:beef:0:2c1a:56c0:ebbd:d46', 'fe80:0:0:0:200:0:0:b']
|
||||
Leader: mac 0x4, rloc 0xb800, net: 0x591354fb
|
||||
Socket created on port 1234
|
||||
Neighbors Table: [(mac=6, role=3, rloc16=28672, rssi=-67, age=0), (mac=4, role=3, rloc16=47104, rssi=-97, age=4)]
|
||||
Neighbors: Router MAC 0xB, rloc16 0xc400, coord (51.45, 5.45313), neigh_num 2, ts 14
|
||||
MAC 0x6, rloc16 0x7000, role 3, rssi -67, age 0
|
||||
MAC 0x4, rloc16 0xb800, role 3, rssi -97, age 4
|
||||
|
||||
>>>>>>>>>>> DONE MESH THREAD ============ 76
|
||||
|
||||
Send message to 5, typ 0, load Hello World
|
||||
Added new message for 5: Hello World
|
||||
Send message b'\x00\x00\x00\x00\x00\x00\x00\x0b09\x00\x0bHello World'
|
||||
Send pack: 0x10 to IP fdde:ad00:beef:0::5
|
||||
done Pymesh init, forever loop, exit/stop with Ctrl+C multiple times
|
||||
```
|
||||
BIN
static/gitbook/assets/pymesh/pymesh_firmware_update.png
Normal file
BIN
static/gitbook/assets/pymesh/pymesh_firmware_update.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
Reference in New Issue
Block a user