Author Topic: Electric needle Valve?  (Read 31566 times)

0 Members and 1 Guest are viewing this topic.

Offline Hooch

  • Posts: 97
Re: Electric needle Valve?
« Reply #120 on: January 24, 2019, 11:36:13 PM »
So many interruptions today.  Luckily I have a smart phone so I have two ways of getting here.

Here is so far before I finish coding.  It may look funny, but what you need to know is:

the time base is the scan cycle about 800ms time it takes to read a DS.
it uses the native data from the DS.  Shifu in your code your minor temp increments should be .0625, the native resolution of a DS, that way you don't create a setpoint which will never exist in real life only a bracketed value which means it will always hunt.

 
Code: [Select]
#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Servo.h>
#include <LCDKeypad.h>


LCDKeypad lcd;
// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
//Define Variables we'll be connecting to
double targetTemp, Input, Output;
Servo myservo;
float Temp;


// define some variables
int lcd_key     = 0;
int adc_key_in  = 0;
int P = 0;
int I = 0;
int D = 0;
int set_I = 0;
int set_P = 1;
int set_D = 2;
int set_C = 2;

int CONTROL_DIRECTION = 0;
int Direction = REVERSE;
int SELECT_BUTTON = 0;

PID myPID(&Input, &Output, &targetTemp,P,I,D, Direction ); // tuning parameters

int cursor_pos = 1;

bool NORMAL = false;
bool setPID = true;
bool get_PID = false;


// Defining button used by the lcd keypad
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// Reflux object constants.
#define TEMP_BUFFER_SIZE 32
#define DALLAS_UNINIT 0x0550
#define DALLAS_READERROR -127
struct {
int start_temp;  // what temp do we activate cooling?
int set_temp; // what is our setpoint temperature
int raw_temp; // stuff the value in here.
int current_temp; // what is the current temp after after processing
CircularBuffer temp_history(TEMP_BUFFER_SIZE);
CircularBuffer control_history(TEMP_BUFFER_SIZE);
int current_position; // where is the valve
int output_zero; // what value turns valve off_type
int output_max;  // what value is maximum cooling
int step; // what direction increases cooling
int crack; // how big a jump to open valve from closed
int sample_size; // how many readings to average must be in 1,2,4,8,16
int max_delta; // clip value for temperature changes to ignore bad inputs
int sensor_errors; // counter for sensor errors.
bool init,learning; // first time flag
} REFLUX = {};

rc REFLUX;

// my control functions
void Reflux_Init (int output_zero, int output_max, int step, int crack, int sample_size,int max_delta,int settle_time)
{

}
int Filter_Value (int intemp);

{
int last_temp, delta_t;

last_temp = rc.temp_history.getElement(0);
if (intemp == DALLAS_READERROR){
rc.sensor_errors++;
return last_temp;
}
delta_t = abs(last_temp-intemp);
if (delta_t > rc.max_delta) {
if (intemp == DALLAS_UNINIT) {
rc.sensor_errors++;
return last_temp;
} else {
return intemp;
}

} else {
return intemp;
}
};

int Reflux_Compute (int target,int current_temp)
{
if (!rc.init) {
rc.current_temp = Filter_Value(rc.raw_temp);
int old_avg = temp_history.averageLast(min(sample_size,temp_history.recordLength()));
temp_history.pushElement(rc.raw_temp);
int new_avg = temp_history.averageLast(min(sample_size,temp_history.recordLength()))
if (new_avg <> old_avg) {
if rc.current_temp == rc.set_temp) {
rc.learning = false; 
}
if (rc.current_temp > rc.set_temp){

} else {}
} else {
  return 0;
}
} else {
temp_history.pushElement(rc.raw_temp);
rc.current_temp = Filter_Value(rc.raw_temp);
rc.init = FALSE;
}
return 0:


void Button_Input()
{
  switch (lcd.button()) {
    case KEYPAD_LEFT:
        delay(100);
        targetTemp = targetTemp - 0.1;
        if(targetTemp<1){targetTemp=0;}
        lcd.setCursor(0,1);
        lcd.print("T:");
        lcd.print(targetTemp);     
      break;
    case KEYPAD_RIGHT:
        delay(100);
        targetTemp = targetTemp + 0.1;
        if(targetTemp>98){targetTemp=99;}
        lcd.setCursor(0,1);
        lcd.print("T:");
        lcd.print(targetTemp);
     
      break;
    case KEYPAD_DOWN:
        delay(100);
        targetTemp = targetTemp - 10;
        if(targetTemp<1){targetTemp=0;}
        lcd.setCursor(0,1);
        lcd.print("T:");
        lcd.print(targetTemp);
     
      break;
    case KEYPAD_UP:
        delay(100);
        targetTemp = targetTemp + 10;
        if(targetTemp>98){targetTemp=99;}
        lcd.setCursor(0,1);
        lcd.print("T:");
        lcd.print(targetTemp);
      break;

  }
  delay(100);
}
// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // reading the button value from the lcd keypad
 // checking which button is pressed
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 if (adc_key_in < 50)   return btnRIGHT; 
 if (adc_key_in < 195)  return btnUP; //250
 if (adc_key_in < 380)  return btnDOWN; //450
 if (adc_key_in < 555)  return btnLEFT; //650
 if (adc_key_in < 790)  return btnSELECT;  //850

 return btnNONE;  // when all others fail, return this...
}

void setup()
{
  Serial.begin(9600);
  lcd.begin(16, 2);              // start communication with LCD keypad shield
  targetTemp = 26; //temperature to set to in degrees F
    rc.set_temp = targetTemp * 16;
rc.start_temp = (rc.set_temp - 2) * 16;  // what temp do we activate cooling?
rc.raw_temp = 0; // stuff the value in here.
rc.current_temp = 0; // what is the current temp after after processing
rc.current_position = 0; // where is the valve
rc.output_zero = 0; // what value turns valve off
rc.output_max = 180;  // what value is maximum cooling
rc.step = 1; // what direction increases cooling
rc.crack = 5; // how big a jump to open valve from closed
rc.sample_size = 4; // how many readings to average must be in 1,2,4,8,16
rc.max_delta = 3; // clip value for temperature changes to ignore bad inputs
rc.sensor_errors = 0; // counter for sensor errors.
rc.init = TRUE; // first time flag
rc.learning = TRUE;
  myservo.attach(3);  //the pin for the servo control
  myservo.write(rc.output_zero); //set initial servo position if desired


}
 
void loop(){

  Serial.print(targetTemp); Serial.print(",  ");
  sensors.requestTemperatures();
  Temp = sensors.getTempCByIndex(0);
  Serial.print(Temp); Serial.print(",   ");   
  //PID section 
  Input =  Temp;
  myPID.Compute();
  int invOutput = map(Output, 0, 180, 180, 0); //need to map 0 to 180 to reverse the output from the PID   // Output, 0, 180, 180, 0  //180, 0, 0, 180
  Serial.println(Output);
  myservo.write(invOutput);
  lcd_key = read_LCD_buttons();  // read the buttons
  switch (lcd_key)
  {
    case btnSELECT:
    {
      delay(300);
      SELECT_BUTTON = SELECT_BUTTON + 1;
      if(SELECT_BUTTON == 4) {SELECT_BUTTON = 0;}
      break;
      }
  }
  Serial.println(SELECT_BUTTON );

   
    if (SELECT_BUTTON == 0) {

    NORMAL_CONDITION();
    // Serial.println("NORMAL");
  }
  else if (SELECT_BUTTON == 1) {
     
  lcd.setCursor(0,0);
  lcd.print("                     ");
  lcd.setCursor(0,1);
  lcd.print("                     ");
  }
 
 
  else if (SELECT_BUTTON == 2) {
   
    set_PID();
 //   Serial.println("set_PID");
  }

  else if (SELECT_BUTTON == 3) {
     
  lcd.setCursor(0,0);
  lcd.print("                 ");
  lcd.setCursor(0,1);
  lcd.print("                 ");
  }
 
   
 
   
 
}

// This function is for normal one
void NORMAL_CONDITION()
{

  lcd.setCursor(0,1);
  lcd.print("T:");
  lcd.print(targetTemp);
  lcd.setCursor(0,0);
  lcd.print("C:");
  lcd.print(Temp);
  lcd.setCursor(8,1);
  lcd.print("A:");
  lcd.print(Output);
 
  Button_Input();
  lcd.setCursor(0,0);
  lcd.print("C:");
  lcd.print(Temp);
 
  lcd.setCursor(8,1);
  lcd.print("A:");
  lcd.print(Output);

}


// This function will set the PID perimeters
void set_PID(){
 
  lcd.setCursor(0, 0);
  lcd.print("PID :");

  lcd.setCursor(1, 1);
  lcd.print("P:");
  lcd.print(P);
 
  lcd.setCursor(6, 1);
  lcd.print("I:");
  lcd.print(I);

  lcd.setCursor(11, 1);
  lcd.print("D:");
  lcd.print(D);

 
  lcd.setCursor(13, 0);
  lcd.print("C:");
 
  lcd.setCursor(0,1);           
  lcd_key = read_LCD_buttons();  // read the buttons

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {

  // if right button is pressed, then move the cursor to minutes
   case btnRIGHT:
     {
      delay(250);
      cursor_pos++;
      if(cursor_pos > 4){cursor_pos = 4;}

     break;
     }
  // if left button is pressed, then move the cursor to hours
   case btnLEFT:
     {
      delay(250);
      cursor_pos--;
      if(cursor_pos <= 0){cursor_pos = 1;}
     break;
     }
  // if up button is pressed, add 1 to the minutes or hours
   case btnUP:
     {
      delay(150);
      if(cursor_pos == 2){
        I++;
       lcd.setCursor(6, 0);
       lcd.print("(I)");
        if(I > 98){
          I = 99;
        }
      }
      else if(cursor_pos == 1){
        P++;
       lcd.setCursor(6, 0);
       lcd.print("(P)");
        if(P > 98){
          P = 99;
        }
      }
     else if(cursor_pos == 3){
        D++;
       lcd.setCursor(6, 0);
       lcd.print("(D)");
        if(D > 98){
          D = 99;
        }
      }
     else if(cursor_pos == 4){
       CONTROL_DIRECTION++;
       lcd.setCursor(6, 0);
       lcd.print("DIR");
        if(CONTROL_DIRECTION > 1){
          CONTROL_DIRECTION = 1;
            lcd.setCursor(13, 0);
             lcd.print("C:");
               lcd.print((char)62 );
           Direction = DIRECT;
               
        }
      }
     break;
     }
   // if down button is pressed, minus 1 from the minutes or hours
   case btnDOWN:
     {
      delay(150);
      if(cursor_pos == 2){
        I--;
       lcd.setCursor(6, 0);
       lcd.print("(I)");
        if(I <= 0){
          I = 0;
        }
      }
      else if(cursor_pos == 1){
        P--;
        lcd.setCursor(6, 0);
        lcd.print("(P)");
        if(P <= 0){
          P = 0;
        }
      }
     else if(cursor_pos == 3){
        D--;
        lcd.setCursor(6, 0);
        lcd.print("(D)");
        if(D <= 0){
          D = 0;
        }
      }
     else if(cursor_pos == 4){
       CONTROL_DIRECTION--;
       lcd.setCursor(6, 0);
       lcd.print("DIR");
        if(CONTROL_DIRECTION <= 0){
          CONTROL_DIRECTION = 0;
            lcd.setCursor(13, 0);
             lcd.print("C:");
               lcd.print((char)60 );
              Direction = REVERSE;
         
        }
      }
 
     break;
     }


  case btnNONE:
     {
     break;
     }
 }
}

Offline ShiFu

  • eParrot.org
  • Admin
  • Posts: 1984
Re: Electric needle Valve?
« Reply #121 on: January 25, 2019, 06:49:00 AM »
Shifu:

... I'm thinking it hunts too much ... 

Yes it does! For now it is hard coded with really stupid PID settings. Easy enough to change those but in the next revision the ability to set those parameters will be part of the Menu so removing the unit from the still, reprogramming it and returning it to the still will not be necessary.

Quote
... I have two enhancement algorithms left to encode, the first being the rate of change setpoint check, and the second being output flutter optimization....

Looking forward to your success!

Quote
... I noticed you dropped the code.  Would you care if I just used the serial port instead of the display? .  I don't have one of those. ...

Had to remove that code, it was such a bad example. Hoping to have much better code to post here in just a few days.
I'd post Max's code but it is not ready yet and it is a very large file. Every library used he wrote or heavily modified.

You should spring for a few of those LCD display shields. They are about $2 or $3 each. Here's a link. Of course you may get an even better deal by shopping around.


Quote
... in your code your minor temp increments should be .0625, the native resolution of a DS, that way you don't create a setpoint which will never exist in real life only a bracketed value which means it will always hunt....

Excellent point. I'll bring that to Max when he recovers from an eye infection. He wrote today that he feels like a one-eye pirate.


Stay calm and follow the screaming people.

Offline Hooch

  • Posts: 97
Re: Electric needle Valve?
« Reply #122 on: January 27, 2019, 03:38:29 AM »
It is fun to tinker with the process.  Those displays are inexpensive and work.  The point is to make a standalone system and they fit the bill. 

Do you have a list of stores on aliexpress you trust that would ship to Canada? 

Here are some musings and scribbles. in the corner is a test jig so you don't need to run the still to tinker with the algorithm.
20190127_163438-2592x3456.jpgElectric needle Valve?
* 20190127_163438-2592x3456.jpg (1459.42 kB. 350x466 - viewed 650 times.)
Hope max feels better.  Have the same problem in my house with a sick child so haven't had a chance to code yet.

Offline ShiFu

  • eParrot.org
  • Admin
  • Posts: 1984
Re: Electric needle Valve?
« Reply #123 on: January 27, 2019, 06:42:58 AM »
... Do you have a list of stores on aliexpress you trust that would ship to Canada? ...

It may sound strange but Alibaba and Aliexpress (and Banggood) do not ship to China.
So it is not possible for me to use those sites.
I did post a Aliexpess link for the display shield but only as an example since I can't buy from Aliexpress, at least not directly.
I buy most of my electronic goodies through a different Alibaba company, Taobao. Or through yet another Alibaba company, 1688.com (that is geared a bit more toward wholesale or quantity purchases).
Alibaba is an octopus with many arms. That includes their popular instant messenger app, WeChat, and payment gateway, AliPay.
Alibaba is the global competitor to Amazon.

Quote
... Those displays are inexpensive and work.  The point is to make a standalone system and they fit the bill. ...

That is exactly right. A $2 display shield with menu navigation buttons.
It is impossible to buy the parts and assemble it yourself at even twice the cost.

Ah, a quick search on Amazon for Arduino Display Shield had prices of about $8 to $15.
But I have no idea of the shipping time, if it ships from the US or is drop-shipped from China.

Ebay has this listing for $5.25 and is stocked in the US.
Stay calm and follow the screaming people.