Xiaomi LYWSD03MMC + HM10 + ESP8266 (WEMOS)

andrew01
Posty: 119
Rejestracja: pt maja 24, 2019 6:49 am

śr paź 28, 2020 11:46 pm

Cześć,

chciałbym pokazać podłączenie kwadratowych czujników temperatury i wilgotności Xiaomi LYWSD03MMC na Wemos'ie.

Do komunikacji użyłem klona HM10, czyli modułu AT09:

https://pl.aliexpress.com/item/32851755 ... 5c0fEm7xUh

Przy pomocy poniższego tutorial'a wgrałem oryginalny firmware HM10:

https://circuitdigest.com/microcontroll ... rduino-uno

Następnie wgrałem najnowszy soft wg opisu do HM10

http://www.jnhuamao.cn/rom/HMSoft-10-2541-V709.zip

W nowszych firmware'ach dodali komendę AT+DISA? i to właśnie za jej pomocą są ściągane dane z czujników.

Kwadratowe czujniki mają to do siebie, że nie przesyłają tak wprost danych, a do tego są one zakodowane.

Powyższe problemy całkowicie rozwiązuje customowy firmware:

https://github.com/atc1441/ATC_MiThermometer

Dane są wysyłane raz na minutę, ale można to zmienić zgodnie z opisem na powyższej stronie.

Do identyfikacji czujników (ich adresów) przydatna jest też apka na telefon - nRF Connect.

https://play.google.com/store/apps/deta ... ndroid.mcp

Do efektu końcowego potrzebny jest jeszcze plik ino (poniżej) dla Wemos'a lub jakiegokolwiek innego ESP, jest w zasadzie jeszcze ciepły, gdyż działa to u mnie od niedawna, więc nie wykluczam, że znajdą się jakieś błędy. Odczyt danych jest co 10 sekund, można to oczywiście wydłużyć. Jeśli przez 5 kolejnych prób nie otrzymamy danych to takiemu czujnikowi zostaną wyłączone wskazania.

Problemem jest niewielki zasięg modułu AT09, w domu w obrębie pokoju łapało ale z piętra już nie.
Na razie podłączyłem zewnętrzną antenę tak w zasadzie na czuja, bo maleństwo jest niemiłosierne.
Zasięg poprawił się znacznie ale docelowo będę szukał modułu BLE z gniazdem na antenę zewnętrzną.

Pozdrawiam
Andrzej

Kod: Zaznacz cały

#include <SoftwareSerial.h>
#include <SuplaDevice.h>
#include <supla/network/esp_wifi.h>
#include <supla/sensor/therm_hygro_meter.h>

#define MODULE_NAME "Xiaomi Mijia" //Nazwa modułu
#define TEMPERATURE_NOT_AVAILABLE -275
#define STASSID "...."
#define STAPSK  "...."

Supla::ESPWifi wifi(STASSID, STAPSK);

bool debug = false;

class myThermHygroMeter : public Supla::Sensor::ThermHygroMeter
{
public:
  myThermHygroMeter(double *temp, double *humi)
  {
    myTemp = temp;
    myHumi = humi;
    lastReadTime = 0;
  }
  void onInit()
  {
    channel.setNewValue(getTemp(), getHumi());
  }

  double getTemp()
  {
    return *myTemp;
  }
  double getHumi()
  {
    return *myHumi;
  }
  double *myTemp;
  double *myHumi;
};

//###### MIJIA #######

//podłaczenie modułu HM-10
SoftwareSerial hm10(D5, D6); // RX, TX 

//liczba czujników
const byte sensorCount = 2;

//adresy czujników, na końcu niezbędny NULL
char addr[sensorCount][7] = {
     {0xA4,0xC1,0x38,0x0E,0x36,0x8F,0x00}, //#1
     {0xA4,0xC1,0x38,0x66,0x23,0x63,0x00}  //#2
     };

//czas pomiędzy odczytami
int readInterval = 10000;

//maksymalna liczba dozwolonych pominięć odczytu
byte maxSkippedReading = 5; 

class Mijia
{
private:
  char mijiaData[100];
  byte mijiaIndex;
  unsigned long lastReadTime;
  unsigned long lastReceiveTime; 
  char receivedAddr[6];
  
public:

  double temp[sensorCount];
  double humi[sensorCount];
  double batt[sensorCount];
  double volt[sensorCount];
  byte skippedReading[sensorCount];

  void clearValues(int i)
  {
    Serial.print(F("*** MIJIA CLEAR VALUES "));
    Serial.println(i);
    temp[i] = TEMPERATURE_NOT_AVAILABLE;
    humi[i] = TEMPERATURE_NOT_AVAILABLE;
    batt[i] = TEMPERATURE_NOT_AVAILABLE;
    volt[i] = TEMPERATURE_NOT_AVAILABLE;
  }

  void clearAllValues()
  {
     for (int i = 0; i < sensorCount; i++)
     {
        clearValues(i);
     }    
  }

  void Initialize()
  {
    Serial.println("AT");
    hm10.write("AT");
    delay(100);
    Serial.println("AT+ROLE1");
    hm10.write("AT+ROLE1");
    delay(100);
    Serial.println("AT+IMME1");
    hm10.write("AT+IMME1");
    delay(100);
    Serial.println("AT+RESET");
    hm10.write("AT+RESET");
    delay(50);    
    clearAllValues();
  }

  void Iterate()
  {
    if (hm10.available() > 0) 
    {
      mijiaData[mijiaIndex] = hm10.read();
      mijiaIndex++;
      lastReceiveTime = millis();
    }

    //paczkę uznajemy za odczytaną gdy minął zadany czas np. 200ms
    //lub kończy się na 0D 0A (nowa linia)
    if ((millis() - lastReceiveTime > 200 and mijiaIndex > 0) or
        (mijiaIndex > 10 and 
          (int)mijiaData[mijiaIndex - 1] == 0x0A and
          (int)mijiaData[mijiaIndex - 2] == 0x0D))
    {
      if (debug)
      {
        Serial.println("ODCZYTANO PACZKĘ DANYCH");
        Serial.print("HEX: ");
        for (int i = 0; i < mijiaIndex; i++)
        {
          Serial.print(mijiaData[i],HEX);
          Serial.print(" ");
        }
        Serial.println();
    
        Serial.print("DEC: ");
        for (int i = 0; i < mijiaIndex; i++)
        {
          Serial.print(mijiaData[i],DEC);
          Serial.print(" ");
        }
        Serial.println();        
      }
      //paczka z danymi
      if (mijiaIndex >= 36 and
         strncmp(mijiaData,"OK+DISA:",8)  == 0 and
         (int)mijiaData[19] == 0x1A and //UUID 181A
         (int)mijiaData[20] == 0x18)
      {
        if (debug) Serial.println("POPRAWNA PACZKA DANYCH");
        
        //wyodrębnienia adresu urządzenia
        for (int i = 21; i < 27; i++) receivedAddr[i - 21] = mijiaData[i];
       
        //sprawdzenie czy adres jest znany
        for (int i = 0; i < sensorCount; i++)
        {
          if (strcmp(receivedAddr,addr[i]) == 0)
          {
            //zerowanie wskaźnika pominięć
            skippedReading[i] = 0;
            
            Serial.print("ZNANY CZUJNIK:");
            for (int j = 0; j < 6; j++)
            {
              Serial.print(" ");
              Serial.printf("%02hhX", receivedAddr[j]);
            }
            Serial.println();
            
            temp[i] = ((double)mijiaData[27] * 256 + (double)mijiaData[28]) / 10;
            humi[i] = mijiaData[29];
            batt[i] = mijiaData[30];
            volt[i] = ((double)mijiaData[31] * 256 + (double)mijiaData[32]) / 1000;            

            Serial.print("Temp: ");
            Serial.println(temp[i]);
            Serial.print("Humi: ");
            Serial.println(humi[i]);
            Serial.print("Batt: ");
            Serial.println(batt[i]);
            Serial.print("Volt: ");
            Serial.println(volt[i]);
          }
        }
      }

      mijiaIndex = 0;
      memset(mijiaData, 0, sizeof(mijiaData));      
    }

    //odczyt danych
    if (hm10.available() == 0 and millis() - lastReadTime > readInterval)
    {
      lastReadTime = millis();
      lastReceiveTime = millis();
      //licznik pominiętych odczytów
      for (int i = 0; i < sensorCount; i++)
      {
        skippedReading[i]++; 
        if (skippedReading[i] > maxSkippedReading) clearValues(i);
      }
      mijiaIndex = 0;
      memset(mijiaData, 0, sizeof(mijiaData));
      hm10.write("AT+DISA?");
    }
  }
};

Mijia myMijia;

int suplaStatus;
void status_func(int status, const char *msg)
{
  suplaStatus = status;
  Serial.println(msg);
}

void setup() { 
  
  Serial.begin(9600);
  while (!Serial)
  {
  }  
  delay(1000);
  Serial.println("SERIAL STARTED");
  
  hm10.begin(9600);
  while (!hm10)
  {
  }
  delay(1000);
  Serial.println("HM10 SERIAL STARTED");
      
  myMijia.Initialize();
  
  //#1
  new myThermHygroMeter(&myMijia.temp[0], &myMijia.humi[0]);
  new myThermHygroMeter(&myMijia.volt[0], &myMijia.batt[0]);
  
  //#2
  new myThermHygroMeter(&myMijia.temp[1], &myMijia.humi[1]);
  new myThermHygroMeter(&myMijia.volt[1], &myMijia.batt[1]);

  SuplaDevice.setStatusFuncImpl(&status_func);
  
  // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-guid
  char GUID[SUPLA_GUID_SIZE] = {...};
  // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey
  char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {...};

  SuplaDevice.setName(MODULE_NAME);
  SuplaDevice.begin(GUID,         // Global Unique Identifier
                    "....",       // SUPLA server address
                    "....",       // Email address used to login to Supla Cloud
                    AUTHKEY);     // Authorization key         // Location Password  
}

void loop() { 

  SuplaDevice.iterate();

  if (suplaStatus == 17)
    myMijia.Iterate();

}

Załączniki
mijia.xlsx
(12.03 KiB) Pobrany 26 razy
20201029_000923.jpg
20201029_000923.jpg (3.52 MiB) Przejrzano 730 razy
Screenshot_20201029-001013_SUPLA.jpg
Screenshot_20201029-001013_SUPLA.jpg (275.48 KiB) Przejrzano 730 razy
Ostatnio zmieniony czw paź 29, 2020 11:24 am przez andrew01, łącznie zmieniany 1 raz.
Awatar użytkownika
Robert Błaszczak
Posty: 1470
Rejestracja: sob gru 22, 2018 8:55 pm
Lokalizacja: Zielona Góra
Kontaktowanie:

czw paź 29, 2020 5:34 am

Gratulacje :) .
Pozdrawiam
Robert Błaszczak

Strona prywatna: https://www.blaszczak.pl
Jakość powietrza: https://robert.aqi.eco
andrew01
Posty: 119
Rejestracja: pt maja 24, 2019 6:49 am

czw paź 29, 2020 11:27 am

Gratulacje należą się autorowi custom'owego firmware'u, jak to tylko poskładałem :)
Przy okazji dodałem plik mijia.xlsx z rozpiską co oznaczają poszczególne (no może nie wszystkie) bajty w odpowiedzi na "wywołanie".

Pozdrawiam
Andrzej
dogu18
Posty: 113
Rejestracja: czw paź 11, 2018 8:35 pm

czw paź 29, 2020 10:42 pm

Wydaje się to fajne rozwiązanie dla kogoś kto nie posada Malinki.
I chyba jest tańsze.
Gratuluję
Zibi
Posty: 265
Rejestracja: śr lip 31, 2019 9:20 am
Lokalizacja: Białogard

pt paź 30, 2020 9:04 am

Również mnie to bardzo zainteresowalo, ponieważ z maliną nie mialem styczności jeszcze nie wiem jak ją się obsługuję, a wemosow w domu pod dostatkiem.
Nawet mam dwa takie moduły HM10 BLE. Pamiętam że rok temu bawiłem się tymi modułami HM10 i przy pomocy tego tutoriala wygrywałem firmware zasięg na otwartej mialem do około 50m. Od tamtej pory leżą w kartonie i chyba będzie okazja zrobić z nich pożytek :mrgreen:
andrew01 jak to u ciebie funkcjonuje? Bo chyba będę zamawiał te Xiaomi jeśli jest taka opcja zrobić to na wemosie.
Awatar użytkownika
artur_n
Posty: 124
Rejestracja: czw sie 17, 2017 2:24 pm

pt paź 30, 2020 10:38 am

Mam 4 czujniki które bezczynnie stoją. Czas się tym zająć. Jaki moduł Bluetooth kupić aby był dobry zasięg oraz był kompatybilny??
Mogę prosić o jakiś link?

Pozdrawia
Artur
George2002
Posty: 141
Rejestracja: pn maja 01, 2017 2:00 am

pt paź 30, 2020 10:52 am

artur_n pisze:
pt paź 30, 2020 10:38 am
Mam 4 czujniki które bezczynnie stoją. Czas się tym zająć. Jaki moduł Bluetooth kupić aby był dobry zasięg oraz był kompatybilny??
Mogę prosić o jakiś link?

Pozdrawia
Artur
Najlepiej aby ktoś to przeportował na esp32 bo ma w sobie Bluetooth :)
Awatar użytkownika
klew
Posty: 1558
Rejestracja: czw cze 27, 2019 12:16 pm

pt paź 30, 2020 11:55 am

George2002 pisze:
pt paź 30, 2020 10:52 am
artur_n pisze:
pt paź 30, 2020 10:38 am
Mam 4 czujniki które bezczynnie stoją. Czas się tym zająć. Jaki moduł Bluetooth kupić aby był dobry zasięg oraz był kompatybilny??
Mogę prosić o jakiś link?

Pozdrawia
Artur
Najlepiej aby ktoś to przeportował na esp32 bo ma w sobie Bluetooth :)
Podchodziłbym do BLE na ESP32 dość ostrożnie. ESP32 ma jedno fizyczne radio, które może być w danym momencie używane wyłącznie przez WiFi lub przez Bluetooth. Ludzie w sieci często mają problemy z uruchomieniem obu na raz, ale niekrórym się to udało.
Awatar użytkownika
artur_n
Posty: 124
Rejestracja: czw sie 17, 2017 2:24 pm

pt paź 30, 2020 11:59 am

Czyli lepiej MH10 czy klona AT09? Czy jeszcze coś innego?
andrew01
Posty: 119
Rejestracja: pt maja 24, 2019 6:49 am

pt paź 30, 2020 1:46 pm

Zibi pisze:
pt paź 30, 2020 9:04 am
andrew01 jak to u ciebie funkcjonuje? Bo chyba będę zamawiał te Xiaomi jeśli jest taka opcja zrobić to na wemosie.
U mnie na razie dwa czujniki, 4 dodatkowe już zamówione :)

Zamówiłem też poniższy moduł aby takiego druciarstwa nie było z podłączeniem anteny zewnętrznej:

https://www.aliexpress.com/item/1005001 ... 4c4dltt5ok

Pozdrawiam
Andrzej
ODPOWIEDZ

Wróć do „Ogólna dyskusja”