Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
0fac09d
First draft of smartmeter demo CHERIoT bits
nwf Feb 6, 2025
195f5d8
Allow speeding up simulated time
nwf Feb 13, 2025
0d0649d
Change xmake default paths to suit cheriot-demos repo.
rmn30 Feb 18, 2025
aef5dc9
Add smart_meter demo to CI.
rmn30 Feb 18, 2025
c394441
broker configuration via xmake
nwf Feb 18, 2025
f9535a0
fix client IDs
nwf Feb 19, 2025
d487995
grid: report some data, not just a Tick
nwf Feb 19, 2025
c244324
Very, very initial web frontend goo
nwf Feb 19, 2025
65e8569
WIP: monolithic image as well
nwf Feb 20, 2025
34d2d85
Incremental progress on user frontend
nwf Feb 20, 2025
af88e65
Monolithic build
nwf Feb 21, 2025
5ea2bac
sample_policy: use spaces, not tabs
nwf Feb 21, 2025
945d6c5
Add "revert to last submitted" button to editor
nwf Feb 21, 2025
af2846f
Further set the stage for badness
nwf Feb 21, 2025
a4fe43d
Further demolish monolith security
nwf Feb 24, 2025
de77361
Add 'unique-id' xmake option for setting meter ID at compile time.
rmn30 Feb 25, 2025
3a95c7c
Update network stack to include fix for MQTT packet bounds.
rmn30 Feb 25, 2025
ac44e0e
Expand CHERIoT component README a bit
nwf Feb 25, 2025
139506f
frontend: user-editor rollup config file
nwf Feb 25, 2025
51d6d43
frontend democonfig: add default meter ID
nwf Feb 25, 2025
e570d8d
frontend: rename directory holding on-device JS
nwf Feb 25, 2025
ad15347
frontend: more functionality in grid view
nwf Feb 25, 2025
fccea39
Fix comment in ffigen.js and regenerate userffi.h.
rmn30 Feb 26, 2025
6a45144
Configure uart1 and add js ffi for writing to it.
rmn30 Feb 26, 2025
3aad2c9
frontend: first stab at provider UI
nwf Feb 26, 2025
e435a2a
Add proposal for house protocol
nwf Feb 26, 2025
48a20f2
Add some smart home python files.
rmn30 Feb 26, 2025
c7e93f1
Add RPi demo js config and add an option to allow self-signed cert.
rmn30 Mar 4, 2025
0c17920
Add notes on setting up RPi server for smartmeter demo.
rmn30 Mar 5, 2025
61cc749
Smart meter: further work on house python.
rmn30 Mar 5, 2025
dbf56ee
frontends: add consumption report graph
nwf Mar 6, 2025
e54ad21
initial attempt at reading powerSamples from house. Had to disable mo…
rmn30 Mar 6, 2025
9dcbbd2
Re-enable the monolith build.
rmn30 Mar 7, 2025
a6997ef
Smart meter demo: latest version of house python with updated lcd dis…
rmn30 Mar 8, 2025
fd73689
device: sensor error handler only in non-monolith
nwf Mar 6, 2025
abf7048
device: use explicit timeouts in MQTT connect
nwf Mar 6, 2025
33a5f3f
device: prepare for coarse and fine sensor data
nwf Mar 7, 2025
d43404c
device sensor: coarse data, too
nwf Mar 7, 2025
7418a82
frontend: allow relative paths for broker certs
nwf Mar 7, 2025
9269944
frontend: user editor dropdown for provided examples
nwf Mar 7, 2025
b44af20
device: report user JS crashes to grid
nwf Mar 8, 2025
61f06f4
frontends: consumption updates might be negative
nwf Mar 8, 2025
247834a
grid frontend: report crash counts
nwf Mar 8, 2025
72bc2d2
clang-format
nwf Mar 8, 2025
a5e462b
device: optional fake sensor reports
nwf Mar 8, 2025
40a0df9
device provider: publish samples
nwf Mar 8, 2025
edc4755
treewide: overhaul js
nwf Mar 8, 2025
3fde6dc
Add omitted file
nwf Mar 9, 2025
48e17f9
Further tweaking of policy files
nwf Mar 9, 2025
ddf6ba7
device: tweak sensor coarsening
nwf Mar 9, 2025
3fa852a
Tweak default policy
nwf Mar 9, 2025
9ea55f7
Attack demo
nwf Mar 9, 2025
5478bd9
device: refactor monolith build; separate user
nwf Mar 9, 2025
414c4a4
Add crash example
nwf Mar 9, 2025
c2ac624
Revise monolith again
nwf Mar 10, 2025
3f82f21
frontends: arrays are 0 indexed, d'oh.
nwf Mar 10, 2025
c359e09
Emit newlines when sending to house
nwf Mar 10, 2025
40a9abd
device sensor: attempt to make reads more robust
nwf Mar 11, 2025
a740d06
device sensor: reuse std::string for lines
nwf Mar 12, 2025
8b9e23d
device js: add export to inject network faults
nwf Mar 26, 2025
1240e9e
smartmeter device user: fix multiwater use
nwf Mar 26, 2025
b59bca3
smartmeter: deeper housekeeping stacks
nwf Apr 28, 2025
06bcdc7
smartmeter: coarsen samples over many minutes
nwf Apr 28, 2025
e0d5cbc
Add more notes and config files for server config on Mac
rmn30 Apr 30, 2025
f4d339b
Add new cheriot.demo cert as the old one expired.
rmn30 Apr 30, 2025
fe49153
Add index.html with iframes for the three views.
rmn30 Apr 30, 2025
bc16823
smartmeter: lower SNTP timeouts
nwf May 2, 2025
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
5 changes: 4 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
matrix:
build-type: [ debug, release ]
demo: [compartmentalisation, configuration_broker_ibex, configuration_broker_sonata, HughV1, HughV2]
demo: [compartmentalisation, configuration_broker_ibex, configuration_broker_sonata, HughV1, HughV2, smart_meter]
include:
- xmake-run: false
- build-type: debug
Expand All @@ -39,6 +39,9 @@ jobs:
- demo: configuration_broker_ibex
build-dir: configuration_broker/ibex-safe-simulator
xmake-run: true
- demo: smart_meter
build-dir: smartmeter/cheriot
extra-config: --IPv6=n
fail-fast: false
runs-on: ubuntu-latest
container:
Expand Down
2 changes: 1 addition & 1 deletion cheriot-rtos
2 changes: 1 addition & 1 deletion network-stack
Submodule network-stack updated 1 files
+28 −2 lib/mqtt/mqtt.cc
63 changes: 63 additions & 0 deletions smartmeter/cheriot/README.house-protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# What

This is a sketch of a protocol between the CHERIoT smartmeter demo components and
and external device representing the house's power distribution system.

This is based on Murali's proposal at
https://github.com/CHERIoT-Platform/cheriot-demos/pull/20#issuecomment-2670632663

# Messages

For ease of debugging, this protocol uses ASCII.
Messages are delimited with newlines (`0x0a`).
Messages are composed of space (`0x20`) separated tokens.
The tokens within a message specify a command (possibly with subcommands) and then its arguments.

## Smartmeter to House

(In the smartmeter, these messages are to be sent by the `user` compartment.)

### Set diagnostic LEDs

led [on|off] [n]

Change diagnostic LEDs on the house side.
The optional `n` field is a bitmask of LEDs to be turned on or off;
if unspecified, an unspecified default is used.

### Control battery charge / discharge

batteryControl N POWER

Indicates that battery N should be charged at a given POWER (in Watts).
If RATE is negative, the battery should be discharged.
The house should respond with a battery status message.

### Power draw

powerTarget N POWER

Indicates the target POWER draw (in Watts) for consumer N.

## House to Smartmeter

(In the smartmeter, these messages are to be processed by the `sensor` compartment.)

### Battery status

batteryStatus N CAPACITY CHARGE

Indicates that battery N has total CAPACITY (in Watt-hours) and is currently holding CHARGE (also Watt-hours).

This message should be sent every minute for each battery before its `powerSample` report, and
it should also be sent in response to `batteryControl` message.

### Power draw

powerSample POWER

A report of total POWER (in Watts) draw for the past minute.
In a real system, this would be measured directly by the smartmeter.
Negative POWER values indicate that the house is feeding back to the grid.

This message should be sent every minute after all `batteryStatus` reports.
188 changes: 188 additions & 0 deletions smartmeter/cheriot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# Disclaimer

This is a work-in-progress and is only ever intended as a technology demonstrator.
(That is, while hopefully interesting, this code should not find its way to production environments!)

# Multi-Tenant Smart Metering

This demo showcases how one might run multiple tenants with different operational concerns on a sensing platform.
Specifically, we consider the task of "smart metering" power consumption.
Our tenants are...

1. The grid controller, who wants to

1. receive real-time information about the local grid,
2. communicate scheduled outages,
3. indicate the need for corrective actions (load shedding, load increase)

2. The provider, who wants to

1. receive usage data (even retrospectively, in the case of, say, network outages),
2. set the price schedule ahead of time, and
3. announce spot price variances relative to the schedule

3. The service integrator (or, perhaps, end user), who wants to

1. receive the price schedule, variances, and grid notifications, and
2. make policy decisions about local resources (batteries, generation, loads) based on those.

We presume that the grid controller and provider logic is minimal and can be fixed at firmware build time,
while the integrator/user policy may be subject to faster change and should not require rebuilding firmware.
To that end, that policy is run on a lightweight JS interpreter.

## Compartmentalization and Information Flow

Internally, these tenants all exist within their own compartments ("grid", "provider", and two for the "user").
These make use of the (also compartmentalized!) MQTT/TLS/TCP/IP network stack to talk to the network.

Sensing and measurement itself is done by another compartment, "sensor".
This compartment makes its measurements available via a static shared object.
This object contains a small ring buffer of recent measurements and a time-stamp of the most recent.
(We presume the tenant compartments are sufficiently responsive that they will not miss updates.)

Similarly, information from the grid controller and providers are also exposed via shared objects.

These shared objects all contain futex words used as version identifiers and update-in-progress flags,
so it is possible to get stable reads of their contents and to wait for updates.

## Building The CHERIoT Bits

The build proceeds via the familiar CHERIoT-RTOS `xmake` system.

### Overriding Source Dependencies

By default, the build will use the RTOS and network stack submodules of the containing repository.
You can use a different RTOS source tree by setting the ``CHERIOT_RTOS_SDK`` environment variable.
Similarly, a different network stack source tree can be called for with ``CHERIOT_NETWORK_STACK``.

### Non-Default MQTT Broker

By default, the device will use `test.mosquitto.org` as the broker, and
the build expects the file named `mosquitto.org.h` to hold the appropriate TLS X.509 trust anchors.
These, respectively, can be overridden with the `--broker-host` and `--broker-anchor` options to `xmake config`.

### Static Device Identifier

Left to its own devices, the demo will generate a random 8-character identifier for itself.
This can instead be baked into the firmware by passing `--unique-id` to `xmake config`.
The value must be exactly 8 ASCII alphanumeric characters (`[A-Za-z0-9]`).

## Running

Most MQTT messages are composed of space-separated integers,
and so are generally straightforward to synthesize by hand on the command line or with other tooling.

At startup, the device will say something like

housekeeping: MQTT Unique: S8MhTd2T

This 8-character sequence is used to distinguish multiple devices connected to the broker.
In practice, some of these topics (provider and grid) would be broadcast, with many devices subscribing.
For the purpose of this demo, all topics are "uniquified".
The examples below assume that this string is in the environment variable `METER_ID`.

### Subscribing to MQTT updates

The device publishes to `cheriot-smartmeter/p/update/${METER_ID}` (provider compartment)
and `cheriot-smartmeter/g/update/${METER_ID}` (grid compartment).

### Setting the provider rate schedule

The provider rate schedule contains a timestamp and 48 values thereafter.
Run something like the following to denote a time-of-use schedule for today
(that is, starting at the most recent past midnight)
and a flat rate schedule for tomorrow.

The units here are intended to be centi-pence per kWh, which should give a realistic amount of precision.

mosquitto_pub -h test.mosquitto.org -p 1883 -q 1 -t cheriot-smartmeter/p/schedule/${METER_ID} -s << HERE
$(date +%s --date="")
760 760 760 760 760 760 760 760 1580 1580 1580 1220 1220 1220 1220 1580 1580 1580 760 760 760 760 760 760
760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760 760
HERE

### Setting a provider variance

To inform the device of a variance in pricing (say, a surplus of solar power is driving prices negative),
use something like the following, which indicates two variances:
- in the first, starting now and lasting for 5 minutes, power has negative cost
- in the second, starting in 5 minutes and lasting a further 10, power has zero cost

Run:

mosquitto_pub -h test.mosquitto.org -p 1883 -q 1 -t cheriot-smartmeter/p/variance/${METER_ID} -s << HERE
$(date +%s)
0 300 -2
300 600 0
HERE

### Scheduling a grid outage

To inform the device of a pending loss of grid power, run something like the following,
which indicates an outage starting in 4 hours and lasting for 6 thereafter.

mosquitto_pub -h test.mosquitto.org -p 1883 -q 1 -t cheriot-smartmeter/g/outage/${METER_ID} -s << HERE
$(($(date +%s) + 4*3600)) $((6*3600))
HERE

### Transmitting a grid request

To inform the device of a request for more or less production, run something like the following,
which indicates an immediate request, lasting for 5 minutes, to reduce consumption (or increase generation).

mosquitto_pub -h test.mosquitto.org -p 1883 -q 1 -t cheriot-smartmeter/g/request/${METER_ID} -s << HERE
$(date +%s) 300 -1024
HERE

### Pushing new JS Policy Code

The `js/compile.sh` script wraps fetching `microvium` (and its Node.js backend, in particular)
and using that to build bytecode files for use with the device.
Running, for example, `compile.sh ./sample_policy.js`, will result in a `sample_policy.js.mvm-bc` file that can be shipped over MQTT to the device with a command like

mosquitto_pub -h test.mosquitto.org -p 1883 -q 1 -t cheriot-smartmeter/u/js/${METER_ID} -s < sample_policy.mvm-bc

### Speeding Up Simulated Time

Because this is a _demonstration_ and not a _real device_, it's helpful to be able to run faster than real time.
Towards that end, the policy evaluator understands a "timebase zero",
after which (simulated) time elapses at a positive multiple rate relative to real time.
While the policy code will still be evaluated in real time
(and, in particular, in response to sensor data that will continue to be published every 30 _wall-clock_ seconds),
the timestamp associated sensor reports, which drives most of the policy's idea of time,
will be artificially advanced.
Specifically, given a _wall-clock_ time T, a timebase zero Z, and a timebase rate R,
if T >= Z, then the time will instead be indicated as

Z + R * (T - Z)

Thus, setting R to 10, for example, will cause the policy code's 30 _wall-clock_ second evaulation interval to
correspond to a 5 _simulated minute_ interval.

As with most everything, setting the timebase is done over MQTT. Run something like

mosquitto_pub -h test.mosquitto.org -p 1883 -q 1 -t cheriot-smartmeter/u/timebase/${METER_ID} -s << HERE
$(date +%s --date="12 minutes ago") 10
HERE

to set the timebase zero (Z) to 12 minutes ago and the rate (R) to 10.
(Making the timebase zero two _simulated hours_ ago.)

You _may_ wish to have the broker retain this message, so that subsequent subscribers will also be informed.
To do so, add `-r` (or `--retain`) to the command line (before the `<<`).
MQTT message retention is entirely at the discretion of the server, so may not be reliable.
Don't abuse public resources.

## Development

### Changing the JS FFI

If you change the information cached and exposed in the `userJS_snapshot` structure (also defined in `common.hh`),
you'll want to update the constants in `js/ffi.js`, tweak `js/ffigen.js`, and run `microvium ffigen.js` (in the `js/` directory) to rebuild `userffi.h`.

### Updating the default JS

At boot or after a crash, the device reloads an initial JS bytecode.
That's sourced from `default-javascript.h`, which can be generated by, in the `js` directory,

./compile.sh -H ../default_javascript.h ./sample_policy.js
31 changes: 31 additions & 0 deletions smartmeter/cheriot/cheriot.demo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

static const unsigned char TA0_DN[] = {
0x30, 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C,
0x0C, 0x63, 0x68, 0x65, 0x72, 0x69, 0x6F, 0x74, 0x2E, 0x64, 0x65, 0x6D,
0x6F
};

static const unsigned char TA0_EC_Q[] = {
0x04, 0xAD, 0x02, 0x25, 0x3D, 0x08, 0x4F, 0xE0, 0x61, 0x99, 0x67, 0xFF,
0x97, 0x2E, 0xE8, 0x7E, 0x2C, 0x91, 0x17, 0xCB, 0x4F, 0xE7, 0xD4, 0x1D,
0x7F, 0x6B, 0x38, 0x87, 0x3C, 0x2B, 0x95, 0xC1, 0xFC, 0xD9, 0x0C, 0x90,
0x8A, 0x8D, 0x44, 0x28, 0x7D, 0x2F, 0x0F, 0x18, 0x6B, 0xE9, 0xDA, 0x20,
0xDB, 0x9A, 0xDF, 0xA9, 0x33, 0x71, 0x12, 0xC4, 0x0C, 0x37, 0x11, 0xB6,
0x12, 0x27, 0x52, 0xAE, 0x10
};

static const br_x509_trust_anchor TAs[1] = {
{
{ (unsigned char *)TA0_DN, sizeof TA0_DN },
BR_X509_TA_CA,
{
BR_KEYTYPE_EC,
{ .ec = {
BR_EC_secp256r1,
(unsigned char *)TA0_EC_Q, sizeof TA0_EC_Q,
} }
}
}
};

#define TAs_NUM 1
Loading