Yummy's DIY Burst fire controller project
Moderator: Site Moderator
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
I have tested the controller with my 2600 W Boiler and it worked as intended.
I had no flickering lights in the house nor other side effects with this 2600 W power.
However my existing Power meters indication started jumping around with power settings below 50%. This power meter can properly measure the distorted waveforms from usual SSR power controllers, but it gives up with burstfire. It seems that the integration period of the meter is just too short to deal with the 2 seconds long power pattern of the burst fire.
I could easily tear the Arduino controller apart and return the to my existing SSR controller without burst fire feature and treat this build as an interesting exercise.
However, the Arduino power controller opens the door for implementing additional features.
Automatic heating power reduction, shortly before the boiler starts producing vapor is one example.
So I decided to keep that burstfire power controller. That decision requires me to build a power meter that is able to deal with the burstfire pattern.
No surprise - Yummy has already developed one. The next project on my todo list.
A minute ago, I tried to control one of my air ventilators with burstfire.
Normal SSR controllers will not be very good in changing the speed of those ventilators. At low power settings, they just stop movement.
It is different with burstfire. The ventilator rotates slowly even with 1% power setting. Looks a bit weird because with very low speed, it moves in steps. Interesting.
Certainly useless, nevertheless interesting.
I had no flickering lights in the house nor other side effects with this 2600 W power.
However my existing Power meters indication started jumping around with power settings below 50%. This power meter can properly measure the distorted waveforms from usual SSR power controllers, but it gives up with burstfire. It seems that the integration period of the meter is just too short to deal with the 2 seconds long power pattern of the burst fire.
I could easily tear the Arduino controller apart and return the to my existing SSR controller without burst fire feature and treat this build as an interesting exercise.
However, the Arduino power controller opens the door for implementing additional features.
Automatic heating power reduction, shortly before the boiler starts producing vapor is one example.
So I decided to keep that burstfire power controller. That decision requires me to build a power meter that is able to deal with the burstfire pattern.
No surprise - Yummy has already developed one. The next project on my todo list.
A minute ago, I tried to control one of my air ventilators with burstfire.
Normal SSR controllers will not be very good in changing the speed of those ventilators. At low power settings, they just stop movement.
It is different with burstfire. The ventilator rotates slowly even with 1% power setting. Looks a bit weird because with very low speed, it moves in steps. Interesting.
Certainly useless, nevertheless interesting.
- Yummyrum
- Global moderator
- Posts: 8641
- Joined: Sat Jul 06, 2013 2:23 am
- Location: Fraser Coast QLD Aussie
Re: Yummy's DIY Burst fire controller project
That's great to hear ....and I'm very happy you have no light flickers.kennstminet wrote: ↑Wed Jul 03, 2024 5:34 am I have tested the controller with my 2600 W Boiler and it worked as intended.
I had no flickering lights in the house nor other side effects with this 2600 W power.
Absolutelykennstminet wrote: ↑Wed Jul 03, 2024 5:34 am However, the Arduino power controller opens the door for implementing additional features.
Automatic heating power reduction, shortly before the boiler starts producing vapor is one example.
![Thumbup :thumbup:](./images/smilies/icon_thumbup.gif)
Once you can program the "knob" the worlds your Oyster
![Very Happy :ebiggrin:](./images/smilies/icon_e_biggrin.gif)
Just remember that that Meter will only work at this stage on 0-100% where the Frame rate is 2 seconds .kennstminet wrote: ↑Wed Jul 03, 2024 5:34 am No surprise - Yummy has already developed one. The next project on my todo list.
It would require a 20 second frame rate to work on 0-1000 , but the loop counter seems to be a signed integer with a max of 32,767 so it might need a total re-Jig to work . I also wonder if 20 seconds is too long for an update . Regardless , it works on Aubers with 0-100%
That is coolkennstminet wrote: ↑Wed Jul 03, 2024 5:34 am I tried to control one of my air ventilators with burstfire.
Normal SSR controllers will not be very good in changing the speed of those ventilators. At low power settings, they just stop movement.
It is different with burstfire. The ventilator rotates slowly even with 1% power setting. Looks a bit weird because with very low speed, it moves in steps. Interesting.
Certainly useless, nevertheless interesting.
![Thumbup :thumbup:](./images/smilies/icon_thumbup.gif)
My recommended goto .
https://homedistiller.org/wiki/index.ph ... ion_Theory
https://homedistiller.org/wiki/index.ph ... ion_Theory
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
Yummy
I'm trying to synchronize the TRMS power meter with the burstfire controller.
For this reason, I would like to set an unused digital output of the controller to high when the 2 secs period starts and reset to zero when the period has finished.
Could you suggest where I could insert code for this function?
I'm trying to synchronize the TRMS power meter with the burstfire controller.
For this reason, I would like to set an unused digital output of the controller to high when the 2 secs period starts and reset to zero when the period has finished.
Could you suggest where I could insert code for this function?
- Yummyrum
- Global moderator
- Posts: 8641
- Joined: Sat Jul 06, 2013 2:23 am
- Location: Fraser Coast QLD Aussie
Re: Yummy's DIY Burst fire controller project
A little preoccupied at work ATM , but on my mind .
Will get back to you soon Kenn
Initial thoughts are the reseting of the frame counter ( as the Frame counter is counting 100 cycles ) and it would need to be as close to absolute Zero cross point as possible ( for accurate TRMS measure of a full 2 Cycles )
I think your Optocoupler had that point nailed . My half wave rectified is not near the crossing point so interrupt timing , although it works for the controller, is not aligned for TRMS
Will get back to you soon Kenn
Initial thoughts are the reseting of the frame counter ( as the Frame counter is counting 100 cycles ) and it would need to be as close to absolute Zero cross point as possible ( for accurate TRMS measure of a full 2 Cycles )
I think your Optocoupler had that point nailed . My half wave rectified is not near the crossing point so interrupt timing , although it works for the controller, is not aligned for TRMS
My recommended goto .
https://homedistiller.org/wiki/index.ph ... ion_Theory
https://homedistiller.org/wiki/index.ph ... ion_Theory
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
Never mind. I will test your hint with the framecounter in the meanwhile.
I have added two lines to the ISR. It need to be tested, haven't done that yet.
I have added two lines to the ISR. It need to be tested, haven't done that yet.
Code: Select all
//########Interrupt Service Routine for the zerocrossing detector ##############
ISR (PCINT0_vect) { // ISR for pin change on port B (Jump to interupt code when Zerocrossing detected )
if (digitalRead(zerocross) == true){
return; // is it a rising edge, then do nothing and returnto loop
}
// only falling edges shall trigger the following steps
//Turn cycle on or off as determined by previous ISR calculation, triggered by the previous falling zerocrossing
digitalWrite(ledPin, setoutput);
//Calculate state of "setoutput" for use after the next falling zerocrossing.
//This state will be based on the Patterns calculated in Main loop, using the desired percentage of power.
if (Frame_counter >= (Maximum+1)) {
digitalWrite(syncPin, 1); // set sync signal to 1 when frame counter starts
Frame_counter = 1;
PttnCnt_1st = 0;
PttnCnt_2nd = 0;
PttnCnt_3rd = 0;
PttnCnt_4th = 0;
PttnCnt_5th = 0;
setoutput = false;
}
Frame_counter++;
PttnCnt_1st++; // inc Pttn_1st counter
if (Frame_counter = Maximum ){digitalWrite(syncPin, 0);} // set sync signal back to 0 when counter is full
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
The synchronization works.
It seems that the TRMS power meter indication is more stable when synchronized. I will give more details in the power meter topic.
I noticed that the burstfirecontroller has an idle period of 40ms between two 2 sec. periods. However, I have not analyzed what exactly is causing that.
Now for something completely different ......
The controller could work as a real power regulator, if the TRMS power meter would be monitoring the voltage changes and telling the controller to increase or decrease the percent setting accordingly.
Will take a while. There is still some work in progress with the power meter.
It seems that the TRMS power meter indication is more stable when synchronized. I will give more details in the power meter topic.
I noticed that the burstfirecontroller has an idle period of 40ms between two 2 sec. periods. However, I have not analyzed what exactly is causing that.
Now for something completely different ......
The controller could work as a real power regulator, if the TRMS power meter would be monitoring the voltage changes and telling the controller to increase or decrease the percent setting accordingly.
Will take a while. There is still some work in progress with the power meter.
Re: Yummy's DIY Burst fire controller project
That would be really useful!
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
Such power regulator would have a limitation.
The burstfirecontroller (nor any controller) cannot amplify the mains voltage, it can only reduce it.
If the mains voltage at a certain location would vary between 210V and 250V, the regulator would keep the voltage limited to 210V and prevent it from increasing. This would keep the voltage stable (and the power stable), however, at the level of the lowest expected voltage.
If the mains voltage would sink below that expected value, the regulator could not amplify it. The voltage would be lower, resulting in a lower power.
Not sure if this makes sense to a "non electrical" person.
The burstfirecontroller (nor any controller) cannot amplify the mains voltage, it can only reduce it.
If the mains voltage at a certain location would vary between 210V and 250V, the regulator would keep the voltage limited to 210V and prevent it from increasing. This would keep the voltage stable (and the power stable), however, at the level of the lowest expected voltage.
If the mains voltage would sink below that expected value, the regulator could not amplify it. The voltage would be lower, resulting in a lower power.
Not sure if this makes sense to a "non electrical" person.
- Yummyrum
- Global moderator
- Posts: 8641
- Joined: Sat Jul 06, 2013 2:23 am
- Location: Fraser Coast QLD Aussie
Re: Yummy's DIY Burst fire controller project
I agree that syncing the power meter should give better results . Glad to hear it worked and is more stable .
Not sure about the missing 40ms ? Can’t say I noticed that .
You are correct about controller not being able to create a higher voltage . I’d hazard a guess that most folk would only be interested in “Regulated” power during a spirit run during which time most are running at quite a bit less than full power . So the Regulator should in most cases be able to supply a stable power even at the minimum dip in Mains supply Voltage .
And obviously when Stripping you just turn it up as much as it will give you .
All sorts of potential here to warn operator how close they are getting to the maximum available power .
Good work Kenn![Thumbup :thumbup:](./images/smilies/icon_thumbup.gif)
Not sure about the missing 40ms ? Can’t say I noticed that .
You are correct about controller not being able to create a higher voltage . I’d hazard a guess that most folk would only be interested in “Regulated” power during a spirit run during which time most are running at quite a bit less than full power . So the Regulator should in most cases be able to supply a stable power even at the minimum dip in Mains supply Voltage .
And obviously when Stripping you just turn it up as much as it will give you .
All sorts of potential here to warn operator how close they are getting to the maximum available power .
Good work Kenn
![Thumbup :thumbup:](./images/smilies/icon_thumbup.gif)
My recommended goto .
https://homedistiller.org/wiki/index.ph ... ion_Theory
https://homedistiller.org/wiki/index.ph ... ion_Theory
- shadylane
- Master of Distillation
- Posts: 11274
- Joined: Sat Oct 27, 2007 11:54 pm
- Location: Hiding In the Boiler room of the Insane asylum
Re: Yummy's DIY Burst fire controller project
Good point, if higher voltage is needed, then a variac becomes a logical option.kennstminet wrote: ↑Fri Jul 26, 2024 11:39 am
The burstfirecontroller (nor any controller) cannot amplify the mains voltage, it can only reduce it.
It's based on an autotransformer, so the output voltage can be higher than the input.
Just like every controller, a small fan makes a big difference.
![Laughing :lol:](./images/smilies/icon_lol.gif)
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
I will look a bit deeper into it and let you know. It is not a problem. I just made the observation.
How much power could the variac handle, you are thinking of?
I own a very old and very large one from the early days of color TV repairs and it's limited to 1500Watts.
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
Yummy
The 40 millisecs is a non issue. It is just related to my incorrect implementation of the sync signal.
Will report details once it is fixed.
The 40 millisecs is a non issue. It is just related to my incorrect implementation of the sync signal.
Will report details once it is fixed.
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
I guess Ivedunnit.
Here is my extension of Yummy's project for the Arduino R3 with added Power display, mains voltage display and power stabilization function.
A toggle switch is needed to turn the stabilization function on and if. It provides Ground to digital pin 6 to deactivate stabilization, or is open circuit to turn it on.
I have used that OLED Display and connected it for the SPI bus which works faster than the I2C interface:
https://www.ebay.de/itm/285819329121?mk ... media=COPY
This is how the displayed data looks like:
With stabilization switched on With stabilization switched off I used a small transformer with bridge rectifier as a voltage sensor for the mains voltage.
Here is a sketch of the transformerless zerocrossing detector I'm using.
Here is the .ino file. I have added a .pdf extension to allow uploading to HD. Please remove the .pdf ending after downloding to mak it a .ino file again.
This is how the code looks like:
This is the complete wiring schematic:
Please feel free to suggest corrections or ask questions.
Here is my extension of Yummy's project for the Arduino R3 with added Power display, mains voltage display and power stabilization function.
A toggle switch is needed to turn the stabilization function on and if. It provides Ground to digital pin 6 to deactivate stabilization, or is open circuit to turn it on.
I have used that OLED Display and connected it for the SPI bus which works faster than the I2C interface:
https://www.ebay.de/itm/285819329121?mk ... media=COPY
This is how the displayed data looks like:
With stabilization switched on With stabilization switched off I used a small transformer with bridge rectifier as a voltage sensor for the mains voltage.
Here is a sketch of the transformerless zerocrossing detector I'm using.
Here is the .ino file. I have added a .pdf extension to allow uploading to HD. Please remove the .pdf ending after downloding to mak it a .ino file again.
This is how the code looks like:
Code: Select all
/*########################################################################################
The sketch is based on the one created by Yummyrum and posted at HD
June 25th, 2025 https://homedistiller.org/forum/viewtopic.php?p=7787168#p7787168
and is modified by kennstminet August 2024 filename Burstfire_5_Stab_4.ino
- Removed LCD code, added code for use of an 1.3 inch OLED display (SH1106) with SPI interface.
I had tested with a similar OLED with I2C interface and found that I2C is too slow and it was disturbing the burstfire timing.
- Introduced a transformerless voltage sensor for detecting the mains voltage zerocrossing
- Added an optional synchronization output to allow syncing the timing with Yummyrums TRMS power meter.
I found that such sycronization is not giving appreciable benefits. I left the code active, in case somebody needs it.
The sync output is set to LOW at start of the Frame_counter and returns to HIGH 100 millisecs before the Frame_counter is full
- Removed the code for writing the pattern to the SSR from the ISR and made a regular function for it.
Now, the ISR just sets a variable at zerocrossing and then returns to loop to avoid blocking the microcontroller for full 2 seconds.
- Added the "volatile" attribute to the variable used in the ISR
- Added code for measuring the current mains voltage (from the wall outlet). An external voltage sensor is needed for translating the
mains AC voltage to a 0 to 5VDC signal to the respective analog input of the arduino.
- Added code to provide a means of power stabilization. It compares the actual mains voltage with a preset
"lowest expected mains voltage" and reduces the selected power accordingly if the actual mains voltage is higher than the lower limit.
The max. available stabilized power is therefor lower than the power in unstabilized mode.
However, it will remain stable, when the mains voltage is variing as long it is higher than the lowr limit.
- Added an digital input for a external switch to turn on and off the stabilization function.
##########################################################################################*/
#include <Arduino.h> // I have no idea why this library is used here, so left it
#include <U8x8lib.h> // library for the OLED
#include <SPI.h> // for the OLED SPI interface
#include <math.h> // for mathematical rounding function "roundf()2
#include "elapsedMillis.h" // for creating a timer for the measurement duration
U8X8_SH1106_128X64_NONAME_4W_HW_SPI u8x8(/* cs=*/10, /* dc=*/9, /* reset=*/8); //creates an OLED object "u8x8"
elapsedMillis timeElapsed1; // timer object for reading the analog inputs
elapsedMillis timeElapsed2; // timer object for printing data
unsigned int interval1{ 10 }; // Timer interval for reading the analog inputs
unsigned int interval2{ 10 }; // Timer interval for printing data
const float mainsVoltage_low{ 210 }; // the lowest voltage, the stabilizer should be able to compensate
const float elementResistance{ 23 }; // Constant holding the resistance of the heating element in Ohms.
const float calMains{ 0.2574 }; // Constant holding the calibration factor for the mains voltage measurement
const byte ssrPin{ 7 }; // SSR drive pin (output)
const byte zerocross{ 12 }; // ISR zerocross trigger pin (input)
const byte syncPin{ 5 }; // defines pin for the sync output to the TRMS power meter
const byte sensePin{ A2 }; // Analog Pin A2 reads voltage from mains plug
const byte stabOnPin{ 6 }; // input for an external on/off switch for turning stabilization on or off.
float mainsVoltage_actual{}; // Variable holding the mains voltage
float mainsAccumulator{}; // used for calculating the moving average mains voltage
int unsigned numberOfSamples{ 150 }; // used for calculating the moving average mains voltage
int unsigned sampleCounter{}; // used for calculating the moving average mains voltage
float powerMax_actual{}; // the potential power resulting from actual mains voltage and 100 percent setting
float powerMax_low{}; // the potential power resulting from the lowest expected mains voltage and 100 percent setting
float powerStabilized{}; // the actual stabilized power resulting from the selected percent setting and the lowest expected mains voltage
float powerSelected_actual{}; // the power resulting from actual mains voltage and selected percent setting
float powerSelected_low{}; // the power resulting from lowest expected mains voltage and selected percent setting
float ratio_powerMax{}; // ratio for adjusting the selected Percent for stabilizing
float percentStabilized{}; // the selected percent, adjusted for stabilizing
bool stabilizationOn{ false }; // holding the status of the stab. on/off switch. True when stabilization is on.
bool setoutput{ false }; // SSR turns on when true, used in writePattern
volatile bool outputActive{ false }; // For use in the ISR and writePattern routines
// ISR sets to true after zerocrossing falling edge,
//writePattern sets to false after patternCounter is full.
int Maximum{ 100 }; //Maximum power IE 100% or 1000%
int Per_maximum{ 1 }; //selected percent setting Power % of the maximum value
int Pttn_1st{}; //1st Cycle Pattern }
int Pttn_2nd{}; //2nd Cycle Pattern }
int Pttn_3rd{}; //3rd Cycle Pattern } These are calculated in the createPattern() routine and
int Pttn_4th{}; //4th Cycle Pattern } used to turn cycles on or off in the writePattern() routine
int Pttn_5th{}; //5th Cycle Pattern }
int Reps_1st{};
int Skip_diff{};
int skip_diff_3{};
int skip_diff_4{};
int Skip{};
int Keep_skip{};
int Loose_keep_skip{};
int Restore_Loose_keep_skip{};
int Frame_counter{ 0 }; //Counter used in writePattern() routine. Each Mains cycle triggers ISR
int PttnCnt_1st{ 0 }; //PatternCounters used in writePattern()
int PttnCnt_2nd{ 0 }; //
int PttnCnt_3rd{ 0 }; //
int PttnCnt_4th{ 0 }; //
int PttnCnt_5th{ 0 }; //
// function prototype declarations
void create_pattern(); // function to calculate the pulse pattern, based of the required percentage value.
void writePattern(); // function for writing the pulse pattern to the SSR after each mains voltage zerocrossing
//##################################################################################################################
void setup() {
Serial.begin(115200); // activate Serial Monitor
u8x8.begin(); // activate the OLED on SPI
u8x8.setPowerSave(0); // not sure if this is needed
pinMode(syncPin, OUTPUT); // output for optional sync signal to TRMS power meter
pinMode(ssrPin, OUTPUT); // Setup SSR output pin
pinMode(zerocross, INPUT_PULLUP); // Setup Zerocross input pin
digitalWrite(syncPin, true); // set sync signal to true till framecounter starts
pinMode(stabOnPin, INPUT_PULLUP); // Setup stabilization on/off input pin
// Set Pin Change Interrupt Control Register and Pin Change Mask
// to allow interrupt driven response to the zero crossing detector
// connected to pin D12. (|= is the "compound bitwise or" operator)
PCICR |= B00000001; // Select port B in the Pin Change Interrupt Control Register
PCMSK0 |= B00010000; // Enable D12 pin in the Pin Change Mask 0
powerMax_low = sq(mainsVoltage_low) / elementResistance; // the power resulting from the lowest expected
// mains voltage and 100 percent setting
}
//##################################################################################################################
void loop() {
writePattern(); // calls the routine to write the pulse pattern to the SSR
// sets and resets outputActive based on zerocrossing and frameCounter
if (outputActive == false) { // calls the routine to calculate the pulse pattern
// only after writePattern() has reset outputActive
createPattern();
}
if (outputActive == false && timeElapsed1 > interval1) { // read analog inputs only when not writing the pattern and
// after a certain interval has passed
Per_maximum = map(analogRead(A0), 0, 1023, 1, Maximum); // read and store potentiometer value as the selected Percent setting
if (sampleCounter <= (numberOfSamples - 1)) { // read actual mains voltage and calculate moving average
mainsAccumulator = mainsAccumulator + analogRead(sensePin);
sampleCounter++;
} else {
mainsVoltage_actual = (mainsAccumulator / numberOfSamples) * calMains;
mainsAccumulator = 0;
sampleCounter = 0;
}
powerMax_actual = sq(mainsVoltage_actual) / elementResistance; // the potential power resulting from actual mains voltage and 100 percent setting
powerSelected_actual = powerMax_actual * Per_maximum / 100; // the power resulting from actual mains voltage and selected percent setting
powerSelected_low = powerMax_low * Per_maximum / 100; // the power resulting from lowest expected mains voltage and selected percent setting
ratio_powerMax = powerMax_low / powerMax_actual; // ratio for adjusting the selected Percent for stabilizing
if (ratio_powerMax > 1) { // do not increase selected percent when actual mains is
// lower than the lower limit
ratio_powerMax = 1;
}
percentStabilized = Per_maximum * ratio_powerMax; // the selected percent, corrected for stabilizing
powerStabilized = powerMax_actual * percentStabilized / 100; // the actual stabilized power resulting from the selected percent setting
// and the lowest expected mains voltage
timeElapsed1 = 0;
}
if (outputActive == false && timeElapsed2 > interval2) { // print data only when not writing the pattern and
// after a certain interval has passed
stabilizationOn = digitalRead(stabOnPin); // read status of stabilization on/off switch and
// change the display layout accordingly
u8x8.setFont(u8x8_font_px437wyse700a_2x2_r); // set large font
u8x8.setCursor(0, 0);
u8x8.print(Per_maximum);
u8x8.print("% ");
u8x8.setFont(u8x8_font_chroma48medium8_r); // set small font for "stab" and "on" or "off"
u8x8.setCursor(9, 0);
u8x8.print("Stab");
u8x8.setCursor(9, 1);
if (stabilizationOn == true) {
u8x8.print("On");
} else {
u8x8.print("Off");
}
u8x8.setFont(u8x8_font_px437wyse700a_2x2_r); // set large font for the power display
u8x8.setCursor(0, 2);
if (stabilizationOn == true) {
u8x8.print(roundf(powerStabilized));
u8x8.print(" W ");
} else {
u8x8.print(roundf(powerSelected_actual));
u8x8.print(" W ");
}
if (outputActive == true) return;
u8x8.setFont(u8x8_font_chroma48medium8_r); // set small font for the voltage readouts
u8x8.setCursor(0, 5);
u8x8.print(roundf(mainsVoltage_actual));
u8x8.print(" V actual");
if (outputActive == true) return;
u8x8.setCursor(0, 6);
u8x8.print(mainsVoltage_low, 0);
u8x8.print(" V stab");
Serial.print(Per_maximum);
Serial.print(" Percent");
Serial.print(" Mains:");
Serial.print(mainsVoltage_actual);
Serial.print(" powerMax_actual:");
Serial.print(powerMax_actual);
Serial.print(" powerStabilized:");
Serial.println(powerStabilized);
timeElapsed2 = 0;
}
} // loop end
//##############################################################################
//###########Function for writing the calculated pattern to the SSR ############
//###########and setting and resetting the outputActive flag ###################
void writePattern() {
if (outputActive == true) { // set by the ISR after each falling zerocrossing
digitalWrite(ssrPin, setoutput); //Turn SSR on or off as determined by previous pattern calculation
outputActive = false; // reset the outputActive flag to prevent from blocking the other activities in the loop
//Calculate state of "setoutput" for use after the next falling zerocrossing.
//This state will be based on the Patterns calculated in Main loop, using the desired percentage of power.
if (Frame_counter == Maximum + 1 - 5) { digitalWrite(syncPin, 1); } // set sync signal to HIGH after 95 frames
// (100 millisecs before next zerocrossing)
if (Frame_counter >= (Maximum + 1)) {
digitalWrite(syncPin, 0); // set sync signal to LOW when frame counter starts
// in order to allow optional start of the TRMS power meter
Frame_counter = 1;
PttnCnt_1st = 0;
PttnCnt_2nd = 0;
PttnCnt_3rd = 0;
PttnCnt_4th = 0;
PttnCnt_5th = 0;
setoutput = false;
}
Frame_counter++;
PttnCnt_1st++; // inc Pttn_1st counter
if (PttnCnt_1st < (Pttn_1st)) // ie. Pttn_1st is 2 when 37% power is selected
{
setoutput = false; //Turn off SSR if pttn_1st hasn't arrived
return; // and return to loop
}
setoutput = true; // Turn on SSR as PttnCnt_1st has reached pttn_1st (ie. 2 for 37%)
PttnCnt_1st = 0; // Reset Pttn_1st counter
PttnCnt_2nd++; // inc Skip counter
if (Pttn_2nd == 0) // if no pulses need to be skipped
{
setoutput = true; // leave it turned on
return; // and return to loop
}
if (PttnCnt_2nd < (Pttn_2nd)) // ie. Pttn_2nd is 3 when 37% power is selected
{
setoutput = true; // keep SSR on if not Skip rate hasn't arrived
return;
}
setoutput = false; //Turn off SSR as Skip rate has arrived
PttnCnt_2nd = 0;
PttnCnt_3rd++; //inc Keepskip counter
if (Pttn_3rd == 0) {
setoutput = false;
return;
}
if (PttnCnt_3rd < (Pttn_3rd)) {
setoutput = false;
return;
}
setoutput = true;
PttnCnt_3rd = 0;
PttnCnt_4th++;
if (Pttn_4th == 0) {
setoutput = true;
return;
}
if (PttnCnt_4th < (Pttn_4th)) {
setoutput = true; // keep SSR on if not Skip rate hasn't arrived
return;
}
setoutput = false; //Turn off SSR as Skip rate has arrived
PttnCnt_4th = 0;
PttnCnt_5th++;
if (Pttn_5th == 0) {
setoutput = true;
return;
}
if (PttnCnt_5th < (Pttn_5th)) {
setoutput = false;
return;
} // keep SSR on if not Skip rate hasn't arrived
setoutput = true; //Turn on SSR as Skip rate has arrived
PttnCnt_5th = 0;
}
}
//#############calculate the pulse pattern#########################
void createPattern() {
if (Per_maximum == 0) Pttn_1st = 0; //Pttn_1st is first fill
else Pttn_1st = Maximum / Per_maximum; //ie turn on every 2nd cycle if 37% is desired 100/37=2
if (Pttn_1st == 0) Reps_1st = 0;
else Reps_1st = (Maximum / Pttn_1st); // i.e 100/2=50 pulsed per cycle
Skip = Reps_1st - Per_maximum; // Pttn_2nd runs through first fill and removes certain cycles IE, every 5th
// i.e for 37% 50-37 = 13
if (Skip == 0) Pttn_2nd = 0;
else Pttn_2nd = (Reps_1st / Skip); // i.e for 37% 50/13=3
if (Pttn_2nd == 0) Skip_diff = 0;
else Skip_diff = (Reps_1st / Pttn_2nd); // i.e for 37% 50/3=16 pulses out of 50 shall be skipped
Keep_skip = Skip_diff - Skip; // Pttn_3rd turns back on cycles switched off by Skip-rate
// to correct missing cycles i.e for 37% 16-13=3
if (Keep_skip == 0) Pttn_3rd = 0;
else Pttn_3rd = (Skip_diff / Keep_skip); // i.e for 37% 16/3=5
if (Pttn_3rd == 0) skip_diff_3 = 0;
else skip_diff_3 = Skip_diff / Pttn_3rd; // i.e for 37% 16/5=3
Loose_keep_skip = skip_diff_3 - Keep_skip; //Pttn_4th turns off required cycles after Pttn_3rd run
// i.e for 37% 3-3=0
if (Loose_keep_skip == 0) Pttn_4th = 0;
else Pttn_4th = skip_diff_3 / Loose_keep_skip; // i.e for 37% 3/3=1
if (Pttn_4th == 0) skip_diff_4 = 0;
else skip_diff_4 = skip_diff_3 / Pttn_4th;
Restore_Loose_keep_skip = skip_diff_4 - Loose_keep_skip; //Pttn_5th turns on required cycles after Pttn_4rd run
if (Restore_Loose_keep_skip == 0) Pttn_5th = 0;
else Pttn_5th = skip_diff_4 / Restore_Loose_keep_skip;
}
//########Interrupt Service Routine for the zerocrossing detector ##############
ISR(PCINT0_vect) // ISR for pin change interrupt on port B pin 12 (Zerocrossing detected )
{
if (digitalRead(zerocross) == false) { // react only when pinchange was falling edge
// otherwise do nothing and return
outputActive = true; // enable the routine for writing the output pattern
// to the SSR with any falling edge
}
}
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
Here is Rev. 1 of the schematic. I had omitted a diode at the optocoupler of the zero crossing detector.
- Black Bull
- Novice
- Posts: 88
- Joined: Fri Aug 16, 2024 11:54 pm
- Location: The Brew Room
Re: Yummy's DIY Burst fire controller project
Yummy, read this as I want to upgrade my controller....
Now my head just hurts and I've gone cross eyed![Wtf? :wtf:](./images/smilies/icon_wtf.gif)
Now my head just hurts and I've gone cross eyed
![Wtf? :wtf:](./images/smilies/icon_wtf.gif)
I'm not fat, I'm in shape....Round is a shape
Getting old has whiskers on it !
Getting old has whiskers on it !
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
Other error corrections are needed in my project.
I had swapped pins 4 and 5 at optocoupler U1 and used an incorrect symbol.
Here is Rev.2 of the schematic:
There was a glitch in the code for printing to the OLED. It caused the Arduino to freeze when 100% was selected with the potentiometer while the Stabilization was set to on.
The erroneous code is
The corrected code is
The difference is not obvious. The line includes 4 spaces after the W.
The corrected code used only 3 spaces.
Here is the corrected .ino file (remove the fake .pdf ending of the filename ofter download.
I had swapped pins 4 and 5 at optocoupler U1 and used an incorrect symbol.
Here is Rev.2 of the schematic:
There was a glitch in the code for printing to the OLED. It caused the Arduino to freeze when 100% was selected with the potentiometer while the Stabilization was set to on.
The erroneous code is
Code: Select all
if (stabilizationOn == true) {
u8x8.print(roundf(powerStabilized));
u8x8.print(" W ");
} else {
u8x8.print(roundf(powerSelected_actual));
u8x8.print(" W ");
}
Code: Select all
if (stabilizationOn == true) {
u8x8.print(roundf(powerStabilized));
u8x8.print(" W ");
} else {
u8x8.print(roundf(powerSelected_actual));
u8x8.print(" W ");
}
Code: Select all
u8x8.print(" W ");
The corrected code used only 3 spaces.
Here is the corrected .ino file (remove the fake .pdf ending of the filename ofter download.
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
I found another error in my code.
In line 161, the following line must be inserted:
Otherwise only the display showed the reduced, stabilized power, however the heating element was still supplied with the unstabilized power.
Sorry!!
Here is the code:
Remove the .pdf file ending after download.
In line 161, the following line must be inserted:
Code: Select all
if(stabilizationOn == true) Per_maximum = percentStabilized;
Sorry!!
Here is the code:
Remove the .pdf file ending after download.
Code: Select all
/*########################################################################################
The sketch is based on the one created by Yummyrum and posted at HD
June 25th, 2025 https://homedistiller.org/forum/viewtopic.php?p=7787168#p7787168
and is modified by kennstminet August 2024 filename Burstfire_5_Stab_6.ino
- Removed LCD code, added code for use of an 1.3 inch OLED display (SH1106) with SPI interface.
I had tested with a similar OLED with I2C interface and found that I2C is too slow and it was disturbing the burstfire timing.
- Introduced a transformerless voltage sensor for detecting the mains voltage zerocrossing
- Added an optional synchronization output to allow syncing the timing with Yummyrums TRMS power meter.
I found that such sycronization is not giving appreciable benefits. I left the code active, in case somebody needs it.
The sync output is set to LOW at start of the Frame_counter and returns to HIGH 100 millisecs before the Frame_counter is full
- Removed the code for writing the pattern to the SSR from the ISR and made a regular function for it.
Now, the ISR just sets a variable at zerocrossing and then returns to loop to avoid blocking the microcontroller for full 2 seconds.
- Added the "volatile" attribute to the variable used in the ISR
- Added code for measuring the current mains voltage (from the wall outlet). An external voltage sensor is needed for translating the
mains AC voltage to a 0 to 5VDC signal to the respective analog input of the arduino.
- Added code to provide a means of power stabilization. It compares the actual mains voltage with a preset
"lowest expected mains voltage" and reduces the selected power accordingly if the actual mains voltage is higher than the lower limit.
The max. available stabilized power is therefor lower than the power in unstabilized mode.
However, it will remain stable, when the mains voltage is variing as long it is higher than the lowr limit.
- Added an digital input for a external switch to turn on and off the stabilization function.
##########################################################################################*/
#include <Arduino.h> // I have no idea why this library is used here, so left it
#include <U8x8lib.h> // library for the OLED
#include <SPI.h> // for the OLED SPI interface
#include <math.h> // for mathematical rounding function "roundf()2
#include "elapsedMillis.h" // for creating a timer for the measurement duration
U8X8_SH1106_128X64_NONAME_4W_HW_SPI u8x8(/* cs=*/10, /* dc=*/9, /* reset=*/8); //creates an OLED object "u8x8"
elapsedMillis timeElapsed1; // timer object for reading the analog inputs
elapsedMillis timeElapsed2; // timer object for printing data
unsigned int interval1{ 10 }; // Timer interval for reading the analog inputs
unsigned int interval2{ 10 }; // Timer interval for printing data
const float mainsVoltage_low{ 210 }; // the lowest voltage, the stabilizer should be able to compensate
const float elementResistance{ 19.70 }; // Constant holding the resistance of the heating element in Ohms.
const float calMains{ 0.2574 }; // Constant holding the calibration factor for the mains voltage measurement
const byte ssrPin{ 7 }; // SSR drive pin (output)
const byte zerocross{ 12 }; // ISR zerocross trigger pin (input)
const byte potPin{ A0 }; // Analog Pin A0 reads the potentionmeter for the percdent setting
const byte syncPin{ 5 }; // defines pin for the sync output to the TRMS power meter
const byte sensePin{ A2 }; // Analog Pin A2 reads voltage from mains plug
const byte stabOnPin{ 6 }; // input for an external on/off switch for turning stabilization on or off.
float mainsVoltage_actual{}; // Variable holding the mains voltage
float mainsAccumulator{}; // used for calculating the moving average mains voltage
int unsigned numberOfSamples{ 150 }; // used for calculating the moving average mains voltage
int unsigned sampleCounter{}; // used for calculating the moving average mains voltage
float powerMax_actual{}; // the potential power resulting from actual mains voltage and 100 percent setting
float powerMax_low{}; // the potential power resulting from the lowest expected mains voltage and 100 percent setting
float powerStabilized{}; // the actual stabilized power resulting from the selected percent setting and the lowest expected mains voltage
float powerSelected_actual{}; // the power resulting from actual mains voltage and selected percent setting
float powerSelected_low{}; // the power resulting from lowest expected mains voltage and selected percent setting
float ratio_powerMax{}; // ratio for adjusting the selected Percent for stabilizing
float percentStabilized{}; // the selected percent, adjusted for stabilizing
bool stabilizationOn{ false }; // holding the status of the stab. on/off switch. True when stabilization is on.
bool setoutput{ false }; // SSR turns on when true, used in writePattern
volatile bool outputActive{ false }; // For use in the ISR and writePattern routines
// ISR sets to true after zerocrossing falling edge,
//writePattern sets to false after patternCounter is full.
int Maximum{ 100 }; //Maximum power IE 100% or 1000%
int Per_maximum{ 1 }; //selected percent setting Power % of the maximum value
int Pttn_1st{}; //1st Cycle Pattern }
int Pttn_2nd{}; //2nd Cycle Pattern }
int Pttn_3rd{}; //3rd Cycle Pattern } These are calculated in the createPattern() routine and
int Pttn_4th{}; //4th Cycle Pattern } used to turn cycles on or off in the writePattern() routine
int Pttn_5th{}; //5th Cycle Pattern }
int Reps_1st{};
int Skip_diff{};
int skip_diff_3{};
int skip_diff_4{};
int Skip{};
int Keep_skip{};
int Loose_keep_skip{};
int Restore_Loose_keep_skip{};
int Frame_counter{ 0 }; //Counter used in writePattern() routine. Each Mains cycle triggers ISR
int PttnCnt_1st{ 0 }; //PatternCounters used in writePattern()
int PttnCnt_2nd{ 0 }; //
int PttnCnt_3rd{ 0 }; //
int PttnCnt_4th{ 0 }; //
int PttnCnt_5th{ 0 }; //
// function prototype declarations
void create_pattern(); // function to calculate the pulse pattern, based of the required percentage value.
void writePattern(); // function for writing the pulse pattern to the SSR after each mains voltage zerocrossing
//##################################################################################################################
void setup() {
Serial.begin(115200); // activate Serial Monitor
u8x8.begin(); // activate the OLED on SPI
u8x8.setPowerSave(0); // not sure if this is needed
pinMode(syncPin, OUTPUT); // output for optional sync signal to TRMS power meter
pinMode(ssrPin, OUTPUT); // Setup SSR output pin
pinMode(zerocross, INPUT_PULLUP); // Setup Zerocross input pin
digitalWrite(syncPin, true); // set sync signal to true till framecounter starts
pinMode(stabOnPin, INPUT_PULLUP); // Setup stabilization on/off input pin
// Set Pin Change Interrupt Control Register and Pin Change Mask
// to allow interrupt driven response to the zero crossing detector
// connected to pin D12. (|= is the "compound bitwise or" operator)
PCICR |= B00000001; // Select port B in the Pin Change Interrupt Control Register
PCMSK0 |= B00010000; // Enable D12 pin in the Pin Change Mask 0
powerMax_low = sq(mainsVoltage_low) / elementResistance; // the power resulting from the lowest expected
// mains voltage and 100 percent setting
}
//##################################################################################################################
void loop() {
writePattern(); // calls the routine to write the pulse pattern to the SSR
// sets and resets outputActive based on zerocrossing and frameCounter
if (outputActive == false) { // calls the routine to calculate the pulse pattern
// only after writePattern() has reset outputActive
createPattern();
}
if (outputActive == false && timeElapsed1 > interval1) { // read analog inputs only when not writing the pattern and
// after a certain interval has passed
Per_maximum = map(analogRead(potPin), 0, 1023, 1, Maximum); // read and store potentiometer value as the selected Percent setting
if (sampleCounter <= (numberOfSamples - 1)) { // read actual mains voltage and calculate moving average
mainsAccumulator = mainsAccumulator + analogRead(sensePin);
sampleCounter++;
} else {
mainsVoltage_actual = (mainsAccumulator / numberOfSamples) * calMains;
mainsAccumulator = 0;
sampleCounter = 0;
}
powerMax_actual = sq(mainsVoltage_actual) / elementResistance; // the potential power resulting from actual mains voltage and 100 percent setting
powerSelected_actual = powerMax_actual * Per_maximum / 100; // the power resulting from actual mains voltage and selected percent setting
powerSelected_low = powerMax_low * Per_maximum / 100; // the power resulting from lowest expected mains voltage and selected percent setting
ratio_powerMax = powerMax_low / powerMax_actual; // ratio for adjusting the selected Percent for stabilizing
if (ratio_powerMax > 1) { // do not increase selected percent when actual mains is
// lower than the lower limit
ratio_powerMax = 1;
}
percentStabilized = Per_maximum * ratio_powerMax; // the selected percent, corrected for stabilizing
powerStabilized = powerMax_actual * percentStabilized / 100; // the actual stabilized power resulting from the selected percent setting
// and the lowest expected mains voltage
if(stabilizationOn == true) Per_maximum = percentStabilized;
timeElapsed1 = 0;
}
if (outputActive == false && timeElapsed2 > interval2) { // print data only when not writing the pattern and
// after a certain interval has passed
stabilizationOn = digitalRead(stabOnPin); // read status of stabilization on/off switch and
// change the display layout accordingly
u8x8.setFont(u8x8_font_px437wyse700a_2x2_r); // set large font
u8x8.setCursor(0, 0);
u8x8.print(Per_maximum);
u8x8.print("% ");
u8x8.setFont(u8x8_font_chroma48medium8_r); // set small font for "stab" and "on" or "off"
u8x8.setCursor(9, 0);
u8x8.print("Stab");
u8x8.setCursor(9, 1);
if (stabilizationOn == true) {
u8x8.print("On");
} else {
u8x8.print("Off");
}
u8x8.setFont(u8x8_font_px437wyse700a_2x2_r); // set large font for the power display
u8x8.setCursor(0, 2);
if (stabilizationOn == true) {
u8x8.print(roundf(powerStabilized));
u8x8.print(" W ");
} else {
u8x8.print(roundf(powerSelected_actual));
u8x8.print(" W ");
}
if (outputActive == true) return;
u8x8.setFont(u8x8_font_chroma48medium8_r); // set small font for the voltage readouts
u8x8.setCursor(0, 5);
u8x8.print(roundf(mainsVoltage_actual));
u8x8.print(" V actual");
if (outputActive == true) return;
u8x8.setCursor(0, 6);
u8x8.print(mainsVoltage_low, 0);
u8x8.print(" V stab");
Serial.print(percentStabilized);
Serial.print(" Percent stab. ");
Serial.print( Per_maximum);
Serial.print(" Percent");
Serial.print(" Mains:");
Serial.print(mainsVoltage_actual);
Serial.print(" powerMax_actual:");
Serial.print(powerMax_actual);
Serial.print(" powerStabilized:");
Serial.print(powerStabilized);
Serial.print( " pSa");
Serial.print(powerSelected_actual);
Serial.print(" Stab: ");
Serial.println(stabilizationOn);
timeElapsed2 = 0;
}
} // loop end
//##############################################################################
//###########Function for writing the calculated pattern to the SSR ############
//###########and setting and resetting the outputActive flag ###################
void writePattern() {
if (outputActive == true) { // set by the ISR after each falling zerocrossing
digitalWrite(ssrPin, setoutput); //Turn SSR on or off as determined by previous pattern calculation
outputActive = false; // reset the outputActive flag to prevent from blocking the other activities in the loop
//Calculate state of "setoutput" for use after the next falling zerocrossing.
//This state will be based on the Patterns calculated in Main loop, using the desired percentage of power.
if (Frame_counter == Maximum + 1 - 5) { digitalWrite(syncPin, 1); } // set sync signal to HIGH after 95 frames
// (100 millisecs before next zerocrossing)
if (Frame_counter >= (Maximum + 1)) {
digitalWrite(syncPin, 0); // set sync signal to LOW when frame counter starts
// in order to allow optional start of the TRMS power meter
Frame_counter = 1;
PttnCnt_1st = 0;
PttnCnt_2nd = 0;
PttnCnt_3rd = 0;
PttnCnt_4th = 0;
PttnCnt_5th = 0;
setoutput = false;
}
Frame_counter++;
PttnCnt_1st++; // inc Pttn_1st counter
if (PttnCnt_1st < (Pttn_1st)) // ie. Pttn_1st is 2 when 37% power is selected
{
setoutput = false; //Turn off SSR if pttn_1st hasn't arrived
return; // and return to loop
}
setoutput = true; // Turn on SSR as PttnCnt_1st has reached pttn_1st (ie. 2 for 37%)
PttnCnt_1st = 0; // Reset Pttn_1st counter
PttnCnt_2nd++; // inc Skip counter
if (Pttn_2nd == 0) // if no pulses need to be skipped
{
setoutput = true; // leave it turned on
return; // and return to loop
}
if (PttnCnt_2nd < (Pttn_2nd)) // ie. Pttn_2nd is 3 when 37% power is selected
{
setoutput = true; // keep SSR on if not Skip rate hasn't arrived
return;
}
setoutput = false; //Turn off SSR as Skip rate has arrived
PttnCnt_2nd = 0;
PttnCnt_3rd++; //inc Keepskip counter
if (Pttn_3rd == 0) {
setoutput = false;
return;
}
if (PttnCnt_3rd < (Pttn_3rd)) {
setoutput = false;
return;
}
setoutput = true;
PttnCnt_3rd = 0;
PttnCnt_4th++;
if (Pttn_4th == 0) {
setoutput = true;
return;
}
if (PttnCnt_4th < (Pttn_4th)) {
setoutput = true; // keep SSR on if not Skip rate hasn't arrived
return;
}
setoutput = false; //Turn off SSR as Skip rate has arrived
PttnCnt_4th = 0;
PttnCnt_5th++;
if (Pttn_5th == 0) {
setoutput = true;
return;
}
if (PttnCnt_5th < (Pttn_5th)) {
setoutput = false;
return;
} // keep SSR on if not Skip rate hasn't arrived
setoutput = true; //Turn on SSR as Skip rate has arrived
PttnCnt_5th = 0;
}
}
//#############calculate the pulse pattern#########################
void createPattern() {
if (Per_maximum == 0) Pttn_1st = 0; //Pttn_1st is first fill
else Pttn_1st = Maximum / Per_maximum; //ie turn on every 2nd cycle if 37% is desired 100/37=2
if (Pttn_1st == 0) Reps_1st = 0;
else Reps_1st = (Maximum / Pttn_1st); // i.e 100/2=50 pulsed per cycle
Skip = Reps_1st - Per_maximum; // Pttn_2nd runs through first fill and removes certain cycles IE, every 5th
// i.e for 37% 50-37 = 13
if (Skip == 0) Pttn_2nd = 0;
else Pttn_2nd = (Reps_1st / Skip); // i.e for 37% 50/13=3
if (Pttn_2nd == 0) Skip_diff = 0;
else Skip_diff = (Reps_1st / Pttn_2nd); // i.e for 37% 50/3=16 pulses out of 50 shall be skipped
Keep_skip = Skip_diff - Skip; // Pttn_3rd turns back on cycles switched off by Skip-rate
// to correct missing cycles i.e for 37% 16-13=3
if (Keep_skip == 0) Pttn_3rd = 0;
else Pttn_3rd = (Skip_diff / Keep_skip); // i.e for 37% 16/3=5
if (Pttn_3rd == 0) skip_diff_3 = 0;
else skip_diff_3 = Skip_diff / Pttn_3rd; // i.e for 37% 16/5=3
Loose_keep_skip = skip_diff_3 - Keep_skip; //Pttn_4th turns off required cycles after Pttn_3rd run
// i.e for 37% 3-3=0
if (Loose_keep_skip == 0) Pttn_4th = 0;
else Pttn_4th = skip_diff_3 / Loose_keep_skip; // i.e for 37% 3/3=1
if (Pttn_4th == 0) skip_diff_4 = 0;
else skip_diff_4 = skip_diff_3 / Pttn_4th;
Restore_Loose_keep_skip = skip_diff_4 - Loose_keep_skip; //Pttn_5th turns on required cycles after Pttn_4rd run
if (Restore_Loose_keep_skip == 0) Pttn_5th = 0;
else Pttn_5th = skip_diff_4 / Restore_Loose_keep_skip;
}
//########Interrupt Service Routine for the zerocrossing detector ##############
ISR(PCINT0_vect) // ISR for pin change interrupt on port B pin 12 (Zerocrossing detected )
{
if (digitalRead(zerocross) == false) { // react only when pinchange was falling edge
// otherwise do nothing and return
outputActive = true; // enable the routine for writing the output pattern
// to the SSR with any falling edge
}
}
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
The error correction in the previous post introduced a new error.
The line 160
assignes a float (percentStabilized) to an int type variable (Per_maximum), cutting off the decimals.
I changed to code to
In addition, there was an old glitch in the code, causing the SSR to turn on full power when the selected Power was set to 0%.
I had mitigated the problem by mapping the range of the potentiometer to 1 to Maximum, avoiding the setting 0.
However since adding the power stabilization feature, the value of Per_maximum could drop below 1 when the mains voltage was high.
The dormant problem of causing the SSR to turn on full power when the selected Power was set to 0% showed up again.
As a solution, I added the line to the function writePattern() in line 235 to cause an early return to loop().
The function now looks like:
This allowed me to remove the previous restriction, avoiding the 0% setting. Now it is possible to turn off the power by selecting 0%.
Line 73 is now
and line 138 is now
Here is the full sketch with filename Burstfire_5_Stab_7.ino
I am really happy with that power stabilization feature. It keeps the heating power stable and independent from changes of the mains voltage.
The indication of the heating power in Watts is accurate enough, so I do not need a separate RMS power meter.
Thanks again to Yummyrum for inventing this burstfire controller solution for Arduino microcontrollers.
I hope that this was the last correction of errors, caused by me.
The line 160
Code: Select all
if(stabilizationOn == true) Per_maximum = percentStabilized;
I changed to code to
Code: Select all
if(stabilizationOn == true) Per_maximum = int(roundf(percentStabilized));
I had mitigated the problem by mapping the range of the potentiometer to 1 to Maximum, avoiding the setting 0.
However since adding the power stabilization feature, the value of Per_maximum could drop below 1 when the mains voltage was high.
The dormant problem of causing the SSR to turn on full power when the selected Power was set to 0% showed up again.
As a solution, I added the line
Code: Select all
if(Per_maximum == 0) return; // do not write to SSR when Per_maximum is set to 0
The function now looks like:
Code: Select all
//###########Function for writing the calculated pattern to the SSR ############
//###########and setting and resetting the outputActive flag ###################
void writePattern() {
if (outputActive == true) { // set by the ISR after each falling zerocrossing
digitalWrite(ssrPin, setoutput); //Turn SSR on or off as determined by previous pattern calculation
outputActive = false; // reset the outputActive flag to prevent from blocking the other activities in the loop
if(Per_maximum == 0) return; // do not write to SSR when Per_maximum is set to 0
.......
.......
Line 73 is now
Code: Select all
int Per_maximum{ 0 };
Code: Select all
Per_maximum = map(analogRead(potPin), 0, 1023, 0, Maximum);
Code: Select all
/*########################################################################################
The sketch is based on the one created by Yummyrum and posted at HD
June 25th, 2025 https://homedistiller.org/forum/viewtopic.php?p=7787168#p7787168
and is modified by kennstminet October 2024 filename Burstfire_5_Stab_7.ino
- Removed LCD code, added code for use of an 1.3 inch OLED display (SH1106) with SPI interface.
I had tested with a similar OLED with I2C interface and found that I2C is too slow and it was disturbing the burstfire timing.
- Introduced a transformerless voltage sensor for detecting the mains voltage zerocrossing
- Added an optional synchronization output to allow syncing the timing with Yummyrums TRMS power meter.
I found that such sycronization is not giving appreciable benefits. I left the code active, in case somebody needs it.
The sync output is set to LOW at start of the Frame_counter and returns to HIGH 100 millisecs before the Frame_counter is full
- Removed the code for writing the pattern to the SSR from the ISR and made a regular function for it.
Now, the ISR just sets a variable at zerocrossing and then returns to loop to avoid blocking the microcontroller for full 2 seconds.
- Added the "volatile" attribute to the variable used in the ISR
- Added code for measuring the current mains voltage (from the wall outlet). An external voltage sensor is needed for translating the
mains AC voltage to a 0 to 5VDC signal to the respective analog input of the arduino.
- Added code to provide a means of power stabilization. It compares the actual mains voltage with a preset
"lowest expected mains voltage" and reduces the selected power accordingly if the actual mains voltage is higher than the lower limit.
The max. available stabilized power is therefor lower than the power in unstabilized mode.
However, it will remain stable, when the mains voltage is variing as long it is higher than the lowr limit.
- Added an digital input for a external switch to turn on and off the stabilization function.
- prevent SSR from turning on full power when Per_maximum is set to 0 (line 235)
Initialize Per_maximum to 0 (line 73) and map potentiometer to the range 0 to Maximum (LINE 138)
- correct error line 160, cast float to int before assigning it
##########################################################################################*/
#include <Arduino.h> // I have no idea why this library is used here, so left it
#include <U8x8lib.h> // library for the OLED
#include <SPI.h> // for the OLED SPI interface
#include <math.h> // for mathematical rounding function "roundf()2
#include "elapsedMillis.h" // for creating a timer for the measurement duration
U8X8_SH1106_128X64_NONAME_4W_HW_SPI u8x8(/* cs=*/10, /* dc=*/9, /* reset=*/8); //creates an OLED object "u8x8"
elapsedMillis timeElapsed1; // timer object for reading the analog inputs
elapsedMillis timeElapsed2; // timer object for printing data
unsigned int interval1{ 10 }; // Timer interval for reading the analog inputs
unsigned int interval2{ 10 }; // Timer interval for printing data
const float mainsVoltage_low{ 210 }; // the lowest voltage, the stabilizer should be able to compensate
const float elementResistance{ 19.70 }; // Constant holding the resistance of the heating element in Ohms.
const float calMains{ 0.2574 }; // Constant holding the calibration factor for the mains voltage measurement
const byte ssrPin{ 7 }; // SSR drive pin (output)
const byte zerocross{ 12 }; // ISR zerocross trigger pin (input)
const byte potPin{ A0 }; // Analog Pin A0 reads the potentionmeter for the percdent setting
const byte syncPin{ 5 }; // defines pin for the sync output to the TRMS power meter
const byte sensePin{ A2 }; // Analog Pin A2 reads voltage from mains plug
const byte stabOnPin{ 6 }; // input for an external on/off switch for turning stabilization on or off.
float mainsVoltage_actual{}; // Variable holding the mains voltage
float mainsAccumulator{}; // used for calculating the moving average mains voltage
int unsigned numberOfSamples{ 150 }; // used for calculating the moving average mains voltage
int unsigned sampleCounter{}; // used for calculating the moving average mains voltage
float powerMax_actual{}; // the potential power resulting from actual mains voltage and 100 percent setting
float powerMax_low{}; // the potential power resulting from the lowest expected mains voltage and 100 percent setting
float powerStabilized{}; // the actual stabilized power resulting from the selected percent setting and the lowest expected mains voltage
float powerSelected_actual{}; // the power resulting from actual mains voltage and selected percent setting
float powerSelected_low{}; // the power resulting from lowest expected mains voltage and selected percent setting
float ratio_powerMax{}; // ratio for adjusting the selected Percent for stabilizing
float percentStabilized{}; // the selected percent, adjusted for stabilizing
bool stabilizationOn{ false }; // holding the status of the stab. on/off switch. True when stabilization is on.
bool setoutput{ false }; // SSR turns on when true, used in writePattern
volatile bool outputActive{ false }; // For use in the ISR and writePattern routines
// ISR sets to true after zerocrossing falling edge,
//writePattern sets to false after patternCounter is full.
int Maximum{ 100 }; //Maximum power IE 100% or 1000%
int Per_maximum{ 0 }; //selected percent setting Power % of the maximum value
int Pttn_1st{}; //1st Cycle Pattern }
int Pttn_2nd{}; //2nd Cycle Pattern }
int Pttn_3rd{}; //3rd Cycle Pattern } These are calculated in the createPattern() routine and
int Pttn_4th{}; //4th Cycle Pattern } used to turn cycles on or off in the writePattern() routine
int Pttn_5th{}; //5th Cycle Pattern }
int Reps_1st{};
int Skip_diff{};
int skip_diff_3{};
int skip_diff_4{};
int Skip{};
int Keep_skip{};
int Loose_keep_skip{};
int Restore_Loose_keep_skip{};
int Frame_counter{ 0 }; //Counter used in writePattern() routine. Each Mains cycle triggers ISR
int PttnCnt_1st{ 0 }; //PatternCounters used in writePattern()
int PttnCnt_2nd{ 0 }; //
int PttnCnt_3rd{ 0 }; //
int PttnCnt_4th{ 0 }; //
int PttnCnt_5th{ 0 }; //
// function prototype declarations
void create_pattern(); // function to calculate the pulse pattern, based of the required percentage value.
void writePattern(); // function for writing the pulse pattern to the SSR after each mains voltage zerocrossing
//##################################################################################################################
void setup() {
Serial.begin(115200); // activate Serial Monitor
u8x8.begin(); // activate the OLED on SPI
u8x8.setPowerSave(0); // not sure if this is needed
pinMode(syncPin, OUTPUT); // output for optional sync signal to TRMS power meter
pinMode(ssrPin, OUTPUT); // Setup SSR output pin
pinMode(zerocross, INPUT_PULLUP); // Setup Zerocross input pin
digitalWrite(syncPin, true); // set sync signal to true till framecounter starts
pinMode(stabOnPin, INPUT_PULLUP); // Setup stabilization on/off input pin
// Set Pin Change Interrupt Control Register and Pin Change Mask
// to allow interrupt driven response to the zero crossing detector
// connected to pin D12. (|= is the "compound bitwise or" operator)
PCICR |= B00000001; // Select port B in the Pin Change Interrupt Control Register
PCMSK0 |= B00010000; // Enable D12 pin in the Pin Change Mask 0
powerMax_low = sq(mainsVoltage_low) / elementResistance; // the power resulting from the lowest expected
// mains voltage and 100 percent setting
}
//##################################################################################################################
void loop() {
writePattern(); // calls the routine to write the pulse pattern to the SSR
// sets and resets outputActive based on zerocrossing and frameCounter
if (outputActive == false) { // calls the routine to calculate the pulse pattern
// only after writePattern() has reset outputActive
createPattern();
}
if (outputActive == false && timeElapsed1 > interval1) { // read analog inputs only when not writing the pattern and
// after a certain interval has passed
Per_maximum = map(analogRead(potPin), 0, 1023, 0, Maximum); // read and store potentiometer value as the selected Percent setting
if (sampleCounter <= (numberOfSamples - 1)) { // read actual mains voltage and calculate moving average
mainsAccumulator = mainsAccumulator + analogRead(sensePin);
sampleCounter++;
} else {
mainsVoltage_actual = (mainsAccumulator / numberOfSamples) * calMains;
mainsAccumulator = 0;
sampleCounter = 0;
}
powerMax_actual = sq(mainsVoltage_actual) / elementResistance; // the potential power resulting from actual mains voltage and 100 percent setting
powerSelected_actual = powerMax_actual * Per_maximum / 100; // the power resulting from actual mains voltage and selected percent setting
powerSelected_low = powerMax_low * Per_maximum / 100; // the power resulting from lowest expected mains voltage and selected percent setting
ratio_powerMax = powerMax_low / powerMax_actual; // ratio for adjusting the selected Percent for stabilizing
if (ratio_powerMax > 1) { // do not increase selected percent when actual mains is
// lower than the lower limit
ratio_powerMax = 1;
}
percentStabilized = Per_maximum * ratio_powerMax; // the selected percent, corrected for stabilizing
powerStabilized = powerMax_actual * percentStabilized / 100; // the actual stabilized power resulting from the selected percent setting
// and the lowest expected mains voltage
if(stabilizationOn == true) Per_maximum = int(roundf(percentStabilized));
timeElapsed1 = 0;
}
if (outputActive == false && timeElapsed2 > interval2) { // print data only when not writing the pattern and
// after a certain interval has passed
stabilizationOn = digitalRead(stabOnPin); // read status of stabilization on/off switch and
// change the display layout accordingly
u8x8.setFont(u8x8_font_px437wyse700a_2x2_r); // set large font
u8x8.setCursor(0, 0);
u8x8.print(Per_maximum);
u8x8.print("% ");
u8x8.setFont(u8x8_font_chroma48medium8_r); // set small font for "stab" and "on" or "off"
u8x8.setCursor(9, 0);
u8x8.print("Stab");
u8x8.setCursor(9, 1);
if (stabilizationOn == true) {
u8x8.print("On");
} else {
u8x8.print("Off");
}
u8x8.setFont(u8x8_font_px437wyse700a_2x2_r); // set large font for the power display
u8x8.setCursor(0, 2);
if (stabilizationOn == true) {
u8x8.print(roundf(powerStabilized));
u8x8.print(" W ");
} else {
u8x8.print(roundf(powerSelected_actual));
u8x8.print(" W ");
}
if (outputActive == true) return;
u8x8.setFont(u8x8_font_chroma48medium8_r); // set small font for the voltage readouts
u8x8.setCursor(0, 5);
u8x8.print(roundf(mainsVoltage_actual));
u8x8.print(" V actual");
if (outputActive == true) return;
u8x8.setCursor(0, 6);
u8x8.print(mainsVoltage_low, 0);
u8x8.print(" V stab");
Serial.print(roundf(percentStabilized));
Serial.print(" Percent stab. ");
Serial.print( Per_maximum);
Serial.print(" Percent");
Serial.print(" Mains:");
Serial.print(mainsVoltage_actual);
Serial.print(" powerMax_actual:");
Serial.print(powerMax_actual);
Serial.print(" powerStabilized:");
Serial.print(powerStabilized);
Serial.print( " pSa");
Serial.print(powerSelected_actual);
Serial.print(" Stab: ");
Serial.println(stabilizationOn);
timeElapsed2 = 0;
}
} // loop end
//##############################################################################
//###########Function for writing the calculated pattern to the SSR ############
//###########and setting and resetting the outputActive flag ###################
void writePattern() {
if (outputActive == true) { // set by the ISR after each falling zerocrossing
digitalWrite(ssrPin, setoutput); //Turn SSR on or off as determined by previous pattern calculation
outputActive = false; // reset the outputActive flag to prevent from blocking the other activities in the loop
if(Per_maximum == 0) return; // do not write to SSR when Per_maximum is set to 0
//Calculate state of "setoutput" for use after the next falling zerocrossing.
//This state will be based on the Patterns calculated in Main loop, using the desired percentage of power.
if (Frame_counter == Maximum + 1 - 5) { digitalWrite(syncPin, 1); } // set sync signal to HIGH after 95 frames
// (100 millisecs before next zerocrossing)
if (Frame_counter >= (Maximum + 1)) {
digitalWrite(syncPin, 0); // set sync signal to LOW when frame counter starts
// in order to allow optional start of the TRMS power meter
Frame_counter = 1;
PttnCnt_1st = 0;
PttnCnt_2nd = 0;
PttnCnt_3rd = 0;
PttnCnt_4th = 0;
PttnCnt_5th = 0;
setoutput = false;
}
Frame_counter++;
PttnCnt_1st++; // inc Pttn_1st counter
if (PttnCnt_1st < (Pttn_1st)) // ie. Pttn_1st is 2 when 37% power is selected
{
setoutput = false; //Turn off SSR if pttn_1st hasn't arrived
return; // and return to loop
}
setoutput = true; // Turn on SSR as PttnCnt_1st has reached pttn_1st (ie. 2 for 37%)
PttnCnt_1st = 0; // Reset Pttn_1st counter
PttnCnt_2nd++; // inc Skip counter
if (Pttn_2nd == 0) // if no pulses need to be skipped
{
setoutput = true; // leave it turned on
return; // and return to loop
}
if (PttnCnt_2nd < (Pttn_2nd)) // ie. Pttn_2nd is 3 when 37% power is selected
{
setoutput = true; // keep SSR on if not Skip rate hasn't arrived
return;
}
setoutput = false; //Turn off SSR as Skip rate has arrived
PttnCnt_2nd = 0;
PttnCnt_3rd++; //inc Keepskip counter
if (Pttn_3rd == 0) {
setoutput = false;
return;
}
if (PttnCnt_3rd < (Pttn_3rd)) {
setoutput = false;
return;
}
setoutput = true;
PttnCnt_3rd = 0;
PttnCnt_4th++;
if (Pttn_4th == 0) {
setoutput = true;
return;
}
if (PttnCnt_4th < (Pttn_4th)) {
setoutput = true; // keep SSR on if not Skip rate hasn't arrived
return;
}
setoutput = false; //Turn off SSR as Skip rate has arrived
PttnCnt_4th = 0;
PttnCnt_5th++;
if (Pttn_5th == 0) {
setoutput = true;
return;
}
if (PttnCnt_5th < (Pttn_5th)) {
setoutput = false;
return;
} // keep SSR on if not Skip rate hasn't arrived
setoutput = true; //Turn on SSR as Skip rate has arrived
PttnCnt_5th = 0;
}
}
//#############calculate the pulse pattern#########################
void createPattern() {
if (Per_maximum == 0) Pttn_1st = 0; //Pttn_1st is first fill
else Pttn_1st = Maximum / Per_maximum; //ie turn on every 2nd cycle if 37% is desired 100/37=2
if (Pttn_1st == 0) Reps_1st = 0;
else Reps_1st = (Maximum / Pttn_1st); // i.e 100/2=50 pulsed per cycle
Skip = Reps_1st - Per_maximum; // Pttn_2nd runs through first fill and removes certain cycles IE, every 5th
// i.e for 37% 50-37 = 13
if (Skip == 0) Pttn_2nd = 0;
else Pttn_2nd = (Reps_1st / Skip); // i.e for 37% 50/13=3
if (Pttn_2nd == 0) Skip_diff = 0;
else Skip_diff = (Reps_1st / Pttn_2nd); // i.e for 37% 50/3=16 pulses out of 50 shall be skipped
Keep_skip = Skip_diff - Skip; // Pttn_3rd turns back on cycles switched off by Skip-rate
// to correct missing cycles i.e for 37% 16-13=3
if (Keep_skip == 0) Pttn_3rd = 0;
else Pttn_3rd = (Skip_diff / Keep_skip); // i.e for 37% 16/3=5
if (Pttn_3rd == 0) skip_diff_3 = 0;
else skip_diff_3 = Skip_diff / Pttn_3rd; // i.e for 37% 16/5=3
Loose_keep_skip = skip_diff_3 - Keep_skip; //Pttn_4th turns off required cycles after Pttn_3rd run
// i.e for 37% 3-3=0
if (Loose_keep_skip == 0) Pttn_4th = 0;
else Pttn_4th = skip_diff_3 / Loose_keep_skip; // i.e for 37% 3/3=1
if (Pttn_4th == 0) skip_diff_4 = 0;
else skip_diff_4 = skip_diff_3 / Pttn_4th;
Restore_Loose_keep_skip = skip_diff_4 - Loose_keep_skip; //Pttn_5th turns on required cycles after Pttn_4rd run
if (Restore_Loose_keep_skip == 0) Pttn_5th = 0;
else Pttn_5th = skip_diff_4 / Restore_Loose_keep_skip;
}
//########Interrupt Service Routine for the zerocrossing detector ##############
ISR(PCINT0_vect) // ISR for pin change interrupt on port B pin 12 (Zerocrossing detected )
{
if (digitalRead(zerocross) == false) { // react only when pinchange was falling edge
// otherwise do nothing and return
outputActive = true; // enable the routine for writing the output pattern
// to the SSR with any falling edge
}
}
The indication of the heating power in Watts is accurate enough, so I do not need a separate RMS power meter.
Thanks again to Yummyrum for inventing this burstfire controller solution for Arduino microcontrollers.
I hope that this was the last correction of errors, caused by me.
Re: Yummy's DIY Burst fire controller project
Yep my head hurts too
So yummy was right, is my take on this
So yummy was right, is my take on this
- Yummyrum
- Global moderator
- Posts: 8641
- Joined: Sat Jul 06, 2013 2:23 am
- Location: Fraser Coast QLD Aussie
Re: Yummy's DIY Burst fire controller project
Yeah ,it was never going to be a project for everyone Harold …. But it works and simulates what Auber controllers do in the way it regulates power . And while average Joe blow will undoubtably just buy an Auber , I did it so folk like Kennstminet can “roll their own” and customise it .
Kenn has done just that and taken this to the next level and added power regulation to account for variations in local supply and included a power meter . That is something the Auber controllers can’t do .
So Harold , you could easily use this controller to feed a three phase system . Just add another two SSRs for the other two phases and you are good …. and a small code adjustment to three times the power reading .
My recommended goto .
https://homedistiller.org/wiki/index.ph ... ion_Theory
https://homedistiller.org/wiki/index.ph ... ion_Theory
- Yummyrum
- Global moderator
- Posts: 8641
- Joined: Sat Jul 06, 2013 2:23 am
- Location: Fraser Coast QLD Aussie
Re: Yummy's DIY Burst fire controller project
Kenn , I am so sorry.
I was aware of that original error with full power at 0% and I had fixed it but I posted an earlier code version that had it
I had addressed it back here .
viewtopic.php?p=7787704#p7787704
Regardless , you found another work-a-round .
….as they say , many ways to skin a cat ![Wink :wink:](./images/smilies/icon_wink.gif)
Glad you have taken this to the next level .
Heck , I might be copying your code and making one fir myself .
…thanks
I was aware of that original error with full power at 0% and I had fixed it but I posted an earlier code version that had it
![Embarassed :oops:](./images/smilies/icon_redface.gif)
I had addressed it back here .
viewtopic.php?p=7787704#p7787704
Regardless , you found another work-a-round .
![Thumbup :thumbup:](./images/smilies/icon_thumbup.gif)
![Wink :wink:](./images/smilies/icon_wink.gif)
Glad you have taken this to the next level .
Heck , I might be copying your code and making one fir myself .
![Thumbup :thumbup:](./images/smilies/icon_thumbup.gif)
My recommended goto .
https://homedistiller.org/wiki/index.ph ... ion_Theory
https://homedistiller.org/wiki/index.ph ... ion_Theory
-
- Bootlegger
- Posts: 135
- Joined: Thu Aug 30, 2018 12:47 pm
- Location: Not there
Re: Yummy's DIY Burst fire controller project
Yummy
I am sorry that I had ignored your code fix.
It seems that for some reason, I had a partial memory erasure. May have to do with our main hobby here.![Laughing :lol:](./images/smilies/icon_lol.gif)
I am sorry that I had ignored your code fix.
It seems that for some reason, I had a partial memory erasure. May have to do with our main hobby here.
![Laughing :lol:](./images/smilies/icon_lol.gif)
Re: Yummy's DIY Burst fire controller project
For 3-phase you should also add a 60 and 120 degree delay for L2 and L3.. ![Smile :-)](./images/smilies/icon_smile.gif)
![Smile :-)](./images/smilies/icon_smile.gif)
- Yummyrum
- Global moderator
- Posts: 8641
- Joined: Sat Jul 06, 2013 2:23 am
- Location: Fraser Coast QLD Aussie
Re: Yummy's DIY Burst fire controller project
I used to think that two zukram , but if you’re trigger pulse is almost half a cycle , its long enough to trigger the other two phases .
BTW , thats what Auber does . And they sell
A three phase SSR module that can be controlled
by a DSPR1 .Or just use three individual SSRs
My recommended goto .
https://homedistiller.org/wiki/index.ph ... ion_Theory
https://homedistiller.org/wiki/index.ph ... ion_Theory
Re: Yummy's DIY Burst fire controller project
Yes. hm. that would work.