/***********************************************************************
 * Project:   "Lichtwecker"
 * Homepage:  http://www.kuspbv.de/projects/electronics/wul.html
 * Version:   1.1
 * State:     stable
 *
 * Author:    Michael Kammer
 *
 **** Pin Layout *******************************************************
 *                      _________
 *                  5V -|       |- Ground
 *        RADIO_ON_BTN -|       |- LIGHT_SWITCH_BTN
 *       RADIO_OFF_BTN -|  PIC  |- DISPLAY_SWITCH_BTN
 *     ALARM_DETECTION -|  16F- |- TRIAC_GATE
 *        ALARM_OFF_OC -|  688  |- Comparator
 *        RADIO_OFF_OC -|       |- Comparator
 *         RADIO_ON_OC -|_______|- DISPLAY_RELAY
 *
 **** License **********************************************************
 *
 * Copyright (C) 2009-2013 Michael Kammer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************/
#define __16f688
#include "pic16f688.h"

static unsigned int at 0x2007 __CONFIG =
  _CP_OFF &
  _WDT_ON &
  _BOD_ON &
  _PWRTE_ON &
  _INTRC_OSC_NOCLKOUT &
  _MCLRE_OFF &
  _IESO_OFF &
  _FCMEN_OFF;

#define RADIO_ON_BTN       RA5
#define RADIO_OFF_BTN      RA4
#define ALARM_DETECTION    RA3
#define ALARM_OFF_OC       RC5
#define RADIO_OFF_OC       RC4
#define RADIO_ON_OC        RC3
#define DISPLAY_RELAY      RC2
#define TRIAC_GATE         RA2
#define DISPLAY_SWITCH_BTN RA1
#define LIGHT_SWITCH_BTN   RA0

#define clrwdt() do {                    \
    _asm                                 \
      CLRWDT                             \
    _endasm;                             \
  while (0)

#define sleep()  do {                    \
    _asm                                 \
      SLEEP                              \
    _endasm;                             \
  while (0)

#define eewrite(D,Ado {                \
    EEIF   0;                          \
    EEADR  A;                          \
    EEDATA D;                          \
    _asm                                 \
      CLRWDT                             \
      BCF EECON13                      \
      BSF EECON12                      \
      CLRWDT                             \
      MOVLW 0x55                         \
      MOVWF EECON2                       \
      MOVLW 0xAA                         \
      MOVWF EECON2                       \
      BSF EECON11                      \
    _endasm;                             \
    while (!EEIFclrwdt();              \
    EEIF 0;                            \
  while (0)

#define eeread(V,Ado {                 \
    EEADR A;                           \
    _asm                                 \
      BSF EECON10                      \
    _endasm;                             \
    EEDATA;                          \
  while (0)

#define await_zerocross() do {           \
    oldC2OUT CMCON0;                   \
    while (CMCON0 == oldC2OUTclrwdt(); \
  while (0)

#define alarm_off() do {                 \
    ALARM_OFF_OC 1;                    \
    while (!ALARM_DETECTIONclrwdt();   \
    ALARM_OFF_OC 0;                    \
  while (0)

void main() {
  // Variables
  static enum {m_manm_wakingm_gotosleepm_wokeupm_wam_wbmode;
  static unsigned char itickssecsrsecsrminsticksHticksL;
  static unsigned char radioalarmActivediagcsecs;
  static signed char   dimmLevel;

  // PIC init
  clrwdt();
  OPTION_REG  0x07// 0000 1111
  TRISA       0x3B// 0011 1011
  TRISC       0x03// 0000 0011
  WPUA        0x33// 0011 0011
  ANSEL       0x30// 0011 0000
  CMCON0      0x05// 0000 0101
  T1CON       0x00// 0000 0000

  clrwdt();
  // Port init
  TRIAC_GATE    0;
  DISPLAY_RELAY 0;
  RADIO_ON_OC   0;
  RADIO_OFF_OC  1;
  ALARM_OFF_OC  0;

  radio 0;
  dimmLevel = -100;
  alarmActive 0;

  /* Okay, this is the plan
   *
   *  1) Init timer ( 1/4 )
   *  2) Start timer when C2OUT goes low
   *  3) Stop timer when C2OUT goes low again
   *  4) Timer counter now holds 90 degrees of sinus wave (1/4)
   */

  clrwdt();
  T1CON 0x00// 0000 0000
  T1IF  0;
  TMR1L 0;
  TMR1H 0;
  while (C2OUT == 0clrwdt();
  while (C2OUT == 1clrwdt();
  // C2OUT gone low
  T1CON 0x21// 0010 0001
  while (C2OUT == 0clrwdt();
  while (C2OUT == 1clrwdt();
  // C2OUT gone low again
  T1CON 0x00// 0000 0000

  ticksH 0xFF TMR1H;
  ticksL 0xFF TMR1L;

  // Diagmode?
  if (!DISPLAY_SWITCH_BTN) {
    diag  1;
    csecs 1;

    clrwdt();
    i     13;
    T1CON 0x31// 0011 0001
    while (i) {
      i--;

      TMR1L 0;
      TMR1H 0;
      T1IF  0;

      while (!T1IFclrwdt();
      DISPLAY_RELAY++;
    }
    T1CON 0x00// 0000 0000
  else {
    diag  0;
    csecs 41;
  }

  // Welcome sequence
  mode m_wa;
  DISPLAY_RELAY 1;

  0;
  while (1) {
    clrwdt();

    // Alarm
    if (!ALARM_DETECTION) {
      if (diag) {
        ALARM_OFF_OC 1;

        20;
        T1CON 0x31// 0011 0001
        while (i) {
          if (ALARM_DETECTIONi--;

          TMR1L 0;
          TMR1H 0;
          T1IF  0;

          while (T1IF == 0clrwdt();
          DISPLAY_RELAY++;
        }
        T1CON 0x00// 0000 0000

        ALARM_OFF_OC 0;
      else alarm_off();

      if (alarmActive) {
        // Start wake up mode
        secs 0;
        dimmLevel = -22;
        mode m_waking;
      }
    }

    clrwdt();

    // Console
    if (!DISPLAY_SWITCH_BTN) {
      while (!DISPLAY_SWITCH_BTN) {
        while (!DISPLAY_SWITCH_BTN) {
          sleep();
          if (!RADIO_ON_BTN) {
            // Activate alarm
            alarmActive 1;

            7;
            T1CON 0x31// 0011 0001
            while (i) {
              i--;

              TMR1L 0;
              TMR1H 0;
              T1IF  0;

              while (T1IF == 0clrwdt();
              DISPLAY_RELAY++;
            }
            T1CON 0x00// 0000 0000
          }

          if (!RADIO_OFF_BTN) {
            // Deactivate alarm
            alarmActive 0;

            3;
            T1CON 0x31// 0011 0001
            while (i) {
              i--;

              TMR1L 0;
              TMR1H 0;
              T1IF  0;

              while (T1IF == 0clrwdt();
              DISPLAY_RELAY++;
            }
            T1CON 0x00// 0000 0000
          }

        }
      }
      DISPLAY_RELAY++;
    }
    if (!LIGHT_SWITCH_BTN) {
      if (mode == m_wokeupRADIO_OFF_OC 1;

      mode m_man;

      while (!LIGHT_SWITCH_BTN) {
        while (!LIGHT_SWITCH_BTN) {
          sleep();
          if (!RADIO_OFF_BTN) {
            // Dimm down
            mode m_gotosleep;
            if (dimmLevel 22dimmLevel 22;

            secs  0;
          }

          if (!RADIO_ON_BTN) {
            // Dimm up
            mode m_waking;
            if (dimmLevel < -22dimmLevel = -22;

            secs  0;
          }
        }
      }

      if (mode == m_man) {
        if (dimmLevel != 100) {
          if (dimmLevel < -22dimmLevel = -22// Inital on
          dimmLevel += 11;
          if (dimmLevel >= 22dimmLevel 100// Instant on
        else dimmLevel = -100;
      }
    }
    if (!RADIO_ON_BTN && !radio) {
      RADIO_ON_OC 1;
      radio       1;

      rsecs 0;
      rmins 0;

      if (mode == m_waking || mode == m_wokeup) {
        mode m_man;
        dimmLevel 100;
      }
    }
    if (!RADIO_OFF_BTN) {
      RADIO_OFF_OC 1;
      radio        0;

      if (mode == m_wokeup) {
        mode m_man;
        dimmLevel = -100;
      }
    }

    // Mode selection
    if (mode == m_wb) {
      if (ticks 1) {
        dimmLevel--;
        if (dimmLevel 22dimmLevel 22;
      }

      if (dimmLevel < -22) {
        dimmLevel = -100;
        mode m_man;
      }
    }
    if (mode == m_wa) {
      if (ticks 1) {
        dimmLevel++;
        if (dimmLevel < -22dimmLevel = -22;
      }
      if (dimmLevel 22mode m_wb;
    }
    if (mode == m_waking) {
      if (secs == csecs) {
        secs 0;

        dimmLevel++;
        if (dimmLevel == 0) {
          // Switch on display
          DISPLAY_RELAY 0;
        }
        if (dimmLevel == 11) {
          // Switch on radio
          RADIO_ON_OC 1;
          radio       1;
        }
        if (dimmLevel == 23) {
          // We're done
          mode m_wokeup;
          dimmLevel 100;
        }
      }
    }
    if (mode == m_gotosleep) {
      if (secs == csecs) {
        secs 0;

        dimmLevel--;
        if (dimmLevel == 0) {
          // Switch off display
          DISPLAY_RELAY 1;
        }
        if (dimmLevel == -11) {
          // Switch off radio
          RADIO_OFF_OC=1;
        }
        if (dimmLevel == -23) {
          // We're done
          mode m_man;
          dimmLevel = -100;
        }
      }
    }

    // Dimmer loop
    if (dimmLevel 22) {
      TRIAC_GATE 1;
      while (!C2OUTclrwdt();
      while (C2OUTclrwdt();
    else {
      if (dimmLevel < -22) {
        TRIAC_GATE 0;
        while (!C2OUTclrwdt();
        while (C2OUTclrwdt();
      else {
        T1CON 0x00// 0000 0000
        TMR1L ticksL;
        TMR1H ticksH;
        T1IF  0;

        // phase shift
        if (!DISPLAY_RELAYTMR1H += 5;

        TMR1H += (dimmLevel >> 1);
        if (TMR1L 127if ( !(dimmLevel 1) ) TMR1L 0;

        while (!C2OUTclrwdt();
        while (C2OUTclrwdt();
        // C2OUT gone low
        T1CON 0x01// 0000 0001

        while (!T1IFclrwdt();
        // Lowest values of sinus
        TRIAC_GATE 1;

        T1CON 0x00// 0000 0000
        TMR1L ticksL;
        TMR1H ticksH;
        T1IF  0;

        // phase shift
        if (!DISPLAY_RELAYTMR1H -= 9else TMR1H -= 7;
        TMR1H += (dimmLevel >> 1);
        if (TMR1L 127if ( !(dimmLevel 1) ) TMR1L 0;

        while (!C2OUTclrwdt();
        TRIAC_GATE 0;
        // C2OUT gone high
        T1CON 0x01// 0000 0001

        while (!T1IFclrwdt();
        // Highes values of sinus
        TRIAC_GATE 1;
        clrwdt();
        clrwdt();
        clrwdt();
        TRIAC_GATE 0;
      }

      // Timer
      ticks++;
      if (ticks == 50) {
        ticks 0;
        secs++;
        rsecs++;
        if (secs == 60secs=0;
        if (rsecs == 60) {
          rsecs 0;
          rmins++;
          if (rmins == 50radio 0;
        }
      }
    }

    // Tidy up
    RADIO_ON_OC  0;
    RADIO_OFF_OC 0;
  }
}