Arduino LCD Display does not work normally - Page 1
Forum section: electronics / beginners

author: mike_mike
author_rank: Super Contributor
timestamp: July 31, 2020, 12:02:49 pm
content: Hello, I have a 1602 LCD which I connected (correctly) to an Arduino Nano.When I run the code, it shows the message from the setup() function, but when it goes into loop() function, then it does show nothing (only the back light is on).Please have a look at the code and tell me what you think .This is the code:Code:#include <LiquidCrystal.h>#include <SPI.h>#include <Wire.h>#include <max6675.h>#include <PID_v1.h>#define thermoDO 12#define thermoCS 10#define thermoCLK 13#define potentiometer A0#define zerocrossing 2#define triac 7#define relay A1float realTemperature;int pottemperature;LiquidCrystal lcd(3, 4, 5, 6, 8, 9);byte thermometer = //icon for termometer{B00100,B01010,B01010,B01110,B01110,B11111,B11111,B01110};byte arrow = //icon for arrow{B11000,B01100,B00110,B00011,B00011,B00110,B01100,B11000};MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);volatile int pidOut = 0;double Setpoint, Input, Output;double aggKp = 4, aggKi = 0.2, aggKd = 1;double consKp = 1, consKi = 0.05, consKd = 0.25;PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT);void setup() {lcd.begin(16, 2);lcd.createChar(0, thermometer);lcd.createChar(1, arrow);lcd.setCursor(0, 0);lcd.print("STATIE DE LIPIT");myPID.SetMode(AUTOMATIC);myPID.SetOutputLimits(0, 240);pinMode(relay, OUTPUT);pinMode(potentiometer, INPUT);pinMode(zerocrossing, INPUT_PULLUP);pinMode(triac, OUTPUT);delay(1200);lcd.clear();digitalWrite(triac, LOW);digitalWrite(relay, HIGH);pidOut = 0;attachInterrupt(digitalPinToInterrupt(2), zero_crosss_int, RISING);}void loop() { //moved the PID computations outside the ISRpottemperature = analogRead(potentiometer);Setpoint = map(pottemperature, 0, 1023, 150, 400);realTemperature = thermocouple.readCelsius();Input = int(0.779828 * realTemperature - 10.3427); // make temperature an integerif (isnan(realTemperature) || Input >= 432) {while (true) {digitalWrite(relay, LOW); // the relay will disconnect the power to the soldering iron heating elementlcd.clear();lcd.setCursor(0, 0);lcd.write((byte)0);lcd.setCursor(1, 0);lcd.write((byte)0);lcd.setCursor(5, 0);lcd.print("ERROR!");lcd.setCursor(14, 0);lcd.write((byte)0);lcd.setCursor(15, 0);lcd.write((byte)0);delay(500);}}double gap = abs(Setpoint - Input); //distance away from setpointif (gap < 10){ //we're close to setpoint, use conservative tuning parametersmyPID.SetTunings(consKp, consKi, consKd);}else{//we're far from setpoint, use aggressive tuning parametersmyPID.SetTunings(aggKp, aggKi, aggKd);}myPID.Compute();noInterrupts();pidOut = int(Output);interrupts();lcd.clear();lcd.setCursor(0, 0);lcd.write((byte)0);lcd.setCursor(2, 0);lcd.print((int)Setpoint);lcd.setCursor(6, 0);lcd.print((char)223); //degree signlcd.setCursor(7, 0);lcd.print("C");lcd.setCursor(0, 1);lcd.write((byte)1);if (Input <= 45) {lcd.setCursor(2, 1);lcd.print("Lo");} else {lcd.setCursor(2, 1);lcd.print((int)Input);}lcd.setCursor(6, 1);lcd.print("[");lcd.setCursor(7, 1);lcd.print((int)realTemperature);lcd.setCursor(10, 1);lcd.print("]");lcd.setCursor(12, 1);lcd.print((char)223);lcd.setCursor(13, 1);lcd.print("C");delay(300);}void zero_crosss_int(){int powertime = (39 * (256 - pidOut));delayMicroseconds(powertime);digitalWrite(triac, HIGH);delayMicroseconds(10);digitalWrite(triac, LOW);}

author: sleemanj
author_rank: Super Contributor
timestamp: July 31, 2020, 12:11:06 pm
content: Without looking at the code deeply, try increasing the final delay, you might be refreshing the 1602 too quickly, they are quite slow.

author: LateLesley
author_rank: Frequent Contributor
timestamp: July 31, 2020, 12:14:19 pm
content: to me, it looks like you have set up the LCD on the wrong pins.you are telling it to use 3,4,5,6,8,9, but it is connected to 12,11,5,4,3,2change your liquid crystal statement to this -LiquidCrystal lcd(12,11,5,4,3,2);This should then match up your circuit wiring to the LCD pins which are LiquidCrystal(rs, enable, d4, d5, d6, d7)You should enter the numbers for the pins you are connected to in this case - - - - - -12, 11, 5 , 4 , 3 , 2QuoteEDIT : link where got the LiquidCrystal statement -https://www.arduino.cc/en/Reference/LiquidCrystalConstructor

author: mike_mike
author_rank: Super Contributor
timestamp: July 31, 2020, 12:23:55 pm
content: @LateLesley The LCD is correctly connected. If I try to display another message, using a simple script, then it works. Using the code from the first post, it doesn't.This:Code:/* The circuit:LCD RS pin to digital pin 12LCD Enable pin to digital pin 11LCD D4 pin to digital pin 5LCD D5 pin to digital pin 4LCD D6 pin to digital pin 3LCD D7 pin to digital pin 2LCD R/W pin to groundLCD VSS pin to groundLCD VCC pin to 5V10K resistor:ends to +5V and groundwiper to LCD VO pin (pin 3)*/Is just an example, it help me when I connected the lcd first time. It can be removed now from the code.@sleemanj I changed the delay to 300 ms but it does not work.LE: If I comment the zero_crosss_int() and the attachinterrupt() in setup() then it displays normally, but then the circuit cannot work because I commented the ISR.Is any solution to this problem ?

author: Siwastaja
author_rank: Super Contributor
timestamp: July 31, 2020, 12:53:43 pm
content: Turn an IO pin on (not using DigitalWrite(), but directly to writing to PORTx register) at the beginning of the interrupt handler and again off as the last thing in interrupt handler. Look at this IO pin with oscillosscope. This gives you idea how much time you are spending in the interrupt, or possibly, if you are stuck on that interrupt repeating all over again.

author: DrG
author_rank: Super Contributor
timestamp: July 31, 2020, 01:42:18 pm
content: Quote from: mike_mike on July 31, 2020, 12:02:49 pmI think that the problem is your interrupt function. (edited to add: acknowledging that @Siwastaja was onto the same conclusion in the post before this one).Specifically,using delay() inside your interrupt function(seehttps://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/). But also, the amount of time spent in the interrupt.Since your program is hardware dependent and we don't have that setup.Help us help you by doing somesimple "break point" debuggingto start to zero in on where it is going bad. So, for example:You know that the LCD is "working" in setup(), now test whether it is working immediately after the attachInterrupt(digitalPinToInterrupt(2), zero_crosss_int, RISING); but while still inside setup().If that works, using that same technique (use the board LED is you want), continue along, checking that it is getting to each point. IOW if it gets to the ISR, does it ever leave?Let us know what happens.Edited to add:The ISR should be extremely short. Something like a simple incrementing of a counter with the variable defined as volatile). In main() you check if the counter >0 and if so, do your thing and decrement the counter.Now, this may blow all of your time-intensive processing for zero crossing, but you will not miss crosses (they will get stacked). Resolution, if that is the problem, will require thinking about a new design.

author: mike_mike
author_rank: Super Contributor
timestamp: July 31, 2020, 08:29:42 pm
content: Ok, so I understood that the sketch will not work if I will use delay().I found on the internet a code and I adapted it to my circuit.Now it display correct, but the soldering iron is not heating up anymore. The temperature of the soldering iron remains at the room temperature. If I touch the tip, it is cold.The code is:Code://source: http://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/?lang=pt&ALLSTEPS//Arduino controlled light dimmer: The software III//The code below has been confirmed to work on the Leonardo/*AC Light ControlUpdated by Robert TwomeyChanged zero-crossing detection to look for RISING edge ratherthan falling. (originally it was only chopping the negative halfof the AC wave form).Also changed the dim_check() to turn on the Triac, leaving it onuntil the zero_cross_detect() turn's it off.Adapted from sketch by Ryan McLaughlin<a href="http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230333861/30" rel="nofollow"> <a rel="nofollow"> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1...</a>>(now here: <a rel="nofollow"> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1...</a>*/#include <TimerOne.h> // Avaiable from <a href="http://www.arduino.cc/playground/Code/Timer1" rel="nofollow"> <a href="http://www.arduino.cc/playground/Code/Timer1" rel="nofollow"> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1...</a>#include <PID_v1.h>#include <max6675.h>#include <LiquidCrystal.h>#include <SPI.h>#include <Wire.h>#define thermoDO 12#define thermoCS 10#define thermoCLK 13#define potentiometer A0#define zerocrossing 2#define relay A1float realTemperature;int pottemperature;LiquidCrystal lcd(3, 4, 5, 6, 8, 9);byte thermometer = //icon for termometer{B00100,B01010,B01010,B01110,B01110,B11111,B11111,B01110};byte arrow = //icon for arrow{B11000,B01100,B00110,B00011,B00011,B00110,B01100,B11000};MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);double Setpoint, Input, Output;double aggKp = 4, aggKi = 0.2, aggKd = 1;double consKp = 1, consKi = 0.05, consKd = 0.25;PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT);volatile int i = 0; // Variable to use as a countervolatile boolean zero_cross = 0; // Boolean to store a "switch" to tell us if we have crossed zeroint triac = 7; // Output to Opto Triacint dim = 0; // Dimming level (0-128) 0 = on, 128 = 0ffint inc = 1; // counting up or down, 1=up, -1=downint freqStep = 75; // This is the delay-per-brightness step in microseconds.// For 60 Hz it should be 65// It is calculated based on the frequency of your voltage supply (50Hz or 60Hz)// and the number of brightness steps you want.//// Realize that there are 2 zerocrossing per cycle. This means// zero crossing happens at 120Hz for a 60Hz supply or 100Hz for a 50Hz supply.// To calculate freqStep divide the length of one full half-wave of the power// cycle (in microseconds) by the number of brightness steps.//// (120 Hz=8333uS) / 128 brightness steps = 65 uS / brightness step// (100Hz=10000uS) / 128 steps = 75uS/stepvoid setup() {lcd.begin(16, 2);lcd.createChar(0, thermometer);lcd.createChar(1, arrow);lcd.setCursor(0, 0);lcd.print("STATIE DE LIPIT");myPID.SetMode(AUTOMATIC);myPID.SetOutputLimits(0, 128);pinMode(relay, OUTPUT);pinMode(potentiometer, INPUT);pinMode(zerocrossing, INPUT_PULLUP);pinMode(triac, OUTPUT); // Set the Triac pin as outputdelay(1200);lcd.clear();digitalWrite(triac, LOW);digitalWrite(relay, HIGH);attachInterrupt(digitalPinToInterrupt(2), zero_cross_detect, RISING); // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross DetectionTimer1.initialize(freqStep); // Initialize TimerOne library for the freq we needTimer1.attachInterrupt(dim_check, freqStep);// Use the TimerOne Library to attach an interrupt// to the function we use to check to see if it is// the right time to fire the triac. This function// will now run every freqStep in microseconds.}void zero_cross_detect() {zero_cross = true; // set the boolean to true to tell our dimming function that a zero cross has occuredi = 0;digitalWrite(triac, LOW); // turn off TRIAC (and AC)}// Turn on the TRIAC at the appropriate timevoid dim_check() {if (zero_cross == true) {if (i >= dim) {digitalWrite(triac, HIGH); // turn on lighti = 0; // reset time step counterzero_cross = false; //reset zero cross detection}else {i++; // increment time step counter}}}void loop() {pottemperature = analogRead(potentiometer);Setpoint = map(pottemperature, 0, 1023, 150, 400); //pottemperature is volatilerealTemperature = thermocouple.readCelsius();Input = int(0.779828 * realTemperature - 10.3427); // make temperature an integerif (isnan(realTemperature) || Input >= 432){while(true){displayErrors();}} else {updateDisplay();}double gap = abs(Setpoint - Input); //distance away from setpointif (gap < 10){ //we're close to setpoint, use conservative tuning parametersmyPID.SetTunings(consKp, consKi, consKd);}else{//we're far from setpoint, use aggressive tuning parametersmyPID.SetTunings(aggKp, aggKi, aggKd);}myPID.Compute();dim = Output;delay(300);}void updateDisplay() {lcd.clear();lcd.setCursor(0, 0);lcd.write((byte)0);lcd.setCursor(2, 0);lcd.print((int)Setpoint);lcd.setCursor(6, 0);lcd.print((char)223); //degree signlcd.setCursor(7, 0);lcd.print("C");lcd.setCursor(0, 1);lcd.write((byte)1);if (Input <= 45) {lcd.setCursor(2, 1);lcd.print("Lo");} else {lcd.setCursor(2, 1);lcd.print((int)Input);}lcd.setCursor(6, 1);lcd.print("[");lcd.setCursor(7, 1);lcd.print((int)realTemperature);lcd.setCursor(10, 1);lcd.print("]");lcd.setCursor(12, 1);lcd.print((char)223);lcd.setCursor(13, 1);lcd.print("C");}void displayErrors() {digitalWrite(relay, LOW);lcd.clear();lcd.setCursor(0, 0);lcd.write((byte)0);lcd.setCursor(1, 0);lcd.write((byte)0);lcd.setCursor(5, 0);lcd.print("ERROR!");lcd.setCursor(14, 0);lcd.write((byte)0);lcd.setCursor(15, 0);lcd.write((byte)0);}I tried to make the "dim" variable volatile, I tried to use noInterrupts() and interrupts() when copying the Output value to dim value, but the problem remained.What do you think ? Where I am wrong ?To be honest, I am working for about 2 weeks at this project, without any good results.

author: DrG
author_rank: Super Contributor
timestamp: July 31, 2020, 08:45:57 pm
content: Quote from: mike_mike on July 31, 2020, 08:29:42 pmI think your reach is exceeding your grasp. Working for two weeks on a project with limited success is not a sin and is something that has happened to me (and I suspect many others).I would go back to your original code and also try to explain to us exactly what you want to happen. You are trying to get zero-crossing on the AC signal to trigger the time of a pulse toward increasing and decreasing the current to the soldering iron? I don't know enough about that in a practical sense to be of much help or even to know if I said it correctly - others here can be of help.I don't mean to be condescending, but, you started the thread with an LCD problem, but now we (or at least I) need to understand exactly what the program is trying to do. Can you clearly tell us?Edited to add:Mike, I just saw this after responding. Is this your project in this thread from 3 months ago?https://forum.allaboutcircuits.com/threads/arduino-pid-soldering-station-with-zero-crossing-detector.169205/Is this a homework typ project?I will still help if I can, but I would like to know theanswers to those two questions please.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 31, 2020, 09:05:31 pm
content: Keep working on it. You have a complex project (for a beginner) with many aspects, so don't expect easy results. You are learning so many things at the same time, it will easily take a few more weeks, or months, but the result is worth it; and I don't mean the soldering iron project, but everything you learn.It's an iterative process of trying out all things you can imagine by yourself - be creative -, and every time this fails on you, then Reading The Damn Documentation, and googling for help, rinse and repeat, really.Even as quite experienced designer, I almost always expectanyproject, no matter how simple ("it's a 4-hour job") it seems first, to stall for 2 weeks due to any strange problem that needs to be investigated, root cause analyzed and fixed. It's just work that needs to be done, and do it properly, and you are not having the exact same trouble ever again.

author: mike_mike
author_rank: Super Contributor
timestamp: July 31, 2020, 09:24:20 pm
content: Quote from: DrG on July 31, 2020, 08:45:57 pm1. The program is trying to control using PID algorithm a soldering iron. The program will read the data form MAX6675 module, then the data from the potentiometer, and will calculate PID, using the Kp, Ki and Kd parameters, and then will drive the output in the phase control mode. I am trying to say that it will detect the zero crossing moment, then it will wait an amount of time and then it will fire the triac.2. Yes, it is.3. No, it is not a homework project. I am working electronics as a hobby...Thank you all for the encouragements.

author: DrG
author_rank: Super Contributor
timestamp: July 31, 2020, 09:33:18 pm
content: ok, fair enough and thanks for the straightforward response.I would suggest that you write a simplified program that drops the PID and pot and max completely. It should simply fire the triac at set times after detecting zero crossing. I don't know what HZ you have but just dividing it so that you have something like 25%, 50% and 75% "duty" and see if the soldering iron responds reasonably. A completely seperate program that has nothing else in there.I am not saying that I am right or that I have the only way to do it, but...I always sketch out the program requirements and then start working on slices to make sure that the code and the hardware can do what I think they can. I have never had success trying to put together a largish program as a single shot - too many places for screw ups that are too hard to find.

author: mike_mike
author_rank: Super Contributor
timestamp: August 01, 2020, 07:56:42 am
content: I made a few tests, as you said.1. I made a short program, which increased and decreased the firing angle automatically, form 0 to 128.2. I connected the pot, the lcd and the max6675, and I modified the program and as I rotated the pot, the firing angle increased and if I rotated the pot the firing angle decreased. On the display, all of the values were shown. - so it worked3. When I introduced the PID algorithm, then it doesn't heated up anymore the soldering iron.LE: I used this "dim = map(Output, 0, 128, 128, 0);" instead of "dim = Output" , and it seems to work. I don't know if it is correctly, probably I should write " myPID.SetOutputLimits(128, 0);" instead of " myPID.SetOutputLimits(0, 128);" ?Code://source: http://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/?lang=pt&ALLSTEPS//Arduino controlled light dimmer: The software III//The code below has been confirmed to work on the Leonardo/*AC Light ControlUpdated by Robert TwomeyChanged zero-crossing detection to look for RISING edge ratherthan falling. (originally it was only chopping the negative halfof the AC wave form).Also changed the dim_check() to turn on the Triac, leaving it onuntil the zero_cross_detect() turn's it off.Adapted from sketch by Ryan McLaughlin<a href="http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230333861/30" rel="nofollow"> <a rel="nofollow"> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1...</a>>(now here: <a rel="nofollow"> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1...</a>*/#include <TimerOne.h> // Avaiable from <a href="http://www.arduino.cc/playground/Code/Timer1" rel="nofollow"> <a href="http://www.arduino.cc/playground/Code/Timer1" rel="nofollow"> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1...</a>#include <PID_v1.h>#include <max6675.h>#include <LiquidCrystal.h>#include <SPI.h>#include <Wire.h>//#define thermoDO 12#define thermoCS 10#define thermoCLK 13#define potentiometer A0#define zerocrossing 2#define relay A1//float realTemperature;int pottemperature;//LiquidCrystal lcd(3, 4, 5, 6, 8, 9);byte thermometer = //icon for termometer{B00100,B01010,B01010,B01110,B01110,B11111,B11111,B01110};byte arrow = //icon for arrow{B11000,B01100,B00110,B00011,B00011,B00110,B01100,B11000};MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);//double Setpoint, Input, Output;double aggKp = 4, aggKi = 0.2, aggKd = 1;double consKp = 1, consKi = 0.05, consKd = 0.25;PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT);volatile int i = 0; // Variable to use as a countervolatile boolean zero_cross = 0; // Boolean to store a "switch" to tell us if we have crossed zeroint triac = 7; // Output to Opto Triacint dim = 0; // Dimming level (0-128) 0 = on, 128 = 0ffint inc = 1; // counting up or down, 1=up, -1=downint freqStep = 75; // This is the delay-per-brightness step in microseconds.// For 60 Hz it should be 65// It is calculated based on the frequency of your voltage supply (50Hz or 60Hz)// and the number of brightness steps you want.//// Realize that there are 2 zerocrossing per cycle. This means// zero crossing happens at 120Hz for a 60Hz supply or 100Hz for a 50Hz supply.// To calculate freqStep divide the length of one full half-wave of the power// cycle (in microseconds) by the number of brightness steps.//// (120 Hz=8333uS) / 128 brightness steps = 65 uS / brightness step// (100Hz=10000uS) / 128 steps = 75uS/stepvoid setup() {lcd.begin(16, 2);lcd.createChar(0, thermometer);lcd.createChar(1, arrow);lcd.setCursor(0, 0);lcd.print("STATIE DE LIPIT");myPID.SetMode(AUTOMATIC);myPID.SetOutputLimits(0, 128);pinMode(relay, OUTPUT);pinMode(potentiometer, INPUT);pinMode(zerocrossing, INPUT_PULLUP);pinMode(triac, OUTPUT); // Set the Triac pin as outputdelay(1200);lcd.clear();// digitalWrite(triac, LOW);digitalWrite(relay, HIGH);attachInterrupt(digitalPinToInterrupt(2), zero_cross_detect, RISING); // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross DetectionTimer1.initialize(freqStep); // Initialize TimerOne library for the freq we needTimer1.attachInterrupt(dim_check, freqStep);// Use the TimerOne Library to attach an interrupt// to the function we use to check to see if it is// the right time to fire the triac. This function// will now run every freqStep in microseconds.}void zero_cross_detect() {zero_cross = true; // set the boolean to true to tell our dimming function that a zero cross has occuredi = 0;digitalWrite(triac, LOW); // turn off TRIAC (and AC)}// Turn on the TRIAC at the appropriate timevoid dim_check() {if (zero_cross == true) {if (i >= dim) {digitalWrite(triac, HIGH); // turn on lighti = 0; // reset time step counterzero_cross = false; //reset zero cross detection}else {i++; // increment time step counter}}}void loop() {//delay(18);pottemperature = analogRead(potentiometer);Setpoint = map(pottemperature, 0, 1023, 150, 400); //pottemperature is volatilerealTemperature = thermocouple.readCelsius();Input = int(0.779828 * realTemperature - 10.3427); // make temperature an integer// dim = map(Setpoint, 150, 400, 0, 128);// if (dim >= 128) dim = 128;// if (dim <= 0) dim = 0;if (isnan(realTemperature) || Input >= 432) {while (true) {displayErrors();}} else {updateDisplay();}double gap = abs(Setpoint - Input); //distance away from setpointif (gap < 10){ //we're close to setpoint, use conservative tuning parametersmyPID.SetTunings(consKp, consKi, consKd);}else{//we're far from setpoint, use aggressive tuning parametersmyPID.SetTunings(aggKp, aggKi, aggKd);}myPID.Compute();dim = map(Output, 0, 128, 128, 0);delay(300);}void updateDisplay() {lcd.clear();lcd.setCursor(0, 0);lcd.write((byte)0);lcd.setCursor(2, 0);lcd.print((int)Setpoint);lcd.setCursor(6, 0);lcd.print((char)223); //degree signlcd.setCursor(7, 0);lcd.print("C");lcd.setCursor(0, 1);lcd.write((byte)1);if (Input <= 45) {lcd.setCursor(2, 1);lcd.print("Lo");} else {lcd.setCursor(2, 1);lcd.print((int)Input);}lcd.setCursor(6, 1);lcd.print("[");lcd.setCursor(7, 1);lcd.print((int)realTemperature);lcd.setCursor(10, 1);lcd.print("]");lcd.setCursor(12, 1);lcd.print((char)223);lcd.setCursor(13, 1);lcd.print("C");}void displayErrors() {digitalWrite(relay, LOW);lcd.clear();lcd.setCursor(0, 0);lcd.write((byte)0);lcd.setCursor(1, 0);lcd.write((byte)0);lcd.setCursor(5, 0);lcd.print("ERROR!");lcd.setCursor(14, 0);lcd.write((byte)0);lcd.setCursor(15, 0);lcd.write((byte)0);delay(300);}In the screenshots, with yellow is the zero crossing signal (pin 2) and with blue is the output (pin 7).The latest code is above.Please have a look and tell me what you think ?

author: Siwastaja
author_rank: Super Contributor
timestamp: August 01, 2020, 08:57:48 am
content: I'm assuming your phase angle control works now because you didn't mention about any problem with it?Using a PID library is quite questionable, because now it becomes a black box, and as you can obviously see, it Just Doesn't Work and you have no idea why.PID itself is just a few mathematical operations, a few lines of code.Start writing your own PID, step by step, verifying what happens every step.Real-world PID is often FFPID: feedforward, P, I, and D.Guess what? You already implemented feedforward:Quote from: mike_mike on August 01, 2020, 07:56:42 amIn other words:"pot value" is called "setpoint" (SP)Temperature measurement is called "process value" (PV)Firing angle is called plant output (output)Now, your first-step code is:output = k_ff * SP,where k_ff is some multiplier constant.This is what you already have according to your description, think about it and play around with the idea. Maybe write it down more explicitly.The next step is to give visibility to the system so you don't have to poke in dark. k_ff is constant, no need to print that; you are already printing the SP (pot value) on the screen. Now print the result of k_ff*SP, and value of the output (which are the same value currently, of course, but you will be adding terms to the calculation of output, and it makes sense to print the results of intermediate calculations).Having done that, the next step is to add P. Think about it and try it yourself.

author: mike_mike
author_rank: Super Contributor
timestamp: August 01, 2020, 09:23:45 am
content: @Siwastaja I already tried to implement my own PID, using some help, information and lines of code from the internet, but the problem was that the first PID it was not stable at all when I introduced the Ki constant, and the second PID had a huge overshoot (of about 50 C). Probably I was missing something.Please find attached the wave form from the heater.

author: Siwastaja
author_rank: Super Contributor
timestamp: August 01, 2020, 09:44:51 am
content: Understanding the PID and how it's tuned is not trivial!It's not made any easier by thetotallack of discussion about feedforward in 99% of the literature, tutorials, and internet comments. Sometimes you can't make PID stable without FF, at all, in motor speed control for example. Other times, it is possible, but more difficult.If you are working in floating point, great, that simplifies your job. If you work in fixed point integers, make extra sure you accidentally don't overflow intermediate calculations, or round them towards too low precision. For such low-speed thing, it's a good idea to start by using thedoubledatatype for all calculations.Now, if your PID isn't stable with Ki introduced, changing from one PID implementation to another likely won't fix it.Start by setting FF, P, I and D all to zero.Print out, in real time:obviously, the current setpoint and the current process value (measured temperature)error (SP-PV)error integral accumulator valueff contribution (Kff * SP)P contribution (Kp * error)I contribution (Ki * error integral accumulator value)Total output (ff contribution + P contribution + I contribution)Now, through the UART interface which you almost always have available, add yourself a possibility to adjust Kff, Kp and Ki in real time (print out the values). I prefer single key presses, like p to decrease, P to increase P; i to decrease, I to increase I, and so on.Now with visibility and adjustability in place, start playing around. First bring just P up, to make a pure P controller, see how it works. Find the point where you can make it overshoot and oscillate (though oscillation will be slow with a soldering iron).Then bring P to zero, bring I up alone, to make a pure I controller. Try to makethatovershoot and oscillate.Finally, try combining these. Bring just a little bit of I so that the temperature reaches the setpoint. Now add about half of the P what made it oscillate.You just can't gain understanding to the PID without playing around in realtime, and you can't do that if you don't have visibility and controllability to the algorithm.Finally, you may want to limit the integral accumulator variable so it doesn't grow towards infinity when the plant output value is saturated. Many ways to do that, one very simple is just to limit the accumulator variable: if(i_acc > LIMIT) i_acc = limit; else if(i_acc < -LIMIT) i_acc = -LIMIT; Visibility helps you to set the limit so that the accumulator saturates within some (tens?) of seconds, or so. This isn't the perfect solution, but should get you started.

author: mike_mike
author_rank: Super Contributor
timestamp: August 01, 2020, 11:18:46 am
content: Quote from: Siwastaja on August 01, 2020, 09:44:51 amThe single possibility for me, is to display the values on the computer screen, and modify them in the code. But, I read that this is not possible (serialprint) when using ISR.Somebody explained to me what should happen when increasing/decreasing P or I or D constants.Should it be easier by using Ziegler-Nichols or another tuning method ?It is interesting that when using PID_v1 then the controller works but when using the custom PID controller, it doesn't.The values for Kp, Ki, Kd (both aggressive and conservative) were took from a project found on internet, which used Mosfet instead of triac and a SMPS instead of transformer, but that project worked good for the guy who built it.

author: Siwastaja
author_rank: Super Contributor
timestamp: August 01, 2020, 11:58:46 am
content: It seems you are hitting very hard against the limitations of the Arduino libraries, either because they can't do what you want them to do, or they end up not being as easy to use as you would have expected. They are, by definition by the Arduino company themselves, meant for artists.There is absolutely no problem using USART in a system with ISRs running. Quite the opposite: running the timing-critical work in ISRs allow you to use simple blocking USART printing, for example.This is the full implementation for USART from some of my random old ATMega project:Code:#define print_char(byte) {while((UCSRA & 0b00100000) == 0) ; UDR = (byte); }static void print_string(const char* str){while(str != 0){print_char(str);str++;}}ISR(USART_RXC_vect){char byte = UDR;if(byte == 'P')...}main(){UCSRB = 0b10011000;UCSRC = 0b10000110;UBRRL = 3; // 115200 bpssei();}Or you could choose not enable the USART interrupt but look at the USART status flag of received byte in your main loop, then act accordingly there. OK for simple key presses where it doesnt' matter if you miss quickly repeated presses...I don't know about Ziegler-Nichols, I have never tried it myself, but I really think you should get a "grasp" on what the P, I, D, and FF actually do by first-hand experimentation, before you go on learning more algorithms. PID in itself is already quite demanding, which is exactly why you need to demystify it for yourself. There's no better way than looking what the terms do in isolation, then starting to combine them.In any case, getting visibility into the system is #1 priority, it will allow you to work on it, regardless of what algorithms, libraries or whatnot you decide end up using.

author: Rick Law
author_rank: Super Contributor
timestamp: August 01, 2020, 09:50:28 pm
content: It may be presumptuous for me to do so, but let me share some from my bag-of-tricks. I found it less problem prone with Arduino when I do the following:(1) Do the minimum in the interrupt handler, set a flag, clear interupt and exit. Let loop() handle the stuff that can wait.(2) To do the above, your loop can't have a long delay(). So best to adopt a state machine kind of approach. A lot easier to debug also.loop() {{thisMsReading = millis(); // make sure time spend in loop doesn't alter elapsed timeif (thisFlagIs on) {do what this flag does;// one of these flags may be a certain interrupt occurred.// now doing the rest of what needs doing}if (digitalRead(nnn) is changed from last read) {do what that change is suppose to do;}// now do the stuff that is time-elasped based (instead of a delay in the loop())//keyPadCheck(thisMsReading); // I usually have a keypad that gets checked every 50ms, but it shall decide//// now the other elapsed time based stuff// say you have an led blinking 1/2 seconds whenever the temperature is > X or something like that,if (thisMsReading - lastLEDSwitched > xyz) { // time elapsed reach, check temperature and/or blink// do the temperature check to turn off or flip to blink}//// now the final catch-allif (thisMsReading - lastMsReading > nnnn) {nnnn milliseconds passed, do what needs done;}////// I usually have a catch-all at the end doing at least serial for debugging (once every second or so// when ON) with thisMsReading+currentStatuses so I can see the program progress//lastMsReading = thisMsReading; // any time spend in loop doesn't add to the elasped time for next}(3) With the LCDs (I use mainly 2004, but I do have one 1602), they are rather slow. A clear and then write produces too much blinking. I hardly ever use clear() at all. I use fixed fields (blank filling the rest) - the write automatically overwrite the old stuff. The blank fill would erase left overs. For example, in cases like a new value "14" writing over the old value "163", you would have a "3" left over that the blank fill would erase. This not-clear but overwrite Looks a lot nicer and rock solid instead of the jitters and blinks one would see with clear and write.I am sure as you move along, you will have your little bag of tricks too. Just sharing with you what is in my bag, hopefully, you'll find it useful.EDIT: the code part to make it a bit easier to read

author: mike_mike
author_rank: Super Contributor
timestamp: August 02, 2020, 09:53:02 am
content: @Rick Law Do you mean something like this ?Code:void loop() {if ((unsigned long)(millis() - time_now) > period) {digitalWrite(test_pin, HIGH);time_now = millis();pottemperature = analogRead(potentiometer);Setpoint = map(pottemperature, 0, 1023, 150, 400); //pottemperature is volatilerealTemperature = thermocouple.readCelsius();Input = int(0.779828 * realTemperature - 10.3427); // make temperature an integerif (isnan(realTemperature) || Input >= 432) {while (true) {displayErrors();}} else {updateDisplay();}double gap = abs(Setpoint - Input); //distance away from setpointif (gap < 10){ //we're close to setpoint, use conservative tuning parametersmyPID.SetTunings(consKp, consKi, consKd);}else{//we're far from setpoint, use aggressive tuning parametersmyPID.SetTunings(aggKp, aggKi, aggKd);}myPID.Compute();dim = map(Output, 0, 128, 128, 0);digitalWrite(test_pin, LOW);}}

author: DrG
author_rank: Super Contributor
timestamp: August 02, 2020, 03:02:20 pm
content: Quote from: mike_mike on August 01, 2020, 11:18:46 amDo you mean this fellow's project?https://create.arduino.cc/projecthub/sfrwmaker/soldering-iron-controller-for-hakko-907-v-2-fc75d7https://github.com/sfrwmaker/soldering_907_lcdI was reading through that this morning, FWIW I thought it was pretty impressive.

author: Rick Law
author_rank: Super Contributor
timestamp: August 02, 2020, 09:29:32 pm
content: Quote from: mike_mike on August 02, 2020, 09:53:02 amYeah, something like that. You got rid of your delay(500) which is a good thing,more on that later. Now you can insert "higher priority" if's before your display codes -- such as checking a flag to do the left over work off an interrupt, or is it time to do sampling, so forth.Interrupt code cannot be messy, you have to make sure you get done with that ASAP. You also have to think about what if another interrupt occurs while you are doing this one? So forth.About the block of codeyou originally hadwith thatdelay(500):if (isnan(realTemperature) || Input >= 432) {while (true) {digitalWrite(relay, LOW); // the relay will disconnect the power to the soldering iron heating elementlcd.clear();lcd.setCursor(0, 0);lcd.write((byte)0);lcd.setCursor(1, 0);lcd.write((byte)0);lcd.setCursor(5, 0);lcd.print("ERROR!");lcd.setCursor(14, 0);lcd.write((byte)0);lcd.setCursor(15, 0);lcd.write((byte)0);delay(500);}}I can't for the world of me figure out when you are going to exit that while(true).Once the interrupt is over, you are returned back into this while(true) loop. You never exit it. In fact, once your loop() gets a -true- with that -if- statement, you are lock in this while() until the universe dies of heat-death.May be that was your intend and you want the interrupt to do all the work, may be not. Any infinite loop automatically rings my alarm bell.Confession time:I held "pointing out the infinite loop" back so you can consider the change to the program structure first, because once when a bug is pointed out, people tends to just go fix that and not think about other issues...But in changing the construct to do things time-based, you also appear to have illuminated that problem. I found that time-based task construct for loop() is much less problematic. You begin to think along the line of "for this task (say update display), I do it once every nnn milliseconds or update once when change; for sampling, I take one once every 10ms... Organizing tasks in those term (I personally believe) is clearer and less problem prone.Also, with this construct, you can more easily implement sampling more to increase resolution, or simply to decrease noise, or just to average out the jitters.loop() {if (flagInterrupted) {doThis ...}if (millSecNow - millSecLastSample > 10) { // once every 10ms, collect a sample... // either single// or get 2 and truncate away the bottom bit// or get 4 and truncate away the bottom 2 bits// or keep summing, the display code do the average and clears the counter/sum}if (...) <--- your update display loop......I personally found this construct a lot better thanloop() {do what you needdelay(nnn); // now sit on your hands, do nothing, just wait till it is time for next loop}Hope this help you too...

author: mike_mike
author_rank: Super Contributor
timestamp: August 03, 2020, 01:15:12 am
content: That infinite loop is to permanently display the error message and to permanently keep the heater disconnected by setting the output Relay pin to LOW, until I switch off the soldering station.

author: Rick Law
author_rank: Super Contributor
timestamp: August 03, 2020, 08:43:24 pm
content: Quote from: mike_mike on August 03, 2020, 01:15:12 amGood thing it is intended. Infinite loop automatically sends my blood pressure infinitely high. Your approach is a good use of that infinitely loop. May I suggest commenting the code as such - make it easier when you look at the code years later, may be...