Thanks for taking the time to contribute.
Here are some things to know:
- .json files are JSON π€‘
- .handlebars files are Handlebar Templates
- .json files contain data like include statements, class initialization or variable declaration, setup, and loop calls
- .handlebar is a template where the JSON data is inserted
- the binary and Node.js package look for .handlebar files in the
./templatesfolder- and the .json files are in the
./jsonfolder
- and the .json files are in the
- All the .json files are merged together to make lists of includes, constructors, setups etc.
Let's just look at an example then.
Open ./json/microfire_mod-ph.json.
Then look at ./templates/print.handlebars. Everywhere you see {{[some_data]}}, ECP will insert some_data.
Notice that in ./json/microfire_mod-ph.json it has include, constructor and several others? Now look through ./templates/print.handlebars towards the top.
Embedded CopyPasta π fills the includes variable with all the include lines from all the .json files. In our example above, only one include file is there, so that handlebar statement would render to
#include <Microfire_Mod-pH.h>It works the same way for the following .json JSON entries:
- include
- constructor
- setup
- loop
- values. This is a little different, it looks like this
{value: [value], unit: [unit]}
Any of them can be empty, but they must be empty arrays (ie. loop:[]).
versionthe library the .json is based onnamefor display purposes onlydependenciesthe PlatformIO library name and version string. It can be found in the "Installation" tab of the PlatformIO library page. Here is an example. If there are multiple dependencies, they can all be listed here, but keep in mind that if the library is configured to have dependencies within the PlatformIO system, they will be installed anyway.includeis for the#includestatementsconstructoris where variables are declared or class initialization occurs outside of and above functions likesetup()orloop().setupfor doing needed configuration in thesetup()function. This is::begin()for many sensors.loopshould be how the data is updated. Maybe something like::update().values.valueis how the data is made available. It might be a the return of a function call, or a variable previously declared and updated in theloopline. It should be formatted to be used as a variable (ie.Serial.println([values.value])), so no ending semicolon.values.unitthe unit of thevalues.valuekey. This is used as a variable name, so should conform to variable name standards (no spaces, special characters etc). This also needs to be sufficiently unique to avoid name collisions (sht30_tempCvstempC).
When rendering, the following variables will be merged from all the used .json files.
- includes
- constructors
- setups
- loops
- values. This is now a flattened array of all the values
- units. This comes from the
values.unitkey in the .copy file and is a flattened array
Now that that is sorted, we can make our template file. ./templates/print.handlebars looks like this:
You can see...
- that the top Handlebar loop just pastes all the
#includelines - then the library classes are constructed/initialized
- after that is the normal Arduino
setup()function - inside the
setup()function, there is normally some setup done, most commonly for sensors it is a::begin()call. - in the
loop()function, you call whatever updates the sensor, and in this example, it prints the value and it identifies that value by unit.- notice that the index of
units[x]andvalues[x]always refers to the same value/unit pair.
- notice that the index of
So in short, the .handlebars file is where everything comes together. This example just prints the values, but there are others that send measurements to AdafruitIO, or the Arduino IoT cloud.
Yes, in the ./templates directory, for each [name].pasta file, there should be a [name].json file. The .json file has all the same data in it with the exception of version being changed to board. board contains which board variant the .handlebars file is for (ie ESP32, IoT 33, Uno).
If the .handlebars file uses libraries, like AdafruitIO or InfluxDB, this is where you specify those libraries, constructors etc to get it running. This will also ensure those libraries are installed. All the fields will be at the top of the various JSON arrays. All the arrays can be empty, but there should at least be the name and board fields present.
Hopefully that explained enough to make you (dangerously?) effective at making your own. Have a look through this repo's examples for how various things are handled.
- Avoid name collisions: use specific class names like sht30 vs sensor
- No
#ifdeffor different boards. Some examples support multiple boards and variants. That doesn't make for readable code and it is easy to just make another template. ESP32 can have a separate template, so can ESP866 and RP2040. - Some decisions need to be made like pin numbers. Pick one to ensure it compiles. use
/**/inline comments to notate it. - Include links to the sketch and sensors if available
You can do it through a PR of this repo, or you can email it to me if that's easier.
If you'd rather a walkthrough, try this out.