Обнуление показания тока при нажатии на кнопку (ACS712)
- Войдите на сайт для отправки комментариев
Здравствуйте, знатаки, помогите пожалуйста. Сделал достаточно точный Ваттметр с использованием 2-х датчиков ACS712 и ADS1115, всё налажено и показывает ток очень точно, но не в этом суть. К примеру, готовый прибор лёжа в корпусе на столе возле компа где занимался отладкой показаний датчиков, при отключенной нагрузке показания на дисплее равны нулю как и должно быть, а стоит мне отодвинуть на метр прибор, нулевые показания либо повышаются на 20-30mA, либо идут в минус (ну это нормально для этих датчиков, из-за воздействия внешних магнитных полей).
Так вот в чём нужна помощь, хочу сделать так: в местах где буду использовать прибор для измерения тока и т.д. сделать обычную тактовую кнопку, при коротком нажатии на которую просто произойдёт обнуление показаний (именно в том месте где будет лежать прибор) и измерения будут происходить от обнуления.
По сути нужно тоже самое, как Обнуление ТАРЫ в обычных весах.
Не могу понять как реализовать такую возможность программно. В инете что-то не смог найти то, что нужно или как это реализовать.
Помогите пожалуйста куском кода - как образец (как это реализеется с объяснением).
P.S. подключить кнопку и как её считать false или true - это без проблем. а как сделать значение Current с выводом показаний с учётом обнуления не знаю.
Если нужно, полностью рабочий код под спойлером:
/******************************************
Ваттметр с ЖК-дисплеем
v.3.0 Июнь 2019
-------------------------------------------
Железо:
- Arduino Pro mini
- 16-bit ADC модуль на основе ADS1115
- LCD дисплей 1602 по шине I2C
- 2 датчика тока ACS712 на 20 Ампер
-------------------------------------------
Возможности (КРАТКОВРЕМЕННЫЕ, максимальные):
- Измерение Напряжения до 32V
- Измерение Тока до 20A
- Измерение Мощности до 640W
- Измерение залитой ёмкости через Ваттметр до 9999Ah
******************************************/
//Подключаем библиотеки
#include <Wire.h>
#include <Adafruit_ADS1015.h>
//#include <LiquidCrystal_I2C.h> //Библиотека Дисплея (закомментировал, так как использую аналогичную библиотеку с Русскими буквами)
#include <LCD_1602_RUS.h> //Библиотека Дисплея "LiquidCrystal_I2C", только с Русскими буквами
#include <SPI.h>
#include <SmartDelay.h> // Библиотека для ожидания без Delay()
//--НАСТРОЙКА КОМПЕНСАЦИЙ И ЧУВСТВИТЕЛЬНОСТИ--//
#define voltageZeroCorrection -0.055 //Компенсирует смещение нулевой точки в канале ADC0
#define currentZeroCorrection -0.0697 //Компенсирует разницу нулевых точек между двумя модулями ACS712
#define voltageSensitivity 0.12486 //Чувствительность ADS1115 = 0.125 для GAIN_ONE (подробности смотрите в datasheet ADS1115)
#define currentSensitivity 0.20375 //Чувствительность двух ACS712 (датчики подключены в противоположном направлении)
#define NOISE_SUPPRESSOR 1 //Шумоподавление (Прокомментируйте эту строку, чтобы отключить шумоподавитель)
//--ЗАДАЁМ I2C АДРЕСА МОДУЛЕЙ--//
#define ADC_I2C_ADDR 0x48 //Аналого-цифровой преобразователь адреса I2C = 72
#define DSP_I2C_ADDR 0x3F //Дисплей LCD1602 I2C адрес = 63
//--ОБЪЕКТЫ--//
Adafruit_ADS1115 ads(ADC_I2C_ADDR); //ADC (АЦП) объект
//LiquidCrystal_I2C lcd(DSP_I2C_ADDR, 16, 2); //Дисплейный объект: 16 символов подряд, 2 строки
LCD_1602_RUS lcd(DSP_I2C_ADDR, 16, 2); //Тоже самое, что и "LiquidCrystal_I2C", только с выводом Русских букв
SmartDelay AntiDelay(150000UL); //Ожидание в Микросекундах. Задаём интервал в 150 миллисекунд. Простое ожидание без задержки
//--ПЕРЕМЕННЫЕ ДЛЯ ИЗМЕРЕНИЯ ТОКА И НАПРЯЖЕНИЯ--//
float Voltage = 0.0; //Результат измерения напряжения
float Current = 0.0; //Результат измерения тока
float Watt = 0.0; //Результат измерения мощности
float Capacity = 0.0; //Начальная ёмкость в Ah
unsigned long prevMillis; //Переменная таймера используются для расчёта измерителя ёмкости
//------ Возвращать результаты как среднее из <итераций> образцов напряжения ------
// 1-й аргумент - номер канала ADC (АЦП) (0 - канал напряжения, 1 и 2 - токовые каналы)
// 2-й аргумент - коэффициент деления входного напряжения (5 - канал напряжения, 1 - канал тока)
// 3-й аргумент - количество итераций (повторите измерение для <итераций> раз и верните среднее значение)
float measureSingleEnded(uint8_t channel, uint8_t divider, uint8_t iterations) {
int16_t adc; //16-разрядное целое число, полученное из ADC (АЦП)
long accum = 0; //сумма <итераций> измерений из <итераций> измерения
for (int i = 0; i < iterations; i++) {
adc = ads.readADC_SingleEnded(channel); //Присваиваем переменной adc значения из channel
accum += adc;
}
/* Voltage = accum * divider * voltageSensitivity / 1000 / iterations (Так происходит расчёт измерения Напряжения)
где:
accum - сумма измерений накопленная а accum
divider - коэффициент деления входного напряжения
voltageSensitivity - 0.1875 for GAIN_TWOTHIRDS(усиление две третих); 0.125 for GAIN_ONE(однократное усиление);
0.0625 for GAIN_TWO(2-х кратное усиление) (подробности смотрите в datasheet ADS115)
1000 - преобразование mV в V
iterations - количество измерений для усреднения
*/
return accum * divider * voltageSensitivity / 1000 / iterations;
}
//--ИЗМЕРЕНИЕ НАПРЯЖЕНИЯ И ТОКА И ВЫВОД РЕЗУЛЬТАТА НА ДИСПЛЕЙ--//
void measure(void) {
//++++ измерение напряжения ++++
// Канал 0, коэффициент входного делителя = 8, среднее из 10 итераций
// добавление коррекции для компенсации смещения нулевой точки входа ADC (АЦП)
Voltage = measureSingleEnded(0, 8, 10) + voltageZeroCorrection;
// сократить отрицательные значения
Voltage = (Voltage > 0) ? Voltage : 0;
//++++ измерение тока ++++
// Канал 1 и 2, без делителя, в среднем 10 итераций
float Chan1 = measureSingleEnded(1, 1, 10);
float Chan2 = measureSingleEnded(2, 1, 10);
// Рассчитать значения для коррекции и компенсации нулевой разницы двух модулей ACS712
Current = (Chan1 - Chan2) / currentSensitivity + currentZeroCorrection;
//++++ вычисление мощности ++++
Watt = Voltage * Current; // Расчёт Мощности нагрузки в Реальном времени.
//++++ вычисление залитой ёмкости ++++
Capacity += (Current * (millis() - prevMillis) / 3600000); //расчёт залитой ёмкости в Ампер-Часах (Ач) через Ваттметр
prevMillis = millis();
#ifdef NOISE_SUPPRESSOR
//++++ обрабатывать значения напряжения менее 5мВ как ноль для подавления шума вблизи нуля ++++
if (Voltage < 0.005) {
Voltage = 0;
}
//++++ обрабатывать значения тока менее 20мА как ноль для подавления шума вблизи нуля ++++
if (abs(Current) < 0.020) {
Current = 0;
}
//++++ обрабатывать значения мощности менее 21мВт как ноль для подавления шума вблизи нуля ++++
if (abs(Watt) < 0.021) {
Watt = 0;
}
#endif
//++++ Вывод НАПРЯЖЕНИЯ на Дисплей ++++
lcd.setCursor(2, 0); //Устанавливаем курсор в позицию (на 2 колонку 1-й строки)
lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения напряжения до 10V и более и дальнейшего его снижения менее 10V))
if (Voltage >= 10.0) { //Если напряжение с переменной "Voltage" больше или равно 10, то...
lcd.setCursor(2, 0); //... устанавливаем курсор на (2 колонку 1-й строки)
} else { //Иначе...
lcd.setCursor(3, 0); //Переводим курсор на (3 колонка 1-й строки)
}
lcd.print(Voltage, 2); //выводим на дисплей значение переменной "Voltage" с 2-ми знаками после запятой.
//++++ Вывод ТОКА на Дисплей ++++
lcd.setCursor(2, 1); //Устанавливаем курсор в позицию (на 2 колонку 2-й строки)
lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения тока до 10A и более и дальнейшего его снижения менее 10А)
if (Current < 0) { //Если ток с переменной "Current" меньше нуля, то...
lcd.setCursor(2, 1); //... устанавливаем курсор на (2 колонку 2-й строки)
} else { //Иначе...
lcd.setCursor(3, 1); //Переводим курсор на (3 колонка 2-й строки)
}
lcd.print(Current, 2); //выводим на дисплей значение переменной "Current" с 2-ми знаками после запятой.
//++++ Вывод МОЩНОСТИ на Дисплей ++++
lcd.setCursor(10, 0); //Устанавливаем курсор в позицию (на 10 колонку 1-й строки)
lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения мощности до 10W и более и дальнейшего его снижения менее 10W))
if (Watt >= 10.0) { //Если мощность с переменной "Watt" больше или равно 10W, то...
lcd.setCursor(10, 0); //... устанавливаем курсор на (10 колонку 1-й строки)
} else { //Иначе...
lcd.setCursor(11, 0); //Переводим курсор на (11 колонку 1-й строки)
}
lcd.print(Watt, 2); //выводим на дисплей значение переменной "Watt" с 2-ми знаками после запятой.
//++++ Вывод залитой ЁМКОСТИ через Ваттметр на Дисплей ++++
lcd.setCursor(11, 1); //Переводим курсор на (11 колонку 2-й строки)
lcd.print(Capacity, 3); //выводим на дисплей значение переменной "Capacity" с 3-ми знаками после запятой.
}
//--ОТОБРАЖЕНИЕ СТАТИЧЕСКИХ СИМВОЛОВ В ЗАВИСИМОСТИ ОТ РЕЖИМА--//
void setDisplayMode() {
lcd.clear(); //Очистить дисплей
lcd.setCursor(0, 0); //Переместить курсор на 1 строку и 1 символ
lcd.print("U:"); //Показать букву "U:" (Напряжение)
lcd.setCursor(0, 1); //Переместить курсор на 2 строку и 1 символ
lcd.print("I:"); //Показать букву "I:" (Ток)
lcd.setCursor(8, 0); //Переместить курсор на 1 строку и 8 символ
lcd.print("W:"); //Показать букву "W:" (Мощность)
lcd.setCursor(8, 1); //Переместить курсор на 1 строку и 8 символ
lcd.print("Ah:"); //Показать букву "Ah:" (Ёмкость)
lcd.noBlink(); //Отключить мигание курсора
}
//*******************************
// ИНИЦИАЛИЗАЦИЯ
//*******************************
void setup(void) {
// инициализировать модуль ADC (АЦП)
ads.begin();
ads.setGain(GAIN_ONE); //4.096V полная величина Спектра
// инициализировать LCD Дисплей
lcd.begin();
lcd.backlight();
// выводим надпись на Дисплей перед загрузкой кода
lcd.setCursor(0, 0);
lcd.print(" Ваттметр Otto");
lcd.setCursor(5, 1);
lcd.print("4 в 1");
delay(2500);
// подготовить экран
setDisplayMode();
}
//********************************
// ОСНОВНОЙ ЦИКЛ ПРОГРАММЫ
//********************************
void loop(void)
{
if (AntiDelay.Now()) { // Обновление данных на дисплее раз в 150 миллисекунд без задержки.
measure(); //Измеряет напряжение,ток, мощность и ёмкость в Ah и отображает результаты на дисплее.
}
}
Вам нужна установка нуля, так? Ну
1. заведите переменную "поправка".
2. показывайте всегда не ток, который Вы считали с датчика, а "считанный ток" минус "поправка".
3. изначально переменная "поправка" равна 0 (т.е. показывается то, что считали с датчика).
4. в момент нажатия кнопки считывайте ток и считанное значение присвойте переменной "поправка".
Прибор тут же начнёт показывать 0 (см п.2 выше). И впредь (до нового нажатия кнопки или включения питания) будет показывать результаты с запомненной поправкой.
Это то, что нужно?
добавлю к сообщению Евгения
То, что он написал - надо сделать и для тока и для напряжения.
И еще полезно по этой же кнопке обнулять сумму емкости, которую вы там в подсчитываете
ЕвгенийП и b707, спасибо вам большое за пояснения. Написал код как вы сказали, работает так как я и задумал.
Завтра выложу готовый код, может кто захочет повторить проект или кому пригодятся куски кода.
Выкладываю рабочий код + Схема
Код:
/****************************************** Ваттметр с ЖК-дисплеем v.3.0 Июнь 2019 ------------------------------------------- Железо: - Arduino Pro mini - 16-bit ADC модуль на основе ADS1115 - LCD дисплей 1602 по шине I2C - 2 датчика тока ACS712 на 20 Ампер ------------------------------------------- Возможности (КРАТКОВРЕМЕННЫЕ, максимальные): - Измерение Напряжения до 32V - Измерение Тока до 20A - Измерение Мощности до 640W - Измерение залитой ёмкости через Ваттметр до 9999Ah ******************************************/ //Подключаем библиотеки #include <Wire.h> #include <Adafruit_ADS1015.h> //#include <LiquidCrystal_I2C.h> //Библиотека Дисплея (закомментировал, так как использую аналогичную библиотеку с Русскими буквами) #include <LCD_1602_RUS.h> //Библиотека Дисплея "LiquidCrystal_I2C", только с Русскими буквами #include <SPI.h> #include <SmartDelay.h> // Библиотека для ожидания без Delay() //--НАСТРОЙКА КОМПЕНСАЦИЙ И ЧУВСТВИТЕЛЬНОСТИ--// #define voltageZeroCorrection -0.055 //Компенсирует смещение нулевой точки в канале ADC0 #define currentZeroCorrection -0.0697 //Компенсирует разницу нулевых точек между двумя модулями ACS712 #define voltageSensitivity 0.12486 //Чувствительность ADS1115 = 0.125 для GAIN_ONE (подробности смотрите в datasheet ADS1115) #define currentSensitivity 0.20375 //Чувствительность двух ACS712 (датчики подключены в противоположном направлении) #define NOISE_SUPPRESSOR 1 //Шумоподавление (Прокомментируйте эту строку, чтобы отключить шумоподавитель) #define RESET_BUTTON 8 //Кнопка для обнуления напряжения, тока и ёмкости (подключена на pin 8) //--ЗАДАЁМ I2C АДРЕСА МОДУЛЕЙ--// #define ADC_I2C_ADDR 0x48 //Аналого-цифровой преобразователь адреса I2C = 72 #define DSP_I2C_ADDR 0x3F //Дисплей LCD1602 I2C адрес = 63 //--ОБЪЕКТЫ--// Adafruit_ADS1115 ads(ADC_I2C_ADDR); //ADC (АЦП) объект //LiquidCrystal_I2C lcd(DSP_I2C_ADDR, 16, 2); //Дисплейный объект: 16 символов подряд, 2 строки LCD_1602_RUS lcd(DSP_I2C_ADDR, 16, 2); //Тоже самое, что и "LiquidCrystal_I2C", только с выводом Русских букв SmartDelay AntiDelayLoop(150000UL); //Ожидание в Микросекундах. Задаём интервал в 150 миллисекунд. Простое ожидание без задержки для цикла "loop" //--ПЕРЕМЕННЫЕ ДЛЯ ИЗМЕРЕНИЯ ТОКА И НАПРЯЖЕНИЯ--// float PrimaryVoltage = 0.0; //Первичный (до обнуления) результат измерения напряжения float AmendmentVoltage = 0.0; //Поправочная переменная для вычета погрешности из напряжения перед измерением (для обнуления) float Voltage = 0.0; //Результат измерения напряжения float PrimaryCurrent = 0.0; //Первичный (до обнуления) результат измерения тока float AmendmentCurrent = 0.0; //Поправочная переменная для вычета погрешности из тока перед измерением (для обнуления) float Current = 0.0; //Конечный (после обнуления) результат измерения тока float Watt = 0.0; //Результат измерения мощности float Capacity = 0.0; //Начальная ёмкость в Ah unsigned long prevMillis; //Переменная таймера используются для расчёта измерителя ёмкости //------ Возвращать результаты как среднее из <итераций> образцов напряжения ------ // 1-й аргумент - номер канала ADC (АЦП) (0 - канал напряжения, 1 и 2 - токовые каналы) // 2-й аргумент - коэффициент деления входного напряжения (5 - канал напряжения, 1 - канал тока) // 3-й аргумент - количество итераций (повторите измерение для <итераций> раз и верните среднее значение) float measureSingleEnded(uint8_t channel, uint8_t divider, uint8_t iterations) { int16_t adc; //16-разрядное целое число, полученное из ADC (АЦП) long accum = 0; //сумма <итераций> измерений из <итераций> измерения for (int i = 0; i < iterations; i++) { adc = ads.readADC_SingleEnded(channel); //Присваиваем переменной adc значения из channel accum += adc; } /* PrimaryVoltage = accum * divider * voltageSensitivity / 1000 / iterations (Так происходит расчёт измерения Напряжения) где: accum - сумма измерений накопленная а accum divider - коэффициент деления входного напряжения voltageSensitivity - 0.1875 for GAIN_TWOTHIRDS(усиление две третьих); 0.125 for GAIN_ONE(однократное усиление); 0.0625 for GAIN_TWO(2-х кратное усиление) (подробности смотрите в datasheet ADS115) 1000 - преобразование mV в V iterations - количество измерений для усреднения */ return accum * divider * voltageSensitivity / 1000 / iterations; } //--ИЗМЕРЕНИЕ НАПРЯЖЕНИЯ И ТОКА И ВЫВОД РЕЗУЛЬТАТА НА ДИСПЛЕЙ--// void measure(void) { //++++ измерение напряжения ++++ // Канал 0, коэффициент входного делителя = 8, среднее из 10 итераций // добавление коррекции для компенсации смещения нулевой точки входа ADC (АЦП) PrimaryVoltage = measureSingleEnded(0, 8, 10) + voltageZeroCorrection; // сократить отрицательные значения PrimaryVoltage = (PrimaryVoltage > 0) ? PrimaryVoltage : 0; //++++ измерение тока ++++ // Канал 1 и 2, без делителя, в среднем 10 итераций float Chan1 = measureSingleEnded(1, 1, 10); float Chan2 = measureSingleEnded(2, 1, 10); // Рассчитать значения для коррекции и компенсации нулевой разницы двух модулей ACS712 PrimaryCurrent = (Chan1 - Chan2) / currentSensitivity + currentZeroCorrection; //++++ сброс тока, напряжения и ёмкости в ноль перед началом измерений (при коротком нажатии кнопки) ++++ Voltage = PrimaryVoltage - AmendmentVoltage; //"считанный первичное напряжение" минус "поправка напряжения" (изначально "AmendmentVoltage" равен нулю и результат "Voltage" не изменится) Current = PrimaryCurrent - AmendmentCurrent; //"считанный первичный ток" минус "поправка тока" (изначально "AmendmentCurrent" равен нулю и результат "Current" не изменится) if (digitalRead (RESET_BUTTON) == !HIGH) { //читаем состояние кнопки (стоит инверсия, т.к. включён "INPUT_PULLUP"), если она нажата, то... AmendmentVoltage = PrimaryVoltage; //первичное напряжение присваиваем переменной "поправка напряжения", и так же... AmendmentCurrent = PrimaryCurrent; //первичный ток присваиваем переменной "поправка тока" Capacity = 0.0; //и ещё обнуляем показания залитой ёмкости через Ваттметр (если это Вам ненужно по какой либо причине, то просто закомментируйте эту строку и ёмкость обнулятся не будет) } //++++ вычисление мощности ++++ Watt = Voltage * Current; // Расчёт Мощности нагрузки в Реальном времени. //++++ вычисление залитой ёмкости ++++ Capacity += (Current * (millis() - prevMillis) / 3600000); //расчёт залитой ёмкости в Ампер-Часах (Ач) через Ваттметр prevMillis = millis(); #ifdef NOISE_SUPPRESSOR //++++ обрабатывать значения напряжения менее 5мВ как ноль для подавления шума вблизи нуля ++++ if (Voltage < 0.005) { Voltage = 0; } //++++ обрабатывать значения тока менее 15мА как ноль для подавления шума вблизи нуля ++++ if (abs(Current) < 0.015) { Current = 0; } //++++ обрабатывать значения мощности менее 15мВт как ноль для подавления шума вблизи нуля ++++ if (abs(Watt) < 0.015) { Watt = 0; } #endif //++++ Вывод НАПРЯЖЕНИЯ на Дисплей ++++ lcd.setCursor(2, 0); //Устанавливаем курсор в позицию (на 2 колонку 1-й строки) lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения напряжения до 10V и более и дальнейшего его снижения менее 10V)) if (Voltage >= 10.0) { //Если напряжение с переменной "Voltage" больше или равно 10, то... lcd.setCursor(2, 0); //... устанавливаем курсор на (2 колонку 1-й строки) } else { //Иначе... lcd.setCursor(3, 0); //Переводим курсор на (3 колонка 1-й строки) } lcd.print(Voltage, 2); //выводим на дисплей значение переменной "Voltage" с 2-ми знаками после запятой. //++++ Вывод ТОКА на Дисплей ++++ lcd.setCursor(2, 1); //Устанавливаем курсор в позицию (на 2 колонку 2-й строки) lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения тока до 10A и более и дальнейшего его снижения менее 10А) if (Current < 0) { //Если ток с переменной "Current" меньше нуля, то... lcd.setCursor(2, 1); //... устанавливаем курсор на (2 колонку 2-й строки) } else { //Иначе... lcd.setCursor(3, 1); //Переводим курсор на (3 колонка 2-й строки) } lcd.print(Current, 2); //выводим на дисплей значение переменной "Current" с 2-ми знаками после запятой. //++++ Вывод МОЩНОСТИ на Дисплей ++++ lcd.setCursor(10, 0); //Устанавливаем курсор в позицию (на 10 колонку 1-й строки) lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения мощности до 10W и более и дальнейшего его снижения менее 10W)) if (Watt >= 10.0) { //Если мощность с переменной "Watt" больше или равно 10W, то... lcd.setCursor(10, 0); //... устанавливаем курсор на (10 колонку 1-й строки) } else { //Иначе... lcd.setCursor(11, 0); //Переводим курсор на (11 колонку 1-й строки) } lcd.print(Watt, 2); //выводим на дисплей значение переменной "Watt" с 2-ми знаками после запятой. //++++ Вывод залитой ЁМКОСТИ через Ваттметр на Дисплей ++++ lcd.setCursor(11, 1); //Переводим курсор на (11 колонку 2-й строки) lcd.print(Capacity, 3); //выводим на дисплей значение переменной "Capacity" с 3-ми знаками после запятой. } //--ОТОБРАЖЕНИЕ СТАТИЧЕСКИХ СИМВОЛОВ--// void setDisplayMode() { lcd.clear(); //Очистить дисплей lcd.setCursor(0, 0); //Переместить курсор на 1 строку и 1 символ lcd.print("U:"); //Показать букву "U:" (Напряжение) lcd.setCursor(0, 1); //Переместить курсор на 2 строку и 1 символ lcd.print("I:"); //Показать букву "I:" (Ток) lcd.setCursor(8, 0); //Переместить курсор на 1 строку и 8 символ lcd.print("W:"); //Показать букву "W:" (Мощность) lcd.setCursor(8, 1); //Переместить курсор на 1 строку и 8 символ lcd.print("Ah:"); //Показать букву "Ah:" (Ёмкость) lcd.noBlink(); //Отключить мигание курсора } //******************************* // ИНИЦИАЛИЗАЦИЯ //******************************* void setup(void) { //Назначаем нашу кнопку как выход с подтягивающим резистором от Arduino pinMode(RESET_BUTTON, INPUT_PULLUP); // инициализировать модуль ADC (АЦП) ads.begin(); ads.setGain(GAIN_ONE); //4.096V полная величина Спектра // инициализировать LCD Дисплей lcd.begin(); lcd.backlight(); // выводим надпись на Дисплей перед загрузкой кода lcd.setCursor(0, 0); lcd.print(" Ваттметр Otto"); lcd.setCursor(5, 1); lcd.print("4 в 1"); delay(2500); // подготовить экран setDisplayMode(); } //******************************** // ОСНОВНОЙ ЦИКЛ ПРОГРАММЫ //******************************** void loop(void) { if (AntiDelayLoop.Now()) { // Обновление данных на дисплее раз в 150 миллисекунд без задержки. measure(); //Измеряет напряжение,ток, мощность и ёмкость в Ah и отображает результаты на дисплее. } }Пару фоток готового Устройства в коробочке от Мыльницы.
Начинка:
Готовое устройство:
Проверка заряда по нажатию кнопки сделал на компораторе LM339N и 4-х светодиодах (просто заранее готовый валялся уже), но можно сделать это через свободные аналоговые порты Ардуинки или свободный pin ADS1115.
блоки просто лежат в коробочке и никак не закреплены?
Нет конечно. Всё достаточно крепко на термоклей закреплено, а провода "мёртво" большим количеством залиты супер клеем во много слоёв.ничего не болтается, даже если сильно трясти (чего не будет происходить) :)
Просто достаточно аккуратно делать старался, хоть и видно в дальнейшем не будет)
у каждого свои понятия о аккуратности :)
Я стараюсь все блоки крепить на плату, а уже плату - в корпус. И выглядит прилично, и любую деталь легко заменить, не отдирая сопли клея
даже когда это просто макетка для тестов (кликабельно, сорри за качество - фотографировалось на телефон при низком освещении)
b707, главное - такой открытый SPI не параллелить с чем-то ещё. А то я как-то сдурил - решил устройство с Ethernet шить через SPI и вывесил контакты. Так девайс меня потом с ума свёл - когда хотел, тогда сетевой модуль и "терял".
b707, главное - такой открытый SPI не параллелить с чем-то ещё. А то я как-то сдурил - решил устройство с Ethernet шить через SPI и вывесил контакты. Так девайс меня потом с ума свёл - когда хотел, тогда сетевой модуль и "терял".
в этом случае можно предложить соединять Ethernet разъемом на эти самые штырьки :) - тогда при прошивке волей-неволей будешь отключать модуль и проблем не будет
Otto
Здравствуйте!
В скрипте Вами предоставленном на 88 строке выдает ошибку: exit status 1
Спасибо!
Otto
Здравствуйте!
В скрипте Вами предоставленном на 88 строке выдает ошибку: exit status 1
Спасибо!
Очевидно, в 88-й строке и исправлять.
Otto
Здравствуйте!
В скрипте Вами предоставленном на 88 строке выдает ошибку: exit status 1
Спасибо!
в скрипте Отто такой ошибки в 88 строке нет.
Очевидно, что вы неправильно скопировали код. Скорее всего , взяли код не как текст. а как HTML. вот у вас и лезет при компиляции всякая ерунда.
Здравствуйте!
Спасибо за ответ, скрипт копировал через Блокнот как текст. Скриншот ошибки прилагаю. Я пока не силен в скриптах, по сему прошу помощи.
Вы всерьез думаете, что на этом скриншоте что-то можно разобрать?
Описание ошибки нужно вставлять текстом и полностью. А перед этим обязательно по правилам форума вставить скетч, который ведет к этой ошибке. Не ссылку, а именно сам скетч.
Вот код. Он взят из сообщения Otto #4.
[code] 001 /****************************************** 002 Ваттметр с ЖК-дисплеем 003 v.3.0 Июнь 2019 004 ------------------------------------------- 005 Железо: 006 - Arduino Pro mini 007 - 16-bit ADC модуль на основе ADS1115 008 - LCD дисплей 1602 по шине I2C 009 - 2 датчика тока ACS712 на 20 Ампер 010 ------------------------------------------- 011 // Возможности (КРАТКОВРЕМЕННЫЕ, максимальные): 012 - Измерение Напряжения до 32V 013 - Измерение Тока до 20A 014 - Измерение Мощности до 640W 015 - Измерение залитой ёмкости через Ваттметр до 9999Ah 016 ******************************************/ 017 // 018 //Подключаем библиотеки 019 #include <Wire.h> 020 #include <Adafruit_ADS1015.h> 021 //#include <LiquidCrystal_I2C.h> //Библиотека Дисплея (закомментировал, так как использую аналогичную библиотеку с Русскими буквами) 022 #include <LCD_1602_RUS.h> //Библиотека Дисплея "LiquidCrystal_I2C", только с Русскими буквами 023 #include <SPI.h> 024 #include <SmartDelay.h> // Библиотека для ожидания без Delay() 025 // 026 // 027 //--НАСТРОЙКА КОМПЕНСАЦИЙ И ЧУВСТВИТЕЛЬНОСТИ--// 028 #define voltageZeroCorrection -0.055 //Компенсирует смещение нулевой точки в канале ADC0 029 #define currentZeroCorrection -0.0697 //Компенсирует разницу нулевых точек между двумя модулями ACS712 030 // 031 #define voltageSensitivity 0.12486 //Чувствительность ADS1115 = 0.125 для GAIN_ONE (подробности смотрите в datasheet ADS1115) 032 #define currentSensitivity 0.20375 //Чувствительность двух ACS712 (датчики подключены в противоположном направлении) 033 // 034 #define NOISE_SUPPRESSOR 1 //Шумоподавление (Прокомментируйте эту строку, чтобы отключить шумоподавитель) 035 #define RESET_BUTTON 8 //Кнопка для обнуления напряжения, тока и ёмкости (подключена на pin 8) 036 // 037 //--ЗАДАЁМ I2C АДРЕСА МОДУЛЕЙ--// 038 #define ADC_I2C_ADDR 0x48 //Аналого-цифровой преобразователь адреса I2C = 72 039 #define DSP_I2C_ADDR 0x27 //Дисплей LCD1602 I2C адрес = 63 040 // 041 // 042 //--ОБЪЕКТЫ--// 043 Adafruit_ADS1115 ads(ADC_I2C_ADDR); //ADC (АЦП) объект 044 //LiquidCrystal_I2C lcd(DSP_I2C_ADDR, 16, 2); //Дисплейный объект: 16 символов подряд, 2 строки 045 LCD_1602_RUS lcd(DSP_I2C_ADDR, 16, 2); //Тоже самое, что и "LiquidCrystal_I2C", только с выводом Русских букв 046 SmartDelay AntiDelayLoop(150000UL); //Ожидание в Микросекундах. Задаём интервал в 150 миллисекунд. Простое ожидание без задержки для цикла "loop" 047 // 048 // 049 //--ПЕРЕМЕННЫЕ ДЛЯ ИЗМЕРЕНИЯ ТОКА И НАПРЯЖЕНИЯ--// 050 float PrimaryVoltage = 0.0; //Первичный (до обнуления) результат измерения напряжения 051 float AmendmentVoltage = 0.0; //Поправочная переменная для вычета погрешности из напряжения перед измерением (для обнуления) 052 float Voltage = 0.0; //Результат измерения напряжения 053 // 054 float PrimaryCurrent = 0.0; //Первичный (до обнуления) результат измерения тока 055 float AmendmentCurrent = 0.0; //Поправочная переменная для вычета погрешности из тока перед измерением (для обнуления) 056 float Current = 0.0; //Конечный (после обнуления) результат измерения тока 057 // 058 float Watt = 0.0; //Результат измерения мощности 059 float Capacity = 0.0; //Начальная ёмкость в Ah 060 unsigned long prevMillis; //Переменная таймера используются для расчёта измерителя ёмкости 061 // 062 // 063 //------ Возвращать результаты как среднее из <итераций> образцов напряжения ------ 064 // 1-й аргумент - номер канала ADC (АЦП) (0 - канал напряжения, 1 и 2 - токовые каналы) 065 // 2-й аргумент - коэффициент деления входного напряжения (5 - канал напряжения, 1 - канал тока) 066 // 3-й аргумент - количество итераций (повторите измерение для <итераций> раз и верните среднее значение) 067 float measureSingleEnded(uint8_t channel, uint8_t divider, uint8_t iterations) { 068 int16_t adc; //16-разрядное целое число, полученное из ADC (АЦП) 069 long accum = 0; //сумма <итераций> измерений из <итераций> измерения 070 for (int i = 0; i < iterations; i++) { 071 adc = ads.readADC_SingleEnded(channel); //Присваиваем переменной adc значения из channel 072 accum += adc; 073 // } 074 /* PrimaryVoltage = accum * divider * voltageSensitivity / 1000 / iterations (Так происходит расчёт измерения Напряжения) 075 где: 076 accum - сумма измерений накопленная а accum 077 divider - коэффициент деления входного напряжения 078 voltageSensitivity - 0.1875 for GAIN_TWOTHIRDS(усиление две третьих); 0.125 for GAIN_ONE(однократное усиление); 079 0.0625 for GAIN_TWO(2-х кратное усиление) (подробности смотрите в datasheet ADS115) 080 1000 - преобразование mV в V 081 iterations - количество измерений для усреднения 082 */ 083 return accum * divider * voltageSensitivity / 1000 / iterations; 084 } 085 // 086 // 087 //--ИЗМЕРЕНИЕ НАПРЯЖЕНИЯ И ТОКА И ВЫВОД РЕЗУЛЬТАТА НА ДИСПЛЕЙ--// 088 void measure(void) { 089 //++++ измерение напряжения ++++ 090 // Канал 0, коэффициент входного делителя = 8, среднее из 10 итераций 091 // добавление коррекции для компенсации смещения нулевой точки входа ADC (АЦП) 092 PrimaryVoltage = measureSingleEnded(0, 8, 10) + voltageZeroCorrection; 093 // сократить отрицательные значения 094 PrimaryVoltage = (PrimaryVoltage > 0) ? PrimaryVoltage : 0; 095 // 096 // 097 //++++ измерение тока ++++ 098 // Канал 1 и 2, без делителя, в среднем 10 итераций 099 float Chan1 = measureSingleEnded(1, 1, 10); 100 float Chan2 = measureSingleEnded(2, 1, 10); 101 // Рассчитать значения для коррекции и компенсации нулевой разницы двух модулей ACS712 102 PrimaryCurrent = (Chan1 - Chan2) / currentSensitivity + currentZeroCorrection; 103 // 104 // 105 //++++ сброс тока, напряжения и ёмкости в ноль перед началом измерений (при коротком нажатии кнопки) ++++ 106 Voltage = PrimaryVoltage - AmendmentVoltage; //"считанный первичное напряжение" минус "поправка напряжения" (изначально "AmendmentVoltage" равен нулю и результат "Voltage" не изменится) 107 Current = PrimaryCurrent - AmendmentCurrent; //"считанный первичный ток" минус "поправка тока" (изначально "AmendmentCurrent" равен нулю и результат "Current" не изменится) 108 // 109 if (digitalRead (RESET_BUTTON) == !HIGH) { //читаем состояние кнопки (стоит инверсия, т.к. включён "INPUT_PULLUP"), если она нажата, то... 110 AmendmentVoltage = PrimaryVoltage; //первичное напряжение присваиваем переменной "поправка напряжения", и так же... 111 AmendmentCurrent = PrimaryCurrent; //первичный ток присваиваем переменной "поправка тока" 112 Capacity = 0.0; //и ещё обнуляем показания залитой ёмкости через Ваттметр (если это Вам ненужно по какой либо причине, то просто закомментируйте эту строку и ёмкость обнулятся не будет) 113 } 114 // 115 // 116 //++++ вычисление мощности ++++ 117 Watt = Voltage * Current; // Расчёт Мощности нагрузки в Реальном времени. 118 // 119 // 120 //++++ вычисление залитой ёмкости ++++ 121 Capacity += (Current * (millis() - prevMillis) / 3600000); //расчёт залитой ёмкости в Ампер-Часах (Ач) через Ваттметр 122 prevMillis = millis(); 123 // 124 // 125 // 126 #ifdef NOISE_SUPPRESSOR 127 //++++ обрабатывать значения напряжения менее 5мВ как ноль для подавления шума вблизи нуля ++++ 128 if (Voltage < 0.005) { 129 Voltage = 0; 130 } 131 // 132 //++++ обрабатывать значения тока менее 15мА как ноль для подавления шума вблизи нуля ++++ 133 if (abs(Current) < 0.015) { 134 Current = 0; 135 } 136 // 137 //++++ обрабатывать значения мощности менее 15мВт как ноль для подавления шума вблизи нуля ++++ 138 if (abs(Watt) < 0.015) { 139 Watt = 0; 140 } 141 #endif 142 // 143 // 144 //++++ Вывод НАПРЯЖЕНИЯ на Дисплей ++++ 145 lcd.setCursor(2, 0); //Устанавливаем курсор в позицию (на 2 колонку 1-й строки) 146 lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения напряжения до 10V и более и дальнейшего его снижения менее 10V)) 147 if (Voltage >= 10.0) { //Если напряжение с переменной "Voltage" больше или равно 10, то... 148 lcd.setCursor(2, 0); //... устанавливаем курсор на (2 колонку 1-й строки) 149 } else { //Иначе... 150 lcd.setCursor(3, 0); //Переводим курсор на (3 колонка 1-й строки) 151 } 152 lcd.print(Voltage, 2); //выводим на дисплей значение переменной "Voltage" с 2-ми знаками после запятой. 153 // 154 // 155 //++++ Вывод ТОКА на Дисплей ++++ 156 lcd.setCursor(2, 1); //Устанавливаем курсор в позицию (на 2 колонку 2-й строки) 157 lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения тока до 10A и более и дальнейшего его снижения менее 10А) 158 if (Current < 0) { //Если ток с переменной "Current" меньше нуля, то... 159 lcd.setCursor(2, 1); //... устанавливаем курсор на (2 колонку 2-й строки) 160 } else { //Иначе... 161 lcd.setCursor(3, 1); //Переводим курсор на (3 колонка 2-й строки) 162 } 163 lcd.print(Current, 2); //выводим на дисплей значение переменной "Current" с 2-ми знаками после запятой. 164 // 165 // 166 //++++ Вывод МОЩНОСТИ на Дисплей ++++ 167 lcd.setCursor(10, 0); //Устанавливаем курсор в позицию (на 10 колонку 1-й строки) 168 lcd.print(" "); //выводим на дисплей пробелы (что бы очищалась колонка после увеличения мощности до 10W и более и дальнейшего его снижения менее 10W)) 169 if (Watt >= 10.0) { //Если мощность с переменной "Watt" больше или равно 10W, то... 170 lcd.setCursor(10, 0); //... устанавливаем курсор на (10 колонку 1-й строки) 171 } else { //Иначе... 172 lcd.setCursor(11, 0); //Переводим курсор на (11 колонку 1-й строки) 173 } 174 lcd.print(Watt, 2); //выводим на дисплей значение переменной "Watt" с 2-ми знаками после запятой. 175 // 176 // 177 //++++ Вывод залитой ЁМКОСТИ через Ваттметр на Дисплей ++++ 178 lcd.setCursor(11, 1); //Переводим курсор на (11 колонку 2-й строки) 179 lcd.print(Capacity, 3); //выводим на дисплей значение переменной "Capacity" с 3-ми знаками после запятой. 180 } 181 // 182 // 183 //--ОТОБРАЖЕНИЕ СТАТИЧЕСКИХ СИМВОЛОВ--// 184 void setDisplayMode() { 185 lcd.clear(); //Очистить дисплей 186 lcd.setCursor(0, 0); //Переместить курсор на 1 строку и 1 символ 187 lcd.print("U:"); //Показать букву "U:" (Напряжение) 188 // 189 lcd.setCursor(0, 1); //Переместить курсор на 2 строку и 1 символ 190 lcd.print("I:"); //Показать букву "I:" (Ток) 191 // 192 lcd.setCursor(8, 0); //Переместить курсор на 1 строку и 8 символ 193 lcd.print("W:"); //Показать букву "W:" (Мощность) 194 // 195 lcd.setCursor(8, 1); //Переместить курсор на 1 строку и 8 символ 196 lcd.print("Ah:"); //Показать букву "Ah:" (Ёмкость) 197 lcd.noBlink(); //Отключить мигание курсора 198 } 199 // 200 // 201 //******************************* 202 // ИНИЦИАЛИЗАЦИЯ 203 //******************************* 204 void setup(void) { 205 //Назначаем нашу кнопку как выход с подтягивающим резистором от Arduino 206 pinMode(RESET_BUTTON, INPUT_PULLUP); 207 // 208 // инициализировать модуль ADC (АЦП) 209 ads.begin(); 210 ads.setGain(GAIN_ONE); //4.096V полная величина Спектра 211 // 212 // 213 // инициализировать LCD Дисплей 214 lcd.begin(); 215 lcd.backlight(); 216 // 217 // выводим надпись на Дисплей перед загрузкой кода 218 lcd.setCursor(0, 0); 219 lcd.print(" Ваттметр Otto"); 220 lcd.setCursor(5, 1); 221 lcd.print("4 в 1"); 222 delay(2500); 223 // 224 // 225 // подготовить экран 226 setDisplayMode(); 227 } 228 // 229 //******************************** 230 // ОСНОВНОЙ ЦИКЛ ПРОГРАММЫ 231 //******************************** 232 void loop(void) 233 { 234 if (AntiDelayLoop.Now()) { // Обновление данных на дисплее раз в 150 миллисекунд без задержки. 235 measure(); //Измеряет напряжение,ток, мощность и ёмкость в Ah и отображает результаты на дисплее. 236 } 237 // 238 } [/code]Привет. Вот ссылка на проект. Сразу не подумал выложить дополнительные библиотеки. Попробуйте с ними. К тому же по ссылке можете скачать *.ino файлик.
https://sysfile.sytes.net/index.php/s/GBLyut4v3TJKgN0
Здравствуйте!
Спасибо за ответ, скрипт копировал через Блокнот как текст. Скриншот ошибки прилагаю. Я пока не силен в скриптах, по сему прошу помощи.
У вас же скопирован код криво.... с порядковыми номерами, ещё бы он не скомпилировался :)
Я пока не силен в скриптах, по сему прошу помощи.
насколько надо быть "не силеным" в скриптах, чтобы скопировать код с номерами строк и пытаться его компилировать? - голову включайте хоть чуть-чуть!
Нафига вообще браться за ардуину, если в программировании ноль?. Какой смысл тупо повторять чужое, как попугай?
Вот код. Он взят из сообщения Otto #4.
Ну, собственно, я не ошибся, попросив Вас выложить код, который Вы пытаетесь компилировать.
Вы сами то разницу между тем, что было в сообщении Отто, и тем, что выложили Вы, видите?
Когда наводите курсор на область кода, в верхнем правом углу появляется небольшая панель инструментов. Надо нажать на самую левую кнопку "<>", откроется дополнительное окно. Вот из него и надо копировать.
Спасибо! Обложили ху...ми, обос...ли. Как всегда от профессионалов..., получил по полной. Чем мне заниматься и чем не заниматься это не Ваше дело.
Отто СПАСИБО!!! Вечером попробую с Вашими библиотеками и кодами.
Здравствуйте!
Спасибо!!! Вечером попробую.
Чем мне заниматься и чем не заниматься это не Ваше дело.
дело, конечно, ваше... но если не научитесь понимать написанное - все равно ни хера не выйдет
Отто еще раз огромное спасибо!!!
Я вот только немножко поправил скетч:
lcd begin заменил на lcd init.
При begin не проходила успешно компиляция.
Вечером прикручу к Ардуинке периферию и посмотрю результаты практической работы Вашего устройства.
Еще раз Вам СПАСИБО!!!
73! RX6KR Yuriy
Я вот только немножко поправил скетч:
lcd begin заменил на lcd init.
значит не ту библиотеку для экрана взяли
Если нужно, можете скачать библиотеки по ссылке