Configuration Guide¶
OWL uses two configuration files to separate detection settings from infrastructure settings. Both are standard INI files that can be edited with any text editor or adjusted through the web dashboard.
Config File Overview¶
File |
Location |
Purpose |
|---|---|---|
|
|
Detection parameters, camera, sensitivity presets, tracking, relay mapping |
|
|
MQTT, network mode, GPS, web dashboard, actuation timing |
An active_config.txt file in the same directory points to the current detection config (defaults to config/GENERAL_CONFIG.ini).
Copy-on-write protection
GENERAL_CONFIG.ini and CONTROLLER.ini are protected defaults. When changes are made through the dashboard, a timestamped copy is created automatically (e.g. GENERAL_CONFIG_20260318_1430.ini) so the originals are never corrupted.
Sensitivity Presets¶
Instead of separate config files for each sensitivity level, OWL embeds sensitivity presets directly in GENERAL_CONFIG.ini:
[Sensitivity]
active = medium
[Sensitivity_Low]
exg_min = 25
exg_max = 200
hue_min = 41
hue_max = 80
...
[Sensitivity_Medium]
exg_min = 25
exg_max = 200
hue_min = 39
hue_max = 83
...
[Sensitivity_High]
exg_min = 22
exg_max = 210
hue_min = 35
hue_max = 85
...
Each preset stores the 9 GreenOnBrown threshold values. The [Sensitivity] active key determines which preset is loaded at startup. You can switch presets from the dashboard, the wired controller sensitivity switch, or the AI assistant.
Custom presets can be saved from the dashboard and are stored as additional [Sensitivity_YourName] sections in the config file.
Changing Settings¶
There are several ways to adjust OWL settings:
- Dashboard sliders (recommended)
The web dashboard provides range sliders for all detection parameters. Changes take effect immediately and are persisted to the config file.
- AI assistant
Open the Agent tab in the dashboard and describe what you want in plain language — e.g. “make detection more sensitive” or “switch to the exg algorithm”. See the AI Assistant guide.
- Direct INI editing
Edit the config file directly with a text editor:
nano ~/owl/config/GENERAL_CONFIG.ini
Save the file and restart OWL for changes to take effect.
- Command line flags
Override the config file or enable display mode at startup:
# Use a specific config file
python owl.py --config config/my_custom.ini
# Show display for threshold adjustment
python owl.py --show-display
# Camera focus mode
python owl.py --focus
# Process a video or image directory
python owl.py --input path/to/media
Complete GENERAL_CONFIG.ini Template¶
[System]
algorithm = exhsv
input_file_or_directory =
relay_num = 4
actuation_duration = 0.15
delay = 0
actuation_top = 0.0
actuation_bottom = 1.0
actuation_zone = 100
on_error = off
[Controller]
controller_type = none
status_led_pin = 40
gps_led_pin = 38
switch_purpose = recording
switch_pin = 36
detection_mode_pin_up = 36
detection_mode_pin_down = 35
recording_pin = 33
sensitivity_pin = 32
[Visualisation]
image_loop_time = 5
[Camera]
resolution_width = 1456
resolution_height = 1088
exp_compensation = -2
awb_mode = auto
awb_red_gain = 2.0
awb_blue_gain = 2.0
rotation = auto
crop_left = 0.02
crop_right = 0.02
crop_top = 0.02
crop_bottom = 0.02
crop_factor_horizontal = 0.02
crop_factor_vertical = 0.02
camera_type = auto
[GreenOnGreen]
model_path = models
confidence = 0.5
detect_classes =
actuation_mode = centre
min_detection_pixels = 50
inference_resolution = 320
crop_buffer_px = 20
[GreenOnBrown]
exg_min = 25
exg_max = 200
hue_min = 39
hue_max = 83
saturation_min = 50
saturation_max = 220
brightness_min = 60
brightness_max = 190
min_detection_area_percent = 0.0075
invert_hue = False
[DataCollection]
image_sample_enable = False
detection_enable = True
sample_method = whole
sample_frequency = 30
sample_mode = frames
sample_distance_m = 1.0
sample_interval_s = 2.0
jpeg_quality = 95
record_mode = images
video_max_s = 60
shutter_cap = False
smear_limit_mm = 2.0
min_speed_kmh = 1.0
save_directory = /media/owl/SanDisk
log_fps = False
camera_name = cam1
storage_location = auto
internal_save_directory = /home/owl/owl_images
min_free_gb = 4
image_quota_gb = 12
[Relays]
0 = 13
1 = 15
2 = 16
3 = 18
[Tracking]
tracking_enabled = False
track_class_window = 5
track_crop_persist = 3
[Sensitivity]
active = medium
[Sensitivity_Low]
exg_min = 25
exg_max = 200
hue_min = 41
hue_max = 80
saturation_min = 52
saturation_max = 218
brightness_min = 62
brightness_max = 188
min_detection_area_percent = 0.015
[Sensitivity_Medium]
exg_min = 25
exg_max = 200
hue_min = 39
hue_max = 83
saturation_min = 50
saturation_max = 220
brightness_min = 60
brightness_max = 190
min_detection_area_percent = 0.0075
[Sensitivity_High]
exg_min = 22
exg_max = 210
hue_min = 35
hue_max = 85
saturation_min = 40
saturation_max = 225
brightness_min = 50
brightness_max = 200
min_detection_area_percent = 0.004
Complete CONTROLLER.ini Reference¶
[MQTT]
enable = True
broker_ip = localhost
broker_port = 1883
device_id = auto
[WebDashboard]
port = 8000
[Network]
mode = networked
static_ip = localhost
controller_ip = localhost
[GPS]
# GPS data source (none / serial / tcp / gpsd)
source = none
# Serial device path and baud (only used when source = serial)
port = /dev/ttyACM0
baudrate = 9600
# TCP listener (only when source = tcp). `enable = True` is a deprecated
# back-compat alias for `source = tcp`.
enable = False
nmea_port = 8500
# Session / coverage tracking (applies to every non-none source)
boom_width = 12.0
track_save_directory = tracks
[Actuation]
# Relay timing — used as fallback when no GPS speed data
actuation_duration = 0.15
delay = 0.0
# Speed-adaptive actuation geometry (networked controller, or an OWL with its own GPS)
actuation_length_cm = 10
offset_cm = 30
speed_avg_window = 2.0
[Security]
# Per-device API token, minted automatically when the OWL is provisioned
# through the phone app (v3.11+). Guards mutating dashboard routes
# (network changes, passwords, shutdown/reboot, session delete) via the
# X-Device-Token header. Absent on units set up before v3.11 - those
# accept requests without the header (legacy behaviour). Deleting the
# section restores legacy behaviour; re-running first-boot setup mints
# a fresh token.
device_token = <minted-at-provisioning>
Parameter Reference — GENERAL_CONFIG.ini¶
System¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Detection algorithm: |
|
(empty) |
Path to image, video, or directory for offline processing. Leave empty for live camera. |
|
|
Number of relay lanes (1-4). |
|
|
Relay activation time in seconds. |
|
|
Delay between detection and actuation in seconds. |
|
|
Top of the actuation band as a fraction of the cropped height ( |
|
|
Bottom of the actuation band as a fraction of the cropped height ( |
|
|
Legacy bottom-anchored zone (% from bottom). Used only if |
|
|
What the nozzles do on a spraying fault: the camera stops sending frames, dashboard and switch control stops updating, or a relay is stuck on. |
Status LED fault codes. The status LED flashes a code, repeated, while a fault or error is active:
Flashes |
Meaning |
|---|---|
1 |
Camera or software dependency failed at start |
2 |
Camera not found at start |
3 |
Camera lost while running (spraying fault) |
4 |
Dashboard and switch control stopped updating (spraying fault) |
5 |
Storage full or storage error |
6 |
No USB drive for recording, or the drive was removed |
7 |
A relay is stuck on (spraying fault) |
Controller¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Wired controller type: |
|
|
BOARD pin for status indicator LED (shared by Ute and Advanced). |
|
|
BOARD pin for GPS status LED (shared by Ute and Advanced). |
|
|
Ute controller switch function: |
|
|
BOARD pin for Ute controller toggle switch. Avoid BOARD 37 if using a Sixfab HAT. Pins used by on-device GPS hardware are refused (see below). |
|
|
BOARD pin for detection mode up (Advanced controller). |
|
|
BOARD pin for detection mode down (Advanced controller). |
|
|
BOARD pin for recording toggle (Advanced controller). |
|
|
BOARD pin for sensitivity switch (Advanced controller). |
Camera¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Camera capture width in pixels. |
|
|
Camera capture height in pixels. |
|
|
Exposure compensation (-8 to 8). Negative values give faster shutter speed. Applied live (no restart) on picamera2. |
|
|
White balance: |
|
|
Gain limit while the shutter limit is active: |
|
|
Red colour gain (0.1-8.0), used only when |
|
|
Blue colour gain (0.1-8.0), used only when |
|
|
Image rotation: |
|
|
Fraction of width cropped from the left edge (0.0-0.49). |
|
|
Fraction of width cropped from the right edge (0.0-0.49). |
|
|
Fraction of height cropped from the top edge (0.0-0.49). |
|
|
Fraction of height cropped from the bottom edge (0.0-0.49). Detection runs only inside the remaining rectangle; relay lanes are spread across the cropped width and recenter automatically. |
|
|
Legacy symmetric horizontal crop. Used only if |
|
|
Legacy symmetric vertical crop. Used only if |
|
|
Camera selection: |
|
|
Bypass the Pi 3/4 832x640 safety clamp. Leave |
Geometry lives in GEOMETRY.ini
The crop edges (crop_left/right/top/bottom) and the actuation band
(actuation_top/bottom) are mount geometry — physical to how a unit is
bolted on. They live in their own config/GEOMETRY.ini, which OWL reads last
so it always wins over any values in the active config. This means switching or
saving a named detection config (sensitivity, algorithm, thresholds) never
changes a unit’s geometry, and geometry is never copied into named configs. Set
it with the dashboard’s Adjust geometry editor (which writes GEOMETRY.ini)
or by editing GEOMETRY.ini by hand. The keys are documented in the [Camera]
and [System] tables for reference.
Setting the resolution¶
Resolution can be changed two ways — pick whichever fits your workflow.
Dashboard Config tab. Open the dashboard → CONFIG tab → Camera section → Resolution dropdown. The
dropdown offers the supported presets (1456x1088, 1280x960, 1280x720, 1024x768, 800x600, 640x480, 416x320, 320x240) and
writes both resolution_width and resolution_height to the active config in a single step. Changes are persisted via
the copy-on-write mechanism described above.
Direct INI editing. Set resolution_width and resolution_height independently under [Camera], then restart OWL
for the change to take effect. This route is fine when you’re already editing the file for other reasons, or when you
need a resolution outside the dashboard preset list.
Pi 3 / Pi 4 safety clamp
On the Raspberry Pi 3 and Pi 4, requests above 832x640 are silently clamped to 640x480. Older Pi models are known to lock up at higher resolutions, and the clamp prevents bricked devices in the field.
To override the clamp, set [Camera] allow_high_resolution = True — but only after verifying that the specific Pi
hardware in front of you actually copes with the resolution you’re requesting. The dashboard surfaces an active clamp
via a banner in the Camera section.
The Pi 5 is not affected by the clamp.
Visualisation¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Seconds between display frame updates when using |
GreenOnGreen¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Directory containing YOLO model files (NCNN or PyTorch). |
|
|
Minimum confidence threshold for detections (0.0-1.0). |
|
(empty) |
Comma-separated class IDs to detect. Empty means all classes. |
|
|
How detections map to relays: |
|
|
Minimum pixel count for valid segmentation detections (zone mode). |
|
|
Input image size for YOLO inference. Smaller is faster, larger is more accurate. |
|
|
Pixel buffer added around crop mask regions (gog-hybrid mode). |
GreenOnBrown¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Minimum Excess Green threshold (0-255). Lower = more sensitive. |
|
|
Maximum Excess Green threshold (0-255). Keep above 180. |
|
|
Minimum hue threshold (0-179). Green vegetation is typically 39-83. |
|
|
Maximum hue threshold (0-179). |
|
|
Minimum saturation threshold (0-255). |
|
|
Maximum saturation threshold (0-255). |
|
|
Minimum brightness threshold (0-255). |
|
|
Maximum brightness threshold (0-255). |
|
|
Minimum weed size as a percentage of the detection frame area (0.0005-5). Increase to ignore small noise. Resolution-independent. |
|
|
When True, detects colours outside the hue range instead of inside. Useful for detecting non-green targets. |
!!! note “Legacy min_detection_area”
Older configs used min_detection_area (contour area in pixels). It is still accepted: OWL converts it to min_detection_area_percent at startup and removes the px key the next time the config is saved. New configs should use the percent key only.
DataCollection¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Enable image data collection to USB drive. |
|
|
Enable weed detection and relay actuation. Set False for data collection only. |
|
|
Image sampling method: |
|
|
Save an image every N frames (when |
|
|
Which frames are saved: |
|
|
Distance mode: metres travelled between images. |
|
|
Time mode, and distance mode without GPS: seconds between images. |
|
|
JPEG quality of saved images (50-100). |
|
|
|
|
|
Video clip length limit in seconds (5-60). |
|
|
Limit the shutter time so ground movement during one exposure (GPS speed x shutter time) stays under |
|
|
Blur limit for |
|
|
No still images while a fresh GPS speed is below this, for example parked or turning at a headland. |
|
|
Directory for saved images. Typically a USB drive mount point. |
|
|
Log frame processing times to file. |
|
|
Camera identifier used in saved file names. |
|
|
Where recordings go: |
|
|
Recording path used in internal storage mode. |
|
|
Internal mode only: recording is refused, and stops within seconds, when free disk space falls below this floor. Recording becomes available again once sessions are deleted or downloaded. |
|
|
Image allowance shown on the app/dashboard storage bar. Display only — the enforced limit is |
Tracking¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Enable ByteTrack multi-object tracking for persistent weed IDs across frames. |
|
|
Number of frames to smooth class predictions over. Reduces class flickering. |
|
|
Number of frames a crop mask region persists after the crop leaves frame (gog-hybrid mode). |
Relays¶
Parameter |
Description |
|---|---|
|
Relay 0 maps to GPIO board pin 13 |
|
Relay 1 maps to GPIO board pin 15 |
|
Relay 2 maps to GPIO board pin 16 |
|
Relay 3 maps to GPIO board pin 18 |
Sensitivity¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Active sensitivity preset name. Built-in presets: |
Parameter Reference — CONTROLLER.ini¶
MQTT¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Enable MQTT communication with the dashboard. |
|
|
MQTT broker IP address. |
|
|
MQTT broker port. |
|
|
Unique device identifier. |
WebDashboard¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Gunicorn listening port (behind Nginx reverse proxy). |
|
|
In-cab controller only: the screen the kiosk opens. |
Collect¶
Read by the in-cab controller’s data-collection screen and, since v3.23, by the standalone dashboard (on the OWL’s own CONTROLLER.ini). Written by the record form and the collect screen’s Settings tab. Every key is optional and can be edited by hand.
Parameter |
Default |
Description |
|---|---|---|
|
|
Language of the record form and capture settings: |
|
built-in lists |
Comma-separated preset slugs for the record form. Values go into image EXIF and |
|
empty |
The choices from the last Start recording, pre-selected next time. Written automatically; notes are never saved here. |
|
|
In-cab controller only: capture settings pushed to every OWL (see DataCollection above). A standalone OWL keeps these in its own active config ( |
|
|
Warning thresholds for the collect screen. |
Network¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Network mode: |
|
|
Static IP for the OWL on the network. |
|
|
IP address of the networked controller (networked mode only). |
Pins used by on-device GPS. When [GPS] source = nrf91, BOARD 37 (GPIO 26, modem power) and BOARD 21 and 24 (the modem UART) belong to the modem. When source = serial with port = /dev/ttyAMA0 or /dev/serial0, BOARD 8 and 10 belong to the GPS. A relay, LED or switch configured on one of these pins is disabled at startup with an error in the journal (the OWL keeps running), and the dashboards, phone app and networked controller refuse the change. Without that hardware configured, nothing is reserved.
GPS¶
The networked controller supports four GPS input modes, selected via the source key. Pick whichever matches your hardware — only the keys relevant to your chosen source are read.
Parameter |
Default |
Description |
|---|---|---|
|
|
GPS data source. One of: |
|
|
Serial device path (used when |
|
|
Serial link speed (used when |
|
|
DEPRECATED. Kept as a back-compat alias: if |
|
|
TCP port the controller listens on when |
|
|
Spray boom width in metres. Used to convert distance into hectares: |
|
|
Directory (relative to project root) where session GeoJSON track files are written as |
The GPS tab in the dashboard renders these breadcrumbs on a Leaflet map (OpenStreetMap tiles when online, dark grid fallback when offline) with a HDOP-coloured current-position marker and Centre/Fit-all controls.
Actuation¶
Parameter |
Default |
Description |
|---|---|---|
|
|
Relay activation time in seconds (fallback when no GPS speed data). |
|
|
Travel time from the camera’s view to the nozzle, in seconds, counted from frame capture. The OWL subtracts the frame’s processing time before scheduling the relay, so |
|
|
Physical spray patch length in cm (speed-adaptive calculation). |
|
|
Distance from camera to nozzle in cm (speed-adaptive calculation). |
|
|
Time window in seconds for GPS speed averaging. |
With GPS speed, spray duration is actuation_length_cm / speed, stretched to
cover the ground travelled between frames (at most 3 patch lengths), and the
delay is offset_cm / speed. An OWL with its own GPS ([GPS] source = serial
or nrf91) computes this itself while it has a fresh fix, and that wins over
the networked controller’s broadcast; when the fix is lost it uses the
controller’s timing, or the fixed values above if there is no controller. Phone
GPS never drives timing. When forward speed makes sprays land late or leaves
gaps between frames, the dashboards and phone app show a warning with a speed
to slow to.
Security¶
Parameter |
Default |
Description |
|---|---|---|
|
(minted at setup) |
Per-device API token (v3.11+, contract 2). |
Cloud¶
Written by owl_cloud_provision.sh (run by hand, or by the phone app after the unit is
claimed in Noktura). See the OWL app claim flow for how a shipped unit gets these values.
Parameter |
Default |
Description |
|---|---|---|
|
|
Master switch for the cloud bridge. |
|
|
Cloud MQTT broker (TLS). |
|
(issued at claim) |
Site slug on the cloud side, e.g. |
|
(issued at claim) |
Bridge login. The password lives only in |
|
|
CA used to verify the cloud broker certificate. The template default is the file |
|
(blank) |
When set, the dashboard and app show an “Open in Noktura” link to |
|
(blank) |
v3.18+. Every upload or download a cloud command asks for must be HTTPS on this host. The phone app sets it when it connects the OWL to Noktura. Blank (manual provisioning) accepts any HTTPS host. |
Cloud-origin commands arrive on a separate local topic and pass an allowlist: list, upload,
preview and delete recorded sessions, update the software to a release tag (vX.Y.Z, never a
branch), download and select a detection model, restart the OWL service and reboot. Nothing else
from the cloud runs: no detection toggles, no configuration edits, no branch installs. The local broker itself listens on
loopback only (v3.18+): the phone app and kiosk reach the OWL through HTTPS, never MQTT.
Next Steps¶
Controller Setup - Build a controller for managing OWLs
Green-on-Green - In-crop weed detection using YOLO
AI Assistant - Control OWL through natural language
Troubleshooting - Common issues and solutions