diff --git a/README.md b/README.md index ae54550..a7ae583 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This is the Arduino sketch to build a double magnetic stirrer based on 2 fans, a In order to put the stirrer into operation you have to change the settings in stir.ino according to your setup. -## Functions +## Overview of Functions All functions are controlled by the KY-040 encoder: @@ -36,12 +36,15 @@ All functions are controlled by the KY-040 encoder: |Value|Description| |:----|:----------| |SPEEDINC|increment of speed values when turning the encoder| -|FANMIN|fan minimum speed (should be a value at which the fan runs safely)| -|FANMAX|maximum speed of the fan (it is important that this is the real maximum value of the fan)| +|FANMIN|fan minimum speed (should be a value at which the fan runs safely) (rpm)| +|FANMAX|maximum speed of the fan (it is important that this is the real maximum value of the fan) (rpm)| |RINTERVAL|regulation interval (ms)| |RDELAY|extra regulation delay when value changes (ms)| -|RTOL|regulation tolerance| +|RTOL|regulation tolerance (rpm)| |CATCHSTOP|stop interval for stir fish catch function (ms)| +|BOOSTRAMP|rise time for boost speed (ms)| +|SINTERVAL|speed measurement interval (ms)| +|SAVERAGE|speed measurement average| |PWM0, PWN1|PWM output pins| |I0, I1|interrupts for rpm measurement (2 and 3 for Leonardo and ProMicro / 0 and 1 for Uno)| |CLK, DT, SW|pins for KY-040 encoder| @@ -50,6 +53,5 @@ All functions are controlled by the KY-040 encoder:

-

diff --git a/images/schematic_leonardo.png b/images/schematic_leonardo.png index 0ae5c3e..9aadef2 100644 Binary files a/images/schematic_leonardo.png and b/images/schematic_leonardo.png differ diff --git a/images/schematic_promicro.png b/images/schematic_promicro.png deleted file mode 100644 index e15d850..0000000 Binary files a/images/schematic_promicro.png and /dev/null differ diff --git a/images/schematic_uno.png b/images/schematic_uno.png index c9b6867..2ee144b 100644 Binary files a/images/schematic_uno.png and b/images/schematic_uno.png differ diff --git a/stir.ino b/stir.ino index bc2aced..aec5450 100644 --- a/stir.ino +++ b/stir.ino @@ -1,4 +1,4 @@ -/////////////////////////////////////////////////////////////////////////////// Stir Control (mwx'2019, v1.2.6) +/////////////////////////////////////////////////////////////////////////////// Stir Control (mwx'2019, v1.3.0) #include #include @@ -6,12 +6,13 @@ #define SXN Serial.println #define MS (long)millis() -int SPEEDINC = 50; // speed increment +int SPEEDINC = 50; // speed increment (rpm) -int FANMIN = 200; // fan minimum speed -int FANMAX = 1600; // fan maximum speed +int FANMIN = 200; // fan minimum speed (rpm) +int FANMAX = 1000; // fan maximum speed (rpm) -int CATCHSTOP = 20000; // catch stop period +int CATCHSTOP = 20000; // catch stop period (ms) +double BOOSTRAMP = 60000; // rise time for boost speed (ms) int PWM0 = 9; // PWM pin for 1. fan int PWM1 = 10; // PWM pin for 2. fan @@ -23,30 +24,33 @@ int CLK = 5; int DT = 6; // dt on KY-040 encoder int SW = 4; // sw on KY-040 encoder -int RINTERVAL = 5000; // regulation interval -int RDELAY = 2000; // regulation delay on changes -int RTOL = 8; // regulation tolerance +int RINTERVAL = 5000; // regulation interval (ms) +int RDELAY = 3000; // regulation delay on changes (ms) +int RTOL = 8; // regulation tolerance (rpm) -int SAVETAG = 1004; // save tag +int SINTERVAL = 2000; // speed measurement interval +int SAVERAGE = 4; // speed measurement average + +int SAVETAG = 1006; // save tag LiquidCrystal_I2C lcd(0x27,16,2); // LCD display (connect to SDA/SCL) -int v0,b0,r0,v1,b1,r1,fanstate0,fanstate1; // speed and regulation -long rpmcount0,rpmcount1; // rpm counter -double rpm0,rpm1; // rpm +int i,v0,b0,r0,v1,b1,r1,fanstate0,fanstate1; // speed and regulation +long rpmcount0,rpmcount1,speedcount0,speedcount1; // rpm counter +double r,rx,rpm0,rpm1,xpm0,xpm1,xb0,xb1; // rpm -long sts,rts,swts,bts,ox,savets,catchts0,catchts1,stop0,stop1,b0ts,b1ts; // timing +long xts,sts,rts,swts,bts,ox,savets,catchts0,catchts1,stop0,stop1,b0ts,b1ts; // timing int bdelay,bprocess,enclast,encval,swmode,mode; // button/encoder processing int OK,SAVE,LOCK,bstate0,bstate1,btime0,btime1,catch0,catch1,ctime0,ctime1; // operation state -char form[8],out[16];String str; // string buffer +char form[8],out[128];String str; // string buffer void setup() { ////////////////////////////////////////////////////////////////////////////////////////// SETUP - rpmcount0=0;rpmcount1=0;rpm0=0;rpm1=0;bprocess=0;r0=0;r1=0;swmode=2; - + rpmcount0=0;rpmcount1=0;speedcount0=0;speedcount1=0;rpm0=0;rpm1=0;xpm0=0;xpm1=0;bprocess=0;r0=0;r1=0;swmode=2; + Serial.begin(9600); // start serial - lcd.init();lcd.backlight();lcd.clear(); // initialize lcd + lcd.init();lcd.clear();lcd.backlight(); // initialize lcd pinMode(PWM0,OUTPUT);pinMode(PWM1,OUTPUT); // set PWM pins pinMode(CLK,INPUT);pinMode(DT,INPUT);pinMode(SW,INPUT); // set KY-040 pins @@ -60,16 +64,18 @@ void setup() { ///////////////////////////////////////////////////////////////// SAVE=0; // load/initialize settings if (eer(0)!=SAVETAG) { v0=300;v1=300;b0=700;b1=700;btime0=30;btime1=30;catch0=0;catch1=0;ctime0=120;ctime1=120; + fanstate0=0;fanstate1=0; eew(0,SAVETAG);save(); } else { v0=eer(1);v1=eer(2);b0=eer(3);b1=eer(4);btime0=eer(5);btime1=eer(6); - catch0=eer(7);catch1=eer(8);ctime0=eer(9);ctime1=eer(10); + catch0=eer(7);catch1=eer(8);ctime0=eer(9);ctime1=eer(10);fanstate0=eer(11);fanstate1=eer(12); } enclast=digitalRead(CLK); // get encoder state - fanstate0=0;fanstate1=0;OCR1A=0;OCR1B=0; // turn fans off - sts=MS;rts=MS;swts=MS;bts=MS;savets=MS;catchts0=MS;catchts1=MS;stop0=MS;stop1=MS; // set timer - bstate0=0;bstate1=0;mode=0;updatelcd();updatespeed();updatemarker();LOCK=0; // set initial states + + xts=MS;sts=MS;rts=MS;swts=MS;bts=MS;savets=MS;catchts0=MS;catchts1=MS;stop0=MS;stop1=MS; // set timer + bstate0=0;bstate1=0;mode=0;updatePWM();updatelcd();updatespeed();updatemarker();LOCK=0; // set initial states + } @@ -135,24 +141,40 @@ void loop() { ////////////////////////////////////////////////////////////////// SAVE++;OK=1;updatelcd();updatespeed();serinfo(); } - if (!OK) SXN("ERROR"); + if (!OK) SXN("error"); } - if (MS-rts>RINTERVAL) { ////////////////////////////////////////////////////////////////////////// regulation - + if (MS-xts>SINTERVAL) { /////////////////////////////////////////////////////////////////// speed measurement + xpm0=xpm0*(SAVERAGE-1)/SAVERAGE+(speedcount0/((MS-xts)/1000.0)*30.0)/SAVERAGE; + xpm1=xpm1*(SAVERAGE-1)/SAVERAGE+(speedcount1/((MS-xts)/1000.0)*30.0)/SAVERAGE; + updatespeed();xts=MS;speedcount0=0;speedcount1=0; + } + + if (MS-rts>RINTERVAL) { //////////////////////////////////////////////////////////////////// speed regulation rpm0=rpmcount0/((MS-sts)/1000.0)*30.0; rpm1=rpmcount1/((MS-sts)/1000.0)*30.0; - sts=MS;rpmcount0=0;rpmcount1=0; - if (!fanstate0) r0=0; - else if (abs((bstate0?b0:v0)-rpm0)>RTOL) r0+=((bstate0?b0:v0)-rpm0)/10.0+1.0; - - if (!fanstate1) r1=0; - else if (abs((bstate1?b1:v1)-rpm1)>RTOL) r1+=((bstate1?b1:v1)-rpm1)/10.0+1.0; + xb0=b0;if (bstate0) xb0=cut((((double)b0-(double)v0)/BOOSTRAMP*(MS-b0ts))+v0,v0,b0); + xb1=b1;if (bstate1) xb1=cut((((double)b1-(double)v1)/BOOSTRAMP*(MS-b1ts))+v1,v1,b1); + if (MS>=stop0+20000) { + if (!fanstate0) r0=0; + else { + r=(bstate0?xb0:v0)-rpm0; + if (abs(r)>RTOL) r0=r<0?r0-1-abs(r)/10:r0+1+abs(r)/10; + } + } + + if (MS>=stop1+20000) { + if (!fanstate1) r1=0; + else { + r=(bstate1?b1:v1)-rpm1; + if (abs(r)>RTOL) r1=r<0?r1-1-abs(r)/10:r1+1+abs(r)/10; + } + } updatePWM();updatespeed();rts=MS; - } + } bdelay=0; //////////////////////////////////////////////////////////////////////////////////// process switch if (!bprocess) { @@ -180,14 +202,14 @@ void loop() { ////////////////////////////////////////////////////////////////// if (swmode==0 && mode==1 && !LOCK) { // boost fan 0 on/off if (bstate0==0) { - rts=MS+RDELAY;bstate0=1;b0ts=MS;fanstate0=1;r0=0;updatePWM(); - } else {;bstate0=0;} + rts=MS+RDELAY;bstate0=1;b0ts=MS;fanstate0=1; + } else {;bstate0=0;rts=MS+RDELAY+2000;updatePWM();} updatespeed(); } if (swmode==1 && mode==1 && !LOCK) { // boost fan 1 on/off if (bstate1==0) { - rts=MS+RDELAY;bstate1=1;b1ts=MS;fanstate1=1;r1=0;updatePWM(); - } else {;bstate1=0;} + rts=MS+RDELAY;bstate1=1;b1ts=MS;fanstate1=1; + } else {;bstate1=0;rts=MS+RDELAY+2000;updatePWM();} updatespeed(); } @@ -195,11 +217,12 @@ void loop() { ////////////////////////////////////////////////////////////////// if (LOCK==0) LOCK=1; else LOCK=0; updatemarker(); + save(); } bdelay=0; - } else if (bdelay>0 && bdelay<20 && !LOCK) { // short button press: switch menu -> fan 0 -> fan 1 + } else if (bdelay>0 && bdelay<20 && !LOCK) { // short button press, switch: menu -> fan 0 -> fan 1 swmode++;if (swmode>2) swmode=0; updatemarker(); bdelay=0; @@ -239,11 +262,11 @@ void loop() { ////////////////////////////////////////////////////////////////// if (swmode==2) mode--; // scroll menu } - if (swmode==0 && mode==0) {;updatePWM();r0=0;rts=MS+RDELAY;} // apply speed change fan 0 - if (swmode==1 && mode==0) {;updatePWM();r1=0;rts=MS+RDELAY;} // apply speed change fan 1 - if (swmode==0 && mode==1) {;updatePWM();r0=0;rts=MS+RDELAY;} // aply boost speed change fan 0 - if (swmode==1 && mode==1) {;updatePWM();r1=0;rts=MS+RDELAY;} // aply boost speed change fan 1 - + if (swmode==0 && mode<=1) {;updatePWM();rts=MS+RDELAY;} // apply (boost) speed change fan 0 + if (swmode==1 && mode<=1) {;updatePWM();rts=MS+RDELAY;} // apply (boost) speed change fan 1 + + if (b04) mode=0;} // check menu mode + if (swmode==2) mode=cut(mode,0,4); // check menu mode SAVE++;updatelcd();delay(50); } @@ -300,7 +323,7 @@ void updatespeed() { /////////////////////////////////////////////////////////// else { if (bstate0) ilcd(1,0,2, (((long)btime0*60000)-(MS-(long)b0ts))/1000/60+1); else slcd(7,0,1," "); - ilcd(7,0,-4,round(rpm0)); + ilcd(7,0,-4,round(xpm0)); } } else slcd(7,0,-4,"OFF"); @@ -309,7 +332,7 @@ void updatespeed() { /////////////////////////////////////////////////////////// else { if (bstate1) ilcd(4,0,2, (((long)btime1*60000)-(MS-(long)b1ts))/1000/60+1); else slcd(12,0,1," "); - ilcd(12,0,-4,round(rpm1)); + ilcd(12,0,-4,round(xpm1)); } } else slcd(12,0,-4,"OFF"); } @@ -334,19 +357,19 @@ void slcd(int x,int y, int l,String s) { /////////////////////////////////////// lcd.setCursor(x,y);lcd.print(out); } -void updatePWM() { ///////////////////////////////////////////////////////////////////// update PWM (fan speed) +void updatePWM() { ////////////////////////////////////////////////////////////// update PWM output (fan speed) v0=cut(v0,FANMIN,FANMAX);v1=cut(v1,FANMIN,FANMAX); b0=cut(b0,FANMIN,FANMAX);b1=cut(b1,FANMIN,FANMAX); - + if (!fanstate0 || MSmax?max:vmax?max:v;} ///// cut values to limit int eer(int adr) {;return EEPROM.read(adr*2)+EEPROM.read(adr*2+1)*256;} /////////////////////////// read EEPROM @@ -354,7 +377,7 @@ void eew(int adr, int val) {;EEPROM.write(adr*2,val%256);EEPROM.write(adr*2+1,va void save() { /////////////////////////////////////////////////////////////////////////////////// save settings eew(1,v0);eew(2,v1);eew(3,b0);eew(4,b1);eew(5,btime0);eew(6,btime1); - eew(7,catch0);eew(8,catch1);eew(9,ctime0);eew(10,ctime1); + eew(7,catch0);eew(8,catch1);eew(9,ctime0);eew(10,ctime1);eew(11,fanstate0);eew(12,fanstate1); } String sstr(String data, char sep, int idx) { ///////////////////////////////////////// get saperated substring