Thingino exposes a user-local customization tree through THINGINO_USER_DIR.
By default it points to user/ in the firmware checkout:
THINGINO_USER_DIR ?= $(BR2_EXTERNAL)/userThis directory is intended for machine-local or user-specific build inputs that should not be committed into camera configs, package sources, or the shared root filesystem overlay.
The customization model is now layered. Common settings live under
user/common/, while camera-specific and device-specific overrides live
directly under user/<camera>/:
user/common/
user/<camera>/
user/<camera>/<ip>/
For all supported file types, precedence is:
user/common/user/<camera>/user/<camera>/<ip>/
Later layers override or extend earlier ones.
Default location:
user/
You can point it elsewhere for a specific build:
THINGINO_USER_DIR=$HOME/thingino-user CAMERA=atom_cam2_t31x_gc2053_atbm6031 makeYou can also preset it in your shell environment:
export THINGINO_USER_DIR=$HOME/thingino-user
CAMERA=atom_cam2_t31x_gc2053_atbm6031 makeThe build only consumes specific files and subdirectories from that tree. It is not a generic catch-all include directory.
For a build such as:
CAMERA=atom_cam2_t31x_gc2053_atbm6031 IP=192.168.88.31 makethe build looks for user customizations in this order:
user/common/
user/atom_cam2_t31x_gc2053_atbm6031/
user/atom_cam2_t31x_gc2053_atbm6031/192.168.88.31/
If a file or directory exists in more than one location, the narrower scope wins:
- global values provide defaults
- camera-scoped values override global ones for that camera family
- device-scoped values override both for the selected
IP
The active device scope follows the current session IP value. If you want to
return to a generic build that ignores user/<camera>/<ip>/, run:
IP= makePaths:
user/common/local.fragment
user/<camera>/local.fragment
user/<camera>/<ip>/local.fragment
Purpose:
- Appends extra Buildroot config symbols to the generated
OUTPUT_DIR/.config - Useful for temporary package enables, debug toggles, or local experiments
Build behavior:
- Added as a configuration dependency
- Concatenated into
OUTPUT_DIR/.configbeforeolddefconfig - Applied in scope order: global, then camera, then device
- Participates in config regeneration checks
Typical use:
BR2_PACKAGE_STRACE=y
BR2_ENABLE_DEBUG=y
Paths:
user/common/local.mk
user/<camera>/local.mk
user/<camera>/<ip>/local.mk
Purpose:
- Adds user-scoped Buildroot package overrides such as
OVERRIDE_SRCDIR - Useful when you want per-user, per-camera, or per-device source overrides
without putting them in the repository root
local.mk
Build behavior:
- Matching files are concatenated into
OUTPUT_DIR/local.mk - Applied in scope order: global, then camera, then device
- Loaded by Thingino's aggregated package override entry point together with the
repository root
local.mk
Typical use:
THINGINO_OVERRIDES_DIR = $(BR2_EXTERNAL_THINGINO_PATH)/overrides
INGENIC_SDK_OVERRIDE_SRCDIR = $(THINGINO_OVERRIDES_DIR)/ingenic-sdkPaths:
user/common/thingino.json
user/<camera>/thingino.json
user/<camera>/<ip>/thingino.json
Purpose:
- Adds or overrides entries in
/etc/thingino.json - This is the user-scoped JSON add-on hook for Thingino core settings
Build behavior:
configs/common.thingino.jsonis installed first- The camera's
thingino.jsonis imported next, if present - User JSON files are imported in scope order: global, then camera, then device
That means device-scoped user values win over camera-scoped user values, which win over global user values, which win over common and camera defaults.
Example:
{
"webui": {
"paranoid": true
},
"mqtt_sub": {
"enabled": true,
"host": "192.168.1.10",
"port": 1883
}
}Paths:
user/common/motors.json
user/<camera>/motors.json
user/<camera>/<ip>/motors.json
Purpose:
- Adds or overrides entries in
/etc/motors.json - This is the user-scoped JSON add-on hook for PTZ and motor tuning values
Build behavior:
- The package default
motors.jsonis installed first - The camera's
motors.jsonis imported next, if present - User
motors.jsonfiles are imported in scope order: global, then camera, then device
That means device-scoped user values win over camera-scoped user values, which win over global user values, which win over package and camera defaults.
Example:
{
"motors": {
"steps_pan": 2000,
"steps_tilt": 1100,
"speed_pan": 8,
"speed_tilt": 8
}
}Paths:
user/common/overlay/
user/<camera>/overlay/
user/<camera>/<ip>/overlay/
Purpose:
- Seeds files into the writable config overlay partition
- Best for user-specific config files, init scripts, certificates, and other files you want present on first boot but still editable on the device
Build behavior:
OUTPUT_DIR/config/is rebuilt from scratch for each config image- Overlay directories are copied in scope order: global, then camera, then device
- Packed into
images/config.jffs2 - Not included in
rootfs.squashfs - Not included in
rootfs.tar
Path mapping mirrors the target filesystem. For example:
user/common/overlay/etc/init.d/S99local
becomes:
/etc/init.d/S99local
Use this when you want to provide a full file as-is.
If the same file exists in multiple overlay scopes, the device-scoped copy wins.
Paths:
user/common/opt/
user/<camera>/opt/
user/<camera>/<ip>/opt/
Purpose:
- Adds user content to the extras partition mounted at
/opt - Suitable for optional binaries, models, helper scripts, and other large or user-managed add-ons that do not belong in the main rootfs
Build behavior:
- Files from
OUTPUT_DIR/target/opt/are first copied intoOUTPUT_DIR/extras/ - Then user
opt/directories are copied in scope order: global, then camera, then device - The result is packed into
images/extras.jffs2
Important detail:
- The build now recreates
OUTPUT_DIR/extras/before layering user content - If the same file exists in multiple scopes, the device-scoped copy wins
Paths:
user/common/local.uenv.txt
user/<camera>/local.uenv.txt
user/<camera>/<ip>/local.uenv.txt
Purpose:
- Adds local U-Boot environment entries
- Useful for extra boot arguments or user-specific environment variables
Build behavior:
configs/common.uenv.txtis read- The camera's
<camera>.uenv.txtis read - User U-Boot fragments are read in scope order: global, then camera, then device
- Comment lines and blank lines are removed
- The combined file is deduplicated with
sort -u - Generated partition-specific lines such as
mtdparts,bootcmd,kern_addr, andkern_sizeare rewritten at the end
In practice, use this for additional environment keys, not for replacing the auto-generated partition layout.
The user-scoped JSON import hooks wired into the build are:
user/common/thingino.json
user/<camera>/thingino.json
user/<camera>/<ip>/thingino.json
user/common/motors.json
user/<camera>/motors.json
user/<camera>/<ip>/motors.json
Other JSON files are handled differently:
/etc/thingino.jsonsupports user-layered import throughthingino.json/etc/motors.jsonsupports user-layered import throughmotors.json/etc/prudynt.jsoncamera defaults are controlled through camera-scopedprudynt.json, not throughTHINGINO_USER_DIR- Other JSON configs such as
/etc/prudynt.jsonor/etc/timelapse.jsondo not have a generic user-side merge hook in the current build system
If you need to seed one of those files from your user tree, provide the full file
through user/common/overlay/, for example:
user/common/overlay/etc/prudynt.json
user/common/overlay/etc/timelapse.json
The same pattern also works in camera-scoped and device-scoped overlay trees.
That replaces the file content in the config overlay rather than merging JSON objects key-by-key.
Use local.fragment when you need to change Buildroot symbols.
Use local.mk when you need Buildroot OVERRIDE_SRCDIR or other package
override variables.
Use thingino.json when you need to add or override keys in
/etc/thingino.json.
Use user/common/overlay/ when you need to place complete files into the writable
config partition.
Use user/common/opt/ when you need files in the extras partition at /opt.
Use local.uenv.txt for additional U-Boot environment variables.
Use camera-scoped paths when the customization applies to every device of one camera model.
Use device-scoped paths when the customization should apply only to one unit,
selected by the build-time IP value.
user/
├── common/
│ ├── local.fragment
│ ├── local.mk
│ ├── thingino.json
│ ├── local.uenv.txt
│ ├── opt/
│ │ └── bin/
│ │ └── my-helper
│ └── overlay/
│ └── etc/
│ ├── init.d/
│ │ └── S99local
│ └── ssl/
│ └── my-ca.pem
└── atom_cam2_t31x_gc2053_atbm6031/
├── local.fragment
├── local.mk
├── thingino.json
├── local.uenv.txt
└── 192.168.88.31/
├── local.fragment
├── local.mk
├── thingino.json
├── local.uenv.txt
└── overlay/
└── etc/
└── hostname
docs/overlayfs.mdexplains whyuser/common/overlay/ends up in the writable overlay partitiondocs/local-overrides.mdcovers package source overrides throughlocal.mkdocs/makefile.mdcovers the general build workflow and config generation