meshcore 0.3.2__tar.gz → 0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {meshcore-0.3.2 → meshcore-0.4}/PKG-INFO +3 -1
- {meshcore-0.3.2 → meshcore-0.4}/README.md +2 -0
- meshcore-0.4/examples/ble_t1000_infos.py +18 -0
- meshcore-0.4/examples/ble_t1000_msg.py +21 -0
- meshcore-0.4/examples/mepo_mc_gps.py +25 -0
- meshcore-0.4/examples/serial_contacts.py +20 -0
- meshcore-0.4/examples/serial_infos.py +21 -0
- meshcore-0.4/examples/serial_msg.py +24 -0
- meshcore-0.4/examples/serial_repeater_status.py +33 -0
- meshcore-0.4/examples/tcp_mchome_contacts.py +18 -0
- meshcore-0.4/examples/tcp_mchome_infos.py +19 -0
- meshcore-0.4/examples/tcp_mchome_msg.py +22 -0
- meshcore-0.4/examples/tcp_mchome_readmsgs.py +25 -0
- {meshcore-0.3.2 → meshcore-0.4}/pyproject.toml +1 -1
- meshcore-0.4/src/meshcore/__init__.py +5 -0
- meshcore-0.4/src/meshcore/ble_cx.py +83 -0
- meshcore-0.3.2/src/meshcore/mclib.py → meshcore-0.4/src/meshcore/meshcore.py +1 -224
- meshcore-0.4/src/meshcore/serial_cx.py +84 -0
- meshcore-0.4/src/meshcore/tcp_cx.py +77 -0
- meshcore-0.3.2/src/meshcore/__init__.py +0 -5
- {meshcore-0.3.2 → meshcore-0.4}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4
|
|
4
4
|
Summary: Base classes for communicating with meshcore companion radios
|
|
5
5
|
Project-URL: Homepage, https://github.com/fdlamotte/meshcore_py
|
|
6
6
|
Project-URL: Issues, https://github.com/fdlamotte/meshcore_py/issues
|
|
@@ -18,5 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
|
|
19
19
|
Bindings to access your [MeshCore](https://meshcore.co.uk) companion radio nodes in python.
|
|
20
20
|
|
|
21
|
+
Can be installed via `pip` using `pip install meshcore`
|
|
22
|
+
|
|
21
23
|
Used by [mccli](https://github.com/fdlamotte/mccli).
|
|
22
24
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import BLEConnection
|
|
7
|
+
|
|
8
|
+
ADDRESS = "t1000"
|
|
9
|
+
|
|
10
|
+
async def main () :
|
|
11
|
+
con = BLEConnection(ADDRESS)
|
|
12
|
+
await con.connect()
|
|
13
|
+
mc = MeshCore(con)
|
|
14
|
+
await mc.connect()
|
|
15
|
+
|
|
16
|
+
print(mc.self_info)
|
|
17
|
+
|
|
18
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import BLEConnection
|
|
7
|
+
|
|
8
|
+
ADDRESS = "t1000" # node ble adress or name
|
|
9
|
+
DEST = "mchome"
|
|
10
|
+
MSG = "Hello World"
|
|
11
|
+
|
|
12
|
+
async def main () :
|
|
13
|
+
con = BLEConnection(ADDRESS)
|
|
14
|
+
await con.connect()
|
|
15
|
+
mc = MeshCore(con)
|
|
16
|
+
await mc.connect()
|
|
17
|
+
|
|
18
|
+
await mc.ensure_contacts()
|
|
19
|
+
await mc.send_msg(bytes.fromhex(mc.contacts[DEST]["public_key"])[0:6],MSG)
|
|
20
|
+
|
|
21
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
DISPLAYNAME="Center at MC node location"
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
|
|
6
|
+
from meshcore import MeshCore
|
|
7
|
+
from meshcore import BLEConnection
|
|
8
|
+
|
|
9
|
+
ADDRESS = "t1000"
|
|
10
|
+
|
|
11
|
+
async def main () :
|
|
12
|
+
con = BLEConnection(ADDRESS)
|
|
13
|
+
await con.connect()
|
|
14
|
+
mc = MeshCore(con)
|
|
15
|
+
await mc.connect()
|
|
16
|
+
|
|
17
|
+
infos = mc.self_info
|
|
18
|
+
|
|
19
|
+
lat=infos["adv_lat"]
|
|
20
|
+
lon=infos["adv_lon"]
|
|
21
|
+
|
|
22
|
+
print('[{"cmd":"prefset_n","args":{"pref":"lat","value":' + str(lat) + '}},')
|
|
23
|
+
print('{"cmd":"prefset_n","args":{"pref":"lon","value":' + str(lon) + '}}]')
|
|
24
|
+
|
|
25
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import SerialConnection
|
|
7
|
+
|
|
8
|
+
PORT = "/dev/ttyUSB0"
|
|
9
|
+
BAUDRATE = 115200
|
|
10
|
+
|
|
11
|
+
async def main () :
|
|
12
|
+
con = SerialConnection(PORT, BAUDRATE)
|
|
13
|
+
await con.connect()
|
|
14
|
+
await asyncio.sleep(0.1)
|
|
15
|
+
mc = MeshCore(con)
|
|
16
|
+
await mc.connect()
|
|
17
|
+
|
|
18
|
+
print(json.dumps(await mc.get_contacts(),indent=4))
|
|
19
|
+
|
|
20
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import SerialConnection
|
|
7
|
+
|
|
8
|
+
PORT = "/dev/ttyUSB0"
|
|
9
|
+
BAUDRATE = 115200
|
|
10
|
+
|
|
11
|
+
async def main () :
|
|
12
|
+
con = SerialConnection(PORT, BAUDRATE)
|
|
13
|
+
await con.connect()
|
|
14
|
+
await asyncio.sleep(0.1) # time for transport to establish
|
|
15
|
+
|
|
16
|
+
mc = MeshCore(con)
|
|
17
|
+
await mc.connect()
|
|
18
|
+
|
|
19
|
+
print(mc.self_info)
|
|
20
|
+
|
|
21
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import SerialConnection
|
|
7
|
+
|
|
8
|
+
PORT = "/dev/ttyUSB0"
|
|
9
|
+
BAUDRATE = 115200
|
|
10
|
+
DEST = "mchome"
|
|
11
|
+
MSG = "hello from serial"
|
|
12
|
+
|
|
13
|
+
async def main () :
|
|
14
|
+
con = SerialConnection(PORT, BAUDRATE)
|
|
15
|
+
await con.connect()
|
|
16
|
+
await asyncio.sleep(0.1) # time for transport to establish
|
|
17
|
+
|
|
18
|
+
mc = MeshCore(con)
|
|
19
|
+
await mc.connect()
|
|
20
|
+
|
|
21
|
+
await mc.ensure_contacts()
|
|
22
|
+
await mc.send_msg(bytes.fromhex(mc.contacts[DEST]["public_key"])[0:6],MSG)
|
|
23
|
+
|
|
24
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import printerr
|
|
7
|
+
from meshcore import SerialConnection
|
|
8
|
+
|
|
9
|
+
PORT = "/dev/ttyUSB0"
|
|
10
|
+
BAUDRATE = 115200
|
|
11
|
+
|
|
12
|
+
REPEATER="FdlRoom"
|
|
13
|
+
PASSWORD="password"
|
|
14
|
+
|
|
15
|
+
async def main () :
|
|
16
|
+
con = SerialConnection(PORT, BAUDRATE)
|
|
17
|
+
await con.connect()
|
|
18
|
+
await asyncio.sleep(0.1) # time for transport to establish
|
|
19
|
+
|
|
20
|
+
mc = MeshCore(con)
|
|
21
|
+
await mc.connect()
|
|
22
|
+
|
|
23
|
+
contacts = await mc.get_contacts()
|
|
24
|
+
repeater = contacts[REPEATER]
|
|
25
|
+
await mc.send_login(bytes.fromhex(repeater["public_key"]), PASSWORD)
|
|
26
|
+
|
|
27
|
+
printerr("Login sent ... awaiting")
|
|
28
|
+
|
|
29
|
+
if await mc.wait_login() :
|
|
30
|
+
await mc.send_statusreq(bytes.fromhex(repeater["public_key"]))
|
|
31
|
+
print(await mc.wait_status())
|
|
32
|
+
|
|
33
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from meshcore import TCPConnection
|
|
6
|
+
from meshcore import MeshCore
|
|
7
|
+
|
|
8
|
+
HOSTNAME = "mchome"
|
|
9
|
+
PORT = 5000
|
|
10
|
+
|
|
11
|
+
async def main () :
|
|
12
|
+
con = TCPConnection(HOSTNAME, PORT)
|
|
13
|
+
await con.connect()
|
|
14
|
+
mc = MeshCore(con)
|
|
15
|
+
await mc.connect()
|
|
16
|
+
|
|
17
|
+
print(json.dumps(await mc.get_contacts(),indent=4))
|
|
18
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import TCPConnection
|
|
7
|
+
|
|
8
|
+
HOSTNAME = "mchome"
|
|
9
|
+
PORT = 5000
|
|
10
|
+
|
|
11
|
+
async def main () :
|
|
12
|
+
con = TCPConnection(HOSTNAME, PORT)
|
|
13
|
+
await con.connect()
|
|
14
|
+
mc = MeshCore(con)
|
|
15
|
+
await mc.connect()
|
|
16
|
+
|
|
17
|
+
print(mc.self_info)
|
|
18
|
+
|
|
19
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from meshcore import MeshCore
|
|
6
|
+
from meshcore import TCPConnection
|
|
7
|
+
|
|
8
|
+
HOSTNAME = "mchome"
|
|
9
|
+
PORT = 5000
|
|
10
|
+
DEST = "t1000"
|
|
11
|
+
MSG = "Hello World"
|
|
12
|
+
|
|
13
|
+
async def main () :
|
|
14
|
+
con = TCPConnection(HOSTNAME, PORT)
|
|
15
|
+
await con.connect()
|
|
16
|
+
mc = MeshCore(con)
|
|
17
|
+
await mc.connect()
|
|
18
|
+
|
|
19
|
+
await mc.ensure_contacts()
|
|
20
|
+
await mc.send_msg(bytes.fromhex(mc.contacts[DEST]["public_key"])[0:6],MSG)
|
|
21
|
+
|
|
22
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from meshcore import TCPConnection
|
|
6
|
+
from meshcore import MeshCore
|
|
7
|
+
|
|
8
|
+
HOSTNAME = "mchome"
|
|
9
|
+
PORT = 5000
|
|
10
|
+
DEST = "t1000"
|
|
11
|
+
MSG = "Hello World"
|
|
12
|
+
|
|
13
|
+
async def main () :
|
|
14
|
+
con = TCPConnection(HOSTNAME, PORT)
|
|
15
|
+
await con.connect()
|
|
16
|
+
mc = MeshCore(con)
|
|
17
|
+
await mc.connect()
|
|
18
|
+
|
|
19
|
+
res = True
|
|
20
|
+
while res:
|
|
21
|
+
res = await mc.get_msg()
|
|
22
|
+
if res :
|
|
23
|
+
print (res)
|
|
24
|
+
|
|
25
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mccli.py : CLI interface to MeschCore BLE companion app
|
|
3
|
+
"""
|
|
4
|
+
import asyncio
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from meshcore import printerr
|
|
8
|
+
|
|
9
|
+
from bleak import BleakClient, BleakScanner
|
|
10
|
+
from bleak.backends.characteristic import BleakGATTCharacteristic
|
|
11
|
+
from bleak.backends.device import BLEDevice
|
|
12
|
+
from bleak.backends.scanner import AdvertisementData
|
|
13
|
+
from bleak.exc import BleakDeviceNotFoundError
|
|
14
|
+
|
|
15
|
+
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
16
|
+
UART_RX_CHAR_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
17
|
+
UART_TX_CHAR_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
18
|
+
|
|
19
|
+
class BLEConnection:
|
|
20
|
+
def __init__(self, address):
|
|
21
|
+
""" Constructor : specify address """
|
|
22
|
+
self.address = address
|
|
23
|
+
self.client = None
|
|
24
|
+
self.rx_char = None
|
|
25
|
+
self.mc = None
|
|
26
|
+
|
|
27
|
+
async def connect(self):
|
|
28
|
+
"""
|
|
29
|
+
Connects to the device
|
|
30
|
+
|
|
31
|
+
Returns : the address used for connection
|
|
32
|
+
"""
|
|
33
|
+
def match_meshcore_device(_: BLEDevice, adv: AdvertisementData):
|
|
34
|
+
""" Filter to mach MeshCore devices """
|
|
35
|
+
if not adv.local_name is None\
|
|
36
|
+
and adv.local_name.startswith("MeshCore")\
|
|
37
|
+
and (self.address is None or self.address in adv.local_name) :
|
|
38
|
+
return True
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
if self.address is None or self.address == "" or len(self.address.split(":")) != 6 :
|
|
42
|
+
scanner = BleakScanner()
|
|
43
|
+
printerr("Scanning for devices")
|
|
44
|
+
device = await scanner.find_device_by_filter(match_meshcore_device)
|
|
45
|
+
if device is None :
|
|
46
|
+
return None
|
|
47
|
+
printerr(f"Found device : {device}")
|
|
48
|
+
self.client = BleakClient(device)
|
|
49
|
+
self.address = self.client.address
|
|
50
|
+
else:
|
|
51
|
+
self.client = BleakClient(self.address)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
await self.client.connect(disconnected_callback=self.handle_disconnect)
|
|
55
|
+
except BleakDeviceNotFoundError:
|
|
56
|
+
return None
|
|
57
|
+
except TimeoutError:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
await self.client.start_notify(UART_TX_CHAR_UUID, self.handle_rx)
|
|
61
|
+
|
|
62
|
+
nus = self.client.services.get_service(UART_SERVICE_UUID)
|
|
63
|
+
self.rx_char = nus.get_characteristic(UART_RX_CHAR_UUID)
|
|
64
|
+
|
|
65
|
+
printerr("BLE Connexion started")
|
|
66
|
+
return self.address
|
|
67
|
+
|
|
68
|
+
def handle_disconnect(self, _: BleakClient):
|
|
69
|
+
""" Callback to handle disconnection """
|
|
70
|
+
printerr ("Device was disconnected, goodbye.")
|
|
71
|
+
# cancelling all tasks effectively ends the program
|
|
72
|
+
for task in asyncio.all_tasks():
|
|
73
|
+
task.cancel()
|
|
74
|
+
|
|
75
|
+
def set_mc(self, mc) :
|
|
76
|
+
self.mc = mc
|
|
77
|
+
|
|
78
|
+
def handle_rx(self, _: BleakGATTCharacteristic, data: bytearray):
|
|
79
|
+
if not self.mc is None:
|
|
80
|
+
self.mc.handle_rx(data)
|
|
81
|
+
|
|
82
|
+
async def send(self, data):
|
|
83
|
+
await self.client.write_gatt_char(self.rx_char, bytes(data), response=False)
|
|
@@ -3,235 +3,12 @@
|
|
|
3
3
|
"""
|
|
4
4
|
import asyncio
|
|
5
5
|
import sys
|
|
6
|
-
import serial_asyncio
|
|
7
|
-
|
|
8
|
-
from bleak import BleakClient, BleakScanner
|
|
9
|
-
from bleak.backends.characteristic import BleakGATTCharacteristic
|
|
10
|
-
from bleak.backends.device import BLEDevice
|
|
11
|
-
from bleak.backends.scanner import AdvertisementData
|
|
12
|
-
from bleak.exc import BleakDeviceNotFoundError
|
|
13
|
-
|
|
14
|
-
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
15
|
-
UART_RX_CHAR_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
16
|
-
UART_TX_CHAR_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
|
17
6
|
|
|
18
7
|
def printerr (s) :
|
|
19
8
|
sys.stderr.write(str(s))
|
|
20
9
|
sys.stderr.write("\n")
|
|
21
10
|
sys.stderr.flush()
|
|
22
11
|
|
|
23
|
-
class SerialConnection:
|
|
24
|
-
def __init__(self, port, baudrate):
|
|
25
|
-
self.port = port
|
|
26
|
-
self.baudrate = baudrate
|
|
27
|
-
self.frame_started = False
|
|
28
|
-
self.frame_size = 0
|
|
29
|
-
self.header = b""
|
|
30
|
-
self.inframe = b""
|
|
31
|
-
|
|
32
|
-
class MCSerialClientProtocol(asyncio.Protocol):
|
|
33
|
-
def __init__(self, cx):
|
|
34
|
-
self.cx = cx
|
|
35
|
-
|
|
36
|
-
def connection_made(self, transport):
|
|
37
|
-
self.cx.transport = transport
|
|
38
|
-
# printerr('port opened')
|
|
39
|
-
transport.serial.rts = False # You can manipulate Serial object via transport
|
|
40
|
-
|
|
41
|
-
def data_received(self, data):
|
|
42
|
-
# printerr('data received')
|
|
43
|
-
self.cx.handle_rx(data)
|
|
44
|
-
|
|
45
|
-
def connection_lost(self, exc):
|
|
46
|
-
printerr('port closed')
|
|
47
|
-
|
|
48
|
-
def pause_writing(self):
|
|
49
|
-
printerr('pause writing')
|
|
50
|
-
|
|
51
|
-
def resume_writing(self):
|
|
52
|
-
printerr('resume writing')
|
|
53
|
-
|
|
54
|
-
async def connect(self):
|
|
55
|
-
"""
|
|
56
|
-
Connects to the device
|
|
57
|
-
"""
|
|
58
|
-
loop = asyncio.get_running_loop()
|
|
59
|
-
await serial_asyncio.create_serial_connection(
|
|
60
|
-
loop, lambda: self.MCSerialClientProtocol(self),
|
|
61
|
-
self.port, baudrate=self.baudrate)
|
|
62
|
-
|
|
63
|
-
printerr("Serial Connexion started")
|
|
64
|
-
return self.port
|
|
65
|
-
|
|
66
|
-
def set_mc(self, mc) :
|
|
67
|
-
self.mc = mc
|
|
68
|
-
|
|
69
|
-
def handle_rx(self, data: bytearray):
|
|
70
|
-
headerlen = len(self.header)
|
|
71
|
-
framelen = len(self.inframe)
|
|
72
|
-
if not self.frame_started : # wait start of frame
|
|
73
|
-
if len(data) >= 3 - headerlen:
|
|
74
|
-
self.header = self.header + data[:3-headerlen]
|
|
75
|
-
self.frame_started = True
|
|
76
|
-
self.frame_size = int.from_bytes(self.header[1:], byteorder='little')
|
|
77
|
-
self.handle_rx(data[3-headerlen:])
|
|
78
|
-
else:
|
|
79
|
-
self.header = self.header + data
|
|
80
|
-
else:
|
|
81
|
-
if framelen + len(data) < self.frame_size:
|
|
82
|
-
self.inframe = self.inframe + data
|
|
83
|
-
else:
|
|
84
|
-
self.inframe = self.inframe + data[:self.frame_size-framelen]
|
|
85
|
-
if not self.mc is None:
|
|
86
|
-
self.mc.handle_rx(self.inframe)
|
|
87
|
-
self.frame_started = False
|
|
88
|
-
self.header = b""
|
|
89
|
-
self.inframe = b""
|
|
90
|
-
if framelen + len(data) > self.frame_size:
|
|
91
|
-
self.handle_rx(data[self.frame_size-framelen:])
|
|
92
|
-
|
|
93
|
-
async def send(self, data):
|
|
94
|
-
size = len(data)
|
|
95
|
-
pkt = b"\x3c" + size.to_bytes(2, byteorder="little") + data
|
|
96
|
-
# printerr(f"sending pkt : {pkt}")
|
|
97
|
-
self.transport.write(pkt)
|
|
98
|
-
|
|
99
|
-
class TCPConnection:
|
|
100
|
-
def __init__(self, host, port):
|
|
101
|
-
self.host = host
|
|
102
|
-
self.port = port
|
|
103
|
-
self.transport = None
|
|
104
|
-
self.frame_started = False
|
|
105
|
-
self.frame_size = 0
|
|
106
|
-
self.header = b""
|
|
107
|
-
self.inframe = b""
|
|
108
|
-
|
|
109
|
-
class MCClientProtocol:
|
|
110
|
-
def __init__(self, cx):
|
|
111
|
-
self.cx = cx
|
|
112
|
-
|
|
113
|
-
def connection_made(self, transport):
|
|
114
|
-
self.cx.transport = transport
|
|
115
|
-
|
|
116
|
-
def data_received(self, data):
|
|
117
|
-
self.cx.handle_rx(data)
|
|
118
|
-
|
|
119
|
-
def error_received(self, exc):
|
|
120
|
-
printerr(f'Error received: {exc}')
|
|
121
|
-
|
|
122
|
-
def connection_lost(self, exc):
|
|
123
|
-
printerr('The server closed the connection')
|
|
124
|
-
|
|
125
|
-
async def connect(self):
|
|
126
|
-
"""
|
|
127
|
-
Connects to the device
|
|
128
|
-
"""
|
|
129
|
-
loop = asyncio.get_running_loop()
|
|
130
|
-
await loop.create_connection(
|
|
131
|
-
lambda: self.MCClientProtocol(self),
|
|
132
|
-
self.host, self.port)
|
|
133
|
-
|
|
134
|
-
printerr("TCP Connexion started")
|
|
135
|
-
return self.host
|
|
136
|
-
|
|
137
|
-
def set_mc(self, mc) :
|
|
138
|
-
self.mc = mc
|
|
139
|
-
|
|
140
|
-
def handle_rx(self, data: bytearray):
|
|
141
|
-
headerlen = len(self.header)
|
|
142
|
-
framelen = len(self.inframe)
|
|
143
|
-
if not self.frame_started : # wait start of frame
|
|
144
|
-
if len(data) >= 3 - headerlen:
|
|
145
|
-
self.header = self.header + data[:3-headerlen]
|
|
146
|
-
self.frame_started = True
|
|
147
|
-
self.frame_size = int.from_bytes(self.header[1:], byteorder='little')
|
|
148
|
-
self.handle_rx(data[3-headerlen:])
|
|
149
|
-
else:
|
|
150
|
-
self.header = self.header + data
|
|
151
|
-
else:
|
|
152
|
-
if framelen + len(data) < self.frame_size:
|
|
153
|
-
self.inframe = self.inframe + data
|
|
154
|
-
else:
|
|
155
|
-
self.inframe = self.inframe + data[:self.frame_size-framelen]
|
|
156
|
-
if not self.mc is None:
|
|
157
|
-
self.mc.handle_rx(self.inframe)
|
|
158
|
-
self.frame_started = False
|
|
159
|
-
self.header = b""
|
|
160
|
-
self.inframe = b""
|
|
161
|
-
if framelen + len(data) > self.frame_size:
|
|
162
|
-
self.handle_rx(data[self.frame_size-framelen:])
|
|
163
|
-
|
|
164
|
-
async def send(self, data):
|
|
165
|
-
size = len(data)
|
|
166
|
-
pkt = b"\x3c" + size.to_bytes(2, byteorder="little") + data
|
|
167
|
-
self.transport.write(pkt)
|
|
168
|
-
|
|
169
|
-
class BLEConnection:
|
|
170
|
-
def __init__(self, address):
|
|
171
|
-
""" Constructor : specify address """
|
|
172
|
-
self.address = address
|
|
173
|
-
self.client = None
|
|
174
|
-
self.rx_char = None
|
|
175
|
-
self.mc = None
|
|
176
|
-
|
|
177
|
-
async def connect(self):
|
|
178
|
-
"""
|
|
179
|
-
Connects to the device
|
|
180
|
-
|
|
181
|
-
Returns : the address used for connection
|
|
182
|
-
"""
|
|
183
|
-
def match_meshcore_device(_: BLEDevice, adv: AdvertisementData):
|
|
184
|
-
""" Filter to mach MeshCore devices """
|
|
185
|
-
if not adv.local_name is None\
|
|
186
|
-
and adv.local_name.startswith("MeshCore")\
|
|
187
|
-
and (self.address is None or self.address in adv.local_name) :
|
|
188
|
-
return True
|
|
189
|
-
return False
|
|
190
|
-
|
|
191
|
-
if self.address is None or self.address == "" or len(self.address.split(":")) != 6 :
|
|
192
|
-
scanner = BleakScanner()
|
|
193
|
-
printerr("Scanning for devices")
|
|
194
|
-
device = await scanner.find_device_by_filter(match_meshcore_device)
|
|
195
|
-
if device is None :
|
|
196
|
-
return None
|
|
197
|
-
printerr(f"Found device : {device}")
|
|
198
|
-
self.client = BleakClient(device)
|
|
199
|
-
self.address = self.client.address
|
|
200
|
-
else:
|
|
201
|
-
self.client = BleakClient(self.address)
|
|
202
|
-
|
|
203
|
-
try:
|
|
204
|
-
await self.client.connect(disconnected_callback=self.handle_disconnect)
|
|
205
|
-
except BleakDeviceNotFoundError:
|
|
206
|
-
return None
|
|
207
|
-
except TimeoutError:
|
|
208
|
-
return None
|
|
209
|
-
|
|
210
|
-
await self.client.start_notify(UART_TX_CHAR_UUID, self.handle_rx)
|
|
211
|
-
|
|
212
|
-
nus = self.client.services.get_service(UART_SERVICE_UUID)
|
|
213
|
-
self.rx_char = nus.get_characteristic(UART_RX_CHAR_UUID)
|
|
214
|
-
|
|
215
|
-
printerr("BLE Connexion started")
|
|
216
|
-
return self.address
|
|
217
|
-
|
|
218
|
-
def handle_disconnect(self, _: BleakClient):
|
|
219
|
-
""" Callback to handle disconnection """
|
|
220
|
-
printerr ("Device was disconnected, goodbye.")
|
|
221
|
-
# cancelling all tasks effectively ends the program
|
|
222
|
-
for task in asyncio.all_tasks():
|
|
223
|
-
task.cancel()
|
|
224
|
-
|
|
225
|
-
def set_mc(self, mc) :
|
|
226
|
-
self.mc = mc
|
|
227
|
-
|
|
228
|
-
def handle_rx(self, _: BleakGATTCharacteristic, data: bytearray):
|
|
229
|
-
if not self.mc is None:
|
|
230
|
-
self.mc.handle_rx(data)
|
|
231
|
-
|
|
232
|
-
async def send(self, data):
|
|
233
|
-
await self.client.write_gatt_char(self.rx_char, bytes(data), response=False)
|
|
234
|
-
|
|
235
12
|
class MeshCore:
|
|
236
13
|
"""
|
|
237
14
|
Interface to a BLE MeshCore device
|
|
@@ -364,7 +141,7 @@ class MeshCore:
|
|
|
364
141
|
case 11: # contact
|
|
365
142
|
self.result.set_result("meshcore://" + data[1:].hex())
|
|
366
143
|
case 12: # battery voltage
|
|
367
|
-
self.result.set_result(int.from_bytes(data[1:
|
|
144
|
+
self.result.set_result(int.from_bytes(data[1:3], byteorder='little'))
|
|
368
145
|
case 13: # device info
|
|
369
146
|
res = {}
|
|
370
147
|
res["fw ver"] = data[1]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mccli.py : CLI interface to MeschCore BLE companion app
|
|
3
|
+
"""
|
|
4
|
+
import asyncio
|
|
5
|
+
import sys
|
|
6
|
+
import serial_asyncio
|
|
7
|
+
|
|
8
|
+
from meshcore import printerr
|
|
9
|
+
|
|
10
|
+
class SerialConnection:
|
|
11
|
+
def __init__(self, port, baudrate):
|
|
12
|
+
self.port = port
|
|
13
|
+
self.baudrate = baudrate
|
|
14
|
+
self.frame_started = False
|
|
15
|
+
self.frame_size = 0
|
|
16
|
+
self.header = b""
|
|
17
|
+
self.inframe = b""
|
|
18
|
+
|
|
19
|
+
class MCSerialClientProtocol(asyncio.Protocol):
|
|
20
|
+
def __init__(self, cx):
|
|
21
|
+
self.cx = cx
|
|
22
|
+
|
|
23
|
+
def connection_made(self, transport):
|
|
24
|
+
self.cx.transport = transport
|
|
25
|
+
# printerr('port opened')
|
|
26
|
+
transport.serial.rts = False # You can manipulate Serial object via transport
|
|
27
|
+
|
|
28
|
+
def data_received(self, data):
|
|
29
|
+
# printerr('data received')
|
|
30
|
+
self.cx.handle_rx(data)
|
|
31
|
+
|
|
32
|
+
def connection_lost(self, exc):
|
|
33
|
+
printerr('port closed')
|
|
34
|
+
|
|
35
|
+
def pause_writing(self):
|
|
36
|
+
printerr('pause writing')
|
|
37
|
+
|
|
38
|
+
def resume_writing(self):
|
|
39
|
+
printerr('resume writing')
|
|
40
|
+
|
|
41
|
+
async def connect(self):
|
|
42
|
+
"""
|
|
43
|
+
Connects to the device
|
|
44
|
+
"""
|
|
45
|
+
loop = asyncio.get_running_loop()
|
|
46
|
+
await serial_asyncio.create_serial_connection(
|
|
47
|
+
loop, lambda: self.MCSerialClientProtocol(self),
|
|
48
|
+
self.port, baudrate=self.baudrate)
|
|
49
|
+
|
|
50
|
+
printerr("Serial Connexion started")
|
|
51
|
+
return self.port
|
|
52
|
+
|
|
53
|
+
def set_mc(self, mc) :
|
|
54
|
+
self.mc = mc
|
|
55
|
+
|
|
56
|
+
def handle_rx(self, data: bytearray):
|
|
57
|
+
headerlen = len(self.header)
|
|
58
|
+
framelen = len(self.inframe)
|
|
59
|
+
if not self.frame_started : # wait start of frame
|
|
60
|
+
if len(data) >= 3 - headerlen:
|
|
61
|
+
self.header = self.header + data[:3-headerlen]
|
|
62
|
+
self.frame_started = True
|
|
63
|
+
self.frame_size = int.from_bytes(self.header[1:], byteorder='little')
|
|
64
|
+
self.handle_rx(data[3-headerlen:])
|
|
65
|
+
else:
|
|
66
|
+
self.header = self.header + data
|
|
67
|
+
else:
|
|
68
|
+
if framelen + len(data) < self.frame_size:
|
|
69
|
+
self.inframe = self.inframe + data
|
|
70
|
+
else:
|
|
71
|
+
self.inframe = self.inframe + data[:self.frame_size-framelen]
|
|
72
|
+
if not self.mc is None:
|
|
73
|
+
self.mc.handle_rx(self.inframe)
|
|
74
|
+
self.frame_started = False
|
|
75
|
+
self.header = b""
|
|
76
|
+
self.inframe = b""
|
|
77
|
+
if framelen + len(data) > self.frame_size:
|
|
78
|
+
self.handle_rx(data[self.frame_size-framelen:])
|
|
79
|
+
|
|
80
|
+
async def send(self, data):
|
|
81
|
+
size = len(data)
|
|
82
|
+
pkt = b"\x3c" + size.to_bytes(2, byteorder="little") + data
|
|
83
|
+
# printerr(f"sending pkt : {pkt}")
|
|
84
|
+
self.transport.write(pkt)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mccli.py : CLI interface to MeschCore BLE companion app
|
|
3
|
+
"""
|
|
4
|
+
import asyncio
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from meshcore import printerr
|
|
8
|
+
|
|
9
|
+
class TCPConnection:
|
|
10
|
+
def __init__(self, host, port):
|
|
11
|
+
self.host = host
|
|
12
|
+
self.port = port
|
|
13
|
+
self.transport = None
|
|
14
|
+
self.frame_started = False
|
|
15
|
+
self.frame_size = 0
|
|
16
|
+
self.header = b""
|
|
17
|
+
self.inframe = b""
|
|
18
|
+
|
|
19
|
+
class MCClientProtocol:
|
|
20
|
+
def __init__(self, cx):
|
|
21
|
+
self.cx = cx
|
|
22
|
+
|
|
23
|
+
def connection_made(self, transport):
|
|
24
|
+
self.cx.transport = transport
|
|
25
|
+
|
|
26
|
+
def data_received(self, data):
|
|
27
|
+
self.cx.handle_rx(data)
|
|
28
|
+
|
|
29
|
+
def error_received(self, exc):
|
|
30
|
+
printerr(f'Error received: {exc}')
|
|
31
|
+
|
|
32
|
+
def connection_lost(self, exc):
|
|
33
|
+
printerr('The server closed the connection')
|
|
34
|
+
|
|
35
|
+
async def connect(self):
|
|
36
|
+
"""
|
|
37
|
+
Connects to the device
|
|
38
|
+
"""
|
|
39
|
+
loop = asyncio.get_running_loop()
|
|
40
|
+
await loop.create_connection(
|
|
41
|
+
lambda: self.MCClientProtocol(self),
|
|
42
|
+
self.host, self.port)
|
|
43
|
+
|
|
44
|
+
printerr("TCP Connexion started")
|
|
45
|
+
return self.host
|
|
46
|
+
|
|
47
|
+
def set_mc(self, mc) :
|
|
48
|
+
self.mc = mc
|
|
49
|
+
|
|
50
|
+
def handle_rx(self, data: bytearray):
|
|
51
|
+
headerlen = len(self.header)
|
|
52
|
+
framelen = len(self.inframe)
|
|
53
|
+
if not self.frame_started : # wait start of frame
|
|
54
|
+
if len(data) >= 3 - headerlen:
|
|
55
|
+
self.header = self.header + data[:3-headerlen]
|
|
56
|
+
self.frame_started = True
|
|
57
|
+
self.frame_size = int.from_bytes(self.header[1:], byteorder='little')
|
|
58
|
+
self.handle_rx(data[3-headerlen:])
|
|
59
|
+
else:
|
|
60
|
+
self.header = self.header + data
|
|
61
|
+
else:
|
|
62
|
+
if framelen + len(data) < self.frame_size:
|
|
63
|
+
self.inframe = self.inframe + data
|
|
64
|
+
else:
|
|
65
|
+
self.inframe = self.inframe + data[:self.frame_size-framelen]
|
|
66
|
+
if not self.mc is None:
|
|
67
|
+
self.mc.handle_rx(self.inframe)
|
|
68
|
+
self.frame_started = False
|
|
69
|
+
self.header = b""
|
|
70
|
+
self.inframe = b""
|
|
71
|
+
if framelen + len(data) > self.frame_size:
|
|
72
|
+
self.handle_rx(data[self.frame_size-framelen:])
|
|
73
|
+
|
|
74
|
+
async def send(self, data):
|
|
75
|
+
size = len(data)
|
|
76
|
+
pkt = b"\x3c" + size.to_bytes(2, byteorder="little") + data
|
|
77
|
+
self.transport.write(pkt)
|
|
File without changes
|