nodeMCU ESP8266 w Lua - Moisture Sensor, Display and extended Breadboard

What we want to achieve

Using a nodeMCU ESP8266 development board, a moisture sensor, a Nokia 5110 LED display (with PCD8544 CMOS LCD controller/driver and Lua - we will build a moisture sensor which will:

  • Upon startup or reset execute the below only once
    • Show welcome screen for three seconds
    • average moisture reading using 10 sample reads over 5 seconds while showing the user a "please wait message"
    • output average moisture for two minutes
    • turn display off


  • nodeMCU ESP8266 compatible development board
  • Display with PCD8544 controller - e.g. Nokia 5110
  • nodeMCU firmware with the following modules included: adc,file,gpio,i2c,net,node,spi,tmr,u8g2,uart
    • u8g2 configuration - ensure you include your fonts. For the example here we will need the 6x10_tf and 5x8_tf font
  • Moisture sensor with
    • 3.3V VCC
    • GND
    • Analog Output with value output representing moisture
  • Breadboards and jumper wires
For simplicity, you can download a suitable firmware here.
The firmware was created using the online service


    • This tip is focused on Lua and ESP8266
    •  The pin connections are guaranteed to work with
      • our Nokia 5110 displays. Other similar displays might have a different pin layout and you will need to adapt to your specific module.
      • our moisture sensor
    • The tip uses SPI, hence there might be conflicts if you are using other SPI modules.
    • Using a single breadboard might be a bit restrictive due to the width of the nodeMCU. We have used two of our expandable breadboards and connected them together.

    How to

    We connect the display and moisture sensor to the nodeMCU. See our other tip for more details - click here. There is a small change (for VCC) - see below.

    Rather than feeding VCC for display and sensor straight from the 3.3V PIN on the nodeMCU, we will connect those to individual GPIOs.

    This will allow us to turn them on and off as needed in preparation of:

    • Power saving
    • Using more than one analog sensor on the single A0 PIN on the nodeMCU

    To read ADC0 (A0), we will need to make sure that the firmware has the adc module include.

    Keep in mind that - after the display and sensor is turned off, the nodeMCU board is still "running" and consuming power.

    To get a reading either reconnect the board or - if already connected - hit the reset button - for this to work, you need to ensure that code is uploaded as init.lua.
    You will also notice in the picture that we have merged two breadboards together to one to get more width. We did this to make the wiring easier.


       PCD8544/LED ESP8266
      Pin 1 - RST D0/GPIO16
      Pin 2 - CE (CS) D8/GPIO15 - pull down with 10k resistor to GND
      Pin 3 - DC D4/GPIO2
      Pin 4 - DIN D7/HMOSI (fixed - you can't use a different pin)
      Pin 5 - CLK D5/HSCLK (fixed - you can't use a different pin)
      Pin 6 - VCC D1/GPIO5
      Pin 7 - BL Not used in this example
      Pin 8 - GND GND - from Development board

      Moisture Sensor

       Sensor ESP8266
      VCC D2 /GPIO4
      GND - from Development board
      DO Not used
      AO A0/ADC0


      Moisture Sensor with output to display - PCD8544 (Nokia 5110 LED)

      run only once post reset/turn on (init.lua)
      - show welcome screen for three seconds
      - average moisture using 10 sample reads over 5 seconds, while showing
      user a "please wait message"
      - output average moisture

      Moisture reading to remain on display until device is turned off or
      reset button is pressed.

      15 November 2018
      nodeMCU ESP8266(EX) Devkit V3
      8 pin Nokia 5110 84x48 display w PCD8544
      Moisture sensor with Analog output

      PCD8544 -> NodeMCU 8266
      1 - RST -> D0/GPIO16
      2 - CE (CS) -> D8/GPIO15 - pull down with 10K to GND
      3 - DC -> D4/GPIO2
      4 - DIN -> D7/HMOSI
      5 - CLK -> D5/HSCLK
      6 - VCC -> D1/GPIO5
      7 - BL -> Not used in this example
      8 - GND -> GND

      Moisture S -> NodeMCU 8266
      VCC -> D2/GPIO4
      GND -> GND
      AO -> A0/ADC0
      nodeMCU Firmware Build
      built against the master branch and includes the following modules: adc, file, gpio, i2c, net, node, spi, timer, u8g2, uart, wifi, tls
      u8g2 - SPI - pcd8544_84x48 module
      u8g2 - fonts: 6x10_tf, 5x8)tf

      Note about a few - not so elegant code below.

      .. through the code we are using tmr.delay() to wait, before something
      else id done. Using tmr.delay is usually not a good idea as nothing
      else gets run. Seeing that all we do is a single sensor read, it is
      For "parallel" execution this should be implemented with

      -- PIN Variables
      PIN_CS = 8 -- GPIO15, pull-down 10k to GND
      PIN_DC = 4 -- GPIO2
      PIN_RES = 0 -- GPIO16
      PIN_LCD = 1 -- GPIO5 - VCC to LCD (turn on/off)
      PIN_MOI = 2 -- GPIO4 - VCC to Moisture Sensor (turn on/off)

      M_BUS = 1
      MOIST_R = 10 -- Number of values from moisture sensor to generate an average

      function outputMoistureData()
      -- vars we will use
      local loop_ctr = 0
      local aggregate_reading = 0

      -- setup screen with please wait
      disp:clearBuffer() -- start with clean buffer
      disp:setFont(u8g2.font_6x10_tf) -- set 6x10 font
      disp:drawStr(1, 1, "Please wait")
      disp:drawStr(1, 9, "Checking Soil")
      disp:sendBuffer() -- sent buffer to display

      -- turn moisture sensor on
      gpio.write(PIN_MOI, gpio.HIGH)
      tmr.delay(500 * 1000) -- wait for it to settle
      -- take number of readings and provide average
      while (loop_ctr < MOIST_R) do
      aggregate_reading = aggregate_reading +
      --print("aggregate_reading =" .. aggregate_reading)
      loop_ctr = loop_ctr + 1;
      -- wait one second (ish) before next reading
      tmr.delay(500 * 1000) -- wait for half of a second before next read
      -- turn moisture sensor off once done (to preserve battery)
      gpio.write(PIN_MOI, gpio.LOW)

      -- calculate average and print out
      disp:clearBuffer() -- start with clean buffer
      disp:setFont(u8g2.font_6x10_tf) -- set 6x10 font
      disp:drawStr(1, 1, "Soil Moisture")
      disp:drawStr(1,12, "..." .. tostring(aggregate_reading/MOIST_R))

      disp:sendBuffer() -- sent buffer to display

      INITIALISE - Begin
      -- set and init PCD8544 and Moist.Sensor GPIO to output
      gpio.mode(PIN_LCD, gpio.OUTPUT)
      gpio.write(PIN_LCD, gpio.LOW)
      gpio.mode(PIN_MOI, gpio.OUTPUT)
      gpio.write(PIN_MOI, gpio.LOW)

      -- Initialise PCD8544 module and show welcome screen
      gpio.write(PIN_LCD, gpio.HIGH)
      spi.setup(M_BUS, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 8)
      disp = u8g2.pcd8544_84x48(M_BUS, PIN_CS, PIN_DC, PIN_RES)


      -- start welcome screen
      disp:clearBuffer() -- start with clean buffer
      disp:setFont(u8g2.font_6x10_tf) -- set 6x10 font
      disp:drawStr(1, 1, "Moisture")
      disp:drawStr(1, 9, "Sensor V 1.0")
      disp:drawStr(2, 17, "by SOTM")
      disp:sendBuffer() -- sent buffer to display

      INITIALISE - End

      -- show moisture sensor data after 5 secs
      --tmr.alarm( 0, 3000, tmr.ALARM_SINGLE,
      tmr.delay(3000 * 1000) -- wait for three seconds before sensor read
      -- wait for 2 minutes and then turn LCD off
      tmr.delay(2 * 60 * 1000 * 1000)
      gpio.write(PIN_LCD, gpio.LOW)
      -- and done

      Leave a Reply

      All blog comments are checked prior to publishing