управлять некоторым выводом на основе некоторого параметра и rtc
я пытаюсь создать программу с заданной конфигурацией (я использовал несколько вложенных структур), мне нужно в цикле() проверять каждый вывод, нужно ли их включать или выключать, и соответствующим образом устанавливать вывод
позвольте мне объяснить конфигурацию (я не уверен, что это лучший способ создать и сохранить эту конфигурацию, предложения приветствуются)
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")
большое спасибо
@Darkmagister, 👍-1
Обсуждение1 ответ
Лучший ответ:
В этих фрагментах кода используется множество вариантов использования класса String
. С использованием
strings вполне естественно в некоторых языках высокого уровня. На Ардуино,
однако я бы настоятельно не советовал вам этого делать по двум причинам:
Строковые объекты используют динамическую память, что не подходит для небольших объем ОЗУ, доступный на типичном Arduino.
Сохранение строки в 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
- Пытаюсь распечатать элементы в структуре на aruino и получаю эту ошибку на compliation
- Является ли использование malloc() и free() действительно плохой идеей для Arduino?
- Как читать и записывать EEPROM в ESP8266
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Какой реальный срок службы EEPROM?
- Как запомнить значения переменных после перезагрузки платы Arduino Uno R3
- Получить доступ к EEPROM ATtiny с помощью кода Arduino?
- Инициализация массива структур
Первая проблема — это строка:
digitalWrite(out.pin, HIGH)
без точки с запятой. Следующая проблема заключается в том, чтоglobalState
не существует. Пожалуйста, прочитайте: https://stackoverflow.com/help/minimal-reproducible-example, @VE7JRO