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} для масивів (але попередні однак треба вказати).

Подякували: taburyak1

45

Re: Blynk, ESP8266, IoT

Вітання панство!

Є масивчик з структурок:

Menu_Item_t const PageStart[] = {Menu1, Menu2, Menu3, Menu4, Menu5};

Є функція, яка приймає як аргумент вказівник на масив з структурок Menu_Item_t:

static void StartTableMenu(Menu_Item_t* page);

І сама функція де хотілось би взнати скільки елементів в масиві і в циклі всі елементи "прогнати":

static void StartTableMenu(Menu_Item_t* page)
{
    table.clear();
    
    int NumItem = sizeof(page) / sizeof(page[0]);

    for (int i = 0; i < NumItem; i++)
    {
        table.addRow((page + i)->Index, (page + i)->Text, (page + i)->Value);        
    }    
}

Але ж, є одне, але! Змінна NumItem завжди буде 1. Як мені переробити, щоб можна було взнати кількість елементів?

46

Re: Blynk, ESP8266, IoT

Робіть отак:

Menu_Item_t const PageStart[] = {Menu1, Menu2, Menu3, Menu4, Menu5, NULL};

та перевіряйте на кінцевий NULL

Подякували: taburyak, pluszz2

47

Re: Blynk, ESP8266, IoT

0xDADA11C7 написав:

Робіть отак:

Menu_Item_t const PageStart[] = {Menu1, Menu2, Menu3, Menu4, Menu5, NULL};

та перевіряйте на кінцевий NULL

От трясця. Це так просто і прийнятно. Щира дяка.

48

Re: Blynk, ESP8266, IoT

taburyak написав:

Як мені переробити, щоб можна було взнати кількість елементів?

Що заважає передавати розмір масиву як параметр ?

static void StartTableMenu(Menu_Item_t* page, int size)
{
    table.clear();

    for (int i = 0; i < size; i++)
    {
        table.addRow(page[i].Index, page[i].Text, page[i].Value);
    }
}
Подякували: fronders1

49 Востаннє редагувалося fronders (15.01.2020 08:27:55)

Re: Blynk, ESP8266, IoT

@taburyak в сі інформація про розмір масиву зберігається тільки в тому ж "контексті" що і об'явлений сам масив (тобто sizeof(arr) поверне розмір масиву в байтах). Якщо масив передати в функцію то передається вказівник (копіюється за значенням в локальну змінну аргумент) і всередині функції  губиться інформація про розмір масиву, там sizeof(arr) поверне розмір вказівника - 4 байта. Тому в сі при передачі масиву в функцію зазвичай використовуbють два аргументи - вказівник та кількість елементів, як написали вище. Щодо NULL як на мене можуть бути нюанси, це підходить тільки для масиву вказівників, інакше NULL, що визначений як "(void *) 0" можна сплутати з порожнім полем, бо у автора масив структур.

Подякували: taburyak, leofun012

50 Востаннє редагувалося taburyak (15.01.2020 09:04:21)

Re: Blynk, ESP8266, IoT

Дякую всім за відповіді. Йдемо далі. Ситуація помінялась.
Було так.
Оголошую структури пунктів меню:

Menu_Item_t const NULL_MENU = { 0, {0}, {0} };
Menu_Item_t const Menu1 = {1, "SSD1306", "--->"};
Menu_Item_t const Menu2 = {2, "Notifycations", "--->"};
Menu_Item_t const Menu3 = {3, "Devices", "--->"};
Menu_Item_t const Menu4 = {4, "Expander 4x4", "--->"};
Menu_Item_t const Menu5 = {5, "Expander PWM", "--->"};

Оголошую масив цих структур для сторінки меню:

Menu_Item_t const PageStart[] = {Menu1, Menu2, Menu3, Menu4, Menu5, NULL_MENU};

Формую сторінку меню:

static void TableMenuWrite(Menu_Item_t const * page)
{    
    table.clear();    

    int i = 0;

    while ((page + i)->Index != 0)
    {
        table.addRow((page + i)->Index, (page + i)->Text, (page + i)->Value);
        i++;
    }    
}

Викликаю функцію так:

TableMenuWrite(PageStart);

Зараз я зрозумів, що краще мати для сторінки не масив структур пунктів меню, а масив вказівників на структури пунктів меню і почав переробляти так:
Оголошую структури пунктів меню (це без змін):

Menu_Item_t const NULL_MENU = { 0, {0}, {0} };
Menu_Item_t const Menu1 = {1, "SSD1306", "--->"};
Menu_Item_t const Menu2 = {2, "Notifycations", "--->"};
Menu_Item_t const Menu3 = {3, "Devices", "--->"};
Menu_Item_t const Menu4 = {4, "Expander 4x4", "--->"};
Menu_Item_t const Menu5 = {5, "Expander PWM", "--->"};

Оголошую вже масив з вказівників:

Menu_Item_t const * PageStart[] = {&Menu1, &Menu2, &Menu3, &Menu4, &Menu5, &NULL_MENU};

Але тепер мені треба якось поміняти функцію формування сторінки меню:

static void TableMenuWrite(Menu_Item_t const * page)
{    
    table.clear();    

    int i = 0;

    while ((page + i)->Index != 0)
    {
        table.addRow((page + i)->Index, (page + i)->Text, (page + i)->Value);
        i++;
    }    
}

Або я не вірно задаю аргумент для виклику функції формування меню:

TableMenuWrite(PageStart);

Бо компілятор свариться: error: cannot convert 'Menu_Item_t** {aka const Menu_Item**}' to 'Menu_Item_t* {aka const Menu_Item*}' for argument '1' to 'void TableMenuWrite(Menu_Item_t*)'
З вказівниками в мене постійно виникають труднощі. Елементарне вже засвоїв, а ось такі "кандєлябри" ще мене вводять в ступор. Як переробити функцію, щоб правильно передати структуру пункту меню і мати в функції доступ до полів структури? Або я не вірно роблю виклик функції? Як правильно?

to @fronders: ну і як буде вже масив вказівників, то кінець масиву вже можна позначати як NULL?

51

Re: Blynk, ESP8266, IoT

(page+i) // вказівник на структуру
(page+i)->Index // елемент структури
page[i] // те саме, що й *(page+i)
page[i].Index // той же елемент, але зручніше записаний
Змінилося визначення page, тепер це Menu_item_t **
(page+i) // тепер це вказівник на вказівник
page[i] // а це просто вказівник
page[i]->Index // а це елемент
Подякували: leofun01, taburyak, NaharD, 0xDADA11C7, fronders5

52

Re: Blynk, ESP8266, IoT

koala написав:
(page+i) // вказівник на структуру
(page+i)->Index // елемент структури
page[i] // те саме, що й *(page+i)
page[i].Index // той же елемент, але зручніше записаний
Змінилося визначення page, тепер це Menu_item_t **
(page+i) // тепер це вказівник на вказівник
page[i] // а це просто вказівник
page[i]->Index // а це елемент

Щира дяка. Коротко, зрозуміло і по суті.
Запрацювало!!!

53

Re: Blynk, ESP8266, IoT

@taburyak
мені при роботі з масивами вказівників набагато більше подобається інший варіант запису.

Menu_Item_t const * PageStart[]

однозначно дає зрозуміти що це масив вказівників на константі структури, а от з варіантом запису

Menu_Item_t const ** PageStart

можна вже наплужити)) так само я роблю і передачі масива вказівників у функцію:

static void TableMenuWrite(Menu_Item_t const * page[])

і тоді всередині функції всі виклики стають

while (page[i]->Index != 0) {
    table.addRow(page[i]->Index, page[i]->Text, page[i]->Value);
    i++;
} 

ну і ще, наступні три записи еквівалентні

page[i]->Index
(*(page + i))->Index
(**(page + i)).Index

використання синтаксису масивів при цьому позбавляє від проблеми приорітетів операцій * . -> і купи скобок :)
та при цьому інформація про розмір масиву при передачі у функцію всеодно не зберігається

Подякували: 0xDADA11C7, leofun01, taburyak3

54

Re: Blynk, ESP8266, IoT

Постала інша проблема. Досвіду не вистачає, щоб придумати як обійти, чи зробити по іншому.

Є оголошені такі структури і дефайн:

typedef struct {
    char  host[33] = NAME_DEVICE;
    char  blynkToken[33] = "";
    char  blynkServer[33] = "blynk-cloud.com";
    char  blynkPort[6] = "8442";
    int   salt = EEPROM_SALT_WM;
} WMSettings;

WMSettings wmSettings;

typedef const struct Menu_Item {        
    const uint16_t  Index;    
    const String    Text;
    const String    Value;
} Menu_Item_t;

#define MENU_ITEM(Name, Index, Text, Value) \
        Menu_Item_t const Name = {Index, Text, Value}

Є оголошені такі пункти меню:

//--------Name----------Index--------Text-----------Value------
MENU_ITEM(Menu1_1_0,    M_BACK_DEVICE, "Back", "<---");
MENU_ITEM(Menu1_1_1,    M_DEVICE_NAME, String(wmSettings.host), " ");
MENU_ITEM(Menu1_1_2,    M_FIRMWARE_VERSION, "Firmware version", String(FIRMWARE_VERSION));

Плюс масив з пунктів меню (сторінка меню):

Menu_Item_t const * PageDeviceInfo[] = {&Menu1_1_0, &Menu1_1_1, &Menu1_1_2, NULL};

Саме проблема тут в Menu1_1_1, а саме String(wmSettings.host), бо при оголошені "прописується" назва хоста за замовчуванням "IoT-ESP8266". А вже потім на початку програми з EEPROM в wmSetting вантажаться поточні налаштування, наприклад host = "test-device". І коли я формую сторінку з пунктами меню:

TableMenuWrite(PageDeviceInfo);

static void TableMenuWrite(Menu_Item_t const * page[])
{    
    tableSettings.clear();    

    int i = 0;

    while (page[i] != NULL)
    {
        tableSettings.addRow(page[i]->Index, page[i]->Text, page[i]->Value);
        i++;
    }    
}

Я отримаю в меню назву host за замовчуванням - "IoT ESP8266", а не бажану назву "test-device". Це зрозуміло мені чому так відбувається. Бо пункт меню сформований під час компіляції і зашитий вже в флеш.
Тимчасово вийшов з положення що після виклику:

TableMenuWrite(PageDeviceInfo);

дописав:

tableSettings.updateRow(M_DEVICE_NAME, wmSettings.host, " ");

тоді вже завантажена з EEPROM назва девайсу перебиває назву за замовчуванням. Але це повна хрінь і нівелює всі мої потуги по зручній структуризації меню і зручної зміни і переробки меню в процесі еволюції прошивки.

От що придумати? Бо даних завантажених з EEPROM чималенько і такі костилі з tableSettings.updateRow мене не влаштовують.

55

Re: Blynk, ESP8266, IoT

Вітання друзі!

Хочу свої 2-ва звичайні конвектора переробити на незвичайні, розумні, IoT. І от я почав проектувати схему і дійшов до силової частини і виникло декілька питань. Схема сама типова з datasheet:
https://replace.org.ua/uploads/images/4476/4228bf485ba6cfcc3d53d4c2bc295f9d.png
Навантаження буде 800 Вт максимум, але для запасу вважаю що 1000 Вт, а це приблизно 4.5А струму.
Семістор буде BTA16-600 це 16 А 600 вольт.
Відповідно графіку з datasheet виходить при струмі в 4.5А буде розсіюватись 4 Ват.
https://replace.org.ua/uploads/images/4476/cee5fbc7624674bd62f7b8b8a0f701e8.png
Я правильно зрозумів? Як розрахувати радіатор, щоб і не займав зайві габарити і було достатньо для розсіювання? Де про це можна почитати?

Гріти панель буду в межах від 40 до 80 градусів по цельсію. Як краще "дімірувати"? Я так розумію PWM тут не підійде? Читав що можна зробити типу таблиці потужності. Таймер на 100Гц.
10% потужності - 1 цикл увімкнено, 9 циклів вимкнено.
20% потужності - 2 цикли увімкнено, 8 циклів вимкнено.
30% потужності - по аналогії.
Плюс ще прикрутити PID регулятор.
Нормальний варіант?

Міряти температуру буду як повітря, так і самої панелі. Для вимірювання температури нагрівача підійде DS18B20? Чи треба щось інше? Що?

Можливий варіант що симістор "проб'ється"? Тоді напруга на нагрівач буде йти постійно. Як зробити аварійнє вимкнення приладу? Реле?

56 Востаннє редагувалося ReAl (06.03.2020 09:59:36)

Re: Blynk, ESP8266, IoT

taburyak написав:

Гріти панель буду в межах від 40 до 80 градусів по цельсію. Як краще "дімірувати"? Я так розумію PWM тут не підійде? Читав що можна зробити типу таблиці потужності. Таймер на 100Гц.
10% потужності - 1 цикл увімкнено, 9 циклів вимкнено.
20% потужності - 2 цикли увімкнено, 8 циклів вимкнено.
30% потужності - по аналогії.

100 Гц загалом не дуже добре, бо при непарному числі увімкнених півперіодів будуть по різному навантажені + та півхвилі мережі, з'являється постійна складова струму. Залежно від якості проводки і потужності для розміщених неподалік пристроїв з'явиться і постійна складова напруги і звичайний 50-герцовий трансформатор (хоч їх і мало залишилося, але, наприклад, у мікрохвильовці є) отримає підмагнічування.

З точки зору «5 циклів вимкнено, 5 увімкнено» — буде або короткий період (мало градацій потужності), або хвилі тепловиділення, залежно від інерційності нагрівача теж може бути нецікаво.

Тому рекомендую як тут https://replace.org.ua/post/129713/#p129713 DDA тільки «тактова» — 50 ГЦ від мережі.
Якщо зробити навіть 64 градації потужності, то це повний період 1.28 секунди при гарно розмазаних по періоду імпульсах.

Подякували: leofun01, taburyak2

57

Re: Blynk, ESP8266, IoT

Якщо актуально:

taburyak написав:

Постала інша проблема. Досвіду не вистачає, щоб придумати як обійти, чи зробити по іншому.

Є оголошені такі структури і дефайн:

typedef struct {
    char  host[33] = NAME_DEVICE;
    char  blynkToken[33] = "";
    char  blynkServer[33] = "blynk-cloud.com";
    char  blynkPort[6] = "8442";
    int   salt = EEPROM_SALT_WM;
} WMSettings;

WMSettings wmSettings;

typedef const struct Menu_Item {        
    const uint16_t  Index;    
    const String    Text;
    const String    Value;
} Menu_Item_t;

#define MENU_ITEM(Name, Index, Text, Value) \
        Menu_Item_t const Name = {Index, Text, Value}

Є оголошені такі пункти меню:

//--------Name----------Index--------Text-----------Value------
MENU_ITEM(Menu1_1_0,    M_BACK_DEVICE, "Back", "<---");
MENU_ITEM(Menu1_1_1,    M_DEVICE_NAME, String(wmSettings.host), " ");
MENU_ITEM(Menu1_1_2,    M_FIRMWARE_VERSION, "Firmware version", String(FIRMWARE_VERSION));

Плюс масив з пунктів меню (сторінка меню):

Menu_Item_t const * PageDeviceInfo[] = {&Menu1_1_0, &Menu1_1_1, &Menu1_1_2, NULL};

Саме проблема тут в Menu1_1_1, а саме String(wmSettings.host), бо при оголошені "прописується" назва хоста за замовчуванням "IoT-ESP8266". А вже потім на початку програми з EEPROM в wmSetting вантажаться поточні налаштування, наприклад host = "test-device". І коли я формую сторінку з пунктами меню:

TableMenuWrite(PageDeviceInfo);

static void TableMenuWrite(Menu_Item_t const * page[])
{    
    tableSettings.clear();    

    int i = 0;

    while (page[i] != NULL)
    {
        tableSettings.addRow(page[i]->Index, page[i]->Text, page[i]->Value);
        i++;
    }    
}

Я отримаю в меню назву host за замовчуванням - "IoT ESP8266", а не бажану назву "test-device". Це зрозуміло мені чому так відбувається. Бо пункт меню сформований під час компіляції і зашитий вже в флеш.
Тимчасово вийшов з положення що після виклику:

TableMenuWrite(PageDeviceInfo);

дописав:

tableSettings.updateRow(M_DEVICE_NAME, wmSettings.host, " ");

тоді вже завантажена з EEPROM назва девайсу перебиває назву за замовчуванням. Але це повна хрінь і нівелює всі мої потуги по зручній структуризації меню і зручної зміни і переробки меню в процесі еволюції прошивки.

От що придумати? Бо даних завантажених з EEPROM чималенько і такі костилі з tableSettings.updateRow мене не влаштовують.

Не сильний в Ардуїно,але прога ділиться на дві? частини init та loop.
Десь перед init:

int InitFromEEPROM;

В init додайте:

 InitFromEEPROM=0;

потім:

WriteEEPROMSettingsToVariables(void * DataFromEEPROM))
{
// наприклад
     tableSettings.updateRow(M_DEVICE_NAME, wmSettings.host, DataFromEEPROM[i]);
}

в loop:

// при чому має бути в перших рядках loop
if(InitFromEEPROM==0)
{
  ReadFromEEPROM(&DataFromEEPROM);
  WriteEEPROMSettingsToVariables(&DataFromEEPROM));   
  InitFromEEPROM=1;
}