Помогите с динамическим массивом

VLDnepr
Offline
Зарегистрирован: 31.01.2022

Добрый день. Помогите начинающему.
Хочу сделать управление поливом. Плата UNO.

Создал структуру CONFIG для хранения конфирационных данных.
- poliv_ZoneCount - количество зон
- poliv_ZonePin - динамический массив номеров зон (по количеству зон)
- poliv_Plans - динамический массив структур плана полива PolivPlan_t, структура собержит время старта и динамический массив таймингов полива зон (по количеству зон)

В void setup() сначала задаю количество зон и количество планов, переконфигурируется переменная CONFIG и потом заполняется.
Для теста сразу выводится конфигурация.

Проблема:
1.Помогите пожалуйста оценить правильно ли я работаю с массивами
2.Иногда код выводит неожиданные числа в тайминге плана, при чем именно в тайминге, стартовое время показывается правильно

Пример правильного вывода

poliv_Plan:0 active:1 time:12:33 timers:5 6 7 8 9 10 11

Но иногдла показывает
poliv_Plan:0 active:1 time:12:33 timers:21 32 8 56 0 1 0

 

#include <EEPROM.h>

#define SerialSpeed 9600
#define InvBit(reg, bit)    reg ^= (1<<(bit))

bool needRestart = false;

// Структура объекта ЗонаПолива
typedef struct PolivPlan_t{
  bool Active ;
  uint8_t TimeStartH; 
  uint8_t TimeStartM;
  uint8_t *poliv_ZoneTimes=0; // массив Таймингов для каждой зоны
  } ;

struct configObj {
  bool serialShow; // показывать диагностический вывод
  bool poliv_Active; // полив может запускаться
  uint8_t poliv_ZoneCount; // количество зон полива
  uint8_t poliv_PlanCount; // количество планов полива
  uint8_t *poliv_ZonePin=0; // Массив номеров зон полива
  PolivPlan_t *poliv_Plans=0;// Массив зон полива
  byte controlByte = 0x55;
} config;


//-----------------------------------------------------------------------
void printConfig();



void ConfigSave(){
  uint16_t startingAddress = 0;
  EEPROM.put(startingAddress, config); startingAddress += sizeof(config); 
  //poliv zone pins
  for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++){
    EEPROM.put(startingAddress, config.poliv_ZonePin[_zone]); startingAddress += sizeof(config.poliv_ZonePin[_zone]);
  }
  //poliv plans
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    EEPROM.put(startingAddress, config.poliv_Plans[_plan].Active); startingAddress += sizeof(config.poliv_Plans[_plan].Active);
    EEPROM.put(startingAddress, config.poliv_Plans[_plan].TimeStartH); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartH);
    EEPROM.put(startingAddress, config.poliv_Plans[_plan].TimeStartM); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartM);
    for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++) {
      EEPROM.put(startingAddress, config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]); startingAddress += sizeof(config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]);
    }
  }
}


void ConfigLoad(){
Serial.print("np1");  
  uint16_t startingAddress = 0;
  EEPROM.get(startingAddress, config); startingAddress += sizeof(config); 
  config.poliv_ZonePin=0;
  config.poliv_Plans=0;

  ChangeConfig();
  
  //poliv zone pins
  for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++){
    EEPROM.get(startingAddress, config.poliv_ZonePin[_zone]); startingAddress += sizeof(config.poliv_ZonePin[_zone]);
  }
  //poliv plans
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    EEPROM.get(startingAddress, config.poliv_Plans[_plan].Active); startingAddress += sizeof(config.poliv_Plans[_plan].Active);
    EEPROM.get(startingAddress, config.poliv_Plans[_plan].TimeStartH); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartH);
    EEPROM.get(startingAddress, config.poliv_Plans[_plan].TimeStartM); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartM);
    for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++) {
      EEPROM.get(startingAddress, config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]); startingAddress += sizeof(config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]);
    }
  }
}

void ChangeConfig(){
  //--- poliv_ZonePin
  config.poliv_ZonePin = (uint8_t*) realloc(config.poliv_ZonePin, config.poliv_ZoneCount * sizeof(uint8_t));

  //--- poliv_Plans
  config.poliv_Plans = (PolivPlan_t*) realloc(config.poliv_Plans, config.poliv_PlanCount * sizeof(PolivPlan_t));
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    config.poliv_Plans[_plan].poliv_ZoneTimes = (uint8_t*)realloc(config.poliv_Plans[_plan].poliv_ZoneTimes, config.poliv_ZoneCount * sizeof(uint8_t));
  }
}

void setup() {//=== SETUP ======= SETUP ======= SETUP 
  Serial.begin(SerialSpeed);


  // --- Предварительная запись в EEPROM ----
  Serial.println(F("test varialbe set"));
  config.poliv_Active = true;
  config.poliv_ZoneCount = 7;
  config.poliv_PlanCount = 1;
  config.poliv_ZonePin = 0; // первоначальная инициализация перед заполнением из EEPROM
  config.poliv_Plans = 0;   // первоначальная инициализация перед заполнением из EEPROM

  Serial.println(F("nChangeConfig();"));
  ChangeConfig(); // переконфигурирование CONFIG

  Serial.println(F("test varialbe set next"));
  config.poliv_ZonePin[0] = 20;
  config.poliv_ZonePin[1] = 21;
  config.poliv_ZonePin[2] = 22;
  config.poliv_ZonePin[3] = 23;
  config.poliv_ZonePin[4] = 24;
  config.poliv_ZonePin[5] = 25;
  config.poliv_ZonePin[6] = 26;
  config.poliv_Plans[0].Active = true;
  config.poliv_Plans[0].TimeStartH = 12;
  config.poliv_Plans[0].TimeStartM = 33;
  config.poliv_Plans[0].poliv_ZoneTimes[0] = 5;
  config.poliv_Plans[0].poliv_ZoneTimes[1] = 6;
  config.poliv_Plans[0].poliv_ZoneTimes[2] = 7;
  config.poliv_Plans[0].poliv_ZoneTimes[3] = 8;
  config.poliv_Plans[0].poliv_ZoneTimes[4] = 9;
  config.poliv_Plans[0].poliv_ZoneTimes[5] = 10;
  config.poliv_Plans[0].poliv_ZoneTimes[6] = 11;

  printConfig();

  Serial.println(F("nChangeSave()"));
  ConfigSave();


/*  Serial.println(F("nConfigLoad()"));
  ConfigLoad();
  Serial.println("nAFTER LOAD");

  printConfig();
*/  
}

  
void loop(){
/*  Serial.println("nvoid loop()");
  printConfig();
  delay(1000);
*/  
}


void printConfig() {//--- Вывод текущей конфигурации ----------
  Serial.print(F("Current config")); 
  if (needRestart) Serial.print(F(" !!! Need restart !!!"));
  Serial.println(); 
  Serial.print(F("serial show:")); Serial.println(config.serialShow);
  Serial.print(F("poliv_Active:")); Serial.println(config.poliv_Active);
  Serial.print(F("poliv_ZoneCount:")); Serial.print(config.poliv_ZoneCount); 
    Serial.print(F(" contact numbers:"));  for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++){Serial.print(config.poliv_ZonePin[_zone], DEC); Serial.print(" ");} Serial.println();
  Serial.print(F("poliv_PlanCount:")); Serial.println(config.poliv_PlanCount);

  // poliv plan
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    Serial.print(F("poliv_Plan:")); Serial.print(_plan);
    Serial.print(F(" active:")); Serial.print(config.poliv_Plans[_plan].Active);
    Serial.print(F(" time:")); Serial.print(config.poliv_Plans[_plan].TimeStartH);Serial.print(F(":")); Serial.print(config.poliv_Plans[_plan].TimeStartM);
    Serial.print(F(" timers:"));
    for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++) {
      Serial.print(config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]);Serial.print(F(" "));
    }
    Serial.println();
  }
}

 

b707
Offline
Зарегистрирован: 26.05.2017

ой мама...

неужели полив огорода нельзя описать попроще, без пяти уровней вложенности структур данных? :)

onfig.poliv_Plans[0].poliv_ZoneTimes[6] = 11;

 

Думаю что искать ошибки в этом коде без бутылки хорошего коньяка смысла нет...

 

VLDnepr
Offline
Зарегистрирован: 31.01.2022

1.Можно, но это часть кода, там предполагается еще, и в памяти будет что хранить,  а посточнно переписывать код - по-моему не очень хорошая идея.

2.Интересно попробовать так, помогите разобраться, если можете.

b707
Offline
Зарегистрирован: 26.05.2017

VLDnepr пишет:

1.Можно, но это часть кода, там предполагается еще, и в памяти будет что хранить,  а посточнно переписывать код - по-моему не очень хорошая идея.

насчет "хорошей идеи"....

Знаете, секрет успешного написания больших программ - писать их как можно более простыми.  А вы написали всего сотню строк - а уже сами не можете понять, что и как у вас в коде происходит...

Делайте выводы.

Может вам помогут классы, подумайте...

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Для "разобраться" что-то сразу "головой в омут", Вам не кажется? )

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

В МК AVR atmega 328 2 КБ ОЗУ. Это не ПК. Не нужно пользоваться маллоком, аллоком и реаллоком. Честное слово. Это самая обычная ошибка новичка в эмбеддинге.

В ПК сохраненная память достанется другим процессам. В МК она ...просто останется. Ни для чего. И по наследству детям не передашь. Поэтому сразу отведи столько, сколько тебе нужно и можно по логике кода. В коде нет и не может быть диких вложенностей и рекурсий, поэтому потребность в памяти и стеке заранее известна.

Ошибки, про которые ты написал, это (скорее всего) какие-то накладки памяти. Изучай, где ошибся, пиши диагностический вывод и пр.

Но сперва избавься от излишней в эмбеддинге "универсальности". Когда пишешь для МК ты решаешь не общую задачу "контроллеров полива", а пишешь один, конкретный контроллер. Иначе трудно отлаживать будет.

Как в старом анекдоте: "Видел одну козу, черную - сверху."  Вот и помни. что тебе нужно не "стада черных коз", а одна коза, черная - сверху.

VLDnepr
Offline
Зарегистрирован: 31.01.2022

Вы отчасти правы, но:

1.Хочется понять почему не работает, и понять методы

2.Память выделается динамически раз по время старта и более не меняется, поэтому как я могу предположить утечек быть не должно.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Ну и накой тебе динамическое выделение памяти, если потом она меняться не будет?

VLDnepr
Offline
Зарегистрирован: 31.01.2022

v258 пишет:

Ну и накой тебе динамическое выделение памяти, если потом она меняться не будет?

да, но при этом сколько нужно зон непонятно, а выделять с запасом - это сколько?

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Как это непонятно? А что ж ты программируешь? Сейчас у тебя с запасом или как придется?

b707
Offline
Зарегистрирован: 26.05.2017

не, я тоже не согласен с тезисом о том, что malloc в микроконтроллерах использовать нельзя. Как это "нельзя"? Если в языке есть такой механизм, почему я должен от него отказываться?

И насчет советов "выделить заранее сколько надо" или "выделить максимум" - это все хорошо только на словах.

Рассмотрим простой пример. Вот у меня есть библиотека для управления светодиодными матрицами. В ней для вывода картинки используется буфер в памяти, размер буфера пропорционален числу матриц. Для простоты скажем 100 байт на матрицу.

Если я должен выделить память заранее - сколько выделять? Я же не знаю, сколько у пользователя будет матриц. Выделю 100 байт - а вдруг у него будет панно 2х2? Выделю, скажем, 800 байт - а у пользователя будет одна матрица и 700 байт будут заняты впустую. А ведь это библиотека, то есть это только часть кода, у пользователя еще будет своя программа, для которой, возможно, ему эти "лишние" 700 байт очень бы пригодились.

Как разрулить эту ситуацию без использования динамической памяти?

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Имхо, здесь другой случай. Человек делает поливалку, и сколько у него каналов (зон) полива ему известно на этапе проектирования. Потому что все данные он задает в setup(). Он же не библиотеку для общего пользования пишет ))

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Если число каналов известно до компиляции, то смысла в динамических массивах НЕТ !

b707
Offline
Зарегистрирован: 26.05.2017

v258 пишет:

Имхо, здесь другой случай. Человек делает поливалку, и сколько у него каналов (зон) полива ему известно на этапе проектирования.

да ну, какая разница?

Может у него два-три огорода с разным числом зон полива и он хочет написать общую прошивку, которая могла бы настраиваться на любой из вариантов без перекомпиляции, например переключателями на плате? Или скажем огород один, но зоны меняются от сезона - весной одни, летом другие... это что ж, каждые пару месяцев прошивку переписывать?

b707
Offline
Зарегистрирован: 26.05.2017

Komandir пишет:

Если число каналов известно до компиляции, то смысла в динамических массивах НЕТ !

а если неизвестно, то есть! :)

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

VLDnepr количество зон/каналов меняется на каком этапе ?

1. При компиляции.

2. Через диалог пользователем в процессе настройки.

...

 

VLDnepr
Offline
Зарегистрирован: 31.01.2022

Блин, почему всегда круче порассуждать зачем, почему и т.д. чем подулиться знанием, опытом. 

Мне приятно попробовать сделать хорошую вещь. а не этот молоток ТОЛЬКО для этого гвоздя.

Я делаю не только для себя, а и для двух своих соседей и число зон будет разное. Хочу сделать это настраиваемым, через консольные команда, они у меня уже написаны, код не приводил (и так много зачем/почему/ты что козу не видишь)

 

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

b707 пишет:

v258 пишет:

Имхо, здесь другой случай. Человек делает поливалку, и сколько у него каналов (зон) полива ему известно на этапе проектирования.

да ну, какая разница?

Может у него два-три огорода с разным числом зон полива и он хочет написать общую прошивку, которая могла бы настраиваться на любой из вариантов без перекомпиляции, например переключателями на плате? Или скажем огород один, но зоны меняются от сезона - весной одни, летом другие... это что ж, каждые пару месяцев прошивку переписывать?

А реле для помп сами будут вырастать или исчезать? Нужно определиться с возможностями МК и потребностями в плане поливов. И исходя из этого плясать. Просто задать максимально возможное количество и не париться. Куда он сэкономленную память девать будет? ))

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

VLDnepr пишет:

Блин, почему всегда круче порассуждать зачем, почему и т.д. чем подулиться знанием, опытом. 

Сообщение #5

VLDnepr
Offline
Зарегистрирован: 31.01.2022

Komandir пишет:

VLDnepr количество зон/каналов меняется на каком этапе ?

1. При компиляции.

2. Через диалог пользователем в процессе настройки.

...

число зон и планов меняется консольными командами, после этого переконфигурируется config, после этого запись и перегрузка.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

VLDnepr пишет:

число зон и планов меняется консольными командами, после этого переконфигурируется config, после этого запись и перегрузка.

Ну и еще раз - зачем? Память экономить? А если новый конфиг выйдет за рамки возможного?

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Рамки можно/нужно и ограничить !

b707
Offline
Зарегистрирован: 26.05.2017

v258 пишет:

Куда он сэкономленную память девать будет? ))

как куда? пригодится :)

Память МК - это как лодка, где надо перевезти волка, козла и капусту. Если козла выкинуть за борт - капусты можно будет взять больше.

Если подобные комбинации можно выбирать без переписывания прошивки - это удобство.

 

 

Я готов согласится, что не стоит злоупотреблять динамическим выделением, особенно если выделение и освобождение памяти происходит в программе многократно, например в цикле. Но выделить нужное количество памяти один раз, при старте программы - и больше не менять - почему нет? Какие это может вызвать проблемы?

VLDnepr
Offline
Зарегистрирован: 31.01.2022

v258 пишет:

VLDnepr пишет:

число зон и планов меняется консольными командами, после этого переконфигурируется config, после этого запись и перегрузка.

Ну и еще раз - зачем? Память экономить? А если новый конфиг выйдет за рамки возможного?

Я услышал Вас, но каждый имеет право на мнение. Если не получится разобраться почему ИНОГДА проскакиют неправильные данные то буду жестко зашивать число зон. 

Ваше мнение единственно правильное? Можно ли добавить чуть конструктива в Ваш злобный клекот.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

VLDnepr пишет:

Ваше мнение единственно правильное? Можно ли добавить чуть конструктива в Ваш злобный клекот.

Злобный?!!! Парень, остынь ))) 

Да, мое мнение не есть единственно верное, но опыта у меня таки чуть больше, да и к мнению Дракулы (опять же пост #5) прислушаться очень даже стоит ;)

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

b707 пишет:

Но выделить нужное количество памяти один раз, при старте программы - и больше не менять - почему нет? Какие это может вызвать проблемы?

Однако ж какие-то проблемы это вызвало ))

b707
Offline
Зарегистрирован: 26.05.2017

VLDnepr пишет:

число зон и планов меняется консольными командами, после этого переконфигурируется config, после этого запись и перегрузка.

а вот как раз эта идея не слишком хорошая.

Представьте, что пользователь заказал клнсольными командами столько зон, что память оказалась перерасходована вдвое. Конфиг создан, записан в ЕЕПРОМ, перезагрузка.... и все :( Восстановить ардуину вы сможете только перепрошивкой.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

VLDnepr пишет:

2.Иногда код выводит неожиданные числа в тайминге плана, при чем именно в тайминге, стартовое время показывается правильно

Иногда - это при каких условиях? Подробнее с этого места.

Просто для аккуратности (это пока ни от чего не поможет):

1. Уберите слово typedef из строки №9. Оно там ничего не делает, просто выносит мозг.

2. После каждого realloc ставьте проверку, что он вернул и поднимайте тревогу, если память не выделилась.

VLDnepr
Offline
Зарегистрирован: 31.01.2022

Спасибо. Если решу вопрос со стабильностью подумаю как ограничить, например ЧислоПланов * ЧислоЗон не более какого-то объема памяти. 

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Если не секрет. Зона полива подразумевает исполнительную часть. Как минимум помпу. Как предполагаете решать вопрос соответствия? Ну, грубо говоря, задали 5 зон полива, нужно 5 помп. Которые куда-то должны подключаться и как-то управляться. А завтра задали 7 зон, нужно уже 7 помп. Как это будет делаться?

VLDnepr
Offline
Зарегистрирован: 31.01.2022

ЕвгенийП пишет:

VLDnepr пишет:

2.Иногда код выводит неожиданные числа в тайминге плана, при чем именно в тайминге, стартовое время показывается правильно

Иногда - это при каких условиях? Подробнее с этого места.

Просто для аккуратности (это пока ни от чего не поможет):

1. Уберите слово typedef из строки №9. Оно там ничего не делает, просто выносит мозг.

2. После каждого realloc ставьте проверку, что он вернул и поднимайте тревогу, если память не выделилась.

Иногда: в setup() сразу после заполнения config делается printConfig(). У меня есть еще код получания времени с RTC, получение команд с консоли и т.д. Одна из коменд консоли - это вызов printConfig(). Иногда printConfig() из setup() печатает конфигурацию с ожидаемыми значениями, а вызов через консольные команда уже битые. При чем, битыми получаются только тайминги по плану, т.е. config.poliv_Plans[0].poliv_ZoneTimes[0]...[7], остальные значения config правильные.

VLDnepr
Offline
Зарегистрирован: 31.01.2022

v258 пишет:

Если не секрет. Зона полива подразумевает исполнительную часть. Как минимум помпу. Как предполагаете решать вопрос соответствия? Ну, грубо говоря, задали 5 зон полива, нужно 5 помп. Которые куда-то должны подключаться и как-то управляться. А завтра задали 7 зон, нужно уже 7 помп. Как это будет делаться?

Не секрет.

1.Число зон на одном "огороде" не изменяется - бдет несколько "огородов" и не хочется для каждого из них править прошивку

2.Помпа, т.е. насос, один, по зонам предпалагаются электроклапаны

3.Еще предполалается контроль наличия потока чтобы мотор не накрылся если вода пропадет.

4.Почему несколько планов - потому как хочу не один раз в день залить по самое нехочу, а два-три раза но умерено, К томуже некоторые участки лучше полить утром, некоторые вечером, поэтому ПланПолива и имеет время запуска и тайминги полива.

5.Так что "сегодня 5 зон, завтра 7" не правильное видиние. На одном огороде 5 зон и 1 ПланПолива, на другом 7 зон и 2 ПланаПолива.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

VLDnepr пишет:

Иногда: в setup() сразу после заполнения config делается printConfig(). У меня есть еще код получания времени с RTC, получение команд с консоли и т.д. Одна из коменд консоли - это вызов printConfig(). Иногда printConfig() из setup() печатает конфигурацию с ожидаемыми значениями, а вызов через консольные команда уже битые. При чем, битыми получаются только тайминги по плану, т.е. config.poliv_Plans[0].poliv_ZoneTimes[0]...[7], остальные значения config правильные.

Ага, т.е. Вы привели нам не тот код, в котором возникает проблема (с консольными командами), а тот - в котором проблем не возникает - в котором нет работы с консолью.

Зачем? Мозги попудрить? И что Вы надеетесь от нас услышать, если код в котором проблема, Вы нам не показываете?

VLDnepr
Offline
Зарегистрирован: 31.01.2022

ЕвгенийП пишет:

VLDnepr пишет:

Иногда: в setup() сразу после заполнения config делается printConfig(). У меня есть еще код получания времени с RTC, получение команд с консоли и т.д. Одна из коменд консоли - это вызов printConfig(). Иногда printConfig() из setup() печатает конфигурацию с ожидаемыми значениями, а вызов через консольные команда уже битые. При чем, битыми получаются только тайминги по плану, т.е. config.poliv_Plans[0].poliv_ZoneTimes[0]...[7], остальные значения config правильные.

Ага, т.е. Вы привели нам не тот код, в котором возникает проблема (с консольными командами), а тот - в котором проблем не возникает - в котором нет работы с консолью.

Зачем? Мозги попудрить? И что Вы надеетесь от нас услышать, если код в котором проблема, Вы нам не показываете?

Нет, когда я это обнаружил, то написал тестовый пример, выкинув оттуда не касающееся.

Пример вывода приведен как раз к указанному коду. Вывод делался в цикле void loop()

Т.к. проблема плавающая, то начал грешить на неправильное выделение/использование динамической памяти - поэтому и был задан вопрос знатокам, может подскажете где в коде ошибка, что переписывает config.

 

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

может заменить все чтения из епрома в ConfigLoad, на заполнение  какими нибудь значениями, чтоб понять что не в этом проблема.

и еще, вы понимаете что вы пытаетесь сделать в этих строках,

  config.poliv_ZonePin=0;
  config.poliv_Plans=0;

 

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

1. Написать полнофункциональный класс для работы одной зоны со всеми настройками, расписаниями и преферансом.

2. Объявить массив объектов класса с максимальной размерностью исходя из ресурсоемкости класса и ресурсов на общие задачи (диспетчер общих ресурсов, погода и т.д.). Например 32, чтобы RainBird с Hunter`ом захлебнулись со своими 12-ю :-)

3. Завести и хранить в ЕЕпроме переменную с количеством АКТИВНЫХ зон в пределах 32. 

4. Работать только с нужным количеством активных зон. Например с тремя. В массиве объектов это делать очень удобно

ЗЫ: разумеется активные зоны добавляются/удаляются/редактируются прямо в интерфейсе

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

VLDnepr пишет:

Нет, когда я это обнаружил, то написал тестовый пример, выкинув оттуда не касающееся.

Но, Вы же писали, что

VLDnepr пишет:

через консольные команда уже битые.

А в данном коде консоли нет

VLDnepr пишет:

Пример вывода приведен как раз к указанному коду. Вывод делался в цикле void loop()

Но в Вашем коде loop пустой - там нет никакого вывода.

VLDnepr пишет:

 проблема плавающая

Это я понимаю, но если Вы говорите, что вывод в loop, так там должен быть вывод, а его нет.

Приведите тот самый код в котором возникает проблема. В коде из начально поста проблемы нет.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

VLDnepr пишет:

2.Помпа, т.е. насос, один, по зонам предпалагаются электроклапаны

Вот. Предельное количество клапанов, которые может потянуть МК и есть максимальное количество зон. Вот от этого и нужно отталкиваться. Т.е. задавать количество зон из числа возможных, неактивные зоны просто не использовать. И не надо никаких динамических массивов. 

В #35 уже расписали ))

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

v258 пишет:

Вот. Предельное количество клапанов, которые может потянуть МК и есть максимальное количество зон. Вот от этого и нужно отталкиваться. 

С клапанами там как раз все просто. Через сдвиговые регистры и ULN2803 на катушки SRD реле, коммутирующие 24v переменки. Хоть 100 реле и клапанов на 4-х пинах МК. Сложнее водяной баланс на всех поделить))

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Rumata пишет:

v258 пишет:

Вот. Предельное количество клапанов, которые может потянуть МК и есть максимальное количество зон. Вот от этого и нужно отталкиваться. 

С клапанами там как раз все просто. Через сдвиговые регистры и ULN2803 на катушки SRD реле, коммутирующие 24v переменки. Хоть 100 реле и клапанов на 4-х пинах МК. Сложнее водяной баланс на всех поделить))

Их у него все равно будет конечное количество

vanila
Offline
Зарегистрирован: 25.08.2015

по мне так предельное количество зон в разы а то и на порядки  меньше чем то сколько может потянуть мк. а если мегу еще взять)) 

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

не зря же бытовые контроллеры 4-6 ну 12 зон а то и вообще одна)

 

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

VLDnepr пишет:

Вы отчасти правы, но:

1.Хочется понять почему не работает, и понять методы

Хочется - понимайте.

Только не надо пытаться привлечь к этой неблагодарной работе других людей.

 

Хорошая программа - это та, что легко отлаживается.

Вам дали советы, как улучшить Вашу программу.

Считаете, что советы Вам не подходят - разбирайтесь сами.

Цитата:

2.Память выделается динамически раз по время старта и более не меняется, поэтому как я могу предположить утечек быть не должно.

Что мешает определить ряд констант (размеров массивов), определить эти массивы статически и несколько раз откомпилировать одну и ту же программу с разными значениями констант?

Заодно компилятор Вам сообщит о расходе памяти для каждого из вариантов.

VLDnepr
Offline
Зарегистрирован: 31.01.2022

xDriver пишет:

может заменить все чтения из епрома в ConfigLoad, на заполнение  какими нибудь значениями, чтоб понять что не в этом проблема.

и еще, вы понимаете что вы пытаетесь сделать в этих строках,

  config.poliv_ZonePin=0;
  config.poliv_Plans=0;

 

В этих переменных (указателях на динамический массив) хранятся номера по зонам и планы полива.

Пытаюсь обнулить адреса указателей, чтобы стереть то, что было записано в EEPROM, а потом восстановлено при чтении. Эти адреса уже недействительны и будут пересозданы. Ну это по задумке.

VLDnepr
Offline
Зарегистрирован: 31.01.2022

xDriver пишет:

может заменить все чтения из епрома в ConfigLoad, на заполнение  какими нибудь значениями, чтоб понять что не в этом проблема.

и еще, вы понимаете что вы пытаетесь сделать в этих строках,

  config.poliv_ZonePin=0;
  config.poliv_Plans=0;

 

В этих переменных (указателях на динамический массив) хранятся номера по зонам и планы полива.

В эти строки закладывалось обнуление указателя, т.е. чтобы он не указывал никуда.

Подскажите (плыву я в указателях), кажется строки обнуляют значение области памяти по этому указателю, а не сам указатель (т.е. куда он указывает)?

Как правильно обнулить указатель в данном случае?

VLDnepr
Offline
Зарегистрирован: 31.01.2022

andriano пишет:

VLDnepr пишет:

Вы отчасти правы, но:

1.Хочется понять почему не работает, и понять методы

Хочется - понимайте.

Только не надо пытаться привлечь к этой неблагодарной работе других людей.

 

Хорошая программа - это та, что легко отлаживается.

Вам дали советы, как улучшить Вашу программу.

Считаете, что советы Вам не подходят - разбирайтесь сами.

Цитата:

2.Память выделается динамически раз по время старта и более не меняется, поэтому как я могу предположить утечек быть не должно.

Что мешает определить ряд констант (размеров массивов), определить эти массивы статически и несколько раз откомпилировать одну и ту же программу с разными значениями констант?

Заодно компилятор Вам сообщит о расходе памяти для каждого из вариантов.

1.Вообщето, никто никого не неволит и не заставляет впрягаться.

2.Я написал сюда, чтобы Профи Опытным глазом глянул на код (если у него есть желание) и указал начинающему на методологические ошибки кода, например как это сделал xDriver (два плюса в карму), а не глумления над высокой идеей реализации настраиваемого устройства, и не для призывов, зачем тебе разные инструменты, используй кувалду - она надежнееееееее.

3.Хорошая программа - это не та что хорошо отладивается, это та которая отлажена и выполняет свои функции, и если нужно перестраивается и подстраивается. И еще - очень плохая идея под отдельную настройку устройства держать свой вариант кода.

4.Мне дали советы - я их прочитал, с некоторыми согласился, но прошу не возводить свое мнение в МнениеПоследнейИнстанции, да именно в программировании контроллеров я начинающий, НО, титаник построили проффессионалы, ковчег построил любитель, да и не боги горшки лепят. Еще раз - советы я прочитал, спасибо за хорошие идеи.

5.Не хотел обидеть никого, не принимайте на свой счет, но и подавлять чудое мнение тоже негоже.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

VLDnepr пишет:

1.Хочется понять почему не работает, и понять методы

Если это правда, то приведите, наконец, код в котором есть проблема (с печатью в loop, с консолью и т.п.). Я Вам уже писал (но Вы игнорируете), что пока Вы сокращали код, Вы выбросили проблему, в этом коде проблемы нет.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ЕвгенийП ну как вы смеете просить выложить интеллектуальную собственность ? Украдут же !!!

VLDnepr
Offline
Зарегистрирован: 31.01.2022

ЕвгенийП пишет:

VLDnepr пишет:

1.Хочется понять почему не работает, и понять методы

Если это правда, то приведите, наконец, код в котором есть проблема (с печатью в loop, с консолью и т.п.). Я Вам уже писал (но Вы игнорируете), что пока Вы сокращали код, Вы выбросили проблему, в этом коде проблемы нет.

Вчера переписывал/испытывал код

/* test_002_DinamicArray_EEPROM
*/

#include <EEPROM.h>

#define SerialSpeed 9600
#define InvBit(reg, bit)    reg ^= (1<<(bit))

bool needRestart = false;

// Структура объекта ЗонаПолива
struct PolivPlan_t {
  bool Active ;
  uint8_t TimeStartH; 
  uint8_t TimeStartM;
  uint8_t *poliv_ZoneTimes=0; // массив Таймингов для каждой зоны
  } ;

struct configObj {
  bool serialShow; // показывать диагностический вывод
  bool poliv_Active; // полив может запускаться
  uint8_t poliv_ZoneCount; // количество зон полива
  uint8_t poliv_PlanCount; // количество планов полива
  uint8_t *poliv_ZonePin=0; // Массив номеров зон полива
  PolivPlan_t *poliv_Plans=0;// Массив зон полива
  byte controlByte = 0x55;
} config;


//-----------------------------------------------------------------------
void printConfig();



void ConfigSave(){
  uint16_t startingAddress = 0;
  EEPROM.put(startingAddress, config); startingAddress += sizeof(config); 
  //poliv zone pins
  for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++){
    EEPROM.put(startingAddress, config.poliv_ZonePin[_zone]); startingAddress += sizeof(config.poliv_ZonePin[_zone]);
  }
  //poliv plans
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    EEPROM.put(startingAddress, config.poliv_Plans[_plan].Active); startingAddress += sizeof(config.poliv_Plans[_plan].Active);
    EEPROM.put(startingAddress, config.poliv_Plans[_plan].TimeStartH); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartH);
    EEPROM.put(startingAddress, config.poliv_Plans[_plan].TimeStartM); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartM);
    for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++) {
      EEPROM.put(startingAddress, config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]); startingAddress += sizeof(config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]);
    }
  }
}


void ConfigLoad(){
Serial.print("\np1");  
  uint16_t startingAddress = 0;
  EEPROM.get(startingAddress, config); startingAddress += sizeof(config); 
  config.poliv_ZonePin=0;
  config.poliv_Plans=0;

  ChangeConfig();
  
  //poliv zone pins
  for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++){
    EEPROM.get(startingAddress, config.poliv_ZonePin[_zone]); startingAddress += sizeof(config.poliv_ZonePin[_zone]);
  }
  //poliv plans
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    EEPROM.get(startingAddress, config.poliv_Plans[_plan].Active); startingAddress += sizeof(config.poliv_Plans[_plan].Active);
    EEPROM.get(startingAddress, config.poliv_Plans[_plan].TimeStartH); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartH);
    EEPROM.get(startingAddress, config.poliv_Plans[_plan].TimeStartM); startingAddress += sizeof(config.poliv_Plans[_plan].TimeStartM);
    for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++) {
      EEPROM.get(startingAddress, config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]); startingAddress += sizeof(config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]);
    }
  }
}

void ChangeConfig(){
  //--- poliv_ZonePin
  config.poliv_ZonePin = (uint8_t*) realloc(config.poliv_ZonePin, config.poliv_ZoneCount * sizeof(uint8_t));

  //--- poliv_Plans
  config.poliv_Plans = (PolivPlan_t*) realloc(config.poliv_Plans, config.poliv_PlanCount * sizeof(PolivPlan_t));
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    config.poliv_Plans[_plan].poliv_ZoneTimes = (uint8_t*)realloc(config.poliv_Plans[_plan].poliv_ZoneTimes, config.poliv_ZoneCount * sizeof(uint8_t));
  }
}

void setup() {//=== SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP ======= SETUP =======
  Serial.begin(SerialSpeed);


  // --- Предварительная запись в EEPROM ----
  Serial.println(F("test varialbe set"));
  config.poliv_Active = true;
  config.poliv_ZoneCount = 7;
  config.poliv_PlanCount = 1;
  config.poliv_ZonePin = 0; // первоначальная инициализация перед заполнением из EEPROM
  config.poliv_Plans = 0;   // первоначальная инициализация перед заполнением из EEPROM

  Serial.println(F("\nChangeConfig();"));
  ChangeConfig(); // переконфигурирование CONFIG

  Serial.println(F("test varialbe set next"));
  config.poliv_ZonePin[0] = 20;
  config.poliv_ZonePin[1] = 21;
  config.poliv_ZonePin[2] = 22;
  config.poliv_ZonePin[3] = 23;
  config.poliv_ZonePin[4] = 24;
  config.poliv_ZonePin[5] = 25;
  config.poliv_ZonePin[6] = 26;
  config.poliv_Plans[0].Active = true;
  config.poliv_Plans[0].TimeStartH = 12;
  config.poliv_Plans[0].TimeStartM = 33;
  config.poliv_Plans[0].poliv_ZoneTimes[0] = 5;
  config.poliv_Plans[0].poliv_ZoneTimes[1] = 6;
  config.poliv_Plans[0].poliv_ZoneTimes[2] = 7;
  config.poliv_Plans[0].poliv_ZoneTimes[3] = 8;
  config.poliv_Plans[0].poliv_ZoneTimes[4] = 9;
  config.poliv_Plans[0].poliv_ZoneTimes[5] = 10;
  config.poliv_Plans[0].poliv_ZoneTimes[6] = 11;

  printConfig();

  Serial.println(F("\nChangeSave()"));
  ConfigSave();


/*  Serial.println(F("\nConfigLoad()"));
  ConfigLoad();
  Serial.println("\nAFTER LOAD");

  printConfig();
*/  
}

  
void loop(){
  Serial.println("\nvoid loop()");
  printConfig();
  delay(1000);

}


void printConfig() {//--- Вывод текущей конфигурации ----------
  Serial.print(F("Current config")); 
  if (needRestart) Serial.print(F(" !!! Need restart !!!"));
  Serial.println(); 
  Serial.print(F("serial show:")); Serial.println(config.serialShow);
  Serial.print(F("poliv_Active:")); Serial.println(config.poliv_Active);
  Serial.print(F("poliv_ZoneCount:")); Serial.print(config.poliv_ZoneCount); 
    Serial.print(F(" contact numbers:"));  for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++){Serial.print(config.poliv_ZonePin[_zone], DEC); Serial.print(" ");} Serial.println();
  Serial.print(F("poliv_PlanCount:")); Serial.println(config.poliv_PlanCount);

  // poliv plan
  for (uint8_t _plan=0; _plan<config.poliv_PlanCount; _plan++) {
    Serial.print(F("poliv_Plan:")); Serial.print(_plan);
    Serial.print(F(" active:")); Serial.print(config.poliv_Plans[_plan].Active);
    Serial.print(F(" time:")); Serial.print(config.poliv_Plans[_plan].TimeStartH);Serial.print(F(":")); Serial.print(config.poliv_Plans[_plan].TimeStartM);
    Serial.print(F(" timers:"));
    for (uint8_t _zone=0; _zone<config.poliv_ZoneCount; _zone++) {
      Serial.print(config.poliv_Plans[_plan].poliv_ZoneTimes[_zone]);Serial.print(F(" "));
    }
    Serial.println();
  }
}


/*
ChangeConfig();
test varialbe set next
Current config
serial show:0
poliv_Active:1
poliv_ZoneCount:7 contact numbers:20 21 22 23 24 25 26 
poliv_PlanCount:1
poliv_Plan:0 active:1 time:12:33 timers:21 32 8 56 0 1 0 

ChangeSave()
*/

 

VLDnepr
Offline
Зарегистрирован: 31.01.2022

Господа, ответте по #43

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

VLDnepr пишет:

Вчера переписывал/испытывал код

Вы точно уверены, что результат приведённый в комментарии получен с этим кодом? Совсем с этим, а не с "я там ... но это ни на что не влияет"?

Если с этим, то ищите проблему в шнуре, в раздолбанных разъёмах или ещё в каких помехах.

Я запускал это код на своей тест-станции 3252 раза и ни разу не видел ни одного сбоя.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

VLDnepr пишет:

xDriver пишет:

может заменить все чтения из епрома в ConfigLoad, на заполнение  какими нибудь значениями, чтоб понять что не в этом проблема.

и еще, вы понимаете что вы пытаетесь сделать в этих строках,

  config.poliv_ZonePin=0;
  config.poliv_Plans=0;

 

В этих переменных (указателях на динамический массив) хранятся номера по зонам и планы полива.

В эти строки закладывалось обнуление указателя, т.е. чтобы он не указывал никуда.

Подскажите (плыву я в указателях), кажется строки обнуляют значение области памяти по этому указателю, а не сам указатель (т.е. куда он указывает)?

Как правильно обнулить указатель в данном случае?

все правильно понимаете, но правильнее "обнулять" указатели раньше было так

uint8_t *ptr = NULL;

сейчас советуют так

uint8_t *ptr = nullptr;

погуглите.