Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/DetSimen/Arduino-MessageList

работаем по взрослому, с очередью сообщений
https://github.com/DetSimen/Arduino-MessageList

arduino message-queue

Last synced: 2 months ago
JSON representation

работаем по взрослому, с очередью сообщений

Awesome Lists containing this project

README

        

# Arduino-MessageList

Другая идея, прямо вытекает из https://github.com/DetSimen/Arduino-. Дело в том, что процедура срабатывания таймера выполняется в контексте прерывания, поэтому не может быть длинной, да и вапще, нехорошо это. Поэтому, придумывать я ничего не стал, просто взял идею из не всеми нами любимой Виндовс. Нужно создать на Ардуине простенькую, плохонькую, но очередь сообщений. Каждый датчик меряет какие-то значения и генерирует какие-то события. Если ждать их наступления в loop(), то эта несчасная функция в оконцовке вырастает до неприемлемо огромных значений, а, как мы помним из детсадовского курса пограммирования, если текст фунции не помещается в один экран - значит ее написала очень плохая и неумная киса, и функцию надо разбивать на более мелкие части, а кису - по спине лопатой и на биржу труда. Поэтому, для своих проектов я применяю достаточно простое решение, существенно облегчающее (мне) жизнь.

Для начала определим, какие сообщения мы хотим видеть в своей программе, и запишем их в файл DEF_Message.h. Файл имеет самую обычную простецкую структуру, ничего, кроме #define там нет.

#pragma once

// коды используемых сообщений, размер - byte

// system messages

#define msg_Empty 0x00
#define msg_Error 0x01
#define msg_Paint 0x02

// user messages

#define msg_SecondTick 0x10 // секунда тикнула
#define msg_ReadMQ2 0x12 //читать данные с датчика MQ2
#define msg_MQ2Changed 0x13 // показания c MQ2 изменились
#define msg_ReadKey 0x14 // читать клавиатуру

#define msg_KeyDown 0x20 // кнопка нажата
#define msg_KeyUp 0x21 // кнопка отпущена

#define msg_VentON 0x22 // включить вытяжку
#define msg_VentOFF 0x23 // выключить вытяжку

Просто определяем числовые значения для всех используемых нами сообщений 0-255 (1 байт). Смотрим только, чтобы разные сообщения имели разные номера, иначе аяяй (будет всегда выполняться функция обработки первого сообщения)

первые 16 сообщений я выделил как системные, разницу между ними и другими расскажу позже.

Теперь о том, что есть сообщение. Сообщение, сопсно, это структура, содержащая код сообщения и два параметра типа int,
описана как

struct TMessage
{
public:
byte Message; // код сообщения, константа, описанная в DEF_Message.h
word LoParam; // два необязательных параметра, младший
word HiParam; // и старший

TMessage(); // конструктор, создает системное пустое сообщение msg_Empty
TMessage(byte msg, word loparam, word hiparam = 0); // конструктор. Полностью создает сообщение со всеми параметрами
TMessage(byte msg); // конструктор. создает сообщение с кодом msg и обоими параметрами равными нулю

bool operator ==(TMessage msg); // переопределенный оператор сравнения. С другим сообщением
bool operator ==(PMessage msg); // с указателем на сообщение
bool operator ==(byte msg); // и просто с числом (с кодом сообщения) (для внутреннего использования)
};

Далее. Одно сообщение малоинтересно, нужно собрать их в очередь и по очереди обрабатывать.
Следующий класс, TMessageList, является агрегатором записей типа TMessagе, грубо говоря - массивом,
организованным в очередь, первым пришел - первым выйдешь (обработаешься). выглядит это кактотак:

class TMessageList
{
private:
PMessage *Items; // массив указателей на сообщения

byte flength; // емкость очереди (максимальное число сообщений в очереди) задается в конструкторе
byte fcount; // счетчик сообщений, которые находятся в очереди
void DeleteFirst(void); // удалить первое сообщение, со сдвигом остальных вперед
bool FindMessage(PMessage msg); //проверить, есть ли такое сообщение в очереди
public:

TMessageList(); // конструктор. По умолчанию максимальная ёмкость очереди - 16 сообщений
TMessageList(byte length); // а тут емкость можно задать самому (макс значение - 255, но память сломаеца гораздо раньше)

bool Available(); // проверка, что очередь сообщений не пуста (fcount>0)

TMessage GetMessage(); // взять сообщение из очереди

bool Add(byte msg, word lo, word high); // добавить полностью описанное сообщение в очередь, с кодом и параметрами
bool Add(byte msg); // добавить в очередь сообщение с кодом msg и обоими параметрами == 0
bool AddEmpty(void); // добавить в очередь пустое сообщение (msg_Empty, 0, 0)
bool Paint(void); // добавить в очередь сообщение перерисовать экран (msg_Paint,0,0)
bool Error(int errornum); // добавить в очередь системное сообщение об ошибке, errornum - номер ошибки
// аналог Add(msg_Error, errornum, 0)
bool Error() { return Add(msg_Error); } // добавить в очередь системное сообщение об ошибке с обоими параметрами == 0

byte Count(); // отдает число сообщений в очереди, которые ждут обработки

};

Например. В сетапе создаем таймер, который будет тикать раз в секунду (часы будем строить)

byte hour, minutes, seconds = 0; // глобальные переменные для времени
THandle hClockTimer;

void setup()
{
MessageList=new TMessageList(); // очередь глубиной 16 сообщений
TimerList.AddSeconds(tmrClock,1); // функция tmrClock будет вызываца 1 раз в секунду
}

void tmrClock(void)
{
seconds++;
if (seconds<60) return; // так как мои часы показывают только часы и минуты, если меняются только секунды - выходим
seconds=0; minutes++;
if (minutes>59){minutes=0; hour++;};
if (hour>23) hour = 0;
MessageList.AddPaint(); // добавляем в очередь сообщение msg_Paint, экран надо перерисовать,
// потому что изменились показания часов или минут
}


Вот чем отличаются системные сообщения - для них у очереди сообщений есть специальные функции для быстрого помещения такого сообщения в очередь

а в loop() начинаем крутить бесконечную карусель

void loop()
{

if (NOT MessageList->Available()) return; // если очередь пуста - выходим
TMessage msg = MessageList->GetMessage(); // иначе - берем первое сообщение и обрататываем

switch (msg.Message)
{
case msg_Empty: break; // пустое сообщение, просто убираем его из очереди
case msg_Paint: evtPaint(); break; // перерисовать икранчег
case msg_MQ2Changed: evtMQChange(msg); break; // сигнал с датчика дыма
default: Serial << "Error in MessageList\n";
}
}

void evtMQChange(TMessage &msg)
{
if (msg.LoParam>250) Vent.On(); else Vent.Off(); // включить вытажку, если значение с дыма > 250
}

void evtPaint()
{
// сопсно, здесь и перерисовываем экран
}

так как сообщения могут иметь параметры, можно в функцию обработки сообщения передавать эти параметры для последующей обработки. Ну и так как параметры имеют тип int, ничто не мешает передавать всё, что угодно, включая указатель (да! даже на класс) и приводить к нужному типу в обрабоччике.

применение:
скачать 3 файла, 2 - h, 1 - срр.

бросить в папку с проектом, подключить хидеры.

обьявить глобально extern TMessageList *MessageList;

в сетапе написать MessageList = new TMessageList();

Если очередь планируеца больше 16 сообщений, написать MessageList = new TMessageList(ваше число);

дописать в DEF_Message.h коды ваших сообщений.

в функции срабатывания таймеров добавлять ваши сообщения MessageList->Add(msg_xxx, loparam, hiparam)

в loop() добавлять в оператор switch обработку своих сообщений через case msg_xxx: ..... break;

Наслаждацца.

По вопросам пишите впочту [email protected] или на форумы amperka.ru и arduino.ru