Автоматизация снятия показаний со счетчиков воды
Счетчики Valtec с импульсным выходом, цена одного импульса 10 литров. Счетчик импульсов собран на Arduino pro mini. Данные текущего расход выводятся на ЖК дисплей и передаются по uart на сервер Умного дома.
Схема счетчика
Для подавления дребезга контактов применил триггер Шмитта. Дополнительно в схему параллельно геркону добавил конденсатор.
Рисуем печатную плату
Печатаем на прозрачной пленке и с помощью фоторезиста переводим рисунок на будущую плату.
Травим в растворе хлорного железа и в итоге получаем готовую плату.
Полностью запаянная плата
Код для 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);
}







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:… Читать далее »
Спасибо, похоже парсер съел часть кода при добавлении.
Обновил код, теперь рабочий
у меня на монтажной плате и то компактнее с аппаратной защитой от дребезга на тригере Шмитта, плюс 485 интерфейс, что бы плюнуть показания куда нибудь далеко. Лайфхак, т.к. у счетчика valtec (а по схеме подключения это точно он) фронт тика проходит в районе 3 литров и 7 литров, то ловить тики надо по CHANGE (т.е. положительный и отрицательный фронты) и считать их каждый по пять литров.
Ну молодец. У меня вот так сделано сейчас https://blog.instalator.ru/archives/711