Самодельный датчик импульсов газового или водяного счетчика

Идея основана на использовании датчика Холла из любого компьютерного вентилятора для улавливания поля магнита, прикрепленного к младшему разряду многих счетчиков расхода газов и жидкостей.
Датчик, традиционно подключаю к Arduino. Ниже представлен скетч для него. Код написан с прицелом на дальнейшую модернизацию с отказом от проводов и питание от батарейки. По результатам мониторинга выяснилось, что датчик Холла за полный круг младшего разряда счетчика (у меня это около 30 сек) меняет свое состояние 3 раза: низкий, высокий и снова низкий уровень на выходе. Поэтому не обязательно все время держать его включенным (датчик потребляет 5 мА). Можно просыпаться раз в 2-4 сек, подавать питание на датчик, проверять состояние, передавать данные если состояние изменилось и снова засыпать. В простейшем случае можно подключить SD карту и просто сохранять туда данные, а можно по радио-каналу передавать на сервер для централизованной сборки и обработки данных.
Обработав выходные данные, можно получить много интересной информации. Как часто включается/выключается котел, как меняется температура в течение дня. Можно добавить сюда еще показания температуры из других комнат, с наружных датчиков температуры, влажности, освещенности, скорости и направления ветра, причем не обязательно своих. Можно использовать общедоступные данные, например с сайта narodmon.ru
В моем случае, видно, что мой газовый котел часто отключается с перерывом по 3.5 мин, но при этом температура держится очень ровно. Если же срабатывает внешнее механическое темореле, то котел отключется на час, а температура за это время падает на 2 градуса ( за окном -10). При достижении нижнего порога термореле, котел включается и тратит 1 м3 чтобы нагреть комнату на 1 градус, при этом греет без остановки. Выводы тут пока делать рано. Пока что вижу, что температурный датчик нужно отодвинуть подальше от наружной стены и батареи отопления. Но уже кое-что изменил. В частности уменьшил порог на батарейных регуляторах второго этажа, увеличил на первом этаже. Снизил уставку срабатывания термореле котла и снизил уставку температуры нагрева воды на самом котле — все это чтобы убрать частые остановки и ввести котел в режим максимального КПД. В дальнейшем вижу замену внешнего механического термореле на самодельный электронный, который будет иметь меньший гистерезис и подстраиваться под температуру в разных комнатах с учетом наружного датчика температуры.

dsc_6233
Ну и видеообзор как это все создавалось.


/* GazCounter http://blog.regimov.net/diy-gaz-counter/
* Author: Istomin Evgeny / aka Gena
* 01.12.2016
* Use: - Hall Effect Switches Sensor IC: U18 http://www.utc-ic.com/uploadfile/2012/0331/20120331125747992.pdf
* - (optional) Temperature sensor IC: DS18B20 http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
* - Arduino board Pro Micro (atmega32u4) https://www.arduino.cc/en/Main/ArduinoBoardMicro
*/

// _______
#define OUT 2 // ----| |\ //
#define GND 3 // ----| U18 | |
#define VCC 4 // ----|_______|/
#define BAUDRATE 115200
#define DS18B20 0 // use DS18B20
#define LOWPOW 0 // not use library LowPower for atmega32u4. Just for 328p
#define LOOP_PERIOD_MS 60 // must be equal LOOP_PERIOD
#if LOWPOW
#include < LowPower.h >
#define LOOP_PERIOD SLEEP_60MS
#endif
#if DS18B20
#define TEMP_PERIOD_MS 1000
#define TEMP_MAX_PERIOD_MS 15*60*1000 // 15 min
// _______
#define DS18B20_GND 5 // ----------------| |\ //
#define DS18B20_OUT 6 // -----*----------| 18B20 | |
#define DS18B20_VCC 7 // ---| |--[4k7]-*-|_______|/
// |__________|
#define TEMP_BIT 0x7F // 12 bit
#define TEMP_H 0xFF
#define TEMP_L 0x00
#define WRITESCRATCH 0x4E // Write to EEPROM
#define SKIPROM 0xCC // Skip ROM
#define STARTCONVO 0x44 // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH 0xBE // Read EEPROM
#include < OneWire.h >
OneWire ds(DS18B20_OUT);
uint32_t dTime = 0;
#else
#define TEMP_AVERAGE_COUNT 3
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Set the internal reference 2.56 and mux[5:0] = 100111 for atmega32u4
#define ADC_ADMUX ((1<<REFS1) | (1<<REFS0) | (1<<MUX2)| (1<<MUX1)| (1<<MUX0))
// http://www.atmel.com/Images/Atmel-8108-Calibration-of-the-AVR's-Internal-Temperature-Reference_ApplicationNote_AVR122.pdf
#define T_OFFSET 266.17
#define T_K 0.807
#else // for 328p
// Set the internal reference 1.1 V
#define ADC_ADMUX ((1<<REFS1) | (1<<REFS0) | (1<<MUX3))
#define T_OFFSET 324.31
#define T_K 0.819
#endif
#endif

uint32_t T=0;
uint32_t imp_count=0;
float Temperature=0;

void wait()
{
#if LOWPOW
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
LowPower.idle(LOOP_PERIOD, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF,
TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
#else // works on 88 / 168 / 328
LowPower.idle(LOOP_PERIOD, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF,
SPI_OFF, USART0_OFF, TWI_OFF);
#endif
#else
delay(LOOP_PERIOD_MS);
#endif
T++;
}

void mprintf(String s)
{
Serial.begin(BAUDRATE);
while (!Serial); // wait for serial port to connect. Needed for native USB port only
Serial.println(s);
Serial.flush();
Serial.end();
}

void setup() {
pinMode(VCC, OUTPUT);
pinMode(GND, OUTPUT);
pinMode(OUT, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(VCC, HIGH);
digitalWrite(GND, LOW);
digitalWrite(OUT, HIGH);
#if DS18B20
pinMode(DS18B20_GND, OUTPUT);
pinMode(DS18B20_VCC, OUTPUT);
pinMode(DS18B20_OUT, INPUT);
digitalWrite(DS18B20_GND, LOW);
digitalWrite(DS18B20_VCC, HIGH);
digitalWrite(DS18B20_OUT, HIGH);
ds.reset();
ds.write(SKIPROM);
ds.write(WRITESCRATCH);
ds.write(TEMP_H);
ds.write(TEMP_L);
ds.write(TEMP_BIT); // set maximum resolution 12 bit
#endif

ADMUX = ADC_ADMUX;
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADCSRB |= (1 << MUX5);
#endif
ADCSRA |= (1<<ADEN); // enable the ADC

PRR0 |= (1 << PRTIM2)|(1 << PRTIM1)|(1 << PRTIM2)|(1 << PRTWI)|(1 << PRSPI)|(1 << PRADC)|(0 << PRUSB); //
PRR1 |= (1 << PRTIM3)|(1 << PRUSART1);
mprintf("Vm3\tdTsec\tTsec\tTemp");
}

void loop() {
uint16_t dt=T;
while (digitalRead(OUT) == 1) wait();
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
imp_count++;
GetTemp();
while (digitalRead(OUT) == 0) wait();
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
String s = String(imp_count * 0.01) + char(9) + String((T-dt)*LOOP_PERIOD_MS/1000.0) + char(9) +
String(T/1000.0*LOOP_PERIOD_MS) + char(9) + String(GetTemp());
mprintf(s);
}

float GetTemp()
{

#if DS18B20

if ((T-dTime)*LOOP_PERIOD_MS >= TEMP_PERIOD_MS) {
if ((T-dTime)*LOOP_PERIOD_MS < TEMP_MAX_PERIOD_MS) {
ds.reset();
ds.write(SKIPROM);
ds.write(READSCRATCH);
byte data[2];
data[0] = ds.read();
data[1] = ds.read();
Temperature=((data[1]<< 8) + data[0]) * 0.0625; // one bit = 0,0625 celsius
dTime=T;
}
ds.reset();
ds.write(SKIPROM);
ds.write(STARTCONVO);
}

#else
// To use the built-in temperature sensor you need to use LowPower library,
// because it is a measurement of core temperature (plus ~10°C to the air temperature).

PRR0 &= ~(1<<PRADC);
wait();
ADCSRA |= (1<<ADSC); // Start the ADC
while ((ADCSRA & (1<<ADSC)) != 0); // Detect end-of-conversion
uint16_t wADC = ADCW; // Reading register "ADCW" takes care of how to read ADCL and ADCH.
Temperature = Temperature * (TEMP_AVERAGE_COUNT-1)/TEMP_AVERAGE_COUNT
+ ((float)wADC - T_OFFSET ) * T_K / TEMP_AVERAGE_COUNT;
PRR0 |= (1<<PRADC);

#endif

return Temperature;
}

Самодельный датчик импульсов газового или водяного счетчика: 1 комментарий

  1. Разобрал 4 вентилятора, там уже не просто датчик Холла, а контроллер переключения катушек со встроенным биполярным датчиком Холла. Биполярные датчики в данном случае не подойдут, так как они меняют состояние по полюсам NS.
    Также пробовал популярный датчик а3144, но у него не хватает чувствительности.
    Для данного проекта нужен очень чувствительный датчик. Буду пробовать датчик с BOP до 100.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *