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.
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.
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.
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ń.
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.
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 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.
//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.
//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.
#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:
String AUTHORIZED_NUMBER = “+442076350000”
A dla numeru telefonu z Polski:
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:
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:
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:
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 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.
//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
}
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.
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
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.
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
}
}
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 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.
//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
}
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.
//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
}
}
}
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.
//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
}
}
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.
//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.
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.
//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.
//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.
//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
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.
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.
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
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.
delay(100); //Wait for 100ms before checking again
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.
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
}
Funkcja loop wywołuję wspomnianą wcześniej funkcję receiveSMS.
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?