// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
#include "EmonLib.h" // Include Emon Library
EnergyMonitor emon1; // Create an instance
// Константы для расчета заряда LiFePO4 4S
// Таблица напряжений и процентов заряда для LiFePO4 4S
const int tableSize = 5;
const float voltageTable[tableSize] = {11.2, 12.0, 12.8, 13.0, 13.4}; // Напряжение (В)
const float percentageTable[tableSize] = {0, 10, 50, 80, 100}; // Процент заряда (%)
// Константы для 220в
int i = 0;
float tenvals = 0.0;
float minval = 1000;
float maxval = 0.0;
int bms = 0; // Переменная состояния BMS
int sbms = 0; // Переменная состояния BMS управления
int zar = 0; // Переменная состояния зарядки
int inv = 0; // Переменная состояния инвертора
int vent = 0; // Переменная состояния вентилятора
int avto = 0; // Переменная состояния автоматического режима
int vvod = 0; // Переменная состояния ввода
const int outputPin = 9; // номер пина, к которому подключен выход
const int analogPin = A0; // номер аналогового пина, к которому подключен переменный резистор 200 кОм
const int thermistorPin = A0; // Аналоговый пин для терморезистора
const float SERIES_RESISTOR = 200000.0; // Номинальное сопротивление резистора (200 кОм)
const float NOMINAL_RESISTANCE = 200000.0; // Номинальное сопротивление термистора при 25°C (200 кОм)
const float NOMINAL_TEMPERATURE = 25.0; // Температура при номинальном сопротивлении (25°C)
const float BETA_COEFFICIENT = 3950.0; // Бета-коэффициент термистора
const float ADC_MAX = 1023.0; // Максимальное значение АЦП Arduino
// Определяем аналоговые входы
const int analogPin1 = A1;
const int analogPin2 = A2;
const int analogPin3 = A3;
const int analogPin4 = A4;
float analogValue1, analogValue2, analogValue3, analogValue4;
float voltage1, voltage2, voltage3, voltage4;
float totalVoltage; // Суммарное напряжение сборки
float batteryPercentage; // Процент заряда
float calibration = 750; // Начальное значение калибровочного коэффициента
//float calibration1 = 1100;
String inputData = ""; // Переменная для хранения введенных данных
String correctPassword = "3432"; // Пароль
String inputPassword = ""; // Введенный
//1+5 2желтый -А5 3синий - 11 4черный - 8 5GND
void setup() {
emon1.voltage(5, calibration, 1.7); // Voltage: input pin, calibration, phase_shift
// emon1.current(1, 111.1); // Current: input pin, calibration.
pinMode(outputPin, OUTPUT);
TCCR1B = (TCCR1B & 0xF8) | 1; //Меняем частоту шим. 31кГц
pinMode(7, OUTPUT); // реле 220в.
pinMode(3, INPUT_PULLUP); //сигнал на включение станции 6
pinMode(4, OUTPUT); //вкл.откл.бмс
digitalWrite(9, 0); // шим управление вентилятором
pinMode(10, OUTPUT); //реле вкл. инвертора
pinMode(11, OUTPUT); // реле откл. основного ввода в квартиру
pinMode(A5, INPUT); // Датчик напряжения 220В. ZMPT101B
digitalWrite(7, 1);
digitalWrite(10, 1);
digitalWrite(11, 1);
zar = 1;
bms = 1;
sbms = 1;
avto = 1;
vvod = 1;
{
digitalWrite(4, 0);
delay(1000);
digitalWrite(4, 1);
}
delay(2000);
Serial.begin(115200);
while (!Serial);
// Serial.println("Вкл. БМС вкл. зарядки");
}
// Функция для расчета процента заряда через интерполяцию
float getBatteryPercentage(float voltage) {
if (voltage <= voltageTable[0]) return percentageTable[0]; // Меньше минимального
if (voltage >= voltageTable[tableSize - 1]) return percentageTable[tableSize - 1]; // Больше максимального
// Найти диапазон напряжения
for (int i = 0; i < tableSize - 1; i++) {
if (voltage >= voltageTable[i] && voltage <= voltageTable[i + 1]) {
// Линейная интерполяция
return percentageTable[i] +
(percentageTable[i + 1] - percentageTable[i]) *
(voltage - voltageTable[i]) / (voltageTable[i + 1] - voltageTable[i]);
}
}
return 0; // Защита от ошибок
}
void loop() {
//Измерение темпиратуры
int analogValue = analogRead(thermistorPin); // Чтение значения с АЦП
float voltage = (analogValue / ADC_MAX) * 5.0; // Преобразование в напряжение
// Вычисление сопротивления термистора
float thermistorResistance = SERIES_RESISTOR / ((5.0 / voltage) - 1.0);
// Применение уравнения Штейнгарта-Харта
float steinhart;
steinhart = thermistorResistance / NOMINAL_RESISTANCE; // R / R25
steinhart = log(steinhart); // ln(R / R25)
steinhart /= BETA_COEFFICIENT; // 1/B * ln(R / R25)
steinhart += 1.0 / (NOMINAL_TEMPERATURE + 273.15); // + (1 / T25)
steinhart = 1.0 / steinhart; // Инвертируем
steinhart -= 273.15; // Перевод в Цельсии
//Измерение напр. 220
emon1.calcVI(20,2000); // Calculate all. No.of half wavelengths (crossings), time-out
float Vrms = (emon1.Vrms);
if (Vrms < 70) { Vrms = 0.0; }
tenvals += Vrms;
if (minval > Vrms) { minval = Vrms; }
if (maxval < Vrms) { maxval = Vrms; }
i++;
if (i == 10)
{
i = 0;
tenvals = 0.0;
minval = 1000.0;
maxval = 0.0;
}
if ( avto == 0) //заряд для режима хранения 3,2-3,3в. авто выключен.
{
// Включение БМС, включение реле зарядки.
//2.8-572/ /3-614/ /3.1-635/ /3.2-655/ /3.3-675/ /3.4-696/ /3.42-700/ /3.45-705/ /3.47-710/ /3.5-716/ /3.52-720/ /3.6-737/ /3.65-747// 696 было
if(((analogRead(A4) <= 655) || (analogRead(A3) <= 655) ||(analogRead(A2) <= 655)||(analogRead(A1) <= 655)) && (zar == 0))
{
zar = 1;
digitalWrite(7, 1);
if (bms == 0)
{
bms = 1;
sbms = 1;
digitalWrite(4, 0);
delay(1000);
digitalWrite(4, 1);
//Serial.println("Вкл. БМС, вкл. реле зарядки");
}
}
// Выключение реле зарядки.
if(((analogRead(A4) >= 675) && (analogRead(A3) >= 675) && (analogRead(A2) >= 675) && (analogRead(A1) >= 675)) && (zar == 1))
{
digitalWrite(7, 0);
zar = 0;
// Serial.println("Выкл. реле зарядки");
if ((digitalRead(3) == HIGH) && (bms == 1) && (inv == 0))
{
digitalWrite(4, 0);
delay(3000);
digitalWrite(4, 1);
delay(5);
sbms = 0;
bms = 0;
// Serial.println("Выкл. БМС, при выкл. зарядки, если не вкл. инвертор");
}
}
}
if ( avto == 1) //заряд для режима работа 3,4-3,47в. авто включен.
{
// Включение БМС, включение реле зарядки.
//2.8-572/ /3-614/ /3.1-635/ /3.2-655/ /3.3-675/ /3.4-696/ /3.42-700/ /3.45-705/ /3.47-710/ /3.5-716/ /3.52-720/ /3.6-737/ /3.65-747// 696 было
if(((analogRead(A4) <= 696) || (analogRead(A3) <= 696) ||(analogRead(A2) <= 696)||(analogRead(A1) <= 696)) && (zar == 0))
{
zar = 1;
digitalWrite(7, 1);
if (bms == 0)
{
bms = 1;
sbms = 1;
digitalWrite(4, 0);
delay(1000);
digitalWrite(4, 1);
//Serial.println("Вкл. БМС, вкл. реле зарядки");
}
}
// Выключение реле зарядки.
if(((analogRead(A4) >= 710) && (analogRead(A3) >= 710) && (analogRead(A2) >= 710) && (analogRead(A1) >= 710)) && (zar == 1))
{
digitalWrite(7, 0);
zar = 0;
avto = 0;
//Serial.println("Выкл. реле зарядки");
if ((digitalRead(3) == HIGH) && (bms == 1) && (inv == 0))
{
digitalWrite(4, 0);
delay(3000);
digitalWrite(4, 1);
delay(5);
sbms = 0;
bms = 0;
// Serial.println("Выкл. БМС, при выкл. зарядки, если не вкл. инвертор");
}
}
}
// Дистанционное Включение БМС и реле Инвертора. (РАБОТА)
if((digitalRead(3) == 0) && (inv == 0))
{
inv = 1;
digitalWrite(10, 0);
delay(20);
// Serial.println("Вкл. инвертора");
}
if ((inv == 1) && (bms == 0))
{
sbms = 1;
bms = 1;
digitalWrite(4, 0);
delay(1000);
digitalWrite(4, 1);
// Serial.println(" Вкл. БМС (РАБОТА)");
}
// Дистанционное Выкл. реле Инвертора.
if((digitalRead(3) == 1) && (inv == 1))
{
inv = 0;
digitalWrite(10, 1);
// Serial.println(" Выкл. Инвертора.");
}
// Дистанционное Выкл. БМС при условии что не включена зарядка.
if((digitalRead(3) == 1) && (zar == 0) && (bms == 1))
{
sbms = 0;
bms = 0;
digitalWrite(4, 0);
delay(3000);
digitalWrite(4, 1);
// Serial.println(" Выкл. БМС при условии что не вкл. зарядка.");
}
if(digitalRead(3) == 0)
avto = 1;
// Управление вентилятором
if ((inv == 1) || (zar == 1))
{
vent = 1;
int sensorValue = analogRead(analogPin);
int outputValue = map(sensorValue, 270, 350, 255, 0);
sensorValue = constrain(sensorValue, 270, 350);
outputValue = constrain(outputValue, 0, 255);
analogWrite(outputPin, outputValue);
Serial.println();
Serial.print("Темп. датчик: ");
Serial.print(sensorValue);
Serial.print(" -> Скорость вент.: ");
Serial.println(outputValue);
delay(10);
}
if ((inv == 0) && (zar == 0) && (vent == 1))
{
vent = 0;
digitalWrite(9, 0);
//Serial.println("Вентилятор отключён");
}
// Считываем значения с аналоговых входов
analogValue1 = analogRead(analogPin1);
analogValue2 = analogRead(analogPin2);
analogValue3 = analogRead(analogPin3);
analogValue4 = analogRead(analogPin4);
// Преобразуем значения в напряжение
voltage1 = analogValue1 * (5.0615 / 1023.0);
voltage2 = analogValue2 * (5.0475 / 1023.0);
voltage3 = analogValue3 * (5.0425 / 1023.0);
voltage4 = analogValue4 * (5.0425 / 1023.0);
// Преобразуем значения в напряжение (учитывая коэффициенты делителей)
// voltage1 = analogValue1 * (5.0 / 1023.0) * 3; // Коэффициент делителя 1:3
// voltage2 = analogValue2 * (5.0 / 1023.0) * 3;
//voltage3 = analogValue3 * (5.0 / 1023.0) * 3;
// voltage4 = analogValue4 * (5.0 / 1023.0) * 3;
// Суммируем напряжения
totalVoltage = voltage1 + voltage2 + voltage3 + voltage4;
// Рассчитываем процент заряда сборки
batteryPercentage = getBatteryPercentage(totalVoltage);
// Выводим результаты в последовательный монитор
Serial.println();
Serial.print(" BAT/A1: ");
Serial.print(voltage1, 3);
Serial.println(" В");
Serial.print(" BAT/A2: ");
Serial.print(voltage2, 3);
Serial.println(" В");
Serial.print(" BAT/A3: ");
Serial.print(voltage3, 3);
Serial.println(" В");
Serial.print(" BAT/A4: ");
Serial.print(voltage4, 3);
Serial.println(" В");
Serial.println();
Serial.print("Напряжение АКБ : ");
Serial.print(totalVoltage, 3);
Serial.println(" В");
Serial.print("Заряд АКБ : ");
Serial.print(batteryPercentage, 1);
Serial.println(" %");
Serial.print("Напряжение в доме : ");
Serial.print(Vrms);
Serial.println(" В");
// Вывод температуры в последовательный монитор
Serial.print("Темп. инвертора : ");
Serial.print(steinhart);
Serial.println(" °C");
Serial.println();
int invState = digitalRead(10);
String invStateText = (invState == 0) ? "Вкл" : "Выкл";
Serial.print("D10(Инвертор) : ");
Serial.println(invStateText);
int zarState = digitalRead(7);
String zarStateText = (zarState == 1) ? "Вкл" : "Выкл";
Serial.print("D7 (Зарядка) : ");
Serial.println(zarStateText);
String sbmsStateText = (sbms == 1) ? "Вкл" : "Выкл";
Serial.print("D4 (БМС) : ");
Serial.println(sbmsStateText);
int sigState = digitalRead(3);
String sigStateText = (sigState == 0) ? "Сигнал есть/Ввод-ИНВЕРТОР" : "Сигнала нет/Ввод-ГОРОД";
Serial.print("D3 (Сигнал/Ввод): ");
Serial.println(sigStateText);
String vvodStateText = (vvod == 1) ? "Город" : "Инв";
Serial.print("D11 (Команда) : ");
Serial.println(vvodStateText);
String avtoStateText = (avto == 1) ? "3,4-3,5В" : "3,2-3,3В";
Serial.print("АВТО РЕЖИМ : ");
Serial.println(avtoStateText);
delay(1000);
// Управление через монитор порта
if (Serial.available()) {
inputData = Serial.readStringUntil('\n');
inputData.trim(); // Удаляем пробелы в начале и конце строки
inputData.toLowerCase(); // Приводим строку к нижнему регистру
Serial.print("Получена команда: ");
Serial.println(inputData);
}
// Проверка и выполнение команд
// Проверка и выполнение команды "колибровка"
if (inputData.equals("калибровка")) {
if (checkPassword()) { // Проверка пароля
Serial.println("Введите новый коэффициент:");
while (!Serial.available()); // Ожидание ввода
String calibrationInput = Serial.readStringUntil('\n');
calibrationInput.trim();
float newCalibration = calibrationInput.toFloat(); // Преобразуем строку в число
if (newCalibration > 0) { // Проверяем, что число положительное
calibration = newCalibration;
emon1.voltage(5, calibration, 1.7); // Обновляем настройки
Serial.print("Новый коэффициент: ");
Serial.println(calibration);
}
//else {
// Serial.println("Ошибка: коэффициент должен быть положительным числом.");
// }
}
}
if (inputData.equals("включить инвертор")) {
if (checkPassword()) {
digitalWrite(10, LOW);
// Serial.println(" Вкл. инвертора");
}
}
else if (inputData.equals("выключить инвертор")) {
if (checkPassword()) {
digitalWrite(10, HIGH);
//Serial.println(" Выкл. инвертора");
}
}
else if (inputData.equals("включить зарядку")) {
if (checkPassword()) {
digitalWrite(7,HIGH );
//Serial.println("Включение зарядки");
}
}
else if (inputData.equals("выключить зарядку")) {
if (checkPassword()) {
digitalWrite(7,LOW);
//Serial.println("Выключение зарядки");
}
}
else if (inputData.equals("инвертор")) {
if (checkPassword()) {
digitalWrite(11, LOW);
//Serial.println("ввод инв");
vvod = 0;
}
}
else if (inputData.equals("город")) {
if (checkPassword()) {
digitalWrite(11, HIGH);
// Serial.println("ввод город");
vvod = 1;
}
}
else if (inputData.equals("включить бмс")) {
if (checkPassword()) {
sbms = 1;
digitalWrite(4, 0);
delay(1000);
digitalWrite(4, 1);
// Serial.println("Вкл. БМС (РАБОТА)");
}
}
else if (inputData.equals("выключить бмс")) {
if (checkPassword()) {
sbms = 0;
digitalWrite(4, 0);
delay(3000);
digitalWrite(4, 1);
delay(5);
// Serial.println("Выкл. БМС");
}
}
else if (inputData.equals("сброс")) {
if (checkPassword()) {
asm volatile("jmp 0x00");
}
}
else if (inputData.equals("авто 3,3")) {
if (checkPassword()) {
avto = 0;
Serial.println("авто 3,3");
}
}
else if (inputData.equals("авто 3,5")) {
if (checkPassword()) {
avto = 1;
Serial.println("авто 3,5");
}
}
else if (inputData.length() > 0) {
Serial.println("Неизв. команда!");
}
inputData = ""; // Сбрасываем переменную для следующей команды
}
// Функция проверки пароля
bool checkPassword() {
Serial.print("Введите пароль: ");
while (!Serial.available()); // Ожидаем, пока пользователь введет пароль
inputPassword = Serial.readStringUntil('\n');
inputPassword.trim();
if (inputPassword == correctPassword) {
Serial.println("Пароль верный.");
return true;
} else {
Serial.println("Неверный пароль!");
return false;
}
}