Pull to refresh

Comments 28

Спасибо за статью! Позволю себе пару замечаний. Если статья для чайников, то я бы добавил еще диаграмму состояний для большей наглядности. В статье вы вводите свои определения "… Назовём их событие..." Во избежании путаницы, думаю было бы здорово упомянуть, что из статьи является чем в теории автоматов. Описать входной алфавит, множество конечных состояний,… в терминах из теории.
Думаю да, чтоит картинок добавить.
Я хотел мультфильм, но это уж для совсем-совсем чайников получится.
Теорию как раз не хотел привлекать тк всё-равное ЦА её читать не будет. Старался понятным непосвящённому языком писать.
А ещё таблицу указателей можно засунуть во флеш микроконтроллера (progmem в терминах ATMEGA, как у ардуины не знаю)
Можно засунуть в PROGMEM, но оттуда доставать адрес надо каждый раз вычисляя смещение.
Красота же метода как раз в вызове из двумерного массива.
Вмето оброащения к элементу массива по индексу надо написать вызов специальной функции, вот и всё.
action[btState][e] заменить на getAction(btState,e), которая возвращает ссылку на функцию.
Можно так, да. Хммм…
Надо поэкспериментировать будет.
В большинстве случаев все чайники сразу хотят реализовать меню. Мне почему-то кажется, что реализация через автоматы будет не очень простой и наглядной.
Почему?
У меня есть меню на МКА. Там наоборот просто: Кнопки вверх, вниз и пыщ. Длинный пыщ вызывает меню, он же закрывает.
Имеем меню с состоянием «текущий пункт» и событиями старт, вверх, вниз, ентер, выход. События прилетают от кнопочек, как я описывал в статье. Во вложенном меню будет «текущий путь».
По желанию можно добавить события «длинный вверх» и такой же вниз как перемотку в начало и конец.
Я к тому, что, если абстрагироваться от конкретных пунктов меню и реализации движения строчек на экране — то всё получится логично и красиво.
Хитрое поведение кнопок можно решить через мой класс SmartButton переопределяя его абстрактные методы, но это уже другая статья будет. Думаю написать вскоре.

Спасибо за статью, но хочу отметить, что ардуиновский компилятор совместим с C++11, так что смело можно использовать enum class вместо enum, значительно повышает читабельность кода и помогает избежать ошибок. И для констант стоит использовать const, а не препроцессор

enum class — интересно. можно указать byte вместо int.
работает! спасибо. хехе, век живи, век учись.
А есть ли какая-то синтаксическая конструкция модная типа сишного
struct {
byte flag : 1,
byte pin : 4
}


Вот применительно к enum class бы? Туда же упаковать?
А то 11 байтов много.
— 4 время, никуда не деться, хотя да, можно в 13 бит запихать тк нас только хвост интересует от времени.
— 1 пин.
— 2 словарь.
— 2 статус.
Итого 9. Хмм… Где ещё два байта?
Переписал на enum class input: byte { };
получилось 9 байт.
Хмм… а должно быть 7.

Запушил новый вариант с enum class, мне понравилось.
Позволю себе критику. Ибо являюсь той самой целевой аудиторией.
Библиотека сделана не «по-ардуиновски». Слишком сложно всё организовано. Примеров нормальных нет. На гитхабе тоже.
void toDispToggle(enum state st, enum input in) {
Serial << «toDispToggle » << st << " " << in << endl;
if (dMode == 0 || dMode == 1) {
dMode ^= 1;

Вот это — НЕ arduino way.
Не должно быть в коде такого. Все эти функции должны быть как-то разделены. Отдельно обработчик кнопки, отдельно обработчик события.
Посмотрите, как сделан Serial. Ничего лишнего. Я не должен парить себе мозги, а где же и что вызывается, и в какое место кода мне надо сунуть обработчик по приходу пакета. Я не должен создавать функции.

Как мне видится удобный обработчик клика в соответствии с концепцией Arduino.
В начале создаём объект типа smartbutton. Как-то типа «SmartButton mybutt».
А в loop мы получаем его состояние, и делаем нужные дела, типа «if (mybutt.pressed) {......}».

Можно сколько угодно говорить, что «а привязать функцию мне удобнее». Но чайникам — неудобно писать кучу кода с неочевидными аргументами.

Кстати, невозможность использовать delay — это как бы тоже не плюс. Smartdelay-ем невозможно пользоваться.
В статье написано не как сделать библиотеку, а как сделать МКА. То есть, вообще, а не только кнопку.

Как упаковать её в класс и спрятать в отдельную папку — я напишу в следующей статье.
Я сделал ООПный интерфейс к кнопкам, можно наследовать базовый абстрактный класс и делать свои классы кнопок для меню, для управления, для двойного клика итп.

В асинхронном режиме же, писать в loop() что-то типа if (bt.pressed()) { код } не очень правильно. Лучше код вставлять в том месте, где собственно случается событие. Без классов если, я указал где, для примера. Событий много, какие нужны лично вам, я угадать не могу.

В модели ООП для МКА с кнопкой я делаю так:

class toggleSmartButton: public SmartButton {
  private:
    byte myToggle=0;
  public:
    toggleSmartButton(int p) : SmartButton(p) {}  // Надо вызвать родительский конструктор, увы.
    virtual void onClick();		// Метод, который переопределяем. В базе он пустой.
};

// делаем объект. пин12.
toggleSmartButton bt(12);

// Этот метод переключает по клику
void modeSmartButton::onClick() {
  if (myToggle) {
     // Код для включения
      Serial.println("ON");
  } else {
    // Код для выключения
      Serial.println("OFF");
  }
  myToggle=!myToggle;
}

void loop() {
  bt.run(); // Надо положить сюда этот вызов.
}


Функция delay() имеет удобство только для чайников или чего-то однопоточного и неспешного. Ну, нет многозадачности в ардуине, нет. delay() блокирует выполнение, чип тупо стоит и ждёт.

Для SmartDelay есть интерфейс ООП, пожалуйста, управляйтесь событиями, делайте МКА для последовательности кусков кода с задержками. Это ардуинский путь как раз.
код1
delay(d1);
код2
delay(d2);
Можно преобразовать к МКА в четыре состояния: код1, ждём d1, код2, ждём d2 и ваш этот код будет выполняться не мешая задержками другому коду.

Если же надо просто выполнять некий код раз в N миллисекунд, то SmartDelay вообще отлично подходит.
Как упаковать её в класс и спрятать в отдельную папку — я напишу в следующей статье.
Я сделал ООПный интерфейс к кнопкам, можно наследовать базовый абстрактный класс и делать свои классы кнопок для меню, для управления, для двойного клика итп.


После следующей статьи стало лучше, спасибо!
Правда, чтобы сделать как Serial, всё равно приходится наворачивать лишнее, но это терпимо, и как там говорилось, при возникновении индивидуальной непереносимости — может быть убрано в отдельный файл.

В асинхронном режиме же, писать в loop() что-то типа if (bt.pressed()) { код } не очень правильно. Лучше код вставлять в том месте, где собственно случается событие.

Лучше, кто же спорит. Только вот понимание как организовать структуру кода, чтобы всё было на своих местах — это черта, далеко не присущая начинающим.

Функция delay() имеет удобство только для чайников или чего-то однопоточного и неспешного. Ну, нет многозадачности в ардуине, нет. delay() блокирует выполнение, чип тупо стоит и ждёт.

Именно. Собственнно, если я юзаю делей в программе — я понимаю, что могу потеряю всё, что происходило во время делея (поэтому я обычно пользуюсь делеем на 20-50 мс, либо вообще не использую его).

Отдельное спасибо за пример для дебилов чайников обычных людей типа меня в следующей статье. =)
да, с той статье специально вам написал :)
дал, так сказать, рыбу и показал, как использовать удочку.

чтобы сделать как Serial


Это как? Так?

if (button.available()) {
  bt=button.read();
}


Не получится. То есть, можно, если речь идёт о кнопках для калькулятора. Для всего прочего это неудобно, жрёт память и тормозит.
Примеры для SmartButton да, надо написать поинтереснее и на весь интерфейс класса. Согласен.
Как мне видится удобный обработчик клика в соответствии с концепцией Arduino.
В начале создаём объект типа smartbutton. Как-то типа «SmartButton mybutt».
А в loop мы получаем его состояние, и делаем нужные дела, типа «if (mybutt.pressed) {......}»


Это не лучший подход. Особенно для любителей delay(). Прикинь, ты уже три раза нажал на кнопку и отпустил, а выполнение только добралось до твоего if (key.pressed()) через задержки.

Я предлагаю асинхронное выполнение. То есть, клик обрабатывается в том месте, где случился. В МКА в статье, я указал место, где вставлять свой код. В SmartButton нет функции pressed, но есть метод onClick() пустой, виртуальный, и его надо определить унаследовав класс. Как вариант, можно сделать колбеки, функции, что будут вызываться по событиям, но они жрут память тоже тк событий много, на каждое надо указатель и не все нужны.

Самого главного нет: сколько ресурсов ушло на опрос одной кнопки. РОМ, РАМ, процессорное время.

Много.
Это не самый эффективный код, конечно же.
Но с чего-то начать надо. Статья не про кнопку, а про МКА.
6 байтов на базовый класс уходит.
я посчитал в следующей статье.
такты не считал, конечно :)

А нас учили, что основой КА является граф переходов, а не табличка. Граф можно представить в виде таблички, это да, но это другой вопрос...

Граф лично мне показался менее наглядным.
Выше уже писали, что неплохо было бы и графом проиллюстрировать. Я, наверное, так и сделаю.
вопрошающих новичков надо отправлять не в википедию, а к трудам Шалыто (ключевая фраза для поиска «SWITCH-технология»)
Спасибо за статью!
Есть, всё таки Люди в этом мире, которые в гугл не отправляют.
Ещё раз спасибо, теперь за Вашу человечность)
Не за что. :)
Если что не понятно — спрашивайте.
Sign up to leave a comment.

Articles