управлять некоторым выводом на основе некоторого параметра и rtc

eeprom loop struct

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

позвольте мне объяснить конфигурацию (я не уверен, что это лучший способ создать и сохранить эту конфигурацию, предложения приветствуются)

struct Period
{
    String name;
    String type;
    String value;
};

struct State
{
    String name;
    Period periods[2];
};

struct Output
{
    String name;
    int pin;
    int forcePin;
    State states[2];
};

Output outputs[4];

теперь я заполняю первый объект (это то, что должно быть отредактировано пользователем, и я думал сохранить его в EEPROM, сейчас я делаю это в setup()

    outputs[0].name = "OUTPUT 1";
    outputs[0].pin = 27;
    outputs[0].forcePin = 25;
    outputs[0].states[1].name = "STATE 1";
    outputs[0].states[1].periods[0].name = "day";
    outputs[0].states[1].periods[0].type = "BOOL";
    outputs[0].states[1].periods[0].value = "ON";
    outputs[0].states[1].periods[1].name = "night";
    outputs[0].states[1].periods[1].type = "BOOL";
    outputs[0].states[1].periods[1].value = "OFF";
    outputs[0].states[0].name = "STATE 2";
    outputs[0].states[0].periods[0].name = "day";
    outputs[0].states[0].periods[0].type = "BOOL";
    outputs[0].states[0].periods[0].value = "ON";
    outputs[0].states[0].periods[1].name = "night";
    outputs[0].states[0].periods[1].type = "BOOL";
    outputs[0].states[0].periods[1].value = "OFF";

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

void manageOutputs()
{
    for (Output out : outputs)
    {
        Serial.println(out.name);
        Serial.println(out.pin);
        Serial.println(out.forcePin);
        if (digitalRead(out.forcePin) == LOW)
        {
            Serial.println("forced on set output");
            digitalWrite(out.pin, HIGH)
            continue;
        }

        Serial.println(out.states[globalState].name);
        Serial.println(out.states[globalState].periods[globalPeriod].name);
        Serial.println(out.states[globalState].periods[globalPeriod].type);
        Serial.println(out.states[globalState].periods[globalPeriod].value);
        if (out.states[globalState].periods[globalPeriod].type.equals("BOOL")) {
            if (out.states[globalState].periods[globalPeriod].value.equals("ON")) {
                digitalWrite(out.pin,HIGH);
            } else {
                digitalWrite(out.pin,LOW);
            }
        }
    }
}

Я имею в виду несколько типов вывода, например, TIMER (поэтому в поле значения у меня будет некоторый параметр для вызова другой функции, которая будет чередовать вывод в течение заданного времени) будет иметь что-то вроде "15: 10quot; таким образом, он останется включенным на 15 минут и выключенным на 10 минут, или условием (например, если VAR1 > 50), поэтому значение поля будет иметь "VAR1:>:50" поэтому он включится только при соблюдении условия. Так что у меня будет случай переключения, где теперь я проверяю, является ли BOOL.

теперь, когда я надеюсь, что объяснил ситуацию, я хотел бы знать, является ли это хорошим способом иметь 3 вложенных структуры или есть лучшее решение, учитывая необходимость хранения ее в EEPROM, и если это хороший способ иметь такое состояние, как это, или если есть лучший способ. Учитывая также, что в будущем я хотел бы создать веб-страницу или API для обновления такой конфигурации с ПК (возможно, используя для этого esp-8266, который также подключен к arduino, или делать все с esp32, но я хотел бы, чтобы сначала работал базовый цикл, а затем я сделал "UI")

большое спасибо

, 👍-1

Обсуждение

Первая проблема — это строка: digitalWrite(out.pin, HIGH) без точки с запятой. Следующая проблема заключается в том, что globalState не существует. Пожалуйста, прочитайте: https://stackoverflow.com/help/minimal-reproducible-example, @VE7JRO


1 ответ


Лучший ответ:

0

В этих фрагментах кода используется множество вариантов использования класса String. С использованием strings вполне естественно в некоторых языках высокого уровня. На Ардуино, однако я бы настоятельно не советовал вам этого делать по двум причинам:

  1. Строковые объекты используют динамическую память, что не подходит для небольших объем ОЗУ, доступный на типичном Arduino.

  2. Сохранение строки в EEPROM не является тривиальным: вы не хотите сохранять Строковый объект (он содержит указатель, который не имел бы смысла в контекст EEPROM), и вместо этого вы должны сохранить массив символов, который он относится к. Это может включать обработку сложного распределения памяти с EEPROM.

Вместо строки, предназначенной для обработки одного из предопределенного набора значений. ("BOOL", "TIMER", ...), используйте enum. Это похоже на int, где каждому возможному значению присвоено имя (TYPE_BOOL равно 0, TYPE_TIMER равен 1, ...). Вместо строки, содержащей числа, сохраните сами числа. По сути, вы хотите создать формат хранения данных это дружественно к двоичной природе ЦП. Если вам действительно нужно хранить строки, например, для имен (эти поля name действительно полезны? в приведенном примере они так не выглядят), ограничьте их длину и хранить их как массивы символов фиксированного размера.

Поле period::value интересно тем, что может хранить разные виды информации в зависимости от поля type. Для такой ситуации подходящей структурой данных является размеченное объединение. союз похож на struct, за исключением того, что все поля используют одно и то же пространство памяти и только один может быть действительным одновременно. В дискриминированном союзе union сопровождается полем ytpe, которое сообщает нам, какой допустимое поле.

Применение этих принципов к вашей проблеме может дать такие данные структура:

// Все возможные типы Period.
enum Type {
    TYPE_BOOL,
    TYPE_TIMER,
    // и т. д.
};

// Это размеченное объединение.
struct Period
{
    Type type;
    union {  // один член объединения каждого возможного типа.
        bool bool_value;  // допустимо, когда тип == TYPE_BOOL
        struct {
            int on_time;
            int off_time;
        } timer_value;  // допустимо, когда тип == TYPE_TIMER
        // и т. д.
    };
};

struct State
{
    Period periods[2];
};

struct Output
{
    int pin;
    int forcePin;
    State states[2];
};

Эти структуры данных можно легко сохранить в EEPROM, поскольку они автономны: они не содержат указателей на данные, хранящиеся в динамической памяти.

Затем в manageOutputs() вы будете считывать только bool_value периода. когда type равен TYPE_BOOL, это timer_value, только когда type равен TYPE_TIMER и т. д.:

void manageOutputs()
{
    for (Output out : outputs)
    {
        if (digitalRead(out.forcePin) == LOW) {
            digitalWrite(out.pin, HIGH);
            continue;
        }
        Period &period = out.states[globalState].periods[globalPeriod];
        switch (period.type) {
            case TYPE_BOOL:
                if (period.bool_value) {
                    digitalWrite(out.pin, HIGH);
                } else {
                    digitalWrite(out.pin, LOW);
                }
                break;
            case TYPE_TIMER:
                digitalWrite(out.pin, HIGH);
                delay(period.timer_value.on_time);
                digitalWrite(out.pin, LOW);
                delay(period.timer_value.off_time);
        }
    }
}
,

Большое спасибо, .name полезно для установки имени на выходе, чтобы его можно было легко настроить и чтобы было легче понять, что делает вывод (я не думаю, что это необходимо для подструктуры, но, возможно, для основного вывода, я проведу тест с массивом символов.. чтобы увидеть, легко ли с ним работать), @Darkmagister