Технологии LoRa всего 10 лет и за все эти годы ее популярность в современном мире росла фантастическими темпами. Устройства LoRa отличаются крайне низкой потребляемой мощностью и при этом способны обеспечивать беспроводную связь на достаточно большие расстояния (относительно небольшой излучаемой устройствами мощности), что сделало технологию LoRa очень удобной для использования в проектах интернета вещей IoT. Ожидается, что к 2025 году в мире будет насчитываться 29 миллиардов устройств, подключенных к сети интернет. Это в 4 раза больше чем количество людей, живущих сейчас на нашей планете.
В данной статье мы рассмотрим подключение HDPA13A, который является оконечным узлом LoRa SX1276, к модулю ESP32 и с его помощью будем передавать данные в сеть TTN (Things Network, сеть вещей). Делать мы это будем через шлюз LoRa RG186. Также в статье будет рассмотрена настройка популярного модуля LoRa SX1276 (868MHz) для работы в Индии (поскольку автор статьи из этой страны, ссылка на оригинал будет приведена в конце статьи). И в конце статьи мы рассмотрим проблемы, которые возникли у автора при установлении соединения с сетью вещей, и способы их решения.
Также на нашем сайте мы рассматривали подключение модулей LoRa к другим микроконтроллерам (платам):
Распиновка модуля LoRa SX1276
Распиновка модуля LoRa SX1276 показана на рисунке ниже. В данной статье мы будем использовать модуль LoRa HDP13A V1.1, который был разработан компанией HPDTeK, хорошо известной в Китае.
Назначение контактов модуля:
GND – контакт общего провода (земли), который в нашем проекте необходимо подключить к контакту земли модуля ESP32.
SDO (MISO) – контакт передачи последовательных данных (Serial Data Out) микроконтроллеру. По нему информация передается по протоколу SPI.
SDI (MOSI) – контакт приема последовательных данных (Serial Data In) от микроконтроллера. Связь осуществляется по протоколу SPI.
SCK (Serial Clock Pin) – контакт тактовой частоты, которая подается на него от микроконтроллера.
SEL (Chip Select) – контакт выбора ведомого устройства в протоколе SPI.
RST – контакт сброса модуля.
IO2-5 – контакты ввода/вывода модуля LoRa. Могут быть программным способом установлены в High или Low.
ANT – контакт для подключения антенны. Возможные типы антенн необходимо уточнять в даташите на модуль.
VCC – контакт для подачи питания на модуль, необходимо подключать к напряжению 3.3V.
Обзор модуля LoRa SX1276 868MHZ
Данный модуль разработан и производится компанией SEMTECH. Модуль прост в использовании, сравнительно дешев, отличается высокой эффективностью и может быть использован в различных приложениях. Его внешний вид показан на следующем рисунке.
Трансиверы SX1276/77/78/79 содержат модем LoRaTM для связи на большие расстояния, отличаются крайне низким потреблением и используют для своей работы широкополосный сигнал, позволяющий добиться высокой помехоустойчивости системы. Чувствительность модуля SX1276 составляет величину не менее -148 дБм, и это при условии использования дешевого кристалла.
В чем разница между модулями Lora HPD13A SX1276 и RFM95W?
Как видно из представленного выше рисунка на модуле написано название HPD13A, а у поставщиков он проходит под именем модуля LoRa SX1276. На первый взгляд, это может показаться запутанным.
Поэтому автор проекта решил снять верхнюю часть модуля и посмотреть что находится у него внутри. Из следующего рисунка можно увидеть что имя компании, которая производит данный модуль – HPDTek, поэтому она использует свой собственный номер для этого компонента, а под крышкой модуля мы можем увидеть микросхему SX1276 LoRa.
Но все еще остается вопрос в чем разница между микросхемами RFM95W и SX1276. Фактически, это одинаковые модули, поэтому и характеристики у них одинаковые. Если ваша цель – установить связь LoRa, это можно сделать с помощью любого из этих модулей. HopeRF получает разрешение (лицензию) от компании Semtech. Незащищенный патентами модуль RFM95W идентичен модулям SX127x и его распиновка и программное обеспечение аналогичны им. На двух представленных выше рисунках вы можете увидеть разницу во внешнем виде между этими двумя модулями.
Изготовление переходной платы с помощью перфорированной платы
Если вы посмотрите на расположение контактов модуля LoRa SX1276 вы увидите что они не подходят для использования на макетной плате, поэтому для этой цели мы изготовим переходную плату/адаптер (Breakout Board) для этого модуля с помощью куска перфорированной платы. Подробно процесс изготовления такой платы показан на следующем рисунке. Также это можно сделать с помощью изготовления печатной платы, но это сложнее и отнимает больше времени.
Если вы посмотрите на модуль HPD13A вы увидите что он поставляется с металлической крышкой, которая уменьшает помехи и электромагнитные излучения. Как показано на представленном рисунке автор проекта удалил эту крышку чтобы посмотреть что находится внутри.
Схема проекта
Схема подключения модуля LoRa SX1276 к модулю ESP32 представлена на следующем рисунке.
Модуль HPD13A LoRa SX1276 | ESP32-WROOM Dev Module V1 |
3.3V | 3.3V |
GND | GND |
SCK | D18 (SCLK) |
SDI (MOSI) | D23 (MOSI) |
SDO (MISO) | D19 (MISO) |
SEL (CS) | D26 (User Defined) |
RST | D25 (User Defined) |
IO0 | D23 (User Defined) |
IO1 | D22 (User Defined) |
Внешний вид собранной на макетной плате конструкции проекта показан на следующем рисунке.
Какой метод активации использовать – TTN ABP или OTAA
Каждое оконечное устройство должно быть зарегистрировано в сети перед передачей и приемом сообщений. Эта процедура называется активацией. Доступны два метода активации:
- Over-The-Air-Activation (OTAA) – беспроводная активация, «по воздуху». Самый безопасный и рекомендованный метод активации для оконечных устройств. Устройства выполняют процедуру присоединения к сети, во время которой им присваивается динамический адрес и выделяются ключи безопасности;
- Activation By Personalization (ABP) – требует «жесткого» аппаратного вмешательства в работу устройства для изменения его адреса и ключей безопасности, выданных ему. Процедура ABP менее безопасна чем OTAA, также в этом случае недостатком является то, что устройство не может сменить провайдера сети без ручного изменения ключей устройства.
Процедура присоединения для стандартов LoRaWAN 1.0.x и 1.1 немного отличается. В следующих разделах статьи рассматриваются процедуры присоединения к LoRaWAN 1.0.x и 1.1 по отдельности.
Если вы хотите получить более подробную информацию относительно процесса активации устройств в сетях TTN, вам необходимо посмотреть официальную документацию на веб-сайте TTN.
Установка оконечных узлов для сети TTN
Для передачи данных в сеть TTN (сеть вещей) вам необходимо сначала настроить свой шлюз, после этого необходимо будет настроить свои оконечные узлы чтобы устройство работало нормально. Для этого вам необходимо создать себе TTN аккаунт или войти в свой существующий аккаунт TTN. После того как вы успешно войдете в свой аккаунт вам необходимо перейти в TTN консоль. Если вы все сделали правильно, то вы должны увидеть экран, показанный на следующем рисунке.
После этого вам необходимо выбрать кластер. Если вы хотите узнать больше информации о кластере, то нажмите на кнопку «more information». В нашем случае мы выбрали кластер europe1.
После этого вы увидите экран, показанный на следующем рисунке. Нажмите на нем на “Go to application”.
На следующем открывшемся экране нажмите кнопку Add application чтобы создать свое первое приложение.
Теперь вам необходимо создать новое приложение, введя для него уникальный идентификатор (Application ID) и имя. Описание (Description part) является опциональным и его можно не заполнять. После ввода идентификатора и имени приложения нажмите на кнопку create application.
После этого вам откроется экран, показанный на следующем рисунке. Теперь вам нужно создать оконечные узлы (End Nodes) для вашего приложения – это устройства, которые передают данные на сервер приложения.
Теперь нажмите на только что созданное приложение и нажмите на кнопку end node слева, и затем на кнопку add application справа. Как вы можете видеть из представленного рисунка, мы уже создали два оконечных узла. Один – для тестирования метода активации ABP, а второй – для тестирования метода активации OTAA.
Когда вы нажмете на кнопку add end node вы увидите экран, показанный на следующем рисунке. На нем кликните на manually (вручную) и заполните необходимые данные. Выберите частотный план (frequency plan), спецификацию LoRaWAN и метод активации (Activation Mode) – для нашего первого примера мы выбрали метод OTAA.
После этого вам необходимо сгенерировать ключи для ваших оконечных узлов. Потом нажмите на кнопку Register end device (зарегистрировать оконечное устройство) чтобы завершить процесс. Но учтите тот факт, что ваши DeviceEUI, AppKey и AppEUI – это ваших три самых важных ключа, поэтому будьте осторожны при работе с ними. Процесс активации по методу ABP будет аналогичен рассмотренному, вам необходимо в качестве метода активации (activity mode) выбрать ABP и сгенерировать для него соответствующие ключи.
На представленном ниже рисунке показано что мы выбрали метод активации ABP и сгенерировали для него Device Address, NwSKey и AppSKey, а Device EUI будет сгенерирован автоматически. После этого вам необходимо всего лишь нажать кнопку Register end device чтобы завершить процесс.
Теперь на стороне TTN мы все сделали и можно переходить к написанию программы.
Объяснение кода программы для модуля ESP32
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Код программы для подключения модуля Lora HPD13A SX1276 к модулю ESP32 достаточно прост. Все, что нам нужно сделать, это скачать библиотеку arduino-lmic от пользователя mcci-catena и использовать пример ее кода для взаимодействия с модулем SX1276. Но прежде чем мы сможем использовать пример кода этой библиотеки нам необходимо настроить модуль HX1276 для работы на частоте 868MHz. Для этого нам необходимо изменить конфигурацию проекта, которую можно найти в библиотеке arduino-lmic в папке Documents Arduino Library, на следующее:
1 2 3 4 5 6 7 8 9 10 |
// project-specific definitions // project-specific definitions #define CFG_eu868 1 //#define CFG_us915 1 //#define CFG_au915 1 //#define CFG_as923 1 // #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP; also define CFG_as923 */ //#define CFG_kr920 1 //#define CFG_in866 1 #define CFG_sx1276_radio 1 |
После этого мы можем открыть пример скетча TTN-OTAA для модуля ESP32. Но перед этим нам необходимо вставить в него свои ключи чтобы иметь возможность взаимодействия с сетью TTN.
Откройте пример скетча TTN-OTAA, расположенный по адресу File > Examples > MCCI LoRaWAN LMIC Library > ttn-otaa.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// This EUI must be in little-endian format, so least-significant-byte // first. When copying an EUI from ttnctl output, this means to reverse // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, // 0x70. static const u1_t PROGMEM APPEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8); } // This should also be in little endian format, see above. static const u1_t PROGMEM DEVEUI[8] = { 0xFF, 0x43, 0x45, 0xD0, 0xDE, 0xD5, 0xB3, 0x70 }; void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8); } // This key should be in big endian format (or, since it is not really a // number but a block of memory, endianness does not really apply). In // practice, a key taken from ttnctl can be copied as-is. static const u1_t PROGMEM APPKEY[16] = { 0xBB, 0xAB, 0xAA, 0xD6, 0x47, 0x5A, 0xAC, 0xBC, 0x36, 0xAC, 0x22, 0x4A, 0xF2, 0x78, 0x33, 0x72 }; |
Теперь, если вы посмотрите на этот фрагмент кода внимательно, то вы найдете в нем места для вставки ключей, которые мы получили ранее. Необходимо вставить эти ключи в формате MSB или LSB. В комментариях более подробно указано какой необходим формат.
После этого нам необходимо вставить контакты для модуля в следующую структуру данных:
1 2 3 4 5 6 |
const lmic_pinmap lmic_pins = { .nss = 5, .rxtx = LMIC_UNUSED_PIN, .rst = 4, .dio = {34, 35, LMIC_UNUSED_PIN}, }; |
В эту структуру нам необходимо правильно вставить контакты nss, rst IO0 и IO1 чтобы модуль работал правильно. Теперь, когда все необходимые настройки сделаны, мы можем загрузить код примера в модуль ESP32. Это все что нужно сделать для примера TTN-OTAA. А сейчас рассмотрим как работает пример TTN-ABP.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// LoRaWAN NwkSKey, network session key // This should be in big-endian (aka msb). static const PROGMEM u1_t NWKSKEY[16] = { 0xE8, 0x72, 0x48, 0x5E, 0x58, 0x57, 0xFF, 0xDB, 0xFA, 0x57, 0x0B, 0x18, 0x95, 0x04, 0x90, 0x21 }; // LoRaWAN AppSKey, application session key // This should also be in big-endian (aka msb). static const u1_t PROGMEM APPSKEY[16] = { 0x44, 0x29, 0x95, 0xC5, 0x13, 0xDB, 0x7C, 0xE0, 0xEB, 0x8C, 0x41, 0x07, 0x56, 0x41, 0x82, 0xC8 }; // LoRaWAN end-device address (DevAddr) // See http://thethingsnetwork.org/wiki/AddressSpace // The library converts the address to network byte order as needed, so this should be in big-endian (aka msb) too. static const u4_t DEVADDR = 0x260B9869 ; // <-- Change this address for every node! const lmic_pinmap lmic_pins = { .nss = 5, .rxtx = LMIC_UNUSED_PIN, .rst = 4, .dio = {34, 35, LMIC_UNUSED_PIN}, }; |
Как мы видим, для примера TTN-ABP код примера практически такой же, единственные изменения, которые вам необходимо сделать, это добавить в него свои DEVADDR (Device Address), APPSKEY(App Session Key) и NWKSKEY(Network Session Key). После этого можно загружать код примера в модуль.
Тестирование работы проекта
Тестирование узла LoRaWAN на основе ABP
После загрузки кода программы в модуль ESP32 вы можете открыть окно монитора последовательной связи и посмотреть правильно ли работает модуль или нет.
Как вы можете видеть из этих выходных данных, мы осуществляем непрерывную передачу данных, а на стороне сети TTN мы принимаем наш трафик.
К примеру, вы приняли шестнадцатеричное значение 48656C6C6F2C20776F726C6421. Если вы преобразуете его в тест, то вы получите текст принятого сообщения.
Тестирование узла LoRaWAN на основе OTAA
Теперь загрузите в модуль ESP32 код примера для работы с режимом OTAA и откройте окно монитора последовательной связи. В нем увидите много сообщений и ключи, что будет означать что устройство успешно присоединилось к сети TTN.
На стороне TTN это будет выглядеть примерно как на следующем рисунке.
И на рисунке выше вы можете увидеть сообщение в текстовом формате.
Работа модуля LoRa SX1276
Для тестирования работы схемы мы сделали небольшие изменения в коде и добавили счетчик, который будет инкрементироваться каждую секунду и передавать данные счетчика в сеть TTN. На представленной ниже GIF картинке вы можете увидеть как мы запитали модуль и как он передает данные в TTN. Это можно проверить открыв рядом окно TTN и окно монитора последовательной связи.
Все необходимые файлы проекта вы можете скачать по следующей ссылке.
Исходный код программы
Код для TTN-OTAA
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
/******************************************************************************* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman Copyright (c) 2018 Terry Moore, MCCI Permission is hereby granted, free of charge, to anyone obtaining a copy of this document and accompanying files, to do whatever they want with them without any restriction, including, but not limited to, copying, modification and redistribution. NO WARRANTY OF ANY KIND IS PROVIDED. This example sends a valid LoRaWAN packet with payload "Hello, world!", using frequency and encryption settings matching those of The Things Network. This uses OTAA (Over-the-air activation), where where a DevEUI and application key is configured, which are used in an over-the-air activation procedure where a DevAddr and session keys are assigned/generated for use with all further communication. Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1, 0.1% in g2), but not the TTN fair usage policy (which is probably violated by this sketch when left running for longer)! To use this sketch, first register your application and device with the things network, to set or generate an AppEUI, DevEUI and AppKey. Multiple devices can use the same AppEUI, but each device has its own DevEUI and AppKey. Do not forget to define the radio type correctly in arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. *******************************************************************************/ #include <lmic.h> #include <hal/hal.h> #include <SPI.h> // // For normal use, we require that you edit the sketch to replace FILLMEIN // with values assigned by the TTN console. However, for regression tests, // we want to be able to compile these scripts. The regression tests define // COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- // working but innocuous value. // #ifdef COMPILE_REGRESSION_TEST # define FILLMEIN 0 #else # warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" # define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) #endif // This EUI must be in little-endian format, so least-significant-byte // first. When copying an EUI from ttnctl output, this means to reverse // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, // 0x70. static const u1_t PROGMEM APPEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8); } // This should also be in little endian format, see above. static const u1_t PROGMEM DEVEUI[8] = { 0xF5, 0x33, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70 }; void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8); } // This key should be in big endian format (or, since it is not really a // number but a block of memory, endianness does not really apply). In // practice, a key taken from ttnctl can be copied as-is. static const u1_t PROGMEM APPKEY[16] = { 0xB8, 0x2B, 0xEA, 0xC6, 0x49, 0x5A, 0xAC, 0xBC, 0x36, 0xAC, 0x22, 0x4A, 0xF2, 0x78, 0x33, 0x72 }; void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16); } static uint8_t mydata[] = "Hello, world!"; static osjob_t sendjob; // Schedule TX every this many seconds (might become longer due to duty // cycle limitations). const unsigned TX_INTERVAL = 60; // указание контактов const lmic_pinmap lmic_pins = { .nss = 26, .rxtx = LMIC_UNUSED_PIN, .rst = 25, .dio = {23, 22, LMIC_UNUSED_PIN}, }; void printHex2(unsigned v) { v &= 0xff; if (v < 16) Serial.print('0'); Serial.print(v, HEX); } void onEvent (ev_t ev) { Serial.print(os_getTime()); Serial.print(": "); switch (ev) { case EV_SCAN_TIMEOUT: Serial.println(F("EV_SCAN_TIMEOUT")); break; case EV_BEACON_FOUND: Serial.println(F("EV_BEACON_FOUND")); break; case EV_BEACON_MISSED: Serial.println(F("EV_BEACON_MISSED")); break; case EV_BEACON_TRACKED: Serial.println(F("EV_BEACON_TRACKED")); break; case EV_JOINING: Serial.println(F("EV_JOINING")); break; case EV_JOINED: Serial.println(F("EV_JOINED")); { u4_t netid = 0; devaddr_t devaddr = 0; u1_t nwkKey[16]; u1_t artKey[16]; LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); Serial.print("netid: "); Serial.println(netid, DEC); Serial.print("devaddr: "); Serial.println(devaddr, HEX); Serial.print("AppSKey: "); for (size_t i = 0; i < sizeof(artKey); ++i) { if (i != 0) Serial.print("-"); printHex2(artKey[i]); } Serial.println(""); Serial.print("NwkSKey: "); for (size_t i = 0; i < sizeof(nwkKey); ++i) { if (i != 0) Serial.print("-"); printHex2(nwkKey[i]); } Serial.println(); } // Disable link check validation (automatically enabled // during join, but because slow data rates change max TX // size, we don't use it in this example. LMIC_setLinkCheckMode(0); break; /* || This event is defined but not used in the code. No || point in wasting codespace on it. || || case EV_RFU1: || Serial.println(F("EV_RFU1")); || break; */ case EV_JOIN_FAILED: Serial.println(F("EV_JOIN_FAILED")); break; case EV_REJOIN_FAILED: Serial.println(F("EV_REJOIN_FAILED")); break; case EV_TXCOMPLETE: Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); if (LMIC.txrxFlags & TXRX_ACK) Serial.println(F("Received ack")); if (LMIC.dataLen) { Serial.print(F("Received ")); Serial.print(LMIC.dataLen); Serial.println(F(" bytes of payload")); } // Schedule next transmission os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(TX_INTERVAL), do_send); break; case EV_LOST_TSYNC: Serial.println(F("EV_LOST_TSYNC")); break; case EV_RESET: Serial.println(F("EV_RESET")); break; case EV_RXCOMPLETE: // data received in ping slot Serial.println(F("EV_RXCOMPLETE")); break; case EV_LINK_DEAD: Serial.println(F("EV_LINK_DEAD")); break; case EV_LINK_ALIVE: Serial.println(F("EV_LINK_ALIVE")); break; /* || This event is defined but not used in the code. No || point in wasting codespace on it. || || case EV_SCAN_FOUND: || Serial.println(F("EV_SCAN_FOUND")); || break; */ case EV_TXSTART: Serial.println(F("EV_TXSTART")); break; case EV_TXCANCELED: Serial.println(F("EV_TXCANCELED")); break; case EV_RXSTART: /* do not print anything -- it wrecks timing */ break; case EV_JOIN_TXCOMPLETE: Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept")); break; default: Serial.print(F("Unknown event: ")); Serial.println((unsigned) ev); break; } } void do_send(osjob_t* j) { // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { Serial.println(F("OP_TXRXPEND, not sending")); } else { // Prepare upstream data transmission at the next possible time. LMIC_setTxData2(1, mydata, sizeof(mydata) - 1, 0); Serial.println(F("Packet queued")); } // Next TX is scheduled after TX_COMPLETE event. } void setup() { Serial.begin(9600); Serial.println(F("Starting")); #ifdef VCC_ENABLE // For Pinoccio Scout boards pinMode(VCC_ENABLE, OUTPUT); digitalWrite(VCC_ENABLE, HIGH); delay(1000); #endif // // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. LMIC_reset(); // Start job (sending automatically starts OTAA too) do_send(&sendjob); } void loop() { os_runloop_once(); } |
Код для TTN-ABP
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
/******************************************************************************* * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman * Copyright (c) 2018 Terry Moore, MCCI * * Permission is hereby granted, free of charge, to anyone * obtaining a copy of this document and accompanying files, * to do whatever they want with them without any restriction, * including, but not limited to, copying, modification and redistribution. * NO WARRANTY OF ANY KIND IS PROVIDED. * * This example sends a valid LoRaWAN packet with payload "Hello, * world!", using frequency and encryption settings matching those of * the The Things Network. * * This uses ABP (Activation-by-personalisation), where a DevAddr and * Session keys are preconfigured (unlike OTAA, where a DevEUI and * application key is configured, while the DevAddr and session keys are * assigned/generated in the over-the-air-activation procedure). * * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably * violated by this sketch when left running for longer)! * * To use this sketch, first register your application and device with * the things network, to set or generate a DevAddr, NwkSKey and * AppSKey. Each device should have their own unique values for these * fields. * * Do not forget to define the radio type correctly in * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. * *******************************************************************************/ // References: // [feather] adafruit-feather-m0-radio-with-lora-module.pdf #include <lmic.h> #include <hal/hal.h> #include <SPI.h> // // For normal use, we require that you edit the sketch to replace FILLMEIN // with values assigned by the TTN console. However, for regression tests, // we want to be able to compile these scripts. The regression tests define // COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non- // working but innocuous value. // #ifdef COMPILE_REGRESSION_TEST # define FILLMEIN 0 #else # warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!" # define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN) #endif // LoRaWAN NwkSKey, network session key // This should be in big-endian (aka msb). static const PROGMEM u1_t NWKSKEY[16] = {0xB0, 0x5D, 0x20, 0x8D, 0x5B, 0x1A, 0x9E, 0x0C, 0xB8, 0x6B, 0xF0, 0x4A, 0xAE, 0x0B, 0x88, 0x7F}; // LoRaWAN AppSKey, application session key // This should also be in big-endian (aka msb). static const u1_t PROGMEM APPSKEY[16] = {0x19, 0x5D, 0x2A, 0xBE, 0xEB, 0xC0, 0x09, 0x95, 0xA4, 0xD9, 0x45, 0xD7, 0x3E, 0x1C, 0x91, 0xF3}; // LoRaWAN end-device address (DevAddr) // See http://thethingsnetwork.org/wiki/AddressSpace // The library converts the address to network byte order as needed, so this should be in big-endian (aka msb) too. static const u4_t DEVADDR = 0x260B9D76 ; // <-- Change this address for every node! // These callbacks are only used in over-the-air activation, so they are // left empty here (we cannot leave them out completely unless // DISABLE_JOIN is set in arduino-lmic/project_config/lmic_project_config.h, // otherwise the linker will complain). void os_getArtEui (u1_t* buf) { } void os_getDevEui (u1_t* buf) { } void os_getDevKey (u1_t* buf) { } static uint8_t mydata[] = "Hello, world!"; static osjob_t sendjob; // Schedule TX every this many seconds (might become longer due to duty // cycle limitations). const unsigned TX_INTERVAL = 60; // указание контактов // Adapted for Feather M0 per p.10 of [feather] const lmic_pinmap lmic_pins = { .nss = 5, .rxtx = LMIC_UNUSED_PIN, .rst = 4, .dio = {34, 35, LMIC_UNUSED_PIN}, }; void onEvent (ev_t ev) { Serial.print(os_getTime()); Serial.print(": "); switch(ev) { case EV_SCAN_TIMEOUT: Serial.println(F("EV_SCAN_TIMEOUT")); break; case EV_BEACON_FOUND: Serial.println(F("EV_BEACON_FOUND")); break; case EV_BEACON_MISSED: Serial.println(F("EV_BEACON_MISSED")); break; case EV_BEACON_TRACKED: Serial.println(F("EV_BEACON_TRACKED")); break; case EV_JOINING: Serial.println(F("EV_JOINING")); break; case EV_JOINED: Serial.println(F("EV_JOINED")); break; /* || This event is defined but not used in the code. No || point in wasting codespace on it. || || case EV_RFU1: || Serial.println(F("EV_RFU1")); || break; */ case EV_JOIN_FAILED: Serial.println(F("EV_JOIN_FAILED")); break; case EV_REJOIN_FAILED: Serial.println(F("EV_REJOIN_FAILED")); break; case EV_TXCOMPLETE: Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); if (LMIC.txrxFlags & TXRX_ACK) Serial.println(F("Received ack")); if (LMIC.dataLen) { Serial.println(F("Received ")); Serial.println(LMIC.dataLen); Serial.println(F(" bytes of payload")); } // Schedule next transmission os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); break; case EV_LOST_TSYNC: Serial.println(F("EV_LOST_TSYNC")); break; case EV_RESET: Serial.println(F("EV_RESET")); break; case EV_RXCOMPLETE: // data received in ping slot Serial.println(F("EV_RXCOMPLETE")); break; case EV_LINK_DEAD: Serial.println(F("EV_LINK_DEAD")); break; case EV_LINK_ALIVE: Serial.println(F("EV_LINK_ALIVE")); break; /* || This event is defined but not used in the code. No || point in wasting codespace on it. || || case EV_SCAN_FOUND: || Serial.println(F("EV_SCAN_FOUND")); || break; */ case EV_TXSTART: Serial.println(F("EV_TXSTART")); break; case EV_TXCANCELED: Serial.println(F("EV_TXCANCELED")); break; case EV_RXSTART: /* do not print anything -- it wrecks timing */ break; case EV_JOIN_TXCOMPLETE: Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept")); break; default: Serial.print(F("Unknown event: ")); Serial.println((unsigned) ev); break; } } void do_send(osjob_t* j){ // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { Serial.println(F("OP_TXRXPEND, not sending")); } else { // Prepare upstream data transmission at the next possible time. LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); Serial.println(F("Packet queued")); } // Next TX is scheduled after TX_COMPLETE event. } void setup() { // pinMode(13, OUTPUT); while (!Serial); // wait for Serial to be initialized Serial.begin(115200); delay(100); // per sample code on RF_95 test Serial.println(F("Starting")); #ifdef VCC_ENABLE // For Pinoccio Scout boards pinMode(VCC_ENABLE, OUTPUT); digitalWrite(VCC_ENABLE, HIGH); delay(1000); #endif // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. LMIC_reset(); // Set static session parameters. Instead of dynamically establishing a session // by joining the network, precomputed session parameters are be provided. #ifdef PROGMEM // On AVR, these values are stored in flash and only copied to RAM // once. Copy them to a temporary buffer here, LMIC_setSession will // copy them into a buffer of its own again. uint8_t appskey[sizeof(APPSKEY)]; uint8_t nwkskey[sizeof(NWKSKEY)]; memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); #else // If not running an AVR with PROGMEM, just use the arrays directly LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); #endif #if defined(CFG_eu868) // Set up the channels used by the Things Network, which corresponds // to the defaults of most gateways. Without this, only three base // channels from the LoRaWAN specification are used, which certainly // works, so it is good for debugging, but can overload those // frequencies, so be sure to configure the full frequency range of // your network here (unless your network autoconfigures them). // Setting up channels should happen after LMIC_setSession, as that // configures the minimal channel set. The LMIC doesn't let you change // the three basic settings, but we show them here. Serial.println("we are in 868");; LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band // TTN defines an additional channel at 869.525Mhz using SF9 for class B // devices' ping slots. LMIC does not have an easy way to define set this // frequency and support for class B is spotty and untested, so this // frequency is not configured here. #elif defined(CFG_us915) || defined(CFG_au915) // NA-US and AU channels 0-71 are configured automatically // but only one group of 8 should (a subband) should be active // TTN recommends the second sub band, 1 in a zero based count. // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_… LMIC_selectSubBand(1); #elif defined(CFG_as923) // Set up the channels used in your country. Only two are defined by default, // and they cannot be changed. Use BAND_CENTI to indicate 1% duty cycle. // LMIC_setupChannel(0, 923200000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // LMIC_setupChannel(1, 923400000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // ... extra definitions for channels 2..n here #elif defined(CFG_kr920) // Set up the channels used in your country. Three are defined by default, // and they cannot be changed. Duty cycle doesn't matter, but is conventionally // BAND_MILLI. // LMIC_setupChannel(0, 922100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); // LMIC_setupChannel(1, 922300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); // LMIC_setupChannel(2, 922500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); // ... extra definitions for channels 3..n here. #elif defined(CFG_in866) // Set up the channels used in your country. Three are defined by default, // and they cannot be changed. Duty cycle doesn't matter, but is conventionally // BAND_MILLI. // LMIC_setupChannel(0, 865062500, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); // LMIC_setupChannel(1, 865402500, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); // LMIC_setupChannel(2, 865985000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_MILLI); // ... extra definitions for channels 3..n here. #else # error Region not supported #endif // Disable link check validation LMIC_setLinkCheckMode(0); // TTN uses SF9 for its RX2 window. LMIC.dn2Dr = DR_SF9; // Set data rate and transmit power for uplink LMIC_setDrTxpow(DR_SF7,14); // начинаем работу do_send(&sendjob); } void loop() { unsigned long now; now = millis(); if ((now & 512) != 0) { digitalWrite(13, HIGH); } else { digitalWrite(13, LOW); } os_runloop_once(); } |