Projects MakingStuff Research Reference
Good SciFi and Fantasy How to Meditate Simple guide to growing mushrooms Grand Conspiracy Theories My debate style Zen FAQ Zen Center of Georgia Home
               


DutyCycle 2.0



//-----------------------------
// Long Cycle PWM Power Controller
//  Will allow a user to use a potentiometer to initialize a period (1/2 to 10 
//  seconds) during the initialization portion of execution.
//
//  Then during main loop, allows the user, using the same POT, to change the 
//  duty cycle within that period. This turns a SSR on and off accordingly.
//
//  This uses 'dutyCycleInput_SensorPin' as the sensor for the 10K POT input, 
//  which is used to a compute percent.
//
//  The output is on control pin ssrCtrlPin, to drive an a couple of sequential 
//  inverters, which will drive an SSR. The ledPin drives an LED to indicate 
//  when the duty cycle is active and power is on.
//
//  Duty cycles less than minDutyCycle, in milliseconds, drops the value to zero, 
//  to keep the SSR from cycling with too short a pulse width. It does the same 
//  thing when close to period, clamping to 100%.
//
//    Glenn Murray - 2/1/2019
//        Modified - 3/12/2019
//             Modified basic mechanism of algorithm, keeping general function
//             the same.
//        Modified - 3/19/2019
//             Added debug code to measure looping time (max loop period).
//
#include 
#include 

#include  

LCD4Bit_mod lcd = LCD4Bit_mod(2); 

// Constants
   // Pins
const int dutyCycleInput_SensorPin = A4; // select the input pin for the
                                         // potentiometer
const int ledPin = 13;      // select the pin for the LED
const int ssrCtrlPin = 12;  // select the pin for SSR control output signal

   // Sensor
const long sensorMin =   33; // Min sensor input * 100
                             // corrosponds to a 330 ohm resistor
const long sensorMax = 1023; // Max sensor input * 100
                             // corrosponds to the 10,000 ohm POT at  max plus 
                             // the 330 sensorMin.
const long sensorRange = sensorMax - sensorMin; // range of sensor input * 100

   // Time
const long periodMax = 10000; // Max period
const long periodMin = 500;  // Min period
const long periodRangeMax = periodMax - periodMin;  // range of period
const long padding = 130;     // milliseconds used to keep from letting the looping 
                              // go past the period.
const long initMinDutyCycle = 130;  // initial minimum SSR toggle time

   // Misc 
const char pc_sign = '%';;


// Variables

   // Display
char on[1];   // = "+";    // Effectively a constant string
char off[1];  // = "-";    // Effectively a constant string
char onval[1]; // Display string mirroring onState

char blanks[16] = "               ";  // Effectively a constant string, Used to
                                       // clear display buffer
char buff[16];


    // Time and State
boolean onState = false;  // Boolean to know if SSR is currently in an ON state. 
long period;       // cycle time - set in setup, then remains unchanged
long dutyCycle;    // what delay for duty cycle is - as integer
long minDutyCycle = initMinDutyCycle;  // minimum portion the SSR will be toggled
long percent;      // what % delay for duty cycle is - as integer
long last_percent; // Used to detect change in percent.

long periodStartTime;   // Time of start of this period
long changeStateTime;   // Time to toggle on/off state
long tempTime;          // temp use time variable

   // misc
int n;  // misc int

   // Debug
boolean debug = false;
long startLoopTime;// Time recorded at start of loop
long maxLoopTime = 0;  // Max time taken to get through loop


//-------------
// Setup code
//
void setup() {
  // put your setup code here, to run once:

  on[0]='+';
  off[0]='-';
  onval[0] = off[0];
  
  lcd.init();
  lcd.clear();

  int ndx = 0;
  last_percent = 150;
  dutyCycle = 0;

  // This loop is used to determine the cycle time
  //   After 2.5 seconds with no change, it will drop out of the loop, proceeding 
  //   to the main processing loop.
  //
  while (ndx < 25) {
     strcpy(buff, blanks);    // Clear buffer
     percent = inputVal();    // Read input, set the percent use
     n = sprintf(buff, " %4ld",percent);  // Write percent to buffer
     buff[n] = pc_sign;       // Add percent sign after percent value
    
     lcd.cursorTo(1,0);       // Set cursor to LCD first row
     lcd.printIn(buff);       // Write to LCD

     period = longPercent( percent, periodRangeMax ) + periodMin; 
                                                            // Compute cycle time
   
     n = sprintf(buff, " %4ld|%4ld ms", dutyCycle, period); // Write period to 
                                                            // display, dutyCycle 
                                                            // is zero at this point
     lcd.cursorTo(2,0);       // Set cursor to LCD second row
     lcd.printIn(buff);       // Write to LCD
     
     delay(100);              // Delay for 1/10th second

     // Change detection code, if change made, reset exit counter 
     ndx++;
     if (last_percent != percent) {
        if ((last_percent < percent-1) || // Fuzzy unequal 
            (last_percent > percent+1))   // detect change if not sensor jiggle
           ndx = 0;
     }
     last_percent = percent;
  }

  // Init for main loop
  lcd.clear();
  last_percent = 150;    // Init main loop so will compute delay times
  minDutyCycle = (padding > minDutyCycle) ? padding : minDutyCycle; //???? Review Logic
  changeState(( (long) millis()) + period-dutyCycle, false);
}

//-------------
// Main processing Loop
//
void loop() {
 
   if (debug) 
       startLoopTime = (long) millis();
   
   strcpy(buff, blanks);  // Clear buffer at start of loop
   buff[15] = ' ';
   percent = inputVal();  // Input percent of period to compute dutyCycle

   // Check for change of state
   if (percent != last_percent) {
      dutyCycle = longPercent( percent, period);       // Compute dutyCycle

      // Make certain either active or inactive portion not less than minDutyCycle
      if (dutyCycle < minDutyCycle) {  // If less than minimum, set to zero
         dutyCycle = 0;
         percent = 0;
      } 
      else if (dutyCycle != period && dutyCycle > (period-minDutyCycle) ) {
         dutyCycle = period;
         percent = 100;
      }
      last_percent = percent;
   }
 

   // Check for end of current state
   //
   if (changeStateTime < ( ((long) millis()) - padding) ) { // If change is immenient

      if (changeStateTime > ((long)millis())) {           // Within padding window
          delay(changeStateTime - ((long) millis()));
          // Run out the clock on this state
      }

      if (onState) {  // Reset state
         if (period-dutyCycle > padding) { // Ensure dutyCycle not 100% on
             changeState(( (long) millis()) + period-dutyCycle, false);
         }
         else {  // Else keep same state
             changeState(( (long) millis()) + dutyCycle, true);
         }
      }
      else {
         if (dutyCycle > padding) {    // Ensure dutyCycle not 0
            changeState(( (long) millis()) + dutyCycle, true);
         }
         else {  // Else keep same state
            changeState(( (long) millis()) + period, false);
         }
      }
   }      


   // Update Display
   //
   lcd.cursorTo(1,0);   // Set line 1 write position
   n = sprintf(buff, " %4ld",percent); // format data
   buff[n] = pc_sign;   // Add state symbol
   buff[14] = onval[0];
   lcd.printIn(buff);   // Write into display

// Writes a plus or minus value to LCD depending on if time section is on or off
//   lcd.cursorTo(1,14);  // Set to write on first row
//   lcd.printIn(onval);

   lcd.cursorTo(2,0);   // Set line 2 write position

   // Debug
   if (debug) {
      tempTime = ((long) millis()) - startLoopTime;                           // Loop transit time
      maxLoopTime = (maxLoopTime < tempTime) ? tempTime : maxLoopTime; // max loop transit time
      n = sprintf(buff, " %4ld|%4ld ms", tempTime, maxLoopTime);     // format data
   }
   else {
      n = sprintf(buff, " %4ld|%4ld ms", dutyCycle, period);  // format data
   }
   lcd.printIn(buff);   // Write into display

   
}


//-------------
// longPercent
// Computes percent. Takes number between 0 and 100, then uses that as 
// percentage to compute the percent of value to return
long longPercent(long per, long val) {
     return (val * per / 100);
}


//-------------
// inputVal
// Reads sensor, returns number between 0 and 100 
long inputVal() {
  long sensorValue = (analogRead(dutyCycleInput_SensorPin)- sensorMin) * 100;
  long rv =  (sensorValue / sensorRange);
  return (rv);
}

//-------------
// changeState
// This sets the output to on or off, depending on 'lit' parameter and resets the 
// onState global var. It then sets the stateChangeTime to 'newTime' parameter value.
void changeState( long newTime, boolean lit) {

      uint8_t sigVal;      // Output signal variable

      if (lit) {
        onval[0] = on[0];
        sigVal = HIGH;
      }
      else {
        onval[0] = off[0];
        sigVal = LOW;
      }

      // Writes a plus or minus value to LCD depending on if time section is on or off
      lcd.cursorTo(1,14);  // Set to write on first row
      lcd.printIn(onval);

      // Turn LED/SSR on/off and set new stateChangeTime
      digitalWrite(ssrCtrlPin, sigVal);  // Sends SSR output value
      digitalWrite(ledPin, sigVal);      // Sends LED output value  
      changeStateTime = newTime;
      onState = lit;
}