-
• #2
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 ofTempHistory
- 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 dofunction onMinute() { ... }; setInterval(onMinute, 60000);
. It means that you can then use the left-hand side of the Web IDE to modify theonMinute
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!
- As a style thing, in the Espruino code I tend to have variables starting with a lowercase letter, and classes starting with uppercase.. So
-
• #3
....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/ -
• #4
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? -
• #5
@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 -
• #6
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 A8var 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 ! -
• #7
new function pidControlLoop() { temp = htu.readHumidity(); humidity = htu.readHumidity();
Is it a mistake of using htu.readHumidity (); both for temperature and humidity?
-
• #8
Thank you Frida! I corrected this error by editing the yesterday posting.
-
• #9
@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. }
-
• #10
Wise! My compliments luwar! I will edit my posting two days ago to reflect this advice.
-
• #11
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
-
• #12
Thanks! Glad it's working so well for you!
-
• #13
The power control looks like this:
1 Attachment
-
• #14
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.
- 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;
-
• #15
that's great - thanks! Really nice to see a direct use of a triac too :)
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):
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.