LoRa – это технология беспроводной передачи данных, разработанная компанией Semtech, ориентированная на передачу с небольшой скоростью данных на большие расстояния. Отличительной особенностью LoRa по сравнению с другими похожими технологиями является крайне низкое энергопотребление устройств, использующих данную технологию. Некоторые датчики LoRa могут работать от одной батарейки (без ее замены) в течение нескольких лет. Более подробно про технологию LoRa и используемый вместе с ней протокол LoRaWAN вы можете прочитать в статьях про подключение модулей LoRa к платам Arduino и Raspberry Pi.
В данной статье мы рассмотрим подключение модуля LoRa SX1278 к модулю ESP32. В проекте мы будем использовать два модуля LoRa – один будет подключен к модулю ESP32, а другой – к плате Arduino. Модуль LoRa, подключенный к ESP32, с помощью специального API будет запрашивать данные о погоде в определенном городе, и передавать их модулю LoRa, подключенному к плате Arduino, которая будет отображать их на экране ЖК дисплея 16x2. Таким образом, модуль LoRa, подключенный к ESP32, будет выступать в качестве передатчика, а модуль LoRa, подключенный к плате Arduino, будет выступать в качестве приемника. К модулю ESP32 будет также подключен OLED дисплей, который будет показывать значение температуры и влажности на стороне передатчика.
Необходимые компоненты
- Модуль ESP32 (купить на AliExpress).
- Плата Arduino Uno (купить на AliExpress).
- Шилд (плата расширения) Lora для Arduino.
- Модуль LoRa SX1278 (433MHz) – 2 шт. (купить на AliExpress).
- ЖК дисплей 16x2 (купить на AliExpress).
- OLED дисплей (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Изготовление шилда Lora для Arduino описано в данной статье, где приведены Gerber файлы для изготовления ее печатной платы. Но подключить модуль LoRa к плате Arduino можно и без использования данного шилда, как описано в этой статье.
Примечание: в России применение модулей LoRa, работающих на частоте 433 МГц, не разрешено радиочастотными органами, вместо них можно купить модули LoRa, работающие на частоте 866 МГц.
Схема проекта
В данном проекте мы будем передавать данные о погоде с модуля ESP32 на плату Arduino Uno используя модули LoRa SX1278. Данные о погоде и влажности будут считываться модулем ESP32 из сети интернет.
Схема передающей части
Схема подключения модуля LoRa SX1278 к ESP32 представлена на следующем рисунке.
На данной схеме к модулю ESP32 также подключен OLED дисплей – на нем мы будем отображать считанные с интернета данные температуры и влажности. Схема соединений данной части проекта приведена в следующих таблицах.
Модуль LoRa SX1278 | ESP32 |
3.3V | 3.3V |
GND | GND |
NSS | D5 |
DIO0 | D2 |
SCK | D18 |
MISO | D19 |
MOSI | D23 |
RST | D14 |
OLED дисплей | ESP32 |
Vcc | 3.3v |
GND | GND |
SCL | D22 |
SDA | D21 |
На следующем рисунке показана собранная на макетной плате конструкция передающей части проекта.
Схема приемной части
Схема подключения модуля LoRa SX1278 к плате Arduino представлена на следующем рисунке. В схеме использован шилд Lora для Arduino, описанный в данной статье. Но без использования данного шилда можно использовать способ подключения модуля LoRa SX1278 к плате Arduino, приведенный в следующей статье.
Шилд состоит из модуля Lora SX1278 433MHz с регулятором напряжения 3.3V на основе микросхемы LM317.
Внешний вид конструкции приемной части проекта показан на следующем рисунке.
API средства для получения данных о погоде
Для получения данных о погоде мы будем использовать сервис Weather API, который достаточно прост в использовании. Зарегистрируйтесь на этом сервисе чтобы в дальнейшем получить на нем ссылку (API link) для считывания данных о погоде.
После регистрации на сервисе Weather API нажмите в нем на ссылку ‘My Account’ и после перехода по ней получите необходимые средства API. Для получения данных о погоде нам будет нужен API ключ.
После этого вернитесь назад, на главную страницу сервиса, и нажмите на ссылку “API Explorer”.
На открывшейся странице API explorer введите API ключ и название города, данные о погоде в котором вы хотите получать.
Далее прокрутите страницу вниз и нажмите на кнопку ‘Show Response’ чтобы создать запрос. В результате вы получите API ключ, который можно будет потом использовать в программе.
После этого вставьте API URL в адресную строку в новой вкладке браузера, в результате чего вы увидите картину примерно как на рисунке ниже. Это так называемые JSON данные.
После получения этих данных JSON нам необходимо сгенерировать код, с помощью которого мы будем считывать эти JSON данные и фразу, которая будет нам необходима. Для этого мы используем сервис ArduinoJson Assistant и вставим в его секцию входных данных (Input section) данные JSON.
Затем пролистайте страницу вниз, к программе парсинга (parsing program) и скопируйте ее код – он нам понадобится в дальнейшем.
Объяснение программы для передающей части проекта
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Первым делом в программе подключим все необходимые библиотеки. Библиотека SPI.h используется для взаимодействия по интерфейсу SPI между модулями ESP32 и LoRa. Библиотека Wire.h используется для связи по протоколу I2C. Скачать все необходимые библиотеки для данной части проекта вы можете по следующим ссылкам:
Далее подключим все необходимые библиотеки в программе.
1 2 3 4 5 6 7 |
#include <HTTPClient.h> #include <WiFi.h> #include <ArduinoJson.h> #include <SPI.h> #include <LoRa.h> #include <Wire.h> #include<SH1106.h> |
Затем создадим объект для работы с OLED дисплеем, в котором укажем адрес дисплея и контакты, к которым он подключен.
1 |
SH1106 display(0x3c, 21, 22); |
После этого укажем данные для подключения к сети Wi-Fi – ее имя и пароль для доступа к ней.
1 2 |
const char* ssid = "Wi-fi Name"; const char* pass = "Password"; |
Далее укажем контакты, к которым подключен модуль LoRa.
1 2 3 |
#define ss 5 #define rst 14 #define dio0 2 |
После этого введем API ссылку (API link), которую мы сформировали ранее. С помощью этой ссылки мы будем считывать данные температуры и влажности из города Джайпур. Вы можете считывать эти данные для своего города.
1 |
Const char* url = "http://api.weatherapi.com/v1/current.json?key=ade61a8aef37445d8c0100632202407&q=Jaipur"; |
Внутри функции setup инициализируем последовательную связь со скоростью 115200 для целей отладки, OLED дисплей и связь по технологии LoRa с помощью функции begin().
1 2 3 4 5 6 7 8 9 10 11 12 |
Serial.begin(115200); Serial.println(ssid); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); } LoRa.setPins(ss, rst, dio0); while (!LoRa.begin(433E6)) { } LoRa.setSyncWord(0xF3); } |
Далее внутри функции loop() мы будем проверять JSON файл, получаемый модулем ESP32, и выводить JSON данные в окно монитора последовательной связи.
1 2 3 4 5 6 |
int httpCode = https.GET(); if (httpCode > 0) { String payload = https.getString(); char charBuf[500]; payload.toCharArray(charBuf, 500); Serial.println(payload); |
Далее используем фразу, сгенерированную сервисом ArduinoJson Assistant. С помощью этой фразы мы будем считывать данные температуры и влажности в городе Jaipur.
1 2 3 4 5 6 7 8 |
const size_t capacity = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(8) + JSON_OBJECT_SIZE(23) + 490; DynamicJsonDocument doc(capacity); const char* json = "{\"location\":{\"name\":\"Jaipur\",\"region\":\"Rajasthan\",\"country\":\"India\",\"lat\":26.92,\"lon\":75.82,\"tz_id\":\"Asia/Kolkata\",\"localtime_epoch\":1595741089,\"localtime\":\"2020-07-26 10:54\"},\"current\":{\"last_updated_epoch\":1595740520,\"last_updated\":\"2020-07-26 10:45\",\"temp_c\":31,\"temp_f\":87.8,\"is_day\":1,\"condition\":{\"text\":\"Mist\",\"icon\":\"//cdn.weatherapi.com/weather/64x64/day/143.png\",\"code\":1030},\"wind_mph\":0,\"wind_kph\":0,\"wind_degree\":0,\"wind_dir\":\"N\",\"pressure_mb\":1008,\"pressure_in\":30.2,\"precip_mm\":0,\"precip_in\":0,\"humidity\":66,\"cloud\":50,\"feelslike_c\":32.2,\"feelslike_f\":89.9,\"vis_km\":5,\"vis_miles\":3,\"uv\":8,\"gust_mph\":7.2,\"gust_kph\":11.5}}"; deserializeJson(doc, json); long current_last_updated_epoch = current["last_updated_epoch"]; const char* current_last_updated = current["last_updated"]; // int current_temp_c = current["temp_c"]; // 31 int current_humidity = current["humidity"]; // 66 |
Затем, на заключительном шаге, мы будем передавать данные температуры и влажности приемнику LoRa.
1 2 3 4 5 6 7 |
LoRa.beginPacket(); LoRa.print("Temperature: "); LoRa.print(current_temp_c); LoRa.print("c"); LoRa.print("Humidity: "); LoRa.print(current_humidity); LoRa.endPacket(); |
Объяснение программы для приемной части проекта (для Arduino)
Первым делом подключим используемые библиотеки и укажем контакты, к которым подключен ЖК дисплей.
1 2 3 4 5 |
#include <SPI.h> #include <LoRa.h> #include <LiquidCrystal.h> constintrs = 8, en = 7, d4 = 6, d5 = 5, d6 = 4, d7 = 3; LiquidCrystallcd(rs, en, d4, d5, d6, d7); |
Внутри функции void loop() мы будем принимать данные от модуля Lora. После приема пакетов плата Arduino начнет считывать их в виде символов и печатать их на экране ЖК дисплея. Когда она будет получать символ “c”, она будет печатать оставшуюся информацию на второй строчке дисплея.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void loop() { intpacketSize = LoRa.parsePacket(); if (packetSize) { Serial.print("Received packet '"); while (LoRa.available()) { char incoming = (char)LoRa.read(); if (incoming == 'c') { lcd.setCursor(0, 1); } else { lcd.print(incoming); } |
Тестирование работы проекта
После того как аппаратная и программная части проекта будут готовы, вы можете приступит к тестированию его работы. Передающий модуль Lora будет передавать данные температуры и влажности приемному модулю. А приемный модуль LoRa будет передавать принятые данные плате Arduino, которая будет отображать их на экране ЖК дисплея как показано на следующем рисунке.
Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
Arduino Code: #include <SPI.h> //SPI Library #include <LoRa.h> //LoRa Library #include <LiquidCrystal.h> //Library for LCD const int rs = 8, en = 7, d4 = 6, d5 = 5, d6 = 4, d7 = 3; //Mention the pin number for LCD connection LiquidCrystal lcd(rs, en, d4, d5, d6, d7);//Initialize LCD method void setup() { Serial.begin(9600); //Serial for Debugging lcd.begin(16, 2); //Initialise 16*2 LCD lcd.print("Arduino LoRa"); //Intro Message line 1 Serial.print("Arduino LoRa"); //Intro Message line 1 lcd.setCursor(0, 1); lcd.print("Receiver"); //Intro Message line 2 delay(2000); if (!LoRa.begin(433E6)) { //Operate on 433MHz Serial.println("Starting LoRa failed!"); lcd.print("LoRa Failed"); while (1); } } void loop() { int packetSize = LoRa.parsePacket(); // lcd.print("LoRa"); if (packetSize) { // If packet received Serial.print("Received packet '"); lcd.clear(); while (LoRa.available()) { char incoming = (char)LoRa.read(); if (incoming == 'c') { lcd.setCursor(0, 1); } else { lcd.print(incoming); Serial.print(incoming); } } } } ESP32 Code: #include <HTTPClient.h> #include <WiFi.h> #include <ArduinoJson.h> #include <SPI.h> #include <LoRa.h> #include <Wire.h> #include<SH1106.h> SH1106 display(0x3c, 21, 22); const char* ssid = "Galaxy-M20"; const char* pass = "ac312124"; int count; //define the pins used by the transceiver module #define ss 5 #define rst 14 #define dio0 2 const char* url = "http://api.weatherapi.com/v1/current.json?key=ade61a8aef37445d8c0100632202407&q=Jaipur"; void setup() { Serial.begin(115200); delay(2000); Serial.println("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); // print ... till not connected } Serial.println("WiFi connected"); Serial.println("LoRa Sender"); LoRa.setPins(ss, rst, dio0); while (!LoRa.begin(433E6)) { Serial.println("."); delay(500); } // Change sync word (0xF3) to match the receiver // The sync word assures you don't get LoRa messages from other LoRa transceivers // ranges from 0-0xFF LoRa.setSyncWord(0xF3); Serial.println("LoRa Initializing OK!"); display.init(); display.flipScreenVertically(); display.setFont(ArialMT_Plain_10); } void loop() { HTTPClient https; String data; https.begin(url); int httpCode = https.GET(); if (httpCode > 0) { //Check for the returning code String payload = https.getString(); char charBuf[500]; payload.toCharArray(charBuf, 500); //Serial.println(payload); const size_t capacity = JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(8) + JSON_OBJECT_SIZE(23) + 490; DynamicJsonDocument doc(capacity); const char* json = "{\"location\":{\"name\":\"Jaipur\",\"region\":\"Rajasthan\",\"country\":\"India\",\"lat\":26.92,\"lon\":75.82,\"tz_id\":\"Asia/Kolkata\",\"localtime_epoch\":1595741089,\"localtime\":\"2020-07-26 10:54\"},\"current\":{\"last_updated_epoch\":1595740520,\"last_updated\":\"2020-07-26 10:45\",\"temp_c\":31,\"temp_f\":87.8,\"is_day\":1,\"condition\":{\"text\":\"Mist\",\"icon\":\"//cdn.weatherapi.com/weather/64x64/day/143.png\",\"code\":1030},\"wind_mph\":0,\"wind_kph\":0,\"wind_degree\":0,\"wind_dir\":\"N\",\"pressure_mb\":1008,\"pressure_in\":30.2,\"precip_mm\":0,\"precip_in\":0,\"humidity\":66,\"cloud\":50,\"feelslike_c\":32.2,\"feelslike_f\":89.9,\"vis_km\":5,\"vis_miles\":3,\"uv\":8,\"gust_mph\":7.2,\"gust_kph\":11.5}}"; deserializeJson(doc, json); JsonObject location = doc["location"]; const char* location_name = location["name"]; // "Jaipur" const char* location_region = location["region"]; // "Rajasthan" const char* location_country = location["country"]; // "India" JsonObject current = doc["current"]; long current_last_updated_epoch = current["last_updated_epoch"]; const char* current_last_updated = current["last_updated"]; // int current_temp_c = current["temp_c"]; // 31 int current_humidity = current["humidity"]; // 66 Serial.print("Temperature: "); Serial.println(current_temp_c); Serial.println("Humidity: "); Serial.println(current_humidity); Serial.print("Sending packet: "); // Serial.println(counter); //Send LoRa packet to receiver LoRa.beginPacket(); LoRa.print("Temperature: "); LoRa.print(current_temp_c); String Temp = String(current_temp_c); LoRa.print("c"); LoRa.print("Humidity: "); LoRa.print(current_humidity); String Humidity = String(current_humidity); LoRa.endPacket(); display.clear(); display.setTextAlignment(TEXT_ALIGN_LEFT); display.setFont(ArialMT_Plain_16); display.drawString(0, 23, "Temperature:"); display.drawString(94, 23, Temp); display.drawString(0, 38, "Humidity:"); display.drawString(70, 38, Humidity); display.display(); delay(5000); } else { Serial.println("Error on HTTP request"); } https.end(); count++; } |