Запуск серии и её остановка

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
Товарищи специалисты, подскажите как решить следующую задачу

Есть 3 кнопки аналоговой клавиатуры: load, unload и motorstop.

при срабатывании варианта нажатия isClick кнопки load (load.isClick) должна запуститься выдача сообщений в сериалпорт "StartLoad" с интервалом 100ms на срок 10000ms, но эту серию можно прекратить досрочно кликом по кнопке motorstop (motorstop.isClick) и выдать при этом сообщение "MotorStop", также запущенную серию можно прекратить досрочно длинным нажатием той же кнопки load в шаговом варианте (load.isStep) чтобы уже силами библиотеки привязанное к этому варианту сообщение "LoadStep" выдавалось с интервалом заданным в настройках библиотеки.

Тоже самое надо сделать и по отношению к кнопке unload - если срабатывает unload.isClick запускается серия сообщений "StartUnload" с интервалом 100ms на срок 1мин, но эту серию можно прекратить досрочно кликом по кнопке motorstop (motorstop.isClick) и выдать при этом сообщение "MotorStop", также запущенную серию можно прекратить досрочно длинным нажатием той же кнопки unload в шаговом варианте (unload.isStep) чтобы начало выдаваться сообщение "UnloadStep" с интервалом заданным в настройках библиотеки пока кнопка удерживается.

Пробовал запускать серию через for (;;) , но как-то оно все хреново работало

C++:
#include "GyverButton.h" /// https://github.com/AlexGyver/GyverLibs/releases/download/GyverButton/GyverButton.zip

GButton load;
GButton unload;
GButton motorstop;

void setup() {
Serial.begin(9600);
}

void loop() {
int analog = analogRead(7);
motorstop.tick(analog < 940 && analog > 865); //2
load.tick(analog < 1024 && analog > 940); //1
unload.tick(analog < 865 && analog > 800); //3

if (motorstop.isSingle()) Serial.println("mStopSingle");
if (motorstop.isHold()) Serial.println("mStopHold");
if (motorstop.isDouble()) Serial.println("mStopDouble");

motorstop.resetStates();
if (unload.isStep()) {Serial.println("UnloadStep");}
if (unload.isClick()) {Serial.println("StartUnload");}
if (load.isStep()) {Serial.println("LoadStep");}
if (load.isClick()) {Serial.println("StartLoad");}
}
 
Изменено:

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

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
1) вместо isStep лучше использовать isHolded, isStep с определенным интервалом выдает тру, удобно когда надо менять переменную. А isHolded один раз срабатывает, когда кнопка нажат больше определенного времени.
2) не совсем понятно, зачем 2 разных способа прекращения одного события. Вполне достаточно одного, это не супермаркет для разнообразия.

На мой взгляд Вы пошли не верным путем.
Если посмотреть на все это с иной точки зрения
Есть какое то устройств. У него есть 3 состояния: 1 мотор выключен, 2) мотор загружает, 3) мотор выгружает. Логично именно в такой последовательности. Так же имеем временные интервалы для каждого периода. Для 1-го бесконечность, для 2го 10 сек, для 3-60. Тут могу путать , но вроде так. И пусть у этого устройства будет таймер. Один на все.
Запускам программу.
Сетап
Состояния мотора=1; Время входа в состояние=0;

далее цикл loop

Однократно нажата кнопка на загрузку
{
если текущее состояние равно 1 (т.е. во время нажатия устройство было выключено)
{
Время входа в состояние=0;
состояние =2;
}
иначе если текущее состояние равно 2
{
если прошло сколько то времени с последнего захода выводить сообщение.
(вот тут вижу 2 пути: в лоб, сделать отдельный таймер и проверять его с текущим временем или считать число n=(millis()-Время входа в состояние)/100 , и если новое n не равно старому, то выдать сообщение, можно сэкономить 3 байта)
}
иначе если текущее состояние равно 3
{
может ли быть такое ?, если действия те же, что и при состоянии 1, то добавить в условие 1-го состояния.
}
}
Если долго нажата кнопка на загрузку.
{
Если состояние 1 или 3, то что делаем ?
если состояние 2, то мотор переходит в состояние 1. Запоминаем время.
}

Как то так, да, условий получается много, но работать будет логично
 

bort707

★★★★★★✩
21 Сен 2020
2,859
850
@martinways, у меня дежавю... Я этот вопрос от вас читаю уже четвертый раз.
Один раз Вам Похабыч даже целое эссе написал, а вы все снова и снова
 
Изменено:

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
Как то так, да, условий получается много, но работать будет логично
Нет, мне нужно именно так как я написал в первом посте, объясняю почему. Представим себе, что мотор крутится только когда по сериал порту поступает серия одинаковых сообщений с определенным интервалом, например "load"; "load"; "load"; ,тоже самое и для выгрузки "unload"; "unload"; "unload"; , так вот допустим я хочу чтобы был автоматический и ручной режим загрузки или выгрузки, то есть нажал на кнопку и держишь, сработал режим load.isStep() и пошел поток сообщений "LoadStep", мотор крутится пока держу кнопку. А допустим хочу, чтобы загрузка шла не держа кнопку, автоматический режим, кликнул по кнопке load.isClick() и запустился поток сообщений, мотор крутится пока не нажмешь motorstop ИЛИ ЖЕ если я захочу во время этого автоматического потока перевести в ручной режим пока держишь кнопку, то есть во время потока нажал ту же кнопку load и держишь, сработал тот же load.isStep(), автоматический поток отключается и идет поток сообщений привязанный к load.isStep(), отпустил - мотор остановился, исходное положение. Это все чисто для повышения удобства пользования устройством, "юзабилити"))
 
Изменено:

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
@martinways, у меня дежавю... Я этот вопрос от вас читаю уже четвертый раз.
Один раз Вам Похабыч даже целое эссе написал, а вы все снова и снова
Дружище, дежавю у Вас скорее из-за одинаковых названий кнопок, но это все я привожу для примера, функции в тех темах были не такие. Разве что я недавно создавал такую тему в разделе заказов, вот там Вы могли такое прочитать точно, но не в разделе помощи.
 

poty

★★★★★★✩
19 Фев 2020
2,956
886
@martinways, вопрос же не в функциях. Если Вы не учитесь на примерах, которые Вам помогают делать, то каждый раз, когда у Вас изменится хоть что-то, совсем маленькое и незначительное, Вы будете обращаться за помощью. Разберитесь уж один раз со всем этим и сможете легко вписывать другие команды сами.
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
@martinways, вопрос же не в функциях. Если Вы не учитесь на примерах, которые Вам помогают делать, то каждый раз, когда у Вас изменится хоть что-то, совсем маленькое и незначительное, Вы будете обращаться за помощью. Разберитесь уж один раз со всем этим и сможете легко вписывать другие команды сами.
Ну конкретно на тех решениях и примерах, которые предложили Вы и другие пользователи в моих предыдущих двух темах я не представляю как применить их в данной ситуации, там такого не было (запуск автоматического потока сериал сообщений по клику) и получается, что совет "Разберитесь уж один раз со всем этим" - это значит берешь учебник C++, запасаешься парой лет времени и вперед))) Вы же большинство тут с высшим образованием, настоящие программисты, основы получали в институтах, а как мне человеку со стороны это изучить самостоятельно да еще и за короткое время, ведь я не могу откладывать свой проект устройства на 3 года, вот и приходится на чужих примерах и подсказках строить скетчи. Я уже несколько устройств сделал так и вносил свои модификации в код, разбираясь как работает тот или иной прием. Конечно можно сказать - не хочешь учить С++ с нуля - плати, но проблема в том, что когда разрабатываешь что-то интересное это же творческий процесс и сегодня одна идея появилась, а завтра возникла другая как можно сделать еще лучше, и получается каждый раз платить за каждую мелочь никакой зарплаты не хватит, ведь у программистов-профессионалов есть некая минимальная сумма, за которую они готовы приступать к задаче, даже если эта задача на пару строчек кода и минуту времени и это абсолютно нормально. Вот такая диллема...
Я уже не одну тысячу рублей заплатил за скетчи для предыдущих устройств, но все-равно приходилось их потом доделывать самому из-за возникающих новых идей.

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

Вот 2 мои предыдущие темы, сами посмотрите, что полученные мною знания из тех решений, которые Вы там мне дали, не помогут решить задачу поставленную в этой теме, там совсем другие функции и приемы

 
Изменено:

poty

★★★★★★✩
19 Фев 2020
2,956
886
@martinways, я вообще ни разу никому не заикнулся о деньгах. Знаю, что многие считают, что это неправильно, но я здесь для, в том числе, повышения своего уровня, заработок у меня в другом месте.
Но вот Вы делали тему. Не больше недели назад. В платном разделе. Постановка задачи - один в один. Потом - ещё парочка ваших и парочка чужих. Неужели нет возможности как-то адаптировать то, что подсказали к совсем небольшому изменению, о котором Вы спрашиваете сейчас? Я к тому, что учиться Вам всё равно придётся, иначе Ваши хотелки, которые и сейчас, извините за прямоту, малоинтересны более-менее практикующему программисту, просто будут игнориться здесь, надоедят.
О Вашем вопросе. Для того, чтобы что-то запрограммировать в real-time, нужно разделить задачу. Это больше объектно-ориентированное программирование, но никто в здравом уме не будет Вас ему учить, поэтому придётся это перекладывать на обычный С.
1. Имеется исполнительное устройство, управляемое через сообщения. В Вашем случае команды составляют следующий список:
  • ничего не делать (ничего не выводится в порт);
  • StartLoad;
  • LoadStep;
  • StartUnload;
  • UnloadStep;
  • MotorStop.
Создайте цикл, который делает ровно это с периодичностью 100мс - это должно быть Вам доступно. То, что выводится в порт, пусть будет записано в какой-нибудь строковой переменной.
Отдельно создайте цикл переключения сообщений (записи в строковую переменную нужного значения по нажатию определённой кнопки).
Всё! Задача решена!
Я к чему? В том, что я написал, нет программирования! Это - логика, которая должна быть понята сначала для себя, а потом реализована в коде. И ни при чём здесь институты и прочее... Я, например, заканчивал "институт" в 80-х годах, когда лучшее, что тогда было - PL/1, Fortran...
Потом изучил Pascal. Потом С, С++, Java, ... Сам.
 

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

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
@martinways, Вот тут логичное объяснение применение isStep:
о есть нажал на кнопку и держишь, сработал режим load.isStep() и пошел поток сообщений "LoadStep"
А тут я понял как совсем другое:
также запущенную серию можно прекратить досрочно длинным нажатием той же кнопки load в шаговом варианте (load.isStep) чтобы уже силами библиотеки привязанное к этому варианту сообщение "LoadStep" выдавалось с интервалом заданным в настройках библиотеки.
Ну и обрабатывайте два нажатия кнопок
if (...isSTep()) выдать сообщение LoadStep ; - это будет ручной режим. При этом отмените авторежим
if (... isSingle ) или if (if ..isClick) поставить режима авто и уже отдельным таймером посылать сообщения
if (...isHolded) - Стоп машина.
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
@martinways
Но вот Вы делали тему. Не больше недели назад. В платном разделе. Постановка задачи - один в один.
Да, я же написал предыдущему комментатору, что я создавал именно такую же тему в платном, но мне предложил услуги 1 человек и я пока еще не ответил ему, так как решил все-таки попробовать найти решение бесплатно. Поэтому создание той платной темы в данном вопросе никак мне не помогло.
Я не обладаю терминологией, поэтому конечно же не сильно понимаю, что Вы мне посоветовали и как это сделать. Все, что у меня на данный момент получилось это вот, может хоть с этой наработкой, пусть и не правильной, поможете

C++:
#include "GyverButton.h" /// https://github.com/AlexGyver/GyverLibs/releases/download/GyverButton/GyverButton.zip

GButton load;
GButton unload;
GButton motorstop;

long previousMillis = 0;
long interval = 50;

void runLoad(){
  for (;;){ 
 int analog = analogRead(7);
  motorstop.tick(analog < 940 && analog > 865); //2
  load.tick(analog < 891 && analog > 819); //3
  unload.tick(analog < 1024 && analog > 975); //1
  if (motorstop.isPress()){
   break;
  }
  else{
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval)
  {
  previousMillis = currentMillis;
  Serial.println("StartLoad");
  }
}
}
}

void setup() {
  Serial.begin(9600);
}
void loop() {
  int analog = analogRead(7);
   load.tick(analog < 891 && analog > 819); //3
   unload.tick(analog < 1024 && analog > 975); //1
   motorstop.tick(analog < 975 && analog > 891); //2

if (motorstop.isClick()) Serial.println("MotorStop");
motorstop.resetStates();
if (load.isStep()) {Serial.println("LoadStep");}
if (load.isClick()) {
  Serial.println("StartLoad");
  runLoad();
  }
}
Серия запускается кнопкой load.isClick() и останавливается motorstop.isPress, но во-первых, если нажать два раза load.isClick() , то как-будто запускается две серии одновременно и для остановки приходится два раза жать motorstop, во-вторых, не знаю как внедрить остановку серии длинным нажатием, чтобы сразу начало работать if (load.isStep()) {Serial.println("LoadStep");}, пробовал добавлять в else if, но серия StartLoad не останавливается, а идет в перемешку с сообщениями "LoadStep", ну и самая незначительная хотелка как ограничить выдачу серии на срок например 1 минуту.
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
@martinways, Вот тут логичное объяснение применение isStep:
if (... isSingle ) или if (if ..isClick) поставить режима авто и уже отдельным таймером посылать сообщения
Вот именно как таймерами сделать запуск потока сообщений я не знаю, получилось только сделать запуск при помощи for, см. мой скетч в предыдущем сообщении.
 

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

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
Motorstop.isPress вызывается всякий раз как кнопка нажимается, и не понятно, что будет дальше. То ли 2-ой клик, то ли удержание. Почему не isClick ?
Так я Вам уже написал. Введите переменную режима работы. стоп, туда или сюда. Туда или сюда будут авто режимы, а при удержании будете без изменения режима спунить что хотите в сериал. Это же программирование, Ватсон!
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
Motorstop.isPress вызывается всякий раз как кнопка нажимается, и не понятно, что будет дальше. То ли 2-ой клик, то ли удержание. Почему не isClick ?
Так я Вам уже написал. Введите переменную режима работы. стоп, туда или сюда. Туда или сюда будут авто режимы, а при удержании будете без изменения режима спунить что хотите в сериал. Это же программирование, Ватсон!
В том-то и проблема, представьте, что вы разговариваете с инопланетянином (это я ) и говорите - "возьми ручку со стола" - но получается, что я не знаю как выглядит ручка и как её можно взять:) Поэтому я не понимаю как это сделать - "Введите переменную режима работы"
Могу только смотря на код догадываться что за что отвечает
 

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

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
@martinways,
, представьте, что вы разговариваете с инопланетянином (это я )
Золотые слова! по этому не налегайте на спиртное заказы программ за деньги. Вы описываете одно, Вам это делают, а по факту Вам надо что то другое и в разы проще. С начала Вам надо понять как все работает, потом можно понять что стоит заказать, а что проще сделать.
Попробую подготовить для Вас еще один пример , но на это надо время.
 

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

★★★★★★★
14 Авг 2019
4,159
1,267
Москва
Ну вот такой код нарисовался. Заметьте ! Никто про деньги не говорит!
Имеем 3 кнопки. Красная кнопка (да, у меня реально такая!)- стоп всему. Любое нажатие - стоп действию
Кнопка загрузки. Если кликнуть ей один раз, то включится режим и каждую STEP_L 100 миллисекунду будет выдавать сообщение в порт. Но не больше 50 штук, т.к. версия триальная, то сделал такое ограничение (MAX_STEPS 50). После останов. Если нажать и держать, то будет выдавать по сообщению (немного другому, что бы видно было что другая часть работает) каждую кажется 300 миллисекунду.
Аналогично с другой кнопкой, которая будет работать в обратную сторону. Те же 50 шагов, та же 0,1 сек.

Что-то где-то и мотор:
#define STOP_BTN_PIN  7
#define LOAD_BTN_PIN 5
#define UNLOAD_BTN_PIN 4

#include "GyverButton.h"
GButton load_Btn(LOAD_BTN_PIN , HIGH_PULL);
GButton unload_Btn(UNLOAD_BTN_PIN , HIGH_PULL);
GButton stop_Btn(STOP_BTN_PIN , HIGH_PULL);


uint8_t MODE = 0; // это и есть переключатель режимов.
uint32_t time_mode = 0;
#define STEP_L 100
uint16_t steps_cntr=0;
#define MAX_STEPS 50

void setup() {
  Serial.begin(115200);
}

void loop() {
  load_Btn.tick();
  unload_Btn.tick();
  stop_Btn.tick();

  if  (stop_Btn.isClick())
  {
    Serial.println("Stop");
    MODE = 0;
  }

  if (load_Btn.isStep())
  {
    Serial.println("Load 1");
    MODE = 0;
  }

  if (unload_Btn.isStep())
  {
    Serial.println("unload 1");
    MODE = 0;
  }

  if (load_Btn.isSingle())
  {
    time_mode = millis();
    MODE = 1;
    steps_cntr=0;
  }

  if (unload_Btn.isSingle())
  {
    time_mode = millis();
    MODE = 2;
    steps_cntr=0;
  }

  if (MODE > 0 && (millis() - time_mode) > STEP_L)
  {
    if (MODE == 1) Serial.println("Load AUTO");
    else Serial.println("unLoad AUTO");
    time_mode = millis();
    steps_cntr++;
    if (steps_cntr>=MAX_STEPS)
      {
      Serial.println("Stop AUTO");
      MODE=0;
      }
  }
}
На все про все ушло 20 минут, ну почти не отвлекаясь.

Еще дополню! На мой взгляд 3-я кнопка (стоп) тут лишняя! Отменять можно нажатием любой кнопки в момент работы. А это экономия пина и экономия машинного времени.
 
  • Лойс +1
Реакции: martinways

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
На все про все ушло 20 минут, ну почти не отвлекаясь.
Блин, круто получилось, адаптировал под свою аналоговую клаву - работает как часы! Теперь разобраться бы как это все фурычит и как применять эти приемы в других работах. Буду интегрировать эту конструкцию в свой основной скетч управления устройством. Кнопка стоп не лишняя, уж поверьте. Я на неё еще несколько других функций повесил, а недостатка пинов не ощущаю, так как у меня аналоговая клава Robotdyn на 16 кнопок))) И все кнопки я задействовал плюс на многих кнопках по несколько разных функций. Не хочу показаться долбанным халявщиком, поэтому хотел бы Вас отблагодарить. Спишемся в личке.
 
Изменено: