From 12b82ee6f9cdff064fec039d80e42d2ad0c34bdc Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 16:51:56 +0300 Subject: [PATCH 01/59] Initial commit --- README.md | 78 +++++++++++++++++++++- _extra/Workflow-CLI.md | 95 ++++++++++++++++++++++++++ _extra/Workflow-ViperIDE.md | 95 ++++++++++++++++++++++++++ _extra/tools/blynk_tag.py | 108 ++++++++++++++++++++++++++++++ _extra/tools/mpota.py | 128 ++++++++++++++++++++++++++++++++++++ cert/ca-bundle.pem | 79 ++++++++++++++++++++++ cfg/fw.json | 4 ++ cfg/sys.json | 10 +++ main.py | 48 ++++++++++++++ 9 files changed, 644 insertions(+), 1 deletion(-) create mode 100644 _extra/Workflow-CLI.md create mode 100644 _extra/Workflow-ViperIDE.md create mode 100755 _extra/tools/blynk_tag.py create mode 100755 _extra/tools/mpota.py create mode 100644 cert/ca-bundle.pem create mode 100644 cfg/fw.json create mode 100644 cfg/sys.json create mode 100644 main.py diff --git a/README.md b/README.md index 4210213..4384957 100644 --- a/README.md +++ b/README.md @@ -1 +1,77 @@ -# Blynk-MicroPython-Edgent \ No newline at end of file + +# Blynk.Edgent for MicroPython + +Blynk offers custom MicroPython builds tailored for IoT applications. These builds provide a standard MicroPython environment enhanced with various fixes, improvements, and additional features: + +- `blynk.inject` - BLE-assisted device claiming and provisioning +- `blynk.air` - OTA updates using **Blynk.Console** and **Blynk.Apps** +- `blynk.time` - Time Zone handling (including DST transitions), Sunrise/Sunset calculation +- `blynk.repl` - Remote MicroPyhton REPL for Blynk Terminal +- `netmgr` - Network management for `WiFi`, `Ethernet` and `Cellular` +- `config` - System-wide configuration +- `aiontp` - A versatile asyncio-based version of NTP client +- `logging` - System-wide, preconfigured logging +- `board` - A unified way to access the board peripherals +- Factory reset function +- Support for TLS certificate bundles +- For `ESP32`: + - `coredump` - Collect crash reports remotely + - OTA updates for MicroPython itself + +# Getting Started + +- Sign up/Log in to your [Blynk Account](https://blynk.cloud) +- Install **Blynk IoT App** for iOS or Android + +## 1. Install `MicroPython` build from Blynk + +Generic Dev Boards: + +- `ESP32`: 4MB / 8MB / 16MB Flash (PSRAM is auto-detected) +- `ESP32-C3`: 4MB Flash +- `ESP32-S3`: 8MB Quad Flash, 8MB / 16MB Octa Flash (PSRAM is auto-detected) +- `Raspberry Pi Pico W`: 2MB Flash +- `Winner Micro W600` + +Pre-configured devices: + +- `Seeed EdgeBox-ESP-100`: WiFi + Ethernet + Cellular +- `TTGO T-Internet-COM`: WiFi + Ethernet + Cellular +- `TTGO T-PCIE`: WiFi + Cellular + +## 2. Setup your board + +Use **Blynk IoT App** to add the device to your account. +1. Open Blynk App +2. + +If you have already created your device in Blynk, you can skip the App-based provisioning and add your device by directly modifying the device configuration using REPL: + +```py +# Add WiFi network +sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassword" }) + +# Setup Blynk Template and Auth Token +sysconfig["blynk"].update({ + "tmpl_id": "TMPxxxxxxxxxx", + "tmpl_name": "Device", + "auth": "rn60Wx*******", + "server": "blynk.cloud", +}) +sysconfig.commit() +``` + +> [NOTE!] +> When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. + + +--- + +## Further reading + +- [`Blynk MQTT API documentation`](https://docs.blynk.io/en/blynk.cloud-mqtt-api/device-mqtt-api) +- [`asyncio` documentation](https://docs.micropython.org/en/latest/library/asyncio.html) +- [`asyncio` tutorial](https://github.com/peterhinch/micropython-async/blob/master/v3/docs/TUTORIAL.md) +- [`mpremote` documentation](https://docs.micropython.org/en/latest/reference/mpremote.html) +- Alternative MQTT libraries like [mqtt_as](https://github.com/peterhinch/micropython-mqtt/tree/master/mqtt_as) + diff --git a/_extra/Workflow-CLI.md b/_extra/Workflow-CLI.md new file mode 100644 index 0000000..c7fb830 --- /dev/null +++ b/_extra/Workflow-CLI.md @@ -0,0 +1,95 @@ + +## 3. Modify the default example + +Make sure your board is connected via USB. It should **not** be opened by any serial monitor. +Run these commands on your development machine: + +```sh +# Install mpremote utility +pip3 install --upgrade mpremote + +# Copy the example files to the device +mpremote cp main.py : +``` + +## 4. Run + +Open MicroPython REPL: + +```sh +mpremote repl +``` + +Press `Ctrl+D` to restart your app. + +The device should get connected in a few seconds: + +```log + ___ __ __ + / _ )/ /_ _____ / /__ + / _ / / // / _ \/ '_/ + /____/_/\_, /_//_/_/\_\ + /___/ + +Connecting to WiFi_SSID... OK: 192.168.1.123 +Connecting to MQTT broker... +Connected to Blynk.Cloud [secure] +``` + +## Edit System Config + +You can edit `sysconfig` directly from MicroPython REPL: + +```py +# Display all sysconfig +sysconfig + +# Display parts of sysconfig +sysconfig.keys() +sysconfig['blynk'] + +# Disable watchdog (requires a hard reset) +sysconfig["wdt"]["enabled"] = False + +# Enable color logs and set log level +sysconfig["log"].update({ "color": True, "level": "debug" }) + +# Remove network by index (0-based) +del sysconfig["nets"][2] + +# Save settings +sysconfig.commit() +``` + +# Create OTA package + +The `cfg/fw.json` will be used to identify the firmware version and type. +Firmware type should match the Blynk Template ID, unless: +- You have a single product that requires differant firmware upgrade packages +- You have multiple products that use a single firmware + +```sh +python3 ./tools/mpota.py app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` +``` + +# Create recovery package + +Create `cfg/sys.json` with your product details: +```json +{ + "blynk": { + "tmpl_id": "TMPxxxxxxxx", + "tmpl_name": "Device", + "vendor": "Blynk", + "server": "blynk.cloud" + } +} +``` + +```sh +# Create recovery package +python3 ./tools/mpota.py recovery.tar.gz main.py cert/ca-bundle.pem cfg/sys.json + +# Freeze into `_recovery` module +python3 ./tools/mkrecovery.py recovery.tar.gz > _recovery.py +``` \ No newline at end of file diff --git a/_extra/Workflow-ViperIDE.md b/_extra/Workflow-ViperIDE.md new file mode 100644 index 0000000..c7fb830 --- /dev/null +++ b/_extra/Workflow-ViperIDE.md @@ -0,0 +1,95 @@ + +## 3. Modify the default example + +Make sure your board is connected via USB. It should **not** be opened by any serial monitor. +Run these commands on your development machine: + +```sh +# Install mpremote utility +pip3 install --upgrade mpremote + +# Copy the example files to the device +mpremote cp main.py : +``` + +## 4. Run + +Open MicroPython REPL: + +```sh +mpremote repl +``` + +Press `Ctrl+D` to restart your app. + +The device should get connected in a few seconds: + +```log + ___ __ __ + / _ )/ /_ _____ / /__ + / _ / / // / _ \/ '_/ + /____/_/\_, /_//_/_/\_\ + /___/ + +Connecting to WiFi_SSID... OK: 192.168.1.123 +Connecting to MQTT broker... +Connected to Blynk.Cloud [secure] +``` + +## Edit System Config + +You can edit `sysconfig` directly from MicroPython REPL: + +```py +# Display all sysconfig +sysconfig + +# Display parts of sysconfig +sysconfig.keys() +sysconfig['blynk'] + +# Disable watchdog (requires a hard reset) +sysconfig["wdt"]["enabled"] = False + +# Enable color logs and set log level +sysconfig["log"].update({ "color": True, "level": "debug" }) + +# Remove network by index (0-based) +del sysconfig["nets"][2] + +# Save settings +sysconfig.commit() +``` + +# Create OTA package + +The `cfg/fw.json` will be used to identify the firmware version and type. +Firmware type should match the Blynk Template ID, unless: +- You have a single product that requires differant firmware upgrade packages +- You have multiple products that use a single firmware + +```sh +python3 ./tools/mpota.py app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` +``` + +# Create recovery package + +Create `cfg/sys.json` with your product details: +```json +{ + "blynk": { + "tmpl_id": "TMPxxxxxxxx", + "tmpl_name": "Device", + "vendor": "Blynk", + "server": "blynk.cloud" + } +} +``` + +```sh +# Create recovery package +python3 ./tools/mpota.py recovery.tar.gz main.py cert/ca-bundle.pem cfg/sys.json + +# Freeze into `_recovery` module +python3 ./tools/mkrecovery.py recovery.tar.gz > _recovery.py +``` \ No newline at end of file diff --git a/_extra/tools/blynk_tag.py b/_extra/tools/blynk_tag.py new file mode 100755 index 0000000..852d50f --- /dev/null +++ b/_extra/tools/blynk_tag.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 + +""" + Extract the binary tag from the firmware binary: + python3 blynk-tag.py extract ./raw_firmware.bin ./tag.bin + + Create a new binary tag manually: + python3 blynk-tag.py create ./tag.bin --mcu 0.1.2 --type TMPL0123456 --build "Apr 19 2023 12:01:07" --blynk "0.3.0" + + Show info in a tag or a firmware: + python3 blynk-tag.py show ./tag.bin + python3 blynk-tag.py show ./raw_firmware.bin + +""" +import re + + +def create_tag(taginfo): + taginfo = map(lambda x: x.encode("utf-8"), taginfo) + return b"\0".join(taginfo) + b"\0\0" + + +def find_tag(data): + r = re.compile(b"blnkinf\\x00[\\w\\s\\.,:\\-\\(\\)\\x00]*?\\x00\\x00") + match = r.search(data) + if match is not None: + return match[0] + return None + + +def parse_tag(tag): + def pairwise(t): + it = iter(t) + return zip(it, it) + + taginfo = tag.split(b"\0") + taginfo = list(map(lambda x: x.decode("utf-8"), taginfo)) + return list(pairwise(taginfo[1:-2])) + + +if __name__ == "__main__": + import sys + import argparse + + def run_extract(args): + with open(args.file_in, "rb") as f: + data = f.read() + tag = find_tag(data) + if tag is None: + print("Blynk info tag not found", file=sys.stderr) + sys.exit(1) + with open(args.file_out, "wb") as f: + f.write(tag) + + def run_show(args): + with open(args.file_in, "rb") as f: + data = f.read() + tag = find_tag(data) + if tag is None: + print("Blynk info tag not found", file=sys.stderr) + sys.exit(1) + + for n, v in parse_tag(tag): + print(f"{n}: {v}") + + def run_create(args): + taginfo = ["blnkinf"] + taginfo.extend(["mcu", args.mcu]) + taginfo.extend(["fw-type", args.type]) + if args.build: + if args.build == "now": + import datetime + + dt = datetime.datetime.now(datetime.timezone.utc) + taginfo.extend(["build", dt.strftime("%b %d %Y %H:%M:%S")]) + else: + taginfo.extend(["build", args.build]) + if args.blynk: + taginfo.extend(["blynk", args.blynk]) + + tag = create_tag(taginfo) + with open(args.file_out, "wb") as f: + f.write(tag) + + parser = argparse.ArgumentParser(description="Blynk firmware tag utility") + subparsers = parser.add_subparsers() + create = subparsers.add_parser("create") + create.add_argument("--mcu", help="Version of the MCU firmware", required=True) + create.add_argument("--type", help="Firmware type (usually same as TemplateID)", required=True) + create.add_argument("--build", help="Firmware build date and time") + create.add_argument("--blynk", help="Blynk library version") + create.add_argument("file_out", metavar="FILE_OUT", help="output file") + create.set_defaults(func=run_create) + + extract = subparsers.add_parser("extract") + extract.add_argument("file_in", metavar="FILE_IN", help="input file") + extract.add_argument("file_out", metavar="FILE_OUT", help="output file") + extract.set_defaults(func=run_extract) + + show = subparsers.add_parser("show") + show.add_argument("file_in", metavar="FILE_IN", help="input file") + show.set_defaults(func=run_show) + + args = parser.parse_args() + if "func" in args: + args.func(args) + else: + parser.print_usage() diff --git a/_extra/tools/mpota.py b/_extra/tools/mpota.py new file mode 100755 index 0000000..cc66621 --- /dev/null +++ b/_extra/tools/mpota.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +# +# Example usage: +# python3 ./tools/mpota.py app_ota.tar.gz main.py some_module.py data.txt +# +# 1. Creates Blynk firmware binary tag based on the configuration +# 2. Packages input files into a tar (or tar.gz) file that can be used for OTA updates +# + +import sys, os, io, time +import re +import json +import tarfile +import datetime +import blynk_tag +import shutil +from subprocess import Popen, PIPE, DEVNULL + +do_minify = False +do_compile = True + +MPY_CROSS = shutil.which("mpy-cross") +now = datetime.datetime.now(datetime.timezone.utc) + + +def mpy_cross(source, filename): + if not MPY_CROSS: + raise RuntimeError(f"mpy-cross not found in PATH") + p = Popen([MPY_CROSS, "-s", filename, "-O3", "-"], stdin=PIPE, stdout=PIPE, stderr=PIPE) + out, err = p.communicate(input=source) + if p.returncode != 0: + raise RuntimeError(f"mpy-cross failed: {err.decode('utf-8')}") + return out + + +def minify(source, filename): + import python_minifier + + return python_minifier.minify( + source, + filename, + remove_annotations=True, + remove_pass=True, + remove_literal_statements=True, + combine_imports=True, + hoist_literals=False, + remove_object_base=False, + remove_debug=True, + remove_asserts=False, + rename_locals=True, preserve_locals=None, + rename_globals=False, preserve_globals=None, + convert_posargs_to_args=True, + preserve_shebang=False, + ) + + +def create_tagged_tar(input_files, output_tar_file): + + fw_ver = None + fw_type = None + + try: + with open("cfg/fw.json", "r") as f: + fwconfig = json.load(f) + fw_ver = fwconfig.get("ver") + fw_type = fwconfig.get("type") + except: + pass + + if not fw_ver: + raise Exception("Firmware version must be configured in 'cfg/fw.json'") + if not fw_type or "xxxxxxx" in fw_type: + raise Exception("Firmware type must be configured in 'cfg/fw.json'") + + # Create Blynk binary tag + taginfo = ["blnkinf"] + taginfo.extend(["mcu", fw_ver]) + taginfo.extend(["fw-type", fw_type]) + taginfo.extend(["build", now.strftime("%b %d %Y %H:%M:%S")]) + tag = blynk_tag.create_tag(taginfo) + + # Create a tar.gz file + with tarfile.open(output_tar_file, "w:gz", format=tarfile.GNU_FORMAT) as tar: + # Add firmware version info + with io.BytesIO(tag) as f: + ti = tarfile.TarInfo(name="fw_info.bin") + ti.size = len(tag) + ti.mtime = now.timestamp() + tar.addfile(tarinfo=ti, fileobj=f) + + # Add the original files to the tar as raw data + for fn in input_files: + try: + with open(fn, "rb") as f: + data = f.read() + + if fn.endswith(".py"): + base_fn = os.path.basename(fn) + if do_minify: + data = minify(data, base_fn).encode("utf-8") + + if do_compile and base_fn not in ("main.py"): + data = mpy_cross(data, base_fn) + fn = fn.replace(".py", ".mpy") + + ti = tarfile.TarInfo(name=fn) + ti.size = len(data) + ti.mtime = now.timestamp() + tar.addfile(tarinfo=ti, fileobj=io.BytesIO(data)) + + print(f"{fn:30} [{len(data)}]") + except Exception as e: + print(f"Error processing {fn}: ", e) + os.unlink(output_tar_file) + sys.exit(1) + + if any(item.startswith("cfg/") for item in input_files): + # Express concerns + print() + print(" = WARNING: System config (cfg/sys.json) should NOT be updated using OTA =") + print() + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: python script.py output_tar_file input_file1 [input_file2 ...]") + + create_tagged_tar(sys.argv[2:], sys.argv[1]) diff --git a/cert/ca-bundle.pem b/cert/ca-bundle.pem new file mode 100644 index 0000000..4fe3b64 --- /dev/null +++ b/cert/ca-bundle.pem @@ -0,0 +1,79 @@ + +4096 bit, ISRG Root X1, expires: Mon, 04 Jun 2035 11:04:38 GMT +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +2048 bit, Amazon Root CA 1, expires: Sun, 17 Jan 2038 00:00:00 GMT +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +2048 bit, DigiCert Global Root G2, expires: Fri, 15 Jan 2038 12:00:00 GMT +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- diff --git a/cfg/fw.json b/cfg/fw.json new file mode 100644 index 0000000..28b502b --- /dev/null +++ b/cfg/fw.json @@ -0,0 +1,4 @@ +{ + "ver": "0.1.0", + "type": "GENERIC" +} diff --git a/cfg/sys.json b/cfg/sys.json new file mode 100644 index 0000000..8a02d86 --- /dev/null +++ b/cfg/sys.json @@ -0,0 +1,10 @@ +{ + "blynk": { + "tmpl_id": "DYNAMIC", + "tmpl_name": "Device", + "vendor": "Blynk" + }, + "wdt": { + "enabled": false + } +} diff --git a/main.py b/main.py new file mode 100644 index 0000000..a56bb04 --- /dev/null +++ b/main.py @@ -0,0 +1,48 @@ +# This example bla bla bla +# Read detailed explanations here: +# https://docs.blynk.io/bla-bla-bla + +from blynk import mqtt, edgent +from asyncio import sleep_ms +import logging, board + +log = logging.getLogger("app") + +""" Register event handlers """ + +@edgent.on(edgent.CONNECTED) +def connected_handler(): + log.info("MQTT connected") + +@edgent.on(edgent.DISCONNECTED) +def disconnected_handler(): + log.info("MQTT disconnected") + +@edgent.on_message("ds/LED") +def led_handler(data): + if hasattr(board, "LED"): + board.LED.write(int(data)) + else: + log.info("Your LED is %s", data) + +@edgent.on_message() # Handle all other messages +def other_handler(topic, payload): + log.info("Got: %s, value: %s", topic, payload) + +""" Define our asyncio tasks """ + +async def publisher_task(): + counter = 0 + while True: + await sleep_ms(1000) + try: + mqtt.publish("ds/Counter", counter) + counter += 1 + except Exception as e: + pass + +""" Run the default asyncio loop """ + +edgent.run_asyncio_loop([ + publisher_task() +]) From b089cea29278de6f3b0f5d466c5e58aafbc5eab2 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 19:33:15 +0300 Subject: [PATCH 02/59] Update README.md --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4384957..0d9f155 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Blynk.Edgent for MicroPython -Blynk offers custom MicroPython builds tailored for IoT applications. These builds provide a standard MicroPython environment enhanced with various fixes, improvements, and additional features: +Blynk offers custom MicroPython builds tailored for IoT applications. +These builds provide a standard, `asyncio`-based MicroPython environment enhanced with various fixes, improvements, and additional features: - `blynk.inject` - BLE-assisted device claiming and provisioning - `blynk.air` - OTA updates using **Blynk.Console** and **Blynk.Apps** @@ -43,7 +44,9 @@ Pre-configured devices: Use **Blynk IoT App** to add the device to your account. 1. Open Blynk App -2. +2. Click **Add device** +3. Select **Find devices nearby** +4. Select your device, follow the setup instructions If you have already created your device in Blynk, you can skip the App-based provisioning and add your device by directly modifying the device configuration using REPL: @@ -61,9 +64,13 @@ sysconfig["blynk"].update({ sysconfig.commit() ``` -> [NOTE!] +> [!NOTE] > When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. +## 3. Edit the default firmware + +- Online IDE for Web and Mobile (ViperIDE) +- Command Line Interface () --- From df6ee004f03676478952b990ee92afba1e439537 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 19:39:12 +0300 Subject: [PATCH 03/59] Update README.md --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 0d9f155..df47b82 100644 --- a/README.md +++ b/README.md @@ -48,29 +48,29 @@ Use **Blynk IoT App** to add the device to your account. 3. Select **Find devices nearby** 4. Select your device, follow the setup instructions -If you have already created your device in Blynk, you can skip the App-based provisioning and add your device by directly modifying the device configuration using REPL: - -```py -# Add WiFi network -sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassword" }) - -# Setup Blynk Template and Auth Token -sysconfig["blynk"].update({ - "tmpl_id": "TMPxxxxxxxxxx", - "tmpl_name": "Device", - "auth": "rn60Wx*******", - "server": "blynk.cloud", -}) -sysconfig.commit() -``` - > [!NOTE] +> If you have already created your device in Blynk, you can skip the App-based provisioning and add your device by directly modifying the device configuration using REPL: +> ```py +> # Add WiFi network +> sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassword" }) +> +> # Setup Blynk Template and Auth Token +> sysconfig["blynk"].update({ +> "tmpl_id": "TMPxxxxxxxxxx", +> "tmpl_name": "Device", +> "auth": "rn60Wx*******", +> "server": "blynk.cloud", +> }) +> sysconfig.commit() +> ``` > When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. ## 3. Edit the default firmware -- Online IDE for Web and Mobile (ViperIDE) -- Command Line Interface () +There are many ways to program your device. We'll guide you through 2 most popular options: + +- [ViperIDE for Web and Mobile](_extra/Workflow-ViperIDE.md) +- [CLI using mpremote](_extra/Workflow-CLI.md) --- From 16dcc67f7a22d5db9d8bb39fe8794794bc40e1ec Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 20:03:11 +0300 Subject: [PATCH 04/59] Add files --- .gitignore | 3 + README.md | 17 +---- _boards/generic-esp32.py | 26 +++++++ _boards/generic-w600.py | 26 +++++++ _boards/rpi-pico-w.py | 17 +++++ _boards/ttgo-t-call-sim800c-20200609.py | 53 ++++++++++++++ _boards/ttgo-t-internet-com.py | 83 +++++++++++++++++++++ _boards/ttgo-t-pcie.py | 53 ++++++++++++++ _extra/Cookbook.md | 29 ++++++++ _extra/Workflow-ViperIDE.md | 95 +------------------------ cfg/sys.json | 4 ++ 11 files changed, 297 insertions(+), 109 deletions(-) create mode 100644 .gitignore create mode 100644 _boards/generic-esp32.py create mode 100644 _boards/generic-w600.py create mode 100644 _boards/rpi-pico-w.py create mode 100644 _boards/ttgo-t-call-sim800c-20200609.py create mode 100644 _boards/ttgo-t-internet-com.py create mode 100644 _boards/ttgo-t-pcie.py create mode 100644 _extra/Cookbook.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9951007 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +**/__pycache__/ +.DS_Store + diff --git a/README.md b/README.md index df47b82..23add97 100644 --- a/README.md +++ b/README.md @@ -49,21 +49,8 @@ Use **Blynk IoT App** to add the device to your account. 4. Select your device, follow the setup instructions > [!NOTE] -> If you have already created your device in Blynk, you can skip the App-based provisioning and add your device by directly modifying the device configuration using REPL: -> ```py -> # Add WiFi network -> sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassword" }) -> -> # Setup Blynk Template and Auth Token -> sysconfig["blynk"].update({ -> "tmpl_id": "TMPxxxxxxxxxx", -> "tmpl_name": "Device", -> "auth": "rn60Wx*******", -> "server": "blynk.cloud", -> }) -> sysconfig.commit() -> ``` -> When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. +> If you have already created your device in Blynk, +> you can [connect your device manually using REPL](_extra/Cookbook.md) ## 3. Edit the default firmware diff --git a/_boards/generic-esp32.py b/_boards/generic-esp32.py new file mode 100644 index 0000000..96fbb14 --- /dev/null +++ b/_boards/generic-esp32.py @@ -0,0 +1,26 @@ +from machine import Pin, RTC +import sys, network + +board_id = "Generic ESP32" + +class Board: + sta = None + rtc = None + +_board = Board() + +def rtc(): + if _board.rtc is None: + try: + _board.rtc = RTC() + except Exception as e: + sys.print_exception(e) + return _board.rtc + +def sta(): + if _board.sta is None: + try: + _board.sta = network.WLAN(network.STA_IF) + except Exception as e: + sys.print_exception(e) + return _board.sta diff --git a/_boards/generic-w600.py b/_boards/generic-w600.py new file mode 100644 index 0000000..9342aef --- /dev/null +++ b/_boards/generic-w600.py @@ -0,0 +1,26 @@ +from machine import Pin, RTC +import sys, network + +board_id = "Generic W600" + +class Board: + sta = None + rtc = None + +_board = Board() + +def rtc(): + if _board.rtc is None: + try: + _board.rtc = RTC() + except Exception as e: + sys.print_exception(e) + return _board.rtc + +def sta(): + if _board.sta is None: + try: + _board.sta = network.WLAN(network.STA_IF) + except Exception as e: + sys.print_exception(e) + return _board.sta diff --git a/_boards/rpi-pico-w.py b/_boards/rpi-pico-w.py new file mode 100644 index 0000000..406b705 --- /dev/null +++ b/_boards/rpi-pico-w.py @@ -0,0 +1,17 @@ +from machine import Pin +import sys, network + +board_id = "Pi Pico W" + +class Board: + sta = None + +_board = Board() + +def sta(): + if _board.sta is None: + try: + _board.sta = network.WLAN(network.STA_IF) + except Exception as e: + sys.print_exception(e) + return _board.sta diff --git a/_boards/ttgo-t-call-sim800c-20200609.py b/_boards/ttgo-t-call-sim800c-20200609.py new file mode 100644 index 0000000..6df7a1c --- /dev/null +++ b/_boards/ttgo-t-call-sim800c-20200609.py @@ -0,0 +1,53 @@ +from machine import Pin, SPI, UART +from modem import Modem +import sys, network + +board_id = "TTGO T-Call SIM800C 20200609" + +# I2C +I2C_SCL = Pin(22) +I2C_SDA = Pin(21) + +# Modem +MODEM_TX = Pin(27) +MODEM_RX = Pin(26) +MODEM_RST = Pin(25) +MODEM_PWRKEY = Pin(4) +MODEM_DTR = Pin(32) +MODEM_RI = Pin(33) + +# Miscelaneous +LED = Pin(12) + +class Board: + modem = None + sta = None + ppp = None + +_board = Board() + +def sta(): + if _board.sta is None: + try: + _board.sta = network.WLAN(network.STA_IF) + except Exception as e: + sys.print_exception(e) + return _board.sta + +def ppp(): + if _board.ppp is None: + try: + modem() + _board.ppp = network.PPP(1) + except Exception as e: + sys.print_exception(e) + return _board.ppp + +def modem(): + if _board.modem is None: + try: + uart = UART(1, tx=MODEM_TX, rx=MODEM_RX, txbuf=2048, rxbuf=2048, timeout=100) + _board.modem = Modem(uart=uart, rst=MODEM_RST, pwrkey=MODEM_PWRKEY) + except Exception as e: + sys.print_exception(e) + return _board.modem diff --git a/_boards/ttgo-t-internet-com.py b/_boards/ttgo-t-internet-com.py new file mode 100644 index 0000000..66679ec --- /dev/null +++ b/_boards/ttgo-t-internet-com.py @@ -0,0 +1,83 @@ +from machine import Pin, SPI, UART, SDCard +from modem import Modem +import sys, network + +board_id = "TTGO T-Internet-COM" + +# Modem +MODEM_TX = Pin(33) +MODEM_RX = Pin(35) +MODEM_RST = Pin(32) +MODEM_PWRKEY = None + +# Ethernet LAN8720 +ETH_MDC = Pin(23) +ETH_MDIO = Pin(18) +ETH_PWR = Pin(4) +ETH_RST = Pin(5) +ETH_REFCLC = Pin(0) + +# SPI (SDCard) +SPI_MISO = Pin(2) +SPI_MOSI = Pin(15) +SPI_CLK = Pin(14) +SD_CS = Pin(13) + +# Miscelaneous +NEOPIXEL = Pin(12) +BUTTON = Pin(0, Pin.IN, Pin.PULL_UP) # Reset Button + +class Board: + modem = None + sta = None + lan = None + ppp = None + sd = None + +_board = Board() + +def sd(): + if _board.sd is None: + try: + _board.sd = SDCard(slot=2, width=1, sck=SPI_CLK, miso=SPI_MISO, mosi=SPI_MOSI, cs=SD_CS, freq=20_000_000) + except Exception as e: + sys.print_exception(e) + return _board.sd + +def lan(): + if _board.lan is None: + try: + ETH_RST.init(Pin.OUT) + ETH_RST.value(1) + _board.lan = network.LAN(0, phy_type=network.PHY_LAN8720, + mdc=ETH_MDC, mdio=ETH_MDIO, power=ETH_PWR, + ref_clk=ETH_REFCLC, ref_clk_mode=Pin.OUT, phy_addr=0) + except Exception as e: + sys.print_exception(e) + return _board.lan + +def sta(): + if _board.sta is None: + try: + _board.sta = network.WLAN(network.STA_IF) + except Exception as e: + sys.print_exception(e) + return _board.sta + +def ppp(): + if _board.ppp is None: + try: + modem() + _board.ppp = network.PPP(1) + except Exception as e: + sys.print_exception(e) + return _board.ppp + +def modem(): + if _board.modem is None: + try: + uart = UART(1, tx=MODEM_TX, rx=MODEM_RX, txbuf=2048, rxbuf=2048, timeout=100) + _board.modem = Modem(uart=uart, rst=MODEM_RST, pwrkey=MODEM_PWRKEY) + except Exception as e: + sys.print_exception(e) + return _board.modem diff --git a/_boards/ttgo-t-pcie.py b/_boards/ttgo-t-pcie.py new file mode 100644 index 0000000..f0dff7e --- /dev/null +++ b/_boards/ttgo-t-pcie.py @@ -0,0 +1,53 @@ +from machine import Pin, SPI, UART +from modem import Modem +import sys, network + +board_id = "TTGO T-PCIE" + +# I2C +I2C_SCL = Pin(22) +I2C_SDA = Pin(21) + +# Modem +MODEM_TX = Pin(27) +MODEM_RX = Pin(26) +MODEM_RST = Pin(25) +MODEM_PWRKEY = Pin(4) +MODEM_DTR = Pin(32) +MODEM_RI = Pin(33) + +# Miscelaneous +LED = Pin(12) + +class Board: + modem = None + sta = None + ppp = None + +_board = Board() + +def sta(): + if _board.sta is None: + try: + _board.sta = network.WLAN(network.STA_IF) + except Exception as e: + sys.print_exception(e) + return _board.sta + +def ppp(): + if _board.ppp is None: + try: + modem() + _board.ppp = network.PPP(1) + except Exception as e: + sys.print_exception(e) + return _board.ppp + +def modem(): + if _board.modem is None: + try: + uart = UART(1, tx=MODEM_TX, rx=MODEM_RX, txbuf=2048, rxbuf=2048, timeout=100) + _board.modem = Modem(uart=uart, rst=MODEM_RST, pwrkey=MODEM_PWRKEY) + except Exception as e: + sys.print_exception(e) + return _board.modem diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md new file mode 100644 index 0000000..429b4a8 --- /dev/null +++ b/_extra/Cookbook.md @@ -0,0 +1,29 @@ + +# MicroPython + Blynk.Edgent Cookbook + +## Manual device connection + +Connect your device to Blynk.Cloud by directly modifying the device configuration using REPL: + +```py +# Add WiFi network +sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassword" }) + +# Setup Blynk Template and Auth Token +sysconfig["blynk"].update({ + "tmpl_id": "TMPxxxxxxxxxx", + "tmpl_name": "Device", + "auth": "rn60Wx*******", + "server": "blynk.cloud", +}) +sysconfig.commit() +``` + +> [!NOTE] +> When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. + +## Change log level + +```py +``` + diff --git a/_extra/Workflow-ViperIDE.md b/_extra/Workflow-ViperIDE.md index c7fb830..6921cea 100644 --- a/_extra/Workflow-ViperIDE.md +++ b/_extra/Workflow-ViperIDE.md @@ -1,95 +1,2 @@ -## 3. Modify the default example - -Make sure your board is connected via USB. It should **not** be opened by any serial monitor. -Run these commands on your development machine: - -```sh -# Install mpremote utility -pip3 install --upgrade mpremote - -# Copy the example files to the device -mpremote cp main.py : -``` - -## 4. Run - -Open MicroPython REPL: - -```sh -mpremote repl -``` - -Press `Ctrl+D` to restart your app. - -The device should get connected in a few seconds: - -```log - ___ __ __ - / _ )/ /_ _____ / /__ - / _ / / // / _ \/ '_/ - /____/_/\_, /_//_/_/\_\ - /___/ - -Connecting to WiFi_SSID... OK: 192.168.1.123 -Connecting to MQTT broker... -Connected to Blynk.Cloud [secure] -``` - -## Edit System Config - -You can edit `sysconfig` directly from MicroPython REPL: - -```py -# Display all sysconfig -sysconfig - -# Display parts of sysconfig -sysconfig.keys() -sysconfig['blynk'] - -# Disable watchdog (requires a hard reset) -sysconfig["wdt"]["enabled"] = False - -# Enable color logs and set log level -sysconfig["log"].update({ "color": True, "level": "debug" }) - -# Remove network by index (0-based) -del sysconfig["nets"][2] - -# Save settings -sysconfig.commit() -``` - -# Create OTA package - -The `cfg/fw.json` will be used to identify the firmware version and type. -Firmware type should match the Blynk Template ID, unless: -- You have a single product that requires differant firmware upgrade packages -- You have multiple products that use a single firmware - -```sh -python3 ./tools/mpota.py app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` -``` - -# Create recovery package - -Create `cfg/sys.json` with your product details: -```json -{ - "blynk": { - "tmpl_id": "TMPxxxxxxxx", - "tmpl_name": "Device", - "vendor": "Blynk", - "server": "blynk.cloud" - } -} -``` - -```sh -# Create recovery package -python3 ./tools/mpota.py recovery.tar.gz main.py cert/ca-bundle.pem cfg/sys.json - -# Freeze into `_recovery` module -python3 ./tools/mkrecovery.py recovery.tar.gz > _recovery.py -``` \ No newline at end of file +TODO diff --git a/cfg/sys.json b/cfg/sys.json index 8a02d86..07853c3 100644 --- a/cfg/sys.json +++ b/cfg/sys.json @@ -6,5 +6,9 @@ }, "wdt": { "enabled": false + }, + "log": { + "color": true, + "level": "debug" } } From 22949987e72e633ecbb5081c1f4aaebb7ce4ae71 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 20:04:13 +0300 Subject: [PATCH 05/59] Update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23add97..245899b 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Use **Blynk IoT App** to add the device to your account. > [!NOTE] > If you have already created your device in Blynk, -> you can [connect your device manually using REPL](_extra/Cookbook.md) +> you can [connect your device manually using REPL](_extra/Cookbook.md#manual-device-connection) ## 3. Edit the default firmware From e8d3058783377ac09c6abd3fe05fe0b92c39dae3 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 20:09:31 +0300 Subject: [PATCH 06/59] Update Cookbook.md --- _extra/Cookbook.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index 429b4a8..4073679 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -16,14 +16,36 @@ sysconfig["blynk"].update({ "auth": "rn60Wx*******", "server": "blynk.cloud", }) + +# Save device configuration sysconfig.commit() ``` > [!NOTE] > When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. -## Change log level +## Change logger settings + +```py +sysconfig["log"].update({ "color": True, "level": "debug" }) +``` + +## Watchdog Timer ```py +sysconfig["wdt"]["enabled"] = False ``` +## Format internal FS + +Use one of these commands depending on your actual hardware: +```py +# ESP32 +import os, flashbdev; os.VfsLfs2.mkfs(flashbdev.bdev) + +# RP2040 +import vfs, rp2; vfs.VfsLfs2.mkfs(rp2.Flash(), progsize=256) + +# WM W600 +import vfs, w600; vfs.VfsLfs2.mkfs(w600.Flash(), progsize=256) +``` From 2a4b0c75cf215819c091f6005fd42fb83af324ad Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 20:11:29 +0300 Subject: [PATCH 07/59] Update Cookbook.md --- _extra/Cookbook.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index 4073679..c12d8b9 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -38,6 +38,9 @@ sysconfig["wdt"]["enabled"] = False ## Format internal FS +> [!WARNING] +> This performs a factory reset, the internal file system will recover to it's initial state + Use one of these commands depending on your actual hardware: ```py # ESP32 From 3528850b812de47574d5123a5f7d1a6f7030e3a0 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 20:13:48 +0300 Subject: [PATCH 08/59] Update Cookbook.md --- _extra/Cookbook.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index c12d8b9..1b26e30 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -34,6 +34,7 @@ sysconfig["log"].update({ "color": True, "level": "debug" }) ```py sysconfig["wdt"]["enabled"] = False +sysconfig.commit() ``` ## Format internal FS @@ -52,3 +53,9 @@ import vfs, rp2; vfs.VfsLfs2.mkfs(rp2.Flash(), progsize=256) # WM W600 import vfs, w600; vfs.VfsLfs2.mkfs(w600.Flash(), progsize=256) ``` + +## Update MicroPython firmware directly from GitHub (ESP32 only) + +```py +blynk.air.start_ota_update("https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240222-v1.22.2.app-bin", validate=False) +``` From 743fd3c8d7fea6dcf1364fe3c1f461ed3fff6a6c Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 21:02:58 +0300 Subject: [PATCH 09/59] Update Cookbook.md --- _extra/Cookbook.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index 1b26e30..b01a569 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -22,12 +22,14 @@ sysconfig.commit() ``` > [!NOTE] -> When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. +> When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. Please contact Blynk for guidance. ## Change logger settings ```py sysconfig["log"].update({ "color": True, "level": "debug" }) +sysconfig.commit() +machine.reset() ``` ## Watchdog Timer @@ -35,6 +37,7 @@ sysconfig["log"].update({ "color": True, "level": "debug" }) ```py sysconfig["wdt"]["enabled"] = False sysconfig.commit() +machine.reset() ``` ## Format internal FS From 2df8da51591aad3e496877ecc3a6ba797669ae12 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 21:06:54 +0300 Subject: [PATCH 10/59] Update Cookbook.md --- _extra/Cookbook.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index b01a569..1e47fc1 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -34,8 +34,10 @@ machine.reset() ## Watchdog Timer +Most of builds come with the watchdog disabled by default. + ```py -sysconfig["wdt"]["enabled"] = False +sysconfig["wdt"]["enabled"] = True # or False sysconfig.commit() machine.reset() ``` From 17c50c7d82ef228004a396738cbec7dcc5eeba78 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 21:08:55 +0300 Subject: [PATCH 11/59] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 245899b..03566be 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Use **Blynk IoT App** to add the device to your account. > If you have already created your device in Blynk, > you can [connect your device manually using REPL](_extra/Cookbook.md#manual-device-connection) -## 3. Edit the default firmware +## 3. Edit the default Python app There are many ways to program your device. We'll guide you through 2 most popular options: From 58ebcf2aaa9d5535cca307686ea6d434d5451ee2 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 21:13:00 +0300 Subject: [PATCH 12/59] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 03566be..b777a09 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Pre-configured devices: - `Seeed EdgeBox-ESP-100`: WiFi + Ethernet + Cellular - `TTGO T-Internet-COM`: WiFi + Ethernet + Cellular - `TTGO T-PCIE`: WiFi + Cellular +- `TTGO T-Call SIM800C`: WiFi + Cellular/2G ## 2. Setup your board From ebe37ea0cd5c2e503a6eaebb675bb7ee8a82e9c3 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 22:21:57 +0300 Subject: [PATCH 13/59] Update docs --- README.md | 11 +++++------ _extra/Cookbook.md | 32 +++++++++++++++++++++++++------- _extra/Workflow-CLI.md | 36 ++++++------------------------------ 3 files changed, 36 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index b777a09..cbbea6c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ These builds provide a standard, `asyncio`-based MicroPython environment enhance - Support for TLS certificate bundles - For `ESP32`: - `coredump` - Collect crash reports remotely - - OTA updates for MicroPython itself + - OTA updates for MicroPython system firmware # Getting Started @@ -43,7 +43,8 @@ Pre-configured devices: ## 2. Setup your board -Use **Blynk IoT App** to add the device to your account. +Use **Blynk IoT App** to add the device to your account + 1. Open Blynk App 2. Click **Add device** 3. Select **Find devices nearby** @@ -53,7 +54,7 @@ Use **Blynk IoT App** to add the device to your account. > If you have already created your device in Blynk, > you can [connect your device manually using REPL](_extra/Cookbook.md#manual-device-connection) -## 3. Edit the default Python app +## 3. Edit the default MicroPython app There are many ways to program your device. We'll guide you through 2 most popular options: @@ -64,9 +65,7 @@ There are many ways to program your device. We'll guide you through 2 most popul ## Further reading -- [`Blynk MQTT API documentation`](https://docs.blynk.io/en/blynk.cloud-mqtt-api/device-mqtt-api) +- [Cookbook](_extra/Cookbook.md) - [`asyncio` documentation](https://docs.micropython.org/en/latest/library/asyncio.html) - [`asyncio` tutorial](https://github.com/peterhinch/micropython-async/blob/master/v3/docs/TUTORIAL.md) -- [`mpremote` documentation](https://docs.micropython.org/en/latest/reference/mpremote.html) -- Alternative MQTT libraries like [mqtt_as](https://github.com/peterhinch/micropython-mqtt/tree/master/mqtt_as) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index 1e47fc1..f728dc3 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -6,7 +6,7 @@ Connect your device to Blynk.Cloud by directly modifying the device configuration using REPL: ```py -# Add WiFi network +# Add your WiFi network sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassword" }) # Setup Blynk Template and Auth Token @@ -17,29 +17,47 @@ sysconfig["blynk"].update({ "server": "blynk.cloud", }) -# Save device configuration +# Save system configuration sysconfig.commit() ``` > [!NOTE] > When entering the production phase, you can **pre-configure** these settings and enable the **Factory Reset** function. Please contact Blynk for guidance. -## Change logger settings +## Edit System Config + +You can edit `sysconfig` directly from MicroPython REPL: ```py +# Display complete sysconfig +sysconfig + +# Display parts of sysconfig +sysconfig.keys() +sysconfig['blynk'] + +# Enable color logs and set log level sysconfig["log"].update({ "color": True, "level": "debug" }) + +# Add your WiFi network +sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassword" }) + +# Remove network by index (0-based) +del sysconfig["nets"][2] + +# Save system configuration sysconfig.commit() -machine.reset() ``` ## Watchdog Timer -Most of builds come with the watchdog disabled by default. +The watchdog is typically disabled by default, as it can complicate prototyping. +It is recommended to enable it at later stages of development: ```py -sysconfig["wdt"]["enabled"] = True # or False +sysconfig["wdt"]["enabled"] = True sysconfig.commit() -machine.reset() +machine.reset() # Chnaging this setting requires a hard reset ``` ## Format internal FS diff --git a/_extra/Workflow-CLI.md b/_extra/Workflow-CLI.md index c7fb830..8501942 100644 --- a/_extra/Workflow-CLI.md +++ b/_extra/Workflow-CLI.md @@ -1,7 +1,7 @@ -## 3. Modify the default example +# Using `mpremote` -Make sure your board is connected via USB. It should **not** be opened by any serial monitor. +Make sure your board is connected via USB. It should **not** be opened by any serial monitor or other tool. Run these commands on your development machine: ```sh @@ -12,9 +12,10 @@ pip3 install --upgrade mpremote mpremote cp main.py : ``` -## 4. Run +> [!NOTE] +> If you have any issues with this, please check out the [`mpremote` documentation](https://docs.micropython.org/en/latest/reference/mpremote.html) -Open MicroPython REPL: +Then, open MicroPython REPL: ```sh mpremote repl @@ -36,31 +37,6 @@ Connecting to MQTT broker... Connected to Blynk.Cloud [secure] ``` -## Edit System Config - -You can edit `sysconfig` directly from MicroPython REPL: - -```py -# Display all sysconfig -sysconfig - -# Display parts of sysconfig -sysconfig.keys() -sysconfig['blynk'] - -# Disable watchdog (requires a hard reset) -sysconfig["wdt"]["enabled"] = False - -# Enable color logs and set log level -sysconfig["log"].update({ "color": True, "level": "debug" }) - -# Remove network by index (0-based) -del sysconfig["nets"][2] - -# Save settings -sysconfig.commit() -``` - # Create OTA package The `cfg/fw.json` will be used to identify the firmware version and type. @@ -92,4 +68,4 @@ python3 ./tools/mpota.py recovery.tar.gz main.py cert/ca-bundle.pem cfg/sys.json # Freeze into `_recovery` module python3 ./tools/mkrecovery.py recovery.tar.gz > _recovery.py -``` \ No newline at end of file +``` From 22ab0681327a56efbbd648ac164c5f6881b53a9c Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 22:26:31 +0300 Subject: [PATCH 14/59] Update README.md --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cbbea6c..dcd2684 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Blynk.Edgent for MicroPython Blynk offers custom MicroPython builds tailored for IoT applications. -These builds provide a standard, `asyncio`-based MicroPython environment enhanced with various fixes, improvements, and additional features: +These builds provide a standard, MicroPython environment enhanced with various fixes, improvements, and additional features: - `blynk.inject` - BLE-assisted device claiming and provisioning - `blynk.air` - OTA updates using **Blynk.Console** and **Blynk.Apps** @@ -56,6 +56,14 @@ Use **Blynk IoT App** to add the device to your account ## 3. Edit the default MicroPython app +The [`main.py`](./main.py) is a simple `asyncio`-based sctipt that defines the high level device operation. +It could be as simple as this: + +```py +from blynk import edgent +edgent.run_asyncio_loop() +``` + There are many ways to program your device. We'll guide you through 2 most popular options: - [ViperIDE for Web and Mobile](_extra/Workflow-ViperIDE.md) From b8f5b88e93af29f6de33b23325589d796eda320e Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 22:32:55 +0300 Subject: [PATCH 15/59] Update README.md --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dcd2684..78efcc7 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,22 @@ Use **Blynk IoT App** to add the device to your account ## 3. Edit the default MicroPython app -The [`main.py`](./main.py) is a simple `asyncio`-based sctipt that defines the high level device operation. +The [`main.py`](./main.py) is a simple `asyncio`-based script that defines the high level device operation. It could be as simple as this: ```py from blynk import edgent -edgent.run_asyncio_loop() +from time import ticks_ms +from asyncio import sleep_ms + +async def publisher_task(): + while True: + await sleep_ms(1000) + edgent.updateDataStream("Uptime", ticks_ms()) + +edgent.run_asyncio_loop([ + publisher_task() +]) ``` There are many ways to program your device. We'll guide you through 2 most popular options: From 84a099612d47c007f82ad0fec7f467a9e49a7667 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 22:41:44 +0300 Subject: [PATCH 16/59] Update README.md --- README.md | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 78efcc7..b52b83e 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,8 @@ # Blynk.Edgent for MicroPython Blynk offers custom MicroPython builds tailored for IoT applications. -These builds provide a standard, MicroPython environment enhanced with various fixes, improvements, and additional features: - -- `blynk.inject` - BLE-assisted device claiming and provisioning -- `blynk.air` - OTA updates using **Blynk.Console** and **Blynk.Apps** -- `blynk.time` - Time Zone handling (including DST transitions), Sunrise/Sunset calculation -- `blynk.repl` - Remote MicroPyhton REPL for Blynk Terminal -- `netmgr` - Network management for `WiFi`, `Ethernet` and `Cellular` -- `config` - System-wide configuration -- `aiontp` - A versatile asyncio-based version of NTP client -- `logging` - System-wide, preconfigured logging -- `board` - A unified way to access the board peripherals -- Factory reset function -- Support for TLS certificate bundles -- For `ESP32`: - - `coredump` - Collect crash reports remotely - - OTA updates for MicroPython system firmware +These builds provide a standard, MicroPython environment enhanced with various fixes, improvements, +and additional features like device claiming and provisioning, OTA updates, configuration store and [many more](#features). # Getting Started @@ -79,6 +65,23 @@ There are many ways to program your device. We'll guide you through 2 most popul - [ViperIDE for Web and Mobile](_extra/Workflow-ViperIDE.md) - [CLI using mpremote](_extra/Workflow-CLI.md) +# Features + +- `blynk.inject` - BLE-assisted device claiming and provisioning +- `blynk.air` - OTA updates using **Blynk.Console** and **Blynk.Apps** +- `blynk.time` - Time Zone handling (including DST transitions), Sunrise/Sunset calculation +- `blynk.repl` - Remote MicroPyhton REPL for Blynk Terminal +- `netmgr` - Network management for `WiFi`, `Ethernet` and `Cellular` +- `config` - System-wide configuration +- `aiontp` - A versatile asyncio-based version of NTP client +- `logging` - System-wide, preconfigured logging +- `board` - A unified way to access the board peripherals +- Factory reset function +- Support for TLS certificate bundles +- For `ESP32`: + - `coredump` - Collect crash reports remotely + - OTA updates for MicroPython system firmware + --- ## Further reading From a79638cf923abc8c5b228e1f062149d66cd0e51f Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 22:44:55 +0300 Subject: [PATCH 17/59] Update README.md --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b52b83e..349aef5 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ Blynk offers custom MicroPython builds tailored for IoT applications. These builds provide a standard, MicroPython environment enhanced with various fixes, improvements, and additional features like device claiming and provisioning, OTA updates, configuration store and [many more](#features). -# Getting Started +## Getting Started - Sign up/Log in to your [Blynk Account](https://blynk.cloud) - Install **Blynk IoT App** for iOS or Android -## 1. Install `MicroPython` build from Blynk +## 1. Install our `MicroPython` build Generic Dev Boards: @@ -27,14 +27,12 @@ Pre-configured devices: - `TTGO T-PCIE`: WiFi + Cellular - `TTGO T-Call SIM800C`: WiFi + Cellular/2G -## 2. Setup your board +## 2. Connect your device to Blynk.Cloud -Use **Blynk IoT App** to add the device to your account - -1. Open Blynk App +1. Open **Blynk IoT App** on your smartphone 2. Click **Add device** 3. Select **Find devices nearby** -4. Select your device, follow the setup instructions +4. Select your device and follow the setup instructions > [!NOTE] > If you have already created your device in Blynk, From 6df7702ae56a56c5c299725176cd3c469c1588cb Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 22:46:57 +0300 Subject: [PATCH 18/59] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 349aef5..4d6f342 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,12 @@ Pre-configured devices: ## 2. Connect your device to Blynk.Cloud 1. Open **Blynk IoT App** on your smartphone -2. Click **Add device** -3. Select **Find devices nearby** -4. Select your device and follow the setup instructions +2. Click **Add device** -> **Find devices nearby** +3. Select your device and follow the setup instructions > [!NOTE] > If you have already created your device in Blynk, -> you can [connect your device manually using REPL](_extra/Cookbook.md#manual-device-connection) +> you can [connect it manually using REPL](_extra/Cookbook.md#manual-device-connection) ## 3. Edit the default MicroPython app From ee7e95ad632e057b9452ba61778bbb7e2d7cdc49 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 25 Jul 2024 22:49:19 +0300 Subject: [PATCH 19/59] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d6f342..71914e8 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ Generic Dev Boards: - `ESP32`: 4MB / 8MB / 16MB Flash (PSRAM is auto-detected) - `ESP32-C3`: 4MB Flash -- `ESP32-S3`: 8MB Quad Flash, 8MB / 16MB Octa Flash (PSRAM is auto-detected) +- `ESP32-S3` (PSRAM is auto-detected): + - Quad Flash: 8MB + - Octa Flash: 8MB / 16MB - `Raspberry Pi Pico W`: 2MB Flash - `Winner Micro W600` From 262b41d276716c25f5428c271627e61ecf525193 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 11:13:22 +0300 Subject: [PATCH 20/59] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 71914e8..17ae98a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Blynk.Edgent for MicroPython -Blynk offers custom MicroPython builds tailored for IoT applications. -These builds provide a standard, MicroPython environment enhanced with various fixes, improvements, -and additional features like device claiming and provisioning, OTA updates, configuration store and [many more](#features). +Blynk provides custom MicroPython builds specifically designed for IoT applications. +These builds offer a standard MicroPython environment, enriched with numerous fixes, improvements, +and additional features such as **device claiming and provisioning, OTA updates, configuration storage**, [and more](#features). ## Getting Started From 1bf50199d2c1da6527b75090ecbda7d459727455 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 11:18:25 +0300 Subject: [PATCH 21/59] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17ae98a..982cf80 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ Generic Dev Boards: - `ESP32-S3` (PSRAM is auto-detected): - Quad Flash: 8MB - Octa Flash: 8MB / 16MB -- `Raspberry Pi Pico W`: 2MB Flash +- `RP2040` + - Raspberry Pi Pico W (2MB Flash) - `Winner Micro W600` Pre-configured devices: @@ -59,7 +60,7 @@ edgent.run_asyncio_loop([ ]) ``` -There are many ways to program your device. We'll guide you through 2 most popular options: +There are many ways to program your device. Here, we'll guide you through the two most popular options: - [ViperIDE for Web and Mobile](_extra/Workflow-ViperIDE.md) - [CLI using mpremote](_extra/Workflow-CLI.md) From 6d03f44cf19241be6e5e6c24a518851df5eb4584 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 11:35:13 +0300 Subject: [PATCH 22/59] Move boards --- {_boards => _extra/boards}/generic-esp32.py | 0 {_boards => _extra/boards}/generic-w600.py | 0 {_boards => _extra/boards}/rpi-pico-w.py | 0 {_boards => _extra/boards}/ttgo-t-call-sim800c-20200609.py | 0 {_boards => _extra/boards}/ttgo-t-internet-com.py | 0 {_boards => _extra/boards}/ttgo-t-pcie.py | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {_boards => _extra/boards}/generic-esp32.py (100%) rename {_boards => _extra/boards}/generic-w600.py (100%) rename {_boards => _extra/boards}/rpi-pico-w.py (100%) rename {_boards => _extra/boards}/ttgo-t-call-sim800c-20200609.py (100%) rename {_boards => _extra/boards}/ttgo-t-internet-com.py (100%) rename {_boards => _extra/boards}/ttgo-t-pcie.py (100%) diff --git a/_boards/generic-esp32.py b/_extra/boards/generic-esp32.py similarity index 100% rename from _boards/generic-esp32.py rename to _extra/boards/generic-esp32.py diff --git a/_boards/generic-w600.py b/_extra/boards/generic-w600.py similarity index 100% rename from _boards/generic-w600.py rename to _extra/boards/generic-w600.py diff --git a/_boards/rpi-pico-w.py b/_extra/boards/rpi-pico-w.py similarity index 100% rename from _boards/rpi-pico-w.py rename to _extra/boards/rpi-pico-w.py diff --git a/_boards/ttgo-t-call-sim800c-20200609.py b/_extra/boards/ttgo-t-call-sim800c-20200609.py similarity index 100% rename from _boards/ttgo-t-call-sim800c-20200609.py rename to _extra/boards/ttgo-t-call-sim800c-20200609.py diff --git a/_boards/ttgo-t-internet-com.py b/_extra/boards/ttgo-t-internet-com.py similarity index 100% rename from _boards/ttgo-t-internet-com.py rename to _extra/boards/ttgo-t-internet-com.py diff --git a/_boards/ttgo-t-pcie.py b/_extra/boards/ttgo-t-pcie.py similarity index 100% rename from _boards/ttgo-t-pcie.py rename to _extra/boards/ttgo-t-pcie.py From e0faff7813e69a5d1fb3f4fc19c16a14ca036f6b Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 12:13:04 +0300 Subject: [PATCH 23/59] Cleanup --- README.md | 2 +- _extra/Workflow-CLI.md | 21 --------------------- main.py | 11 ++++------- 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 982cf80..3b69bc6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Blynk provides custom MicroPython builds specifically designed for IoT applications. These builds offer a standard MicroPython environment, enriched with numerous fixes, improvements, -and additional features such as **device claiming and provisioning, OTA updates, configuration storage**, [and more](#features). +and additional features such as **secure Blynk.Cloud connection, device claiming and provisioning, OTA updates, configuration storage**, [and more](#features). ## Getting Started diff --git a/_extra/Workflow-CLI.md b/_extra/Workflow-CLI.md index 8501942..7cb55cc 100644 --- a/_extra/Workflow-CLI.md +++ b/_extra/Workflow-CLI.md @@ -48,24 +48,3 @@ Firmware type should match the Blynk Template ID, unless: python3 ./tools/mpota.py app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` ``` -# Create recovery package - -Create `cfg/sys.json` with your product details: -```json -{ - "blynk": { - "tmpl_id": "TMPxxxxxxxx", - "tmpl_name": "Device", - "vendor": "Blynk", - "server": "blynk.cloud" - } -} -``` - -```sh -# Create recovery package -python3 ./tools/mpota.py recovery.tar.gz main.py cert/ca-bundle.pem cfg/sys.json - -# Freeze into `_recovery` module -python3 ./tools/mkrecovery.py recovery.tar.gz > _recovery.py -``` diff --git a/main.py b/main.py index a56bb04..b3030cb 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,7 @@ # Read detailed explanations here: # https://docs.blynk.io/bla-bla-bla -from blynk import mqtt, edgent +from blynk import edgent from asyncio import sleep_ms import logging, board @@ -29,17 +29,14 @@ def led_handler(data): def other_handler(topic, payload): log.info("Got: %s, value: %s", topic, payload) -""" Define our asyncio tasks """ +""" Define asyncio tasks """ async def publisher_task(): counter = 0 while True: await sleep_ms(1000) - try: - mqtt.publish("ds/Counter", counter) - counter += 1 - except Exception as e: - pass + edgent.updateDataStream("Counter", counter) + counter += 1 """ Run the default asyncio loop """ From f46f30857202a1b6c51d50bcef983b93e58db352 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 12:13:11 +0300 Subject: [PATCH 24/59] Cleanup --- _extra/boards/seeed-edge-box-esp100.py | 202 +++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 _extra/boards/seeed-edge-box-esp100.py diff --git a/_extra/boards/seeed-edge-box-esp100.py b/_extra/boards/seeed-edge-box-esp100.py new file mode 100644 index 0000000..b039128 --- /dev/null +++ b/_extra/boards/seeed-edge-box-esp100.py @@ -0,0 +1,202 @@ +from machine import Pin, ADC, PWM, SPI, I2C, UART, unique_id +from modem import Modem +from pcf8563 import PCF8563 +from ads1x15 import ADS1115 +import sys, network, time + +board_id = "Seeed EdgeBox-ESP-100" + +# Digital Outputs +DO0 = Pin(40, Pin.OUT) +DO1 = Pin(39, Pin.OUT) +DO2 = Pin(38, Pin.OUT) +try: + DO3 = Pin(37, Pin.OUT) + DO4 = Pin(36, Pin.OUT) + DO5 = Pin(35, Pin.OUT) +except: + print("DO3,DO4,DO5 unavailable due to enabled Octal PSRAM") + +# Digital Inputs +DI0 = Pin(4, Pin.IN) +DI1 = Pin(5, Pin.IN) +DI2 = Pin(6, Pin.IN) +DI3 = Pin(7, Pin.IN) + +# Analog Outputs +AO0 = PWM(Pin(42)) +AO1 = PWM(Pin(41)) + +# RS485 +RS485_TX = Pin(17) +RS485_RX = Pin(18) +RS485_RTS = Pin(8) + +# CAN +CAN_TX = Pin(1) +CAN_RX = Pin(2) + +# I2C +I2C_SCL = Pin(19) +I2C_SDA = Pin(20) + +I2C_ADDR_FRAM = 0x50 # FM24CL64B +I2C_ADDR_RTC = 0x51 # PCF8563 +I2C_ADDR_ECC = 0x68 # ATECC608A +I2C_ADDR_ADC = 0x48 # ADS1115/SGM58031 + +# RTC +PCF8563_INT = Pin(9, Pin.IN, Pin.PULL_UP) + +# Modem A7670G-LABE +MODEM_TX = Pin(48) +MODEM_RX = Pin(47) +MODEM_RST = Pin(16) # Enable power through SY8089A +MODEM_PWRKEY = Pin(21) + +# SPI (Ethernet) +SPI_MISO = Pin(11) +SPI_MOSI = Pin(12) +SPI_CLK = Pin(13) + +# Ethernet W5500 +ETH_CS = Pin(10) +ETH_INT = Pin(14) +ETH_RST = Pin(15) + +# Miscelaneous +LED = Pin(43) # Same as TX +LED_ERR = Pin(44) # Same as RX +BUZZER = Pin(45) # Buzzer (active high) +BUTTON = Pin(0, Pin.IN, Pin.PULL_UP) # Reset Button + +class Board: + spi = None + i2c = None + rs485 = None + modem = None + sta = None + lan = None + ppp = None + rtc = None + adc1 = None + fram = None + +_board = Board() + + +class I2CMemDev: + def __init__(self, i2c:I2C, address, block_size, block_count, addrsize=16): + self.i2c = i2c + self.addr = address + self.addrsize = addrsize + self.block_size = block_size + self.block_count = block_count + + def readblocks(self, block_num, buf, offset=0): + memaddr = block_num * self.block_size + offset + self.i2c.readfrom_mem_into(self.addr, memaddr, buf, addrsize=self.addrsize) + + def writeblocks(self, block_num, buf, offset=0): + memaddr = block_num * self.block_size + offset + self.i2c.writeto_mem(self.addr, memaddr, buf, addrsize=self.addrsize) + + def ioctl(self, op, arg): + if op == 4: + return self.block_count + if op == 5: + return self.block_size + if op == 6: # block erase + return 0 + +def i2c(): + if _board.i2c is None: + try: + _board.i2c = I2C(1, scl=I2C_SCL, sda=I2C_SDA, freq=400_000) + except Exception as e: + sys.print_exception(e) + return _board.i2c + +def spi(): + if _board.spi is None: + try: + _board.spi = SPI(2, sck=SPI_CLK, mosi=SPI_MOSI, miso=SPI_MISO, baudrate=40_000_000) + except Exception as e: + sys.print_exception(e) + return _board.spi + +def rtc(): + if _board.rtc is None: + try: + _board.rtc = PCF8563(i2c(), I2C_ADDR_RTC) + except Exception as e: + sys.print_exception(e) + return _board.rtc + +def adc1(): + if _board.adc1 is None: + try: + _board.adc1 = ADS1115(i2c(), I2C_ADDR_ADC) + except Exception as e: + sys.print_exception(e) + return _board.adc1 + +def fram(): + if _board.fram is None: + try: + _board.fram = I2CMemDev(i2c(), I2C_ADDR_FRAM, 128, 8192 // 128, 16) + except Exception as e: + sys.print_exception(e) + return _board.fram + +def lan(): + if _board.lan is None: + try: + _board.lan = network.LAN(0, phy_type=network.PHY_W5500, spi=spi(), + phy_addr=1, cs=ETH_CS, int=ETH_INT) + mac = bytearray(unique_id())[:6] + mac[5] = (mac[5] + 4) % 255 + _board.lan.config(mac=mac) + except Exception as e: + sys.print_exception(e) + return _board.lan + +def sta(): + if _board.sta is None: + try: + _board.sta = network.WLAN(network.STA_IF) + except Exception as e: + sys.print_exception(e) + return _board.sta + +def ppp(): + if _board.ppp is None: + try: + m = modem() + _board.ppp = network.PPP(1) + except Exception as e: + sys.print_exception(e) + return _board.ppp + +def modem(): + if _board.modem is None: + try: + uart = UART(1, tx=MODEM_TX, rx=MODEM_RX, txbuf=2048, rxbuf=2048, timeout=100) + _board.modem = Modem(uart=uart, rst=MODEM_RST, pwrkey=MODEM_PWRKEY) + except Exception as e: + sys.print_exception(e) + return _board.modem + +def rs485(): + if _board.rs485 is None: + try: + _board.rs485 = UART(2, tx=RS485_TX, rx=RS485_RX, rts=RS485_RTS, txbuf=2048, rxbuf=2048, timeout=100) + except Exception as e: + sys.print_exception(e) + return _board.rs485 + +def beep(dur=100): + BUZZER.init(Pin.OUT) + BUZZER.on() + time.sleep_ms(dur) + BUZZER.off() From 6ff09e484d2d9d7eeafa2b3f7d61268595e7fe61 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 12:26:38 +0300 Subject: [PATCH 25/59] Update README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 3b69bc6..97c0e75 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,8 @@ There are many ways to program your device. Here, we'll guide you through the tw - `coredump` - Collect crash reports remotely - OTA updates for MicroPython system firmware ---- - -## Further reading +# Further reading - [Cookbook](_extra/Cookbook.md) - [`asyncio` documentation](https://docs.micropython.org/en/latest/library/asyncio.html) - [`asyncio` tutorial](https://github.com/peterhinch/micropython-async/blob/master/v3/docs/TUTORIAL.md) - From d5d3bab946b44f03a0c1834f9f69d0546e85a50f Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 13:28:58 +0300 Subject: [PATCH 26/59] Flash instructions --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 97c0e75..903e347 100644 --- a/README.md +++ b/README.md @@ -12,24 +12,26 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ## 1. Install our `MicroPython` build -Generic Dev Boards: +### ESP32 -- `ESP32`: 4MB / 8MB / 16MB Flash (PSRAM is auto-detected) -- `ESP32-C3`: 4MB Flash -- `ESP32-S3` (PSRAM is auto-detected): +- Generic `ESP32`: 4MB / 8MB / 16MB Flash (PSRAM is auto-detected) +- Generic `ESP32-C3`: 4MB Flash +- Generic `ESP32-S3` (PSRAM is auto-detected): - Quad Flash: 8MB - Octa Flash: 8MB / 16MB -- `RP2040` - - Raspberry Pi Pico W (2MB Flash) -- `Winner Micro W600` - -Pre-configured devices: - - `Seeed EdgeBox-ESP-100`: WiFi + Ethernet + Cellular - `TTGO T-Internet-COM`: WiFi + Ethernet + Cellular - `TTGO T-PCIE`: WiFi + Cellular - `TTGO T-Call SIM800C`: WiFi + Cellular/2G + + Try it with ESP Launchpad + + +### Raspberry Pi Pico W + +`TODO: Instructions` + ## 2. Connect your device to Blynk.Cloud 1. Open **Blynk IoT App** on your smartphone From ea0d7a2371c502e9b50e9bbf05766d7e466c36cc Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 13:35:20 +0300 Subject: [PATCH 27/59] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 903e347..cc406bf 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,10 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ### Raspberry Pi Pico W -`TODO: Instructions` +1. Download the latest `UF2 firmware file` +2. Hold down the `BOOTSEL` button while plugging the board into USB +3. The UF2 file below should then be copied to the USB mass storage device that appears +4. Once programming of the new firmware is complete the device will automatically reset and be ready for use ## 2. Connect your device to Blynk.Cloud From 301294bffa4c8b46cd51802cc405ecc351d2ab40 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 13:39:08 +0300 Subject: [PATCH 28/59] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cc406bf..64a93c8 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,9 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ### Raspberry Pi Pico W -1. Download the latest `UF2 firmware file` -2. Hold down the `BOOTSEL` button while plugging the board into USB -3. The UF2 file below should then be copied to the USB mass storage device that appears -4. Once programming of the new firmware is complete the device will automatically reset and be ready for use +1. Hold down the `BOOTSEL` button while plugging the board into a USB port +2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears +3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use ## 2. Connect your device to Blynk.Cloud From 4b053d2a6c73e992e64aec55bb18277cabcdb646 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 14:11:15 +0300 Subject: [PATCH 29/59] Update README.md --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 64a93c8..c8bf49e 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,15 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ### ESP32 +1. Open [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) +2. Plug your board into a USB port +3. Click `Connect` in upper right corner and select your board +4. Select Application (generic boards vs specialized builds) +5. Select your Develop Kit variant +6. Click the `Flash` button + +The builds listed in the ESP launchpad will include: + - Generic `ESP32`: 4MB / 8MB / 16MB Flash (PSRAM is auto-detected) - Generic `ESP32-C3`: 4MB Flash - Generic `ESP32-S3` (PSRAM is auto-detected): @@ -24,10 +33,6 @@ and additional features such as **secure Blynk.Cloud connection, device claiming - `TTGO T-PCIE`: WiFi + Cellular - `TTGO T-Call SIM800C`: WiFi + Cellular/2G - - Try it with ESP Launchpad - - ### Raspberry Pi Pico W 1. Hold down the `BOOTSEL` button while plugging the board into a USB port From 2cce97a5881656af4c709d44e1324e35c11d0961 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 14:26:40 +0300 Subject: [PATCH 30/59] Update README.md --- README.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c8bf49e..adb3c24 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,8 @@ and additional features such as **secure Blynk.Cloud connection, device claiming 5. Select your Develop Kit variant 6. Click the `Flash` button -The builds listed in the ESP launchpad will include: - -- Generic `ESP32`: 4MB / 8MB / 16MB Flash (PSRAM is auto-detected) -- Generic `ESP32-C3`: 4MB Flash -- Generic `ESP32-S3` (PSRAM is auto-detected): - - Quad Flash: 8MB - - Octa Flash: 8MB / 16MB -- `Seeed EdgeBox-ESP-100`: WiFi + Ethernet + Cellular -- `TTGO T-Internet-COM`: WiFi + Ethernet + Cellular -- `TTGO T-PCIE`: WiFi + Cellular -- `TTGO T-Call SIM800C`: WiFi + Cellular/2G +> [!NOTE] +> Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) ### Raspberry Pi Pico W From c90a2d07f5f69e9968e9bbfc3b2b098272c257cb Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 14:30:11 +0300 Subject: [PATCH 31/59] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index adb3c24..c0ecfe8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming 3. Click `Connect` in upper right corner and select your board 4. Select Application (generic boards vs specialized builds) 5. Select your Develop Kit variant -6. Click the `Flash` button +6. Click the `Flash` button (if disabled, click the `Connect` button again) > [!NOTE] > Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) From 3e502e12feee74cd1ee9db9d7ece54eb9e98ee55 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 15:38:37 +0300 Subject: [PATCH 32/59] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c0ecfe8..cc66bc0 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming 4. Select Application (generic boards vs specialized builds) 5. Select your Develop Kit variant 6. Click the `Flash` button (if disabled, click the `Connect` button again) +7. Press `Reset` button on your board to run the MicroPython firmware > [!NOTE] > Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) From c8572bf69f4f4fdbc0dd1ed50ec053d0bafc65f6 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 15:43:30 +0300 Subject: [PATCH 33/59] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index cc66bc0..0db6cbe 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ### ESP32 +
+ Expand + 1. Open [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) 2. Plug your board into a USB port 3. Click `Connect` in upper right corner and select your board @@ -25,12 +28,18 @@ and additional features such as **secure Blynk.Cloud connection, device claiming > [!NOTE] > Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) +
+ ### Raspberry Pi Pico W +
+ Expand 1. Hold down the `BOOTSEL` button while plugging the board into a USB port 2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use +
+ ## 2. Connect your device to Blynk.Cloud 1. Open **Blynk IoT App** on your smartphone From 5aeffc6646848ec002146c94347ebec674be70b7 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 15:43:53 +0300 Subject: [PATCH 34/59] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0db6cbe..8929dbf 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ and additional features such as **secure Blynk.Cloud connection, device claiming 6. Click the `Flash` button (if disabled, click the `Connect` button again) 7. Press `Reset` button on your board to run the MicroPython firmware -> [!NOTE] -> Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) + > [!NOTE] + > Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) @@ -34,6 +34,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming
Expand + 1. Hold down the `BOOTSEL` button while plugging the board into a USB port 2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use From c4f12912eaba9d52aec9fdf1ad68660ee4dbae8f Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 15:45:04 +0300 Subject: [PATCH 35/59] Update README.md --- README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8929dbf..200c2a6 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,8 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ## 1. Install our `MicroPython` build -### ESP32 -
- Expand + See ESP32, ESP32-S3, ESP32-C3 instructions 1. Open [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) 2. Plug your board into a USB port @@ -30,10 +28,8 @@ and additional features such as **secure Blynk.Cloud connection, device claiming
-### Raspberry Pi Pico W -
- Expand + See Raspberry Pi Pico W instructions 1. Hold down the `BOOTSEL` button while plugging the board into a USB port 2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears @@ -47,7 +43,6 @@ and additional features such as **secure Blynk.Cloud connection, device claiming 2. Click **Add device** -> **Find devices nearby** 3. Select your device and follow the setup instructions -> [!NOTE] > If you have already created your device in Blynk, > you can [connect it manually using REPL](_extra/Cookbook.md#manual-device-connection) From d93227f15ff6ff5164cd2285be523ab88c4d9348 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 15:45:34 +0300 Subject: [PATCH 36/59] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 200c2a6..e6e2d75 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming 6. Click the `Flash` button (if disabled, click the `Connect` button again) 7. Press `Reset` button on your board to run the MicroPython firmware - > [!NOTE] - > Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) +> Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest)
@@ -43,6 +42,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming 2. Click **Add device** -> **Find devices nearby** 3. Select your device and follow the setup instructions +> [!NOTE] > If you have already created your device in Blynk, > you can [connect it manually using REPL](_extra/Cookbook.md#manual-device-connection) From 446d53c80a118fc21c439ddc9f75656b16511801 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 15:49:06 +0300 Subject: [PATCH 37/59] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6e2d75..2f30173 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ## 1. Install our `MicroPython` build
- See ESP32, ESP32-S3, ESP32-C3 instructions + See instructions for ESP32, ESP32-S3, ESP32-C3 based devices 1. Open [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) 2. Plug your board into a USB port @@ -28,7 +28,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming
- See Raspberry Pi Pico W instructions + See instructions for Raspberry Pi Pico W 1. Hold down the `BOOTSEL` button while plugging the board into a USB port 2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears From 83bccef43cdc255c25680168e54f9d5f9ccbc82c Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 15:51:50 +0300 Subject: [PATCH 38/59] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f30173..6abdcd2 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ from asyncio import sleep_ms async def publisher_task(): while True: await sleep_ms(1000) - edgent.updateDataStream("Uptime", ticks_ms()) + edgent.publish("Uptime", ticks_ms()) edgent.run_asyncio_loop([ publisher_task() From 56899d7f5cba70d564e83815622f5fe8cb3c83a9 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 16:08:23 +0300 Subject: [PATCH 39/59] Update README.md --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6abdcd2..4d24540 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,14 @@ and additional features such as **secure Blynk.Cloud connection, device claiming
See instructions for ESP32, ESP32-S3, ESP32-C3 based devices -1. Open [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) -2. Plug your board into a USB port -3. Click `Connect` in upper right corner and select your board -4. Select Application (generic boards vs specialized builds) -5. Select your Develop Kit variant -6. Click the `Flash` button (if disabled, click the `Connect` button again) -7. Press `Reset` button on your board to run the MicroPython firmware +You can use [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) to flash your device + +1. Plug your board into a USB port +2. Click `Connect` in upper right corner and select your board +3. Select **Application** (generic boards vs specialized builds) +4. Select **Develop Kit** variant based on flash size and type +5. Click the `Flash` button (if disabled, try clicking the `Connect` button again) +6. Press `Reset` button on your board to run the MicroPython firmware > Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) From 205a2aedf14b3020ab503f2c91b459c5eb055a77 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 16:08:42 +0300 Subject: [PATCH 40/59] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d24540..22d01ed 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming
See instructions for ESP32, ESP32-S3, ESP32-C3 based devices - + You can use [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) to flash your device 1. Plug your board into a USB port @@ -30,7 +30,7 @@ You can use [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashCon
See instructions for Raspberry Pi Pico W - + 1. Hold down the `BOOTSEL` button while plugging the board into a USB port 2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use From 6bfa12ae796b8995a2e0e3565f1282a426f0bd97 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 16:09:19 +0300 Subject: [PATCH 41/59] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 22d01ed..0ef1431 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming
See instructions for ESP32, ESP32-S3, ESP32-C3 based devices - + You can use [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) to flash your device 1. Plug your board into a USB port @@ -30,7 +30,7 @@ You can use [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashCon
See instructions for Raspberry Pi Pico W - + 1. Hold down the `BOOTSEL` button while plugging the board into a USB port 2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use From 678a331119353a680d76bff1a5f20df054378bf1 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 16:10:53 +0300 Subject: [PATCH 42/59] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0ef1431..fc635a1 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ## 1. Install our `MicroPython` build
- See instructions for ESP32, ESP32-S3, ESP32-C3 based devices - -You can use [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) (you will need a Chrome-based browser) to flash your device + See instructions for ESP32, ESP32-S3, ESP32-C3 based devices
+ +You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) to flash your device. You will need a Chrome-based browser. 1. Plug your board into a USB port 2. Click `Connect` in upper right corner and select your board @@ -29,8 +29,8 @@ You can use [ESP Launchpad](https://espressif.github.io/esp-launchpad/?flashCon
- See instructions for Raspberry Pi Pico W - + See instructions for Raspberry Pi Pico W
+ 1. Hold down the `BOOTSEL` button while plugging the board into a USB port 2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use From f05a8561ce965047e07fd060d11d7c20f4393fcc Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 16:12:38 +0300 Subject: [PATCH 43/59] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc635a1..c8e778a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flash See instructions for Raspberry Pi Pico W
1. Hold down the `BOOTSEL` button while plugging the board into a USB port -2. Copy the latest `UF2 firmware file` to the USB mass storage device that appears +2. Copy the latest [`UF2 firmware file`](https://vsh.pp.ua/Blynk-MicroPython-Edgent/RPI_PICO_W.uf2) to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use
From c43b040c0ce54bc1f422fda0b4dace06a0bfde06 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 16:16:06 +0300 Subject: [PATCH 44/59] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c8e778a..b6d05dc 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ## Getting Started - Sign up/Log in to your [Blynk Account](https://blynk.cloud) -- Install **Blynk IoT App** for iOS or Android +- Install **Blynk IoT App** for [iOS](https://apps.apple.com/us/app/blynk-iot/id1559317868) or [Android](https://play.google.com/store/apps/details?id=cloud.blynk) -## 1. Install our `MicroPython` build +## 1. Install `MicroPython` + `Blynk.Edgent`
See instructions for ESP32, ESP32-S3, ESP32-C3 based devices
@@ -37,7 +37,7 @@ You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flash
-## 2. Connect your device to Blynk.Cloud +## 2. Connect your device to `Blynk.Cloud` 1. Open **Blynk IoT App** on your smartphone 2. Click **Add device** -> **Find devices nearby** From 97f124712e9181f909cfbd39f4af973cf3c38548 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 16:16:29 +0300 Subject: [PATCH 45/59] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6d05dc..c8aedb0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming - Sign up/Log in to your [Blynk Account](https://blynk.cloud) - Install **Blynk IoT App** for [iOS](https://apps.apple.com/us/app/blynk-iot/id1559317868) or [Android](https://play.google.com/store/apps/details?id=cloud.blynk) -## 1. Install `MicroPython` + `Blynk.Edgent` +## 1. Install MicroPython + Blynk.Edgent
See instructions for ESP32, ESP32-S3, ESP32-C3 based devices
@@ -37,7 +37,7 @@ You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flash
-## 2. Connect your device to `Blynk.Cloud` +## 2. Connect your device to Blynk.Cloud 1. Open **Blynk IoT App** on your smartphone 2. Click **Add device** -> **Find devices nearby** From 8cf749c60500fdbbe29123e7e7060b9c3de9586a Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 26 Jul 2024 17:30:12 +0300 Subject: [PATCH 46/59] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8aedb0..5dcece4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming ## 1. Install MicroPython + Blynk.Edgent
- See instructions for ESP32, ESP32-S3, ESP32-C3 based devices
+ See instructions for ESP32 based devices
You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) to flash your device. You will need a Chrome-based browser. From de0c73e594c1a3c73d4e7004207d0d58cd3f6b14 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Sun, 28 Jul 2024 12:36:00 +0300 Subject: [PATCH 47/59] Cleanup --- _extra/tools/blynk_tag.py | 108 -------------------------------------- _extra/tools/mpota.py | 6 ++- 2 files changed, 4 insertions(+), 110 deletions(-) delete mode 100755 _extra/tools/blynk_tag.py diff --git a/_extra/tools/blynk_tag.py b/_extra/tools/blynk_tag.py deleted file mode 100755 index 852d50f..0000000 --- a/_extra/tools/blynk_tag.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python3 - -""" - Extract the binary tag from the firmware binary: - python3 blynk-tag.py extract ./raw_firmware.bin ./tag.bin - - Create a new binary tag manually: - python3 blynk-tag.py create ./tag.bin --mcu 0.1.2 --type TMPL0123456 --build "Apr 19 2023 12:01:07" --blynk "0.3.0" - - Show info in a tag or a firmware: - python3 blynk-tag.py show ./tag.bin - python3 blynk-tag.py show ./raw_firmware.bin - -""" -import re - - -def create_tag(taginfo): - taginfo = map(lambda x: x.encode("utf-8"), taginfo) - return b"\0".join(taginfo) + b"\0\0" - - -def find_tag(data): - r = re.compile(b"blnkinf\\x00[\\w\\s\\.,:\\-\\(\\)\\x00]*?\\x00\\x00") - match = r.search(data) - if match is not None: - return match[0] - return None - - -def parse_tag(tag): - def pairwise(t): - it = iter(t) - return zip(it, it) - - taginfo = tag.split(b"\0") - taginfo = list(map(lambda x: x.decode("utf-8"), taginfo)) - return list(pairwise(taginfo[1:-2])) - - -if __name__ == "__main__": - import sys - import argparse - - def run_extract(args): - with open(args.file_in, "rb") as f: - data = f.read() - tag = find_tag(data) - if tag is None: - print("Blynk info tag not found", file=sys.stderr) - sys.exit(1) - with open(args.file_out, "wb") as f: - f.write(tag) - - def run_show(args): - with open(args.file_in, "rb") as f: - data = f.read() - tag = find_tag(data) - if tag is None: - print("Blynk info tag not found", file=sys.stderr) - sys.exit(1) - - for n, v in parse_tag(tag): - print(f"{n}: {v}") - - def run_create(args): - taginfo = ["blnkinf"] - taginfo.extend(["mcu", args.mcu]) - taginfo.extend(["fw-type", args.type]) - if args.build: - if args.build == "now": - import datetime - - dt = datetime.datetime.now(datetime.timezone.utc) - taginfo.extend(["build", dt.strftime("%b %d %Y %H:%M:%S")]) - else: - taginfo.extend(["build", args.build]) - if args.blynk: - taginfo.extend(["blynk", args.blynk]) - - tag = create_tag(taginfo) - with open(args.file_out, "wb") as f: - f.write(tag) - - parser = argparse.ArgumentParser(description="Blynk firmware tag utility") - subparsers = parser.add_subparsers() - create = subparsers.add_parser("create") - create.add_argument("--mcu", help="Version of the MCU firmware", required=True) - create.add_argument("--type", help="Firmware type (usually same as TemplateID)", required=True) - create.add_argument("--build", help="Firmware build date and time") - create.add_argument("--blynk", help="Blynk library version") - create.add_argument("file_out", metavar="FILE_OUT", help="output file") - create.set_defaults(func=run_create) - - extract = subparsers.add_parser("extract") - extract.add_argument("file_in", metavar="FILE_IN", help="input file") - extract.add_argument("file_out", metavar="FILE_OUT", help="output file") - extract.set_defaults(func=run_extract) - - show = subparsers.add_parser("show") - show.add_argument("file_in", metavar="FILE_IN", help="input file") - show.set_defaults(func=run_show) - - args = parser.parse_args() - if "func" in args: - args.func(args) - else: - parser.print_usage() diff --git a/_extra/tools/mpota.py b/_extra/tools/mpota.py index cc66621..7c7cec7 100755 --- a/_extra/tools/mpota.py +++ b/_extra/tools/mpota.py @@ -13,7 +13,6 @@ import json import tarfile import datetime -import blynk_tag import shutil from subprocess import Popen, PIPE, DEVNULL @@ -54,6 +53,9 @@ def minify(source, filename): preserve_shebang=False, ) +def create_tag(taginfo): + taginfo = map(lambda x: x.encode("utf-8"), taginfo) + return b"\0".join(taginfo) + b"\0\0" def create_tagged_tar(input_files, output_tar_file): @@ -78,7 +80,7 @@ def create_tagged_tar(input_files, output_tar_file): taginfo.extend(["mcu", fw_ver]) taginfo.extend(["fw-type", fw_type]) taginfo.extend(["build", now.strftime("%b %d %Y %H:%M:%S")]) - tag = blynk_tag.create_tag(taginfo) + tag = create_tag(taginfo) # Create a tar.gz file with tarfile.open(output_tar_file, "w:gz", format=tarfile.GNU_FORMAT) as tar: From 5706156f17069b9da5e246fbde5a026f53a8653c Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Mon, 29 Jul 2024 12:01:46 +0300 Subject: [PATCH 48/59] Cleanup --- _extra/Cookbook.md | 38 ++++++++++++++++++++++++++------------ cert/ca-bundle.pem | 18 ++++++++++++++++++ cfg/sys.json | 5 +++-- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index f728dc3..5bb8bd8 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -49,7 +49,28 @@ del sysconfig["nets"][2] sysconfig.commit() ``` -## Watchdog Timer +## Add diagnostics + +```py +import netmgr +import micropython +import gc + +async def diagnostics_task(): + while True: + mem_prev = gc.mem_free() + gc.collect() + mem_free = gc.mem_free() + gc.threshold(mem_free // 4 + gc.mem_alloc()) + edgent.publish("ds/Heap Free", mem_free / 1024) + edgent.publish("ds/GC Collect", (mem_free - mem_prev) / 1024) + edgent.publish("ds/WiFi RSSI", netmgr.sta.status("rssi")) + await asyncio.sleep(60) +``` + +Also, add `diagnostics_task()` to `edgent.run_asyncio_loop`. + +## Watchdog Timer (WDT) The watchdog is typically disabled by default, as it can complicate prototyping. It is recommended to enable it at later stages of development: @@ -57,7 +78,7 @@ It is recommended to enable it at later stages of development: ```py sysconfig["wdt"]["enabled"] = True sysconfig.commit() -machine.reset() # Chnaging this setting requires a hard reset +machine.reset() # Changing this setting requires a hard reset ``` ## Format internal FS @@ -65,20 +86,13 @@ machine.reset() # Chnaging this setting requires a hard reset > [!WARNING] > This performs a factory reset, the internal file system will recover to it's initial state -Use one of these commands depending on your actual hardware: ```py -# ESP32 -import os, flashbdev; os.VfsLfs2.mkfs(flashbdev.bdev) - -# RP2040 -import vfs, rp2; vfs.VfsLfs2.mkfs(rp2.Flash(), progsize=256) - -# WM W600 -import vfs, w600; vfs.VfsLfs2.mkfs(w600.Flash(), progsize=256) +edgent.factory_reset() ``` ## Update MicroPython firmware directly from GitHub (ESP32 only) ```py -blynk.air.start_ota_update("https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240222-v1.22.2.app-bin", validate=False) +from blynk import air +air.start_ota_update("https://micropython.org/resources/firmware/ESP32_GENERIC-SPIRAM-20240222-v1.22.2.app-bin", validate=False) ``` diff --git a/cert/ca-bundle.pem b/cert/ca-bundle.pem index 4fe3b64..f31d1c0 100644 --- a/cert/ca-bundle.pem +++ b/cert/ca-bundle.pem @@ -77,3 +77,21 @@ Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl MrY= -----END CERTIFICATE----- + +EC 384 bits, USERTrust ECC, expires: Mon, 18 Jan 2038, 11:59:59 GMT +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- diff --git a/cfg/sys.json b/cfg/sys.json index 07853c3..fe342ae 100644 --- a/cfg/sys.json +++ b/cfg/sys.json @@ -1,8 +1,9 @@ { "blynk": { - "tmpl_id": "DYNAMIC", + "tmpl_id": "DYNAMIC", "tmpl_name": "Device", - "vendor": "Blynk" + "auth": null, + "server": "blynk.cloud" }, "wdt": { "enabled": false From f5018e20ad4fe9289991ad08e0d7f05abe826540 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Mon, 29 Jul 2024 14:18:15 +0300 Subject: [PATCH 49/59] Cleanup --- _extra/Cookbook.md | 6 +++++- cfg/sys.json | 2 +- main.py | 40 ++++++++++++++++++++-------------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index 5bb8bd8..457a7ec 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -13,12 +13,16 @@ sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassw sysconfig["blynk"].update({ "tmpl_id": "TMPxxxxxxxxxx", "tmpl_name": "Device", - "auth": "rn60Wx*******", + "auth": "rn60Wxxxxxxxxxxxxxxxxxxxxxxxxxxx", "server": "blynk.cloud", }) # Save system configuration sysconfig.commit() + +# Restart +import machine +machine.soft_reset() ``` > [!NOTE] diff --git a/cfg/sys.json b/cfg/sys.json index fe342ae..6bc7a7a 100644 --- a/cfg/sys.json +++ b/cfg/sys.json @@ -10,6 +10,6 @@ }, "log": { "color": true, - "level": "debug" + "level": "info" } } diff --git a/main.py b/main.py index b3030cb..0636ec0 100644 --- a/main.py +++ b/main.py @@ -1,41 +1,41 @@ -# This example bla bla bla -# Read detailed explanations here: -# https://docs.blynk.io/bla-bla-bla +# SPDX-FileCopyrightText: 2024 Volodymyr Shymanskyy for Blynk Technologies Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# This example ... TODO +# Read more: https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent +# +# The software is provided "as is", without any warranties or guarantees (explicit or implied). +# This includes no assurances about being fit for any specific purpose. from blynk import edgent from asyncio import sleep_ms -import logging, board +import logging +import board log = logging.getLogger("app") """ Register event handlers """ -@edgent.on(edgent.CONNECTED) -def connected_handler(): - log.info("MQTT connected") +def connection_handler(): + log.info("Blynk connected") -@edgent.on(edgent.DISCONNECTED) -def disconnected_handler(): - log.info("MQTT disconnected") +def disconnection_handler(): + log.info("Blynk disconnected") -@edgent.on_message("ds/LED") -def led_handler(data): - if hasattr(board, "LED"): - board.LED.write(int(data)) - else: - log.info("Your LED is %s", data) - -@edgent.on_message() # Handle all other messages -def other_handler(topic, payload): +def data_callback(topic, payload): log.info("Got: %s, value: %s", topic, payload) +edgent.on_connected = connected +edgent.on_disconnected = disconnected +edgent.on_message = data_callback + """ Define asyncio tasks """ async def publisher_task(): counter = 0 while True: await sleep_ms(1000) - edgent.updateDataStream("Counter", counter) + edgent.publish("Counter", counter) counter += 1 """ Run the default asyncio loop """ From 5c2c7d4826620d52b9513ad50fb0c99586bcbf00 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Tue, 30 Jul 2024 14:43:24 +0300 Subject: [PATCH 50/59] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dcece4..b7dde18 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ and additional features such as **secure Blynk.Cloud connection, device claiming
See instructions for ESP32 based devices
-You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://vsh.pp.ua/Blynk-MicroPython-Edgent/esp-quickstart.toml) to flash your device. You will need a Chrome-based browser. +You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://blynk-fw-builds.fra1.cdn.digitaloceanspaces.com/Blynk-Edgent-MicroPython/latest/esp-quickstart.toml) to flash your device. You will need a Chrome-based browser. 1. Plug your board into a USB port 2. Click `Connect` in upper right corner and select your board From 0a7c84d6d11118d7c34943edc7720059c9a86dc4 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Tue, 30 Jul 2024 15:48:24 +0300 Subject: [PATCH 51/59] Fix main.py --- README.md | 2 +- main.py | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b7dde18..cd23cb2 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flash See instructions for Raspberry Pi Pico W
1. Hold down the `BOOTSEL` button while plugging the board into a USB port -2. Copy the latest [`UF2 firmware file`](https://vsh.pp.ua/Blynk-MicroPython-Edgent/RPI_PICO_W.uf2) to the USB mass storage device that appears +2. Copy the latest [`UF2 firmware file`](https://blynk-fw-builds.fra1.cdn.digitaloceanspaces.com/Blynk-Edgent-MicroPython/latest/RPI_PICO_W.uf2) to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use
diff --git a/main.py b/main.py index 0636ec0..e1496c7 100644 --- a/main.py +++ b/main.py @@ -9,10 +9,8 @@ from blynk import edgent from asyncio import sleep_ms -import logging -import board -log = logging.getLogger("app") +log = edgent.logging.getLogger("app") """ Register event handlers """ @@ -22,12 +20,12 @@ def connection_handler(): def disconnection_handler(): log.info("Blynk disconnected") -def data_callback(topic, payload): +def data_handler(topic, payload): log.info("Got: %s, value: %s", topic, payload) -edgent.on_connected = connected -edgent.on_disconnected = disconnected -edgent.on_message = data_callback +edgent.on_connected = connection_handler +edgent.on_disconnected = disconnection_handler +edgent.on_message = data_handler """ Define asyncio tasks """ From cde854232ae3c25ca2bfa7379b49643f36de366b Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 1 Aug 2024 13:56:39 +0300 Subject: [PATCH 52/59] Update mpota script, combine it with mkrecovery --- _extra/Cookbook.md | 2 +- _extra/Workflow-CLI.md | 2 +- _extra/tools/mpota.py | 132 +++++++++++++++++++++++++++++------------ 3 files changed, 97 insertions(+), 39 deletions(-) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index 457a7ec..04f91db 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -22,7 +22,7 @@ sysconfig.commit() # Restart import machine -machine.soft_reset() +machine.reset() ``` > [!NOTE] diff --git a/_extra/Workflow-CLI.md b/_extra/Workflow-CLI.md index 7cb55cc..893b65d 100644 --- a/_extra/Workflow-CLI.md +++ b/_extra/Workflow-CLI.md @@ -45,6 +45,6 @@ Firmware type should match the Blynk Template ID, unless: - You have multiple products that use a single firmware ```sh -python3 ./tools/mpota.py app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` +python3 _extra/tools/mpota.py -o app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` ``` diff --git a/_extra/tools/mpota.py b/_extra/tools/mpota.py index 7c7cec7..596ffdd 100755 --- a/_extra/tools/mpota.py +++ b/_extra/tools/mpota.py @@ -1,39 +1,33 @@ #!/usr/bin/env python3 -# -# Example usage: -# python3 ./tools/mpota.py app_ota.tar.gz main.py some_module.py data.txt -# -# 1. Creates Blynk firmware binary tag based on the configuration -# 2. Packages input files into a tar (or tar.gz) file that can be used for OTA updates -# - -import sys, os, io, time -import re +import sys +import os +import io import json import tarfile import datetime import shutil -from subprocess import Popen, PIPE, DEVNULL - -do_minify = False -do_compile = True +from subprocess import Popen, PIPE MPY_CROSS = shutil.which("mpy-cross") now = datetime.datetime.now(datetime.timezone.utc) +# +# Tools +# -def mpy_cross(source, filename): +def mpy_cross(source, options): + """ Compile MicroPython to bytecode """ if not MPY_CROSS: - raise RuntimeError(f"mpy-cross not found in PATH") - p = Popen([MPY_CROSS, "-s", filename, "-O3", "-"], stdin=PIPE, stdout=PIPE, stderr=PIPE) + raise RuntimeError("mpy-cross not found in PATH") + p = Popen([MPY_CROSS, *options, "-"], stdin=PIPE, stdout=PIPE, stderr=PIPE) out, err = p.communicate(input=source) if p.returncode != 0: raise RuntimeError(f"mpy-cross failed: {err.decode('utf-8')}") return out - def minify(source, filename): + """ Minify Python source """ import python_minifier return python_minifier.minify( @@ -54,11 +48,36 @@ def minify(source, filename): ) def create_tag(taginfo): + """ Create a Blynk OTA package binary tag """ taginfo = map(lambda x: x.encode("utf-8"), taginfo) return b"\0".join(taginfo) + b"\0\0" -def create_tagged_tar(input_files, output_tar_file): +def bytes2py(data): + """ Create a Python module with data """ + result = """ +# Generated module, DO NOT EDIT + +import io +def data(): + return io.BytesIO( +""" + offset = 0 + while offset < len(data): + line = "" + for size in range(1, 128): + line = f" {data[offset:offset+size]!r}" + if len(line) > 120: + break + offset += size + result += line + "\n" + result += " )\n" + return result + +# +# Package creation +# +def create_tagged_tar(args): fw_ver = None fw_type = None @@ -67,7 +86,7 @@ def create_tagged_tar(input_files, output_tar_file): fwconfig = json.load(f) fw_ver = fwconfig.get("ver") fw_type = fwconfig.get("type") - except: + except Exception: pass if not fw_ver: @@ -82,8 +101,9 @@ def create_tagged_tar(input_files, output_tar_file): taginfo.extend(["build", now.strftime("%b %d %Y %H:%M:%S")]) tag = create_tag(taginfo) - # Create a tar.gz file - with tarfile.open(output_tar_file, "w:gz", format=tarfile.GNU_FORMAT) as tar: + # Create a package file + tar_fileobj = io.BytesIO() + with tarfile.open(fileobj=tar_fileobj, mode="w:gz", format=tarfile.GNU_FORMAT) as tar: # Add firmware version info with io.BytesIO(tag) as f: ti = tarfile.TarInfo(name="fw_info.bin") @@ -92,18 +112,21 @@ def create_tagged_tar(input_files, output_tar_file): tar.addfile(tarinfo=ti, fileobj=f) # Add the original files to the tar as raw data - for fn in input_files: + for fn in args.files: try: with open(fn, "rb") as f: data = f.read() if fn.endswith(".py"): base_fn = os.path.basename(fn) - if do_minify: + if args.minify: data = minify(data, base_fn).encode("utf-8") - if do_compile and base_fn not in ("main.py"): - data = mpy_cross(data, base_fn) + if args.compile and base_fn not in ("main.py"): + options = [ "-s", base_fn ] + if args.march: + options.append("-march=" + args.march) + data = mpy_cross(data, options) fn = fn.replace(".py", ".mpy") ti = tarfile.TarInfo(name=fn) @@ -113,18 +136,53 @@ def create_tagged_tar(input_files, output_tar_file): print(f"{fn:30} [{len(data)}]") except Exception as e: - print(f"Error processing {fn}: ", e) - os.unlink(output_tar_file) + print(f"ERROR: cannot process {fn}: ", e) sys.exit(1) - if any(item.startswith("cfg/") for item in input_files): - # Express concerns - print() - print(" = WARNING: System config (cfg/sys.json) should NOT be updated using OTA =") - print() + return tar_fileobj.getvalue() + +def main(input_args=None): + import argparse + + parser = argparse.ArgumentParser( + description="Blynk MPOTA Utility", + formatter_class=argparse.RawTextHelpFormatter, + epilog="""\ +Create App OTA package: + mpota.py -o app_ota.tar.gz main.py cert/ca-bundle.pem +Create a Factory/Recovery module: + mpota.py --factory -o _factory.py main.py cert/ca-bundle.pem cfg/sys.json +""") + + parser.add_argument("files", nargs="+", help="Input files") + parser.add_argument("--output", "-o", required=True, help="The output package file") + parser.add_argument("--factory", action="store_true", help="Generate factory app image") + parser.add_argument("--minify", action="store_true", help="Minify the files") + parser.add_argument("--compile", "-c", action="store_true", help="Compile the files") + parser.add_argument("-march", type=str, help="Specify the architecture for compilation") + + args = parser.parse_args(input_args) + if args.factory: + data = create_tagged_tar(args) + if args.output.endswith(".tar.gz"): + pass + elif args.output.endswith(".py"): + data = bytes2py(data).encode() + elif args.output.endswith(".mpy"): + data = bytes2py(data).encode() + data = mpy_cross(data, ["-O3", "-s", "raw"]) + else: + raise RuntimeError(f"Output format not supported: {args.output}") + + with open(args.output, "wb") as f: + f.write(data) + else: + if any(item.endswith("cfg/sys.json") for item in args.files): + print("ERROR: System config (cfg/sys.json) should NOT be updated using OTA") + sys.exit(1) + data = create_tagged_tar(args) + with open(args.output, "wb") as f: + f.write(data) if __name__ == "__main__": - if len(sys.argv) < 3: - print("Usage: python script.py output_tar_file input_file1 [input_file2 ...]") - - create_tagged_tar(sys.argv[2:], sys.argv[1]) + main() From 3eeaee7e73ff5325d12b3093d23a746695a7929f Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 1 Aug 2024 13:59:04 +0300 Subject: [PATCH 53/59] Cleanup --- _extra/tools/mpota.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_extra/tools/mpota.py b/_extra/tools/mpota.py index 596ffdd..72a1962 100755 --- a/_extra/tools/mpota.py +++ b/_extra/tools/mpota.py @@ -150,7 +150,7 @@ def main(input_args=None): epilog="""\ Create App OTA package: mpota.py -o app_ota.tar.gz main.py cert/ca-bundle.pem -Create a Factory/Recovery module: +Create a Factory/Recovery module (can output .py .mpy or .tar.gz): mpota.py --factory -o _factory.py main.py cert/ca-bundle.pem cfg/sys.json """) From d002d8b9349aec93bc3e680e8c3fbfdb3e343486 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 1 Aug 2024 16:23:58 +0300 Subject: [PATCH 54/59] Report package size --- _extra/tools/mpota.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/_extra/tools/mpota.py b/_extra/tools/mpota.py index 72a1962..3c38714 100755 --- a/_extra/tools/mpota.py +++ b/_extra/tools/mpota.py @@ -134,7 +134,7 @@ def create_tagged_tar(args): ti.mtime = now.timestamp() tar.addfile(tarinfo=ti, fileobj=io.BytesIO(data)) - print(f"{fn:30} [{len(data)}]") + print(f"{fn:48} {len(data)}") except Exception as e: print(f"ERROR: cannot process {fn}: ", e) sys.exit(1) @@ -161,16 +161,21 @@ def main(input_args=None): parser.add_argument("--compile", "-c", action="store_true", help="Compile the files") parser.add_argument("-march", type=str, help="Specify the architecture for compilation") + def report_result(size, msg="Package size"): + print("=== %s: %d bytes ===" % (msg, size)) + args = parser.parse_args(input_args) if args.factory: data = create_tagged_tar(args) if args.output.endswith(".tar.gz"): pass elif args.output.endswith(".py"): + report_result(len(data) + 66, msg="Approx. compiled package size") data = bytes2py(data).encode() elif args.output.endswith(".mpy"): data = bytes2py(data).encode() data = mpy_cross(data, ["-O3", "-s", "raw"]) + report_result(len(data)) else: raise RuntimeError(f"Output format not supported: {args.output}") @@ -183,6 +188,7 @@ def main(input_args=None): data = create_tagged_tar(args) with open(args.output, "wb") as f: f.write(data) + report_result(len(data)) if __name__ == "__main__": main() From e87074c365064e5cdcd00a0b9b44bce19e40b90a Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 1 Aug 2024 18:10:20 +0300 Subject: [PATCH 55/59] Cleanup --- _extra/Workflow-CLI.md | 2 +- _extra/tools/{mpota.py => upy-pack.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename _extra/tools/{mpota.py => upy-pack.py} (100%) diff --git a/_extra/Workflow-CLI.md b/_extra/Workflow-CLI.md index 893b65d..48247f6 100644 --- a/_extra/Workflow-CLI.md +++ b/_extra/Workflow-CLI.md @@ -45,6 +45,6 @@ Firmware type should match the Blynk Template ID, unless: - You have multiple products that use a single firmware ```sh -python3 _extra/tools/mpota.py -o app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` +python3 _extra/tools/upy-pack.py -o app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` ``` diff --git a/_extra/tools/mpota.py b/_extra/tools/upy-pack.py similarity index 100% rename from _extra/tools/mpota.py rename to _extra/tools/upy-pack.py From 530a9fa16a9e05be1552a5cba80ca3132fdcd590 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Thu, 1 Aug 2024 18:32:53 +0300 Subject: [PATCH 56/59] Cleanup --- _extra/tools/upy-pack.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_extra/tools/upy-pack.py b/_extra/tools/upy-pack.py index 3c38714..f73f2c1 100755 --- a/_extra/tools/upy-pack.py +++ b/_extra/tools/upy-pack.py @@ -145,13 +145,13 @@ def main(input_args=None): import argparse parser = argparse.ArgumentParser( - description="Blynk MPOTA Utility", + description="Blynk uPy Pack Utility", formatter_class=argparse.RawTextHelpFormatter, epilog="""\ Create App OTA package: - mpota.py -o app_ota.tar.gz main.py cert/ca-bundle.pem + upy-pack.py -o app_ota.tar.gz main.py cert/ca-bundle.pem Create a Factory/Recovery module (can output .py .mpy or .tar.gz): - mpota.py --factory -o _factory.py main.py cert/ca-bundle.pem cfg/sys.json + upy-pack.py --factory -o _fs_img.py main.py cert/ca-bundle.pem cfg/sys.json """) parser.add_argument("files", nargs="+", help="Input files") From 14fe8f1f95f39a3dd21f9622c5684539f4a60d8d Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Mon, 12 Aug 2024 14:54:39 +0300 Subject: [PATCH 57/59] Update README.md --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cd23cb2..0987641 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,12 @@ and additional features such as **secure Blynk.Cloud connection, device claiming You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://blynk-fw-builds.fra1.cdn.digitaloceanspaces.com/Blynk-Edgent-MicroPython/latest/esp-quickstart.toml) to flash your device. You will need a Chrome-based browser. 1. Plug your board into a USB port -2. Click `Connect` in upper right corner and select your board -3. Select **Application** (generic boards vs specialized builds) -4. Select **Develop Kit** variant based on flash size and type -5. Click the `Flash` button (if disabled, try clicking the `Connect` button again) -6. Press `Reset` button on your board to run the MicroPython firmware +2. Click Connect in upper right corner and select your board + - Recommended: click Erase Flash on the **DIY** tab +4. Select **Application** (generic boards vs specialized builds) +5. Select **Develop Kit** variant based on flash size and type +6. Click the Flash button (if disabled, try clicking the `Connect` button again) +7. Press Reset button on your board to run the MicroPython firmware > Alternatively, you can [flash your ESP32 device manually](https://github.com/Blynk-Technologies/Blynk-MicroPython-Edgent/releases/latest) @@ -81,6 +82,8 @@ There are many ways to program your device. Here, we'll guide you through the tw - `netmgr` - Network management for `WiFi`, `Ethernet` and `Cellular` - `config` - System-wide configuration - `aiontp` - A versatile asyncio-based version of NTP client +- `aioinput` - An asyncio variant of `input` function +- `aioprof` - Asyncio [profiling tool](https://gitlab.com/alelec/aioprof) - `logging` - System-wide, preconfigured logging - `board` - A unified way to access the board peripherals - Factory reset function From 2be58bb3aa6d9dc20149a1de38c9cf5ad4f62351 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Tue, 20 Aug 2024 12:52:17 +0300 Subject: [PATCH 58/59] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0987641..dda5fee 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ You can use [**ESP Launchpad**](https://espressif.github.io/esp-launchpad/?flash
See instructions for Raspberry Pi Pico W
-1. Hold down the `BOOTSEL` button while plugging the board into a USB port -2. Copy the latest [`UF2 firmware file`](https://blynk-fw-builds.fra1.cdn.digitaloceanspaces.com/Blynk-Edgent-MicroPython/latest/RPI_PICO_W.uf2) to the USB mass storage device that appears +1. Hold down the BOOTSEL button while plugging the board into a USB port +2. Copy the latest [UF2 firmware file](https://blynk-fw-builds.fra1.cdn.digitaloceanspaces.com/Blynk-Edgent-MicroPython/latest/RPI_PICO_W.uf2) to the USB mass storage device that appears 3. Once programming of the new firmware is complete, the device will automatically reset and be ready for use
From d17cccf2452d6f47abc27af0a565377d93c5d7b6 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Tue, 20 Aug 2024 12:53:06 +0300 Subject: [PATCH 59/59] Update docs --- _extra/Cookbook.md | 4 ++++ _extra/Workflow-CLI.md | 6 ++++++ _extra/Workflow-ViperIDE.md | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/_extra/Cookbook.md b/_extra/Cookbook.md index 04f91db..f5212c5 100644 --- a/_extra/Cookbook.md +++ b/_extra/Cookbook.md @@ -49,6 +49,9 @@ sysconfig["nets"].append({ "type": "wlan", "ssid": "YourSSID", "psk": "YourPassw # Remove network by index (0-based) del sysconfig["nets"][2] +# Remove all networks +sysconfig['nets'].clear() + # Save system configuration sysconfig.commit() ``` @@ -91,6 +94,7 @@ machine.reset() # Changing this setting requires a hard reset > This performs a factory reset, the internal file system will recover to it's initial state ```py +from blynk import edgent edgent.factory_reset() ``` diff --git a/_extra/Workflow-CLI.md b/_extra/Workflow-CLI.md index 48247f6..15c8dd4 100644 --- a/_extra/Workflow-CLI.md +++ b/_extra/Workflow-CLI.md @@ -48,3 +48,9 @@ Firmware type should match the Blynk Template ID, unless: python3 _extra/tools/upy-pack.py -o app_ota.tar.gz main.py cert/ca-bundle.pem `find ./lib -name '*.py'` ``` +--- + +# Further reading + +- [Cookbook](Cookbook.md) + diff --git a/_extra/Workflow-ViperIDE.md b/_extra/Workflow-ViperIDE.md index 6921cea..965598d 100644 --- a/_extra/Workflow-ViperIDE.md +++ b/_extra/Workflow-ViperIDE.md @@ -1,2 +1,9 @@ TODO + +--- + +# Further reading + +- [Cookbook](Cookbook.md) +