41

Re: Настраиваемый термометр на Arduino

ГорыныЧ ©:
SLOG ©:

Я уже зарядку 5-ти вольтовую прикрутил.

Если блок питания на 2 ампера, то вряд ли просядет.

у меня uno на 5 в проседает, а на 12 греется и начинает глючить.
надо наверно что то среднее

42

Re: Настраиваемый термометр на Arduino

dmytry ©:

у меня uno на 5 в проседает

Если 5 вольт подавать в гнездо внешнего питания, то оно попадает на внутренний стабилизатор, соответственно на выходе на 1 вольт меньше.  5 вольт только в юсби гнездо, а внешнее работает от 7 до 12 вольт.

РК медь 35/1000&1500, 2КВт/ Arduino от d.styler (собираю БК 28мм на клампах)
Volkswagen T5 Caravella 2006

43 (2017-06-29 07:38:40 отредактировано dmytry)

Re: Настраиваемый термометр на Arduino

ГорыныЧ ©:
dmytry ©:

у меня uno на 5 в проседает

Если 5 вольт подавать в гнездо внешнего питания, то оно попадает на внутренний стабилизатор, соответственно на выходе на 1 вольт меньше.  5 вольт только в юсби гнездо, а внешнее работает от 7 до 12 вольт.

В моем случае 12 для стабилизатора многовато, сильно греется.

44 (2017-06-29 08:30:05 отредактировано Андрей М)

Re: Настраиваемый термометр на Arduino

если просядет ниже 4,2 вольта будет глючить, если ниже 4 - может ардуинка пыхнуть. Линейный стабилизатор вполне может греться, но это тепло нужно рассеивать т.к. китайские платки не для космоса делаются и от перегрева портятся.

45 (2017-07-03 13:28:45 отредактировано Андрей М)

Re: Настраиваемый термометр на Arduino

Пока анонс.
Будет кнопка "стартЪ/стопЪ" с фиксацией дельты температуры в +.1*.
По фиксации температуры будет завязан алгоритм изменения состояния выводов (D5,D6)
D5 = отбор с реле и нормально закрытым клапаном
D6 = сигнальный (биппер)


1. Начальная температура Тстарт задается при нажатии кнопки верхнего и среднего датчиков. D5=1,D6=1 на 0.5 сек
2. Измеряем температуру со всех трех датчиков
3. Если D5==1 и любой датчик колонны превышает пороговое значение  Тстарт+0.1, то D5=0, D6=1 на 0.5 сек
4. Если D5==0 и сработавшие датчики вернулись к T<=Тстарт, то D5=1, D6=1 на 0.5 сек
5. цикл на 2.

46 (2017-07-05 11:17:57 отредактировано Андрей М)

Re: Настраиваемый термометр на Arduino

Версия 2.2

#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <OneWire.h>
#include <SFE_BMP180.h>
#include <Wire.h>

//#define SOLID_GRAPH // если нравятся "залитые" графики, раскоментируйте
#define VERSION "v2.2"
#define MAGIC "alcodistillers.ru"
// пины
#define DS18_PIN 10                   // DS18B20
#define BEEPER_PIN 13                 // гудок
#define PUMP_PIN 11                   // перистальтический насос или клапан в импульсном режиме
#define SOLENOID_VALVE_PIN 12         // электрический клапан и/или светодиод состояния

enum {KEY_START, KEY_PLUS, KEY_MINUS, KEY_5 }; // функция кнопки
int8_t keysGpios[] = {5, 6, 7, 8};             // пины кнопок

// дельта по пару/колонне
#define TEMP_DELTA 0.1

// настройки сигнализации температуры воды
#define TV_C_MAX 65
#define TV_C_MIN 45
#define TV_BEEP // бипать при выходе температуры воды за пределы  

// настройки перистальтического насоса или клапана в импульсном режиме
#ifdef PUMP_PIN
#define PUMP_CYCLE 1000       // цикл насоса в 1/1000 сек
#define PUMP_STEP 50          // шаг настройки насоса
#define MAX_PUMP_SPEED 3500   // реальная производительность насоса
#define MIN_PUMP_SPEED 200    // меньше чего не нужно
#define START_PUMP_SPEED 200  // установка при включении
#endif

// --------------------------------------------------------
// дальше менять нечего :)
// --------------------------------------------------------

typedef struct button {
  uint8_t scnt;
  uint8_t rcnt;
} button_t;


#define PIN_BUTTON (keysGpios[0])
#define KEY_COUNT (sizeof(keysGpios))

button_t buttons[KEY_COUNT];
#define LONG_PRESS_COUNT 500/MIN_POLL_DELAY // 500/40 = 12 опросов длинное нажатие
#define NORM_PRESS_COUNT 160/MIN_POLL_DELAY // 160/40= 4 опроса вподряд обычное нажатие
#define RESET_PRESS_COUNT 2  // три опроса вподряд "0" - клавиша отпущена

void beep();

#define MAX_DS18 3
#define AVERAGING_COUNT 3
#define MIN_POLL_DELAY 40 //ms

typedef struct {
  uint8_t pausedState;
  float t;
} termo_t;
termo_t termFSM[2];
float initPressure;

#ifdef PUMP_PIN
int pumpSpeed = START_PUMP_SPEED;
#endif
uint8_t tvStateNormal = 1;

OneWire  *ds;
SFE_BMP180 pressure;
LiquidCrystal_I2C lcd(0x27, 20, 4);

#define VAL_COUNT 5*8
float t0[VAL_COUNT];
float t1[VAL_COUNT];
uint8_t t0ChCnt = 99;
uint8_t t1ChCnt = 99;

uint16_t tpos = 0;
#define P(x)   Serial.print(x)
#define PL(x)   Serial.println(x)

struct ds18ScratchPad_struct {
  union {
    uint8_t bytes[9];
    struct {
      uint8_t tempLsb, tempMsb, alarmHiTemp, alarmLoTemp, config,
              internalByte, countRemain, countPerC, crc;
    } regs;
  };
};
typedef struct ds18ScratchPad_struct ds18ScratchPad_t;

struct ds18DeviceRom_struct {
  union {
    uint8_t bytes[8];
    struct {
      uint8_t family, serial[6], crc;
    } regs;
    uint64_t dword;
    uint32_t u32[2];
  };
};
typedef struct ds18DeviceRom_struct ds18DeviceRom_t;

typedef struct {
  ds18DeviceRom_t addr;
  float aTemp[AVERAGING_COUNT];
  float cTemp;
  uint8_t aCount;
  uint8_t errorCount;
} ds18;

ds18 ds18s[MAX_DS18];
double bmp180Temp = 0, bmp180Pressure = 0;
uint8_t hasBmp180 = 0;

uint32_t nextLcdUpdate = 0;
//

#define abs(a) (a>0?a:-a)
void graph(float temp0, float temp1) {
  int16_t i;
  float tmin, tmax, tmin0;
  t0[tpos] = tmin = tmax = temp0;
  t1[tpos] = temp1;
  tpos++;
  tpos %= VAL_COUNT;
  int8_t dx0 = 0, dx1 = 0;
  int p, p0;

  // обработка пиков
  t0ChCnt = 0;
  t1ChCnt = 0;
  for (i = 1; i < VAL_COUNT - 1; i++) {
    p = (i + (int)tpos); // % VAL_COUNT;
    p %= VAL_COUNT;
    p0 = (i + (int)tpos - 1); // % VAL_COUNT;
    p0 %= VAL_COUNT;

    if (t0[p] == INT16_MAX || t0[p - 1] == INT16_MAX) continue;
    if (t0[p] > t0[p0] && dx0 != 1) {
      dx0 = 1;
      if (dx0 != 0) t0ChCnt++;
    }
    if (t0[p] < t0[p0] && dx0 != -1) {
      dx0 = -1;
    }
    if (t1[p] > t1[p0] && dx1 != 1) {
      dx1 = 1;
      if (dx1 != 0) t1ChCnt++;
    }
    if (t0[p] < t0[p0] && dx0 != -1) {
      dx1 = -1;
    }
  }
  P(t0ChCnt); P(";"); PL(t1ChCnt);
  // график
  for (i = 0; i < VAL_COUNT; i++) {
    if (t0[i] == INT16_MAX) continue;
    if (tmin > t0[i]) tmin = t0[i];
    if (tmax < t0[i]) tmax = t0[i];
  }
  float tdiv = (tmax - tmin) / 8;
  if (tdiv < 0.0625) { //1/16 temp step && 8 steps per line
    tdiv = 0.0625;
    tmin0 = (tmax + tmin) / 2 - 0.0625 * 3;
  } else {
    tmin0 = tmin;
  }

  uint8_t c[8];
  uint8_t y, x, py = 100;
  //  P("+++++ Temp:"); P(temp); P(" tpos="); P(tpos); P(" min="); P(tmin); P(" max="); P(tmax); P(" div="); PL(tdiv);
  for (i = 0; i < VAL_COUNT; i++) {
    p = (i + (int)tpos);// % VAL_COUNT;
    p %= VAL_COUNT;
    if (i % 5 == 0) {
      memset(c, 0, sizeof(c));
    }
    if (t0[p] < INT16_MAX) {
      x = 1 << (4 - i % 5);
      y = (uint8_t)((t0[p] - tmin0) / tdiv) + 1;
      if (y > 8) y = 8;
#ifdef SOLID_GRAPH
      for (int y1 = 8 - y; y1 < 8; y1++)
        c[y1] |= x;
#else
      //      P(p); P(" "); P(t[p]); P(" "); P(x); P(" "); P(y); P(" "); P(py); PL(";");
      if (py > 8  || py == y) {
        if (y) c[8 - y] |= x;
      } else if (py > y) {
        for (int y1 = y; y1 < py; y1++)
          c[8 - y1] |= x;
      } else {
        for (int y1 = py + 1; y1 <= y; y1++)
          c[8 - y1] |= x;
      }
      py = y;
#endif
    }
    if (i % 5 == 4)
      lcd.createChar(i / 5, c);
  }

  char str[21];
  snprintf(str, 21, "%2d.%02d \010\011\012\013\014\015\016\017 %2d.%02d", (int)tmin , (int)(tmin * 100) % 100,
           (int) tmax , (int)(tmax * 100) % 100);
  lcd.setCursor(0, 3);
  lcd.print(str);
}

//
int readConfig() {
  char t[sizeof(MAGIC)];
  EEPROM.get(0, t);
  Serial.println("Read config");
  if (memcmp(MAGIC, t, sizeof(MAGIC))) {
    Serial.println("no magic");
    resetConfig();
    return 0;
  }
  for (int i = 0; i < MAX_DS18; i++)
    EEPROM.get(sizeof(MAGIC) + i * sizeof(ds18DeviceRom_t),  ds18s[i].addr.bytes);
}

int saveConfig() {
  Serial.println("Save Config");
  EEPROM.put(0, MAGIC);
  for (int i = 0; i < MAX_DS18; i++) {
    EEPROM.put(sizeof(MAGIC) + i * sizeof(ds18DeviceRom_t), ds18s[i].addr.bytes);
  }
}

int resetConfig() {
  Serial.println("Reset config");
  memset (ds18s, 0, sizeof(ds18s));
  saveConfig();
}

int dumpConfig() {
  int i, i1;
  for (i1 = 0; i1 < MAX_DS18; i1++) {
    Serial.print("ROM =");
    Serial.print(i1 + 1);
    for ( i = 0; i < 8; i++) {
      Serial.write(' ');
      Serial.print(ds18s[i1].addr.bytes[i], HEX);
    }
    Serial.println();
  }
}

int scanDs18() {
  int i, empty, saveFlag = 0;
  ds18DeviceRom_t addr, addr_zero;
  memset(&addr_zero, 0, sizeof(ds18DeviceRom_t));
  ds->reset_search();
  while ( ds->search((void*)&addr.bytes)) {
    empty = -1;
    for (i = 0; i < MAX_DS18; i++) {
      if (!memcmp(&ds18s[i].addr, &addr, sizeof(ds18DeviceRom_t))) {
        break;
      }
      if (empty == -1 && !memcmp(&ds18s[i].addr, &addr_zero, sizeof(ds18DeviceRom_t))) {
        empty = i;
      }
    }
    if (i == MAX_DS18) {
      if (empty >= 0) {
        memcpy(&ds18s[empty].addr, &addr, sizeof(ds18DeviceRom_t));
        saveFlag = 1;
      } else {
        Serial.println("Unable to save new ds18, manual reset needed");
      }
    }
  }
  if (saveFlag) {
    saveConfig();
  }
}
// корректировка по давлению [url=https://alcodistillers.ru/forum/viewtopic.php?pid=10973#p10973]Автоматика на Arduino для ректификации и дистилляции (схема-прошивки)[/url]
uint8_t bak[] = {219, 191, 165, 143, 122, 102, 85, 69, 53, 39, 25, 12, 0};
uint16_t par[] = {689, 667, 641, 613, 579, 536, 490, 436, 368, 295, 207, 108, 0};

float getParVolPercent(float temp) {
  if (hasBmp180 & bmp180Pressure > 0) {
    temp += (760 - bmp180Pressure) * 0.04;
  }
  if (temp >= 100 || temp <= 88) return 0;
  uint8_t idx = temp - 88;
  float ret = 0.1 * par[idx] - 0.1 * (par[idx] - par[idx + 1]) * (temp - 88 - idx);
  return ret;
}

float getBakVolPercent(float temp) {
  if (hasBmp180 & bmp180Pressure > 0) {
    temp += (760 - bmp180Pressure) * 0.04;
  }
  if (temp >= 100 || temp < 88) return 0;
  uint8_t idx = temp - 88;
  float ret = 0.1 * bak[idx] - 0.1 * (bak[idx] - bak[idx + 1]) * (temp - 88 - idx);
  return ret;
}

typedef enum {PUMP_ON, PUMP_OFF, PUMP_STOP} pumpState_t;
pumpState_t pumpState = PUMP_STOP;

#ifdef PUMP_PIN
uint32_t next_change = 0, ms0, ms1, msc;
uint32_t prevPumpMillis = 0;

int pumpRun(int speed);

void ppump(int inc) {
  //  P(">>"); P(inc); P(" "); P(pumpSpeed); P(" "); PL(pumpSpeed + inc);
  pumpSpeed += inc;
  if (pumpSpeed > MAX_PUMP_SPEED)
    pumpSpeed = MAX_PUMP_SPEED;
  else if (pumpSpeed  < MIN_PUMP_SPEED)
    pumpSpeed = MIN_PUMP_SPEED;

  ms0 = (float)PUMP_CYCLE * pumpSpeed / MAX_PUMP_SPEED;
  ms1 = PUMP_CYCLE - ms0;

  if (ms0 < 200) {
    ms1 = 200 * (float)ms1 / (float)ms0;
    ms0 = 200;
  }
  nextLcdUpdate = 0;
}

int pollPump() {
  uint32_t m = MIN_POLL_DELAY;
  if (termFSM[0].pausedState || termFSM[1].pausedState) {
#ifdef PUMP_PIM
    digitalWrite(PUMP_PIN, 0);
#endif
    digitalWrite(SOLENOID_VALVE_PIN, 0);
    return (1000);
  }
  digitalWrite(SOLENOID_VALVE_PIN, pumpState == PUMP_STOP ? 0 : 1);

#ifdef PUMP_PIN
  switch (pumpState) {
    case PUMP_ON:
      pumpState = PUMP_OFF;
      digitalWrite(PUMP_PIN, 0);
      m = ms1;
      break;
    case PUMP_OFF:
      pumpState = PUMP_ON;
      digitalWrite(PUMP_PIN, 1);
      m = ms0;
      break;
    case PUMP_STOP:
      digitalWrite(PUMP_PIN, 0);
      m = MIN_POLL_DELAY;
      break;
  }
#endif
  // P(pumpState);P(">");PL(millis());
  return m;
}
#endif
void initkeys() {
  for (int i = 0; i < KEY_COUNT; i++) {
    pinMode(keysGpios[i], INPUT);
    pinMode(keysGpios[i], INPUT_PULLUP); //подтягиваем входы кнопок к плюсу встроенными резисторами
  }
  memset(&buttons, 0, sizeof(buttons));


}
void setup(void) {
  Serial.begin(115200);
  Serial.println(MAGIC);

  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print(">>>>>>>>>  <<<<<<<<<");
  lcd.setCursor(2, 1);
  lcd.print(MAGIC);
  lcd.setCursor(3, 2);
  lcd.print("samogon powered!");


  pinMode(BEEPER_PIN, OUTPUT);
  digitalWrite(BEEPER_PIN, 0);
#ifdef PUMP_PIN
  pinMode(PUMP_PIN, OUTPUT);
  digitalWrite(PUMP_PIN, 0);
#endif
  pinMode(SOLENOID_VALVE_PIN, OUTPUT);
  digitalWrite(SOLENOID_VALVE_PIN, 0);

  readConfig();
  scanDs18();
  dumpConfig();
  initkeys();

  pinMode(DS18_PIN, INPUT_PULLUP); //подтягиваем входы кнопок к плюсу встроенными резисторами
  ds = new OneWire(DS18_PIN);
  if (!digitalRead(PIN_BUTTON)) {
    if (!digitalRead(PIN_BUTTON)) {
      lcd.clear();
      lcd.setCursor(0, 3);
      lcd.print("reset ds18b20 config");
      Serial.println("Reset");
      resetConfig();
    }
  }

  // Wire.pins(PIN_SDA, PIN_SCL);
  if (pressure.begin()) {
    Serial.println("BMP180 init success");
    hasBmp180 = 1;
  } else  {
    Serial.println("BMP180 init fail\n\n");
  }
  for (int i = 0; i < VAL_COUNT; i++) {
    t0[i] = INT16_MAX;
  }
  float q = 77;
  for (int i = 0; i < 44; i++) {
    q += 0.125 * (1 - random(0, 3));
    graph(q, INT16_MAX);
    // delay(10);
  }
  lcd.clear();
  for (int i = 0; i < VAL_COUNT; i++) {
    t0[i] = INT16_MAX;
    t1[i] = INT16_MAX;
  }

}

// coRoutines
#define crBegin switch(crStaticVar) { case 0:
#define crReturn(x) do { crStaticVar=__LINE__; return x ; case __LINE__:; } while (0)
#define crFinish }


#ifdef crStaticVar
#undef crStaticVar
#endif

#define crStaticVar _dsStatus
int _dsStatus = 0;
int dsCnt;
int readTemp() {
  ds18ScratchPad_t scp;
  crBegin;
  ds->reset();
  ds->skip();
  ds->write(0x44, 0);
  crReturn(750);        // time to calculate temp
  for (dsCnt = 0; dsCnt < MAX_DS18; dsCnt++) {
    ds->reset();
    ds->select((void*)&ds18s[dsCnt].addr.bytes);
    ds->write(0xBE);         // Read Scratchpad
    for (int i = 0; i < 9; i++) {           // we need 9 bytes
      scp.bytes[i] = ds->read();
    }
    if (OneWire::crc8((void*)&scp.bytes, 8) != scp.regs.crc) {
      if (ds18s[dsCnt].errorCount < 10)
        ds18s[dsCnt].errorCount++;
      else
        ds18s[dsCnt].cTemp = 0;
      PL("Error!!!!");
    } else {
      ds18s[dsCnt].errorCount = 0;
      int TReading, SignBit;
      float temp = 11.11;

      TReading = (scp.regs.tempMsb << 8) + scp.regs.tempLsb;
      SignBit = TReading & 0x8000;  // test most sig bit

      if (SignBit) // negative
        TReading = (TReading ^ 0xffff) + 1; // 2's comp
      temp = 1.0 * (SignBit ? (-TReading) : TReading) / 16;
      ds18s[dsCnt].aTemp[ds18s[dsCnt].aCount++] = temp;
      if (ds18s[dsCnt].aCount == AVERAGING_COUNT) {
        ds18s[dsCnt].aCount = 0;
        temp = 0;
        for (int i = 0; i < AVERAGING_COUNT; i++) {
          temp += ds18s[dsCnt].aTemp[i];
        }
        ds18s[dsCnt].cTemp = temp / AVERAGING_COUNT;
      }
    }
    crReturn(MIN_POLL_DELAY);
  }
  crFinish;
  crStaticVar = 0;
  nextLcdUpdate = 0;
  return MIN_POLL_DELAY;
}
#ifdef crStaticVar
#undef crStaticVar
#endif

#define crStaticVar _bmp180Status
int _bmp180Status = 0;

int readPressure() {
  if (!hasBmp180)
    return 5000;
  crBegin
  pressure.startTemperature();
  crReturn(MIN_POLL_DELAY);
  pressure.getTemperature(bmp180Temp);
  crReturn(MIN_POLL_DELAY);
  pressure.startPressure(3);
  crReturn(MIN_POLL_DELAY);
  pressure.getPressure(bmp180Pressure, bmp180Temp);
  bmp180Pressure /= 1.333;

  crFinish;
  crStaticVar = 0;
  nextLcdUpdate = 0;
  return 2000;
}

void keyPress(int keynum, int longpress) {
  P("keypress:"); P(keynum); P(" "); PL(longpress);
  switch (keynum) {
    case KEY_START:
      beep();
      pumpState = pumpState != PUMP_STOP ? PUMP_STOP : PUMP_OFF;
      digitalWrite(PUMP_PIN, 0);
      if (pumpState == PUMP_OFF) {
        for (int i = 0; i < 2; i++) {
          termFSM[i].t = ds18s[i].cTemp;
          termFSM[i].pausedState = 0;
        }
        initPressure = bmp180Pressure;
      }
      ppump(0);
      break;
    case KEY_PLUS:
      ppump(!longpress ? PUMP_STEP : PUMP_STEP * 8);
      break;
    case KEY_MINUS:
      ppump(!longpress ? -PUMP_STEP : -PUMP_STEP * 8);
      break;
  }
}
// todo отработка "отжатия"
int pollButtons() {
  button_t *b;
  for (int cnt = 0; cnt < KEY_COUNT; cnt++) {
    b = &buttons[cnt];
    int st = !digitalRead(keysGpios[cnt]);
    if (st && b->scnt < LONG_PRESS_COUNT) {
      b->scnt++;
      b->rcnt = 0;
    } else {
      if (b->scnt > RESET_PRESS_COUNT) { // цикл нажатия
        b->rcnt++;
        if (b->scnt >= LONG_PRESS_COUNT) { // длинное нажатие может повторяться
          b->rcnt = 0;
          b->scnt = 0;
          keyPress(cnt, 1);
        } else if (b->scnt >= NORM_PRESS_COUNT && b->rcnt > RESET_PRESS_COUNT) { // короткое нажатие
          b->rcnt = 0;
          b->scnt = 0;
          keyPress(cnt, 0);
        }
      }
    }
  }
  return MIN_POLL_DELAY;
}

int dumpTemp() {
  if (ds18s[0].cTemp > 0)
    graph(ds18s[0].cTemp, ds18s[1].cTemp);



  for (int i = 0; i < 3; i++) {
    P(ds18s[0].cTemp); P(" ");
  }
  PL(bmp180Pressure);
  if ((ds18s[2].cTemp > TV_C_MAX || ds18s[2].cTemp < TV_C_MIN) && pumpState != PUMP_STOP)
    tvStateNormal = 0;
  else
    tvStateNormal = 1;
#ifdef TV_BEEP
  if (!tvStateNormal)
    beep();
#endif
  return 5000;
}
int pos = 0;
int updateLcd() {
  char str[21];
  if (nextLcdUpdate < millis()) {
    //Serial.println("----------");

    // строка 1
#ifndef PUMP_PIN
    snprintf(str, 21, "n:%02d.%02d o:%2d      ", (int)ds18s[0].cTemp, (int)(ds18s[0].cTemp * 100) % 100,
             (int)ds18s[2].cTemp);
#else
    snprintf(str, 21, "n:%02d.%02d o:%2d p:%4d  ", (int)ds18s[0].cTemp, (int)(ds18s[0].cTemp * 100) % 100,
             (int)ds18s[2].cTemp, pumpSpeed);
#endif
    lcd.setCursor(0, 0);
    lcd.print(str);
    //    Serial.println(str);

    // строка 2
    float   pt = (78.91 - (780 - bmp180Pressure ) * 0.038);
    //    strcpy(str,"To=55 P=777.7 Tc=77.7f");
    if (hasBmp180) {
      snprintf(str, 21, "k:%02d.%02d c:%2d.%1d/%3d.%1d      ", (int)ds18s[1].cTemp, (int)(ds18s[1].cTemp * 100) % 100,
               (int)pt, (int)(pt * 10) % 10, (int)bmp180Pressure, (int)(bmp180Pressure * 10) % 10);
    } else {
      snprintf(str, 21, "o:%2d                        ", (int)ds18s[2].cTemp);
    }
    lcd.setCursor(0, 1);
    lcd.print(str);
    //Serial.println(str);

    // строка 3
    if (ds18s[0].cTemp > 88 || ds18s[1].cTemp > 88) { // дистилляция
      int vpp = getParVolPercent(ds18s[0].cTemp) * 10;
      int vpb = getBakVolPercent(ds18s[1].cTemp) * 10;
      snprintf(str, 20, "%par=%2d.%1d %bak=%2d.%1d    ", vpp / 10, vpp % 10, vpb / 10, vpb % 10);
    } else {
      //      memset(str, ' ', 20);
      snprintf(str, 21, "Tn:%c[%c]Tk:%c[%c]To:[%c] ", t0ChCnt < 10 ? '0' + t0ChCnt : '~', termFSM[0].pausedState ? 'X' : 'v',
               t1ChCnt < 10 ? '0' + t1ChCnt : '~', termFSM[1].pausedState ? 'X' : 'v',  tvStateNormal ? 'v' : 'X');

      str[20] = '\0';
    }
    lcd.setCursor(0, 2);
    lcd.print(str);
    //Serial.println(str);

    // строка 4 - графики
    nextLcdUpdate = millis() + 2000;
  }
  return 200;
}

int pollLogic() {
  if (pumpState == PUMP_STOP)
    return 1000;
  for (int i = 0; i < 2 ; i++) {
    float corrT = termFSM[i].t + (bmp180Pressure - initPressure) * 0.04;
    //    P(bmp180Pressure-initPressure);P("/");P(ds18s[i].cTemp);P(" ");PL(corrT);
    if (termFSM[i].pausedState) {
      if (ds18s[i].cTemp <= corrT) {
        termFSM[i].pausedState = 0;
        beep();
      }
    } else {
      if (ds18s[i].cTemp > corrT + TEMP_DELTA) {
        termFSM[i].pausedState = 1;
        beep();
      }
    }
  }
  return 3000;
}
uint32_t beepStart = 0;
uint32_t beepDuration;
void beep() {
  beepStart = millis();
  beepDuration = 100;
  digitalWrite(BEEPER_PIN, 1);
}

int pollBeep() {
  if (beepStart == 0) {
    return 100;
  }
  uint32_t m = millis();
  if (m - beepStart  >= beepDuration) {
    beepStart = 0;
    digitalWrite(BEEPER_PIN, 0);
  }
  return 50;
}


uint32_t prev_time = 0;
typedef int (*poll_cb)();
poll_cb pCb[] = {&readTemp, &dumpTemp, &readPressure, &pollButtons, &pollPump, &updateLcd, &pollBeep, &pollLogic, NULL};
#define MAX_POLL_MS 10000
uint32_t polls[sizeof(pCb) / sizeof(poll_cb) + 1] = {0};
void loop(void) {
  uint32_t timems = millis();
  uint32_t minms = timems + MAX_POLL_MS;
  uint32_t x;

  for (int i = 0; pCb[i] != NULL; i++) {
    if (polls[i] < timems) {
      x = pCb[i]();
      polls[i] = timems + x;
    }
    if (minms > polls[i] ) {
      minms = polls[i];
    }
  }
  delay(minms - timems);
}

https://yadi.sk/d/Z7nJUdoD3KmFTw

47 (2017-07-06 14:55:10 отредактировано Андрей М)

Re: Настраиваемый термометр на Arduino

Описание и функции (+к версии 1)
Добавлено:
-управление (отсечка) отбора по температуре по датчику пара и датчику в колонне
-подключение нормально закрытого электромагнитного клапана (режим открыто/закрыто)
-подключение узла отбора на перистальтическом насосе или клапане (импульсный режим)
-анализатор стабильности температуры
-вывод состояния датчиков и стабильности температуры
-корректировка отбора по давлению
-сигнализация об изменении состояния (биппер)
Изменено
- кнопка перенесена с D2 на D5!!!!


Электрическая часть:
-кнопки нормально разомкнутые без фиксации: старт/стоп отбора - D5, "+"/"-" - D6,D7

- электромотор постоянного тока перистальтического насоса. Подключается к D11 при помощи драйвера (например L298 Dual H-Bridge Motor Driver), бесконтактного реле или полевого транзистора (ищется поисковиками "dc motor arduino mosfet") (подробнее про такой узел отбора Узел отбора на перистальтическом насосе)

R1 200-500 ом, R2 10k, Диод и транзистор - любые из доступных, рассчитанные на напряжение питания и ток моторчика.

- электромагнитный клапан отбора. Подключается к D12 при помощи реле (традиционно). На эту же ножку можно установить светодиод, последовательно с резистором 4.7к, он будет включен при отборе и выключен на паузе или если температурные показатели выйдут за пределы дельты.

- Биппер - любой небольшой, подключаем к D13. Если орет громко или при сигнале мигает экран - поставьте последовательно резистор 1-4.7k

По электричеству - задавайте вопросы, отвечу.

Описание интерфейса

1я строка:
пар, охлаждение, скорость отбора (при управляемом отборе)
2я строка:
колонна, температура кипения спирта, давление
3я строка:
Тп:С[X]Tk:C[v]To[v]
C- коэффициент стабильности температуры. от 0 до 9, если больше 9, отображается "~". Обозначает количество пиков (горбов) за последние 10 минут.
[X]/[v] - состояние при отборе - "Х" - температуры вышла за пределы, "v" - галочка - нормальное состояние

48

Re: Настраиваемый термометр на Arduino

Андрей М, Годится, единственная просьба нарисовать схемку.
Можно прям фото макетки без проводов. А в редакторе подрисовать что, куда подключать.
Было-бы совсем замечательно.

49

Re: Настраиваемый термометр на Arduino


Рисунок на спине упитого друга ;)

50 (2017-07-05 19:45:20 отредактировано Gerundey)

Re: Настраиваемый термометр на Arduino

Андрей М ©:

Рисунок на спине упитого друга ;)

Отлично! Спасибо!

Попробовал, вроде работает. Давление по крайней мере показывает. Кнопка старт\стоп работает.
С датчиками не понял. Чтоб их добавить в первом посте на D2 кнопка  была, здесь ее не надо или как?
И при компиляции и загрузке выдает вот такие предупреждения?

D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino:124:0: warning: "abs" redefined

 #define abs(a) (a>0?a:-a)

 ^

In file included from sketch\alcodist-2.2.ino.cpp:1:0:

D:\arduino-1.8.1\hardware\arduino\avr\cores\arduino/Arduino.h:94:0: note: this is the location of the previous definition

 #define abs(x) ((x)>0?(x):-(x))

 ^

D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino: In function 'int scanDs18()':

D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino:262:40: warning: invalid conversion from 'void*' to 'uint8_t* {aka unsigned char*}' [-fpermissive]

   while ( ds->search((void*)&addr.bytes)) {

                                        ^

In file included from D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino:3:0:

D:\arduino-1.8.1\libraries\OneWire/OneWire.h:181:13: note: initializing argument 1 of 'uint8_t OneWire::search(uint8_t*)'

     uint8_t search(uint8_t *newAddr);

             ^

D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino: In function 'int readTemp()':

D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino:464:47: warning: invalid conversion from 'void*' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]

     ds->select((void*)&ds18s[dsCnt].addr.bytes);

                                               ^

In file included from D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino:3:0:

D:\arduino-1.8.1\libraries\OneWire/OneWire.h:135:10: note: initializing argument 1 of 'void OneWire::select(const uint8_t*)'

     void select(const uint8_t rom[8]);

          ^

D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino:469:43: warning: invalid conversion from 'void*' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]

     if (OneWire::crc8((void*)&scp.bytes, 8) != scp.regs.crc) {

                                           ^

In file included from D:\TempArduino\Arduino\alcodist-2.2\alcodist-2.2.ino:3:0:

D:\arduino-1.8.1\libraries\OneWire/OneWire.h:187:20: note: initializing argument 1 of 'static uint8_t OneWire::crc8(const uint8_t*, uint8_t)'

     static uint8_t crc8(const uint8_t *addr, uint8_t len);

51

Re: Настраиваемый термометр на Arduino

Gerundey ©:

Чтоб их добавить в первом посте на D2 кнопка  была, здесь ее не надо или как?

На D2 и D3 можно подключать прерывания. Они понадобятся дальше. По этому кнопка перенесена на D5.
Функционал остался.
Ошибка #define abs... решается комментированием (// #define ...)
остальные предупреждения на скорость не влияют. Но я бы рекомендовал перейти на 1.8.3.

52

Re: Настраиваемый термометр на Arduino

Всем привет.
Есть вопрос по BMP180.
Давление прыгает от измерения к измерению в пределах 0,2-0,5 мм.рт.ст.
Меняется циферка младшего разряда.
Так и должно быть или где-то у меня косяк ?

РК 35х1600

53 (2017-07-09 12:45:52 отредактировано Толян)

Re: Настраиваемый термометр на Arduino

kvic ©:

Так и должно быть или где-то у меня косяк ?

Все таки в одной поре стоит только у молодого, в других случаях давление прыгает, :)  имхо.

Справедливость проявляется в воздаянии каждому по его заслугам.
                                                                                                            Цицерон

54

Re: Настраиваемый термометр на Arduino

Толян ©:

Все таки в одной поре стоит только у молодого, в других случаях давление прыгает, :)  имхо.

Как то это слишком мудрёно.
Либо стоит либо не стоит.
А вот ПРЫГАЕТ - это не очень распространенный факт.

Это ладно, а по существу вопрос можно снять.
Даташит почитал более внимательно:
"Low noise: 0.06hPa (0.5m) in ultra low power mode"
Вот эти 6 Па шумов и дают пляску младших разрядов.

Приношу извинения автору темы за офтоп.

РК 35х1600

55

Re: Настраиваемый термометр на Arduino

Андрей,  а что в скетче поменять для но клапана?

56 (2017-09-23 14:56:56 отредактировано )

Re: Настраиваемый термометр на Arduino

dmi1082 ©:

а что в скетче поменять для но клапана?

Нормально открытый, отсечка по превышению температуры?

int pollPump() {
  uint32_t m = MIN_POLL_DELAY;
  if (termFSM[0].pausedState || termFSM[1].pausedState) {
#ifdef PUMP_PIN
    digitalWrite(PUMP_PIN, 0);
#endif
    digitalWrite(SOLENOID_VALVE_PIN, 0);
    return (1000);
  }
  digitalWrite(SOLENOID_VALVE_PIN, pumpState == PUMP_STOP ? 0 : 1);

на

int pollPump() {
  uint32_t m = MIN_POLL_DELAY;
  if (termFSM[0].pausedState || termFSM[1].pausedState) {
#ifdef PUMP_PIN
    digitalWrite(PUMP_PIN, 0);
#endif
    digitalWrite(SOLENOID_VALVE_PIN, 1);
    return (1000);
  }
  digitalWrite(SOLENOID_VALVE_PIN, pumpState == PUMP_STOP ? 1 : 0);

57

Re: Настраиваемый термометр на Arduino

Да. Спасибо!

58

Re: Настраиваемый термометр на Arduino

Андрей М ©:

Нормально открытый, отсечка по превышению температуры?

А тут не надо?

  pinMode(BEEPER_PIN, OUTPUT);
  digitalWrite(BEEPER_PIN, 0);
#ifdef PUMP_PIN
  pinMode(PUMP_PIN, OUTPUT);
  digitalWrite(PUMP_PIN, 0);
#endif
  pinMode(SOLENOID_VALVE_PIN, OUTPUT);
  digitalWrite(SOLENOID_VALVE_PIN, 0);

На -   digitalWrite(SOLENOID_VALVE_PIN, 1);   ?

У меня тоже нормально открытый клапан...

59

Re: Настраиваемый термометр на Arduino

Это поведение при старте устройства. Краник обычно закрыт, так что не критично, но правильнее в 1 (закрыто) перевести

60

Re: Настраиваемый термометр на Arduino

Андрей М ©:

Это поведение при старте устройства. Краник обычно закрыт, так что не критично, но правильнее в 1 (закрыто) перевести

Ага, так и понял!
Теперь еще в программу управление тиристором добавить, импульсным или фазовым способом, по выбору. А если еще и с измерением и стабилизацией напряжения или тока...  *YAHOO*  То получится идеальная и простая программа для самогонщика!!!   *THUMBSUP*