Project tutorial
Submersible Pump

Submersible Pump © GPL3+

Keep your home foundation dry and log all pump actions. The control is built with an Arduino MEGA, HMI and logging on your Windows 10 PC.

  • 1,359 views
  • 0 comments
  • 8 respects

Components and supplies

Ardgen mega
Arduino Mega 2560 & Genuino Mega 2560
×1
Relay Module (Generic)
×1
ACS712
5A Range Current New Sensor Module ACS712 Module For Arduino Board
×1
Ed01 09 0049 a 600x6001  23921.1539128511
Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer
Temperature sensor
×1

Apps and online services

About this project

Submersible pump

Since the building of our house, we had a lot of water in the foundation, sometimes about 40-50 m3 of water. So I have made a system under the house that lead the water to a sump with a submersible pump. The pump was needed because the sewerage level was higher then the level of water in the foundation. At a certain level the sump is emptied by pumping the water automatically to the sewerage. The submersible pump has a floating switch and works autonomous. I was curious how it was working or better; is it working? Seems that he was doing his work, because there was no longer water in the foundation.... but to verify, I had to open a hatch. Lot of stupid work. So years ago I build a system with "conventional" components. The system counted the times of the operation of the pump. Even a time-out was present when the pump was working longer then 3 minutes; something must be wrong with the pump. From the number of operations it was possible to see if the pump was working frequently or -maybe- is out of order...?

The old stuff and the new submersible pump control.

The new control is build with an Arduino MEGA2560, a Hall element for measuring the current of the well pump and an APC220 transmitter for the communication with the PC. The APC220 is used because of the distance between the control and the PC. The distance is more than 20m and not ideal for Bluetooth. RF is better and an APC220 set is working perfect. When the well pump is active, this action is processed by the Arduino board. In most cases the pump operates for 45 sec to pump all the water from the sump to the sewerage. For reporting and get ride of possible disturbances, the indication of the pump must be present for at least 5 seconds. When the pump is active for more then 10 minutes, the pump is blocked (by a relay) and must be reset to operate again (reset button or via the supervisory and control standard Visual Studio application). What a pleasure working with the standard Visual Studio application! Communication ready to roll, masking, forcing and trending of the signals is ideal and gives a lot of efficiency during building and debugging the application.

Click here for the UserConfiguration and here for the UserFiniteStateMachine. Remark: UserConfiguration and UserFiniteStateMachine are the user parts of the Standard Finite State machine.

Remark: You can download the Windows 10 and Raspberry version of the debugger from my website. The raspberry version of the debugger lacks the graphical plotter. Read all about the debuggers on the website!

Below a picture of the standard windows debug application for this application. Data (of the bold process values) is logged to an access database.

Plotter example: the spikes in the graph represent an active submersible pump. The orange pen is a DS18B20 temperature sensor, also connected to the board.

The result (what counts...), no water....

Code

Standard sketchArduino
Basic finite state machine sketch
// V38 Standaard Arduino application by J.B. Siemonsma
// Copyright  Wytgaard 2015
//
//   MM          MM   EEEEEEEEEE     GGGGGGGG        AAAAAA
//   MMMM      MMMM   EE           GGG      GG      AA    AA
//   MM  MM  MM  MM   EE          GG               AA      AA
//   MM    MM    MM   EEEEEE      GG      GGGGGG  AA        AA
//   MM          MM   EE          GG          GG  AAAAAAAAAAAA
//   MM          MM   EE           GGG      GGG   AA        AA
//   MM          MM   EEEEEEEEEE     GGGGGGGG     AA        AA
//
// Supporting:
// - hardware Arduino MEGA 2560
// - initialization of digital inputs (pullup used, so switch to gnd)
// - initialization of digital ouputs
// - initialization of analog inputs
// - initialization of analog outputs (PWN, puls width modulation)
// - initialization of servos
// - Visual control of a working/checked configuration (Flashing LED PIN 13)
// - Help functions in the serial monitor (use "h" for help)
// - cyclic reading of defined digital inputs, with current status, timestamp, change notification; e.g. usefull for phase transitions;
//   if the status of an input changes in cycle n, it is notified by field "changed", cycle n+1, the field "changed" is reset again.
// - cyclic reading of defined analog inputs
// - calculated cycle time, can be visualised in the help function
// - asynchronous timer function, waiting without using "delay" function. Made up by pointerstructure.
// - function for setting digital outputs
// - function for setting analog outputs
// - example of interrupt handling (see also , loop and the Isr function)
// - available functions to determine and check bits by there tagname
// - available functions to determine and check analogue values by there tagname
// - available function to send messages by I2C (e.g. to the SMS Alarm sketch or the LCD display sketch).
// - serial communication for HMI functionality, see also the documentation
// - RTC, Real time clock for setting the system time
// - One wire channel for reading e.g. temperature(s)
//
// Upcoming features
// - PID regulator
//
// Available USER functions
// - digitalClockDisplay(), present the current time
// - InputOverview(), present an overview of all defined digital input pins
// - OutputOverview(), present an overview of all defined digital output pins
// - AnaInOverview(), present an overview of all defined analog input pins (digital value and calculated voltage, 0-5 Volts)
// - AnaOutOverview(), present an overview of all defined analog output pins (digital value and calculated voltage, 0-5 Volts)
// - boolean Timer (unsigned long Time, int TimerNo ), Start/check a timer, when the timer not exists (# not known) it will be created,
//   when expired it will return true and the timer -pointer entries- will be deleted. The timer works asynchronous, not delaying the loop.
// - boolean CancelTimer (int TimerNo ), When a timer is not needed anymore, use this function to cancel the timer to prevent not wanted actions
// - Isr (), Example of an Interrupt Service Routine. The Isr call is defined in the setup part. In this particular case it is called by the rising edge of pin 20,
//   Beware: Variables used in the Isr must be declared volatile. In de main loop they must be copied to local varables for processing/printing while interrups disabled. See the example in the loop part.
// - boolean DigRising ( String Tag ), Determine by tagname the bit-rising event
// - boolean DigFalling ( String Tag ), Determine by tagname the bit-falling event
// - boolean DigLow ( String Tag ), Determine by tagname the bit low status
// - boolean DigHigh ( String Tag ), Determine by tagname the bit high status
// - boolean Activate (String Tag ), Set the digital output by tagname, there is no deactivate function, this is always done in de main loop (Fonterra strategie)
// - int AnaValue ( String Tag ), returns the analogue value of tagname
// - int Distance ( String Tag ), returns the distance in cm of tagname
// - boolean AnaBetween (String Tag, int LowVal, IntHighVal), determine by tagname
// - boolean AnaLower (String Tag, int LowVal), determine by tagname
// - boolean AnaHigher (String Tag, Int HighVal), determine by tagname
// - boolean AnaGoUnder (String Tag, int LowVal), determine by tagname
// - boolean AnaGoOver (String Tag, Int HighVal), determine by tagname
// - boolean DistanceBetween ( String Tag, int LowVal, int HighVal ), determine by tagname
// - boolean DistanceShorter ( String Tag, int LowVal ), determine by tagname
// - boolean DistanceLonger ( String Tag, int HighVal ), determine by tagname
// - boolean UpdateAna (String Tag, Value ), set the analog output by tagname
// - boolean UpdateServo (String Tag, Angle), control servos by tagname
// - MessageI2C(String Message), send a message to slave 9 by I2C. Use the SlaveCommunicator sketch
//   to read the message and present it -for example- on a LCD display.
// - Support for HMI. Cummunication by message from HMI. See the discription.
//
// 
//
// All included libraries and constants, do not remove or change
//
//#include <PID_v1.h>
#include "UserConfiguration.h"
#include <TimeLib.h>
#include <RTClib.h>
#include <NewPing.h>
//#include <Time.h>
#include <Servo.h>
#include <Wire.h>
#include <MemoryFree.h>
#include <OneWire.h>
#include <DallasTemperature.h>
//
#define TIME_MSG_LEN       11  // time sync to PC is HEADER followed by unix time_t as ten ascii digits
#define TIME_HEADER        'T' // Header tag for serial time sync message
#define TIME_REQUEST       7   // ASCII bell character requests a time sync message 

#define StartDigitalPins   22  //Start of the pin layout for the Digital pins
#define EndDigitalPins     53  //End of the pin layout for the digital pins
boolean LayoutDigital[EndDigitalPins - StartDigitalPins + 1]; //Check array digital pins

#define StartPWMPins        2  //Start of the pin layout for the PWM pins
#define EndPWMPins          13 //End of the pin layout for the PWM Pins
boolean LayoutPWM[EndPWMPins - StartPWMPins]; // Check array PWM pins

#define StartAnalogInPins   0  //Start of the pin layout for the analogue in pins
#define EndAnalogInPins    15  //End of the pin layout for the analogue in pins
boolean LayoutAnalogInPins[EndAnalogInPins - StartAnalogInPins]; // check array analogue in Pins

//
// Predefined timer id's, do not remove!
//
const byte TimOneHour    = 200;
const byte TimISR        = 201;
const byte TimUS         = 202;
const byte TimTT         = 203;
const byte TimDelayGMI   = 204;
const byte TimHMIWtd     = 205;
const byte TimBlinking   = 206;
const byte TimBlinkWtdOn = 207;
const byte TimBlinkWtdOff= 208;
const byte TimGpsPoll    = 209;
//
// Temperature sensors. Do not remove.
//
byte OneWireBus = OneWireChannel;
OneWire oneWire(OneWireBus);
DallasTemperature sensors(&oneWire);
//
// Real time, type clock DS1307
RTC_DS1307 RealTimeClock;
////
//
// Interrupt Pin numbers (do not remove/change)
//int Pin2  = 0;
//int Pin3  = 1;
//int Pin21 = 2; SCL
//int Pin20 = 3; SCA
//int Pin19 = 4;
//int Pin18 = 5;
//
//reserved variables, do not remove/change
//
int                    Pin13    = 0;
int                    Blinking = 500;
byte                   incomingByte;
boolean                CConfigurationOK;
boolean                OnOff;
time_t                 tijd;
boolean                RTCAvailable;
unsigned long          CycleStart;
unsigned long          NoOfCycle;
boolean                CycleCalc;
boolean                CycleCalcHMI;
unsigned int           PWN_value;
String                 OldMessageI2C;
char                   WatchdogI2C = 5;
volatile unsigned long ISR_Count;
unsigned long          ISR_Count_Copy;
int                    Threads = 1;
int                    CurrentThread = 0;
int                    NoFSMStates = 0;
int                    MachineState = 0;
char                   Karakter = -1;
String                 KarakterString = "";
String                 GPSString = "";
int                    KarakterIndex = 0;
int                    Getal;
int                    NoHandTags = 0;
int                    NoMaskTags = 0;
String                 ValidationId;
String                 DummyCommands [] = {"DUMMY"};
String                 HMICommands [] = { "SDI", "SAI", "FDO", "FAO", "RTA", "PDI", "PAI", "CAN", "RDC", "GMI", 
//                                           0      1      2      3      4      5      6      7      8      9
                                          "MDI", "MAI", "RMM", "FSM", "FSO", "WTD", "PMT", "FMT", "MTC", "THM",
//                                        	10     11     12     13     14     15     16     17     18     19
                                          "CLT", "RAM", "TIM", "JM0", "RDB", "CDB" };
//                                          20     21     22     23     24     25
	
int                    NoHMICommands = 25+1;
String                 GPSCommands[] = { "PUBX"};
//                                           0 
int                    NoGPSCommands = 0 + 1;
char                   LF = 10;
char                   CR = 13;
boolean                DelayGMI = false;
boolean                HMIWatchDog;
unsigned long          HMIWatchDogTime;


struct FSMStateType
{
  String         FSMStateName;
  int            FSMStateNo;
  String         ActualState;
  boolean        Enter;
  boolean        Exit;
  int            ThreadNo;
  unsigned long  StartTime;
  unsigned long  TotalTime;
  struct         FSMStateType* Next;
};
FSMStateType FSMState;
FSMStateType* FSMStateKetting;
FSMStateType* FSMStateSchakel;

struct TimerType
{
  int           TimerId;
  unsigned long Initial;
  unsigned long Wait;
  boolean       State;
  struct        TimerType* Next;
  struct        TimerType* Previous;
};
TimerType TimerSchakel;
TimerType* TimerKetting;
TimerType* HuidigeSchakel;
TimerType* NieuweSchakel;
TimerType* VorigeSchakel;
int AantalSchakels;

struct MessageType
{
  String        Message;
  unsigned long SendTime;
  struct        MessageType* Next;
  struct        MessageType* Previous;
};
MessageType MessageSchakel;
MessageType* MessageKetting;
MessageType* HuidigeMessage;
MessageType* NieuweMessage;
MessageType* VorigeMessage;
MessageType* VolgendeMessage;

typedef struct DigInPinType
{
  int           Pin;
  boolean       Status;
  unsigned long TimeChange;
  boolean       Changed;
  String        Tagname;
  boolean       Report;
  boolean       Poll;
  unsigned long PollFreq;
  unsigned long PollTime;
  boolean       Mask;
  boolean       MaskValue;
  boolean       DBLogging;
};
DigInPinType InPin[NoInputPins + 1];

typedef struct DigOutPinType
{
  int           Pin;
  boolean       Status;
  boolean       PreStatus;
  unsigned long TimeChange;
  boolean       FixedStatus;
  boolean       Changed;
  String        Tagname;
  boolean       Report;
  boolean       Poll;
  unsigned long PollFreq;
  unsigned long PollTime;
  boolean       Auto;
  boolean       DBLogging;
} ;
DigOutPinType OutPin[NoOutputPins + 1];

typedef struct TempSensorType
{
  int           Channel;
  int           DeviceNr;
  float         Value;
  float         PrevValue;
  unsigned long TimeChange;
  boolean       Changed;
  String        Tagname;
  boolean       Poll;
  unsigned long PollFreq;
  unsigned long PollTime;
  boolean       Mask;
  boolean       DBLogging;
} ;
TempSensorType TempSensor[NoTemp + 1];

typedef struct AnaInPinType
{
  int           Pin;
  unsigned int  Value;
  unsigned int  PrevValue;
  unsigned long TimeChange;
  boolean       Changed;
  String        Tagname;
  boolean       Poll;
  unsigned long PollFreq;
  unsigned long PollTime;
  boolean       Mask;
  boolean       DBlogging;
} ;
AnaInPinType AnaInPin[NoAnaInPins + 1];

typedef struct AnaOutPinType
{
  int Pin;
  unsigned int  Value;
  unsigned int  PrevValue;
  unsigned long TimeChange;
  unsigned int  FixedValue;
  boolean       Changed;
  String        Tagname;
  boolean       Poll;
  unsigned long PollFreq;
  unsigned long PollTime;
  boolean       Auto;
  boolean       DBLogging;
} ;
AnaOutPinType AnaOutPin[NoAnaOutPins + 1];

typedef struct MyServoType
{
  int           Pin;
  int           Angle;
  Servo         MyServo;
  unsigned long TimeChange;
  int           FixedAngle;
  boolean       Changed;
  String        Tagname;
  boolean       Poll;
  unsigned long PollFreq;
  unsigned long PollTime;
  boolean       Auto;
  boolean       DBlogging;
} ;
MyServoType ServoPin[NoServos + 1];

typedef struct MyUltraType
{
  int           Trigger;
  int           Echo;
  int           MaxDistance;
  int           Distance;
  String        Tagname;
  boolean       Poll;
  unsigned long PollFreq;
  unsigned long PollTime;
  boolean       Mask;
  boolean       DBLogging;
} ;
MyUltraType Ultrasonic[NoUltrasonic + 1];

typedef struct MarkerType
{
  boolean       Status;
  boolean       PreStatus;
  boolean       FixedStatus;
  int           Value;
  int           FixedValue;
  int           PrevValue;
  String        TextString;
  String        PreTextString;
  unsigned long TimeChange;
  boolean       Changed;
  boolean       ChangedAnalogue;
  boolean       ChangedText;
  String        Tagname;
  boolean       Report;
  boolean       Poll;
  boolean       Auto;
  unsigned long PollFreq;
  unsigned long PollTime;
  unsigned long PollTimeAnalogue;
  unsigned long PollTimeText;
  boolean       DBLogging;
} ;
MarkerType Marker[NoMarkers + 1];

struct GPSType
{
  //NEO-6M Type with use of $PUBX commands
  String        UTC;           //2
  String        Latitude;      //3
  String        NSIndicator;   //4
  String        Longitude;     //5
  String        EWIndicator;   //6
  String        NavStat;       //8
  String        Hacc;          //9
  String        SOG;           //11
  String        COG;           //12
  String        GU;            //18 
};
GPSType GPSdata;

//*******************************************************************************
//
//  Name: HMISendString 
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Send a message to HMI (via hardware communication channel 1)
//
//
//*******************************************************************************
void HMISendString ( String MessageToHMI )
{

  char LF = 10;
  char CR = 13;
  char Buffer[1000];
  int LenMessage;

  LenMessage = MessageToHMI.length();
  MessageToHMI.toCharArray(Buffer, 500);
  //Serial.println ( "@HMISendString (length:" + String(LenMessage) + "): >" + MessageToHMI + "<" );
  for (int index = 0; index < LenMessage; index++) Serial1.write(Buffer[index]);
  Serial1.write(CR);
  Serial1.write(LF);

}

//*******************************************************************************
//
//  Name: digitalClockDisplay
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Print the current time on the serial monitor.
//
//
//*******************************************************************************
void digitalClockDisplay()
{
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
}

//*******************************************************************************
//  Name: LeadingZero
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Return a string of 4 characters, with leading zero's. 
//  Input an unsigned integer
//
//
//*******************************************************************************
String LeadingZero ( unsigned int Value )
{

  String Leading;
  String StrValue;

  StrValue = String ( Value );
  if (Value < 10 ) Leading = "0";
  if (Value < 100 ) Leading = Leading + "0";
  if (Value < 1000 ) Leading = Leading + "0";
  StrValue = Leading + StrValue;
  return StrValue;

}

//*******************************************************************************
//
//  Name: PrintDigits
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Print semicolon and a leading zero on serial monitor, input is an integer
//
//
//*******************************************************************************
void printDigits(int digits)
{
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

//*******************************************************************************
//
//  Name: processSyncMessage 
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  When there is a unix time stamp from the serial monitor available,
//  set the time on the board. When a RTC available, set also the time on the
//  real time clock.
//
//
//*******************************************************************************
void processSyncMessage() {
  // if time sync available from serial port, update time and return true
  while (Serial.available() >=  TIME_MSG_LEN ) { // time message consists of header & 10 ASCII digits
    char c = Serial.read() ;
    //Serial.print(c);
    if ( c == TIME_HEADER ) {
      time_t pctime = 0;
      for (int i = 0; i < TIME_MSG_LEN - 1; i++) {
        c = Serial.read();
        if ( c >= '0' && c <= '9') {
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number
        }
      }
      setTime(pctime);   
      if (RTCAvailable) 
        { 
		RealTimeClock.adjust(pctime);
		//RTC.set(pctime);
        //Serial.println ( F("%INF-RTC-CTC, RTC is also updated"));
        }
    }
  }
}

//*******************************************************************************
//
//  Name: InitFSMStates 
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Set the pointer structure for the FSM states. See also the documentation
//  for information about FMS states. Set the default states for the FSM's on
//  HIB (hibernate).
//
//
//*******************************************************************************
void InitFSMStates ()
{

  int StateNo = 0;
  FSMStateType* FSMStateLaatste;

  Serial.println ( F("Setup FSM States"));
  NoFSMStates = sizeof(PossibleFSMStates) / 6;

  while (StateNo < NoFSMStates)
  {
    // Build the state list..
    if ( FSMStateKetting == 0 )
    {
      // Chain doesn't exists yet
      FSMStateKetting = new FSMStateType;
      FSMStateKetting->FSMStateName = PossibleFSMStates [StateNo];
      FSMStateKetting->FSMStateNo = StateNo + 1;
      FSMStateKetting->ActualState = "HIB";
      FSMStateKetting->ThreadNo = 0;
      FSMStateKetting->StartTime = 0;
      FSMStateKetting->TotalTime = 0;
      FSMStateKetting->Next = 0;
      FSMStateLaatste = FSMStateKetting;
    }
    else
    {
      FSMStateSchakel = new FSMStateType;
      FSMStateSchakel->FSMStateName = PossibleFSMStates [StateNo];
      FSMStateSchakel->FSMStateNo = StateNo + 1;
      FSMStateSchakel->ActualState = "HIB";
      FSMStateSchakel->ThreadNo = 0;
      FSMStateSchakel->StartTime = 0;
      FSMStateSchakel->TotalTime = 0;
      FSMStateSchakel->Next = 0;
      FSMStateLaatste->Next = FSMStateSchakel;
      FSMStateLaatste = FSMStateSchakel;
    }
    ++StateNo;
  }

  //Going trough the chain....
  FSMStateSchakel = FSMStateKetting;
  do
  {
    Serial.println ( "State " + FSMStateSchakel->FSMStateName + " (PID=" + String (FSMStateSchakel->FSMStateNo ) + "), current state = " + FSMStateSchakel->ActualState );
    FSMStateSchakel = FSMStateSchakel->Next;
  } while ( FSMStateSchakel != 0);

  Serial.println ( F("Setup FSM States ready"));
  Serial.println ( F("-------------------------------------"));
  Serial.println ( F(""));

} // End of InitFSMStates

//*******************************************************************************
//
//  Name: FSMStateOverview
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Present an overview of the FSM states on the serial monitor.
//
//
//*******************************************************************************
void FSMStateOverview()
{

  String RunTime;

  Serial.println();
  Serial.print(F("FSM state overview at "));
  digitalClockDisplay();
  Serial.println(F("----------------------------------------- "));
  FSMStateSchakel = FSMStateKetting;
  do
  {
    if (FSMStateSchakel->StartTime == 0 )
    {
      RunTime = String ( FSMStateSchakel->TotalTime / 1000 );
    }
    else
    {
      RunTime = String ( ( FSMStateSchakel->TotalTime + millis() - FSMStateSchakel->StartTime ) / 1000 ) + "+";
    };
    Serial.println ( "State " + FSMStateSchakel->FSMStateName + " (PID=" + String (FSMStateSchakel->FSMStateNo ) + "), current state = " + FSMStateSchakel->ActualState + " in thread no. " + String (FSMStateSchakel->ThreadNo) + " Runtime: " + RunTime );
    FSMStateSchakel = FSMStateSchakel->Next;
  } while ( FSMStateSchakel != 0);
  Serial.println(" ");

} // End of FSMStateOverview;

//*******************************************************************************
//
//  Name: TransitionToState 
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  - Find the current state in the active thread and put it on hibernate (HIB).
//  - Examine all existing threads and find the highest thread.
//  - Check the existens of the new statename.
//  - Put the new statename on state pending (PEN). 
//  - If the name of the state is END do nothing.
//
//  Explanation of handling FSM states (most be documented somewhere...)
//
//  - Initial all states have the status HIB (hibernate)
//  - By calling TransitionToState, the state's status is changed to PEN (pending)
//    so the state is a candidate for executing the code in the main loop.
//  - At the end of the PLC cycle, all PEN (pending) states are put in RUN (running) state.
//    Also all states still on COM (computing) are put on RUN (running) again.
//  - In the new PLC cylce, all RUN (running) states are executed once and the status is
//    put on COM (computing). When TransitionToState is called in the active state,
//    the active state is put in HIB (hibernate).
//  
//  - It is possible to have more FSM's executed in parallel. Just call 
//    TransitionToState more than once from the active state. They are then devided
//    over more parallel Threads.
//  - It is possible to Kill a thread when calling the END state in TransistionToState.
//    Be aware that killing the only living thread will kill the FSM.  
//
//*******************************************************************************
void TransitionToState ( String StateName )
{
  FSMStateType* Chain;
  boolean FSMStateNameFound = false;
  int CurrentThread;
  int HighThreadNo;
  unsigned long EndTime;

  // find the current state and put it on HIB
  Chain = FSMStateKetting;
  do
  {
    if (Chain->FSMStateNo == MachineState)
    {
      // Current state is found... put it on HIB
      Chain->ActualState = "HIB";
	  Chain->Exit = true;
      Chain->ThreadNo = 0;
      if (Chain->StartTime != 0)
      {
        Chain->TotalTime = Chain->TotalTime + millis() - Chain->StartTime;
        Chain->StartTime = 0;
      }
    if (UseHMISerial) HMISendString("@FSM," + String(Chain->FSMStateName) + ",HIB");
    }
    Chain = Chain->Next;
  } while ( Chain != 0);

  // What can happen now?
  // Look for States on RUN and find highest ThreadNo
  Chain = FSMStateKetting;
  HighThreadNo = 0;
  do
  {
    //State with status on RUN?
    if ((Chain->ActualState == "RUN" ) || (Chain->ActualState == "PEN" ))
    {
      Threads = Threads + 1;
      if (Chain->ThreadNo > HighThreadNo ) HighThreadNo = Chain->ThreadNo;
    };
    Chain = Chain->Next;
  } while ( Chain != 0);
  HighThreadNo = HighThreadNo + 1;

  // To be sure, check if StateName is known...
  Chain = FSMStateKetting;
  do
  {
    //State with status on PEN and ThreadNo is set, except for END (=do Nothing)
    if (Chain->FSMStateName == StateName ) FSMStateNameFound = true;
    Chain = Chain->Next;
  } while ( Chain != 0);
  if (!FSMStateNameFound) Blinking = 1000;

  // ThreadNo is determined and now we can set the new state on PEN
  Chain = FSMStateKetting;
  do
  {
    //State with status on PEN and ThreadNo is set, except for END (=do Nothing)
    if ((Chain->FSMStateName == StateName ) && ( StateName != "END") && (Chain->ActualState == "HIB"))
    {
      Chain->ThreadNo = HighThreadNo;
      Chain->ActualState = "PEN";
	  //extra check; when exit event is set, this must be done in this transition; so it's a call to this transistion to remain running. 
	  //do nothing with the enter or exit event!
	  if (!Chain->Exit) Chain->Enter = true;
      Chain->StartTime = millis();
      if (UseHMISerial) HMISendString ( "@FSM," + String(StateName) + ",RUN" );
    };
    Chain = Chain->Next;
  } while ( Chain != 0);

} // End of TransitionToState

//*******************************************************************************
//
//  Name: CurrentState
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Return the PID number of the running (RUN) state and change the actual state 
//  to computing (COM)
//
//
//*******************************************************************************
int CurrentState()
{
  FSMStateType* Chain;
  int Action;
  boolean Stop;

  Action = 0;
  Chain = FSMStateKetting;
  do
  {
    if ((Chain->ActualState == "RUN"))
    {
      Chain->ActualState = "COM";
      Action = Chain->FSMStateNo;
      break;
    };
    Chain = Chain->Next;
  } while (( Chain != 0)) ;

  return Action;

} // End of CurrentState

//*******************************************************************************
//
//  Name: ComToRunState 
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Change a computing (COM) or pending (PEN) state to running state(RUN).
//
//
//*******************************************************************************
void ComToRunState()
{
  FSMStateType* Chain;
  int Action;

  Action = 0;
  Chain = FSMStateKetting;
  do
  {
    if ((Chain->ActualState == "COM") || (Chain->ActualState == "PEN"))
    {
      Chain->ActualState = "RUN";
    };
    Chain = Chain->Next;
  } while ( Chain != 0) ;

} // End of ComToRunState


//*******************************************************************************
//
//  Name: FiniteState
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Return the PID number of the CurrentState
//
//
//*******************************************************************************
int FiniteState ( String CurrentState )
{
  FSMStateType* Chain;
  int Action;

  Action = 0;
  Chain = FSMStateKetting;
  do
  {
    if (Chain->FSMStateName == CurrentState)
    {
      Action = Chain->FSMStateNo;
    };
    Chain = Chain->Next;
  } while ( Chain != 0) ;

  return Action;

}

//*******************************************************************************
//
//  Name: EnterState
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Return true when entering the state the first time and reset the enter event
//
//
//*******************************************************************************
boolean EnterState()
{
	FSMStateType* Chain;
	boolean Action;

	Action = false;
	Chain = FSMStateKetting;
	do
	{
		if (Chain->FSMStateNo == MachineState)
		{
			Action = Chain->Enter;
			Chain->Enter = false;
		}
		Chain = Chain->Next;
	} while (Chain != 0);

	return Action;

} //EnterState


  //*******************************************************************************
  //
  //  Name: ExitState
  //
  //  Modification date: 
  //  Changed by: 
  //
  //  Function:
  //  Return true when leaving the state and reset the exit event
  //
  //
  //*******************************************************************************
boolean ExitState()
{
	FSMStateType* Chain;
	boolean Action;

	Action = false;
	Chain = FSMStateKetting;
	do
	{
		if (Chain->FSMStateNo == MachineState)
		{
			Action = Chain->Exit;
			Chain->Exit = false;
		}
		Chain = Chain->Next;
	} while (Chain != 0);

	return Action;

} //ExitState


//*******************************************************************************
//
//  Name: InitInputs 
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Initialize all digital input tags. Default INPUT_PULLUP. So not connected
//  the pins are high.
//
//
//*******************************************************************************
void InitInputs ()
{
  Serial.println ( F("Setup inputs"));
  Serial.println ( String("No. of inputs ") + String(NoInputPins));
  for (int InputIndex = 1; InputIndex <= (NoInputPins); InputIndex++)
  {
    InPin[InputIndex].Pin = FirstInput + InputIndex - 1;
    pinMode(InPin[InputIndex].Pin, INPUT_PULLUP );
    InPin[InputIndex].Status = digitalRead (InPin[InputIndex].Pin);
    InPin[InputIndex].Changed = 0;
    InPin[InputIndex].Poll = false;
    InPin[InputIndex].Mask = false;
    InPin[InputIndex].TimeChange = millis();
    InPin[InputIndex].Tagname = InputTags[InputIndex - 1];
	InPin[InputIndex].DBLogging = false;
	Serial.println ( "Init " + String (InPin[InputIndex].Tagname) + " #" + String ( InPin[InputIndex].Pin ) );
  }
  Serial.println ( F("Setup inputs ready"));
  Serial.println ( F("-------------------------------------"));
  Serial.println ();

} // End of InitInputs

//*******************************************************************************
//
//  Name: InitMarkers
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Initialize all markers tags.
//
//
//*******************************************************************************
void InitMarkers ()
{
  Serial.println ( F("Setup markers"));
  Serial.println ( String("No. of Markers ") + String(NoMarkers));
  for (int Index = 1; Index <= (NoMarkers); Index++)
  {
    Marker[Index].Status = 0;
	Marker[Index].PreStatus = 0;
	Marker[Index].FixedStatus = 0;
	Marker[Index].TextString = "";
    Marker[Index].Changed = 0;
	Marker[Index].ChangedAnalogue = 0;
	Marker[Index].ChangedText = 0;
	Marker[Index].Auto = true;
    Marker[Index].Poll = false;
    Marker[Index].Report = false;
    Marker[Index].TimeChange = millis();
    Marker[Index].Tagname = MarkerTags[Index - 1];
	Marker[Index].DBLogging = false;
    Serial.println ( "Init " + String (Marker[Index].Tagname) );
  }
  Serial.println ( F("Setup markers ready"));
  Serial.println ( F("-------------------------------------"));
  Serial.println ( F(""));

} // End of InitMarkers


//*******************************************************************************
//
//  Name: InitUltrasonics
//
//  Modification date: 
//  Changed by: 
//
//  Function:
//  Initialize all ultrasonic devices. Each ultrasonic devices uses 2 digital
//  pins. The first is the trigger pin, the next the echo pin.
//
//
//*******************************************************************************
...

This file has been truncated, please download it to see its full contents.
UserConfiguration.hC Header File
The configuration of I/O, possible FSM states and user variables
// UserConfiguation.h
//
//
//*========================================================================================
//*========================================================================================
//*============  START USER CONFIGURATION FOR THE I/O   ===================================
//*========================================================================================
//*========================================================================================
//
// Define all used pins for the application.
//
// The range of digitals is commenly used. First to define the inputs,outputs and ultrasonics.
// The analog out en servo pins are in the PWN range of the board.
//
const byte NoInputPins    =  1;
const byte FirstInput     = 30;
const byte NoOutputPins   =  5;
const byte FirstOutput    = 40;
const byte NoUltrasonic   =  0;
const byte FirstUltra     =  0;
const byte NoAnaInPins    =  1;
const byte FirstAnaIn     =  0;
const byte NoAnaOutPins   =  1;
const byte FirstAnaOut    =  4;
const byte NoServos       =  0;
const byte FirstServo     =  0;
const byte NoTemp         =  1;
const byte OneWireChannel =  2;
const byte NoMarkers      =  2;
//
// IMPORTANT!
//
// No. of tagnames have to be equal with No. of pins! This is the addressing part between the pins and the tags!
// Tagnames are connected with the in/outputs by their order. When done correctly it is possible to programm all
// software by tags and phases in an easy way. Don't remove Tag lines (if not used keep them empty)!
//
String DummyTags[]   = { "" };
String InputTags[]   = { "RESET" };
String OutputTags[]  = { "POMPEN", "GEPOMPT", "TIMEOUT", "BLOCK", "STORING" };
String AnaInTags[]   = { "INMELDING" };
String AnaOutTags[]  = { "METING" };
String ServoTags[]   = { "" };
String UltraTags[]   = { "" };
String MarkerTags[]  = { "AANTAL", "MELDINGTXT" };
String TempTags[]    = { "BUITEN" };
//
// Declaration of all used states in the FSM.
// The first and last state "START"and "END" are obligatory and may not be removed, the rest is up to you
//
String PossibleFSMStates[] = { "START",
                               "AMETING",
	                           "IDLE",
	                           "POMPEN",
	                           "NAAR-VERLENGT",
	                           "VERLENGT",
	                           "STORING",
                               "END" };
//
// Start defining User timers don't use predefined timers (stay out of range 200-300)
//
const int timMeting = 1;
const int timMaxDuur = 2;
const int timVerlengt = 3;
const int timNaarVerlengen = 4;
const int timVerlengen = 5;

//
// End defining user tinmers
//
// START USER SPECIFIC DECLARATIONS
//
// Start User varaibles:
//

int AMax = 0;
int AMin = 0;
int Meting = 0;
int NoMetingen = 0;

// End User varaibles:
//
// use of the extra MEGA board for the messages, use MessageI2C and/or UseHMISerial, do not remove, only set true or false
//
boolean UseI2C = false;
boolean UseHMISerial = true;
boolean UseGPS = false;
//
//*========================================================================================
//*========================================================================================
//*==============  END USER CONFIGURATION =================================================
//*========================================================================================
//*========================================================================================
UserFiniteStateMachine.hC Header File
The finite state machine code
// UserFiniteStateMachine.h

//*========================================================================================
//*  Application code
//*
//*  Name: UserFSM()
//*  Version:
//*  Date:
//*  Author:
//*
//*  Short description:
//*
//*
//*========================================================================================
//*========================================================================================
//*============ START USER APPLICATION ======= START USER APPLICATION =====================
//*========================================================================================
//*========================================================================================
//
// State machine
//
// Each phase exists of 2 parts, e.g. the phase actions and transition conditions.
// In the phase actions, all activated tags are mentioned. Deactivation is not needed, deactivation is
// done at the end of the main loop. In the conditions section, all conditions for the transitions to another
// phase are mentioned. When all condition are true, the new phase can be set. It is possible to programm
// more then 1 transition. In such a case it is necassery to keep the priority in mind.
//
//*========================================================================================


void UserFSM()
{


	//
	// Machine state "START" is the default state at startup of the board!
	//
	if (MachineState == FiniteState("START"))
	{
		//PHASE ACTIONS
		TransitionToState("AMETING");
		TransitionToState("IDLE");
		//TRANSITION CONDITIONS//
	};


	if (MachineState == FiniteState("AMETING"))
	{
		//PHASE ACTIONS
		if (Timer(500, timMeting))
		{
			Meting = AMax - AMin;
			if (Meting > 255) Meting = 255;
			UpdateAna("METING", Meting);
			AMax = 0;
			AMin = 1024;
		}
		else
		{
			if (AnaValue("INMELDING") > AMax) AMax = AnaValue("INMELDING");
			if (AnaValue("INMELDING") < AMin) AMin = AnaValue("INMELDING");
		}
	};


	if (MachineState == FiniteState("IDLE"))
	{
		//PHASE ACTIONS
		UpdateText("MELDINGTXT", "-");
		CancelTimer(timMaxDuur);
		CancelTimer(timNaarVerlengen);
		//TRANSITION CONDITIONS//
		if (Meting > 10)
		{
			TransitionToState("POMPEN");
			TransitionToState("NAAR-VERLENGT");
		}
	};


	if (MachineState == FiniteState("POMPEN"))
	{
		//PHASE ACTIONS
		Activate("POMPEN");
		if (Timer(600000, timMaxDuur)) TransitionToState("STORING");
		if (Meting <= 10) TransitionToState("IDLE");
	};



	if (MachineState == FiniteState("NAAR-VERLENGT"))
	{
		if (Meting <= 10)
		{
			CancelTimer(timNaarVerlengen);
			TransitionToState("END");
		}
		if (Timer(5000, timNaarVerlengen))
		{
			TransitionToState("VERLENGT");
			NoMetingen = NoMetingen + 1;
			UpdateText("AANTAL", "No: " + String(NoMetingen));
		}
	};



	if (MachineState == FiniteState("VERLENGT"))
	{
		//PHASE ACTIONS
		Activate("GEPOMPT");
		if (Timer(600000, timVerlengen)) TransitionToState("END");
		//TRANSITION CONDITIONS//
	};


	if (MachineState == FiniteState("STORING"))
	{
		//PHASE ACTIONS
		Activate("STORING");
		Activate("BLOCK");
		UpdateText("MELDINGTXT", "Pomp time-out");
		if (DigRising("RESET")) TransitionToState("IDLE");
	};

	//*========================================================================================
	//*========================================================================================
	//*============  END USER APPLICATION ========= END USER APPLICATION =====================
	//*========================================================================================
	//*========================================================================================
}

Schematics

Schematic
Schematic of submersible pump application
Submersiblepump o0zsaudxap

Comments

Similar projects you might like

Notification Call System

Project tutorial by Jelle

  • 2,363 views
  • 0 comments
  • 10 respects

Automated Dehumidifier Sump Pump

Project tutorial by Zachary J. Fields

  • 4,706 views
  • 10 comments
  • 17 respects

Arduino Sump Pump

Project tutorial by Team RobotGeek Projects Team

  • 2,246 views
  • 0 comments
  • 2 respects

Smart Plant IoT

Project tutorial by Shin Ae Hong and Peter Ma

  • 8,123 views
  • 3 comments
  • 30 respects

Sump Pump Monitor and Alarm

Project showcase by JoSain

  • 1,247 views
  • 1 comment
  • 7 respects
Add projectSign up / Login