Skip to content

Commit 7a16d56

Browse files
committed
Implemented code updater
1 parent 7038b31 commit 7a16d56

File tree

9 files changed

+104
-7
lines changed

9 files changed

+104
-7
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ print(connection.get_status())
2222
-------------------------------------------------------------------
2323

2424

25+
## Limitations & Considerations
26+
27+
- This library disables concurrent write protection-
28+
If your code stores something to a file, be careful when writing to it while the board is plugged in to avoid corruption!
29+
2530
### Testing Status:
2631
| MCU | Description |
2732
| ----------- | ----------- |

boot.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# OFFICIAL CubeServer Helper Code - DO NOT REMOVE!
2+
""" This file is run on hard resets and during code updates.
3+
Copyright (c) 2023 Joseph R. Freeston
4+
"""
5+
6+
import storage
7+
8+
print("Mounting storage with concurrent write protection off.")
9+
storage.remount("/", False, disable_concurrent_write_protection=True)
10+

code.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,23 @@
22

33
from servercom import Connection, Text
44

5+
# Connect to server:
56
print("Connecting to the server...")
67
connection = Connection()
78
print("Connected!")
89

9-
connection.post(Text("Test from CircuitPython!"))
10+
# Turn off WiFi to save power:
11+
connection.close_wifi()
1012

13+
# Reconnect WiFi when ready:
14+
connection.connect_wifi()
15+
16+
# Check for code updates from the server:
17+
connection.code_update()
18+
19+
# If none, post Hello World:
20+
connection.post(Text("Hello World!"))
21+
22+
# Get status:
1123
print("Getting status:")
1224
print(connection.get_status())

cubeserver-api-python.zip

184 KB
Binary file not shown.

package.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env bash
22

33
./update_version.sh
4-
zip -r cubeserver-api-python.zip . -x .gitignore -x ./.git 2>&1 > /dev/null
4+
zip -r cubeserver-api-python.zip . -x .gitignore -x ./.git -x package.sh -x update_version.sh -x version.txt 2>&1 > /dev/null
55
echo cubeserver-api-python.zip

LICENSE renamed to servercom/LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2022 Joseph R. Freeston (snorklerjoe)
3+
Copyright (c) 2022-2023 Joseph R. Freeston (snorklerjoe)
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

servercom/implementations/circuitpy.py

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,23 @@
44

55
import ssl
66
import wifi
7+
import time
78
import socketpool
89
from gc import collect
910
from errno import EAGAIN
10-
from binascii import b2a_base64
11+
from binascii import b2a_base64, a2b_base64
1112
from json import loads
13+
from microcontroller import reset
1214

1315
# Helpers:
16+
def _replace_code(new_code: bytes, do_reset=True):
17+
"""Replaces the contents of code.py with a new bytes
18+
"""
19+
with open("/code.py", "wb") as fp:
20+
fp.write(new_code)
21+
if do_reset:
22+
reset()
23+
1424
def enum(**enums):
1525
"""Fake enum-maker"""
1626
return type('Enum', (), enums)
@@ -157,12 +167,21 @@ def __init__(
157167
self.context.load_verify_locations(cadata=server_cert)
158168
self.connect_wifi()
159169

160-
def connect_wifi(self) -> None:
170+
def connect_wifi(self, attempts=10) -> None:
161171
"""Creates the wifi connection to the access point"""
162172
wifi.radio.enabled = True
163173
if self.v:
164174
print("Connecting to the access point...")
165-
wifi.radio.connect(self.conf.AP_SSID)
175+
connected = False
176+
while not connected and attempts > 0:
177+
try:
178+
wifi.radio.connect(self.conf.AP_SSID)
179+
continue
180+
except ConnectionError as e:
181+
if self.v:
182+
print(e.with_traceback)
183+
attempts -= 1
184+
time.sleep(1)
166185
if self.v:
167186
print("Initializing socket pool...")
168187
self.pool = socketpool.SocketPool(wifi.radio)
@@ -356,11 +375,19 @@ def request(
356375
raise last_error
357376

358377
def get_status(self) -> GameStatus:
359-
resp = self.request('GET', '/status')
378+
if self.v:
379+
print("Getting status...")
380+
resp = self.request('GET', '/status',
381+
headers=['User-Agent: CircuitPython, dude!']
382+
)
360383
resp_json = loads(resp[1])
384+
if self.v:
385+
print(f"It is {resp_json['unix_time']} seconds since the epoch.")
361386
return GameStatus(resp_json['unix_time'], resp_json['status']['score'], resp_json['status']['strikes'])
362387

363388
def post(self, point: DataPoint) -> bool:
389+
if self.v:
390+
print("Posting datapoint!")
364391
return self.request(
365392
'POST',
366393
'/data',
@@ -370,6 +397,8 @@ def post(self, point: DataPoint) -> bool:
370397
).code == 201
371398

372399
def email(self, msg: Email) -> bool:
400+
if self.v:
401+
print(f"Sending email {msg.subject}...")
373402
return self.request(
374403
'POST',
375404
'/email',
@@ -378,6 +407,44 @@ def email(self, msg: Email) -> bool:
378407
headers=['User-Agent: CircuitPython, dude!']
379408
).code == 201
380409

410+
def code_update(self, reset=True) -> bool:
411+
"""Checks for code updates from the server (uploaded by team)
412+
call like code_update(reset=False) to download the update but not reset
413+
(Must reset to run the new code)
414+
Otherwise, run like code_update()
415+
Returns True if reset is False and the update is successful.
416+
Reset the microcontroller in this case.
417+
Otherwise, returning False, the update is stale or invalid.
418+
"""
419+
if self.v:
420+
print("Checking for updates...")
421+
response = self.request(
422+
'GET',
423+
'/update',
424+
headers=['User-Agent: CircuitPython, dude!']
425+
)
426+
if response.code != 200:
427+
if self.v:
428+
print(f"Bad response code {response.code}")
429+
return False
430+
print(f"Response: {response[1]}")
431+
resp_json = loads(response[1])
432+
if resp_json['new']:
433+
if self.v:
434+
print("New Update!")
435+
_replace_code(
436+
a2b_base64(
437+
resp_json['code']
438+
),
439+
reset=reset
440+
)
441+
if self.v:
442+
print("code.py replaced!")
443+
return True
444+
if self.v:
445+
print("Stale update.")
446+
return False
447+
381448
def __exit__(self):
382449
if self.v:
383450
print("Closing the server connection-")

servercom/implementations/cpy.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ def email(self, msg: Email) -> bool:
354354
content_type = 'application/json',
355355
headers=['User-Agent: CPython, dude!']
356356
).code == 201
357+
def code_update(reset=True):
358+
pass
357359
def __exit__(self):
358360
if self.v:
359361
print("Closing the server connection-")

update_version.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ VERSION=$(cat ./version.txt)
44
VERSION_STR="__version__ = \"$VERSION\""
55

66
echo $VERSION_STR > ./servercom/_version.py
7+
echo $VERSION_STR > ./cube/_version.py

0 commit comments

Comments
 (0)