
/*
                                _
  ___  _ __   ___ _ __    _ __ (_) ___ ___  _   _ ____
 / _ \| '_ \ / _ \ '_ \  | '_ \| |/ __/ _ \| | | |_  /
| (_) | |_) |  __/ | | | | |_) | | (_| (_) | |_| |/ /
 \___/| .__/ \___|_| |_| | .__/|_|\___\___/ \__,_/___|
      |_|                |_|

This software is released under the GPL, Copyleft Samuel Lesueur @electrolab

DISCLAIMER : I am not a medicine professionnal, use at your one risks !
This code is probably not state of the art but it is written in a safe way and should
allow you to build reliable devices to help during the COVID outbreak.
This .ino is designed to be used with the open picouz hardware that was specifically designed
to be easy to solder and only use common components in the maker world.

*/

/* DEBUG DEFINES */

//#define DEBUG_PRESSURE  //use this to graph the syringe pressure 
//#define DEBUG_VALUES    //use this to get the motor / sensor values on the serial terminal
//#define DEBUG_STATES    //use this to get state machines states on the serial terminal
//#define DEBUG_PROCESS   //use this to have detailed informations about the medicine calculations
//#define DEBUG_TIME      //use this to print the elapsed and remaining time into the console
//#define DEBUG_POWER     //use this to display power information into the console

#if defined(DEBUG_PRESSURE) || defined(DEBUG_VALUES) || defined(DEBUG_STATES) || defined(DEBUG_PROCESS) || defined(DEBUG_TIME) || defined(DEBUG_POWER)

#define DEBUG

#endif


//LCD HD44780 library
#include <LiquidCrystal.h>
// EEprom library
#include <EEPROM.h>

#define SOFTWARE_REVISION "V 0.9 WIP!" // 10 chars max

/* Hardware definitions */

/* Memory map */
//Here we store the injection parameters and the state of the device in order to restore them at boot time
#define MEMORY_LAST_STATE_MSB 0x00 //int (2 Bytes)
#define MEMORY_LAST_STATE_LSB 0x01
#define MEMORY_SYRINGE_INDEX_MSB 0x02 //int (2 Bytes)
#define MEMORY_SYRINGE_INDEX_LSB 0x03
#define MEMORY_INJECTION_SPEED_MSB 0x04 //int (2 Bytes)
#define MEMORY_INJECTION_SPEED_LSB 0x05
#define MEMORY_REMAINING_TIME_ALARM_MSB 0x06 //int (2 Bytes)
#define MEMORY_REMAINING_TIME_ALARM_LSB 0x07
#define MEMORY_SENSOR_POSITION_MSB 0x08 //int (2 Bytes)
#define MEMORY_SENSOR_POSITION_LSB 0x09
#define MEMORY_MOTOR_STEPS_B0 0x0A //long (4 Bytes)
#define MEMORY_MOTOR_STEPS_B1 0x0B
#define MEMORY_MOTOR_STEPS_B2 0x0C
#define MEMORY_MOTOR_STEPS_B3 0x0D
#define MEMORY_INJECTION_START_B0 0x0E //long (4 Bytes)
#define MEMORY_INJECTION_START_B1 0x0F
#define MEMORY_INJECTION_START_B2 0x10
#define MEMORY_INJECTION_START_B3 0x11

/* Watchdog stuff */
// The hardware watchdog is basically monitoring the voltage on a capacitor. putting a  High level on PIN_WD_IO will increase this voltage,
// putting a low level will decrease it, switching the pin to input will let it float and drop slowly.
// We use PIN_WD_ADC to monitor the voltage of the capacitor with the arduino to keep it bwteen the tolerances of the hardware watchdog (between 2 and 4V)
// We use PIN_WD_FB to know if the watchdog has been triggered (and latched) by the hardware to display a message if the code is still running.

#define PIN_WD_FB A0
#define PIN_WD_IO 13
#define PIN_WD_ADC A7

#define WD_THRESHOLD_MIN 512
#define WD_THRESHOLD_MAX 640
int wd_count=0;
int wd_debounce=0;

/* Power management */
// the Open syringe project include a 18V 36Wh lion Ryobi battery
// we don't have much control over it, we just get a reading of the battery voltage when the psu
// automatically switches in battery mode. If voltage is null : we are on main power, not a single electron is drawn
// from the battery
#define PIN_PSU_VMES A5
#define BATTERY_MAINS_TRESHOLD 150
#define BATTERY_LOW_TRESHOLD 553 // 4S low voltage is 13.6V, we have a 33k/8k voltage divider so around 2.7V when battery is low which is 553 on a 12bits ADC

#define BATTERY_STATE_MAINS_CONNECTED 0x0
#define BATTERY_STATE_ON              0x01
#define BATTERY_STATE_LOW             0x02

int ui_battery_state=0;



/* Motor control */
// the motor control is done using a pololu (A4988 Stepper Motor Driver Carrier) or any step / dir compatible stepper driver circuit
// we chose the pololu as we know many makers use them and will have a few drivers to spare for this project.

#define PIN_MOTOR_ENABLE 8
#define PIN_MOTOR_STEP 9
#define PIN_MOTOR_DIR 10

#define MOTOR_STATE_BOOTING 0x01
#define MOTOR_STATE_READY 0x02
#define MOTOR_STATE_INJECTING 0x03
#define MOTOR_STATE_FREE_MOVE 0x04
#define MOTOR_STATE_BOLUS 0x05
#define MOTOR_STATE_LOOK_FOR_SWITCH 0x06
#define MOTOR_STATE_GOTO_SYRINGE_START 0x07
#define MOTOR_STATE_LOOK_FOR_SYRINGE 0x08
#define MOTOR_STATE_RELOAD 0x09
#define MOTOR_STATE_END 0x10

#define MOTOR_STATE_ERROR 0xF0
#define MOTOR_STATE_JAMMED 0xF1
#define MOTOR_STATE_OVERPRESSURE 0xF2
#define MOTOR_STATE_NO_SYRINGE 0xF3

#define MOTOR_STATE_WATCHDOG 0xFF

// Change this to chose the speed of the motor during manual movements through the LCD menus
#define CONFIG_MOTOR_FREE_MOVE_SPEED 1024

//change this to chose the speed of the motor during automatic movements
#define CONFIG_MOTOR_AUTO_MOVE_SPEED 512

//change this to chose the speed of syringe approach movements
#define CONFIG_MOTOR_SYRINGE_APPROACH_SPEED 2048

//motor state machine state storage
int motor_state=0, motor_old_state=0, motor_interrupted_state=0;

//motor state machine messenger between UI and motor
int motor_requested_state=0;

//motor pulses counter
volatile long motor_steps = 0;
//motor toggle
int motor_toggle = 0;
//motor direction (0 = towards end of syringe)
volatile int motor_dir =0;
int motor_prescaler=1;

//motor storage of the injection start position
long motor_injection_start_steps=0;

//motor storage of the bolus start position
long motor_bolus_start_steps=0;

//pulse counting interrupt
ISR(TIMER1_COMPA_vect){
  //we have interruptions on rising and falling edges, the DRV only uses rising edges
  if(motor_toggle){
    if(!motor_dir){
      motor_steps++;
    }
    else{
      motor_steps--;
    }
  }
  motor_toggle = !motor_toggle;
}

// basic motor control functions

//this function rewinds the motor back (toward the end switch), 
//speed is the value of the OCR1A register and will be applied with a /1 prescaler
void motor_rewind(unsigned int motor_speed){
  //move back
  digitalWrite(PIN_MOTOR_ENABLE, HIGH);
  digitalWrite(PIN_MOTOR_DIR, HIGH);
  motor_dir = 1;
  motor_prescaler =1;
  
  //start the timer
  OCR1A = motor_speed;
  TCCR1A = (1<< COM1A0);//toggle Oc1a on compare,
  TCCR1B = (1<< WGM12); //CTC mode (clr TCNT1 on compare);
  TCCR1B |= (1 << CS10); // /1 prescaler on timer 1 clock
  
}

//this function pushes the motor forward (twowards the syringe), speed is the value of the OCR1A register
//speed is the value of the OCR1A register and will be applied with a /1 prescaler
void motor_forward(unsigned int motor_speed){
  //move forward
  digitalWrite(PIN_MOTOR_ENABLE, HIGH);
  digitalWrite(PIN_MOTOR_DIR, LOW);
  motor_dir = 0;
  motor_prescaler =1;
  
  //start the timer
  OCR1A = motor_speed;
  TCCR1A = (1<< COM1A0);//toggle Oc1a on compare,
  TCCR1B = (1<< WGM12); //CTC mode (clr TCNT1 on compare);
  TCCR1B |= (1 << CS10); // /1 prescaler on timer 1 clock
  
}

//this function takes the desired injection speed (in ml per hour) and the syringe step per ml magic value
//it will fill the OCR1A register with the right value and appy a /8 or /64 prescaler depending on the required speed.
void motor_play(int desired_injection_speed, int current_syringe_steps_to_ml){
  unsigned long int timer=0;

  //move forward
  digitalWrite(PIN_MOTOR_ENABLE, HIGH);
  digitalWrite(PIN_MOTOR_DIR, LOW);
  motor_dir = 0;

  // value for 1ms in 1/32 microsteps mode: 31.2500 = (8*10^6) / (1000*256)
  //as desired_injection_speed is now in 0.1ml/h we multiply per 10 this magic value to compensate
  timer = ((312.500*3600000UL)/((unsigned long)desired_injection_speed*(unsigned long)current_syringe_steps_to_ml));
  #ifdef DEBUG_PROCESS
  Serial.println("********************* TIMER VALUE *********************");
  Serial.println(timer);
  #endif

  if(timer > 65535){
    //very slow injection speeds require a different prescaler on timer0
      TCCR1B = (1 << CS11) | (1 << CS10);    // /64 prescaler on timer 1 clock
      OCR1A = timer / 8;
      motor_prescaler = 64;
      #ifdef DEBUG_PROCESS
      Serial.println("very slow injection speed : setting prescaler to /64 ");
      #endif
  }
  else{
    #ifdef DEBUG_PROCESS
    Serial.println("setting prescaler to /8 ");
    #endif
    motor_prescaler = 8;
    //make sure the clock is still set to a slow value
    TCCR1B = (1 << CS11);    // /8 prescaler on timer 1 clock

    //start the timer using the calculated value
    OCR1A =timer;
  }
  
  #ifdef DEBUG_PROCESS
  Serial.println("OCR1A = ");
  Serial.print(OCR1A);
  #endif
  
  TCCR1A = (1<< COM1A0); //toggle Oc1a on compare,
  TCCR1B |= (1<< WGM12); //CTC mode (clr TCNT1 on compare);
}

//stop the engine !
void motor_stop(){
  
  //stop the motor
  digitalWrite(PIN_MOTOR_ENABLE, LOW);
  digitalWrite(PIN_MOTOR_DIR, LOW);
  motor_dir = 0;
  motor_prescaler = 0;
  //shut down the timer
  TCCR1A = 0;
  TCCR1B = 0;
}


/* Sensors */

//position encoder
#define PIN_POSITION_SENSOR_A 2
#define PIN_POSITION_SENSOR_B 3

volatile int sensor_position_counter = 0;
volatile int pos_sensor_buffer=0;

//this variable is used to store the motor_steps value when a sensor pulse occurs
volatile long sensor_last_motor_steps = 0;

void pos_sensor_ISR() {
  int a;
    a = digitalRead(PIN_POSITION_SENSOR_A);
    if(a != pos_sensor_buffer){
      if(digitalRead(PIN_POSITION_SENSOR_B) != a){
        sensor_position_counter++;
      }
      else{
        sensor_position_counter--;
      }
      //store the motor steps value
      sensor_last_motor_steps = motor_steps;
    }
    pos_sensor_buffer = a;
}

//end switch and clutch are connected using a 10k/1k network on one of the ADC pins.
#define PIN_CLUTCH_AND_END_SWITCHES A4

#define CLUTCH_SWITCH_TRESHOLD 250
#define END_SWITCH_TRESHOLD 768


// Pressure Sensor
#define PIN_PRESSURE_SENSOR A6

/* Hardware limitations */

//maximum motor step between two sensor pulse : if above, hardware is considered mechanically wrong or jammed.
#define SENSOR_MAX_MOTOR_STEPS_PER_PULSE 12000L

//maximum sensor steps : if above, hardware is considered mechanically wrong or jammed.
#define SENSOR_MAX_VALUE 105

//maximum motor steps : if above, hardware is considered mechanically wrong or jammed.
#define MOTOR_MAX_STEPS 622600UL

#define PRESSURE_SENSOR_SYRINGE_TRESHOLD 200
#define PRESSURE_SENSOR_ALARM_TRESHOLD 990 //should trigger around 500 mmHg

/* UI */
// We are also using a very common LCD interface that should be able to talk with most alphanumerical displays
// To save a few pins on the arduino, the LCD is driven in 4 bits mode so W/R, LCD_IO3, LCD_IO2, LCD_IO1, LCD_IO0 should be tied to GND
#define PIN_LCD_RS 11
#define PIN_LCD_EN  12
#define PIN_LCD_IO4 4
#define PIN_LCD_IO5 5
#define PIN_LCD_IO6 6
#define PIN_LCD_IO7 7
#define PIN_BTN_PRV A1
#define PIN_BTN_NXT A2
#define PIN_BTN_OK A3

// buzzer stuff
#define PIN_BUZZER 0
const char buzzer_timing_pre_alarm[10] = {1,0,1,0,0,0,0,0,0,0};

#define PIN_GREEN_LED 1

//States of the UI
#define UI_STATE_BOOTING 0x01
#define UI_STATE_WAITING 0x02
#define UI_STATE_READY 0x03
#define UI_STATE_CONFIRM_INJECTION 0x04
#define UI_STATE_RUNNING 0x05
#define UI_STATE_BOLUS 0x06
#define UI_STATE_CONFIRM_STOP 0x07
#define UI_STATE_END 0x08

#define UI_STATE_MAIN_MENU 0xF0
#define UI_STATE_MENU_SPEED 0xF1
#define UI_STATE_MENU_SYRINGE 0xF2
#define UI_STATE_MENU_ALARM_TIME 0xF3
#define UI_STATE_MENU_MANUAL 0xF4
#define UI_STATE_MENU_CALIBRATE 0xF5
#define UI_STATE_MENU_SELFTEST 0xF6
#define UI_STATE_MENU_ABOUT 0xF7

#define UI_STATE_MENU_CONFIRM_VALUE 0xFA
#define UI_STATE_MENU_CONFIRM_RESUME 0xFB
#define UI_STATE_MENU_CONFIRM_CALIBRATE 0xFC

#define UI_STATE_ALARM 0xFE
#define UI_STATE_WATCHDOG 0xFF

//Change this to control the selection characters that will be displayed around the selected option in the menus
#define UI_SELECT_CHAR_LEFT 0x7E
#define UI_SELECT_CHAR_RIGHT 0x7F

//change this to control the time before a menu timeout (in 0.1 secs)
#define UI_MENU_TIMEOUT 300

//UI state machine state storage
int ui_state=0, ui_old_state=0;

//buttons press counter (in 0.1s)
int ui_btn_prev=0, ui_btn_next=0, ui_btn_ok=0;

//misc variables available for each sate to use as temporary variables at its discretion
int ui_blink=0, ui_selection=0, ui_state_count=0;

//keeping track of the time
unsigned long last_millis=0;
unsigned long ui_remaining_time=0; // in seconds

//In running state we have a remaining time alarm (stored in eeprom)
int ui_remaining_time_alarm = 0; //in minutes
int ui_elapsed_time = 0; //in seconds
int ui_current_injected_volume=0; //in 0.1 ml
int ui_current_bolus_injected_volume=0; //in 0.1ml
int ui_total_bolus_injected_volume=0; // in 0.1ml

//misc variables available for each state to use as temporaray variables at its discretion
int h=0,m=0,s=0,tmp=0;

#if defined(DEBUG)
  //Advanced Main configuration Menu
  #define UI_MAIN_MENU_ITEMS 7
  const char ui_menu_names[UI_MAIN_MENU_ITEMS +1][21] = {"Debit", "Taille seringue", "Alarme fin", "Mouvement chariot", "Calibration", "Autotest", "A propos", "----sortie menu----"};
#endif

#if !(defined(DEBUG))
  //User only configuration Menu
  #define UI_MAIN_MENU_ITEMS 3
  const char ui_menu_names[UI_MAIN_MENU_ITEMS +1][21] = {"Debit", "Taille seringue", "Alarme fin", "----sortie menu----"};
#endif

//should be here so it can define a global lcd object
LiquidCrystal lcd(PIN_LCD_RS,PIN_LCD_EN, PIN_LCD_IO4, PIN_LCD_IO5, PIN_LCD_IO6, PIN_LCD_IO7);

#define UI_WELCOME_STRING_1 "    Open syringe !"
#define UI_WELCOME_STRING_2 "    by ELECTROLAB"

#define UI_DISCLAIMER_STRING_1 "  === ATTENTION ==="
#define UI_DISCLAIMER_STRING_2 "Produit non certifie"
#define UI_DISCLAIMER_STRING_3 "Utilisation R&D et"
#define UI_DISCLAIMER_STRING_4 "crises sanitaires"

//nice syringe icon :)
unsigned char Syringe_char[] = {
  B00100,
  B01110,
  B01010,
  B01010,
  B01010,
  B01110,
  B00100,
  B01110
};

/* Process Definition */
#define CONFIG_MAX_INJECTION_SPEED 3000 // in 0.1ml/h

// Change this to chose the speed of the motor during bolus mode
#define CONFIG_BOLUS_SPEED 6000 //in 0.1 ml/h

#define CONFIG_MAX_REMAINING_TIME_ALARM  120//in minutes

//process variables (stored in eeprom)
int current_syringe=0, current_injection_speed=0;

//Syringe list : syringe name, syringe step per ml ration and syringe start position
#define UI_MAX_SYRINGE 4

const int ui_syringe_list[UI_MAX_SYRINGE+1] = {10,20,50,100,150};
const int config_syringe_step_per_ml[UI_MAX_SYRINGE+1] = {1115,558,293,111,74}; //in motor steps (not micro steps !)
const int config_syringe_start_pos [UI_MAX_SYRINGE+1] = {0,0,16,0,0}; // in sensor steps

void setup() {

  #ifdef DEBUG
  Serial.begin(9600);
  #endif

  //output pins setup
  pinMode(PIN_WD_IO, OUTPUT);
  pinMode(PIN_WD_FB, INPUT);

  pinMode(PIN_MOTOR_ENABLE, OUTPUT);
  pinMode(PIN_MOTOR_STEP, OUTPUT);
  pinMode(PIN_MOTOR_DIR, OUTPUT);

  digitalWrite(PIN_MOTOR_ENABLE, LOW);
  digitalWrite(PIN_MOTOR_STEP, LOW);
  digitalWrite(PIN_MOTOR_DIR, LOW);

  pinMode(PIN_BUZZER, OUTPUT);
  pinMode(PIN_GREEN_LED, OUTPUT);

  digitalWrite(PIN_GREEN_LED, HIGH);
  digitalWrite(PIN_BUZZER, HIGH); //inverted

  //sensor pins
  pinMode(PIN_POSITION_SENSOR_A, INPUT);
  pinMode(PIN_POSITION_SENSOR_B, INPUT);
  pinMode(PIN_CLUTCH_AND_END_SWITCHES, INPUT);

  //register sensor interrupt
  attachInterrupt(0, pos_sensor_ISR, CHANGE);

  //this sets the requested output pins so we dont have to
  lcd.begin(20, 4);

  //storing the custom chars in memory
  lcd.createChar(0, Syringe_char);

  //reading last values from eeprom
  current_syringe = ((int)EEPROM.read(MEMORY_SYRINGE_INDEX_MSB) << 8) |  EEPROM.read(MEMORY_SYRINGE_INDEX_LSB);
  current_injection_speed = ((int)EEPROM.read(MEMORY_INJECTION_SPEED_MSB) << 8) |  EEPROM.read(MEMORY_INJECTION_SPEED_LSB);
  ui_remaining_time_alarm = ((int)EEPROM.read(MEMORY_REMAINING_TIME_ALARM_MSB) << 8) |  EEPROM.read(MEMORY_REMAINING_TIME_ALARM_LSB);

  //verifying the read values
  if((current_syringe > UI_MAX_SYRINGE) || (current_syringe < 0) ) current_syringe = 0;
  if((current_injection_speed > CONFIG_MAX_INJECTION_SPEED) || (current_injection_speed < 0) ) current_injection_speed = 0;
  if((ui_remaining_time_alarm > CONFIG_MAX_REMAINING_TIME_ALARM) || (ui_remaining_time_alarm < 0) ) ui_remaining_time_alarm = 5;

  //setting the motor state machine in booting state
  motor_state = MOTOR_STATE_BOOTING;
  motor_old_state = MOTOR_STATE_BOOTING;

  //setting the UI state machine in booting state
  ui_state = UI_STATE_BOOTING;
  ui_old_state = UI_STATE_BOOTING;

}

void loop() {

  //here we store the values of the volatile variables
  long safe_motor_steps = 0;
  int safe_sensor_position_counter =0;
  long safe_sensor_last_motor_steps = 0;
  
  //watchdog management and debounce
  pinMode(PIN_WD_FB, INPUT);
  if(analogRead(PIN_WD_FB) < 512){
    wd_debounce++;
  }
  else{
    wd_debounce=0;
  }

  //buttons management and debounce
  if(!digitalRead(PIN_BTN_PRV)){
    ui_btn_prev++;
  }
  else{
    ui_btn_prev = 0;
  }
   if(!digitalRead(PIN_BTN_NXT)){
    ui_btn_next++;
  }
  else{
    ui_btn_next = 0;
  }
  if(!digitalRead(PIN_BTN_OK)){
    ui_btn_ok++;
  }
  else{
    ui_btn_ok = 0;
  }

  //power management
  if(analogRead(PIN_PSU_VMES) < BATTERY_MAINS_TRESHOLD){
    if(ui_battery_state != BATTERY_STATE_MAINS_CONNECTED){
       //clear the buzzer
       digitalWrite(PIN_BUZZER, HIGH);
    }
    ui_battery_state = BATTERY_STATE_MAINS_CONNECTED;
  }
  else if(analogRead(PIN_PSU_VMES) < BATTERY_LOW_TRESHOLD){
    if(ui_battery_state != BATTERY_STATE_LOW){
       //set the buzzer
       digitalWrite(PIN_BUZZER, LOW);
    }
    ui_battery_state = BATTERY_STATE_LOW;
  }
  else{
     if(ui_battery_state != BATTERY_STATE_ON){
       //clr the buzzer
       digitalWrite(PIN_BUZZER, HIGH);
    }   
    ui_battery_state = BATTERY_STATE_ON;
  }

  //volatile variable copy
  noInterrupts();
  safe_motor_steps = motor_steps;
  safe_sensor_position_counter = sensor_position_counter;
  safe_sensor_last_motor_steps = sensor_last_motor_steps;
  interrupts();
    
 /* UI state machine */
 //All ui related calculations are done in this section

  switch(ui_state) {

    //This state is the first state the UI starts in. It displays a welcome message and jump to
    //ready or confirm resume state depending on the MEMORY_LAST_STATE value
    //user has no possible input
    //2 Secs timeout
    case UI_STATE_BOOTING:
      lcd.clear();
      lcd.print(UI_WELCOME_STRING_1);
      lcd.setCursor(0, 2);
      lcd.print(UI_WELCOME_STRING_2);
      lcd.setCursor(0, 3);
      lcd.print("Version : ");
      lcd.print(SOFTWARE_REVISION);

      //this is the only blocking wait in the state machine, it is just here to prevent the watchdog message to kick in at boot time before
      //we can read the software version
      #ifndef DEBUG
      while(millis()/1000 <= 2);
      #endif

      lcd.clear();
      lcd.print(UI_DISCLAIMER_STRING_1);
      lcd.setCursor(0, 1);
      lcd.print(UI_DISCLAIMER_STRING_2);
      lcd.setCursor(0, 2);
      lcd.print(UI_DISCLAIMER_STRING_3);
      lcd.setCursor(0, 3);
      lcd.print(UI_DISCLAIMER_STRING_4);

      //this is the only blocking wait in the state machine, it is just here to prevent the watchdog message to kick in at boot time before
      //we can read the disclaimer
      #ifndef DEBUG
      while(millis()/1000 <= 6);
      #endif

      //trying to resume injection if needed
      if((((int)EEPROM.read(MEMORY_LAST_STATE_MSB) << 8) |  EEPROM.read(MEMORY_LAST_STATE_LSB)) == MOTOR_STATE_INJECTING){
        ui_state = UI_STATE_MENU_CONFIRM_RESUME;
      }
      else{
        ui_state = UI_STATE_WAITING;
      }
    break;
    
    //simple Waiting screen, go back to ready when the motor state machine is ready
    case UI_STATE_WAITING:
      if(ui_old_state != UI_STATE_WAITING){
        lcd.clear();
        lcd.setCursor(0, 1);
        lcd.print("  REINITIALISATION");        
        lcd.setCursor(0, 2);
        lcd.print(" VEUILLEZ PATIENTER");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }
      if(motor_state == MOTOR_STATE_READY){
        ui_state = UI_STATE_READY;
      }
    break;

    //This state is the waiting state. It allows the user to start injection or entering the main menu
    case UI_STATE_READY:

       if(ui_old_state != UI_STATE_READY){
        //send a stop message to the motor state machine
        motor_requested_state = MOTOR_STATE_READY;
        lcd.clear();
        lcd.write((byte)0x00);
        lcd.print(ui_syringe_list[current_syringe]);
        lcd.setCursor(4, 0);
        lcd.print("ml");
        lcd.setCursor(11, 0);
        if(current_injection_speed < 1000) lcd.print("0");
        if(current_injection_speed < 100) lcd.print("0");
        lcd.print(current_injection_speed / 10);
        lcd.print(".");
        lcd.print(current_injection_speed % 10);
        lcd.setCursor(16, 0);
        lcd.print("ml/h");
        lcd.setCursor(7, 1);
        lcd.print("=PRET= ");
        lcd.setCursor(1, 3);
        lcd.print("menu");
        lcd.setCursor(11, 3);
        lcd.print("injecter");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //tasteful animation in idle mode
      lcd.setCursor(7, 1);
      if(ui_blink){
        lcd.print("-");
      }
      else{
        lcd.print("|");
      }
      lcd.setCursor(12, 1);
      if(ui_blink){
        lcd.print("-");
      }
      else{
        lcd.print("|");
      }

      //display item selection
      if(!ui_selection){
        lcd.setCursor(0, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(5, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
        lcd.setCursor(10, 3);
        lcd.print(" ");
        lcd.setCursor(19, 3);
        lcd.print(" ");
      }
      else{
        lcd.setCursor(0, 3);
        lcd.print(" ");
        lcd.setCursor(5, 3);
        lcd.print(" ");
        lcd.setCursor(10, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(19, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
      }

      //display the battery status
      lcd.setCursor(0, 2);
      if(ui_battery_state == BATTERY_STATE_LOW){
        lcd.print("   BATTERIE VIDE!   ");
      }
      else if(ui_battery_state == BATTERY_STATE_ON){
        lcd.print("    SUR BATTERIE    ");
      }
      else{
        lcd.print("                    ");
      }

      //handle the ui button press
      if((ui_btn_prev == 2)|| (ui_btn_next == 2)){

        ui_selection = !ui_selection;
      }

      if(ui_btn_ok == 2){

        if(ui_selection){
          ui_state = UI_STATE_CONFIRM_INJECTION;
        }
        else{
          ui_state = UI_STATE_MAIN_MENU;
        }
      }

      //if the motor state machine is not ready anymore, display the waiting screen
      if(motor_state != MOTOR_STATE_READY){
        ui_state = UI_STATE_WAITING;
      }

    break;

    //this state is a simple yes/no question to confirm the start of the injection
    //depending on the user input it will switch the UI in the RUNNING state or back to ready.
    case UI_STATE_CONFIRM_INJECTION:

       if(ui_old_state != UI_STATE_CONFIRM_INJECTION){
        lcd.clear();
        lcd.print("CONFIRMEZ INJECTION?");
        lcd.setCursor(0, 1);
        lcd.print("Seringue  : ");
        lcd.print(ui_syringe_list[current_syringe]);
        lcd.print("ml");
        lcd.setCursor(0, 2);
        lcd.print("Debit :");
        lcd.print(current_injection_speed/10);
        lcd.print(".");
        lcd.print(current_injection_speed%10);
        lcd.print("ml/h");
        lcd.setCursor(1, 3);
        lcd.print("retour");
        lcd.setCursor(11, 3);
        lcd.print("injecter");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //display item selection
      if(!ui_selection){
        lcd.setCursor(0, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(7, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
        lcd.setCursor(10, 3);
        lcd.print(" ");
        lcd.setCursor(19, 3);
        lcd.print(" ");
      }
      else{
        lcd.setCursor(0, 3);
        lcd.print(" ");
        lcd.setCursor(7, 3);
        lcd.print(" ");
        lcd.setCursor(10, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(19, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
      }

      //handle the ui button press
      if((ui_btn_prev == 2) || (ui_btn_next == 2)){
        ui_state_count=0;
        ui_selection = !ui_selection;
      }

      if(ui_btn_ok == 2){
        ui_state_count=0;
        if(ui_selection){
          ui_state = UI_STATE_RUNNING;
        }
        else{
          ui_state = UI_STATE_READY;
        }
      }

    // menu timout
    if(ui_state_count > UI_MENU_TIMEOUT){
      ui_state = UI_STATE_READY;
    }

    break;

    //this state is where the injection happens. It triggers the Motor sate machine and diplays the progress.
    //the user can stop the injection with the stop menu or initiate a bolus mode. this would send the UI
    //in the UI_STATE_CONFIRM_STOP or UI_STATE_BOLUS
    case UI_STATE_RUNNING:

       if(ui_old_state != UI_STATE_RUNNING){
        //send a start message to the motor state machine
        motor_requested_state = MOTOR_STATE_INJECTING;
        lcd.clear();
        lcd.print("   Debit");
        lcd.setCursor(9, 0);
        if(current_injection_speed < 1000) lcd.print("0");
        if(current_injection_speed < 100) lcd.print("0");
        lcd.print(current_injection_speed / 10);
        lcd.print(".");
        lcd.print(current_injection_speed % 10);
        lcd.setCursor(14, 0);
        lcd.print("ml/h");
        lcd.setCursor(1, 3);
        lcd.print("stop");
        lcd.setCursor(7, 3);
        lcd.print("ml/h");
        lcd.setCursor(14, 3);
        lcd.print("bolus");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }
      else{
        //if the motor state machine stops, go to stop mode
        if(motor_state == MOTOR_STATE_END){
          ui_state = UI_STATE_END;
        }
      }

      //syringe animation
      if(ui_blink){
        lcd.setCursor(0, 0);
        lcd.write((byte)0x00);
        lcd.setCursor(19, 0);
        lcd.write((byte)0x00);
      }
      else{
        lcd.setCursor(0, 0);
        lcd.print(" ");
        lcd.setCursor(19, 0);
        lcd.print(" ");
      }

      if(motor_state == MOTOR_STATE_INJECTING){
        //Refresh the values every  second
        if(!(ui_state_count %10)){
          
          lcd.setCursor(0, 1);
          lcd.print("Volume    :     . ml");
          lcd.setCursor(0, 2);
          lcd.print("Vide dans :      :  ");
          
          lcd.setCursor(14, 1);
          tmp = ui_current_injected_volume + ui_total_bolus_injected_volume;
          if(tmp < 100) lcd.print("0");
          lcd.print(tmp / 10);
          lcd.setCursor(17, 1);
          lcd.print(tmp % 10);

          //display the battery status
          lcd.setCursor(0, 2);
          if(ui_battery_state == BATTERY_STATE_LOW){
            lcd.print("   BATTERIE VIDE!   ");
          }
          else if(ui_battery_state == BATTERY_STATE_ON){
              lcd.print("    SUR BATTERIE    ");
          }
          else{
            //display the remaining time
            //as the syringe might not be full, we can't trust the injected volume to calculate time
            //but we can use the remaining steps to travel from the hardware limitations
            
            h=(ui_remaining_time / 3600UL);
            m=(ui_remaining_time - (h*3600UL))/60UL;
            s=ui_remaining_time - (h*3600UL) - (m*60UL);
            
            //handle the remaining time and pre alarm
            if((m >= ui_remaining_time_alarm) || ui_blink){
              lcd.setCursor(15, 2);
              lcd.print("  :  ");
              if(h > 0){
                lcd.setCursor(15, 2);
                if(h<10) lcd.print("0");
                lcd.print(h);
                lcd.setCursor(18, 2);
                if(m<10) lcd.print("0");
                lcd.print(m);
              }
              else{
                lcd.setCursor(15, 2);
                if(m<10) lcd.print("0");
                lcd.print(m);
                lcd.setCursor(18, 2);
                if(s<10) lcd.print("0");
                lcd.print(s);              
              }
            }
            else{
              lcd.setCursor(15, 2);
              lcd.print("  :  ");
            }
          }
        }
        //Fancy pre-alarm with the Buzzer
        if((m < ui_remaining_time_alarm) && (buzzer_timing_pre_alarm[ui_state_count %10])){
            digitalWrite(PIN_BUZZER, LOW);
        }
        else{
            digitalWrite(PIN_BUZZER, HIGH);
        }     
      }
      else{
          lcd.setCursor(0, 1);
          lcd.print(" RECHERCHE SERINGUE ");
      }

      //display item selection
      if(ui_selection == 0){
        lcd.setCursor(0, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(5, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
        lcd.setCursor(6, 3);
        lcd.print(" ");
        lcd.setCursor(11, 3);
        lcd.print(" ");
        lcd.setCursor(13, 3);
        lcd.print(" ");
        lcd.setCursor(19, 3);
        lcd.print(" ");
      }
       else if(ui_selection == 1){
        lcd.setCursor(0, 3);
        lcd.print(" ");
        lcd.setCursor(5, 3);
        lcd.print(" ");
        lcd.setCursor(6, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(11, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
        lcd.setCursor(13, 3);
        lcd.print(" ");
        lcd.setCursor(19, 3);
        lcd.print(" ");
      }     
      else if(ui_selection == 2){
        lcd.setCursor(0, 3);
        lcd.print(" ");
        lcd.setCursor(5, 3);
        lcd.print(" ");
        lcd.setCursor(6, 3);
        lcd.print(" ");
        lcd.setCursor(11, 3);
        lcd.print(" ");
        lcd.setCursor(13, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(19, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
      }

      //display the battery status
      lcd.setCursor(0, 2);
      if(ui_battery_state == BATTERY_STATE_LOW){
        lcd.print("   BATTERIE VIDE!   ");
      }
      else if(ui_battery_state == BATTERY_STATE_ON){
        lcd.print("    SUR BATTERIE    ");
      }


      //handle the ui button press
      if((ui_btn_prev == 2) && (ui_selection >0)){
        ui_selection--;
      }
      else if ((ui_btn_next == 2) && (ui_selection <2)){
        ui_selection++;
      }

      if(ui_btn_ok == 2){

        if(ui_selection == 0){
           ui_state = UI_STATE_CONFIRM_STOP;
        }
        else if(ui_selection == 1){
          ui_state = UI_STATE_MENU_SPEED;
        }
        else if(ui_selection ==2){
          ui_state = UI_STATE_BOLUS;
        }
      }
    break;

    //this state is a specific mode of injection. The user forces a volume of medicin into the patient. It triggers the Motor sate machine at a specific speed as long as the user presses the button.
    case UI_STATE_BOLUS:
      if(ui_old_state != UI_STATE_BOLUS){
      //send a bolus message to the motor state machine
      motor_requested_state = MOTOR_STATE_BOLUS;

      lcd.clear();
      lcd.print("     Mode BOLUS");
      lcd.setCursor(0, 1);
      lcd.print("Volume    :  00.0 ml");
      lcd.setCursor(0, 3);
      lcd.print("Ok pour revenir");
      ui_selection=0;
      ui_state_count=0;
      ui_old_state = ui_state;
    }

      //display the bolus value

      lcd.setCursor(13, 1);
      lcd.print("  . ");
      lcd.setCursor(13, 1);
      if(ui_current_bolus_injected_volume < 100)lcd.print("0");
      lcd.print(ui_current_bolus_injected_volume / 10);
      lcd.print(".");
      lcd.print(ui_current_bolus_injected_volume % 10);

      //generate a buzzer pulse everytime we send 1ml in bolus mode
      if(!(ui_current_bolus_injected_volume % 10)){
        if(!ui_selection) digitalWrite(PIN_BUZZER, LOW);
        ui_selection = 1;
      }
      else{
        digitalWrite(PIN_BUZZER, HIGH);
        ui_selection = 0;
      }

      //handle the ui button press
      if(ui_btn_next > 2){
        ui_state_count = 0;
      }
      if(ui_btn_ok == 2){
        ui_state_count = 0;
        ui_state = UI_STATE_RUNNING;
      }

    // menu timout
    if(ui_state_count > UI_MENU_TIMEOUT){
      ui_state = UI_STATE_RUNNING;
    }


    break;


    //simple yes/no question stop ton confirm injection abort. Will send the ui
    //in the UI_STATE_END or back to UI_STATE_RUNNING
    case UI_STATE_CONFIRM_STOP:

       if(ui_old_state != UI_STATE_CONFIRM_STOP){
        lcd.clear();
        lcd.print("  CONFIRMEZ ARRET?");
        lcd.setCursor(1, 3);
        lcd.print("retour");
        lcd.setCursor(13, 3);
        lcd.print("arret");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //display item selection
      if(!ui_selection){
        lcd.setCursor(0, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(7, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
        lcd.setCursor(12, 3);
        lcd.print(" ");
        lcd.setCursor(19, 3);
        lcd.print(" ");
      }
      else{
        lcd.setCursor(0, 3);
        lcd.print(" ");
        lcd.setCursor(7, 3);
        lcd.print(" ");
        lcd.setCursor(12, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(19, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);

      }

      //handle the ui button press
      if((ui_btn_prev == 2) || (ui_btn_next == 2)){
        ui_state_count = 0;
        ui_selection = !ui_selection;
      }

      if(ui_btn_ok == 2){
        ui_state_count = 0;
        if(ui_selection){
          //storing the status to disable recover
          EEPROM.write(MEMORY_LAST_STATE_MSB, MOTOR_STATE_READY >> 8);
          EEPROM.write(MEMORY_LAST_STATE_LSB, MOTOR_STATE_READY & 0x00FF);
          ui_state = UI_STATE_END;
        }
        else{
          ui_state = UI_STATE_RUNNING;
        }
      }

     // menu timout
    if(ui_state_count > UI_MENU_TIMEOUT){
      ui_state = UI_STATE_RUNNING;
    }

    break;

    //this state is the end of the injection. It can be triggered by user input or by
    //the motor state machine if it reaches the end of the syringe
    //If user acknoledge the end of injection by pressing the OK button, th ui will go back to the READY state
    case UI_STATE_END:

       if(ui_old_state != UI_STATE_END){
        //send a stop message to the motor state machine
        motor_requested_state = MOTOR_STATE_END;
        lcd.clear();
        lcd.print("  Fin d'injection !");
        lcd.setCursor(0, 2);
        tmp = ui_current_injected_volume + ui_total_bolus_injected_volume;
        if(tmp < 100) lcd.print("0");
        lcd.print(tmp / 10);
        lcd.print(".");
        lcd.print(tmp % 10);
        lcd.setCursor(4, 2);
        lcd.print("ml en   :  :");
        h = (ui_elapsed_time / 3600);
        m = (ui_elapsed_time - (h*3600))/60;
        s = ui_elapsed_time - (h*3600) - (m*60);
        lcd.setCursor(10, 2);
        if(h<10) lcd.print("0");
        lcd.print(h);
        lcd.setCursor(13, 2);
        if(m<10) lcd.print("0");
        lcd.print(m);
        lcd.setCursor(16, 2);
        if(s<10) lcd.print("0");
        lcd.print(s);
        lcd.setCursor(0, 3);
        lcd.print("Dont");
        lcd.setCursor(5, 3);
        if(ui_total_bolus_injected_volume < 100) lcd.print("0");
        lcd.print(ui_total_bolus_injected_volume / 10);
        lcd.print(".");
        lcd.print(ui_total_bolus_injected_volume % 10);
        lcd.setCursor(9, 3);
        lcd.print("ml en bolus");
        digitalWrite(PIN_BUZZER, LOW);
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //handle the ui button press
      if(ui_btn_ok == 2){
        digitalWrite(PIN_BUZZER, HIGH);        
        ui_state = UI_STATE_READY;
      }

    break;

    //this step is simply displaying the main menu and allow the user to select
    //the next UI step (within the step menus of course)
    case UI_STATE_MAIN_MENU:

       if(ui_old_state != UI_STATE_MAIN_MENU){
        lcd.clear();
        lcd.print("   MENU PRINCIPAL");
        lcd.setCursor(0, 2);
        lcd.print(ui_menu_names[0]);
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //handle the ui button press
      if(ui_btn_prev == 2){
        if(ui_selection > 0){
          ui_selection --;
        }
        else{
          ui_selection = UI_MAIN_MENU_ITEMS;
        }
      }

      if(ui_btn_next == 2){
        if(ui_selection < UI_MAIN_MENU_ITEMS){
          ui_selection ++;
        }
        else{
          ui_selection =0;
        }
      }

      if((ui_btn_prev == 2) || (ui_btn_next == 2)){
        lcd.setCursor(0, 2);
        lcd.print("                    ");
        lcd.setCursor(0, 2);

        lcd.print(ui_menu_names[ui_selection]);
        ui_state_count=0;
      }

      if(ui_btn_ok == 2){
         ui_state_count = 0;
         if(ui_selection == UI_MAIN_MENU_ITEMS){
           ui_state = UI_STATE_READY;
         }
         else{
           ui_state = UI_STATE_MAIN_MENU + 1 + ui_selection;
         }
      }

      // 60 seconds timout
      if(ui_state_count > UI_MENU_TIMEOUT){
        ui_state = UI_STATE_READY;
      }

    break;

    //this menu state allows the user to move the syringe manually.
    //it swithes the motor state machine in a specific mode and wait for the user
    //to end the process using the ok button
    case UI_STATE_MENU_MANUAL:
       if(ui_old_state != UI_STATE_MENU_MANUAL){
        //send a start message to the motor state machine
        motor_requested_state = MOTOR_STATE_FREE_MOVE;
        lcd.clear();
        lcd.print("Deplacement chariot");
        lcd.setCursor(0, 3);
        lcd.print("btn ok pour quitter");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //handle button press
      if(ui_btn_ok == 2){
        ui_state_count = 0;
        ui_state = UI_STATE_MAIN_MENU;
        motor_requested_state = MOTOR_STATE_READY;
      }

      //just here to reset the timeout
      if((ui_btn_prev > 2) || (ui_btn_next > 2)) ui_state_count = 0;

      // menu timout
      if(ui_state_count > UI_MENU_TIMEOUT){
        ui_state = UI_STATE_MAIN_MENU;
        motor_requested_state = MOTOR_STATE_READY;
      }

    break;

    //this menu allows the user to set the injection speed in ml/h
    //this is the only menu that can be called while injecting.
    case UI_STATE_MENU_SPEED:
       if(ui_old_state != UI_STATE_MENU_SPEED){
        lcd.clear();
        lcd.print("Debit injection");
        lcd.setCursor(0, 2);
        if(current_injection_speed < 1000) lcd.print("0");
        if(current_injection_speed < 100) lcd.print("0");
        lcd.print(current_injection_speed / 10);
        lcd.print(".");
        lcd.print(current_injection_speed % 10);
        lcd.setCursor(6, 2);
        lcd.print("ml/h");
        ui_selection= current_injection_speed;
        ui_state_count=0;
        ui_old_state = ui_state;
      }


      //handle the ui buttons press
      if((ui_btn_prev == 2) || ((ui_btn_prev > 0) && !(ui_btn_prev % 2))){
        if((ui_selection > 100) && !(ui_selection % 100) && (ui_btn_prev > 100)){
          ui_selection -= 100;
        }
        else if((ui_selection > 10) && !(ui_selection % 10) && (ui_btn_prev > 50)){
          ui_selection -= 10;
        }
        else if(ui_selection > 1){
          ui_selection --;
        }

        lcd.setCursor(0, 2);
        lcd.print("   . ");
        lcd.setCursor(0, 2);
        if(ui_selection < 1000) lcd.print("0");
        if(ui_selection < 100) lcd.print("0");
        lcd.print(ui_selection / 10);
        lcd.print(".");
        lcd.print(ui_selection % 10);
        ui_state_count=0;
      }

      if((ui_btn_next == 2) || ((ui_btn_next > 0) && !(ui_btn_next % 2))){
        if((ui_selection >= 100) && !(ui_selection % 100) && (ui_btn_next > 100) && (ui_selection + 100 < CONFIG_MAX_INJECTION_SPEED)){
          ui_selection += 100;
        }
        else if((ui_selection >= 10) && !(ui_selection % 10) && (ui_btn_next > 50) && (ui_selection + 10 < CONFIG_MAX_INJECTION_SPEED)){
          ui_selection += 10;
        }
        else if(ui_selection < CONFIG_MAX_INJECTION_SPEED){
          ui_selection ++;
        }
        lcd.setCursor(0, 2);
        lcd.print("   ");
        lcd.setCursor(0, 2);
        if(ui_selection < 1000) lcd.print("0");
        if(ui_selection < 100) lcd.print("0");
        lcd.print(ui_selection / 10);
        lcd.print(".");
        lcd.print(ui_selection % 10);
        ui_state_count=0;
      }

      if(ui_btn_ok == 2){
        //save the new value
        current_injection_speed = ui_selection;
        ui_state = UI_STATE_MENU_CONFIRM_VALUE;
      }

      // menu timout
      if(ui_state_count > UI_MENU_TIMEOUT){
        //if we are injecting, go back to the running state
        if(motor_requested_state == MOTOR_STATE_INJECTING){
          ui_state = UI_STATE_RUNNING;
        }
        else{
          //if not, return to the main menu
          ui_state = UI_STATE_MAIN_MENU;
        }
      }

     break;

    //this menu allows the user to select a syringe from the list
    case UI_STATE_MENU_SYRINGE:
        if(ui_old_state != UI_STATE_MENU_SYRINGE){
        lcd.clear();
        lcd.print("Taille seringue");
        lcd.setCursor(0, 2);
        lcd.print(ui_syringe_list[current_syringe]);
        lcd.setCursor(4, 2);
        lcd.print("ml");
        ui_selection=current_syringe;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //handle the ui buttons press
      if((ui_btn_prev == 2) && (ui_selection > 0)){
        ui_selection --;
      }

      if((ui_btn_next == 2) && (ui_selection < UI_MAX_SYRINGE)){
        ui_selection ++;
      }

      if((ui_btn_prev == 2) || (ui_btn_next == 2)){
        lcd.setCursor(0, 2);
        lcd.print("   ");
        lcd.setCursor(0, 2);
        lcd.print(ui_syringe_list[ui_selection]);
        ui_state_count=0;
      }

       if(ui_btn_ok == 2){
        //save the new value
        current_syringe = ui_selection;
        ui_state = UI_STATE_MENU_CONFIRM_VALUE;
      }

     // menu timout
     if(ui_state_count > UI_MENU_TIMEOUT){
        ui_state = UI_STATE_MAIN_MENU;
     }

    break;

    //this menu allows the user to select an alarm time
    case UI_STATE_MENU_ALARM_TIME:
       if(ui_old_state != UI_STATE_MENU_ALARM_TIME){
        lcd.clear();
        lcd.print("Sonnerie fin inject");
        lcd.setCursor(0, 2);
        lcd.print(ui_remaining_time_alarm);
        lcd.setCursor(4, 2);
        lcd.print("min");
        ui_selection=ui_remaining_time_alarm;
        ui_state_count=0;
        ui_old_state = ui_state;
      }


      //handle the ui buttons press
      if((ui_btn_prev == 2) || ((ui_btn_prev > 0) && !(ui_btn_prev % 2))){

        if((ui_selection > 10) && !(ui_selection % 10) && (ui_btn_prev > 50)){
          ui_selection -= 10;
        }
        else if(ui_selection > 0){
          ui_selection --;
        }

        lcd.setCursor(0, 2);
        lcd.print("   ");
        lcd.setCursor(0, 2);
        lcd.print(ui_selection);
        ui_state_count=0;
      }

      if((ui_btn_next == 2) || ((ui_btn_next > 0) && !(ui_btn_next % 2))){

        if((ui_selection >= 10) && !(ui_selection % 10) && (ui_btn_next > 50) && (ui_selection + 10 < CONFIG_MAX_REMAINING_TIME_ALARM)){
          ui_selection += 10;
        }
        else if(ui_selection < CONFIG_MAX_REMAINING_TIME_ALARM){
          ui_selection ++;
        }
        lcd.setCursor(0, 2);
        lcd.print("   ");
        lcd.setCursor(0, 2);
        lcd.print(ui_selection);
        ui_state_count=0;
      }

      if(ui_btn_ok == 2){
        //save the new value
        ui_remaining_time_alarm = ui_selection;
        ui_state = UI_STATE_MENU_CONFIRM_VALUE;
      }

     // menu timout
     if(ui_state_count > UI_MENU_TIMEOUT){
        ui_state = UI_STATE_MAIN_MENU;
     }

    break;

    //this state is called after every menu that can change an important value.
    //it stores the values in the EEPROM and display a confirmation message
    case UI_STATE_MENU_CONFIRM_VALUE:
      if(ui_old_state != UI_STATE_MENU_CONFIRM_VALUE){
        //storing configuration values in eeprom
        EEPROM.write(MEMORY_SYRINGE_INDEX_MSB, current_syringe >> 8);
        EEPROM.write(MEMORY_SYRINGE_INDEX_LSB, current_syringe & 0x00FF);
        EEPROM.write(MEMORY_INJECTION_SPEED_MSB, current_injection_speed >> 8);
        EEPROM.write(MEMORY_INJECTION_SPEED_LSB, current_injection_speed & 0x00FF);
        EEPROM.write(MEMORY_REMAINING_TIME_ALARM_MSB,  ui_remaining_time_alarm >> 8);
        EEPROM.write(MEMORY_REMAINING_TIME_ALARM_LSB, ui_remaining_time_alarm & 0x00FF);

        lcd.setCursor(0, 0);
        lcd.print("VALEUR ENREGISTREE !");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      // 2 seconds timout
      if(ui_state_count > 20){
        //if we are injecting, go back to the running state
        if(motor_requested_state == MOTOR_STATE_INJECTING){
          //request a reload of the speed
          motor_requested_state = MOTOR_STATE_RELOAD;
          ui_state = UI_STATE_RUNNING;
        }
        else{
          //if not, return to the main menu
          ui_state = UI_STATE_MAIN_MENU;
        }
      }

    break;

    //this state is called if an aborted injection can be resumed
    //it provides the user with a yes/no question and waits indefinetly for the answer
    case UI_STATE_MENU_CONFIRM_RESUME:
      if(ui_old_state != UI_STATE_MENU_CONFIRM_RESUME){
        //make sure motor won't restart without authorization
        motor_requested_state = MOTOR_STATE_READY;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("REPRENDRE INJECTION?");
        lcd.setCursor(1, 3);
        lcd.print("non");
        lcd.setCursor(16, 3);
        lcd.print("oui");
        lcd.setCursor(7, 1);
        if(current_injection_speed < 1000) lcd.print("0");
        if(current_injection_speed < 100) lcd.print("0");
        lcd.print(current_injection_speed / 10);
        lcd.print(".");
        lcd.print(current_injection_speed % 10);
        lcd.setCursor(12, 1);
        lcd.print("ml/h");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //display item selection
      if(!ui_selection){
        lcd.setCursor(0, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(4, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);
        lcd.setCursor(15, 3);
        lcd.print(" ");
        lcd.setCursor(19, 3);
        lcd.print(" ");
      }
      else{
        lcd.setCursor(0, 3);
        lcd.print(" ");
        lcd.setCursor(4, 3);
        lcd.print(" ");
        lcd.setCursor(15, 3);
        lcd.write(UI_SELECT_CHAR_LEFT);
        lcd.setCursor(19, 3);
        lcd.write(UI_SELECT_CHAR_RIGHT);

      }

      //handle the ui buttons press
      if((ui_btn_prev == 2) || (ui_btn_next == 2)){

        ui_selection = !ui_selection;
      }

      if(ui_btn_ok == 2){

        if(!ui_selection){
          
          //storing the status to disable recover
          EEPROM.write(MEMORY_LAST_STATE_MSB, MOTOR_STATE_READY >> 8);
          EEPROM.write(MEMORY_LAST_STATE_LSB, MOTOR_STATE_READY & 0x00FF);
          
          //if we don't resume, ask the motor to go back to it's original position
          motor_requested_state = MOTOR_STATE_LOOK_FOR_SWITCH;
          
          ui_state = UI_STATE_END;
        }
        else{
          ui_state = UI_STATE_RUNNING;
        }
      }


    break;


    case UI_STATE_MENU_CALIBRATE:

    break;

    case UI_STATE_MENU_SELFTEST:
      if(ui_old_state != UI_STATE_MENU_SELFTEST){
        //send a start message to the motor state machine
        motor_requested_state = MOTOR_STATE_LOOK_FOR_SWITCH;

        lcd.clear();
        lcd.print("Selftest  . . .");
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      //if motor back to ready
      if((motor_state == MOTOR_STATE_READY) && (motor_requested_state != MOTOR_STATE_LOOK_FOR_SWITCH)){
        ui_state = UI_STATE_MAIN_MENU;
      }
      // 200 seconds timout
      if(ui_state_count > 2000){
        ui_state = UI_STATE_MAIN_MENU;
      }

    break;

    //this menu simply displays the welcome string and software version for two seconds
    //before returning to the MAIN_MENU state
    case UI_STATE_MENU_ABOUT:
      if(ui_old_state != UI_STATE_MENU_ABOUT){
        lcd.clear();
        lcd.print(UI_WELCOME_STRING_1);
        lcd.setCursor(0, 1);
        lcd.print(UI_WELCOME_STRING_2);
        lcd.setCursor(0, 2);
        lcd.print("Version : ");
        lcd.print(SOFTWARE_REVISION);
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      // 2 seconds timout
      if(ui_state_count > 20){
        ui_state = UI_STATE_MAIN_MENU;
      }

    break;

    //this state displays an alarm, require user acknoledge to dismiss
    case UI_STATE_ALARM:
      if(ui_old_state != UI_STATE_ALARM){
        lcd.clear();
        lcd.print("  === ATTENTION === ");
        if(motor_state == MOTOR_STATE_JAMMED){
          lcd.setCursor(0, 1);
          lcd.print("Blocage detecte ");
          lcd.setCursor(0, 2);
          lcd.print("Verifiez chariot et");
          lcd.setCursor(0, 3);
          lcd.print("appuyez sur OK");
        }
        else if(motor_state == MOTOR_STATE_OVERPRESSURE){
          lcd.setCursor(0, 1);
          lcd.print("Occlusion detectee");
          lcd.setCursor(0, 2);
          lcd.print("Verifiez circuit et");
          lcd.setCursor(0, 3);
          lcd.print("appuyez sur OK");
        }
        else if(motor_state == MOTOR_STATE_NO_SYRINGE){
          lcd.setCursor(0, 1);
          lcd.print("Seringue non trouvee");
          lcd.setCursor(0, 2);
          lcd.print("Verifiez position et");
          lcd.setCursor(0, 3);
          lcd.print("appuyez sur OK");
        }
        ui_selection=0;
        ui_state_count=0;
        ui_old_state = ui_state;
      }

      if(ui_blink){
        lcd.setCursor(0, 0);
        lcd.print("                    ");
        digitalWrite(PIN_BUZZER, HIGH);
      }
      else{
        lcd.setCursor(0, 0);
        lcd.print("  === ATTENTION === ");
        digitalWrite(PIN_BUZZER, LOW);
      }
      
      //handle buttons press
      if(ui_btn_ok == 2){
        digitalWrite(PIN_BUZZER, HIGH);

        //clear error by sending it back to the motor state machine
        motor_requested_state = MOTOR_STATE_ERROR;

        //trying to resume injection if needed
        if((((int)EEPROM.read(MEMORY_LAST_STATE_MSB) << 8) |  EEPROM.read(MEMORY_LAST_STATE_LSB)) == MOTOR_STATE_INJECTING){
          ui_state = UI_STATE_MENU_CONFIRM_RESUME;
        }
        else{
          ui_state = UI_STATE_READY;
        }
      }

    break;

    //this state is called if the hardware watchdog triggers.
    //users cant do anyting until the watchdog is reset.
    case UI_STATE_WATCHDOG:
      if(ui_old_state != UI_STATE_WATCHDOG){
        //we clear the motor request flag so it wont restart by itself
        motor_requested_state = MOTOR_STATE_READY;
        lcd.clear();
        lcd.print("  === ATTENTION === ");
        lcd.setCursor(0, 1);
        lcd.print("! SECURITE ACTIVE !");
        lcd.setCursor(0, 3);
        lcd.print("Appuyez sur acquitt");
        ui_state_count=0;
        ui_old_state = ui_state;
      }
      if(ui_blink){
        lcd.setCursor(0, 1);
        lcd.print("                    ");
      }
      else{
        lcd.setCursor(0, 1);
        lcd.print("! SECURITE ACTIVE !");
      }

      if(wd_debounce == 0){
        if((((int)EEPROM.read(MEMORY_LAST_STATE_MSB) << 8) |  EEPROM.read(MEMORY_LAST_STATE_LSB)) == MOTOR_STATE_INJECTING){
          ui_state = UI_STATE_MENU_CONFIRM_RESUME;
        }
        else{
          ui_state = UI_STATE_READY;
        }
      }

    break;

    //should never happen...
    default:
        lcd.clear();
        lcd.print("!! ETAT INCONU !!");
        lcd.setCursor(0, 1);
        lcd.print(ui_state);
        //suicide
        //while(1);

    break;
  }

  //if the watchdog is triggered we override the ui state
  if(wd_debounce > 2){
    ui_state = UI_STATE_WATCHDOG;
  }
  //if the motor is in error state, and this error is not ack, we override the ui to diplay a message
  else if((motor_state >= MOTOR_STATE_ERROR && motor_state < MOTOR_STATE_WATCHDOG) && (motor_requested_state != MOTOR_STATE_ERROR)){
    ui_state = UI_STATE_ALARM;
  }

  //simple counter that allows states to keep track of their active time
  ui_state_count++;

  //every second
  if(!(ui_state_count %10)){
      ui_blink = !ui_blink;
      if(ui_blink){
        digitalWrite(PIN_GREEN_LED, HIGH);
      }
      else if (motor_requested_state == MOTOR_STATE_INJECTING){
        digitalWrite(PIN_GREEN_LED, LOW);
      }
  }
  
  /* Motor state machine */
  //this state machine is where the real magic happens.
  //it uses the timer 1 to generate pulses depending on the needs.
  switch(motor_state){

    //this is the first state after boot. We make sur the motor is off and move on to the READY state
    case MOTOR_STATE_BOOTING:
      
      // initialize timer1
      TCCR1A = 0;
      TCCR1B = 0;

      TCNT1= 0;
      OCR1A = 0;

      TCCR1B |= (1 << CS11);    // /8 prescaler on timer 1 clock
      //stop the motor (to be sure)
      motor_stop();

      // enable timer compare interrupt
      TIMSK1 |= (1 << OCIE1A);

      //we try to load last known position from flash memory if the last known status was injecting
      if((((int)EEPROM.read(MEMORY_LAST_STATE_MSB) << 8) |  EEPROM.read(MEMORY_LAST_STATE_LSB)) == MOTOR_STATE_INJECTING){
        //here we do everything related to interupt changed variables
        noInterrupts();
        motor_steps = (((long)EEPROM.read(MEMORY_MOTOR_STEPS_B0) << 24) | ((long)EEPROM.read(MEMORY_MOTOR_STEPS_B1) << 16) | ((long)EEPROM.read(MEMORY_MOTOR_STEPS_B2) << 8) | (long)EEPROM.read(MEMORY_MOTOR_STEPS_B3));
        motor_injection_start_steps =((long)EEPROM.read(MEMORY_INJECTION_START_B0) << 24) | ((long)EEPROM.read(MEMORY_INJECTION_START_B1) << 16) | ((long)EEPROM.read(MEMORY_INJECTION_START_B2) << 8) | (long)EEPROM.read(MEMORY_INJECTION_START_B3);
        sensor_position_counter = ((int)EEPROM.read(MEMORY_SENSOR_POSITION_MSB) << 8) |  EEPROM.read(MEMORY_SENSOR_POSITION_LSB);
        //clear Jamming error
        sensor_last_motor_steps = motor_steps;
        interrupts();
        motor_state = MOTOR_STATE_READY;
      }
      else{
        motor_state = MOTOR_STATE_LOOK_FOR_SWITCH;
      }
      
    break;

    //this is the idle state, nothing much happening here, we wait for the
    //motor_requested_state to switch the motor to the INJECTING or FREE_MOVE state
    case MOTOR_STATE_READY:
      if(motor_old_state != MOTOR_STATE_READY){
        
        //stop the motor
        digitalWrite(PIN_MOTOR_ENABLE, LOW);
        digitalWrite(PIN_MOTOR_DIR, LOW);
        motor_dir = 0;
   
        //shut down the timer
        TCCR1A = 0;
        TCCR1B = 0;

        //if we were injecting, we store the state again to disable resume
        if(motor_old_state == MOTOR_STATE_INJECTING){
          //storing the status to recover
          EEPROM.write(MEMORY_LAST_STATE_MSB, motor_state >> 8);
          EEPROM.write(MEMORY_LAST_STATE_LSB, motor_state & 0x00FF);
        }
        //moving from jammed to ready means the LOOK FOR SWITCH STATE failed.
        else if(motor_old_state == MOTOR_STATE_JAMMED){
          //retry
          //motor_state = MOTOR_STATE_LOOK_FOR_SWITCH;
        }
        /*
        //if we are lost (we did not move, we are not on the switch an no pulses were registered), we recover
        else if((motor_steps <100) && (sensor_position_counter == 0) && (analogRead(PIN_CLUTCH_AND_END_SWITCHES) > END_SWITCH_TRESHOLD)){
          motor_state = MOTOR_STATE_LOOK_FOR_SWITCH;
        }
*/
        motor_old_state = motor_state;
      }

      
      //handle requests from the UI
      else if(motor_requested_state == MOTOR_STATE_INJECTING){
        //quick sanity checks before allowing motor movement
        if(current_injection_speed==0) break;
        //if we can resume
        if((((int)EEPROM.read(MEMORY_LAST_STATE_MSB) << 8) |  EEPROM.read(MEMORY_LAST_STATE_LSB)) == MOTOR_STATE_INJECTING){
          motor_state = MOTOR_STATE_INJECTING;
        }
        else{
          //else go to the start of the syringe
          motor_state = MOTOR_STATE_GOTO_SYRINGE_START;
        }
      }
      else if(motor_requested_state == MOTOR_STATE_FREE_MOVE){
        motor_state = MOTOR_STATE_FREE_MOVE;
      }
      else if(motor_requested_state == MOTOR_STATE_LOOK_FOR_SWITCH){
        motor_state = MOTOR_STATE_LOOK_FOR_SWITCH;
      }
    break;

    //In this state we generate pulses to slowly empty the syringe according to the configuration
    case MOTOR_STATE_INJECTING:       
      if(motor_old_state != MOTOR_STATE_INJECTING){
       
        //start the motor
        motor_play(current_injection_speed,config_syringe_step_per_ml[current_syringe]);
  
        //storing the status to recover
        EEPROM.write(MEMORY_LAST_STATE_MSB, motor_state >> 8);
        EEPROM.write(MEMORY_LAST_STATE_LSB, motor_state & 0x00FF);
        
        //storing the injection start position
        EEPROM.write(MEMORY_INJECTION_START_B0, motor_injection_start_steps >> 24);
        EEPROM.write(MEMORY_INJECTION_START_B1, motor_injection_start_steps >> 16);
        EEPROM.write(MEMORY_INJECTION_START_B2, motor_injection_start_steps >> 8);
        EEPROM.write(MEMORY_INJECTION_START_B3, motor_injection_start_steps & 0x000000FF);

        //reset the time counter
        ui_elapsed_time = 0;

        motor_old_state = motor_state;
      }

      //every four sensor pulses (approx 5mm), we backup the motor_steps and position_counter values. It may be a bit harsh for the flash, might change this later to improve memory life
      if(!(safe_sensor_position_counter % 4)){
        EEPROM.write(MEMORY_MOTOR_STEPS_B0, safe_motor_steps >> 24);
        EEPROM.write(MEMORY_MOTOR_STEPS_B1, safe_motor_steps >> 16);
        EEPROM.write(MEMORY_MOTOR_STEPS_B2, safe_motor_steps >> 8);
        EEPROM.write(MEMORY_MOTOR_STEPS_B3, safe_motor_steps & 0x000000FF);
        EEPROM.write(MEMORY_SENSOR_POSITION_MSB, safe_sensor_position_counter >> 8);
        EEPROM.write(MEMORY_SENSOR_POSITION_LSB, safe_sensor_position_counter & 0x00FF);
      }
      
      //calculate the current injected volume
      
      ui_current_injected_volume = (long)((((safe_motor_steps - motor_injection_start_steps)*10UL)/(32UL)) / config_syringe_step_per_ml[current_syringe]);

      //the injection time
      if(millis() >= (last_millis + 1000)){
        ui_elapsed_time++;
        last_millis = millis();
      }

      //and the remaining time 
      //we calculate remaining steps * timer compare value * prescaler to know the number of CPU tics before the end
      //then divide by the CPU frequency to get seconds while trying not to overflow a 32 bits variable...
      
      ui_remaining_time = (MOTOR_MAX_STEPS - safe_motor_steps)/8;
      ui_remaining_time = ui_remaining_time * OCR1A;
      ui_remaining_time = ui_remaining_time/1000000;
      ui_remaining_time = ui_remaining_time * motor_prescaler;
            
       //handle requests from the UI
      if(motor_requested_state == MOTOR_STATE_READY){
        //return to ready mode
        motor_state = MOTOR_STATE_READY;
      }
      if(motor_requested_state == MOTOR_STATE_END){
        //return syringe to waiting position
        motor_state = MOTOR_STATE_LOOK_FOR_SWITCH;
      }

      else if(motor_requested_state == MOTOR_STATE_BOLUS){
        motor_state = MOTOR_STATE_BOLUS;
      }
      else if(motor_requested_state == MOTOR_STATE_RELOAD){
        motor_state = MOTOR_STATE_RELOAD;
      }

    break;

    //this state allows the user to control de syringe and make high speed movements. Speed is set by
    // the CONFIG_MOTOR_FREE_MOVE_SPEED parameter
    case MOTOR_STATE_FREE_MOVE:
      if(motor_old_state != MOTOR_STATE_FREE_MOVE){

        //stop the motor
        motor_stop();
        motor_old_state = motor_state;
      }

      //handle requests from the UI
      if(ui_state == UI_STATE_MENU_MANUAL){
        if(ui_btn_prev == 2){
          //move left
          motor_rewind(CONFIG_MOTOR_FREE_MOVE_SPEED);
        }
        else if(ui_btn_next == 2){
          //move right
          motor_forward(CONFIG_MOTOR_FREE_MOVE_SPEED);
        }
        else if((ui_btn_prev == 0) && (ui_btn_next == 0)){
          //stop the motor
          motor_stop();
        }
      }
      else{
        //stop the motor
        motor_stop();
      }

      if(motor_requested_state == MOTOR_STATE_READY){
        //then go for it !
        motor_state = MOTOR_STATE_READY;
      }
    break;

    //this state is a mix between INJECTING and FREE MOVE : without any user action the motor will stay on its motion, user can only speed things up
    // to generate a bolus effect. Speed is set by the CONFIG_MOTOR_BOLUS_SPEED parameter
    case MOTOR_STATE_BOLUS:
      if(motor_old_state != MOTOR_STATE_BOLUS){
        //clear the current bolus injected volume when we enter the bolus mode
        ui_current_bolus_injected_volume = 0 ;
        //store the current motor position as the bolus start
        motor_bolus_start_steps = safe_motor_steps;
        motor_old_state = motor_state;
      }

      //handle requests from the UI
      if(ui_state == UI_STATE_BOLUS){
        if(ui_btn_next >= 2){
          motor_forward(CONFIG_BOLUS_SPEED);
        }
        else{
           //start the motor
           motor_play(current_injection_speed,config_syringe_step_per_ml[current_syringe]);
          }
      }

      
      //calculate the bolus injected volume
      ui_current_bolus_injected_volume = (long)((((safe_motor_steps - motor_bolus_start_steps)*10UL)/32UL) / config_syringe_step_per_ml[current_syringe]);

      if(motor_requested_state == MOTOR_STATE_INJECTING){
        //we add the current bolus to the total bolus for this session 
        ui_total_bolus_injected_volume += ui_current_bolus_injected_volume;
        //then go for it !
        motor_state = MOTOR_STATE_INJECTING;
      }
    break;

    //this state dynamically changes the injection speed
    case MOTOR_STATE_RELOAD:
      if(motor_old_state != MOTOR_STATE_RELOAD){
         motor_old_state = motor_state;
      }
        //restore the motor previous state
        motor_play(current_injection_speed,config_syringe_step_per_ml[current_syringe]);        
        //set the requested state back to injecting
        motor_requested_state = MOTOR_STATE_INJECTING;
        //then go back to injecting
        motor_state = MOTOR_STATE_INJECTING;
    break;


    //this state reset the motor position to 0 (wide open)
    // the CONFIG_MOTOR_AUTO_MOVE_SPEED parameter can change the speed of this movement
    case MOTOR_STATE_LOOK_FOR_SWITCH:
      if(motor_old_state != MOTOR_STATE_LOOK_FOR_SWITCH){
        //critical section as all those values are modified in interruption !
        noInterrupts();
        //reset the jamming protection
        sensor_last_motor_steps = motor_steps;
        interrupts();

        //move back
        motor_rewind(CONFIG_MOTOR_AUTO_MOVE_SPEED);
        
        motor_old_state = motor_state;
      }

      //wait for the end switch contact
      if(analogRead(PIN_CLUTCH_AND_END_SWITCHES) < END_SWITCH_TRESHOLD){
        //stop the motor
        motor_stop();
        
        //critical section as all those values are modified in interruption !
        noInterrupts();
        
        //reset all counters
        sensor_position_counter = 0;
        motor_steps = 0;
        sensor_last_motor_steps = 0;
        interrupts();
        
        //clear injected volumes
        ui_current_injected_volume = 0;
        ui_current_bolus_injected_volume=0;
        ui_total_bolus_injected_volume = 0;


        //TEMP : we clear the motor request flag so it wont restart by itself (useful when called from the UI menu)
        motor_requested_state = MOTOR_STATE_READY;
        //then go back to ready
        motor_state = MOTOR_STATE_READY;
      }
    break;

    //this state brings the motor to the theoretical start position of the syringe (if defined)
    // the CONFIG_MOTOR_AUTO_MOVE_SPEED parameter can change the speed of this movement
    case MOTOR_STATE_GOTO_SYRINGE_START:
      if(motor_old_state != MOTOR_STATE_GOTO_SYRINGE_START){
        if(config_syringe_start_pos[current_syringe] != 0){
          if(safe_sensor_position_counter < config_syringe_start_pos[current_syringe]){
            //move forward
            motor_forward(CONFIG_MOTOR_AUTO_MOVE_SPEED);
          }
          else{
            //something is clearly wrong, maybe a half full syringe ? maybe the user disengaged the drive and moved it ?
            //anyway we jump to the fine approach state
            motor_state = MOTOR_STATE_LOOK_FOR_SYRINGE;
            break;
          }
        }
        else{
          //if we dont know where to go, just stay there
          motor_state = MOTOR_STATE_LOOK_FOR_SYRINGE;
          break;
        }
        motor_old_state = motor_state;
      }

      //arriving or hitting the syringe
      //TEMPORARY
      if((safe_motor_steps >= 195360UL) || ((1024 - analogRead(PIN_PRESSURE_SENSOR) >= PRESSURE_SENSOR_SYRINGE_TRESHOLD))){
      //if(sensor_position_counter >= config_syringe_start_pos[current_syringe]){
        //stop the motor
        motor_stop();
        motor_state = MOTOR_STATE_LOOK_FOR_SYRINGE;
      }

    break;

    //this state brings the motor closer and closer to the syringe until the pressure sensor detects it
    // the CONFIG_MOTOR_SYRINGE_APPROACH_SPEED parameter can change the speed of this movement
    case MOTOR_STATE_LOOK_FOR_SYRINGE:
 
      if(motor_old_state != MOTOR_STATE_LOOK_FOR_SYRINGE){
        motor_old_state = motor_state;
        //move forward
        motor_forward(CONFIG_MOTOR_SYRINGE_APPROACH_SPEED);
      }

      //waiting for the syringe
      if((1024 - analogRead(PIN_PRESSURE_SENSOR) >= PRESSURE_SENSOR_SYRINGE_TRESHOLD)){

        motor_injection_start_steps = safe_motor_steps;
        
        //stop the motor
        motor_stop();
        motor_state = MOTOR_STATE_INJECTING;
      }

    break;

    //the motor hit the end of device limit
    case MOTOR_STATE_END:

      if(motor_old_state != MOTOR_STATE_END){

        //stop the motor
        motor_stop();

        motor_old_state = motor_state;
      }

      //the only thing the ui can do now is ask the motor to be ready again
     // if(motor_requested_state == MOTOR_STATE_READY){
        //then go for it !
        motor_state = MOTOR_STATE_LOOK_FOR_SWITCH;
     // }
    break;


    //this state is entered in case of errors and must be acknoledged by the ui
    case MOTOR_STATE_ERROR:
    case MOTOR_STATE_JAMMED:
    case MOTOR_STATE_OVERPRESSURE:
    case MOTOR_STATE_NO_SYRINGE:
      if(motor_old_state != motor_state){

        //stop the motor
        motor_stop();

        motor_old_state = motor_state;
      }

      //the only thing the ui can do now is acknoledge the error by changing the motor required state to the MOTOR_STATE_ERROR (which will disable the motor state override down below)
      //in this state we make sure we don't change the motor state so the state machine will simply return to its previous state once the override clears up.

      if(motor_requested_state == MOTOR_STATE_ERROR){
        //clear all errors
        //critical section as all those values are modified in interruption !
        noInterrupts();
        //clear Jamming error
        sensor_last_motor_steps = motor_steps;
        interrupts();

        //now request the motor to go back to the previous state
        motor_state = motor_interrupted_state;
        motor_interrupted_state = MOTOR_STATE_READY;

      }
      
    break;


    //this state is triggered by the watchdog timer. stops everything and go back to READY
    case MOTOR_STATE_WATCHDOG:
      if(motor_old_state != MOTOR_STATE_WATCHDOG){

        //we clear the motor request flag so it wont restart by itself
        motor_requested_state = MOTOR_STATE_READY;

        //stop the motor
        motor_stop();
        motor_old_state = motor_state;
      }

      //if watchdog reset, go back to previous state
      if(wd_debounce == 0){
        motor_state = motor_interrupted_state;
        motor_interrupted_state = MOTOR_STATE_READY;
      }

    break;

    //should never happen
    default:
      while(1); //suicide to trigger the watchdog
    break;
  }


  /* PRIORITY STATES */
  //here we averride all decisions made by the state machine if we have hardware errors
  //if the watchdog is triggered we override the motor state
  if(wd_debounce > 2){
    //on the first time we backup the previous state
    if(motor_state != MOTOR_STATE_WATCHDOG){
      motor_interrupted_state = motor_state;
    }
    motor_state = MOTOR_STATE_WATCHDOG;
  }
  //if no watchdog and the UI is not clearing errors, we check on other errors.
  else if(motor_requested_state != MOTOR_STATE_ERROR) {
    //checking the motor setp / sensor pulses consistency to detect any jamming
    if(abs(safe_motor_steps - safe_sensor_last_motor_steps) >= SENSOR_MAX_MOTOR_STEPS_PER_PULSE){
      //on the first time we backup the previous state
      if(motor_state != MOTOR_STATE_JAMMED){
        motor_interrupted_state = motor_state;
      }
      motor_state = MOTOR_STATE_JAMMED;
    }
    //checking the pressure for an overpressurized syringe
    else if((1024 - analogRead(PIN_PRESSURE_SENSOR) >= PRESSURE_SENSOR_ALARM_TRESHOLD)){
      //on the first time we backup the previous state
      if(motor_state != MOTOR_STATE_OVERPRESSURE){
        motor_interrupted_state = motor_state;
      }
      motor_state = MOTOR_STATE_OVERPRESSURE;
    }
    //checking end of syringe if moving and going forward
    else if((motor_prescaler) && (!motor_dir) && ((abs(safe_motor_steps)) >= MOTOR_MAX_STEPS)){// || (safe_sensor_position_counter >= SENSOR_MAX_VALUE)){
      motor_state = MOTOR_STATE_END;
    }
    
  }


  // Watchdog subroutine : should be called at least every second to prevent the hardware from going into safe mode
  wd_count=0;
  if(analogRead(PIN_WD_ADC)< WD_THRESHOLD_MIN)
  while(analogRead(PIN_WD_ADC) < WD_THRESHOLD_MAX){
      pinMode(PIN_WD_IO, OUTPUT);
      digitalWrite(PIN_WD_IO, HIGH);
      delay(1);
      wd_count ++;
  }
  pinMode(PIN_WD_IO, INPUT);

  //debug
  #ifdef DEBUG_VALUES
  Serial.print("motor_jamm = ");
  Serial.print(abs(motor_steps - sensor_last_motor_steps));
  Serial.print(" sensor = ");
  Serial.print(sensor_position_counter);
  Serial.print(" motor = ");
  Serial.print(motor_steps);
  Serial.print(" inj_start= ");
  Serial.println(motor_injection_start_steps);
  #endif
  
  #ifdef DEBUG_STATES
  Serial.print(" ui_state = ");
  Serial.print(ui_state, HEX);
  Serial.print(" motor_state = ");
  Serial.print(motor_state, HEX);
  Serial.print(" motor_interrupted_state = ");
  Serial.print(motor_interrupted_state, HEX);
  Serial.print(" motor_requested_state = ");
  Serial.println(motor_requested_state, HEX);
  #endif

  #ifdef DEBUG_PRESSURE
  Serial.print("pressure = ");
  Serial.println(1024 - analogRead(PIN_PRESSURE_SENSOR));
  #endif

  #ifdef DEBUG_TIME
  Serial.print("time = ");
  Serial.print(ui_elapsed_time);
  Serial.print(" alarm time (m)= ");
  Serial.print(ui_remaining_time_alarm);
  Serial.print(" remaining time = ");
  Serial.println(ui_remaining_time); 
  #endif

  #ifdef DEBUG_POWER
  Serial.print("Vmes battery = ");
  Serial.print(analogRead(PIN_PSU_VMES));
  Serial.print(" battery_state = ");
  Serial.println(ui_battery_state, HEX);

  #endif


  //wait between loops (I know, not the best thing to do but... might do better later)
  if(wd_count < 100) delay(100 - wd_count);
}
