YASM - Yet Another State Machine library

Hello

I needed for some of my projects to use state machines. I looked for the existing ibraries and found nothing that suited my needs. So I made one, and in the event it could be usefull to someone I post it there.

It is based on function pointers.
All states must be void functions with no parameters, and with these few restrictions, you can do almost everything with it in a very simple way.

You can create as much different machines you need, run them concurrently or run one (or more) from within another, and so on. You can even combinate them to mix states, but I guess this can produce very unexpected behaviour :slight_smile:

EDIT : Please get it on github to get latest version : GitHub - bricofoy/yasm: Yet Another State Machine library - a function pointer based state machine library for arduino

EDIT 2 : library now directly available in the library manager

You can have a look at the provided example but as it was created in a debug purpose it is not very clear.

As alternative you can have a look at this sketch wich use the library to create a button object capable of sending CLICK and LONGCLICK events to drive some basic lcd menu. Purpose of this was to store in eeprom some DS18B20 addresses but stripping out the dallas part it is easy enought to re-use it for something else.

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <yasm.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 9
#define TEMPERATURE_PRECISION 9

#define EEPROM_BASE_ADR 0
#define pin_sw 10
#define NBR_SONDE 9 //nombre de sondes à rechercher et stocker en eeprom
uint8_t numSonde=1;

#define BTN_NONE 0
#define BTN_CLICK 1
#define BTN_LONGCLICK 2

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// arrays to hold device addresses
DeviceAddress adrSonde;

LiquidCrystal_I2C lcd(0x20, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE);

YASM menu;
YASM btn;

void setup(void)
{
  // start serial port
  Serial.begin(9600);
  
  pinMode(pin_sw,INPUT_PULLUP);

  // Start up the library
  sensors.begin();
  lcd.begin(4,20);
  lcd.backlight();
  lcd.clear();
  
  menu.next(menu_start);
  btn.next(btn_wait);


}

uint8_t flagbtn=0;
uint8_t btnstate=BTN_NONE;

void loop(void)
{
	flagbtn = !digitalRead(pin_sw);
	menu.run();
	btn.run();
	
}

/////////////button state machine///////////////

void btn_wait()
{
	if(btn.elapsed(100)) if(flagbtn) btn.next(btn_debounce);
}

void btn_debounce()
{
	if(!flagbtn) btn.next(btn_wait);
	if(btn.elapsed(5)) btn.next(btn_check);
}

void btn_check()
{
	if(btn.elapsed(1E3)) { 
		btn.next(btn_longpress); 
		btnstate = BTN_LONGCLICK;
		return;
	}
	if(!flagbtn) {
		btn.next(btn_wait);
		btnstate = BTN_CLICK;
	}
}

void btn_longpress()
{
	if(!flagbtn) btn.next(btn_wait);
}

///////////menu state machine////////////

void menu_start()
{
	if(menu.isFirstRun) {
		lcd.clear();
		lcd.print("Appuyez pour rechercher sonde ");
		lcd.print(numSonde);
		lcd.print("/");
		lcd.print(NBR_SONDE);
	}
	if(btnstate==BTN_CLICK) {
		btnstate=BTN_NONE;
		menu.next(menu_recherche);
		lcd.clear();
	}	
	if(btnstate==BTN_LONGCLICK) {
		btnstate=BTN_NONE;
		menu.next(menu_affiche);
		numSonde=1;
		//lcd.clear();
	}
	if(numSonde>NBR_SONDE) menu.next(menu_fin);
}

void menu_affiche()
{
	if(menu.isFirstRun) {
		lcd.clear();
		for(uint8_t j=0; j<4;j++)
		{
			lcd.setCursor(0,j);
			lcd.print(numSonde);
			lcd.print("  ");
			EEPROM.get(EEPROM_BASE_ADR + (sizeof(adrSonde)*(numSonde-1)) , adrSonde);
			for (uint8_t i = 0; i < 8; i++)
			{
				// zero pad the address if necessary
				if (adrSonde[i] < 16) lcd.print("0");
				lcd.print(adrSonde[i], HEX);
			}
			numSonde++;
		}
	}
	if(btnstate==BTN_CLICK) {
		btnstate=BTN_NONE;
		menu.next(menu_affiche_next);
		lcd.clear();
	}
}

void menu_affiche_next()
{
		menu.next(menu_affiche);
}

void menu_fin()
{
	if(menu.isFirstRun) {
		lcd.clear();
		lcd.print("Fin d'enregistrement");
	}
}

void menu_recherche()
{
	if(menu.periodic(1.5E3)){
		//sensors.begin();
		lcd.clear();
		sensors.getDeviceCount();
		if (!sensors.getAddress(adrSonde, 0)) lcd.print("erreur sonde");
		else {
			sensors.setResolution(adrSonde, TEMPERATURE_PRECISION);
			lcd.print("sonde ");
			lcd.print(numSonde);
			lcd.print("/");
			lcd.print(NBR_SONDE);
			lcd.print("   ");
			sensors.getTempC(adrSonde);
			lcd.print(sensors.getTempC(adrSonde));
			lcd.setCursor(0,1);
			for (uint8_t i = 0; i < 8; i++){
				// zero pad the address if necessary
				if (adrSonde[i] < 16) lcd.print("0");
				lcd.print(adrSonde[i], HEX);
			}
			lcd.setCursor(0,2);
			lcd.print("Appui court SAVE");
			lcd.setCursor(0,3);
			lcd.print("Appui long SUIVANTE");
		}
	}
	if(btnstate==BTN_CLICK) {
		btnstate=BTN_NONE;
		menu.next(menu_save);
	}
	if(btnstate==BTN_LONGCLICK) {
		btnstate=BTN_NONE;
		menu.next(menu_start);
		numSonde++;
	}
}

void menu_save()
{
	if(menu.isFirstRun){
		lcd.clear();
		lcd.print("enreg. sonde ");
		lcd.print(numSonde);
		lcd.print("/");
		lcd.print(NBR_SONDE);
		lcd.print(" ?");
		lcd.setCursor(0,1);
		lcd.print("long=SAVE, court=RETOUR");
	}
	if(btnstate==BTN_CLICK){
		btnstate=BTN_NONE;
		menu.next(menu_start);
	}	
	if(btnstate==BTN_LONGCLICK){
		btnstate=BTN_NONE;
		EEPROM.put(EEPROM_BASE_ADR + (sizeof(adrSonde)*(numSonde-1)), adrSonde);
		numSonde++;
		menu.next(menu_start);
	}
}

library now on github. Please get the latest version there, as I add features and make bug correction as long as I use it on the project I made it for.

library now directly available in the library manager