Неадекватное поведение вроде бы простой программы, гуру направьте.

Alex-GK
Offline
Зарегистрирован: 10.04.2012

Добрый день, уважаемые любители ардуино!

Краткая предыстория:

Решил я заново автоматизировать свой процесс самогоноварения.

Взял Arduino Mega2560, присоединил LCD дисплей 320х240 с тачскрином, SD карточку, несколько датчиков 18В20 и пачку реле.

И начал программировать.

Функционально вся программа должна выполнять несколько производственных процессов по выбору пользователя.

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

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

Вроде все просто, никакого ООП, никаких прерываний, никаких ссылок, никакой ядреной математики.

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

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

Я решил, что израсходовал оперативную память. Померял известной функцией "расстояние" между кучей и стэком, памяти оказалось дофига.

Тогда я безотносительно к памяти решил что в программе слишком много строковых данных для вывода на экран и перенёс все строки на SD карточку, проблема ушла.

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

На этот момент вес скомпилированного скетча был около 45 килобайт и по словам компилятора просил около 1,5 килобайта оперативной памяти.

Долго я ломал голову. Крутил и так и этак. Ничего умного в голову не приходило, пока не родилась мысль отключить оптимизацию компилятора.

Сменил я -Os на -O0, скомпилировал. Скетч получился больше 130 килобайт, запрос оперативки 5,5 килобайт. Правда компилятор ругается на delay.h Ругается, но компилирует.

Загрузил в плату. Работает идеально, никаких глюков, все как запрограммировано. Но чтение с карты и вывод на экран так медленно, что видно как отдельные буквы отрисовываются.

Попробовал поменять оптимизацию на -O1. Скетч получился около 100 килобайт и опять глюк на глюке и глюком погоняет.

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

Но может все таки можно как-то и без тормозов и без глюков!?

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


#define ThermoPin 3                                             // Назначение выводов
#define DCPowSensPin  A2  // Датчик напряжения 12в
#define DCPowISensPin A0  // Датчик тока 12в
#define MixSensPin    A3  // Датчик напряжения миксера
#define PumpBtnPin    47  // !!!  Кнопка включеня мини насоса
#define PumpRelPin    A15 // Реле Мини насоса
#define PumpSensPin   A4  // Датчик напряжения мини насоса
#define FanRelPin     A14 // Реле вентиляторов
#define FanSensPin    A5  // Датчик напряжения вентиляторов
#define SirRelPin     A11 // Реле Сирены
#define ActLRelPin    A12 // Реле активатор - налево
#define ActLSensPin   5   // Левый датчик активатора
#define ActRRelPin    A13 // Реле активатор - направо
#define ActRSensPin   6   // Правый датчик активатора
#define FltrSensPin   7   // Датчик - поплавок
#define ACPowSensPin  15  // Датчик питания 220 в
#define CircRelPin    A10 // Реле циркуляционного насоса
#define CircSensPin   18  // Датчик напряжения циркуляционного насоса
#define WCircSensPin  2   // !!!  Датчик протока воды
#define SHtrRelPin    A9  // Реле малого нагревателя
#define SHtrSensPin   14  // Датчик напряжения малого нагревателя
#define SHtrISensPin  16  // Датчик тока малого нагревателя
#define BHtrRelPin    A8  // Реле большого нагревателя
#define BHtrSensPin   17  // Датчик напряжения большого нагревателя
#define BHtrISensPin  19  // Датчик тока большого нагревателя

#define ACFail            1                                       // Типы ошибок
#define DCFail            2
#define MixerFail         3
#define MiniPumpRelayFail 4
#define MiniPumpFail      5
#define FanRelayFail      6
#define FanFail           7
#define WtrPumpRelayFail  8
#define WtrPumpFail       9
#define SHtrRelayFail     10
#define SHtrFail          11
#define BHtrRelayFail     12
#define BHtrFail          13
#define BotSwFail         14
#define BottleFull        15
#define CoolantOverheat   16
#define DSFail            17

#define DegasTime         1                                       // Назначение номеров параметров
#define CCHeadsTime       2
#define St2HeadsTime      3
#define RTDHeadsTime      4
#define CoolantEdge       5
#define DeflEdge          6
#define MaxTempMaxPow     7
#define MaxTempMedPow     8
#define MaxTempLowPow     9
#define MaxTempStage2     10
#define MaxTempRTD        11
#define DegasStartTemp    12
#define CoolingTime       13
#define SBottleWait       14
#define RTDRdyTime        15
#define RTDContTime       16

#define ParamNumber 17

#define NoHeat    0                                               // Варианты мощностей СС
#define MaxHeat   1
#define MedHeat   2
#define LowHeat   3

#define DeviceOn    1                                             // Назначение режимов устройств и сенсоров  
#define DeviceOff   0
#define SensorOn    0
#define SensorOff   1
#define StageOff    1
#define StageOn     0

#define NoScreen    0                                             // Назначение номеров экранов
#define MnScreen    1
#define SetScreen   2
#define St1SScreen  3
#define St2SScreen  4
#define ComSScreen  5
#define AdjScreen   6
#define DoStageScreen   7
#define ChActionScreen  8

#define NoStage    0
#define PreHeat    1                                                 // Стадии для всех циклов
#define Degasation 2
#define Heating    3
#define DoHeads    4
#define DoBody     5
#define DoCool     6
#define DoFinish   7

#define OprDecr  2                                                // Типы изменения параметра
#define OprIncr  3

#define BottleHeads 1                                             // Типы бутылок
#define BottleBody  2

#define BeepAlarm   1                                             // Назначение типов сигналов
#define BBeepAlarm  2
#define FullAlarm   3
#define StopAlarm   4
 
#define ActPeriod   1000                                          // Нерегулируемые параметры
#define DCIMixThreshold 514
#define DCIFanThreshold 522
#define AdjDelay    120

byte DoCCStage = StageOff;                                        // Переменные включения производственных циклов
byte DoStage2 = StageOff;
byte DoContSt2 = StageOff;
byte DoRTD = StageOff;
byte DoContRTD = StageOff;
byte DoClean = StageOff;

int Params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};               // Массив хранения настраиваемых параметров
const byte ParamType[17] = {0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,};  // Массив хранения типа вывода параметров на дисплей

// Не забыть удалить комментарий в Аджусте


#include <OneWire.h>
#include <EEPROM.h>
#include <SPI.h>
#include <SD.h>
#include <UTFT.h>
#include <URTouch.h>



extern uint8_t SmallFont[];

File dataFile;
UTFT myGLCD(ITDB32S,38,39,40,41);
URTouch  myTouch( 46, 45, 44, 43, 42);
OneWire  ds(ThermoPin);

const int chipSelect = 4;
const char StrEnd = '\0';

char Str[20];
int x,y;

byte MainScr = 1;                   // Переменные определяющие отрисован экран или нет
byte StageScr = 1;
byte SettsScr = 1;
byte St1StScr = 1;
byte St2StScr = 1;
byte CommStScr = 1;
byte AdjustScr = 1;
byte ActionsScr = 1;
byte ScrMode = MnScreen;            //Текущий экран для отображения
byte AdjParam =0;                   //Номер параметра для настройки
byte TmpParam;

byte addr1[8];                        // Адрес 1-го датчика температуры (Воды)
byte addr2[8];                        // Адрес 2-го датчика температуры (Куба)
byte addr3[8];                        // Адрес 3-го датчика температуры (Дефлегматор)
int ClntTemp = 0;                      // Температура охл. жидкости
int CubeTemp = 0;                      // Температура в кубе
int DeflTemp = 0;                      // Температура входа дефлегматора

byte SHeater = DeviceOff;             //Переменные включения-выключения исполнительных устройств
byte BHeater = DeviceOff;
byte WtrPump = DeviceOff;
byte MiniPump = DeviceOff;
byte WtrFan = DeviceOff;

unsigned long MyTimer;                //Разнообразные таймеры
unsigned long TouchedTime;
unsigned long FltrTimer;

byte BotSwTst = 1;
byte Stage = NoStage;
byte TmpStage = NoStage;
byte SysError = 0;
byte NumOfBottles = 0;
byte CurrentBottle;
byte IgnoreFltr = 0;
byte HeaterPower = NoHeat;
byte Cooling = 0;

boolean TakeTouch ();
void ToEE ();
int MakeCool ();

void ErrorHandler (int ErrorType) {       //Это будет обработчик разнообразных ошибок железа
}

void FromEE () {                          //Считываем параметры из EEPROM
  int i = 0;
  EEPROM.get(0,Params[ParamNumber]);              // Считываем первый элемент из ЕЕПРОМ
  if (i == -1) {                // Если чип новый заполняем массив параметров из переменных и зыписываем в ЕЕПРОМ
    ToEE ();
  }
  else {                        // Если чип не новый, то считываем массив параметров из ЕЕПРОМ
    for (byte i=0; i < ParamNumber; i++) {
      EEPROM.get((i*sizeof(int)), Params[i]);
    }
  }
}

void ToEE () {                                  // Массив параметров в цикле записывается в ЕЕПРОМ
  for (byte i=0; i < ParamNumber; i++) {
    EEPROM.put((i*sizeof(int)), Params[i]);
  }
}

void MyPinsSetup () {
  Params[DegasTime] = 45;
  Params[CCHeadsTime] = 17;
  Params[St2HeadsTime] = 24;
  Params[RTDHeadsTime] = 25;
  Params[CoolantEdge] = 500;
  Params[DeflEdge] = 500;
  Params[MaxTempMaxPow] = 820;
  Params[MaxTempMedPow] = 830;
  Params[MaxTempLowPow] = 840;
  Params[MaxTempStage2] = 830;
  Params[MaxTempRTD] = 820;
  Params[DegasStartTemp] = 720;
  Params[CoolingTime] = 3;
  Params[SBottleWait] = 5;
  Params[RTDRdyTime] = 150;
  Params[RTDContTime] = 180;
  pinMode (DCPowSensPin,INPUT);
  digitalWrite (DCPowSensPin,HIGH);
  pinMode (MixSensPin,INPUT);
  digitalWrite (MixSensPin,HIGH);
  pinMode (PumpBtnPin,INPUT);
  digitalWrite (PumpBtnPin,HIGH);
  pinMode (PumpSensPin,INPUT);
  digitalWrite (PumpSensPin,HIGH);
  pinMode (FanSensPin,INPUT);
  digitalWrite (FanSensPin,HIGH);
  pinMode (ActLSensPin,INPUT);
  digitalWrite (ActLSensPin,HIGH);
  pinMode (ActRSensPin,INPUT);
  digitalWrite (ActRSensPin,HIGH);
  pinMode (FltrSensPin,INPUT);
  digitalWrite (FltrSensPin,HIGH);
  pinMode (ACPowSensPin,INPUT);
  digitalWrite (ACPowSensPin,HIGH);
  pinMode (CircSensPin,INPUT);
  digitalWrite (CircSensPin,HIGH);
  pinMode (WCircSensPin,INPUT);
  pinMode (SHtrSensPin,INPUT);
  digitalWrite (SHtrSensPin,HIGH);
  pinMode (SHtrISensPin,INPUT);
  digitalWrite (SHtrISensPin,HIGH);
  pinMode (BHtrSensPin,INPUT);
  digitalWrite (BHtrSensPin,HIGH);
  pinMode (BHtrISensPin,INPUT);
  digitalWrite (BHtrISensPin,HIGH);
  pinMode (WCircSensPin, INPUT);
  digitalWrite (WCircSensPin, HIGH);  

  digitalWrite (PumpRelPin,DeviceOff);
  pinMode (PumpRelPin,OUTPUT);
  digitalWrite (FanRelPin,DeviceOff);
  pinMode (FanRelPin,OUTPUT);
  digitalWrite (SirRelPin,DeviceOff);
  pinMode (SirRelPin,OUTPUT);
  digitalWrite (ActLRelPin,DeviceOff);
  pinMode (ActLRelPin,OUTPUT);
  digitalWrite (ActRRelPin,DeviceOff);
  pinMode (ActRRelPin,OUTPUT);
  digitalWrite (CircRelPin,DeviceOff);
  pinMode (CircRelPin,OUTPUT);
  digitalWrite (SHtrRelPin,DeviceOff);
  pinMode (SHtrRelPin,OUTPUT);
  digitalWrite (BHtrRelPin,DeviceOff);
  pinMode (BHtrRelPin,OUTPUT);  
}

void GetTemp() {                          // Функция измерения температуры
  ds.reset();
  ds.select(addr1);
  ds.write(0x44); 
  ds.reset();
  ds.select(addr1);    
  ds.write(0xBE);         // Read Scratchpad
  ClntTemp = int((ds.read() | (ds.read() << 8)) / 1.6);
  //ds.reset_search();
  ds.reset();
  ds.select(addr2);
  ds.write(0x44); 
  ds.reset();
  ds.select(addr2);    
  ds.write(0xBE);         // Read Scratchpad
  CubeTemp = int((ds.read() | (ds.read() << 8)) / 1.6);
  ds.reset();
  ds.select(addr3);
  ds.write(0x44); 
  ds.reset();
  ds.select(addr3);    
  ds.write(0xBE);         // Read Scratchpad
  DeflTemp = int((ds.read() | (ds.read() << 8)) / 1.6);
}

void SwitchAllOff () {                    // Выключаем ВСЁ
  SHeater = DeviceOff;
  BHeater = DeviceOff;
  WtrPump = DeviceOff;
  MiniPump = DeviceOff;
  WtrFan = DeviceOff;
  DoCCStage = StageOff;
  DoStage2 = StageOff;
  DoContSt2 = StageOff;
  DoRTD = StageOff;
  DoContRTD = StageOff;
  DoClean = StageOff;

  MainScr = 1;
  StageScr = 1;
  SettsScr = 1;
  St1StScr = 1;
  St2StScr = 1;
  CommStScr = 1;
  AdjustScr = 1;
  ActionsScr = 1;
  ScrMode = MnScreen;

  BotSwTst = 1;
  Stage = 0;
}

int WaterFlowChk () {                     // Проверяем состояние поплавка на переполнение бутылки
int State = digitalRead (WCircSensPin);
unsigned long WtrFTimer = millis()+250;
  while (millis () < WtrFTimer ) {
    if (!(State == digitalRead (WCircSensPin))) {
      return (0);
    }
  }  
  return (1);
}

void Alarm (int AlarmType) {              // Разнообразные сигналы сирены
  switch (AlarmType) {
    case BeepAlarm :
      digitalWrite (SirRelPin,DeviceOn);
      delay (150);
      digitalWrite (SirRelPin,DeviceOff);
    break;
    case BBeepAlarm :
      digitalWrite (SirRelPin,DeviceOn);
      delay (130);
      digitalWrite (SirRelPin,DeviceOff);
      delay (130);
      digitalWrite (SirRelPin,DeviceOn);
      delay (130);
      digitalWrite (SirRelPin,DeviceOff);
    break;
    case FullAlarm :
      digitalWrite (SirRelPin,DeviceOn);
    break;
    case StopAlarm :
      digitalWrite (SirRelPin,DeviceOff);
    break;    
  }
}

int SwitchBottle (int BottleType) {       // Функция переключения выхода на разные бутылки с контролем выполнения
  static int LSens,RSens;
  static unsigned long SwTime;
  LSens = digitalRead (ActLSensPin);
  RSens = digitalRead (ActRSensPin);
  CurrentBottle = BottleType;
  if ((LSens==SensorOn) && (RSens==SensorOn)) {
    return (1);
  }
  if ((LSens==SensorOff) && (RSens==SensorOff)) {
    SwTime = millis ();
    digitalWrite (ActLRelPin,DeviceOn);
cycle1:
    LSens=digitalRead (ActLSensPin);
    if (LSens) {  
      if ((SwTime+ActPeriod) < millis ()) {
        digitalWrite (ActLRelPin,DeviceOff);
        return (1);
      }
      goto cycle1;
    }
    digitalWrite (ActLRelPin,DeviceOff);
    return (0);    
  }
  if (LSens==SensorOn) {
    if (BottleType == BottleHeads) {
      return (0);
    }
    if (BottleType == BottleBody) {
      SwTime = millis ();
      digitalWrite (ActRRelPin,DeviceOn);
cycle2:
      RSens=digitalRead (ActRSensPin);
      if (RSens) {  
        if ((SwTime+ActPeriod) < millis ()) {
          digitalWrite (ActRRelPin,DeviceOff);
          return (1);
      }
      goto cycle2; 
      }
      digitalWrite (ActRRelPin,DeviceOff); 
      return (0);
    }
    return (1);
  }
  if (RSens==SensorOn) {
    if (BottleType == BottleBody) {
      return (0);
    }
    if (BottleType == BottleHeads) {
      SwTime = millis ();
      digitalWrite (ActLRelPin,DeviceOn);
cycle3:
      LSens=digitalRead (ActLSensPin);
      if (LSens) {  
        if ((SwTime+ActPeriod) < millis ()) {
          digitalWrite (ActLRelPin,DeviceOff);
          return (1);
        }
      goto cycle3;
      }
      digitalWrite (ActLRelPin,DeviceOff);    
      return (0);
    }
  }
  return (1);
}

int SysCheck () {                         // Проверка системы
  if (digitalRead (DCPowSensPin) == SensorOff) {
    return (DCFail);
  }
  if (BotSwTst) {
    if ( SwitchBottle (BottleHeads) ) {
      return (BotSwFail);
    }
    if ( SwitchBottle (BottleBody) ) {
      return (BotSwFail);
    }
    BotSwTst = 0;
  }
  if (digitalRead (ACPowSensPin) == SensorOff) {
    return (ACFail);
  }
  if (digitalRead (MixSensPin) == SensorOn) {
    if (analogRead (DCPowISensPin) < DCIMixThreshold) {
      return (MixerFail);
    }
  }
  if (MiniPump == DeviceOff) {
    if (digitalRead (PumpSensPin) == SensorOn) {
      return (MiniPumpRelayFail);
    }
  }
  if (MiniPump == DeviceOn) {
    if (digitalRead (PumpSensPin) == SensorOff) {
      return (MiniPumpRelayFail);
    }
    if (analogRead (DCPowISensPin) < DCIMixThreshold) {
      return (MiniPumpFail);
    }    
  }
  if (WtrFan == DeviceOff) {
    if (digitalRead (FanSensPin) == SensorOn) {
      return (FanRelayFail);
    }
  }
  if (WtrFan == DeviceOn) {
    if (digitalRead (FanSensPin) == SensorOff) {
      return (FanRelayFail);
    }
    if (analogRead (DCPowISensPin) < DCIFanThreshold) {
      return (FanFail);
    }    
  }
  if (WtrPump == DeviceOff) {
    if (digitalRead (CircSensPin) == SensorOn) {
      return (WtrPumpRelayFail);
    }
  }
  if (WtrPump == DeviceOn) {
    if (digitalRead (CircSensPin) == SensorOff) {
      return (WtrPumpRelayFail);
    }
    if (WaterFlowChk ()) {
      return (WtrPumpFail);
    }    
  }
  if (SHeater == DeviceOff) {
    if (digitalRead (SHtrSensPin) == SensorOn) {
      return (SHtrRelayFail);
    }
  }
  if (SHeater == DeviceOn) {
    if (digitalRead (SHtrSensPin) == SensorOff) {
      return (SHtrRelayFail);
    }
    if (digitalRead (SHtrISensPin) ==SensorOff) {
      return (SHtrFail);
    }    
  }
  if (BHeater == DeviceOff) {
    if (digitalRead (BHtrSensPin) == SensorOn) {
      return (BHtrRelayFail);
    }
  }
  if (SHeater == DeviceOn) {
    if (digitalRead (BHtrSensPin) == SensorOff) {
      return (BHtrRelayFail);
    }
    if (digitalRead (BHtrISensPin) ==SensorOff) {
      return (BHtrFail);
    }    
  }
  return (0);  
}

void SwitchDevs () {                      // Включение-выключение устройств согласно значениям переменных
  digitalWrite(SHtrRelPin,SHeater);
  digitalWrite(BHtrRelPin,BHeater);
  digitalWrite(CircRelPin,WtrPump);
  digitalWrite(PumpRelPin,MiniPump);
  digitalWrite(FanRelPin,WtrFan);
}

int SDintRead(File CurFile) {             // Читаем число из файла, не более 3 знаков, ограниченное ";"
  byte byte1 = 0;
  byte byte2 = 0;
  byte byte3 = 0;
  int number = 0;
  if (CurFile.available()) {      
    byte1 = CurFile.read()-48;
    if (!(byte1 == 11)) {
      byte2 = CurFile.read()-48;
      if (byte2 == 11) {
        number = byte1;
      }
      else {
        byte3 = CurFile.read()-48;
        if (byte3 == 11) {
          number = byte1*10+byte2;
        }
        else {
          number = byte1*100+byte2*10+byte3;
          CurFile.read();
        }
      }
    }
    if (number == 999) {
      number = 9998;
    }
    return (number);
  }
  else return (65535);
}

void SDStrRead (File CurFile) {           // Читаем строку из файла
  byte temp = 0;
  int i = 0;
 
    do {
      if (CurFile.available()) {
        temp = CurFile.read();
      }
      else {
        temp = 59;
      }
     if ( temp == 59 ) {
      Str [i] = StrEnd; 
     }
     else {
      Str [i] = char (temp);
     }
     i++;
    }
    while (temp != 59);  
}

void DrawFile (int FileNum) {             // Отрисовываем экран по данным из файла под номером FileNum
String FName = String (FileNum); 
  dataFile = SD.open(FName,FILE_READ);
  while (dataFile.available()) {
    switch (char (dataFile.read ())) {
      case '^':
        myGLCD.drawLine(SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
      break;
      case '&':
        myGLCD.drawRoundRect(SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
      break;
      case '%':
      case '<':
        myGLCD.setColor(SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
      break;
      case '$':
        myGLCD.drawRect(SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
      break;
      case '@':
        myGLCD.fillScr(SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
      break;
      case '#':
        SDStrRead (dataFile);
        myGLCD.print (Str,SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
        break;
      case '*':
         myGLCD.fillRect(SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
      break;
      case '!':
        myGLCD.setBackColor(SDintRead (dataFile),SDintRead (dataFile),SDintRead (dataFile));
      break;
    }
  }
  dataFile.close();
}

void MainScreen () {                      // Отрисовка и опрос тачскрина в основном экране
  if (MainScr) {
    DrawFile (1);
    MainScr = 0;
  }
  if (TakeTouch () ) {    
    while (myTouch.dataAvailable())
      myTouch.read();
    if ((y>21) && (y<69)) {
      if ((x>16) && (x<104)) {
        DoCCStage = StageOn;
        ScrMode = DoStageScreen;
        MainScr = 1;
        Stage = 0;
        HeaterPower = NoHeat;
      }
      if ((x>116) && (x<204)) {
        DoStage2 = StageOn;
        ScrMode = DoStageScreen;
        MainScr = 1;
        Stage = 0;
        NumOfBottles = 0;           
      }
      if ((x>216) && (x<304)) {
        DoRTD = StageOn;
        ScrMode = DoStageScreen;
        MainScr = 1;  
        NumOfBottles = 0;          
      }
    }
    if ((y>86) && (y<134)) {
      if ((x>16) && (x<104)) {
        DoClean = StageOn;
        ScrMode = DoStageScreen;
        MainScr = 1;
      }
      if ((x>116) && (x<204)) {
        DoContSt2 = StageOn;
        ScrMode = DoStageScreen;
        MainScr = 1;
        NumOfBottles = 0;            
      }
      if ((x>216) && (x<304)) {
        DoContRTD = StageOn;
        ScrMode = DoStageScreen;
        MainScr = 1;
        NumOfBottles = 0;            
      }
    }
      if ((y>151) && (y<199)) {
        ScrMode = SetScreen;
        MainScr = 1; 
      }
  }  
}

void SettsScreen () {                     // Отрисовка и опрос тачскрина в экране выбора настроек
  if (SettsScr) {
    DrawFile (2);
    SettsScr = 0;
  }
  if (TakeTouch () ) {    
    while (myTouch.dataAvailable())
      myTouch.read();
    if ((x>16) && (x<119)) {
      if ((y>86) && (y<134)) {
        ScrMode = St1SScreen;
        SettsScr = 1;
      }
      if ((y>151) && (y<199)) {
        ScrMode = ComSScreen;
        SettsScr = 1;           
      }
    }
    if ((x>201) && (x<304)) {
      if ((y>86) && (y<134)) {
        ScrMode = St2SScreen;
        SettsScr = 1;
      }
      if ((y>151) && (y<199)) {
        ScrMode = ComSScreen;
        SettsScr = 1;           
      }
    }
    if ((x>136) && (x<184)) {
      SettsScr = 1;
      ScrMode = MnScreen;
    }
  }  
}

void St2StScreen () {                     // Отрисовка и опрос тачскрина в экране настроек 2 и 3 процессов
  if (St2StScr) {
    DrawFile (4);
    St2StScr = 0;
  }
  if (TakeTouch () ) {    
    while (myTouch.dataAvailable())
      myTouch.read();
    if ((x>16) && (x<119)) {
      if ((y>41) && (y<79)) {
        AdjParam = St2HeadsTime;
        ScrMode = AdjScreen;
        St2StScr = 1;
      }
      if ((y>161) && (y<199)) {
        AdjParam = RTDHeadsTime;
        ScrMode = AdjScreen;
        St2StScr = 1;           
      }
    }
    if ((x>201) && (x<304)) {
      if ((y>41) && (y<79)) {
        AdjParam = MaxTempStage2;
        ScrMode = AdjScreen;
        St2StScr = 1;
      }
      if ((y>161) && (y<199)) {
        AdjParam = MaxTempRTD;
        ScrMode = AdjScreen;
        St2StScr = 1;           
      }
    }
    if ((x>136) && (x<184)) {
      St2StScr = 1;
      ScrMode = MnScreen;
    }
  }  
}

void St1StScreen () {                     // Отрисовка и опрос тачскрина в экране настроек 1 процесса
  if (St1StScr) {
    DrawFile (3);
    St1StScr = 0;
  }
  if (TakeTouch () ) {    
    while (myTouch.dataAvailable())
      myTouch.read();
    if ((x>16) && (x<119)) {
      if ((y>41) && (y<79)) {
        AdjParam = DegasStartTemp;
        ScrMode = AdjScreen;
        St1StScr = 1;
      }
      if ((y>101) && (y<139)) {
        AdjParam = DegasTime;
        ScrMode = AdjScreen;
        St1StScr = 1;           
      }
      if ((y>161) && (y<199)) {
        AdjParam = CCHeadsTime;
        ScrMode = AdjScreen;
        St1StScr = 1;           
      }
    }
    if ((x>201) && (x<304)) {
      if ((y>41) && (y<79)) {
        AdjParam = MaxTempMaxPow;
        ScrMode = AdjScreen;
        St1StScr = 1;
      }
      if ((y>101) && (y<139)) {
        AdjParam = MaxTempMedPow;
        ScrMode = AdjScreen;
        St1StScr = 1;           
      }
      if ((y>161) && (y<199)) {
        AdjParam = MaxTempLowPow;
        ScrMode = AdjScreen;
        St1StScr = 1;           
      }
    }
    if ((x>136) && (x<184)) {
      St2StScr = 1;
      ScrMode = MnScreen;
    }
  }  
}

void CommStScreen () {                    // Отрисовка и опрос тачскрина в экране общих настроек
  if (CommStScr) {
    DrawFile (5);
    CommStScr = 0;
  }
  if (TakeTouch () ) {    
    while (myTouch.dataAvailable())
      myTouch.read();
    if ((x>16) && (x<119)) {
      if ((y>41) && (y<79)) {
        AdjParam = DeflEdge;
        ScrMode = AdjScreen;
        CommStScr = 1;
      }
      if ((y>101) && (y<139)) {
        AdjParam = SBottleWait;
        ScrMode = AdjScreen;
        CommStScr = 1;           
      }
      if ((y>161) && (y<199)) {
        AdjParam = 0;
        ScrMode = AdjScreen;
        CommStScr = 1;           
      }
    }
    if ((x>201) && (x<304)) {
      if ((y>41) && (y<79)) {
        AdjParam = CoolingTime;
        ScrMode = AdjScreen;
        CommStScr = 1;
      }
      if ((y>101) && (y<139)) {
        AdjParam = CoolantEdge;
        ScrMode = AdjScreen;
        CommStScr = 1;           
      }
      if ((y>161) && (y<199)) {
        AdjParam = 0;
        ScrMode = AdjScreen;
        CommStScr = 1;           
      }
    }
    if ((x>136) && (x<184)) {
      CommStScr = 1;
      ScrMode = MnScreen;
    }
  }  
}

void AdjustScreen () {                    // Отрисовка и опрос тачскрина в экране настройки параметров, и собственно настройка
  int Oper = 0; 
  if (AdjustScr) {
    DrawFile (6);
    AdjustScr = 0;
    TmpParam = Params [AdjParam];
    DrawFile (AdjParam+49);
  }
  if (ParamType [AdjParam]) {
    myGLCD.printNumF ((TmpParam/10), 1, CENTER, 105);
  }
  else {    
    myGLCD.printNumI(TmpParam, CENTER, 105,3,'0');
  }
  if (TakeTouch () ) {  
    if ((y>86) && (y<134)) {
      TouchedTime = millis ();
      if ((x>16) && (x<109)) {
        Oper = OprDecr;
        TmpParam--;
        if (ParamType [AdjParam]) {
          myGLCD.printNumF ((TmpParam/10), 1, CENTER, 105); 
        }
        else {
          myGLCD.printNumI(TmpParam, CENTER, 105,3,'0');              
        }
      }
      if ((x>201) && (x<304)) {
        Oper = OprIncr;
        TmpParam++;
        if (ParamType [AdjParam]) {          
          myGLCD.printNumF ((TmpParam/10), 1, CENTER, 105);
        }
        else {
          myGLCD.printNumI(TmpParam, CENTER, 105,3,'0');        
        }
      }
      while (myTouch.dataAvailable()) {
        myTouch.read();
        if ((TouchedTime+300) < millis ()) {
          TouchedTime = millis ();
          while (myTouch.dataAvailable()) {
            myTouch.read();
            if ((TouchedTime + AdjDelay) < millis ()) {
              if (Oper == OprDecr) {
                TmpParam--;
              }
              if (Oper == OprIncr) {
                TmpParam++;
              }              
              if (ParamType [AdjParam]) { 
                myGLCD.printNumF ((TmpParam/10), 1, CENTER, 105);
              }
              else {                  
                myGLCD.printNumI(TmpParam, CENTER, 105,3,'0');
              }
              TouchedTime = millis ();
            }
          }
        }
      }
    }
    if ((y>151) && (y<199)) {
      while (myTouch.dataAvailable())
        myTouch.read();
      if ((x>16) && (x<149)) {
        AdjustScr = 1;      
        ScrMode = MnScreen;
      }
      if ((x>181) && (x<304)) {
        Params [AdjParam] = TmpParam;
        AdjustScr = 1;      
        ScrMode = MnScreen;
      // ToEE ();
    } 
    }
  }
}

void HeatPause () {                       // Экран паузы в нагревании
  DrawFile (32);  
  digitalWrite(SHtrRelPin,DeviceOff);
  digitalWrite(BHtrRelPin,DeviceOff);
  delay (150);
  while (!(myTouch.dataAvailable())) {    
  }
  myTouch.read ();
}

void ActionsScreen () {                   // Отрисовка и опрос тачскрина в экране действий внутри процесса
  if (ActionsScr) {
    DrawFile (8);
    switch (TmpStage) {
      case PreHeat:  
        DrawFile (25);
      break;
      case Degasation:  
        DrawFile (26);      
      break;
      case Heating:  
        DrawFile (27);
      break;
      case DoHeads:  
        DrawFile (28);
      break;
      case DoBody:  
        DrawFile (29);
      break;
      case DoCool:
        DrawFile (30);
      break;
    }
    ActionsScr = 0;
  }
  if (TakeTouch () ) {    
    while (myTouch.dataAvailable())
      myTouch.read();
    if ((x>16) && (x<119)) {
      if ((y>41) && (y<79)) {
        ActionsScr = 1;
        SwitchAllOff ();
        ScrMode = MnScreen;
      }
      if ((y>101) && (y<139)) {   
        ActionsScr = 1;  
        HeatPause ();         
        ScrMode = DoStageScreen;        
      }
      if ((y>161) && (y<199)) {
        ActionsScr = 1;      
        NumOfBottles = 1;    
      }
    }
    if ((x>201) && (x<304)) {
      if ((y>41) && (y<79)) {
        ActionsScr = 1;
        if (DoCCStage == StageOff) {
          TmpStage++;
        }
        else {
          if (Stage == DoBody) {
            if (HeaterPower == LowHeat) {
              TmpStage++;
            }
            else {
              HeaterPower++;
            }
          }
          else {
            TmpStage++;
          }
        }
        if (TmpStage == (DoCool+1)) {
          TmpStage = DoCool;
        }
      }
      if ((y>101) && (y<139)) {
        ActionsScr = 1;
        Stage = TmpStage;
        ScrMode = DoStageScreen;
        if ((TmpStage == Degasation) || (TmpStage == DoHeads) || (TmpStage == DoCool)) {
          MyTimer = millis ();
        }       
      }
      if ((y>161) && (y<199)) {
        ActionsScr = 1;       
        IgnoreFltr = 0; 
        FltrTimer = millis ();   
      }
    }
    if ((x>136) && (x<184)) {
      ActionsScr = 1;
      ScrMode = DoStageScreen;
    }
  }  
}

void ParamsScreen () {                    // Отрисовка текущих параметров процесса
  myGLCD.printNumF ((ClntTemp/10), 1, 105, 100);  
  myGLCD.printNumF ((DeflTemp/10), 1, 290, 100);
  if (SHeater == DeviceOn) {
    DrawFile (70);   
  }
  else {
    DrawFile (71);
  }
  if (BHeater == DeviceOn) {
    DrawFile (72);   
  }
  else {
    DrawFile (73);
  }
  if (WtrPump == DeviceOn) {
    DrawFile (74);   
  }
  else {
    DrawFile (75); 
  }
  if (WtrFan == DeviceOn) {
    DrawFile (76);   
  }
  else {
    DrawFile (77); 
  }
  if (DoCCStage == StageOff) {
    DrawFile (31);
    myGLCD.printNumI ((NumOfBottles+1), 1, 290, 175); 
    if (CurrentBottle == BottleHeads) {
      DrawFile (78);
    }
    else {
      DrawFile (79);   
    }
  }
  else {
    if (CurrentBottle == BottleHeads) {
      DrawFile (80);
    }
    else {
      DrawFile (81);   
    }
  }
  myGLCD.printNumF ((CubeTemp/10), 1, 105, 175);  
}

void StageScreen () {                     // Отрисовка основного экрана процесса
  if (StageScr) {
    DrawFile (7);    
    if (DoCCStage == StageOn) {      
      DrawFile (14);
    } 
    if (DoStage2 == StageOn) {        
      DrawFile (15);
    } 
    if (DoRTD == StageOn) {        
      DrawFile (16);
    } 
    if (DoContSt2 == StageOn) {        
      DrawFile (17);
    } 
    if (DoContRTD == StageOn) {        
      DrawFile (18);
    } 
    StageScr = 0;
  }   
  switch (Stage) {
    case PreHeat:  
      DrawFile (19);
    break;
    case Degasation:  
      DrawFile (20);
      myGLCD.printNumI ((int (((MyTimer + Params[DegasTime]*60000)-millis ())/60000)+1), 235, 60); 
      myGLCD.print ((char *)F("Min"), 255, 60);       
    break;
    case Heating:  
      DrawFile (21);
    break;
    case DoHeads:  
      DrawFile (22);
    break;
    case DoBody:  
      DrawFile (24);
    break;
    case DoCool:
      DrawFile (23);
      myGLCD.printNumI ((int (((MyTimer + Params[CoolingTime]*60000)-millis ())/60000)+1), 235, 60); 
      myGLCD.print ((char *)F("Min"), 255, 60);
    break;
  }
  ParamsScreen (); 
  if (TakeTouch () ) {    
    while (myTouch.dataAvailable())
      myTouch.read();
    if ((x>136) && (x<184)) {
      StageScr = 1;
      ActionsScr = 1; 
      TmpStage = Stage; 
      ScrMode = ChActionScreen;
    }
  }
}

void Screens () {                         // Выбор экрана для отрисовки
  switch (ScrMode) {
    case MnScreen:
      MainScreen ();
    break;
    case SetScreen:
      SettsScreen ();
    break;
    case St1SScreen:
      St1StScreen ();
    break;
    case St2SScreen:
      St2StScreen ();
    break;
    case ComSScreen:
      CommStScreen ();
    break;
    case AdjScreen:
      AdjustScreen ();
    break;
    case DoStageScreen:
      StageScreen ();
    break;
    case ChActionScreen:
      ActionsScreen ();
    break;
  }  
}

int MakeCC () {                           // Процесс первого перегона
  if (DoCCStage) {        // Если цикл не включен возвращаемся без ошибок.
    return (0);
  }
  switch (Stage) {      
    case 0:               // Если начался цикл переходим к стадии преднагрева
      Stage = PreHeat;
    break;
    case PreHeat:         // Преднагрев - включаем обегрелки
      SHeater = DeviceOn;
      BHeater = DeviceOn;
      if (CubeTemp >= Params[DegasStartTemp]) {  // Если температура достигла дегазации засекаем время и переходим к стадии дегазации
        Stage = Degasation;
        MyTimer = millis ();
      }
    break;
    case Degasation:                  // Дегазация - выключаем обе грелки
      SHeater = DeviceOff;
      BHeater = DeviceOff;
      if (millis () >= (MyTimer + Params[DegasTime]*60000)) {   // Проверяем время и если вышло - переходим к нагреву
        Stage = Heating;
      }
    break;
    case Heating:               // Нагрев - включаем обе грелки
      SHeater = DeviceOn;
      BHeater = DeviceOn;
      if (DeflTemp > Params[DeflEdge]) {    //Если температура дефлегматора достигла границы переходим к головам - выключаем большую грелку и включаем насос.
        MyTimer = millis ();      //Засекаем время
        Stage = DoHeads;
        Alarm (BeepAlarm);
      }
    break;
    case DoHeads:                     // Делаем головы
      WtrPump = DeviceOn;
      BHeater = DeviceOff;
      if (SwitchBottle (BottleHeads)) { // Если выход не переключился - пытаемся обработать ошибку
        SHeater  = DeviceOff;
        ErrorHandler (BotSwFail);
        return (1);
      }
      else {
        if (millis () >= (MyTimer + Params[CCHeadsTime]*60000)) {  // Проверяем время - если вышло - переходим к телу        
          Stage = DoBody;
          Alarm (BeepAlarm);
        }
      }
    break;
    case DoBody:       
      if (SwitchBottle (BottleBody)) { // Если выход не переключился - пытаемся обработать ошибку
        SHeater  = DeviceOff;
        ErrorHandler (BotSwFail);
        return (1);
      } 
      switch (HeaterPower) {      // Разные действия для разной мощности
        case NoHeat:              // Если только началось - включаем обе грелки и переходим в режим максимальной мощности
          WtrPump = DeviceOn;
          SHeater = DeviceOn;
          BHeater = DeviceOn;
          HeaterPower = MaxHeat;
          return (0);
        break;
        case MaxHeat:             // Проверяем граници температуры куба и охлаждающей жидкости если превысило - снижаем мощность
          if (CubeTemp >= Params[MaxTempMaxPow]) {
            SHeater = DeviceOff;
            HeaterPower = MedHeat;
          }
          if (ClntTemp >= Params[CoolantEdge]) {
            SHeater = DeviceOff;
            HeaterPower = MedHeat;
          }
          return (0);
        break;
        case MedHeat:             // Проверяем граници температуры куба и охлаждающей жидкости если превысило - снижаем мощность
          if (CubeTemp >= Params[MaxTempMedPow]) {
            BHeater = DeviceOff;
            SHeater = DeviceOn;
            HeaterPower = LowHeat;
          }
          if (ClntTemp >= Params[CoolantEdge]) {
            BHeater = DeviceOff;
            SHeater = DeviceOn;
            HeaterPower = LowHeat;
          }
          return (0);
        break;
        case LowHeat:
          if (CubeTemp >= Params[MaxTempLowPow]) {    // Если температура куба превысила границу сбрасываем стадии СС выключаем грелку и переходим к охлаждению
            HeaterPower = NoHeat;
            DoCCStage = StageOff;
            Stage = DoCool;
          }
          if (ClntTemp >= Params[CoolantEdge]) {      // Если температура охлаждайки превысила границу - пытаемся обработать ошибку 
            SHeater = DeviceOff;
            HeaterPower = NoHeat;
            Stage = 0;
            ErrorHandler (CoolantOverheat);
          }
        break;       
      }
    break;   
    case DoCool:
      MakeCool ();
    break;    
  }
  return (0);
}

int MakeCool () {                         // Функция охлаждения системы
  if (Cooling) {        // Если охлаждение включено проверяем не пора ли выключаться если пора всё выключаем и сигналим.
    if (millis () >= (MyTimer+Params[CoolingTime]*60000)) {
      WtrPump = DeviceOff;
      SwitchAllOff ();
      Alarm (BBeepAlarm);
      return (0);
    }
  }
  else {                // Если охлаждение не включено - включаем и засекаем время.
    SHeater = DeviceOff;
    BHeater = DeviceOff;
    WtrPump = DeviceOn;
    MyTimer = millis ();
    Cooling = 1;
  }
}

int MakeSt2 () {                          // Процесс повторного перегона
  if (DoStage2) {
    return (0);
  }
  switch (Stage) {
    case 0:
      Stage = Heating;
    break;
    case Heating:
      SHeater = DeviceOn;
      BHeater = DeviceOn;
      if (DeflTemp > Params[DeflEdge]) {    //Если температура дефлегматора достигла границы переходим к головам - выключаем большую грелку и включаем насос.
        MyTimer = millis ();      //Засекаем время
        Stage = DoHeads;
        WtrPump = DeviceOn;
        Alarm (BeepAlarm);
      }
    break;
    case DoHeads:                     // Делаем головы
      BHeater = DeviceOff;
      if (SwitchBottle (BottleHeads)) { // Если выход не переключился - пытаемся обработать ошибку
        SHeater  = DeviceOff;
        ErrorHandler (BotSwFail);
        return (1);
      }
      else {
        if (millis () >= (MyTimer + Params[St2HeadsTime]*60000)) {  // Проверяем время - если вышло - переходим к телу        
          if (SwitchBottle (BottleBody)) { // Если выход не переключился - пытаемся обработать ошибку
            SHeater  = DeviceOff;
            ErrorHandler (BotSwFail);
          return (1);
          }        
          Stage = DoBody;
          Alarm (BeepAlarm);
        }
      }
    break;
    case DoBody:
      if (!(IgnoreFltr)) {
        if (digitalRead (FltrSensPin)) {
          if (NumOfBottles) {
            if (SwitchBottle (BottleHeads)) { // Если выход не переключился - пытаемся обработать ошибку
              SHeater  = DeviceOff;
              ErrorHandler (BotSwFail);
              return (1);
            }
            Alarm (BBeepAlarm);
          }
          else {
            SHeater = DeviceOff;
            Alarm (BBeepAlarm);
            delay (300);
            Alarm (BBeepAlarm);
            MyTimer = millis ();
            if (millis () >= (MyTimer + Params[SBottleWait]*60000)) {
              DoStage2 = StageOff;
              Stage = DoCool;
            }
          }
        }
      }
      if (CubeTemp >= Params[MaxTempStage2]) {    // Если температура куба превысила границу сбрасываем стадии выключаем грелку и переходим к охлаждению
        SHeater = DeviceOff;
        DoStage2 = StageOff;
        Stage = DoCool;
        Alarm (BeepAlarm);
      }
      if (ClntTemp >= Params[CoolantEdge]) {      // Если температура охлаждайки превысила границу - пытаемся обработать ошибку 
        SHeater = DeviceOff;
        Stage = 0;
        ErrorHandler (CoolantOverheat);
      }
    break; 
    case DoCool:
      MakeCool ();
    break;
  }
  return (0);
}

int ContSt2 () {                          // Функция продолжения процесса повторного перегона
  if (DoContSt2) {
    return (0);
  }
  switch (Stage) {
    case 0:
      Stage = Heating;
    break;
    case Heating:
      SHeater = DeviceOn;
      BHeater = DeviceOn;
      if (DeflTemp > Params[DeflEdge]) {    //Если температура дефлегматора достигла границы переходим к телу - выключаем большую грелку и включаем насос.
        if (SwitchBottle (BottleBody)) { // Если выход не переключился - пытаемся обработать ошибку
          SHeater  = DeviceOff;
          ErrorHandler (BotSwFail);
          return (1);
        }
        Stage = DoBody;
        WtrPump = DeviceOn;
        Alarm (BeepAlarm);
      }
    break;
    case DoBody:
      if (!(IgnoreFltr)) {
        if (digitalRead (FltrSensPin)) {
          if (NumOfBottles) {
            if (SwitchBottle (BottleHeads)) { // Если выход не переключился - пытаемся обработать ошибку
              SHeater  = DeviceOff;
              ErrorHandler (BotSwFail);
              return (1);
            }
            Alarm (BBeepAlarm);
          }
          else {
            SHeater = DeviceOff;
            Alarm (BBeepAlarm);
            delay (300);
            Alarm (BBeepAlarm);
            MyTimer = millis ();
            if (millis () >= (MyTimer + Params[SBottleWait]*60000)) {
              DoContSt2 = StageOff;
              Stage = DoCool;
            }
          }
        }
      }
      if (CubeTemp >= Params[MaxTempStage2]) {    // Если температура куба превысила границу сбрасываем стадии выключаем грелку и переходим к охлаждению
        SHeater = DeviceOff;
        DoContSt2 = StageOff;
        Stage = DoCool;
        Alarm (BeepAlarm);
      }
      if (ClntTemp >= Params[CoolantEdge]) {      // Если температура охлаждайки превысила границу - пытаемся обработать ошибку 
        SHeater = DeviceOff;
        Stage = 0;
        ErrorHandler (CoolantOverheat);
      }
    break;
    case DoCool:
      MakeCool ();
    break;
  }
  return (0); 
}

int MakeRTD () {                          // Процесс заключительного перегона
  if (DoRTD) {
    return (0);
  }
  switch (Stage) {
    case 0:
      Stage = Heating;
    break;
    case Heating:
      SHeater = DeviceOn;
      BHeater = DeviceOn;
      if (DeflTemp > Params[DeflEdge]) {    //Если температура дефлегматора достигла границы переходим к головам - выключаем большую грелку и включаем насос.
        MyTimer = millis ();      //Засекаем время
        Stage = DoHeads;
        WtrPump = DeviceOn;
        Alarm (BeepAlarm);
      }
    break;
    case DoHeads:                     // Делаем головы
      BHeater = DeviceOff;
      if (SwitchBottle (BottleHeads)) { // Если выход не переключился - пытаемся обработать ошибку
        SHeater  = DeviceOff;
        ErrorHandler (BotSwFail);
        return (1);
      }
      else {
        if (millis () >= (MyTimer + Params[RTDHeadsTime]*60000)) {  // Проверяем время - если вышло - переходим к телу        
          if (SwitchBottle (BottleBody)) { // Если выход не переключился - пытаемся обработать ошибку
            SHeater  = DeviceOff;
            ErrorHandler (BotSwFail);
          return (1);
          }        
          Stage = DoBody;
          Alarm (BeepAlarm);
          MyTimer = millis ();
        }
      }
    break;
    case DoBody:
      if (!(IgnoreFltr)) {
        if (digitalRead (FltrSensPin)) {
          if (NumOfBottles) {
            if (SwitchBottle (BottleHeads)) { // Если выход не переключился - пытаемся обработать ошибку
              SHeater  = DeviceOff;
              ErrorHandler (BotSwFail);
              return (1);
            }
            Alarm (BBeepAlarm);
          }
          else {
            SHeater = DeviceOff;
            Alarm (BBeepAlarm);
            delay (300);
            Alarm (BBeepAlarm);
            MyTimer = millis ();
            if (millis () >= (MyTimer + Params[SBottleWait]*60000)) {
              DoRTD = StageOff;
              Stage = DoCool;
            }
          }
        }
      }
      if (millis () >= (MyTimer + Params[RTDRdyTime]*60000)) {
        Alarm (BBeepAlarm);
        MyTimer = millis() + 900000;
      }
      if (CubeTemp >= Params[MaxTempRTD]) {    // Если температура куба превысила границу сбрасываем стадии выключаем грелку и переходим к охлаждению
        SHeater = DeviceOff;
        DoRTD = StageOff;
        Stage = DoCool;
        Alarm (BeepAlarm);
      }
      if (ClntTemp >= Params[CoolantEdge]) {      // Если температура охлаждайки превысила границу - пытаемся обработать ошибку 
        SHeater = DeviceOff;
        Stage = 0;
        ErrorHandler (CoolantOverheat);
      }
    break; 
    case DoCool:
      MakeCool ();
    break;
  }
  return (0);
}

int ContRTD () {                          // Функция продолжения процесса заключительного перегона
  if (DoContRTD) {
    return (0);
  }
  switch (Stage) {
    case 0:
      Stage = Heating;
    break;
    case Heating:
      SHeater = DeviceOn;
      BHeater = DeviceOn;
      if (DeflTemp > Params[DeflEdge]) {    //Если температура дефлегматора достигла границы переходим к головам - выключаем большую грелку и включаем насос.
        MyTimer = millis ();      //Засекаем время
        Stage = DoBody;
        WtrPump = DeviceOn;
        Alarm (BeepAlarm);
      }
    break;
    case DoBody:
      if (!(IgnoreFltr)) {
        if (digitalRead (FltrSensPin)) {
          if (NumOfBottles) {
            if (SwitchBottle (BottleHeads)) { // Если выход не переключился - пытаемся обработать ошибку
              SHeater  = DeviceOff;
              ErrorHandler (BotSwFail);
              return (1);
            }
            Alarm (BBeepAlarm);
          }
          else {
            SHeater = DeviceOff;
            Alarm (BBeepAlarm);
            delay (300);
            Alarm (BBeepAlarm);
            MyTimer = millis ();
            if (millis () >= (MyTimer + Params[SBottleWait]*60000)) {
              Stage = 0;
              DoContRTD = StageOff;
              Stage = DoCool;
            }
          }
        }
      }
      if (millis () >= (MyTimer + Params[RTDContTime]*60000)) {
        Alarm (BBeepAlarm);
        MyTimer = millis() + 900000;
      }
      if (CubeTemp >= Params[MaxTempRTD]) {    // Если температура куба превысила границу сбрасываем стадии выключаем грелку и переходим к охлаждению
        SHeater = DeviceOff;
        DoContRTD = StageOff;
        Stage = DoCool;
        Stage = 0;
        Alarm (BeepAlarm);
      }
      if (ClntTemp >= Params[CoolantEdge]) {      // Если температура охлаждайки превысила границу - пытаемся обработать ошибку 
        SHeater = DeviceOff;
        Stage = 0;
        ErrorHandler (CoolantOverheat);
      }
    break; 
    case DoCool:
      MakeCool ();
    break;
  }
  return (0);
}

void setup() {
  MyPinsSetup ();
  Serial.begin(9600);
  while (!Serial) {
  }
  SD.begin(chipSelect);

  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);
  myGLCD.clrScr();  
  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);
  if (!ds.search(addr1)) {
    ErrorHandler (DSFail);
  }
  if (!ds.search(addr2)) {
    ErrorHandler (DSFail);
  }
  if (!ds.search(addr3)) {
    ErrorHandler (DSFail);
  }
  Serial.println("Start");   
}

boolean TakeTouch () {            // Обслуживает тачскреен
  if (myTouch.dataAvailable())
    {
      myTouch.read();
      x=myTouch.getX();
      y=myTouch.getY();
      return (true);
    }
    else {
      return (false);
    }
}

extern int __bss_end;
extern void *__brkval;

int memoryFree()
{
   int freeValue;
   if((int)__brkval == 0)
      freeValue = ((int)&freeValue) - ((int)&__bss_end);
   else
      freeValue = ((int)&freeValue) - ((int)__brkval);
   return freeValue;
}

void loop() {  
  Screens (); 
  MakeCC ();
  MakeSt2 ();
  ContSt2 ();
  MakeRTD ();
  ContRTD ();
  SwitchDevs ();
  SysError = SysCheck ();
  ErrorHandler (SysError);
  Serial.println (memoryFree ());
  if (IgnoreFltr) {
    if ((FltrTimer+300000) < millis ()) {
      IgnoreFltr = 0;
    }
  }
}

Можете подсказать что-нибудь умное?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Единственное, что могу подсказать сходу - не передавать сложные объекты по значению в функции, т.е. вот такое:

int SDintRead(File CurFile)

заменять на такое:

int SDintRead(File& CurFile)

 

Alex-GK
Offline
Зарегистрирован: 10.04.2012

Спасибо!

Я хоть и старый, но тоже чайник. Объясните мне, пожалуйста, что означает вышенаписанная крокозябра.

Или где почитать про это доступным языком?

Alex-GK
Offline
Зарегистрирован: 10.04.2012

А может его вообще не передавать?

Объявить как "volatile" и обращаться напрямую?

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

Alex-GK пишет:

Решил я заново автоматизировать свой процесс самогоноварения.

Пункт 8 вот здесь не смущает? :)

Alex-GK
Offline
Зарегистрирован: 10.04.2012

Для себя уже лет 10 разрешено! А я принципиально не торгую, друзей за так угощаю, ну если шашлыка привезут ;)

Не рекламы ради а здоровия для: homedistiller.ru

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Alex-GK пишет:

Спасибо!

Я хоть и старый, но тоже чайник. Объясните мне, пожалуйста, что означает вышенаписанная крокозябра.

Или где почитать про это доступным языком?

Вышенаписанная кракозябра означает передачу по ссылке. Подробности - в любом учебнике по языку С++.

Alex-GK
Offline
Зарегистрирован: 10.04.2012

Кстати, если кто из Питера и любитель бани и самогона - Вэлкам!

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Alex-GK пишет:

А может его вообще не передавать?

Объявить как "volatile" и обращаться напрямую?

Как volatile не надо объявлять, избыточно. А если объект класса File используется всего один - то тогда можно и не передавать в функции, а сделать его глобальной переменной и обращаться напрямую.

Alex-GK
Offline
Зарегистрирован: 10.04.2012

В том-то и дело, что в каждый конкретный момент времени только один, а всего в программе штук 70 файлов открывается, можно конечно переделать всё в один, но такая каша будет у меня мозг закипит. :(

Спасибо за помощь.

До завтра я в ауте.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

А я понял. Вы ООП и классы не использовали. Вот и ваша простыня стремиться к саморазрушению.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

в самогоноварении главное - объем аппарата для закладки исходного продукта, 200-400 литров и будет вам щастье

inspiritus
Offline
Зарегистрирован: 17.12.2012

Главное это головы отделить, а остальное дело вкуса ;)

посмотрел код. Труд то какой , и комменты и отформатировано . Зачотно.

как то странно выглядит процесс отгона голов. Не понял, чем обеспечивается объем 10-15% от АС  в режиме капельного отбора. То есть мощность для капельного отбора уже подобрана? И чисто по времени?

тс походу применяет дробный процесс с автоматической сменой приемных емкостей.

интересно подача линейная , или карусельная?

Alex-GK
Offline
Зарегистрирован: 10.04.2012

inspiritus пишет:

Главное это головы отделить, а остальное дело вкуса ;)

посмотрел код. Труд то какой , и комменты и отформатировано . Зачотно.

как то странно выглядит процесс отгона голов. Не понял, чем обеспечивается объем 10-15% от АС  в режиме капельного отбора. То есть мощность для капельного отбора уже подобрана? И чисто по времени?

тс походу применяет дробный процесс с автоматической сменой приемных емкостей.

интересно подача линейная , или карусельная?

Головы отделяю на всех трех этапах.

Сейчас процесс автоматизирован на 328 меге с LCD 16х2, только функционал убогий. Зато технология отработана.

Поэтому с написанием скетча неторопился и комментировал и форматировал, месяца два писал, торопиться то некуда.

Головы отбираются просто на минимальной мощности - маленький ТЭН, потому-что число-импульсную регулировку применить нельзя, а с фазовым управлением пока не заморачивался, да и помехи... Заполняя куб на второй - третьей перегонке стараюсь довести крепость до одного значения около 40%, поэтому по времени отбор получается адекватным.

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

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

А хочется чтобы залил, емкости поставил, кнопку нажал и пошел на халтуру не беспокоясь о возможных факапах.

Вот и пилю железяку:

А собственно по теме вопроса идеи есть? А то пока только полтора совета было.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Alex-GK пишет:
А собственно по теме вопроса идеи есть? А то пока только полтора совета было.
Вам что рассказывать про ООП и как писать структурированую программу? И самое главное зачем это надо.

inspiritus
Offline
Зарегистрирован: 17.12.2012

У Вас есть место в программной памяти. Попробуйте строки для экранов хранить не в файлах, а в пямяти программ( flash) через PROGMEM. 

Мне видится, что файловые операции жрут немеряно оперативки.

Alex-GK
Offline
Зарегистрирован: 10.04.2012

qwone пишет:

Вам что рассказывать про ООП и как писать структурированую программу? И самое главное зачем это надо.

Честно говоря об ООП у меня весьма смутные представление, во времена молодости, когда меня учили программированию, "в тренде" были бэйсик и паскаль, а там ООП не было, а сейчас на пятом десятке сложно менять парадигму программирования.

Тем более, что я очень плохо представляю ЗАЧЕМ ООП в этой программе, по сути построенной на конечных автоматах.

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

Alex-GK
Offline
Зарегистрирован: 10.04.2012

inspiritus пишет:

У Вас есть место в программной памяти. Попробуйте строки для экранов хранить не в файлах, а в пямяти программ( flash) через PROGMEM. 

Мне видится, что файловые операции жрут немеряно оперативки.

Я думал об этом, но там в файлах еще прорва чисел - координат на экране, так что придется наворотить пару процедур вокруг преобразования pgm_read  в int и string, что тоже не облегчит программу.

К тому же судя по 

int memoryFree()
{
   int freeValue;
   if((int)__brkval == 0)
      freeValue = ((int)&freeValue) - ((int)&__bss_end);
   else
      freeValue = ((int)&freeValue) - ((int)__brkval);
   return freeValue;
}

Памяти до вала (примерно 6000 байт).

А без оптимизации, когда все работает memoryFree()  дает 2800 байт.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Сложно, но надо. Тем более фундаментальных объянений что такое ООП и сигнальное программирование нет. По крайней мере книг, хотя попытки есть. Рассказывать про  закон перехода количества в качество вам не надо. Вот и в программировании он тоже работает. Чем больше кода, тем сложнее за ним уследить. Вот и начинают структурировать код программы. И цепочка Деталь->Изделие превращается Деталь->Узел->Изделие  или  в Деталь->Мелкие Узелы->Крупные Узелы->Изделие. Вот по этой причине произошел переход к Си , а потом с Си++ , и затем ООП Си++.  Другие языки это обеспечивали хуже.  Опять же есть 4 этап- заливка и отладка скетча. Вот вы на этом и зависли. Потому что чем длинее программы, тем сложнее ее тестировать. Но если у вас была  бы структурирована программа. То тестировали каждый узел отдельно , а потом сборку целиком. Так меньше времени занимает этот процесс. Ну ООП позволяет написать именно структурированую программу. И опять же хорошо структурированую программу легче всего абгдейдить добавив дополнительные функции. 

Alex-GK
Offline
Зарегистрирован: 10.04.2012

DIYMan пишет:

Единственное, что могу подсказать сходу - не передавать сложные объекты по значению в функции, т.е. вот такое:

int SDintRead(File CurFile)

заменять на такое:

int SDintRead(File& CurFile)

 

Отделался я от передачи File в функции. Поскольку dataFile объявлен глобально, то и мороки было немного.

Но ничего не изменилось, как глючило при включеной оптимизации так и глючит.

Чёрт с ним, буду жить без оптимизации, а к тормознутости привыкну.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Кстате о тормознутости 

0341 void Alarm (int AlarmType) {              // Разнообразные сигналы сирены
0342   switch (AlarmType) {
0343     case BeepAlarm :
0344       digitalWrite (SirRelPin,DeviceOn);
0345       delay (150);
0346       digitalWrite (SirRelPin,DeviceOff);
0347     break;
0348     case BBeepAlarm :
0349       digitalWrite (SirRelPin,DeviceOn);
0350       delay (130);
0351       digitalWrite (SirRelPin,DeviceOff);
0352       delay (130);
0353       digitalWrite (SirRelPin,DeviceOn);
0354       delay (130);
0355       digitalWrite (SirRelPin,DeviceOff);
0356     break;
0357     case FullAlarm :
0358       digitalWrite (SirRelPin,DeviceOn);
0359     break;
0360     case StopAlarm :
0361       digitalWrite (SirRelPin,DeviceOff);
0362     break;   
0363   }
0364 }

 delay видете . Привет тормознутость. К ней не привыкать надо, а от нее "лечить"

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Вот еще пример тормознутости 

0330 int WaterFlowChk () {                     // Проверяем состояние поплавка на переполнение бутылки
0331 int State = digitalRead (WCircSensPin);
0332 unsigned long WtrFTimer = millis()+250;
0333   while (millis () < WtrFTimer ) {
0334     if (!(State == digitalRead (WCircSensPin))) {
0335       return (0);
0336     }
0337   
0338   return (1);
0339 }

Скрытый delay

inspiritus
Offline
Зарегистрирован: 17.12.2012

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

Alex-GK
Offline
Зарегистрирован: 10.04.2012

inspiritus пишет:

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

Именно так, все чтение с карты и вывод на экран идет внутри процедуры DrawFile();

А проверка протока воды, если этот самый проток есть 250 миллисекунд тоже не ждет, а завершается по приходу импульса от датчика, да и происходит не чаще раза за проход loop{}.

Другое дело, что внутри библиотеки UTFT может delay быть... Пойду поищу.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

По любому писать блокирующие процедуры это плохая привычка ведущая к тормозам программы.