Skip to content

Commit 2ace1c2

Browse files
authored
Merge pull request #13 from maxgerhardt/ota
OTA v1
2 parents 5bb8311 + 47726da commit 2ace1c2

File tree

19 files changed

+637
-6
lines changed

19 files changed

+637
-6
lines changed

.github/workflows/examples.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ jobs:
1111
example:
1212
- "examples/arduino-blink"
1313
- "examples/arduino-wifi-scan"
14+
- "examples/arduino-ota"
15+
- "examples/arduino-signed-ota"
1416
- "examples/arduino-external-libs"
1517
runs-on: ${{ matrix.os }}
1618
steps:
@@ -25,6 +27,20 @@ jobs:
2527
run: |
2628
pip install -U https://github.com/platformio/platformio/archive/develop.zip
2729
pio pkg install --global --platform symlink://.
30+
# OpenSSL needed for signed OTA update example
31+
- name: Install OpenSSL
32+
run: |
33+
if [ "$RUNNER_OS" == "Linux" ]; then
34+
sudo apt-get install -y openssl
35+
elif [ "$RUNNER_OS" == "Windows" ]; then
36+
choco install openssl
37+
elif [ "$RUNNER_OS" == "macOS" ]; then
38+
brew install openssl
39+
else
40+
echo "$RUNNER_OS not supported"
41+
exit 1
42+
fi
43+
shell: bash
2844
- name: Build examples
2945
run: |
3046
pio run -d ${{ matrix.example }}

builder/main.py

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import sys
1616
from platform import system
17-
from os import makedirs
17+
from os import makedirs, remove
1818
from os.path import isdir, join, isfile
1919
import re
2020
import time
@@ -184,6 +184,22 @@ def generate_uf2(target, source, env):
184184
"$TARGET"
185185
]), "Building $TARGET"),
186186
suffix=".hex"
187+
),
188+
BinToSignedBin=Builder(
189+
action=env.VerboseAction(" ".join([
190+
'"$PYTHONEXE" "%s"' % join(
191+
platform.get_package_dir("framework-arduinopico") or "",
192+
"tools", "signing.py"),
193+
"--mode",
194+
"sign",
195+
"--privatekey",
196+
'"%s"' % join("$PROJECT_SRC_DIR", "private.key"),
197+
"--bin",
198+
"$SOURCES",
199+
"--out",
200+
"$TARGET"
201+
]), "Building $TARGET"),
202+
suffix=".bin.signed"
187203
)
188204
)
189205
)
@@ -209,14 +225,30 @@ def generate_uf2(target, source, env):
209225
)
210226
)
211227

228+
is_arduino_pico_build = env.BoardConfig().get("build.core", "arduino") == "earlephilhower" and "arduino" in env.get("PIOFRAMEWORK")
229+
if is_arduino_pico_build:
230+
pubkey = join(env.subst("$PROJECT_SRC_DIR"), "public.key")
231+
if isfile(pubkey):
232+
header_file = join(env.subst("$BUILD_DIR"), "core", "Updater_Signing.h")
233+
env.Prepend(CCFLAGS=['-I"%s"' % join("$BUILD_DIR", "core")])
234+
env.Execute(" ".join([
235+
'"$PYTHONEXE" "%s"' % join(
236+
platform.get_package_dir("framework-arduinopico"), "tools", "signing.py"),
237+
"--mode", "header",
238+
"--publickey", '"%s"' % join("$PROJECT_SRC_DIR", "public.key"),
239+
"--out", '"%s"' % join("$BUILD_DIR", "core", "Updater_Signing.h")
240+
]))
241+
212242
#
213243
# Target: Build executable and linkable firmware
214244
#
215245

216246
target_elf = None
247+
target_signed_bin = None
217248
if "nobuild" in COMMAND_LINE_TARGETS:
218249
target_elf = join("$BUILD_DIR", "${PROGNAME}.elf")
219250
target_firm = join("$BUILD_DIR", "${PROGNAME}.bin")
251+
target_firm = join("$BUILD_DIR", "${PROGNAME}.bin.signed")
220252
else:
221253
target_elf = env.BuildProgram()
222254
if set(["buildfs", "uploadfs"]) & set(COMMAND_LINE_TARGETS):
@@ -225,11 +257,14 @@ def generate_uf2(target, source, env):
225257
AlwaysBuild(target_firm)
226258
else:
227259
target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
260+
if is_arduino_pico_build:
261+
target_signed_bin = env.BinToSignedBin(join("$BUILD_DIR", "${PROGNAME}"), target_firm)
262+
env.Depends(target_signed_bin, "checkprogsize")
228263
env.Depends(target_firm, "checkprogsize")
229264

230265
env.AddPlatformTarget("buildfs", target_firm, target_firm, "Build Filesystem Image")
231266
AlwaysBuild(env.Alias("nobuild", target_firm))
232-
target_buildprog = env.Alias("buildprog", target_firm, target_firm)
267+
target_buildprog = env.Alias("buildprog", [target_firm, target_signed_bin], target_firm)
233268

234269
env.AddPostAction(
235270
target_elf, env.VerboseAction(generate_uf2, "Generating UF2 image")
@@ -284,14 +319,93 @@ def UploadUF2ToDisk(target, source, env):
284319
copyfile(fpath, join(env.subst("$UPLOAD_PORT"), "%s.%s" % (progname, ext)))
285320
print(
286321
"Firmware has been successfully uploaded.\n"
287-
"(Some boards may require manual hard reset)"
288322
)
289323

324+
def TryResetPico(target, source, env):
325+
upload_options = {}
326+
if "BOARD" in env:
327+
upload_options = env.BoardConfig().get("upload", {})
328+
ports = list_serial_ports()
329+
if len(ports) != 0:
330+
last_port = ports[-1]["port"]
331+
if upload_options.get("use_1200bps_touch", False):
332+
env.TouchSerialPort(last_port, 1200)
333+
time.sleep(2.0)
334+
335+
from platformio.device.list.util import list_logical_devices
336+
from platformio.device.finder import is_pattern_port
337+
from fnmatch import fnmatch
338+
339+
def find_rpi_disk(initial_port):
340+
msdlabels = ("RPI-RP2")
341+
item:str
342+
for item in list_logical_devices():
343+
if item["path"].startswith("/net"):
344+
continue
345+
if (
346+
initial_port
347+
and is_pattern_port(initial_port)
348+
and not fnmatch(item["path"], initial_port)
349+
):
350+
continue
351+
mbed_pages = [join(item["path"], n) for n in ("INDEX.HTM", "INFO_UF2.TXT")]
352+
if any(isfile(p) for p in mbed_pages):
353+
return item["path"]
354+
if item["name"] and any(l in item["name"].lower() for l in msdlabels):
355+
return item["path"]
356+
return None
357+
358+
def AutodetectPicoDisk(target, source, env):
359+
initial_port = env.subst("$UPLOAD_PORT")
360+
if initial_port and not is_pattern_port(initial_port):
361+
print(env.subst("Using manually specified: $UPLOAD_PORT"))
362+
return
363+
364+
if upload_protocol == "mbed":
365+
env.Replace(UPLOAD_PORT=find_rpi_disk(initial_port))
366+
367+
if env.subst("$UPLOAD_PORT"):
368+
print(env.subst("Auto-detected: $UPLOAD_PORT"))
369+
else:
370+
sys.stderr.write(
371+
"Error: Please specify `upload_port` for environment or use "
372+
"global `--upload-port` option.\n"
373+
"For some development platforms it can be a USB flash "
374+
"drive (i.e. /media/<user>/<device name>)\n"
375+
)
376+
env.Exit(1)
377+
290378
if upload_protocol == "mbed":
291379
upload_actions = [
292-
env.VerboseAction(env.AutodetectUploadPort, "Looking for upload disk..."),
380+
env.VerboseAction(TryResetPico, "Trying to reset Pico into bootloader mode..."),
381+
env.VerboseAction(AutodetectPicoDisk, "Looking for upload disk..."),
293382
env.VerboseAction(UploadUF2ToDisk, "Uploading $SOURCE")
294383
]
384+
elif upload_protocol == "espota":
385+
if not env.subst("$UPLOAD_PORT"):
386+
sys.stderr.write(
387+
"Error: Please specify IP address or host name of ESP device "
388+
"using `upload_port` for build environment or use "
389+
"global `--upload-port` option.\n"
390+
"See https://docs.platformio.org/page/platforms/"
391+
"espressif8266.html#over-the-air-ota-update\n")
392+
env.Replace(
393+
UPLOADER=join(
394+
platform.get_package_dir("framework-arduinopico") or "",
395+
"tools", "espota.py"),
396+
UPLOADERFLAGS=["--debug", "--progress", "-i", "$UPLOAD_PORT", "-p", "2040"],
397+
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE'
398+
)
399+
if "uploadfs" in COMMAND_LINE_TARGETS:
400+
env.Append(UPLOADERFLAGS=["-s"])
401+
else:
402+
# check if we have a .bin.signed file available.
403+
# since the file may not be build yet, we try to predict that we will
404+
# have that file if they private signing key exists.
405+
if isfile(join(env.subst("$PROJECT_SRC_DIR"), "private.key")):
406+
sys.stdout.write("Using signed OTA update file.")
407+
upload_source = target_signed_bin
408+
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
295409
elif upload_protocol == "picotool":
296410
env.Replace(
297411
UPLOADER=join(platform.get_package_dir("tool-rp2040tools") or "", "rp2040load"),
@@ -405,5 +519,4 @@ def _jlink_cmd_script(env, source):
405519
#
406520
# Default targets
407521
#
408-
409522
Default([target_buildprog, target_size])

examples/arduino-ota/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.pio

examples/arduino-ota/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
How to build PlatformIO based project
2+
=====================================
3+
4+
1. [Install PlatformIO Core](https://docs.platformio.org/page/core.html)
5+
2. Download [development platform with examples](https://github.com/platformio/platform-raspberrypi/archive/develop.zip)
6+
3. Extract ZIP archive
7+
4. Run these commands:
8+
9+
```shell
10+
# Change directory to example
11+
$ cd platform-raspberrypi/examples/arduino-ota
12+
13+
# Build project
14+
$ pio run
15+
16+
# Upload firmware
17+
$ pio run --target upload
18+
19+
# Clean build files
20+
$ pio run --target clean
21+
```
22+
23+
## Notes
24+
25+
This examples showcases the usage of Over-The-Air (OTA) updates with the Raspberry Pi Pico W.
26+
27+
For more details, see the [documentation](https://arduino-pico.readthedocs.io/en/latest/ota.html).
28+
29+
For the initial firmware update, use the `rpipicow_via_usb` environment.
30+
31+
Then, open the serial monitor and note down the IP of the Pico that it outputs.
32+
33+
Use this IP as the `upload_port` in the `rpipicow_via_ota` environment and use the "Upload" project task there.

examples/arduino-ota/include/README

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
This directory is intended for project header files.
3+
4+
A header file is a file containing C declarations and macro definitions
5+
to be shared between several project source files. You request the use of a
6+
header file in your project source file (C, C++, etc) located in `src` folder
7+
by including it, with the C preprocessing directive `#include'.
8+
9+
```src/main.c
10+
11+
#include "header.h"
12+
13+
int main (void)
14+
{
15+
...
16+
}
17+
```
18+
19+
Including a header file produces the same results as copying the header file
20+
into each source file that needs it. Such copying would be time-consuming
21+
and error-prone. With a header file, the related declarations appear
22+
in only one place. If they need to be changed, they can be changed in one
23+
place, and programs that include the header file will automatically use the
24+
new version when next recompiled. The header file eliminates the labor of
25+
finding and changing all the copies as well as the risk that a failure to
26+
find one copy will result in inconsistencies within a program.
27+
28+
In C, the usual convention is to give header files names that end with `.h'.
29+
It is most portable to use only letters, digits, dashes, and underscores in
30+
header file names, and at most one dot.
31+
32+
Read more about using header files in official GCC documentation:
33+
34+
* Include Syntax
35+
* Include Operation
36+
* Once-Only Headers
37+
* Computed Includes
38+
39+
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

examples/arduino-ota/lib/README

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
This directory is intended for project specific (private) libraries.
3+
PlatformIO will compile them to static libraries and link into executable file.
4+
5+
The source code of each library should be placed in a an own separate directory
6+
("lib/your_library_name/[here are source files]").
7+
8+
For example, see a structure of the following two libraries `Foo` and `Bar`:
9+
10+
|--lib
11+
| |
12+
| |--Bar
13+
| | |--docs
14+
| | |--examples
15+
| | |--src
16+
| | |- Bar.c
17+
| | |- Bar.h
18+
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
19+
| |
20+
| |--Foo
21+
| | |- Foo.c
22+
| | |- Foo.h
23+
| |
24+
| |- README --> THIS FILE
25+
|
26+
|- platformio.ini
27+
|--src
28+
|- main.c
29+
30+
and a contents of `src/main.c`:
31+
```
32+
#include <Foo.h>
33+
#include <Bar.h>
34+
35+
int main (void)
36+
{
37+
...
38+
}
39+
40+
```
41+
42+
PlatformIO Library Dependency Finder will find automatically dependent
43+
libraries scanning project source files.
44+
45+
More information about PlatformIO Library Dependency Finder
46+
- https://docs.platformio.org/page/librarymanager/ldf.html

examples/arduino-ota/platformio.ini

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
; PlatformIO Project Configuration File
2+
;
3+
; Build options: build flags, source filter, extra scripting
4+
; Upload options: custom port, speed and extra flags
5+
; Library options: dependencies, extra library storages
6+
;
7+
; Please visit documentation for the other options and examples
8+
; https://docs.platformio.org/page/projectconf.html
9+
10+
[env]
11+
platform = raspberrypi
12+
framework = arduino
13+
14+
; upload via USB
15+
[env:rpipicow_via_usb]
16+
board = rpipicow
17+
upload_protocol = mbed
18+
19+
; upload via OTA (change IP)
20+
[env:rpipicow_via_ota]
21+
board = rpipicow
22+
upload_protocol = espota
23+
upload_port = 192.168.0.206

0 commit comments

Comments
 (0)