41 Востаннє редагувалося taburyak (23.02.2018 14:56:20)

Re: Blynk, ESP8266, IoT

Тааак, вже постало питання руба черга на передачу/прийом і пріоритети. Встановлювати на початку і скидувати на кінці прийому/передачі прапорці, я скумєкав де і як. А от як обробляти їх ще треба думкати. І як в чергу ставити? Це типу поки передача чи прийом не звільняться - чекаємо?

42

Re: Blynk, ESP8266, IoT

Передмова

Треба "замути" за допомоги віджету Table в системі Blynk меню. Щоб було зручно додавати, видаляти та розширяти пункти меню при програмуванні. Подобається реалізація мікроменю і захотілось щось подібне реалізувати. Почав переробляти мікроменю на свій лад і стикнувся з такою проблемою:

  • мікроменю - це однорядкове меню де є попереднє, наступне, батьківське і дочірнє меню, та функції зворотнього виклику - коли обрав меню і коли натиснув ввід. На екрані виводиться один пункт меню а доступ до інших пунктів меню та зміни параметрів виконується за допомоги клавішної навігації. На екрані по суті бачимо один пункт меню і все. Все інше гортаємо кнопками.

  • що мені треба! Є таблиця, яка складається з полів (рядків), а поле (рядок) з: Id, Name, Value.  Меню сторінкове. Наприклад перша сторінка це набір рядків для головного меню. Коли тицяти в якийсь рядок меню, то, або потрапляєш на іншу сторінку меню (підменю), або виконується якась функція чи міняється параметр. Ну і так далі. З навігації достатньо мати батьківську сторінку і дочірню (оперуємо вже сторінками, а не пунктами меню)

  • Що я не можу осягнути і зрозуміти! Це як згрупувати пункти меню посторінково і оперувати сторінками, а не окремими пунктами. Ще одне. Ідентифікація на який пункт меню натиснуто йде по Id рядку. Треба якось додати ще те Id до структур, щоб можна було виконувати навігацію та виклик функцій зворотнього виклику

Керування таблицею

Перелічу тільки що потрібно.
Формуються рядки в таблиці таким чином:

Blynk.virtualWrite(V1, "add", id, "Name", "Value");

Оновити рядок:

Blynk.virtualWrite(V1, "update", id, "UpdatedName", "UpdatedValue");

А обробник зміни стану рядка "marked/unmarked", наприклад, має такий вигляд. Це просто для розуміння як працює:

table.onSelectChange([](int index, bool selected) {
    Serial.print("Item ");
    Serial.print(index);
    Serial.print(selected ? " marked" : " unmarked");
  });

Де index це Id рядку, а selected це - "позначено" чи "знята позначка" з того рядку.

Думаю що треба сформувати якесь "сховище" - структуру/макрос, де будуть зберігатись Id для головного меню і всіх інших підменю. А вже якесь окреме "сховище" для кожного рядку меню де будуть зберігатись Id, Parent, Child, SelectCallBack, EnterCallBack, Text, Value.

Тоді я виконую якусь функцію/макрос, яка формує головну сторінку, і вже далі, в залежності який рядок я натисну, викликається чи функція по зміні параметру, чи функція/макрос формування нової сторінки підменю, в залежності від параметрів що зберігає "сховище" для цього рядку, який позначили.

Що вже зробив

файл TableMenu.h:

// tableMenu.h

#ifndef _TABLEMENU_h
#define _TABLEMENU_h

#if defined(ARDUINO) && ARDUINO >= 100
    #include "arduino.h"
#else
    #include "WProgram.h"
#endif

typedef const struct TableMenu
{
    const struct TableMenu *Parent;
    const struct TableMenu *Child;
    void(*SelectCallback)(void);
    void(*EnterCallback) (void);
    const char Text[16];
    const char Value[16];
}TableMenuStruct;

#define MENU_ITEM(Name, Parent, Child, SelectFunc, EnterFunc, Text, Value) \
        extern TableMenuStruct const Parent;   \
        extern TableMenuStruct const Child;  \
        TableMenuStruct const Name = {&Parent, &Child, SelectFunc, EnterFunc, Text, Value}

#define MENU_PARENT         (Menu_GetCurrentMenu()->Parent)
#define MENU_CHILD          (Menu_GetCurrentMenu()->Child)

extern TableMenuStruct NULL_MENU;

TableMenuStruct* Menu_GetCurrentMenu(void);

void Menu_Navigate(TableMenuStruct* const NewMenu);

void Menu_SetGenericWriteCallback(void(*WriteFunc)(const char* Text, const char* Value));

void Menu_EnterCurrentItem(void);
#endif

Файл TableMenu.cpp:

#include "tableMenu.h"

TableMenuStruct NULL_MENU = { 0 };

static void(*MenuWriteFunc)(const char* Text, const char* Value) = NULL;

static TableMenuStruct* CurrentMenuItem = &NULL_MENU;

TableMenuStruct* Menu_GetCurrentMenu(void)
{
    return CurrentMenuItem;
}

void Menu_Navigate(TableMenuStruct* const NewMenu)
{
    if ((NewMenu == &NULL_MENU) || (NewMenu == NULL))
        return;

    CurrentMenuItem = NewMenu;

    if (MenuWriteFunc)
        MenuWriteFunc(CurrentMenuItem->Text, CurrentMenuItem->Value);

    void(*SelectCallback)(void) = CurrentMenuItem->SelectCallback;

    if (SelectCallback)
        SelectCallback();
}

void Menu_SetGenericWriteCallback(void(*WriteFunc)(const char* Text, const char* Value))
{
    MenuWriteFunc = WriteFunc;
    Menu_Navigate(CurrentMenuItem);
}

void Menu_EnterCurrentItem(void)
{
    if ((CurrentMenuItem == &NULL_MENU) || (CurrentMenuItem == NULL))
        return;

    void(*EnterCallback)(void) = CurrentMenuItem->EnterCallback;

    if (EnterCallback)
        EnterCallback();
}

В main.cpp додаю таке, для прикладу, просто початкова сторінка меню з трьох пунктів і як натиснути потрапити у відповідне підменю з одного пункту, натиснувши який повернутись в попереднє меню:

static void Generic_Write(const char* Text, const char* Value);

// Menus  Name | Parent | Child | SelectFunction | EnterFunction | Text | Value
MENU_ITEM(Menu_1, NULL_MENU, Menu_1_1, NULL, NULL, "Menu-1", "->");
MENU_ITEM(Menu_2, NULL_MENU, Menu_2_1, NULL, NULL, "Menu-2", "->");
MENU_ITEM(Menu_3, NULL_MENU, Menu_3_1, NULL, NULL, "Menu-3", "->");

MENU_ITEM(Menu_1_1, Menu_1, NULL_MENU, NULL, NULL, "Menu-1.1", "<-");
MENU_ITEM(Menu_2_1, Menu_2, NULL_MENU, NULL, NULL, "Menu-2.1", "<-");
MENU_ITEM(Menu_3_1, Menu_3, NULL_MENU, NULL, NULL, "Menu-3.1", "<-");
static void Generic_Write(const char* Text, const char* Value)
{
    if (Text && Value)
    {
        //TODO: тут формування рядка таблиці
    }
}

Встановлення функції зворотнього виклику і встановлення початкового меню:

Menu_SetGenericWriteCallback(Generic_Write);
  Menu_Navigate(&Menu_1);

На цьому етапі я зрозумів, що:

  1. в мене не вистачає ще параметру Id, який треба додати до MENU_ITEM і відповідно до структури TableMenuStruct і передавати Id в функцію Generic_Write. Так?

  2. в мене не вистачає ще якогось коду де буде формуватись список з айдішніків для головного меню та всіх підменю. Бо виходить що я викликаю формування одного рядку Menu_1 головного меню, а не списку рядків меню. І якщо повернутись з підменю Menu_1_1 я потрапляю конкретно на Menu_1, а не список з трьох пунктів головного меню. Так?

  3. ще що?

Так що, допоможіть будь ласка, якщо мої здогадки вірні, з кодом (макросом, структурою чи масивом, чи enum) який формує список айдішніків для сторінок меню і як це зв'язати з тим що вже є. Не можу докумекати хоч ти лусни. Хоч щось порадьте шановні.

p.s. особливі сподівання на моїх постійних наставників ReAl та koala :)

43 Востаннє редагувалося taburyak (27.11.2018 12:26:02)

Re: Blynk, ESP8266, IoT

Ще й на це свариться:
error: uninitialized const member 'TableMenu::Text
   TableMenuStruct NULL_MENU = { 0 }

Щодо цього рядку:

TableMenuStruct NULL_MENU = { 0 };

Що не так?

44

Re: Blynk, ESP8266, IoT

up-ну цю тему, зараз вчитуватися ніколи.

З приводу uninitialized const member 'TableMenu::Text — можливо, воно хоче, щоб для маисвів було вказано повне {0, 0, 0, 0, {0}, {0}}. Можливо, хоче лише явні ініціалізатори {0} для масивів (але попередні однак треба вказати).

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1