/*
  Module for "minute cook timer" with multiplexed 4digit LED display
  Arduino Mini operate different types of display with common A or C
  Common pins must be driven through transistor, segments through resisors
  Rotation encoder increments or decrements time for driving output LED
  Use: MsTimer2 library
  
  This example code is in the public domain.
  S. Pechal 2018, Version: 1.0
 */

#include <MsTimer2.h>
// You must define display type here
#define DA56
#ifndef LEDDISPDEF
#define LEDDISPDEF

//**********************************************************
// Display type dependent part
// You can add your display driving here
// -------------------------------------// ---- Common for VQE12,14,22,24, DA56 ------
// ------- Display with common anode ---------
#if defined VQEX2 || defined VQEX4 || defined DA56
// Turn on/off digit
#define TURNON LOW
#define TURNOFF HIGH
#endif

// ---- Common for all DA56 ------
#if defined DA56 
// Assign segment to Arduino pin number
#define COM1 11
#define COM2 12
#define COM3 14
#define COM4 15

#define SEG0A 7
#define SEG0B 8
#define SEG0C 9
#define SEG0D 10
#define SEG0E 16
#define SEG0F 3
#define SEG0G 4
#define SEG0H 2

#define SEG1A 7
#define SEG1B 8
#define SEG1C 9
#define SEG1D 10
#define SEG1E 16
#define SEG1F 3
#define SEG1G 4
#define SEG1H 2

#define SEG2A 7
#define SEG2B 8
#define SEG2C 9
#define SEG2D 10
#define SEG2E 16
#define SEG2F 3
#define SEG2G 4
#define SEG2H 2

#define SEG3A 7
#define SEG3B 8
#define SEG3C 9
#define SEG3D 10
#define SEG3E 16
#define SEG3F 3
#define SEG3G 4
#define SEG3H 2
#endif
// ---------------------------------------
#if defined DA56
#define MAXDISVAL 9999
#define MINDISVAL -999
#endif

// End of display type dependent part
// ****************************************************
#endif

#include "leddispd.h"

// Output (LED) parameters
#define LEDOUT 13
#define OUTON HIGH
#define OUTOFF LOW
// Pins for encoder
#define PINCLK 6
#define PINDT 5

// Milisec timer
unsigned char volatile msTimer;
// Second timer
unsigned char volatile secTimer = 0;
// Minute timer
unsigned int volatile minTimer = 0;
// Last status CLK 
int  statLast;   

// Digit segments to display
unsigned char disparr[4];

// Table for chars '0'-'9','°','-','P','L','H',' '
unsigned char chartab[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x63,0x40,0x73,0x38,0x76,0x00};
// Sequence to switch digits off
unsigned char cleardig[4] = {COM4,COM1,COM2,COM3};
// Sequence to switch digits on
unsigned char ondigit[4] = {COM1,COM2,COM3,COM4};

// Set decimal point at position
void SetDP(unsigned char DPposit)
{
  if(DPposit < 4) disparr[DPposit] |= 0x80;
}

// Set first digit with minus
void Set1digit(unsigned char digit1, bool sign)
{
  if(sign) disparr[0] |= chartab[0x0B];
  if(digit1) disparr[0] |= chartab[digit1];
}

// Clear display
void ClrDisp(void)
{
  disparr[0] = 0; disparr[1] = 0; disparr[2] = 0; disparr[3] = 0;
}

// Integer to display
void IntToDisp(int dispnum)
{
  unsigned char fract;
  bool minussig=0;

  ClrDisp();  
  if(dispnum > MAXDISVAL) {  // Test very high value - show "HI"
    disparr[1] = chartab[0x0E];
    disparr[2] = chartab[0x01];
    return;
  }
  if(dispnum < MINDISVAL) {  // Test very low value - show "LO"
    disparr[1] = chartab[0x0D];
    disparr[2] = chartab[0x00];
    return;
  }
  // Process negative numbers
  if(dispnum < 0) {
    dispnum= -dispnum;
    minussig=1;
  }
  // Calculate particular digits
  fract = dispnum % 10;
  disparr[3] = chartab[fract];
  dispnum /= 10;
  fract = dispnum % 10;
  disparr[2] = chartab[fract];
  dispnum /= 10;
  fract = dispnum % 10;
  disparr[1] = chartab[fract];
  dispnum /= 10;
  Set1digit(dispnum,minussig); // First digit is special case
}

// Interrupt procedure connected to Timer2  
void leddisp()
{
  static unsigned char digit=0;
  
   msTimer++;  // Milisec timer +4 milisec
  if(msTimer == 250) msTimer=0;   // 250*4 ms = 1 sec
  if(minTimer && (msTimer == 125)) { IntToDisp(minTimer); SetDP(3); }  // For second half blink last DP
  if(!msTimer) {  // New second - update timers and output
    if(minTimer) digitalWrite(LEDOUT, OUTON); else digitalWrite(LEDOUT, OUTOFF);  // Set output if minute timer is ON
    secTimer++;
    if(secTimer == 60) {
      secTimer=0;
      if(minTimer) minTimer--;
    }
    IntToDisp(minTimer);
  }
 
  digitalWrite(cleardig[digit], TURNOFF);   // turn off old digit LED display
  // Set appropriate pins for new digit
  switch (digit) {
    case 0: {
      if(disparr[0] & 0x01) digitalWrite(SEG0A, TURNON); else digitalWrite(SEG0A, TURNOFF);
      if(disparr[0] & 0x02) digitalWrite(SEG0B, TURNON); else digitalWrite(SEG0B, TURNOFF);
      if(disparr[0] & 0x04) digitalWrite(SEG0C, TURNON); else digitalWrite(SEG0C, TURNOFF);
      if(disparr[0] & 0x08) digitalWrite(SEG0D, TURNON); else digitalWrite(SEG0D, TURNOFF);
      if(disparr[0] & 0x10) digitalWrite(SEG0E, TURNON); else digitalWrite(SEG0E, TURNOFF);
      if(disparr[0] & 0x20) digitalWrite(SEG0F, TURNON); else digitalWrite(SEG0F, TURNOFF);
      if(disparr[0] & 0x40) digitalWrite(SEG0G, TURNON); else digitalWrite(SEG0G, TURNOFF);
      if(disparr[0] & 0x80) digitalWrite(SEG0H, TURNON); else digitalWrite(SEG0H, TURNOFF);
    } break;
    case 1: {
      if(disparr[1] & 0x01) digitalWrite(SEG1A, TURNON); else digitalWrite(SEG1A, TURNOFF);
      if(disparr[1] & 0x02) digitalWrite(SEG1B, TURNON); else digitalWrite(SEG1B, TURNOFF);
      if(disparr[1] & 0x04) digitalWrite(SEG1C, TURNON); else digitalWrite(SEG1C, TURNOFF);
      if(disparr[1] & 0x08) digitalWrite(SEG1D, TURNON); else digitalWrite(SEG1D, TURNOFF);
      if(disparr[1] & 0x10) digitalWrite(SEG1E, TURNON); else digitalWrite(SEG1E, TURNOFF);
      if(disparr[1] & 0x20) digitalWrite(SEG1F, TURNON); else digitalWrite(SEG1F, TURNOFF);
      if(disparr[1] & 0x40) digitalWrite(SEG1G, TURNON); else digitalWrite(SEG1G, TURNOFF);
      if(disparr[1] & 0x80) digitalWrite(SEG1H, TURNON); else digitalWrite(SEG1H, TURNOFF);
    } break;
    case 2: {
      if(disparr[2] & 0x01) digitalWrite(SEG2A, TURNON); else digitalWrite(SEG2A, TURNOFF);
      if(disparr[2] & 0x02) digitalWrite(SEG2B, TURNON); else digitalWrite(SEG2B, TURNOFF);
      if(disparr[2] & 0x04) digitalWrite(SEG2C, TURNON); else digitalWrite(SEG2C, TURNOFF);
      if(disparr[2] & 0x08) digitalWrite(SEG2D, TURNON); else digitalWrite(SEG2D, TURNOFF);
      if(disparr[2] & 0x10) digitalWrite(SEG2E, TURNON); else digitalWrite(SEG2E, TURNOFF);
      if(disparr[2] & 0x20) digitalWrite(SEG2F, TURNON); else digitalWrite(SEG2F, TURNOFF);
      if(disparr[2] & 0x40) digitalWrite(SEG2G, TURNON); else digitalWrite(SEG2G, TURNOFF);
      if(disparr[2] & 0x80) digitalWrite(SEG2H, TURNON); else digitalWrite(SEG2H, TURNOFF);
    } break;
    case 3: {
      if(disparr[3] & 0x01) digitalWrite(SEG3A, TURNON); else digitalWrite(SEG3A, TURNOFF);
      if(disparr[3] & 0x02) digitalWrite(SEG3B, TURNON); else digitalWrite(SEG3B, TURNOFF);
      if(disparr[3] & 0x04) digitalWrite(SEG3C, TURNON); else digitalWrite(SEG3C, TURNOFF);
      if(disparr[3] & 0x08) digitalWrite(SEG3D, TURNON); else digitalWrite(SEG3D, TURNOFF);
      if(disparr[3] & 0x10) digitalWrite(SEG3E, TURNON); else digitalWrite(SEG3E, TURNOFF);
      if(disparr[3] & 0x20) digitalWrite(SEG3F, TURNON); else digitalWrite(SEG3F, TURNOFF);
      if(disparr[3] & 0x40) digitalWrite(SEG3G, TURNON); else digitalWrite(SEG3G, TURNOFF);
      if(disparr[3] & 0x80) digitalWrite(SEG3H, TURNON); else digitalWrite(SEG3H, TURNOFF);
    } break;
  }
  digitalWrite(ondigit[digit], TURNON);   // turn on new digit LED display
  // and pointer to next digit
  ++digit &= 0x03; 
} 

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pins for display driving
  pinMode(COM1, OUTPUT);
  pinMode(COM2, OUTPUT);
  pinMode(COM3, OUTPUT);
  pinMode(COM4, OUTPUT);
  pinMode(SEG0A, OUTPUT);
  pinMode(SEG0B, OUTPUT);
  pinMode(SEG0C, OUTPUT);
  pinMode(SEG0D, OUTPUT);
  pinMode(SEG0E, OUTPUT);
  pinMode(SEG0F, OUTPUT);
  pinMode(SEG0G, OUTPUT);
  pinMode(SEG0H, OUTPUT);
  // Initialize Timer2 for 4 ms period
  MsTimer2::set(4, leddisp); // 4 ms period
  MsTimer2::start();
  // Output is on the internal LED output pin
  pinMode(LEDOUT, OUTPUT);

  // Pins for encoder
  pinMode(PINCLK, INPUT);
  pinMode(PINDT, INPUT);
  // Initial status CLK 
  statLast = digitalRead(PINCLK);   

}

// *****************************************************
// Main loop - set time for lighting output LED
int statCLK;
// The loop function runs over and over again forever
void loop() {
  noInterrupts();
  // Read pin CLK
  statCLK = digitalRead(PINCLK);
  // When actual status is LOW and different from last, then rotation goes
  if ((statCLK == LOW) && (statCLK != statLast)) {
    // If pin DT is not the same as pin CLK, then rotation is UP
    if (digitalRead(PINDT) != statCLK) { // Increment minute timer
      if(minTimer < (MAXDISVAL-1)) {
        minTimer++;
        msTimer = 124;  // To display new time immediately
        secTimer = 0;
      }
    }
    // If pin DT is the same as pin CLK, then rotation is DOWN
    else { // Decrement minute timer (if value>0 is in it)
      if(minTimer) {
        minTimer--;
        msTimer = 124;
        secTimer = 0;
      }
    }
  }
  interrupts();  
  // Save actual status as "last"
  statLast = statCLK;
  delay(1);  // Time for a short break :-) - debounce
}
