The world is growing and becomes more and more connected and dependend on cooperation between people, corporations and even whole nations. Recent years show that it's not impossible to break once estabilished production and transportation chains between those entities. Due to the fact that world's climate varies by the region, it's not possible to create a self sufficient farmlands anywhere in the world in a natural way. Breaking these chains basically means that regions with harsh agriculture climate left on their own won't be able to produce enough food for it's population. Regions with good cultivating conditions are not in good spot too. The humanity is growing rapidly and we require more space to live but also more food to feed ourselves, so we require more space for farmlands and it's worth to note that not all land is cultivable, some due to being too poor quality nutritient wise and some due to droughts or pesticide overusage. Left untoched, in upcoming years it can create serious food shortage problem.
Current state of the art in farming presents a solution to all of these problems by creating artificial farms that thanks to advanced control algorithms and devices with high computional possibilities, closed, controlled environment can be build pretty much anywhere in the world. These farms are not tied to the ground, allowing them to be stacked vertically, greatly increasing yield per square meter. In this type of a farm, roots of the plant are not placed in the soil, from which it takes required nutritions, but exposed to the air while being sprayed with nutrition rich solution (aeroponics) or submerged in nutrition rich water (hydroponics). Thanks to the fact that these mediums provide much more oxygen to the plant than standard soil, it even more increases growth compared to traditional farming methonds and as a result, provides better yield.
This project is my pesonal idea of such farm that utilizes aformentioned current state of the art in farming, control engineering and communication technology to create self sufficient, scalable and automated aeroponic farm.
Overall system conceptThe main goal of this project was to create stackable, decentralized and as much automated and self sufficient growth environment that can give as much yield per square meter as possible. For this purpose I came up with an idea of a system of vertically stackable pots.
Each pot like tower has it's own environmental sensors and conditioning actuators making all measurements and acting local only to a single pot, which makes it easier to scale whole system by adding or removing pots seamlessly. These sensors and actuators are managed by a Field Controller (a real time device). Apart from sensing and acting, this device is also responsible for communication with a Farm Overseer (a high level device). Considering that the system has to be seamlessly scalable, the communication was decided to be performed wirelessly, using Zigbee protocol, which was designed to be used in similar IoT applications.
Pot towers with the same same environmental requirements, such as temperature, humidity and nutrition composition (e.g. same plants) are aggregated into Farm Areas, whose are maintained by single Farm Overseer device.
The Farm Overseer is a non real time device that reads the state of all vertical pots in Farm Area via Field Controllers then, depending on configured setpoints for basic parameters such as growth light intensity, issues commands to each of them. The Farm Overseer is also responsible for controlling containers with water and liquid nutrition as it would be pretty pointles and hard to mantain an one container per each pot (refill would be terribly hard!). This device is also an end point for supervisory control and management systems. It is capable to communicate with SCADA like front ends to display real time state of the Farm Area and accept goals but also with Cloud / database systems to save historical telemetry for further analyze.
On the image below you can see rough design of the whole system:
Next image shows more detailed concept of the Farm Area:
The pot design was created as 3D printable project in Fusion 360. My main goal while designing it was to create compact and adjustable design that can contain as much functionality and as many plants as possible. For this purpose I have designed multiple assemblies that combined together create single functional pot.
The first assembly is single vertical layer of the pot. It can fit four standard (in my country) plastic seedling pots. The pots are designed to be placed at non straight angle for two reasons. Firstly - simply to not fall from the pot and secondly - to not grow exactly below next layer of plants or above previous layer:
Considering that for optimal growth a single pot should be placed in closed controlled environment, like greenhouse, it is important to actively push and move fresh air around the plant. For this purpose the base of the vertical pot was designed as an air intlet that, with the help of a fan, distributes the air through a system of four air channels:
To feed the roots of all plants with liquid nutrition, a special chainable nozzle with four outputs was created. There is one nozzle like this pointed at all plants at every layer of the pot:
To prevent waste of any leftover nutrition that did not stick to any of the plant, all leftover liquid falls to funnel that is positioned at the bottom of the pot. The center part is an liquid nutrition input, that connects the pump in nutrition container to set of mentioned above nozzles, while the hole on the left is connected directly to the nutrition container moving leftover back to it creating closed loop.
To keep the optimal humidity in check, a water spraying system was mounted on the top of the tower's wooden frame. This system is pretty basic and was not designed to be a closed loop as water residue inside the greenhouse can greatly help to increase humidity.
Full render of the pot can be seen in two pictures below:
The pot was printed using white PETG material and mounted on wooden quadropod:
As you can see, the quadropod is also used as a mount point for electric valves and column with growth lights / water spraying nozzles. These columns also act as support structure for stretch foil that wrapped around will create aformentioned greenhouse like closed environment. Assembled pot can be seen below:
As I did not had any stiff plastic pipe to keep the sprays inside the pot straight, I've just zip tied elastic pipe with mounted nozzles to a plant stick:
For the Field Controller I've selected Thingy:53 platform as in it's compact form it is equipped with a lot of good quality environmental sensors and supports various wireless communication protocols (and OTA updates for the future).
Physicaldesignand electronics
The controller is mounted at the top of the Vertical Pot, slotted directly into it:
For the electronic design one would say that there was nothing to do as the device itself is contained in it's compact, cute cube form. But with my love to play with received dev kits up to their limits, I've decided to utilize TRACE 2.54 pin header provided on debug card for even more functionality. For this purpose I've created additional PCB that connects to this header and allows me to access:
- 2 Electric valves
- 2 Growth LED strips
- 2 Light intensity sensors
- 1 Air fan
The board was designed in KiCad and ordered from one of popular PCB manufacturers.
Soldered PCB mounted on the Field Controller can be seen below:
Software
The software was mostly written in modern (for embedded) C++ and utilizes ZephyrOS' to provide multiple threads to control various tasks that it's designed to perform.
The first task is to perform automatic light intensity control. In separate thread, every 30 seconds, the Controller reads the state of two light sensors attached at the top of the Vertical Pot, and based on these measures it corrects intensity of two growth LEDs. The main purpose here is to not overload Farm Overseer with such trivial tasks and don't keep wasting energy to keep the LEDs ON when there is enough of ambient light from other sources (i.e thanks to sun). This auto update can be disabled by Farm Overseer, which can take care of the task by itself.
void lightCtrlThread()
{
while (hardwareGood())
{
if (led1.getControlStrategy() == ClosedController::ControlStrategy::SELF)
{
LOG_INF("Updating growth light intensity");
led1.update();
led2.update();
LOG_INF("Growth light control updated");
}
else
{
LOG_INF("Growth lights are in ZIGBEE state - skipping update ");
}
k_sleep(K_SECONDS(30));
}
LOG_ERR("Hardware failure detected - stopping light control thread");
}
The closed control of the light is pretty simple. Firstly, light sensor read is performed which return value between 0 (no light) and 255 (max light intensity). This value is then passed to app::helpers::exponential, which is an linear to exponential converter that due to LED's exponential voltage / current characteristics makes the light intensity to change in an linear way. To prevent rapid changes of the light, this value is then processed via low pass filter.
void ClosedController::updateSelf()
{
uint8_t state = m_sensor.getState();
state = app::helpers::exponential(255 - state, 0, 255);
m_valSelf = filter.read(state);
}
The second task is to secure and maintain Zigbee connection with the Farm Overseer and read state of the environment. This connection is two directional, in which the Farm Overseer can issue various commands to the Field Controller:
- Turn the air fan ON and OFF (endpoint 10)
- Change light control policy to AUTO (managed by Field Controller) or ZIGBEE (managed by Farm Overseer) (endpoint 13)
- Control first and second pair of LEDs (only in ZIGBEE light control policy) (endpoints 11 and 12)
- Open or close water valve (endpoint 14)
- Open or close nutrition valve (endpoint 15)
Apart from the commands, the Field Controller reports it's own state to the Farm Overseer:
- Environmental sensor (temperature, pressure, humidity) (endpoint 16)
- Neural state classifier (endpoint 17)
The task to read this state is not issued by separate thread, but Zigbee thread using timed callbacks.
void ZigbeeInterface::sensorReadCallback(zb_bufid_t bufid)
{
SensorResult res;
LOG_INF("Performing sensor read");
if (!envSensor.read())
{
LOG_ERR("Failed to read environmental sensor");
res.ok = false;
return res;
}
res.temperature = envSensor.getTemperature();
res.humidity = envSensor.getHumidity();
res.pressure = envSensor.getPressure();
res.gasResistance = envSensor.getGasResistance();
res.classification = static_cast<uint8_t>(classifier.readNetwork(res.temperature, res.pressure, res.humidity, res.gasResistance));
if (hardwareGood())
{
res.ok = true;
LOG_INF("Sensor read done");
}
else
{
LOG_ERR("Hardware failure detected - skipping sensor read");
res.ok = false;
}
return res;
}
For the environmental sensing I've used build in BME688 sensor with custom written driver (included in project's repo) that allows me to read whole state of the sensor (temperature, pressure, humidity and gas resistance). BME680 driver used Zigbee Weather Station example returned wrong, constant gas resistance value due to differences in register addresses and values.
The main purpose of the neural classifier is to check, whether the plant is growing in a healthy way and is not affected by pests or other diseases.
Considering that it would be impossible to make the whole project, plant and grow enough of test plants to create various samples for even simpliest classification task, I've created simple Edge Impulse neural classifier mock (https://studio.edgeimpulse.com/public/148469/latest) with two classes: GOOD and MOULD. Input to this network consists of temperature, pressure, humidity and gas resistance from BME688. The network was trained using samples of neutral air for GOOD class and alcohol based solvent for MOULD class. In real life scenario, the network would be trained on various plants with various growth state, environmental conditions, diseases and pests and would be able to detect various anomalies that could affect the yield. This mock also serves a purpose of a placeholder. Thanks to Edge Impulse's network export as std C++ library option, it is possible to swap to rightly trained model seamlessly just by swapping model files (and editing some enums in case of more / less classes).
The last task is the main thread, in which all hardware initialization takes place after which it it performs simple blink program while checking whether any of the hardware failed for some reason. In such case it performs hard reset of the device.
int main()
{
if (usb_enable(NULL))
{
LOG_ERR("Failed to initialize USB");
handleCriticalError();
}
LOG_INF("Application core starting");
k_sleep(K_SECONDS(5));
using ZigbeeCallbackType = app::networking::zigbee::CallbackType::Values;
zigbee.registerCallback(ZigbeeCallbackType::FAN, OutputControlCallback<fan>);
zigbee.registerCallback(ZigbeeCallbackType::RELAY_WATER, OutputControlCallback<relayWater>);
zigbee.registerCallback(ZigbeeCallbackType::RELAY_NUTRI, OutputControlCallback<relayNutri>);
zigbee.registerCallback(ZigbeeCallbackType::LED_SIDE_1, lightControlCallback<led1>);
zigbee.registerCallback(ZigbeeCallbackType::LED_SIDE_2, lightControlCallback<led2>);
zigbee.registerCallback(ZigbeeCallbackType::LED_STRATEGY, lightStrategyCallback);
zigbee.registerSensorCallback(sensorReadCallback);
if (settings_subsys_init())
{
LOG_ERR("Settings initialization failed");
handleCriticalError();
}
if (!initHardware())
{
LOG_ERR("Hardware initialization failed");
handleCriticalError();
}
classifier.init();
if (settings_load())
{
LOG_ERR("Settings load failed");
handleCriticalError();
}
zigbee.doWorkDetached();
while (hardwareGood())
{
k_sleep(K_SECONDS(5));
LOG_DBG("Blinking");
if (!zigbee.isIdentifying())
{
blinkLed.setState(1);
k_sleep(K_MSEC(100));
blinkLed.setState(0);
}
}
handleCriticalError();
return 0;
}
Farm OverseerFor the Farm Overseer I've selected well know and available (well...) Raspberry Pi 4 B. As it's capable enough to perform all required computations and coordination of Zigbee devices but also is easy to set up and provides more than enough of GPIO to control liquid tanks and visualize own state.
Physicaldesignand electronics
The physical design of the Global Controller is rather simple and is made out of 3 small parts.
The first two parts are two liquid tanks: one for water to moisturize the environment around the pot and one for nutrition solution to spray the exposed plant roots with. These tanks are used to supply ALL of the available vertical pots via system of pipes and electric valves controlled by Field Controllers attached directly to the pots.
When it comes to electronics, both of the tanks of are course equipped with pumps to move the liquids to other parts of the system. On the lid of each tank there is HC-SR04 ultrasonic sensor that is mostly used to measure level of the liquid to inform the system about required refill, but also to prevent dry run of the pump and warn about potential overflow. Lids of both tanks are also equipped with reed switches to stop measurements when the lid is open (i.e for refill) as these measurements would be deceitful and in example could trigger pump when the container is empty. Both tanks are also provided with DS18B20 digital temperature sensors to track the temperature of solutions to prevent in example root shock caused by too cold water or liquid nutrition.
The liquid nutrition container is also equipped two sensors to estimate it's quality. Firstly it has a pH sensor, which is used to estimate whether the solution is more acidic or alkaline. Some plants are really sensitive to pH of the water and nutrition and the liquid being on the opposite of the pH spectrum could cause damage to the plant or even kill it. Secondly it has electrical conductivity sensor, which measures total salts concentration in the nutrition solution and is used to decide whether the liquid from closed loop should be refilled with additional nutritions to keep healthy grow or should be dilluted with water to prevent over nutrition that can dry the roots and kill the plant.
To control both pumps and measure the state of both tanks I've designed special hat for Raspberry Pi that contains a lot of connectors and additional hardware for accurate measurements:
The hat is divided into two sections - one for water tank and the second for nutrition tank control. Both of these sections contain connectors for reed switch, level sensor and for the pump. Each of these sections also contains a red diode to indicate state of the tank, a blue diode that shows whether the pump is running and simple tact switch to enable/disable level measurement. Nutrition tank also contains connectors for EC and pH sensors and yellow diode to indicate status of nutrition solution. There is also an additional 1-Wire connector for digital thermometers for both of tanks.
Apart from reed switches, all other sensors were connected to the Raspberry Pi through real time microcontroller. There are two reasons for this. The first one is that it's not possible to connect pH and EC analog sensors directly to the Raspberry Pi as it lacks ADC inputs. The second reason are ultrasonic sensors - these devices measure the level based on super tight timings in micro and nano seconds range. While it's possible to perform level measurements using RPi directly, these measures would be too much inaccurate as RPi is not a real time device and the timing depends mostly on Linux's scheduling algorithms and other running processes. As the bridge between sensors and the RPi I've selected cheap (that's what I thought at least) Attiny85. Two level sensors, EC and pH sensors utilize 4 out of 5 (excl. RESET pin) available pins of the microcontroller.
The last pin was used as the software half serial with only option to transmit measurements:
void writeByte(char b)
{
constexpr uint32_t baud = 9600;
constexpr uint16_t delay = (F_CPU / baud) / 4;
constexpr uint16_t sub = 15 / 4;
constexpr uint16_t txBitDelay = (delay > sub) ? delay - sub : 1; //!< Fine tuned delay for soft serial.
uint8_t mask = (1 << serial);
uint8_t invMask = ~mask;
cli();
// Start bit.
PORTB &= invMask;
_delay_loop_2(txBitDelay);
// Byte bits.
for (uint8_t i = 0; i < 8; i++)
{
if (b & 1)
PORTB |= mask; // 1
else
PORTB &= invMask; // 0
_delay_loop_2(txBitDelay);
b >>= 1;
}
// Restore state.
PORTB |= mask;
_delay_loop_2(txBitDelay);
sei();
}
The delays here were taken from Arduino's implementation of SoftwareSerial.
For a fun note I have challenged myself a bit at this stage and decided to use only AVR available libraries (w/o Arduino Core) and don't use ANY of the floating point math as this microcontroller doesn't have hardware support for hardware FP operations.
The code for this uC is pretty basic as it just performs simple measurements, then for better accuracy converts them to some multiplication of physical unit (i.e range 0 - 14 pH becomes 0 - 14000 to fit in int16 variable while being easy enough to convert back).
int main()
{
_delay_ms(30000);
transmitter_setup();
usensor_commonSetup();
phsensor_setup();
tdssensor_setup();
while (true)
{
transmitter_sendMeasurement(u0Prefix, usensor_read(u0));
transmitter_sendMeasurement(u1Prefix, usensor_read(u1));
transmitter_sendMeasurement(phPrefix, phsensor_read());
transmitter_sendMeasurement(ecPrefix, tdssensor_read());
_delay_ms(10);
}
}
Converted variables are being sent via soft half serial in using the following syntax:
<sensor name>:<value>*<checksum>
i.e for first ultrasonic sensor:
u1:20000*10
which means that u1 reads water level of 20cm. Checksum at the end is just bitewise XOR over the measurement (20000 in this case), which is good enough for such application.
Below you can see the completed setup with all the sensors and hats connected:
Software
Architectural wise, the software of the Farm Overseer is the most complicated part of the project as it consists of multiple subprocesses that do communicate with themselves and the external world in non deterministic way.
For external communication with Field Controllers I've selected Zigbee as it's well defined and secure standard for low power IoT devices.
External communication with supervisory systems and databases is performed using popular long distance (even worldwide) IoT protocol: MQTT.
Internal communication between subprocesses is performed using ROS2 as apart from standard publisher - subscriber communication, ROS2 is a rich development framework that has explicitly defined messages, rich access control, dynamic reconfiguration of parameters and additional types of control in form of actions and services.
Software architecture of Farm Overseer can be divided into three logical parts: tank control, farm control and frontend publishers.
Tank control ROS2 nodes are mainly written to perform safety checks and state estimation of both liquids. These nodes can be imagined as a software link to Raspberry Pi hat described above. Description of each node can be seen on the list and image below:
- Temperature Observer - A simple node that reads two DS18B20 1-Wire sensors (1 for each liquid tank) and publishes these readings as well as discrete state (TOO_HOT, TOO_COLD, GOOD) that describes whether liquid can be used on the plant. Thresholds for these states are reconfigurable via ROS2 dynamic parameter reconfigure.
- uC Bridge - This node is directly attached to Attiny85 sensor reader, processes received frames using regex expressions, checks for transmission errors and then publishes to other ROS2 processes as raw values
- Level Observer - Processes raw measurement from uC Bridge, then publishes processed level and tank's state. State (MEASUREMENT_DISABLED, TANK_OPEN, UNKNOWN, LEVEL_LOW, LEVE_HIGH, GOOD) depends on the level from uC Bridge, reed switch on tank's cover, on reconfigurable min and max temperature variables, enable / disable physical button on the hat and ROS2 enable / disable service.
- Pump Driver - Provides ROS2 action to control the pump for specified time. Pump's state is published and shown on hat's blue LED. This node also subscribes to tank level state and prevents pump's dry run.
- Quality Observer - This node subscribes to pH and TDS topics from uC Bridge, processes them and publishes state of the nutrition solution (UNKNOWN, BAD_ALL, BAD_PH, BAD_TDS, GOOD).
Farm Controller graph is much simplier as it contains only services and two ROS2 nodes. The services are purely responsible for translation data packets received from Zigbee to MQTT and vice versa. MQTT2ROS translates MQTT packets into ROS2 explicitly defined messaging system and vice versa.
Farm Controller is the main node of the Farm Overseer. It reads state of all Field Controllers and Liquid Tanks, then depending on all this state it issues commands to Field Controllers and tank control nodes, i.e issues command to Field Controller to turn on growth LEDs and to open water valve then to Water Tank control nodes to enable the pump driver for desired time, then again to Field Controller to close the water valve.
The farm controller works as simple event queue. Every time a new value arrives from one of the Field Controllers, the Farm Controller checks whether enough time has passed since last control of this Field Controller and if so, it moves it's id to the queue. In this setup all of the Field Controllers are processed separately in a FIFO manner.
def __on_state_received_callback(self, msg: State) -> None:
device_id = msg.header.frame_id
if device_id not in self.__field_controller_state:
self.__field_controller_state[device_id] = {
"temperature": -255,
"pressure": -255,
"humidity": -255,
"classification": -255,
"last_control_date": datetime.datetime.min
}
self.get_logger().info(
f"Registered new Field Controller with ID {device_id}")
if msg.type == State.TYPE_TEMPERATURE:
self.__field_controller_state[device_id]["temperature"] = msg.value
if msg.type == State.TYPE_PRESSURE:
self.__field_controller_state[device_id]["pressure"] = msg.value
if msg.type == State.TYPE_HUMIDITY:
self.__field_controller_state[device_id]["humidity"] = msg.value
if msg.type == State.TYPE_CLASSIFICATION:
self.__field_controller_state[device_id]["classification"] = int(
msg.value)
diff = datetime.datetime.now()
diff -= self.__field_controller_state[device_id]["last_control_date"]
if (self.__water_state == LevelState.GOOD or self.__water_state == LevelState.LEVEL_CRITICAL_HIGH) and \
(self.__nutri_state == LevelState.GOOD or self.__nutri_state == LevelState.LEVEL_CRITICAL_HIGH) and \
self.__water_temp_state == TemperatureState.GOOD and self.__nutri_temp_state == TemperatureState.GOOD and \
self.__nutri_quality_state == QualityState.GOOD and diff.total_seconds() > self.__min_time_between_controls and \
self.__field_controller_state[device_id]["classification"] == 0 and \
self.__field_controller_state[device_id]["temperature"] > -255 and \
self.__field_controller_state[device_id]["pressure"] > -255 and \
self.__field_controller_state[device_id]["humidity"] > -255:
self.__queue.put(device_id)
def __queue_worker(self) -> None:
while True:
device_id = self.__queue.get()
self.get_logger().info(f"Working on {device_id}")
self.__perform_control(device_id)
self.__queue.task_done()
def __perform_control(self, device_id: str) -> None:
self.get_logger().info(f"Performing control on {device_id}")
temp_cmd = Command()
temp_cmd.header.frame_id = device_id
temp_cmd.header.stamp = self.get_clock().now().to_msg()
temp_cmd.endpoint = Command.FAN_EP
if self.__field_controller_state[device_id]["temperature"] <= self.__desired_temperature:
temp_cmd.value = 0.0
self.get_logger().info(f"Fan on {device_id} set to off")
elif self.__field_controller_state[device_id]["temperature"] > self.__desired_temperature:
temp_cmd.value = 1.0
self.get_logger().info(f"Fan on {device_id} set to on")
self.__cmd_publisher.publish(temp_cmd)
hum_cmd = Command()
hum_cmd.header.frame_id = device_id
hum_cmd.header.stamp = self.get_clock().now().to_msg()
if self.__field_controller_state[device_id]["humidity"] <= self.__desired_temperature:
self.get_logger().info(f"Driving relays on {device_id}")
# Open relays.
hum_cmd.value = 1.0
hum_cmd.endpoint = Command.WATER_EP
self.__cmd_publisher.publish(hum_cmd)
hum_cmd.endpoint = Command.NUTRI_EP
self.__cmd_publisher.publish(hum_cmd)
# Wait for relays.
time.sleep(self.__command_wait_time)
# Drive pumps.
pump_msg = DrivePump.Goal()
pump_msg.seconds = self.__water_pumping_time
self.__drive_water_client.wait_for_server()
self.__drive_water_client.send_goal_async(pump_msg)
pump_msg.seconds = self.__nutri_pumping_time
self.__drive_nutri_client.wait_for_server()
self.__drive_nutri_client.send_goal_async(pump_msg)
# Wait pumps to finish.
time.sleep(
max([self.__water_pumping_time, self.__nutri_pumping_time] + self.__command_wait_time))
# Close relays.
hum_cmd.header.stamp = self.get_clock().now().to_msg()
hum_cmd.value = 0.0
hum_cmd.endpoint = Command.WATER_EP
self.__cmd_publisher.publish(hum_cmd)
hum_cmd.endpoint = Command.NUTRI_EP
self.__cmd_publisher.publish(hum_cmd)
self.get_logger().info(
f"Control of {device_id} done, next potential control in {self.__min_time_between_controls} seconds")
self.__field_controller_state[device_id]["last_control_date"] = datetime.datetime.now()
Frontend nodes are the most basic of the nodes described in the Farm Overseer as these only publish to ALL of available ROS2 topics, then publish them to remote MQTT broker. Database Frontend publishes measurements in blobs containing all of the topics at some specified time interval (i.e every 30 seconds). Real Time Frontend publishes these measurements as soon as they appear. Real Time Frontend can also subscribe to remote config topics and use ROS2 to dynamic reconfigure mentioned above parameters such as liquid levels or temperatures.
Below you can see examples of Frontend's telemetry packets for SCADA and database from console of mosquitto_sub command.
It was my first time creating such a big decentralized system with a lot of different tech / abstraction levels and I think that software wise the project is well designed and easy enough to expand or reconfigure.
For the hardware aspect of the project I've made some small design mistakes (such as transistors with too big gate threshold voltage to handle 4.2W pumps in a stable manner) and would change the design a bit but overall for the first iteration of the PCB I think that it was more a success rather than failure.
Mistakes aside I think that the project as a whole is a big success for me and provided me with challenges and learning opportunities that expanded my embedded knowledge by a lot. With each hour spent on this project the idea of IoT powered smart farming grew on me more and more and during the development I was taking a lot of notes for improvements that will act as base for potential next iteration of the project.
It is worth nothing that It was also pretty much my very first project using Zigbee procotol and while being rather sceptical about it (why use it instead of faster WiFi?) I would say that simplicity of the pairing alone made me change my mind about it and use it more widely in my next IoT projects.
I was trying hard to provide some example of the system running, especially piping, but it turned out that piping is a bit leaky and I didn't want to flood my room more than I did during tests. I've tried to place as much as I can in wash bowl but the pot is too big to fit in any available wash bowl that I could find before contest deadline.
RPi HAT (YouTube marked it as short so I can't embed it as video on Hackster)
- 3D printed mist nozzles weren't as effective as I was hoping. In the next iteration of such project I would use standard gardening ones.
- Specialized growth lights instead of standard leds would be a better choice.
- Just in case I'd put leds in moisture proof enclosure.
- Thingy:53 should be placed a bit higher with additional layer of moisture proof separation between the closed enclosure and device itself.
- Better cable management would be good too.
- A lot of sensors used in this project (such as ultrasonic, pH and EC) would greatly improve from temperature compensation, so in next versions of the project I would replace used Attiny85 with uC with more GPIOs and provide additional connectors for air temperature sensors.
- Hydroponic systems tend to utilize natural or artificial water tanks or lakes with living organisms in them such as fish. This creates additional environmental friendly closed symbiotic loop in which fish feed on the plant waste and enrich the water with nutritients. I think it would be easy to incorporate such system into the project and greatly reduce usage of artificial nutrition solution.
- Going all environmental friendly, I think most of the system (especially Field Controllers) could gain a lot from solar power. The Field Controllers themselves do not use too much energy, so single small solar panel per Vertical Pot should be enough. With the help of some battery packs and more panels even the Farm Overseer could be solar powered making it possible to place these vertical farms even in places without access to power lines.
- I think that the overall quality of control would improve after adding heater and cooling system to both tanks as in current state, the Farm Overseer can't do anything when the temperature of either liquid is too cold or hot.
- My Control Engineering master's self cries when he sees those open loop controlled pumps. In next iteration it would be good to close the loop based on the pressure in the piping.
- Adding some sort of rain collector (with filtering and monitoring!) could also make it even more self sufficient.
- Different pot designs for diferrent plant types is a must for easier yield collection as you harvest i.e potatoes and salad differently.
- In the current state of the project the Farm Area is more like an abstract concept rather than real thing, but in the future it could be treated like an aggregator for even more devices than pots and Farm Overseer and in example could incorporate autonomous agents.
- The aformentioned autonomous agents could be robots that autonomously perform repetivive task such as visual inspection or harvest.
- In this robot enabled environment I think it would be a good idea to mark pot towers and even single plants with markers such as QR codes to help robots navigate.
- Considering that the Farm Overseer is ROS enabled, it could be promoted to more autonomous decision making role and depending on the state of the farm (from Field Controllers, agents and itself) it could assign automatic tasks for aformetnioned agents and pretty much could be treated as the brain of the whole system.
- In robotized environment vertical pot design too should be changed to incorporate some handles to make harvest and visual inspection easier.
Comments