Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/posix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Every feature the port has
- name: Every feature the port has, and the provisioning library
run: |
cmake -S ports/posix -B build -DTEDGE_WERROR=ON
cmake -S ports/posix -B build -DTEDGE_WERROR=ON -DTEDGE_PROVISIONING=ON
cmake --build build -j4
ctest --test-dir build --output-on-failure

Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,46 @@ jobs:
release-please:
name: Release PR and tag
runs-on: ubuntu-24.04
outputs:
released: ${{ steps.release.outputs.release_created }}
tag: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v5
id: release
with:
token: ${{ secrets.COMMUNITY_ACTIONS_PAT }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

# tedge-onboard's binaries, attached to the release just published.
onboard-tool:
name: tedge-onboard for ${{ matrix.name }}
needs: release-please
if: needs.release-please.outputs.released == 'true'
runs-on: ${{ matrix.os }}
permissions:
contents: write
strategy:
matrix:
include:
- name: Linux and Windows
os: ubuntu-latest
targets: linux/amd64 linux/arm64 windows/amd64 windows/arm64
- name: macOS
os: macos-latest
targets: darwin/arm64 darwin/amd64
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release-please.outputs.tag }}
- uses: actions/setup-go@v5
with:
go-version-file: tools/onboard/go.mod
cache-dependency-path: tools/onboard/go.sum
- name: Build and attach
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release-please.outputs.tag }}
run: |
tools/onboard/build.sh "${TAG#v}" ${{ matrix.targets }}
gh release upload "$TAG" tools/onboard/dist/* --clobber
52 changes: 52 additions & 0 deletions .github/workflows/tools.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# SPDX-License-Identifier: Apache-2.0
#
# The host tools: tedge-onboard's checks and its builds for every platform.
name: tools

on:
pull_request:
paths: ["tools/**", ".github/workflows/tools.yml"]
push:
branches: [main]
paths: ["tools/**", ".github/workflows/tools.yml"]

permissions:
contents: read

jobs:
test:
name: tedge-onboard checks
runs-on: ubuntu-latest
defaults:
run:
working-directory: tools/onboard
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: tools/onboard/go.mod
cache-dependency-path: tools/onboard/go.sum
- name: Formatting
run: test -z "$(gofmt -l .)" || { gofmt -l .; exit 1; }
- run: go vet ./...
- run: go test ./...

build:
name: tedge-onboard on ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- name: Linux and Windows
os: ubuntu-latest
targets: linux/amd64 linux/arm64 windows/amd64 windows/arm64
- name: macOS
os: macos-latest
targets: darwin/arm64 darwin/amd64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: tools/onboard/go.mod
cache-dependency-path: tools/onboard/go.sum
- run: tools/onboard/build.sh "0.0.0-${GITHUB_SHA::7}" ${{ matrix.targets }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ build*.log
# /zephyr/: that directory holds this repository's module.yml.
/modules/
/bootloader/
/tools/
/tools/*
!/tools/onboard/
/.west/

# Local overlays and credentials: never commit secrets.
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ tiny-edge/
│ ├── zephyr/ build glue, Kconfig, profiles, samples, tests, and the
│ │ Zephyr-only sources
│ └── posix/ CMake library, options, sample, and tests
├── provisioning/ optional library: set a device up over Improv Wi-Fi
│ (include/tedge/provisioning.h)
├── tools/onboard/ tedge-onboard: set a device up from a computer
├── certs/ default trust anchors
├── cumulocity/ tenant setup: Smart Functions and DTM definitions
├── openspec/ specifications and change history
Expand Down
62 changes: 55 additions & 7 deletions core/src/tedge_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,33 @@ int tedge_set_c8y_url(const char *host)
return tedge_c8y_host_set(host);
}

int tedge_get_c8y_url(char *buf, size_t len)
{
const char *host = tedge_c8y_host();
size_t n = strlen(host);

if (n >= len) {
return -ENOSPC;
}
memcpy(buf, host, n + 1);
return (int)n;
}

int tedge_get_device_id(char *buf, size_t len)
{
size_t n;

if (!initialised) {
return -EPERM;
}
n = strlen(identity.external_id);
if (n >= len) {
return -ENOSPC;
}
memcpy(buf, identity.external_id, n + 1);
return (int)n;
}

static void load_c8y_host(void)
{
int n = tedge_port_kv_get(TEDGE_KEY_C8Y_URL, c8y_host,
Expand Down Expand Up @@ -330,6 +357,9 @@ static void client_thread(void *arg)

tedge_set_state(TEDGE_STATE_CONNECTING);
rc = transport->connect();
if (rc == -ECANCELED) {
break; /* tedge_stop() during enrollment */
}
if (rc != 0) {
backoff = tedge_backoff_next(backoff,
TEDGE_RECONNECT_BACKOFF_MAX_S);
Expand Down Expand Up @@ -398,6 +428,7 @@ static void client_thread(void *arg)
TEDGE_PORT_STACK_DEFINE(client_stack, TEDGE_THREAD_STACK_SIZE);
static tedge_port_thread_t client_tid;
static bool running;
static bool started_once;

/* ------------------------------------------------------------------------ */
/* Public lifecycle */
Expand Down Expand Up @@ -461,18 +492,25 @@ int tedge_start(void)
return -EALREADY;
}
tedge_port_event_clear(&events, EV_STOP);
/* Once per boot: a client stopped and started again (to take new
* onboarding data, say) has declared all this already. */
if (!started_once) {
started_once = true;
#if defined(TEDGE_LOG_UPLOAD)
tedge_log_upload_init();
tedge_log_upload_init();
#endif
#if defined(TEDGE_PARAMETERS_SELF)
/* After the application's sets, so a declaration of its own wins a
* name clash, and before the sweep, which needs them all. */
tedge_self_params_declare();
/* After the application's sets, so a declaration of its own
* wins a name clash, and before the sweep, which needs them
* all. */
tedge_self_params_declare();
#endif
#if defined(TEDGE_PARAMETERS)
/* Every set the application means to declare is declared by now. */
tedge_params_on_start();
/* Every set the application means to declare is declared by
* now. */
tedge_params_on_start();
#endif
}
running = true;
(void)tedge_port_thread_start(&client_tid, client_stack,
TEDGE_PORT_STACK_SIZEOF(client_stack),
Expand All @@ -486,7 +524,17 @@ int tedge_stop(void)
return -EALREADY;
}
tedge_port_event_post(&events, EV_STOP | EV_WAKE);
(void)tedge_port_thread_join(&client_tid, 10000);
/* A thread still running after the wait (stuck in a network call
* longer than any of its timeouts) keeps the client "running": a
* start now would put a second thread on the same stack. */
if (tedge_port_thread_join(&client_tid, 10000) != 0) {
return -EBUSY;
}
running = false;
return 0;
}

bool tedge_wait_for_stop(uint32_t ms)
{
return (tedge_port_event_wait(&events, EV_STOP, ms) & EV_STOP) != 0;
}
5 changes: 4 additions & 1 deletion core/src/tedge_enroll.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,10 @@ int tedge_auth_prepare(char *id_out, size_t id_len)
if (ret == 0) {
break;
}
tedge_port_sleep_ms(wait_s * 1000);
if (tedge_wait_for_stop(wait_s * 1000)) {
ret = -ECANCELED;
break;
}
wait_s = TEDGE_MIN(wait_s * 2, 60U);
ret = -EAGAIN;
}
Expand Down
5 changes: 5 additions & 0 deletions core/src/tedge_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ int tedge_c8y_host_set(const char *host);
/** Ask the client to drop this session and connect again (a new credential). */
void tedge_request_reconnect(void);

/** Wait up to @p ms, or less when tedge_stop() asks the client thread to
* end. @return true when a stop was asked for. For the client thread's
* long waits, so a stop does not wait them out. */
bool tedge_wait_for_stop(uint32_t ms);

/** Republish every stored twin fragment (after a reconnect). */
void tedge_twin_republish(void);

Expand Down
131 changes: 131 additions & 0 deletions include/tedge/provisioning.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* SPDX-License-Identifier: Apache-2.0 */

/**
* @file
* @brief Setting a device up over a cable or Bluetooth (TEDGE_PROVISIONING).
*
* An optional library beside the client: it answers the Improv Wi-Fi
* protocol (https://www.improv-wifi.com), which tools such as
* `tedge-onboard`, ESP Web Tools and Improv phone apps speak, and hands what
* it receives to the application (the Wi-Fi network) and to the client (the
* Cumulocity tenant and one-time password, through tedge_set_c8y_url() and
* tedge_set_enroll_otp()). The client itself knows nothing about it.
*
* The application calls tedge_provisioning_init() after tedge_init(). The
* port connects a transport: for Improv Serial, it feeds the console's
* bytes to tedge_provisioning_serial_feed() and writes what the library
* gives its write function.
*
* Besides the Improv commands, the library answers three of tiny-edge's
* own (TEDGE_PROVISIONING_CMD_*): what onboarding needs to know about the
* device, the onboarding data itself, and a request to identify the board.
*/

#ifndef TEDGE_PROVISIONING_H_
#define TEDGE_PROVISIONING_H_

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/** The version of tiny-edge's commands, reported by the info command. A
* tool that knows an older version refuses a newer device. */
#define TEDGE_PROVISIONING_PROTOCOL_VERSION 1

/** tiny-edge's RPC commands, numbered away from Improv's own. */
#define TEDGE_PROVISIONING_CMD_INFO 0xE0
#define TEDGE_PROVISIONING_CMD_ONBOARD 0xE1
#define TEDGE_PROVISIONING_CMD_IDENTIFY 0xE2

/** In the onboard command's flags: set the device up again even though it
* is connected (it gets a new certificate). */
#define TEDGE_PROVISIONING_FLAG_REONBOARD 0x01

/** A Wi-Fi network the board can see (tedge_provisioning_hooks::wifi_scan). */
struct tedge_provisioning_network {
char ssid[33];
int8_t rssi; /**< dBm */
bool secured; /**< needs a password */
};

/** What the application provides. Every member may be NULL. */
struct tedge_provisioning_hooks {
/**
* Join @p ssid with @p password (empty for an open network) and keep
* them for the next boot, in the application's own way. Block until
* the network is joined or the attempt failed, for 30 s at most.
* Without it, every Wi-Fi request is answered "unable to connect".
*
* @return 0 once joined, or a negative errno.
*/
int (*wifi_connect)(const char *ssid, const char *password, void *ctx);

/** Make this board recognisable for about 10 s (blink an LED, say).
* The library also logs a line naming the device. */
void (*identify)(void *ctx);

/**
* Scan for Wi-Fi networks, filling up to @p max entries of @p out.
* Block until the scan is done, for 15 s at most. A tool uses it to
* offer the networks the board can actually reach. Without it, the
* scan request is answered "unknown RPC".
*
* @return The number of networks found, or a negative errno.
*/
int (*wifi_scan)(struct tedge_provisioning_network *out, size_t max,
void *ctx);

/** The chip or board, for Improv's device info ("ESP32-S3"). NULL
* reports "unknown". */
const char *chip;

/** The firmware's name and version, for Improv's device info, when the
* application knows them better than the port does. NULL takes the
* port's (tedge_port_firmware_name() and _version()). */
const char *firmware_name;
const char *firmware_version;

void *ctx;
};

/**
* Start the library: its worker thread, and the state it reports. Call once,
* after tedge_init(). @p hooks is kept, so it must stay valid.
*
* @return 0, or a negative errno.
*/
int tedge_provisioning_init(const struct tedge_provisioning_hooks *hooks);

/* --- Improv Serial (TEDGE_PROVISIONING_SERIAL) --------------------------- */

/**
* Writes @p len bytes to the console, all of them, as one unit: no other
* output (log lines) may land in between. Called from the library's
* worker thread.
*/
typedef void (*tedge_provisioning_write_t)(const void *data, size_t len,
void *ctx);

/** Connect the serial transport: the library writes its frames with
* @p write. NULL disconnects it. */
void tedge_provisioning_serial_attach(tedge_provisioning_write_t write,
void *ctx);

/**
* Feed bytes received on the console. Bytes outside a valid Improv frame
* (typed text, a tool's other output) are skipped. May be called from any
* thread; complete frames are handled on the library's worker thread.
*
* @return How many valid frames these bytes completed.
*/
size_t tedge_provisioning_serial_feed(const void *data, size_t len);

#ifdef __cplusplus
}
#endif

#endif /* TEDGE_PROVISIONING_H_ */
Loading
Loading