{"id":9053,"date":"2021-11-25T13:15:32","date_gmt":"2021-11-25T13:15:32","guid":{"rendered":"https:\/\/bryceautomation.com\/?p=9053"},"modified":"2021-12-19T21:49:19","modified_gmt":"2021-12-19T21:49:19","slug":"arduino-pid-simulator","status":"publish","type":"post","link":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/","title":{"rendered":"Arduino PID Simulator"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">Introduction to Arduino PID Simulator<\/h3>\n\n\n\n<p>This project is an Arduino PID Simulator.    Basically, the Arduino will send a process variable to a PID controller.  In this case, the signal is 0 to 5v.   Additionally, the Arduino reads the control variable from the PID controller, and simulates a real world process.  Realize this is not a PID controller.   This is a field device to simulate a process.  I&#8217;ve written this project so simulate various types of processes.  <\/p><div id=\"bryce-3604393420\" class=\"bryce-afterfirst bryce-entity-placement\"><script async src=\"\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-8316758073402323\" crossorigin=\"anonymous\"><\/script><ins class=\"adsbygoogle\" style=\"display:block;\" data-ad-client=\"ca-pub-8316758073402323\" \ndata-ad-slot=\"7728240895\" \ndata-ad-format=\"auto\"><\/ins>\n<script> \n(adsbygoogle = window.adsbygoogle || []).push({}); \n<\/script>\n<\/div>\n\n\n\n<p>You can adjust load, losses,, lag, delay, process time, and noise.    Additionally, there is a setting to adjust the process time.  Because some real loads might take a very long time to tune, this project has the ability to speed up those processes for the purpose of training.  Unlike some software simulators, this project already has some inherent noise.  This is due to both the resolution of the analog output, converting PWM to analog, and some small amount of line noise.  This project is for the purpose of training and practice only.<\/p>\n\n\n\n<p>I would recommend building two of these units if you wish to practice cascading control.   The main difference with the second controller is that you will use a 5v Zener instead of one of the 5k resistors to limit the incoming control variable to 5v.  <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"485\" data-src=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-104-1024x485.png\" alt=\"\" class=\"wp-image-9345 lazyload\" data-srcset=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-104-1024x485.png 1024w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-104-300x142.png 300w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-104-768x364.png 768w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-104.png 1077w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/485;\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">What you Need<\/h3>\n\n\n\n<p>For this project, you will need a few components. <\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Arduino Board (or equivalent)<\/li><li>Two 5K resistors (for the voltage divider circuit on the Control Variable)  Use these if you anticipate a 10v signal on the control variable.  These resistors will be in series from the ControlVariable input to ground.  Take your CV signal from the point between the two resistors.  This will effectively be a 0 to 5v signal.<\/li><li>3.9k Resistor  <\/li><li>1Uf Capacitor (The resistor and capacitor are to convert the PWM to analog)<\/li><li>If you want to use Feed Forward, you need an additional 3.9k Resistor with another .1uF Capacitor.k<\/li><li>A TM1638 Lock &amp; Key unit.  This is a display with 8 segments, 8 buttons, and 8 LED&#8217;s.  Strobe connects to GPIO4, Clock to 6, and Data (DIO) to 7.<\/li><li>You also need some jumpers, and wire to connect things together.<\/li><li>A PID controller, such as a ControlLogix, SLC, or PLC-5 with Analog Input and Output ability (voltage)<\/li><li>A Breadboard<\/li><li>For Cascading, you also need a 5v Zener Diode.   In this case, you would build a second simulator, and the Control Variable would have a 5k resistor in series with the reverse biased Zener diode to ground.  Your CV signal will come from the point between the Zener and the 5k resistor.<\/li><li>You can filter the PV, and FF with a 100uF to 1000uF Capacitor.  100uF gives a faster response with a little more noise, whereas 1000uF gives a slower response and a smoother signal.<\/li><li>If you build a second unit for cascading, I would also recommend using a 220uF to 1000uF capacitor across the Control Variable input.  For cascading, wire the PV of the first simulator to the CV of the second simulator. <\/li><li>Note, your PID controller, such as a PLC will only write to the ControlVariable of the first simulator.  The PID controller will then read the Process Variable, and Feed Forward as analog inputs.<\/li><\/ul>\n\n\n\n<p>In my own project, I just used an Atmega328 processor with a 16MHz resonator.  This saved some money vs. purchasing an Arduino board.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the Arduino PID Simulator works<\/h3>\n\n\n\n<p>Compared to software simulators, this simulator needs a real voltage for the control variable.   Keep in mind, your limit is 5v.  If you plant to use a 10v output, simply set up a voltage divider.   I&#8217;ve tried to keep this controller as simple as possible to make it easy to customize the code.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>To begin with, the Arduino reads the CV from the controller.<\/li><li>This CV goes through an array of 100 elements.<\/li><li>At this time, the lag setting will choose which element of the array to use.  A lower setting is faster.  Likewise, a higher lag setting is slower.<\/li><li>Next, based on the CV, we&#8217;ll add 1 unit to the bulkpv variable for each percent of the CV per time unit.<\/li><li>On the other hand, loss and load will subtract from the bulkpv.  This is a sliding scale.  For example, if the PV is at 0, even though loss is 100, there will be no loss.  This is because if there is no pressure or temperature, the loss is minimum.   Likewise, the load works the same way.<\/li><li>At this point, the bulkpv variable goes into another array.   The delay variable selects which element of this array to use.   A lower delay will select a more recent PV to use.  <\/li><li>After that, we inject noise into the PV.   This is simply a random value based on the noise setting.   For example, if the PV is 50, and the noise is 5, you will get a randomized value between 45 and 55 for the PV.<\/li><li>At this time, the program writes the PV to the analog output channel of the Arduino.  Obviously, this will be the analog input of your PID controller.<\/li><li>** The Test button allows you to inject a PV of 0 and PV of 100.  This is to calibrate your scaling in the controller.   In addition, you can change the process time.  For example, a base setting of 60 could inject up to 100 units per minute into the bulkpv, and remove the same with combined loss and load at 100.   If you press the test button again, you have the option to reset the Atmega328.<\/li><li>** If you go to your Serial Monitor, you will see different values of the process.  The Serial Monitor updates every 1 Second.  Set it at 9600 baud.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Tuning the PID<\/h3>\n\n\n\n<p>To illustrate, you simply tune this PID like any other.   As you cause disturbances, simply increase the controller gain until it becomes unstable.  At this time, record the natural period of the oscillations (peak to peak).  Next, cut the Kp in half.  You will use the natural period to calculate the starting Integral and Derivative settings.  Most of the time, however, you may not need derivative.  It might, in fact, be detrimental to your process.  Especially if you have a noisy process variable.  <\/p>\n\n\n\n<p>For information on tuning PID, check out the <a href=\"https:\/\/bryceautomation.com\/index.php\/2017\/06\/15\/proportional-integral-derivative-pid\/\">CotnrolLogix PID post.<\/a>  It&#8217;s important to realize that different controllers have different units for your gains.  For example, if your controller uses minutes per repeat, the natural period itself is a good starting point for integral.  Derivative would be 1\/8th of that in minutes.  For other units, you will have to convert the values to match your processor, and PID equation type.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"932\" height=\"519\" data-src=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-20.png\" alt=\"\" class=\"wp-image-9060 lazyload\" data-srcset=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-20.png 932w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-20-300x167.png 300w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-20-768x428.png 768w\" data-sizes=\"(max-width: 932px) 100vw, 932px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 932px; --smush-placeholder-aspect-ratio: 932\/519;\" \/><\/figure>\n\n\n\n<p><strong><em>Note:  To eliminate the noise on the simulator&#8217;s PV, you could add a 1000uF capacitor across the PV terminals.  Be sure the polarity is correct.<\/em><\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Putting together the Arduino PID Simulator<\/h3>\n\n\n\n<p>Here, I am mounting the TM1638 display unit into a 3d printed box.   I simply found a good TM1638 case and project box on <a href=\"http:\/\/thingiverse.com\">thingiverse<\/a>.  You might use <a href=\"http:\/\/tinkercad.com\">tinkercad<\/a> to to put your own face on the box as I did in this case.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"686\" height=\"349\" data-src=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png\" alt=\"\" class=\"wp-image-9062 lazyload\" data-srcset=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png 686w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21-300x153.png 300w\" data-sizes=\"(max-width: 686px) 100vw, 686px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 686px; --smush-placeholder-aspect-ratio: 686\/349;\" \/><\/figure>\n\n\n\n<p>In this case, I just put an atmega328 onto prototype paper.  <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"255\" height=\"340\" data-src=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-163.png\" alt=\"\" class=\"wp-image-9482 lazyload\" data-srcset=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-163.png 255w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-163-225x300.png 225w\" data-sizes=\"(max-width: 255px) 100vw, 255px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 255px; --smush-placeholder-aspect-ratio: 255\/340;\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Definitions I&#8217;ve used for the Arduino PID Simulator<\/h3>\n\n\n\n<p><strong>Load<\/strong>:  The amount of load on the system.  Obviously, the more load you have, the more output you will need.  In this case, the range is 0 to 100%.<\/p>\n\n\n\n<p><strong>Lag<\/strong>:  Basically, this is the process lag.  In the simulator, an array samples the control variable.  The last X samples will be averaged, while X equals the lag setting.  For example, a lag setting of 50 creates a lag time of 5 seconds.<\/p>\n\n\n\n<p><strong>Loss<\/strong>:  This will simulate ambient losses.  The range is 0 to 100%.<\/p>\n\n\n\n<p><strong>Noise<\/strong>:  The noise setting is the % of random noise added to the process variable.<\/p>\n\n\n\n<p><strong>Delay<\/strong>:  In this case, we simulate the delay in reading the feedback (the process variable).<\/p>\n\n\n\n<p><strong>Test<\/strong>:  There are several options under test that I did not have enough buttons for on the display.  Initially, the first step of test will send out a control variable of 0%.  In the same way, pressing test again sends out a control variable of 100%.  This allows you to calibrate the controller.  Other settings are the time base, forward acting, and reset.  The time base is essentially the number of of real seconds in which one minute passes on the simulator.<\/p>\n\n\n\n<p><strong>Process Variable: <\/strong> This is the feedback from the simulator, and connects to the controllers analog input.<\/p>\n\n\n\n<p><strong>Control Variable:<\/strong>  This is the analog output from the controller to the simulator.<\/p>\n\n\n\n<p><strong>Feed Forward: <\/strong> This is simply the load percentage, and connects to another analog input on the controller.<\/p>\n\n\n\n<p>Here is the response under 80% load with otherwise default settings.  Expect a little bit of noise since the Atmega328 has a fairly low resolution, and we are using a 0 to 5v signal for the process variable.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"837\" height=\"458\" data-src=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image.png\" alt=\"\" class=\"wp-image-9090 lazyload\" data-srcset=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image.png 837w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-300x164.png 300w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-768x420.png 768w\" data-sizes=\"(max-width: 837px) 100vw, 837px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 837px; --smush-placeholder-aspect-ratio: 837\/458;\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Cascading Configuration<\/h3>\n\n\n\n<p>Keep in mind, that if you wish to use a cascading configuration, you need a second controller.  Remember the changes on the second controller.  Instead of a voltage divider for the PV, we&#8217;ll use a 5K resistor in series with a 5v Zener diode.  This just protects the simulator from over voltage.  Additionally, you can add a 220uF to 1000uF capacitor across the incoming Control Variable.  This just filters the PWM signal from the first simulator.  Connect the PV of the first controller to the CV of the second controller.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"565\" height=\"275\" data-src=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-105.png\" alt=\"\" class=\"wp-image-9351 lazyload\" data-srcset=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-105.png 565w, https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/12\/image-105-300x146.png 300w\" data-sizes=\"(max-width: 565px) 100vw, 565px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 565px; --smush-placeholder-aspect-ratio: 565\/275;\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">The code for the Arduino PID Simulator<\/h3>\n\n\n\n<p>Although I&#8217;m sure there are better ways to write the code, this seems to work well.   I&#8217;m sure I will be changing the code, and updating this in the future.  The code is simple, and should be easy to follow.   Some of the variables are not currently in use anymore.   I will remove them in the future if I decide not to use them.<\/p>\n\n\n\n<p>If you are using two simulators in a cascading configuration, download to the project to the first controller.  Next, change the standalone value to 0, then download to the second controller.  This simply changes the default settings that are better for the cascading controller.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\/\/ Written by Ricky Bryce with addition of TM1638Plus library;\r\n\/\/ Version 0.5.3\r\n\r\n\r\n\r\n\/\/******* Set this definition before you download ********\/\/ Normal 1\r\n#define standalone 1   \/\/1 for Standalone or slave, 0 for master in cascading loop\r\n\/\/********************************************************\/\/\r\n#if standalone != 1\r\n\/\/ Standalone or Slave Mode\r\nint timeunits = 2; \/\/ Default 2\r\nint timeconstant = 500; \/\/ default 500\r\nint processtype = 1;\r\nint lag = 0;       \/\/ default 1\r\n#else\r\n\/\/  Level Mode\r\nint timeunits = 2;  \/\/ default 2\r\nint timeconstant = 250; \/\/ default 250\r\nint processtype = 0;\r\nint lag = 15;       \/\/ default 15\r\n#endif\r\n\r\n\/\/ Default Settings for Simulation\r\n\r\nint forwardacting = 0; \/\/ default 0 (Reverse acting)\r\nint load = 50;      \/\/ default 50\r\nint loss = 10;     \/\/ default 10\r\nint pvnoise = 0;   \/\/ default 0\r\nint pvdelay = 0;   \/\/ default 1\r\n\r\nint maxpv = 120;\r\n\r\n\/\/ Display\r\nchar displayformat&#91;13];\r\nint cvdisplay = 0;\r\nint pvdisplay = 0;\r\n\r\n\/\/ Buttons \/ Pins\r\nint resetPin = 12;\r\nint processvariablePin = 9;\r\nint  feedforwardPin = 10;\r\nuint8_t tmbuttons = 0;\r\nint decbutton = 0;\r\nint prevtmbuttons = 0;\r\n\/\/ Note:  TM1638 Strobe:4  Clock: 6  DIO: 7\r\n\/\/ LED wired to GPIO 13 for future use\r\n\r\n\/\/ Modes\r\nint mode = 0;\r\nint prevmode = 0;\r\nint teststep = 0;\r\nint lockchannels = 0;\r\nbool modeflag = 0;\r\nint modereq = 0;\r\nint resetproc = 0;\r\n\r\n\/\/ PID Variables\r\nfloat controlvariable = 50;\r\nfloat processvariable = 0;\r\nfloat actualpower = 0;\r\nfloat actualload = 0;\r\nfloat bulkpv = 0;\r\nlong lagcvsum = 0;\r\nfloat pvafterdelay = 0;\r\nfloat cvafterlag = 0;\r\nfloat pvafternoise = 0;\r\nfloat pvdelayarray&#91;101];\r\nfloat cvlagarray&#91;101];\r\nfloat loadeffect = 0;\r\n\r\n\/\/ Timers\r\nlong currentmillis = 0;\r\nlong prevmillis = 0;\r\nlong lagdelaymillis = 0;\r\nlong displaymillis = 0;\r\nlong calculationmillis = 0;\r\nlong pvdelaymillis = 0;\r\nlong buttonrepeatmillis = 0;\r\nlong serialmillis = 0;\r\n\r\n#include &lt;TM1638plus.h>\r\n\r\n#define  STROBE_TM 4 \/\/ strobe = GPIO connected to strobe line of module\r\n#define  CLOCK_TM 6  \/\/ clock = GPIO connected to clock line of module\r\n#define  DIO_TM 7 \/\/ data = GPIO connected to data line of module\r\nbool high_freq = false; \/\/default false, If using a high freq CPU > ~100 MHZ set to true.\r\n\/\/Constructor object\r\nTM1638plus tm(STROBE_TM, CLOCK_TM , DIO_TM, high_freq);\r\n\r\n\r\nvoid setup() {\r\n  digitalWrite(resetPin, HIGH);\r\n  delay(200);\r\n  pinMode(resetPin, OUTPUT);\r\n  pinMode(feedforwardPin, OUTPUT);\r\n  pinMode(processvariablePin, OUTPUT);\r\n  Serial.begin(9600);\r\n  delay(100);\r\n  Serial.println(\"--Comms UP -- PID Simulation -- Reverse Acting--\");\r\n  tm.displayBegin();\r\n  tm.brightness(1);\r\n  tm.displayText(\"pid rdy \");\r\n  delay(1000);\r\n\r\n}\r\n\r\nvoid loop() {\r\n  currentmillis = millis();\r\n\r\n  tmbuttons = tm.readButtons();\r\n  if (tmbuttons == 0) {\r\n    decbutton = 0;\r\n    prevmillis = currentmillis;\r\n  }\r\n\r\n  if (tmbuttons != prevtmbuttons) {\r\n    switch (tmbuttons)\r\n    {\r\n      case 0: decbutton = 0; break;   \/\/ No Buttons Pressed\r\n      case 1: decbutton = 1; break;   \/\/ Change Load\r\n      case 2: decbutton = 2; break;   \/\/ Change Lag\r\n      case 4: decbutton = 3; break;   \/\/ Change Loss\r\n      case 8: decbutton = 4; break;   \/\/ Change Noise\r\n      case 16: decbutton = 5; break;  \/\/ Change Delay\r\n      case 32: decbutton = 6; break;  \/\/ Run Test\r\n      case 64: decbutton = 7; break;  \/\/ Up Button\r\n      case 128: decbutton = 8; break; \/\/ Down Button\r\n\r\n    }\r\n  } else {\r\n    decbutton = 0;\r\n  }\r\n  \r\n  prevtmbuttons = tmbuttons;\r\n\r\n  \/\/set up repeating up\/down keys\r\n  if (((currentmillis - prevmillis) > 1000) and ((tmbuttons == 64) or (tmbuttons == 128))) {\r\n\r\n    if ((tmbuttons == 64) &amp; ((currentmillis - buttonrepeatmillis) > 40)) {\r\n      decbutton = 7;\r\n      buttonrepeatmillis = currentmillis;\r\n    }\r\n\r\n    if ((tmbuttons == 128) &amp; ((currentmillis - buttonrepeatmillis) > 40)) {\r\n      decbutton = 8;\r\n      buttonrepeatmillis = currentmillis;\r\n    }\r\n  } else {\r\n    buttonrepeatmillis = currentmillis;\r\n  }\r\n\r\n  if ((decbutton > 0) &amp; (decbutton &lt; 7)) {\r\n    mode = decbutton;\r\n    if (mode != prevmode) {\r\n      modeflag = 0;\r\n      tm.setLED((mode - 1), 1);\r\n      \r\n      for (int x = 0; x &lt; 7; x++) {\r\n        if (x != (mode - 1)) {\r\n          tm.setLED(x, 0);\r\n        }\r\n      }\r\n      prevmode = mode;\r\n    }\r\n  }\r\n\r\n  switch (mode)\r\n  {\r\n    case 0: modePID(); break;\r\n    case 1: modeLoad(); break;\r\n    case 2: modeLag(); break;\r\n    case 3: modeLoss(); break;\r\n    case 4: modeNoise(); break;\r\n    case 5: modeDelay(); break;\r\n    case 6: modeTest(); break;\r\n  }\r\n  if (mode == 0) {\r\n    for (int x = 0; x &lt; 7; x++) {\r\n      tm.setLED(x, 0);\r\n    }\r\n  }\r\n\r\n  \/\/ Unlock PV channel if not in test mode.\r\n  if (mode != 6) {\r\n    teststep = 0;\r\n    lockchannels = 0;\r\n  }\r\n\r\n  if ((currentmillis - calculationmillis) > timeconstant) {\r\n    CalculateLoad();\r\n    CalculatePower();\r\n    CalculateLoss();\r\n    CalculateNoise();\r\n    ProcessPID();\r\n    WriteSerial();\r\n    calculationmillis = currentmillis;\r\n  }\r\n\r\n  if ((currentmillis - lagdelaymillis) > (timeconstant \/ 100)) {\r\n    CalculateLagDelay();\r\n\r\n  }\r\n}\r\n\r\n\r\n\/\/********** Modes **********\/\/\r\nvoid modeLoad () {\r\n  \r\n  int mymode = 1;\r\n  tm.setLED((mymode - 1), 1);\r\n  if (decbutton == 0) {\r\n    modeflag = 1;\r\n  }\r\n\r\n  sprintf(displayformat, \"load %03d\", load);\r\n  tm.displayText(displayformat);\r\n  \r\n  if (decbutton == 7) {\r\n    if (load &lt; 100) {\r\n      load ++;\r\n    }\r\n  } else if (decbutton == 8) {\r\n    if (load > 0) {\r\n      load --;\r\n    }\r\n  }\r\n\r\n  if ((decbutton == mymode) &amp; (modeflag == 1)) {\r\n    modeflag = 0;\r\n    mode = 0;  \/\/ Switch to PID Mode\r\n    tm.setLED((mymode - 1), 0);\r\n  }\r\n}\r\n\r\n\r\nvoid modeLag () {\r\n  int mymode = 2;\r\n  tm.setLED((mymode - 1), 1);\r\n  if (decbutton == 0) {\r\n    modeflag = 1;\r\n  }\r\n\r\n  sprintf(displayformat, \" lag %03d\", lag);\r\n  tm.displayText(displayformat);\r\n  if (decbutton == 7) {\r\n    if (lag &lt; 100) {\r\n      lag ++;\r\n    }\r\n  } else if (decbutton == 8) {\r\n    if (lag > 0) {\r\n      lag --;\r\n    }\r\n  }\r\n  if ((decbutton == mymode) &amp; (modeflag == 1)) {\r\n    modeflag = 0;\r\n    mode = 0;  \/\/ Switch to PID Mode\r\n    tm.setLED((mymode - 1), 0);\r\n  }\r\n}\r\n\r\n\r\nvoid modeLoss () {\r\n  int mymode = 3;\r\n  tm.setLED((mymode - 1), 1);\r\n  if (decbutton == 0) {\r\n    modeflag = 1;\r\n  }\r\n\r\n  sprintf(displayformat, \"loss %03d\", loss);\r\n  tm.displayText(displayformat);\r\n  if (decbutton == 7) {\r\n    if (loss &lt; 100) {\r\n      loss ++;\r\n    }\r\n  } else if (decbutton == 8) {\r\n    if (loss > 0) {\r\n      loss --;\r\n    }\r\n  }\r\n  \r\n  if ((decbutton == mymode) &amp; (modeflag == 1)) {\r\n    modeflag = 0;\r\n    mode = 0;  \/\/ Switch to PID Mode\r\n    tm.setLED((mymode - 1), 0);\r\n\r\n  }\r\n}\r\n\r\n\r\nvoid modeNoise () {\r\n  int mymode = 4;\r\n  tm.setLED((mymode - 1), 1);\r\n  \r\n  if (decbutton == 0) {\r\n    modeflag = 1;\r\n  }\r\n\r\n  sprintf(displayformat, \" nse %03d\", pvnoise);\r\n  tm.displayText(displayformat);\r\n  if (decbutton == 7) {\r\n    if (pvnoise &lt; 100) {\r\n      pvnoise ++;\r\n    }\r\n  } else if (decbutton == 8) {\r\n    if (pvnoise > 0) {\r\n      pvnoise --;\r\n    }\r\n  }\r\n  \r\n  if ((decbutton == mymode) &amp; (modeflag == 1)) {\r\n    modeflag = 0;\r\n    mode = 0;  \/\/ Switch to PID Mode\r\n    tm.setLED((mymode - 1), 0);\r\n  }\r\n}\r\n\r\n\r\nvoid modeDelay () {\r\n  int mymode = 5;\r\n  tm.setLED((mymode - 1), 1);\r\n  if (decbutton == 0) {\r\n    modeflag = 1;\r\n  }\r\n\r\n  sprintf(displayformat, \" dly %03d\", pvdelay);\r\n  tm.displayText(displayformat);\r\n  \r\n  if (decbutton == 7) {\r\n    if (pvdelay &lt; 100) {\r\n      pvdelay ++;\r\n    }\r\n  } else if (decbutton == 8) {\r\n    if (pvdelay > 0) {\r\n      pvdelay --;\r\n    }\r\n  }\r\n\r\n  if ((decbutton == mymode) &amp; (modeflag == 1)) {\r\n    modeflag = 0;\r\n    mode = 0;  \/\/ Switch to PID Mode\r\n    tm.setLED((mymode - 1), 0);\r\n\r\n  }\r\n}\r\n\r\nvoid modeTest () {\r\n  int mymode = 6;\r\n  tm.setLED((mymode - 1), 1);\r\n  if (decbutton == 0) {\r\n    modeflag = 1;\r\n  }\r\n  \r\n  if (teststep == 0) {\r\n    lockchannels = 1;\r\n    processvariable = 0;\r\n    pvdisplay = processvariable;\r\n    cvdisplay = controlvariable;\r\n    sprintf(displayformat, \"%03d  %03d\", pvdisplay, cvdisplay);\r\n    tm.displayText(displayformat);\r\n  }\r\n  \r\n  if (teststep == 1) {\r\n    lockchannels = 1;\r\n    processvariable = 100;\r\n    pvdisplay = processvariable;\r\n    cvdisplay = controlvariable;\r\n    sprintf(displayformat, \"%03d  %03d\", pvdisplay, cvdisplay);\r\n    tm.displayText(displayformat);\r\n  }\r\n  \r\n  if (teststep == 2) {\r\n    lockchannels = 0;\r\n    sprintf(displayformat, \"base %03d\", timeunits);\r\n    tm.displayText(displayformat);\r\n    \r\n    if (decbutton == 7) {\r\n      if (timeunits &lt; 999) {\r\n        timeunits ++;\r\n      }\r\n    } else if (decbutton == 8) {\r\n      if (timeunits > 0) {\r\n        timeunits --;\r\n      }\r\n    }\r\n  }\r\n\r\n  if (teststep == 3) {\r\n    lockchannels = 0;\r\n    sprintf(displayformat, \" frd  %2d\", forwardacting);\r\n    tm.displayText(displayformat);\r\n    \r\n    if (decbutton == 7) {\r\n      if (forwardacting &lt; 1) {\r\n        forwardacting ++;\r\n      }\r\n    } else if (decbutton == 8) {\r\n      if (forwardacting > 0) {\r\n        forwardacting --;\r\n      }\r\n    }\r\n  }\r\n\r\n  if (teststep == 4) {\r\n    lockchannels = 0;\r\n    sprintf(displayformat, \"type  %2d\", processtype);\r\n    tm.displayText(displayformat);\r\n    \r\n    if (decbutton == 7) {\r\n      if (processtype &lt; 1) {\r\n        processtype ++;\r\n      }\r\n    } else if (decbutton == 8) {\r\n      if (processtype > 0) {\r\n        processtype --;\r\n      }\r\n    }\r\n  }\r\n\r\n  if (teststep == 5) {\r\n    lockchannels = 0;\r\n    sprintf(displayformat, \"reset %2d\", resetproc);\r\n    tm.displayText(displayformat);\r\n    if (decbutton == 7) {\r\n      if (resetproc &lt; 1) {\r\n        resetproc ++;\r\n      }\r\n    } else if (decbutton == 8) {\r\n      if (resetproc > 0) {\r\n        resetproc --;\r\n      }\r\n    }\r\n  }\r\n\r\n  if (teststep == 6) {\r\n    if (resetproc == 1) {\r\n      Serial.println(\"Resetting\");\r\n      delay(10);\r\n      digitalWrite(resetPin, LOW);\r\n    } else {\r\n      resetproc = 0;\r\n    }\r\n\r\n    teststep = 0;\r\n    mode = 0;\r\n    modeflag = 0;\r\n    lockchannels = 0;\r\n    tm.setLED((mymode - 1), 0);\r\n  }\r\n\r\n  if ((decbutton == mymode) &amp; (modeflag == 1) &amp; (teststep &lt; 6)) {\r\n    teststep ++;\r\n  }\r\n}\r\n\r\n\r\nvoid modePID () {\r\n\r\n  if ((currentmillis - displaymillis) > 500) {\r\n    pvdisplay = processvariable;\r\n    cvdisplay = controlvariable;\r\n    if (!processtype) {\r\n      sprintf(displayformat, \"%03d  %03d\", pvdisplay, cvdisplay);\r\n    } else {\r\n      sprintf(displayformat, \"%03d  LEL\", pvdisplay, cvdisplay);\r\n    }\r\n    tm.displayText(displayformat);\r\n    displaymillis = currentmillis;\r\n  }\r\n}\r\n\r\n\/\/********** Calculations **********\/\/\r\n\r\nvoid CalculateLoad() {\r\n\r\n  if (actualload &lt; load) {\r\n    actualload ++;\r\n  } else if (actualload > load) {\r\n    actualload --;\r\n  }\r\n  \r\n  \/\/loadeffect = (((actualload) \/ ((1000.0 \/ timeconstant) * timeunits)) * (pvafternoise \/ 50.0)); \/\/ Max Load = 100\/minute\r\n  loadeffect = (((actualload) \/ ((1000.0 \/ timeconstant) * timeunits))); \/\/ Max Load = 100\/minute\r\n  bulkpv = bulkpv - loadeffect;\r\n  CheckBulkPV();\r\n\r\n}\r\n\r\n\r\nvoid CalculatePower() {\r\n\r\n  actualpower = ((cvafterlag) \/ ((1000.0 \/ timeconstant) * timeunits)); \/\/Add 100 Per Minute\r\n  bulkpv = 0.0 + bulkpv + actualpower * 1;\r\n  CheckBulkPV();\r\n}\r\n\r\n\r\nvoid CalculateLoss() {\r\n  bulkpv = bulkpv - (((loss) \/ ((1000 \/ timeconstant) * timeunits) * pvafternoise \/ 100)); \/\/ Max Loss = 100\/minute\r\n  CheckBulkPV();\r\n}\r\n\r\n\r\nvoid CalculateLagDelay() {\r\n\r\n  if (lag > 0) {\r\n    if (currentmillis - lagdelaymillis > 100) {\r\n      for (int y = 100; y >= 0; y--) {\r\n        cvlagarray&#91;y] = cvlagarray&#91;y - 1];\r\n      }\r\n      \/\/ Average the Lag Array\r\n      lagcvsum = 0;\r\n      cvlagarray&#91;0] = controlvariable;\r\n\r\n      for (int z = 0; z &lt;= lag; z++) {\r\n        lagcvsum = lagcvsum + cvlagarray&#91;z] + 0.0;\r\n      }\r\n      \/\/cvafterlag = cvlagarray&#91;lag];\r\n      cvafterlag = lagcvsum \/ (lag + 0.0);\r\n      lagdelaymillis = currentmillis;\r\n    }\r\n  } else {\r\n    cvafterlag = controlvariable;\r\n  }\r\n\r\n\r\n  if (pvdelay > 0) {\r\n    if (currentmillis - pvdelaymillis > 10) {\r\n      for (int x = 100; x >= 0; x--) {\r\n        pvdelayarray&#91;x] = pvdelayarray&#91;x - 1];\r\n      }\r\n      pvdelayarray&#91;0] = bulkpv;\r\n      pvafterdelay = pvdelayarray&#91;pvdelay];\r\n      pvdelaymillis = currentmillis;\r\n    } else {\r\n      \r\n    }\r\n  }else{\r\n    pvafterdelay = bulkpv;\r\n  }\r\n}\r\n\r\n\r\nvoid CalculateNoise() {\r\n  pvafternoise = pvafterdelay + random(-pvnoise, pvnoise);\r\n}\r\n\r\n\r\nvoid ProcessPID() {\r\n  if (lockchannels == 0) {\r\n    if (!forwardacting) {\r\n      processvariable = pvafternoise;\r\n    } else {\r\n      processvariable = 100 - pvafternoise;\r\n    }\r\n  }\r\n\r\n\r\n  if (processvariable > 120) {\r\n    processvariable = 120;\r\n  } else if (processvariable &lt; 0) {\r\n    processvariable = 0;\r\n  }\r\n\r\n  analogWrite(processvariablePin, map(processvariable, 0, maxpv, 0, 255));\r\n  analogWrite(feedforwardPin, map(load, 0, maxpv, 0, 255));\r\n  if (!processtype) {\r\n    controlvariable = map(analogRead(A0), 0, 1024, 0, 100);\r\n  } else {\r\n    controlvariable = (map(analogRead(A0), 0, 1024, 0, 100)) * 1.2;\r\n  }\r\n\r\n  if (controlvariable > 100) {\r\n    controlvariable = 100;\r\n  } else if (controlvariable &lt; 0) {\r\n    controlvariable = 0;\r\n  }\r\n}\r\n\r\n\r\nvoid WriteSerial() {\r\n  if (currentmillis - serialmillis > 2000) {\r\n    Serial.print(F(\"Actual Load (from load): \"));\r\n    Serial.println(actualload);\r\n    Serial.print(F(\"Load Effect: \"));\r\n    Serial.println(loadeffect);\r\n    Serial.print(F(\"Actual Power (from lag): \"));\r\n    Serial.println(actualpower);\r\n    Serial.print(F(\"Lag Sum: \"));\r\n    Serial.println(lagcvsum);\r\n    Serial.print(F(\"Process Variable: \"));\r\n    Serial.println(processvariable);\r\n    Serial.print(F(\"Control Variable: \"));\r\n    Serial.println(controlvariable);\r\n    Serial.print(F(\"BulkPV: \"));\r\n    Serial.println(bulkpv);\r\n    Serial.print(F(\"Mode: \"));\r\n    Serial.println(mode);\r\n    Serial.print(F(\"Loss: \"));\r\n    Serial.println(loss);\r\n    Serial.print(F(\"Lag: \"));\r\n    Serial.println(lag);\r\n    Serial.print(F(\"Noise: \"));\r\n    Serial.println(pvnoise);\r\n    Serial.print(F(\"Delay: \"));\r\n    Serial.println(pvdelay);\r\n    Serial.print(F(\"Test Step: \"));\r\n    Serial.println(teststep);\r\n    Serial.println(F(\"---------------------------------------------------\"));\r\n    serialmillis = currentmillis;\r\n  }\r\n}\r\n\r\n\r\nvoid CheckBulkPV() {\r\n  if (bulkpv > maxpv) {\r\n    bulkpv = maxpv;\r\n  } else if (bulkpv &lt; 0) {\r\n    bulkpv = 0;\r\n  }\r\n}\n\n<\/code><\/pre>\n\n\n\n<p>If you find any errors, or have suggestions, please post in the comment section below.<\/p>\n\n\n\n<p>For more information, check out the main page at <a href=\"https:\/\/bryceautomation.com\">https:\/\/bryceautomation.com!<\/a><\/p>\n\n\n\n<p>&#8212; Ricky Bryce<\/p>\n<div id=\"bryce-81133051\" class=\"bryce-after-content bryce-entity-placement\"><script async src=\"\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-8316758073402323\" crossorigin=\"anonymous\"><\/script><ins class=\"adsbygoogle\" style=\"display:block;\" data-ad-client=\"ca-pub-8316758073402323\" \ndata-ad-slot=\"4667596182\" \ndata-ad-format=\"auto\"><\/ins>\n<script> \n(adsbygoogle = window.adsbygoogle || []).push({}); \n<\/script>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Introduction to Arduino PID Simulator This project is an Arduino PID Simulator. Basically, the Arduino will send a process variable to a PID controller. In this case, the signal is 0 to 5v. Additionally, the Arduino reads the control variable from the PID controller, and simulates a real world process. Realize this is not a <a class=\"moretag btn btn-primary\" href=\"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/\">Read More \u00bb<\/a><\/p>\n","protected":false},"author":1,"featured_media":9062,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,4,410],"tags":[402,48,403],"class_list":{"0":"post-9053","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-advanced","8":"category-controllogix","9":"category-pid","10":"tag-controller","11":"tag-pid","12":"tag-process-control","13":"czr-hentry"},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Arduino PID Simulator - Bryce Automation<\/title>\n<meta name=\"description\" content=\"Build your own Arduino PID Simulator. This logic allows you to add lag, delay, load, loss, and noise to the PV\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Arduino PID Simulator - Bryce Automation\" \/>\n<meta property=\"og:description\" content=\"Build your own Arduino PID Simulator. This logic allows you to add lag, delay, load, loss, and noise to the PV\" \/>\n<meta property=\"og:url\" content=\"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/\" \/>\n<meta property=\"og:site_name\" content=\"Bryce Automation\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/ricky.bryce.7\" \/>\n<meta property=\"article:published_time\" content=\"2021-11-25T13:15:32+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-12-19T21:49:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png\" \/>\n\t<meta property=\"og:image:width\" content=\"686\" \/>\n\t<meta property=\"og:image:height\" content=\"349\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Ricky\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/\"},\"author\":{\"name\":\"Ricky\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/#\\\/schema\\\/person\\\/5d5b0f6f6ad768f1ee52968338e63af7\"},\"headline\":\"Arduino PID Simulator\",\"datePublished\":\"2021-11-25T13:15:32+00:00\",\"dateModified\":\"2021-12-19T21:49:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/\"},\"wordCount\":1694,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/bryceautomation.com\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/image-21.png\",\"keywords\":[\"Controller\",\"PID\",\"Process Control\"],\"articleSection\":[\"Advanced\",\"ControlLogix\",\"PID\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/\",\"url\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/\",\"name\":\"Arduino PID Simulator - Bryce Automation\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/bryceautomation.com\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/image-21.png\",\"datePublished\":\"2021-11-25T13:15:32+00:00\",\"dateModified\":\"2021-12-19T21:49:19+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/#\\\/schema\\\/person\\\/5d5b0f6f6ad768f1ee52968338e63af7\"},\"description\":\"Build your own Arduino PID Simulator. This logic allows you to add lag, delay, load, loss, and noise to the PV\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#primaryimage\",\"url\":\"https:\\\/\\\/bryceautomation.com\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/image-21.png\",\"contentUrl\":\"https:\\\/\\\/bryceautomation.com\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/image-21.png\",\"width\":686,\"height\":349,\"caption\":\"PID Simulator\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/2021\\\/11\\\/25\\\/arduino-pid-simulator\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/bryceautomation.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Arduino PID Simulator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/#website\",\"url\":\"https:\\\/\\\/bryceautomation.com\\\/\",\"name\":\"Bryce Automation\",\"description\":\"Automating Home and Industry...\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/bryceautomation.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/#\\\/schema\\\/person\\\/5d5b0f6f6ad768f1ee52968338e63af7\",\"name\":\"Ricky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/bryceautomation.com\\\/wp-content\\\/wphb-cache\\\/gravatar\\\/a8f\\\/a8fe6bf79d292b388ffee281ccb12488x96.jpg\",\"url\":\"https:\\\/\\\/bryceautomation.com\\\/wp-content\\\/wphb-cache\\\/gravatar\\\/a8f\\\/a8fe6bf79d292b388ffee281ccb12488x96.jpg\",\"contentUrl\":\"https:\\\/\\\/bryceautomation.com\\\/wp-content\\\/wphb-cache\\\/gravatar\\\/a8f\\\/a8fe6bf79d292b388ffee281ccb12488x96.jpg\",\"caption\":\"Ricky\"},\"sameAs\":[\"http:\\\/\\\/bryceautomation.com\",\"https:\\\/\\\/www.facebook.com\\\/ricky.bryce.7\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/ricky-bryce-4367a416\\\/\"],\"url\":\"https:\\\/\\\/bryceautomation.com\\\/index.php\\\/author\\\/ricky\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Arduino PID Simulator - Bryce Automation","description":"Build your own Arduino PID Simulator. This logic allows you to add lag, delay, load, loss, and noise to the PV","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/","og_locale":"en_US","og_type":"article","og_title":"Arduino PID Simulator - Bryce Automation","og_description":"Build your own Arduino PID Simulator. This logic allows you to add lag, delay, load, loss, and noise to the PV","og_url":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/","og_site_name":"Bryce Automation","article_author":"https:\/\/www.facebook.com\/ricky.bryce.7","article_published_time":"2021-11-25T13:15:32+00:00","article_modified_time":"2021-12-19T21:49:19+00:00","og_image":[{"width":686,"height":349,"url":"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png","type":"image\/png"}],"author":"Ricky","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#article","isPartOf":{"@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/"},"author":{"name":"Ricky","@id":"https:\/\/bryceautomation.com\/#\/schema\/person\/5d5b0f6f6ad768f1ee52968338e63af7"},"headline":"Arduino PID Simulator","datePublished":"2021-11-25T13:15:32+00:00","dateModified":"2021-12-19T21:49:19+00:00","mainEntityOfPage":{"@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/"},"wordCount":1694,"commentCount":0,"image":{"@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#primaryimage"},"thumbnailUrl":"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png","keywords":["Controller","PID","Process Control"],"articleSection":["Advanced","ControlLogix","PID"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/","url":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/","name":"Arduino PID Simulator - Bryce Automation","isPartOf":{"@id":"https:\/\/bryceautomation.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#primaryimage"},"image":{"@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#primaryimage"},"thumbnailUrl":"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png","datePublished":"2021-11-25T13:15:32+00:00","dateModified":"2021-12-19T21:49:19+00:00","author":{"@id":"https:\/\/bryceautomation.com\/#\/schema\/person\/5d5b0f6f6ad768f1ee52968338e63af7"},"description":"Build your own Arduino PID Simulator. This logic allows you to add lag, delay, load, loss, and noise to the PV","breadcrumb":{"@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#primaryimage","url":"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png","contentUrl":"https:\/\/bryceautomation.com\/wp-content\/uploads\/2021\/11\/image-21.png","width":686,"height":349,"caption":"PID Simulator"},{"@type":"BreadcrumbList","@id":"https:\/\/bryceautomation.com\/index.php\/2021\/11\/25\/arduino-pid-simulator\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/bryceautomation.com\/"},{"@type":"ListItem","position":2,"name":"Arduino PID Simulator"}]},{"@type":"WebSite","@id":"https:\/\/bryceautomation.com\/#website","url":"https:\/\/bryceautomation.com\/","name":"Bryce Automation","description":"Automating Home and Industry...","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/bryceautomation.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/bryceautomation.com\/#\/schema\/person\/5d5b0f6f6ad768f1ee52968338e63af7","name":"Ricky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/bryceautomation.com\/wp-content\/wphb-cache\/gravatar\/a8f\/a8fe6bf79d292b388ffee281ccb12488x96.jpg","url":"https:\/\/bryceautomation.com\/wp-content\/wphb-cache\/gravatar\/a8f\/a8fe6bf79d292b388ffee281ccb12488x96.jpg","contentUrl":"https:\/\/bryceautomation.com\/wp-content\/wphb-cache\/gravatar\/a8f\/a8fe6bf79d292b388ffee281ccb12488x96.jpg","caption":"Ricky"},"sameAs":["http:\/\/bryceautomation.com","https:\/\/www.facebook.com\/ricky.bryce.7","https:\/\/www.linkedin.com\/in\/ricky-bryce-4367a416\/"],"url":"https:\/\/bryceautomation.com\/index.php\/author\/ricky\/"}]}},"_links":{"self":[{"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/posts\/9053","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/comments?post=9053"}],"version-history":[{"count":0,"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/posts\/9053\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/media\/9062"}],"wp:attachment":[{"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/media?parent=9053"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/categories?post=9053"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bryceautomation.com\/index.php\/wp-json\/wp\/v2\/tags?post=9053"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}