Skip to content

JPWaldin/RPI_Coop

Repository files navigation

Raspberry Pi Chicken Coop GUI Project

Screenshot of phone

This is a NODE.js application to control the chicken coop environment with a Raspberry Pi. The concept was proposed to me when my son-in-law introduced me to the Raspberry PI and suggested it could control some of the things we do manually for the chickens.

I share with you what I did and what I used that worked. However, you are welcome to deviate from that if you wish. Your mileage may vary.

Hardware Requirements

Modules

  • node 18.13.0 LTS
  • bme280 I2C driver for the BME280 sensor on the Raspberry Pi
  • onoff GPIO access and interrupt detection on the Raspberry Pi
  • fastify web framework for Node.js.
  • @fastify-static Plugin for serving static files as fast as possible.
  • fastify-socket.io Enables the use of Socket.io in a Fastify app.
  • socket.io Enables bidirectional and event-based communication between a client and a server.
  • bootstrap Allows for proper responsive behavior in mobile devices.

Raspberry PI 64-bit OS Lite

Photocell Configuration

GPIO Configuration

Retrieve the Humidity, Pressure and Temperature to be used in the environment control routines

Note: Once the event listeners are registered in memory, the While Loop is where controlling takes place. There is a delay shown here. I have used 2-3 seconds with no ill effects but you can delay longer if you like.

const runApplication = async _ => {
  const format = number => (Math.round(number * 100) / 100).toFixed(2)
  const delay = millis => new Promise(resolve => setTimeout(resolve, millis))

  while (true) {
    const sensor = await bme280.open({
      i2cBusNumber: 1,
      i2cAddress: 0x77,
      humidityOversampling: bme280.OVERSAMPLE.X1,
      pressureOversampling: bme280.OVERSAMPLE.X16,
      temperatureOversampling: bme280.OVERSAMPLE.X2,
      filterCoefficient: bme280.FILTER.F16
    })

    const reading = await sensor.read()
    objConfig._degC = format(reading.temperature)
    objConfig._degF = format((objConfig._degC * 1.8)   32)
    objConfig._pctRH = format(reading.humidity)
    objConfig._inHg = format(reading.pressure * 0.02953)

    // console.log(objConfig._degC   ' degC '   objConfig._degF   ' degF '   objConfig._pctRH   '% RH '   objConfig._inHg   ' inHg')

    await sensor.close()

    await delay(2000) // 1000 = 1 second
    await heatController()  // see controller logic example below
    await exhaustController()
    await doorController()
    await lightController()
    refreshPageData() // see example below
  }
}

Example of the controller logic for the heat

We turn on/off the gpio as needed but only write the text "ON" or "OFF" to the config file. After the config object is populated it is then sent to the client via sockit-io.

async function heatController () {
  // console.log(`_heatMode: ${objConfig._heatMode} -- _heatRelayTxt: ${objConfig._heatRelayTxt}  -- gpioHeat.readSync(): ${gpioHeat.readSync()}`)
  // Only run if mode = Auto
  if (objConfig._heatMode === 'Auto') {
    if (objConfig._degF < objConfig._heatSetPnt) {
      // Console.log('Turning Relay ON');
      if (gpioHeat.readSync() === off) {
        gpioHeat.writeSync(on) // Turn relay on
      }
      objConfig._heatRelayTxt = 'ON'
    }

    if (objConfig._degF > (objConfig._heatSetPnt   1)) {
      // Console.log('Turning Relay OFF');
      if (gpioHeat.readSync() === on) {
        gpioHeat.writeSync(off) // Turn relay off
      }
      objConfig._heatRelayTxt = 'OFF'
    }
  } else {
    // Only run if mode = Manual
    if (objConfig._heatRelayTxt === 'ON') {
      if (gpioHeat.readSync() === off) {
        gpioHeat.writeSync(on) // Turn relay on
      }
    } else {
      if (gpioHeat.readSync() === on) {
        gpioHeat.writeSync(off) // Turn relay off
      }
    }
  }
  // console.log(`HEAT -- DegF: ${objConfig._degF} -- SetPnt: ${objConfig._heatSetPnt} -- Mode: ${objConfig._heatMode} -- Relay: ${objConfig._heatRelayTxt} -- Gpio: ${gpioHeat.readSync()}`)
}

Refresh the client page

The config object is populated by many different functions through out the code and finally is pushed to the client by this function.

function refreshPageData () {
  // Send status to client only if there is a client connected.
  if (clientCnt > 0) {
    // Let's update the current time
    objConfig._svrTime = currentTime()
    // Monitor the gpio status
    objConfig._heatRelayTxt = gpioStatus(gpioHeat, 'OnOff')
    objConfig._exhaustRelayTxt = gpioStatus(gpioExhaust, 'OnOff')
    objConfig._doorRelayTxt = gpioStatus(gpioDoor, 'OpenClose')
    objConfig._lightRelayTxt = gpioStatus(gpioLight, 'OnOff')
    objConfig._photocellTxt = gpioStatus(gpioPhoto, 'OnOff')
    // push the data to the html page
    svr.io.sockets.emit('refreshPageData', objConfig)
  }
}

Issues? Questions? Contributions?

Feel free to leave comments or questions in my YouTube Video GUI or use the Discussion Page