{"id":4086,"date":"2023-07-27T05:29:18","date_gmt":"2023-07-27T10:29:18","guid":{"rendered":"http:\/\/blog.espol.edu.ec\/girni\/?p=4086"},"modified":"2026-08-04T22:15:42","modified_gmt":"2026-08-05T03:15:42","slug":"lorawan-pluviometro-archivo-ino","status":"publish","type":"post","link":"https:\/\/blog.espol.edu.ec\/girni\/lorawan-pluviometro-archivo-ino\/","title":{"rendered":"LoRaWan - Pluviometro: Archivo.ino"},"content":{"rendered":"\n<p>Las instrucciones para control del pluvi\u00f3metro y sensores complementarios se encuentran segmentadas por bloques:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Bloque principal<\/li>\n\n\n\n<li>Sensores<\/li>\n\n\n\n<li>Env\u00edo de tramas<\/li>\n\n\n\n<li>Recepci\u00f3n de tramas<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p>[ <a href=\"#sensores\">sensores<\/a> ] [ <a href=\"#envia\">env\u00eda<\/a> ] [ <a href=\"#recibe\">recibe<\/a> ] [ <a href=\"#principal\">principal<\/a> ]<br><a id=\"sensores\"><\/a>...<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<h2 class=\"wp-block-heading\">Sensores<\/h2>\n\n\n\n<p>El bloque de sensores se simplifica por partes de control y comunicaci\u00f3n acorde al&nbsp; tipo de sensor:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pulsos: Pluvi\u00f3metro<\/li>\n\n\n\n<li>Anal\u00f3gicos: bater\u00eda<\/li>\n\n\n\n<li>I2C: humedad, temperatura y presi\u00f3n atmosf\u00e9rica<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Pulsos: pluvi\u00f3metro<\/h3>\n\n\n\n<p>El pluvi\u00f3metro se centra en medir el vaciado de la \"doble cubeta basculante\".<\/p>\n\n\n\n<figure class=\"wp-block-image alignright\"><a href=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/Pluviometro_CubetasBaculantes.png\"><img loading=\"lazy\" decoding=\"async\" width=\"283\" height=\"215\" src=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/Pluviometro_CubetasBaculantes.png\" alt=\"Doble Cubeta Basculante\" class=\"wp-image-4111\" \/><\/a><\/figure>\n\n\n\n<p>Para la detecci\u00f3n se usa un interruptor magn\u00e9tico que marca el cambio de estado mediante un pulso (<code>pulse<\/code>) conectado al pin asignado (<code>pluvioPin<\/code>). Para que la detecci\u00f3n sea independiente del estado del dispositivo se usa una interrupci\u00f3n de instrucciones (<code>pulse_tip<\/code>)&nbsp; que cuenta el pulso (<code>pulse<\/code>) en flanco de subida (<code>RISING<\/code>)y marca el evento (<code>pulse_flag<\/code>).<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>attachInterrupt(pluvioPin, pulse_tip, RISING);<\/code><\/pre>\n\n\n\n<p>El dispositivo cuenta&nbsp; los pulsos generados durante un minuto&nbsp; (<code>sample_period<\/code>) y los almacena en una lista (<code>pulse_list<\/code>) hasta la transmisi\u00f3n de los datos&nbsp; hacia el gateway LoRa.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/Pluviometro_pulse_tip01.png\"><img loading=\"lazy\" decoding=\"async\" width=\"730\" height=\"181\" src=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/Pluviometro_pulse_tip01.png\" alt=\"pulse_tip muestreo\" class=\"wp-image-4113\" srcset=\"https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/Pluviometro_pulse_tip01.png 730w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/Pluviometro_pulse_tip01-300x74.png 300w\" sizes=\"auto, (max-width: 730px) 100vw, 730px\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Anal\u00f3gicos: Bater\u00eda<\/h3>\n\n\n\n<p>El sensor de estado de bater\u00eda es un ADC que toma una lectura del estado de la bater\u00eda antes de cada transmisi\u00f3n LoRa.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">I2C: humedad, temperatura y presi\u00f3n atmosf\u00e9rica<\/h3>\n\n\n\n<p>Los sensores de humedad, temperatura y presi\u00f3n son de tipo I2C que se activan solo al ser le\u00eddos en cada ciclo de transmisi\u00f3n mediante el pin&nbsp;<code>Vext<\/code>, Posterior a la lectura apagan para optimizar el ahorro de energ\u00eda.<\/p>\n\n\n\n<p>Antes de cada lectura, se realiza una verificaci\u00f3n de estado de conexi\u00f3n al sensor optimizando el tiempo de \u00e9ste proceso en caso de fallas del sensor expuesto a condiciones ambientales.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroSensores01.png\"><img loading=\"lazy\" decoding=\"async\" width=\"894\" height=\"113\" src=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroSensores01.png\" alt=\"LoRaWan pluviometro sensores bloque\" class=\"wp-image-4089\" srcset=\"https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroSensores01.png 894w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroSensores01-300x38.png 300w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroSensores01-768x97.png 768w\" sizes=\"auto, (max-width: 894px) 100vw, 894px\" \/><\/a><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code alignwide\"><pre class=\"brush: arduino; title: ; notranslate\" title=\"\">\n\/\/ sensors block\n\/\/ pluviometer pulse counter at interrupt\nvoid pulse_tip(){\n \u00a0if (pulse&lt;=255){pulse = pulse + 1;}\n \u00a0pulse_flag = true;\n}\nvoid pulse_check(){\n \u00a0if (pulse_flag==true){\n \u00a0\u00a0\u00a0if (pulse&gt;255){pulse=255;}\n \u00a0\u00a0\u00a0pulse_flag = false;\n \u00a0\u00a0\u00a0tipfree = 0;\n \u00a0\u00a0\u00a0if (serial_msg){\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(&quot;_|_: &quot;);Serial.println(pulse);\n \u00a0\u00a0\u00a0}\n \u00a0\u00a0\u00a0turnOnRGB(128,200);turnOffRGB();\n \u00a0}\n}\n\nvoid minute_check(){\n \u00a0int i = 0; \n \u00a0pulse_list&#x5B;i_minute] = pulse;\n \u00a0if (i_minute&gt;=n_minute){\n \u00a0\u00a0\u00a0\/\/ pluviometer update\n \u00a0\u00a0\u00a0for (i = 0; i &lt; n_minute; i = i + 1) {\n \u00a0\u00a0\u00a0\u00a0\u00a0pluviometer&#x5B;i] = pulse_list&#x5B;i];\n \u00a0\u00a0\u00a0\u00a0\u00a0pulse_list&#x5B;i] = 0;\n \u00a0\u00a0\u00a0}\n \u00a0\u00a0\u00a0i_minute = 0;\n \u00a0\u00a0\u00a0loop_0 = false;\n \u00a0\u00a0\u00a0sensor_flag = true;\n \u00a0\u00a0\u00a0tx_done = false;\n \u00a0}\n\n \u00a0if (serial_msg){\n \u00a0\u00a0\u00a0Serial.print(&quot;i_minute:&quot;);Serial.print(i_minute);\n \u00a0\u00a0\u00a0Serial.print(&quot; ; pulse_list:&quot;);\n \u00a0\u00a0\u00a0for (i = 0; i &lt; n_minute; i = i + 1) {\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(&quot; &quot;); Serial.print(pulse_list&#x5B;i]);\n \u00a0\u00a0\u00a0}\n \u00a0\u00a0\u00a0Serial.print(&quot; ; pluviometer:&quot;);\n \u00a0\u00a0\u00a0for (i = 0; i &lt; n_minute; i = i + 1) {\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(&quot; &quot;); Serial.print(pluviometer&#x5B;i]);\n \u00a0\u00a0\u00a0}\n \u00a0\u00a0\u00a0Serial.print(&quot; ; tipfree &quot;); Serial.println(tipfree);\n \u00a0}\n}\n\nvoid read_sensors(){\n \u00a0batteryVoltage = getBatteryVoltage();\n\n \u00a0\/\/ sensors check i2c on\n \u00a0byte sensor_error, address;\n \u00a0digitalWrite(Vext, LOW); delay(300);\n \u00a0Wire.begin();\n \u00a0\n \u00a0humitemp_active = false;\n \u00a0Wire.beginTransmission(0x40);\n \u00a0sensor_error = Wire.endTransmission();\n \u00a0if (sensor_error == 0){humitemp_active = true;}\n\n \u00a0prestemp_active = false;\n \u00a0Wire.beginTransmission(0x77);\n \u00a0sensor_error = Wire.endTransmission();\n \u00a0if (sensor_error == 0){prestemp_active = true;}\n\n \u00a0if (humitemp_active){hdc1080.begin(0x40);}\n \u00a0if (prestemp_active){bmp.begin();}\n \u00a0\u00a0\u00a0\n \u00a0if (humitemp_active){\n \u00a0\u00a0\u00a0ht_temp = hdc1080.readTemperature();\n \u00a0\u00a0\u00a0ht_humi = hdc1080.readHumidity();\n \u00a0}\n\n \u00a0float bar_alti = 0, bar_sea_pres = 0, bar_sea_alt = 0;\n \u00a0if (prestemp_active){\n \u00a0\u00a0\u00a0bar_temp = bmp.readTemperature();\n \u00a0\u00a0\u00a0bar_pres = bmp.readPressure();\n \u00a0\u00a0\u00a0bar_alti = bmp.readAltitude();\n \u00a0\u00a0\u00a0bar_sea_pres = bmp.readSealevelPressure();\n \u00a0\u00a0\u00a0bar_sea_alt \u00a0= bmp.readAltitude(100800);\n \u00a0}\n\n \u00a0\/\/ sensors I2C off\n \u00a0Wire.end();\n \u00a0digitalWrite(Vext, HIGH);\n\n \u00a0if (serial_msg){\n \u00a0\u00a0\u00a0Serial.print(&quot;Battery = &quot;);Serial.print(batteryVoltage);\n \u00a0\u00a0\u00a0\n \u00a0\u00a0\u00a0Serial.print(&quot; ; --- sensor &quot;);\n \u00a0\u00a0\u00a0Serial.print(&quot;hum_temp: &quot;); Serial.print(humitemp_active);\n \u00a0\u00a0\u00a0Serial.print(&quot; ; bar_temp: &quot;); Serial.println(prestemp_active);\n \u00a0\u00a0\u00a0\n \u00a0\u00a0\u00a0if (humitemp_active){\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(&quot;HDC1080 Temperature = &quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(ht_temp); Serial.print(&quot; C ; Humidity = &quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(ht_humi);Serial.println(&quot; %&quot;);\n \u00a0\u00a0\u00a0}\n \u00a0\u00a0\u00a0if (prestemp_active){\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(&quot;BMP180 \u00a0Temperature = &quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(bar_temp);Serial.println(&quot; C&quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(&quot; \u00a0Pressure = \u00a0\u00a0\u00a0\u00a0\u00a0&quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(bar_pres);Serial.print(&quot; Pa ; Altitude = &quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(bar_alti);Serial.println(&quot; meters&quot;);\n\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(&quot; \u00a0SeaLevel(calc)= &quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(bar_sea_pres); Serial.print(&quot; Pa ; Altitude = &quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0Serial.print(bar_sea_alt); Serial.println(&quot; meters&quot;);\n \u00a0\u00a0\u00a0}\n \u00a0Serial.println(&quot;&quot;);\n \u00a0}\n}\n\nvoid onSleep(){\n \u00a0Serial.print(&quot;\\n \u00a0i_minute:&quot;);Serial.print(i_minute);\n \u00a0Serial.printf(&quot; ;Going into lowpower mode, %d ms later wake up.\\r\\n&quot;,timetillwakeup);\n \u00a0lowpower = 1;\n \u00a0\/\/timetillwakeup ms later wake up;\n \u00a0timetillwakeup = sample_period*1000 - (millis()-sample_ti);\n \u00a0TimerSetValue( &amp;wakeUp, timetillwakeup );\n \u00a0TimerStart( &amp;wakeUp );\n}\nvoid onWakeUp(){\n \u00a0Serial.println(&quot; \u00a0... Woke up by time&quot;);\/\/, %d ms later into lowpower mode.\\r\\n&quot;,timetillsleep);\n\tlowpower = 0;\n \u00a0sleep_flag = false;\n \u00a0sleep_done = true;\n\t\/\/timetillsleep ms later into lowpower mode;\n\t\/\/TimerSetValue( &amp;sleep, timetillsleep );\n\t\/\/TimerStart( &amp;sleep );\n}\n<\/pre><\/div>\n\n\n<p>Adicionalmente existen dos procedimientos usados para el control del modo de ahorro de energ\u00eda: <code>onSleep()<\/code> y <code>onWakeup()<\/code>, que junto a la instrucci\u00f3n <code>lowPowerHandler()<\/code> permiten un mayor ahorro de energ\u00eda en los ciclos donde no se han producido pulsos del pluvi\u00f3metro. Esto minimiza el uso de bater\u00eda ante los periodos sin lluvia y manteniendo a\u00fan el registro por minuto durante largos periodos.<\/p>\n\n\n\n<p>B\u00e1sicamente, se usa el modo de ahorro de energ\u00eda si no han ocurrido eventos de conteo del pluvi\u00f3metro durante al menos un ciclo de <code>n_minute<\/code>, caso contrario se mantiene el controlador activo.<\/p>\n\n\n\n<p>[ <a href=\"#sensores\">sensores<\/a> ] [ <a href=\"#envia\">env\u00eda<\/a> ] [ <a href=\"#recibe\">recibe<\/a> ] [ <a href=\"#principal\">principal<\/a> ]<br><a id=\"envia\"><\/a>...<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">LoRaWan - Envia trama<\/h2>\n\n\n\n<p>La trama se configura usando los par\u00e1metros obtenidos en el dispositivo para cada sensor.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rssi Downlink (1 byte)<\/li>\n\n\n\n<li>Snr Downlink (1 byte)<\/li>\n\n\n\n<li>Datarate Downlink (1 byte)<\/li>\n\n\n\n<li>Voltaje de bater\u00eda (2 bytes)<\/li>\n\n\n\n<li>sensores (15 bytes)<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroEnvia01.png\"><img loading=\"lazy\" decoding=\"async\" width=\"895\" height=\"115\" src=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroEnvia01.png\" alt=\"LoRaWan pluviometro envia\" class=\"wp-image-4091\" srcset=\"https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroEnvia01.png 895w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroEnvia01-300x39.png 300w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroEnvia01-768x99.png 768w\" sizes=\"auto, (max-width: 895px) 100vw, 895px\" \/><\/a><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code alignwide\"><pre class=\"brush: arduino; title: ; notranslate\" title=\"\">\n\/* Prepares the payload of the frame *\/\nstatic void prepareTxFrame( uint8_t port ) {\n \u00a0unsigned char *puc;\n \u00a0signed char *pucs;\n \u00a0\/\/ trama\n \u00a0appDataSize = 15 + n_minute; \/\/ 15+pluvio_list size\n \u00a0appData&#x5B;0] = ack_rssi; \/\/Ack leido en dispositivo\n \u00a0appData&#x5B;1] = ack_snr;\n \u00a0appData&#x5B;2] = ack_datarate;\n \u00a0appData&#x5B;3] = (uint8_t)batteryVoltage;\n \u00a0appData&#x5B;4] = (uint8_t)(batteryVoltage&gt;&gt;8);\n\n \u00a0\/\/ convierte float a bytes\n \u00a0int ht_temp_int = round(ht_temp*100);\n \u00a0pucs = (signed char *)(&amp;ht_temp_int);\n \u00a0appData&#x5B;5] = pucs&#x5B;0];\n \u00a0appData&#x5B;6] = pucs&#x5B;1];\n\n \u00a0\/\/ convierte float a bytes\n \u00a0int ht_humi_int = round(ht_humi*100);\n \u00a0puc = (unsigned char *)(&amp;ht_humi_int);\n \u00a0appData&#x5B;7] = puc&#x5B;0];\n \u00a0appData&#x5B;8] = puc&#x5B;1];\n\n \u00a0\/\/ convierte float a bytes\n \u00a0int bar_temp_int = round(bar_temp*100);\n \u00a0pucs = (signed char *)(&amp;bar_temp_int);\n \u00a0appData&#x5B;9] = pucs&#x5B;0];\n \u00a0appData&#x5B;10] = pucs&#x5B;1];\n\n \u00a0\/\/ convierte float a bytes\n \u00a0puc = (unsigned char *)(&amp;bar_pres);\n \u00a0appData&#x5B;11] = puc&#x5B;0];\n \u00a0appData&#x5B;12] = puc&#x5B;1];\n \u00a0appData&#x5B;13] = puc&#x5B;2];\n \u00a0appData&#x5B;14] = puc&#x5B;3];\n\n \u00a0for (int i = 0; i &lt; n_minute; i = i + 1) {\n \u00a0\u00a0\u00a0appData&#x5B;15+i] = pluviometer&#x5B;i];\n \u00a0}\n\n \u00a0\/\/ if (serial_msg){\n \u00a0\/\/ \u00a0\u00a0Serial.print(&quot;send pluviometer:&quot;);\n \u00a0\/\/ \u00a0\u00a0for (int i = 0; i &lt; n_minute; i = i + 1) {\n \u00a0\/\/ \u00a0\u00a0\u00a0\u00a0Serial.print(&quot; &quot;);\n \u00a0\/\/ \u00a0\u00a0\u00a0\u00a0Serial.print(pluviometer&#x5B;i]);\n \u00a0\/\/ \u00a0\u00a0}\n \u00a0\/\/ \u00a0\u00a0Serial.println(&quot;&quot;);\n \u00a0\/\/ }\n \u00a0for (int i = 0; i &lt; n_minute; i = i + 1) {\n \u00a0\u00a0\u00a0pluviometer&#x5B;i]=0;\n \u00a0}\n}\n<\/pre><\/div>\n\n\n<p>[ <a href=\"#sensores\">sensores<\/a> ] [ <a href=\"#envia\">env\u00eda<\/a> ] [ <a href=\"#recibe\">recibe<\/a> ] [ <a href=\"#principal\">principal<\/a> ]<br><a id=\"recibe\"><\/a>...<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">LoRaWan - Recibe trama<\/h2>\n\n\n\n<p>Manejo de tramas recibidas por el dispositivo para control o recibo de recibidos (Ack)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroRecibe01.png\"><img loading=\"lazy\" decoding=\"async\" width=\"894\" height=\"119\" src=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroRecibe01.png\" alt=\"\" class=\"wp-image-4090\" srcset=\"https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroRecibe01.png 894w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroRecibe01-300x40.png 300w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroRecibe01-768x102.png 768w\" sizes=\"auto, (max-width: 894px) 100vw, 894px\" \/><\/a><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code alignwide\"><pre class=\"brush: arduino; title: ; notranslate\" title=\"\">\n\/\/downlink data handle and downLink Ack Handle functions\nvoid downLinkDataHandle(McpsIndication_t *mcpsIndication) {\n \u00a0\/\/ revisa parametros\n \u00a0Serial.print(&quot;\\nLleg\u00f3 un mensaje para dispositivo...&quot;);\n \u00a0\/\/ Serial.print(&quot;Rssi: &quot;);\n \u00a0\/\/ Serial.println(mcpsIndication-&gt;Rssi);\n\n \u00a0\/\/ Serial.printf(&quot;+REV DATA:%s,RXSIZE %d,PORT %d\\r\\n&quot;,\n \u00a0\/\/ \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mcpsIndication-&gt;RxSlot ? &quot;RXWIN2&quot; : &quot;RXWIN1&quot;,\n \u00a0\/\/ \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mcpsIndication-&gt;BufferSize, mcpsIndication-&gt;Port);\n \u00a0\/\/ Serial.print(&quot;+REV DATA:&quot;);\n \u00a0\/\/ for (uint8_t i = 0; i &lt; mcpsIndication-&gt;BufferSize; i++) {\n \u00a0\/\/ \u00a0\u00a0Serial.printf(&quot;%02X&quot;, mcpsIndication-&gt;Buffer&#x5B;i]);\n \u00a0\/\/ }\n\n \u00a0\/\/ parametros de recepcion\n \u00a0ack_rssi = uint8_t(abs(mcpsIndication-&gt;Rssi));\n \u00a0ack_snr \u00a0= uint8_t(mcpsIndication-&gt;Snr);\n \u00a0ack_datarate = uint8_t(mcpsIndication-&gt;RxDoneDatarate);\n \u00a0\/\/ recibido de trama\n \u00a0Up_rssi = uint8_t(mcpsIndication-&gt;Buffer&#x5B;0]);\n\n \u00a0\/\/\n \u00a0Serial.print(&quot;Rx ack_Rssi:-&quot;); Serial.print(ack_rssi);\n \u00a0Serial.print(&quot;, ack_Snr:&quot;);Serial.print(ack_snr);\n \u00a0Serial.print(&quot;, ack_Datarate: &quot;);Serial.println(ack_datarate);\n \u00a0Serial.print(&quot; Up_rssi:&quot;);\n \u00a0Serial.print(-1*Up_rssi);\n \u00a0Serial.printf(&quot; +REV DATA:%s,RXSIZE %d,PORT %d\\r&quot;,mcpsIndication-&gt;RxSlot?&quot;RXWIN2&quot;:&quot;RXWIN1&quot;,mcpsIndication-&gt;BufferSize,mcpsIndication-&gt;Port);\n \u00a0Serial.println();\n}\n\nvoid downLinkAckHandle(McpsIndication_t *mcpsIndication){\n \u00a0ack_rssi = uint8_t(abs(mcpsIndication-&gt;Rssi));\n \u00a0ack_snr \u00a0= uint8_t(mcpsIndication-&gt;Snr);\n \u00a0ack_datarate = uint8_t(mcpsIndication-&gt;RxDoneDatarate);\n \u00a0tx_done = true;\n \u00a0\/\/ if (serial_msg){\n \u00a0\/\/ \u00a0\u00a0Serial.println(' ');\n \u00a0\/\/ \u00a0\u00a0Serial.print(&quot; ack received(rssi,snr,datarate): -&quot;);\n \u00a0\/\/ \u00a0\u00a0Serial.print(ack_rssi);Serial.print(&quot; ,&quot;);\n \u00a0\/\/ \u00a0\u00a0Serial.print(ack_snr);Serial.print(&quot; ,&quot;);\n \u00a0\/\/ \u00a0\u00a0Serial.println(ack_datarate);\n \u00a0\/\/ }\n}\n<\/pre><\/div>\n\n\n<p>[ <a href=\"#sensores\">sensores<\/a> ] [ <a href=\"#envia\">env\u00eda<\/a> ] [ <a href=\"#recibe\">recibe<\/a> ] [ <a href=\"#principal\">principal<\/a> ]<br><a id=\"principal\"><\/a>...<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">LoRaWan - Bloque principal<\/h2>\n\n\n\n<p>El bloque principal se basa en el esquema b\u00e1sico de LoRaWan usado para sensores, cambiando la parte de ahorro de energ\u00eda entre transmisiones DEVICE_STATE_SLEEP a una controlada por periodos de muestreo del pluvi\u00f3metro con los procedimientos onSleep() y onWakeUp() descritos en la parte de sensores. Se controla las actividades de Lectura de sensores y modos de ahorro de energ\u00eda por la sucesi\u00f3n de cada evento.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroPrincipal01.png\"><img loading=\"lazy\" decoding=\"async\" width=\"895\" height=\"118\" src=\"http:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroPrincipal01.png\" alt=\"LoRaWan pluviometro bloque principal\" class=\"wp-image-4092\" srcset=\"https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroPrincipal01.png 895w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroPrincipal01-300x40.png 300w, https:\/\/blog.espol.edu.ec\/girni\/files\/2023\/07\/LoRaWan_PluviometroPrincipal01-768x101.png 768w\" sizes=\"auto, (max-width: 895px) 100vw, 895px\" \/><\/a><\/figure>\n\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code alignwide\"><pre class=\"brush: arduino; title: ; notranslate\" title=\"\">\n\/\/ LoRaWan Pluviometer, temperature, humidity and barometric pressure\n\/\/ 2023 April\n\/\/ http:\/\/blog.espol.edu.ec\/girni\/lorawan-pluviometro-ino\/\n#include &quot;LoRaWan_APP.h&quot;\n#include &quot;Arduino.h&quot;\n#include &lt;Wire.h&gt;\n#include &lt;HDC1080.h&gt;\n#include &lt;BMP180.h&gt;\n\n\/* set LoraWan_RGB to Active,the RGB active in loraWan\n * red \u00a0\u00a0|sending; \u00a0\u00a0purple | joined done;\n * blue \u00a0|RxWindow1; yellow | means RxWindow2;\n * green | received done;\n *\/\n\/* LoRaWan: OTAA parameters*\/\nuint8_t devEui&#x5B;] = { 0x2e, 0x4f, 0xa4, 0xdd, 0xf0, 0x2f, 0x06, 0xeb };\nuint8_t appEui&#x5B;] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\nuint8_t appKey&#x5B;] = { 0x13, 0xb9, 0xd1, 0x66, 0x30, 0x2a, 0xeb, 0x53,\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a00x46, 0x6c, 0x0d, 0x2d, 0xa2, 0x31, 0x6b, 0xf0 };\n\/* ABP parameters*\/\nuint8_t nwkSKey&#x5B;] = { 0xe2, 0x28, 0x89, 0xe0, 0x73, 0x22, 0xcb, 0xd1,\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a00xa7, 0x95, 0x64, 0x2e, 0xdb, 0xe5, 0x94, 0x42 };\nuint8_t appSKey&#x5B;] = { 0x1a, 0xfc, 0x10, 0xc5, 0x6f, 0xb8, 0xba, 0x86,\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a00x0d, 0xf3, 0xcf, 0xc5, 0xd2, 0xdb, 0x44 ,0xb8 };\nuint32_t devAddr = \u00a0( uint32_t )0x01d06174;\n\/*LoraWan channelsmask, default channels 0-7*\/ \nuint16_t userChannelsMask&#x5B;6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 };\n\n\/*Select in arduino IDE tools*\/\nLoRaMacRegion_t loraWanRegion = ACTIVE_REGION;\nDeviceClass_t loraWanClass = LORAWAN_CLASS;\nbool overTheAirActivation = LORAWAN_NETMODE;\nbool loraWanAdr = LORAWAN_ADR;\nbool keepNet = LORAWAN_NET_RESERVE;\nbool isTxConfirmed = LORAWAN_UPLINKMODE;\n\n\/\/ sample period\nuint8_t sample_min = 0; \nuint8_t sample_seg = 5; \nuint32_t sample_period = (sample_min*60 + sample_seg);\n\nuint32_t appTxDutyCycle = (sample_min*60 + sample_seg)*1000; \/\/ min*seg*ms\n\nuint8_t appPort = 4; \/* Application port *\/\n\/* trials to transmit frame, if didn't receive ack.\n * The MAC performs a datarate adaptation,\n * Tx nb|Data Rate\n * -----|----------\n * 1 \u00a0\u00a0\u00a0|DR \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* 5 \u00a0\u00a0\u00a0| max(DR-2,0)\n * 2 \u00a0\u00a0\u00a0|DR \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* 6 \u00a0\u00a0\u00a0| max(DR-2,0)\n * 3 \u00a0\u00a0\u00a0|max(DR-1,0) \u00a0* 7 \u00a0\u00a0\u00a0| max(DR-3,0)\n * 4 \u00a0\u00a0\u00a0|max(DR-1,0) \u00a0* 8 \u00a0\u00a0\u00a0| max(DR-3,0)\n*\/\nuint8_t confirmedNbTrials = 4;\n\n\/\/ Ack reception parameters\nuint8_t ack_rssi = 0;\nuint8_t ack_snr = 0;\nuint8_t ack_datarate = 0;\n\nuint8_t Up_rssi = 0;\n\n\/\/ serial print messages\nbool serial_msg = true;\n\n\/\/ time variables and control check\nunsigned long sample_t0 = 0;\nunsigned long sample_ti = 0;\nunsigned long sample_dt = 0;\nbool pluvio_flag = false;\nbool sensor_flag = false;\nunsigned long tx_t0 = 0;\nbool tx_flag = false;\nbool tx_done = false;\nuint8_t tipfree = 0;\nbool loop_0 = true;\nbool sleep_flag = false;\nbool sleep_done = true;\n\nuint8_t i_minute = 0;\nconst uint8_t n_minute = 5;\nuint8_t pulse_list&#x5B;n_minute];\nuint8_t pluviometer&#x5B;n_minute];\nuint8_t pulse = 0;\nbool pulse_flag = false;\nconst uint8_t pluvioPin = GPIO5;\n\n#define timetillsleep 60*60*1000\nint timetillwakeup = sample_period*1000;\nstatic TimerEvent_t sleep;\nstatic TimerEvent_t wakeUp;\nuint8_t lowpower=1;\n\n\/\/sensors libraries\nHDC1080 hdc1080; \u00a0\/\/ humidity and temperature sensor\nBMP085 bmp; \/\/ barometer and temperature sensor\n\n\/\/ sensors I2C active check and value variables\nbool humitemp_active = false;\nbool prestemp_active = false;\nfloat ht_temp = 0, ht_humi = 0;\nfloat bar_temp = 0, bar_pres = 0;\n\nuint16_t batteryVoltage = 0;\n\nvoid setup() {\n \u00a0Serial.begin(115200);\n\n#if(AT_SUPPORT)\n\tenableAt();\n#endif\n\n \u00a0\/\/ OLED display status\n \u00a0\/\/LoRaWAN.displayMcuInit();\n\n \u00a0pinMode(pluvioPin, INPUT); \/\/OUTPUT_PULLUP);\n \u00a0attachInterrupt(pluvioPin, pulse_tip, RISING);\n \u00a0\n \u00a0for (int i = 0; i &lt; n_minute; i = i + 1) {\n \u00a0\u00a0\u00a0pulse_list&#x5B;i] = 0;\n \u00a0\u00a0\u00a0pluviometer&#x5B;i] = 0;\n \u00a0}\n \u00a0\n \u00a0\/\/ sensors I2C Vcc pin control\n \u00a0pinMode(Vext, OUTPUT);\n \u00a0read_sensors();\n\n \u00a0\/\/ time variables iniciated\n \u00a0sample_t0 = millis();\n \u00a0sample_ti = sample_t0;\n \u00a0tx_t0 = sample_t0;\n \u00a0\n \u00a0deviceState = DEVICE_STATE_INIT;\n\tLoRaWAN.ifskipjoin(); \/\/if joinned,skip\n\n \u00a0\/\/ lowpower mode\n \u00a0Radio.Sleep( );\n \u00a0TimerInit( &amp;sleep, onSleep );\n \u00a0TimerInit( &amp;wakeUp, onWakeUp );\n \u00a0onWakeUp();\n}\n\nvoid loop() {\n \u00a0if(lowpower){lowPowerHandler();}\n\n \u00a0pulse_check();\n \u00a0sample_t0 = millis();\n \u00a0sample_dt = abs(sample_t0-sample_ti);\n \u00a0if (sample_dt&gt;=sample_period*1000){\n \u00a0\u00a0\u00a0sample_ti = sample_t0;\n \u00a0\u00a0\u00a0i_minute = i_minute + 1;\n \u00a0\u00a0\u00a0if (pulse==0){ \/\/ tipfree counter do not overflow\n \u00a0\u00a0\u00a0\u00a0\u00a0if (tipfree&lt;=250){tipfree = tipfree + 1;}\n \u00a0\u00a0\u00a0\u00a0\u00a0if (tipfree&gt;250){tipfree = 1;}\n \u00a0\u00a0\u00a0}\n \u00a0\u00a0\u00a0pulse = 0;\n \u00a0\u00a0\u00a0minute_check();\n \u00a0}\n \u00a0\n \u00a0\/\/ read I2C sensors at i_minute=0\n \u00a0if (i_minute==0 &amp;&amp; sensor_flag == true){\n \u00a0\u00a0\u00a0Serial.println(&quot; \u00a0reading sensors ...&quot;);\n \u00a0\u00a0\u00a0read_sensors();\n \u00a0\u00a0\u00a0sensor_flag = false;\n \u00a0\u00a0\u00a0}\n\n \u00a0\/\/ tx check\n \u00a0if (i_minute==0 &amp;&amp; tx_flag==false &amp;&amp; tx_done == false &amp;&amp; loop_0==false){\n \u00a0\u00a0\u00a0tx_t0 = sample_t0;\n \u00a0\u00a0\u00a0deviceState = DEVICE_STATE_SEND;\n \u00a0\u00a0\u00a0tx_flag = true;\n \u00a0\u00a0\u00a0tx_done = false;\n \u00a0\u00a0\u00a0}\n \u00a0if (i_minute &gt;= n_minute){tx_flag = false;}\n \u00a0\n \u00a0\/\/ sleep check\n \u00a0if (i_minute==0 &amp;&amp; tipfree&gt;=n_minute &amp;&amp; sleep_flag==false &amp;&amp; sleep_done==true &amp;&amp; tx_done==true){\n \u00a0\u00a0\u00a0sleep_flag = true;\n \u00a0\u00a0\u00a0sleep_done = false;\n \u00a0\u00a0\u00a0lowpower = 1;\n \u00a0\u00a0\u00a0\/\/timetillsleep ms later into lowpower mode;\n \u00a0\u00a0\u00a0TimerSetValue( &amp;sleep, 1 );\n \u00a0\u00a0\u00a0TimerStart( &amp;sleep );\n \u00a0\u00a0\u00a0}\n \u00a0if (i_minute &gt; 0 &amp;&amp; tipfree&gt;=n_minute &amp;&amp; sleep_flag==false &amp;&amp; sleep_done==true){\n \u00a0\u00a0\u00a0sleep_flag = true;\n \u00a0\u00a0\u00a0sleep_done = false;\n \u00a0\u00a0\u00a0lowpower = 1;\n \u00a0\u00a0\u00a0\/\/timetillsleep ms later into lowpower mode;\n \u00a0\u00a0\u00a0TimerSetValue( &amp;sleep, 1 );\n \u00a0\u00a0\u00a0TimerStart( &amp;sleep );\n \u00a0\u00a0\u00a0}\n \u00a0\n\tswitch( deviceState ) {\n\t\tcase DEVICE_STATE_INIT: {\n#if(LORAWAN_DEVEUI_AUTO)\n\t\t\tLoRaWAN.generateDeveuiByChipID();\n#endif\n#if(AT_SUPPORT)\n\t\t\tgetDevParam();\n#endif\n\t\t\tprintDevParam();\n\t\t\tLoRaWAN.init(loraWanClass,loraWanRegion);\n\t\t\tdeviceState = DEVICE_STATE_JOIN;\n\t\t\tbreak;\n\t\t}\n\t\tcase DEVICE_STATE_JOIN: {\n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/LoRaWAN.displayJoining();\n\t\t\tLoRaWAN.join();\n\t\t\tbreak;\n\t\t}\n\t\tcase DEVICE_STATE_SEND:\t{\n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/LoRaWAN.displaySending();\n\t\t\tprepareTxFrame( appPort );\n\t\t\tLoRaWAN.send();\n\t\t\tdeviceState = DEVICE_STATE_CYCLE;\n\t\t\tbreak;\n\t\t}\n\t\tcase DEVICE_STATE_CYCLE: {\n\t\t\t\/\/ Schedule next packet transmission\n\t\t\t\/\/txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND );\n\t\t\t\/\/LoRaWAN.cycle(txDutyCycleTime);\n\t\t\tdeviceState = DEVICE_STATE_SLEEP;\n\t\t\tbreak;\n\t\t}\n\t\tcase DEVICE_STATE_SLEEP: {\n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/LoRaWAN.displayAck();\n \u00a0\u00a0\u00a0\u00a0\u00a0if (sleep_flag==true &amp;&amp; sleep_done==false){ \n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/ \u00a0\u00a0if (serial_msg){\n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/ \u00a0\u00a0\u00a0\u00a0Serial.print(&quot; --- Sleep mode ; deviceState : &quot;);\n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/ \u00a0\u00a0\u00a0\u00a0Serial.print(deviceState);\n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/ \u00a0\u00a0\u00a0\u00a0Serial.print(&quot; ; i_minute : &quot;); Serial.println(i_minute);\n \u00a0\u00a0\u00a0\u00a0\u00a0\/\/ \u00a0\u00a0\u00a0\u00a0}\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/LoRaWAN.sleep();\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0sleep_done = true;\n \u00a0\u00a0\u00a0\u00a0\u00a0}\n\t\t\tbreak;\n\t\t}\n\t\tdefault: {\n\t\t\tdeviceState = DEVICE_STATE_INIT;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n<\/pre><\/div>\n\n\n<p>[ <a href=\"#sensores\">sensores<\/a> ] [ <a href=\"#envia\">env\u00eda<\/a> ] [ <a href=\"#recibe\">recibe<\/a> ] [ <a href=\"#principal\">principal<\/a> ]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Las instrucciones para control del pluvi\u00f3metro y sensores complementarios se encuentran segmentadas por bloques: [ sensores ] [ env\u00eda ] [ recibe ] [ principal ]... Sensores El bloque de sensores se simplifica por partes de control y comunicaci\u00f3n acorde al&nbsp; tipo de sensor: Pulsos: pluvi\u00f3metro El pluvi\u00f3metro se centra en medir el vaciado de [&hellip;]<\/p>\n","protected":false},"author":8043,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1481157],"tags":[],"class_list":["post-4086","post","type-post","status-publish","format-standard","hentry","category-lorawan-pluviometro"],"_links":{"self":[{"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/posts\/4086","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/users\/8043"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/comments?post=4086"}],"version-history":[{"count":10,"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/posts\/4086\/revisions"}],"predecessor-version":[{"id":4885,"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/posts\/4086\/revisions\/4885"}],"wp:attachment":[{"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/media?parent=4086"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/categories?post=4086"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.espol.edu.ec\/girni\/wp-json\/wp\/v2\/tags?post=4086"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}