sketch_ups_nano_3.ino

      // VERSION 3

      #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 pinOutAcCtrl   10
      #define pinOutAux      11
      #define pinOutLedRun   LED_BUILTIN

      #define VERSION            3
      #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_BEEP_MODE          8

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

      int     T;

      boolean outBeep;
      boolean outBatCtrl;
      boolean outAcCtrl;
      boolean outAux;
      boolean outLedBat;
      boolean outLedPlc;

      boolean manual_BatCtrl;
      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(pinOutAcCtrl, OUTPUT);
        pinMode(pinOutAux, OUTPUT);
        pinMode(pinOutLedRun, OUTPUT);

        Serial.begin(9600);

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

        manual_BatCtrl = 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_BEEP_MODE, beep_mode);

        outAux = 0;
        digitalWrite(pinOutAux, outAux);

        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);
      }

      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);
        digitalWrite(pinOutLedRun, !outLedPlc);
        digitalWrite(pinOutLedBat, outLedBat);
      }

      void loop200ms() {
        // led bat
        if( !digitalRead(pinInpACOK) ) {
          if( (digitalRead(pinInpBatLow)) == HIGH )
            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) ) {
              if( cntBatCtrl >= timeBatCtrl ) {
                outBatCtrl = 0;
              } else
                cntBatCtrl++;
            } else
              cntBatCtrl = 0;
          }
        }
        digitalWrite(pinOutBatCtrl, outBatCtrl);

        // beep
        if( !digitalRead(pinInpACOK) ) {
          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);
        }

        // acctrl
        outAcCtrl = !serConnected  &&  plcHasBeenDetected  &&  (lastACOKSentToPlc == 0);
        digitalWrite(pinOutAcCtrl, outAcCtrl);

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

      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];

        if( outAux != serbuf[9] ) {
          outAux = serbuf[9];
          digitalWrite(pinOutAux, outAux);
        }

        beep_mode = (serbuf[10] >> 4) & 0xF;

        validate_vars();

        EEPROM.put(ADDR_SERIAL_CONNECTED, timeoutSerialConnected);
        EEPROM.put(ADDR_BAT_CTRL, timeBatCtrl);
        EEPROM.put(ADDR_BEEP_MODE, beep_mode);
      }