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