Reflash Elivco LSPA9 smart plug with libretuya-esphome to use it with Homeassistant

Elivco smart plug LSPA9

By default this is tuya device and requires cloud servers to work. The goal is to reflash it with free firmware and use it with local homeassistsant instance.

  • aliexpress link: https://www.aliexpress.com/item/4000478798085.htm
  • built on CB2S board using BL2028N which is the same as BK7231N
  • BL0937 power meter, two LEDs, relay, button

Tools needed

  • USB to 3.3V UART adapter / cable
  • 5V power supply (usb power is OK)
  • soldering iron
  • some wires
  • libretuya-esphome - the GUI (docker) version
  • ltchiptool - CLI version

Flash

  • disassemble the plug (more info TBD)
  • solder wires to +5V, GND, RX, TX pads on the LSPA9 board and connect them to the UART adapter and 5V power supply (more info TBD)
  • run libretuya-ephome (docker-compose up) and open its dashboard in your browser (localhost:6052)
  • in the dashboard new device, name it, select device type "LibreTuya", "CB2S WiFi module", click "install", "manual download", "modern format"
  • if you are using the dashboard for the first fime you may need to enter WiFi credentials also
  • wait until it compiles and save the firmware (uf2) file
  • you can try to flash directly from dashboard over serial, but that never worked for me (even when I added the serial port into docker config and it was visible from the container)
  • flash the u2f file into the CB2S using ltchiptool
  • you need to disconnect +5V from the plug
  • run ltchiptool flash write .uf2 and connect +5V back
  • it may take few tries to make it start
  • wait until finished (about 2 minutes)
  • for me it always ended up with error about CRC timout at 99%, but it worked, ignore such error
  • disconnect the +5V and reconnect again (to reset the device)
  • go to the dashboard and wait until it shows online, then try to open its logs - if that works CONGRATS - from we can continue flashing wirelessly

Really don't use this when:

  • any kind of personal or sensitive data are involved / transfered - like passwords, keys, personal messages
  • faked value / information may cause damage - like turning on all heaters in your house during your 2 weeks long summer vacation because somebody faked the API you are using to control them, or bot which sells all your bitcoins by "mistake" because of faked exchange rate received

How?

Very easily, here is the original Arduino BasicHttpsClient example (with fingerprint):

/**
   BasicHTTPSClient.ino

    Created on: 20.08.2018

*/

#include <Arduino.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

#include <ESP8266HTTPClient.h>

#include <WiFiClientSecureBearSSL.h>
// Fingerprint for demo URL, expires on June 2, 2019, needs to be updated well before this date
const uint8_t fingerprint[20] = {0x5A, 0xCF, 0xFE, 0xF0, 0xF1, 0xA6, 0xF4, 0x5F, 0xD2, 0x11, 0x11, 0xC6, 0x1D, 0x2F, 0x0E, 0xBC, 0x39, 0x8D, 0x50, 0xE0};

ESP8266WiFiMulti WiFiMulti;

void setup() {

  Serial.begin(115200);
  // Serial.setDebugOutput(true);

  Serial.println();
  Serial.println();
  Serial.println();

  for (uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }

  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP("SSID", "PASSWORD");
}

void loop() {
  // wait for WiFi connection
  if ((WiFiMulti.run() == WL_CONNECTED)) {

    std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);

    client->setFingerprint(fingerprint);

    HTTPClient https;

    Serial.print("[HTTPS] begin...\n");
    if (https.begin(*client, "https://jigsaw.w3.org/HTTP/connection.html")) {  // HTTPS

      Serial.print("[HTTPS] GET...\n");
      // start connection and send HTTP header
      int httpCode = https.GET();

      // httpCode will be negative on error
      if (httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);

        // file found at server
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = https.getString();
          Serial.println(payload);
        }
      } else {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      }

      https.end();
    } else {
      Serial.printf("[HTTPS] Unable to connect\n");
    }
  }

  Serial.println("Wait 10s before next round...");
  delay(10000);
}

Just replace "client->setFingerprint(fingerprint);" with "client->setInsecure();", you can also comment out the fingerprint constant and thats all!

Its a pitty that there is no official example showing this...

Full example:

/**
   BasicHTTPSClientNofingerprint.ino

   1.03.2019

*/

#include <Arduino.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

#include <ESP8266HTTPClient.h>

#include <WiFiClientSecureBearSSL.h>
// Fingerprint for demo URL, expires on June 2, 2019, needs to be updated well before this date
// const uint8_t fingerprint[20] = {0x5A, 0xCF, 0xFE, 0xF0, 0xF1, 0xA6, 0xF4, 0x5F, 0xD2, 0x11, 0x11, 0xC6, 0x1D, 0x2F, 0x0E, 0xBC, 0x39, 0x8D, 0x50, 0xE0};

ESP8266WiFiMulti WiFiMulti;

void setup() {

  Serial.begin(115200);
  // Serial.setDebugOutput(true);

  Serial.println();
  Serial.println();
  Serial.println();

  for (uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }

  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP("SSID", "PASSWORD");
}

void loop() {
  // wait for WiFi connection
  if ((WiFiMulti.run() == WL_CONNECTED)) {

    std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);

    //client->setFingerprint(fingerprint);
    client->setInsecure();

    HTTPClient https;

    Serial.print("[HTTPS] begin...\n");
    if (https.begin(*client, "https://jigsaw.w3.org/HTTP/connection.html")) {  // HTTPS

      Serial.print("[HTTPS] GET...\n");
      // start connection and send HTTP header
      int httpCode = https.GET();

      // httpCode will be negative on error
      if (httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);

        // file found at server
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = https.getString();
          Serial.println(payload);
        }
      } else {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      }

      https.end();
    } else {
      Serial.printf("[HTTPS] Unable to connect\n");
    }
  }

  Serial.println("Wait 10s before next round...");
  delay(10000);
}

Better example - how to get bitcoin exchange rate from blockchain.info API

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>

void setup() {
  Serial.begin(115200);
  Serial.println(F("\n\r* * * ESP BOOT * * *"));
  Serial.println(F("WiFi begin!"));
  WiFi.mode(WIFI_STA);
  WiFi.begin("SSID", "PASSWORD");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println(F("\n\rWiFi connected!"));
}

void getpr24h() {
  std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
  client->setInsecure();
  HTTPClient https;

  if (https.begin(*client, "https://blockchain.info/q/24hrprice")) {  // HTTPS
    Serial.println("[HTTPS] GET...");
    int httpCode = https.GET();

    // httpCode will be negative on error
    if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
      // file found at server?
      if (httpCode == HTTP_CODE_OK) {
        String payload = https.getString();
        Serial.println(String("[HTTPS] Received payload: ") + payload);
        Serial.println(String("1BTC = ") + payload + "USD");
      }
    } else {
      Serial.printf("[HTTPS] GET... failed, error: %s\n\r", https.errorToString(httpCode).c_str());
    }

    https.end();
  } else {
    Serial.printf("[HTTPS] Unable to connect\n\r");
  }
}

void loop() {
  getpr24h();
  Serial.println("Wait 20s before next round to not get banned on API server...");
  delay(20000);
}

serial output

Tested using:

  • Arduino 1.8.7
  • Arduino core for ESP8266 WiFi chip 2.5.0
  • Ubuntu Mate 16.04
  • NodeMCU ESP8266 board

Links

Like my blog? Want to buy me coffee or beer?

LTC (litecoin): LeWzkcV2ArRv7Bi7TmrTpwkp6j2CZSLwfY

BTC (bitcoin): 1LzmUcwHK5Ys4zGPRoxYodjzpJsWiG61JY

DOGE (dogecoin): DQmS6EdDXssriDgSBpQMxYicHTiji6kMhx

ETH (ethereum): 0x387ff39c66e71c454ce5844c188c1a87835d2263

USDT (tether@ETH): 0xa69cae5a1da5ff5fb226e4bc87fe5d0f8c45908a

MANA (decentraland): 0xa69cae5a1da5ff5fb226e4bc87fe5d0f8c45908a

XMR (monero): 4JUdGzvrMFDWrUUwY3toJATSeNwjn54LkCnKBPRzDuhzi5vSepHfUckJNxRL2gjkNrSqtCoRUrEDAgRwsQvVCjZbRxBb8sEWJB1SCCuUEa