/*

   Fuzzy logic example showing the use of
   linguistic variables

   (c) 1995, 2001 Byte Craft Limited
   Waterloo, Canada N2J 4E4

   Walter Banks
 */

#include <16c74.h>  ;     // Use the Microchip PIC16c74
#include <ad74.h>   ;     // Use 16C74's AD converter routines
#include <math.h>;
#include "fuzzc.h"
char __IDOM[2];
#pragma portrw portb @ 6; // external control port.
#define C_const 122
#define F_const 220
#define C_offset -11662  /* -45.5C * 256 */
#define F_offset -12800  /* -50F * 256   */
/* define the control bits that drive the relays */
#define Heat_On  PORTB.0
#define AC_On    PORTB.1
#define Fan_On   PORTB.2
#define Humid_On PORTB.3
#define fifty // powerline frequency
#ifdef fifty
#define rollover 100
#else
#define rollover 120
#endif
/*   This home environment control system bases its control on
     the following parameters.
OutsideTemp
InsideTemp
Time
InsideHumidity
TemperatureSP
HumiditySp
*/
char ticks;   // 1/60 sec tics roll over at 120
              // 1/50 sec tics roll over at 100
char seconds; // actually two seconds 180 = 6 min
//  hours;    .1 hour 0 .. 240 for a day
//            The crisp value hours resolves the day into
//            6 minute intervals which fits nicely into
//            a sigle byte.
//
//  CRISP variable hours has the the following 
//        Linguistic definitions.
//
//   day      starts at 5:30 am is truly day at 6:30 am and 
//            begins to end at 5:30 pm (17:30) and is no 
//            long day at 6:30PM (18:30) .
//
//   night    is not day
//
//   morning  starts at 5:00am is morning by 6:00am
//            begins to end at 8:00am over by 9:00am
//
//   evening  starts at 4:00PM (1600) goes to 8:00pm 
//
//   nightsb  Night set back: The time of the night 
//            that is not evening. 
//
/*  LINGUISTIC hours TYPE char MIN 0 MAX 240 */
/*    { */
char  hours ;
/*     MEMBER day     { 55 ,  65 ,  175 , 185 } */
/*
 
  1-|          ...............           
    |          .              .          
    |         .               .          
    |         .                .         
  0-| ........                 ........  
     ----------------------------------
      0      60     120     180     240
 
*/
char hours_day (char __CRISP) 
  {
  if (__CRISP < 55) return(0);
  else 
    {
    if (__CRISP <= 65) return(((__CRISP - 55) * 25) + 2);
    else
      {
      if (__CRISP <= 175) return(255);
      else
        {
        if (__CRISP <= 185)
          return((( + 185 - __CRISP) * 25) + 2);
        else
          return(0);
        }
      }
    }
  }
/*     MEMBER night   {FUZZY { hours IS NOT day }} */
char hours_night (char __CRISP) 
  {
    return(F_NOT(hours_day(hours)) );
  }
/*     MEMBER morning {50,  60 , 80 , 90 } */
/*
 
  1-|         ....                       
    |         .  .                       
    |         .   .                      
    |        .    .                      
  0-| ........    .....................  
     ----------------------------------
      0      60     120     180     240
 
*/
char hours_morning (char __CRISP) 
  {
  if (__CRISP < 50) return(0);
  else 
    {
    if (__CRISP <= 60) return(((__CRISP - 50) * 25) + 2);
    else
      {
      if (__CRISP <= 80) return(255);
      else
        {
        if (__CRISP <= 90)
          return((( + 90 - __CRISP) * 25) + 2);
        else
          return(0);
        }
      }
    }
  }
/*     MEMBER evening { 160 ,170 ,190 , 200 } */
/*
 
  1-|                        ...         
    |                        .  .        
    |                       .   .        
    |                       .    .       
  0-| ......................     ......  
     ----------------------------------
      0      60     120     180     240
 
*/
char hours_evening (char __CRISP) 
  {
  if (__CRISP < 160) return(0);
  else 
    {
    if (__CRISP <= 170) return(((__CRISP - 160) * 25) + 2);
    else
      {
      if (__CRISP <= 190) return(255);
      else
        {
        if (__CRISP <= 200)
          return((( + 200 - __CRISP) * 25) + 2);
        else
          return(0);
        }
      }
    }
  }
/*     MEMBER nightsb {FUZZY { hours IS night AND hours IS NOT evening }} */
char hours_nightsb (char __CRISP) 
  {
  __IDOM[1] = hours_night(hours) ;
  __IDOM[0] = F_NOT(hours_evening(hours)) ;
  __IDOM[0] = F_AND(__IDOM[1],__IDOM[0]);
    return(__IDOM[0]);
  }
/*    } */
/*
 
     Fuzzy Sets for hours
 
  1-|         ..................         
    |         .. .           .. .        
    |         .   .         . . .        
    |        ..   .         .  . .       
  0-| ........    .        .   . ......  
     ----------------------------------
      0      60     120     180     240
 
 
*/
 void time (void)
 /* called each 1/60 or (1/50) of a second */
  {
    if (++ticks >= rollover)
      {
       ticks = 0;
       if (++seconds >= 180)
        {
          seconds = 0;
          if (++hours >= 240)
          { // new day
           hours = 0;
          }
        }
      }
  }
/*   LINGUISTIC OutsideTemp TYPE char MIN -40 MAX 150 */
/*    { */
char  OutsideTemp ;
/*      MEMBER cold { -40, -40, 40, 60 } */
/*
 
  1-| ..............                     
    | .             .                    
    | .              .                   
    | .               .                  
  0-| .                ................  
     ----------------------------------
    -40       8      55     103     150
 
*/
char OutsideTemp_cold (char __CRISP) 
  {
    {
    if (__CRISP <= 40) return(255);
    else
      {
      if (__CRISP <= 60)
        return((( + 60 - __CRISP) * 12) + 7);
      else
        return(0);
      }
    }
  }
/*      MEMBER cool { -40, -40, 55, 70 } */
/*
 
  1-| .................                  
    | .                .                 
    | .                 .                
    | .                  .               
  0-| .                  ..............  
     ----------------------------------
    -40       8      55     103     150
 
*/
char OutsideTemp_cool (char __CRISP) 
  {
    {
    if (__CRISP <= 55) return(255);
    else
      {
      if (__CRISP <= 70)
        return(( + 70 - __CRISP) * 17);
      else
        return(0);
      }
    }
  }
/*      MEMBER warm { 55 , 68 , 78, 82 } */
/*
 
  1-|                   ...              
    |                   . .              
    |                  .   .             
    |                  .   .             
  0-| .................    ............  
     ----------------------------------
    -40       8      55     103     150
 
*/
char OutsideTemp_warm (char __CRISP) 
  {
  if (__CRISP < 55) return(0);
  else 
    {
    if (__CRISP <= 68) return(((__CRISP - 55) * 19) + 4);
    else
      {
      if (__CRISP <= 78) return(255);
      else
        {
        if (__CRISP <= 82)
          return((( + 82 - __CRISP) * 63) + 1);
        else
          return(0);
        }
      }
    }
  }
/*      MEMBER hot  { 68 , 85, 150, 150 } */
/*
 
  1-|                      ............  
    |                      .          .  
    |                     .           .  
    |                    .            .  
  0-| ...................             .  
     ----------------------------------
    -40       8      55     103     150
 
*/
char OutsideTemp_hot (char __CRISP) 
  {
  if (__CRISP < 68) return(0);
  else 
    {
    if (__CRISP <= 85) return((__CRISP - 68) * 15);
    else
      {
        return(255);
      }
    }
  }
/*    } */
/*
 
     Fuzzy Sets for OutsideTemp
 
  1-| ................. ...............  
    | .             .  .. ..          .  
    | .              . .. ..          .  
    | .               .. . .          .  
  0-| .................... ............  
     ----------------------------------
    -40       8      55     103     150
 
 
*/
/*   LINGUISTIC room  TYPE char  MIN -40  MAX +150 */
/*   {   */
char  room ;
/*   MEMBER cold   { -40,   -40,  -8,  -5 } */
/*
 
  1-| ......                             
    | .    .                             
    | .     .                            
    | .     .                            
  0-| .     ...........................  
     ----------------------------------
    -40       8      55     103     150
 
*/
char room_cold (char __CRISP) 
  {
    {
    if (__CRISP <= -8) return(255);
    else
      {
      if (__CRISP <= -5)
        return(( - 5 - __CRISP) * 85);
      else
        return(0);
      }
    }
  }
/*   MEMBER normal { -3,  0,  3       } */
/*
 
  1-|        .                           
    |        .                           
    |        .                           
    |       ..                           
  0-| .................................  
     ----------------------------------
    -40       8      55     103     150
 
*/
char room_normal (char __CRISP) 
  {
  if (__CRISP < -3) return(0);
  else 
    {
    if (__CRISP <= 0) return((__CRISP + 3) * 85);
    else
      {
        {
        if (__CRISP <= 3)
          return(( + 3 - __CRISP) * 85);
        else
          return(0);
        }
      }
    }
  }
/*   MEMBER hot    { 0 , 8, 150 , 150 } */
/*
 
  1-|         .........................  
    |         .                       .  
    |         .                       .  
    |        .                        .  
  0-| ........                        .  
     ----------------------------------
    -40       8      55     103     150
 
*/
char room_hot (char __CRISP) 
  {
  if (__CRISP < 0) return(0);
  else 
    {
    if (__CRISP <= 8) return((__CRISP * 31) + 3);
    else
      {
        return(255);
      }
    }
  }
/*   } */
/*
 
     Fuzzy Sets for room
 
  1-| ...... ..........................  
    | .    . ..                       .  
    | .     ...                       .  
    | .     ..                        .  
  0-| .................................  
     ----------------------------------
    -40       8      55     103     150
 
 
*/
/*  CONSEQUENCE heat TYPE char MIN 0 MAX 255 DEFUZZ cg */
/*  ACTION { Heat_On = (heat >= 128); } */
/*  { */
char  heat ;
  int fa_heat, fc_heat;
/*    MEMBER OFF      {   0 } */
/*
 
  1-| .                                  
    | .                                  
    | .                                  
    | .                                  
  0-| *................................  
     ----------------------------------
      0      64     128     191     255
 
 
*/
void heat_OFF (char __DOM) 
  {
     fc_heat += __DOM;
     fa_heat += (__DOM * (0));
  }
/*    MEMBER ON       { 255 } */
/*
 
  1-|                                 .  
    |                                 .  
    |                                 .  
    |                                 .  
  0-| ................................*  
     ----------------------------------
      0      64     128     191     255
 
 
*/
void heat_ON (char __DOM) 
  {
     fc_heat += __DOM;
     fa_heat += (__DOM * (255));
  }
/*  } */
void fd_heat (void) 
  {
    if (fc_heat == 0)
     heat = 0;
    else
     heat = fa_heat / fc_heat;
   Heat_On = (heat >= 128); 
  }
/*  CONSEQUENCE ac TYPE char MIN 0 MAX 255 DEFUZZ cg */
/*  ACTION { AC_On = (ac >= 128); } */
/*  { */
char  ac ;
  int fa_ac, fc_ac;
/*    MEMBER OFF      {   0 } */
/*
 
  1-| .                                  
    | .                                  
    | .                                  
    | .                                  
  0-| *................................  
     ----------------------------------
      0      64     128     191     255
 
 
*/
void ac_OFF (char __DOM) 
  {
     fc_ac += __DOM;
     fa_ac += (__DOM * (0));
  }
/*    MEMBER ON       { 255 } */
/*
 
  1-|                                 .  
    |                                 .  
    |                                 .  
    |                                 .  
  0-| ................................*  
     ----------------------------------
      0      64     128     191     255
 
 
*/
void ac_ON (char __DOM) 
  {
     fc_ac += __DOM;
     fa_ac += (__DOM * (255));
  }
/*  } */
void fd_ac (void) 
  {
    if (fc_ac == 0)
     ac = 0;
    else
     ac = fa_ac / fc_ac;
   AC_On = (ac >= 128); 
  }
/*  FUZZY room_control */
void room_control (void)
  {
     fa_ac = 0;
     fc_ac = 0;
     fa_heat = 0;
     fc_heat = 0;
/*    { */
/*     IF room IS cold THEN */
/*           ac   IS OFF */
/*           heat IS ON */
  __IDOM[0] = room_cold(room) ;
  ac_OFF( __IDOM[0] );
  heat_ON( __IDOM[0] );
/*     IF room IS normal THEN */
/*            ac   IS OFF */
/*            heat IS OFF */
  __IDOM[0] = room_normal(room) ;
  ac_OFF( __IDOM[0] );
  heat_OFF( __IDOM[0] );
/*     IF room IS hot THEN */
/*            ac   IS ON */
/*            heat IS OFF */
  __IDOM[0] = room_hot(room) ;
  ac_ON( __IDOM[0] );
  heat_OFF( __IDOM[0] );
/*     IF room IS cold AND hours IS morning THEN */
/*            heat IS OFF */
  __IDOM[1] = room_cold(room) ;
  __IDOM[0] = hours_morning(hours) ;
  __IDOM[0] = F_AND(__IDOM[1],__IDOM[0]);
  heat_OFF( __IDOM[0] );
/*     IF room IS cold AND OutsideTemp IS hot THEN */
/*            heat IS OFF */
  __IDOM[1] = room_cold(room) ;
  __IDOM[0] = OutsideTemp_hot(OutsideTemp) ;
  __IDOM[0] = F_AND(__IDOM[1],__IDOM[0]);
  heat_OFF( __IDOM[0] );
/*     IF room IS hot AND OutsideTemp IS cold THEN */
/*            ac IS OFF */
  __IDOM[1] = room_hot(room) ;
  __IDOM[0] = OutsideTemp_cold(OutsideTemp) ;
  __IDOM[0] = F_AND(__IDOM[1],__IDOM[0]);
  ac_OFF( __IDOM[0] );
/*    } */
  fd_ac ();
  fd_heat ();
   }
// DOMtoCRISP
// This function takes a Degree of Membership value and 
// a full scale CRISP value and returns the CRISP 
// equivalent
 char DOMtoCRISP(char DOM, char CRISP)
  {
   unsigned long result;
   result = ( DOM * CRISP ) / (F_ONE - F_ZERO);
   return result;
  }
// convert the reading from an A/D to 
// the temerature in degres F
 char convert_temp (char temp)
   {
     return ((((long)temp * F_const) + F_offset) >> 8);
   }
 char room_temp,SPtemp,SBtemp;
 void main (void)
   {
    SPtemp = 71;     // This is the nominal room set point
                     // It can (should) be set interactively
                     // with a keypad and LCD display
    SBtemp = 3;      // Night setback temperature.
    Init_A2D();
    while(1)
      {
/*      room_temp is the current room temperature
        room is the error in the room temperature from 
             the desired setting. The room set point
             is the thermostat setting less the night setback
             if needed. The night setback is a linguistic
             variable that varies between fuzzy zero and fuzzy
             one. The transistion between day time temperature 
             and night time temperature is linear and smooth
             at the end of the evening.
*/
        room_temp = convert_temp(ReadAD(0));;
        room = DOMtoCRISP(hours_nightsb(hours),SBtemp);
        room = room_temp - SPtemp - room;
/*      room_control call a fuzzy function that 
        that evaluates all the control statements and 
        then activates two consequences either 
        airconditioning or furnace.
*/
        room_control();
       }
    }
