Железная дорога на Arduino

mcvall

✩✩✩✩✩✩✩
15 Ноя 2020
33
3
@ll, Т.е. не должно быть задержки в 1 сек после нажатия кнопки?
Дело в том, что задержки нет после нажатия кнопки, сразу срабатывает при удержании кнопки и всё сначала. Без кнопки задержка сначала есть. Вы написали мне такую конструкцию, что я ещё два дня буду разгребать этот код:) Спасибо:)
Upd. Смотрите, как должно быть в общем. Старт Esp32, стоим курим, зажимаем кнопку (у меня сенсорная, настроенная на триггер High), начинается плавный старт мотора, разогнались - держим скорость до "кнопка выкл - т.е. триггер LOW" или до срабатывания любого из датчиков, значение которого даст команду остановить мотор. T.е. в первом случае мне нужно просто включать поезд, чтобы он ехал и отключать его. Во втором, включать поезд и ждать команды от датчиков.
 
Изменено:

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
Дело в том, что задержки нет после нажатия кнопки, сразу срабатывает при удержании кнопки и всё сначала.
Ну это тоже логично, т.к. таймер объявлен до того, как нажата кнопка, чтобы была задержка, то надо его в момент нажатия на кнопку обнулить.
Вопрос в том, должна ли она быть вообще?)

Вы написали мне такую конструкцию, что я ещё два дня буду разгребать этот код:)
Расписал комментарии
Если не хотите разбираться с той конструкцией, то можете доработать первую

C++:
  if (millis() - last_time > 6000) {    // срабатывает спустя 6 сек и обнуляет таймер
      last_time = millis();
  } else if (millis() - last_time > 4000) {   // срабатывает спустя 4 сек
      dutyCycle_1 = 50; // pin5
      dutyCycle = 50; // pin18
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
  } else if (millis() - last_time > 2000) {  // срабатывает спустя 2 сек
      dutyCycle_1 = 30; // pin5
      dutyCycle = 30; // pin18
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
  } else if (millis() - last_time > 0) {   // срабатывает сразу после нажатия на кнопку
      dutyCycle_1 = 10; // pin5
      dutyCycle = 10; // pin18
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
  }
 

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
Обновил пред. сообщение как должно всё работать.
Теперь понятно)

На выходе должно получиться что-то типа увеличение скорости поезда на 10 каждую секунду до максимального значения
C++:
static uint32_t last_time = 0;
static const uint8_t MAX_SPEED = 50;
static uint8_t speed = 0;      // Эту переменную можем использовать как для таймера, так и для значения, стартовое значение 1
if (!digitalRead(buttonPin)) { // Проверяем, нажата ли кнопка
  if (millis() - last_time > speed*100 && speed <= MAX_SPEED) {     // проверяем, прошло ли speed умноженное на 100 миллисекунд (вначале 0 * на 100, т.е. 0 сек, срабатывает сразу)
    speed += 10;          // увеличиваем speed на 10;
    if (speed <= MAX_SPEED) {                // тут смотрим, не превышает ли speed  наше максимальное значение (которое мы будем пихать в ledcWrite)
      dutyCycle_1 = speed; // pin5           // тут присваиваем значение (speed, при первом проходе будет 10)
      dutyCycle = speed; // pin18            // тоже самое
      ledcWrite(ledChannel_1, dutyCycle_1);  // теперь вызываем с нужным значением
      ledcWrite(ledChannel, dutyCycle);
    }
  }
}
else {      // если кнопка отпущена - сбрасываем таймер, обнуляем скорость и останавливаем движок
  last_time = millis();
  speed = 0;
  dutyCycle_1 = speed; // pin5           
  dutyCycle = speed; // pin18           
  ledcWrite(ledChannel_1, dutyCycle_1); 
  ledcWrite(ledChannel, dutyCycle);
}
 

mcvall

✩✩✩✩✩✩✩
15 Ноя 2020
33
3
Расписал комментарии
Если не хотите разбираться с той конструкцией, то можете доработать первую
Спасибо Вам большое, буду разбираться. Обновил пред. сообщение как должно всё работать
На выходе должно получиться что-то типа
Да, так, но ещё плюс плавное торможение, не в ноль сразу) Вы классно всё расписали, но я теперь должен как-то менять эти значения. Ведь когда я установлю мотор в поезд (сейчас он снят), подцеплю вагоны, нужно отталкиваться от механических свойств и собственных предпочтений. Спасибо огромное!
 

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
@mcvall, Тогда вот так

C++:
static uint32_t last_time = 0;
static const uint8_t MAX_SPEED = 50;
static uint8_t old_speed = 0;
static uint8_t speed = 0;
bool buttonState = !digitalRead(buttonPin);
static bool oldButtonState = !buttonState;     // можно указать любое подходящее значение, !buttonState значит, что мы войдем в область под таймером

if (millis() - last_time > 1000 || buttonState != oldButtonState) {     // меняем скорость либо по таймеру, либо когда изменилось значение кнопки
  if (buttonState)
    speed+10 <= MAX_SPEED ? speed += 10 : MAX_SPEED;  // проверяем тенарным оператором, будет ли speed+10 меньше максимального значения, если да, то плюсуем, если нет, то приравниваем к максимальному (чтобы не выйти за область максимума)
  else
    speed > 10 ? speed -= 10 : speed = 0;  // та же история, только наоборот, если speed больше 10, то можем минусовать, а иначе делаем 0
  last_time = millis();
}
oldButtonState = buttonState;      // фиксируем уже старое значение кнопки

if (old_speed != speed) {                // применяем скорость, если она изменилась
  dutyCycle_1 = speed; // pin5           // тут присваиваем значение (speed, при первом проходе будет 10)
  dutyCycle = speed; // pin18            // тоже самое
  ledcWrite(ledChannel_1, dutyCycle_1);  // теперь вызываем с нужным значением
  ledcWrite(ledChannel, dutyCycle);
  old_speed = speed;               // фиксируем текущую скорость
}
Если кнопка нажата, то поезд будет постепенно набирать скорость, если кнопку отпустить - будет постепенно тормозить.
И ledcWrite() вызывается только тогда, когда скорость изменилась, иначе нет смысла ее дергать
 
Изменено:
  • Лойс +1
Реакции: mcvall

Старик Похабыч

★★★★★★★
14 Авг 2019
4,191
1,281
Москва
Я не стал лезть в код автора, т.к. у меня другая идея этой реализации. Её алгоритм я изложил выше. Но т.к. у автора топика не очень получается, решил сделать простую реализацию. Код ниже постарался откомментировать подробно.
Коротко о работе:
1) Одна кнопка для арудино нано на 2-ой пин с подтижкой и конденсатором. Простой концевик, по умолчанию кнопка нажата получается, т.е. выдает 0, ( у концевика 3 контакта)
2) Нет ни мотора ни светодиода. Все выводится в монитор порта на скорости 115200 - там и смотреть
3) При нажатой кнопке идет разгон до указанной в коде скорости. По умолчанию 100%, при отпускании 0%
4) Реализовано разное ускорение для разгона и торможения - как это просто сделать.
C++:
#define BTN_PIN 2     // пин кнопки
#define MAX_SPEED 100 // максимальная скорость в процентах, у меня проценты. но тут можно сразу ШИМ указывать
#define MIN_SPEED 0   // минимальная скорость в процентах, у меня проценты. но тут можно сразу ШИМ указывать, поезд может и не тронуться с 1% ШИМ, тогда можно бдует переделать соответствия.
#define MOTO_TICK 20  // что быне гнать постоянно процедуру мотора ограничим его выполнение 10 раз в секунду. Можно чаще, но для теста пойдет.
#define STOP_TICK 1   // торможение будет менять скорость каждый 1-ый тик
#define GO_TICK 3     // ускорение будет менять скорость каждый 3-ий тик, т.е разгон в 3 раза медленнее торможения.


int8_t currentSpeed = 0;  // текущая реальная скорость
int8_t Speed = 0;         // скорость к которой надо стермиться, при нажатой кнопке это будет MAX_SPEED, при отпушенной MIN_SPEED


void setup() {
  Serial.begin(115200);             //вместо светодиода буду использовать вывод в монитор порта.
  pinMode(BTN_PIN, INPUT_PULLUP);
  Serial.println("Start");
}

void loop() {
  btnProc();
  motoTick();
  testOutput();
}
void btnProc()
{
  // обработка 20 раз в секуду.
  static uint32_t tmr = millis();
  if (millis() - tmr < 50) return; 
  tmr = millis();
  // тут надеюсь все ясно.
  if (digitalRead(BTN_PIN))
    Speed = MAX_SPEED;
  else Speed = MIN_SPEED;
}

void motoTick() {
  // обработка с нужной частотой 
  static uint32_t tmr = millis();
  if (millis() - tmr < MOTO_TICK) return;
  tmr = millis(); 
 
  // если текущая скорость равна нужной, то ничего не делаем.
  if (currentSpeed == Speed) return;
    


  static uint8_t c_ticks = 0; // текущие тики для ускорения и торможения
  static uint8_t old_delta=0; // старое изменение скорости, для сброса тиков.
  int8_t delta = (Speed - currentSpeed) / abs(Speed - currentSpeed); // вычисляем изменение скорости (+1 или -1) со знаком
  if (old_delta!=delta) // если знак изменения поменялся - обнуляем тики! Это важно, т.к. если не обнулять и тики будут 2 , а начнется торможение, то будет задержка.
    {
    old_delta=delta;
    c_ticks=0;
    }
 
  c_ticks++; // увеличиваем тики
 
  if (delta > 0)
  {
    // обработка ускорения
    if (c_ticks == GO_TICK)
    {
      c_ticks = 0;
      currentSpeed = currentSpeed + delta;
    }
  }
  if (delta < 0)
  {
    // обработка замедления
    if (c_ticks == STOP_TICK)
    {
      c_ticks = 0;
      currentSpeed = currentSpeed + delta;
    }
  }
}

// это тестовый вывод, все выводиться не сплошняком, а только при изменении значений!
void testOutput()
{
  static int8_t  old_Speed = -1;
  static int8_t  old_CurrentSpeed = -1;
  if (old_Speed != Speed)
  {
    Serial.print("New speed:");
    Serial.println(Speed);
    old_Speed = Speed;
  }
  if (old_CurrentSpeed != currentSpeed)
  {
    Serial.print("New c_speed:");
    Serial.println(currentSpeed);
    old_CurrentSpeed = currentSpeed;
  }
}
 
  • Лойс +1
Реакции: mcvall

bort707

★★★★★★✩
21 Сен 2020
2,902
863
по-моему, логика кода станет на порядок проще, если добавить машину состояний.
Всего 4 состояния

1. Ожидание
2. Разгон
3. Равномерное движение
4. Замедление

Переход между состояниями - по состоянию кнопки и таймеру
 
  • Лойс +1
Реакции: mcvall

Старик Похабыч

★★★★★★★
14 Авг 2019
4,191
1,281
Москва
Ну я выше вообще менял только скорость к которой следует стремиться, а все остальные состояния вытекают из разности скоростей : текущей и нужной.
Мне тут все время мерещится ПИД регулятор. Делал его для проекта, где выставляется скорость двигателя, так мотор получалось крутить на таких низких оборотах, что без ПИД мне и не снились. Но нужна обратная связь, увы.
 

bort707

★★★★★★✩
21 Сен 2020
2,902
863
Мне тут все время мерещится ПИД регулятор.
могу ошибаться, но к проекту автора ПИД ну никаким боком... В первую очередь потому что у него нет обратной связи - без чего никакое регулирование в принципе невозможно.
 

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
И то и другое делается "левой задней ногой" .. дал же ссыль и на временные автоматы и на автоматы состояний .. ;)
 

mcvall

✩✩✩✩✩✩✩
15 Ноя 2020
33
3
1. Ожидание
2. Разгон
3. Равномерное движение
4. Замедление

Переход между состояниями - по состоянию кнопки и таймеру
И то и другое делается "левой задней ногой"
Переделал код под Машину Состояний, она же Конечный автомат. Посмотрите пожалуйста в правильном ли направлении иду?
Управление двигателем State Machine:
// Управление двигателем  State Machine

const int motor1Pin1 = 5;  // номер контакта для IN1 драйвера двигателя
const int motor1Pin2 = 18; // номер контакта для IN2 драйвера двигателя
const int buttonPin = 22;  // номер контакта для кнопки
int buttonState = 0;       // переменная для хранения статуса кнопки

const int STATE_WAIT = 0;  // Состояние ожидания
const int STATE_START = 1; // Состояние набора оборотов
const int STATE_MOVE = 2;  // Состояние движения на одной скорости
const int STATE_BRAKE = 3; // Состояние торможения до полной остановки

int state;  // Переменная для хранения текущего состояния

// задаем свойства ШИМ-сигнала:
const int freq = 5000;     // Частота
const int ledChannel = 0;  // Канал,генерирующий PWM-сигнал для motor1Pin2
const int ledChannel_1 = 1;// Канал,генерирующий PWM-сигнал для motor1Pin1
const int resolution = 8;  // Разрешение
int dutyCycle;
int dutyCycle_1;

void setup() {
  ledcSetup(ledChannel, freq, resolution); // настраиваем ШИМ-сигнал согласно свойствам, заданным выше
  ledcSetup(ledChannel_1, freq, resolution);
 
  pinMode(motor1Pin1, OUTPUT);
  pinMode(motor1Pin2, OUTPUT); 
  pinMode(buttonPin, INPUT);
 
  ledcAttachPin(motor1Pin1, ledChannel_1); // где: motor1Pin1 - контакт для вывода PWM, ledChannel - канал,генерирующий PWM-сигнал для motor1Pin1
  ledcAttachPin(motor1Pin2, ledChannel); 
  state = STATE_WAIT;                      // Состояние после перезагрузки
 
}

void loop(){
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH)  {
    state = STATE_START;          // Если кнопка удерживается, включить Состояние набора оборотов
    state = STATE_MOVE;           // и перейти в Состояние движения на одной скорости
  } else {
    state = STATE_BRAKE;          // Если кнопка Не удерживается, включить Состояние торможения до полной остановки
    state = STATE_WAIT;           // и перейти в Состояние ожидания
  }
 
  switch (state) {
    case STATE_WAIT:
      dutyCycle_1 = 0;  // pin5
      dutyCycle = 0;    // pin18
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    case STATE_START: 
      dutyCycle_1 = 50; 
      dutyCycle = 50;   
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    case STATE_MOVE: 
      dutyCycle_1 = 100; 
      dutyCycle = 100;   
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    case STATE_BRAKE: 
      dutyCycle_1 = 10; 
      dutyCycle = 10;   
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    }
 
}
 

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
@mcvall, внимательно смотрите, что вы тут делаете

C++:
  if (buttonState == HIGH)  {
    state = STATE_START;          // Если кнопка удерживается, включить Состояние набора оборотов
    state = STATE_MOVE;           // и перейти в Состояние движения на одной скорости
  } else {
    state = STATE_BRAKE;          // Если кнопка Не удерживается, включить Состояние торможения до полной остановки
    state = STATE_WAIT;           // и перейти в Состояние ожидания
  }
state может хранить только одно значение за раз. А вы присваиваете одно, потом сразу другое, как вы думаете, какие условия switch (state) никогда не выполнятся?

Состояния лучше хранить не в константах (да еще и интовых, которые будут занимать по 2 байта каждая), а в enum, а сам state лучше объявлять как short (uint8_t)
 

mcvall

✩✩✩✩✩✩✩
15 Ноя 2020
33
3
state может хранить только одно значение за раз.
Да, я понял. Между ними должна быть пауза в виде набора оборотов.
а в enum, а сам state лучше объявлять как short (uint8_t)
Переделал, так правильно?
C++:
// Управление двигателем  State Machine

const int motor1Pin1 = 5;  // номер контакта для IN1 драйвера двигателя
const int motor1Pin2 = 18; // номер контакта для IN2 драйвера двигателя
const int buttonPin = 22;  // номер контакта для кнопки
int buttonState = 0;       // переменная для хранения статуса кнопки

enum States {
     STATE_WAIT,  // Состояние ожидания
     STATE_START, // Состояние набора оборотов
     STATE_MOVE,  // Состояние движения на одной скорости
     STATE_BRAKE  // Состояние торможения до полной остановки
};
uint8_t state;  // Переменная для хранения текущего состояния

// задаем свойства ШИМ-сигнала:
const int freq = 5000;     // Частота
const int ledChannel = 0;  // Канал,генерирующий PWM-сигнал для motor1Pin2
const int ledChannel_1 = 1;// Канал,генерирующий PWM-сигнал для motor1Pin1
const int resolution = 8;  // Разрешение
int dutyCycle;
int dutyCycle_1;

void setup() {
  ledcSetup(ledChannel, freq, resolution); // настраиваем ШИМ-сигнал согласно свойствам, заданным выше
  ledcSetup(ledChannel_1, freq, resolution);
 
  pinMode(motor1Pin1, OUTPUT);
  pinMode(motor1Pin2, OUTPUT);
  pinMode(buttonPin, INPUT);
 
  ledcAttachPin(motor1Pin1, ledChannel_1); // где: motor1Pin1 - контакт для вывода PWM, ledChannel - канал,генерирующий PWM-сигнал для motor1Pin1
  ledcAttachPin(motor1Pin2, ledChannel);
  state = STATE_WAIT;                      // Состояние после перезагрузки
 
}

void loop(){
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH)  {
    state = STATE_START;          // Если кнопка удерживается, включить Состояние набора оборотов
    state = STATE_MOVE;           // и перейти в Состояние движения на одной скорости
  } else {
    state = STATE_BRAKE;          // Если кнопка Не удерживается, включить Состояние торможения до полной остановки
    state = STATE_WAIT;           // и перейти в Состояние ожидания
  }
 
  switch (state) {
    case STATE_WAIT:
      dutyCycle_1 = 0;  // pin5
      dutyCycle = 0;    // pin18
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    case STATE_START:
      dutyCycle_1 = 50;
      dutyCycle = 50;   
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    case STATE_MOVE:
      dutyCycle_1 = 100;
      dutyCycle = 100;   
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    case STATE_BRAKE:
      dutyCycle_1 = 10;
      dutyCycle = 10;   
      ledcWrite(ledChannel_1, dutyCycle_1);
      ledcWrite(ledChannel, dutyCycle);
      break;
    }
 
}
 

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
Переделал, так правильно?
Да
Кстати, state можете объявлять уже как States state, и если попытаетесь запихнуть туда что-то другое, кроме типов состояния, то получите ошибку (или варнинг, не помню точно).

Да, я понял. Между ними должна быть пауза в виде набора оборотов.
Да, по факту там должен быть триггер, который переключит стадию (когда скорость достигнет максимума, или когда опустится до 0)

К блоку со switch (state){} тоже есть свои вопросы, но это уже потом.
 

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Так и что тут "улучшено"?

Каждый заход в loop() приведет к проверке кнопки и ПРИНУДИТЕЛЬНОЙ установке состояния сначала в STATE_START и тут же следом (мгновенно, через пару команд .. 200нсек) в состояние STATE_STOP, или в иную пару.. кнопка проверяется КАЖДЫЙ заход в цикл (ещё раз)..

Толку-то от вашего "автомата состояний"? Я уже молчу про "дребезг контактов" кнопки, который успеет попрыгать туда-сюда-обратно раз 30 пока Вы ее только прожимаете..

Используйте макрос EveryMillis() и все встанет на места..
 

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Да, по факту там должен быть триггер, который переключит стадию (когда скорость достигнет максимума, или когда опустится до 0)
Да нет жеж. Автомат Состояний и есть ваш "триггер".. Кстати, "кнопка" как объект программы - ровно такой же "автомат состояний", чего в коде нет ни в одном глазу.. ;)
 

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
@Arhat109, Под вашими словами подписываюсь, но) От простого к сложному, предлагаете сейчас нагрузить человека прерываниями и тд и тп?)
Пусть сделает мало-мальски рабочую конструкцию, а тогда уже будет думать над улучшениями и переделками
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,191
1,281
Москва
Я выше предложил и даже показал как работает другой метод всего с 2-мя состояниями: теущее и нужное. И далее текущее стремиться к нужному. Кмк так проще
 
  • Лойс +1
Реакции: Arhat109

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
@Lumenjer, Да не надо тут никаких "прерываний" .. все до безобразия просто, типо такого:

C:
// Управление двигателем  State Machine
const int motor1Pin1 = 5;  // номер контакта для IN1 драйвера двигателя
const int motor1Pin2 = 18; // номер контакта для IN2 драйвера двигателя
const int buttonPin = 22;  // номер контакта для кнопки
// задаем свойства ШИМ-сигнала:
const int freq = 5000;     // Частота
const int ledChannel = 0;  // Канал,генерирующий PWM-сигнал для motor1Pin2
const int ledChannel_1 = 1;// Канал,генерирующий PWM-сигнал для motor1Pin1
const int resolution = 8;  // Разрешение

const int BTN_PERIOD = 100;   // период опроса кнопки для исключения дребезга, ок. 0.1сек
const int STATE_PERIOD = 200; // период изменения скорости мотора, ок. 0.2сек
const int DSPEED = 1;          // "ускорорение" прироста или уменьшения ШИМ на каждом цикле

// Состояния кнопки: отпущена или нажата
typedef enum { BTN_UP, BTN_DOWN } BtnEnum;

// Состояния моторов машинки (или чего ишо)
typedef enum {
    STATE_WAIT   = 0        // Состояние ожидания
    ,STATE_START = 1        // Состояние набора оборотов
    ,STATE_MOVE  = 2        // Состояние движения на одной скорости
    ,STATE_BRAKE = 3        // Состояние торможения до полной остановки
} States;

int     GlDutyCycle;
int     GlDutyCycle_1;
BtnEnum GlBtnState = BTN_UP;     // переменная для хранения статуса кнопки
States  GlState = STATE_WAIT;    // Переменная для хранения текущего состояния

void setup() {
    // @TODO вынести в функцию настройки "кнопка"
    pinMode(buttonPin, INPUT);
    GlButtonState = BTN_UP;

    // @TODO вынести в функцию настройки "мотора"
    pinMode(motor1Pin1, OUTPUT);
    pinMode(motor1Pin2, OUTPUT);
    ledcSetup(ledChannel, freq, resolution);
    ledcSetup(ledChannel_1, freq, resolution);
    ledcAttachPin(motor1Pin1, ledChannel_1);
    ledcAttachPin(motor1Pin2, ledChannel);
    GlState = STATE_WAIT;
    GlDutyCycle = GlDutyCycle_1 = 0;
}

void loop(){

    EveryMillis(BTN_PERIOD, {
        uint8_t btnCurrent = digitalRead(buttonPin)
        if( btnCurrent != GlBtnState){
            GlBtnState = btnCurrent
            switch( GlBtnState ){
            case BTN_UP:   GlState = STATE_START; break;
            case BTN_DOWN: GlState = STATE_BRAKE; break;
            }
        }
    })
   
    EveryMillis(STATE_PERIOD, {
        switch( GlState ){
        case STATE_WAIT:
            // тут в общем-то тоже это не нужно т.к. скорость не меняется
            GlDutyCycle_1 = 0;  // pin5
            GlDutyCycle = 0;    // pin18
            ledcWrite(ledChannel_1, GlDutyCycle_1);
            ledcWrite(ledChannel, GlDutyCycle);
            break;
        case STATE_START:
            // @TODO выделить в отдельную функцию изменения скорости
            if( GlDutyCycle < MAX_SPEED ){
                GlDutyCycle += DSPEED
                ledcWrite(ledChannel, GlDutyCycle);
            }
            if( GlDutyCycle_1 < MAX_SPEED ){
                GlDutyCycle_1 += DSPEED
                ledcWrite(ledChannel_1, GlDutyCycle_1);
            }
            // Разгон завершен? Переходим к движению
            if( GlDutyCycle >= MAX_SPEED || GlDutyCycle_1 >= MAX_SPEED ){
                GlDutyCycle = GlDutyCycle_1 = MAX_SPEED;
                GlState = STATE_MOVE;
            }
          break;
        case STATE_MOVE:
            // нечего тут делать.. уже разогнались
            break;
        case STATE_BRAKE:
            // @TODO выделить в отдельную функцию изменения скорости, она же в целом
            if( GlDutyCycle > 0 ){
                GlDutyCycle -= DSPEED
                ledcWrite(ledChannel, GlDutyCycle);
            }
            if( GlDutyCycle_1 > 0 ){
                GlDutyCycle_1 -= DSPEED
                ledcWrite(ledChannel_1, GlDutyCycle_1);
            }
            // Торможение завершено? Стоим дальше
            if( GlDutyCycle <= 0 || GlDutyCycle_1 <= 0 ){
                GlState = STATE_WAIT;
            }
            break;
        }
    })
}
Вместо того, чтобы вдумчиво перечитать предложенное выше, автор начал городить невесть что.. :( Ну все же "разжевано", дальше некуда.. :(

P.S. не компилировал, и даже не смотрел, ибо нарисовано "на салфетке" (блокноте) в системе где нет вообще ничего кроме интернета.. ;)
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,191
1,281
Москва
автор начал городить невесть что.
Это обычная история у новичков. Усложнять задачу до безобразия! Это проходит )

Еще раз. 2 состояния скорости, то что надо и то , есть.
Если то что надо равно тому что есть, то либо едем равномерно либо стоим. Если текущая скорсть 0 - стоим. ИНачае - равномерно едем
Если то что надо больше того что есть - ускоряемся
Если то что надо меньше того что есть - тормозим.
 
  • Лойс +1
Реакции: Arhat109

mcvall

✩✩✩✩✩✩✩
15 Ноя 2020
33
3
И то и другое делается "левой задней ногой" .. дал же ссыль и на временные автоматы и на автоматы состояний .. ;)
... и без "конченных автоматов". ;)
Какой там ногой? Вы уж определитесь:) Далее, где Вы увидели дребезг кнопки? У меня сенсорные кнопки, нет там дребезга. То, что вы все исправляли мой код мне особо не помогло, т.к. думали Вы, а не я. Мне будет проще и понятнее когда укажут на ошибку. Новичек решил на автомате, что плохого? Без обид.
Я выше предложил и даже показал как работает другой метод всего с 2-мя состояниями
А я только 3 дня назад узнал, что Const int можно дефайнами заменить.
 
Изменено:

Arhat109

★★★★✩✩✩
9 Июн 2019
473
203
Посмотрите внимательно код, он как раз в автоматом стиле.

P.S. насчёт отсутствия дребезга у сенсора, рекомендую погуглить "ложные срабатывания сенсорной кнопки" там своих заморочек в достатке..