Skip to content
Research
Page 5 of the Heeley City Farm paper: the live dashboard and the vision model approach, a table of pre-training results for the three models, and the formula for the Matthews correlation coefficient
A page from the paper2026

Heeley City Farm

Monitoring since July 2026From March 2026

Farm welfare is usually checked by people on rounds, and nobody can watch five enclosures round the clock. I built sensors that judge welfare on the spot every 30 seconds, a camera that deletes any photo with a person in it, and a public dashboard anyone can open.

Download research PDF, 2.8 MB

Licensed under CC BY 4.0.

Inside one of the weatherproof sensor units: the Raspberry Pi camera module, a sensor board and loops of coloured jumper wire in the grey box
Inside a sensor unit. Photo, June 2026

The problem

Farm welfare is usually checked by people on rounds, now and then, and what they notice depends on who is looking. Nobody can watch five enclosures round the clock. Most monitoring is built for large commercial farms or for single animals wearing sensors, which leaves community farms like this one out, and the farm wanted live welfare readings that anyone visiting could see.

Welfare is normally assessed through periodic manual inspection, which is labour-intensive, intermittent and subjective. Existing monitoring targets large commercial operations or individual animals with wearables, so community and urban farms are underserved. The requirement was live, public welfare data at the point of visit across five enclosures: goats, sheep, cattle, ponies and chickens.

What I did

I built five weatherproof sensor units and installed one in each enclosure. Each judges welfare against the Five Freedoms every 30 seconds, deletes any photo with a person in it, and feeds a public dashboard that needs no login. Offline, I trained vision models on the photos to see whether a camera could spot what the sensors notice.

I assembled and installed five Raspberry Pi 4B units and wrote the Python polling service that classifies each reading against species ranges on the device. A YOLOv8n person check deletes frames before any path is recorded. Readings land in two Supabase tables, which a TypeScript Edge Function serves to a static public dashboard. An offline PyTorch pipeline trains and compares EfficientNet-B0, ResNet-50 and Swin Tiny on the overhead images.

How it works

  1. Measure

    A weatherproof box in each of the five enclosures reads temperature, humidity, light, movement and sound every 30 seconds.

  2. Judge on the spot

    The box checks each reading against what that species needs, using the Five Freedoms, and marks each one good or worth a look.

  3. Leave people out

    A camera takes overhead photos for later. If a person is in one, the box deletes it before it is ever saved.

  4. Keep a copy

    Every reading is written to the box itself and sent online when the connection allows, so a dropped signal loses nothing.

  5. Show everyone

    A public dashboard shows each enclosure's status and readings, refreshed every 30 seconds, for staff and visitors alike.

  6. Learn later

    On my own computer I train vision models on the photos, to see whether a camera can spot what the sensors notice.

On each of the five units

  1. 01Sense

    Five identical units, one per enclosure, each in an IP65 box with the camera looking through the clear lid.

    • Raspberry Pi 4B 4 GB
    • DHT22
    • BH1750
    • PIR, 7 m
    • MAX9814 + MCP3008
    • Camera Module 3
  2. 02Poll and classify

    A Python service under systemd samples all five sensors every 30 seconds and marks each freedom good or attention against species ranges. Freedom 2 uses the temperature-humidity index, with an onset of 74 for poultry and 72 for the rest.

    THI = (1.8T + 32) − (0.55 − 0.0055·RH)(1.8T − 26)
    • Python
    • systemd
  3. 03Log locally

    Every reading is appended to a log on the SD card before anything leaves the device, so connectivity loss costs nothing.

    readings.jsonl
  4. 04Person check

    YOLOv8n through ONNX Runtime runs in 156 ms on the Pi 4B. A person above 0.5 confidence and the frame is deleted before any path is recorded. ONNX because the PyTorch runtime crashes on the Pi's Cortex-A72.

    os.remove()
    • YOLOv8n
    • ONNX Runtime

In the cloud

  1. 05Store

    Each cycle writes the raw readings and the classification over HTTPS to two Supabase PostgreSQL tables.

    raw_readings, classifications
    • Supabase
    • PostgreSQL
  2. 06Serve

    A TypeScript Edge Function merges both tables into one public JSON response that carries welfare indicators and environmental values only.

    • Edge Function
    • TypeScript
  3. 07Show

    A static dashboard fetches that response every 30 seconds and draws a Five Freedoms card per enclosure. No login.

    • Static site

On my machine, offline

  1. 08Learn offline

    Images and the local logs are pulled from each Pi over SSH through Tailscale. Labels are recomputed with the current classifier, models train in PyTorch on Apple Silicon, and predictions go back to the dashboard.

    • Tailscale
    • PyTorch
    • EfficientNet-B0
    • ResNet-50
    • Swin Tiny
Simplified. One of the five identical units is shown, and capture schedules, retries and the dashboard's comparison panel are left out.

In numbers

  • 88,000+

    Polling cycles classified on the devices

  • 30s

    Polling loop and dashboard refresh

  • 156ms

    Person detection on a Pi 4B

  • 13,692

    Labelled overhead images, 1 to 21 July 2026

  • +0.732MCC

    Best specialist: ponies, EfficientNet-B0

    Single run, indicative

  • +0.540MCC

    Best generalist: ResNet-50

    Single run, indicative

  • ≈80THI

    Poultry enclosure at 29°C and 43% humidity

    Onset for poultry is 74

  • ≈7×

    More training-eligible rows in the local logs than in the database

My part

  1. Edge software

    I wrote the polling service, the Five Freedoms classifier and the person check that run on each Raspberry Pi.

    All of it

  2. Cloud and dashboard

    I set up the two database tables and the Edge Function, and built the public dashboard.

    All of it

  3. Vision models

    I built the offline training pipeline, trained the models and ran every comparison.

    All of it

  4. Hardware

    I assembled and installed all five weatherproof sensor units, one per enclosure.

    All of it

  5. Co-design

    A workshop with farm staff, run by two of my co-authors from the university, set what to measure and how the dashboard should read.

    Part of it

  6. Research

    I am first author of the paper, written with four co-authors and accepted at IoT 2026.

    Part of it

Built with

Raspberry Pi 4B
Five edge units
Python and systemd
Polling service and classifier
YOLOv8n and ONNX Runtime
Person check on the device
Supabase
PostgreSQL tables and the Edge Function
TypeScript
The Edge Function
Tailscale
SSH access to the units
PyTorch
Offline training
EfficientNet-B0, ResNet-50, Swin Tiny
The models compared

In pictures

Me inside an enclosure at Heeley City Farm, reaching up to a sensor unit on the wall with a laptop in my other hand, the window bright behind me
Working on a sensor unit at the farm. Photo, 2026
The sensors on the bench before deployment: temperature and humidity sensors on a small green breadboard, wired to a Raspberry Pi 4 with the camera ribbon cable
The sensors on the bench. Photo, 2026
Page 3 of the Heeley City Farm paper: the system design, the hardware table and the sensing and classification approach
A page from the paper, 2026

From the work

These are pictures of the project's own code running on simulated input. They show how it works, not what it found.

The internal welfare dashboard refreshing on simulated readings, one enclosure turning amber on an injected breach
The live dashboardScreen recording, simulated input2026
The edge unit's poll loop on simulated sensor input: eight cycles in the terminal, and one line of its local log
The edge unit's poll loopRendered from the project's code, simulated input2026
The camera burst filter on five drawn frames: near-duplicates dropped, one frame kept, and a frame with a drawn stand-in figure deleted by the person check
Five frames in, one keptRendered from the project's code, simulated input2026
Simulated readings from five enclosures posted to the pipeline, and the rule-based classifier's answer for each of the Five Freedoms on an injected breach
Readings in, classifications outRendered from the project's code, simulated input2026
The training script's two-phase schedule on a small set of drawn images, with its loss curve. A demonstration on simulated input, not results
A training run, not resultsRendered from the project's code, simulated input2026

Where it stands

The units have been monitoring the farm since July 2026, with more than 88,000 polling cycles classified. The paper was accepted at IoT 2026 in Newcastle.

What it found

The chickens were too warm. A typical reading of 29°C and 43% humidity gives a heat stress score of about 80, against a threshold of 74 for poultry. The dashboard flagged it continuously, and the staff were made aware through it.

Four of the five freedoms flagged nothing in 21 days, apart from the poultry heat.

Movement was the only thing that varied, so it was the only thing worth teaching a camera. The best model, for the ponies, scored +0.732 on a scale where 0 is chance and 1 is perfect. That was a single run, so read it as a promising sign rather than a result.

Every model result here comes from a single run, so each is indicative only.

Pre-training on a public overhead pig dataset, not farm data, put EfficientNet-B0 at 91.8% validation accuracy. Swin Tiny reached 74.6% after collapsing at epochs 8 to 9, and ResNet-50 64.9%.

Freedom 4, movement, was the only freedom with variation: 1,200 attention rows. Swin Tiny trailed ResNet-50 on three of five specialists, so the idea that a transformer reads a whole enclosure better was not supported here.

A CNN-LSTM over five consecutive chicken frames reached +0.416 against +0.379 for one frame. Adding the PIR readings gave +0.417, no further value. In embedding space, attention and good images sit in essentially the same place (d = 0.102), so Freedom 4 is not something a single frame can show.

What went wrong

  • A filter that only knew animals from the side

    I tried a ready-made detector to throw out photos with no animals in them. It had learned animals side-on, so looking down from above it rejected over 89% of my images. I switched it off.

    YOLOv8n, trained on side-on, ground-level COCO images, rejected over 89% of overhead frames across all enclosures. It is disabled, and an overhead animal classifier is future work. The person check on the device is a different task and works.

  • A smudged lens

    From 13 July the chicken camera's lens was partly obscured, and 63% of the chicken photos in the second training round were affected.

    A camera_clean column in the training data records image quality, so the effect can be audited on its own. ResNet-50 still reached +0.656 on chickens; convolution may cope better with uniform haze.

  • The goats could not be learned

    The goat pen raised only three movement alerts in the training data. No model learns from three examples, so every one scored zero.

    Three attention rows in training. MCC 0.0 for EfficientNet-B0, Swin Tiny and ResNet-50 alike.

  • A wild pigeon triggered the PIR in the poultry enclosure once, which is the limit of a fixed motion sensor in an open pen.

  • A different species was in the sheep pen from 8 July 2026, so some labels stopped matching the animals in the photos.

  • Two units lost their connection now and then. The local logs kept everything, with about seven times more training-eligible rows than the database.

What comes next

A longer deployment, so the other freedoms get a chance to raise alerts, and more data, so the goats can be learned. Then an overhead animal detector to replace the one I switched off, and the sequence model at a larger scale.

A second phase across more farms has been discussed.

Explanation

Hi. Tell me your name and I will use it now and then as you look around. It stays on this device. I never collect it, so not even I will know.