Temperature regulator

Posted on
  • I received my first pico boards few days ago ( thank you Gordon, brilliant design from both software and hardware perspective!).
    I started this temperature regulator project, mainly to have a start with Java Script which is unknown to me until now, but also with a real application in mind: as an amateur photographer I maintain a nice inventory of vintage cameras and lenses in a dependency of our house which is unheated over the winter.
    Now, the design goals, adapted to this application are:
    -maintain a low regulated temperature when I am not working, for the sake of decent electricity bill;
    -maintain a comfortable temp whil I am spending time in that room;
    -take care about the condensation and avoid rapid jumps in temperature when moving between the two;
    Nice to have:
    -remote control;
    -temperature indication on some LCD or LEDs;
    -Air Conditioning control for the summer days;
    -some possibility to check for efficiency and status of control algorithm;
    -real time clock to be able to program on a daily basis the target temperature;
    I was not wise and I commited the aquisition of some components before having in mind the final design and thus I spend few good days trying hopelessly to write a HTU21D module... Christmas miracle: on December 22nd I found the just published HTU21D module on the Espruino site !!! Thank you Tom Gidden and Luwar !
    Now, the code as of today ( functionality not checked yet since I am awaiting the prototype components):

    // Will use:
    //Espruino pico as controller
    //I2C1 for communication with HTU21D
    //IE: pin B7 I2C1 SDA .... DATA, pin B6 I2C1 SCL... CLOCK
    //triac control is connected via a MOC3083 optocoupler whoes LED is sourced via
    // a resistor on pin A8
    
    // Before anything else we should turn off the triac
    var triac=A8;
    digitalWrite(triac,0); //this shoud reset the pin and set it as output
    
    I2C1.setup( {scl: B6, sda: B7, bitrate 50000 } );
    var htu = require('HTU21D').connect( I2C1 );
    
    //soft reset is required before use of HTU21D
    htu.softReset();
    
    //turn HTU21D heater on for two seconds just to make sure the HTU21D sensor
    //has no condensation
    htu.setHeaterOn( true );
    setTimeout('htu.setHeaterOn( false )'; 2000);
    
    var temp = htu.readTemperature();
    var humidity = htu.readHumidity(); 
    var CompHumidity = htu.getCompensatedHumidity( humidity, temp ); 
    //returns improved accuracy for humidity
    var TargetTemp = 8.50; 
    // this assumes 8.50°C as a perfect target for the scope of the regulation
    
    // for the finetuning of the regulation we will keep in TempHistory the
    // hystorical values of the last hour
    var TempHistory  = Float32Array(60); 
    
    // at power-on we assume the measured temperature was constant for the last hour
    TempHistory.fill(temp);
    var AverageTemp = temp;
    
    new function PWMregulation(t,CH) {
       var RegulatedTemp = TargetTemp;
       //we want to check that moving towards regulated temp does not produce condensation
       //furthermore, if the current temp is close or bellow dew point we wish to move 
       //to higher temp
       var DewPointTemp=htu.getDewPoint(t,CH);
       if ((TargetTemp-DewPointTemp) < 1) || ((t - DewPointTemp) < 1) {
          RegulatedTemp=DewPointTemp + 1;
       }
       //same, we want to check that the temperature one hour ago was above the 
       //dew point temperature. This is because the metal and glass of the photolenses
       // has thermal inertia and we don't want condensation to occur on these. 
       // We better move slowly to higher temperature if such.
       // Remember, the project aim is to protect photo gear & lenses against 
       // condensation and resulting  fungus !
      
       if (TempHistory[59] - DewPointTemp) < 1 {
          RegulatedTemp = TempHistory[59] + 1;
       }
       //here we apply the proportional-integrative-derivative control with some 
      // coeficients we feel appropriate. 
       //we will make some temperature regulation graphs to finetune these coeficients
       // once the system is running
       var PIDresult =0.5*(RegulatedTemp-t + 10*(RegulatedTemp-AverageTemp) + 40*(TempHistory[0]-t));
       var calculatedPWM  = E.clip (PIDresult, 0, 1);
       return calculatedPWM;
    }
    
    // we set the control loop to 60 seconds
    // all planned activities for optimal regulation are executed inside the bellow loop
    setInterval( function() {
       temp = htu.readHumidity();
       humidity = htu.readHumidity();
       CompHumdity = htu.getCompensatedHumidity( humidity, temp );
       analogWrite(triac, PWMregulation(temp,CompHumdity), { freq : 1 });
       //Prepare for the next loop
       AverageTemp = AverageTemp - (TempHistory[59] - temp)/60;
       for (i=59;i>0;i--) {
          TempHistory[i] = TempHistory[i-1];
       }
       TempHistory[0] = temp;
    },60000);
    

    As you may notice the code is under construction and not all the design goals are fulfiled as of today. But I would welcome your thoughts on the approach, any errors I might have done sofar and ideas for code optimization.

  • I've only had time for a quick glance, but it looks good to me.

    Only things I'd say are:

    • As a style thing, in the Espruino code I tend to have variables starting with a lowercase letter, and classes starting with uppercase.. So tempHistory instead of TempHistory - but that's purely personal style :)
    • You might want to put your initialisation code in an onInit function - when you get around to saving your code into flash, that function will then get called at power-on.
    • Where you do setInterval( function() { ... }, 60000); it might be easier to do function onMinute() { ... }; setInterval(onMinute, 60000);. It means that you can then use the left-hand side of the Web IDE to modify the onMinute function while your code is running - it's handy for debugging, especially in a system like this where it'll take a few minutes for the PID system to get happy (so you're unlikely to want to reset too often!).

    With the whole PID thing, I'd really like to come up with a PID library for Espruino that had some kind of self-calibrating functionality. Getting the correct values seems like magic to me!

  • ....Getting the correct values seems like magic to me!
    Yes have spent weeks of my life tuning PID loops.
    For most simple control we don't use the D term this is a reasonable into to the subject
    http://www.csimn.com/CSI_pages/PIDforDummies.html and http://www.pidtuning.net/

  • Thanks Gordon!
    Changing tempHistory instead of TempHistory will be easy. Understanding what a class is and how this could be used for my code will be postponed for my next project...
    I am decided to follow your advice and I will revise the code as suggested and in the meantime further enhance it for functionalities and runtime efficiency.
    For the moment I struggle to understand if onInit is a system function and if such why isn't it documented in the Espruino reference section. If it is not, what makes us sure that code defined inside onInit will be executed after power on?

  • @verdeimpacat
    the onInit is mentioned in the reference of E.on() which creates handlers(!) executed when Espruino gets power.
    While these handlers are independend from each other, there can be only one onInit() which may be more convenient if you want to modify "on the fly".
    Greetings from Frankfurt/Germany, Uli

  • Danke UliMerkel ! It is mentioned indeed!
    Second pass over the code without any additional functionality but just with the advices of Gordon implemented.
    Will use:
    Espruino pico as controller
    I2C1 for communication with HTU21D
    IE: pin B7 I2C1 SDA .... DATA, pin B6 I2C1 SCL... CLOCK
    triac control is connected via a MOC3083 optocoupler whoes LED is sourced via a resistor on pin A8

    var triac;
    I2C1.setup( {scl: B6, sda: B7, bitrate 56000 } );
    var htu = require('HTU21D').connect( I2C1 );
    var temp;
    var averageTemp;
    var humidity;
    var compHumidity; // improved accuracy for humidity
    var targetTemp;
    // for the finetuning of the regulation we will keep in TempHistory the  
    // historical values of the last hour
    var tempHistory = Float32Array(60); 
    
    function onInit () {
       // Before anything else we should turn off the triac
       triac=A8;
       digitalWrite(triac,0); //this shoud reset the pin and set it as output
       temp = htu.readTemperature();
       //at power-on we assume the temperature was constant for the last hour
       tempHistory.fill(temp);
       averageTemp = temp;
       //turn HTU21D heater on for two seconds just to make sure the HTU21D sensor
       //has no condensation
       htu.setHeaterOn( true );
       setTimeout('htu.setHeaterOn( false )'; 2000);
       targetTemp = 8.50;
    }
           
    function pidControlLoop() {
       temp = htu.readTemperature();
       humidity = htu.readHumidity();
       compHumdity = htu.getCompensatedHumidity( humidity, temp );
       var regulatedTemp = targetTemp;
       //we want to check that moving towards regulated temp does not produce 
       //condensation. Furthermore, if the current temp is close or bellow
       //dew point we wish to move to higher temp
       var dewPointTemp=htu.getDewPoint(temp,compHumdity);
       if (((targetTemp-dewPointTemp) < 1) || ((temp - dewPointTemp) < 1)) {
          regulatedTemp=dewPointTemp + 1;
       }
       //same, we want to check that the temperature one hour ago was above
       //the dew point temperature
       //this is because the metal and glass of the photo lenses has thermal  
       //inertia and we don't want condensation to occur on these. We better
       //move slowly to higher temperature if such.
       if ((tempHistory[59] - dewPointTemp) < 1) {
          regulatedTemp = tempHistory[59] + 1;
       }
       // here we apply the proportional-integrative-derivative control with some  
       // coefficients we feel appropriate. We will make some temperature regulation 
       // graphs to finetune these coefficients once the system is running
       var PIDresult =0.5 * (regulatedTemp-temp + 10 * (regulatedTemp-averageTemp) + 25 * (tempHistory[0]-temp));
    
       //for summer days we should imagine vent./AC control if PIDresult is < zero...
       calculatedPWM  = E.clip (PIDresult, 0, 1);
       analogWrite(triac, calculatedPWM, { freq : 2 });
       //Prepare for the next loop
       averageTemp = averageTemp - (tempHistory[59] - temp)/60;
       for (i=59;i>0;i--) {
          tempHistory[i] = tempHistory[i-1];
       }
       tempHistory[0] = temp;
    }
    
    //power on initialization
    onInit();
    // hereby we set the control loop to 60 seconds
    setInterval('pidControlLoop()',60000);
    

    My "mission" over the week-end: tweak the HTU21D module and define instead some sort of a function that reads the values of temperature and humidity from two potentiometers just to move forward with code debugging and triac control while awaiting for the prototype components.
    Wish you will enjoy your weekend too !

  • new function pidControlLoop() {
       temp = htu.readHumidity();
       humidity = htu.readHumidity();
    
    

    Is it a mistake of using htu.readHumidity (); both for temperature and humidity?

  • Thank you Frida! I corrected this error by editing the yesterday posting.

  • @verdeimpacat
    It is unnecessary to call

    //soft reset is required before use of HTU21D
    htu.softReset();
    

    I put this call to the constructor of HTU21D which will be called during "require('HTU21D').connect(…);". Soft Reset is recommended in the data sheet, so nobody can forget it.

    function HTU21D( i2c ) {
      …
      this.softReset(); // Soft reset is recommended at start according to the datasheet.
    }
    
  • Wise! My compliments luwar! I will edit my posting two days ago to reflect this advice.

  • The testing over the weekend went fine and revealed several errors that are already corrected in the second version of the code published above.
    Attached the image of the "test benchmark". It took three minutes to build ( half of the time because the LED was connected the other way around in the first instance) : )

    Meanwhile, all the prototype components arrived and I will move from software to electronics. It will take a while so... make no mistake, the project is not dropped!
    PS: Really fast process working with an interpreter and not compiled code!


    1 Attachment

    • Espruino test benchmark.jpg
  • Thanks! Glad it's working so well for you!

  • The power control looks like this:


    1 Attachment

    • Power control schematics.PNG
  • Updated code after extensive testing:

    var triac;
    var htu;
    var temp;
    var averageTemp;
    var humidity;
    var compHumidity;
    var targetTemp;
    var tempHistory = Float32Array(60); 
    function onInit () {
       I2C2.setup( {scl: B10, sda: B3, bitrate: 19200 } );
       htu = require('HTU21D').connect( I2C2 );
       // Before anything else we should turn off the triac
       triac=A8;
       digitalWrite(triac,0); //this shoud reset the pin and set it as output
       temp = (htu.readTemperature()+htu.readTemperature()+htu.readTemperature())/3;
       //at power-on we assume the temperature was constant for the entire measurement history
       tempHistory.fill(temp);
       averageTemp = temp;
       //turn HTU21D heater on for two seconds just to make sure the HTU21D sensor
       //has no condensation
       htu.setHeaterOn( true );
       setTimeout('htu.setHeaterOn(false);', 2000);
       targetTemp = 8.00;
    }
           
    function pidControlLoop() {
       temp = (htu.readTemperature()+htu.readTemperature()+htu.readTemperature())/3;
       humidity = htu.readHumidity();
       compHumdity = htu.getCompensatedHumidity( humidity, temp );
       var regulatedTemp = targetTemp;
       //we want to check that moving towards regulated temp does not produce 
       //condensation. Furthermore, if the current temp is close or bellow
       //dew point we wish to move to slightly higher temp
       var dewPointTemp=htu.getDewPoint(temp,compHumdity);
       if (((targetTemp-dewPointTemp) < 1) || ((temp - dewPointTemp) < 1)) {
          regulatedTemp=dewPointTemp + 1;
       }
       //same, we want to check that the oldest temperature measured was above the dew point temperature
       //this is needed to avoid condensation due to thermal inertia for glass or metal objects
       //we better move slowly to higher temperature if such.
       if ((tempHistory[59] - dewPointTemp) < 1) {
          regulatedTemp = tempHistory[59] + 1;
       }
       // here we apply the proportional-integrative-derivative control 
       var PIDresult =0.75 * (regulatedTemp-temp + 2 * (regulatedTemp-averageTemp) + 2 * (tempHistory[0]-temp));
       calculatedPWM  = E.clip (PIDresult, 0, 1);
       analogWrite(triac, calculatedPWM, { freq : 0.2 });
       //Prepare for the next loop
       averageTemp = averageTemp - (tempHistory[59] - temp)/60;
       for (i=59;i>0;i--) {
          tempHistory[i] = tempHistory[i-1];
       }
       tempHistory[0] = temp;
    }
    //power on initialization. Will be deleted if code saved in flash
    onInit();
    // hereby we set the control loop to 60 seconds
    setInterval('pidControlLoop()',60000);
    

    And few thoughts after the prototype build:

    • It is wiser to use a snuberless triac. It's even chipper and allows you to build the power control circuit without R4 and C1. That's good because you will want to keep the ac circuitry as simple as possible;
    • The triac needs a 6 cm x 4cm radiator. I trust the triac will not dissipate more than 10-15W, and due to the fact that it is triggered with the MOC3083 zero cross circuit, the heat is kept to a minimum. But since it is powered directly to the ac mains circuitry it's good to avoid any overheating, plastic melting or even worse - fire. After all the load is over 10A at 220V ac;

    The regulation precision is 0,15°C with the suited PID parameters but may easily move to +/- 1°C when the PID parameters are not adequate.

    No humans, animals or Espruino pico boards were hurt during this experiment, do not attempt to build this project if you are not familiar working with mains electricity and power electronics.

  • that's great - thanks! Really nice to see a direct use of a triac too :)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Temperature regulator

Posted by Avatar for verdeimpacat @verdeimpacat

Actions