Jelle
Published © MIT

The Lemonade Cocktail Bar

Our 3-day village festival's theme is "Ibiza," so a cocktail bartender seems applicable!

IntermediateShowcase (no instructions)Over 1 day2,957
The Lemonade Cocktail Bar

Things used in this project

Story

Read more

Code

UserConfiguration

C/C++
Definition of I/O and machine states
// 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    =  5;
const byte FirstInput     = 22;
const byte NoOutputPins   = 10;
const byte FirstOutput    = 30;
const byte NoUltrasonic   =  1;
const byte FirstUltra     = 46;
const byte NoAnaInPins    =  0;
const byte FirstAnaIn     =  0;
const byte NoAnaOutPins   =  0;
const byte FirstAnaOut    =  0;
const byte NoServos       =  0;
const byte FirstServo     =  0;
const byte NoTemp         =  0;
const byte OneWireChannel =  0;
const byte NoMarkers      =  1;
//
// 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[]   = { "F1","F2","F3","START","ZERO"  };
String OutputTags[]  = { "&D1","&D2","&D3","&DW","ACKD1","ACKD2","ACKD3","ACKSTART","READY","&CIP"  };
String AnaInTags[]   = { "" };
String AnaOutTags[]  = { "" };
String ServoTags[]   = { "" };
String UltraTags[]   = { "PRESENT" };
String MarkerTags[]  = { "MUZIEK" };
String TempTags[]    = { "" };
//
// 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",
                               "INIT","CHOICE","GOTO-LIQUIR", "LIQUIR","GOTO-WATER", "WATER", "READY", 
	                           "MIX","NEWCUP","CUPPRESSENT","NORMAL", "SERVICE", "CIP","CIPCYCLE",
                               "END" };
//
// Start defining User timers don't use predefined timers (stay out of range 200-300)
//
const byte timAanUit = 1;
const byte timWait = 2;
const byte timLimo = 3;
const byte timWater = 4;
const byte timTask4 = 5;
const byte timStartup = 6;
const byte timGameOver = 7;
const byte timService = 8;
const byte timServiceMode = 9;
const byte timWaitCIP = 10;
const byte timCIP = 11;
//
// End defining user tinmers
//
// START USER SPECIFIC DECLARATIONS
//
// Start User varaibles:
//
boolean AanUit;
int LiquirNo;
int PositionLiquir;
int TimeLiquir;
boolean NewCup;
boolean CupPressent;
boolean GoMix;
boolean PrepareDrink = false;
boolean Service = false;
int TimeNoCupPressent;
int PresentCount;
int ServiceLED = 1;
//
// 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;
boolean UseIntercard = false;
boolean PollUltra = false;
//
//*========================================================================================
//*========================================================================================
//*==============  END USER CONFIGURATION =================================================
//*========================================================================================
//*========================================================================================

UserFiniteStateMachine

C/C++
User part of the finite state machine
// 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()
{

	// START
	//
	// Doe de initialisatie voor het programma. 
	// Breng de spindel naar rechts tot het eindcontact, zodat het programma 
	// weet waar de beker houder is. Ga start daarna de status INIT, NORMAL enn CIP.
	//
	if (MachineState == FiniteState("START"))
	{
		// Move the cup transport to the left to find the zero position...
        // Now we know where we are in the machine
		Spindle.setSpeed(-200);
		Spindle.runSpeed();
		CupPressent = false;
		NewCup = true;
		PrepareDrink = false;
		//UpdateServo("HATCH", 90);
		//UseHMISerial = false;
		if (DigLow("ZERO"))
		{
			Spindle.stop();
			Spindle.disableOutputs();
			Spindle.setCurrentPosition(0);
			TransitionToState("INIT");
			TransitionToState("NORMAL");
			TransitionToState("CIP");
		}
	};

	// NORMAL
	//
	// Door de toetsen combinatie F1 en F2, 5 seconden in te houden en onder  
	// voorwaarde dat we géén drankjes maken; start dan nu de service mode.
	//
	if (MachineState == FiniteState("NORMAL"))
	{
		Service = 0;
		//if (DigLow("F1") && DigLow("F2") && DigLow("F3") && !Service) if (Timer(5000, timService)) TransitionToState("SERVICE");
		if (DigLow("F1") && DigLow("F2") && !PrepareDrink)
		{
			if (Timer(5000, timService)) TransitionToState("SERVICE");
		}
		else CancelTimer(timService);
	}

    // SERVICE
	//
	// Laat de LEDS in een looplicht oplichten. Het is nu mogelijk de betreffende
	// pompjes en de water klep te bedienen buiten de besturing om.
	// Leid het water naar een riool punt!
	// Wanneer 3 seconden op F1 en F2 wordt gedrukt, wordt de service mode 
	// weer verlaten.
	//
	if (MachineState == FiniteState("SERVICE"))
	{
		Service = 1;
		if (Timer(50, timServiceMode)) ServiceLED = ServiceLED + 1;
		if (ServiceLED == 6) ServiceLED = 1;
		switch (ServiceLED)
		{
		case 1:
			Activate("ACKD1");
			break;
		case 2:
			Activate("ACKD2");
			break;
		case 3:
			Activate("ACKD3");
			break;
		case 4:
			Activate("ACKSTART");
			break;
		case 5:
			Activate("READY");
			break;
		}
		if (DigLow("F1") && DigHigh("F2") && DigHigh("F3") && DigHigh("START")) { Activate("&D1"); Activate("ACKD1"); }
		if (DigHigh("F1") && DigLow("F2") && DigHigh("F3") && DigHigh("START")) { Activate("&D2"); Activate("ACKD2"); }
		if (DigHigh("F1") && DigHigh("F2") && DigLow("F3") && DigHigh("START")) { Activate("&D3"); Activate("ACKD3"); }
		if (DigHigh("F1") && DigHigh("F2") && DigHigh("F3") && DigLow("START")) { Activate("&DW"); Activate("ACKSTART"); }
		//if (DigLow("F1") && DigLow("F2") && DigLow("F3")) if (Timer(3000, timService)) TransitionToState("NORMAL");
		if (DigLow("F1") && DigLow("F2"))
		{
			if (Timer(3000, timService)) TransitionToState("NORMAL");
		}
		else CancelTimer(timService);
	};

	// INIT
	//
	// Na het initialiseren van het programma, brengt de besturing de beker houder op de 
	// laad/los positie van de machine.
	// Start nu de state CHOICE (keuze maken) en CUPPRESSENT (check op "beker aanwezig").
	//
	if (MachineState == FiniteState("INIT"))
	{
		// Move to the load/unload position
		if (Timer(2000, timStartup))
		{
			Spindle.setSpeed(350);
			Spindle.runToNewPosition(200);
			Spindle.setCurrentPosition(0);
			Spindle.disableOutputs();
			TransitionToState("CHOICE");
			TransitionToState("CUPPRESSENT");
		}        
	};

	// CHOICE
	//
	// Wacht op de keuze van de gebruiker voor één van de drie smaken.
	// Wanneer er een keuze is gemaakt EN er is een beker aanwezig EN  
	// de machine zit niet in de servide mode; Start dan status MIX.
	//
	if (MachineState == FiniteState("CHOICE"))
	{
		if (CupPressent && !Service) if (Timer(400, timAanUit)) AanUit = !AanUit; else if (AanUit)
		{
			Activate("ACKD1");
			Activate("ACKD2");
			Activate("ACKD3");
		}
		if (DigRising("F1"))
		{
			LiquirNo = 1;
			TimeLiquir = 2200;
			PositionLiquir = 6750;
		}
		if (DigRising("F2"))
		{
			LiquirNo = 2;
			TimeLiquir = 2400;
			PositionLiquir = 4850;
		}
		if (DigRising("F3"))
		{
			LiquirNo = 3;
			TimeLiquir = 2200;
			PositionLiquir = 2850;
		}
		if ((DigRising("F1") || DigRising("F2") || DigRising("F3")) && CupPressent && !Service) TransitionToState("MIX");
		if (!CupPressent) if (Timer(150, timAanUit)) AanUit = !AanUit; else if (AanUit) Activate("READY");
	};

	// MIX
	//
	// Wacht op de bevestiging van de keuze (knop "START") OF op het drukken op een 
	// keuze knop. Wanneer er weer op een keuze knop wordt gedrukt, gaat de status 
	// terug naar CHOICE. Bij een bevestiging, sluit het toegangshekje en start de 
	// besturing de status GOTO-LIQUIR, ondervoorwaarde dat er een nieuwe beker 
	// staat EN deze op de laad/los plaats staat.
	//
	if (MachineState == FiniteState("MIX"))
	{
		Activate("ACKD" + String(LiquirNo));
		if (Timer(400, timAanUit)) AanUit = !AanUit; else if (AanUit) Activate("ACKSTART");
		if (!CupPressent) if (Timer(150, timAanUit)) AanUit = !AanUit; else if (AanUit) Activate("READY");
		if ((DigRising("START")) && CupPressent && NewCup)
		{
			//UpdateServo("HATCH", 1);
			GoMix = true;
			PrepareDrink = true;
			Activate("ACKSTART");
			CupPressent = false;
			//UseHMISerial = true;
			UpdateText("MUZIEK", "---" );
			//UseHMISerial = false;
			TransitionToState("GOTO-LIQUIR");
		}
		if (DigRising("F1") || DigRising("F2") || DigRising("F3")) TransitionToState("CHOICE");
	};

	// GOTO-LIQUIR
	//
	// Stop bij de juiste positie voor één van de drie
	// limonade vulpunten. Bij aankomst start de status LIQUIR.
	//
	if (MachineState == FiniteState("GOTO-LIQUIR"))
	{
		Activate("ACKD" + String(LiquirNo));
		Activate("ACKSTART");
		UpdateText("MUZIEK", "F" + String(LiquirNo));
		if (Timer(200, timWait))
		{
			Spindle.setSpeed(350);
			Spindle.runToNewPosition(PositionLiquir);
			Spindle.disableOutputs();
			TransitionToState("LIQUIR");
		}
	};

	// LIQUIR
	//
	// Sart de pomp voor het limonade smaak voor bepaalde tijd. 
	// Ga daarna naar status GOTO-WATER.
	//
	if (MachineState == FiniteState("LIQUIR"))
	{
		Activate("ACKD" + String(LiquirNo));
		Activate("ACKSTART");
		Activate("&D" + String(LiquirNo));
		if (Timer(TimeLiquir,timLimo)) TransitionToState("GOTO-WATER");
	};

	// GOTO-WATER
	//
	// Wacht eerst 4 seconden, om de limonade te laten uitdruppen en
	// ga dan terug naar de laad/los positie. Bij aankomst start
	// de water dosering in status WATER.
	//
	if (MachineState == FiniteState("GOTO-WATER"))
	{
		Activate("ACKD" + String(LiquirNo));
		Activate("ACKSTART");
		if (Timer(4000, timWait))
		{
			Spindle.setSpeed(350);
			Spindle.runToNewPosition(0);
			Spindle.disableOutputs();
			TransitionToState("WATER");
		}
	};

	// WATER
	//
	// Start de water dosering voor bepaalde tijd door openen van de afsluiter.
	// Daarna starten de states READY en NEWCUP.
	//
	if (MachineState == FiniteState("WATER"))
	{
		Activate("ACKSTART");
		Activate("ACKD" + String(LiquirNo));
		Activate("&DW");
		if (Timer(1500, timWater))
		{
			NewCup = false;
			TransitionToState("READY");
			TransitionToState("NEWCUP");
		}
	};

	// READY
	// 
	// Hekje open sturen. Start status CHOICE en CUPPRESENT.
	//
	if (MachineState == FiniteState("READY"))
	{
		PrepareDrink = false;
		//UpdateServo("HATCH", 90);
		if (Timer(150, timAanUit)) AanUit = !AanUit; else if (AanUit && !Service) Activate("READY");
		if ((Timer(5000, timGameOver)) && NewCup)
		{
			GoMix = false;
			TransitionToState("CHOICE");
			TransitionToState("CUPPRESSENT");
		}
	};

	// NEWCUP
	//
	// Controle of de beker is wegehaald van de laad/los plaats.
	// Dit is een voorwaarde (NewCup) voor het opnieuw starten van een cyclus. 
	// Hiermee wordt voorkomen dat een gevulde beker weer de machine 
	// in gaat.. en ja daar wordt alles niet skjinner van!
	//
	if (MachineState == FiniteState("NEWCUP"))
	{
		PollUltra = true;
		if (DistanceLonger("PRESENT", 8))
		{
			PollUltra = false;
			NewCup = true;
			TransitionToState("END");
		}
	};

	// CUPPRESSENT
	//
	// In deze status wordt gecontroleerd of er een beker op de laad/los plek
	// aanwezig is (CupPressent). Check d.m.v. een ultrasone meting. 
	//
	if (MachineState == FiniteState("CUPPRESSENT"))
	{
		PollUltra = true;
		if (Distance("PRESENT") != 0) if (DistanceBetween("PRESENT", 4, 7)) CupPressent = true; else CupPressent = false;
		if (GoMix)
		{
			PollUltra = false;
			TransitionToState("END");
		}
	};
	
	// CIP
	//
	// Elke 2 minuten, wanneer er geen drankjes worden gemaakt, zal
	// een afsluiter worden open gestuurd door CIPCYCLE om de aanvoerslang 
	// te verversen. 
	//
	if (MachineState == FiniteState("CIP"))
	{
		if (Timer(120000, timWaitCIP))
		{
			if (!PrepareDrink)
			{
				TransitionToState("CIPCYCLE");
				TransitionToState("CIP");
			}
			else CancelTimer(timWaitCIP);
		}
	};

	// CIPCYCLE
	//
	// Afsluiter voor de CIP gaat open voor 10 seconden. Daarna wordt
	// de status gestopt. Het kan zijn dat iemand ondertussen weer
	// een drankje wil maken; de CIP afsluiter wordt nu ook dichtgestuurd.
	// 
	if (MachineState == FiniteState("CIPCYCLE"))
	{
		Activate("&CIP");
		if (Timer(10000, timCIP)) TransitionToState("END");
		if (PrepareDrink)
		{
			TransitionToState("END");
			CancelTimer(timCIP);
		}
	};

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

Credits

Jelle

Jelle

8 projects • 19 followers
I'am developing software only during rainy days.

Comments