/* 

FastAccelStepper Example: StepperDemo

https://wokwi.com/projects/411744818216451073

This has a serial command interpreter with several modes.

Try thse commands:
```
M1 H3000 A30000 P10000 M2 H1000 A10000 P10000
M1 P0 M2 P0
M1 h1000 A100 P200
```
From https://github.com/gin66/FastAccelStepper/tree/master/examples/StepperDemo

*/

#include "AVRStepperPins.h"
#include "FastAccelStepper.h"
#include "test_seq.h"

#ifdef SIM_TEST_INPUT
#include <avr/sleep.h>
#endif
#if defined(ARDUINO_ARCH_ESP32)
#include <esp_task_wdt.h>
#include <esp_log.h>
#endif

#define VERSION "post-4f2e1e4"

struct stepper_config_s {
  uint8_t step;
  uint8_t enable_low_active;
  uint8_t enable_high_active;
  uint8_t direction;
  uint16_t dir_change_delay;
  bool direction_high_count_up;
  bool auto_enable;
  uint32_t on_delay_us;
  uint16_t off_delay_ms;
};

#if defined(ARDUINO_ARCH_AVR)
#if (defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || \
     defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__))
// Example hardware configuration for Arduino Nano
// Please adapt to your configuration
const uint8_t led_pin = 13;  // turn off with PIN_UNDEFINED
const struct stepper_config_s stepper_config[MAX_STEPPER] = {
    {
      // stepper 1 shall be connected to OC1A
      step : stepPinStepper1A,
      enable_low_active : 6,
      enable_high_active : PIN_UNDEFINED,
      direction : 5,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500000,
      off_delay_ms : 5000
    },
    {
      // stepper 2 shall be connected to OC1B
      step : stepPinStepper1B,
      enable_low_active : 8,
      enable_high_active : PIN_UNDEFINED,
      direction : 7,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }};
#elif defined(__AVR_ATmega2560__)
// Example hardware configuration for Arduino ATmega2560
// Please adapt to your configuration
const uint8_t led_pin = PIN_UNDEFINED;  // turn off with PIN_UNDEFINED
const struct stepper_config_s stepper_config[MAX_STEPPER] = {
    {
      step : stepPinStepperA,
      enable_low_active : 19,
      enable_high_active : PIN_UNDEFINED,
      direction : 21,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500000,
      off_delay_ms : 5000
    },
    {
      step : stepPinStepperB,
      enable_low_active : 18,
      enable_high_active : PIN_UNDEFINED,
      direction : 20,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      // stepper 3 shall be connected to OC4C
      step : stepPinStepperC,
      enable_low_active : 43,
      enable_high_active : PIN_UNDEFINED,
      direction : 42,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }};
#elif defined(__AVR_ATmega32U4__)
// Example hardware configuration for Arduino ATmega32u4
// Please adapt to your configuration
const uint8_t led_pin = PIN_UNDEFINED;  // turn off with PIN_UNDEFINED
const struct stepper_config_s stepper_config[MAX_STEPPER] = {
    {
      step : stepPinStepperA,
      enable_low_active : 16,
      enable_high_active : PIN_UNDEFINED,
      direction : 26,  // PB4
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500000,
      off_delay_ms : 5000
    },
    {
      step : stepPinStepperB,
      enable_low_active : 15,
      enable_high_active : PIN_UNDEFINED,
      direction : 14,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }};
#endif
#elif defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM)
// Example hardware configuration for esp32 board.
// Please adapt to your configuration
#ifdef CONFIG_IDF_TARGET_ESP32C3
const uint8_t led_pin = 8;
#else
const uint8_t led_pin = PIN_UNDEFINED;
#endif
const struct stepper_config_s stepper_config[MAX_STEPPER] = {
// clang-format off
    // Test-HW
    // Position 01 linked to atmega nano
    // 2: Enable Left Pin 13 GPIO13   , DIR Right Pin 7 GPIO18,    Step Right Pin 13 GPIO15
    // 3: Enable Left Pin 12 GPIO12   , DIR Right Pin 6 GPIO19,    Step Right Pin 12 GPIO2  blue LED
    // 4: Enable Left Pin 11 GPIO14   , DIR Right Pin 5 GPIO21,    Step Right Pin 11 GPIO4
    // 5: Enable Left Pin 10 GPIO27   , DIR Right Pin 4 GPIO3 RX0, Step Right Pin 10 GPIO16 RX2
    // 6: Enable Left Pin 9  GPIO26 A9, DIR Right Pin 3 GPIO1 TX0, Step Right Pin 9  GPIO17 TX2
    // 7: Enable Left Pin 8  GPIO25 A8, DIR Right Pin 2 GPIO22,    Step Right Pin 8  GPIO5
    //                          ALL Enable: Right Pin 1 GPIO23
    // Left Pin 15: +5V
// clang-format on
#ifdef CONFIG_IDF_TARGET_ESP32C3
    {
      step : 6,
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : PIN_UNDEFINED,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 50,
      off_delay_ms : 1000
    },
    {
      step : 7,
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : PIN_UNDEFINED,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500,
      off_delay_ms : 1000
    }
#else
    {
      // position 01.234567 => 2
      step : 17,
      enable_low_active : 26,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,  // was GPIO 1 in conflict with TXD, via wire to Dir of
                       // next stepper
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 50,
      off_delay_ms : 1000
    },
    {
      // position 01.234567 => 3
      step : 15,
      enable_low_active : 13,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500,
      off_delay_ms : 1000
    }
#endif
#if MAX_STEPPER > 2
    ,
    {
      // position 01.234567 => 4, step is linked to blue LED
      step : 2,
      enable_low_active : 12,
      enable_high_active : PIN_UNDEFINED,
      direction : 19,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500,
      off_delay_ms : 1000
    },
    {
      // position 01.234567 => 5
      step : 5,
      enable_low_active : 25,
      enable_high_active : PIN_UNDEFINED,
      direction : 22,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }
#endif
#if MAX_STEPPER > 4
    ,
    {
      // position 01.234567 => 6
      step : 16,
      enable_low_active : 27,
      enable_high_active : PIN_UNDEFINED,
      direction : 21,  // was GPIO 3 in conflict with RXD, via wire to GPIO21
                       // (Dir next stepper)
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      // position 01.234567 => 7
      step : 4,
      enable_low_active : 14,
      enable_high_active : PIN_UNDEFINED,
      direction : 21,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }
#endif
#if MAX_STEPPER > 6
    ,
    {
      step : 14,  // direction pin of M3
      enable_low_active : 26,
      enable_high_active : PIN_UNDEFINED,
      direction : 19,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 23,  // ALL ENABLE PIN !!!!
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }
#endif
#if MAX_STEPPER == 14
    ,
    {
      step : 32,
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 33,
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 25,  // enable pin of M6
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 26,  // enable pin of M5
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 22,  // direction pin of M6
      enable_low_active : 26,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 21,  // direction pin of M3
      enable_low_active : PIN_UNDEFINED,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }
#endif
};
#elif defined(ARDUINO_ARCH_SAM)
// Hardware configuration copied from esp32 board. Not used on due board
// Please adapt to your configuration
const uint8_t led_pin = PIN_UNDEFINED;
const struct stepper_config_s stepper_config[MAX_STEPPER] = {
    {
      step : 17,
      enable_low_active : 26,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,  // was GPIO 1 in conflict with TXD, via wire to Dir of
                       // next stepper
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 50,
      off_delay_ms : 1000
    },
    {
      step : 15,
      enable_low_active : 13,
      enable_high_active : PIN_UNDEFINED,
      direction : 18,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500,
      off_delay_ms : 1000
    },
    {
      step : 2,
      enable_low_active : 12,
      enable_high_active : PIN_UNDEFINED,
      direction : 19,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 500,
      off_delay_ms : 1000
    },
    {
      step : 5,
      enable_low_active : 25,
      enable_high_active : PIN_UNDEFINED,
      direction : 22,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 16,
      enable_low_active : 27,
      enable_high_active : PIN_UNDEFINED,
      direction : 21,  // was GPIO 3 in conflict with RXD, via wire to GPIO21
                       // (Dir next stepper)
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    },
    {
      step : 4,
      enable_low_active : 14,
      enable_high_active : PIN_UNDEFINED,
      direction : 21,
      dir_change_delay : 0,
      direction_high_count_up : true,
      auto_enable : true,
      on_delay_us : 5000,
      off_delay_ms : 10
    }};
#endif

FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper[MAX_STEPPER];

enum { normal, test, config } mode = normal;
bool test_ongoing = false;
struct test_seq_s test_seq[MAX_STEPPER];

// Forward declaration
void usage();
void output_info(bool only_running);

#define _NL_ "\n"
#define _SEP_ "|"
#define _SEP_CHAR_ '|'

// clang-format off
const static char messages[] PROGMEM =
#define _Move_ "\200"
    "Move " _SEP_
#define _Output_driver_ "\201"
    "Output driver " _SEP_
#define _Cannot_set_ "\202"
    "Cannot set " _SEP_
#define _pin_ "\203"
    "pin " _SEP_
#define _step "\204"
    "step" _SEP_
#define _to_ "\205"
    "to " _SEP_
#define _LOW_ "\206"
    "LOW " _SEP_
#define _HIGH_ "\207"
    "HIGH " _SEP_
#define _to_LOW_nl "\210"
     _to_  _LOW_ _NL_ _SEP_
#define _to_HIGH_nl "\211"
     _to_  _HIGH_ _NL_ _SEP_
#define _Toggle_ "\212"
    "Toggle " _SEP_
#define _ERROR_ "\213"
    "ERROR " _SEP_
#define _high_counts_ "\214"
    "high counts " _SEP_
#define _enable_ "\215"
    "enable " _SEP_
#define _disable "\216"
    "disable" _SEP_
#define _direction_ "\217"
    "direction " _SEP_
#define _pulse_counter_ "\220"
    "pulse counter " _SEP_
#define _attach "\221"
    "attach" _SEP_
#define _set_ "\222"
    "set " _SEP_
#define _time_to_ "\223"
    "time" _to_ _SEP_
#define _is_not_defined_nl "\224"
    "is not defined" _NL_ _SEP_
#define _run_ "\225"
    "run " _SEP_
#define _forward "\226"
    "forward" _SEP_
#define _backward "\227"
    "backward" _SEP_
#define _Stepped_ "\230"
    "Stepped " _SEP_
#define _acceleration_ "\231"
    "acceleration " _SEP_
#define _speed_ "\232"
    "speed " _SEP_
#define _test_ "\233"
    "test " _SEP_
#define _erroneous_ "\234"
    "erroneous " _SEP_
#define _digitalRead_ "\235"
    "digitalRead() " _SEP_
#define ____ "\236"
    "    " _SEP_
#define ________ "\237"
	____ ____  _SEP_
#define _ooo_ "\240"
    " ... " _SEP_
#define _Enter_ "\241"
    "Enter " _SEP_
#define _mode "\242"
    "mode" _SEP_
#define _clear_ "\243"
    "clear " _SEP_
#define _stepper "\244"
    _step "per" _SEP_
#define _select "\245"
    "select" _SEP_
#define _selected_stepper "\246"
    _select "ed " _stepper _SEP_
#define _usage_ "\247"
    "usage " _SEP_
#define _steps_ "\250"
    _step "s " _SEP_
#define _output_ "\251"
    " output " _SEP_
#define _test_sequence_ "\252"
    _test_ "sequence " _SEP_
#define _Perform_ "\253"
    "Perform " _SEP_
#define _configuration_ "\254"
    "configuration " _SEP_
#define _delay_ "\255"
    "delay " _SEP_
#define _step_pin_ "\256"
    _step " " _pin_ _SEP_
#define _one_step_ "\257"
    "one " _step " " _SEP_
#define _position_ "\260"
    "position " _SEP_
#define __of_the_ "\261"
    " of the " _SEP_
#define _Turn_ "\262"
    "Turn " _SEP_
#define _stop "\263"
    "stop"  _SEP_
#define _from_ "\264"
    "from "  _SEP_
#define _comma__counting_ "\265"
    ", counting "  _SEP_
#define __disable_auto_enable_nl "\266"
    " " _disable " auto " _enable_ _NL_ _SEP_
#define _ooo_set_selected_stepper_s_ "\267"
   _ooo_ _set_ _selected_stepper "'s " _SEP_
#define _m1_m2_to_select_stepper_ "\270"
    ____ "M1/M2/.. " _ooo_ _to_ _select " " _stepper _NL_ _SEP_
#define _print_this_usage_ "\271"
    ____ "?" ________ _ooo_ "Print this usage" _NL_ _NL_ _SEP_
#define _toggle_print_usage_after_stepper_stop "\272"
    ____ "Q" ________ _ooo_ _Toggle_ "print " _usage_ "on " _stepper " " _stop _NL_ _SEP_
#define _I_speed_I_ "\273"
    "<speed> "  _SEP_
#define _I_accel_I_ "\274"
    "<accel> "  _SEP_
#define _Enter_command_separated_by_space_carriage_return_or_newline_NL "\275"
    _Enter_ "commands separated by space, carriage return or newline:" _NL_ _SEP_
#define MSG_OFFSET 62
#define MSG_SELECT_STEPPER 0+MSG_OFFSET
    "Select " _stepper " " _SEP_
#define MSG_TOGGLE_MOTOR_INFO 1+MSG_OFFSET
    _Toggle_ _stepper " info" _NL_ _SEP_
#define MSG_TOGGLE_USAGE_INFO 2+MSG_OFFSET
    _Toggle_  _usage_ "info" _NL_ _SEP_
#define MSG_ENTER_TEST_MODE 3+MSG_OFFSET
    _Enter_ _test_ _mode _NL_ _SEP_
#define MSG_SET_ACCELERATION_TO 4+MSG_OFFSET
    _set_ _acceleration_ _to_ _SEP_
#define MSG_SET_SPEED_TO_US 5+MSG_OFFSET
    _set_ _speed_ "(us/" _step ") " _to_ _SEP_
#define MSG_MOVE_STEPS 6+MSG_OFFSET
    _Move_ _steps_ _SEP_
#define MSG_MOVE_TO_POSITION 7+MSG_OFFSET
    _Move_ _to_ _position_ _SEP_
#define MSG_RETURN_CODE 8+MSG_OFFSET
    "returncode = " _SEP_
#define MSG_SET_POSITION 9+MSG_OFFSET
    _set_ _position_ _SEP_
#define MSG_SET_ENABLE_TIME 10+MSG_OFFSET
    _set_ _enable_ _time_to_ _SEP_
#define MSG_SET_DISABLE_TIME 11+MSG_OFFSET
    _set_ _disable " " _time_to_ _SEP_
#define MSG_OUTPUT_DRIVER_ON 12+MSG_OFFSET
    _Output_driver_ "on" _NL_ _SEP_
#define MSG_OUTPUT_DRIVER_OFF 13+MSG_OFFSET
    _Output_driver_ "off" _NL_ _SEP_
#define MSG_OUTPUT_DRIVER_AUTO 14+MSG_OFFSET
    _Output_driver_ "automatic " _mode _NL_ _SEP_
#define MSG_STOP 15+MSG_OFFSET
    _stop _NL_ _SEP_
#define MSG_KEEP_RUNNING 16+MSG_OFFSET
    "Keep running" _NL_ _SEP_
#define MSG_RUN_FORWARD 17+MSG_OFFSET
    _run_ _forward _NL_ _SEP_
#define MSG_RUN_BACKWARD 18+MSG_OFFSET
    _run_ _backward _NL_ _SEP_
#define MSG_IMMEDIATE_STOP 19+MSG_OFFSET
    "immediate " _stop _NL_ _SEP_
#define MSG_UPDATE_SPEED_ACCELERATION 20+MSG_OFFSET
    "Update " _speed_ " / " _acceleration_ _NL_ _SEP_
#define MSG_BLOCKING_WAIT 21+MSG_OFFSET
    "Blocking wait for running " _stepper " " _to_ _stop _NL_ _SEP_
#define MSG_TEST_DIRECT_DRIVE 22+MSG_OFFSET
    _test_ "direct drive" _NL_ _SEP_
#define MSG_STEPPED_FORWARD 23+MSG_OFFSET
    _Stepped_ _forward _NL_ _SEP_
#define MSG_STEPPED_BACKWARD 24+MSG_OFFSET
    _Stepped_ _backward _NL_ _SEP_
#define MSG_WAIT_MS 25+MSG_OFFSET
    " ms wait" _NL_ _SEP_
#define MSG_SELECT_TEST_SEQUENCE 26+MSG_OFFSET
    "Select " _test_ "sequence: " _SEP_
#define MSG_EXIT_TO_MAIN_MENU 27+MSG_OFFSET
    "Exit " _to_ "main menu" _NL_ _SEP_
#define MSG_RUN_TESTS 28+MSG_OFFSET
    _run_ _test_ _NL_ _SEP_
#define MSG_WAIT_COMPLETE 29+MSG_OFFSET
    "Wait complete" _NL_ _SEP_
#define MSG_FAILED_STATUS 30+MSG_OFFSET
    "Failed status " _from_ _test_ _NL_ _SEP_
#define MSG_ENABLE_LOW_PIN_IS_NOT_LOW 31+MSG_OFFSET
    _Cannot_set_ _enable_  _LOW_  _pin_ _to_LOW_nl _SEP_
#define MSG_ENABLE_LOW_PIN_IS_NOT_HIGH 32+MSG_OFFSET
    _Cannot_set_ _enable_  _LOW_  _pin_ _to_HIGH_nl _SEP_
#define MSG_ENABLE_HIGH_PIN_IS_NOT_LOW 33+MSG_OFFSET
    _Cannot_set_ _enable_  _HIGH_  _pin_ _to_LOW_nl _SEP_
#define MSG_ENABLE_HIGH_PIN_IS_NOT_HIGH 34+MSG_OFFSET
    _Cannot_set_ _enable_  _HIGH_  _pin_ _to_HIGH_nl _SEP_
#define MSG_STEP_PIN_IS_NOT_LOW 35+MSG_OFFSET
    _Cannot_set_ _step_pin_ _to_LOW_nl _SEP_
#define MSG_STEP_PIN_IS_NOT_HIGH 36+MSG_OFFSET
    _Cannot_set_ _step_pin_ _to_HIGH_nl _SEP_
#define MSG_CANNOT_SET_DIRECTION_PIN 37+MSG_OFFSET
    _Cannot_set_ _direction_ _pin_ _to_ _SEP_
#define MSG_STEPPER_VERSION 38+MSG_OFFSET
    "StepperDemo Version " VERSION
    _NL_ _SEP_
#define MSG_ATTACH_PULSE_COUNTER 39+MSG_OFFSET
    _attach " " _pulse_counter_ _SEP_
#define MSG_ERROR_ATTACH_PULSE_COUNTER 40+MSG_OFFSET
    _ERROR_ _attach "ing "  _pulse_counter_ _NL_ _SEP_
#define MSG_ERROR_INVALID_VALUE 41+MSG_OFFSET
    _ERROR_ "invalid value" _NL_ _SEP_
#define MSG_ERROR_MOVE_ERR_ACCELERATION_IS_UNDEFINED__MINUS_3 42+MSG_OFFSET
    _ERROR_ _acceleration_ _is_not_defined_nl _SEP_
#define MSG_ERROR_MOVE_ERR_SPEED_IS_UNDEFINED__MINUS_2 43+MSG_OFFSET
    _ERROR_ _speed_ _is_not_defined_nl _SEP_
#define MSG_ERROR_MOVE_ERR_NO_DIRECTION_PIN__MINUS_1 44+MSG_OFFSET
    _ERROR_ "no " _direction_  _pin_ "=> impossible move" _NL_ _SEP_
#define MSG_MOVE_OK 45+MSG_OFFSET
    "OK" _NL_ _SEP_
#define MSG_STRAY_DIGITAL_READ_TOGGLE 46+MSG_OFFSET
    _Toggle_ _erroneous_ _digitalRead_ _to_ _step_pin_ _NL_ _SEP_
#define MSG_STRAY_DIGITAL_READ_ENABLED 47+MSG_OFFSET
    _erroneous_ _digitalRead_ _to_ _step_pin_ "IS ON !!!" _NL_ _SEP_
#define MSG_LONG_INTERRUPT_BLOCK_TOGGLE 48+MSG_OFFSET
    _Toggle_ _erroneous_ "100 µs ISR block" _NL_ _SEP_
#define MSG_LONG_INTERRUPT_BLOCK_ENABLED 49+MSG_OFFSET
    _erroneous_ "100 µs ISR BLOCK IS ON" _NL_ _SEP_
#define MSG_SET_UNIDIRECTIONAL_STEPPER 50+MSG_OFFSET
    _set_ "unidirectional " _stepper _NL_ _SEP_
#define MSG_CLEAR_PULSE_COUNTER 51+MSG_OFFSET
    _clear_ _pulse_counter_ _NL_ _SEP_
#define MSG_SET_SPEED_TO_HZ 52+MSG_OFFSET
    _set_ _speed_ "(" _steps_ "/s) " _to_ _SEP_
#define MSG_PASS_STATUS 53+MSG_OFFSET
    _test_ "passed" _NL_ _SEP_
#define MSG_TEST_COMPLETED 54+MSG_OFFSET
    _test_ "completed" _NL_ _SEP_
#define MSG_ENTER_CONFIG_MODE 55+MSG_OFFSET
    _Enter_ "config " _mode _NL_ _SEP_
#define MSG_DIRECTION_PIN 56+MSG_OFFSET
    _direction_ _pin_ _SEP_
#define MSG_SET_TO_PIN 57+MSG_OFFSET
    _set_  _to_  _pin_ _SEP_
#define MSG_DISABLED 58+MSG_OFFSET
    _disable "d" _NL_ _SEP_
#define MSG_HIGH_COUNT_UP 59+MSG_OFFSET
    _high_counts_ "up" _SEP_
#define MSG_HIGH_COUNT_DOWN 60+MSG_OFFSET
    _high_counts_ "down" _SEP_
#define MSG_DELAY 61+MSG_OFFSET
    _delay_ "in us = " _SEP_
#define MSG_UNKNOWN_COMMAND 62 + MSG_OFFSET
    "Unknown command: " _SEP_
#define MSG_SET_SPEED_TO_MILLI_HZ 63+MSG_OFFSET
    _set_ _speed_ "(" _steps_ "/1000s) " _to_ _SEP_
#define MSG_ACCELERATION_STATUS 64+MSG_OFFSET
     " " _acceleration_ " [" _step "s/s^2]=" _SEP_
#define MSG_SPEED_STATUS_FREQ 65+MSG_OFFSET
     " " _speed_ "[m" _step "/s]=" _SEP_
#define MSG_SPEED_STATUS_TIME 66+MSG_OFFSET
     " " _speed_ "[us/" _step "]=" _SEP_
#define MSG_LINEAR_ACCELERATION 67+MSG_OFFSET
     "linear " _acceleration_ _step "s=" _SEP_
#define MSG_JUMP_START 68+MSG_OFFSET
     "jump start " _step "s=" _SEP_
#define MSG_USAGE_NORMAL 69+MSG_OFFSET
#define MSG_USAGE_TEST 70+MSG_OFFSET
#define MSG_USAGE_CONFIG 71+MSG_OFFSET
#if MSG_USAGE_CONFIG >= 128+32
#error "TOO MANY ENTRIES"
#endif
    /* USAGE NORMAL */
    _Enter_command_separated_by_space_carriage_return_or_newline_NL
	_m1_m2_to_select_stepper_
    ____ "c" ________ _ooo_ _Enter_ _configuration_ _mode _NL_
    ____ "V" _I_speed_I_  _ooo_set_selected_stepper_s_ _speed_ "in us/" _step _NL_
    ____ "H" _I_speed_I_  _ooo_set_selected_stepper_s_ _speed_ "in " _steps_ "/s" _NL_
    ____ "h" _I_speed_I_  _ooo_set_selected_stepper_s_ _speed_ "in " _steps_ "/1000s" _NL_
    ____ "A" _I_accel_I_  _ooo_set_selected_stepper_s_ _acceleration_ _NL_
    ____ "a" _I_accel_I_  _ooo_ _acceleration_ "control with +/-" _acceleration_ "values" _NL_
    ____ "J<" _step "s> " _ooo_set_selected_stepper_s_ "linear " _acceleration_  " for <" _step "s>" _NL_
    ____ "j<" _step "s> " _ooo_set_selected_stepper_s_ "jump start to <" _step "s>" _NL_
    ____ "U" ________ _ooo_ "Update " _selected_stepper "'s " _speed_ "/ " _acceleration_ "while "
    "running" _NL_
    ____ "P<pos>   " _ooo_ _Move_ _selected_stepper " " _to_ "+/- " _position_ _NL_
    ____ "R<n> " ____ _ooo_ _Move_ _selected_stepper " by +/- n " _steps_ _NL_
    ____ "f" ________ _ooo_ _run_ _forward _comma__counting_ "up" _NL_
    ____ "b" ________ _ooo_ _run_ _backward _comma__counting_ "down" _NL_
    ____ "K" ________ _ooo_ "Keep " _selected_stepper " running in current " _direction_ _NL_
    ____ "@<pos>   " _ooo_ _set_ _selected_stepper " " _to_ _position_ "(can be "
    "negative)" _NL_
    ____ "E<us>" ____ _ooo_set_selected_stepper_s_ _delay_ _from_ _enable_ _to_ _steps_ _NL_
    ____ "D<ms>" ____ _ooo_set_selected_stepper_s_ _delay_ _from_ _steps_ _to_ _disable _NL_
    ____ "N" ________ _ooo_ _Turn_ _selected_stepper _output_ "on, " __disable_auto_enable_nl
    ____ "F" ________ _ooo_ _Turn_ _selected_stepper _output_ "off," __disable_auto_enable_nl
    ____ "O" ________ _ooo_ "Put " _selected_stepper " into auto " _enable_ _mode _NL_
    ____ "S" ________ _ooo_ _stop " " _selected_stepper " with deceleration" _NL_
    ____ "X" ________ _ooo_ "Immediately " _stop " " _stepper " and " _set_ "zero position" _NL_
    ____ "I" ________ _ooo_ _Toggle_ _stepper " info, while any " _stepper " is running" _NL_
#if !defined(__AVR_ATmega32U4__)
    ____ "W" ________ _ooo_ "Blocking wait until " _selected_stepper " is " _stop "ped, will "
    "deadlock if " _stepper " never " _stop "s" _NL_
    ____ "w<ms>" ____ _ooo_ "Wait time in ms" _NL_
    ____ "+" ________ _ooo_ _Perform_ _one_step_ _forward __of_the_ _selected_stepper _NL_
    ____ "-" ________ _ooo_ _Perform_ _one_step_ _backward __of_the_ _selected_stepper _NL_
    ____ "T" ________ _ooo_ _test_  _select "ed " _stepper " with direct port access" _NL_
#endif
#if defined(ARDUINO_ARCH_ESP32)
    ____ "r" ________ _ooo_ "Call ESP.restart()" _NL_
    ____ "reset" ____ _ooo_ _Perform_ "reset" _NL_
    ____ "p<n> " ____ _ooo_ _attach " " _pulse_counter_ "n<=7" _NL_
    ____ "p<n>,l,h " _ooo_ _attach " " _pulse_counter_ "n<=7 with low,high limits" _NL_
    ____ "pc   " ____ _ooo_ _clear_ _pulse_counter_ _NL_
#endif
    ____ "t" ________ _ooo_ _Enter_ _test_ _mode _NL_
    ____ "u" ________ _ooo_ "Unidirectional " _mode " (need reset " _to_ "restore)" _NL_
#if defined(ARDUINO_ARCH_AVR)
    ____ "r" ________ _ooo_ _Toggle_ _erroneous_ _digitalRead_ __of_the_ _stepper " pin" _NL_
#endif
    ____ "e" ________ _ooo_ _Toggle_ _erroneous_ "long 100us interrupt block" _NL_
    _toggle_print_usage_after_stepper_stop
	_print_this_usage_
    _SEP_


    /* USAGE TEST */
#if !defined(SIM_TEST_INPUT)
    _Enter_command_separated_by_space_carriage_return_or_newline_NL
	_m1_m2_to_select_stepper_
    ____ "R" ________ _ooo_ "start all " _select "ed tests" _NL_
    ____ "I" ________ _ooo_ _Toggle_ _stepper " info, while " _test_sequence_ "is running" _NL_
    ____ "01   " ____ _ooo_ _select " " _test_sequence_ "01 for " _selected_stepper _NL_
    ____ ":" _NL_
    ____ "13   " ____ _ooo_ _select " " _test_sequence_ "13 for " _selected_stepper _NL_
    ____ "W" ________ _ooo_ "Blocking wait until test is finished" _NL_
    ____ "x" ________ _ooo_ "Exit test mode" _NL_
    _toggle_print_usage_after_stepper_stop
	_print_this_usage_
#endif
    _SEP_


    /* USAGE CONFIG */
#if !defined(SIM_TEST_INPUT) && !defined(__AVR_ATmega32U4__)
    _Enter_command_separated_by_space_carriage_return_or_newline_NL
	_m1_m2_to_select_stepper_
    ____ "d<p> " ____ _ooo_ _set_ _direction_ _pin_ _NL_
    ____ "d<p,n>" _NL_
    ____ "d<p,n,t>" _NL_
    ________ ________ ________ "p" _ooo_ _pin_ "number" _NL_
    ________ ________ ________ "n" _ooo_ "1: " _high_counts_ "up 0: " _high_counts_ "down" _NL_
    ________ ________ ________ "t" _ooo_ _delay_ _from_ "dir change " _to_ "step in us, 0 means "
    "off" _NL_
    ____ "dc   " ____ _ooo_ _clear_ _direction_ _pin_ "(unidirectional)" _NL_
    ____ "x" ________ _ooo_ "Exit " _configuration_ _mode _NL_
	_print_this_usage_
#endif
    _SEP_
;
// clang-format on

// The preprocessor for .ino is buggy
#include "generic.h"

void output_msg(uint8_t x) {
  char ch;
  MSG_TYPE p = messages;
  while (x > 0) {
    ch = get_char(p);
    p++;
    if (ch == _SEP_CHAR_) {
      x--;
    }
  }
  for (;;) {
    ch = get_char(p);
    p++;
    if (ch == _SEP_CHAR_) {
      break;
    }
    uint8_t y = (uint8_t)(ch ^ 0x80);
    if (y <= MSG_USAGE_CONFIG) {
      output_msg(y);
    } else {
      PRINTCH(ch);
    }
  }
}

#if !defined(__AVR_ATmega32U4__)
void delay10us() { DELAY_US(10); }
void do3200Steps(uint8_t step) {
  for (uint16_t i = 0; i < 3200; i++) {
    digitalWrite(step, HIGH);
    delay10us();
    if (digitalRead(step) != HIGH) {
      output_msg(MSG_STEP_PIN_IS_NOT_HIGH);
    }
    digitalWrite(step, LOW);
    DELAY_US(190);
    if (digitalRead(step) != LOW) {
      output_msg(MSG_STEP_PIN_IS_NOT_LOW);
    }
  }
}
void setDirectionPin(uint8_t direction, bool polarity) {
  if (direction != PIN_UNDEFINED) {
    digitalWrite(direction, polarity);
    pinMode(direction, OUTPUT);
    delay10us();
    if (digitalRead(direction) != polarity) {
      output_msg(MSG_CANNOT_SET_DIRECTION_PIN);
      PRINTLN(polarity ? "HIGH" : "LOW");
    }
  }
}
void test_direct_drive(const struct stepper_config_s *stepper) {
  // Check stepper motor+driver is operational
  // This is not done via FastAccelStepper-Library for test purpose only
  uint8_t step = stepper->step;
  uint8_t enableLow = stepper->enable_low_active;
  uint8_t enableHigh = stepper->enable_high_active;
  uint8_t direction = stepper->direction;
  bool direction_high_count_up = stepper->direction_high_count_up;

  pinMode(step, OUTPUT);

  if (enableLow != PIN_UNDEFINED) {
    digitalWrite(enableLow, LOW);
    pinMode(enableLow, OUTPUT);
    delay10us();
    if (digitalRead(enableLow) != LOW) {
      output_msg(MSG_ENABLE_LOW_PIN_IS_NOT_LOW);
    }
  }
  if (enableHigh != PIN_UNDEFINED) {
    digitalWrite(enableHigh, HIGH);
    pinMode(enableHigh, OUTPUT);
    delay10us();
    if (digitalRead(enableHigh) != HIGH) {
      output_msg(MSG_ENABLE_HIGH_PIN_IS_NOT_HIGH);
    }
  }
  if (direction != PIN_UNDEFINED) {
    setDirectionPin(direction, direction_high_count_up);
    do3200Steps(step);
    setDirectionPin(direction, !direction_high_count_up);
  }
  do3200Steps(step);
  if (enableLow != PIN_UNDEFINED) {
    digitalWrite(enableLow, HIGH);
    delay10us();
    if (digitalRead(enableLow) != HIGH) {
      output_msg(MSG_ENABLE_LOW_PIN_IS_NOT_HIGH);
    }
  }
  if (enableHigh != PIN_UNDEFINED) {
    digitalWrite(enableHigh, LOW);
    delay10us();
    if (digitalRead(enableHigh) != LOW) {
      output_msg(MSG_ENABLE_HIGH_PIN_IS_NOT_LOW);
    }
  }
  // Done
}
#endif

void setup() {
  PRINT_INIT();
#if defined(ARDUINO_ARCH_ESP32)
  printf("LOG start\n");
  esp_log_level_set("*", ESP_LOG_INFO);
  esp_log_level_set("rmt", ESP_LOG_INFO);
  ESP_LOGI("StepperDemo", "Started INFO");
  ESP_LOGE("*", "Started ERROR");
  PRINTLN("");
#endif
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM)
  PRINT("ESP-IDF: ");
  PRINTI16((int16_t)ESP_IDF_VERSION_MAJOR);
  PRINTCH('.');
  PRINTI16((int16_t)ESP_IDF_VERSION_MINOR);
  PRINTLN("");
#endif

  for (uint8_t i = 0; i < MAX_STEPPER; i++) {
    test_seq[i].test = NULL;
  }

  output_msg(MSG_STEPPER_VERSION);
#ifdef F_CPU
  PRINT("    F_CPU=");
  PRINTU32(F_CPU);
  PRINTLN("");
#endif
  PRINT("    TICKS_PER_S=");
  PRINTU32((uint32_t)TICKS_PER_S);
  PRINTLN("");
  PRINT("    Steppers=");
  PRINTU8(MAX_STEPPER);
  PRINTLN("");

  // If you are not sure, that the stepper hardware is working,
  // then try first direct port manipulation and uncomment the next line.
  // Alternatively use e.g. M1 T by serial command
  // test_direct_drive(&stepper_config[0]);

  engine.init();
  if (led_pin != PIN_UNDEFINED) {
    engine.setDebugLed(led_pin);
  }

  for (uint8_t i = 0; i < MAX_STEPPER; i++) {
    FastAccelStepper *s = NULL;
    const struct stepper_config_s *config = &stepper_config[i];
    if (config->step != PIN_UNDEFINED) {
      s = engine.stepperConnectToPin(config->step);
      if (s) {
        s->setDirectionPin(config->direction, config->direction_high_count_up,
                           config->dir_change_delay);
        s->setEnablePin(config->enable_low_active, true);
        s->setEnablePin(config->enable_high_active, false);
        s->setAutoEnable(config->auto_enable);
        s->setDelayToEnable(config->on_delay_us);
        s->setDelayToDisable(config->off_delay_ms);
      }
    }
    stepper[i] = s;
  }

  usage();
}

#ifdef SIM_TEST_INPUT
const char *input = SIM_TEST_INPUT
    " ";  // final space is too easy forgotten in platformio.ini test
#else
const char *input = NULL;
#endif
uint8_t write_ptr = 0;
uint8_t read_ptr = 0;
char in_buffer[256];  // This allows to let in/out_ptr wrap around
uint8_t out_ptr = 0;
char out_buffer[256];
bool stopped = true;
bool verbose = true;
bool usage_info = true;
bool speed_in_milli_hz = false;
uint32_t last_time = 0;
int16_t selected = 0;
uint32_t pause_ms = 0;
uint32_t pause_start = 0;
#if defined(ARDUINO_ARCH_AVR)
bool simulate_digitalRead_error = false;
#endif
bool simulate_blocked_ISR = false;

void info(FastAccelStepper *s, bool long_info) {
  PRINTCH('@');
#if defined(SUPPORT_ESP32_PULSE_COUNTER)
  if (s->pulseCounterAttached()) {
    int16_t pcnt_pos_1 = s->readPulseCounter();
    int32_t pos = s->getCurrentPosition();
    int16_t pcnt_pos_2 = s->readPulseCounter();
    PRINTI32(pos);
    PRINT(" [");
    PRINTI16(pcnt_pos_1);
    PRINTCH(']');
    if (pcnt_pos_1 != pcnt_pos_2) {
      PRINT(" [");
      PRINTI16(pcnt_pos_2);
      PRINTCH(']');
    }
  } else {
    int32_t pos = s->getCurrentPosition();
    PRINTI32(pos);
  }
#else
  int32_t pos = s->getCurrentPosition();
  PRINTI32(pos);
#endif
  if (s->isRunning()) {
    if (s->isRunningContinuously()) {
      PRINT(" nonstop");
    } else {
      PRINT(" => ");
      PRINTI32(s->targetPos());
    }
    PRINT(" QueueEnd=");
    PRINTI32(s->getPositionAfterCommandsCompleted());
    PRINT(" v=");
    if (speed_in_milli_hz) {
      PRINTI32(s->getCurrentSpeedInMilliHz());
      PRINT("mSteps/s");
    } else {
      PRINTU32(s->getPeriodInUsAfterCommandsCompleted());
      PRINT("us/");
      PRINTU32(s->getPeriodInTicksAfterCommandsCompleted());
      PRINT("ticks");
    }
    if (s->isRampGeneratorActive()) {
      switch (s->rampState() & RAMP_STATE_MASK) {
        case RAMP_STATE_IDLE:
          PRINT(" IDLE ");
          break;
        case RAMP_STATE_ACCELERATE:
          PRINT(" ACC  ");
          break;
        case RAMP_STATE_DECELERATE:
          PRINT(" RED  ");  // Reduce
          break;
        case RAMP_STATE_COAST:
          PRINT(" COAST");
          break;
        case RAMP_STATE_REVERSE:
          PRINT(" REV  ");
          break;
        default:
          PRINTU8(s->rampState());
      }
    } else {
      PRINT(" MANU");
    }
  } else {
    if (long_info) {
      output_msg(MSG_ACCELERATION_STATUS);
      PRINTU32(s->getAcceleration());
      if (speed_in_milli_hz) {
        output_msg(MSG_SPEED_STATUS_FREQ);
        PRINTU32(s->getSpeedInMilliHz());
      } else {
        output_msg(MSG_SPEED_STATUS_TIME);
        PRINTU32(s->getSpeedInUs());
      }
    }
  }
  PRINTCH(' ');
}

void stepper_info() {
  output_info(false);
  if (simulate_blocked_ISR) {
    output_msg(MSG_LONG_INTERRUPT_BLOCK_ENABLED);
  }
#if defined(ARDUINO_ARCH_AVR)
  if (simulate_digitalRead_error) {
    output_msg(MSG_STRAY_DIGITAL_READ_ENABLED);
  }
#endif
}

void usage() {
  switch (mode) {
    case normal:
      output_msg(MSG_USAGE_NORMAL);
      break;
    case test:
      output_msg(MSG_USAGE_TEST);
      break;
    case config:
      output_msg(MSG_USAGE_CONFIG);
      break;
  }
  stepper_info();
}

void output_info(bool only_running) {
  bool need_ln = false;
  for (uint8_t i = 0; i < MAX_STEPPER; i++) {
    if (stepper[i]) {
      if (!only_running) {
        if (i == selected) {
          PRINT(">> ");
        } else {
          PRINT("   ");
        }
      }
      if (!only_running || stepper[i]->isRunning()) {
        PRINTCH('M');
        PRINTU8(i + 1);
        PRINT(": ");
        info(stepper[i], !only_running);
        if (!only_running) {
          PRINTLN("");
        } else {
          need_ln = true;
        }
      }
    }
  }
  if (need_ln) {
    PRINTLN("");
  }
}

#if defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM)
void esp_reset() {
#if ESP_IDF_VERSION_MAJOR == 5
  const esp_task_wdt_config_t wdt_config = {
      .timeout_ms = 1, .idle_core_mask = 1, .trigger_panic = true};

  esp_task_wdt_reconfigure(&wdt_config);
#else
  esp_task_wdt_init(1, true);
  esp_task_wdt_add(NULL);
#endif
  while (true) {
  }
}
#endif

#define MODE(mode, CMD) ((mode << 8) + CMD)

int32_t val_n[3];
int8_t get_val1_val2_val3(char *cmd) {
  char *endptr;
  for (uint8_t i = 0; i < 3; i++) {
    val_n[i] = strtol(cmd, &endptr, 10);
    if (endptr == cmd) {
      return -1;
    }
    cmd = endptr;
    if (*cmd == 0) {
      return i + 1;
    }
    if ((i < 2) && (*cmd++ != ',')) {
      return -1;
    }
  }
  return -1;
}

bool process_cmd(char *cmd) {
  FastAccelStepper *stepper_selected = stepper[selected];
  uint16_t s = *cmd++;
  char *endptr;
  int8_t res;
  int8_t gv;
  switch (MODE(mode, s)) {
    case MODE(normal, 'M'):
    case MODE(test, 'M'):
    case MODE(config, 'M'):
      if (get_val1_val2_val3(cmd) == 1) {
        if ((val_n[0] > 0) && (val_n[0] <= MAX_STEPPER)) {
          output_msg(MSG_SELECT_STEPPER);
          selected = val_n[0] - 1;
          PRINTI16(selected + 1);
          PRINTLN("");
          return true;
        }
      }
      break;
    case MODE(normal, 'r'):
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM)
      if (strcmp(cmd, "eset") == 0) {
        PRINTLN("ESP reset");
        esp_reset();
      }
      if (*cmd == 0) {
#if defined(ARDUINO_ARCH_ESP32)
        PRINTLN("ESP restart");
        ESP.restart();
#else
        esp_reset();
#endif
      }
#endif
#if defined(ARDUINO_ARCH_AVR)
      if (*cmd == 0) {
        output_msg(MSG_STRAY_DIGITAL_READ_TOGGLE);
        simulate_digitalRead_error ^= true;
        return true;
      }
#endif
      break;
    case MODE(normal, 'I'):
    case MODE(test, 'I'):
      if (*cmd == 0) {
        output_msg(MSG_TOGGLE_MOTOR_INFO);
        verbose = !verbose;
        return true;
      }
      break;
    case MODE(normal, 'Q'):
    case MODE(test, 'Q'):
      if (*cmd == 0) {
        output_msg(MSG_TOGGLE_USAGE_INFO);
        usage_info = !usage_info;
        return true;
      }
      break;
    case MODE(normal, '?'):
    case MODE(test, '?'):
    case MODE(config, '?'):
      if (*cmd == 0) {
        usage();
        return true;
      }
      break;
    case MODE(normal, 't'):
      if (*cmd == 0) {
        output_msg(MSG_ENTER_TEST_MODE);
        mode = test;
        usage();
        return true;
      }
      break;
    case MODE(normal, 'c'):
      if (*cmd == 0) {
        output_msg(MSG_ENTER_CONFIG_MODE);
        mode = config;
        usage();
        return true;
      }
      break;
    case MODE(normal, 'e'):
      if (*cmd == 0) {
        output_msg(MSG_LONG_INTERRUPT_BLOCK_TOGGLE);
        simulate_blocked_ISR ^= true;
        return true;
      }
      break;
    case MODE(normal, 'A'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_SET_ACCELERATION_TO);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->setAcceleration(val_n[0]);
        if (res < 0) {
          output_msg(MSG_ERROR_INVALID_VALUE);
        }
        return true;
      }
      break;
    case MODE(normal, 'J'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_LINEAR_ACCELERATION);
        PRINTI32(val_n[0]);
        PRINTLN("");
        stepper_selected->setLinearAcceleration(val_n[0]);
        return true;
      }
      break;
    case MODE(normal, 'j'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_JUMP_START);
        PRINTI32(val_n[0]);
        PRINTLN("");
        stepper_selected->setJumpStart(val_n[0]);
        return true;
      }
      break;
    case MODE(normal, 'V'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        speed_in_milli_hz = false;
        output_msg(MSG_SET_SPEED_TO_US);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->setSpeedInUs(val_n[0]);
        if (res < 0) {
          output_msg(MSG_ERROR_INVALID_VALUE);
        }
        return true;
      }
      break;
    case MODE(normal, 'H'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        speed_in_milli_hz = true;
        output_msg(MSG_SET_SPEED_TO_HZ);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->setSpeedInHz(val_n[0]);
        if (res < 0) {
          output_msg(MSG_ERROR_INVALID_VALUE);
        }
        return true;
      }
      break;
    case MODE(normal, 'h'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        speed_in_milli_hz = true;
        output_msg(MSG_SET_SPEED_TO_MILLI_HZ);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->setSpeedInMilliHz(val_n[0]);
        if (res < 0) {
          output_msg(MSG_ERROR_INVALID_VALUE);
        }
        return true;
      }
      break;
    case MODE(normal, 'a'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_SET_ACCELERATION_TO);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->moveByAcceleration(val_n[0]);
        output_msg(MSG_MOVE_OK + res);
        return true;
      }
      break;
    case MODE(normal, 'R'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_MOVE_STEPS);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->move(val_n[0]);
        output_msg(MSG_MOVE_OK + res);
        return true;
      }
      break;
    case MODE(normal, 'P'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_MOVE_TO_POSITION);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->moveTo(val_n[0]);
        output_msg(MSG_MOVE_OK + res);
        return true;
      }
      break;
    case MODE(normal, '@'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_SET_POSITION);
        PRINTI32(val_n[0]);
        PRINTLN("");
        stepper_selected->setCurrentPosition(val_n[0]);
        return true;
      }
      break;
    case MODE(normal, 'E'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_SET_ENABLE_TIME);
        PRINTI32(val_n[0]);
        PRINTLN("");
        res = stepper_selected->setDelayToEnable(val_n[0]);
        output_msg(MSG_RETURN_CODE);
        PRINTU8(res);
        PRINTLN("");
        return true;
      }
      break;
    case MODE(normal, 'D'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        output_msg(MSG_SET_DISABLE_TIME);
        PRINTI32(val_n[0]);
        PRINTLN("");
        stepper_selected->setDelayToDisable(val_n[0]);
        return true;
      }
      break;
    case MODE(normal, 'w'):
      val_n[0] = strtol(cmd, &endptr, 10);
      if (*endptr == 0) {
        PRINTI32(val_n[0]);
        output_msg(MSG_WAIT_MS);
        pause_ms = val_n[0];
        pause_start = MILLIS();
        return true;
      }
      break;
    case MODE(config, 'd'):
      if (strcmp(cmd, "c") == 0) {
        output_msg(MSG_DISABLED);
        stepper_selected->setDirectionPin(PIN_UNDEFINED);
        return true;
      }
      gv = get_val1_val2_val3(cmd);
      switch (gv) {
        case 1:
          output_msg(MSG_DIRECTION_PIN);
          output_msg(MSG_SET_TO_PIN);
          PRINTI32(val_n[0]);
          PRINTLN("");
          stepper_selected->setDirectionPin(val_n[0]);
          return true;
        case 2:
        case 3:
          output_msg(MSG_DIRECTION_PIN);
          output_msg(MSG_SET_TO_PIN);
          PRINTI32(val_n[0]);
          PRINTLN("");
          output_msg(MSG_DIRECTION_PIN);
          if (val_n[1] != 0) {
            output_msg(MSG_HIGH_COUNT_DOWN);
          } else {
            output_msg(MSG_HIGH_COUNT_UP);
          }
          PRINTLN("");
          if (gv == 2) {
            stepper_selected->setDirectionPin(val_n[0], val_n[1]);
          } else {
            output_msg(MSG_DELAY);
            PRINTI32(val_n[2]);
            PRINTLN("");
            stepper_selected->setDirectionPin(val_n[0], val_n[1], val_n[2]);
          }
          return true;
        default:
          break;
      }
      break;
    case MODE(normal, 'N'):
      if (*cmd == 0) {
        output_msg(MSG_OUTPUT_DRIVER_ON);
        stepper_selected->setAutoEnable(false);
        stepper_selected->enableOutputs();
        return true;
      }
      break;
    case MODE(normal, 'F'):
      if (*cmd == 0) {
        output_msg(MSG_OUTPUT_DRIVER_OFF);
        stepper_selected->setAutoEnable(false);
        stepper_selected->disableOutputs();
        return true;
      }
      break;
    case MODE(normal, 'O'):
      if (*cmd == 0) {
        output_msg(MSG_OUTPUT_DRIVER_AUTO);
        stepper_selected->setAutoEnable(true);
        return true;
      }
      break;
    case MODE(normal, 'S'):
      if (*cmd == 0) {
        output_msg(MSG_STOP);
        stepper_selected->stopMove();
        return true;
      }
      break;
    case MODE(normal, 'K'):
      if (*cmd == 0) {
        output_msg(MSG_KEEP_RUNNING);
        stepper_selected->keepRunning();
        return true;
      }
      break;
    case MODE(normal, 'f'):
      if (*cmd == 0) {
        output_msg(MSG_RUN_FORWARD);
        res = stepper_selected->runForward();
        output_msg(MSG_RETURN_CODE);
        PRINTU8(res);
        PRINTLN("");
        return true;
      }
      break;
    case MODE(normal, 'b'):
      if (*cmd == 0) {
        output_msg(MSG_RUN_BACKWARD);
        res = stepper_selected->runBackward();
        output_msg(MSG_RETURN_CODE);
        PRINTU8(res);
        PRINTLN("");
        return true;
      }
      break;
    case MODE(normal, 'X'):
      if (*cmd == 0) {
        output_msg(MSG_IMMEDIATE_STOP);
        stepper_selected->forceStopAndNewPosition(0);
        return true;
      }
      break;
    case MODE(normal, 'U'):
      if (*cmd == 0) {
        output_msg(MSG_UPDATE_SPEED_ACCELERATION);
        stepper_selected->applySpeedAcceleration();
        return true;
      }
      break;
    case MODE(normal, 'u'):
      if (*cmd == 0) {
        output_msg(MSG_SET_UNIDIRECTIONAL_STEPPER);
        stepper_selected->setDirectionPin(PIN_UNDEFINED);
        return true;
      }
      break;
    case MODE(normal, 'W'):
    case MODE(test, 'W'):
      if (*cmd == 0) {
#ifdef SIM_TEST_INPUT
        if (stepper_selected->isRunning() || test_ongoing) {
          read_ptr -= 2;
        }
#else
        output_msg(MSG_BLOCKING_WAIT);
        if (!stepper_selected->isRunningContinuously() ||
            stepper_selected->isStopping()) {
          // Wait for stepper stop
          while (stepper_selected->isRunning()) {
            // do nothing
          }
          PRINTLN("STOPPED");
        }
#endif
        return true;
      }
      break;
#if !defined(__AVR_ATmega32U4__)
    case MODE(normal, 'T'):
      if (*cmd == 0) {
        if (!stepper_selected->isRunning()) {
          output_msg(MSG_TEST_DIRECT_DRIVE);
          stepper_selected->detachFromPin();
          test_direct_drive(&stepper_config[selected]);
          stepper_selected->reAttachToPin();
        }
        return true;
      }
      break;
#endif
    case MODE(normal, '+'):
      if (*cmd == 0) {
        if (!stepper_selected->isRunning()) {
          stepper_selected->forwardStep(true);
          output_msg(MSG_STEPPED_FORWARD);
        }
        return true;
      }
      break;
    case MODE(normal, '-'):
      if (*cmd == 0) {
        if (!stepper_selected->isRunning()) {
          stepper_selected->backwardStep(true);
          output_msg(MSG_STEPPED_BACKWARD);
        }
        return true;
      }
      break;
    case MODE(test, 'x'):
    case MODE(config, 'x'):
      if (*cmd == 0) {
        output_msg(MSG_EXIT_TO_MAIN_MENU);
        test_ongoing = false;
        mode = normal;
        usage();
        return true;
      }
      break;
    case MODE(test, 'R'):
      if (*cmd == 0) {
        output_msg(MSG_RUN_TESTS);
        test_ongoing = true;
        return true;
      }
      break;
#if defined(SUPPORT_ESP32_PULSE_COUNTER)
    case MODE(normal, 'p'):
      if (strcmp(cmd, "c") == 0) {
        output_msg(MSG_CLEAR_PULSE_COUNTER);
        stepper_selected->clearPulseCounter();
        return true;
      }
      if (get_val1_val2_val3(cmd) == 3) {
        output_msg(MSG_ATTACH_PULSE_COUNTER);
        PRINTI32(val_n[0]);
        PRINTLN("");
        if (!stepper_selected->attachToPulseCounter(val_n[0], val_n[1],
                                                    val_n[2])) {
          output_msg(MSG_ERROR_ATTACH_PULSE_COUNTER);
        }
        return true;
      }
      if (get_val1_val2_val3(cmd) == 1) {
        output_msg(MSG_ATTACH_PULSE_COUNTER);
        PRINTI32(val_n[0]);
        PRINTLN("");
        if (!stepper_selected->attachToPulseCounter(val_n[0])) {
          output_msg(MSG_ERROR_ATTACH_PULSE_COUNTER);
        }
        return true;
      }
      break;
#endif
    case MODE(test, '0'):
    case MODE(test, '1'):
      for (uint8_t i = 0; i < NUM_TEST_SEQUENCE; i++) {
        const struct test_seq_def_s *ts = &test_sequence[i];
        if (strcmp(out_buffer, ts->code) == 0) {
          output_msg(MSG_SELECT_TEST_SEQUENCE);
          PRINTLN(out_buffer);
          test_seq[selected].test = ts->test;
          test_seq[selected].state = 0;
          return true;
        }
      }
      break;
  }

  return false;
}

void loop() {
  char ch = 0;
  if (input == NULL) {
    POLL_CHAR_IF_ANY(ch)
  } else {
    ch = *input;
    if (ch == 0) {
      if (read_ptr == write_ptr) {
        input = NULL;
#ifdef SIM_TEST_INPUT
        delay(1000);
        noInterrupts();
        sleep_cpu();
#endif
      }
    } else {
      input++;
    }
  }

  if (ch != 0) {
    if ((uint8_t)(write_ptr + 1) == read_ptr) {
      read_ptr++;
    }
    in_buffer[write_ptr++] = ch;
  }
  if (pause_ms > 0) {
    if ((uint32_t)(MILLIS() - pause_start) >= pause_ms) {
      pause_ms = 0;
      output_msg(MSG_WAIT_COMPLETE);
    }
  } else if (read_ptr != write_ptr) {
    ch = in_buffer[read_ptr++];
    if ((ch != ' ') && (ch != '\n') && (ch != '\r')) {
      if (out_ptr == 255) {
        out_ptr = 0;
      }
      out_buffer[out_ptr++] = ch;
    } else if ((ch == ' ') || (ch == '\n') || (ch == '\r')) {
      out_buffer[out_ptr] = 0;

      if (out_ptr > 0) {
        if (!process_cmd(out_buffer)) {
          output_msg(MSG_UNKNOWN_COMMAND);
          PRINTLN(out_buffer);
        }
      }
      out_ptr = 0;
    }
  }

  uint32_t ms = MILLIS();

#if defined(ARDUINO_ARCH_AVR)
  if (simulate_digitalRead_error) {
    digitalRead(stepPinStepperA);
    digitalRead(stepPinStepperB);
#ifdef stepPinStepperC
    digitalRead(stepPinStepperC);
#endif
  }
#endif
  if (simulate_blocked_ISR) {
    if ((ms & 0xff) < 0x40) {
      fasDisableInterrupts();
      DELAY_US(100);
      fasEnableInterrupts();
    }
  }

  if (mode == test) {
    if (test_ongoing) {
      bool finished = true;
      for (uint8_t i = 0; i < MAX_STEPPER; i++) {
        struct test_seq_s *s = &test_seq[i];
        if ((s->test != NULL) && (s->state != TEST_STATE_ERROR)) {
          bool res = s->test(stepper[i], &test_seq[i], ms);
          if (res) {
            s->test = NULL;
          }
          finished &= res;
        }
      }
      if (finished) {
        test_ongoing = false;
        bool test_failed = false;
        stepper_info();
        for (uint8_t i = 0; i < MAX_STEPPER; i++) {
          struct test_seq_s *s = &test_seq[i];
          s->test = NULL;
          if (s->state == TEST_STATE_ERROR) {
            test_failed = true;
          }
        }
        if (test_failed) {
          output_msg(MSG_FAILED_STATUS);
        } else {
          output_msg(MSG_PASS_STATUS);
        }
        output_msg(MSG_TEST_COMPLETED);
      } else {
        uint32_t now = MILLIS();
        if (now - last_time >= 100) {
          if (verbose) {
            output_info(true);
          }
          last_time = now;
        }
      }
    }
  } else {
    bool running = false;
    for (uint8_t i = 0; i < MAX_STEPPER; i++) {
      if (stepper[i]) {
        running |= stepper[i]->isRunning();
      }
    }
    if (running) {
      if (ms - last_time >= 100) {
        if (verbose) {
          output_info(true);
        }
        last_time = ms;
      }
    }
    if (!stopped && !running) {
      output_info(true);
      if (usage_info) {
        usage();
      }
    }
    stopped = !running;
  }
}

#ifdef NEED_APP_MAIN
#include "hal/wdt_hal.h"
extern "C" void app_main() {
#if ESP_IDF_VERSION_MAJOR == 5
  esp_task_wdt_deinit();
  esp_task_wdt_config_t config = {
      .timeout_ms = 1000, .idle_core_mask = 0, .trigger_panic = true};
  esp_task_wdt_init(&config);
#else
  esp_task_wdt_init(1000, true);
  esp_task_wdt_add(NULL);
#endif
  esp_task_wdt_add(NULL);

  printf("Non-arduino version\n");
  setup();
  while (true) {
    loop();
    esp_task_wdt_reset();
    DELAY_MS(2);
  }
}
#endif
A4988
A4988