Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
user1793315
Published © GPL3+

Dosing Peristaltic Pump (PWM SSR), HX711 Scale and an FSM

Finite state machine, DC motor variable speed (PWM, solid state relay), software timer objects, HX711 scale and WawiLib. Tutorial value!

IntermediateFull instructions provided5,343
Dosing Peristaltic Pump (PWM SSR), HX711 Scale and an FSM

Things used in this project

Hardware components

Arduino Mega 2560
Arduino Mega 2560
×1
Arduino Ethernet Shield 2
Arduino Ethernet Shield 2
×1
Bench Power Supply, DC
Bench Power Supply, DC
×1
Digital Load Cell Weight Sensor HX711 AD Converter Breakout Module 5KG Portable Electronic Kitchen Scale for Arduino Scale new Digital Load Cell Weight Sensor HX711 AD Converter Breakout Module 5KG Portable Electronic Kitchen Scale for Arduino Sca
×1
DC Small Dosing Pump 3V 6V 12V 24V Micro Self priming Mute Mini Peristaltic Liquid
×1
OOTDTY 2 Channel SSR Solid State Relay High Lever Trigger 5A 5v12v For Uno R3 APR11_10
×1
Mini 5V Traffic Light LED Display Module for Arduino Red Yellow Green 5mm LED RG
×1

Software apps and online services

WawiLib

Story

Read more

Schematics

Scale

SSR

SSR power section

Code

The final program.

Arduino
//------------------------------------------------------------
#include <WawiEthernet.h>
// the media access control (ethernet hardware) address for the shield:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x88 }; 

// the IP address of Arduino shield:
byte ipArd[] = { 192, 168, 0, 88 };    

// the communication port Arduino side for WawiLib communication
unsigned int port = 49152;

// the router's gateway address:
byte gateway[] = { 10, 0, 0, 1 };

// the network subnet:
byte subnet[] = { 255, 255, 225, 0 };

// declare communication object:
EthernetServer server = EthernetServer(port);

// WawiLib communications object:
WawiEthernet WawiSrv;

//------------------------------------------------------------
// HX711 scale load cell:
#include "HX711.h" // credits to Bogdan Necula
HX711 scale;
const int LOADCELL_DOUT_PIN = 6; // HX711 data pin
const int LOADCELL_SCK_PIN = 7;  // HX711 clock pin
bool triggerTare;

#include "timer.h"
WawiTimer tWdScale; // watchdog timer scale communication
bool scaleOk=false; // scale communication status

//------------------------------------------------------------
// Finite state machine variables:
enum class FsmState       { STEP0_IOTEST=0, STEP1_IDLE=1, STEP2_TARRA=2, STEP3_WAIT_CMD_FILL=3, STEP4_P_FILL_FAST=4, STEP5_P_FILL_SLOW=5, STEP6_WAIT=6, STEP7_WAIT_CMD_EMPTY=7, STEP8_P_EMPTY_FAST=8, STEP9_P_EMTPY_TIMEBASED=9, STEP10_ERROR=10 };
const char* stepStrings[] { "Step 0: Iotest", "Step 1: Idle", "Step 2: Tarra scale", "Step 3: Await cmd fill", "Step 4: Fill fast", "Step 5: Fill slow", "Step 6: Wait time", "Step 7: Await cmd empty", "Step 8: Empty fast", "Step 9: Empty time based", "Step 10: Error" };
char stepString[30];
FsmState stepCurrent,stepPrev;
bool firstNewStep;

// commands for finite state machine:
bool cmdTarra;
bool cmdFill;
bool cmdEmpty;
bool cmdClrError;

// status variables
bool errScale, errTimeout;

// fill parameters
float wFillMax=100; 
float wFillBatchFast=10; 
float wFillBatchSlow=5; 
float wActual, wTargetStep;

unsigned long long tWdFillTimeout=30l*1000l; // 30 sec
unsigned long long tWdEmptyTimeout=300l*1000l; // 300 sec

WawiTimer tWdFsm; // watchdog timers for FSM time-out
bool ledR=0,ledY=0,ledG=0; // LED control
//------------------------------------------------------------
// digital outputs connected to solid state relays 1 and 2:
#define DO_SSR_P1 8
#define DO_SSR_P2 9

// PWM ratio control pump operation:
unsigned short pump1PwmFast=255;
unsigned short pump2PwmFast=255;
unsigned short pump1PwmSlow=128;
unsigned short pump2PwmSlow=128;
// actual value of SSR PWM setpoints:
unsigned short pump1Pwm=0;
unsigned short pump2Pwm=0;

//------------------------------------------------------------
// digital outputs connected to solid state relays 1 and 2:
#define DO_LED_RED 49
#define DO_LED_YELLOW 47
#define DO_LED_GREEN 45

// blinker bit helper timers (software timers):
WawiTimer tBlink250; // 250 ms on
WawiTimer tBlink500; // 500 ms
// blinking bits:
bool blink250;
bool blink500;

//------------------------------------------------------------
// make variables of interest known to WawiLib:
// this function is used in WawiSrv.begin(....)
void wawiVarDef()
{
  WawiSrv.wawiVar(triggerTare);
  WawiSrv.wawiVar(scaleOk);
  WawiSrv.wawiVar(tWdScale.m_act);
  
  WawiSrv.wawiVar(cmdTarra);
  WawiSrv.wawiVar(cmdFill);
  WawiSrv.wawiVar(cmdEmpty);
  WawiSrv.wawiVar(cmdClrError);
  
  WawiSrv.wawiVar(errScale);
  WawiSrv.wawiVar(errTimeout);
 
  WawiSrv.wawiVar(wFillMax);
  WawiSrv.wawiVar(wFillBatchFast);
  WawiSrv.wawiVar(wFillBatchSlow);
  WawiSrv.wawiVar(wActual);
  WawiSrv.wawiVar(wTargetStep);

  WawiSrv.wawiVar(tWdFillTimeout);
  WawiSrv.wawiVar(tWdEmptyTimeout);
  WawiSrv.wawiVar(tWdFsm);

  WawiSrv.wawiVar(stepCurrent);
  WawiSrv.wawiVar(stepPrev);
  WawiSrv.wawiVarArray(stepString);
  WawiSrv.wawiVar(stepString);
  WawiSrv.wawiVar(tWdFsm.m_act);

  WawiSrv.wawiVar(pump1PwmFast);
  WawiSrv.wawiVar(pump2PwmFast);
  WawiSrv.wawiVar(pump1PwmSlow);
  WawiSrv.wawiVar(pump2PwmSlow);
  WawiSrv.wawiVar(pump1Pwm);
  WawiSrv.wawiVar(pump2Pwm);

  WawiSrv.wawiVar(tBlink250.m_act);
  WawiSrv.wawiVar(tBlink500.m_act);
  WawiSrv.wawiVar(ledR);
  WawiSrv.wawiVar(ledG);
  WawiSrv.wawiVar(ledY);
}

//------------------------------------------------------------
// set finite state machine to other step:
void FsmGoTo(FsmState newStep) 
{
  stepCurrent = newStep;
  WawiSrv.println((String) "New step = "+ stepString + " (current weight=" +(String) wActual+")");
}

//------------------------------------------------------------
// processing of scale (0..5KG) communication:
void ScaleLoop()
{
  // read weight, if available:
  if (scale.is_ready())
  {
    // read weight
    wActual = scale.get_units(1);
    // set watchdog:    
    tWdScale.setMs(1000);
    // process tare cmd if required:
    if (triggerTare)
    {
      // reset cmd to Tare:
      triggerTare=0;
      scale.tare();  
      WawiSrv.print("Scale tare operation completed. ");
    }
  }  
  
  // process watchdog status:
  if (scaleOk != !tWdScale.isZero())
  {
    scaleOk = !tWdScale.isZero();
    if (scaleOk)
      WawiSrv.println("Weight processing restarted, scale ok. ");
    else
      WawiSrv.println("Weight processing stopped, scale communication fault. ");
  }

  // loop part:
  tWdScale.loop();
}

//------------------------------------------------------------
// Finite state machine:
void FsmLoop() 
{
  switch(stepCurrent)
  {
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP0_IOTEST: 
    {
      // no control of IO (for test purposes)
      break;
    }
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP1_IDLE: 
    {
      if (cmdFill && !cmdTarra) FsmGoTo(FsmState::STEP3_WAIT_CMD_FILL); 
      if (cmdTarra) FsmGoTo(FsmState::STEP2_TARRA); 
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP2_TARRA:    
    {
      // send cmd to ScaleLoop()
      if (firstNewStep)
      {
        tWdFsm.setMs(1000);
        triggerTare=true;
      }
      // cmd completed OK:
      if (triggerTare==false)
        FsmGoTo(FsmState::STEP3_WAIT_CMD_FILL); 
      
      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step tarra scale: timout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
      if (scaleOk==false)
      {
        errScale=true;
        WawiSrv.println("Error in step tarra scale: communication with scale failure.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP3_WAIT_CMD_FILL:    
    {
      if (cmdFill) 
        FsmGoTo(FsmState::STEP4_P_FILL_FAST); 
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP4_P_FILL_FAST:    
    {
      if (firstNewStep) 
      {
        tWdFsm.setMs(tWdFillTimeout); // timeout value for slow and fast filling
        wTargetStep=wActual+wFillBatchFast;
      }

      if (wActual>wFillMax-wFillBatchSlow || wActual>wTargetStep) 
        FsmGoTo(FsmState::STEP5_P_FILL_SLOW); 

      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step fill fast: timeout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP5_P_FILL_SLOW:    
    {
      if (firstNewStep) 
      {
        wTargetStep=wActual+wFillBatchSlow;
      }

      if (wActual>wFillMax || wActual>wTargetStep) 
        FsmGoTo(FsmState::STEP6_WAIT); 

      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step fill slow: timeout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP6_WAIT:    
    {
      if (firstNewStep) 
        tWdFsm.setMs(20000);

      if (tWdFsm.isZero() && wActual<wFillMax) FsmGoTo(FsmState::STEP4_P_FILL_FAST); 
      if (tWdFsm.isZero() && wActual>=wFillMax) FsmGoTo(FsmState::STEP7_WAIT_CMD_EMPTY); 
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP7_WAIT_CMD_EMPTY:    
    {
      if (cmdEmpty) FsmGoTo(FsmState::STEP8_P_EMPTY_FAST); 
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP8_P_EMPTY_FAST:    
    {
      if (firstNewStep) 
        tWdFsm.setMs(tWdEmptyTimeout);

      if (wActual<5) 
        FsmGoTo(FsmState::STEP9_P_EMTPY_TIMEBASED); 
      
      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step empty fast: timeout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP9_P_EMTPY_TIMEBASED:    
    {
      if (firstNewStep) tWdFsm.setMs(10000); // 10 seconds
      
      if (tWdFsm.isZero())
        FsmGoTo(FsmState::STEP3_WAIT_CMD_FILL); 
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP10_ERROR:    
    {
      if (cmdClrError) 
      {
        FsmGoTo(FsmState::STEP1_IDLE); 
        errTimeout=false;
        errScale=false;
        cmdClrError=false;
      }
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    default:
    {
      stepCurrent = FsmState::STEP1_IDLE;
      break;
    }
  }  
  firstNewStep=(stepCurrent!=stepPrev);
  stepPrev=stepCurrent;
  strcpy(stepString,stepStrings[(int) stepCurrent]);
  tWdFsm.loop();
}

//----------------------------------------------------------------
// control I/O based on FSM step
void IoLoop()
{
  ledR=0;
  switch(stepCurrent)
  {
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP0_IOTEST: 
      // blink fast all leds = IO check
      ledR=blink250;   
      ledY=blink250;   
      ledG=blink250;   
      break;
     
    //+++++++++++++++++++++++++++++++++++++++
     case FsmState::STEP1_IDLE:
     case FsmState::STEP3_WAIT_CMD_FILL:    
     case FsmState::STEP7_WAIT_CMD_EMPTY: 
      // yellow blink is wait for input/user intervention:   
      ledG=0;   
      ledR=0;
      ledY=blink500; 
      pump1Pwm=0;
      pump2Pwm=0;
      break;
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP4_P_FILL_FAST:    
      pump1Pwm=pump1PwmFast;
      pump2Pwm=0;
      ledG=blink250;   
      ledR=0;
      ledY=0;
      break;
   
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP5_P_FILL_SLOW:    
      // green slow = pump slow
      pump1Pwm=pump1PwmSlow;
      pump2Pwm=0;
      ledG=blink500;   
      ledR=0;
      ledY=0;
      break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP6_WAIT:    
      pump1Pwm=0;
      pump2Pwm=0;
      ledG=1;   
      ledR=0;
      break;
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP8_P_EMPTY_FAST:    
      pump1Pwm=0;
      pump2Pwm=pump2PwmFast;
      ledG=blink250;   
      ledR=0;
      ledY=0;
      break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP9_P_EMTPY_TIMEBASED:    
      pump1Pwm=0;
      pump2Pwm=pump2PwmSlow;
      ledG=blink500;   
      ledR=0;
      ledY=0;
      break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP10_ERROR:    
      pump1Pwm=0;
      pump2Pwm=0;
      ledG=blink500;   
      ledR=0;
      ledY=0;
      break;

    //+++++++++++++++++++++++++++++++++++++++
    default:
      pump1Pwm=0;
      pump2Pwm=0;
      ledR=250;
      ledY=0;
      ledG=0;
      break;
  }  
  
  // limit pumpxPwm to range 0..255
  pump1Pwm=min(pump1Pwm,255);
  pump2Pwm=min(pump2Pwm,255);

  // send pump value to IO:
  analogWrite(DO_SSR_P1, 255-pump1Pwm);
  analogWrite(DO_SSR_P2, 255-pump2Pwm);
  
  // control the LED's
  digitalWrite(DO_LED_RED,ledR);
  digitalWrite(DO_LED_YELLOW,ledY);
  digitalWrite(DO_LED_GREEN,ledG);
}


//------------------------------------------------------------
// create blinkbits:
void UtilsLoop()
{
  if (tBlink250.isZero())
  {
    tBlink250.setMs(250);
    blink250=!blink250;  
  }
  if (tBlink500.isZero())
  {
    tBlink500.setMs(500);
    blink500=!blink500;  
  }

  tBlink250.loop(); // 250 ms on
  tBlink500.loop(); // 500 ms
}

//------------------------------------------------------------
void setup() 
{
  // init ethernet and WawiLib:
  Ethernet.begin(mac, ipArd, gateway, subnet);
  server.begin();
  WawiSrv.begin(wawiVarDef, server, "MyArduino");

  // init finit State machine:
  FsmGoTo(FsmState::STEP1_IDLE);
  stepPrev=FsmState::STEP0_IOTEST;

  // init scale HX711:
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(417.f); 

  // init PWM IO:
  pinMode(DO_SSR_P1, OUTPUT);
  pinMode(DO_SSR_P2, OUTPUT);

  // LED IO
  pinMode(DO_LED_RED,OUTPUT);
  pinMode(DO_LED_YELLOW,OUTPUT);
  pinMode(DO_LED_GREEN,OUTPUT);

  // reduce PWM frequency to 30 hz
  TCCR2B = TCCR2B & B11111000 | B00000111; // D9 to 30 Hz
  TCCR4B = TCCR4B & B11111000 | B00000101; // D8 to 30 Hz
}
//------------------------------------------------------------
void loop()
{
  // read weight of scale:
  ScaleLoop();
  // finite state machine loop:
  FsmLoop();
  // sent results to IO:
  IoLoop();
  // varia (blinkbits ...)
  UtilsLoop();
  // wawilib communication update:
  WawiSrv.loop();
}

THE Program

Arduino
//------------------------------------------------------------
#include <WawiEthernet.h>
// the media access control (ethernet hardware) address for the shield:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x88 }; 

// the IP address of Arduino shield:
byte ipArd[] = { 192, 168, 0, 88 };    

// the communication port Arduino side for WawiLib communication
unsigned int port = 49152;

// the router's gateway address:
byte gateway[] = { 10, 0, 0, 1 };

// the network subnet:
byte subnet[] = { 255, 255, 225, 0 };

// declare communication object:
EthernetServer server = EthernetServer(port);

// WawiLib communications object:
WawiEthernet WawiSrv;

//------------------------------------------------------------
// HX711 scale load cell:
#include "HX711.h" // credits to Bogdan Necula
HX711 scale;
const int LOADCELL_DOUT_PIN = 6; // HX711 data pin
const int LOADCELL_SCK_PIN = 7;  // HX711 clock pin
bool triggerTare;

#include "timer.h"
WawiTimer tWdScale; // watchdog timer scale communication
bool scaleOk=false; // scale communication status

//------------------------------------------------------------
// Finite state machine variables:
enum class FsmState       { STEP0_IOTEST=0, STEP1_IDLE=1, STEP2_TARRA=2, STEP3_WAIT_CMD_FILL=3, STEP4_P_FILL_FAST=4, STEP5_P_FILL_SLOW=5, STEP6_WAIT=6, STEP7_WAIT_CMD_EMPTY=7, STEP8_P_EMPTY_FAST=8, STEP9_P_EMTPY_TIMEBASED=9, STEP10_ERROR=10 };
const char* stepStrings[] { "Step 0: Iotest", "Step 1: Idle", "Step 2: Tarra scale", "Step 3: Await cmd fill", "Step 4: Fill fast", "Step 5: Fill slow", "Step 6: Wait time", "Step 7: Await cmd empty", "Step 8: Empty fast", "Step 9: Empty time based", "Step 10: Error" };
char stepString[30];
FsmState stepCurrent,stepPrev;
bool firstNewStep;

// commands for finite state machine:
bool cmdTarra;
bool cmdFill;
bool cmdEmpty;
bool cmdClrError;

// status variables
bool errScale, errTimeout;

// fill parameters
float wFillMax=100; 
float wFillBatchFast=10; 
float wFillBatchSlow=5; 
float wActual, wTargetStep;

unsigned long long tWdFillTimeout=30l*1000l; // 30 sec
unsigned long long tWdEmptyTimeout=300l*1000l; // 300 sec

WawiTimer tWdFsm; // watchdog timers for FSM time-out
bool ledR=0,ledY=0,ledG=0; // LED control
//------------------------------------------------------------
// digital outputs connected to solid state relays 1 and 2:
#define DO_SSR_P1 8
#define DO_SSR_P2 9

// PWM ratio control pump operation:
unsigned short pump1PwmFast=255;
unsigned short pump2PwmFast=255;
unsigned short pump1PwmSlow=128;
unsigned short pump2PwmSlow=128;
// actual value of SSR PWM setpoints:
unsigned short pump1Pwm=0;
unsigned short pump2Pwm=0;

//------------------------------------------------------------
// digital outputs connected to solid state relays 1 and 2:
#define DO_LED_RED 49
#define DO_LED_YELLOW 47
#define DO_LED_GREEN 45

// blinker bit helper timers (software timers):
WawiTimer tBlink250; // 250 ms on
WawiTimer tBlink500; // 500 ms
// blinking bits:
bool blink250;
bool blink500;

//------------------------------------------------------------
// make variables of interest known to WawiLib:
// this function is used in WawiSrv.begin(....)
void wawiVarDef()
{
  WawiSrv.wawiVar(triggerTare);
  WawiSrv.wawiVar(scaleOk);
  WawiSrv.wawiVar(tWdScale.m_act);
  
  WawiSrv.wawiVar(cmdTarra);
  WawiSrv.wawiVar(cmdFill);
  WawiSrv.wawiVar(cmdEmpty);
  WawiSrv.wawiVar(cmdClrError);
  
  WawiSrv.wawiVar(errScale);
  WawiSrv.wawiVar(errTimeout);
 
  WawiSrv.wawiVar(wFillMax);
  WawiSrv.wawiVar(wFillBatchFast);
  WawiSrv.wawiVar(wFillBatchSlow);
  WawiSrv.wawiVar(wActual);
  WawiSrv.wawiVar(wTargetStep);

  WawiSrv.wawiVar(tWdFillTimeout);
  WawiSrv.wawiVar(tWdEmptyTimeout);
  WawiSrv.wawiVar(tWdFsm);

  WawiSrv.wawiVar(stepCurrent);
  WawiSrv.wawiVar(stepPrev);
  WawiSrv.wawiVarArray(stepString);
  WawiSrv.wawiVar(stepString);
  WawiSrv.wawiVar(tWdFsm.m_act);

  WawiSrv.wawiVar(pump1PwmFast);
  WawiSrv.wawiVar(pump2PwmFast);
  WawiSrv.wawiVar(pump1PwmSlow);
  WawiSrv.wawiVar(pump2PwmSlow);
  WawiSrv.wawiVar(pump1Pwm);
  WawiSrv.wawiVar(pump2Pwm);

  WawiSrv.wawiVar(tBlink250.m_act);
  WawiSrv.wawiVar(tBlink500.m_act);
  WawiSrv.wawiVar(ledR);
  WawiSrv.wawiVar(ledG);
  WawiSrv.wawiVar(ledY);
}

//------------------------------------------------------------
// set finite state machine to other step:
void FsmGoTo(FsmState newStep) 
{
  stepCurrent = newStep;
  WawiSrv.println((String) "New step = "+ stepString + " (current weight=" +(String) wActual+")");
}

//------------------------------------------------------------
// processing of scale (0..5KG) communication:
void ScaleLoop()
{
  // read weight, if available:
  if (scale.is_ready())
  {
    // read weight
    wActual = scale.get_units(1);
    // set watchdog:    
    tWdScale.setMs(1000);
    // process tare cmd if required:
    if (triggerTare)
    {
      // reset cmd to Tare:
      triggerTare=0;
      scale.tare();  
      WawiSrv.print("Scale tare operation completed. ");
    }
  }  
  
  // process watchdog status:
  if (scaleOk != !tWdScale.isZero())
  {
    scaleOk = !tWdScale.isZero();
    if (scaleOk)
      WawiSrv.println("Weight processing restarted, scale ok. ");
    else
      WawiSrv.println("Weight processing stopped, scale communication fault. ");
  }

  // loop part:
  tWdScale.loop();
}

//------------------------------------------------------------
// Finite state machine:
void FsmLoop() 
{
  switch(stepCurrent)
  {
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP0_IOTEST: 
    {
      // no control of IO (for test purposes)
      break;
    }
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP1_IDLE: 
    {
      if (cmdFill && !cmdTarra) FsmGoTo(FsmState::STEP3_WAIT_CMD_FILL); 
      if (cmdTarra) FsmGoTo(FsmState::STEP2_TARRA); 
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP2_TARRA:    
    {
      // send cmd to ScaleLoop()
      if (firstNewStep)
      {
        tWdFsm.setMs(1000);
        triggerTare=true;
      }
      // cmd completed OK:
      if (triggerTare==false)
        FsmGoTo(FsmState::STEP3_WAIT_CMD_FILL); 
      
      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step tarra scale: timout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
      if (scaleOk==false)
      {
        errScale=true;
        WawiSrv.println("Error in step tarra scale: communication with scale failure.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP3_WAIT_CMD_FILL:    
    {
      if (cmdFill) 
        FsmGoTo(FsmState::STEP4_P_FILL_FAST); 
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP4_P_FILL_FAST:    
    {
      if (firstNewStep) 
      {
        tWdFsm.setMs(tWdFillTimeout); // timeout value for slow and fast filling
        wTargetStep=wActual+wFillBatchFast;
      }

      if (wActual>wFillMax-wFillBatchSlow || wActual>wTargetStep) 
        FsmGoTo(FsmState::STEP5_P_FILL_SLOW); 

      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step fill fast: timeout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP5_P_FILL_SLOW:    
    {
      if (firstNewStep) 
      {
        wTargetStep=wActual+wFillBatchSlow;
      }

      if (wActual>wFillMax || wActual>wTargetStep) 
        FsmGoTo(FsmState::STEP6_WAIT); 

      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step fill slow: timeout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP6_WAIT:    
    {
      if (firstNewStep) 
        tWdFsm.setMs(20000);

      if (tWdFsm.isZero() && wActual<wFillMax) FsmGoTo(FsmState::STEP4_P_FILL_FAST); 
      if (tWdFsm.isZero() && wActual>=wFillMax) FsmGoTo(FsmState::STEP7_WAIT_CMD_EMPTY); 
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP7_WAIT_CMD_EMPTY:    
    {
      if (cmdEmpty) FsmGoTo(FsmState::STEP8_P_EMPTY_FAST); 
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP8_P_EMPTY_FAST:    
    {
      if (firstNewStep) 
        tWdFsm.setMs(tWdEmptyTimeout);

      if (wActual<5) 
        FsmGoTo(FsmState::STEP9_P_EMTPY_TIMEBASED); 
      
      if (tWdFsm.isZero())
      {
        errTimeout=true;
        WawiSrv.println("Error in step empty fast: timeout.");
        FsmGoTo(FsmState::STEP10_ERROR); 
      }
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP9_P_EMTPY_TIMEBASED:    
    {
      if (firstNewStep) tWdFsm.setMs(10000); // 10 seconds
      
      if (tWdFsm.isZero())
        FsmGoTo(FsmState::STEP3_WAIT_CMD_FILL); 
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP10_ERROR:    
    {
      if (cmdClrError) 
      {
        FsmGoTo(FsmState::STEP1_IDLE); 
        errTimeout=false;
        errScale=false;
        cmdClrError=false;
      }
    }
    break;

    //+++++++++++++++++++++++++++++++++++++++
    default:
    {
      stepCurrent = FsmState::STEP1_IDLE;
      break;
    }
  }  
  firstNewStep=(stepCurrent!=stepPrev);
  stepPrev=stepCurrent;
  strcpy(stepString,stepStrings[(int) stepCurrent]);
  tWdFsm.loop();
}

//----------------------------------------------------------------
// control I/O based on FSM step
void IoLoop()
{
  ledR=0;
  switch(stepCurrent)
  {
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP0_IOTEST: 
      // blink fast all leds = IO check
      ledR=blink250;   
      ledY=blink250;   
      ledG=blink250;   
      break;
     
    //+++++++++++++++++++++++++++++++++++++++
     case FsmState::STEP1_IDLE:
     case FsmState::STEP3_WAIT_CMD_FILL:    
     case FsmState::STEP7_WAIT_CMD_EMPTY: 
      // yellow blink is wait for input/user intervention:   
      ledG=0;   
      ledR=0;
      ledY=blink500; 
      pump1Pwm=0;
      pump2Pwm=0;
      break;
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP4_P_FILL_FAST:    
      pump1Pwm=pump1PwmFast;
      pump2Pwm=0;
      ledG=blink250;   
      ledR=0;
      ledY=0;
      break;
   
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP5_P_FILL_SLOW:    
      // green slow = pump slow
      pump1Pwm=pump1PwmSlow;
      pump2Pwm=0;
      ledG=blink500;   
      ledR=0;
      ledY=0;
      break;
    
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP6_WAIT:    
      pump1Pwm=0;
      pump2Pwm=0;
      ledG=1;   
      ledR=0;
      break;
    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP8_P_EMPTY_FAST:    
      pump1Pwm=0;
      pump2Pwm=pump2PwmFast;
      ledG=blink250;   
      ledR=0;
      ledY=0;
      break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP9_P_EMTPY_TIMEBASED:    
      pump1Pwm=0;
      pump2Pwm=pump2PwmSlow;
      ledG=blink500;   
      ledR=0;
      ledY=0;
      break;

    //+++++++++++++++++++++++++++++++++++++++
    case FsmState::STEP10_ERROR:    
      pump1Pwm=0;
      pump2Pwm=0;
      ledG=blink500;   
      ledR=0;
      ledY=0;
      break;

    //+++++++++++++++++++++++++++++++++++++++
    default:
      pump1Pwm=0;
      pump2Pwm=0;
      ledR=250;
      ledY=0;
      ledG=0;
      break;
  }  
  
  // limit pumpxPwm to range 0..255
  pump1Pwm=min(pump1Pwm,255);
  pump2Pwm=min(pump2Pwm,255);

  // send pump value to IO:
  analogWrite(DO_SSR_P1, 255-pump1Pwm);
  analogWrite(DO_SSR_P2, 255-pump2Pwm);
  
  // control the LED's
  digitalWrite(DO_LED_RED,ledR);
  digitalWrite(DO_LED_YELLOW,ledY);
  digitalWrite(DO_LED_GREEN,ledG);
}


//------------------------------------------------------------
// create blinkbits:
void UtilsLoop()
{
  if (tBlink250.isZero())
  {
    tBlink250.setMs(250);
    blink250=!blink250;  
  }
  if (tBlink500.isZero())
  {
    tBlink500.setMs(500);
    blink500=!blink500;  
  }

  tBlink250.loop(); // 250 ms on
  tBlink500.loop(); // 500 ms
}

//------------------------------------------------------------
void setup() 
{
  // init ethernet and WawiLib:
  Ethernet.begin(mac, ipArd, gateway, subnet);
  server.begin();
  WawiSrv.begin(wawiVarDef, server, "MyArduino");

  // init finit State machine:
  FsmGoTo(FsmState::STEP1_IDLE);
  stepPrev=FsmState::STEP0_IOTEST;

  // init scale HX711:
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(417.f); 

  // init PWM IO:
  pinMode(DO_SSR_P1, OUTPUT);
  pinMode(DO_SSR_P2, OUTPUT);

  // LED IO
  pinMode(DO_LED_RED,OUTPUT);
  pinMode(DO_LED_YELLOW,OUTPUT);
  pinMode(DO_LED_GREEN,OUTPUT);

  // reduce PWM frequency to 30 hz
  TCCR2B = TCCR2B & B11111000 | B00000111; // D9 to 30 Hz
  TCCR4B = TCCR4B & B11111000 | B00000101; // D8 to 30 Hz
}
//------------------------------------------------------------
void loop()
{
  // read weight of scale:
  ScaleLoop();
  // finite state machine loop:
  FsmLoop();
  // sent results to IO:
  IoLoop();
  // varia (blinkbits ...)
  UtilsLoop();
  // wawilib communication update:
  WawiSrv.loop();
}

Credits

user1793315
2 projects • 1 follower
Contact

Comments

Please log in or sign up to comment.