How the countdown works.
One test, one forecast and two published tables give each batch of maize a number of storage days. Here is every step, with the numbers.
01 A day in the engine
Every evening at 17:00, five steps.
1. Starting moisture
It comes only from the farmer’s own test. Salt sticks: above 15%, and the countdown uses 18%. Salt flows: below about 15%, and it uses 14.5%, never as proof of 13.5%. A hygrometer reading is converted with the moisture table at the reading’s temperature. A meter reading is used as read. Untested grain gets a request to test, never a countdown.
2. Drift
Grain in woven sacks, cribs and floors takes on the moisture of the air around it. Each day it closes 5% of the gap to the moisture it would settle at in that air. Hermetic bags do not change. Drying grain is held until the next test.
Example. Kitale in a normal October, 18.3 °C and 63% humidity 5, settles maize near 14.1% 6. A sack at 18% moves to 17.81% after one day.
3. Storage life used
Each day uses 1 divided by the storage days at that temperature and moisture 7. When the fractions add up to 1, the grain has lost about 0.5% of its dry weight, which the source puts at roughly one US grade level. The source counts from harvest; the engine counts from the first test.
Example. At 17.81% and 18.3 °C the table gives about 48 days, so that day uses 2.1% of the batch’s storage life.
4. Countdown and level
The engine runs the days forward over the 7-day forecast, then repeats the forecast’s average, and counts the days until the life is used up. HIGH: 80% used, or 14 days or fewer. MEDIUM: 50% used, or 30 days or fewer. These thresholds are ours, set for the lead time sun-drying needs in a wet season.
5. What is sent
At most one unprompted message a day, in this order: rain in the next 24 hours while the farmer is drying; a changed recommendation; a worse countdown (risk up, or days left halved and 60 or fewer); a spaced and capped reminder; the one-off insect reminder. A reply is answered at once and never uses up the evening slot.
The repeat rules
An ordinary repeat is spaced at least 3 days apart and sent at most twice per test. Re-tests are asked at most twice in 30 days, and rain alerts pause after three go unanswered.
Urgent warnings (HIGH) repeat weekly, at most four times per test result. The fourth says it is the last reminder on that result and asks for a new test; after it the engine stays quiet until a new test result restarts the count, so a batch can go silent while its countdown is at zero.
02 Drift
Sixty days in a sack.
The same rule, run forward. Wet grain in dry air dries slowly. Dry grain in damp air does not stay dry: at 85% humidity sack B heads for 18.6% and passes the 13.5% line within days. That is the failure Harvestly watches for.
- A. Starts at 18%, in dry air (18.3 °C, 63% humidity). Falls toward 14.1%.
- B. Starts at 13%, in damp air (18 °C, 85% humidity). Rises toward 18.6%.
- The 13.5% limit.
Engine rule: 5% of the gap closed each day, in woven sacks. The 5% rate is a calibration assumption, not a measurement.
03 The tables
The two published tables.
Both are US corn tables from the University of Arkansas Extension, copied into the engine and checked cell by cell. Shading: in the first table, amber from 13.5% upward; in the second, amber as the days run short. Between the printed values the engine interpolates. The tables are in degrees Fahrenheit; the rows show both.
Scroll the table sideways.
| Temperature (rows), humidity % (columns) | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 °C (35 °F) | 9.3 | 10.3 | 11.2 | 12.1 | 13.0 | 13.9 | 14.8 | 15.7 | 16.6 | 17.6 | 18.7 | 19.8 | 21.2 | 22.9 |
| 4 °C (40 °F) | 9.1 | 10.0 | 10.9 | 11.8 | 12.7 | 13.5 | 14.4 | 15.3 | 16.2 | 17.1 | 18.2 | 19.3 | 20.7 | 22.3 |
| 7 °C (45 °F) | 8.8 | 9.8 | 10.6 | 11.5 | 12.3 | 13.2 | 14.0 | 14.9 | 15.8 | 16.7 | 17.7 | 18.9 | 20.2 | 21.8 |
| 10 °C (50 °F) | 8.6 | 9.5 | 10.4 | 11.2 | 12.0 | 12.9 | 13.7 | 14.5 | 15.4 | 16.3 | 17.3 | 18.5 | 19.8 | 21.4 |
| 13 °C (55 °F) | 8.4 | 9.3 | 10.1 | 11.0 | 11.8 | 12.6 | 13.4 | 14.2 | 15.1 | 16.0 | 17.0 | 18.1 | 19.3 | 20.9 |
| 16 °C (60 °F) | 8.2 | 9.1 | 9.9 | 10.7 | 11.5 | 12.3 | 13.1 | 13.9 | 14.8 | 15.7 | 16.6 | 17.7 | 18.9 | 20.5 |
| 18 °C (65 °F) | 8.0 | 8.9 | 9.7 | 10.5 | 11.3 | 12.0 | 12.8 | 13.6 | 14.5 | 15.3 | 16.3 | 17.4 | 18.6 | 20.1 |
| 21 °C (70 °F) | 7.9 | 8.7 | 9.5 | 10.3 | 11.0 | 11.8 | 12.6 | 13.4 | 14.2 | 15.0 | 16.0 | 17.0 | 18.2 | 19.8 |
| 24 °C (75 °F) | 7.7 | 8.5 | 9.3 | 10.1 | 10.8 | 11.6 | 12.3 | 13.1 | 13.9 | 14.8 | 15.7 | 16.7 | 17.9 | 19.4 |
| 27 °C (80 °F) | 7.6 | 8.4 | 9.1 | 9.9 | 10.6 | 11.4 | 12.1 | 12.9 | 13.7 | 14.5 | 15.4 | 16.4 | 17.6 | 19.1 |
| 29 °C (85 °F) | 7.4 | 8.2 | 9.0 | 9.7 | 10.4 | 11.2 | 11.9 | 12.6 | 13.4 | 14.3 | 15.2 | 16.2 | 17.3 | 18.8 |
| 32 °C (90 °F) | 7.3 | 8.1 | 8.8 | 9.5 | 10.3 | 11.0 | 11.7 | 12.4 | 13.2 | 14.0 | 14.9 | 15.9 | 17.0 | 18.5 |
| 35 °C (95 °F) | 7.2 | 7.9 | 8.7 | 9.4 | 10.1 | 10.8 | 11.5 | 12.2 | 13.0 | 13.8 | 14.7 | 15.6 | 16.8 | 18.2 |
| 38 °C (100 °F) | 7.1 | 7.8 | 8.5 | 9.2 | 9.9 | 10.6 | 11.3 | 12.0 | 12.8 | 13.6 | 14.5 | 15.4 | 16.5 | 17.9 |
Scroll the table sideways.
| Temperature (rows), moisture % (columns) | 15 | 17 | 19 | 21 | 23 | 25 | 30 |
|---|---|---|---|---|---|---|---|
| 2 °C (35 °F) | 2,126 | 671 | 295 | 161 | 102 | 72 | 41 |
| 4 °C (40 °F) | 1,413 | 448 | 197 | 107 | 68 | 48 | 27 |
| 7 °C (45 °F) | 931 | 299 | 131 | 72 | 45 | 32 | 18 |
| 10 °C (50 °F) | 621 | 199 | 88 | 48 | 30 | 21 | 12 |
| 13 °C (55 °F) | 414 | 133 | 58 | 32 | 21 | 14 | 8 |
| 16 °C (60 °F) | 275 | 88 | 39 | 22 | 14 | 10 | 6 |
| 18 °C (65 °F) | 206 | 66 | 29 | 16 | 11 | 8 | 5 |
| 21 °C (70 °F) | 154 | 49 | 22 | 12 | 8 | 6 | 4 |
| 24 °C (75 °F) | 115 | 37 | 16 | 9 | 6 | 5 | 3 |
04 What the farmer is told
Eighteen messages, in two languages.
Show all 18 messages
| Situation | Code | Message |
|---|---|---|
| Drying, and rain is dueRain in the next 24 hours: 60% chance or 5 mm. | RAIN_COVER | Harvestly: Rain likely rain when (rain). Cover your drying maize or bring it indoors now. Reply 1 when covered. credit date.Harvestly: Mvua inatarajiwa rain when (rain). Funika mahindi unayoanika au uyaingize ndani sasa. Jibu 1 ukishafunika. credit date. |
| Not yet testedAsks for a meter reading first and offers the salt test as a draft, with no amounts or times. At most 3 requests. | TEST_FIRST | Harvestly: We do not know how wet your maize is yet. Test it before you store it: use a moisture meter at your co-op or NCPB depot and reply METER and the number, e.g. METER 14.2. Salt test (draft, unconfirmed): reply SALT YES if salt sticks, SALT NO if it stays loose.Harvestly: Hatujui bado mahindi yako yana unyevu kiasi gani. Yapime kabla ya kuhifadhi: tumia mita ya unyevu kwenye chama chako au bohari ya NCPB kisha jibu MITA na namba, mf. MITA 14.2. Kipimo cha chumvi (rasimu, bado hakijathibitishwa): jibu CHUMVI NDIYO chumvi ikinata, CHUMVI HAPANA ikibaki huru. |
| Sealed, but the test said wetOpen the bags and dry the maize. | OPEN_AND_DRY | Harvestly: Maize sealed at 14% moisture or more can still go mouldy (evidence). Storage time left: risk. Open the bags, dry the maize in full sun and reply DRYING. Reseal only after a test shows it is dry.Harvestly: Mahindi yaliyofungwa yakiwa na unyevu wa 14% au zaidi bado yanaweza kuota kuvu (evidence). Muda uliobaki: risk. Fungua mifuko, anika juani kisha jibu ANIKA. Usifunge tena mpaka kipimo kionyeshe yamekauka. |
| Sealed in hermetic bags on a salt test onlyAsks for a meter check within a week. | CONFIRM_DRY | Harvestly: Sealed bags protect maize only below 14% moisture, and this is not yet confirmed (evidence). Within a week, get a meter check at checkpoint and reply METER with the number.Harvestly: Mifuko isiyopitisha hewa hulinda mahindi yenye unyevu chini ya 14% tu, na hili bado halijathibitishwa (evidence). Ndani ya wiki moja, pima kwa mita katika checkpoint kisha tuma MITA na namba. |
| Most of the storage life used80% used. | SORT_AND_SELL | Harvestly: This batch has used most of its estimated storage time. Check it today: remove mouldy or discoloured grain (do not eat it or feed it to animals), then sell or use this batch first.Harvestly: Mahindi haya yametumia sehemu kubwa ya muda wa uhifadhi uliokadiriwa. Yakague leo: ondoa punje zenye kuvu au zilizobadilika rangi (usizile wala usiwalishe mifugo), kisha yauze au uyatumie kwanza. |
| HIGH risk, repeatedAn unanswered urgent warning, spaced and capped. | HIGH_ALERT | Harvestly: URGENT. Storage time left: risk. Check this maize today: remove mouldy or discoloured grain (do not eat it or feed it to animals), then sell or use it first. closingHarvestly: HARAKA. Muda uliobaki: risk. Kagua mahindi haya leo: ondoa punje zenye kuvu au zilizobadilika rangi (usizile wala usiwalishe mifugo), kisha yauze au uyatumie kwanza. closing |
| The test says wetSent again if the countdown halves or the risk level rises, and at most twice more while it stays HIGH. | DRY_NOW | Harvestly: Your maize is too wet to store (evidence). Storage time left: risk. Dry it on a tarpaulin in full sunreply drying, then test again. credit date.Harvestly: Mahindi yako yana unyevu mwingi mno kuhifadhiwa (evidence). Muda uliobaki: risk. Yaanike juu ya turubai juanireply drying, kisha pima tena. credit date. |
| The estimate has moved past the testA full point past the salt line, up or down. | RETEST | Harvestly: why (evidence). Storage time left: risk. Do the salt test today and reply SALT YES or SALT NO. credit date.Harvestly: why (evidence). Muda uliobaki: risk. Fanya kipimo cha chumvi leo kisha jibu CHUMVI NDIYO au CHUMVI HAPANA. credit date. |
| Salt test passed, or a reading of 13.6–15%Answers a test. Never pushed on an estimate. | NEARLY_DRY | Harvestly: Not yet proven dry enough to seal or sell (evidence); the limit is 13.5%. Dry more in full sun if needed, then confirm with a meter at checkpoint. Reply METER and the number.Harvestly: Bado haijathibitishwa kuwa mahindi yamekauka vya kutosha kufungwa au kuuzwa (evidence); kiwango ni 13.5%. Anika zaidi juani ikihitajika, kisha thibitisha kwa mita katika checkpoint. Jibu MITA na namba. |
| Meter or hygrometer at 13.5% or belowThe only message that says grain is dry. | READY_TO_STORE | Harvestly: Dry enough to store (evidence). Seal it in hermetic bags (stockist) or deposit it at warehouse for a warehouse receipt. Reply SEALED, SOLD or DEPOSITED when done.Harvestly: Mahindi yamekauka vya kutosha (evidence). Yafunge kwenye mifuko isiyopitisha hewa (stockist) au yaweke kwenye warehouse upate stakabadhi ya ghala. Ukimaliza jibu NIMEFUNGA, NIMEUZA au GHALANI. |
| Seven weeks or more in sacks, measured dryOne reminder that insects get more likely the longer grain stays in sacks, with a pointer to hermetic bags. | INSECT_REMINDER | Harvestly: Your maize has been in sacks or open storage for number weeks. Weevil damage grows the longer maize stays in sacks. It tested dry, so consider moving it into hermetic bags (stockist) or selling soon.Harvestly: Mahindi yako yamekaa kwenye magunia au hifadhi ya wazi wiki number. Uharibifu wa fukusi huongezeka mahindi yakikaa muda mrefu kwenye magunia. Kipimo kilionyesha yamekauka, kwa hiyo fikiria kuyahamishia kwenye mifuko isiyopitisha hewa (stockist) au kuyauza mapema. |
| A reading or a storage change is recordedGives the last reading and when it was taken, then the estimate since, labelled as an estimate. | STATUS | Harvestly: Recorded (measured). Storage time left: risk. This is an estimate, not a guarantee.Harvestly: Tumepokea (measured). Muda wa kuhifadhi uliobaki: risk. Ni makadirio, si hakikisho. |
| At registrationSays what the messages are, credits the weather source, and explains STOP. | WELCOME | Harvestly: Welcome. These messages give storage advice for your maize by SMS. It is an estimate. Weather data by Open-Meteo.com (CC BY 4.0). Reply STOP at any time to stop all messages.Harvestly: Karibu. Ujumbe huu unatoa ushauri wa kuhifadhi mahindi yako kwa SMS. Ni makadirio. Data ya hali ya hewa: Open-Meteo.com (CC BY 4.0). Jibu SIMAMISHA wakati wowote kusimamisha ujumbe wote. |
| A reply it does not understandLists the replies it knows. | HELP | Harvestly: Sorry, we did not understand. Reply SALT YES, SALT NO, METER 14.2, HYGRO 62 18, DRYING, SACKS, SEALED, SOLD, DEPOSITED or STOP.Harvestly: Samahani, hatukuelewa. Jibu CHUMVI NDIYO, CHUMVI HAPANA, MITA 14.2, HYGRO 62 18, ANIKA, MAGUNIA, NIMEFUNGA, NIMEUZA, GHALANI au SIMAMISHA. |
| An acknowledgement | THANKS | Harvestly: Thank you, noted.Harvestly: Asante, tumepokea. |
| Other message | START_OK | Harvestly: Messages are on again for this number. To register grain reply JOIN <ward> <storage> <bags>, e.g. JOIN KITALE SACK 12. Reply STOP to stop.Harvestly: Ujumbe umewashwa tena kwa namba hii. Kusajili mahindi jibu SAJILI <kata> <hifadhi> <magunia>, mf. SAJILI KITALE MAGUNIA 12. Jibu SIMAMISHA kusimamisha. |
| The batch is sold or depositedAlerts for the batch end. | CLOSED | Harvestly: Noted. Alerts for this batch have ended. Reply START to restart them.Harvestly: Tumepokea. Arifa za mahindi haya zimekoma. Jibu ANZA kuziwasha tena. |
| The farmer replies STOPNo more messages until START. | STOPPED | Harvestly: You will get no more messages on this number, for any batch. Reply START to restart.Harvestly: Hutapokea ujumbe zaidi kwenye namba hii, kwa mahindi yoyote. Jibu ANZA kuanza tena. |
05 The parameters
Fifteen parameters, each labelled.
Labels: SOURCE from a publication, SOURCE-BASED our reading of one, CALIBRATION our assumption, PROGRAMME a rule we chose, UNVALIDATED not checked against its source or against Kenyan data.
| Parameter | Value | Label | Where it comes from | In the code |
|---|---|---|---|---|
| 1Moisture table | 14 × 14 values: 35–100 °F, 25–90% humidity | SOURCE | University of Arkansas Extension FSA1074, Table A 6. Checked cell by cell against the publication. | EMC_TABLE |
| 2Storage time table | 9 × 7 values: 35–75 °F, 15–30% moisture | SOURCE | University of Arkansas Extension FSA1058, Table 1 7. Checked cell by cell. Time to about 0.5% dry-matter loss, a US grade level. The source counts from harvest; the engine counts from the first test. | AST_TABLE |
| 3Limit for storage, sale and warehouse intake | 13.5% moisture | SOURCE | East African Standard EAS 2, draft revision DEAS 2:2024 3. This is the limit for sale; Arkansas guidance for keeping corn a full year is 12%. | EAS_MAX_MC |
| 4Limit for hermetic bags | 14% moisture | SOURCE | Ng’ang’a et al. 2016a 4, cited and not re-opened in our checks: sealed bags stop aflatoxin building up only below 14%. | HERMETIC_MAX_MC |
| 5Salt test line | 15% moisture | UNVALIDATED | Secondary, unverified: a rule of thumb attributed to Johnson 1992 18. We have not opened that paper, so the 15% line is not checked against it. The salt-test protocol is being verified, so our first message gives no amounts or times. Only a meter, hygrometer or depot reading clears grain. The protocol asks for non-iodised salt. | SALT_LINE_MC |
| 6Week-7 weevil reminder | 49 days in sacks | SOURCE-BASED | Based on Ng’ang’a et al. 2016b 22, one trial in Makueni. The week-7 timing is our reading and is not stated in the abstract. The count starts at the first test, not at harvest. | INSECT_REMINDER_DAYS |
| 7Moisture used when salt sticks | 18% | CALIBRATION | Our assumption. The true value is only known to be above 15%. A pilot would replace it with meter readings. | SALT_STICKY_MC |
| 8Moisture used when salt flows | 14.5% | CALIBRATION | Our assumption: at most about 15%. Never treated as proof of 13.5%. | SALT_FLOWS_MC |
| 9Drift in sacks, cribs and floors | 5% of the gap to the air’s equilibrium moisture, each day | CALIBRATION | Our assumption, not fitted to data. Afzal et al. 21 report grain wetting from 12.5% to 15.6% over 2 months in polypropylene bags in Pakistan (final reading only), the same order of magnitude. | SACK_EXCHANGE_RATE |
| 10Grain temperature | The day’s mean air temperature | CALIBRATION | A stand-in. Grain temperature is not air temperature. | temp_c |
| 11Risk levels | HIGH: 80% of storage life used, or 14 days or fewer. MEDIUM: 50%, or 30 days or fewer. Warn again when the countdown has halved and is 60 days or fewer. | PROGRAMME | Our thresholds, set for the lead time sun-drying needs in a wet season. | HIGH_LIFE, HIGH_DAYS, MEDIUM_LIFE, MEDIUM_DAYS, WORSE_FACTOR |
| 12Rain alert | 60% chance, or 5 mm, in the next 24 hours, while grain is drying | PROGRAMME | Our rule. | RAIN_PROB_ALERT, RAIN_MM_ALERT |
| 13Spacing and cap of repeats | One rule for an unanswered HIGH batch: after the ordinary advice message (repeated at most twice, 3 days apart, sealed bags 7), an urgent reminder goes out weekly, at most four times per test result. The fourth says it is the last reminder, then nothing more is sent until a new test result | PROGRAMME | Our rule, against alert fatigue. | REMIND_GAP_DAYS, SEALED_GAP_DAYS, REPEAT_MAX, HIGH_ALERT_GAP_DAYS, HIGH_ALERT_MAX |
| 14Re-test trigger | The estimate is 1 point past the salt line (16% going up, 14% going down). At most 2 re-test requests in 30 days | PROGRAMME | Our rule. The engine never orders drying on an estimate alone. | RETEST_MARGIN, RETEST_PER_30_DAYS |
| 15Other caps | An untested farmer is asked to test at most 3 times. Rain alerts pause after 3 unanswered. One proactive message a day | PROGRAMME | Our rule. | TEST_REMINDERS_MAX, RAIN_UNACKED_MAX |
Also in the code
- HORIZON_DAYS = 365. A countdown is capped at 365 days. The site shows 365+.
- MAX_AST_DAYS = 3650. Storage time is capped at 3,650 days.
- Forecast beyond day 7. After the 7-day forecast the engine repeats the forecast’s average.
- Humidity clamp. Humidity above 90% is treated as 90%, and below 25% as 25%, because the moisture table ends there.
- Same drift for all permeable storage. Sacks, cribs and floors drift at the same rate.
- PLACE_MAX_CHARS. A place or stockist name is cut to a fixed length so a message stays within two SMS.
What the demos assume
- CALIBRATION Salt that sticks is treated as 18% moisture (the true value is anything above 15%).
- CALIBRATION Salt that flows is treated as 14.5% and never counts as proof of 13.5% or less.
- CALIBRATION A woven sack closes 5% of the gap to the air's equilibrium moisture per day. The headline demo (261 days, then 34) depends on it: with 0%/day it reads 45, 43, 42 days; with 2%/day 145, 143, 38.
- CALIBRATION Grain temperature equals the daily mean air temperature (the storage-time table is for grain temperature).
- PROGRAMME Alert thresholds (80% / 50% of life, 14 / 30 days), spacing and caps are design rules, not findings.
- UNVALIDATED The Arkansas tables were built for US corn and have not been validated for Kenyan white maize.
- UNVALIDATED Outdoor regional weather stands in for the conditions inside the sack.
- UNVALIDATED Daily means of temperature and humidity are used although the moisture table is not linear.
- UNVALIDATED The storage-life clock starts at the farmer's first test, not at harvest (the source clocks from harvest).
- UNVALIDATED Drying grain keeps its tested moisture and still uses up storage life at that moisture.
- UNVALIDATED Hermetic bags are perfectly sealed: moisture stays constant.
- UNVALIDATED Weather beyond the 7-day forecast equals the 7-day mean.
- PROGRAMME A countdown is capped at 365 days (HORIZON_DAYS) and storage time at 3650 days (MAX_AST_DAYS); a 365 means 365 or more.
- UNVALIDATED Sacks, cribs and floors all drift at the same rate.
- UNVALIDATED Relative humidity above 90% is treated as 90% (the table ends there), and below 25% as 25%.
06 How it is checked
How it is checked.
The Python reference has automated tests. Its results are published as vectors.json: moisture values, storage-time values and day-by-day scenarios that any other version must reproduce. The JavaScript behind the calculator and the week replay on this site is checked against them by tools/test-engine-js.mjs, and a script asserts the replay equals vectors.json before the data is written.
What is not validated, and the pilot status, are listed once on the home page.