Contents — 27 sections
Project Overview
A CNN that diagnoses plant leaf diseases from a single phone photo — putting an agronomist's eye in every farmer's pocket.
A plant disease caught early can be treated; caught late, it can take a whole crop — and the difference is often a diagnosis a smallholder farmer has no easy way to get. Yet many crop diseases show clear visual symptoms on the leaves: characteristic spots, blights, rusts, mildews and discolourations that a trained eye can identify. This project builds a CNN image classifier that reads those symptoms from a single phone photo of a leaf and names the likely disease — putting an expert diagnostic eye in the pocket of anyone with a phone.
It is an image classification problem: given a photo of a leaf, output the disease class (or "healthy"). A convolutional neural network (CNN) — the architecture that revolutionised image recognition — learns, from thousands of labelled leaf images, the visual features that distinguish each disease: the shape, colour and pattern of the lesions. The practical route is transfer learning — starting from a network pretrained on general images and fine-tuning it on the leaf dataset — which achieves strong accuracy with far less data and compute than training from scratch, and makes the model small enough to run on a phone for offline, in-field use.
The value is accessible, instant, early diagnosis that guides treatment and reduces crop loss. It is honest about the gap between a benchmark and a field tool: models trained on clean lab images (like the popular PlantVillage set) often degrade on real field photos with messy backgrounds, mixed lighting and co-occurring problems; the classifier only knows the crops and diseases it was trained on; and it should advise, not dictate — a confident-looking label can be wrong, so it must be framed as decision support with a path to expert confirmation for serious calls. Built and framed honestly, it is both a genuinely valuable agricultural tool and a clear, complete lesson in CNN image classification and transfer learning.
What this project does
- Diagnoses plant leaf diseases from a single photo
- Classifies a leaf image into a disease (or healthy)
- Learns disease features with a CNN
- Uses transfer learning for accuracy with less data
- Runs on a phone/edge for offline in-field use
- Guides early treatment to reduce crop loss
- Advises rather than dictates (decision support)
Real-World Applications
| Setting | How it is used |
|---|---|
| Smallholder crop diagnosis | Instant, accessible leaf-disease diagnosis by phone. |
| Agri-advisory services | Scaling expert diagnosis to many farmers. |
| Farm scouting | Early detection during field walks. |
| Plant-health education | Learning to recognise disease symptoms. |
Deployment contexts where a build of this kind earns its keep.
Features & Capabilities
- CNN image classification of leaf diseases
- Transfer learning from a pretrained network
- Confidence-scored predictions
- Phone/edge (offline) deployment
- Per-crop/disease vocabulary
- Decision-support framing
- Honest about lab-vs-field gap and scope
Difficulty, Time & Required Skills
| Attribute | Value |
|---|---|
| Difficulty level | Intermediate |
| Estimated completion time | 12–18 hours |
| Indicative build cost | Software; compute-dependent |
| Primary discipline | Computer Vision |
| Reference platform | Phone/edge or GPU workstation |
Skills you should have (or will pick up)
- CNN image classification
- Transfer learning / fine-tuning
- Data handling, augmentation, class balance
- Confidence and evaluation (accuracy, confusion)
- Edge/phone deployment and honest scoping
Bill of Materials
Every part below is commonly available from Indian and international hobby-electronics suppliers. Prices are indicative 2026 retail figures in Indian rupees and will drift — treat them as a budgeting guide, not a quotation.
| Component | Key specification | Qty | Approx. cost |
|---|---|---|---|
| Raspberry Pi Camera Module 3 Pi 5 uses a narrower 22-pin CSI cable — the old 15-pin ribbon will not fit. | 12 MP IMX708, autofocus, HDR, 1080p50, CSI-2 ribbon | 1 | ₹2,600 |
| Phone/edge or GPU | Phone/edge for inference; GPU for training | 1 | — |
| Pretrained CNN | ImageNet-pretrained backbone for transfer learning | 1 | — |
| Leaf dataset Field images crucial for real use | Labelled leaf images per crop/disease (lab + field ideal) | 1 | — |
| Camera/phone | For capturing leaf photos | 1 | — |
Estimated total: ₹2,600, excluding tools, shipping and consumables.
Tools and consumables
- Soldering iron (temperature controlled, 350 °C) with 0.8 mm 60/40 or lead-free solder
- Digital multimeter — continuity, DC volts and current ranges
- Wire strippers, flush cutters and a small set of precision screwdrivers
- Heat-shrink tubing and a heat gun (or a lighter, carefully)
- A laptop with a USB port and the toolchain listed above
Hardware Specifications
| Part | Specification | Supply | Interface | Reference |
|---|---|---|---|---|
| Raspberry Pi Camera Module 3 | 12 MP IMX708, autofocus, HDR, 1080p50, CSI-2 ribbon | 3.3 V via CSI | CSI-2 | Datasheet |
Consolidated electrical and interface specifications for every active part in the build.
Power Budget & Supply Sizing
Add up the typical active current of every part, then size the supply with at least 50 % headroom so transmit bursts and motor inrush never brown out the controller.
| Load | Supply rail | Typical current (mA) | Notes |
|---|---|---|---|
| Raspberry Pi Camera Module 3 | 3.3 V via CSI | 250 | Pi 5 uses a narrower 22-pin CSI cable — the old 15-pin ribbon will not fit. |
Summed typical draw is 250 mA. With a 1.5× design margin the supply should deliver at least 400 mA continuously at the stated rail voltage.
Software Requirements & Development Environment
Reference toolchain: Python 3.11 + PyTorch / TensorFlow. Anything newer normally works; anything older may lack the board definitions used here.
- Install the Arduino IDE 2.3.x (or PlatformIO if you prefer a real editor and dependency locking).
- Add
https://espressif.github.io/arduino-esp32/package_esp32_index.jsonunder File → Preferences → Additional Board Manager URLs, then install esp32 from the Boards Manager. - Set the correct port under Tools → Port. On Linux add yourself to the
dialoutgroup:sudo usermod -aG dialout $USERand log out and back in. - Open the Serial Monitor at 115200 baud — every sketch here logs its state there.
- Keep File → Preferences → Show verbose output during: compilation switched on while you are debugging build errors.
Required libraries
| Library | Why it is needed | Install |
|---|---|---|
| Python 3.11+ | Runtime for the analysis, training and service code. | sudo apt install python3 python3-venv python3-pip |
| PyTorch 2.4+ | Model definition, autograd and GPU training. | pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 |
| TensorFlow / Keras 2.17+ | High-level model building and the TFLite converter. | pip install tensorflow |
| NumPy 1.26+ | Vectorised array maths underpinning every other library here. | pip install numpy |
| scikit-learn 1.5+ | Classical models, preprocessing pipelines and evaluation metrics. | pip install scikit-learn |
Block Diagram
The block diagram shows the functional decomposition of the system — what senses, what decides, what acts, and where the data ends up.
Circuit Diagram & Wiring
The "wiring" is the inference data flow — a phone photo of a leaf is preprocessed and passed to the CNN, which outputs a disease class with confidence.
| Peripheral | Peripheral pin | Controller pin | Signal |
|---|---|---|---|
| Phone/camera | photo | — | Leaf image |
| Preprocess | resize/norm | — | Model input |
| CNN classifier | infer | — | Disease class |
| Advice output | display | — | Diagnosis + confidence |
Wire one row at a time and tick it off — most "it does not work" reports trace back to a single swapped pair.
Wiring explanation
- A phone or camera captures a leaf photo.
- The image is resized/normalised to the model input.
- The CNN classifies it into a disease (or healthy) with confidence.
- The result is shown as decision support, with a path to expert confirmation.
- Prefer field-representative training data for real-world accuracy.
System Architecture
Read the stack from the bottom up: physical hardware, the firmware that drives it, the transport that moves data off the device, and the software a human actually looks at.
Working Principle
Crop disease classification works because a great many plant diseases are, at heart, a visual pattern-recognition problem: the disease writes its signature on the leaf as characteristic lesions — the concentric rings of an early blight, the orange pustules of a rust, the powdery film of a mildew, the yellowing pattern of a nutrient or viral problem. A trained agronomist recognises these by eye, which means the knowledge is learnable from images. Framing diagnosis as image classification — photo in, disease label out — turns expert diagnosis into something a model can do, and a phone can carry.
The engine is a convolutional neural network, the architecture that made modern image recognition work. A CNN learns a hierarchy of visual features: early layers detect edges and colours, deeper layers combine them into textures and lesion shapes, and the final layers map those high-level features to disease classes. Crucially, the network learns the relevant features itself from labelled examples — you do not hand-engineer "detect concentric rings"; you show it thousands of labelled leaves and it discovers the discriminative patterns. This learned-feature capability is exactly why CNNs excel where fixed rules fail.
The practical key to doing this well with limited data is transfer learning. Training a large CNN from scratch needs enormous data and compute, which a leaf dataset rarely has. Instead, you start from a network pretrained on millions of general images — which has already learned broadly useful visual features (edges, textures, shapes) — and fine-tune it on the leaf dataset, adapting those features to the disease task. This achieves strong accuracy with a few thousand images and modest compute, and yields a model small and fast enough to run on a phone, offline, in the field, which is where a farmer actually needs it. Add data augmentation (rotations, crops, colour/lighting jitter) and the model generalises better from the data it has.
The honesty this project demands is about the gap between a benchmark and a field tool, and it is a gap that has embarrassed many crop-disease demos. Popular datasets (like PlantVillage) are often clean, single-leaf lab images on plain backgrounds, and a model trained on them can score superbly in testing yet fail on real field photos — cluttered backgrounds, mixed lighting, multiple leaves, co-occurring diseases, unfamiliar growth stages. So genuine field use needs field-representative training data, not just lab images, and realistic evaluation. The model also only knows the crops and diseases it was trained on — it will confidently mislabel anything outside that set — and its confidence scores are not certainty. For all these reasons it must be framed as decision support that advises, not dictates: it suggests a likely diagnosis and guidance, flags low-confidence cases for a retake or expert, and leaves serious or costly decisions to human confirmation. Built with that honesty — CNN plus transfer learning, trained on representative data, deployed on-phone, and clearly positioned as advice — it delivers real value (accessible, early, disease diagnosis that reduces crop loss) while being a complete, textbook lesson in the workhorse skills of image classification.
The maths behind it
Image classification
CNN(image) → class probabilities p over diseases
prediction = argmax(p)
confidence = max(p)
"healthy" is just one of the classes. Softmax over the
trained disease vocabulary.
Transfer learning
start from a network pretrained on general images
(already knows edges, textures, shapes)
replace/retrain the final layers on leaf classes
fine-tune → strong accuracy with FEW images + little compute
→ small enough to run on a phone offline.
Honest deployment
if confidence < THRESHOLD: advise retake / seek expert
knows ONLY trained crops/diseases → out-of-set = wrong
lab-trained → validate on FIELD images
Advise, do not dictate — decision support.
Program Flowchart
The firmware is a single cooperative loop. Nothing blocks for long, so networking, sensing and the user interface all stay responsive.
Assembly Instructions
Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.
Prepare data and a transfer-learning model
Assemble labelled leaf images (lab plus field), and fine-tune a pretrained CNN backbone on the disease classes with augmentation.
Evaluate honestly
Validate on held-out field images, inspect the confusion matrix, and calibrate a confidence threshold for deferral.
Deploy on-phone with advice framing
Quantise/export for offline phone use, and present results as guidance with a path to expert confirmation.
Step-by-Step Implementation Guide
Work through these in order. Each step ends in something you can observe, so a failure is always localised to the step you just finished.
Classify a leaf and gate on confidence
Preprocess the photo, run the CNN for class probabilities, and either advise a diagnosis or defer low-confidence cases.
pythondiagnose.pyimport torch, torch.nn.functional as F THRESHOLD = 0.6 def diagnose(image, model, classes): x = preprocess(image) # to backbone input with torch.no_grad(): probs = F.softmax(model(x[None]), dim=1)[0] # disease probabilities conf, idx = float(probs.max()), int(probs.argmax()) if conf < THRESHOLD: # not confident enough return {"advice": "retake / consult expert", "confidence": conf} return {"disease": classes[idx], "confidence": conf, "note": "advice, not a verdict"} # decision supportprobs = F.softmax(model(x[None]), dim=1)[0] # disease probabilitiesThe CNN outputs a probability over the trained disease classes; the top one is the likely diagnosis.if conf < THRESHOLD: # not confident enoughLow-confidence cases are deferred to a retake or an expert rather than asserting a shaky label — the advise-don't-dictate principle.return {"disease": classes[idx], "confidence": conf,A confident prediction is returned with its confidence, so the farmer sees how sure the model is."note": "advice, not a verdict"} # decision supportThe output is explicitly framed as decision support, not a definitive diagnosis.Present guidance and enable confirmation
Show the likely disease, confidence and treatment guidance, and provide a path to expert confirmation for serious or costly decisions.
Complete Source Code
The listing below is complete and compiles as written — there are no elided sections. Read the annotations under each block before you upload it.
#!/usr/bin/env python3
"""
Crop Disease Classifier (CNN + transfer learning)
Classifies a leaf photo into a plant disease (or healthy) with a CNN,
fine-tuned from a pretrained backbone (transfer learning), deployable
on-phone for offline in-field use. Confidence-gated DECISION SUPPORT —
advises, does not dictate. Validate on FIELD data, not just lab images.
"""
import torch, torch.nn as nn, torch.nn.functional as F
from torchvision import models
THRESHOLD = 0.6
def build_model(num_classes):
net = models.mobilenet_v3_small(weights="IMAGENET1K_V1") # pretrained
net.classifier[-1] = nn.Linear(net.classifier[-1].in_features,
num_classes) # new head for diseases
return net # fine-tune this
class Diagnoser:
def __init__(self, model, classes):
self.model = model.eval(); self.classes = classes
def diagnose(self, image):
x = preprocess(image) # resize/normalise
with torch.no_grad():
probs = F.softmax(self.model(x[None]), dim=1)[0]
conf, idx = float(probs.max()), int(probs.argmax())
if conf < THRESHOLD: # unsure -> defer
return {"advice": "unclear — retake or consult an expert",
"confidence": round(conf, 2)}
return {"disease": self.classes[idx], # likely diagnosis
"confidence": round(conf, 2),
"guidance": treatment_hint(self.classes[idx]),
"note": "decision support — confirm before major action"}
if __name__ == "__main__":
model = build_model(len(CLASSES))
# ... fine-tune on leaf data (lab + FIELD) with augmentation ...
dx = Diagnoser(model, CLASSES)
print(dx.diagnose(load_photo("leaf.jpg")))
# Knows only trained crops/diseases; validate on field images.
Configuration & Calibration
Configuration steps
- Configure the backbone, classes, and transfer-learning fine-tuning.
- Configure augmentation and class balancing.
- Configure the confidence threshold for deferral.
- Configure on-phone export and advice/guidance presentation.
Calibration procedure
An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.
Field validation
Validate on held-out field photos; expect and tune for lower-than-lab accuracy.
Confidence threshold
Set the deferral threshold so shaky predictions are flagged rather than asserted.
Confusions
Inspect the confusion matrix; add data for confused disease pairs.
Dataset, Model & Training
Dataset
Labelled leaf images per crop and disease. Lab datasets (e.g. PlantVillage) are a starting point, but field-representative images are essential for real-world accuracy.
Class balance and coverage of growth stages, lighting and backgrounds shape generalisation.
| Dataset | Size | Licence | Use here |
|---|---|---|---|
| PlantVillage (lab) | ~54k images, many classes | Open (check terms) | Baseline training (lab conditions) |
| Field leaf photos | As many as possible | Yours | Real-world robustness (crucial) |
| ImageNet-pretrained backbone | — | Model terms | Transfer-learning base |
| Augmented data | Generated | — | Generalisation |
Data preprocessing
- Resize/normalise to the backbone input; centre on the leaf where possible.
- Augment (rotate, crop, colour/lighting jitter) for field robustness.
- Balance classes; hold out field images for honest validation.
| Layer / stage | Shape or configuration | Purpose |
|---|---|---|
| Backbone | pretrained CNN (e.g. MobileNet/ResNet) | Learned visual features (transfer) |
| Head | new FC + softmax over classes | Map features → diseases |
| Augmentation | rotate/crop/colour | Generalise to field variation |
| Calibration | confidence threshold | Defer low-confidence cases |
| Deployment | quantised on-phone | Offline in-field use |
Hyperparameters
| Hyperparameter | Value | Why |
|---|---|---|
| Backbone | MobileNet/ResNet | Size vs accuracy (phone) |
| Learning rate | small (fine-tune) | Adapt without forgetting |
| Augmentation | strong | Field generalisation |
| Confidence threshold | app-specific | Advise vs defer |
Training process
- Fine-tune a pretrained backbone on the leaf classes with augmentation.
- Validate on held-out FIELD images, not just lab test splits.
- Watch the confusion matrix for confused disease pairs.
Evaluation, Metrics & Deployment
Accuracy and per-class confusion matter, but the decisive honest metric is accuracy on real field images, which is usually lower than lab test accuracy.
| Metric | Value | What it tells you |
|---|---|---|
| Lab test accuracy | often high | Flattering — clean images |
| Field accuracy | lower (honest) | The number that matters |
| Per-class confusion | inspect | Which diseases are confused |
| Model size / latency | phone-fit | Offline in-field use |
Figures from the reference training run described above — reproduce them before trusting your own changes.
Inference example
import torch, torch.nn.functional as F
THRESHOLD = 0.6
def diagnose(image, model, classes):
x = preprocess(image) # resize/normalise to backbone
with torch.no_grad():
probs = F.softmax(model(x[None]), dim=1)[0] # class probabilities
conf, idx = float(probs.max()), int(probs.argmax())
if conf < THRESHOLD:
return {"advice": "unclear — retake photo or consult an expert",
"confidence": conf}
return {"disease": classes[idx], # likely diagnosis
"confidence": conf,
"note": "decision support — confirm before major action"}
# Knows only trained crops/diseases; validate on FIELD images.
Testing Procedure & Expected Output
Test from the bottom up. Confirm power, then each sensor in isolation, then the integrated loop — the first failing step tells you exactly where to look.
| Test | What you should see |
|---|---|
| Clear diseased-leaf photo | Correct disease, good confidence |
| Healthy leaf | Classified healthy |
| Messy field photo | Works but lower accuracy — validate |
| Ambiguous/blurry photo | Low confidence → defer to retake/expert |
| Untrained crop/disease | Confidently wrong — note scope |
| Run on phone offline | Fast, offline inference |
Bench-test checklist. If a row fails, stop and fix it before moving on.
Expected output
A confidence-scored likely diagnosis with guidance, deferring unclear cases — decision support in the field.
{
"disease": "Tomato — Early Blight",
"confidence": 0.87,
"guidance": "remove affected leaves; consider appropriate fungicide",
"note": "decision support — confirm before major action"
}
A confident early-blight diagnosis from a single photo, with guidance — accessible early diagnosis, framed honestly as advice to confirm before costly action.
Troubleshooting: Common Errors & Fixes
Performance Optimisation
- Use transfer learning for accuracy with limited data.
- Augment strongly and validate on field images.
- Gate on confidence; defer unclear cases.
- Use a phone-sized backbone; quantise for offline use.
- Replace every
delay()with amillis()comparison — blocking delays are the single most common cause of dropped readings. - Sample sensors on a fixed cadence and publish on a slower one; you almost never need to transmit at the sampling rate.
- Move networking into its own FreeRTOS task so a slow DNS lookup cannot stall the control loop.
- Use
uint8_t/uint16_twhere the range allows; on an 8-bit AVR a 32-bit add costs four times as much. - Profile before optimising — print
micros()deltas around each stage and fix the slowest one first.
Safety Precautions
- Advise, do not dictate — a confident label can be wrong; confirm before costly or irreversible action.
- Validate on field data; a lab-only model can mislead in the field.
- It knows only trained crops/diseases — be explicit about scope.
- Pair with expert confirmation for serious diagnoses.
- Wear eye protection when soldering or cutting, and solder in a ventilated space — rosin flux fumes are a respiratory irritant.
- Power the circuit through a bench supply with a current limit while you are testing. A 300 mA limit turns a wiring mistake into a beep instead of a dead board.
- Disconnect power before changing any wiring. Hot-plugging a sensor onto a live bus is the fastest way to lose a controller.
Maintenance
- Add field data and classes; retrain periodically.
- Re-validate field accuracy and confusions over seasons.
- Update the model/backbone as better ones appear.
- Keep the decision-support framing and scope clear.
- Re-check every screw terminal and header after the first week — thermal cycling loosens connections that felt tight on day one.
- Recalibrate at the interval given in the calibration section, and keep the constants in a text file next to the firmware — not only in flash.
- Keep a short logbook of firmware versions and what changed. Six months later you will not remember why that constant is 1.083.
Future Improvements & Upgrades
A working v1 is a platform, not a finish line. These are the upgrades that add the most capability for the least rework.
- Add severity estimation and treatment dosing guidance.
- Add detection/segmentation of lesions (not just whole-leaf).
- Add multi-crop coverage and growth-stage awareness.
- Add on-device continual learning from confirmed cases.
- Design a proper PCB. Once the breadboard version has run for a month, moving to a two-layer board removes the intermittent-contact failures that dominate prototype faults.
- Add connectivity — an ESP32 and an MQTT publish turn a local gadget into something you can graph, alert on and analyse over months.
- Add persistent local storage (microSD or the on-chip flash) so a network outage does not create a hole in your data.
- Move configuration out of the source: a captive-portal setup page or a JSON config file makes the build reusable without a recompile.
- Add a battery and solar option so the unit survives a power cut and can be sited away from a socket.
- Write a small test harness that feeds synthetic sensor values through the decision logic, so you can validate thresholds without physically triggering the event.
Frequently Asked Questions
References & Learning Resources
These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.
- Convolutional neural networkReference
- Transfer learningReference
- Image classificationReference
- PlantVillage datasetDataset
- Plant disease diagnosisReference