Skip to content

Settings

doomke edited this page Jun 13, 2023 · 10 revisions

Because the firmware is entirely modular, the ESP32 needs to be told which hardware to initialize by reading the settings from flash memory. Settings are stored and read using LittleFS. It is formated as a single JSON file in which all necessary information is contained.

Module Type and Initialization

Each key value pair in the highest hierarchy of the JSON file represents one module, with the key being the controlId of the module and the value being a nested JSON object containing the specific module settings. The module type is stored therein attached to the key type and formatted as an integer value. The integer represents the module type:

type module
0 socket client
1 WiFi client
2 info LED
3 stepper motor
4 servo motor
5 camera
6 input
7 output
8 macro

All other settings are stored within the same hierarchy, although the number and names of the setting parameters vary based on the module type. If a necessary parameter can not be found within the file, it will be initialized with a default parameter. A settings file thus might look like this:

{
    "wifi":
    {
        "type": 1,
        "ssid": "Access point",
        "password": "********",
        ...
    },
    "socket":
    {
        "type": 0,
        "ip": "192.168.178.1",
        "port": "3000",
        ...
    },
    "relay":
    {
        "type": 7,
        "pin": 33,
        "pwm": false,
        ...
    },
    ...
}

Input Module

Since the input module might contain type conversions that may be added dynamically, there is another layer to the hierarchy of the settings for this module type. The conversions are stored under the key conversions as a JSON array. Conversions are applied to the raw mV value as measured by the ADC in the order they are entered. Additionally, the amount and names of the conversion parameters may vary depending on the conversion. For example, the settings of a thermistor read out via a voltage divider with a reference resistor would look like this:

{
    "type": 6,
    "averageTime": 500,
    "updateTime": 1000,
    ...
    "conversions":
    [
        {
            "type": 1,              <- voltage divider, convert to resistance
            "refVoltage": 3300,     <- 3300 mV
            "refResistor": 3        <- 3 kΩ, output now in kΩ
        },
        {
            "type": 0,              <- thermistor, convert to K
            "tempNormal": 298.15,   <- normal temperatur is 25°C = 298.15 K
            "resNormal": 10         <- 10 kΩ, unit must match previous conversion output
            "beta": 3750            <- beta value of the thermistor in K, output now in K
        },
        {
            "type": 3,              <- offset
            "offsetValue": -273.15  <- substract 273.15 K, output now in °C
        }
    ]
}

Macro Module

Another module type that can be appended dynamically is the macro module. States are are stored under the key states. Each entry represents a state, holding a JSON array where each entry represents another command, which itself holds controlId, command key and command value. The following example shows a macro module holding three states off, cycle and pulse that can switch an infoLED between continous cycle and pulse.

{
    "type": 8,
    "initState": "led",
    "controlKey": "state",
    "infoLED": "infoLED",
    "states":                        <- holds states
    {
        "off":                       <- no commands
        [
            {
            }
        ]
        "cycle":                     <- instruct infoLED to show a cycle pattern
        [
            {
                "id": "infoLED",
                "cycle": 5000        <- show cycle with 5 s length
            },
            {
                "id": "macro",
                "wait": 5000         <- instruct macro module (this) to wait for 5 s
            },
            {
                "id": "macro",
                "state": "cycle"     <- call this state and restart the cycle
            }
        ],
        "pulse":                     <- instruct infoLED to show a pulse pattern
        [
            {
                "id": "infoLED",
                "pulse": 1000        <- show pulse pattern with 1 s length
            },
            {
                "id": "macro",
                "wait": 1000         <- instruct macro module (this) to wait for 1 s
            },
            {
                "id": "macro",
                "state": "pulse"     <- call this state to restart the pulse
            }
        ]
    }
}

Event Hooks

Command hooks reacting on internal events also can be added dynamically and are stored under the reserved key internal, which holds a JSON array. Each element of the array represents one custom event hook including the type of the internal event, the controlId of the module the event is listening for and the command that gets send in case the event occurs. In the following example two custom event hooks get defined which listen for high and low input events of a module. They tell the module to send a status to the server if this occurs, effectively mirroring the input state to the server.

...,
"internal":
    [
        {
            "type": 10,             <- if a low input event occurs ...
            "listen": "input",      <- ... and the issuing module is named "input" ...
            "command":
            {
                "id": "input",
                "getStatus": true   <- ... instruct "input" to send its status
            }
        },
        {
            "type": 11,             <- if a high input event occurs ...
            "listen": "input",      <- ... and the issuing module is named "input" ...
            "command":
            {
                "id": "input",
                "getStatus": true   <- ... instruct "input" to send its status
            }
        }
    ]

General

Guides

Principle of Operation

Modules

Software Hardware
camera camera
infoLED infoLED
input input
macro macro
output output
servo servo
socket socket
stepper stepper
WiFi wifi

Clone this wiki locally