├── ckt_esquematico ├── lorawan_freertos_arduino.fzz └── lorawan_freertos_arduino.jpg ├── src ├── BATERIA_defs.h ├── TEMPORIZACOES_defs.h ├── RADIO_LORA_defs.h ├── OLED_defs.h ├── LORAWAN_defs.h └── lorawan_freertos.ino ├── LICENSE └── README.md /ckt_esquematico/lorawan_freertos_arduino.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phfbertoleti/lorawan_freertos_esp32/HEAD/ckt_esquematico/lorawan_freertos_arduino.fzz -------------------------------------------------------------------------------- /ckt_esquematico/lorawan_freertos_arduino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phfbertoleti/lorawan_freertos_esp32/HEAD/ckt_esquematico/lorawan_freertos_arduino.jpg -------------------------------------------------------------------------------- /src/BATERIA_defs.h: -------------------------------------------------------------------------------- 1 | /* Header com definições da leitura da bateria */ 2 | #define TENSAO_MINIMA_DE_BATERIA 3.2 //V 3 | #define NUMERO_LEITURAS_BATERIA 10 4 | #define TEMPO_ENTRE_LEITURAS_BATERIA 500 //ms 5 | -------------------------------------------------------------------------------- /src/TEMPORIZACOES_defs.h: -------------------------------------------------------------------------------- 1 | /* Header com definições de temporizações (filas e semáforos) */ 2 | 3 | #define TEMPO_PARA_LER_FILAS ( TickType_t )100 4 | #define TEMPO_PARA_INSERIR_FILAS ( TickType_t )100 5 | #define TEMPO_PARA_OBTER_SEMAFORO ( TickType_t )1000 6 | -------------------------------------------------------------------------------- /src/RADIO_LORA_defs.h: -------------------------------------------------------------------------------- 1 | /* Header com definições do rádio LoRa (SX1276) */ 2 | 3 | #define GANHO_LORA_DBM 20 //dBm 4 | #define RADIO_RESET_PORT 14 5 | #define RADIO_MOSI_PORT 27 6 | #define RADIO_MISO_PORT 19 7 | #define RADIO_SCLK_PORT 5 8 | #define RADIO_NSS_PORT 18 9 | #define RADIO_DIO_0_PORT 26 10 | #define RADIO_DIO_1_PORT 33 11 | #define RADIO_DIO_2_PORT 32 12 | -------------------------------------------------------------------------------- /src/OLED_defs.h: -------------------------------------------------------------------------------- 1 | /* Header com definições do display OLED */ 2 | 3 | #define OLED_CLEAR_LINE " " 4 | #define OLED_LINE_MAX_SIZE 21 5 | #define OLED_SDA_PIN 4 6 | #define OLED_SCL_PIN 15 7 | #define OLED_ADDRESS 0x3C 8 | #define OLED_RESET 16 9 | #define OLED_SCREEN_HEIGHT 64 10 | #define OLED_SCREEN_WIDTH 128 11 | #define OLED_LINHA_1 0 12 | #define OLED_LINHA_2 16 13 | #define OLED_LINHA_3 32 14 | #define OLED_LINHA_4 48 15 | 16 | #define TEMPO_REFRESH_DISPLAY 500 //ms 17 | -------------------------------------------------------------------------------- /src/LORAWAN_defs.h: -------------------------------------------------------------------------------- 1 | /* Header com definições e constantes do LoRaWAN */ 2 | 3 | /* Constantes do LoraWAN */ 4 | /* - Chaves (network e application keys) */ 5 | static const PROGMEM u1_t NWKSKEY[16] = { }; //coloque aqui sua network session key 6 | static const u1_t PROGMEM APPSKEY[16] = { }; //coloque aqui sua application session key 7 | 8 | /* - Device Address */ 9 | static const u4_t DEVADDR = 0x00000000; //coloque aqui seu device address 10 | 11 | /* - Tempo entre envios de pacotes LoRa */ 12 | const unsigned TX_INTERVAL = 1800; //tempo (em segundos) entre duas transmissoes LoRaWAN 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pedro Bertoleti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Projeto - comunicação LoRaWAN, usando SX1276 e ESP32 2 | 3 | Este repositório contém um projeto de end-device LoRaWAN (ABP), preparado para se conectar a rede pública da ATC (American Tower Corporation). 4 | No quesito hardware, este projeto utiliza a placa de desenvolvimento Heltec WiFi LoRa v1 (alimentado a bateria ou via cabo USB) e sensor o BMP180 (pressão barométrica e temperatura ambiente). Esta placa de desenvolvimento conta com o SX1276 como rádio LoRa e o ESP32 como SoC. 5 | Todo o software embarcado é feito utilizando o FreeRTOS como sistema operacional embarcado. Esse projeto deve ser aberto e compilado na Arduino IDE. O projeto possui as seguintes finalidades: 6 | 7 | * Leitura periódica da pressão barométrica (hPa) e da temperatura ambiente (°C) medidos pelo sensor BMP180 8 | * Leitura periódica da tensão de bateria (V) e carga da bateria (0 .. 100%) 9 | * Envio periódico (30 em 30 minutos) das medições para o gateway LoRaWAN (o software embarcado já está preparado para funcionar com a operadora ATC) 10 | * Exibição das medições no display OLED 128x64 contido na placa de desenvolvimento. 11 | 12 | **IMPORTANTE:** 13 | 1) Este projeto considera a tensão da bateria lida no GPIO37 (ADC1_1), onde a tensão é lida num divisor de tensão 14 | (resistor de 470k / 0,25W e resistor de 100k / 0,25W). 15 | 16 | NÃO SE ESQUEÇA DE USAR O DIVISOR DE TENSÃO AQUI!! O ADC do ESP32 suporta, no máximo, 1,1V (0dB), 17 | enquanto a tensão de bateria pode chegar a 4,2V. 18 | 19 | 2) Esse projeto faz uso da biblioteca "MCCI LoRaWAN LMIC Library". Este projeto foi testado com a versão 2.3.2 da mesma. 20 | 3) Antes de compilar, é preciso deixar o arquivo lmic_project_config.h (dentro na pasta da biblioteca: project_config/lmic_project_config.h) com o conteúdo conforme abaixo: 21 | ``` 22 | // project-specific definitions 23 | //#define CFG_eu868 1 24 | //#define CFG_us915 1 25 | #define CFG_au921 1 26 | //#define CFG_as923 1 27 | // #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP 28 | //#define CFG_in866 1 29 | #define CFG_sx1276_radio 1 30 | //#define LMIC_USE_INTERRUPTS 31 | ``` 32 | 4) Você precisará da network session key, application session key (definidos por você ou pela operadora da ATC) e do Device Address (fornecido pela operadora em caráter experimental ou adquirido por meios oficiais). Substitua estas informações no arquivo LORAWAN_defs.h. 33 | Para obtenção das chaves e tudo mais em termos de conectividade 34 | LoRaWAN, entre em contato com uma das empresas credenciadas pela ATC: 35 | 36 | https://iotopenlabs.io/home/catalogo-de-solucoes/conectividade-lorawan/ 37 | 38 | 5) O circuito esquemático encontra-se na pasta "ckt_esquematico", tanto como figura como no formato Fritzing. 39 | 40 | Este projeto é de autoria de Pedro Bertoleti. 41 | 42 | Agradecimentos as seguintes pessoas: 43 | * Professor Marcelus Guirardello (ETEC - Bento Quirino - Campinas-SP), por toda a ajuda na codificação da comunicação LoRaWAN. 44 | * Renan Tesch, por toda a ajuda na melhoria da rotina de leitura de tensão de bateria. 45 | * José Morais, por toda a ajuda com revisão de código e rotinas de watch dog. 46 | -------------------------------------------------------------------------------- /src/lorawan_freertos.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Projeto: estação de medições (temperatura, pressão barométrica e nível de bateria) com LoRaWAN 10 | * (modo: ABP) usando FreeRTOS. Projeto já configurado para operar na rede LoRaWAN 11 | * da ATC / Everynet no Brasil. 12 | * Este programa NÃO faz uso do modo sleep (light sleep ou deep sleep) do ESP32. Dessa forma, 13 | * não se trata de um projeto low power. 14 | * 15 | * Autor: Pedro Bertoleti e agradecimentos ao professor Marcelus Guirardello por toda a ajuda na codificação da 16 | * comunicação LoRaWAN. 17 | * 18 | * Placa de desenvolvimento utilizada: Heltec LoRa wifi v1 (https://www.curtocircuito.com.br/placa-wifi-lora-32-esp32-lora-display-oled.html?gclid=EAIaIQobChMIqOLj9ZG-6gIVBL7ACh1xvQorEAkYASABEgKLOfD_BwE) 19 | * 20 | * Sensor utilizado: BMP180 (SDA: GPIO4; SCL: GPIO15) 21 | * 22 | * IMPORTANTE: 23 | * 1) Este projeto considera a tensão da bateria lida no GPIO37 (ADC1_1), onde a tensão é lida num divisor de tensão 24 | * (resistor de 470k / 0,25W e resistor de 100k / 0,25W). 25 | * 26 | * VBAT ----------- 27 | * | R1: resistor de 470k / 0,25W 28 | * --- R2: resistor de 100k / 0,25W 29 | * R1 RL: impedância do ADC (calculado: 13M) 30 | * --- 31 | * | ADC1_1 (Vmax: 0.73V) 32 | * |--------- 33 | * | | 34 | * --- --- 35 | * R2 RL 36 | * --- --- 37 | * | | 38 | * GND --------------------- 39 | * 40 | * NÃO SE ESQUEÇA DE USAR O DIVISOR DE TENSÃO AQUI!! O ADC do ESP32 suporta, no máximo, 1,1V (0dB), 41 | * enquanto a tensão de bateria pode chegar a 4,2V. 42 | * 43 | * 2) Esse projeto faz uso da biblioteca "MCCI LoRaWAN LMIC Library". 44 | * Este projeto foi testado com a versão 2.3.2 da mesma. 45 | * 46 | * 3) Antes de compilar, é preciso deixar o arquivo lmic_project_config.h 47 | * (dentro na pasta da biblioteca: project_config/lmic_project_config.h) com o 48 | * conteúdo conforme abaixo: 49 | * 50 | * // project-specific definitions 51 | * //#define CFG_eu868 1 52 | * //#define CFG_us915 1 53 | * #define CFG_au921 1 54 | * //#define CFG_as923 1 55 | * // #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP 56 | * //#define CFG_in866 1 57 | * #define CFG_sx1276_radio 1 58 | * //#define LMIC_USE_INTERRUPTS 59 | */ 60 | 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include "OLED_defs.h" 68 | #include "BATERIA_defs.h" 69 | #include "RADIO_LORA_defs.h" 70 | #include "LORAWAN_defs.h" 71 | #include "TEMPORIZACOES_defs.h" 72 | 73 | /* Definições gerais */ 74 | #define BAUDRATE_SERIAL_DEBUG 115200 75 | #define TEMPO_ENTRE_LEITURAS_BMP180 500 //ms 76 | 77 | /* Constantes do rádio LoRa: GPIOs utilizados para comunicação 78 | com rádio SX1276 */ 79 | const lmic_pinmap lmic_pins = { 80 | .nss = RADIO_NSS_PORT, 81 | .rxtx = LMIC_UNUSED_PIN, 82 | .rst = RADIO_RESET_PORT, 83 | .dio = {RADIO_DIO_0_PORT, RADIO_DIO_1_PORT, LMIC_UNUSED_PIN}, //dio2 não é utilizado. 84 | }; 85 | 86 | /* Estruturas */ 87 | 88 | /* Estrutura que contem leituras de temperatura e pressao do sensor BMP180 */ 89 | typedef struct 90 | { 91 | float temperatura; 92 | float pressao; 93 | }TTemp_pressao; 94 | 95 | /* Estrutura que contem cada linha que pode ser escrita no display OLED da placa */ 96 | typedef struct 97 | { 98 | char linha2[OLED_LINE_MAX_SIZE]; 99 | char linha3[OLED_LINE_MAX_SIZE]; 100 | char linha4[OLED_LINE_MAX_SIZE]; 101 | }TTela_display; 102 | 103 | /* Estrutura com os dados já no formado que são enviados via LoRaWAN */ 104 | typedef struct __attribute__((__packed__)) 105 | { 106 | char temperatura; //1 byte 107 | char tensao_mult_10; //1 byte 108 | short pressao_hpa; //2 byte 109 | char carga_bateria; //1 byte 110 | }TDados_lorawan; 111 | 112 | #define TAMANHO_DADOS_LORAWAN sizeof(TDados_lorawan) 113 | 114 | /* Objetos globais */ 115 | Adafruit_SSD1306 display(OLED_SCREEN_WIDTH, 116 | OLED_SCREEN_HEIGHT, 117 | &Wire, 118 | OLED_RESET, 119 | 100000UL, 120 | 100000UL); 121 | 122 | Adafruit_BMP085 bmp180; 123 | 124 | /* Semáforo */ 125 | SemaphoreHandle_t xI2C_semaphore; 126 | SemaphoreHandle_t xSerial_semaphore; 127 | 128 | /* Filas */ 129 | QueueHandle_t xQueue_temp_pressao; 130 | QueueHandle_t xQueue_bateria; 131 | QueueHandle_t xQueue_display; 132 | 133 | /* Variaveis e objetos globais */ 134 | static osjob_t sendjob; //objeto para job de envio de dados via ABP 135 | esp_adc_cal_characteristics_t adc_cal; //Estrutura que contem as informacoes para calibracao 136 | 137 | /* Relação de tensão x carga da bateria */ 138 | #define PONTOS_MAPEADOS_BATERIA 11 139 | char cargas_mapeadas[PONTOS_MAPEADOS_BATERIA] = { 0, 140 | 3, 141 | 13, 142 | 22, 143 | 39, 144 | 53, 145 | 62, 146 | 74, 147 | 84, 148 | 94, 149 | 100 }; 150 | 151 | float tensao_x_carga[PONTOS_MAPEADOS_BATERIA] = {3.2, //0% 152 | 3.3, //3% 153 | 3.4, //13% 154 | 3.5, //22% 155 | 3.6, //39% 156 | 3.7, //53% 157 | 3.8, //62% 158 | 3.9, //74% 159 | 4.0, //84% 160 | 4.1, //94% 161 | 4.2 }; //100% 162 | 163 | 164 | 165 | /* Tarefas */ 166 | void task_oled( void *pvParameters ); 167 | void task_mede_pressao_temperatura( void *pvParameters ); 168 | void task_formata_medicoes_display( void *pvParameters ); 169 | void task_envio_lorawan( void *pvParameters ); 170 | 171 | /* Protótipos */ 172 | void init_bmp180(void); 173 | void escreve_alerta_bateria_baixa(void); 174 | char calculo_carga_bateria(float tensao_bateria); 175 | float le_temperatura(void); 176 | float le_pressao(void); 177 | void configura_adc_bateria(void); 178 | float le_tensao_bateria(void); 179 | unsigned long diferenca_tempo(unsigned long tstamp); 180 | 181 | /* Função: calcula diferença de tempo (ms) entre tempo atual e referência passada 182 | * Parâmetros: referÊncia de tempo passada 183 | * Retorno: diferença de tempo 184 | */ 185 | unsigned long diferenca_tempo(unsigned long tstamp) 186 | { 187 | return (millis() - tstamp); 188 | } 189 | 190 | 191 | /* Função: le temperatura (do sensor BMP180) 192 | * Parâmetros: nenhum 193 | * Retorno: temperatura lida 194 | */ 195 | float le_temperatura(void) 196 | { 197 | return bmp180.readTemperature(); 198 | } 199 | 200 | /* Função: le sensor de pressão barométrica 201 | * Parâmetros: nenhum 202 | * Retorno: pressao lida 203 | */ 204 | float le_pressao(void) 205 | { 206 | return bmp180.readPressure(); 207 | } 208 | 209 | /* Função: configura ADC para leitura da tensão de bateria 210 | * Parâmetros: nenhum 211 | * Retorno: nenhum 212 | */ 213 | void configura_adc_bateria(void) 214 | { 215 | adc1_config_width(ADC_WIDTH_BIT_12); 216 | adc1_config_channel_atten(ADC1_CHANNEL_1,ADC_ATTEN_DB_0); 217 | 218 | esp_adc_cal_value_t adc_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_0, ADC_WIDTH_BIT_12, 1100, &adc_cal); 219 | 220 | if (adc_type == ESP_ADC_CAL_VAL_EFUSE_VREF) 221 | { 222 | if (xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) == pdTRUE) 223 | { 224 | Serial.println("ADC CALV ref eFuse encontrado: "); 225 | Serial.print(adc_cal.vref); 226 | Serial.print("mV"); 227 | xSemaphoreGive(xSerial_semaphore); 228 | } 229 | } 230 | else if (adc_type == ESP_ADC_CAL_VAL_EFUSE_TP) 231 | { 232 | if (xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) == pdTRUE) 233 | { 234 | Serial.println("ADC CAL Two Point eFuse encontrado"); 235 | xSemaphoreGive(xSerial_semaphore); 236 | } 237 | } 238 | else 239 | { 240 | if (xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) == pdTRUE) 241 | { 242 | Serial.println("ADC CAL Nada encontrado, utilizando Vref padrao: "); 243 | Serial.print(adc_cal.vref); 244 | Serial.print("mV"); 245 | xSemaphoreGive(xSerial_semaphore); 246 | } 247 | } 248 | } 249 | 250 | /* Função: le tensão da bateria 251 | * Parâmetros: nenhum 252 | * Retorno: tensão da bateria (V) 253 | */ 254 | float le_tensao_bateria(void) 255 | { 256 | unsigned long leitura_adc_bateria = 0; 257 | unsigned long soma_leitura_adc_bateria = 0; 258 | float tensao_bateria = 0.0; 259 | float tensao_adc = 0.0; 260 | int i; 261 | 262 | for(i=0; i y = m*tensoa_bateria -m*x0 + y0 339 | 340 | x0 = tensao_x_carga[idx_menor_distancia]; 341 | y0 = cargas_mapeadas[idx_menor_distancia]; 342 | x1 = tensao_x_carga[idx_menor_distancia + 1]; 343 | y1 = cargas_mapeadas[idx_menor_distancia + 1]; 344 | m = ( (y1-y0) / (x1 - x0) ); 345 | carga_bateria_float = ((m*tensao_bateria) - (m*x0) + y0); 346 | } 347 | 348 | /* Caso a bateria esteja totalmente carregada, ainterpolação seja feita nos dois últimos níveis mapeados. 349 | * Nesse caso, se o ADC apresentar algum erro de leitura para cima, a carga calculada poderá ser ligeiramente 350 | * maior que 100%. Nesse caso, trava-se a carga em 100%. 351 | */ 352 | if (carga_bateria_float > 100.0) 353 | carga_bateria_float = 100.0; 354 | 355 | carga_bateria = (char)carga_bateria_float; 356 | return carga_bateria; 357 | } 358 | 359 | /* Callbacks para uso cpm OTAA apenas (por este projeto usar ABP, isso, eles 360 | * estão vazios) */ 361 | void os_getArtEui (u1_t* buf) 362 | { 363 | /* Não utilizado neste projeto */ 364 | } 365 | 366 | void os_getDevEui (u1_t* buf) 367 | { 368 | /* Não utilizado neste projeto */ 369 | } 370 | 371 | void os_getDevKey (u1_t* buf) 372 | { 373 | /* Não utilizado neste projeto */ 374 | } 375 | 376 | /* Callback de evento: todo evento do LoRaAN irá chamar essa 377 | callback, de forma que seja possível saber o status da 378 | comunicação com o gateway LoRaWAN. */ 379 | void onEvent (ev_t ev) 380 | { 381 | if ( xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) == pdTRUE ) 382 | { 383 | Serial.print(os_getTime()); 384 | Serial.print(": "); 385 | Serial.println(ev); 386 | xSemaphoreGive(xSerial_semaphore); 387 | } 388 | 389 | switch(ev) 390 | { 391 | case EV_SCAN_TIMEOUT: 392 | break; 393 | case EV_BEACON_FOUND: 394 | break; 395 | case EV_BEACON_MISSED: 396 | break; 397 | case EV_BEACON_TRACKED: 398 | break; 399 | case EV_JOINING: 400 | break; 401 | case EV_JOINED: 402 | break; 403 | case EV_JOIN_FAILED: 404 | break; 405 | case EV_REJOIN_FAILED: 406 | break; 407 | case EV_TXCOMPLETE: 408 | if ( xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) != pdTRUE ) 409 | { 410 | break; 411 | } 412 | 413 | /* COntrole do semáforo serial obtido. Printa na serial as informações do evento. */ 414 | Serial.println (millis()); 415 | Serial.println(F("EV_TXCOMPLETE (incluindo espera pelas janelas de recepção)")); 416 | 417 | /* Verifica se ack foi recebido do gateway */ 418 | if (LMIC.txrxFlags & TXRX_ACK) 419 | Serial.println(F("Ack recebido")); 420 | 421 | /* Verifica se foram recebidos dados do gateway */ 422 | if (LMIC.dataLen > 0) 423 | { 424 | Serial.println(F("Recebidos ")); 425 | Serial.println(LMIC.dataLen); 426 | Serial.println(F(" bytes (payload) do gateway")); 427 | 428 | /* Como houve recepção de dados do gateway, os coloca 429 | em um array para uso futuro. */ 430 | uint8_t dados_recebidos = LMIC.frame[LMIC.dataBeg + 0]; 431 | Serial.print(F("Dados recebidos: ")); 432 | Serial.write(dados_recebidos); 433 | } 434 | 435 | /* Devolve o controle do semáforo da serial */ 436 | xSemaphoreGive(xSerial_semaphore); 437 | 438 | break; 439 | 440 | case EV_LOST_TSYNC: 441 | break; 442 | case EV_RESET: 443 | break; 444 | case EV_RXCOMPLETE: 445 | break; 446 | case EV_LINK_DEAD: 447 | break; 448 | case EV_LINK_ALIVE: 449 | break; 450 | case EV_TXSTART: 451 | if ( xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) == pdTRUE ) 452 | { 453 | Serial.println(F("EV_TXSTART")); 454 | Serial.println (millis()); 455 | Serial.println(LMIC.freq); 456 | xSemaphoreGive(xSerial_semaphore); 457 | } 458 | break; 459 | default: 460 | break; 461 | } 462 | } 463 | 464 | /* Função para envio de dados ao gateway LoRaWAN */ 465 | void do_send(osjob_t* j) 466 | { 467 | static uint8_t mydata[TAMANHO_DADOS_LORAWAN]; 468 | float tensao_bateria_mult_10; 469 | float tensao_bateria; 470 | char carga_bateria; 471 | TDados_lorawan dados_lorawan; 472 | TTemp_pressao temp_pressao; 473 | BaseType_t resultado_peek_fila; 474 | 475 | /* le temperatura e pressao */ 476 | esp_task_wdt_reset(); 477 | 478 | do{ 479 | resultado_peek_fila = xQueuePeek(xQueue_temp_pressao, (void *)&temp_pressao, TEMPO_PARA_LER_FILAS); 480 | }while(resultado_peek_fila != pdTRUE); 481 | 482 | esp_task_wdt_reset(); 483 | 484 | /* le tensão e calcula carga da bateria */ 485 | esp_task_wdt_reset(); 486 | 487 | do{ 488 | resultado_peek_fila = xQueuePeek(xQueue_bateria, (void *)&tensao_bateria, TEMPO_PARA_LER_FILAS); 489 | }while(resultado_peek_fila != pdTRUE); 490 | 491 | esp_task_wdt_reset(); 492 | 493 | tensao_bateria_mult_10 = tensao_bateria*10.0; 494 | carga_bateria = calculo_carga_bateria(tensao_bateria); 495 | 496 | /* Formata dados a serem enviados na estrutura */ 497 | dados_lorawan.temperatura = (char)temp_pressao.temperatura; 498 | dados_lorawan.tensao_mult_10 = (char)(tensao_bateria_mult_10); 499 | dados_lorawan.pressao_hpa = (short)(temp_pressao.pressao/100.0); 500 | dados_lorawan.carga_bateria = carga_bateria; 501 | memcpy(mydata, (uint8_t *)&dados_lorawan, TAMANHO_DADOS_LORAWAN); 502 | 503 | /* Verifica se já há um envio sendo feito. 504 | Em caso positivo, o envio atual é suspenso. */ 505 | if (LMIC.opmode & OP_TXRXPEND) 506 | { 507 | if ( xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) == pdTRUE ) 508 | { 509 | Serial.println(F("OP_TXRXPEND: ha um envio ja pendente, portanto o envio atual nao sera feito")); 510 | xSemaphoreGive(xSerial_semaphore); 511 | } 512 | } 513 | else 514 | { 515 | /* Aqui, o envio pode ser feito. */ 516 | /* O pacote LoRaWAN é montado e o coloca na fila de envio. */ 517 | LMIC_setTxData2(4, mydata, sizeof(mydata), 0); 518 | 519 | if ( xSemaphoreTake(xSerial_semaphore, TEMPO_PARA_OBTER_SEMAFORO) == pdTRUE ) 520 | { 521 | Serial.println(F("Pacote LoRaWAN na fila de envio.")); 522 | xSemaphoreGive(xSerial_semaphore); 523 | } 524 | } 525 | esp_task_wdt_reset(); 526 | } 527 | 528 | 529 | /* Função: inicializa sensor BMP180 530 | * Parâmetros: nenhum 531 | * Retorno: nenhum 532 | */ 533 | void init_bmp180(void) 534 | { 535 | if (!bmp180.begin()) 536 | { 537 | Serial.println("[BMP180] Sensor nao encontrado."); 538 | while (1) 539 | { 540 | delay(1); 541 | } 542 | } 543 | } 544 | 545 | void setup() 546 | { 547 | /* Inicializa serial de debug */ 548 | Serial.begin(BAUDRATE_SERIAL_DEBUG); 549 | 550 | /* Inicializa I²C (para comunicação com OLED e BMP180)*/ 551 | Wire.begin(OLED_SDA_PIN, OLED_SCL_PIN); 552 | 553 | /* Inicializa display */ 554 | if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) 555 | { 556 | Serial.println("[ERRO] não foi possivel inicializar display. O NodeMCU será reiniciado em 1s..."); 557 | delay(1000); 558 | ESP.restart(); 559 | } 560 | else 561 | { 562 | Serial.println("Display inicializado."); 563 | 564 | display.clearDisplay(); 565 | display.setTextColor(WHITE); 566 | display.setTextSize(1); 567 | display.setCursor(0,OLED_LINHA_1); 568 | display.println("Inicializando..."); 569 | display.display(); 570 | } 571 | 572 | /* Inicializa comunicação com sensor BMP180 */ 573 | init_bmp180(); 574 | 575 | /* Criação dos semáforos para serial e I²C (usados para comunicação com OLED e BMP180) */ 576 | xSerial_semaphore = xSemaphoreCreateMutex(); 577 | xI2C_semaphore = xSemaphoreCreateMutex(); 578 | 579 | if ( (xI2C_semaphore == NULL) || (xSerial_semaphore == NULL) ) 580 | { 581 | Serial.println("Falha ao criar semáforos."); 582 | delay(1000); 583 | ESP.restart(); 584 | } 585 | 586 | /* Criação das filas */ 587 | xQueue_temp_pressao = xQueueCreate( 1, sizeof( TTemp_pressao ) ); 588 | xQueue_display = xQueueCreate( 1, sizeof( TTela_display ) ); 589 | xQueue_bateria = xQueueCreate( 1, sizeof( float ) ); 590 | 591 | if ( (xQueue_temp_pressao == NULL) || (xQueue_display == NULL) || (xQueue_bateria == NULL) ) 592 | { 593 | Serial.println("Falha ao criar filas."); 594 | delay(1000); 595 | ESP.restart(); 596 | } 597 | 598 | /* Inicia o Task WDT com 60 segundos */ 599 | esp_task_wdt_init(60, true); 600 | 601 | /* Agenda execução das tarefas */ 602 | xTaskCreatePinnedToCore(task_oled, 603 | "oled", 604 | 4096, 605 | NULL, 606 | 5, 607 | NULL, 608 | 1); 609 | 610 | xTaskCreate(task_envio_lorawan , 611 | "lorawan", 612 | 4096, 613 | NULL, 614 | 10, 615 | NULL ); 616 | 617 | xTaskCreate(task_mede_pressao_temperatura , 618 | "temp_pressao", 619 | 4096, 620 | NULL, 621 | 7, 622 | NULL ); 623 | 624 | xTaskCreate(task_formata_medicoes_display , 625 | "formata_tela", 626 | 4096, 627 | NULL, 628 | 8, 629 | NULL ); 630 | 631 | xTaskCreate(task_bateria , 632 | "bateria", 633 | 4096, 634 | NULL, 635 | 9, 636 | NULL ); 637 | } 638 | 639 | void loop() 640 | { 641 | /* Nada é feito aqui. As tarefas cuidam de tudo. */ 642 | } 643 | 644 | /* Tarefa responsável por atualizar display OLED */ 645 | void task_oled( void *pvParameters ) 646 | { 647 | TTela_display tela_display; 648 | 649 | /* Habilita o monitoramento do Task WDT nesta tarefa */ 650 | esp_task_wdt_add(NULL); 651 | 652 | while(1) 653 | { 654 | if ( xSemaphoreTake(xI2C_semaphore, TEMPO_PARA_OBTER_SEMAFORO ) != pdTRUE ) 655 | { 656 | /* Alimenta WDT e tenta novamente */ 657 | esp_task_wdt_reset(); 658 | continue; 659 | } 660 | 661 | if (xQueueReceive(xQueue_display, (void *)&tela_display, TEMPO_PARA_LER_FILAS) == pdTRUE) 662 | { 663 | display.clearDisplay(); 664 | display.setCursor(0,OLED_LINHA_1); 665 | display.println(" Leituras"); 666 | display.setCursor(0,OLED_LINHA_2); 667 | display.print(tela_display.linha2); 668 | display.setCursor(0,OLED_LINHA_3); 669 | display.print(tela_display.linha3); 670 | display.setCursor(0,OLED_LINHA_4); 671 | display.print(tela_display.linha4); 672 | display.display(); 673 | } 674 | 675 | xSemaphoreGive(xI2C_semaphore); 676 | esp_task_wdt_reset(); 677 | vTaskDelay( TEMPO_REFRESH_DISPLAY / portTICK_PERIOD_MS ); 678 | } 679 | } 680 | 681 | /* Tarefa responsavel por receber todas as medições e colocá-las (formatadas para o display) 682 | em uma estrutura */ 683 | void task_formata_medicoes_display( void *pvParameters ) 684 | { 685 | TTemp_pressao temp_pressao; 686 | TTela_display tela_display; 687 | BaseType_t resultado_envio_fila_display; 688 | float tensao_bateria; 689 | 690 | /* Tempo para comunciação I²C com display acontecer sem problemas */ 691 | vTaskDelay( 1000 / portTICK_PERIOD_MS ); 692 | 693 | /* Habilita o monitoramento do Task WDT nesta tarefa */ 694 | esp_task_wdt_add(NULL); 695 | 696 | while(1) 697 | { 698 | esp_task_wdt_reset(); 699 | 700 | xQueuePeek(xQueue_temp_pressao, (void *)&temp_pressao, TEMPO_PARA_LER_FILAS); 701 | xQueuePeek(xQueue_bateria, (void *)&tensao_bateria, TEMPO_PARA_LER_FILAS); 702 | 703 | sprintf(tela_display.linha2, "T: %.1fC/P: %.1fkPa", temp_pressao.temperatura, (temp_pressao.pressao/1000)); 704 | sprintf(tela_display.linha3, "Bat: %.2fV", tensao_bateria); 705 | sprintf(tela_display.linha4, "-4-"); 706 | 707 | esp_task_wdt_reset(); 708 | 709 | do 710 | { 711 | resultado_envio_fila_display = xQueueSend(xQueue_display, (void *)&tela_display, TEMPO_PARA_INSERIR_FILAS); 712 | }while (resultado_envio_fila_display != pdTRUE); 713 | 714 | esp_task_wdt_reset(); 715 | 716 | vTaskDelay( 1000 / portTICK_PERIOD_MS ); 717 | } 718 | } 719 | 720 | /* Tarefa responsavel por medir pressao e temperatura (BMP180) */ 721 | void task_mede_pressao_temperatura( void *pvParameters ) 722 | { 723 | TTemp_pressao temp_pressao; 724 | 725 | vTaskDelay( 1000 / portTICK_PERIOD_MS ); 726 | 727 | /* Habilita o monitoramento do Task WDT nesta tarefa */ 728 | esp_task_wdt_add(NULL); 729 | 730 | while(1) 731 | { 732 | esp_task_wdt_reset(); 733 | 734 | if( xSemaphoreTake( xI2C_semaphore, TEMPO_PARA_OBTER_SEMAFORO ) == pdTRUE ) 735 | { 736 | temp_pressao.temperatura = le_temperatura(); 737 | temp_pressao.pressao = le_pressao(); 738 | xQueueOverwrite(xQueue_temp_pressao, (void *)&temp_pressao); 739 | xSemaphoreGive(xI2C_semaphore); 740 | } 741 | 742 | vTaskDelay( TEMPO_ENTRE_LEITURAS_BMP180 / portTICK_PERIOD_MS ); 743 | } 744 | } 745 | 746 | /* Tarefa responsavel por enviar via LoRaWAN as medições */ 747 | void task_envio_lorawan( void *pvParameters ) 748 | { 749 | int b; 750 | uint8_t appskey[sizeof(APPSKEY)]; 751 | uint8_t nwkskey[sizeof(NWKSKEY)]; 752 | unsigned long timestamp_envio; 753 | 754 | /* Inicializa comunicação SPI com rádio LoRa */ 755 | SPI.begin(RADIO_SCLK_PORT, RADIO_MISO_PORT, RADIO_MOSI_PORT); 756 | 757 | /* Inicializa stack LoRaWAN */ 758 | os_init(); 759 | LMIC_reset(); 760 | 761 | /* Inicializa chaves usadas na comunicação ABP */ 762 | memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); 763 | memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); 764 | LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); 765 | 766 | /* Faz inicializações de rádio pertinentes a região do 767 | gateway LoRaWAN (ATC / Everynet Brasil) */ 768 | for (b=0; b<8; ++b) 769 | LMIC_disableSubBand(b); 770 | 771 | LMIC_enableChannel(0); // 915.2 MHz 772 | LMIC_enableChannel(1); // 915.4 MHz 773 | LMIC_enableChannel(2); // 915.6 MHz 774 | LMIC_enableChannel(3); // 915.8 MHz 775 | LMIC_enableChannel(4); // 916.0 MHz 776 | LMIC_enableChannel(5); // 916.2 MHz 777 | LMIC_enableChannel(6); // 916.4 MHz 778 | LMIC_enableChannel(7); // 916.6 MHz 779 | 780 | LMIC_setAdrMode(0); 781 | LMIC_setLinkCheckMode(0); 782 | 783 | /* Data rate para janela de recepção RX2 */ 784 | LMIC.dn2Dr = DR_SF12CR; 785 | 786 | /* Configura data rate de transmissão e ganho do rádio 787 | LoRa (dBm) na transmissão */ 788 | LMIC_setDrTxpow(DR_SF12, GANHO_LORA_DBM); 789 | 790 | /* Força envio do primeiro pacote */ 791 | do_send(&sendjob); 792 | 793 | /* Inicializa temporização para envio LoRaWAN */ 794 | timestamp_envio = millis(); 795 | 796 | /* Habilita o monitoramento do Task WDT nesta tarefa */ 797 | esp_task_wdt_add(NULL); 798 | 799 | while(1) 800 | { 801 | esp_task_wdt_reset(); 802 | 803 | /* Envia um pacote LoRaWAN de acordo com periodicidade definida em TX_INTERVAL */ 804 | if (diferenca_tempo(timestamp_envio) >= TX_INTERVAL*1000) 805 | { 806 | do_send(&sendjob); 807 | timestamp_envio = millis(); 808 | } 809 | 810 | os_runloop_once(); 811 | 812 | vTaskDelay( ( TickType_t )1 ); 813 | } 814 | } 815 | 816 | /* Tarefa responsavel por ler a tensão da bateria */ 817 | void task_bateria( void *pvParameters ) 818 | { 819 | float tensao_bateria; 820 | 821 | /* Configura ADC da bateria */ 822 | configura_adc_bateria(); 823 | 824 | /* Habilita o monitoramento do Task WDT nesta tarefa */ 825 | esp_task_wdt_add(NULL); 826 | 827 | while(1) 828 | { 829 | esp_task_wdt_reset(); 830 | 831 | /* Le o ADC considerando sua calibração */ 832 | tensao_bateria = le_tensao_bateria(); 833 | xQueueOverwrite(xQueue_bateria, (void *)&tensao_bateria); 834 | vTaskDelay( TEMPO_ENTRE_LEITURAS_BATERIA / portTICK_PERIOD_MS ); 835 | } 836 | } 837 | --------------------------------------------------------------------------------