Tworzenie systemu Smart Home przy pomocy płytki Micromis Base V1

Systemy smart home znacznie poprawiły jakość naszego życia. Dzięki nim nasze wnętrza stały się bardziej wygodne, komfortowe i bezpieczne. W tym poradniku zaprezentujemy, jak w prosty sposób można stworzyć taki system samemu. Projekt będzie skupiał się na kontrolowaniu oświetlenia, oraz monitorowania temperatury za pomocą wiadomości SMS.

Podzespoły

Micromis Base V1
x1
Micromis Power Shield
x1
Antena GSM ze złączem U.FL
x1
Karta SIM w formacie nanoSIM
x1

Opis projektu

W niniejszy poradniku stworzymy oprogramowanie dla systemu smart home, opartego o płytkę Micromis Base V1 oraz jej rozszerzenie Micromis Power Shield.

Projekt będzie mieć na celu kontrolowanie urządzeń poprzez wysyłanie i odbieranie wiadomości SMS dzięki wbudowanemu modemowi Quectel M65. Do komunikacji z modemem użyjemy komend AT, dzięki czemu w łatwy sposób będziemy mogli obsługiwać polecenia takie jak łączenie się z siecią GSM, wysyłanie i otrzymywanie wiadomości SMS

System smart home, który za chwilę stworzymy, będzie w stanie włączać i wyłączać dwa rodzaje oświetlenia oraz odczytywać wartość temperatury ze wbudowanego czujnika LM75 i wysyłać ją w wiadomości zwrotnej.

Wykonanie poradnika wymaga posiadania płytek Micromis Base V1, Micromis Power Shield, anteny GSM ze złączem U.FL oraz aktywnej karty SIM.

Zadania w projekcie

Krok 1 - Konfiguracja sprzętowej części projektu

Pierwszym etapem rozpoczęcia prac nad projektem jest podłączenie anteny GSM do złącza U.FL na płytce Micromis Base V1 oraz wsunięcie karty nanoSIM do odpowiedniego slotu. Upewnij się, że karta SIM jest aktywna i posiada wystarczające środki na wysyłanie wiadomości SMS oraz łączenie się z internetem. Na zakończenie, połącz płytki Micromis Base V1 i Micromis Power Shield, umieszczając jedną na drugiej.

Krok 2 - Konfigurowanie środowiska Arduino IDE

Aby uruchomić program tworzony w ramach tego poradnika, konieczne jest posiadanie środowiska Arduino IDE z zainstalowanym zestawem zewnętrznych płytek ESP32. Zalecamy używanie wersji Arduino IDE nie starszej niż 2.0.0. Aby poprawnie wgrać program na płytce Micromis Base V1, wybierz płytę ESP32 Dev Module w menu “Narzędzia” > “Płytka” oraz wskaż port, do którego podłączona jest płytka Micromis Base V1.

Krok 3 - Analiza kodu

W celu wdrożenia przygotowanego oprogramowania umieść kod przedstawiony w poradniku w środowisku Arduino IDE. Dla właściwego działania kodu konieczne jest dostosowanie wartości zmiennych umieszczonych na początku. Wartości zmiennych dla numeru telefonu, na który będą wysyłane alerty, oraz adresu APN używanej sieci są niezbędne. Dodaj kod PIN do karty SIM, jeśli to konieczne. Ważne jest, aby zmieniać tylko wartości zmiennych, nie ich nazwy, w przeciwnym wypadku oprogramowanie nie będzie funkcjonować prawidłowo.

W sekcji “Kod – krok po kroku” znajdują się szczegółowe informacje dotyczące oprogramowania, w tym zastosowanie różnych funkcji i poleceń.

Krok 4 - Test

Następnie kliknij przycisk „wgraj”, żeby przetestować program! Po wgraniu programu przetestuj go wysyłając przykładowy SMS o treści “TEMPERATURE”. 

Uwaga! Jeśli podczas wgrywania programu pojawi się błąd: 

“A fatal error occurred: Failed to connect to ESP32: Wrong boot mode detected (0x13)! The chip needs to be in download mode”. 

Wystarczy przytrzymać przycisk “boot” do momentu, kiedy pokaże się wiadomość “Connecting… ” w oknie wyjściowym. To powinno rozwiązać problem.

Krok 5 - Efekt projektu

Prawidłowo wgrane i skonfigurowane oprogramowanie sprawi, że będziemy mogli włączać i wyłączyć oświetlenie oraz otrzymać informacje o aktualnej temperaturze w pomieszczeniu. Wszystko to za sprawą wiadomości SMS.

Kod – krok po kroku

Kod rozpoczyna się od konfiguracji stałych oraz zmiennych wykorzystywanych w dalszej jego części.

Kod będzie operował na otrzymywanych wiadomościach SMS, dlatego wymagane jest użycie biblioteki regex, która po zdefiniowaniu wyrażeń regularnych używa funkcji std::regex_search w celu wyszukania dopasowania w treści wiadomości. Całość wyjaśniona będzie dokładniej w dalszej części. Następnie dodana jest biblioteka Temperature_LM75_Derived.h służąca do odczytów temperatury ze wbudowanego czujnika LM75.

C++
//Include libraries
#include <regex>
#include  <Temperature_LM75_Derived.h>

W kodzie zdefiniowane są również piny RX i TX wbudowanego modemu, ze względu na trwałe połączenie interfejsu UART modemu wraz z pinami 16 i 17 układu ESP32 nie powinniśmy zmieniać wartości tych zmiennych, w przeciwnym wypadku nawiązanie komunikacji pomiędzy tymi dwoma układami będzie niemożliwe.

C++
//Defines the RX and TX pins for the cellular modem
#define RXD2 16
#define TXD2 17

Kolejny fragment kodu przypisuję wartości czterech pinów do zmiennych. Pierwsze dwa przekaźniki, relay1 i relay2, kontrolują odpowiednio Lampę 1 i Lampę 2, i mają przypisane wartości 23 i 19. Pozostałe dwa piny, power_output1 i power_output2, są pinami wyjściowymi dla paska LED 1 i paska LED 2 i są przypisane odpowiednio 18 i 12. Warto mieć na uwadze, że wartości zmiennych, nie powinny zostać zmieniane, ponieważ płytka Micromis Base V1 I Micromis Power Shield są tak zaprojektowane, aby używały tych konkretnych pinów.

C++
#define relay1 23 //Relay control pin for Lamp 1
#define relay2 19 //Relay control pin for Lamp 2
#define power_output1 18 //Output pin for LED strip 1
#define power_output2 12 //Output pin for LED strip 2

Dla osiągnięcia skutecznego połączenia z siecią komórkową oraz wysłania wiadomości SMS, istotne jest skonfigurowanie trzech zmiennych. Zmienna AUTHORIZED_NUMBER, stosowana w kodzie, ma na celu przechowywanie numeru telefonu upoważnionego do kontroli systemu za pomocą wiadomości kierowanych do urządzenia. By wiadomość została przekazana właściwie, należy sprawdzić, czy numer telefonu jest zapisany w odpowiedni sposób – w numerze musi być uwzględniony prefix kierunkowy dla określonego kraju, a zmienna powinna być jednolitym ciągiem cyfr, bez spacji czy innych znaków pomiędzy nimi. Przykładowy numer telefonu z Wielkiej Brytanii będzie prezentować się jako zmienna:

C++
String AUTHORIZED_NUMBER =+442076350000

A dla numeru telefonu z Polski:

C++
String AUTHORIZED_NUMBER =+48505505505

Aby skutecznie wykorzystać numer telefonu w zmiennej AUTHORIZED_NUMBER, warto upewnić się, czy używana w projekcie karta SIM wymaga podania kodu PIN przy połączeniu. W przypadku konieczności wprowadzenia kodu PIN, należy wpisać go do zmiennej SIM_CARD_PIN_NUMBER. Na przykład, jeśli kod PIN to “1234”, wartość zmiennej  będzie prezentować się jako:

C++
String SIM_CARD_PIN_NUMBER =1234

W przypadku, gdy karta SIM nie wymaga podania kodu PIN, zmienna SIM_CARD_PIN_NUMBER powinna pozostać pusta.

Ustawienie adresu punktu dostępu (APN) jest ostatnim krokiem w konfiguracji projektu. APN jest wykorzystywany przez daną sieć komórkową, a jego adres należy wprowadzić do zmiennej APN_ADDRESS. W wielu krajach najczęściej używanym adresem punktu dostępu jest “internet”. W tym przypadku, wartość zmiennej będzie równa:

C++
String APN_ADDRESS = “internet”

Dla przykładu, w sieci Deutsche Telekom adres APN to “internet.telekom”, więc wartość zmiennej będzie następująca:

C++
String APN_ADDRESS =internet.telekom

Możliwy jest łatwy sposób, aby sprawdzić adres APN sieci, do której należy karta SIM używana w projekcie. Wystarczy wejść na stronę: 
apn.how

Warto również zauważyć, że w kodzie projektu znajduje się również definicja pinu, który jest wymagany do uruchomienia modemu, zmienne odpowiadające za maksymalną liczbę prób połączenia oraz czas oczekiwania między alertami.

Funkcja setupModem

Funkcja setupModem odpowiada za uruchomienie modemu. W pierwszej kolejności wysyłane jest polecenie AT, aby sprawdzić, czy modem jest już uruchomiony. Następnie sprawdzana jest odpowiedź, która zawiera „OK”. Takie zdarzenie sygnalizuje, że modem działa poprawnie i można rozpocząć komunikację z urządzeniem.

C++
//Function to turn on a modem
void setupModem()
{
  //Read any available data from the modem's serial interface
  download_data = Serial2.readString(); //Reading data from modem UART

  //Check if the modem is already on or not
  Serial2.println("AT");  //Send command to check if modem is on
  download_data = Serial2.readString(); //Reading data from modem UART
  if(download_data.indexOf("OK") > -1)  //Waiting for "OK" response from modem
  {
    // Modem is already on
    Serial.println(); //Print blank line in Serial Monitor
    Serial.println(download_data); //Print data from modem UART
    Serial.println(); //Print blank line in Serial Monitor
    Serial.println("Set modem startup:"); //Printing information in Serial Monitor
    Serial.println("Modem is aleady on"); //Printing information in Serial Monitor
    Serial.println(); //Print blank line in Serial Monitor
    download_data = ""; //Clearing variable
  } 
Expand

W przypadku, gdy modem nie jest uruchomiony, funkcja setupModem jest odpowiedzialna za jego włączenie poprzez ustawienie pinu MODEM_PWRKEY w stanie wysokim na 1000 ms. Następnie, przy pomocy pinu PWR_KEY, zostaje wysłane polecenie ATE1, aby ustawić tryb echa w modemie. Ustawienie trybu echa skutkuje przesyłaniem przez modem wszystkich informacji generowanych podczas pracy na interfejsu UART.

C++
else 
  {
    //Modem was off, turn it on
    Serial.println(); //Print blank line in Serial Monitor
    Serial.println(download_data); //Print data from modem UART
    Serial.println("Set modem startup:"); //Printing information in Serial Monitor
    Serial.println("The modem has been switched on"); //Printing information in Serial Monitor
    Serial.println(); //Print blank line in Serial Monitor
    digitalWrite(MODEM_PWRKEY, HIGH); //Turning ON modem by set power on PWR_KEY pin through 1000 ms
    delay(1000); //1000 ms pause
    digitalWrite(MODEM_PWRKEY, LOW); //Turning OFF PWR_KEY pin
    Serial2.println("ATE1"); //Set echo text mode on modem
    download_data = ""; //Clearing variable
    download_data = Serial2.readString(); //Reading data from modem UART
    int initial_counter = 0; //Blank initial counter
    delay(1000); //1000 ms pause
Expand

Funkcja następnie odpowiada za odczyt danych z interfejsu UART i wyszukiwanie ciągu znaków “Call Ready“. Taki ciąg sygnalizuje, że modem został prawidłowo uruchomiony i jest gotowy do nawiązywania połączeń oraz komunikacji.

C++
    while(download_data.indexOf("Call Ready") == -1) //Waiting for "Call Ready" response from modem
    {
      Serial.println("Waiting for modem init"); //Printing information in Serial Monitor
      delay(500); //500 ms pause
      download_data = Serial2.readString(); //Reading data from modem UART
      Serial.println(download_data); //Print data from modem UART
      initial_counter++; //Increase the counter value by 1
      if(initial_counter > 10) //Trying to turn up modem again
      {
        digitalWrite(MODEM_PWRKEY, HIGH); //Turning ON modem by set power on PWR_KEY pin through 1000 ms
        delay(1000); //1000 ms pause
        digitalWrite(MODEM_PWRKEY, LOW); //Turning OFF PWR_KEY pin
        initial_counter = 0; //Blank initial counter
      }
    }
Expand

Jeśli funkcja nie otrzyma informacji o poprawnym uruchomieniu modemu w ciągu 10 prób, zostanie on ponownie uruchomiony przy pomocy pinu PWR_KEY, a licznik prób zostanie wyzerowany.

Funkcja configurationModem

Funkcja configurationModem odpowiada za konfigurację modemu poprzez wysłanie serii odpowiednich poleceń AT do modemu za pośrednictwem wcześniej skonfigurowanego interfejsu szeregowego (Serial2).Dzięki tym poleceniom modem jest w stanie działać prawidłowo i nawiązywać połączenia z siecią komórkową.

ATV1 – włącza tryb opisowy. Gdy włączony jest tryb opisowy, modem wysyła szczegółowe odpowiedzi na wysyłane do niego polecenia.
AT+CMEE=2 – włącza rozszerzone raportowanie błędów. Oznacza to, że modem będzie wyświetlał bardziej szczegółowe komunikaty o błędach w przypadku niepowodzenia polecenia.
AT+IPR=115200 – ustawia szybkość transmisji na 115200 bits/s. Szybkość transmisji to szybkość przesyłania danych przez interfejs szeregowy.
ATI – pobiera informacje o modemie, takie jak producent, model i wersja oprogramowania układowego.
AT+CPIN=“Kod PIN” – jeśli karta SIM wymaga numeru PIN, to polecenie wprowadza numer PIN w celu odblokowania karty SIM.
AT+GSN – pobiera numer IMEI (International Mobile Equipment Identity) modemu. IMEI to unikalny identyfikator przypisany do każdego telefonu komórkowego lub modemu.
AT+CIMI – pobiera numer IMSI (International Mobile Subscriber Identity) modemu. IMSI to unikalny identyfikator przypisany do każdej karty SIM.
AT+QCCID – pobiera numer ICCID (Integrated Circuit Card Identifier) modemu. ICCID to unikalny identyfikator przypisany do każdej karty SIM.
AT+QICSGP=1,”Adres APN” – ustawia adres APN (nazwa punktu dostępu).

Gdy komendy AT zostają przesłane, odpowiedź modemu jest odczytywana i zapisywana w zmiennej “download_data“. Po ukończeniu etapu konfiguracyjnego, w Serial Monitorze pojawia się komunikat o zakończonym działaniu funkcji.

C++
//Function to configuration the modem
void configurationModem() 
{
  Serial2.println("ATV1"); //Set verbose mode on
  Serial2.println("AT+CMEE=2"); //Enable extended error reporting
  Serial2.println("AT+IPR=115200"); //Set baud rate to 115200
  Serial2.println("ATI"); //Get modem info
  if(SIM_CARD_PIN_NUMBER != "") 
  {   //If SIM card requires a PIN, enter it
    Serial2.println((String) "AT+CPIN=" + SIM_CARD_PIN_NUMBER);
  }
  Serial2.println("AT+GSN"); //Get IMEI number
  Serial2.println("AT+CIMI"); //Get IMSI number
  Serial2.println("AT+QCCID"); //Get ICCID number
  Serial2.println((String) "AT+QICSGP=1,\"" + APN_ADDRESS + "\""); //Set APN address
  delay(100); //100 ms pause
  download_data = Serial2.readString(); //Reading data from modem UART
  Serial.println(); //Print blank line in Serial Monitor
  Serial.println(download_data); //Print data from modem UART
  Serial.println(); //Print blank line in Serial Monitor
  Serial.println("MODEM HAS BEEN CONFIGURED"); //Printing information in Serial Monitor
  download_data = ""; //Clearing variable
}
Expand

Funkcja networkCheck

Funkcja networkCheck ma za zadanie monitorować status połączenia z siecią komórkową.

W sposób cykliczny funkcja kieruje zapytanie do modemu za pomocą polecenia AT+CREG?, które pozwala na weryfikację stanu rejestracji w sieci. Po uzyskaniu połączenia z siecią lokalną, otrzymamy odpowiedź “+CREG 0,1“, zaś dla połączenia z siecią roamingową odpowiedź to “+CREG 0,5“. W razie problemów z połączeniem sieciowym, program zainicjuje ponowną konfigurację modemu.

Funkcja umożliwia także identyfikację problemów z kartą SIM. Jeśli podczas testów programu napotkamy komunikat “SIM card error”, powinniśmy sprawdzić, czy karta SIM umieszczona w urządzeniu Micromis Base V1 jest w pełni sprawna.

C++
//Function to check status of cellular connection
void networkCheck() 
{
  download_data = ""; //Clearing variable
  Serial.println("Network test"); //Printing information in Serial Monitor
  int connecting_count = 0; //Blank initial counter
  while (download_data == "") //While download_data variable is empty
  {
    Serial2.println("AT+CREG?");  //Send command to get network registration status
    download_data = Serial2.readString(); //Reading data from modem UART
    if(download_data.indexOf("+CREG: 0,1") != -1) //Check if device is registered on home network
    {
      Serial.println("Network registered - home"); //Printing information in Serial Monitor
      Serial.println(download_data); //Print data from modem UART
      delay(50); //50 ms pause
      break; //Quit from while loop
    } 
    else if(download_data.indexOf("+CREG: 0,5") != -1) //Check if device is registered on roaming network
    {
      Serial.println("Network registered - roaming"); //Printing information in Serial Monitor
      Serial.println(download_data); //Print data from modem UART
      delay(50); //50 ms pause
      break; //Quit from while loop
    } 
    else if(download_data.indexOf("+CME ERROR: SIM failure") != -1) //Check if there is a SIM failure error
    {
      Serial.println("SIM card error"); //Printing information in Serial Monitor
      Serial.println(download_data); //Print data from modem UART
      download_data = Serial2.readString(); //Reading data from modem UART
      download_data = ""; //Clearing variable
      delay(50); //50 ms pause
    } 
    else if(download_data.indexOf("+CME ERROR: 13") != -1) //Check if there is a SIM failure error in CME numeric form
    {
      Serial.println("SIM card error"); //Printing information in Serial Monitor
      Serial.println(download_data); //Print data from modem UART
      download_data = Serial2.readString(); //Reading data from modem UART
      download_data = ""; //Clearing variable
      delay(50); //50 ms pause
    }
    else
    {
      Serial.println("Connecting to network"); //Printing information in Serial Monitor
      Serial.println(download_data); //Print data from modem UART
      download_data = ""; //Clearing variable
      delay(1000); //1000 ms pause
      connecting_count++; //Increase the counter value by 1
    }
    if(connecting_count > 75)
    {
      setupModem(); //Check that the modem is working properly
      configurationModem(); //Make configuration again
      Serial.println("Some network connectivity issues... trying to connect again"); //Printing information in Serial Monitor
      delay(5000); //5000 ms pause
      connecting_count = 0; //Blank initial counter
    }
  }
}
Expand

Funkcja sendSMS

Funkcja sendSMS umożliwia przesyłanie wiadomości tekstowych (SMS) i przyjmuje dwa parametry: numer telefonu oraz treść wiadomości SMS.

Na początku funkcja konfiguruje modem w celu przesyłania wiadomości SMS w formie tekstowej. Komenda AT+CMGF=1 ustanawia format tekstu, podczas gdy AT+CSCS=”GSM” wybiera zestaw znaków używany w wiadomości.

W dalszym kroku funkcja przesyła odpowiednią wiadomość tekstową przy użyciu polecenia AT+CMGS, po czym przekazuje numer odbiorcy oraz treść wiadomości. Komenda Serial2.write(26) przesyła znak “Ctrl-Z” wskazujący na zakończenie wiadomości.

Po przesłaniu wiadomości, funkcja czeka na odpowiedź modemu za pomocą funkcji Serial2.readString. Jeśli otrzymana odpowiedź zawiera sekwencję +CMGS:, oznacza to pomyślne wysłanie wiadomości.

C++
//Function to send an SMS message
void sendSMS(String phone_number, String message) 
{
  Serial2.println("AT+CMGF=1"); //Setup type of text message
  Serial2.println("AT+CSCS=\"GSM\""); //Select character set
  Serial2.println((String) "AT+CMGS=\"" + phone_number + "\""); //Send SMS in text mode
  Serial2.println(message); //Printing message in Serial Monitor
  Serial2.write(26); //Send CTRL+Z in Ascii
  Serial2.println(); //Print blank line in Serial Monitor
  //Wait for a response from the modem
  delay(2000); //2000 ms pause
    String response = Serial2.readString(); //Reading data from modem UART
    Serial.println(response); //Printing information in Serial Monitor
    if(response.indexOf("+CMGS:") != -1) //Waiting for "+CMGS:" response from modem
    {
      Serial.println("SMS message sent successfully!"); //Printing information in Serial Monitor
      Serial.println("Modem sent SMS message to:"); //Printing information in Serial Monitor
      Serial.println(AUTHORIZED_NUMBER); //Printing phone number in Serial Monitor
      Serial.println("SMS content:"); //Printing information in Serial Monitor
      Serial.println(message); //Printing message in Serial Monitor
      Serial.println(); //Print blank line in Serial Monitor
    }
}
Expand

Funkcja receiveSMS

Funkcja receiveSMS odbiera i przetwarza przesyłane informacje o nowych wiadomościach SMS w modemie Quectel M65. Gdy funkcja jest wywoływana, wykonuje szereg działań w sekwencji:

AT+CMGF – ustawia format komunikatu na tryb tekstowy. To polecenie mówi modułowi GSM, aby oczekiwał wiadomości przychodzących w formacie tekstowym.
AT+CSCS – ustawia zestaw znaków na GSM za pomocą polecenia. To polecenie mówi modułowi, aby używał zestawu znaków GSM, który jest zestawem standardowym.
AT+CNMI – konfiguruje moduł w celu powiadamiania programu o nadejściu nowej wiadomości. W szczególności ustawia polecenie AT+CNMI na „2,1”, co oznacza, że moduł powinien wysłać powiadomienie, gdy nadejdzie nowa wiadomość SMS, a powiadomienie powinno zawierać numer indeksu nowej wiadomości.

C++
//Function to receive and process SMS message
void receiveSMS() {
  Serial2.println("AT+CMGF=1"); //Set message format to text mode
  delay(100); //100 ms pause
  Serial2.println("AT+CSCS=\"GSM\""); //Set the character set to GSM
  delay(50); //50 ms pause
  Serial2.println("AT+CNMI=2,1"); //Set module to notify on new SMS messages
  delay(100); //100 ms pause
  Serial.println("Waiting for incoming SMS message..."); //Set module to notify on new SMS messages

Następnie funkcja oczekuje na nadejście przychodzącej wiadomości SMS. Robi to, sprawdzając, czy są dostępne dane na porcie szeregowym podłączonym do modułu GSM za pomocą Serial2.available. Jeśli dane są dostępne, funkcja odczytuje odpowiedź z modułu za pomocą funkcji Serial2.readString.

Jeśli odpowiedź zawiera wskaźnik „+CMTI:”, co oznacza, że nadszedł nowy komunikat, funkcja wykorzystuje polecenie AT+CMGR do pobrania komunikatu o indeksie 1. Następnie odczytuje treść komunikatu jako ciąg znaków za pomocą Serial2.readString.

C++
if (Serial2.available()) {
    String response = Serial2.readString(); //Read the response from the GSM module
    if (response.indexOf("+CMTI:") != -1) { //Check if the response contains the "+CMTI:" indicator, which means a new message has arrived
      Serial2.println("AT+CMGR=1"); //Send the command to retrieve the message at index 1
      delay(100); //100 ms pause
      String messageContent = Serial2.readString(); //Read the message content as a string
      Serial.println("Received SMS message:"); //Print information in Serial Monitor
      Serial.println(messageContent); //Print message content in Serial Monitor
      Serial2.println("AT+CMGD=1,4"); //Delete the message at index 1 after reading it

Następnie funkcja wyodrębnia numer telefonu nadawcy z treści wiadomości. Robi to, znajdując pozycje pierwszego i drugiego przecinka w treści wiadomości, wyodrębniając część wartości string między tymi pozycjami. Na końcu usuwa znak cudzysłowu z początku i końca zmiennej, aby uzyskać numer telefonu.

C++
//Extract the sender's phone number from the response
      int firstComma = messageContent.indexOf(',') + 1; //Find the position of the first comma
      int secondComma = messageContent.indexOf(',', firstComma + 1); //Find the position of the second comma
      String phoneNumber = messageContent.substring(firstComma, secondComma); //Extract the phone number
      phoneNumber.remove(0, 1); //Remove the first double quote
      phoneNumber.remove(phoneNumber.length() - 1); //Remove the last double quote
      Serial.println(phoneNumber); //Print the extracted phone number

Po wyodrębnieniu numeru telefonu funkcja sprawdza, czy numer telefonu jest uprawniony do wysyłania poleceń do modułu.

C++
//Check if the sender's phone number is authorized
      if (phoneNumber == AUTHORIZED_NUMBER) {

Jeśli numer telefonu jest autoryzowany, funkcja za pomocą wyrażeń regularnych przeszukuje treść wiadomości pod kątem określonych słów kluczowych, takich jak „WŁĄCZ LAMPĘ 1” lub „TEMPERATURA”. 

Ta część kodu definiuje kilka wyrażeń regularnych wykorzystujących klasę std::regex w celu dopasowania do określonych słów kluczowych, które program ma rozpoznawać w przychodzących wiadomościach SMS

Wyrażenia regularne zdefiniowane w tej części kodu mają za zadanie dopasować określone słowa kluczowe związane ze sterowaniem różnymi urządzeniami, takimi jak lampy i diody LED, a także słowo kluczowe do odczytu temperatury z czujnika. 

Przykład:
turnOnRegex: To wyrażenie regularne pasuje dokładnie do frazy „WŁĄCZ LAMPĘ 1”, która jest używana do włączania lampy podłączonej do pierwszego przekaźnika.

C++
        //Use Regular Expressions to extract the user's intent
        std::regex turnOnRegex("TURN ON LAMP 1"); //Define a Regular Expression to match the keywords "TURN ON LAMP 1"
        std::regex turnOffRegex("TURN OFF LAMP 1"); //Define a Regular Expression to match the keywords "TURN OFF LAMP 1"
        std::smatch match; //Create a std::smatch object to hold the match results
        std::regex turnOnRegex2("TURN ON LAMP 2"); //Define a Regular Expression to match the keywords "TURN ON LAMP 2"
        std::regex turnOffRegex2("TURN OFF LAMP 2"); //Define a Regular Expression to match the keywords "TURN OFF LAMP 2"
        std::smatch match2; //Create a std::smatch object to hold the match results
        std::regex turnOnRegex3("TURN ON LED 1"); //Define a Regular Expression to match the keywords "TURN ON LED 1"
        std::regex turnOffRegex3("TURN OFF LED 1"); //Define a Regular Expression to match the keywords "TURN OFF LED 1"
        std::smatch match3; //Create a std::smatch object to hold the match results
        std::regex turnOnRegex4("TURN ON LED 2"); //Define a Regular Expression to match the keywords "TURN ON LED 2"
        std::regex turnOffRegex4("TURN OFF LED 2"); //Define a Regular Expression to match the keywords "TURN OFF LED 2"
        std::smatch match4; //Create a std::smatch object to hold the match results
        std::regex turnOnRegex5("TEMPERATURE"); //Define a Regular Expression to match the keyword "TEMPERATURE"
        std::smatch match5; //Create a std::smatch object to hold the match results
Expand

Po zdefiniowaniu tych wyrażeń regularnych funkcja konwertuje przychodzącą wiadomość SMS na std::string za pomocą messageContent.c_str, a następnie przeszukuje ten ciąg znaków za pomocą std::regex_search.

C++
std::string messageContentStr(messageContent.c_str()); //Convert messageContent to a std::string before passing it to std::regex_search

Obiekty std::smatch match, match2, match3, match4 i match5 służą do przechowywania wyników wyszukiwania wyrażeń regularnych. Obiekty te są tworzone przed wykonaniem wyszukiwania wyrażeń regularnych, dzięki czemu można ich ponownie użyć przy każdym wyszukiwaniu. Dopasowania są przekazywane jako odniesienia do funkcji std::regex_search() w celu przechowywania wyników każdego wyszukiwania.

W przypadku znalezienia dopasowania wykonywana jest odpowiednia akcja, taka jak włączenie lampy lub odczyt temperatury z czujnika.

C++
if (std::regex_search(messageContentStr, match, turnOnRegex)) { //Check if the message contains the "TURN ON LAMP 1" keyword
          digitalWrite(relay1, HIGH); //Turn on the relay connected to Lamp 1
          Serial.println("Lamp 1 turned on"); //Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match, turnOffRegex)) { //Check if the message contains the "TURN OFF LAMP 1" keyword
          digitalWrite(relay1, LOW); //Turn off the relay connected to Lamp 1
          Serial.println("Lamp 1 turned off"); //Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match2, turnOnRegex2)) { //Check if the message contains the "TURN ON LAMP 2" keyword
          digitalWrite(relay2, HIGH); //Turn on the relay connected to Lamp 2
          Serial.println("Lamp 2 turned on"); // Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match2, turnOffRegex2)) { //Check if the message contains the "TURN OFF LAMP 2" keyword
          digitalWrite(relay2, LOW); //Turn off the relay connected to Lamp 2
          Serial.println("Lamp 2 turned off"); //Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match3, turnOnRegex3)) { //Check if the message contains the "TURN ON LED 1" keyword
          digitalWrite(power_output1, HIGH); //Turn on the LED strip connected to output 1
          Serial.println("LED 1 strip turned on"); // Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match3, turnOffRegex3)) { //Check if the message contains the "TURN OFF LED 1" keyword
          digitalWrite(power_output1, LOW); //Turn off the LED strip connected to output 1
          Serial.println("LED 1 strip turned off"); //Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match4, turnOnRegex4)) { //Check if the message contains the "TURN ON LED 2" keyword
          digitalWrite(power_output2, HIGH); //Turn on the LED strip connected to output 2
          Serial.println("LED 2 strip turned on"); //Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match4, turnOffRegex4)) { //Check if the message contains the "TURN OFF LED 2" keyword
          digitalWrite(power_output2, LOW); //Turn off the LED strip connected to output 2
          Serial.println("LED 2 strip turned off"); //Print information in Serial Monitor
        } else if (std::regex_search(messageContentStr, match5, turnOnRegex5)) { // Check if the message contains the "TEMPERATURE" keyword
          float temperature = LM75.readTemperatureC(); //Read temperature from LM75 sensor
          Serial.println("The temperature is: " + String(temperature)); //Print a message to the Serial Monitor with current temperature
          String SMSmessage = "The temperature is: " + String(temperature) + " Celsius "; // Create a string to hold the SMS message
          sendSMS(AUTHORIZED_NUMBER, SMSmessage); //Send an SMS message containing the current temperature
        } else { //If the message does not contain any recognized keywords, print an error message
          Serial.println("Unknown command"); //Print a message to the Serial Monitor
Expand

Na koniec funkcja czeka 100 ms przed ponownym sprawdzeniem nowych wiadomości przychodzących. Opóźnienie to jest konieczne, aby moduł GSM nie został obciążony poleceniami i dał mu czas na prawidłowe przetworzenie każdego polecenia.

C++
 delay(100); //Wait for 100ms before checking again

Funkcja setup

Funkcja setup uruchamia komunikację szeregową między mikrokontrolerem ESP32, która zostaje skonfigurowana na prędkość 115200 bits/s, konfiguruje drugi port szeregowy do komunikacji z modemem o tej samej prędkości, konfiguruje pin włączania/wyłączania modemu jako wyjście, ustawia limity czasu dla portu szeregowego z obu interfejsów UART oraz inicjalizuje interfejs I2C, który jest używany do komunikacji z czujnikiem temperatury LM75. Na końcu wywołuje omówione wcześniej funkcje setupModem, configurationModem i networkCheck.

C++
void setup() 
{
  Serial.begin(115200); //Start main UART interface
  Serial.setTimeout(100); //Set timeout for main UART interface
  Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2); //Configure and start Serial2 UART interface
  pinMode(MODEM_PWRKEY, OUTPUT); //Set MODEM_PWRKEY as output
  Serial2.setTimeout(100); //Set timeout for Serial2 UART interface
  Wire.begin(); //Initialize I2C interface
  delay(500); //50 ms pause
  Serial.println("----------- MICROMIS POWER SHIELD start! -----------"); //Print information in Serial Monitor
  setupModem(); //Run GSM modem
  configurationModem(); //Configuration GSM modem
  networkCheck(); //Check status of cellular connection
}
Expand

Funkcja loop

Funkcja loop wywołuję wspomnianą wcześniej funkcję receiveSMS.

C++
void loop() {
  receiveSMS(); //Call function to check for incoming SMS messages
}

Inteligentne domy otwierają przed nami świat pełen wygód, oszczędności i innowacji. Jakie rozwiązania smart home planujesz zastosować, korzystając z naszych wskazówek?

O Autorze

Nikola Pająk

Absolwentka technologii informatycznych na uniwersytecie w Danii, pasjonuje się elektroniką, kognitywistyką i grą w szachy. Lubi stale poszerzać swoją wiedzę. Miłośniczka sportu, gotowania i spędzania czasu na świeżym powietrzu.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Label
Copyright © 2023 Device Prototype 
|
Polityka prywatności