321 (2020-09-11 19:59:29 отредактировано )

Re: Узел отбора на перистальтическом насосе

peppe ©:

Головка насоса самодельная из алюминия

*THUMBSUP*

peppe ©:

выложу сюда прошивку, схему..

...чертёж головки...
*Jong*

Ибо дядюшка Ляо что-то цены ломить начал.

РК 35х1600

322

Re: Узел отбора на перистальтическом насосе

...чертёж головки...
*Jong*

Ибо дядюшка Ляо что-то цены ломить начал.

Да, не вопрос! )

323 (2020-09-12 16:30:16 отредактировано peppe)

Re: Узел отбора на перистальтическом насосе

Выкладываю схему, логику и скетч моей версии перистальтического отбора, основанный на трудах kvic (https://alcodistillers.ru/forum/viewtop … 18#p157718).
Вместо TM1637 прикрутил LCD1602 I2C, добавил кнопку сброса скорости мотора на ноль (она же для входа в режим калибровки - у меня энкодер сильно контактами дребезжит, что вызывало сложности входа в режим, выходил сразу же).

#include <EEPROM.h>                           // Стандартная библиотека
#include <Wire.h>                             // Библиотека для управления устройствами по I2C 
#include <LiquidCrystal_I2C.h>                // Библиотека LCD_I2C

//-------------------------------
//#define _LCD_TYPE 1                           //Тип подключения дисплея: 1 - по шине I2C, 2 - десятиконтактное. Обязательно указывать ДО подключения библиотеки
//#include <LCD_1602_RUS_ALL.h>                 // Кириллица https://github.com/ssilver2007/LCD_1602_RUS_ALL
//-------------------------------

#include <GyverEncoder.h>                     // Библиотеки с сайта 
#include <directTimers.h>                     // https://codeload.github.com/AlexGyver/GyverLibs/zip/master



//************************************************************************************************************
//#define STEP8     // Закомментировать, если используется шаг/4, раскомментировать, если используется шаг/8 *
//************************************************************************************************************

//------------------------------------------------------------------------------------------
// Настройка железа
//----------------------------------------------

// Подключение энкодера
#define SW1 7                                // Сигнал энкодера 1 // Если кручение влево/вправо перепутано,
#define SW2 6                                // Сигнал энкодера 2 // можно поменять местами 
#define KEY 5                                // Кнопка энкодера
Encoder enc1(SW1, SW2, KEY, TYPE1);

// Подключение LCD1602
LiquidCrystal_I2C lcd(0x27,16,2);             // Указываем I2C адрес (наиболее распространенное значение), а также параметры экрана (в случае LCD 1602 - 2 строки по 16 символов в каждой)

//LCD_1602_RUS <LiquidCrystal_I2C> lcd(0x27, 16, 2);   //Если используется кириллица

// Подключение A4988 (DRV8825)
#define STEP 11                                // Тактовые импульсы на драйвер ШД

// Внешнее управление
#define TMAS  4                               // Сюда подключаем управление от TMAS, низкий уровень вызовет паузу
#define LED   13                              // Встроенный светодиод
#define Button 9                              // Сюда подключаем кнопку
#define PIEZO 8
#define soundA B00000101
#define soundB B00011111


//--------------------------------------------------------------------------------------
// Наименования режимов работы
//----------------------------------------------
#define RUNNING               0               //
#define PAUSED                1               //
#define PAUSED_BY_EXT         2               //
#define PAUSED_BY_VOL         3               //
#define SELECT_VOL            4               //
#define TUNING                10              //
#define TUNING_PAUSE          11              //
//----------------------------------------------
// Константы
//----------------------------------------------
#define maximumRate           7000           //  Максимальная скорость отбора мл/час
#define rateStep              10              //  Мелкий шаг регулировки отбора
#define rateMidStep           50              //  Средний шаг регулировки отбора
#define rateBigStep           100             //  Крупный шаг регулировки отбора
#define volStep               10              //  Шаг регулировки дозатора
#define volMidStep            50              //  Средний шаг регулировки дозатора
#define volBigStep            500             //  Крупный шаг регулировки дозатора
#define maxDrinkVol           5000            //  Максимальный объем, выдаваемый дозатором
#define defaultDrink          0               //  Объем дозатора по умолчанию. 0 - без ограничения.
#define defaultStepsFor100ml  400000          //  Количество шагов потребное для отбора 100 мл по умолчанию при шаге/4
#define minStepsFor100ml      150000          //  Минимально возможное число шагов для отбора 100 мл при шаге/4    
#ifdef STEP8
#define defaultStepsFor100ml  800000          //  Количество шагов потребное для отбора 100 мл по умолчанию при шаге/8
#define minStepsFor100ml      200000          //  Минимально возможное число шагов для отбора 100 мл при шаге/8
#endif
//----------------------------------------------
// Переменные
//----------------------------------------------
unsigned long stepsFor100ml;                  //  Количество шагов потребное для отбора 100 мл
unsigned int  stepsForOneMl;                  //  Количество шагов потребное для отбора 1 мл
unsigned long stepsCount     = 0;             //  Счетчик количества шагов
unsigned long drinkBackCounter = 0;           //  Обратный счётчик шагов дозатора
unsigned long temp           = 0;             //  Временная переменная для разнообразных нужд
unsigned int  timer1EndValue = 65535;         //  Значение при котором происходит прерывание от таймера 1 (11739)
byte          thousandth     = 0;             //
byte          tenth          = 0;             //
byte          backCounter0   = 0;             //
byte          backCounter1   = 0;             //
byte          counterA       = 0;             //
byte          currentMode    = RUNNING;       // Текущий режим работы
byte          pattern        = 0;             //
int           rate           = 0;             // Скорость отбора мл/час 0 - maximumRate
int           totalVolume    = 0;             // Общий объем отбора мл
int           drinkVolume    = 0;             // Шаг рОзлива
bool          newSecond      = false;
bool          newTenth       = false;
bool          stepEnabled    = false;
bool          tmasStop       = false;
bool          drinkStop      = false;
bool          infoEnable     = false;
bool          bc0flag        = false;
bool          bc1flag        = false;
bool          sndFlag        = false;


//--------------------------------------------------------------------------------------
void setup()
{
  Serial.begin(115200);
  lcd.begin();                      // Инициализация дисплея
  lcd.backlight();                 // Подключение подсветки
  lcd.clear();
  lcd.setCursor(0,0);              // Установка курсора в начало первой строки
  
  
// Настройка входов и выходов
  pinMode(STEP, OUTPUT);
  pinMode(LED,  OUTPUT);
  pinMode(PIEZO, OUTPUT);
  pinMode(SW1,  INPUT_PULLUP);
  pinMode(SW2,  INPUT_PULLUP);
  pinMode(KEY,  INPUT_PULLUP);
  pinMode(TMAS, INPUT_PULLUP);
  pinMode(Button, INPUT_PULLUP);
  digitalWrite(STEP, 1);
  digitalWrite(PIEZO, 1);

  // Настройка таймера 1, он задаёт частоту шагания двигателя
  TIMER1_setClock(PRESCALER_64);            // Частота тактирования таймера 1: 16/64 = 0.25 МГц при шаге/4
#ifdef STEP8
  TIMER1_setClock(PRESCALER_8);           // Частота тактирования таймера 1: 16/8 = 2 МГц при шаге/8
#endif
  TIMER1_setMode(CTC_MODE);                 // Режим работы таймера - сравнение со значением, прерывание и рестарт
  TIMER1_COMPA_setValue(timer1EndValue);    // Значение для сравнения
  TIMER1_attach_COMPA();                    // Прерывание от таймера 1 по сравнению

  // Настройка таймера 2, он отсчитывает временные интервалы для работы программы
  TIMER2_setClock(PRESCALER_128);           // Частота тактирования таймера 2: 16/128 = 0.125 МГц
  TIMER2_setMode(CTC_MODE);                 // включаем сброс таймера по совпадению
  TIMER2_COMPA_setValue(124);               // настраиваем следование прерываний с частотой 1000 гц
  TIMER2_attach_COMPA();                    // прерывание с опросом энкодера

  rate = 0;
  totalVolume = 0;
  stepsCount = 0;

  // Попытка прочитать установки из EEPROM
  EEPROM.get(0, stepsFor100ml);
  if ((stepsFor100ml < minStepsFor100ml) or (stepsFor100ml > minStepsFor100ml * 4)) // Если в EEPROM не установлено число шагов на 100 мл, то
  {
    stepsFor100ml = defaultStepsFor100ml;       // Установим число шагов на 100 мл по умолчанию
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("EEPROM empty!")); 
    lcd.setCursor(0, 1);
    lcd.print(F("Settings default"));
    delay(5000);
  }
  
  stepsForOneMl = round((float)stepsFor100ml / 100);
  
  calcOCR1A();
  drinkVolume = defaultDrink;
  drinkBackCounter = (uint32_t)drinkVolume * stepsForOneMl - 1;
  Serial.println(drinkVolume);          //
  Serial.println(stepsFor100ml);        //
  Serial.println(stepsForOneMl);        //
  Serial.println(drinkBackCounter);     //
  Serial.println("--READY--");
  Serial.end();
  
  //--------------------
    
  if (!digitalRead(Button))   //(enc1.isHolded())
  {
    currentMode = TUNING;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Calibrating mode"));
    backCounter0 = 0;
    infoEnable = false;
    
  }
  else {
    currentMode = RUNNING;
    infoEnable = true;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Extraction"));
    lcd.setCursor(0, 1);
    lcd.print(F("Unit V1.0"));
    //delay(5000);
  }
  
    
}

//--------------------------------------------------------------------------------------
// Расчет числа, загружаемого в OCR1A в зависимости от нужной скорости отбора ----------
void calcOCR1A()
{
  stepEnabled = false;
  temp = round((float)450000000 / stepsForOneMl); // шаг/4
#ifdef STEP8
  temp = round((float)3600000000 / stepsForOneMl); // шаг/8
#endif
  // OCR1A = (Fcpu*3600)/(2*N*K*R)-1
  temp = round((float)temp / rate);             // Fcpu = 16000000 Гц, N - количество шагов на 1 мл,
  temp = temp - 1;                              // K - делитель перед счетчиком Т1 (64), R - скорость отбора мл/час
  
  if (temp >= 65535)
  {
    rate = 0;
        
  }
  else
  {
    noInterrupts();                               // Обеспечиваем атомарность действия
    timer1EndValue = temp;                        //
    interrupts();                                 // Восстанавливаем прерывания
  }
}
void resumeRun()
{
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Process"));
  lcd.setCursor(0, 1);
  lcd.print(F("started.."));
  currentMode = RUNNING;
  if (rate != 0)
  {
    stepEnabled = true;
  }
  backCounter0 = 5;
}
//--------------------------------------------------------------------------------------

void speedReset()
{
  rate = 0;
  stepEnabled = false;
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Pump speed"));
  lcd.setCursor(0, 1);
  lcd.print(F("reset.."));
  currentMode = RUNNING;
  backCounter0 = 10;
}
//--------------------------------------------------------------------------------------
// То, что выполняется каждую секунду --------------------------------------------------
void oneSecSub()
{
  calcTotalVolume();
  //Serial.println(String(stepsCount) + " " + String(totalVolume) + " " + String(timer1EndValue) + " " + drinkBackCounter);
  if (infoEnable)
  {
    
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Total Volume, ml:");
    lcd.setCursor(0, 1);
    lcd.print(totalVolume % 10000);
  }
}
//--------------------------------------------------------------------------------------
// То, что выполняется каждую десятую долю секунды -------------------------------------
void oneTenthSub()
{
  tmasStop = !digitalRead(TMAS);
  digitalWrite(13, tmasStop);
  if ((sndFlag) and (tenth < 8))
  {
    if (bitRead(pattern, tenth))
    {
      digitalWrite(PIEZO, 0);
    }
    else
    {
      digitalWrite(PIEZO, 1);
    }
  }
  else
  {
    digitalWrite(PIEZO, 1);
  }
}
//--------------------------------------------------------------------------------------
// Прерывание таймера 2 (1000 раз в секунду) -------------------------------------------
ISR_T2_COMPA
{
  enc1.tick();
  thousandth++;                                         // Счетчик миллисекунд
  if (thousandth >= 100)                                // Началась новая десятая доля секунды
  { thousandth = 0; tenth++; newTenth = true;           // Счетчик десятых долей секунды
    if (backCounter0  != 0) {
      backCounter0--;
      if (backCounter0 == 0) {
        bc0flag = true;
      }
    }
    if (backCounter1  != 0) {
      backCounter1--;
      if (backCounter1 == 0) {
        bc1flag = true;
      }
    }
    if (tenth >= 10) {
      tenth = 0;  // Началась новая секунда
      newSecond = true;
    }
  }
}

//--------------------------------------------------------------------------------------
// Прерывание по совпадению А в таймере 1 ----------------------------------------------
ISR_T1_COMPA
{
  if ((stepEnabled) or (!(stepEnabled) and !(digitalRead(STEP).
    // Если шаги разрешены или запрещены и уровень на тактовом выходе низкий, то:
  {
    digitalWrite(STEP, !digitalRead(STEP));   // Меняем уровень на шагательной ноге на противоположный
    if (digitalRead(STEP))                    // Если был переход 0->1,
    {
      stepsCount++;                           // то увеличиваем счетчик шагов
      TIMER1_COMPA_setValue(timer1EndValue);  // и перезагружаем значение сравнения счетчика.
      if (drinkVolume != 0)                   // Если задан ненулевой объем наливайки
      {
        if (drinkBackCounter == 0)            // и налито, сколько запрошено,
        {
          stepEnabled = false;                // останавливаем отбор
          drinkStop = true;                   // и устанавливаем флаг готовности дринка. (обрабатывается в основном цикле)
        }
        else
        {
          drinkBackCounter--;                 // Иначе уменьшаем дринковый счетчик.
        }
      }
    }
  }
}


//--------------------------------------------------------------------------------------
// Пересчёт количества шагов в миллилитры ----------------------------------------------
void calcTotalVolume()
{
  totalVolume = stepsCount / stepsForOneMl;
}
//--------------------------------------------------------------------------------------
// Увеличение скорости отбора ----------------------------------------------------------
void increaseRate()
{
  if (rate >= 1000) {
    rate = rate + rateBigStep;
  } else {
    if (rate >= 200) {
      rate = rate + rateMidStep;
    } else {
      rate = rate + rateStep;
    }
  }
  if (rate >= maximumRate) rate = maximumRate;
  calcOCR1A();
}
//--------------------------------------------------------------------------------------
// Уменьшение скорости отбора ----------------------------------------------------------
void decreaseRate()
{
  if (rate >= (1000 + rateBigStep)) {
    rate = rate - rateBigStep;
  } else {
    if (rate >= (200 + rateMidStep)) {
      rate = rate - rateMidStep;
    } else {
      rate = rate - rateStep;
    }
  }
  if (rate <= 0) rate = 0;
  calcOCR1A();
}
//--------------------------------------------------------------------------------------
void tryToSaveStepsFor100ml()
{
                       
    backCounter1 = 20;
  counterA++;
  if (counterA > 10)
  {
    if ((stepsCount > minStepsFor100ml) and (stepsCount < 4 * minStepsFor100ml))
    {
      EEPROM.put(0, stepsCount);
      
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Calibrating done");
      lcd.setCursor(0, 1);
      lcd.print("Restarting..");
      
      setup();
    }
    else //(stepsCount < minStepsFor100ml)
    {
      
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Steps ERROR!");
                
    }
  }
}

//--------------------------------------------------------------------------------------
//---------------------- Начало основного цикла ----------------------------------------
void loop()
{
  if (newSecond) {
    newSecond = false;
    oneSecSub();
  }
  if (newTenth)  {
    newTenth = false;
    oneTenthSub();
  }
  if (bc0flag)   {
    bc0flag = false;
    infoEnable = true;
  }
  //---------------------------------------------------------------
  switch (currentMode)
  {
    //-------------------------------------------------------------
    // Режим калибровки насоса основной экран ---------------------
    //-------------------------------------------------------------
    case TUNING:            // Настройка количества шагов на 100 мл.
      {
                
        infoEnable = false;
        //--------------------
        if (bc1flag)
        {
          bc1flag = false;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print("Fill the pump..");
          
          backCounter1 = 0;
          
        }
        //--------------------
        if (enc1.isRight()) // Поворот по часовой стрелке увеличивает частоту шагов
        {
          if (backCounter1 != 0)
          {
            increaseRate();
            stepEnabled = true;
          }
         
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print(F("SpeedPump:"));
          lcd.setCursor(11, 1);
          lcd.print(rate);
          backCounter1 = 20;
        }
        //--------------------
        if (enc1.isLeft()) // Поворот против часовой стрелки уменьшает частоту шагов
        {
          if (backCounter1 != 0)
          {
            decreaseRate();
            if (rate != 0)
          {
            stepEnabled = true;
          }
          }
                    
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print(F("SpeedPump:"));
          lcd.setCursor(11, 1);
          lcd.print(rate);
          backCounter1 = 20;
         
        }
        //--------------------
        if (enc1.isClick())
        {
          stepEnabled = false;
          currentMode = TUNING_PAUSE;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print(F("Pause.."));
          lcd.setCursor(8, 1);
          lcd.print(stepsCount);
          backCounter1 = 0;
        }
        //--------------------
        if (enc1.isHolded())
        {
         setup();
        }
        //--------------------
      }
      break;
    //-------------------------------------------------------------
    // Пауза в режиме калибровки насоса ---------------------------
    //-------------------------------------------------------------
    case TUNING_PAUSE:
      {
        //--------------------
        
        if (!digitalRead(Button)) 
        {
          rate = 0;
          stepEnabled = false;
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Pump speed"));
          lcd.setCursor(0, 1);
          lcd.print(F("reset.."));
          backCounter1 = 15;
        }
        
        //--------------------        
        if (bc1flag)
        {
          bc1flag = false;
          counterA = 0;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print(F("Pause.."));
          lcd.setCursor(8, 1);
          lcd.print(stepsCount);
          backCounter1 = 0;
        }
        
        //--------------------
        if (enc1.isRight()) // Поворот по часовой стрелке увеличивает частоту шагов
        {
          if (backCounter1 != 0)
          {
            increaseRate();
          }          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print(F("SpeedPump:"));
          lcd.setCursor(11, 1);
          lcd.print(rate);
          backCounter1 = 20;
        }
        //--------------------
        if (enc1.isLeft()) // Поворот против часовой стрелки уменьшает частоту шагов
        {
          if (backCounter1 != 0)
          {
            decreaseRate();
          }
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print(F("SpeedPump:"));
          lcd.setCursor(11, 1);
          lcd.print(rate);
          backCounter1 = 20;
        }
        //--------------------
        if (enc1.isClick())
        {
          stepEnabled = true;
          currentMode = TUNING;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calculating  ..."));
          lcd.setCursor(0, 1);
          lcd.print(F("steps for 100 ml"));
        }
        //--------------------
        if (enc1.isHolded())
        {
          if (backCounter1 != 0)
          {
            
          }
          stepsCount = 0;
          totalVolume = 0;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Calibrating mode:"));
          lcd.setCursor(0, 1);
          lcd.print("Couters cleared");
          backCounter1 = 20;
          bc1flag = false;
        }
        //-----------------------              
       
        if (enc1.isRightH())
        {
          tryToSaveStepsFor100ml();
        }
   //--------------------
        if (enc1.isLeftH())
        {
          tryToSaveStepsFor100ml();
        }
   //--------------------
       
        
      }
      break;
    //-------------------------------------------------------------
    // Основной режим. Идёт отбор. --------------------------------
    //-------------------------------------------------------------
    case RUNNING:
      {
        //--------------------
        if (enc1.isClick()) // Клик на кнопку (режим RUNNING)
        {
          stepEnabled = false;
          currentMode = PAUSED;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Process paused"));
          lcd.setCursor(0, 1);
          lcd.print(F("by operator.."));
          backCounter0 = 0;
          infoEnable = false;
        }
        //--------------------
        if (tmasStop)      // Внешний сигнал, появление (режим RUNNING)
        {
          stepEnabled = false;
          currentMode = PAUSED_BY_EXT;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Process paused"));
          lcd.setCursor(0, 1);
          lcd.print(F("by External.."));
          backCounter0 = 0;
          infoEnable = false;
          pattern = soundB;
          sndFlag = true;
        }
        //--------------------
        if (drinkStop)     // Налит запрошенный объем (режим RUNNING)
        {
          stepEnabled = false;
          drinkStop = false;
          currentMode = PAUSED_BY_VOL;
          drinkBackCounter = (uint32_t)drinkVolume * stepsForOneMl - 1;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Process stopped"));
          lcd.setCursor(0, 1);
          lcd.print(F("Volume: Done!"));
          backCounter0 = 0;
          infoEnable = false;
          pattern = soundA;
          sndFlag = true;
        }
        //--------------------
        if (enc1.isRight())     // Вращение по часовой (режим RUNNING)
        {
          if (backCounter0 != 0)
          {
            increaseRate();
            stepEnabled = true;
          }
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Speed ml/H:"));
          lcd.setCursor(0, 1);
          lcd.print(rate);
          infoEnable = false;
          backCounter0 = 20;
        }
        //--------------------
        if (enc1.isLeft())      // Вращение против часовой (режим RUNNING)
        {
          if (backCounter0 != 0)
          {
            decreaseRate();
            if (rate != 0)
            {
              stepEnabled = true;
            }
          }
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Speed ml/H:"));
          lcd.setCursor(0, 1);
          lcd.print(rate);
          infoEnable = false;
          backCounter0 = 20;
        }
        //--------------------
        if (enc1.isHolded())    // Удержание кнопки (режим RUNNING)
        {
          if ((rate == 0) and (stepsCount != 0))
          {
            stepsCount = 0;
            totalVolume = 0;
            drinkBackCounter = (uint32_t)drinkVolume * stepsForOneMl - 1;
            
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print(F("Processing:"));
            lcd.setCursor(0, 1);
            lcd.print("Couters cleared");
          }
          else
          {
            if (infoEnable) {
              if (drinkVolume == 0)
              {
                
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print("Remaining volume");
              lcd.setCursor(0, 1);
              lcd.print("ml: ");
              lcd.setCursor(4, 1);
              lcd.print("---");
              }
              else
              {
              
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print("Remaining volume");
              lcd.setCursor(0, 1);
              lcd.print("ml: ");
              lcd.setCursor(4, 1);
              lcd.print((drinkBackCounter + 1) / stepsForOneMl);
              }
            }
            infoEnable = false;
            backCounter0 = 20;
          }
        }
        
        //--------------------
        if (!digitalRead(Button)) 
        {
        speedReset();
        }
        //--------------------
        
        
      }
      break;
    //-------------------------------------------------------------
    // Остановка по нажатию кнопки --------------------------------
    //-------------------------------------------------------------
    case PAUSED:
      {
        //--------------------
        if (bc1flag)
        {
          bc1flag = false;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Process paused"));
          lcd.setCursor(0, 1);
          lcd.print(F("by operator.."));
          infoEnable = false;
        }
        //--------------------
        if (!digitalRead(Button)) 
        {
          speedReset();
        }
        //--------------------
        if (enc1.isClick())
        {
          resumeRun();
        }
        //--------------------
        if (enc1.isRight())
        {
          if (backCounter1 != 0)
          {
            increaseRate();
          }
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Speed, ml/H"));
          lcd.setCursor(0, 1);
          lcd.print(rate);
          infoEnable = false;
          backCounter1 = 20;
        }
        //--------------------
        if (enc1.isLeft())
        {
          if (backCounter1 != 0)
          {
            decreaseRate();
          }
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Speed, ml/H"));
          lcd.setCursor(0, 1);
          lcd.print(rate);
          infoEnable = false;
          backCounter1 = 20;
        }
        //--------------------
        if (enc1.isHolded())
        {
          infoEnable = false;
          currentMode = SELECT_VOL;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Volume set, ml:"));
          lcd.setCursor(0, 1);
          lcd.print(drinkVolume); 
        }
        //--------------------
      }
      break;
    //-------------------------------------------------------------
    // Остановка по внешнему сигналу ------------------------------
    //-------------------------------------------------------------
    case PAUSED_BY_EXT:
      {
        if (!tmasStop)
        {
          sndFlag = false;
          resumeRun();
        }
        //--------------------
        if (enc1.isRight() || enc1.isLeft())
        {
          sndFlag = false;
        }
        
        if (enc1.isClick())
        {
          stepEnabled = false;
          currentMode = PAUSED;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Process paused"));
          lcd.setCursor(0, 1);
          lcd.print(F("by operator.."));
          backCounter0 = 0;
          infoEnable = false;
        }
        //------------------------
        if (enc1.isHolded())
        {
          infoEnable = false;
          currentMode = SELECT_VOL;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Volume set, ml:"));
          lcd.setCursor(0, 1);
          lcd.print(drinkVolume); 
        }
        
        //---------------------
        if (!digitalRead(Button)) 
        {
          sndFlag = false;                    // Заткнуть пищалку
          speedReset();                        // и сбросить скорость мотора
        }
        
        
        
      }
      break;
    //-------------------------------------------------------------
    // Остановка по достижению заданного разового объема отбора ---
    //-------------------------------------------------------------
    case PAUSED_BY_VOL:
      {
        //--------------------
        if (enc1.isHolded())
        {
          infoEnable = false;
          currentMode = SELECT_VOL;            //Переход в режим SELECT_VOL
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Volume set, ml:"));
          lcd.setCursor(0, 1);
          lcd.print(drinkVolume);
          sndFlag = false;                  // Заткнуть пищалку
        }
        //---------------------
        if (!digitalRead(Button)) 
        {
          sndFlag = false;                    // Заткнуть пищалку
          speedReset();                        // и сбросить скорость мотора
        }
        //--------------------
        
        if (enc1.isClick())
        {
          stepEnabled = false;
          currentMode = PAUSED;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Process paused"));
          lcd.setCursor(0, 1);
          lcd.print(F("by operator.."));
          backCounter0 = 0;
          infoEnable = false;
        }
        
        
        //--------------------
        if (enc1.isRight() || enc1.isLeft())
        {
          sndFlag = false;                  // Заткнуть пищалку
          
          
        }
        //--------------------
        

      }
      break;
    //-------------------------------------------------------------
    // Режим выбора разового объема -------------------------------
    //-------------------------------------------------------------
    case SELECT_VOL:
      {
        //--------------------
        if (bc1flag)
        {
          bc1flag = false;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Volume set, ml:"));
          lcd.setCursor(0, 1);
          lcd.print(drinkVolume);  
        }
        //--------------------
        if (enc1.isHolded())
        {
          drinkBackCounter = (uint32_t)drinkVolume * stepsForOneMl - 1;
          resumeRun();
        }
        //--------------------
        if (enc1.isClick())
        {
          currentMode = PAUSED;
          drinkBackCounter = (uint32_t)drinkVolume * stepsForOneMl - 1;
          
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Processing paused"));
          lcd.setCursor(0, 1);
          lcd.print(F("by operator.."));
        }
        //--------------------
        if (enc1.isRight())
        {
          if (backCounter1 != 0)
          {
            if (drinkVolume >= 1000) {
              drinkVolume = drinkVolume + volBigStep;
            } else {
              if (drinkVolume >= 100) {
                drinkVolume = drinkVolume + volMidStep;
              } else {
                drinkVolume = drinkVolume + volStep;
              }
            }
            if (drinkVolume >= maxDrinkVol) drinkVolume = 5000;
          }
            
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print(F("Set volume, ml:"));
            lcd.setCursor(0, 1);
            lcd.print(drinkVolume);
            backCounter1 = 20;
        }
        //--------------------
        if (enc1.isLeft())
        {
          if (backCounter1 != 0)
          {
            if (drinkVolume >= (1000 + volBigStep)) {
              drinkVolume = drinkVolume - volBigStep;
            } else {
              if (drinkVolume >= (100 + volMidStep)) {
                drinkVolume = drinkVolume - volMidStep;
              } else {
                drinkVolume = drinkVolume - volStep;
              }
            }
            if (drinkVolume <= 0) drinkVolume = 0;
          }
            
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print(F("Set volume, ml:"));
            lcd.setCursor(0, 1);
            lcd.print(drinkVolume);
            backCounter1 = 20;
        }
        //--------------------
      }
      break;
    //-------------------------------------------------------------
    default:
      //-------------------------------------------------
    break;
  
  }
}
  //-------------------------------------------------
//---------------------- Конец основного цикла -----------------------------------------
//--------------------------------------------------------------------------------------

По ссылке ниже, можно скачать скетч, используемые библиотеки, схему и логику. Там же лежат чертежи головки насоса под шланг 7мм (по наружи) со стенкой 1,5мм и подшипниками (4x11x4). Чертежи в следующих форматах: STL, STEP, DWG, PDF.

https://drive.google.com/drive/folders/ … sp=sharing


Насос буду еще упрощать - сделаю более плоским, уменьшу количество подшипников..

  • Scheme.jpg
    size: 141.29Кб type: jpg
  • Logics.jpg
    size: 229.71Кб type: jpg
  • Сборка1_3.jpg
    size: 153.23Кб type: jpg
  • Сборка1_2.jpg
    size: 182.97Кб type: jpg
  • Сборка1_1.jpg
    size: 184.04Кб type: jpg

324 (2020-09-15 10:38:51 отредактировано peppe)

Re: Узел отбора на перистальтическом насосе

Опытным путем (заранее думать я не очень то не умею, как оказалось :[ ) пришел к тому, что калибровать насос надо на работающей в режиме колонне!
Насос я калибровал на весах водой ~22 °С (100гр=100мл), а из узла отбора спирт идет горячий ~60 °C. Соответственно он занимает больший объем, чем после холодильника. Так, отобрав 750мл голов в мензурку, обнаружил недобор ~30мл. Откалибровал заново, на колонне.

325 (2020-09-15 10:59:41 отредактировано kvic)

Re: Узел отбора на перистальтическом насосе

peppe ©:

отобрав 750мл голов в мензурку, обнаружил недобор ~30мл

Почти точно как в теории. 750*0.0011*(60-22)=31.35 мл.

peppe ©:

Откалибровал заново, на колонне.

Может лучше спирт через предохладитель пустить?
Он совсем небольшой нужен при наших расходах.
Т.е. приведём к нормальным условиям.

А то в следующий раз спирт потеряет где-нибудь по дороге к насосу 10-20 цельсиев и случится перелив.

РК 35х1600

326

Re: Узел отбора на перистальтическом насосе

kvic ©:

Может лучше спирт через предохладитель пустить?

Так получится, что весь отбор будет стекать в доохладитель и там накапливаться? А уже после капать посекундно.
Или я не в теме?

Винокурня Аркадия
---
До седин я у жизни хожу в подмастерьях,
Все еще не зачислен в разряд мастеров... (c)

327 (2020-09-15 12:12:48 отредактировано peppe)

Re: Узел отбора на перистальтическом насосе

kvic ©:

Может лучше спирт через предохладитель пустить?

У меня система охлаждения автономная (Автономное охлаждение) и температура охлаждающей жидкости меняется (иногда аж до 50°С доходила этим летом. Приходилось мощьность ТЭНа уменьшать, доохладитель полностью перекрывать, хоть и уже 2 радиатора охлаждения на чердаке(так и не перенес в подвал в всязи со стройкой отдельного помещеня винокурни - места не хватает уже  *PARDON* )), в зависимости от погоды, от второго аппарата для сырца (работает он или нет). Температуру флегмы регулирую пока вручную скоростью потока охлаждайки через дефлегматор.
Есть мысли прикрутить к узлу отбора ds18b20 и корректировать скорость отбора отностительно температуры. Но надо ли? Я откалибровал щас на работающей колонне и теперь отбирает как надо и сколько задано.

328 (2020-09-15 12:14:43 отредактировано kvic)

Re: Узел отбора на перистальтическом насосе

Аркадий ©:

весь отбор будет стекать в доохладитель и там накапливаться?

Не будет, он насквозь идёт.
Вот старая фотка:

Под №4 доохладитель.
Трубка внутренним диаметром 2 мм, длиной 40 см, намотанная на 8 мм.
Через 8 мм идёт охлаждающая вода.
Через 2 мм - отбор.
Вместе с подводящим шлангом объем тонкой трубки примерно 2 мл.

РК 35х1600

329

Re: Узел отбора на перистальтическом насосе

kvic, опять туплю.В трубку №2 льётся струёй, т.к. ничего, кроме диаметра трубки, не препятствует. А перистальтика уменьшает отбор до покапельного. Куда избыток девается? Накапливается в доохладителе, потом в трубке №2 и потом выше и в итоге возврат в царгу. Где я ошибся?

Винокурня Аркадия
---
До седин я у жизни хожу в подмастерьях,
Все еще не зачислен в разряд мастеров... (c)

330

Re: Узел отбора на перистальтическом насосе

Аркадий ©:

Куда избыток девается?

не куда, стоит в доохладителе пока насос не выкачает

Рк 42 1700 3кв

331

Re: Узел отбора на перистальтическом насосе

Dimyn999,

kvic ©:

Не будет, он насквозь идёт.

Винокурня Аркадия
---
До седин я у жизни хожу в подмастерьях,
Все еще не зачислен в разряд мастеров... (c)

332

Re: Узел отбора на перистальтическом насосе

Судя по подключению на фото, спирт проходя через доохлодитедь попадает в насос так? поэтому этот доохлодитель несущественно увеличивает буферную зону, там буквально капли

Рк 42 1700 3кв

333

Re: Узел отбора на перистальтическом насосе

Dimyn999 ©:

там буквально капли

Понятно, что капли. Но это у него такой доохладитель. У меня на порядок ёмкость больше. И  ещё вопрос:

kvic ©:

Трубка внутренним диаметром 2 мм

Через неё 2,4 литра в час самотёком будет литься-то? Уж больно диаметр маленький.

Винокурня Аркадия
---
До седин я у жизни хожу в подмастерьях,
Все еще не зачислен в разряд мастеров... (c)

334

Re: Узел отбора на перистальтическом насосе

Аркадий ©:

Через неё 2,4 литра в час самотёком будет литься-то? Уж больно диаметр маленький.

будет, я думаю даже больше 3х л в час

Рк 42 1700 3кв

335

Re: Узел отбора на перистальтическом насосе

peppe ©:

Температуру флегмы регулирую пока вручную скоростью потока охлаждайки через дефлегматор.
Есть мысли прикрутить к узлу отбора ds18b20 и корректировать скорость отбора отностительно температуры. Но надо ли?

:o
Не надо...

Homo est mundi pars

336

Re: Узел отбора на перистальтическом насосе

Аркадий ©:

kvic, опять туплю.В трубку №2 льётся струёй, т.к. ничего, кроме диаметра трубки, не препятствует. А перистальтика уменьшает отбор до покапельного. Куда избыток девается? Накапливается в доохладителе, потом в трубке №2 и потом выше и в итоге возврат в царгу. Где я ошибся?

Избыток из стакана отбора идёт в возврат.
Классическая конструкция же.
В трубку №2 насос засасывает из стакана отбора.
Это своего рода продолжение его.
Длинное и тонкое.
Темп засоса задает контроллер.
Чем быстрее крутишь насос, тем больше скорость отсасывания из стакана.
Насквозь не протечёт, так как сосательная трубка пережата роликами насоса.
Сосает только при движении роликов.

Аркадий ©:

Через неё 2,4 литра в час самотёком будет литься-то? Уж больно диаметр маленький.

Там нет самотёка. Насос принудительно сосает.
С двумясполовиной литрами вполне справляется.

---
Наверное корректнее будет не "доохладитель", а "предохладитель".
Чтобы не путаться.

РК 35х1600

337

Re: Узел отбора на перистальтическом насосе

kvic ©:

В трубку №2 насос засасывает из стакана отбора.

Вот! Этого я не знал, спасибо.  *THUMBSUP*

Винокурня Аркадия
---
До седин я у жизни хожу в подмастерьях,
Все еще не зачислен в разряд мастеров... (c)

338

Re: Узел отбора на перистальтическом насосе

kvic ©:

Наверное корректнее будет не "доохладитель", а "предохладитель".

Логичнее всё же "доохладитель", ведь он доохлаждает то что не смог охладить димрот по понятным причинам.

339

Re: Узел отбора на перистальтическом насосе

Вчера перегонял сс,за весь перегон моторчик останавливался сам по себе раз 10.С чем это связано я не знаю!Рукой помогаешь,продолжает работать.

Выпил воды- печень в шоке!

340

Re: Узел отбора на перистальтическом насосе

Аркаим ©:

моторчик останавливался сам по себе раз 10

Стоит и гудит?
Мощи не хватает.
Увеличь ток подстроечником на плате драйвера.
А может бп проседает.

РК 35х1600