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
43 changes: 43 additions & 0 deletions DEVICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,49 @@ Test note: a TCP probe of a local `c8y remoteaccess server` port opens a
tunnel of its own, which holds the device's single session for a moment; an
SSH connection straight after it can find the slot busy.

### Lean cloud shell on the ESP32-C6 (2026-11-11)

The C6 Modbus `tedge-full` image now carries the cloud shell command too,
with `release/extras/shell-lean.conf` after `shell-diagnostics.conf`. The
full extra did not fit: +15.9 KB of RAM, and +120 KB of flash, which put the
image at 82.4 % of its slot (budget 80 %). The lean extra keeps the
allow-list's purpose and drops what a cloud command cannot use:

- No network or Wi-Fi shell (about 65 KB of flash). `net iface` and
`wifi status` print more than a result holds (it is cut at about 220
bytes), so `tedge net` (lib/common/net.c, `CONFIG_APP_NET_SHELL_CMD`)
answers instead: Wi-Fi state, SSID, channel and RSSI, address and prefix,
gateway, DNS server and the gateway ping count.
- No history, completion, VT100, metakeys, wildcards, stats, shell log
backend, device shell, log commands or stack painting; a 1.5 KB shell
thread (it only starts the dummy backend; commands run on the client's
command thread).
- Output buffers of 256 B instead of 512 B (the result queue holds two).
- `kernel heap` joins the allow-list: it reports the kernel heap that the
Wi-Fi driver and the tunnel's websocket allocate from.

Against `main` (0.11.1), measured with `scripts/release/build.sh`:

| Image | Flash | RAM (sram0) | malloc arena |
|---|---|---|---|
| modbus `tedge-full` | 959,692 B | 435,312 B | 74,128 B |
| + full shell extra | 1,080,508 B (82.4 %) | +15,928 B | 63,280 B |
| + lean shell extra | 966,202 B (73.7 %) | +9,360 B | 64,768 B |

Of the 9.4 KB: the command thread's stack 4,096 B (tiny-edge's default, not
yet measured), the shell thread's stack 1,536 B, the dummy backend 528 B, the
shell instance 512 B, the result queue 520 B, two thread objects 592 B, the
job 176 B, and about 1.4 KB of small objects and code in RAM. The Wi-Fi
driver allocates from the kernel heap (`k_malloc`, about 55 KB, fixed by the
drivers' minimums), which none of this touches; the shell takes its RAM from
the libc malloc arena.

On the board (eu-latest, `tedge-modbuse8f60afb8198`): the image installed
over the air and confirmed, and the device lists `c8y_Command`. The commands,
a refused command, and a 30-minute run with a firmware download and a tunnel
(where the full extra failed on the agent image in September) are still to
be recorded here.

### Identify measured (2026-10-06)

`device-identify` adds `tedge identify [seconds]` (cloud shell) and the sw0
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1392,8 +1392,9 @@ c8y operations create --device <device> \

This needs an image with the cloud shell command, which today means the
images built with `tiny-edge/ports/zephyr/profiles/extras/shell-diagnostics.conf`
(the S3-DevKitC and QT Py modbus-server and tedge-agent full builds; see
DEVICES.md). That file puts `tedge identify` on the allow-list. On other
(the S3-DevKitC and QT Py modbus-server and tedge-agent full builds, and the
ESP32-C6 modbus-server full build with `release/extras/shell-lean.conf`; see
DEVICES.md). Both put `tedge identify` on the allow-list. On other
images the cloud has no shell, so only the button works. The same command
works on a local console where the image has one.

Expand Down
15 changes: 12 additions & 3 deletions cumulocity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,16 @@ the device runs:
`net ping` is not offered: Zephyr prints the replies after the command
returns, so the cloud would only ever see the `PING <host>` line.

The ESP32-C6 Modbus `tedge-full` image carries a leaner set
(`release/extras/shell-lean.conf`), because the full one costs too much RAM
and flash there (DEVICES.md, "Lean cloud shell on the ESP32-C6"). In place
of `net iface`, `net conn` and `wifi status` it has:

| Command | Answers |
|---|---|
| `tedge net` | Wi-Fi state, SSID, channel and RSSI, address, gateway, DNS server |
| `kernel heap` | free and peak use of the kernel heap (Wi-Fi driver, tunnel) |

Anything else is refused with the reason. Output travels in one SmartREST
field, so a long answer is cut. The other boards' images leave it out: on
the ESP32-C6 its ~16 KB of RAM left too little for the Wi-Fi driver and the
firmware download (DEVICES.md, "Shell diagnostics measured").
field of about 220 bytes, so a long answer is cut. The other boards' images
leave the shell out.
11 changes: 11 additions & 0 deletions lib/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@ config APP_WIFI_PROV_ERASE_HOLD_S

endif # APP_PROV_HANDOFF

config APP_NET_SHELL_CMD
bool "Shell command `tedge net`: the network state in one short answer"
depends on TEDGE && SHELL && !NET_SOCKETS_OFFLOAD
help
Prints the Wi-Fi network, signal and channel, the address, gateway
and DNS server, and the gateway ping count, in less than a cloud
command's result holds (about 220 bytes). For builds that leave out
the network and Wi-Fi shells to save flash (release/extras/
shell-lean.conf); the cloud runs it once it is on the
TEDGE_SHELL_COMMAND_ALLOW_LIST.

config APP_IDENTIFY
bool "Identify the device from the cloud and from its button"
default y
Expand Down
75 changes: 75 additions & 0 deletions lib/common/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -998,3 +998,78 @@ int app_net_init(void)
}

#endif

#if defined(CONFIG_APP_NET_SHELL_CMD)
#include <zephyr/shell/shell.h>
#if defined(CONFIG_DNS_RESOLVER)
#include <zephyr/net/dns_resolve.h>
#endif

/* `tedge net`: the network state in one short answer. A cloud command's
* result is cut at about 220 bytes, which `net iface` and `wifi status`
* overrun on their own, and those shells cost about 65 KB of flash. This
* prints what a support call asks first. */
static int cmd_net(const struct shell *sh, size_t argc, char **argv)
{
struct net_if *iface = net_if_get_default();
char ip[16], gw[16];
int prefix = 0;

ARG_UNUSED(argc);
ARG_UNUSED(argv);

#if defined(CONFIG_WIFI)
struct net_if *wifi = net_if_get_first_wifi();
struct wifi_iface_status st = {0};

if (wifi != NULL &&
net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, wifi, &st, sizeof(st)) == 0) {
if (st.state >= WIFI_STATE_ASSOCIATED) {
shell_print(sh, "wifi %s \"%.32s\" ch %u rssi %d dBm",
wifi_state_txt(st.state), st.ssid,
st.channel, st.rssi);
} else {
shell_print(sh, "wifi %s", wifi_state_txt(st.state));
}
}
#endif
fmt_ip(ip, sizeof(ip), current_ipv4);
if (iface != NULL) {
struct net_in_addr self = { .s_addr = current_ipv4 };
uint32_t mask = net_ntohl(
net_if_ipv4_get_netmask_by_addr(iface, &self).s_addr);

fmt_ip(gw, sizeof(gw), net_if_ipv4_get_gw(iface).s_addr);
while (mask & 0x80000000U) {
prefix++;
mask <<= 1;
}
shell_print(sh, "ip %s/%d gw %s", ip, prefix, gw);
}
#if defined(CONFIG_DNS_RESOLVER)
struct dns_resolve_context *dns = dns_resolve_get_default();

for (int i = 0; dns != NULL && i < DNS_RESOLVER_MAX_SERVERS; i++) {
const struct net_sockaddr *sa = &dns->servers[i].dns_server;

if (dns->servers[i].is_mdns || sa->sa_family != NET_AF_INET) {
continue;
}
fmt_ip(ip, sizeof(ip), net_sin(sa)->sin_addr.s_addr);
shell_print(sh, "dns %s", ip);
}
#endif
#if defined(CONFIG_NET_IPV4)
shell_print(sh, "gateway ping %d/%u", (int)atomic_get(&ping_ok),
ping_sent);
#endif
if (g_last_reason >= 0) {
shell_print(sh, "last wifi reason %d", g_last_reason);
}
return 0;
}

/* Hangs off the client's own "tedge" root, which tiny-edge defines. */
SHELL_SUBCMD_ADD((tedge), net, NULL,
"network state: Wi-Fi, address, gateway, DNS", cmd_net, 1, 0);
#endif /* CONFIG_APP_NET_SHELL_CMD */
6 changes: 5 additions & 1 deletion release/devices.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ devices:
- { app: snmp-agent, variant: standalone }
- app: modbus-server
variant: tedge-full
measured: DEVICES.md#release-builds-measured-on-the-boards-2026-09-21
pr: true
extra_conf:
- "{tiny-edge}/ports/zephyr/profiles/extras/shell-diagnostics.conf"
- release/extras/shell-lean.conf
measured: DEVICES.md#lean-cloud-shell-on-the-esp32-c6-2026-11-11
- app: modbus-server
variant: tedge-ota
measured: DEVICES.md#release-builds-measured-on-the-boards-2026-09-21
Expand Down
48 changes: 48 additions & 0 deletions release/extras/shell-lean.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# SPDX-License-Identifier: Apache-2.0
#
# The cloud shell command (c8y_Command) for boards without room for the full
# shell-diagnostics extra: goes after
# {tiny-edge}/ports/zephyr/profiles/extras/shell-diagnostics.conf in a build's
# extra_conf (release/devices.yml), and trims it. Measured on the
# ESP32-C6 Modbus tedge-full image in DEVICES.md.
#
# A cloud command runs on the client's command thread against the dummy
# backend, and its result is cut at about 220 bytes. So the shell needs no
# history, completion, terminal handling or log backend, and its own thread
# only starts the backend.
CONFIG_SHELL_STACK_SIZE=1536
CONFIG_SHELL_LOG_BACKEND=n
CONFIG_SHELL_HISTORY=n
CONFIG_SHELL_TAB=n
CONFIG_SHELL_WILDCARD=n
CONFIG_SHELL_VT100_COMMANDS=n
CONFIG_SHELL_VT100_COLORS=n
CONFIG_SHELL_METAKEYS=n
CONFIG_SHELL_STATS=n
CONFIG_SHELL_CMDS=n
CONFIG_SHELL_CMD_BUFF_SIZE=160
CONFIG_SHELL_ARGC_MAX=12
CONFIG_DEVICE_SHELL=n
CONFIG_LOG_CMDS=n
CONFIG_LOG_RUNTIME_FILTERING=n
# Only "kernel uptime", "kernel version" and "kernel heap" are allowed, not
# the thread commands that stack painting serves.
CONFIG_INIT_STACKS=n
# What a command printed is cut to one result of about 220 bytes, so
# capturing more only fills the result queue (two entries).
CONFIG_SHELL_BACKEND_DUMMY_BUF_SIZE=256
CONFIG_TEDGE_SHELL_COMMAND_OUTPUT_BYTES=256

# `tedge net` (lib/common/net.c) answers in one short result instead of the
# network and Wi-Fi shells (`net iface`, `wifi status`), which print more
# than a result holds and cost about 65 KB of flash.
CONFIG_NET_SHELL=n
CONFIG_NET_L2_WIFI_SHELL=n
CONFIG_APP_NET_SHELL_CMD=y

# kernel uptime / kernel version how long it has run, what it runs
# kernel heap the kernel heap (Wi-Fi driver, tunnel)
# tedge net Wi-Fi, address, gateway, DNS
# tedge params list / tedge diag the client's parameter sets and state
# tedge identify [seconds] blink the status LED to find the board
CONFIG_TEDGE_SHELL_COMMAND_ALLOW_LIST="kernel uptime,kernel version,kernel heap,tedge net,tedge params list,tedge diag,tedge identify"
8 changes: 4 additions & 4 deletions release/size-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@
}
},
"modbus-server-tedge-full-esp32c6-devkitc": {
"image": 959692,
"content": 839144,
"image": 966203,
"content": 862607,
"ram": {
"sram0_0_seg": 435312,
"sram0_0_seg": 444672,
"lp_ram_seg": 36
},
"arena": 74128,
"arena": 64768,
"provisioner": {
"image": 1013963,
"content": 909467,
Expand Down
Loading