Skip to main content

Resource files

Some of what a program needs is not code: a table of values, an image, a blob of firmware. Those go in the project beside the code, ship with it, and are read by path.

This is how the Build HAT library works. It carries the HAT's firmware as a resource and sends it over on startup — so this is not a demonstration feature, it is how a real library solves a real problem.

Where files go​

Under src/main/resource/, in whatever folders suit you:

my-robot/
src/
main/
code/
robot.code
resource/
greeting.txt

New Project makes the folder. If it is not there, make it.

Reading one​

A file's path is its place under src/main/resource/, with / between folders. So src/main/resource/greeting.txt is read as "greeting.txt", and a file at src/main/resource/motors/curve.csv as "motors/curve.csv".

namespace hello

entrypoint.default main() {
# Resources.data answers the file's bytes, a u8[].
let bytes = Resources.data("greeting.txt")
print("greeting.txt is ${bytes.size} bytes")
}

Bytes are what you get. V0 has no way to turn a whole file into text — there is no decode in the language yet — so a program either uses the bytes as bytes, the way the Build HAT library hands its firmware to the hardware, or reads them one at a time: bytes[0] is a u8, and string(bytes[0]) is its number as text.

A .png, a .wav or a .bin you pass to whatever understands it. That is the case this is built for.

What the compiler checks, and when​

Two things are settled when you build rather than when you run — which is the useful way round, because a robot is a bad place to discover a missing file.

That the file is there. Write a path nothing ships and the build refuses, and the refusal tells you what you do have:

Resources.data("greting.txt") names resource://greting.txt, which no module of this program ships: it ships resource://greeting.txt

That check only applies to a path written out as a literal, as above. A path held in a variable cannot be checked before the program runs, so keep it a literal unless you have a reason not to.

That the type is known. A resource's type comes from its extension, through a fixed table. The compiler will not guess one, because a guessed type gets read as the truth by whatever opens the file later. The extensions it knows:

.bin .csv .gif .html .jpeg .jpg .json .png .svg .txt .wav .xml

Anything else is a build problem naming the file. Case does not matter, so NOTES.TXT is fine. A file with no extension is refused the same way — so a README with no suffix will stop your build.

Your operating system's own files are skipped rather than refused: macOS's .DS_Store and ._ files, Windows' Thumbs.db and desktop.ini, and editors' .swp and ~ backups. Finder writes .DS_Store into any folder you open, so without that rule, simply looking at your resource folder would break the build.

Libraries bring their own​

A library's resources ship with it, so a program can read what its libraries carry as well as its own files. That is how the Build HAT's firmware arrives: your project has no copy of it, and the library's Resources.data("buildhat/firmware.bin") still works, because the library ships it.

note

This page is waiting on its board run. The program above is written but has not run on a real board yet, and that is the rule for everything here. Resources.data itself is board-proven — the Build HAT library uses it on a Raspberry Pi 3 B+ every time it brings the HAT up — but this page's own program has not had its turn.