new file: app/httpclient.py new file: app/ota_updater.py new file: app/start.py new file: main.py new file: update.pymaster
parent
209b215f94
commit
77249a05c5
@ -0,0 +1,184 @@
|
|||||||
|
import usocket, os, gc
|
||||||
|
class Response:
|
||||||
|
|
||||||
|
def __init__(self, socket, saveToFile=None):
|
||||||
|
self._socket = socket
|
||||||
|
self._saveToFile = saveToFile
|
||||||
|
self._encoding = 'utf-8'
|
||||||
|
if saveToFile is not None:
|
||||||
|
CHUNK_SIZE = 512 # bytes
|
||||||
|
with open(saveToFile, 'w') as outfile:
|
||||||
|
data = self._socket.read(CHUNK_SIZE)
|
||||||
|
while data:
|
||||||
|
outfile.write(data)
|
||||||
|
data = self._socket.read(CHUNK_SIZE)
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self._socket:
|
||||||
|
self._socket.close()
|
||||||
|
self._socket = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def content(self):
|
||||||
|
if self._saveToFile is not None:
|
||||||
|
raise SystemError('You cannot get the content from the response as you decided to save it in {}'.format(self._saveToFile))
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self._socket.read()
|
||||||
|
return result
|
||||||
|
finally:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
return str(self.content, self._encoding)
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
try:
|
||||||
|
import ujson
|
||||||
|
result = ujson.load(self._socket)
|
||||||
|
return result
|
||||||
|
finally:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class HttpClient:
|
||||||
|
|
||||||
|
def __init__(self, headers={}):
|
||||||
|
self._headers = headers
|
||||||
|
|
||||||
|
def is_chunked_data(data):
|
||||||
|
return getattr(data, "__iter__", None) and not getattr(data, "__len__", None)
|
||||||
|
|
||||||
|
def request(self, method, url, data=None, json=None, file=None, custom=None, saveToFile=None, headers={}, stream=None):
|
||||||
|
chunked = data and self.is_chunked_data(data)
|
||||||
|
redirect = None #redirection url, None means no redirection
|
||||||
|
def _write_headers(sock, _headers):
|
||||||
|
for k in _headers:
|
||||||
|
sock.write(b'{}: {}\r\n'.format(k, _headers[k]))
|
||||||
|
|
||||||
|
try:
|
||||||
|
proto, dummy, host, path = url.split('/', 3)
|
||||||
|
except ValueError:
|
||||||
|
proto, dummy, host = url.split('/', 2)
|
||||||
|
path = ''
|
||||||
|
if proto == 'http:':
|
||||||
|
port = 80
|
||||||
|
elif proto == 'https:':
|
||||||
|
import ussl
|
||||||
|
port = 443
|
||||||
|
else:
|
||||||
|
raise ValueError('Unsupported protocol: ' + proto)
|
||||||
|
|
||||||
|
if ':' in host:
|
||||||
|
host, port = host.split(':', 1)
|
||||||
|
port = int(port)
|
||||||
|
|
||||||
|
ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM)
|
||||||
|
if len(ai) < 1:
|
||||||
|
raise ValueError('You are not connected to the internet...')
|
||||||
|
ai = ai[0]
|
||||||
|
|
||||||
|
s = usocket.socket(ai[0], ai[1], ai[2])
|
||||||
|
try:
|
||||||
|
s.connect(ai[-1])
|
||||||
|
if proto == 'https:':
|
||||||
|
gc.collect()
|
||||||
|
s = ussl.wrap_socket(s, server_hostname=host)
|
||||||
|
s.write(b'%s /%s HTTP/1.0\r\n' % (method, path))
|
||||||
|
if not 'Host' in headers:
|
||||||
|
s.write(b'Host: %s\r\n' % host)
|
||||||
|
# Iterate over keys to avoid tuple alloc
|
||||||
|
_write_headers(s, self._headers)
|
||||||
|
_write_headers(s, headers)
|
||||||
|
|
||||||
|
# add user agent
|
||||||
|
s.write(b'User-Agent: MicroPython Client\r\n')
|
||||||
|
if json is not None:
|
||||||
|
assert data is None
|
||||||
|
import ujson
|
||||||
|
data = ujson.dumps(json)
|
||||||
|
s.write(b'Content-Type: application/json\r\n')
|
||||||
|
|
||||||
|
if data:
|
||||||
|
if chunked:
|
||||||
|
s.write(b"Transfer-Encoding: chunked\r\n")
|
||||||
|
else:
|
||||||
|
s.write(b"Content-Length: %d\r\n" % len(data))
|
||||||
|
s.write(b"\r\n")
|
||||||
|
if data:
|
||||||
|
if chunked:
|
||||||
|
for chunk in data:
|
||||||
|
s.write(b"%x\r\n" % len(chunk))
|
||||||
|
s.write(chunk)
|
||||||
|
s.write(b"\r\n")
|
||||||
|
s.write("0\r\n\r\n")
|
||||||
|
else:
|
||||||
|
s.write(data)
|
||||||
|
elif file:
|
||||||
|
s.write(b'Content-Length: %d\r\n' % os.stat(file)[6])
|
||||||
|
s.write(b'\r\n')
|
||||||
|
with open(file, 'r') as file_object:
|
||||||
|
for line in file_object:
|
||||||
|
s.write(line + '\n')
|
||||||
|
elif custom:
|
||||||
|
custom(s)
|
||||||
|
else:
|
||||||
|
s.write(b'\r\n')
|
||||||
|
|
||||||
|
l = s.readline()
|
||||||
|
#print('l: ', l)
|
||||||
|
l = l.split(None, 2)
|
||||||
|
status = int(l[1])
|
||||||
|
reason = ''
|
||||||
|
if len(l) > 2:
|
||||||
|
reason = l[2].rstrip()
|
||||||
|
while True:
|
||||||
|
l = s.readline()
|
||||||
|
if not l or l == b'\r\n':
|
||||||
|
break
|
||||||
|
#print('l: ', l)
|
||||||
|
if l.startswith(b'Transfer-Encoding:'):
|
||||||
|
if b'chunked' in l:
|
||||||
|
raise ValueError('Unsupported ' + l)
|
||||||
|
elif l.startswith(b'Location:') and not 200 <= status <= 299:
|
||||||
|
if status in [301, 302, 303, 307, 308]:
|
||||||
|
redirect = l[10:-2].decode()
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("Redirect {} not yet supported".format(status))
|
||||||
|
except OSError:
|
||||||
|
s.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
if redirect:
|
||||||
|
s.close()
|
||||||
|
if status in [301, 302, 303]:
|
||||||
|
return self.request('GET', url=redirect, **kw)
|
||||||
|
else:
|
||||||
|
return self.request(method, redirect, **kw)
|
||||||
|
else:
|
||||||
|
resp = Response(s,saveToFile)
|
||||||
|
resp.status_code = status
|
||||||
|
resp.reason = reason
|
||||||
|
return resp
|
||||||
|
|
||||||
|
def head(self, url, **kw):
|
||||||
|
return self.request('HEAD', url, **kw)
|
||||||
|
|
||||||
|
def get(self, url, **kw):
|
||||||
|
return self.request('GET', url, **kw)
|
||||||
|
|
||||||
|
def post(self, url, **kw):
|
||||||
|
return self.request('POST', url, **kw)
|
||||||
|
|
||||||
|
def put(self, url, **kw):
|
||||||
|
return self.request('PUT', url, **kw)
|
||||||
|
|
||||||
|
def patch(self, url, **kw):
|
||||||
|
return self.request('PATCH', url, **kw)
|
||||||
|
|
||||||
|
def delete(self, url, **kw):
|
||||||
|
return self.request('DELETE', url, **kw)
|
@ -0,0 +1,250 @@
|
|||||||
|
import os, gc
|
||||||
|
from .httpclient import HttpClient
|
||||||
|
|
||||||
|
class OTAUpdater:
|
||||||
|
"""
|
||||||
|
A class to update your MicroController with the latest version from a GitHub tagged release,
|
||||||
|
optimized for low power usage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, github_repo, github_src_dir='', module='', main_dir='main', new_version_dir='next', secrets_file=None, headers={}):
|
||||||
|
self.http_client = HttpClient(headers=headers)
|
||||||
|
self.github_repo = github_repo.rstrip('/').replace('https://github.com/', '')
|
||||||
|
self.github_src_dir = '' if len(github_src_dir) < 1 else github_src_dir.rstrip('/') + '/'
|
||||||
|
self.module = module.rstrip('/')
|
||||||
|
self.main_dir = main_dir
|
||||||
|
self.new_version_dir = new_version_dir
|
||||||
|
self.secrets_file = secrets_file
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.http_client = None
|
||||||
|
|
||||||
|
def check_for_update_to_install_during_next_reboot(self) -> bool:
|
||||||
|
"""Function which will check the GitHub repo if there is a newer version available.
|
||||||
|
|
||||||
|
This method expects an active internet connection and will compare the current
|
||||||
|
version with the latest version available on GitHub.
|
||||||
|
If a newer version is available, the file 'next/.version' will be created
|
||||||
|
and you need to call machine.reset(). A reset is needed as the installation process
|
||||||
|
takes up a lot of memory (mostly due to the http stack)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool: true if a new version is available, false otherwise
|
||||||
|
"""
|
||||||
|
|
||||||
|
(current_version, latest_version) = self._check_for_new_version()
|
||||||
|
if latest_version > current_version:
|
||||||
|
print('New version available, will download and install on next reboot')
|
||||||
|
self._create_new_version_file(latest_version)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def install_update_if_available_after_boot(self, ssid, password) -> bool:
|
||||||
|
"""This method will install the latest version if out-of-date after boot.
|
||||||
|
|
||||||
|
This method, which should be called first thing after booting, will check if the
|
||||||
|
next/.version' file exists.
|
||||||
|
|
||||||
|
- If yes, it initializes the WIFI connection, downloads the latest version and installs it
|
||||||
|
- If no, the WIFI connection is not initialized as no new known version is available
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.new_version_dir in os.listdir(self.module):
|
||||||
|
if '.version' in os.listdir(self.modulepath(self.new_version_dir)):
|
||||||
|
latest_version = self.get_version(self.modulepath(self.new_version_dir), '.version')
|
||||||
|
print('New update found: ', latest_version)
|
||||||
|
OTAUpdater._using_network(ssid, password)
|
||||||
|
self.install_update_if_available()
|
||||||
|
return True
|
||||||
|
|
||||||
|
print('No new updates found...')
|
||||||
|
return False
|
||||||
|
|
||||||
|
def install_update_if_available(self) -> bool:
|
||||||
|
"""This method will immediately install the latest version if out-of-date.
|
||||||
|
|
||||||
|
This method expects an active internet connection and allows you to decide yourself
|
||||||
|
if you want to install the latest version. It is necessary to run it directly after boot
|
||||||
|
(for memory reasons) and you need to restart the microcontroller if a new version is found.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
bool: true if a new version is available, false otherwise
|
||||||
|
"""
|
||||||
|
|
||||||
|
(current_version, latest_version) = self._check_for_new_version()
|
||||||
|
if latest_version > current_version:
|
||||||
|
print('Updating to version {}...'.format(latest_version))
|
||||||
|
self._create_new_version_file(latest_version)
|
||||||
|
self._download_new_version(latest_version)
|
||||||
|
self._copy_secrets_file()
|
||||||
|
self._delete_old_version()
|
||||||
|
self._install_new_version()
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _using_network(ssid, password):
|
||||||
|
import network
|
||||||
|
sta_if = network.WLAN(network.STA_IF)
|
||||||
|
if not sta_if.isconnected():
|
||||||
|
print('connecting to network...')
|
||||||
|
sta_if.active(True)
|
||||||
|
sta_if.connect(ssid, password)
|
||||||
|
while not sta_if.isconnected():
|
||||||
|
pass
|
||||||
|
print('network config:', sta_if.ifconfig())
|
||||||
|
|
||||||
|
def _check_for_new_version(self):
|
||||||
|
current_version = self.get_version(self.modulepath(self.main_dir))
|
||||||
|
latest_version = self.get_latest_version()
|
||||||
|
|
||||||
|
print('Checking version... ')
|
||||||
|
print('\tCurrent version: ', current_version)
|
||||||
|
print('\tLatest version: ', latest_version)
|
||||||
|
return (current_version, latest_version)
|
||||||
|
|
||||||
|
def _create_new_version_file(self, latest_version):
|
||||||
|
self.mkdir(self.modulepath(self.new_version_dir))
|
||||||
|
with open(self.modulepath(self.new_version_dir + '/.version'), 'w') as versionfile:
|
||||||
|
versionfile.write(latest_version)
|
||||||
|
versionfile.close()
|
||||||
|
|
||||||
|
def get_version(self, directory, version_file_name='.version'):
|
||||||
|
if version_file_name in os.listdir(directory):
|
||||||
|
with open(directory + '/' + version_file_name) as f:
|
||||||
|
version = f.read()
|
||||||
|
return version
|
||||||
|
return '0.0'
|
||||||
|
|
||||||
|
def get_latest_version(self):
|
||||||
|
latest_release = self.http_client.get('https://api.github.com/repos/{}/releases/latest'.format(self.github_repo))
|
||||||
|
gh_json = latest_release.json()
|
||||||
|
try:
|
||||||
|
version = gh_json['tag_name']
|
||||||
|
except KeyError as e:
|
||||||
|
raise ValueError(
|
||||||
|
"Release not found: \n",
|
||||||
|
"Please ensure release as marked as 'latest', rather than pre-release \n",
|
||||||
|
"github api message: \n {} \n ".format(gh_json)
|
||||||
|
)
|
||||||
|
latest_release.close()
|
||||||
|
return version
|
||||||
|
|
||||||
|
def _download_new_version(self, version):
|
||||||
|
print('Downloading version {}'.format(version))
|
||||||
|
self._download_all_files(version)
|
||||||
|
print('Version {} downloaded to {}'.format(version, self.modulepath(self.new_version_dir)))
|
||||||
|
|
||||||
|
def _download_all_files(self, version, sub_dir=''):
|
||||||
|
url = 'https://api.github.com/repos/{}/contents{}{}{}?ref=refs/tags/{}'.format(self.github_repo, self.github_src_dir, self.main_dir, sub_dir, version)
|
||||||
|
gc.collect()
|
||||||
|
file_list = self.http_client.get(url)
|
||||||
|
file_list_json = file_list.json()
|
||||||
|
for file in file_list_json:
|
||||||
|
path = self.modulepath(self.new_version_dir + '/' + file['path'].replace(self.main_dir + '/', '').replace(self.github_src_dir, ''))
|
||||||
|
if file['type'] == 'file':
|
||||||
|
gitPath = file['path']
|
||||||
|
print('\tDownloading: ', gitPath, 'to', path)
|
||||||
|
self._download_file(version, gitPath, path)
|
||||||
|
elif file['type'] == 'dir':
|
||||||
|
print('Creating dir', path)
|
||||||
|
self.mkdir(path)
|
||||||
|
self._download_all_files(version, sub_dir + '/' + file['name'])
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
file_list.close()
|
||||||
|
|
||||||
|
def _download_file(self, version, gitPath, path):
|
||||||
|
self.http_client.get('https://raw.githubusercontent.com/{}/{}/{}'.format(self.github_repo, version, gitPath), saveToFile=path)
|
||||||
|
|
||||||
|
def _copy_secrets_file(self):
|
||||||
|
if self.secrets_file:
|
||||||
|
fromPath = self.modulepath(self.main_dir + '/' + self.secrets_file)
|
||||||
|
toPath = self.modulepath(self.new_version_dir + '/' + self.secrets_file)
|
||||||
|
print('Copying secrets file from {} to {}'.format(fromPath, toPath))
|
||||||
|
self._copy_file(fromPath, toPath)
|
||||||
|
print('Copied secrets file from {} to {}'.format(fromPath, toPath))
|
||||||
|
|
||||||
|
def _delete_old_version(self):
|
||||||
|
print('Deleting old version at {} ...'.format(self.modulepath(self.main_dir)))
|
||||||
|
self._rmtree(self.modulepath(self.main_dir))
|
||||||
|
print('Deleted old version at {} ...'.format(self.modulepath(self.main_dir)))
|
||||||
|
|
||||||
|
def _install_new_version(self):
|
||||||
|
print('Installing new version at {} ...'.format(self.modulepath(self.main_dir)))
|
||||||
|
if self._os_supports_rename():
|
||||||
|
os.rename(self.modulepath(self.new_version_dir), self.modulepath(self.main_dir))
|
||||||
|
else:
|
||||||
|
self._copy_directory(self.modulepath(self.new_version_dir), self.modulepath(self.main_dir))
|
||||||
|
self._rmtree(self.modulepath(self.new_version_dir))
|
||||||
|
print('Update installed, please reboot now')
|
||||||
|
|
||||||
|
def _rmtree(self, directory):
|
||||||
|
for entry in os.ilistdir(directory):
|
||||||
|
is_dir = entry[1] == 0x4000
|
||||||
|
if is_dir:
|
||||||
|
self._rmtree(directory + '/' + entry[0])
|
||||||
|
else:
|
||||||
|
os.remove(directory + '/' + entry[0])
|
||||||
|
os.rmdir(directory)
|
||||||
|
|
||||||
|
def _os_supports_rename(self) -> bool:
|
||||||
|
self._mk_dirs('otaUpdater/osRenameTest')
|
||||||
|
os.rename('otaUpdater', 'otaUpdated')
|
||||||
|
result = len(os.listdir('otaUpdated')) > 0
|
||||||
|
self._rmtree('otaUpdated')
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _copy_directory(self, fromPath, toPath):
|
||||||
|
if not self._exists_dir(toPath):
|
||||||
|
self._mk_dirs(toPath)
|
||||||
|
|
||||||
|
for entry in os.ilistdir(fromPath):
|
||||||
|
is_dir = entry[1] == 0x4000
|
||||||
|
if is_dir:
|
||||||
|
self._copy_directory(fromPath + '/' + entry[0], toPath + '/' + entry[0])
|
||||||
|
else:
|
||||||
|
self._copy_file(fromPath + '/' + entry[0], toPath + '/' + entry[0])
|
||||||
|
|
||||||
|
def _copy_file(self, fromPath, toPath):
|
||||||
|
with open(fromPath) as fromFile:
|
||||||
|
with open(toPath, 'w') as toFile:
|
||||||
|
CHUNK_SIZE = 512 # bytes
|
||||||
|
data = fromFile.read(CHUNK_SIZE)
|
||||||
|
while data:
|
||||||
|
toFile.write(data)
|
||||||
|
data = fromFile.read(CHUNK_SIZE)
|
||||||
|
toFile.close()
|
||||||
|
fromFile.close()
|
||||||
|
|
||||||
|
def _exists_dir(self, path) -> bool:
|
||||||
|
try:
|
||||||
|
os.listdir(path)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _mk_dirs(self, path:str):
|
||||||
|
paths = path.split('/')
|
||||||
|
|
||||||
|
pathToCreate = ''
|
||||||
|
for x in paths:
|
||||||
|
self.mkdir(pathToCreate + x)
|
||||||
|
pathToCreate = pathToCreate + x + '/'
|
||||||
|
|
||||||
|
# different micropython versions act differently when directory already exists
|
||||||
|
def mkdir(self, path:str):
|
||||||
|
try:
|
||||||
|
os.mkdir(path)
|
||||||
|
except OSError as exc:
|
||||||
|
if exc.args[0] == 17:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def modulepath(self, path):
|
||||||
|
return self.module + '/' + path if self.module else path
|
@ -0,0 +1 @@
|
|||||||
|
print('Version 2 installed using USB')
|
@ -0,0 +1,84 @@
|
|||||||
|
SSID='925'
|
||||||
|
PASSWD='925925925'
|
||||||
|
def connectToWifiAndUpdate():
|
||||||
|
import time
|
||||||
|
# upd.save(b"update","False")
|
||||||
|
# aoff()
|
||||||
|
time.sleep_ms(200)
|
||||||
|
from network import WLAN ,AP_IF,STA_IF
|
||||||
|
sta_if=WLAN(STA_IF)
|
||||||
|
print(sta_if.ifconfig())
|
||||||
|
sta_if.active(True)
|
||||||
|
ap_if=WLAN(AP_IF)
|
||||||
|
ap_if.active(False)
|
||||||
|
if sta_if.ifconfig()[0]=='0.0.0.0':
|
||||||
|
print('connectToWifiAndUpdate')
|
||||||
|
try:
|
||||||
|
sta_if.connect(SSID,PASSWD)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
print('waiting for connection...')
|
||||||
|
while sta_if.ifconfig()[0]=='0.0.0.0':
|
||||||
|
print(sta_if.ifconfig())
|
||||||
|
time.sleep(3)
|
||||||
|
from ota_updater import OTAUpdater
|
||||||
|
if(sta_if.ifconfig()[0]!='0.0.0.0'):
|
||||||
|
print('network config:', sta_if.ifconfig())
|
||||||
|
token='git access token'
|
||||||
|
git='https://git.msb-co.ir/MSB_Electronics/esp32_ota_example'
|
||||||
|
md='app'
|
||||||
|
try:
|
||||||
|
print(md,'\n',git,'\n')
|
||||||
|
# otaUpdater = OTAUpdater(git, main_dir=md,headers={'Authorization': 'token {}'.format(token)})
|
||||||
|
otaUpdater = OTAUpdater(git, main_dir=md)
|
||||||
|
hasUpdated=None
|
||||||
|
try:
|
||||||
|
hasUpdated = otaUpdater.install_update_if_available()
|
||||||
|
except OSError as e:
|
||||||
|
import sys
|
||||||
|
from uio import StringIO
|
||||||
|
s=StringIO()
|
||||||
|
sys.print_exception(e, s)
|
||||||
|
s=s.getvalue()
|
||||||
|
print(s)
|
||||||
|
if hasUpdated:
|
||||||
|
import machine
|
||||||
|
machine.reset()
|
||||||
|
else:
|
||||||
|
del(otaUpdater)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
print('DONE')
|
||||||
|
import gc
|
||||||
|
gc.collect()
|
||||||
|
connectToWifiAndUpdate()
|
||||||
|
|
||||||
|
# def connectToWifiAndUpdate():
|
||||||
|
# import time, machine, network, gc, app.secrets as secrets
|
||||||
|
# time.sleep(1)
|
||||||
|
# print('Memory free', gc.mem_free())
|
||||||
|
#
|
||||||
|
# from app.ota_updater import OTAUpdater
|
||||||
|
#
|
||||||
|
# sta_if = network.WLAN(network.STA_IF)
|
||||||
|
# if not sta_if.isconnected():
|
||||||
|
# print('connecting to network...')
|
||||||
|
# sta_if.active(True)
|
||||||
|
# sta_if.connect(secrets.WIFI_SSID, secrets.WIFI_PASSWORD)
|
||||||
|
# while not sta_if.isconnected():
|
||||||
|
# pass
|
||||||
|
# print('network config:', sta_if.ifconfig())
|
||||||
|
# otaUpdater = OTAUpdater('https://github.com/rdehuyss/micropython-ota-updater', main_dir='app', secrets_file="secrets.py")
|
||||||
|
# hasUpdated = otaUpdater.install_update_if_available()
|
||||||
|
# if hasUpdated:
|
||||||
|
# machine.reset()
|
||||||
|
# else:
|
||||||
|
# del(otaUpdater)
|
||||||
|
# gc.collect()
|
||||||
|
#
|
||||||
|
# def startApp():
|
||||||
|
# import app.start
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# connectToWifiAndUpdate()
|
||||||
|
# startApp()
|
Loading…
Reference in new issue