{"id":2050,"date":"2022-04-27T12:01:16","date_gmt":"2022-04-27T10:01:16","guid":{"rendered":"https:\/\/www.sabulo.com\/sb\/?p=2050"},"modified":"2022-04-27T16:59:23","modified_gmt":"2022-04-27T14:59:23","slug":"gps-equipped-portable-atmospheric-recorder-with-lcd-and-map-display","status":"publish","type":"post","link":"https:\/\/www.sabulo.com\/sb\/esp32-development-board\/gps-equipped-portable-atmospheric-recorder-with-lcd-and-map-display\/","title":{"rendered":"GPS-equipped portable atmospheric recorder, with LCD and map display"},"content":{"rendered":"\n<p><strong>NOTE &#8211; all code you see here is in better, complete <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/HeikkiHietala\/gps-lcd-bmp280-map\" target=\"_blank\">form at my GitHub page<\/a>.<\/strong><\/p>\n\n\n\n<p>As I have returned from Madrid after a week-long seminar with nine students, I thought I&#8217;d go thru some of the features of the IoT device we presented to and built with the other students (Swiss and Spanish).   <\/p>\n\n\n\n<p>The Seminar is now in its 14th implementation, and it has only been suspended twice, once in 2013 and now 2020 and 2021 for obvious reasons. We started this yearly Seminar in 2006 at Haaga-Helia. The other schools are <a rel=\"noreferrer noopener\" href=\"http:\/\/www.cphbusiness.dk\" target=\"_blank\">Copenhagen North<\/a> who couldn&#8217;t make it this year, <a rel=\"noreferrer noopener\" href=\"http:\/\/www.hes-so.ch\" target=\"_blank\">HES-SO Switzerland<\/a>, and <a rel=\"noreferrer noopener\" href=\"http:\/\/www.uem.es\" target=\"_blank\">Universidad Europea<\/a>. Usually the students get to ideate what we want to make and present at the Seminar, and form a consensus. This year, due to the global shortage of electronic parts, I had to act dictatorial and go shopping before the team was even selected. For example, I wanted to buy long range WLAN (LORAWAN) ESP32s for building this device, but they just weren&#8217;t available right now.<\/p>\n\n\n\n<p>I found other parts on Amazon.de and a local electronics shop, Partco, to build a portable device that has<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>a GPS device<\/li><li>a sensor with temperature and atmospheric pressure<\/li><li>an LCD display, <\/li><li>an ESP32 to drive it  <\/li><li>the capability to send the measured latitude, longitude, altitude, and number of seen satellites as well as the air variables to a file on the Web, and<\/li><li>a PHP file that can take the readings from the files and show them on an online map.<\/li><\/ul>\n\n\n\n<p>This list I presented as a fait accompli to the newly selected team, and let them form five subteams.<\/p>\n\n\n\n<p>The idea of the seminar is to have each country present something to the other students, who have been mixed into multinational teams. This time, the Finnish students will walk the others through installing a display, then adding an atmospheric sensor, then the GPS sensor, making the ESP32 connect to the Web, send the data, and finally, write the PHP.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The LCD display<\/h2>\n\n\n\n<p>The LCD is the same LCD 1604 as I have shown you in <a rel=\"noreferrer noopener\" href=\"https:\/\/www.sabulo.com\/sb\/3d-printing-2\/finding-rotation-speed-using-arduino\/#more-1466\" data-type=\"post\" target=\"_blank\">the servo rotation speed counter<\/a>, with a little difference. The ESP32 is using the I2C protocol to send the data to the display, and in the rotation speed example, it is an Arduino. The difference is that Arduino uses analog pins A4 and A5 as the SDA (data) and SCL (clock) pins, but in the ESP32, the preferred pins for I2C are 21 and 22. Anyway, here&#8217;s the <a href=\"http:\/\/www.randomnerdtutorials.com\" target=\"_blank\" rel=\"noreferrer noopener\">Random Nerd Tutorial <\/a>example file for connecting the LCD:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;LiquidCrystal_I2C.h&gt;\n\n\/\/ set the LCD number of columns and rows\nint lcdColumns = 20;\nint lcdRows = 4;\n\n\/\/ set LCD address, number of columns and rows\n\/\/ if you don't know your display address, run an I2C scanner sketch\nLiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);  \n\nvoid setup(){\n  \/\/ initialize LCD\n  lcd.init();\n  \/\/ turn on LCD backlight                      \n  lcd.backlight();\n}\n\nvoid loop(){\n  \/\/ set cursor to first column, first row\n  lcd.setCursor(0, 0);\n  \/\/ print message\n  lcd.print(\"Hello, World!\");\n  delay(1000);\n  \/\/ clears the display to print new message\n  lcd.clear();\n  \/\/ set cursor to first column, second row\n  lcd.setCursor(0,1);\n  lcd.print(\"Hello, World!\");\n  delay(1000);\n  lcd.clear(); \n} <\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The BMP280 sensor<\/h2>\n\n\n\n<p>Next, we connect the atmospheric sensor BMP280. There are two different BM* 280 versions, of which BMP delivers air pressure and temperature, while BME280 measures air humidity and temperature. The libraries for each can be installed into the Arduino IDE via the Manage Libraries \/ Search system. Make sure you get the proper one for your device, they are not interchangeable.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336.jpg?resize=825%2C825&#038;ssl=1\" alt=\"\" class=\"wp-image-2072\" width=\"825\" height=\"825\" srcset=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?resize=1024%2C1024&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?resize=300%2C300&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?resize=150%2C150&amp;ssl=1 150w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?resize=768%2C768&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?resize=1536%2C1536&amp;ssl=1 1536w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?resize=2048%2C2048&amp;ssl=1 2048w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?w=1650&amp;ssl=1 1650w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133336-scaled.jpg?w=2475&amp;ssl=1 2475w\" sizes=\"auto, (max-width: 825px) 100vw, 825px\" \/><\/a><figcaption>BMP280 with pins. You can see this is a tiny sensor.<\/figcaption><\/figure><\/div>\n\n\n\n<p>Since the BMP280 is another I2C device, you can use the same pins 21 and 22 on ESP32 to drive this sensor too. The address, which is similar to the one set up above &#8211; LiquidCrystal_I2C lcd(<strong>0x27<\/strong>, lcdColumns, lcdRows) &#8211;  is probably 0x56 for the BMP280, but if you have issues, you may want to scan for the I2C addresses. The address can be set up in the command that starts the device. <\/p>\n\n\n\n<p>By the way, the easiest way to deal with two devices wanting to share two pins on the ESP32 is to lead the pins into holes 21 and 22 on a breadboard, then leading the wires to each device from the breadboard.<\/p>\n\n\n\n<p>This is the sample code that is available after you install the library for this device:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;Wire.h&gt;\n#include &lt;SPI.h&gt;\n#include &lt;Adafruit_BMP280.h&gt;\n\nAdafruit_BMP280 bmp; \/\/ I2C\n\nvoid setup() {\n  Serial.begin(9600);\n  while ( !Serial ) delay(100);   \/\/ wait for native usb\n  Serial.println(F(\"BMP280 test\"));\n  unsigned status;\n  \/\/status = bmp.begin(BMP280_ADDRESS_ALT, BMP280_CHIPID);\n  status = bmp.begin();\n  if (!status) {\n    Serial.println(F(\"Could not find a valid BMP280 sensor, check wiring or \"\n                      \"try a different address!\"));\n    Serial.print(\"SensorID was: 0x\"); \n    Serial.println(bmp.sensorID(),16);\n    Serial.print(\"        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\\n\");\n    Serial.print(\"        ID of 0x56-0x58 represents a BMP 280,\\n\");\n    Serial.print(\"        ID of 0x60 represents a BME 280.\\n\");\n    Serial.print(\"        ID of 0x61 represents a BME 680.\\n\");\n    while (1) delay(10);\n  }\n\n  \/* Default settings from datasheet. *\/\n  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     \/* Operating Mode. *\/\n                  Adafruit_BMP280::SAMPLING_X2,     \/* Temp. oversampling *\/\n                  Adafruit_BMP280::SAMPLING_X16,    \/* Pressure oversampling *\/\n                  Adafruit_BMP280::FILTER_X16,      \/* Filtering. *\/\n                  Adafruit_BMP280::STANDBY_MS_500); \/* Standby time. *\/\n}\n\nvoid loop() {\n    Serial.print(F(\"Temperature = \"));\n    Serial.print(bmp.readTemperature());\n    Serial.println(\" *C\");\n\n    Serial.print(F(\"Pressure = \"));\n    Serial.print(bmp.readPressure());\n    Serial.println(\" Pa\");\n\n    Serial.print(F(\"Approx altitude = \"));\n    Serial.print(bmp.readAltitude(1013.25)); \/* Adjusted to local forecast! *\/\n    Serial.println(\" m\");\n\n    Serial.println();\n    delay(2000);\n}<\/code><\/pre>\n\n\n\n<p>Note the local forecast pressure to be adjusted in the last few lines. Check with the local meteorological institute for the current pressure at ground level to make this device function properly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The GPS sensor<\/h2>\n\n\n\n<p>The GPS sensor we want to use is the <a rel=\"noreferrer noopener\" href=\"https:\/\/www.waveshare.com\/wiki\/UART_GPS_NEO-6M\" target=\"_blank\">UART GPS NEO-6M<\/a>, which is very neat and surprisingly easy to take into use It uses two other pins on the ESP32, ie. 12 and 13 for its receiving and transmitting, so it doesn&#8217;t want to crash the party going on at pins 21 and 22. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"825\" height=\"825\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317.jpg?resize=825%2C825&#038;ssl=1\" alt=\"\" class=\"wp-image-2073\" srcset=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?resize=1024%2C1024&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?resize=300%2C300&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?resize=150%2C150&amp;ssl=1 150w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?resize=768%2C768&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?resize=1536%2C1536&amp;ssl=1 1536w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?resize=2048%2C2048&amp;ssl=1 2048w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?w=1650&amp;ssl=1 1650w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220404_133317-scaled.jpg?w=2475&amp;ssl=1 2475w\" sizes=\"auto, (max-width: 825px) 100vw, 825px\" \/><\/a><figcaption>GPS receiver with antenna. Do not start the sensor without the antenna.<\/figcaption><\/figure><\/div>\n\n\n\n<p>The sentences that GPS receivers deliver are not the easiest of things to understand. Luckily there is the library TinyGPSPlus-ESP32 which takes in the data and renders it into variables, which you can then access. Here&#8217;s the code you need to set it up.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;TinyGPSPlus.h&gt;\n\/* \n   This sample sketch should be the first you try out when you are testing a TinyGPSPlus\n   (TinyGPSPlus) installation.  In normal use, you feed TinyGPSPlus objects characters from\n   a serial NMEA GPS device, but this example uses static strings for simplicity.\n*\/\n\n\/\/ A sample NMEA stream.\nconst char *gpsStream =\n  \"$GPRMC,045103.000,A,3014.1984,N,09749.2872,W,0.67,161.46,030913,,,A*7C\\r\\n\"\n  \"$GPGGA,045104.000,3014.1985,N,09749.2873,W,1,09,1.2,211.6,M,-22.5,M,,0000*62\\r\\n\"\n  \"$GPRMC,045200.000,A,3014.3820,N,09748.9514,W,36.88,65.02,030913,,,A*77\\r\\n\"\n  \"$GPGGA,045201.000,3014.3864,N,09748.9411,W,1,10,1.2,200.8,M,-22.5,M,,0000*6C\\r\\n\"\n  \"$GPRMC,045251.000,A,3014.4275,N,09749.0626,W,0.51,217.94,030913,,,A*7D\\r\\n\"\n  \"$GPGGA,045252.000,3014.4273,N,09749.0628,W,1,09,1.3,206.9,M,-22.5,M,,0000*6F\\r\\n\";\n\n\/\/ The TinyGPSPlus object\nTinyGPSPlus gps;\n\nvoid setup()\n{\n  Serial.begin(115200);\n\n  Serial.println(F(\"BasicExample.ino\"));\n  Serial.println(F(\"Basic demonstration of TinyGPSPlus (no device needed)\"));\n  Serial.print(F(\"Testing TinyGPSPlus library v. \")); Serial.println(TinyGPSPlus::libraryVersion());\n  Serial.println(F(\"by Mikal Hart\"));\n  Serial.println();\n\n  while (*gpsStream)\n    if (gps.encode(*gpsStream++))\n      displayInfo();\n\n  Serial.println();\n  Serial.println(F(\"Done.\"));\n}\n\nvoid loop()\n{\n}\n\nvoid displayInfo()\n{\n  Serial.print(F(\"Location: \")); \n  if (gps.location.isValid())\n  {\n    Serial.print(gps.location.lat(), 6);\n    Serial.print(F(\",\"));\n    Serial.print(gps.location.lng(), 6);\n  }\n  else\n  {\n    Serial.print(F(\"INVALID\"));\n  }\n\n  Serial.print(F(\"  Date\/Time: \"));\n  if (gps.date.isValid())\n  {\n    Serial.print(gps.date.month());\n    Serial.print(F(\"\/\"));\n    Serial.print(gps.date.day());\n    Serial.print(F(\"\/\"));\n    Serial.print(gps.date.year());\n  }\n  else\n  {\n    Serial.print(F(\"INVALID\"));\n  }\n\n  Serial.print(F(\" \"));\n  if (gps.time.isValid())\n  {\n    if (gps.time.hour() &lt; 10) Serial.print(F(\"0\"));\n    Serial.print(gps.time.hour());\n    Serial.print(F(\":\"));\n    if (gps.time.minute() &lt; 10) Serial.print(F(\"0\"));\n    Serial.print(gps.time.minute());\n    Serial.print(F(\":\"));\n    if (gps.time.second() &lt; 10) Serial.print(F(\"0\"));\n    Serial.print(gps.time.second());\n    Serial.print(F(\".\"));\n    if (gps.time.centisecond() &lt; 10) Serial.print(F(\"0\"));\n    Serial.print(gps.time.centisecond());\n  }\n  else\n  {\n    Serial.print(F(\"INVALID\"));\n  }\n\n  Serial.println();\n}<\/code><\/pre>\n\n\n\n<p>This is only a partial set of the data that is available from the GPS sensor. In the delightfully named example, <strong>KitchenSink.ino<\/strong>, you can see every bit of information that can be gleaned from the GPS system.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ESP32 data output to the Web<\/h2>\n\n\n\n<p>This is a simple thing. Make your ESP32 aware of a local access point &#8211; we will use mobile phone hotspots for this. All you need to do is to install the <strong>Wifi<\/strong> and <strong>HTTPClient<\/strong> libraries, then use something like this code. The stuff below has the relevant parts, but won&#8217;t work as such, you need to edit it to suit your needs.<\/p>\n\n\n\n<p>First I&#8217;ll show the PHP file that will receive your sent data. It merely listens to the server&#8217;s input channel, and whatever it gets, it writes into a text file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?php\n    $_POST = file_get_contents('php:\/\/input');\n    $myYear = date(\"d.m.Y\");\n    $myHour = date(\"H:i\");\n    $myFile = \"my_iot.txt\";\n    $fh = fopen($myFile, 'a') or die(\"can't open file\");\n    fwrite($fh, $myYear . \",\" .  $myHour);\n    fwrite($fh, \",\");\n    fwrite($fh, $_POST);\n    fwrite($fh, \"\\n\");    \n    fclose($fh);\n?&gt;<\/code><\/pre>\n\n\n\n<p>The thing you must note is the filename <strong>my_iot.txt<\/strong>. This script will just append the data it receives to the file, and if it doesn&#8217;t exist for some reason, it&#8217;ll create a new one. It is comma separated, so there are commas after date and time. The data is comma separated as well, so it&#8217;s easy to parse into variables.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;WiFi.h&gt;   \/\/wifi library\n#include &lt;HTTPClient.h&gt; \/\/http client\n\/\/ network settings\nconst char* ssid       = \"YOUR_SSID\";\nconst char* password   = \"YOUR_PASSWORD\";\nconst char* serverName = \"YOUR_WEB_INTERFACE\"; \/\/ for example http:\/\/www.yourserver.com\/gps.php\nString WebMessage\nvoid setup() {\n\n  Serial.begin(115200);\n  while (WiFi.status() != WL_CONNECTED) {\n    delay(200);\n    Serial.print(\".\");\n\n    WiFi.begin(ssid, password);\n    Serial.println(\"Connecting\");\n    while (WiFi.status() != WL_CONNECTED) {\n      delay(500);\n      Serial.print(\".\");\n    }\n    Serial.println(\"\");\n    Serial.print(\"Connected to WiFi network with IP Address: \");\n    Serial.println(WiFi.localIP());\n    UpdateActivity(\"Booted\");\n  }\n}\n\nvoid loop() { \/\/this is just a sample, won't work as such\n              \/\/note the comma separated values\n\n  WebMessage = strLat + \", \"\n               + strLon + \", \"\n               + strPressure + \", \"\n               + strTemperature + \", \"\n               + strLat + \", \" +\n               + strNumSats + \", \";\n  UpdateActivity(webMessage);\n  delay (10000);\n}\n\nvoid UpdateActivity(String myActivity) {\n  \/\/sending data to the server:\n\n  Serial.println(\"add data to web\");\n  Serial.printf(\"Connecting to %s \", ssid);\n  if (WiFi.status() == WL_CONNECTED) {\n    WiFiClient client;\n    HTTPClient http;\n\n    \/\/ Your Domain name with URL path or IP address with path\n    http.begin(serverName);\n\n    \/\/ If you need an HTTP request with a content type: text\/plain\n    http.addHeader(\"Content-Type\", \"text\/plain\");\n    String httpRequestData = myActivity\n    \/\/ Send HTTP POST request\n    int httpResponseCode = http.POST(httpRequestData);\n\n    Serial.println(\"HTTP Response code: \");\n    Serial.println(httpRequestData);\n\n    \/\/ Free resources\n    http.end();\n  }\n  else {\n    Serial.println(\"WiFi Disconnected\");\n  }\n}<\/code><\/pre>\n\n\n\n<p>This will send the string myActivity to the web page, and it looks like this on the server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>31.03.2022,17:55,60.201082,24.933874,24.65,1008.67,65.20,7<\/code><\/pre>\n\n\n\n<p>This is then easy to parse into relevant variables for your map to digest and show.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reading the web data and showing a marker on the map<\/h2>\n\n\n\n<p>For this, I will rely on code developed by one of my students, <strong><em>Vincent Le<\/em><\/strong>, who was really quick on learning PHP and putting together two versions of the map code &#8211; many thanks for allowing me to share the files! He was also instrumental in editing the map end until the very end of our day at the Seminar. His site <a rel=\"noreferrer noopener\" href=\"https:\/\/www.vincentle.me\/\" target=\"_blank\"> is really interesting <\/a>and you should pay it a visit.<\/p>\n\n\n\n<p>The code for this part is <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/HeikkiHietala\/gps-lcd-bmp280-map\" target=\"_blank\">on my GitHub page<\/a>, in the folders <strong>\/gps-singlemap<\/strong> and <strong>\/gps-multimap<\/strong>. Have a look over there to see the code while you read this.<\/p>\n\n\n\n<p>Here is a sample of the data that gets collected into the iotwrite.php file: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>27.04.2022 12:48\t60.201167,24.933687,23.65,1021.37,42.00,6\n27.04.2022 12:48\t60.201167,24.933687,23.65,1021.37,42.00,6\n27.04.2022 12:48\t60.201595,24.934066,23.65,1021.35,42.00,6\n27.04.2022 12:49\t60.201595,24.934066,23.65,1021.36,42.00,6\n27.04.2022 12:49\t60.201595,24.934066,23.65,1021.35,42.00,6\n27.04.2022 12:49\t60.201649,24.934002,23.66,1021.35,42.00,6<\/code><\/pre>\n\n\n\n<p>To show the data, we needed two versions of the map code. One is to show the location of each box, and the other is to get all ten boxes&#8217; data and show all of them on the same map. In multimap view, clicking on the map marker will show you the data associated with the box, in single map view the data is shown all the time.   <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/image-1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"825\" height=\"465\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/image-1.png?resize=825%2C465&#038;ssl=1\" alt=\"\" class=\"wp-image-2092\" srcset=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/image-1.png?w=1003&amp;ssl=1 1003w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/image-1.png?resize=300%2C169&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/image-1.png?resize=768%2C433&amp;ssl=1 768w\" sizes=\"auto, (max-width: 825px) 100vw, 825px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>The Fetch Data button is used in Vincent&#8217;s system to update the location, but there is nothing to keep you from updating the page with Refresh at any interval you choose. <\/p>\n\n\n\n<p>We had nine boxes for the teams, hence the folder structure was arranged so that we had subfolders like <strong>\/madrid\/iot01 &#8212;\/madrid\/iot09<\/strong>. In the subfolder <strong>\/madrid\/iot00<\/strong> we had the multimap view. Every folder had an <strong>index.php<\/strong> file, which read the data and showed it on the map. There is also another file there for extracting the data from the file for the <strong>index.php<\/strong> file. The difference of the two extraction files is that in the multimap version, the files are in an array of nine items, whereas in the single map file, the array has only one item. <\/p>\n\n\n\n<p>The difference of any single map index file and the multimap file is that the multimap uses a loop to pass through the files listed in the array, while the single map just passes through the one coordinate and atmospheric data file. When you go to Github, you can take into use whichever you please. In both files you need to edit in your server data.<\/p>\n\n\n\n<p>We started our day with a presentation on IoT, followed by the installation of Arduino IDE with ESP32 support. We used the WifiScan example file to make sure the integrated development environment works. After that, it took us from maybe ten o&#8217;clock to 15.30 to build the devices, first the LCD, then the BMP20, followed by the GPS. When each bit worked to a sufficient degree, we took out the master file that has all the functionality, uploaded it to the numbered boxes, then went out to see that satellite acquisition worked, <\/p>\n\n\n\n<p>Our students each helped one Spanish and one Swiss student to get the hang of first Arduino IDE, then understand the peculiarities of the ESP32 and then showing them wire by wire how to install the parts and how they correspond to the code. At least one team experimented further using the LCD display and created alternate datasets to show at one second intervals. That is the true spirit of the IT Seminar, going above and beyond what you are shown.<\/p>\n\n\n\n<p>What I didn&#8217;t tell the students was that I had Box #11. So, I slipped away at the time when all teams were too excited while testing their box and went down through the buildings B and A, to the corner of the tennis courts to hide. Then I sent my students this picture:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"825\" height=\"825\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010.jpg?resize=825%2C825&#038;ssl=1\" alt=\"\" class=\"wp-image-2093\" srcset=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?resize=1024%2C1024&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?resize=300%2C300&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?resize=150%2C150&amp;ssl=1 150w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?resize=768%2C768&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?resize=1536%2C1536&amp;ssl=1 1536w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?resize=2048%2C2048&amp;ssl=1 2048w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?w=1650&amp;ssl=1 1650w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/20220420_152010-scaled.jpg?w=2475&amp;ssl=1 2475w\" sizes=\"auto, (max-width: 825px) 100vw, 825px\" \/><\/a><figcaption>Faculty member location data<\/figcaption><\/figure><\/div>\n\n\n\n<p>Since they didn&#8217;t know that the path <strong>\/iot11<\/strong> existed and had the map in it, they had to resort to moving around with their box and see every ten seconds if the numbers moved in the right direction in relation to my numbers. I was watching the boxes move on <strong>\/iot00:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><a href=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/Screenshot_20220420-154008_Chrome.jpg?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"555\" height=\"1024\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/Screenshot_20220420-154008_Chrome.jpg?resize=555%2C1024&#038;ssl=1\" alt=\"\" class=\"wp-image-2094\" srcset=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/Screenshot_20220420-154008_Chrome.jpg?resize=555%2C1024&amp;ssl=1 555w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/Screenshot_20220420-154008_Chrome.jpg?resize=163%2C300&amp;ssl=1 163w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/Screenshot_20220420-154008_Chrome.jpg?resize=768%2C1417&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/Screenshot_20220420-154008_Chrome.jpg?resize=832%2C1536&amp;ssl=1 832w, https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2022\/04\/Screenshot_20220420-154008_Chrome.jpg?w=1074&amp;ssl=1 1074w\" sizes=\"auto, (max-width: 555px) 100vw, 555px\" \/><\/a><figcaption>Watching the teams approach me. <\/figcaption><\/figure><\/div>\n\n\n\n<p>I was sitting on a bench next to the tennis courts, under the text &#8220;edificio A&#8221; on the map, but this map doesn&#8217;t show my mystery box. Following the teams&#8217; movement was fun in the extreme, especially seeing two teams embarking on a wild goose chase to the northwest (near Calle Tajo in the image). Finally two teams saw me at the same time and ran for the bag of candy, and it was won by the team whose point man crashed through a hedge, tumbled on the asphalt skinning his knee, and finally stopped against the tennis court wire fence.<\/p>\n\n\n\n<p>True sporting spirit there, don&#8217;t you think?<\/p>\n\n\n\n<p>I had another bag of candy for everyone else, so we retired to the seminar classroom to wrap up the day&#8217;s learnings.  In essence we showed the students that to get going with IoT is yet again easy but takes perseverance and a good nose for finding examples that actually work.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Demo of the device booting and running<\/h2>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"GPS weather station with air pressure, temperature, and Web interface\" width=\"825\" height=\"619\" src=\"https:\/\/www.youtube.com\/embed\/pClqnxGrZOU?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_2050\" class=\"pvc_stats all  \" data-element-id=\"2050\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif?resize=16%2C16&#038;ssl=1\" border=0 \/><\/p><div class=\"pvc_clear\"><\/div>","protected":false},"excerpt":{"rendered":"<p>NOTE &#8211; all code you see here is in better, complete form at my GitHub page. As I have returned &hellip; <a href=\"https:\/\/www.sabulo.com\/sb\/esp32-development-board\/gps-equipped-portable-atmospheric-recorder-with-lcd-and-map-display\/\" class=\"more-link\">More <span class=\"screen-reader-text\">GPS-equipped portable atmospheric recorder, with LCD and map display<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_2050\" class=\"pvc_stats all  \" data-element-id=\"2050\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/www.sabulo.com\/sb\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[101,64,74,93,99,97],"tags":[],"class_list":["post-2050","post","type-post","status-publish","format-standard","hentry","category-bmp280","category-esp32-development-board","category-internet-of-things-iot","category-iot-web-interface","category-lcd-display","category-php"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6vhqE-x4","_links":{"self":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts\/2050","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/comments?post=2050"}],"version-history":[{"count":14,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts\/2050\/revisions"}],"predecessor-version":[{"id":2102,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts\/2050\/revisions\/2102"}],"wp:attachment":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/media?parent=2050"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/categories?post=2050"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/tags?post=2050"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}