Автоматизация снятия показаний со счетчиков воды

Счетчики Valtec с импульсным выходом, цена одного импульса 10 литров. Счетчик импульсов собран на Arduino pro mini. Данные текущего расход выводятся на ЖК дисплей и передаются по uart на сервер Умного дома.

water-meter

Схема счетчика

Схема счетчика

Для подавления дребезга контактов применил триггер Шмитта. Дополнительно в  схему  параллельно геркону добавил конденсатор.

Безымянный

Рисуем печатную плату

плата

Печатаем на прозрачной пленке и с помощью фоторезиста переводим рисунок на будущую плату.

IMG_20150305_001807

Травим в растворе хлорного железа и в итоге получаем готовую плату.

IMG_20150305_204842

Полностью запаянная плата

IMG_20150306_202410

Код для Arduino

/*
Перед загрузкой данной программы, необходимо обнулить eeprom, т.е. записать во все ячейки eeprom 0.
*/
#include <EEPROM.h>
#include <LiquidCrystal.h> //Подключаем библиотеку для работы с LCD

volatile unsigned long counter_1 = 699 ; // Начальные показания Холодной воды
volatile unsigned long counter_2 = 696; // Начальные показания Горячей воды
unsigned long counter_max = 70000; //Максимальное значения которое хранится в одном банке памяти
#define rashod 1 //Литры за один импульс *10
#define pin1 2 // 2-й цифровой пин, нулевое прерывание Холодная вода
#define pin2 3 // 3-й цифровой пин, первое прерывание Горячая Вода
#define pinButton 10 //Кнопка 1
#define pinButton2 11 //Кнопка 2
int counter1_byte = 0; //Ячейка eeprom для хранения данных Холодной воды
int counter2_byte = 64; //Ячейка eeprom для хранения данных Горячей воды
int adress1_byte = 128; //Адрес хранения адреса хранения данных холодной воды))
int adress2_byte = 130; //Адрес хранения адреса хранения данных горячей воды)))
int adress1 = 0;
int adress2 = 0;
long previousMillis = 0;
int val=0;
volatile unsigned long newcounter_1 = 0;
volatile unsigned long newcounter_2 = 0;
LiquidCrystal lcd(9, 8, 7, 6, 5, 4);
 /*LCD RS pin to digital pin 9
 LCD Enable pin to digital pin 8
 LCD D4 pin to digital pin 7
 LCD D5 pin to digital pin 6
 LCD D6 pin to digital pin 5
 LCD D7 pin to digital pin 4*/
//static unsigned long millis_prev = millis();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
 void setup() {
 Serial.begin(9600);
 lcd.begin(16, 2);
 lcd.clear();
 if (EEPROM.read(150)==0){ // Если первый запуск то запишем в ячейки адреса где хранятся данные
 if (counter_1 >= counter_max && counter_1<=(counter_max*2)-1){
 counter1_byte = 4;
 }
 if (counter_1>=counter_max*2 && counter_1<=(counter_max*3)-1){
 counter1_byte = 8;
 }
 if (counter_1>=counter_max*3 && counter_1<=(counter_max*4)-1){
 counter1_byte = 12;
 }

 if (counter_2>=counter_max && counter_2<=(counter_max*2)-1){
 counter2_byte = counter2_byte + 4;
 }
 if (counter_2>=counter_max*2 && counter_2<=(counter_max*3)-1){
 counter2_byte = counter2_byte + 8;
 }
 if (counter_2>=counter_max*3 && counter_2<=(counter_max*4)-1){
 counter2_byte = counter2_byte + 12;
 }
 EEPROM.write(adress1_byte, counter1_byte);
 EEPROM.write(adress2_byte, counter2_byte);
 EEPROMWriteInt(counter1_byte, counter_1); //Пишем показания счетчика в eeprom из переменной
 EEPROMWriteInt(counter2_byte, counter_2); //Пишем показания счетчика в eeprom из переменной
 EEPROM.write(150, 1);
 }
 counter1_byte = EEPROM.read(adress1_byte);
 counter2_byte = EEPROM.read(adress2_byte);
 counter_1 = EEPROMReadInt(counter1_byte);
 counter_2 = EEPROMReadInt(counter2_byte); 

 //Настраиваем цифровой вход
 pinMode(13, OUTPUT);
 pinMode(pinButton, INPUT); // Порт для кнопки 1
 digitalWrite(pinButton, 1);
 pinMode(pinButton2, INPUT); // Порт для кнопки 2
 digitalWrite(pinButton2, 1);
 pinMode(pin1, INPUT); // Сюда будем подключать подтягивающий резюк
 pinMode(pin2, INPUT); // Сюда будем подключать подтягивающий резюк
 digitalWrite(pin1, 1); // "Подключаем" подтягивающий резистор (реализован внутри ATmega)
 digitalWrite(pin2, 1); // "Подключаем" подтягивающий резистор (реализован внутри ATmega)
 attachInterrupt(0, count_1, FALLING); // задаём обработчик прерывания 0 (2-й пин).
 // прерывание будет при изменении уровня с HIGHT на LOW
 // вызывать функицю count
 attachInterrupt(1, count_2, FALLING); // задаём обработчик прерывания 1 (3-й пин).
 // прерывание будет при изменении уровня с HIGHT на LOW
 // вызывать функицю count
 }
///////////////////////////////////ОСНОВНОЙ ЦИКЛ/////////////////////////////////////////////
void loop(){
 if(digitalRead(pinButton)==HIGH){ //Когда нажата кнопка 1
 if (millis() - previousMillis > 1000){ //Если от предыдущего срабатывания прошло больше 100 миллисекунд
 previousMillis = millis(); //Запоминаем время первого срабатывания
 digitalWrite(13, 1); //Включение подсветки дисплея
 }
 }
 else {//когда не нажата
 digitalWrite(13, 0); //Выключение подсветки дисплея
 }
 // delay(100);
 lcd.setCursor(0, 0); // устанавливаем курсор в 0-ом столбце, 1 строке (начинается с 0)
 lcd.print("Cold=");
 lcd.print(counter_1);
 lcd.setCursor(0, 1); // устанавливаем курсор в 0-ом столбце, 2 строке
 lcd.print("Hot =");
 lcd.print(counter_2);

 if (Serial.available() > 0) {
 if (Serial.find("Status")) { //Ищем на входе символ "C" Если нашли получаем данные
 for (int i=0; i <= 1; i++){
 Serial.print("C");
 Serial.print(counter_1); // выводим в консоль значение счётчика
 Serial.print("H");
 Serial.print(counter_2); // выводим в консоль значение счётчика
 Serial.print("$");
 }
 }
 if (Serial.find("CHG")) { //Ищем на входе символ "C" Если нашли получаем данные
 unsigned long change_counter1 = Serial.parseInt(); // Парсинг числа во входящем потоке
 counter_1 = change_counter1;
 unsigned long change_counter2 = Serial.parseInt(); // Парсинг числа во входящем потоке
 counter_2 = change_counter2;
 if (counter_1 > 0 && counter_2 > 0){
 if (counter_1 >= counter_max && counter_1<=(counter_max*2)-1){
 counter1_byte = 4;
 }
 if (counter_1>=counter_max*2 && counter_1<=(counter_max*3)-1){
 counter1_byte = 8;
 }
 if (counter_1>=counter_max*3 && counter_1<=(counter_max*4)-1){
 counter1_byte = 12;
 }

 if (counter_2>=counter_max && counter_2<=(counter_max*2)-1){
 counter2_byte = counter2_byte + 4;
 }
 if (counter_2>=counter_max*2 && counter_2<=(counter_max*3)-1){
 counter2_byte = counter2_byte + 8;
 }
 if (counter_2>=counter_max*3 && counter_2<=(counter_max*4)-1){
 counter2_byte = counter2_byte + 12;
 }
 EEPROM.write(adress1_byte, counter1_byte);
 EEPROM.write(adress2_byte, counter2_byte);
 EEPROMWriteInt(counter1_byte, counter_1); //Пишем показания счетчика в eeprom из переменной
 EEPROMWriteInt(counter2_byte, counter_2); //Пишем показания счетчика в eeprom из переменной
 }
 }
 }

 if (counter_1 != newcounter_1 || counter_2 != newcounter_2){
 detachInterrupt (0);
 detachInterrupt (1);
 for (int i=0; i <= 1; i++){
 Serial.print("C");
 Serial.print(counter_1); // выводим в консоль значение счётчика
 Serial.print("H");
 Serial.print(counter_2); // выводим в консоль значение счётчика
 Serial.print("$");
 newcounter_1 = counter_1;
 newcounter_2 = counter_2;
 }
 attachInterrupt(0, count_1, FALLING);
 attachInterrupt(1, count_2, FALLING);
 }
}
//////////////////////////////////ФУНКЦИИ СЧЕТЧИКА//////////////////////////////////////
void count_1(){
 detachInterrupt (0);
 // вызывается прерыванием 0 от 2-го цифрового входа
 static unsigned long millis_prev;
 if(millis()-50 > millis_prev) { //Защита от дребезга контаков
 counter_1=counter_1+rashod; // ясен перец, увеличиваем переменную. Утекло ещё 10 литров
 if (counter_1>=counter_max && counter_1<=(counter_max*2)-1){
 counter1_byte = counter1_byte + 4;
 EEPROM.write(adress1_byte, counter1_byte);
 }
 if (counter_1>=counter_max*2 && counter_1<=(counter_max*3)-1){
 counter1_byte = counter1_byte + 8;
 EEPROM.write(adress1_byte, counter1_byte);
 }
 if (counter_1>=counter_max*3 && counter_1<=(counter_max*4)-1){
 counter1_byte = counter1_byte + 12;
 EEPROM.write(adress1_byte, counter1_byte);
 }
 EEPROMWriteInt(counter1_byte, counter_1); //Пишем данные счетчика в энергонезависимую память
 millis_prev = millis();
 }
 attachInterrupt(0, count_1, FALLING);
}
void count_2(){
 detachInterrupt (1);
 // вызывается прерыванием 1 от 3-го цифрового входа
 static unsigned long millis_prev;
 if(millis()-50 > millis_prev) { //Защита от дребезга контаков
 counter_2=counter_2+rashod; // ясен перец, увеличиваем переменную. Утекло ещё 10 литров
 if (counter_2>=counter_max && counter_2<=(counter_max*2)-1){
 counter2_byte = counter2_byte + 4;
 EEPROM.write(adress2_byte, counter2_byte);
 }
 if (counter_2>=counter_max*2 && counter_2<=(counter_max*3)-1){
 counter2_byte = counter2_byte + 8;
 EEPROM.write(adress2_byte, counter2_byte);
 }
 if (counter_2>=counter_max*3 && counter_2<=(counter_max*4)-1){
 counter2_byte = counter2_byte + 12;
 EEPROM.write(adress2_byte, counter2_byte);
 }
 EEPROMWriteInt(counter2_byte, counter_2); //Пишем данные счетчика в энергонезависимую память
 millis_prev = millis();
 }
 attachInterrupt(1, count_2, FALLING);
}
////////////////////////////////////ФУНКЦИИ ПАМЯТИ///////////////////////////////////////////
//кушаем аж 4 байта EEPROM
void EEPROMWriteInt(int p_address, unsigned long p_value){
 byte four = (p_value & 0xFF);
 byte three = ((p_value >> 8) & 0xFF);
 byte two = ((p_value >> 16) & 0xFF);
 byte one = ((p_value >> 24) & 0xFF);
 EEPROM.write(p_address, four);
 EEPROM.write(p_address + 1, three);
 EEPROM.write(p_address + 2, two);
 EEPROM.write(p_address + 3, one);
}
// считаем нашы 4 байта
unsigned long EEPROMReadInt(int p_address){
 long four = EEPROM.read(p_address);
 long three = EEPROM.read(p_address + 1);
 long two = EEPROM.read(p_address + 2);
 long one = EEPROM.read(p_address + 3);
 return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
 }
Подписаться
Уведомлять
guest

4 комментариев
Старые вначале
Новые вначале По голосам
Межтекстовые Отзывы
Посмотреть все комментарии
Антон
Антон
7 лет назад

ide 1.6.9 Вот такие ошибки sketch_aug12a:44: error: expected unqualified-id before ‘else’ else {//когда не нажата ^ sketch_aug12a:48: error: ‘lcd’ does not name a type lcd.setCursor(0, 0); // устанавливаем курсор в 0-ом столбце, 1 строке (начинается с 0) ^ sketch_aug12a:49: error: ‘lcd’ does not name a type lcd.print(«Cold=»); ^ sketch_aug12a:50: error: ‘lcd’ does not name a type lcd.print(counter_1); ^ sketch_aug12a:51: error: ‘lcd’ does not name a type lcd.setCursor(0, 1); // устанавливаем курсор в 0-ом столбце, 2 строке ^ sketch_aug12a:52: error: ‘lcd’ does not name a type lcd.print(«Hot =»); ^ sketch_aug12a:53: error: ‘lcd’ does not name a type lcd.print(counter_2); ^ sketch_aug12a:55: error:… Читать далее »

Игорь
Игорь
3 лет назад

у меня на монтажной плате и то компактнее с аппаратной защитой от дребезга на тригере Шмитта, плюс 485 интерфейс, что бы плюнуть показания куда нибудь далеко. Лайфхак, т.к. у счетчика valtec (а по схеме подключения это точно он) фронт тика проходит в районе 3 литров и 7 литров, то ловить тики надо по CHANGE (т.е. положительный и отрицательный фронты) и считать их каждый по пять литров.

изображение_viber_2021-03-07_16-44-26.jpg
4
0
Поделиться своими мыслямиx