Для щелочных аккумуляторов, с их проблемами и преимуществами ниодин из производителей не выпустил ниодного бюджетного толкового контроллера, способного не только заряжать, но и производить их легкое обслуживание. Эта работа - проба пера. Познакомившись с микроконтроллерами (Atmega 328) возникло желание сделать нечто большее, чем просто пределко-свистелку. Предлагаю бета версию, бета версии зарядного автомата на ардуино.

 Эта версия с открытым исходным кодом, открытыми печатками, ограниченными (но весьма широкими) возможностями. Следующие версии будут поддерживать MPPT, учитывать все что можно, иметь стабилизированный выход и продвинутые алгоритмы заряда (основанные на информации от WathCat). Я думаю если все это я реализую сам - я в праве попросить за это денег(на содержание сайта). Но это в будущем. Этот проект есть и будет открыт для всех интересующихся. 

Для затравки - несколько фото.

 IMG 20151103 012643

IMG 20151103 012705

IMG 20151103 012717

 Поскольку NICD аккумуляторы - токовые товарищи - то контроль напряжения для них весьма косвенный показатель. Поэтому проба пера была спроектированна минимальной и достаточной. Она должна уметь заряжать и разряжать качелями до любых нужных напряжений в авторежиме. Даже без участия пользователя. Пока еще не реализован, но скоро будет, алгоритм заряда щелочи от WatchCat. Он заключается в заливке батарей до тех пор пока напруга на них не перестанет расти в течение получаса . На данный момент реализовано 80 процентов функционала контроля, заряда, разряда. 100 процентов управления напряжениями заряда, включения, выключения, разряда, качелей, буста, обеспечении сохранности настроек и режимов при отключении питанияи тд. Софт на ардуино. Прошивается ей же. Железная часть от популярных китайских контроллеров которые с успехом работают у болшинства солнечных энергетиков. 

26.06.2017

Забил на эту разработку. Решил продолжать на  основе этой более перспективную. Выкладываю рабочий на 90 процентов код. 

Кому надо берите но с ссылкой на эту страницу. 

/* Main.ino file generated by New Project wizard

 *

 * Created:   Thu Sep 5 2013

 * Processor: ATmega328P

 * Compiler:  Arduino AVR

 */

 

#include <LiquidCrystal.h>

#include <EEPROM.h>

#include <EEPROMAnything.h>

LiquidCrystal lcd (2,4,5,6,7,8);

int menuItem = 0; /* 0 - idle menu

menuIdleUbat - показывает информацию о батарее в дежурном режиме 

menuIdleUsp - показывает информацию о солнечной батарее

menuIdleTime - показывает UpTime контроллера 

menuIdleKtc - показывает последние 4 результата КТЦ

                 1-меню установки максимального напряжения заряда

11 - меню установки напряжения начала заряда

13 - Меню метод заряда

14 - Меню напряжения конечного напряжения буста 

15 - Меню Напряжения установки необходимости буста 

16 - Меню Напряжения отключения нагрузки 

17 - Меню напряжения возобновления нагрузки 

                 18 - меню подстройки датчика тока 

2- discharge menu

3-KTC menu Экран меню  позволяющий нам запустить ктц.

30- Экран запущеного ктц. Отображает напряжение на батарее, слитую емкость и время КТЦ 

31 - Экран выбора напряжения, до которого будет производиться КТЦ. 

 

39 - экран отмены КТЦ

*/

///таймеры 

word counter = 0; // (0) счетчик запусков контроллера

long timeKtc = 0; // (8)таймер КТЦ. показывает сколько длится КТЦ. - пишем в память по адресу 

int timer00=0;  

int timer0 = 0; //основной таймер использующийся для подсчета времени  

int timer1 = 0; // время нажатия кнопки 

int timer2 =0;  

///Коды кнопок

int kodKnopki = 0; // возвращаемый код нажатия кнопки 1-вверх 2-вправо 3-вниз 4-влево 5-центр

float UbatKacheliMax = 15; // (12) // верхний предел качелей

float UbatKacheliMin = 14; // (16)  // нижний предел качелей

byte ahIdleTime = 0; // Счетчик меняющий отображение влитой и слитой энергии аккумулятора

byte day=0; // (20)

byte god=0;// (21)

byte sec=0;// (22)

byte minut=0;//(23)

byte hour = 0;//(24)

byte idleTime=0;//

// переменные для измерений напряжений и токов

 

float Ubat = 0; // напряжение на аккумуляторной батарее 

float Ibat = 0; // ток проходящий через батарею. Положительное значение - зарядка. Отрицательное - разрядка.

float Usp = 0; //  напряжение солнечной панели. Пока используется только для мониторинга. В будущем возможно задействовать мппт

float Isp = 0; // То же самое только хранит данные о токе СП 

long Ubatp = 0; // Промежуточные значения используемые для усреднения

long Ibatp = 0; // Промежуточные значения используемые для усреднения

long Uspp = 0; // Промежуточные значения используемые для усреднения

long Ispp = 0; // Промежуточные значения используемые для усреднения

float Pbat=0;

float ahBatIn=0; // (28)Общий глобальный счетчик влитой а аккумулятор энергии

float ahBatOut = 0; // (85)Общий глобальный счетчик взятой из аккумулятора энергии

//Параметры KTC

byte executeKTC = 0; /* значeние этой переменной 1 - говорит программе о намереннии пользователя запустить ктц после нажатия кнопки МЕНЮ. 

Следовательно в лупе должна проходить проверка условия намерения и запускаться КТЦ после этого */

byte ktcIdet = 0; /* (33)если в 1 - то делаем ктц. ktcEnable при перезагрузке дает понять что цикл был перерван по каким то

причинам и нам надо его продолжить с того места в котором он остановился*/

byte ktcEnableLoad = 0; // флаг инициализации настроек ктц. Отличается от ktcEnable тем что выполняется при инициализации цикла. 

// Обнуляет настройки предыдущего ктц. Устанавливает флаги времени и емкости в нули.   

byte numberOfSecondsKtc = 0;// (noEpromSave)

byte numberOfMinutesKtc  = 0;// (noEpromSave)

byte numberOfHoursKtc = 0;// (noEpromSave)

float minBatVolForKtc = 9;// (34)

float AhKtc = 0;//(38)

byte AbortKTC = 0;

byte metodZaryada=0; //39

byte loadButton=1; //40

float UbatLoadOff = 8.5; // 41

byte boostEnable = 1; //45

byte kacheliVverh = 1;//46

float UbatNugenBoost = 12;//50

byte chargeStatus=0;

float UbatBoostOff = 16;

float rezultatKtc1 = 0;

float rezultatKtc2 = 0;

float rezultatKtc3 = 0;

float rezultatKtc4 = 0;

int zadergkaKoncaKtc=0;

int zadergkaKoncaKtc00=0;

byte eepromCounter=0; //..счетчик записи епром 

byte progonovKtc=1;

int AmpTune=0;

//////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////

 

 

void loadOn(){

if (loadButton==1){digitalWrite(9, LOW);}// включили нагрузку

else{digitalWrite(9, HIGH);} // выключили нагрузку 

}

 

void loadOff(){

digitalWrite(9, HIGH);

}

 

void sunOn(){

digitalWrite(10, LOW);

}

 

void sunOff(){

digitalWrite(10, HIGH);

}

 

void kacheli(){

 

if (boostEnable == 1) {

loadOn();

sunOn(); // фигачим до напряжения отключения буста

   if (Ubat>UbatBoostOff){//  перепрыгнули буст - тушим заряд и отменяем буст 

   kacheliVverh=0;

   sunOff();

   loadOn();

   boostEnable=0;

   chargeStatus=7;//SOn/LOn/v

   if (progonovKtc>0){ktcIdet=1;timeKtc = 0;AhKtc=0; loadButton=0; }// надо сбросить предидущие результаты ктц в ноль. отключили нагрузку

   

   }

 

}

 

else {

if(Ubat<UbatLoadOff){ // если напряжение батареи ниже порога работы мосфетов нагрузки-отключаем нагрузку,Включаем солнце,

loadOff();

sunOn();

boostEnable=1;

kacheliVverh=1; // качели вверх

chargeStatus = 0; // SOn/Loff/^/Boost

}

if(Ubat>UbatLoadOff && Ubat<UbatNugenBoost){// Интервал когда напряжение уже перевалило за разрешенное. разрешаем лоад и бустим после заряда

sunOn();

loadOn();

boostEnable=1;

kacheliVverh=1; // качели вверх

chargeStatus=1; // SOn/LOn/^/Boost

}

if(Ubat>UbatNugenBoost&&Ubat<UbatKacheliMin){// Если напряжение больше того чем порог бустирования то включаем качели не включая буст 

sunOn();

loadOn();

kacheliVverh=1; // качели вверх

chargeStatus=2; // SOn/LOn/^/Boost

}

 

if(Ubat>UbatKacheliMin&&Ubat<UbatKacheliMax){ // если напряжение в области качелей - проверяем какой флаг качелей 

   if (kacheliVverh==1){

   sunOn();

   loadOn();

   kacheliVverh=1; // качели вверх 

   chargeStatus=3;// SOn/LOn/^/Boost

   }

   if (kacheliVverh==0) { 

   sunOff();

   loadOn();

   chargeStatus=4;// SOn/LOn/v/Boost

   }

}

 

if (Ubat>UbatKacheliMax){ // если дошли до верхнего значения качелей-проверяем флаг буста и  или бустим или гасим

kacheliVverh=0;

chargeStatus=6; //SOn/LOn/v

sunOff();

loadOn();

}

      }

}

 

 

 

 

void menuIdle(){ //Основное 0 меню. Меняет экраны раз в три секунды. Периодически показывает данные о батаее, солнечной панели, времени жизни и тд. 

if (idleTime<= 4){ menuIdleUbat();}// длительность первого экрана в секундах 

if (idleTime > 4 && idleTime<=8){menuIdleUsp();}

if (idleTime > 8 && idleTime < 9){menuIdleTime();}

if (idleTime >= 9 ) {menuIdleRezultatKtc();}

if (kodKnopki==5) {

menuItem = 1; // Если в дежурном режиме центральную кнопку - показываем пункт меню - установка напряжений.  

kodKnopki=0; // И возвращаем флаг нажатия кнопок в ноль. То есть с этого момоента начинаем снова отслеживать условие нажатия кнопки.

}

if (kodKnopki==3) { kodKnopki=0; // вниз 

 if(loadButton==1){loadButton=0;lcd.setCursor(0,0);lcd.print("x");}

 else {loadButton=1;lcd.setCursor(0,0);lcd.print("*");}

 }

}

 

void menuIdleUbat(){ // Дежурный экран - Напряжение батареи

lcd.setCursor(0,0);lcd.print("Ub=");lcd.print(Ubat,1);lcd.print(" Ib=");if (Ibat>=0){lcd.print (" "); lcd.print (Ibat,1);} if(Ibat<0){lcd.print (Ibat,1);lcd.print (" ");}

lcd.setCursor (0,1);lcd.print ("Pb=");lcd.print (Pbat,1);lcd.print ("  ");lcd.setCursor(10,1); if (ahIdleTime==0){lcd.print (" ");lcd.print (ahBatIn,1);lcd.print ("   ");} if (ahIdleTime==1){lcd.print (ahBatOut,1);lcd.print ("   ");}

}

 

void menuIdleUsp(){ // Дежурный экран - напряжение солнечной панели

lcd.setCursor(0,0);lcd.print("Us=");lcd.print (Usp,1);lcd.print ("V");lcd.print ("     ");

lcd.setCursor (0,2);

 

if (chargeStatus==0){lcd.print ("SOn/LOff/^/Boost");lcd.print ("              ");}

if (chargeStatus==1){lcd.print ("SOn/LOn/^/Boost");lcd.print ("              ");}

if (chargeStatus==2){lcd.print ("SOn/LOn/^/Boost");lcd.print ("              ");}

if (chargeStatus==3){lcd.print ("SOn/LOn/^/Boost");lcd.print ("              ");}

if (chargeStatus==4){lcd.print ("SOn/LOn/v/Boost");lcd.print ("              ");}

if (chargeStatus==5){lcd.print ("SOn/LOn/v/BBOST");lcd.print ("              ");}

if (chargeStatus==6){lcd.print ("SOn/LOn/v");lcd.print ("              ");}

if (chargeStatus==7){lcd.print ("SOn/LOn/v");lcd.print ("              ");}

 

}

 

// дежурный экран - время жизни

 

void menuIdleTime(){

lcd.setCursor(0,0);lcd.print (" LTime-");

if(god==0){lcd.print("00");}

else{if (god<10){lcd.print("0");lcd.print("god");}

     else{lcd.print(god);}

}

lcd.print("Year");

lcd.setCursor(0,2);

if (day==0){lcd.print("00");}

else{if (day<10){lcd.print("0");lcd.print("day");}

else{lcd.print(day);}

}

lcd.print("d:");

if(hour==0){lcd.print("00");}

else{if(hour<10){lcd.print("0");lcd.print(hour);}

else{lcd.print(hour);}

}

lcd.print("H:");

if(minut==0){lcd.print("00");}

else{if(minut<10){lcd.print("0");lcd.print(minut);}

else{lcd.print(minut);}

}

lcd.print("m:");

if (sec<10){lcd.print("0");lcd.print(sec);}

else{lcd.print(sec);}

lcd.print("s    ");

}

 

void menuIdleRezultatKtc(){

lcd.setCursor(0,0);lcd.print("1-");lcd.print(rezultatKtc1);lcd.print(" 2-");lcd.print(rezultatKtc2);lcd.print("    ");

lcd.setCursor(0,2);lcd.print("3-");lcd.print(rezultatKtc3);lcd.print(" 4-");lcd.print(rezultatKtc4);lcd.print("    ");

}

 

void menuChargeUpLevel(){ // меню 1 выбора максимального напряжения заряда 

lcd.setCursor(0,0);lcd.print("MaxKacheliVolume");

lcd.setCursor(0,2);lcd.print ("<");lcd.print( UbatKacheliMax,1);lcd.print(">V          ");

if(kodKnopki==1){UbatKacheliMax=UbatKacheliMax+0.1;kodKnopki=0;}

if (kodKnopki==3){UbatKacheliMax=UbatKacheliMax-0.1;kodKnopki=0;}

if (kodKnopki==5){kodKnopki=0;eepromSave();menuItem=0;}

if(kodKnopki==2){kodKnopki=0;menuItem = 11 ;lcd.clear();}

if(kodKnopki==4){kodKnopki=0;menuItem = 3 ;lcd.clear();}

}

 

void menuChargeResumeLevel(){ // меню 11 - возобновления заряда при падении напряжения на батарее в режиме качелей

lcd.setCursor(0,0);lcd.print("MinKacheliVolume         ");

lcd.setCursor(0,2);lcd.print ("<");lcd.print( UbatKacheliMin,1);lcd.print(">V             ");

if(kodKnopki==1){ UbatKacheliMin=UbatKacheliMin+0.1;kodKnopki=0;} // вверх

if (kodKnopki==3){ UbatKacheliMin=UbatKacheliMin-0.1;kodKnopki=0;}//  вниз 

if (kodKnopki==5){ kodKnopki=0; menuItem=0; eepromSave();}// Меню // eprom save// переход на дежурный экран

if(kodKnopki==2){kodKnopki=0;menuItem = 13 ;lcd.clear();}

if(kodKnopki==4){kodKnopki=0;menuItem = 1 ;lcd.clear();}

}

 

void SwatchCat(){}

 

void regimRaboti(){

if (ktcIdet==0&&metodZaryada==0){kacheli();}

if (ktcIdet==0&&metodZaryada==1){SwatchCat();}

if (ktcIdet==1){menuItem = 30;}

}

 

void menuViboraMetodaZaryada(){  // 13 меню 

lcd.setCursor(0,0);lcd.print("Metod Zaryada");

lcd.setCursor(0,2);

if (metodZaryada==0) {lcd.print ("<Kacheli>           "); lcd.print(metodZaryada);}

if (metodZaryada==1) {lcd.setCursor(0,2);lcd.print ("<SWachCat>          ");lcd.print (metodZaryada);}

if(kodKnopki==1){metodZaryada=metodZaryada+1;kodKnopki=0;}

if(kodKnopki==3){metodZaryada=metodZaryada-1;kodKnopki=0;} //  вниз 

if(kodKnopki==5){kodKnopki=0;eepromSave();menuItem=0;} // переход на дежурный экран

if(kodKnopki==2){kodKnopki=0;menuItem = 14 ;lcd.clear();}

if(kodKnopki==4){kodKnopki=0;menuItem = 11 ;lcd.clear();}

if (metodZaryada>1){metodZaryada=1;}

if (metodZaryada<0){metodZaryada=0;}

}

 

 

void menuBoostOffVol(){ //14

lcd.setCursor(0,0);lcd.print("BoostOffVol");

lcd.setCursor(0,2);lcd.print ("<");lcd.print (UbatBoostOff,1);lcd.print (">             ");

if(kodKnopki==1){UbatBoostOff=UbatBoostOff+0.1;kodKnopki=0;}

if(kodKnopki==3){UbatBoostOff=UbatBoostOff-0.1;kodKnopki=0;}

if(kodKnopki==5){kodKnopki=0;eepromSave();menuItem=0;} // переход на дежурный экран

if(kodKnopki==2){kodKnopki=0;menuItem = 15 ;lcd.clear();}

if(kodKnopki==4){kodKnopki=0;menuItem = 13 ;lcd.clear();}

}

 

void menuBoostOnLevel(){ // меню 15 - напряжение при которм включется кипятильник 

lcd.setCursor(0,0);lcd.print("BoostOnVolume         ");

lcd.setCursor(0,2);lcd.print ("<");lcd.print( UbatNugenBoost,1);lcd.print(">V             ");

if(kodKnopki==1){ UbatNugenBoost=UbatNugenBoost+0.1;kodKnopki=0;} // вверх

if (kodKnopki==3){ UbatNugenBoost=UbatNugenBoost-0.1;kodKnopki=0;}//  вниз 

if (kodKnopki==5){ kodKnopki=0; menuItem=0; eepromSave();}// Меню // eprom save// переход на дежурный экран

if(kodKnopki==2){kodKnopki=0;menuItem = 16 ;lcd.clear();}

if(kodKnopki==4){kodKnopki=0;menuItem = 14 ;lcd.clear();}

}

 

 

void menuLoadOffLevel(){ // меню 16 - напряжение при которм отключается нагрузка 

lcd.setCursor(0,0);lcd.print("LoadOffVolume         ");

lcd.setCursor(0,2);lcd.print ("<");lcd.print( UbatLoadOff,1);lcd.print(">V             ");

if(kodKnopki==1){ UbatLoadOff=UbatLoadOff+0.1;kodKnopki=0;} // вверх

if (kodKnopki==3){ UbatLoadOff=UbatLoadOff-0.1;kodKnopki=0;}//  вниз 

if (kodKnopki==5){ kodKnopki=0; menuItem=0; eepromSave();}// Меню // eprom save// переход на дежурный экран

if(kodKnopki==2){kodKnopki=0;menuItem = 18 ;lcd.clear();}

if(kodKnopki==4){kodKnopki=0;menuItem = 15 ;lcd.clear();}

}

 

void menuKtcSetting(){ // меню 3 запуска КТЦ

lcd.setCursor(0,0);lcd.print("KTC settings");

lcd.setCursor(0,2);lcd.print ("Execute?   ");

if(kodKnopki==1){lcd.setCursor(0,2);lcd.print ("Execute    <Yes>     ");executeKTC=1;kodKnopki=0;}

if(kodKnopki==3){lcd.setCursor(0,2);lcd.print ("Execute    < No>     ");executeKTC=0;kodKnopki=0;}

if (kodKnopki==5){kodKnopki=0;  // Меню по нажатию либо выходим в ИДЛЕ меню. Либо запускаем КТЦ

if (executeKTC==1){

menuItem = 30; 

executeKTC=0;

ktcIdet=1; // надо запустить ктц 

timeKtc = 0;

AhKtc=0;// надо сбросить предидущие результаты ктц в ноль.

eepromSave();

}

else{eepromSave();

menuItem=0; // переход на дежурный экран

executeKTC=0;

}

}

if(kodKnopki==2){kodKnopki=0;menuItem = 1 ;lcd.clear();}

if(kodKnopki==4){kodKnopki=0;menuItem = 31 ;lcd.clear();}

}  

 

void menuAktivnogoKtc(){ //экран 30 активного КТЦ

loadButton=1;

sunOff(); // отключаем панель

loadOn(); // подключаем нагрузку

lcd.setCursor(0,0);lcd.print("KTC");lcd.print(progonovKtc);lcd.print(" ");lcd.print(numberOfHoursKtc,1);lcd.print(":");lcd.print(numberOfMinutesKtc);lcd.print(":");lcd.print(numberOfSecondsKtc);lcd.print("        ");lcd.setCursor(15,0);lcd.print((zadergkaKoncaKtc/1000));

numberOfSecondsKtc = (timeKtc)%60;

numberOfMinutesKtc  = (timeKtc/60)%60;

numberOfHoursKtc  = timeKtc/3600;

lcd.setCursor(0,2);lcd.print("Ub:");lcd.print(Ubat,1);lcd.print(" AH:");lcd.print(0-AhKtc,1);lcd.print("    ");

if (kodKnopki==5){kodKnopki=0;menuItem=39; lcd.clear();}// переход на экран выбора отмены КТЦ.

if (Ubat<minBatVolForKtc){zadergkaKoncaKtc=millis()-zadergkaKoncaKtc00;}

else{zadergkaKoncaKtc00=millis();}

if (zadergkaKoncaKtc>4000){rezultatKtc4=rezultatKtc3;rezultatKtc3=rezultatKtc2;rezultatKtc2=rezultatKtc1;rezultatKtc1=0-AhKtc; // таймаут для качелей на разряд и одновременно защита от сброса флага ктц при пропадании питания 

  ktcIdet=0;

  menuItem=0;

  zadergkaKoncaKtc=0;

progonovKtc = progonovKtc - 1;

if (progonovKtc>0){loadButton=0; } //  если прогоны еще остались - выключаем LOAD чтобы поскорее зарядить аккумуляторы. Можно включить руками 

 

  }  

 

}

 

 

 

void menuKtcDischargeBatLevel(){ // меню 31 -выбор минимального напряжения до которого будет вестись КТЦ

lcd.setCursor(0,0);lcd.print("MinVolForKTC        ");

lcd.setCursor(0,2);lcd.print ("<");lcd.print(minBatVolForKtc,1);lcd.print(">V        ");

if(kodKnopki==1){minBatVolForKtc=minBatVolForKtc+0.1;kodKnopki=0;}

if(kodKnopki==3){minBatVolForKtc=minBatVolForKtc-0.1;kodKnopki=0;}

if(kodKnopki==5){kodKnopki=0;menuItem=3;} // переход на экран запуска ктц

if(kodKnopki==2){kodKnopki=0;menuItem = 3;lcd.clear();}// переход на экран запуска ктц

if(kodKnopki==4){kodKnopki=0;menuItem = 32;lcd.clear();}// переход на экран запуска ктц

}

 

void menuKtcAbort(){// экран 39 - отмены ктц 

ktcIdet=0;

lcd.setCursor(0,0);lcd.print("KTC abort!");

lcd.setCursor(0,2);lcd.print ("Execute?   ");

if(kodKnopki==1){lcd.clear();lcd.setCursor(0,2);lcd.print ("Execute    <Yes>    ");kodKnopki=0;AbortKTC=1;}// 

if(kodKnopki==3){lcd.clear();lcd.setCursor(0,2);lcd.print ("Execute    < No>    ");kodKnopki=0;AbortKTC=0;}

if(kodKnopki==5){ 

   kodKnopki=0;

      if (AbortKTC==1){AbortKTC=0;ktcIdet = 0;menuItem = 0;lcd.clear();rezultatKtc4=rezultatKtc3;rezultatKtc3=rezultatKtc2;rezultatKtc2=rezultatKtc1;rezultatKtc1=0-AhKtc;}

      else{AbortKTC=0;menuItem=30;lcd.clear();ktcIdet = 1;}

      

}

}

 

 

void menuChisloProgonovKtc(){ // меню 32 -выбор количества прогонов КТЦ

lcd.setCursor(0,0);lcd.print("Progonov KTC        ");

lcd.setCursor(0,2);lcd.print ("<");lcd.print(progonovKtc);lcd.print(">times        ");

if(kodKnopki==1){progonovKtc=progonovKtc+1;kodKnopki=0;}

if(kodKnopki==3){progonovKtc=progonovKtc-1;kodKnopki=0;}

if(kodKnopki==5){kodKnopki=0;menuItem=3;eepromSave();} // переход на экран запуска ктц

if(kodKnopki==2){kodKnopki=0;menuItem = 31;lcd.clear();}// 

if(kodKnopki==4){kodKnopki=0;menuItem = 32;lcd.clear();}// 

}

 

 void AmpErrorTune(){ // меню18 -выбор количества прогонов КТЦ

lcd.setCursor(0,0);lcd.print("ErrAmpTune        ");

lcd.setCursor(0,2);lcd.print ("<");lcd.print(AmpTune);lcd.print(">                ");

if(kodKnopki==1){AmpTune=AmpTune+1;kodKnopki=0;}

if(kodKnopki==3){AmpTune=AmpTune-1;kodKnopki=0;}

if(kodKnopki==5){kodKnopki=0;menuItem=0;eepromSave();} // переход на экран запуска ктц

if(kodKnopki==2){kodKnopki=0;menuItem = 18;lcd.clear();}// 

if(kodKnopki==4){kodKnopki=0;menuItem = 16;lcd.clear();}// 

}

 

 

void oprosKnopok(){

if (analogRead(0)>10 ) {timer2 = millis() - timer1;}

else {timer1 = millis();timer2=0; }

if (timer2 > 10){kodKnopki = analogRead(0); timer2=0;timer1=millis()+500;}

if (kodKnopki>242 && kodKnopki<262){kodKnopki = 1;} //вверх 252

if (kodKnopki>201 && kodKnopki<221){kodKnopki = 2;} //вправо 211 

if (kodKnopki>52 && kodKnopki<72){kodKnopki = 3; }//вниз 62

if (kodKnopki>155 && kodKnopki<175){kodKnopki = 4;} // влево 165

if (kodKnopki>106 && kodKnopki<126){kodKnopki = 5;} //меню 116

}

 

void timer(){

//правильный таймер

timer00 = millis() + timer0;

if(timer00 > 1000){

timer0 = (millis()%1000) - millis(); //Делаем корректировку c учетом загрузки процессора. Отнимаем остаток от деления на тысячу. 

sec=sec+1;

timeKtc=timeKtc+1; // ведем параллельный подсчет времени ушедшего на КТЦ

ahBatCounter(); // учитываем счетчик емкости влитой в батарею 1 раз в секунду

AhKtc=AhKtc+(Ibat/3600); // учитываем счетчтик емкости в проводимом КТЦ 

idleTime=idleTime+1;

ahIdleTime=ahIdleTime+1;

if (ahIdleTime>1) {ahIdleTime=0;}

}

if (idleTime>10){idleTime=0;} // время смены экранов в дежурном режиме. Секунды.

if(sec>59){minut=minut+1;sec=0;

 if(minut>59){hour=hour+1;minut=0;

  if (hour>24){day=day+1;hour=0;

   if (day>365){god=god+1;day=0;}

   }

  }

 }

}

 

void ahBatCounter(){

if (Ibat>0){ahBatIn=ahBatIn+(Ibat/3600);}

else {ahBatOut=ahBatOut+(Ibat/3600);}

}

 

void oprosNaprug(){

Ubatp=0; // Устанавливаем промежуточные переменные в ноль перед замером значений 

Ibatp=0;

Uspp = 0;

Ispp=0;

for (int i=1; i<=100;i++){ // сто раз опрашиваем входы

Ubatp = 1023-analogRead(4) + Ubatp; //Поскольку схема с общим плюсом - то +5В на входном пине надо преобразовать в 0разряд , а 0В на пине в 255 разряд. Затем 

//сто раз прибавляем намерянное значение уже к имеющемуся. Таким образом реализуем усреднение. Максимальное 

// начение того что намеряли с порта равно 1024. Значит 100 раз по 1024 = 102400. Значение INT может хранить числа от -32 768 до 32 767. 

// Значит надо использовать  long для хранения целых чисел в расширенном диапазоне от -2,147,483,648 до 2,147,483,647

Ibatp=1024-512+progonovKtc-analogRead(2) + Ibatp;

Uspp = 1024-analogRead(1) + Uspp;

}

Ubat=Ubatp/100*0.058594; // переводим усредненное значение в флоат чтобы увидеть запятую. 0.058594 - это вольт на разряд ацп. 100 - количество усреднений.

Ibat=Ibatp/100*0.048828; //(5V/0.1VA=50(Ампер)- разрешение диапазона измерения датчика тока => 50А/1024 = 0,048828(Ампер на один разряд )

Usp = Uspp/100*0.058594;

Pbat = Ubat*Ibat;

}

 

void UpitMonitor(){

if (Ubat<8||Ibat<-19||Ibat>19){ // если напряжение батареи меньiе 8 вольт - екстренно сохраняем все переменные в память, тушим выход вход и останавливаем программу.

sunOff();

loadOff();

lcd.setCursor(0,8);lcd.print(millis());lcd.print(" ");

eepromSave();

lcd.print("Overload");

lcd.print(millis()); // отображаеv сколько милисекунд  нам понадобилось чтобы записать епром

delay(4000); // ждем  секунд

}

}

 

void eepromSave(){

eepromCounter=eepromCounter+1;

EEPROM_writeAnything(0, counter);

EEPROM_writeAnything(8,timeKtc);//таймер КТЦ. показывает сколько длится КТЦ. - пишем в память по адресу 

EEPROM_writeAnything(12,UbatKacheliMax);

EEPROM_writeAnything(16,UbatKacheliMin);

EEPROM_writeAnything(20,day);

EEPROM_writeAnything(21,god); //byte god=0;// (21)

EEPROM_writeAnything(22,sec); //byte sec=0;// (22)

EEPROM_writeAnything(23,minut);//byte minut=0;//(23)

EEPROM_writeAnything(24,hour); //byte hour = 0;//(24)

EEPROM_writeAnything(28,ahBatIn);//float ahBatIn=0; // (28)Общий глобальный счетчик влитой а аккумулятор энергии

EEPROM_writeAnything(33,ktcIdet);//byte ktcEnable = 0; // (33)если в 1 - то делаем ктц. ktcEnable при перезагрузке дает понять что цикл был перерван по каким то

EEPROM_writeAnything(34,minBatVolForKtc);//float minBatVolForKtc = 9;// (34)

EEPROM_writeAnything(38,AhKtc);//float AhKtc = 0;//(38)

EEPROM_writeAnything(39,metodZaryada); // byte metodZaryada=0; //39

EEPROM_writeAnything(40,loadButton);// byte loadButton=1; //40

EEPROM_writeAnything(41,UbatLoadOff);//float UbatLoadOff = 8; // 41

EEPROM_writeAnything(45,boostEnable); // byte boostEnable = 0; //45

EEPROM_writeAnything(46,kacheliVverh); // byte kacheliVverh = 1;//46

EEPROM_writeAnything(52,UbatBoostOff); //float UbatBoostOff = 19;

EEPROM_writeAnything(56,rezultatKtc1); // float rezultatKtc1 = 0;

EEPROM_writeAnything(60,rezultatKtc2); //float rezultatKtc2 = 0;

EEPROM_writeAnything(64,rezultatKtc3); // rezultatKtc3 float  = 0;

EEPROM_writeAnything(68,rezultatKtc4); //rezultatKtc4 float  = 0;

EEPROM_writeAnything(72,eepromCounter);//Счетчиr записи епром Byte

EEPROM_writeAnything(76,UbatNugenBoost); // float UbatNugenBoost = 16;//50

EEPROM_writeAnything(81,progonovKtc); // Byte progonovKtc = 0

EEPROM_writeAnything(85,ahBatOut);//float ahBatOut = 0; // (85)Общий глобальный счетчик взятой из аккумулятора энергии

EEPROM_writeAnything(89,AmpTune);//AmpTune - byte - погрешность вносимая датчиком тока 

}

 

void eepromRead(){

EEPROM_readAnything(0, counter);

EEPROM_readAnything(8,timeKtc);//таймер КТЦ. показывает сколько длится КТЦ. - пишем в память по адресу 

EEPROM_readAnything(12,UbatKacheliMax);

EEPROM_readAnything(16,UbatKacheliMin);

EEPROM_readAnything(20,day);

EEPROM_readAnything(21,god); //byte god=0;// (21)

EEPROM_readAnything(22,sec); //byte sec=0;// (22)

EEPROM_readAnything(23,minut);//byte minut=0;//(23)

EEPROM_readAnything(24,hour); //byte hour = 0;//(24)

EEPROM_readAnything(28,ahBatIn);//float ahBatIn=0; // (28)Общий глобальный счетчик влитой а аккумулятор энергии

EEPROM_readAnything(33,ktcIdet);//byte ktcEnable = 0; // (33)если в 1 - то делаем ктц. ktcEnable при перезагрузке дает понять что цикл был перерван по каким то

EEPROM_readAnything(34,minBatVolForKtc);//float minBatVolForKtc = 9;// (34)

EEPROM_readAnything(38,AhKtc);//float AhKtc = 0;//(38)

EEPROM_readAnything(39,metodZaryada); // byte metodZaryada=0; //39

EEPROM_readAnything(40,loadButton);// byte loadButton=1; //40

EEPROM_readAnything(41,UbatLoadOff);//float UbatLoadOff = 8; // 41

EEPROM_readAnything(45,boostEnable); // byte boostEnable = 0; //45

EEPROM_readAnything(46,kacheliVverh); // byte kacheliVverh = 1;//46

EEPROM_readAnything(52,UbatBoostOff); //float UbatBoostOff = 19;

EEPROM_readAnything(56,rezultatKtc1); // float rezultatKtc1 = 0;

EEPROM_readAnything(60,rezultatKtc2); //float rezultatKtc2 = 0;

EEPROM_readAnything(64,rezultatKtc3); // rezultatKtc3 float  = 0;

EEPROM_readAnything(68,rezultatKtc4); //rezultatKtc4 float  = 0;

EEPROM_readAnything(72,eepromCounter);//Счетчиr записи епром Byte

EEPROM_readAnything(76,UbatNugenBoost); // float UbatNugenBoost = 16;//50

EEPROM_readAnything(81,progonovKtc); // Byte progonovKtc = 0

EEPROM_readAnything(85,ahBatOut);//float ahBatOut = 0; // (85)Общий глобальный счетчик взятой из аккумулятора энергии

EEPROM_readAnything(89,AmpTune);//AmpTune - byte - погрешность вносимая датчиком тока 

}

 

void resetAll(){for (int i = 8 ; i < 512 ; i++) {EEPROM.write(i, 0);}} //чистим память в ноль

 

void setup(){ //  ВВОДНЫЕ !!!!!!!!!!!!!!!!!!

lcd.begin(16,2);

 

EEPROM_readAnything(0, counter); // первый старт системы. Из за того что в ячейках памяти епром по умолчанию прописано 255 - 

//восстановленные переменные Float, будут отображаться некорректно. Этой структурой мы обходим это ограничение. 

lcd.setCursor(0,8);lcd.print(counter);lcd.print("/");

 

if (counter==65535){counter=1;resetAll();}

pinMode (A0,INPUT); // Вывод 0 на плате ардуино, 23 в атмеге. Используется для контроля нажатия кнопок.

pinMode (A1,INPUT); // вывод 1 на плате ардуино, 24 - в атмеге. Используется для контроля напряжения СП. 

pinMode (A2,INPUT); // вывод 2 на плате ардуино, 25 в атмеге . Используется для мониторинга датчика тока.  

pinMode (9,OUTPUT); // вывод 9 на плате ардуино, 15 - в атмеге. Используется для управления нагрузкой. 

pinMode (10,OUTPUT); // вывод 10 на плате ардуино, 16 - в атмеге. Используется для управления солнечной панелью. 

eepromRead();

counter=counter+1;// считаем сколько раз пускался контроллер 

lcd.print(eepromCounter);

delay(1000);

}

 

void loop(){// начало программы 

 

///UpitMonitor();

oprosKnopok();

oprosNaprug();

timer();

if (menuItem==0) {menuIdle();} //дежурный режим. когда меняются экраны

if (menuItem == 1) {menuChargeUpLevel();}   //меню выбора максимального напряжения заряда в режиме качелей

if (menuItem==11){menuChargeResumeLevel();} // меню установки напряжения начала заряда в режиме качелей 

if(menuItem ==3) { menuKtcSetting();} // Экран запуска КТЦ

if(menuItem ==30){ menuAktivnogoKtc();} // Экран хода КТЦ

if(menuItem ==39){ menuKtcAbort();} // экран отмены КТЦ

if (menuItem==31){ menuKtcDischargeBatLevel();} // выбор нижнего напряжения для КТЦ

if (menuItem==13){ menuViboraMetodaZaryada();} // выбор нижнего напряжения для Заряда

if (menuItem==14){ menuBoostOffVol();} // выбор  напряжения Boost  для заряда 

if (menuItem==15){ menuBoostOnLevel();} // выбор напряжения при котором включается кипятилка 

if (menuItem==16){ menuLoadOffLevel();} // выбоh напряжения когда отключается вся нагрузка 

if (menuItem==32){ menuChisloProgonovKtc();}

if (menuItem==18) { AmpErrorTune();} 

regimRaboti();

}