Remotely Controlled Plant Watering System Using nRF7002 Dk

About the project

An IoT-based device that sends indoor plant status over MQTT and can be watered automatically or remotely.

Project info

Items used in this project

Hardware components

Development kit for the nRF7002 Wi-Fi 6 dual-band companion IC Development kit for the nRF7002 Wi-Fi 6 dual-band companion IC x 1
A capacitive soil moisture detection and adjustment unit, integrates water pump A capacitive soil moisture detection and adjustment unit, integrates water pump x 1
M5Stack - USB TypeC2Grove Unit M5Stack - USB TypeC2Grove Unit x 1
Generic Power Bank Generic Power Bank x 1

Software apps and online services

nRF Connect for Desktop nRF Connect for Desktop
Zephyr RTOS Zephyr RTOS
Home Assistant Home Assistant


Setting up an indoor watering system doesn’t have to be complicated and is so worthwhile when you’re finished. Plant irrigation indoors saves time that you can devote to other areas of your plant’s needs. But how about getting to know your plant status and watering them properly? Every plant species has its specific watering needs. If you’ve ever drowned an indoor plant, you’re not alone. In this project, we are going to make a system that allows plants to get watered automatically or remotely controlled by monitoring the soil moisture level.

Hardware Setup

To send the plant status and control the watering system, we are using a Nordic nRF7002 DK, a development kit for the nRF7002 Wi-Fi 6 Companion IC, which has an nRF5340 multi-protocol System-on-Chip (SoC) as a host processor for the nRF7002.

We are using an M5Stack Watering unit which has a capacitive soil moisture sensor and a water pump. The water pump has a 5W rating so we will be powering it from an external power supply. To connect a USB power bank, we are using an M5Stack USB TypeC2Grove Unit.

The nRF7002 DK runs at 1.8 V but the Watering Unit soil moisture analog output is 0 - 3.3V as mentioned in the datasheet so we need a logic level shifter to interface it with the nRF7002 DK header pins. We tested the Watering Unit soil moisture analog output with an Oscilloscope and found that it never exceeds 1.8 V at extreme conditions. So we are not using a logic level shifter for this specific sensor but please check your sensor voltage before interfacing it with the nRF7002 DK otherwise it may cause unexpected results or can damage the MCU.

The connection schematics are given below.


Setup Development Environment

For the development work, I am using macOS but the setup process is similar for all platforms. First, we need to download nRF connect for Desktop from here:

The nRF Connect for Desktop is a cross-platform tool that enables testing and development with nRF7002 DK. Please follow the installation guide in the link above. When the installation is completed, open the app and click on the Toolchain Manager and choose nRF Connect SDK v2.3.

By default, the SDK is installed in the /opt/nordic/ncs directory in macOS. After installation, click on the Open Terminal which opens up a command line terminal with all environment variables initialized to get started quickly with the development.

Application Development

We are using the MQTT sample code from the nordic/ncs/v2.3.0/nrf/samples/net/mqtt from the SDK. MQTT is a messaging protocol for the Internet of Things (IoT) which is designed as an extremely lightweight publish/subscribe messaging transport that is ideal for connecting remote devices with minimal network bandwidth. The MQTT sample communicates with an MQTT broker over Wi-Fi using the nRF7002 DK. We will be using the Home Assistant,  an open-source software for home automation, for the MQTT broker services and dashboard to display the application user interface.

The sample has a modular structure, where each module has a defined scope of responsibility. The communication between modules is handled by the Zephyr message bus (zbus) using messages that are passed over channels. If a module has internal state handling, it is implemented using the Zephyr State Machine Framework.  The default sample has 4 main modules: Network WiFI, Sampler, Trigger, and Transport.  The Sampler module has been modified to sample soil moisture data over the ADC channel. The Trigger module triggers the sampler to publish the data at configurable intervals.  To control the water pump, the Water Switch module has been added to the modules stack that receives the subscription payload from the Transport module. The following figure illustrates the relationship between modules, channels, and network stacks in the sample.

To enable ADC and GPIO support we have added the following lines to the prj.conf file.

  1. # ADC
  6. # Water pump Digital Pin

Also, we have created an overlay file nrf7002dk_nrf5340_cpuapp.overlay with the following contents.

  1. &adc {
  2. status = "okay";
  3. };

The Water Switch module (src/modules/water_switchcode is given below.

  1. #include <zephyr/kernel.h>
  2. #include <zephyr/logging/log.h>
  3. #include <zephyr/zbus/zbus.h>
  4. #include <zephyr/drivers/gpio.h>
  5. #include "message_channel.h"
  7. /* Register log module */
  8. LOG_MODULE_REGISTER(water_switch);
  10. #define WATER_PUMP_PIN 10 // P1.10 (D8)
  11. const static struct device *digital_pin_dev;
  12. static bool configured = false;
  14. void configure()
  15. {
  16. digital_pin_dev = DEVICE_DT_GET(DT_NODELABEL(gpio1));
  17. if (!digital_pin_dev) {
  18. LOG_ERR("device gpio1 not foundn");
  19. return;
  20. }
  22. if (!device_is_ready(digital_pin_dev)) {
  23. LOG_ERR("GPIO controller not ready");
  24. return;
  25. }
  27. gpio_pin_configure(digital_pin_dev, WATER_PUMP_PIN, GPIO_OUTPUT);
  28. }
  30. void water_switch_callback(const struct zbus_channel *chan)
  31. {
  32. int err = 0;
  33. const enum water_switch_status *status;
  35. if (&WATER_SWITCH_CHAN == chan) {
  36. if (!configured) {
  37. configure();
  38. configured = true;
  39. LOG_INF("GPIO pin configured");
  40. }
  42. /* Get network status from channel. */
  43. status = zbus_chan_const_msg(chan);
  45. LOG_INF("status = %d", *status);
  47. switch (*status) {
  48. case SWITCH_OFF:
  49. err = gpio_pin_set(digital_pin_dev, WATER_PUMP_PIN, 0);
  50. if (err) {
  51. LOG_ERR("digital pin, error: %d", err);
  52. } else {
  53. LOG_INF("SWITCH_OFF: %d", err);
  54. }
  55. break;
  56. case SWITCH_ON:
  57. err = gpio_pin_set(digital_pin_dev, WATER_PUMP_PIN, 1);
  58. if (err) {
  59. LOG_ERR("digital pin, error: %d", err);
  60. } else {
  61. LOG_INF("SWITCH_ON: %d", err);
  62. }
  63. break;
  64. default:
  65. LOG_ERR("Unknown event: %d", *status);
  66. break;
  67. }
  68. }
  69. }
  71. /* Register listener - water_switch_callback will be called everytime a channel that the module listens on
  72. * receives a new message.
  73. */
  74. ZBUS_LISTENER_DEFINE(water_switch, water_switch_callback);

In the src/modules/transport  file, we have modified the on_mqtt_publish function to pass the subscription call to the Water Switch module over the Zbus channel.

  1. static void on_mqtt_publish(struct mqtt_helper_buf topic, struct mqtt_helper_buf payload)
  2. {
  3. LOG_INF("Received payload: %.*s on topic: [%.*s]", payload.size,
  4. payload.ptr,
  5. topic.size,
  6. topic.ptr);
  7. enum water_switch_status status;
  8. if (payload.size == 1) {
  9. if (payload.ptr[0] == 48) {
  11. status = SWITCH_OFF;
  12. }
  14. if (payload.ptr[0] == 49) {
  16. status = SWITCH_ON;
  17. }
  18. }
  20. int err = zbus_chan_pub(&WATER_SWITCH_CHAN, &status, K_SECONDS(1));
  22. if (err) {
  23. LOG_ERR("zbus_chan_pub, error: %d", err);
  25. }
  26. }

Setup Home Assistant

There are many ways to install the Home Assistant Operating System. Please follow the instructions at the link below.

I am using a Home Assistant Virtual Machine on a macOS host. After starting up the VM instance, we can access the Home Assistant portal using a web browser at http://homeassistant.local:8123.  The Home Assistant provides a few options to set up an MQTT broker. I am using EMQX which can be installed by searching at Settings > Add-ons > Add-on Store. Another option is to install Mosquitto but I had to install EMQX because it allows me to configure the broker without any username/password. I couldn't find an easy way to provide a username/password in the sample MQTT application so I chose EMQX. After installing EMQX, we need to set up an MQTT integration by going to Settings > Devices & Services > Add Integration and searching for MQTT.   After adding the MQTT integration click on Configure and set the values as shown below.

Also, we always need to restart the Home Assistant after any configuration changes by clicking Settings > Top Right Kebab menu > Restart Home Assistant. 

We have added two UI cards in the Overview > Dashboard, a Gauge card for displaying the soil moisture sensor data and a Button card for controlling the water pump. We need to configure the card entity using configuration files that can be accessed and edited using the SAMBA share at the host computer. The following configuration is added to the configuration.yaml.

  2. mqtt:
  3. sensor:
  4. - name: "Soil Moisture"
  5. state_topic: "/myhome/plant/status"
  6. unit_of_measurement: "%"
  7. value_template: "{{ value_json.soil_moisture | float }}"
  8. switch:
  9. - name: "Watering Switch"
  10. command_topic: "/myhome/plant/watering"
  11. qos: 1
  12. payload_on: 1
  13. payload_off: 0
  14. retain: true

We have added a configuration in the config/automations.yaml file for automatically switching off the Water Switch after 5 seconds of manual click and triggering the switch automatically if soil moisture is below 35%.

  1. - trigger:
  2. - platform: state
  3. entity_id: switch.watering_switch
  4. to: "on"
  5. for:
  6. milliseconds: 5000
  7. action:
  8. service: switch.turn_off
  9. target:
  10. entity_id: switch.watering_switch
  11. - trigger:
  12. - platform: numeric_state
  13. entity_id: sensor.soil_moisture
  14. below: 35
  15. action:
  16. service: switch.turn_on
  17. target:
  18. entity_id: switch.watering_switch

Again, we would need to restart the Home assistant to load the configuration.

The dashboard looks as below.

Build and flash the firmware

First, open the terminal using the Toolchain manager as described in the Setup Development Environment section and clone the GitHub repository. 

  1. $ git clone
  3. $ cd nRF7002_DK_Indoor_Plant_Watering_System

To configure the WiFi credential and the MQTT broker hostname, we need to execute the command below.

  1. $ west build -b nrf7002dk_nrf5340_cpuapp -t menuconfig

It will open up a console-based UI where we can find and set values for the required parameters.

Similarly, we can set the MQTT broker hostname/IP address, publish topic, and subscribe topic as shown in the image below.

Now connect the nRF7002 DK to the computer using a USB cable and execute the command below to build and flash the firmware.

  1. $ west build -b nrf7002dk_nrf5340_cpuapp
  2. $ west flash


For the demo, the nRF7002 DK and the Watering unit are powered by a power bank. The Home Assistant can be accessed using a mobile phone browser. Although this project is developed using a home network and cannot be accessed outside the network but using the Home Assistant Cloud we can access it from outside.


This project showcases the low-powered and seamless WiFi connectivity capabilities of the nRF7002 DK  for battery-powered IoT-based home automation projects.

Schematics, diagrams and documents



Code Repository


Photo of knaveen


Bioinformatician, Researcher, Programmer, Maker, Community contributor Machine Learning Tokyo


Leave your feedback...