diff --git a/src/crop/almass.jl b/src/crop/almass.jl
index a306348d49c290f660ec4d2a977549ef6db25b16..92033230333a7cf194851cf5a50c03ec1769720a 100644
--- a/src/crop/almass.jl
+++ b/src/crop/almass.jl
@@ -9,7 +9,6 @@ module ALMaSS
 
 using Persefone:
     Management,
-    FarmPlot,
     Length,
     cm,
     SimulationModel,
@@ -74,8 +73,8 @@ cropname(ct::CropType) = ct.name
 """
     CropState
 
-The state data for an ALMaSS vegetation point calculation as used in
-FarmPlot.
+The state data for an ALMaSS vegetation point calculation.  Usually
+part of a `FarmPlot`.
 """
 mutable struct CropState
     #TODO add Unitful
@@ -168,15 +167,14 @@ function readcropparameters(generalcropfile::String, growthfile::String)
 end
 
 """
-    stepagent!(farmplot, model)
+    stepagent!(cropstate, model)
 
 Update a farm plot by one day.
 """
-function stepagent!(farmplot::FarmPlot{CropState}, model::SimulationModel)
+function stepagent!(cs::CropState, model::SimulationModel)
     # update growing degree days
     # if no crop-specific base temperature is given, default to 5°C
     # (https://www.eea.europa.eu/publications/europes-changing-climate-hazards-1/heat-and-cold/heat-and-cold-2014-mean)
-    cs = farmplot.crop_state
     basetemp = cs.croptype.mingrowthtemp
     ismissing(basetemp) && (basetemp = 5.0)
     gdd = (maxtemp(model)+mintemp(model))/2 - basetemp
@@ -185,19 +183,18 @@ function stepagent!(farmplot::FarmPlot{CropState}, model::SimulationModel)
     monthday(model.date) == (1,1) && (cs.phase = ALMaSS.janfirst)
     monthday(model.date) == (3,1) && (cs.phase = ALMaSS.marchfirst)
     # update crop growth
-    growcrop!(farmplot, model)
+    growcrop!(cs, model)
 end
 
 
 ## CROP MANAGEMENT AND GROWTH FUNCTIONS
 
 """
-    sow!(cropname, farmplot, model)
+    sow!(cropstate, model, cropname)
 
-Sow the specified crop on this farmplot.
+Change the cropstate to sow the specified crop.
 """
-function sow!(cropname::String, farmplot::FarmPlot{CropState}, model::SimulationModel)
-    cs = farmplot.crop_state
+function sow!(cs::CropState, model::SimulationModel, cropname::String)
     createevent!(model, farmplot.pixels, sowing)
     cs.croptype = model.crops[cropname]
     cs.phase = ALMaSS.sow
@@ -205,12 +202,11 @@ function sow!(cropname::String, farmplot::FarmPlot{CropState}, model::Simulation
 end
 
 """
-    harvest!(farmplot, model)
+    harvest!(cropstate, model)
 
-Harvest the crop on this farmplot.
+Harvest the crop of this cropstate.
 """
-function harvest!(farmplot::FarmPlot{CropState}, model::SimulationModel)
-    cs = farmplot.crop_state
+function harvest!(cs::CropState, model::SimulationModel)
     createevent!(model, farmplot.pixels, harvesting)
     cs.phase in [ALMaSS.harvest1, ALMaSS.harvest2] ?
         cs.phase = ALMaSS.harvest2 :
@@ -224,13 +220,13 @@ end
 #TODO till!()
 
 """
-    growcrop!(farmplot, model)
+    growcrop!(cropstate, model)
 
-Apply the relevant crop growth model to update the plants on this farm plot.
-Currently only supports the ALMaSS crop growth model by Topping et al.
+Apply the relevant crop growth model to update the plants crop state
+on this farm plot.  Implements the ALMaSS crop growth model by Topping
+et al.
 """
-function growcrop!(farmplot::FarmPlot{CropState}, model::SimulationModel)
-    cs = farmplot.crop_state
+function growcrop!(cs::CropState, model::SimulationModel)
     fertiliser in cs.events ?
         curve = cs.croptype.lownutrientgrowth :
         curve = cs.croptype.highnutrientgrowth
diff --git a/src/crop/farmplot.jl b/src/crop/farmplot.jl
index 018ec54356b62fbe5972a34de2dbf65024d8c755..b1c479ac16212d8c0741976f6d6dfeaeed186248 100644
--- a/src/crop/farmplot.jl
+++ b/src/crop/farmplot.jl
@@ -15,6 +15,36 @@ cropheight(f::FarmPlot{T}) where {T} = cropheight(f.crop_state)
 cropcover(f::FarmPlot{T}) where {T} = cropcover(f.crop_state)
 cropyield(f::FarmPlot{T}) where {T} = cropyield(f.crop_state)
 
+"""
+    stepagent!(farmplot, model)
+
+Update a farm plot by one day.
+"""
+function stepagent!(farmplot::FarmPlot{T}, model::SimulationModel) where T
+    stepagent!(farmplot.crop_state, model)
+end
+
+"""
+    sow!(farmplot, model, cropname)
+
+Sow the specified crop on the farmplot.
+"""
+function sow!(farmplot::FarmPlot, model::SimulationModel, cropname::String)
+    createevent!(model, farmplot.pixels, sowing)
+    sow!(farmplot.crop_state, model, cropname)
+    #XXX test if the crop is sowable?
+end
+
+"""
+    harvest!(farmplot, model)
+
+Harvest the crop of this farmplot.
+"""
+function harvest!(farmplot::FarmPlot{T}, model::SimulationModel) where T
+    createevent!(model, farmplot.pixels, harvesting)
+    harvest!(farmplot.crop_state, model)  # TODO: multiply with area to return units of `g`
+end
+
 ## UTILITY FUNCTIONS
 
 """
diff --git a/src/crop/simplecrop.jl b/src/crop/simplecrop.jl
index dead41ffbfd5e13d109ee368668e6c94ebe64505..6bfe858364cf472af1a49b87cf0a8f455dfd5b8c 100644
--- a/src/crop/simplecrop.jl
+++ b/src/crop/simplecrop.jl
@@ -3,7 +3,7 @@ module SimpleCrop
 using Persefone:
     FarmPlot,
     Length,
-    m,
+    cm,
     SimulationModel
 
 import Persefone:
@@ -35,10 +35,36 @@ cropyield(cs::CropState) = 0.0  # TODO: units?
 """
     stepagent!(farmplot, model)
 
-Update a farm plot by one day.
+Update a crop state by one day.
 """
-function stepagent!(farmplot::FarmPlot{CropState}, model::SimulationModel)
-    # TODO: do something simple
+function stepagent!(cs::CropState, model::SimulationModel)
+    # height undergoes random diffusion, bounded by 0
+    delta_height = (2 * rand() - 1) * cm
+    cs.height = max(0.0cm, cs.height + delta_height)
+end
+
+"""
+    sow!(cropstate, model, cropname)
+
+Change the cropstate to sow the specified crop.
+"""
+function sow!(cs::CropState, model::SimulationModel, cropname::String)
+    cs.croptype = model.crops[cropname]
+    cs.height = 0cm
+end
+
+"""
+    harvest!(cropstate, model)
+
+Harvest the crop of this cropstate.
+"""
+function harvest!(cs::CropState, model::SimulationModel)
+    # TODO: set cs.croptype ?
+    # TODO: this 1.0g/cm/m^2 height_to_yield factor should be a param
+    #       for every crop type
+    yield = cs.height * 1.0g/cm/m^2
+    cs.height = 0cm
+    return yield
 end
 
 end # module SimpleCrop
diff --git a/src/farm/farm.jl b/src/farm/farm.jl
index e9e70c39bf1ebb56ccda136c207b1c7493ebee88..6d7b1588f33e54cb67e14a5fe86f4153e4379c15 100644
--- a/src/farm/farm.jl
+++ b/src/farm/farm.jl
@@ -12,7 +12,8 @@ mutable struct Farmer <: ModelAgent
     #XXX make this into an abstract type and create subtypes for different
     # farm submodels? (#69)
     const id::Int64
-    fields::Vector{ALMaSS.FarmPlot}
+    # TODO: hardcoded ALMaSS crop model
+    fields::Vector{FarmPlot{ALMaSS.CropState}}
     croprotation::Vector{ALMaSS.CropType}
     #TODO add AES
 end
diff --git a/test/crop_tests.jl b/test/crop_tests.jl
index 1cdbe4fc0454a25917fc713e80e5b2904e8d078b..91469250561ef844d1fd99969b6442701c8133e2 100644
--- a/test/crop_tests.jl
+++ b/test/crop_tests.jl
@@ -34,7 +34,7 @@ end
 
 @testset "Submodule SimpleCrop" begin
     ct = Ps.SimpleCrop.CropType("olive tree")
-    fp = FarmPlot(0, [(0,0)], Ps.SimpleCrop.CropState(ct, 0.0m))
+    fp = FarmPlot(0, [(0,0)], Ps.SimpleCrop.CropState(ct, 0.0cm))
     @test fp isa FarmPlot
     @test fp isa FarmPlot{Ps.SimpleCrop.CropState}
     @test croptype(fp) isa Ps.SimpleCrop.CropType