sketch_ups_nano_1.ino

      // VERSION 2 (с разрывом входного напряжения 220В блока питания при помощи реле AUX клемм NC)

      #include <EEPROM.h>
      #include <CRC32.h>
      #include <OneWire.h>
      #include <DallasTemperature.h>

      #define pinInpT        2
      #define pinInpACOK     3
      #define pinInpBatLow   4
      #define pinInpAux      5
      #define pinOutLedBat   6
      #define pinOutLedPlc   7
      #define pinOutBeep     8
      #define pinOutBatCtrl  9
      #define pinOutCooler   10
      #define pinOutAux      11
      #define pinOutLedRun   LED_BUILTIN

      #define VERSION            0
      #define REQUEST_LEN        15
      #define REQUEST_TIMEOUT    100
      #define DEFAULT_TIMEOUT_SERIAL  10
      #define DEFAULT_TIMEOUT_BATCTRL  5

      #define ADDR_SERIAL_CONNECTED   0
      #define ADDR_BAT_CTRL           2
      #define ADDR_T_OFF              4
      #define ADDR_T_ON               6
      #define ADDR_BEEP_MODE          8

      OneWire oneWire(pinInpT);
      DallasTemperature dallasTemp(&oneWire);

      int     T;
      int     Toff;
      int     Ton;

      boolean outBeep;
      boolean outBatCtrl;
      boolean outCooler;
      boolean outAux;
      boolean outLedBat;
      boolean outLedPlc;

      boolean manual_BatCtrl;
      boolean manual_Cooler;
      int beep_mode;

      int timeBatCtrl;
      int cntBatCtrl;

      int cntLedPlc;

      long ms;
      int cnt5000ms;
      int cnt1000ms;
      int cnt200ms;

      boolean serConnected;
      int timeoutSerialConnected;
      long serms;
      int  sercnt;
      uint8_t  serbuf[REQUEST_LEN];

      boolean plcHasBeenDetected;
      int     lastACOKSentToPlc;

      void setup() {
        pinMode(pinInpACOK, INPUT);
        pinMode(pinInpBatLow, INPUT);
        pinMode(pinInpAux, INPUT);
        pinMode(pinOutLedBat, OUTPUT);
        pinMode(pinOutLedPlc, OUTPUT);
        pinMode(pinOutBeep, OUTPUT);
        pinMode(pinOutBatCtrl, OUTPUT);
        pinMode(pinOutCooler, OUTPUT);
        pinMode(pinOutAux, OUTPUT);
        pinMode(pinOutLedRun, OUTPUT);

        Serial.begin(9600);

        dallasTemp.begin();
        dallasTemp.setWaitForConversion(false);
        dallasTemp.requestTemperatures();

        manual_BatCtrl = false;
        manual_Cooler = false;
        cnt5000ms = 0;
        cnt1000ms = 0;
        cnt200ms = 0;
        cntLedPlc = 0;
        cntBatCtrl = 0;
        plcHasBeenDetected = false;
        lastACOKSentToPlc = 0;
        sercnt = 0;
        serConnected = false;
        ms = millis();

        EEPROM.get(ADDR_SERIAL_CONNECTED, timeoutSerialConnected);
        EEPROM.get(ADDR_BAT_CTRL, timeBatCtrl);
        EEPROM.get(ADDR_T_OFF, Toff);
        EEPROM.get(ADDR_T_ON, Ton);
        EEPROM.get(ADDR_BEEP_MODE, beep_mode);

        validate_vars();
        greeting();
      }

      void greeting() {
        Serial.print("UPS NANO version ");
        Serial.println(VERSION);

        Serial.print("SerTimeout = ");
        Serial.println(timeoutSerialConnected);
        Serial.print("BatCtrlTimeout = ");
        Serial.println(timeBatCtrl);
        Serial.print("Toff = ");
        Serial.println(Toff);
        Serial.print("Ton = ");
        Serial.println(Ton);
      }

      void validate_vars() {
        if(timeBatCtrl <= 0)
          timeBatCtrl = 1;

        if(timeoutSerialConnected <= 0)
          timeoutSerialConnected = 1;

        if(beep_mode < 0  ||  beep_mode > 2)
          beep_mode = 0;
      }

      void loop() {
        long msdelta = millis() - ms;
        if( msdelta >= 100 ) {
          ms += 100;
          loop100ms();
        } else
        if( msdelta < 0 )  {
          msdelta += 0x80000000L;
          if( msdelta >= 100 ) {
            ms += 100 + 0x80000000L;
            loop100ms();
          }
        }

        if( Serial ) {
          if( Serial.available() == 0 ) {
            msdelta = millis() - serms;
            if( msdelta < 0 )
              msdelta += 0x80000000L;
            if( sercnt > 0  &&  msdelta > REQUEST_TIMEOUT ) {
              sercnt = 0;
              serConnected = false;
            }
            if( serConnected  &&  (msdelta/1000) >= timeoutSerialConnected ) {
              serConnected = false;
            }
          } else {
            while( Serial.available() > 0 ) {
              serbuf[sercnt++] = Serial.read();
              if( sercnt == REQUEST_LEN ) {
                onSerRequest();
                sercnt = 0;
              }
            }
            serms = millis();
          }
        }
      }

      void loop100ms() {
        if( cnt5000ms++ >= 50 ) {
          cnt5000ms = 0;
          loop5000ms();
        }

        if( cnt1000ms++ >= 10 ) {
          cnt1000ms = 0;
          loop1000ms();
        }

        if( cnt200ms++ >= 2 ) {
          cnt200ms = 0;
          loop200ms();
        }

        // led plc
        if( !serConnected ) {
          if( cntLedPlc++ < 10 )
            outLedPlc = cntLedPlc % 2;
          else if( cntLedPlc >= 30 ) {
            outLedPlc = false;
            cntLedPlc = 0;
          }
        } else
          cntLedPlc = 0;

        digitalWrite(pinOutLedPlc, !outLedPlc); //inv
        digitalWrite(pinOutLedRun, !outLedPlc); //inv
        digitalWrite(pinOutLedBat, !outLedBat); //inv
      }

      void loop200ms() {
        // led bat
        if( digitalRead(pinInpACOK) ) { //inv
          if( (!digitalRead(pinInpBatLow)) == HIGH ) //inv
            outLedBat = !outLedBat;
          else
            outLedBat = HIGH;
        } else
          outLedBat = LOW;
      }

      void loop1000ms() {
        if( serConnected )
          outLedPlc = !outLedPlc;

        // bat control
        if( !manual_BatCtrl ) {
          outBatCtrl = 1;
          if( timeBatCtrl > 0  &&  !serConnected ) {
            if( digitalRead(pinInpACOK) ) { //inv
              if( cntBatCtrl >= timeBatCtrl ) {
                outBatCtrl = 0;
              } else
                cntBatCtrl++;
            } else
              cntBatCtrl = 0;
          }
        }
        digitalWrite(pinOutBatCtrl, outBatCtrl); //

        // beep
        if( digitalRead(pinInpACOK) ) { //inv
          if( outBeep = !outBeep )
            // second arg:
            //   1200 - normal tone
            //     40 - debug tone (very low)
            if( beep_mode == 0 )
              tone(pinOutBeep, 1200, 1000);
            else if( beep_mode == 1 )
              tone(pinOutBeep, 40, 1000);
          else
            noTone(pinOutBeep);
        }

        // aux
        outAux = !serConnected  &&  plcHasBeenDetected  &&  (lastACOKSentToPlc == 0);

        digitalWrite(pinOutAux, outAux);

        // cooler
        int t = (int)round(dallasTemp.getTempCByIndex(0) * 10);
        dallasTemp.requestTemperatures();
        if( t > -500  &&  t < 1200 )
          T = t;

        if( !manual_Cooler ) {
          if( Ton > Toff? T >= Ton: T <= Ton )
            outCooler = 1;
          else
          if( Ton > Toff? T <= Toff: T >= Toff )
            outCooler = 0;
        }
        digitalWrite(pinOutCooler, !outCooler); //inv
      }

      void loop5000ms() {
      }

      void onSerRequest() {
        serConnected = false;

        uint32_t cs1 = CRC32::calculate(serbuf, REQUEST_LEN-4);
        uint32_t cs2 =
              (((uint32_t)serbuf[REQUEST_LEN-4])<<24) +
              (((uint32_t)serbuf[REQUEST_LEN-3])<<16) +
              (((uint32_t)serbuf[REQUEST_LEN-2])<<8) +
                (uint32_t)serbuf[REQUEST_LEN-1];

        if( cs1 != cs2 ) {
          return;
        }

        if( serbuf[0] != VERSION ) {
          sendErrorVersion();
          return;
        }

        getSerbufValues();
        sendGoodAnswer();

        serConnected = true;
        plcHasBeenDetected = true;
      }

      void sendGoodAnswer() {
        lastACOKSentToPlc = !digitalRead(pinInpACOK);

        serbuf[0] = 0;
        serbuf[1] = lastACOKSentToPlc + (!digitalRead(pinInpBatLow))*2 + (!digitalRead(pinInpAux))*4;
        serbuf[2] = (T >> 8) & 0xFF;
        serbuf[3] = T & 0xFF;

        addSerbufCrc(4);
        Serial.write(serbuf, 8);
      }

      void sendErrorVersion() {
          serbuf[0] = 0xFF;
          serbuf[1] = VERSION;
          serbuf[2] = serbuf[3] = 0;
          addSerbufCrc(4);
          Serial.write(serbuf, 8);
      }

      void addSerbufCrc(int len) {
        uint32_t cs = CRC32::calculate(serbuf, len);
        serbuf[len++] = cs >> 24;
        serbuf[len++] = (cs %= 0x1000000) >> 16;
        serbuf[len++] = (cs %= 0x10000) >> 8;
        serbuf[len++] = cs %= 0x100;
      }

      void getSerbufValues() {
        timeBatCtrl =
              (serbuf[1] << 8) +
               serbuf[2];

        timeoutSerialConnected =
              (serbuf[3] << 8) +
               serbuf[4];

        Ton =
              (serbuf[5] << 8) +
               serbuf[6];

        Toff =
              (serbuf[7] << 8) +
               serbuf[8];

      //  int aux_mode =
      //         serbuf[9] & 3;
      //
      //  if( aux_mode == 0 )
      //    outAux = 0;
      //  else if( aux_mode == 1 )
      //    outAux = 1;

        int manuals =
              serbuf[10];

        manual_BatCtrl = (manuals & 0x1) > 0;
        if( manual_BatCtrl )
          outBatCtrl = (manuals & 0x2) > 0;

        manual_Cooler = (manuals & 0x4) > 0;
        if( manual_Cooler )
          outCooler = (manuals & 0x8) > 0;

        beep_mode = (manuals >> 4) & 0xF;

        validate_vars();

        EEPROM.put(ADDR_SERIAL_CONNECTED, timeoutSerialConnected);
        EEPROM.put(ADDR_BAT_CTRL, timeBatCtrl);
        EEPROM.put(ADDR_T_OFF, Toff);
        EEPROM.put(ADDR_T_ON, Ton);
        EEPROM.put(ADDR_BEEP_MODE, beep_mode);
      }