спасибо, попробую понять, но наверное пока не мой уровень
Вы нас где-то обманываете. Если они имеют разный тип, то их нельзя поместить в массив.
Эту информацию, возможно, тоже есть смысл добавить в структуру, чтобы не “помнить”.
вот это похоже на то, что я ищу. буду изучать
Что-то я запутался. Из-за ответа #17 решил что @Smith2007 и ТС - это один и тот же человек.
переменные из одной точки теплового процесса имеютразный тип, а параметры термопар конечно же однотипные, датчикиодинаковые, разное у них только сопротивление при температуре калибровки, ну и плечо. хотя и это в конце можно привести будет к одним цыфрам, просто откалибровав одним разом(конечно разным останется сопротивление)
Ну, в таком случае Вам нужен самостоятельный объект (оформить можно, например, как структуру) “датчик”. Внутри них должны быть прописаны их взаимосвязи и взаимовлияния (именно внутри, а не снаружи) и дальше работать с ним, как самостоятельными сущностями, через методы самой структуры, а не таскать их номера пинов и связи по всей программе. Всё, связанное с датчиком прячется в его описание и доступно только через методы. Если будете гуглить, эта концепция называется “инкапсуляцией”.
Вам тут очень правильную мысль подсказали:
Это частный случай более общего утверждения: всё, что связано с объектом должно быть собрано в его описании и меняться в одном месте. То, что при изменении чего-то в датчике, Вам надо изменять это в 100500 местах в программе – признак неправильного проектирования программы.
Попробуйте изолировать сущности (а не размазывать их по программе), программа сразу на порядок прибавит в организованности и управляемости.
Извините, протупил. Принял вас за ТС-а.)
Вот и я тоже…
Наследование. Предок - общая часть для всех типов структур. Именно по указателю на предка (базовый объект) структуры хранятся, передаются, перебираются. В базовом объекте хранится и описательная часть (указывающая место в процессе, название, другие общие для всех признаки). В наследниках добавлены уникальные поля, определены операторы копирования, сравнения, сериализации. Возможно, аксессоры для доступа к полям через общие базовые функции - тут простор для проектирования. Короче, классика ООП. Но без полного видения задачи советовать что-то конкретное сложно.
Если у вас что-то несложное, одноразовое, проще сгородить велосипед без этих всех ООП-наворотов, запустить и забыть. Все-таки эти приемы для больших, долгоживущих, развивающихся проектов. А так у вас больше сил уйдет на разбирание с абстракциями, чем просто руками закодировать несколько “простынь” однотипного кода.
Не правильная организация структуры?
Эту же структуру записываю во флеш esp32 (псевдофлеш).
/*****************************************
функция записи данных во flash.
Перед загрузкой рассчитываем crc32
******************************************/
void flash_write(void) {
config_t& conf = getConfig();
// Обновляем CRC (рассчитываем для всех полей кроме crc)
size_t dataSize = sizeof(config_t) - sizeof(uint32_t);
conf.crc = calculateHardwareCRC32((uint8_t*)&conf, dataSize);
EEPROM.put(0, conf);
EEPROM.commit();
}
uint32_t calculateHardwareCRC32(const uint8_t* data, size_t length) {
return esp_crc32_le(0xFFFFFFFF, data, (uint32_t)length);
}
Получается короткий код и легче читать
это плохая идея, подключи часики на DS3231 там 24c32 и памяти вагон и в цикле 100 тысяч. перезаписей кольцевого буфера, ну не знаю сколько у тебя там хранится, я ужался до 64 байт, так что есть надежда что при перезаписи раз в минуту проживёт = 97 лет )))
Не правильная организация структуры?
Эту же структуру записываю во флеш esp32 (псевдофлеш).
Да, я в курсе, что количество циклов перезаписи ограничено. Но фактически настройки делаются только во время отладки. В работе только чтение. Ни какие оперативные значения не сохраняются.
И структура уже 260 байт занимает. Это примерно 2/3 всех необходимых параметров.
Мне присылали ссылки на память. Да, интересная штука. Возможно позже я ее подключу.
Но пока задачи куда банальнее.
Ранее я писал о проблеме, что сервомоторы при первом движении на максимальной скорости дергаются. Искал способ как избежать этой проблемы. В итоге вышел на сервомоторы scs, управляемые по последовательной шине.
Вчера удалось подключить сразу 4 мотора и управлять ими раздельно. И там проблема “первого рывка” решена.
Следующий шаг нужно разобраться с подключением моторов по CAN.
Извиняюсь, что в этой теме поднял не профильный вопрос. Перехожу в профильную ![]()
Мне кажется, здесь напрашивается виртуальный метод.
Именно массив для датчиков. И обрабатывать его именно в цикле. Но только для элементов различных типов будут вызываться предназначенные именно для них методы - для одного будет обрабатывать 2 переменные, а для другого - 10.
Можно, если типы унаследованы от общего.
нашел я Вашу статью на эту тему, если я правильно понял не совсем мой вариант, так как у меня несколько структур имеющих одинаковые переменные и несколько разных, одинаковые переменные я передаю в одну функцию, которая пересчитывает температуру. так вотписать одну функцию в каждую структуру не совсем рационально.
вот эта конструкция Вашего кода помоему ближе всего
struct SSensorValue {
unsigned long timeStamp; // Время съёма показаний (мс от запуска МК)
float temperture; // температура в градусах С
float humidity; // влажность в %
float pressure; // давление в мм.рт.ст.
};
void printSensorValue(const void * vv) {
const SSensorValue * sv = (const SSensorValue *) vv;
Serial.print("SENSOR VALUE: Time:");
Serial.print(sv->timeStamp);
Serial.print("ms; T:");
Serial.print(sv->temperture);
Serial.print("C; H:");
Serial.print(sv->humidity);
Serial.print("%; P:");
Serial.print(sv->pressure);
Serial.println("mm");
}
void setup() {
Serial.begin(115200);
Serial.println("Fun begins!");
//
// Объявляем структуру и заполняем какими-то значениями
//
SSensorValue sv;
sv.timeStamp = millis();
sv.temperture = 36.6;
sv.humidity = 93.2;
sv.pressure = 755;
//
// Печатаем структуру
//
printSensorValue(&sv);
}
void loop() {}
но я не совсем понимаю что происходит в этих строках
void printSensorValue(const void * vv) {
const SSensorValue * sv = (const SSensorValue *) vv;
нет, я не правильно возможно обьяснил, структуры собраны по принципу в какой точке процесса идет измерение температуры, помимо темп есть еще данные, так вот эти данные пока нас не интересуют, мы берем одинаково названные данные относящиеся к температуре и передаем в функцию для рассчета самой температуры и возвращаем в переменную массива. функция одна, массивов несколько, они разные, но переменные для функции одинаковые и названы одинаково
Тогда элементы массива - указатели на базовый тип, а это один и тот же тип, а не разные. Я решил не путать ТСа разными уточнениями. Можно и в std::any завернуть всё, если новые стандарты юзать.
Если что выше сказали - не понятно, вот ещё гляньте - простой способ. Массив указателей на функции. Можно использовать в цикле. Функции должны быть одного типа.
Спойлер
void Print_time();
void Print_sens();
void Print_moto();
void (*Print_func[])() = { Print_time, Print_sens, Print_moto};
struct time {
uint16_t year;
uint8_t month;
uint8_t date;
uint8_t hours;
uint8_t minuts;
};
struct sens {
uint8_t n;
float t;
};
struct moto {
bool ON;
uint32_t change_time;
};
struct time t;
struct sens s;
struct moto m;
void setup() {
t.year = 2026;
t.month = 6;
t.hours = 12;
t.minuts = 33;
s.n = 4;
s.t = 43.00;
m.ON = true;
m.change_time = 2335;
Serial.begin(115200);
for (uint8_t i = 0; i < 3; i++)
Print_func[i]();
}
void Print_time()
{
Serial.print(" Time: ");
Serial.print(t.year);
Serial.print(" ");
Serial.print(t.month);
Serial.print(" ");
Serial.print(t.hours);
Serial.print(".");
Serial.println(t.minuts);
}
void Print_sens()
{
Serial.print(" Sensor number ");
Serial.print(s.n);
Serial.print(" val = ");
Serial.println(s.t);
}
void Print_moto() {
Serial.print(" moto is ON = ");
Serial.print(m.ON);
Serial.print(" change_time = ");
Serial.print(m.change_time);
Serial.println(" min");
}
void loop() {
}
P.P.S. При копировании, затёрлась первая строчка - исправил.
Тот пример был, видимо про “указатели на указатели” а не про написание структур.
Вот это:
идеологически неверно. Всё, что касается структуры, лучше держать в ней самой, внутри неё.
Вот Ваш пример (я его скопировал), но печать убрана внутрь структуры, сравните.
struct SSensorValue : public Printable {
unsigned long timeStamp; // Время съёма показаний (мс от запуска МК)
float temperture; // температура в градусах С
float humidity; // влажность в %
float pressure; // давление в мм.рт.ст.
size_t printTo(Print& p) const {
size_t res = p.print("SENSOR VALUE: Time:");
res += p.print(timeStamp);
res += p.print("ms; T:");
res += p.print(temperture);
res += p.print("C; H:");
res += p.print(humidity);
res += p.print("%; P:");
res += p.print(pressure);
res += p.print("mm");
return res;
}
};
void setup() {
Serial.begin(9600); // я поменял скорость, если надо, то поменяйте обратно
Serial.println("Fun begins!");
//
// Объявляем структуру и заполняем какими-то значениями
//
SSensorValue sv;
sv.timeStamp = millis();
sv.temperture = 36.6;
sv.humidity = 93.2;
sv.pressure = 755;
//
// Печатаем структуру
//
Serial.println(sv);
}
void loop() {}
Т.е. печать сидит внутри структуры и не торчит наружу. Подробнее про эту технику вот здесь.
Кстати, здесь Вам таже нетрудно сделать персонифицированную печать. Сейчас сделаю пример
— продолжение следует —
---- продолжение —
Вот смотрите, теперь у датчиков есть названия, а функция печати всегда печатает о каком датчике речь
struct SSensorValue : public Printable {
const char * sensorName; // это будет название датчика
unsigned long timeStamp; // Время съёма показаний (мс от запуска МК)
float temperture; // температура в градусах С
float humidity; // влажность в %
float pressure; // давление в мм.рт.ст.
// зададим название, а заодно обнулим все значения, нечего им болтаться в случайных значениях
SSensorValue(const char * name = "") : sensorName(name), timeStamp(0), temperture(0), humidity(0), pressure(0) {}
size_t printTo(Print& p) const {
size_t res = p.print("SENSOR \"");
res += p.print(sensorName);
res += p.print("\" VALUE: Time:");
res += p.print(timeStamp);
res += p.print("ms; T:");
res += p.print(temperture);
res += p.print("C; H:");
res += p.print(humidity);
res += p.print("%; P:");
res += p.print(pressure);
res += p.print("mm");
return res;
}
};
void setup() {
Serial.begin(9600);
Serial.println("Fun begins!");
//
// Объявляем структуру и заполняем какими-то значениями
//
SSensorValue sv("Vozle vhoda");
sv.timeStamp = millis();
sv.temperture = 36.6;
sv.humidity = 93.2;
sv.pressure = 755;
SSensorValue sv1("Na Vyhode");
//
// Печатаем структуры
//
Serial.println(sv);
Serial.println(sv1);
}
void loop() {}
Ну, далеко не уходите, завтра я покажу Вам как можно передавать функции только часть полей структуры (а другой функции, например, другую часть)
Если разным функциям нужны разные части структуры, можно сделать это через наследование - при этом каждая функция будет получать только ту часть структуры, которая ей нужна.
Пример
// В этой структуре есть три поля a, b и c
struct ABC {
int a;
int b;
int c;
int sumABC(void) const { return a + b + c; }
};
// А в этой тоже три поля: d, e и f
struct DEF {
int d;
int e;
int f;
int sumDEF(void) const { return d + e + f; }
};
// А вот в этой структуре будут все 6 полей, т.к. она НАСЛЕДУЕТ,
// всё, что есть в "struct DEF" и в "struct ABC" и ещё свое поле g
struct SevenMembers :
public ABC, // Это значит, что в этой структуре будут все поля структуры ABC
public DEF // Это значит, что в этой структуре будут все поля структуры DEF
{
int g;
int sum() const { return sumABC() + sumDEF() + g; }
};
//
// Очеь важное свойство наследования - полиморфизм.
// ПРостыми словами это означает, что любой экземпляр
// SevenMembers будет также полноценным экземпляром
// тех структур, от которых SevenMembers пронаследована,
// т.е. ABC и DEF. Сейчас мы это продемонстрируем.
//
// Эта функция ожидает паратетра struct ABC
int getSum1(const ABC & str) {
return str.sumABC();
}
// Эта функция ожидает паратетра struct DEF
int getSum2(const DEF & str) {
return str.sumDEF();
}
void setup(void) {
Serial.begin(9600);
// Объявляем и инициализируем экземпляр структуры SevenMembers
SevenMembers sm;
sm.a = 1;
sm.b = 2;
sm.c = 3;
sm.d = 4;
sm.e = 5;
sm.f = 6;
sm.g = 7;
// Функция getSum1 ожидает параметр типа ABC.
// У нас есть только SevenMembers sm
// Но мы уже говорили, что SevenMembers - наследник ABC,
// а. значит, sm - полноправный экземпляр типа ABC
Serial.println(getSum1(sm));
// Опять же!
// Функция getSum2 ожидает параметр типа DEF.
// У нас есть только SevenMembers sm
// Но мы уже говорили, что SevenMembers - наследник DEF,
// а. значит, sm - полноправный экземпляр типа DEF
Serial.println(getSum2(sm));
}
void loop(void) {}
Могу, конечно ответить на изначальный вопрос темы – показать, как перебирать поля структуры (если они одного типа, разумеется) циклом, только повторяю - это через задницу. В реальности я так не сразу даже могу придумать где бы это могло быть реально нужно. Чаще всего, если Вам это нужно – это признак того, что в проектировании / архитектуре программы что-то не так.
Сейчас подготовлю пример.
Пример череззадничного решения вопроса из стартового топика
//
// Определим структуру с тремя полями
//
struct SKaka {
SKaka (const int _a=0, const int _b=0, const int _c=0) : a(_a), b(_b), c(_c) {}
int a;
int b;
int c;
int errorValue = -1;
int length = 3;
//
// Введём оператор взятия по индексу
// При индексе 0, речь будет идти о поле a
// при индексе 1 - о поле b
// при индексе 2 - о поле c
//
int & operator [](const int index) {
switch (index) {
case 0: return a;
case 1: return b;
case 2: return c;
default: return errorValue;
}
}
};
void setup(void) {
Serial.begin(9600);
// Обявляем экземпляр м назначаем начальные значения
// kaka.a=1, kaka.b=2, kaka.c=3
SKaka kaka(1, 2, 3);
// Выполним арифметическое действие
// над всеми полями по индексу
for (int i = 0; i < kaka.length; i++) {
kaka[i] *= kaka[i];
}
// А теперь распечатаем все поля,
// опять же по индексу.
for (int i = 0; i < kaka.length; i++) {
Serial.print("kaka[");
Serial.print(i);
Serial.print("]=");
Serial.println(kaka[i]);
}
}
void loop(void) {}
