Sonntag, 20. Mai 2018

ESP32

http://esp-idf.readthedocs.io/en/latest/api-reference/index.html

https://www.espressif.com/en/products/hardware/esp32/resources

https://www.bluetooth.com/specifications/gatt/services

https://www.instructables.com/id/ESP32ESP8266-Weather-ForecasterPredictor/

https://www.instructables.com/id/IOT-Made-Simple-Playing-With-the-ESP32-on-Arduino-/


Small WeatherStation for ESP32 with BMP180 and SSD1306

[code]/* Last udpate: Added enumerated weather types, improved efficiency * Last update: 25-March-2018, with improved forecast rules *  * ESP32 and BMP180 or BME280 and OLED SH1106 or SSD1306 display Weather Forecaster  * Using air pressure changes to predict weather based on an advanced set of forecasting rules.  * The 'MIT License (MIT) Copyright (c) 2016 by David Bird'. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated  * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,   * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the  * following conditions:     * The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the    * software use is visible to an end-user.   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * See more at http://dsbird.org.uk*/
#include "SSD1306.h" // Version 4 or higher#include "OLEDDisplayUi.h" // Version 4 or higher
#include <WiFi.h>#include "time.h"#include <Wire.h>//#include <Adafruit_BME280.h>#include <Adafruit_BMP085.h> // If using BMP180 or BMP085 and update line: 173/174 accordingly e.g. BME and BME or BMP and BMP 
#define icon_width  40#define icon_height 40
// Define each of the *icons for displayconst uint8_t rain_icon[] PROGMEM = {  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0x81, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0xFE, 0xFF, 0xFF, 0xDF, 0xF0, 0xFC,   0xFF, 0xFF, 0xE7, 0xFF, 0xFB, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0xFF,   0xFD, 0xFF, 0xE7, 0xFF, 0xFF, 0xFD, 0xFF, 0x0F, 0xFE, 0x0F, 0xF8, 0xFF,   0x8F, 0xFC, 0xE7, 0xFB, 0xFF, 0xC7, 0xF9, 0xF7, 0xE3, 0xFF, 0xC3, 0xF3,   0xF3, 0xDF, 0xFF, 0xE8, 0xE7, 0xF9, 0xFF, 0xFF, 0xFE, 0xEF, 0xFD, 0xFF,   0xFF, 0xFF, 0xE7, 0xF9, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF,   0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xF9, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF,   0xFF, 0xFF, 0x3F, 0xFE, 0x3F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x7D, 0xBF,   0xEF, 0xFF, 0xFF, 0xBE, 0xDF, 0xF7, 0xFF, 0x7F, 0xDF, 0xEF, 0xFB, 0xFF,   0xBF, 0xEF, 0xF7, 0xFD, 0xFF, 0xDF, 0xF7, 0xFB, 0xFE, 0xFF, 0xEF, 0xFB,   0x7D, 0xFF, 0xFF, 0xF7, 0xFD, 0xBE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
const uint8_t sunny_icon[] PROGMEM = {  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF,   0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF,   0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFD, 0xDF, 0xFF, 0xE3, 0xFF, 0xF8, 0x8F,   0xFF, 0xE3, 0x7F, 0xF0, 0x07, 0xFF, 0xE3, 0x3F, 0xF8, 0x0F, 0xFE, 0xFF,   0x1F, 0xFC, 0x1F, 0x7C, 0x00, 0x0E, 0xFE, 0x3F, 0x18, 0x00, 0x1C, 0xFF,   0x7F, 0xCC, 0xFF, 0xB1, 0xFF, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xFF, 0xF3,   0xFF, 0xCF, 0xFF, 0xFF, 0xF1, 0xFF, 0x9F, 0xFF, 0xFF, 0xF9, 0xFF, 0x9F,   0xFF, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0x01,   0xF9, 0xFF, 0x9F, 0x80, 0x01, 0xF9, 0xFF, 0x9F, 0x80, 0x01, 0xF9, 0xFF,   0x9F, 0x80, 0xFF, 0xF9, 0xFF, 0x9F, 0xFF, 0xFF, 0xF1, 0xFF, 0x8F, 0xFF,   0xFF, 0xF1, 0xFF, 0x8F, 0xFF, 0xFF, 0xE3, 0xFF, 0xE7, 0xFF, 0xFF, 0xC7,   0xFF, 0xF3, 0xFF, 0xFF, 0x8D, 0xFF, 0xD8, 0xFF, 0xFF, 0x38, 0x00, 0x8C,   0xFF, 0x7F, 0x70, 0x00, 0x07, 0xFF, 0x3F, 0xF8, 0xFF, 0x0F, 0xFE, 0x1F,   0xFC, 0xE3, 0x1F, 0xFC, 0x0F, 0xFE, 0xE3, 0x3F, 0xF8, 0x07, 0xFF, 0xE3,   0x7F, 0xF0, 0x8F, 0xFF, 0xE3, 0xFF, 0xF8, 0xDF, 0xFF, 0xE3, 0xFF, 0xFD,   0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF,   0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
const uint8_t mostlysunny_icon[] PROGMEM = {  0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFD, 0x7E,   0xFF, 0xFF, 0xFF, 0xFB, 0xBF, 0xEF, 0xFF, 0xFF, 0x17, 0xE0, 0xF7, 0xFF,   0xFF, 0xCF, 0x9F, 0xF9, 0xFF, 0xFF, 0xE6, 0x3F, 0xFD, 0xFF, 0xFF, 0xF5,   0x7F, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFE, 0xFF, 0xFF, 0xF9, 0xFF, 0x00,   0xFF, 0xFF, 0xFD, 0x7F, 0x08, 0xFC, 0xFF, 0xFD, 0xBF, 0xE1, 0xF9, 0xFF,   0xFD, 0xCF, 0xFF, 0xF7, 0xFF, 0xFD, 0xF7, 0xFF, 0xE7, 0xFF, 0xF9, 0xFB,   0xFF, 0xCF, 0xFF, 0xF3, 0xFB, 0xFF, 0x1F, 0xFC, 0x17, 0xF0, 0xFF, 0x1F,   0xF9, 0xC7, 0xF7, 0xFF, 0x8F, 0xF3, 0xEF, 0xC7, 0xFF, 0x87, 0xE7, 0xE7,   0xBF, 0xFF, 0xD1, 0xCF, 0xF3, 0xFF, 0xFF, 0xFD, 0xDF, 0xFB, 0xFF, 0xFF,   0xFF, 0xCF, 0xF3, 0xFF, 0xFF, 0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xE7,   0xF7, 0xFF, 0xFF, 0xFF, 0xF3, 0xEF, 0xFF, 0xFF, 0xFF, 0xF9, 0x9F, 0xFF,   0xFF, 0x7F, 0xFC, 0x7F, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };  const uint8_t cloudy_icon[] PROGMEM = {  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0x7F, 0x78, 0xFC, 0xFF,   0xFF, 0xBF, 0xFF, 0xF9, 0xFF, 0xFF, 0xCF, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7,   0xFF, 0xE7, 0xFF, 0xFF, 0xFB, 0xFF, 0xCF, 0xFF, 0xFF, 0xFB, 0xFF, 0x1F,   0xFC, 0x3F, 0xF0, 0xFF, 0xE7, 0xFB, 0xCF, 0xF7, 0xFF, 0xF3, 0xF7, 0xEF,   0xCF, 0xFF, 0xF9, 0xEF, 0xF7, 0xBF, 0xFF, 0xFD, 0xCF, 0xF3, 0xFF, 0xFF,   0xFD, 0xDF, 0xFB, 0xFF, 0xFF, 0xFF, 0xDF, 0xFB, 0xFF, 0xFF, 0xFF, 0xEF,   0xF7, 0xFF, 0xFF, 0xFF, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF, 0xF3, 0xEF, 0xFF,   0xFF, 0xFF, 0xF9, 0x9F, 0xFF, 0xFF, 0x7F, 0xFC, 0x7F, 0x00, 0x00, 0x00,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
const uint8_t tstorms_icon[] PROGMEM = {  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0x81, 0xFF, 0xFF, 0xFF, 0x3F, 0x04, 0xFE, 0xFF, 0xFF, 0xDF, 0xF0, 0xFC,   0xFF, 0xFF, 0xE7, 0xFF, 0xFB, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0xFF,   0xFD, 0xFF, 0xE7, 0xFF, 0xFF, 0xFD, 0xFF, 0x0F, 0xFE, 0x0F, 0xF8, 0xFF,   0x8F, 0xFC, 0xE7, 0xFB, 0xFF, 0xC7, 0xF9, 0xF7, 0xE3, 0xFF, 0xC3, 0xF3,   0xF3, 0xDF, 0xFF, 0xE8, 0xE7, 0xF9, 0xFF, 0xFF, 0xFE, 0xEF, 0xFD, 0xFF,   0xFF, 0xFF, 0xE7, 0xF9, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF,   0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xF9, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF,   0xFF, 0xFF, 0x3F, 0xFE, 0x3F, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x7F, 0x7F,   0xFF, 0xFF, 0xFF, 0xBF, 0xBF, 0xFF, 0xFF, 0xFF, 0xDF, 0x8F, 0xFF, 0xFF,   0xFF, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0x4F,   0xF7, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xF1, 0xFF,   0xFF, 0xFF, 0xDF, 0xFE, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,   0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF,   0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };  String      time_str, weather_text, weather_extra_text;int         last_reading_hour, reading_hour, hr_cnt;const char* ssid     = "UT-VNet";const char* password = "43246641485350293845";enum image_names { // enumerated table used to point to images                  rain_img, sunny_img, mostlysunny_img, cloudy_img, tstorms_img,                 } image;
// Define and enumerated type and assign values to expected weather types.// These values help to determine the average weather preceeding a 'no-change' forecast e.g. rain, rain then mostlysun = -1 (-1 + -1 + 1) resulting on balance = more rainenum weather_type {unknown     =  4,                   sunny       =  2,                   mostlysunny =  1,                   cloudy      =  0,                   rain        = -1,                   tstorms     = -2                   };
enum weather_description {GoodClearWeather, BecomingClearer,                          NoChange, ClearSpells, ClearingWithin12hrs, ClearingAndColder,                          GettingWarmer, WarmerIn2daysRainLikely,                          ExpectRain, WarmerRainWithin36hrs, RainIn18hrs, RainHighWindsClearAndCool,                          GalesHeavyRainSnowInWinter                          };
weather_type current_wx; // Enable the current wx to be recorded
const uint8_t* image_table[] PROGMEM = {rain_icon, sunny_icon, mostlysunny_icon, cloudy_icon, tstorms_icon}; // An array of image icons
// An array structure to record pressure, temperaturre, humidity and weather statetypedef struct {  float pressure;            // air pressure at the designated hour  float temperature;         // temperature at the designated hour  float humidity;            // humidity at the designated hour  weather_type wx_state_1hr; // weather state at 1-hour  weather_type wx_state_3hr; // weather state at 3-hour point} wx_record_type;
wx_record_type reading[24]; // An array covering 24-hours to enable P, T, % and Wx state to be recorded for every hour
int wx_average_1hr, wx_average_3hr; // Indicators of average weather
bool look_3hr = true;bool look_1hr = false; SSD1306 display(0x3c, 5,4); // OLED display object definition (address, SDA, SCL)OLEDDisplayUi ui     ( &display );
//Adafruit_BME280 bme;Adafruit_BMP085 bme; // If using BMP180 or BMP085
WiFiClient client; // wifi client object
#define pressure_offset 3.3 // Used to adjust sensor reading to correct pressure for your location
/////////////////////////////////////////////////////////////////////////// What's displayed along the top linevoid msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {  display->setTextAlignment(TEXT_ALIGN_LEFT);  display->drawString(0,0, time_str.substring(0,8));  //HH:MM:SS Sat 05-07-17  display->setTextAlignment(TEXT_ALIGN_RIGHT);  display->drawString(128,0, time_str.substring(9));  display->setTextAlignment(TEXT_ALIGN_LEFT);}
// This frame draws a weather icon based on 3-hours of data for the predictionvoid drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  float trend  = reading[23].pressure - reading[20].pressure;                          // Trend over the last 3-hours  ForecastToImgTxt(get_forecast_text(reading[23].pressure, trend, look_3hr));          // From forecast and trend determine what image to display  display->drawXbm(x+0,y+15, icon_width, icon_height, image_table[image]);             // Display corresponding image  display->drawStringMaxWidth(x+45,y+12,90,String(reading[23].pressure,1)+" hPA");     // Show current air pressure   display->drawStringMaxWidth(x+45,y+25,90,String(trend,1)+" "+get_trend_text(trend)); // and pressure trend}
// This frame shows a weather description based on 3-hours of data for the predictionvoid drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  float  trend = reading[23].pressure - reading[20].pressure;                             // Get current trend over last 3-hours  weather_description wx_text = get_forecast_text(reading[23].pressure, trend, look_3hr); // Convert to forecast text based on 3-hours  ForecastToImgTxt(wx_text);                                                              // Display corresponding text  display->setFont(ArialMT_Plain_16);  display->drawStringMaxWidth(x+0,y+10,127,weather_text);  display->setFont(ArialMT_Plain_10);}
// This frame draws a graph of pressure (delta) change for the last 24-hours, see Annex* for more detailsvoid drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  int gwidth   = 75; // Graph width in pixels  int gscale   = 30; // Graph height in pixels  int num_bars = 8;  // Number of bars to display  #define yscale 8   // Graph +/- y-axis scale  e.g. 8 displays +/-8 and scales data accordingly  float bar_width = gwidth / (num_bars+1); // Determine bar width based on graph width  x = 30; // Sets position of graph on screen  y = 15; // Sets position of graph on screen  display->drawVerticalLine(x, y, gscale+1);  display->drawString(x-18,y-6,">+"+String(yscale));  display->drawString(x-8,y+gscale/2-6,"0");  display->drawString(x-15,y+gscale-6,"<-"+String(yscale));  display->drawString(x-30,y+gscale/2-6,String(hr_cnt%24));  display->drawString(x+2+(bar_width+3)*0, y+gscale,"-24");  // 24hr marker at bar 0  display->drawString(x+2+(bar_width+3)*2, y+gscale,"-12");  // 12hr marker at bar 2  display->drawString(x+2+(bar_width+3)*5, y+gscale,"-2");   // 2hr  marker at bar 5  display->drawString(x+2+(bar_width+3)*7, y+gscale,"0");    // 0hr  marker at bar 7  int display_points [8] = {0,5,11,17,20,21,22,23}; // Only display time for hours 0,5,11,17,20,21,22,23  float value;  for (int bar_num = 0; bar_num < num_bars; bar_num++){      // Now display a bar at each hour position -24, -18, -12, -6, -3, -2, -1 and 0 hour    value = map(reading[display_points[bar_num]].pressure, reading[23].pressure-yscale, reading[23].pressure+yscale, gscale, 0);    if (value > gscale) value = gscale;                      // Screen scale is 0 to e.g. 40pixels, this stops drawing beyond graph bounds    if (value < 0     ) value = 0;                           // 0 is top of graph, this stops drawing beyond graph bounds    display->drawHorizontalLine(x+bar_num*(bar_width+3)+2, y+value, bar_width);    for (int yplus=gscale; yplus > value; yplus = yplus - 1) {      display->drawHorizontalLine(x+bar_num*(bar_width+3)+2, y + yplus, bar_width);    }  }}
// This frame draws a weather icon based on 1-hour of data for the predictionvoid drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  reading[23].pressure = (reading[23].pressure + read_pressure())/2;                 // Update rolling average, gets reset on the hour transition  float  trend = reading[23].pressure - reading[22].pressure;                        // Get short-term trend for the last 1-hour  weather_description wx_text = get_forecast_text(read_pressure(), trend, look_1hr); // Convert to forecast text based on 1-hours  ForecastToImgTxt(wx_text);  display->drawXbm(x+0,y+15, 40, 40, image_table[image]);           // Display corresponding image  display->drawStringMaxWidth(x+45,y+12,90,"1-Hr forecast");  display->drawStringMaxWidth(x+45,y+22,90,String(read_pressure(),1)+" hPA");  display->drawStringMaxWidth(x+47,y+32,90,String(trend,1)+" "+get_trend_text(trend));}
// This frame shows a weather description based on 1-hour of data for the predictionvoid drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {  reading[23].pressure = (reading[23].pressure + read_pressure())/2;                 // Update rolling average  float  trend = reading[23].pressure - reading[22].pressure;                        // Get short-term trend  weather_description wx_text = get_forecast_text(read_pressure(), trend, look_1hr); // Convert to forecast text based on 1-hours  ForecastToImgTxt(wx_text);  display->drawString(x+0,y+10,"Short-term forecast:");  display->setFont(ArialMT_Plain_16);  display->drawStringMaxWidth(x+0,y+18,127,weather_text);  display->setFont(ArialMT_Plain_10);}
float read_pressure(){  int reading = (bme.readPressure()/100.0F+pressure_offset)*10; // Rounded result to 1-decimal place  return (float)reading/10;}
// Convert pressure trend to textString get_trend_text(float trend){  String trend_str = "Steady"; // Default weather state  if (trend > 3.5)                          { trend_str = "Rising fast";  }  else if (trend >   1.5  && trend <= 3.5)  { trend_str = "Rising";       }  else if (trend >   0.25 && trend <= 1.5)  { trend_str = "Rising slow";  }  else if (trend >  -0.25 && trend <  0.25) { trend_str = "Steady";       }  else if (trend >= -1.5  && trend < -0.25) { trend_str = "Falling slow"; }  else if (trend >= -3.5  && trend < -1.5)  { trend_str = "Falling";      }  else if (trend <= -3.5)                   { trend_str = "Falling fast"; }  return trend_str;}
// Convert forecast text to a corresponding image for display together with a record of the current weathervoid ForecastToImgTxt(weather_description wx_text){  if      (wx_text == GoodClearWeather)           {image = sunny_img;       current_wx = sunny;        weather_text = "Good clear weather";}  else if (wx_text == BecomingClearer)            {image = mostlysunny_img; current_wx = mostlysunny;  weather_text = "Becoming clearer";}  else if (wx_text == NoChange)                   {image = cloudy_img;      current_wx = cloudy;       weather_text = "No change, clearing";}  else if (wx_text == ClearSpells)                {image = mostlysunny_img; current_wx = mostlysunny;  weather_text = "Clear spells";}  else if (wx_text == ClearingWithin12hrs)        {image = mostlysunny_img; current_wx = mostlysunny;  weather_text = "Clearing within 12-hrs";}  else if (wx_text == ClearingAndColder)          {image = mostlysunny_img; current_wx = mostlysunny;  weather_text = "Clearing and colder";}  else if (wx_text == GettingWarmer)              {image = mostlysunny_img; current_wx = mostlysunny;  weather_text = "Getting warmer";}  else if (wx_text == WarmerIn2daysRainLikely)    {image = rain_img;        current_wx = rain;         weather_text = "Warmer in 2-days, rain likely";}  else if (wx_text == ExpectRain)                 {image = rain_img;        current_wx = rain;         weather_text = "Expect rain";}  else if (wx_text == WarmerRainWithin36hrs)      {image = rain_img;        current_wx = rain;         weather_text = "Warmer, rain within 36-hrs";}  else if (wx_text == RainIn18hrs)                {image = rain_img;        current_wx = rain;         weather_text = "Rain in 18-hrs";}  else if (wx_text == RainHighWindsClearAndCool)  {image = rain_img;        current_wx = rain;         weather_text = "Rain, high winds, clear and cool";}  else if (wx_text == GalesHeavyRainSnowInWinter) {image = tstorms_img;     current_wx = tstorms;      weather_text = "Gales, heavy rain, in winter snow";}}
// Convert pressure and trend to a weather description either for 1 or 3 hours with the boolean true/false switchweather_description get_forecast_text(float pressure_now, float trend, bool range) {  String trend_str = get_trend_text(trend);  weather_description wx_text = NoChange; //As a default forecast   image = cloudy_img; // Generally when there is 'no change' then cloudy is the conditions  if (pressure_now >= 1022.68 )                                                          {wx_text = GoodClearWeather;}  if (pressure_now >= 1022.7  && trend_str  == "Falling fast")                           {wx_text = WarmerRainWithin36hrs;}  if (pressure_now >= 1013.2  && pressure_now <= 1022.68 &&      (trend_str == "Steady" || trend_str == "Rising slow"))                              {wx_text = NoChange; (range?wx_history_3hr():wx_history_1hr()); }  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 &&     (trend_str == "Rising" || trend_str == "Rising fast"))                              {wx_text = GettingWarmer;}  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && trend_str == "Rising slow")   {wx_text = BecomingClearer;}  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 &&      (trend_str == "Falling slow" || trend_str == "Falling fast"))                       {wx_text = ExpectRain;}  if (pressure_now >= 1013.2 && pressure_now <= 1022.68 && trend_str  == "Steady")       {wx_text = ClearSpells; (range?wx_history_3hr():wx_history_1hr());};  if (pressure_now <= 1013.2 && (trend_str == "Falling slow" || trend_str == "Falling")) {wx_text = RainIn18hrs;}  if (pressure_now <= 1013.2  &&  trend_str == "Falling fast")                           {wx_text = RainHighWindsClearAndCool;}  if (pressure_now <= 1013.2  &&      (trend_str == "Rising" || trend_str=="Rising slow"||trend_str=="Rising fast"))      {wx_text = ClearingWithin12hrs;}  if (pressure_now <= 1009.14 && trend_str  == "Falling fast")                           {wx_text = GalesHeavyRainSnowInWinter;}  if (pressure_now <= 1009.14 && trend_str  == "Rising fast")                            {wx_text = ClearingAndColder;}  return wx_text;}
// Convert 1-hr weather history to textvoid wx_history_1hr() {  if      (wx_average_1hr >  0) weather_extra_text = ", expect sun";  else if (wx_average_1hr == 0) weather_extra_text = ", mainly cloudy";  else if (wx_average_1hr <  0) weather_extra_text = ", expect rain";  else weather_extra_text = "";}
// Convert 3-hr weather history to textvoid wx_history_3hr() {  if      (wx_average_3hr >  0) weather_extra_text = ", expect sun";  else if (wx_average_3hr == 0) weather_extra_text = ", mainly cloudy";  else if (wx_average_3hr <  0) weather_extra_text = ", expect rain";  else weather_extra_text = "";}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// This array keeps function pointers to all frames// frames are the single views that slide inFrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5};
// how many frames are there?int frameCount = 5;
// Overlays are statically drawn on top of a frame eg. a clockOverlayCallback overlays[] = { msOverlay };int overlaysCount = 1;
void setup() {   float p,t;  Serial.begin(115200);  Wire.begin(5,4);  if (!StartWiFi(ssid,password)) Serial.println("Failed to start WiFi Service after 20 attempts");;  configTime(1, 3600, "pool.ntp.org");  if (!bme.begin()) { Serial.println("Could not find a sensor, check wiring!");}    else   {    Serial.println("Found a sensor continuing");    while (isnan(bme.readPressure())) { Serial.println(bme.readPressure()); }  }  while (!update_time());  //Get the latest time  for (int i = 0; i <= 23; i++){ // At the start all array values are the same as a baseline     reading[i].pressure     = read_pressure();       // A rounded to 1-decimal place version of pressure    reading[i].temperature  = bme.readTemperature(); // Although not used, but avialable    reading[i].humidity     = bme.readAltitude();    // Although not used, but avialable    reading[i].wx_state_1hr = unknown;               // To begin with      reading[i].wx_state_3hr = unknown;               // To begin with   }                                                  // Note that only 0,5,11,17,20,21,22,23 are used as display positions  last_reading_hour = reading_hour;  wx_average_1hr = 0; // Until we get a better idea  wx_average_3hr = 0; // Until we get a better idea    // An ESP is capable of rendering 60fps in 80Mhz mode but leaves little time for anything else, run at 160Mhz mode or just set it to about 30 fps  ui.setTargetFPS(20);  ui.setIndicatorPosition(BOTTOM);         // You can change this to TOP, LEFT, BOTTOM, RIGHT  ui.setIndicatorDirection(LEFT_RIGHT);    // Defines where the first frame is located in the bar  ui.setFrameAnimation(SLIDE_LEFT);        // You can change the transition that is used SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN  ui.setFrames(frames, frameCount);        // Add frames  ui.setOverlays(overlays, overlaysCount); // Add overlays  ui.init(); // Initialising the UI will init the display too.  display.flipScreenVertically();  display.setFont(ArialMT_Plain_10);  display.setTextAlignment(TEXT_ALIGN_LEFT);}
void loop() {  int remainingTimeBudget = ui.update();  update_time_and_data();  if (remainingTimeBudget > 0) { // Do some work here if required    //for (int i = 0; i < 24;i++){    //  Serial.println(String(i)+" "+String(reading[pressure][i]));    //}    delay(remainingTimeBudget);  }}
void update_time_and_data(){  while (!update_time());  if (reading_hour != last_reading_hour) { // If the hour has advanced, then shift readings left and record new values at array element [23]    for (int i = 0; i < 23;i++){      reading[i].pressure     = reading[i+1].pressure;      reading[i].temperature  = reading[i+1].temperature;      reading[i].wx_state_1hr = reading[i+1].wx_state_1hr;      reading[i].wx_state_3hr = reading[i+1].wx_state_3hr;    }    reading[23].pressure     = read_pressure(); // Update time=now with current value of pressure    reading[23].wx_state_1hr = current_wx;    reading[23].wx_state_3hr = current_wx;    last_reading_hour        = reading_hour;    hr_cnt++;    wx_average_1hr = reading[22].wx_state_1hr + current_wx;           // Used to predict 1-hour forecast extra text    wx_average_3hr = 0;    for (int i=23;i >= 21; i--){                                      // Used to predict 3-hour forecast extra text       wx_average_3hr = wx_average_3hr + (int)reading[i].wx_state_3hr; // On average the last 3-hours of weather is used for the 'no change' forecast - e.g. more of the same?    }  }  }
bool update_time(){  struct tm timeinfo;  if(!getLocalTime(&timeinfo)){    Serial.println("Failed to obtain time");    return false;  }  //See http://www.cplusplus.com/reference/ctime/strftime/  Serial.println(&timeinfo, "%A, %d %B %y %H:%M:%S"); // Displays: Saturday, 24 June 17 14:05:49  char strftime_buf[64];  strftime(strftime_buf, sizeof(strftime_buf), "%R:%S   %a %d-%m-%y", &timeinfo);  time_str = strftime_buf;  // Now is this format HH:MM:SS Sat 05-07-17  reading_hour = time_str.substring(0,2).toInt();  return true;}
int StartWiFi(const char* ssid, const char* password){  int connAttempts = 0;  Serial.println("\r\nConnecting to: "+String(ssid));  WiFi.begin(ssid, password);  while (WiFi.status() != WL_CONNECTED ) {    delay(500);    Serial.print(".");    if(connAttempts > 20) return false;    connAttempts++;  }  Serial.print("WiFi connected\r\nIP address: ");  Serial.println(WiFi.localIP());  return true;}/*FRAME-3 description// This frame draws a graph of pressure (delata) change for the last 24-hours, see Annex* for more details// Draws a 'relative value' chart using reading[23] as the baseline// +8 |// +7 |--  //    : // +1 |--  --  --  --  --  --  --   //  0 +-24+-18+-12+-8-+-3-+-2-+-1-+-0-+// -1 |  // -2 |//    The 'reading' array holds values for Pressure, Temperature, Humidity and Wx State for the last 24-hours//    [00][01][02][03][04][05][06][07][08][09][10][11][12][13][14][15][16][17][18][19][20][21][22][23] Values are shifted left <-- each hour//     ^-23Hr              ^-18Hr                  ^-12Hr                  ^-6Hr       ^-3 ^-2 ^-1 ^0Hr//     P  ~ readings in each array position//     T  ~ readings in each array position//     %  ~ readings in each array position//     Wx ~ readings in each array position
// Forecast basics:// Look at the pressure change over3 hours// If pressure is descending, then a low pressure area is approaching // If pressure is ascending , then a low is passing or a high pressure is coming// When pressure is changing rapidly (>6hPa/3 hours), it will be windy (or potentially windy) 
// More detailed:// Pressure falling slowly (0.5 - 3 hPa in 3h): low is weak, dying or moving slowly. You might get some rain but typically no high winds.// Pressure falling moderately (3-6 hPa/3h): rapid movement or deepening low. Moderate winds and rain and a warm front.                                           : the low is passing fast, the day after tomorrow will typically be fine. // Pressure falling fast (6-12 hPa/3h) : Storm conditions highly likely.// Pressure rises are connected with gradually drier weather
 */[/code]

ESP32 und MicroPython (2.6"/2.8"TFT LCD Shield inkl. SD Card SPI)

Der neue MicroShip der von der Firma Espressif Systems entwickelt wurde, ist seit einigen Monaten am Markt und bringt eine Menge neue Futures mit.
Eine der angenehmeren Möglichkeiten mit dem ESP32 ein Projekt umzusetzen, ist das verwenden von "MicroPython". In Verbindung mit dem "Atom" Editor und dem "pymakr" PlugIn.


WeMos D1 R32 Uno mit 2.6"/2.8"TFT LCD Shield

SD-Card (SPI / on Shield)

SS - CS - 05
SCK - CLK - 18
DO - MISO - 19
DI - MOSI - 23

LCD_TFT (2.6"/2.8"TFT LCD Shield)

LCD_RST - LCD_Reset - 36
LCD_CS - ChipSelect - 34
LCD_RS - (RegisterSelect) - 35
LCD_WR - (WRite data) - 04
LCD_RD - (ReaD data) - 02

LCD_D0 - 12
LCD_D1 - 13

LCD_D2 - 26
LCD_D3 - 25
LCD_D4 - 17
LCD_D5 - 16
LCD_D6 - 27
LCD_D7 - 14

WiKi / Infos zum Shield: 2.6" / 2.8" TFT_LCD


>>> import gc >>> gc.collect() >>> gc.mem_free() 90208 >>> import uos >>> uos.uname() (sysname='esp32', nodename='esp32', release='1.9.3', version='v1.9.3-240-ga275cb0f on 2018-01-27', machine='ESP32 module with ESP32') >>>





SPI / ESP32 / MicroPython
Serial Peripheral Interface

... ist ein Protokoll um zwischen 2 Geräten Daten in beide Richtungen zu senden.
Als Beispiel: Ein LED-Controller bekommt Informationen/Daten gesendet um zu wissen was er tun soll. Hingegen ein Temperatursensor schickt Daten an den Microkontroller.

Und so läuft das Spiel:



Der SPI Master:

Im SPI Protokoll gibt es einen wesentlichen Unterschied zwischen dem Master, der die Verbindung kontrolliert und steuert, und dem Slave der Daten von und zum Master sendet und empfängt.
In allen Fällen ist der Microkontroller der Master und steuert die SPI Verbindung.
In dem folgenden Beispiel wollen wir einmal ausprobieren wie der ESP32 mit Hilfe von MicroPython die SPI Verbindung mit anderen Geräten steuert.

In den meisten Fällen braucht eine SPI Verbindung mindestens 4 Leitungen um sich zu verbinden.

  • Clock, das SPI Master Gerät schaltet diese Leitung auf "I oder 0" um der Gegenstelle (Slave) zu sagen wann sie senden und wann sie empfangen soll/darf.
  • MOSI (master output, slave input), über diese Leitung werden Daten vom Master an ein Slave-Gerät gesendet. Das ist quasi der Datenausgang vom Master-Gerät.
  • MISO (master input, slave output), Über diese Leitung senden die Slave-Geräte ihre Daten an den Master (Microkontroller). Also der wenn man so will ist das der Daten-Ausgang der Slave-Geräte.
  • Chip Select (CS), auch wenn es nicht nötig ist, haben die meistern SPI Geräte eine CS-Leitung. Über diese Leitung kann der Master (mit I oder 0) den angeschlossenen Slave-Geräten mitteilen ob sie gerade auf SPI-Befehle hören sollen oder nicht. So lange alle angeschlossenen SPI-Geräte an eine SC Leitung angeschlossen sind, können sie sich die anderen Leitungen (Clock, MOSI und MISO) teilen. So kann jedes Gerät einzeln angesprochen werden.
Allerdings solltest Du dir bewusst sein, dass bei den unterschiedlichen Boards(Controllern), auf denen MicroPythen läuft, die SPI API beim erstellen des Master Mode unterschiedliche Wege kennt.