Community#
pamoja means together. The library is written so that the people who know a field, a river, a clinic, or a workshop can add what they know without becoming Rust engineers, and so that what they add is held to the same bar as the rest: it runs, it is checked in CI, and it is documented where the next person will look. Four things are open to anyone. A profile is a JSON file. An example is one program. A driver is a decoder written from a datasheet, with a checklist that keeps it honest. A board is a page and a program that compiles. Each has a path below, a check that runs before it merges, and an issue form for the reader who has the thing but not the toolchain to run the check. The rules for code itself are in CONTRIBUTING.md, and the building page says how to run everything locally.
Share a profile#
A profile is the whole behavior of a node as data, and sharing one needs no code.
- Start from the manifest closest to what you built, from the
catalog, and change the numbers to the ones that worked: the
setpoint and its deadband, the safe band an alert waits for, the sampling
intervals your battery allowed. Give it a
namein lowercase words joined by hyphens, atopic, and adescriptionof a sentence or two saying what it watches or holds and what it does about it. - Run it. Load it with
Profile::from_json(orfromJson,from_json,FromJson) and let its controller decide a few readings, as the device profiles guide does, or run it on the node. - Save it as
profiles/<name>.json, where the file name is the profile's name, and runcargo xtask profiles. The task reads the file with the parser a device uses, checks it, and rewrites it into the form the library writes. Thencargo xtask docsadds it to the catalog. - Open a pull request with the file and the regenerated page. The pull request template asks how it was tested; "ran on a Pi in a chicken house for a week" is the right kind of answer.
Without a Rust toolchain, file the share a profile form with the manifest pasted in, and a maintainer runs the steps above and credits you in the pull request.
What the check enforces, so a reader knows what a listed profile has been held to:
the name matches the file and is unique; the description is there and reads as a
sentence; the topic is one publishable path, with no wildcards; a setpoint policy
has a deadband above zero and a safe band no narrower than it; a level policy
warns at least one sample ahead; a surge policy has a limit above zero; the
sampling intervals do not shorten as the battery drains, and the two thresholds
sit between zero and one in the right order; every dashboard element has a
snake_case key used once, a unit, a label, a band with its low end first, and a
starting state the dashboard has words for, either one it ships or one the
manifest supplies under messages, where a per-locale message carries an en
text for the locales it does not name.
Share an example#
An example is a complete program with a main, written to be read
top to bottom, that runs with nothing plugged in because a simulator or the
loopback transport stands in for the hardware. Every one runs in CI on every
change, so an example on the page is never one that worked once.
- Write it as
examples/community/<name>.rs. The module doc's first paragraph is what the examples page shows, and a line readingRun with:followed by the command in backticks is the line beside it; without one the page shows the defaultcargo runline. Say who wrote it in the doc, since the page credits the file and its history, not a list of names. - Register it in
examples/Cargo.tomlas an[[example]]whosepathiscommunity/<name>.rs, and run it:cargo run -p pamoja-examples --example <name>. It must finish on its own and print what it found. - Run
cargo xtask docs, which lists it under "Community programs", and open a pull request.
A program in TypeScript, Python, or C# is welcome too; say which language in the share an example form, and it lands beside the guide examples for that language.
Contribute a driver#
A driver is a decoder for a part, written from the manufacturer's datasheet and
nothing else, with a driver type over the bus traits that runs it
on real hardware and implements Sensor or Actuator so the kit, the profiles,
and the dashboard take it as they take the ones that ship. The
your own device guide shows the shape for a part that stays in
your own tree; this is the path for one that ships with pamoja.
Claim the part first with the contribute a driver form, with a link to the datasheet, so two people do not write the same decoder in the same month. Then the checklist, which is the same audit every shipped driver went through:
- The datasheet is the manufacturer's own, linked from a new entry in
docs/hardware.tomlwith the figures the hardware page shows, the price band, and two or three places to buy it with the price on the day.cargo xtask linksmust fetch the source; a vendor page that refuses scripted readers is marked as such rather than replaced with a mirror. - The decode module in
pamoja-sensorsorpamoja-actuatorsisno_std, takes no dependencies, and carries every register address, command word, timing, and compensation formula as a named constant or a documented function. - Every constant is searched for in the datasheet text, in the spellings a
datasheet uses (hex with and without
0x, zero-padded, decimal, scaled), and a value that a table splits across columns is checked by reading the table, not by loosening the search. - Every formula is recomputed independently, by transcribing the datasheet's formula into a script and reproducing the values the tests assert, without reading the module first.
- Every register layout is checked field by field against the datasheet's field table, including reserved bits that must keep a fixed pattern.
- The tests anchor to the datasheet's own worked examples and published check values, not only to round trips, so a decoder that is wrong but self-consistent is caught. When the datasheet and the code disagree, find which other value in the datasheet the printed one matches before assuming the code is wrong; a misprint has been found this way.
- The driver type runs the part's transfer sequence over the bus traits and is tested against a scripted bus that plays the part's side of the conversation.
- The hardware entry claims the module, so
cargo xtask docs --checkties the two together, and the sensors or actuators guide names the part. - Where a decoded value crosses into the bindings, it reaches all three, with a conformance vector every language asserts.
- The CHANGELOG entry says what the part is and what document it was written from.
Add a board#
A board is documentation and a program, not a type: a page under docs/boards/
with the board's pins for each bus, wiring for one shipped part, the settings or
toolchain the board needs, and a first program that reads the part through the
shipped driver, every figure from the maker's own documentation and linked at the
end of the page. The program is a standalone package under examples/boards/
with its own empty [workspace] table and a .cargo/config.toml naming its
target and runner; CI builds it from inside its directory on every change. The
board's card in docs/hardware.toml names the page in its page field, and the
ESP32 and RP2040 pages are the two shapes
to copy, for a chip with its own Rust hardware layer and for one on a community
one.
Report and request#
Something that behaves differently from its documentation is a bug. A part, a protocol, or a job pamoja cannot do yet is a capability request. A page that is wrong, unclear, or missing is a documentation problem. A vulnerability goes through the private channel in SECURITY.md, never a public issue.