Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.

Commit 6e2ff29

Browse files
committed
Update Pybytes to version 1.6.1
1 parent 9a9a5b9 commit 6e2ff29

7 files changed

+282
-43
lines changed

esp32/frozen/Pybytes/_OTA.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ def update(self, customManifest=None, fwtype=None, token=None):
137137

138138
def get_file(self, f):
139139
new_path = "{}.new".format(f['dst_path'])
140-
141140
# If a .new file exists from a previously failed update delete it
142141
try:
143142
os.remove(new_path)
@@ -184,6 +183,15 @@ def delete_file(self, f):
184183

185184
def write_firmware(self, f):
186185
# hash =
186+
url = f['URL'].split("//")[1].split("/")[0]
187+
188+
if url.find(":") > -1:
189+
self.ip = url.split(":")[0]
190+
self.port = int(url.split(":")[1])
191+
else:
192+
self.ip = url
193+
self.port = 443
194+
187195
self.get_data(
188196
f['URL'].split("/", 3)[-1],
189197
hash=True,
@@ -222,7 +230,6 @@ def _http_get(self, path, host):
222230

223231
def get_data(self, req, dest_path=None, hash=False, firmware=False):
224232
h = None
225-
226233
useSSL = int(self.port) == 443
227234

228235
# Connect to server
@@ -232,11 +239,9 @@ def get_data(self, req, dest_path=None, hash=False, firmware=False):
232239
if (int(self.port) == 443):
233240
print("Wrapping socket")
234241
s = ssl.wrap_socket(s)
235-
236242
print("Sending request")
237243
# Request File
238244
s.sendall(self._http_get(req, "{}:{}".format(self.ip, self.port)))
239-
240245
try:
241246
content = bytearray()
242247
fp = None
@@ -247,6 +252,7 @@ def get_data(self, req, dest_path=None, hash=False, firmware=False):
247252
fp = open(dest_path, 'wb')
248253

249254
if firmware:
255+
print_debug(4, "Starting OTA...")
250256
pycom.ota_start()
251257

252258
h = uhashlib.sha1()

esp32/frozen/Pybytes/_pybytes_constants.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,12 @@ class constants:
6969
__TYPE_OTA = 0x05
7070
__TYPE_FCOTA = 0x06
7171
__TYPE_PONG = 0x07
72-
__TYPE_PYMESH = 0x0D
73-
__TYPE_PYBYTES = 0x0E
74-
__TYPE_RELEASE_INFO = 0x0B
7572
__TYPE_RELEASE_DEPLOY = 0x0A
73+
__TYPE_RELEASE_INFO = 0x0B
7674
__TYPE_DEVICE_NETWORK_DEPLOY = 0x0C
75+
__TYPE_PYMESH = 0x0D
76+
__TYPE_PYBYTES = 0x0E
77+
__TYPE_ML = 0x0F
7778
__PYBYTES_PROTOCOL = ">B%ds"
7879
__PYBYTES_PROTOCOL_PING = ">B"
7980
__PYBYTES_INTERNAL_PROTOCOL = ">BBH"
@@ -90,6 +91,8 @@ class constants:
9091
__COMMAND_ANALOG_WRITE = 4
9192
__COMMAND_CUSTOM_METHOD = 5
9293
__COMMAND_CUSTOM_LOCATION = 6
94+
__COMMAND_START_SAMPLE = 7
95+
__COMMAND_DEPLOY_MODEL = 8
9396

9497
__FCOTA_COMMAND_HIERARCHY_ACQUISITION = 0x00
9598
__FCOTA_COMMAND_FILE_ACQUISITION = 0x01
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
'''
2+
Copyright (c) 2020, Pycom Limited.
3+
This software is licensed under the GNU GPL version 3 or any
4+
later version, with permitted additional terms. For more information
5+
see the Pycom Licence v1.0 document supplied with this file, or
6+
available at https://www.pycom.io/opensource/licensing
7+
'''
8+
9+
import math
10+
import json
11+
12+
try:
13+
from pybytes_debug import print_debug
14+
except:
15+
from _pybytes_debug import print_debug
16+
17+
try:
18+
import urequest
19+
except:
20+
import _urequest as urequest
21+
22+
try:
23+
from pybytes_constants import constants
24+
except:
25+
from _pybytes_constants import constants
26+
27+
import pycom
28+
29+
try:
30+
from LIS2HH12 import *
31+
except:
32+
print_debug(5, "LIS2HH12 not imported")
33+
34+
# 20 seconds, max window in time for recording
35+
MAX_LEN_MSEC = const(20000)
36+
37+
# 350Hz, max frequency
38+
MAX_FREQ_HZ = const(350)
39+
40+
41+
class MlFeatures():
42+
def __init__(self, pybytes_protocol=None, parameters=None):
43+
if parameters is not None:
44+
self.__length = parameters["length"]
45+
self.__label = parameters["label"]
46+
self.__sampleName = parameters["sampleName"]
47+
self.__type = parameters["type"]
48+
self.__device = parameters["device"]
49+
self.__model = parameters["model"]
50+
self.__mlSample = parameters["mlSample"]
51+
self.__frequency = parameters["frequency"]
52+
self.__pybytes_protocol = pybytes_protocol
53+
self.__data = []
54+
55+
def _debug_hack(self, pybytes):
56+
self.__pybytes = pybytes
57+
58+
def start_sampling(self, pin):
59+
# here define the required libraries
60+
try:
61+
from pysense import Pysense
62+
except:
63+
print_debug(5, "pysense not imported")
64+
65+
try:
66+
from pytrack import Pytrack
67+
except:
68+
print_debug(5, "pytrack not imported")
69+
70+
lib = False
71+
try:
72+
py = Pysense()
73+
lib = True
74+
except NameError:
75+
print_debug(5, "Pysense not defined")
76+
77+
if not lib:
78+
try:
79+
py = Pytrack()
80+
except NameError:
81+
print_debug(5, "Check if Pysense/Pytrack libraries are loaded")
82+
return
83+
84+
try:
85+
li = LIS2HH12(py)
86+
except NameError:
87+
print_debug(5, "LIS2HH12 library are not loaded")
88+
return
89+
li.set_odr(ODR_400_HZ)
90+
91+
# make the max record length to 20 seconds
92+
self.__length = min(MAX_LEN_MSEC, self.__length)
93+
94+
# make the max frequency to 350Hz
95+
self.__frequency = min(MAX_FREQ_HZ, self.__frequency)
96+
97+
# compute time interval between 2 consecutive samples
98+
delta_t_us = int(1000000.0 / self.__frequency)
99+
# compute the number of samples to be acquisition
100+
samples_num = math.ceil(self.__length * self.__frequency / 1000) + 1
101+
102+
try:
103+
pycom.heartbeat(False)
104+
pycom.rgbled(0x7f7f00)
105+
except:
106+
pass
107+
time.sleep(0.5)
108+
109+
self.__data = []
110+
index = 0
111+
print("Start acquisition data for %d msec, freq %d Hz" % (self.__length, self.__frequency))
112+
113+
next_ts = time.ticks_us()
114+
ts_orig = next_ts
115+
while True:
116+
while time.ticks_diff(next_ts, time.ticks_us()) > 0:
117+
pass
118+
acc = li.acceleration()
119+
ts = next_ts
120+
self.__data.append((ts - ts_orig, acc))
121+
next_ts = ts + delta_t_us
122+
index += 1
123+
if index >= samples_num:
124+
break # done
125+
126+
print("Done acquisition %d samples, real freq %.1f Hz" % (index, index / (self.__length / 1000)))
127+
self._parse_data(pin)
128+
129+
def _send_data(self, data, pin, acc, ts):
130+
if self.__pybytes_protocol is not None:
131+
if self.__type == 2:
132+
self.__label = self.__sampleName
133+
self.__pybytes_protocol.send_pybytes_custom_method_values(pin, [
134+
data],
135+
'sample/{}/{}/{}/{}/{}'.format(self.__label, self.__type, self.__model, self.__device, self.__mlSample))
136+
else:
137+
self.__pybytes.send_signal(pin & 0xFF, str((int(ts / 1000), acc)))
138+
139+
def _parse_data(self, pin):
140+
print("_parse_data, %d samples" % len(self.__data))
141+
try:
142+
pycom.rgbled(0x8d05f5)
143+
except:
144+
pass
145+
data = ['{"data": "ml"}']
146+
for (ts, acc) in self.__data:
147+
data.append('{' + '"data": [{},{},{}], "ms": {}'.format(acc[0], acc[1], acc[2], int(ts / 1000)) + '}')
148+
if len(data) > 25:
149+
self._send_data(data, pin, acc, ts)
150+
data = ['{"data": "ml"}']
151+
self._send_data(data, pin, acc, ts)
152+
try:
153+
pycom.heartbeat(True)
154+
except:
155+
pass
156+
157+
def deploy_model(self, modelId, silent=False):
158+
try:
159+
file = '/flash/model_definition.json'
160+
modelDefinition = {}
161+
url = '{}://{}/ml/{}'.format(
162+
constants.__DEFAULT_PYCONFIG_PROTOCOL,
163+
constants.__DEFAULT_PYCONFIG_DOMAIN,
164+
modelId
165+
)
166+
print_debug(2, '{}'.format(url))
167+
result = urequest.get(url, headers={'content-type': 'application/json'})
168+
modelDefinition = json.loads(result.content.decode())
169+
print_debug(2, 'modelDefinition: {}'.format(modelDefinition))
170+
f = open(file, 'w')
171+
f.write(json.dumps(modelDefinition).encode('utf-8'))
172+
f.close()
173+
print_debug(2, "Model definition written to {}".format(file))
174+
except Exception as e:
175+
if not silent:
176+
print_debug(2, "Exception: {}".format(e))

esp32/frozen/Pybytes/_pybytes_protocol.py

+61-21
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
except:
3232
from _pybytes_pymesh_config import PybytesPymeshConfig
3333

34+
try:
35+
from pybytes_machine_learning import MlFeatures
36+
except:
37+
from _pybytes_machine_learning import MlFeatures
38+
3439
try:
3540
from pybytes_config_reader import PybytesConfigReader
3641
except:
@@ -281,10 +286,10 @@ def __process_recv_message(self, message):
281286
splittedBody = bodyString.split(',')
282287
if (len(splittedBody) >= 2):
283288
path = splittedBody[0]
284-
print_debug(2, path[len(path)-7:len(path)])
285-
if (path[len(path)-7:len(path)] != '.pymakr'):
289+
print_debug(2, path[len(path) - 7:len(path)])
290+
if (path[len(path) - 7:len(path)] != '.pymakr'):
286291
self.send_fcota_ping('updating file...')
287-
newContent = bodyString[len(path)+1:len(body)]
292+
newContent = bodyString[len(path) + 1:len(body)]
288293
if (self.__FCOTA.update_file_content(path, newContent) is True): # noqa
289294
size = self.__FCOTA.get_file_size(path)
290295
self.send_fcota_file(newContent, path, size)
@@ -319,7 +324,18 @@ def __process_recv_message(self, message):
319324
if (len(body) > 3):
320325
value = body[2] << 8 | body[3]
321326

322-
if (command == constants.__COMMAND_PIN_MODE):
327+
if (command == constants.__COMMAND_START_SAMPLE):
328+
parameters = ujson.loads(body[2: len(body)].decode("utf-8"))
329+
sampling = MlFeatures(self, parameters=parameters)
330+
sampling.start_sampling(pin=parameters["pin"])
331+
self.send_ota_response(result=2, topic='sample')
332+
elif (command == constants.__COMMAND_DEPLOY_MODEL):
333+
parameters = ujson.loads(body[2: len(body)].decode("utf-8"))
334+
sampling = MlFeatures()
335+
sampling.deploy_model(modelId=parameters["modelId"])
336+
self.send_ota_response(result=2, topic='deploymlmodel')
337+
338+
elif (command == constants.__COMMAND_PIN_MODE):
323339
pass
324340

325341
elif (command == constants.__COMMAND_DIGITAL_READ):
@@ -633,16 +649,11 @@ def write_firmware(self, customManifest=None):
633649
def get_application_details(self, body):
634650
application = self.__conf.get('application')
635651
if application is not None:
636-
if 'id' in application and application['id']:
637-
applicationID = application['id']
638-
else:
639-
applicationID = body['applicationId']
640652
if 'release' in application and 'codeFilename' in application['release']:
641653
currentReleaseID = application['release']['codeFilename']
642654
else:
643655
currentReleaseID = None
644656
else:
645-
applicationID = body['applicationId']
646657
currentReleaseID = None
647658
self.__conf['application'] = {
648659
"id": "",
@@ -652,6 +663,7 @@ def get_application_details(self, body):
652663
"version": 0
653664
}
654665
}
666+
applicationID = body['applicationId']
655667
return (applicationID, currentReleaseID)
656668

657669
def get_update_manifest(self, applicationID, newReleaseID, currentReleaseID):
@@ -755,21 +767,46 @@ def update_network_config(self, letResp):
755767
except Exception as e:
756768
print_debug(1, "error while updating network config pybytes_config.json! {}".format(e))
757769

758-
def update_firmware(self, body):
770+
def update_firmware(self, body, applicationID, fw_type='pybytes'):
759771
if "firmware" not in body:
760772
print_debug(0, "no firmware to update")
761773
return
762-
version = body['firmware']["version"]
763-
print_debug(0, "updating firmware to {}".format(version))
764-
customManifest = {
765-
"firmware": {
766-
"URL": "https://{}/downloads/appimg/firmware_{}_{}.bin".format(
767-
constants.__DEFAULT_SW_HOST,
768-
os.uname().sysname,
769-
version),
774+
775+
if "version" in body['firmware']:
776+
version = body['firmware']["version"]
777+
print_debug(0, "updating firmware to {}".format(version))
778+
779+
customManifest = {
780+
"firmware": {
781+
"URL": "https://{}/findupgrade?redirect=true&strict=true&type={}&model={}&version={}&download=true".format(
782+
constants.__DEFAULT_SW_HOST,
783+
fw_type,
784+
os.uname().sysname,
785+
version),
786+
}
770787
}
771-
}
772-
self.write_firmware(customManifest)
788+
self.write_firmware(customManifest)
789+
else:
790+
fileUrl = '{}://{}/firmware?'.format(constants.__DEFAULT_PYCONFIG_PROTOCOL, constants.__DEFAULT_PYCONFIG_DOMAIN)
791+
customFirmwares = body['firmware']["customFirmwares"]
792+
firmwareFilename = ''
793+
for firmware in customFirmwares:
794+
print_debug(1, "firmware['firmwareType']={} and os.uname().sysname.lower()={}".format(firmware['firmwareType'], os.uname().sysname.lower()))
795+
print_debug(1, "firmware={}".format(firmware))
796+
if (firmware['firmwareType'] == os.uname().sysname.lower()):
797+
firmwareFilename = firmware['firmwareFilename']
798+
targetFileLocation = '{}application_id={}&target_ver={}&target_path={}'.format(
799+
fileUrl,
800+
applicationID,
801+
firmwareFilename,
802+
'/{}.bin'.format(firmwareFilename)
803+
)
804+
customManifest = {
805+
"firmware": {
806+
"URL": targetFileLocation,
807+
}
808+
}
809+
self.write_firmware(customManifest)
773810

774811
def deploy_new_release(self, body):
775812
try:
@@ -783,12 +820,15 @@ def deploy_new_release(self, body):
783820
applicationID, currentReleaseID = self.get_application_details(body)
784821

785822
letResp = self.get_update_manifest(applicationID, newReleaseID, currentReleaseID)
823+
786824
if not letResp:
787825
return
788826

827+
fwtype = 'pygate' if hasattr(os.uname(), 'pygate') else 'pybytes'
828+
fwtype = 'pymesh' if hasattr(os.uname(), 'pymesh') else fwtype
789829
self.update_files(letResp, applicationID, newReleaseID)
790830
self.delete_files(letResp)
791831
self.update_application_config(letResp, applicationID)
792832
self.update_network_config(letResp)
793-
self.update_firmware(letResp)
833+
self.update_firmware(letResp, applicationID, fw_type=fwtype)
794834
machine.reset()

0 commit comments

Comments
 (0)