Infineon Team
Published © MIT

Plant Watering Automation

We use an Arduino Uno and a TLE94112 multi-half-bridge board to control a pump for automated plant watering.

BeginnerFull instructions provided4 hours7,912
Plant Watering Automation

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
DC Motor Shield with TLE94112ES for Arduino
Infineon DC Motor Shield with TLE94112ES for Arduino
×1
A 12 V DC water pump
Any Pump with VDC =< 12V and A < 1.5A should do. Be aware of current spikes during switching.
×1
Watering hose
The diameter should be chosen according to the fittings of the pump
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
not strictly necessary

Story

Read more

Schematics

Schematics

See here how to wire the board up

Code

plant_watering.ino

C/C++
The main file of the project.
/*!
 * \name        multi-half-bridge plant watering
 * \author      Infineon Technologies AG
 * \copyright   2021 Infineon Technologies AG
 * \version     1.0.0
 * \brief       A MCU controls one or more water pumps for regular automated plant watering
 * \details
 * This is to be published on hackster.io
 * A pump is controlled in order to water plants
 * SPDX-License-Identifier: MIT
 *
 */
#include <Arduino.h>
#include <tle94112-ino.hpp>
#include <tle94112-motor-ino.hpp>
#include <SPI.h>

#include "plant.hpp"
#include "helpers.hpp" //Logger

#define BAUD 115200

// Tle94112 Object on Shield 1
Tle94112Ino controller = Tle94112Ino();

// Tle94112Motor Objects on controller
Tle94112Motor motor_0(controller);

Logger logger(500, true); //logger wraps Serial.print in order to disable logging with one variable

Plant p1(&motor_0, 2000, 1);

const uint8_t plant_nr = 1;

Plant* plants[plant_nr] = { //array of all plants, for iteration purposes
  &p1
};

//Timing:
uint32_t millis_in_day = (uint32_t)1000*60*60*24; //calculate the milliseconds in a day, for timekeeping
// uint32_t millis_in_day = (uint32_t)1000*5; //this is the testing version to see immediate results
uint32_t last_pumped[plant_nr] = {0}; //this is not a plant::variable since timing method is independent of plant class

void setup()
{
  // Switch on communications
  Serial.begin(BAUD);
  while (!Serial){};

  // Enable MotorController on all Shields and Motors
  controller.begin();

  // IMPORTANT connect PWM to Lowside as higside is active Free wheeling
  motor_0.initConnector(motor_0.HIGHSIDE, controller.TLE_NOPWM, controller.TLE_HB1, controller.TLE_HB2, controller.TLE_HB7, controller.TLE_HB8);
  motor_0.initConnector(motor_0.LOWSIDE,  controller.TLE_PWM1,  controller.TLE_HB3, controller.TLE_HB4, controller.TLE_HB5, controller.TLE_HB6);

  // start the motor controller
  motor_0.begin();
  motor_0.coast();

  // end the setup function
  logger.logln("Init ready");
}

void loop()
{
  uint32_t ct = millis();
  for(int i = 0; i < plant_nr; i++){
    if((ct - last_pumped[i]) >= (plants[i]->getPump_interval_days()) * millis_in_day){
      logger.log("Pumping Pump ");
      logger.log(i);
      logger.log(" at ");
      logger.logln(ct);

      plants[i]->pump();
      last_pumped[i] = ct;
    }
  }
  logger.error_status(controller);
  delay((uint32_t)1000); //this can be increased even more
}

helpers.hpp

C/C++
#ifndef helpers_hpp
#define helpers_hpp

#include <tle94112-ino.hpp>
#include <tle94112-motor-ino.hpp>
#include <Arduino.h>

class Logger
{
private:
    /* data */
    uint32_t error_timer_millis = 0;
    uint32_t error_delay_millis = 500;

    bool logging = true;

public:
    Logger(uint32_t error_delay_millis_p, bool logging_p);

    void log(String txt);
    void logln(String txt);
    void log(int txt, int form);
    void logln(int txt, int form);
    void log(int txt);
    void logln(int txt);
    void log(char txt[]);
    void logln(char txt[]);
    void error_status(Tle94112Ino);
};

#endif

helpers.cpp

C/C++
#include "helpers.hpp"

Logger::Logger(uint32_t error_delay_millis_p, bool logging_p){
    error_delay_millis = error_delay_millis_p;
    logging = logging_p;
}

void Logger::log(String txt){
  if (logging)
  {
    Serial.print(txt);
  }
}
void Logger::logln(String txt){
  if (logging)
  {
    Serial.println(txt);
  }
}

void Logger::log(char txt[]){
  if (logging)
  {
    Serial.print(txt);
  }
}
void Logger::logln(char txt[]){
  if (logging)
  {
    Serial.println(txt);
  }
}

void Logger::log(int txt,  int form){
  if (logging)
  {
    Serial.print(txt, form);
  }
}
void Logger::logln(int txt, int form){
  if (logging)
  {
    Serial.println(txt, form);
  }
}

void Logger::log(int txt){
  if (logging)
  {
    Serial.print(txt, DEC);
  }
}
void Logger::logln(int txt){
  if (logging)
  {
    Serial.println(txt, DEC);
  }
}

void Logger::error_status(Tle94112Ino controller)
{
  uint32_t t = millis();
  if(t - error_timer_millis >= error_delay_millis){
    error_timer_millis = t;
    uint8_t status = controller.getSysDiagnosis();
    /**
       The SPI error flag shows if a SPI protocol
      error is detected.
    */
    if (status & controller.TLE_SPI_ERROR)
    {
      logln("SPI error detected!");
      // Handle the SPI error here.
    }

    /**
       The under voltage flag shows if a supply
      voltage below the lower limit is detected.

      All outputs are disabled when flag is set.

      This error is latched and needs to be
      cleared manually.
    */
    if (status & controller.TLE_UNDER_VOLTAGE)
    {
      logln("Under voltage detected!");
      // Handle the under voltage error here.
    }

    /**
       The over voltage flag shows if a supply
      voltage above the upper limit is detected.

      All outputs are disabled when flag is set.

      This error is latched and needs to be
      cleared manually.
    */
    if (status & controller.TLE_OVER_VOLTAGE)
    {
      logln("Over voltage detected!");
      // Handle the over voltage error here.
    }

    /**
       The over voltage flag shows if a supply
      voltage above the upper limit is detected.

      All outputs are disabled when flag is set.

      This error is latched and needs to be
      cleared manually.
    */
    if (status & controller.TLE_POWER_ON_RESET)
    {
      logln("Power on reset detected!");
      // Handle the power on reset here.
    }

    /**
       The pre-warning temperature flag shows that
      the junction temperature exceeded the
      temperature pre-warning threshold.

      This error is latched and needs to be
      cleared manually.
    */
    if (status & controller.TLE_TEMP_WARNING)
    {
      logln("Junction temperature above pre-warning threshold!");
      // Handle the temperature warning here.
    }

    /**
       The shutdown temperature flag shows that
      the junction temperature exceeded the
      shutdown temperature threshold.

      All outputs are disabled when flag is set.

      This error is latched and needs to be
      cleared manually.
    */
    if (status & controller.TLE_TEMP_SHUTDOWN)
    {
      logln("Junction temperature above shutdown threshold!");
      // Handle the temperature shutdown here.
    }

    /**
       The load error flag shows that either
      - an open load error or
      - an over-current error
      is detected on at least one output.

      The faulty output is disabled in case
      of an over-current error.

      This error is latched and needs to be
      cleared manually.
    */
    if (status & controller.TLE_LOAD_ERROR)
    {
      logln("Load error detected!");
      // Handle the Load error here.

      /**
         A load error can be specified more precisely.
        The chip knows which kind of error occurred in
        which half-bridge. This can be red as follows:
      */

      // For each half bridge (0 is placeholder for no half bridge)
      for (uint8_t halfBridge = 1; halfBridge <= 12; halfBridge++)
      {

        // Read over-current status of this half bridge from chip
        uint8_t oc = controller.getHBOverCurrent((Tle94112::HalfBridge)halfBridge);

        // Check for an over-current error on the low-side of this half bridge
        if (oc & controller.TLE_LOWSIDE)
        {
          log("\tHB");
          log(halfBridge);
          logln(":\tOver-current detected in low-side switch.");
        }

        // Check for an over-current error on the high-side of this half bridge
        if (oc & controller.TLE_HIGHSIDE)
        {
          log("\tHB");
          log(halfBridge);
          logln(":\tOver-current detected in high-side switch.");
        }

        // Read open load status of this half bridge from chip
        uint8_t ol = controller.getHBOpenLoad((Tle94112::HalfBridge)halfBridge);

        // Check for an open load error in this half bridge
        if (ol)
        {
          log("\tHB");
          log(halfBridge);
          logln(":\tOpen load detected.");
        }
      }
    }
    log("Error code: ");
    logln(status, BIN);
    controller.clearErrors();
  }
}

plant.hpp

C/C++
#ifndef plant_hpp
#define plant_hpp

#include <Arduino.h>
#include <tle94112-ino.hpp>
#include <tle94112-motor-ino.hpp>

class Plant {
private:
  /* data */
  uint32_t pump_duration; //pump powered time in millis
  uint8_t pump_interval_days; //nr of days between pumping
  Tle94112Motor *pump_motor;
  

public:

  void pump(); //function to pump water for pump_time length

  Plant(Tle94112Motor* pump_motor_p, uint32_t pump_duration_p, uint8_t pump_interval_days_p); //constructor
  
  void setPump_duration(uint32_t t_milli);
  uint32_t getPump_duration();
  
  void setPump_interval_days(uint8_t interval);
  uint8_t getPump_interval_days();
};

#endif

plant.cpp

C/C++
#include "plant.hpp"

Plant::Plant(Tle94112Motor *pump_motor_p, uint32_t pump_duration_p, uint8_t pump_interval_days_p){
    pump_duration = pump_duration_p;
    pump_interval_days = pump_interval_days_p;
    pump_motor = pump_motor_p;
}
void Plant::pump(){

    pump_motor->rampSpeed(255, pump_duration);
    delay(pump_duration);
    pump_motor->coast();       //coast the motor to save power (as opposed to forced holding)
}

////// getters and setters:

void Plant::setPump_duration(uint32_t t_milli){
    pump_duration = t_milli;
}
uint32_t Plant::getPump_duration(){
    return pump_duration;
}

void Plant::setPump_interval_days(uint8_t interval){
    pump_interval_days = interval;
}
uint8_t Plant::getPump_interval_days(){
    return pump_interval_days;
}

Infineon multi-half-bridge

Credits

Infineon Team
94 projects • 143 followers

Comments