Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
This application shows a dosing application using 2 peristaltic pumps (PWM driven via solid state relays), 1 digital weight transducer (using a HX711), a LED traffic light and an Arduino Mega2560 with an Ethernet shield.
First the vessel on the scale is filled in 15 ml steps up to 100ml and then it is emptied. Commands to start and stop are issued via Ethernet using WawiLib to read and write sketch variables. The state changes of the state machine are reflected to the output window of WawiLib.
There are 3 movies documenting the application:
Technologies used and explained in detail:
// Finite state machine code expamle:
//+++++++++++++++++++++++++++++++++++++++
case FsmState::STEP3_WAIT_CMD_FILL:
{
if (cmdFill)
FsmGoTo(FsmState::STEP4_P_FILL_FAST);
}
break;
Arduino "millis()" based software timers (as a C++ object in a separate file in the sketch). The use is different from what you normally see (millis() is encapsulated in the C++ object and the object is added in a separate file to the ide). All is explained step by step in the.pdf that can be downloaded from sylvestersolutions.com/documentation.
// blinking 2 timers at the same time with variable time period:
#include "WawiTimer.h"
// blinker bit helper timers(software timers):
WawiTimer tBlink250; // 250 ms on
WawiTimer tBlink500; // 500 ms
// blinking bits that toggle:
bool blink250;
bool blink500;
//------------------------------------------------------------
// create blinkbits:
void Loop()
{
if (tBlink250.isZero())
{
tBlink250.setMs(250);
blink250 = !blink250;
}
if
{
tBlink500.setMs(500);
blink500 = !blink500;
}
tBlink250.loop(); // 250 ms
tBlink500.loop(); // 500 ms
}
Forget about millis()! Add your C++ timer object that is counts down so your loop is not interrupted: the only thing you need to do is add 2 files to your sketch! (The documentation explains in detail how.)
- PWM (pulse width modulation) via SSR (solid state relay) on a DC motor.
Pulse width modulation is a technique used to control (e.g.) the speed of an DC motor. Some of the Arduino outputs can switch on and of very fast (hardware based) at multiple kHz. If you use this function to regulate the current to a DC motor you can make it run at variable speed. The movie and documents explain in detail how to do this.
A DC motor contains a coil. As a coil is an inductance. You must make sure that its current flow is not interrupted when the relay opens. A diode is the ideal component to do this. This tutorial explains the concept of a flywheel diode in detail.
- The AppNotePeriPump.pdf detailed tutorial can be downloaded from www.sylvestersolutions.com explaining the details step by step. Go to the documentation page of the Sylvester Solutions website.
If you do not know how to program an automation task properly (i.e. without delay statements). Look at this tutorial and you will learn a lot. All concepts are in detail explained in the pdf and the.ino source code is also included.
The code can be used as a framework for many other applications as it is structured like the pro's do.
Have fun and learn!
//------------------------------------------------------------
#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();
}
//------------------------------------------------------------
#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();
}
Comments
Please log in or sign up to comment.