logo elektroda
logo elektroda
X
logo elektroda

[BK7231N/CB2S/BL0937] Wifi Smart Plug with Energy Measurement (RMC021)

Raufaser 9375 47
ADVERTISEMENT
  • #31 21148742
    Daro1003
    Level 34  
    I added an ESP8266 module to such a plug, onto which I threw Tasmote and then added it to domoticz.
    Of course CB2S soldered out and in its place on the ESP8266 wires

    If anyone would be interested in the topic of converting to ESP 8266 and Tasmote it downloads:

    PCB with marked pin numbers for connecting ESP8266. .


    1. VCC +3.3V
    2. GND
    3. button -> GPIO 0
    4. hlwbl SEL_i -> GPIO 12
    5. relay -> GPIO 15
    6. HLWBL CF1 -> GPIO 4
    7. BL0937 CF -> GPIO 5
    8. LED -> GPIO 2

    Works ok already 3 pieces so converted function in home automation. Switching on and off devices and measuring energy consumption all in Domoticz. Much better value than the SP111, just a little of your own time at night.

    Maybe the above list will help someone. To get there, take a meter and look along the paths.
    It will also be useful then:
    HLW8012 pin configuration diagram with function descriptions. .
  • ADVERTISEMENT
  • #32 21149147
    tun0
    Level 6  
    p.kaczmarek2 wrote:
    None of those look promising. Still, what are those 3 soic chips at the bottom of the blue one?


    Yeah, that's pretty much what I was afraid of. I haven't been able to determine any of those 3 chips, yet. Perhaps with my microscope I can, but haven't gotten around to try that yet.
    Also, pretty sure I should have a "known good" adapter "somewhere". But those things tend to be darn good at playing hide and seek 😂

    Added after 2 [minutes]:

    >>21148742

    Interesting! 🤔
    Which ESP8266 module did you use for this modification? And how did you configure Tasmota?
  • #33 21177527
    p.kaczmarek2
    Moderator Smart Home
    CB2S/WB2S can be replaced with TYWE2S or ESP02S.
    Diagram of the TYWE2S module with pin descriptions. WiFi module ESP-02S with markings from Doiting.
    To quote Blakader: This module can replace modules such as: BT2S, CB2S, ESP-02S, FL_M99_V1, TYWE2S, TYZS6, TW-02, WR2, WB2, WBR2, WBR2D, WR2E, WB2S, WA2, XT-BL02, ZS2S
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #34 21271875
    mjab

    Level 10  
    I converted a BLOW WiFi SMART SOCKET (about 25 PLN on Allegro) with Volt and Amp meter just like that. All the pins match, only for the LED instead of GPIO2 I used GPIO14 because under 2ka is the built-in LED in the ESP8266.

    I soldered out the CB2S module using braid and soldered a clean ESP8266MOD chip in its place with wires to these pins. On the ESP8266 I connected the EN pins with a 10k resistor to VCC and GPIO15 with a 10k resistor to GND. This fits snugly, no extra PCB to the ESP8266 will go in.

    I have currently uploaded SUPLA on a test basis and it works ok, ultimately I want to write my own program in the Arduino IDE.
  • ADVERTISEMENT
  • #35 21272544
    mjab

    Level 10  
    I've spent a lot of time on the https://github.com/xoseperez/hlw8012 library and ... In the program under the Arduino IDE for the ESP8266 it measures some silly things. It shows Volts in thousands ... Over 3h I tried to calibrate it and only Watts was successful. The Supla, on the other hand, measures to the nearest decimal, so is it possible? I am momentarily stuck with this ...
  • #36 21272970
    p.kaczmarek2
    Moderator Smart Home
    Have you calibrated?

    The watts are on CF, and the current and voltage are on CF1. You need to set the state on SEL.

    Diagram of the HLW8012 integrated circuit with pin labels and functions. .
    Helpful post? Buy me a coffee.
  • #37 21273019
    mjab

    Level 10  
    All these libraries in the Arduino IDE for the BL0937 are lame !
    I started writing all the code myself....

    Currently my code is like this :

    
    #include <Arduino.h>
    #include "Timer.h"
    #include <ESP8266WiFi.h>
    #include <WiFiClient.h>
    #include <ESP8266WebServer.h>
    #include <ESP8266HTTPClient.h>
    #include <ESP8266HTTPUpdateServer.h>
    #include <ESP8266mDNS.h>
    
    Timer t;
    WiFiClient client;
    ESP8266WebServer server(80);
    ESP8266HTTPUpdateServer httpUpdater;
    
    
    // --- USTAWIENIA ---
    const String plyta = "BLOW WiFi SMART SOCKET [EAN : 5900804116486]"; // Producent i model płyty głównej urządzenia
    const char *OSver = "v.2.3"; // Wersja oprogramowania
    const char *deviceName = "jGniazdko"; // Nazwa widoczna w routerze (bramie)
    
    const char *ssid =  "xxxxxx"; // Nazwa sieci WiFi
    const char *pass =  "xxxxxxxx"; // Hasło do sieci WiFi
    
    const String DOMOTICZ_SERVER_IP = "[login]:[hasło]@192.168.x.x:8080"; // Login, hasło i adres IP wraz z portem do serwera Domoticz
    // ------
    
    
    int SerwerWWW = 1;
    
    // BL0937
    float Waty = 0.00;
    int Volty = 0;
    float Ampery = 0.00;
    
    // Deklaracja i definicja funkcji do mierzenia częstotliwości
    unsigned long measurePulse(int pin) {
      unsigned long pulseDuration = pulseIn(pin, HIGH);  // Pomiar czasu trwania impulsu w mikrosekundach
      if (pulseDuration > 0) {
        return 1000000 / pulseDuration;  // Zwraca częstotliwość w Hz (impulsy na sekundę)
      }
      return 0;
    }
    
    // Odczyt napięcia
    float readVoltage() {
      digitalWrite(12, HIGH);  // SEL = HIGH -> Odczyt napięcia
      delay(50);  // Małe opóźnienie na stabilizację sygnału
      unsigned long freqCF1 = measurePulse(4);  // CF1 -> Pin 4
      delay(50);
      freqCF1 += measurePulse(4);
      freqCF1 = freqCF1 / 2;
    
      float voltage = freqCF1 * 0.00733 * 1.137;  // Przelicznik zależny od kalibracji
      return voltage;
    }
    
    // Odczyt prądu
    float readCurrent() {
      digitalWrite(12, LOW);  // SEL = LOW -> Odczyt prądu
      delay(50);  // Małe opóźnienie na stabilizację sygnału
      unsigned long freqCF1 = measurePulse(4);  // CF1 -> Pin 4
      float cosPhi = 0.9;  // Współczynnik mocy
      float current = (freqCF1 * 0.00001405) * cosPhi;  // Kalibracja z uwzględnieniem współczynnika mocy
      return current;
    }
    
    // Odczyt mocy
    float readPower() {
      unsigned long freqCF = measurePulse(5);  // CF -> Pin 5
      float power = freqCF * 0.00306;  // Przelicznik zależny od kalibracji
      return power;
    }
    
    void miernik() {
     SerwerWWW = 0;
     delay(10);
     Ampery = readCurrent();
     Volty = readVoltage();
     Waty = readPower();
     SerwerWWW = 1;
    }
    
    
    String getValue(String data, char separator, int index) { 
     int found = 0; 
     int strIndex[] = {0, -1}; 
     int maxIndex = data.length()-1; 
         
     for(int i=0; i<=maxIndex && found<=index; i++){ 
      if(data.charAt(i)==separator || i==maxIndex){ 
       found++; 
       strIndex[0] = strIndex[1]+1; 
       strIndex[1] = (i == maxIndex) ? i+1 : i; 
      } 
     } 
         
     return found>index ? data.substring(strIndex[0], strIndex[1]) : ""; 
    }
    
    
    void www() {
     String html = "<html><head><meta charset='UTF-8'>";
     html += "<style>";
     html += "body { background-color: black; color: white; }";
     html += "table { width: 800px; background-color: white; color: black; border-spacing: 10px; }";
     html += "th { background-color: orange; }";
     html += ".color-bar { height: 32px; width: 100%; background: linear-gradient(to right, green, yellow, red); }";
     html += ".progress-bar { width: 100%; background-color: gray; }";
     html += "h1 { color: white; }";
     html += ".center-cell { text-align: center; }";
     html += ".option-column { width: 25%; }";
     html += ".value-column { width: 75%; }";
     html += ".status-active { color: darkred; }";
     html += ".status-inactive { color: darkgreen; }";
     html += "button { padding: 10px; background-color: orange; border: none; color: white; font-size: 16px; cursor: pointer; }";
     html += "</style>";
     html += "<meta http-equiv=\"refresh\" content=\"2; url=/\"></head><body>";
     html += "<center><h1>- " + String(deviceName) + " -</h1>";
     html += "<table>";
     html += "<tr><th class='option-column'>Opcja</th><th class='value-column'>Wartość</th></tr>";
     html += "<tr><td style='text-align: right;'>Płyta główna : </td><td style='text-align: left;'><b>" + String(plyta) + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'>Wersja programu : </td><td style='text-align: left;'><b>" + String(OSver) + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'> </td><td style='text-align: left;'> </td></tr>";
     html += "<tr><td style='text-align: right;'>IP : </td><td style='text-align: left;'><b>" + WiFi.localIP().toString() + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'>MAC : </td><td style='text-align: left;'><b>" + WiFi.macAddress() + "</b></td></tr>";
     int SilaWiFi = WiFi.RSSI();
     if (SilaWiFi < -65) html += "<tr><td style='text-align: right;'>Sygnał WiFi : </td><td style='text-align: left;'><b>" + String(SilaWiFi) + " dBm</b> [ SLABY SYGNAL ! ]</td></tr>";
     else html += "<tr><td style='text-align: right;'>Sygnał WiFi : </td><td style='text-align: left;'><b>" + String(SilaWiFi) + " dBm</b></td></tr>";
     html += "<tr><td style='text-align: right;'> </td><td style='text-align: left;'> </td></tr>";
     html += "<tr><td style='text-align: right;'>Volty : </td><td style='text-align: left;'><b>" + String(Volty) + " V</b></td></tr>";
     html += "<tr><td style='text-align: right;'>Ampery : </td><td style='text-align: left;'><b>" + String(Ampery,1) + " A</b> / 10 A</td></tr>";
     html += "<tr><td style='text-align: right;'>Waty : </td><td>";
    
     float wattPercentage = (Waty / 2300.0) * 100;
     if (wattPercentage > 100) wattPercentage = 100;
     else if (Waty < 1) wattPercentage = 0;
    
     html += "<div class='progress-bar'><div class='color-bar' style='width: " + String(wattPercentage) + "%;'></div></div>";
     html += "<span><center>" + String(Waty, 1) + " W <i>/ 2300 W</i>  [" + String(wattPercentage, 0) + "% ]</center></span></td></tr>";
     html += "</table><br>";
    
     html += "<table>";
     html += "<tr><th colspan='2'>Zasilanie</th></tr>";
     html += "<tr><td style='text-align: center;'>Stan zasilania : </td><td>";
    
     int StanGPIO15 = digitalRead(15);
    
     if (StanGPIO15 == HIGH) {
       html += "<b class='status-active'>[Aktywne] </b> <i> [Uwaga na możliwe wysokie napięcie !]</i></td></tr>";
     } else {
       html += "<b class='status-inactive'>[Wyłączone]</b></td></tr>";
     }
    
     html += "<tr><td colspan='2' style='text-align: center;'>";
     if (StanGPIO15 == HIGH) {
       html += "<button onclick=\"window.location.href='ustaw?zasilanie=0'\">Wyłącz</button>";
     } else {
       html += "<button onclick=\"window.location.href='ustaw?zasilanie=1'\">Włącz</button>";
     }
     html += "</td></tr>";
     html += "</table>";
     html += "<br><br><br><i>Prawa autorskie - Michał Jabłoński (www.k.j.pl) - Jabłoński KOMPUTERY</i></center></body></html>";
    
     server.send(200, "text/html", html);
    }
    
    
    void Polacz_WiFi() {
      if (WiFi.status() != WL_CONNECTED) {
       Serial.println("Laczenie z WiFi");
    
       WiFi.setOutputPower(20.5);
       WiFi.begin(ssid, pass);
       WiFi.hostname(deviceName);
      
       while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        digitalWrite(14, LOW);
        delay(100);
        digitalWrite(14, HIGH);
        delay(100);
       }
       
       Serial.println("");
       Serial.println("Przydzielono adres IP: ");
       Serial.print(WiFi.localIP());
       Serial.println("");
       digitalWrite(12, LOW);
      }
    }
    
    
    // Wysyłanie danych do Domoticz
    void domoticz(int idx, String nvalue, String svalue) {
      Serial.println(" - Wysylam dane do domoticz...");
      HTTPClient http;
      String adres = "http://" + DOMOTICZ_SERVER_IP + "/json.htm?type=command&param=udevice&idx=" + String(idx) + "&nvalue=" + nvalue + "&svalue=" + svalue;
      Serial.print("     URL : "); Serial.println(adres);
      http.begin(client, adres);
      http.addHeader("Content-Type", "application/html");
      int httpCode = http.GET();
      http.end();
      Serial.println("    Dane do Domoticz zostaly wyslane.");
    }
    
    
    void setup() {
     delay(1000);
     Serial.begin(115200);
    
     pinMode(0, INPUT_PULLUP); // Przycisk (LOW = Wciśnięty)
     pinMode(4, INPUT_PULLUP); // CF1
     pinMode(5, INPUT_PULLUP); // CF
     pinMode(12, OUTPUT); // SELi  [ LOW = Ampery | HIGH = Volty ]
     pinMode(14, OUTPUT); // LED niebieska
     pinMode(15, OUTPUT); // Przekaźnik
    
    
     digitalWrite(12, LOW);
     digitalWrite(14, HIGH);
     digitalWrite(15, HIGH);
    
     Polacz_WiFi();
     delay(250);
    
     httpUpdater.setup(&server);
     server.on("/", www);
     server.on("/ustaw", HTTP_GET, www);
     server.begin();
    
     miernik();
     t.every(5000, miernik);
    }
    
    
    void loop() {
     if (WiFi.status() == WL_CONNECTED and SerwerWWW == 1) server.handleClient();
     else if (WiFi.status() != WL_CONNECTED) Polacz_WiFi();
     t.update();
    }
    
    .


    However, it measures inaccurately all the time ... what needs to be improved here and how would the measurements be as accurate as in Supla?

    Also the relay control is not written yet, but this is a formality.
  • #38 21273631
    p.kaczmarek2
    Moderator Smart Home
    Instead of measuring one pulse, try setting up an interrupt and counting how many pulses there are per second, for example, and then calibrating.

    In my case it looks like this:
    https://github.com/openshwprojects/OpenBK7231T_App/blob/main/src/driver/drv_bl0937.c

    Here is some library under ESP:
    https://github.com/MacWyznawca/HLW8012_BL0937_ESP
    Helpful post? Buy me a coffee.
  • #39 21273735
    mjab

    Level 10  
    OK, I'll try to count these pulses :)

    and this library I have already seen, but there are no examples in it and I don't know how to use it.
  • #40 21273742
    p.kaczmarek2
    Moderator Smart Home
    See here:
    https://github.com/MacWyznawca/HLW8012_BL0937_ESP/blob/master/HLW8012_ESP82.h
    You probably init first, then calibrate HLW8012_expectedCurrent etc, and then fetch HLW8012_getCurrent
    Interestingly, as far as I can see, you don't need to call HLW8012_toggleMode as it is called automatically.
    Helpful post? Buy me a coffee.
  • #41 21273750
    mjab

    Level 10  
    I have come up with this code and am currently testing :

    
    unsigned long countPulses(int pin, unsigned long durationMs) {
      unsigned long startTime = millis();
      unsigned long pulseCount = 0;
    
      while (millis() - startTime < durationMs) {
        if (digitalRead(pin) == HIGH) {
          pulseCount++;
          while (digitalRead(pin) == HIGH);  // Czekaj na spadek sygnału
      }
    
      return pulseCount;
    }
    
    float readVoltage() {
      digitalWrite(12, HIGH);  // SEL = HIGH -> Odczyt napięcia
      unsigned long pulseCount = countPulses(4, 250);  // Zlicz impulsy na CF1 przez 250 ms
    
      float voltage = (pulseCount * 4) * 0.00733 * 1.137;  // Przelicz liczba impulsów na napięcie
      return voltage;
    }
    
    float readCurrent() {
      digitalWrite(12, LOW);  // SEL = LOW -> Odczyt prądu
      unsigned long pulseCount = countPulses(4, 250);  // Zlicz impulsy na CF1 przez 250 ms
      float cosPhi = 0.9;  // Współczynnik mocy
      float current = (pulseCount * 4 * 0.00001405) * cosPhi;  // Kalibracja prądu
      return current;
    }
    
    float readPower() {
      unsigned long pulseCount = countPulses(5, 250);  // Zlicz impulsy na CF przez 250 ms
      float power = (pulseCount * 4 * 0.00306);  // Kalibracja mocy
      return power;
    }
    
    .

    Added after 17 [minutes]:

    After setting a factor of 0.1217 for float readVoltage(), this seems to be much more stable in measurements ...

    Added after 43 [minutes]:

    Currently my code is :

    
    #include <Arduino.h>
    #include "Timer.h"
    #include <ESP8266WiFi.h>
    #include <WiFiClient.h>
    #include <ESP8266WebServer.h>
    #include <ESP8266HTTPClient.h>
    #include <ESP8266HTTPUpdateServer.h>
    #include <ESP8266mDNS.h>
    
    Timer t;
    WiFiClient client;
    ESP8266WebServer server(80);
    ESP8266HTTPUpdateServer httpUpdater;
    
    
    // --- USTAWIENIA ---
    const String plyta = "BLOW WiFi SMART SOCKET [EAN : 5900804116486]"; // Producent i model płyty głównej urządzenia
    const char *OSver = "v.2.4"; // Wersja oprogramowania
    const char *deviceName = "jGniazdko"; // Nazwa widoczna w routerze (bramie)
    
    const char *ssid =  "xxxxxx"; // Nazwa sieci WiFi
    const char *pass =  "xxxxxxxx"; // Hasło do sieci WiFi
    
    const String DOMOTICZ_SERVER_IP = "[login]:[hasło]@192.168.x.x:8080"; // Login, hasło i adres IP wraz z portem do serwera Domoticz
    // ------
    
    
    int SerwerWWW = 1;
    
    // BL0937
    float Waty = 0.00;
    float Volty = 0.00;
    float Ampery = 0.00;
    
    // Deklaracja i definicja funkcji do mierzenia ilości impulsów w zadanym czasie
    unsigned long countPulses(int pin, unsigned long durationMs) {
      unsigned long startTime = millis();
      unsigned long pulseCount = 0;
    
      while (millis() - startTime < durationMs) {
        if (digitalRead(pin) == HIGH) {
          pulseCount++;
          while (digitalRead(pin) == HIGH);  // Czekaj na spadek sygnału
        }
      }
    
      return pulseCount;
    }
    
    // Odczyt napięcia (V)
    float readVoltage() {
      digitalWrite(12, HIGH);  // SEL = HIGH -> Odczyt napięcia
      unsigned long pulseCount = countPulses(4, 250);  // Zlicz impulsy na CF1 przez 250 ms
    
      float voltage = (pulseCount * 4) * 0.1218; // Współczynnik kalibracji
      return voltage;
    }
    
    // Odczyt prądu (A)
    float readCurrent() {
      digitalWrite(12, LOW);  // SEL = LOW -> Odczyt prądu
      unsigned long pulseCount = countPulses(4, 250);  // Zlicz impulsy na CF1 przez 250 ms  
      float current = pulseCount * 0.0454; // Współczynnik kalibracji
      return current;
    }
    
    // Odczyt mocy (W)
    float readPower() {
      unsigned long pulseCount = countPulses(5, 250);  // Zlicz impulsy na CF przez 250 ms
      float power = pulseCount * 5.43; // Współczynnik kalibracji
      return power;
    }
    
    void miernik() {
     SerwerWWW = 0;
     delay(10);
     Ampery = readCurrent();
     Volty = readVoltage();
     Waty = readPower();
     SerwerWWW = 1;
    }
    
    
    String getValue(String data, char separator, int index) { 
     int found = 0; 
     int strIndex[] = {0, -1}; 
     int maxIndex = data.length()-1; 
         
     for(int i=0; i<=maxIndex && found<=index; i++){ 
      if(data.charAt(i)==separator || i==maxIndex){ 
       found++; 
       strIndex[0] = strIndex[1]+1; 
       strIndex[1] = (i == maxIndex) ? i+1 : i; 
      } 
     } 
         
     return found>index ? data.substring(strIndex[0], strIndex[1]) : ""; 
    }
    
    
    void www() {
     String html = "<html><head><meta charset='UTF-8'>";
     html += "<style>";
     html += "body { background-color: black; color: white; }";
     html += "table { width: 800px; background-color: white; color: black; border-spacing: 10px; }";
     html += "th { background-color: orange; }";
     html += ".color-bar { height: 32px; width: 100%; background: linear-gradient(to right, green, yellow, red); }";
     html += ".progress-bar { width: 100%; background-color: gray; }";
     html += "h1 { color: white; }";
     html += ".center-cell { text-align: center; }";
     html += ".option-column { width: 25%; }";
     html += ".value-column { width: 75%; }";
     html += ".status-active { color: darkred; }";
     html += ".status-inactive { color: darkgreen; }";
     html += "button { padding: 10px; background-color: orange; border: none; color: white; font-size: 16px; cursor: pointer; }";
     html += "</style>";
     html += "<meta http-equiv=\"refresh\" content=\"2; url=/\"></head><body>";
     html += "<center><h1>- " + String(deviceName) + " -</h1>";
     html += "<table>";
     html += "<tr><th class='option-column'>Opcja</th><th class='value-column'>Wartość</th></tr>";
     html += "<tr><td style='text-align: right;'>Płyta główna : </td><td style='text-align: left;'><b>" + String(plyta) + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'>Wersja programu : </td><td style='text-align: left;'><b>" + String(OSver) + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'> </td><td style='text-align: left;'> </td></tr>";
     html += "<tr><td style='text-align: right;'>IP : </td><td style='text-align: left;'><b>" + WiFi.localIP().toString() + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'>MAC : </td><td style='text-align: left;'><b>" + WiFi.macAddress() + "</b></td></tr>";
     int SilaWiFi = WiFi.RSSI();
     if (SilaWiFi < -65) html += "<tr><td style='text-align: right;'>Sygnał WiFi : </td><td style='text-align: left;'><b>" + String(SilaWiFi) + " dBm</b> [ SLABY SYGNAL ! ]</td></tr>";
     else html += "<tr><td style='text-align: right;'>Sygnał WiFi : </td><td style='text-align: left;'><b>" + String(SilaWiFi) + " dBm</b></td></tr>";
     html += "<tr><td style='text-align: right;'> </td><td style='text-align: left;'> </td></tr>";
     html += "<tr><td style='text-align: right;'>Volty : </td><td style='text-align: left;'><b>" + String(Volty,1) + " V</b></td></tr>";
     html += "<tr><td style='text-align: right;'>Ampery : </td><td style='text-align: left;'><b>" + String(Ampery,1) + " A</b> / 10 A</td></tr>";
     html += "<tr><td style='text-align: right;'>Waty : </td><td>";
    
     float wattPercentage = (Waty / 2300.0) * 100;
     if (wattPercentage > 100) wattPercentage = 100;
     else if (Waty < 1) wattPercentage = 0;
    
     html += "<div class='progress-bar'><div class='color-bar' style='width: " + String(wattPercentage) + "%;'></div></div>";
     html += "<span><center>" + String(Waty, 1) + " W <i>/ 2300 W</i>  [" + String(wattPercentage, 0) + "% ]</center></span></td></tr>";
     html += "</table><br>";
    
     html += "<table>";
     html += "<tr><th colspan='2'>Zasilanie</th></tr>";
     html += "<tr><td style='text-align: center;'>Stan zasilania : </td><td>";
    
     int StanGPIO15 = digitalRead(15);
    
     if (StanGPIO15 == HIGH) {
       html += "<b class='status-active'>[Aktywne] </b> <i> [Uwaga na możliwe wysokie napięcie !]</i></td></tr>";
     } else {
       html += "<b class='status-inactive'>[Wyłączone]</b></td></tr>";
     }
    
     html += "<tr><td colspan='2' style='text-align: center;'>";
     if (StanGPIO15 == HIGH) {
       html += "<button onclick=\"window.location.href='ustaw?zasilanie=0'\">Wyłącz</button>";
     } else {
       html += "<button onclick=\"window.location.href='ustaw?zasilanie=1'\">Włącz</button>";
     }
     html += "</td></tr>";
     html += "</table>";
     html += "<br><br><br><i>Prawa autorskie - Michał Jabłoński (www.k.j.pl) - Jabłoński KOMPUTERY</i></center></body></html>";
    
     server.send(200, "text/html", html);
    }
    
    
    void Polacz_WiFi() {
      if (WiFi.status() != WL_CONNECTED) {
       Serial.println("Laczenie z WiFi");
    
       WiFi.setOutputPower(20.5);
       WiFi.begin(ssid, pass);
       WiFi.hostname(deviceName);
      
       while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        digitalWrite(14, LOW);
        delay(100);
        digitalWrite(14, HIGH);
        delay(100);
       }
       
       Serial.println("");
       Serial.println("Przydzielono adres IP: ");
       Serial.print(WiFi.localIP());
       Serial.println("");
       digitalWrite(12, LOW);
      }
    }
    
    
    // Wysyłanie danych do Domoticz
    void domoticz(int idx, String nvalue, String svalue) {
      Serial.println(" - Wysylam dane do domoticz...");
      HTTPClient http;
      String adres = "http://" + DOMOTICZ_SERVER_IP + "/json.htm?type=command&param=udevice&idx=" + String(idx) + "&nvalue=" + nvalue + "&svalue=" + svalue;
      Serial.print("     URL : "); Serial.println(adres);
      http.begin(client, adres);
      http.addHeader("Content-Type", "application/html");
      int httpCode = http.GET();
      http.end();
      Serial.println("    Dane do Domoticz zostaly wyslane.");
    }
    
    
    void setup() {
     delay(1000);
     Serial.begin(115200);
    
     pinMode(0, INPUT_PULLUP); // Przycisk (LOW = Wciśnięty)
     pinMode(4, INPUT_PULLUP); // CF1
     pinMode(5, INPUT_PULLUP); // CF
     pinMode(12, OUTPUT); // SELi  [ LOW = Ampery | HIGH = Volty ]
     pinMode(14, OUTPUT); // LED niebieska
     pinMode(15, OUTPUT); // Przekaźnik
    
    
     digitalWrite(12, LOW);
     digitalWrite(14, HIGH);
     digitalWrite(15, HIGH);
    
     Polacz_WiFi();
     delay(250);
    
     httpUpdater.setup(&server);
     server.on("/", www);
     server.on("/ustaw", HTTP_GET, www);
     server.begin();
    
     miernik();
     t.every(5000, miernik);
    }
    
    
    void loop() {
     if (WiFi.status() == WL_CONNECTED and SerwerWWW == 1) server.handleClient();
     else if (WiFi.status() != WL_CONNECTED) Polacz_WiFi();
     t.update();
    }
    


    Still no button and relay support ... while it seems to count more precisely. The thought is to use interrupts
    attachInterrupt(digitalPinToInterrupt(4), onPulseCF1, RISING);  // Przerwanie na CF1 (pin 4)
      attachInterrupt(digitalPinToInterrupt(5), onPulseCF, RISING);   // Przerwanie na CF (pin 5)
    eventually ...
  • #42 21273841
    p.kaczmarek2
    Moderator Smart Home
    Do not use digitalRead, this approach is bad, imprecise and blocks program execution. It is definitely better to use an interrupt.
    See here:
    https://github.com/MacWyznawca/HLW8012_BL0937_ESP/blob/master/HLW8012_ESP82.c

    Do this, for now without entering SEL:
    1. set the GPIO interrupt once to CF and CF1
    2. in the interrupts just increment the number of pulses by 1
    3. every second or so, view how many pulses it has counted, reset the counter, make a time correction to e.g. estimate the number of pulses per second, and then calibrate it
    Helpful post? Buy me a coffee.
  • #43 21275404
    clanking6535
    Level 1  
    I obtained the same CB2S smart plug as OP. Initially I had problems with flashing, but after I removed the module and set the reply delay to 5 and everything worked smooth as butter. I can post the JSON and backup but they match OP:

    
    {
    	"sel_pin_pin":"24",
    	"rl1_lv":"1",
    	"bt1_pin":"10",
    	"net_trig":"4",
    	"jv":"1.0.5",
    	"netled1_lv":"0",
    	"netled_reuse":"0",
    	"bt1_type":"0",
    	"ffc_select":"0",
    	"nety_led":"1",
    	"vi_pin":"6",
    	"resistor":"1",
    	"over_cur":"21000",
    	"bt1_lv":"0",
    	"reset_t":"5",
    	"netled1_pin":"8",
    	"chip_type":"0",
    	"lose_vol":"90",
    	"over_vol":"265",
    	"module":"CB2S",
    	"ele_pin":"7",
    	"ch_cddpid1":"9",
    	"ch1_stat":"2",
    	"rl1_type":"0",
    	"ch_num":"1",
    	"ele_fun_en":"1",
    	"rl1_pin":"26",
    	"netn_led":"0",
    	"vol_def":"0",
    	"ch_dpid1":"1",
    	"sel_pin_lv":"1",
    	"crc":"86"
    }
    


    I was also able to upload ESPHome on it by just selecting the CB2S module and using the OTA feature to upload it. I did have to rename the file so it'd match the OTA check.
    Here is my config so far:

    
    light:
      - platform: status_led
        id: "status"
        name: "Switch state"
        pin:
          number: GPIO8
          inverted: true
    
    switch:
      - platform: gpio
        id: "relay"
        pin:
          number: GPIO26
          inverted: false
        name: "Relay"
        restore_mode: ALWAYS_ON
    
    binary_sensor:
      - platform: gpio
        pin:
          number: GPIO10
          inverted: true
        name: "Button"
        on_press:
          - switch.toggle: relay
    
    sensor:
      - platform: hlw8012
        sel_pin:
          number: GPIO24
          inverted: true
        cf_pin: GPIO7
        cf1_pin: GPIO6
        model: BL0937
        update_interval: 10s
        current:
          name: "Current"
        voltage:
          name: "Voltage"
        power:
          name: "Power"
        energy:
          name: "Energy"
    


    However, the BL0937 reads as all zeros. Any idea what I'm doing wrong?
    Unfortunately I didn't try OpenBeken extensively to see if it worked there, and there doesn't seem to be an easy way to use ESPHome OTA to upload OpenBeken.
  • #44 21275863
    mjab

    Level 10  
    Web interface of a smart WiFi socket showing device status and energy consumption. .

    
    #include <Arduino.h>
    #include "Timer.h"
    #include <ESP8266WiFi.h>
    #include <WiFiClient.h>
    #include <ESP8266WebServer.h>
    #include <ESP8266HTTPClient.h>
    #include <ESP8266HTTPUpdateServer.h>
    #include <ESP8266mDNS.h>
    #include <EEPROM.h>
    
    Timer t;
    WiFiClient client;
    ESP8266WebServer server(80);
    ESP8266HTTPUpdateServer httpUpdater;
    
    
    // --- USTAWIENIA ---
    const String plyta = "BLOW WiFi SMART SOCKET [EAN : 5900804116486]"; // Producent i model płyty głównej urządzenia
    const char *OSver = "v.6.4"; // Wersja oprogramowania
    const char *deviceName = "jGniazdko-WiFi-01"; // Nazwa widoczna w routerze (bramie)
    
    const int StartStan = 1; // Domyślny stan po zaniku prądu [ 1 - Właczony | 0 - Wyłaczony ]
    int BlokadaPrzycisku = 0; // Stan blokady przycisku na urzadzeniu [ 1 - Przycisk zabokowany | 0 - Przycisk odblokowany ]
    
    const float WspolczynnikV = 0.1207; // Współczynnik napięcia (V) [KALIBRACJA]
    const float WspolczynnikW = 1.3302; // Współczynnik mocy (W) [KALIBRACJA]
    const float MaxWaty = 2300; // Cyfrowy bezpiecznik, gdy moc przekroczy tę wartośc to rozłaczy przekaźnik.
    
    const float MinVolty = 0; // Napięcie sieciowe poniżej którego przekaźnik zostanie rozłaczony.
    const float MaxVolty = 0; // Napięcie sieciowe powyżej którego przekaźnik zostanie rozłaczony.
    const String NormaEnergetyczna = "PN-IEC 60038"; // Numer normy energetycznej w danej sieci.
    
    const char *ssid =  "xxxxx"; // Nazwa sieci WiFi
    const char *pass =  "xxxxxxxx"; // Hasło do sieci WiFi
    
    const String DOMOTICZ_SERVER_IP = "[LOGIN[:[HASŁO]@192.168.x.x:8080"; // Login, hasło i adres IP wraz z portem do serwera Domoticz
    const int IDXprzekaznik = 285; // Numer IDX w Domoticz typu "przełącznik". Do zarządzania przekaźnikiem.
    const int IDXmiernik = 284; // Numer IDX w Domoticz typu "zużycie (energia elektryczna)". Do pomiarów zużytej energii.
    // ------
    
    
    int StanPrzycisku = 0;
    int StanPrzekaznikDomoticz = 2;
    
    
    String getValue(String data, char separator, int index) { 
     int found = 0; 
     int strIndex[] = {0, -1}; 
     int maxIndex = data.length()-1; 
         
     for (int i=0; i<=maxIndex && found<=index; i++) { 
      if (data.charAt(i)==separator || i==maxIndex) { 
       found++; 
       strIndex[0] = strIndex[1]+1; 
       strIndex[1] = (i == maxIndex) ? i+1 : i; 
      }
     }
         
     return found>index ? data.substring(strIndex[0], strIndex[1]) : ""; 
    }
    
    
    void EEPROM_ZAPIS(String buffer, int N) {
     digitalWrite(14, LOW);
     EEPROM.begin(512);
     delay(5);
    
     for (int L = 0; L < 32; ++L) {
      EEPROM.write(N + L, buffer[L]);
     }
    
     EEPROM.commit();
     delay(5);
     digitalWrite(14, HIGH);
    }
    
    
    String EEPROM_ODCZYT(int min, int max) {
     digitalWrite(14, LOW);
     EEPROM.begin(512);
     delay(10);
     String buffer;
     for (int L = min; L < max; ++L) if (isAlphaNumeric(EEPROM.read(L))) buffer += char(EEPROM.read(L));    
     return buffer;
     digitalWrite(14, HIGH);
    }
    
    
    // BL0937
    float Volty = 0.00;
    float Waty = 0.00;
    float Ampery = 0.00;
    float kWh = 0.000;
    int kwhMEM = 0;
    String DanekWhOLD = "";
    int bezpiecznik = 0;
    int UszkodzonyPrzekaznik = 0;
    int BledneVolty = 0;
    volatile unsigned long pulseCountCF = 0;
    volatile unsigned long pulseCountCF1 = 0;
    
    // Liczenie inpulsów napięcia (V)
    void IRAM_ATTR onPulseCF() {
      pulseCountCF++;
    }
    
    // Liczenie inpulsów mocy (W)
    void IRAM_ATTR onPulseCF1() {
      pulseCountCF1++;
    }
    
    void miernik() {
      noInterrupts(); // Wyłączamy przerwania na czas odczytu
      unsigned long pulsesCF = pulseCountCF;
      unsigned long pulsesCF1 = pulseCountCF1;
      pulseCountCF = 0;
      pulseCountCF1 = 0;
      interrupts(); // Włączamy przerwania z powrotem
    
      Volty = pulsesCF * WspolczynnikV;  // Napięcie (V)
      Waty = pulsesCF1 * WspolczynnikW;  // Moc (W)
      
      if (Volty > 0 and Waty > 0) {
        Ampery = Waty / Volty;  // Obliczamy prąd (A)
      } else Ampery = 0.00;
    
      if (Volty > 0) {
        if (Waty > MaxWaty+5 and digitalRead(15) == HIGH) {
          bezpiecznik = 1;
          digitalWrite(15, LOW);
          WyslijStanPrzekaznika();
        } else if (Waty > 0 and digitalRead(15) == LOW and UszkodzonyPrzekaznik == 0) {
          UszkodzonyPrzekaznik = 2;
          digitalWrite(15, LOW);
          WyslijStanPrzekaznika();
        } else if (Waty > 0 and digitalRead(15) == LOW and UszkodzonyPrzekaznik == 2) {
          UszkodzonyPrzekaznik = 1;
          digitalWrite(15, LOW);
          WyslijStanPrzekaznika();
        } else if (UszkodzonyPrzekaznik == 1) UszkodzonyPrzekaznik = 0;
    
        if (Volty < MinVolty and Volty > 0 and BledneVolty == 0 and digitalRead(15) == HIGH) { 
          BledneVolty = 1;  
          digitalWrite(15, LOW);
          WyslijStanPrzekaznika();
        }
        else if (Volty > MaxVolty and BledneVolty == 0 and digitalRead(15) == HIGH) { 
          BledneVolty = 2; 
          digitalWrite(15, LOW);
          WyslijStanPrzekaznika();
        }
    
        // Liczenie zużycia energii w kWh
        kWh += (Waty / 1000.0) / 3600.0;  // Waty na kilowatogodziny w ciągu 1 sekundy
        
        int whole_kWh = floor(kWh);
        if (whole_kWh > kwhMEM) {
         kwhMEM = whole_kWh;
         ZapisStanuLicznika();
        }
    
        if (IDXmiernik > 0) {
          String DomoticzDane = String(Waty,0) + ";" + String(kWh,0);
          DomoticzDane.replace(" ", "");
          if (DanekWhOLD != DomoticzDane) {
           DanekWhOLD = DomoticzDane;
           domoticz(IDXmiernik, "0", DomoticzDane);
          }
        }
      }
    }
    
    void ZapisStanuLicznika() {
     String eepromTXT = String(kwhMEM) + ":";
     EEPROM_ZAPIS(eepromTXT, 0);
    }
    // ---
    
    
    // Obsługa przycisku
    void SprawdzPrzycisk() {
     if (BlokadaPrzycisku == 0) {
      int przycisk = digitalRead(0);
      if (przycisk == LOW and StanPrzycisku == 0) {
       StanPrzycisku = 1;
       bezpiecznik = 0;
       BledneVolty = 0;
       digitalWrite(15, !digitalRead(15));
       WyslijStanPrzekaznika();
      } else if (przycisk == HIGH and StanPrzycisku == 1) StanPrzycisku = 0;
     }
    }
    
    
    // Strona www urządzenia
    void www() {
     String html = "<html><head><meta charset='UTF-8'>";
     html += "<style>";
     html += "body { background-color: black; color: white; }";
     html += "table { width: 800px; background-color: white; color: black; border-spacing: 10px; }";
     html += "th { background-color: orange; }";
     html += ".color-bar { height: 32px; width: 100%; background: linear-gradient(to right, green, yellow, red); }";
     html += ".progress-bar { width: 100%; background-color: gray; }";
     html += "h1 { color: white; }";
     html += ".center-cell { text-align: center; }";
     html += ".option-column { width: 25%; }";
     html += ".value-column { width: 75%; }";
     html += ".status-active { color: darkred; }";
     html += ".status-inactive { color: darkgreen; }";
     html += "button { padding: 10px; background-color: orange; border: none; color: white; font-size: 16px; cursor: pointer; }";
     html += "</style>";
     html += "<meta http-equiv=\"refresh\" content=\"2; url=/\"></head><body>";
     html += "<center><h1>- " + String(deviceName) + " -</h1>";
     html += "<table>";
     html += "<tr><th class='option-column'>Opcja</th><th class='value-column'>Wartość</th></tr>";
     html += "<tr><td style='text-align: right;'>Płyta główna : </td><td style='text-align: left;'><b>" + String(plyta) + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'>Wersja programu : </td><td style='text-align: left;'><b>" + String(OSver) + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'> </td><td style='text-align: left;'> </td></tr>";
    
     if (bezpiecznik == 1) html += "<tr><td style='text-align: right;'><u>Bezpiecznik : </td><td style='text-align: left;'><b class='status-active'>Przekroczono " + String(MaxWaty,0) + "W, zasilanie odłączono !</b></td></tr>";
     if (UszkodzonyPrzekaznik == 1) html += "<tr><td style='text-align: right;'><u>Awaria : </td><td style='text-align: left;'><b class='status-active'>Przekaźnik jest rozłaczony i dalej występuje pobór prądu !</b></td></tr>";
     if (BledneVolty == 1) html += "<tr><td style='text-align: right;'><u>Uwaga : </td><td style='text-align: left;'><b class='status-active'>Wykryto napięcie [V] <u>niższe</u> niż dopuszcza norma " + String(NormaEnergetyczna) + " !<br> [Zasilanie wyłaczono] </b></td></tr>";
     else if (BledneVolty == 2) html += "<tr><td style='text-align: right;'><u>Uwaga : </td><td style='text-align: left;'><b class='status-active'>Wykryto napięcie [V] <u>wyższe</u> niż dopuszcza norma " + String(NormaEnergetyczna) + " !<br> [Zasilanie wyłaczono] </b></td></tr>";
     if (bezpiecznik == 1 or UszkodzonyPrzekaznik == 1 or BledneVolty > 0) html += "<tr><td style='text-align: right;'> </td><td style='text-align: left;'> </td></tr>";
     
     html += "<tr><td style='text-align: right;'>IP : </td><td style='text-align: left;'><b>" + WiFi.localIP().toString() + "</b></td></tr>";
     html += "<tr><td style='text-align: right;'>MAC : </td><td style='text-align: left;'><b>" + WiFi.macAddress() + "</b></td></tr>";
     int SilaWiFi = WiFi.RSSI();
     if (SilaWiFi < -65) html += "<tr><td style='text-align: right;'>Sygnał WiFi : </td><td style='text-align: left;'><b>" + String(SilaWiFi) + " dBm</b> [ SLABY SYGNAŁ ! ]</td></tr>";
     else html += "<tr><td style='text-align: right;'>Sygnał WiFi : </td><td style='text-align: left;'><b>" + String(SilaWiFi) + " dBm</b></td></tr>";
     html += "<tr><td style='text-align: right;'> </td><td style='text-align: left;'> </td></tr>";
     html += "<tr><td style='text-align: right;'><u>V</u>olty : </td><td style='text-align: left;'><b>" + String(Volty,1) + " V</b></td></tr>";
     if (Ampery < 0.1 and Ampery > 0) html += "<tr><td style='text-align: right;'><u>A</u>mpery : </td><td style='text-align: left;'><b>" + String(Ampery,2) + " A</b></td></tr>";
     else html += "<tr><td style='text-align: right;'><u>A</u>mpery : </td><td style='text-align: left;'><b>" + String(Ampery,1) + " A</b></td></tr>";
     if (kWh > 0) html += "<tr><td style='text-align: right;'>Licznik : </td><td style='text-align: left;'><b>" + String(kWh,3) + " kWh</b></td></tr>";
     html += "<tr><td style='text-align: right;'><u>W</u>aty : </td><td>";
    
     float wattPercentage = (Waty / MaxWaty) * 100;
     if (wattPercentage > 100) wattPercentage = 100;
     else if (Waty < 1) wattPercentage = 0;
    
     html += "<div class='progress-bar'><div class='color-bar' style='width: " + String(wattPercentage) + "%;'></div></div>";
     if (Waty > 0 and Waty < 1) html += "<span><center>" + String(Waty, 1) + " W <i>/ " + String(MaxWaty,0) + " W</i>  [" + String(wattPercentage, 0) + "% ]</center></span></td></tr>";
     else html += "<span><center>" + String(Waty, 0) + " W <i>/ " + String(MaxWaty,0) + " W</i>  [" + String(wattPercentage, 0) + "% ]</center></span></td></tr>";
     html += "</table><br>";
    
     html += "<table>";
     html += "<tr><th colspan='2'>Zasilanie</th></tr>";
     html += "<tr><td style='text-align: center;'>Stan zasilania : </td><td>";
    
     int StanGPIO15 = digitalRead(15);
     if ((StanGPIO15 == HIGH and Volty > 0) or Waty > 0) html += "<b class='status-active'>[Aktywne] </b> <i> [Uwaga na wysokie napięcie !]</i></td></tr>";
     else html += "<b class='status-inactive'>[Wyłączone]</b></td></tr>";
     html += "<tr><td colspan='2' style='text-align: center;'>";
    
     if (BlokadaPrzycisku == 0) html += " <button onclick=\"window.location.href='ustaw?blokada=tak'\">Zablokuj</button> ";
     else html += " <button onclick=\"window.location.href='ustaw?blokada=nie'\">Odblokuj</button> ";
     
     if (StanGPIO15 == HIGH) html += " <button onclick=\"window.location.href='ustaw?zasilanie=0'\">Wyłącz</button> ";
     else html += " <button onclick=\"window.location.href='ustaw?zasilanie=1'\">Włącz</button> ";
     
     html += " <button onclick=\"window.location.href='ustaw?wyzeruj=tak'\">Reset kWh</button> ";
     html += " <button onclick=\"window.location.href='ustaw?restart=tak'\">Restart</button> ";
     html += " <button onclick=\"window.location.href='update'\">Aktualizacja</button> ";
     html += "</td></tr>";
     html += "</table>";
     html += "<br><br><br><i>Prawa autorskie - Michał Jabłoński (www.k.j.pl) - Jabłoński KOMPUTERY</i></center></body></html>";
    
     server.send(200, "text/html", html);
    }
    
    // Funkcja do obsługi żądań GET na ścieżce "/ustaw"
    void handleUstaw() {
      if (server.hasArg("zasilanie")) {
        String zasilanie = server.arg("zasilanie");
        if (zasilanie == "0") {
          digitalWrite(15, LOW);  // Wyłącz przekaźnik
          WyslijStanPrzekaznika();
        } else if (zasilanie == "1") {
          bezpiecznik = 0;
          BledneVolty = 0;
          digitalWrite(15, HIGH);  // Włącz przekaźnik
          WyslijStanPrzekaznika();
        }
        server.sendHeader("Location", "/", true);
        server.send(302, "text/plain", "");  // 302 Moved Temporarily, przekierowanie
        return;
      }
    
      if (server.hasArg("blokada")) {
        String bl = server.arg("blokada");
        if (bl == "nie") {
          BlokadaPrzycisku = 0;
        } else if (bl == "tak") {
          BlokadaPrzycisku = 1;
        }
        server.sendHeader("Location", "/", true);
        server.send(302, "text/plain", "");  // 302 Moved Temporarily, przekierowanie
        return;
      }
    
      if (server.hasArg("wyzeruj")) {
        kWh = 0.000;
        kwhMEM = kWh;
        ZapisStanuLicznika();
        String html = "<html><head><meta http-equiv=\"refresh\" content=\"5; url=/\"></head><body>TRWA WYZEROWANIE DANYCH kWh ...</body></html>";
        server.send(200, "text/html", html);
        return;
      } else if (server.hasArg("kwh")) {
        String kwhValue = server.arg("kwh");
        kWh = kwhValue.toFloat();
        kwhMEM = kWh;
        ZapisStanuLicznika();
        String html = "<html><head><meta http-equiv=\"refresh\" content=\"5; url=/\"></head><body>TRWA ZAPIS DANYCH NA " + String(kwhMEM) + " kWh ...</body></html>";
        server.send(200, "text/html", html);
        return;
      }
    
      if (server.hasArg("restart")) {
        String restartValue = server.arg("restart");
        if (restartValue == "tak") {
          String html = "<html><head><meta http-equiv=\"refresh\" content=\"10; url=/\"></head><body>TRWA RESTART ...</body></html>";
          server.send(200, "text/html", html);
          delay(250);
          ESP.restart();
        }
      }
    
      server.sendHeader("Location", "/", true);
      server.send(302, "text/plain", "");  // 302 Moved Temporarily, przekierowanie
    }
    
    
    void Polacz_WiFi() {
      if (WiFi.status() != WL_CONNECTED) {
       Serial.println("Laczenie z WiFi");
    
       WiFi.setOutputPower(20.5);
       WiFi.begin(ssid, pass);
       WiFi.hostname(deviceName);
      
       while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        digitalWrite(14, HIGH);
        delay(100);
        digitalWrite(14, LOW);
        delay(100);
       }
       
       Serial.println("");
       Serial.println("Przydzielono adres IP: ");
       Serial.print(WiFi.localIP());
       Serial.println("");
       digitalWrite(14, HIGH);
      }
    }
    
    
    // Wysyłanie danych do Domoticz
    void domoticz(int idx, String nvalue, String svalue) {
      Serial.println(" - Wysylam dane do domoticz...");
      nvalue.replace(" ", "");
      svalue.replace(" ", "");
      HTTPClient http;
      String adres = "http://" + DOMOTICZ_SERVER_IP + "/json.htm?type=command&param=udevice&idx=" + String(idx) + "&nvalue=" + nvalue + "&svalue=" + svalue;
      Serial.print("     URL : "); Serial.println(adres);
      http.begin(client, adres);
      http.addHeader("Content-Type", "application/html");
      int httpCode = http.GET();
      http.end();
      Serial.println("    Dane do Domoticz zostaly wyslane.");
    }
    
    // Wysyłanie aktualnego stanu pzekaźnika do Domoticz
    void WyslijStanPrzekaznika() {
     int StanLed = digitalRead(14);
     int StanPrzekaznika = digitalRead(15);
    
     if (StanLed == HIGH and StanPrzekaznika == HIGH) digitalWrite(14, LOW);
     else if (StanLed == LOW and StanPrzekaznika == LOW) digitalWrite(14, HIGH);
     
     if (IDXprzekaznik > 0) { 
      if (StanPrzekaznikDomoticz != StanPrzekaznika) {
       StanPrzekaznikDomoticz = StanPrzekaznika;
       domoticz(IDXprzekaznik, String(StanPrzekaznika), "");
      }
     }
    }
    
    
    void setup() {
     Serial.begin(115200);
    
     pinMode(0, INPUT_PULLUP); // Przycisk (LOW = Wciśnięty)
     pinMode(4, INPUT_PULLUP); // CF
     pinMode(5, INPUT_PULLUP); // CF1
     pinMode(12, OUTPUT); // SELi  [ LOW = Ampery | HIGH = Volty ]
     pinMode(14, OUTPUT); // LED niebieska [Invert - LOW to świeci]
     pinMode(15, OUTPUT); // Przekaźnik
    
     digitalWrite(12, HIGH);
     digitalWrite(14, LOW);
     if (StartStan == 1) digitalWrite(15, HIGH);
     else digitalWrite(15, LOW);
    
      delay(1000);
    
     // Odczyt z EEPROM
     String EEPROMtxt = EEPROM_ODCZYT(0, 64);
     if (getValue(EEPROMtxt, ':', 0).toInt() > 0) kWh = getValue(EEPROMtxt, ';', 0).toFloat(); // Odczyt stanu całkowitego kWh
    
     Polacz_WiFi();
     delay(250);
    
     httpUpdater.setup(&server);
     server.on("/", www);
     server.on("/ustaw", HTTP_GET, handleUstaw);
     server.begin();
    
     attachInterrupt(digitalPinToInterrupt(4), onPulseCF, RISING);  // CF
     attachInterrupt(digitalPinToInterrupt(5), onPulseCF1, RISING); // CF1
     t.every(1000, miernik);
    
     WyslijStanPrzekaznika();
     
     t.every(5, SprawdzPrzycisk);
     t.every(250, MiganieLED);
    }
    
    
    void loop() {
     if (WiFi.status() == WL_CONNECTED) server.handleClient();
     else if (WiFi.status() != WL_CONNECTED) Polacz_WiFi();
     
     t.update();
    }
    
    
    void MiganieLED() {
     if (bezpiecznik == 1 or BledneVolty > 0 or UszkodzonyPrzekaznik == 1) digitalWrite(14, !digitalRead(14));
     else if (digitalRead(14) == LOW and digitalRead(15) == HIGH) digitalWrite(14, HIGH);
    }
    
    .

    All the code looks like this and works. There is already button support and sending to Domoticz works.

    To control the relay in Domoticz we have the url http://192.168.x.x/ustaw?zasilanie=0/1 (1 = on, 0 = off).

    It is also possible to restart it under the url http://192.168.x.x/ustaw?restart=tak and we can change the kWh status under the url http://192.168.x.x/ustaw?kwh=1.123.

    You can update the software at http://192.168.x.x/update and upload the compiled *.bin file there.

    This works fairly stably and the measurements seem quite accurate. Apparently there are poor quality 3.3v power supplies in these plugs, and there is something in that, because after removing the cube from the power supply you can feel that the "pins" are slightly warm. However, there seems to be no way to save some current to "unload" the stabilisers. :)
  • #45 21297547
    tun0
    Level 6  
    I guess I messed up the CB2S while removing it. Using my latest USB-TTL adapter I can flash an ESP02S just fine. Flashed Tasmota on it and placed it in place of the CB2S. Took a bit of fiddling around with the configuration, but I managed to make it work, kinda. The voltage showed 298V though, which is a tad high obviously. Used VoltageSet to bring it down to 228 (based on P1 data). Have yet to do proper measurements (and calibration, if needed) to see if the rest of the numbers are okay or not.
  • #46 21512298
    kantora98
    Level 4  
    I have several smart plugs with energy meter that have the same PIN configuration as the plug in the topic, but a bit different designation RMC005. CB2S board has more recent (as I understand it) version number.
    Close-up of a circuit board with visible electronic components, markings, and traces.CB2S board with BK7231N chip and other electronic components.Close-up of a circuit board with markings RX2, TX2, and CSN.
    I flashed one of them with no problem, but had to de-solder CB2S board first. For some reason I could not make flashing work when I soldered wires in place.

    In any case, my question is this:
    In Tuya (or SmartLife) App, this plug has a few more functions like:
    - Clear power data
    - Relay status (On/Off/Remember-Last-Status)
    - Light Mode (ON/OFF/Depending-On-ON-OFF-Status)
    - Overcharge Switch (turn off in power is less than 3W for 40 minutes)
    - Child Lock

    Has anyone converted these Tuya options to OpenBK configurations?
  • ADVERTISEMENT
  • #47 21512528
    p.kaczmarek2
    Moderator Smart Home
    Clear power data - we have a command for that:
    https://github.com/openshwprojects/OpenBK7231T_App/blob/main/docs/commands.md
    Relay status - we have gui (startup menu) and command for that
    Light mode - set AlwaysHigh, or AlwaysLow, or LED or LED_n or WiFiLED
    Overcharge Switch - could be scripted, scripting samples are here, including the "trigger event when energy exceeds" https://github.com/openshwprojects/OpenBK7231T_App/blob/main/docs/autoexecExamples.md
    If you need a working script, tell me, I can try to prepare one later.
    Child Lock - I think we have flag for that https://github.com/openshwprojects/OpenBK7231T_App/blob/main/docs/flags.md
    Helpful post? Buy me a coffee.
  • #48 21514086
    kantora98
    Level 4  
    All in all, it is not just about features/commands but how to expose them to Home Assistant in a form of buttons/sliders/etc. Can you point me to explanation(s)? I'm very very new to all these things. And, I guess, I'm not the first one with such questions.

    p.kaczmarek2 wrote:

    I saw it in WEB APP, but am looking into a way to trigger it from HA.

    p.kaczmarek2 wrote:
    Relay status - we have gui (startup menu) and command for that

    Are you talking about this menu?
    Pin start value setting interface with channels for saving.


    p.kaczmarek2 wrote:
    Light mode - set AlwaysHigh, or AlwaysLow, or LED or LED_n or WiFiLED

    Can you point me to a link where I can read about these? (Found the doc about it)

    p.kaczmarek2 wrote:
    Overcharge Switch - could be scripted, scripting samples are here, including the "trigger event when energy exceeds" https://github.com/openshwprojects/OpenBK7231T_App/blob/main/docs/autoexecExamples.md
    If you need a working script, tell me, I can try to prepare one later.

    Such script might be usable not only for me. When I plug the plug into socket (with metering capabilities) the plug initializes connection but stays OFF (by default), and meter shows that the plug consumes around 0.2Wh in "wifi-connected" state, but once I press the On/Off switch the metering socket starts to show 1.1-1.2Wh. So, I guess, people which are used to save every bit of electricity might benefit from such script.

    p.kaczmarek2 wrote:

    From the description I understood that once flag set it will ignore any button presses, however in original Tuya firmware you had to hold the button pressed for 4 seconds to unlock the child lock. How does current implementation work?

    Thanks for your time.

Topic summary

The discussion centers on the RMC021 WiFi smart plug featuring a BK7231N/CB2S chip and a BL0937 energy measurement IC. Users shared experiences with disassembling the device, desoldering the CB2S module to enable flashing with OpenBK firmware using BK7231Flasher on Linux Mint, and challenges related to RX/TX line interference and USB-to-UART adapter compatibility. Several contributors provided detailed pin configurations for BL0937 integration, flashing procedures, and JSON templates for device inclusion in OpenBekenIOT device lists. Alternative firmware approaches include ESPHome and Tasmota on ESP8266 modules replacing the CB2S, with calibration challenges for voltage and current measurement pulses from BL0937. Advanced users developed custom Arduino IDE code for pulse counting via interrupts to improve measurement accuracy. Additional features from the original Tuya firmware such as power data clearing, relay status modes, light modes, overcharge protection, and child lock were discussed with references to OpenBK commands, scripting, and flags to replicate these functions. The conversation also covered hardware repair advice for damaged relays and desoldering techniques, as well as suggestions for integrating the smart plug with home automation platforms like Home Assistant and Domoticz.
Summary generated by the language model.
ADVERTISEMENT