Introduction: NodeMCU Relay Controlled Solenoid Valve

In this tutorial we will show you how to use a NodeMCU and a relay board that can be used to control things like solenoid valves. The nodeMCU uses the ESP8266 Chip from Espressif. It has built in Wifi and is compatible with the Arduino IDE. If you haven't used a NodeMCU before check out this quick start guide.

We are going to use the platform to observe a sensor (in this case a water level sensor) and if it is low, open our solenoid valve to let a tank fill up.

The board we have created is fairly generic and provides for two inputs - that could be from push buttons or level switches or some other type of switch. It also has two outputs that are connected to two relays. There is also a DC-DC power module that allows you to power the NodeMCU from an external power supply up to 25VDC and provides the output for the relay board.

Step 1: Components

  1. NodeMCU
  2. Bread Board. (We've used Adafruit Perma-Proto BreadBoards)
  3. 5V 2 Channel Relay Module
  4. DC-DC Power Module
  5. 12 or 24VDC Solenoid Valve
  6. 10K Ohm Resistor
  7. LED
  8. 220 Ohm Resistors
  9. Screw terminals
  10. Enclosure & Cable Glands
  11. 2.1mm barrel DC jack
  12. 12 or 24 VDC Power supply depending on your valve

Step 2: Wiring It Up

Here is the basic wiring diagram for the relay unit. You can see what it looks like once it has been built at the end of the instructable.

D5 and D6 are connected to two terminal block that you can connect external sensors or buttons to. These are optional and we haven't used them in this project (although the code has been included for them)

Step 3: Source Code

For the relay unit - we aren't posting any data - but reading a particular topic from our channel and then making a decision on what to do. i.e. open or close the the valve.

Hence in our config file we have a TOPIC that we can write to, and also a LISTEN-TOPIC that we are listening to, to change the position of the valve.

In addition to the relays we also have two inputs. These could be external buttons (which you could use to open and close the valve) or switches like a level-switch or Hall effect sensor. The code for these is included - but has been disabled for this example.

In our loop - we check the LISTEN-TOPIC every 5 seconds and return the latest reading from the topic. If the reading is 0 we open the valve. If it is anything else we close it.

  • You can download the full source code here


const String CHANNEL_ID = "XXXXXXXXX"; // The channel ID
const String SENSOR_ID = "XXXXXXXXX"; // Your AgriWebb or made up sensor ID
const String TOPIC = "solenoid-valve"; // The type of sensor or name of the data your sending
const String VERSION = "1";
const String FORMAT = "";
constchar* WIFI_SSID = "XXXXXXXXX"; // Your WiFi SSID / name
constchar* WIFI_PASS = "XXXXXXXXX"; // Your WiFi password
constuint16_t UPDATE_RATE = 5; // How long to wait between sending data back (in seconds)
constuint8_t TIMEZONE_OFFSET = 10; // The timezone the sensor is located in (eg. 10 for GMT)
const String LISTEN_TOPIC = "water-level"; // the name of the sensor that you are listening to
// make your solenoid valve change.
view rawconfig.h hosted with ❤ by GitHub


constint LED_PIN = 5; // The pin connecting the LED (D3)
constint INTERRUPT1_PIN = 14; // The pin connects the test button (D5)
constint INTERRUPT2_PIN = 12; // The pin connects the 2nd test button (D6)
constint SOLENOID_PIN = 13; // The pin connects to the relay (D7)
volatile byte interrupt = 0;
Ticker timer;
voidbutton1Interrupt() {
digitalWrite(LED_PIN, HIGH);
digitalWrite(SOLENOID_PIN, HIGH);
voidbutton2Interrupt() {
digitalWrite(LED_PIN, LOW);
digitalWrite(SOLENOID_PIN, LOW);
/* Interrupt timer for collecting data to the server */
voidsetup() {
digitalWrite(SOLENOID_PIN, HIGH);
digitalWrite(BUILTIN_LED, HIGH); // internal LED is switched on when low - so we have to switch it off/
while(!Serial) { } // Wait for serial to initialize.
Serial.println("Device Started");
Serial.print("Connecting to ");
client.wifiConnection(WIFI_SSID, WIFI_PASS);
attachInterrupt(digitalPinToInterrupt(INTERRUPT2_PIN), button2Interrupt, RISING); // Attach the interrupt.
attachInterrupt(digitalPinToInterrupt(INTERRUPT1_PIN), button1Interrupt, RISING); // Attach the interrupt.
timer.attach(UPDATE_RATE, readFromServer);
voidloop() {
String result = client.subscribe(LISTEN_TOPIC);
float reading = client.getReading(result);
if (reading == 0) {
digitalWrite(SOLENOID_PIN, HIGH);
} else {
digitalWrite(SOLENOID_PIN, LOW);
view rawrelayUnit.ino hosted with ❤ by GitHub

Step 4: The Finished Product

Here is the enclosure of the finished unit (valve not included). It has one cable gland for the valve wires to enter. As it also has a barrel jack for the power supply this version is recommended for use in exposed areas - but you could easily replace it with another cable gland and wire directly to the board to ensure it water tightness.