// Парсер BL0942
// Подходит для проектов на ардуино и ESP32 (без изменений в коде) с использованием данных умных розеток и выключателей с измерением мощности, собранных на BL0942 и CB2S
// (почти все розетки и выключатели нижнего ценового диапазона выпущенные в 2022-2024г)
// Mini Smart Switch, Ausess Power Monitor Switch, проходные розетки с кнопкой и т.д. Работоспособность розетки полностью сохраняется. Удобнее всего использовать Mini Smart Switch, в нем оптопара легко размещается под крышкой
// корпуса розетки, и корпус розетки с установленной оптопарой штатно закрывается.
// Парсер получает из данных обмена между BL0942 и CB2S напряжение сети, ток, мощность, потребленную энергию.
// Для гальванической развязки необходимо использовать оптопару (использовалась первая попавшаяся NEC PS2501).
// Светодиод оптопары подключается выходу TX BL0942 (входу RX CB2S, они соединены), другая нога светодиода на +3.3 розетки через резистор 470 Ом.
// К выводам оптотранзистора оптопары припаиваются два провода, тщательно изолируются и выводятся из корпуса розетки. У меня работало с длинной этих проводов 15 см до ключа, но желательно их делать как можно короче.
// Оптотранзистор управляет ключем (открытым коллектором), собранным на любом транзисторе. Выход с коллектора ключа подключаем к RX ардуино или ESP32. На время прошивки это подключение от ардуино нужно отключать.
//--------------------------------------------------------------------------------------------
const int BUFFER_SIZE = 23; // Число байт в пакете, получаемом от BL0942
char buf[BUFFER_SIZE]; // Создаем массив для хранения пакета побайтово
uint32_t raw_unscaled_voltage;
uint32_t raw_unscaled_tok;
uint32_t raw_unscaled_power;
uint32_t raw_unscaled_energy;
float newVoltage;
float newTok;
float newPower;
float newEnergy;
float goodVoltage = 0;
float goodTok = 0;
float goodPower = 0;
float goodEnergy = 0;
float BL0942_PREF = 646; //598 Подобрать офсет чтобы мощность в приложении на смартфоне и в мониторе порта совпадали
float BL0942_UREF = 16170; //15188 Подобрать офсет чтобы напряжение в приложении на смартфоне и в мониторе порта совпадали
float BL0942_IREF = 267417; //251210 Подобрать офсет чтобы ток в приложении на смартфоне и в мониторе порта совпадали
float BL0942_WREF = 3333; //11111 Подобрать офсет чтобы киловатт часы в приложении на смартфоне и в мониторе порта совпадали
void setup() {
Serial.begin(4800); // BL0942 как правило обменивается данными на скорости 4800, монитор порта в Arduino IDE во время отладки необходимо так жк переключить на эту скорость
Serial.setTimeout(100);
}
void loop() {
if (Serial.available() > 0) {
int rlen = Serial.readBytesUntil('\n', buf, BUFFER_SIZE);
// Расчет и проверка контрольной суммы пакета
unsigned char cs = 0x58;
unsigned char realCS = buf[22]; // берем значение контрольной суммы из пакета - оно в конце пакета
for(int j = 0; j < 22; j++) { // считаем контрольную сумму данных в пакете
cs+=buf[ j ]; }
cs ^= 0xFF;
if(cs == realCS){ // сравниваем рассчитанную и полученную контрольные суммы, если все впорядке - погнали
Serial.print("Packet from BL9042 test Ok\n"); // пакет целый
//Извлечение из пакета напряжения, тока, мощности
raw_unscaled_voltage = ((uint32_t)buf[6] << 16) | ((uint32_t)buf[5] << 8) | ((uint32_t)buf[4]); // извлекаем напряжение по мнению BL0942
newVoltage = (raw_unscaled_voltage / BL0942_UREF); // приводим напряжение к Вольтам
if ( newVoltage < 1000) { goodVoltage = newVoltage; } // проверяем на валидность, если напряжение в разумных пределах - запоминаем (невалидное значение больше реального в тысячи раз)
raw_unscaled_tok = ((uint32_t)buf[3] << 16) | ((uint32_t)buf[2] << 8) | ((uint32_t)buf[1]); ; // извлекаем средневзвешенный ток по мнению BL0942
newTok = (raw_unscaled_tok / BL0942_IREF); // приводим ток к Амперам
if ( newTok < 16) { goodTok = newTok; } // проверяем на валидность, если ток в разумных пределах - запоминаем (невалидное значение больше реального в тысячи раз)
raw_unscaled_power = ((uint32_t)buf[12] << 16) | ((uint32_t)buf[11] << 8) | ((uint32_t)buf[10]);; // извлекаем средневзвешенную мощность по мнению BL0942
newPower = (raw_unscaled_power / BL0942_PREF); // приводим мощность к Ваттам
if ( newPower < 16000) { goodPower = newPower; } // проверяем на валидность, если мощность в разумных пределах - запоминаем (невалидное значение больше реального в тысячи раз)
raw_unscaled_energy = ((uint32_t)buf[15] << 16) | ((uint32_t)buf[14] << 8) | ((uint32_t)buf[13]);; // извлекаем потребленную энергию по мнению BL0942
newEnergy = (raw_unscaled_energy / BL0942_WREF); // приводим потребленную эергию к киловатт часам
if ( newEnergy < 16000) { goodEnergy = newEnergy; } // проверяем на валидность, если энергия в разумных пределах - запоминаем (невалидное значение больше реального в тысячи раз)
// вывод данных в монитор порта
Serial.print("V = "); Serial.println(goodVoltage);
Serial.print("I = "); Serial.println(goodTok);
Serial.print("P = "); Serial.println(goodPower);
Serial.print("kW.h = "); Serial.println(goodEnergy);
}
else {
Serial.print("Packet from BL9042 not ok\n"); } // Пакет битый
}
}