Crystalfontz 7 Inch TFT Weather Display

by LEKtronics in Circuits > Arduino

159 Views, 0 Favorites, 0 Comments

Crystalfontz 7 Inch TFT Weather Display

fnished_assembly_r1.jpg
finished_mount.jpg

This project is a wireless display for viewing the weather data from a Davis Vantage Pro 2 weather station. It utilizes a 7" TFT display from Crystalfontz and is driven by an Arduino MKR1000 WIFI.

Supplies

display.jpg
breakout.jpg
mkr1000.jpg
USB_C.jpg
photo_resistor.jpg
uSD.jpg
breadboard.jpg
transformer.jpg

Crystalfontz 7 inch TFT display.

Crystalfontz breakout board.

Arduino MKR1000

Adafruit uSD breakout.

PhotoResistor (Generic) and 10k ohm resistor

Adafruit USB C breakout

5vdc (1 amp minimum) wall transformer w/ USB A to USB C cable

SB830 PC BreadBoard

5" x 7" picture frame

Custom 3D printed component holder and frame mask.

Wiring and Initial Programming

MKR1000_pinout.jpg
SRL-image-0.png

This project originally started with a 7 inch ePaper display powered by a ESP8266 processor. It worked good for about a year, and then the ePaper display started to fade. I replaced the display and much to my dismay, it started to fade again.

Enter Brent from Crystalfontz. He provided me with a 7 inch TFT display kit. The kit included the display, a breakout board, and a Seeeduino V4.3. (The Seeeduino is a Arduino clone that runs at 3.3 volts instead of the Uno's 5 volts.)

However, the Seeeduino does not have the WIFI necessary to connect with the Davis weather station. I tried making it all work with the ESP8266 that I used with the ePaper display, but there are too many differences between the AVR processor architecture of the Seeeduino and that of the ESP's. That led me to the Arduino MKR1000.

I was still not out of the woods yet as the MKR uses a SAMD based processor.

The first step was seeing if the demo sketch provided with the Crystalfontz display kit would compile with the MKR1000 selected as a board. I had to make some changes to the pinout definitions before I started the compile. The display sketch include defines that were specific to the AVR:

//Faster direct port access (specific to AVR)
#define CLR_EVE_PD_NOT       (PORTB &= ~(0x01))
#define SET_EVE_PD_NOT       (PORTB |= (0x01))
#define CLR_EVE_CS_NOT       (PORTB &= ~(0x02))
#define SET_EVE_CS_NOT       (PORTB |= (0x02))
#define CLR_SD_CS_NOT        (PORTB &= ~(0x04))
#define SET_SD_CS_NOT        (PORTB |= (0x04))
#define CLR_MOSI             (PORTB &= ~(0x08))
#define SET_MOSI             (PORTB |= (0x08))
#define CLR_MISO             (PORTB &= ~(0x10))
#define SET_MISO             (PORTB |= (0x10))
#define CLR_SCK              (PORTB &= ~(0x20))
#define SET_SCK              (PORTB |= (0x20))
#define CLR_DEBUG_LED        (PORTD &= ~(0x08))
#define SET_DEBUG_LED        (PORTD |= (0x08))

These defines will not work with the MKR. I changed them to the following:

#define CLR_EVE_PD_NOT       (digitalWrite(4,LOW))//set to 0
#define SET_EVE_PD_NOT       (digitalWrite(4,HIGH))//set to 1
#define CLR_EVE_CS_NOT       (digitalWrite(7,LOW))//set to 0
#define SET_EVE_CS_NOT       (digitalWrite(7,HIGH))//set to 1
#define CLR_SD_CS_NOT        (digitalWrite(3,LOW))//set to 0
#define SET_SD_CS_NOT        (digitalWrite(3,HIGH))//set to 1
#define CLR_MOSI             (digitalWrite(8,LOW))//set to 0
#define SET_MOSI             (digitalWrite(8,HIGH))//set to 1
#define CLR_MISO             (digitalWrite(10,LOW))//set to 0
#define SET_MISO             (digitalWrite(10,HIGH))//set to 1
#define CLR_SCK              (digitalWrite(9,LOW))//set to 0
#define SET_SCK              (digitalWrite(9,HIGH))//set to 1
#define CLR_DEBUG_LED        (digitalWrite(6,LOW))//set to 0
#define SET_DEBUG_LED        (digitalWrite(6,HIGH))//set to 1

The rest of the pinouts are as follows:

// Wiring for Arduino MKR1000 connected to CFA10098 breakout for testing.
//  MKR     | Port | 10098/EVE          | Color
// -----------+------+---------------------|--------
// #6/D6    | PD3 | DEBUG_LED          | N/C
// #5/D5    | PD7 | EVE_INT            | Purple
// #4/D4    | PB0 | EVE_PD_NOT         | Blue
// #7/D7    | PB1 | EVE_CS_NOT         | Brown
// #3/D3    | PB2 | SD_CS_NOT          | Grey
// #8/D8    | PB3 | MOSI (hardware SPI) | Yellow
// #10/D10  | PB4 | MISO (hardware SPI) | Green
// #9/D9    | PB5 | SCK (hardware SPI) | White
//Arduino style pin defines
// Debug LED, or used for scope trigger or precise timing
#define DEBUG_LED  (6)
// Interrupt from EVE to Arduino - input, not used in this example.
#define EVE_INT    (5)
// PD_N from Arduino to EVE - effectively EVE reset
#define EVE_PD_NOT (4)
// SPI chip select - defined separately since it's manipulated with GPIO calls
#define EVE_CS_NOT (7)
// Reserved for use with the SD card library
#define SD_CS      (3)
//#define MOSI_PIN   (8)
//#define MISO_PIN   (10)
//#define SCK_PIN    (9)

The MKR doesn't like the vsnprintf_P command in the EVE_draw.cpp file, so by working with Brent, who wrote the original program, he came up with this "fix":

// Don't call _EVE_PrintFF() directly, use EVE_PrintF() macro.
//
// ref http://playground.arduino.cc/Main/Printf
//
uint16_t _EVE_PrintFF(uint16_t FWol,
                     uint16_t x,
                     uint16_t y,
                     uint16_t Font,
                     uint16_t Options,
                     const __FlashStringHelper *fmt, ... )
 {
 //Use the variable argument functions to create the string
 char
   tmp[256]; // resulting string limited to 256 chars
 va_list
   args;
 va_start(args, fmt );
  #if (defined(ARDUINO_ARCH_AVR))
   vsnprintf_P(tmp, 256, (const char *)fmt, args);
 #else
   vsnprintf(tmp, 256, (const char *)fmt, args);
 #endif 
 va_end (args);
 //Put that string into to the EVE Display List

 return(EVE_Text(FWol,
                 x,
                 y,
                 Font,
                 Options,
                 tmp));
 }

The MKR also doesn't like dtostrf() so Brent came up with this:

 // The dtostrf() that is used for the floating point
 // comment is apparently AVR specific. Sigh.
 // There does not seem to be a good SAMD architecture
 // solution as of January 2024 - So I am killing the
 // comment for SAMD :-(

 for(uint8_t i=0;i<=5;i++)
   {
   #if (defined(ARDUINO_ARCH_AVR))
     char
       float_string[12];
     dtostrf((float)touch_transform[i]/(float)0x00010000,10,4, float_string);
     DBG_GEEK("   (int32_t)0x%08lX%c // [%c] = %s\n",
               touch_transform[i],i==5?' ':',',
               i+'A',
               float_string);
   #else
     DBG_GEEK("   (int32_t)0x%08lX%c // [%c] = %s\n",
               touch_transform[i],i==5?' ':',',
               i+'A',
               "no dtostrf() on SAMD, sorry");
   #endif
   }
 DBG_GEEK("   };\n"); 
 }
#endif //(DEBUG_LEVEL==DEBUG_GEEK)

The downside is the touchscreen calibration values don't get displayed. I don't use the touch screen in this project, but if I did, I would hookup to the Seeduino, get the values, and then go back to the MKR.

After I made these changes, the sketch compiled properly!

At this point I temporarily hooked everything up and verified that the demo program worked as it was designed.

Success! Now on to make the display show the weather data.


Retrieve the Data

DSC02164.JPG
console.jpg
HP_pc.jpg

As stated, I have a Davis Vantage Pro2 weather station hooked up via USB to an old HP desktop computer (Windows 10). The HP runs 24/7 and is in my detached garage because the garage is closer to the weather equipment than the house is.

The desktop is running WeatherLink 6.0.5 software which gathers the data from the Davis console and uploads it to WeatherLink.com. After requesting and receiving an API Key from WeatherLink, I have the ability to download the data via a JSON statement. Now the fun begins!

The first thing we have to do is get the JSON from the internet via WIFI.

#include <Arduino.h>
#include <SPI.h>
#include <stdarg.h>
#include <WiFi101.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;    // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA)
char key[] = SECRET_KEY;      // your API key
//-----------------------------------------------------------
// Just the base of the URL you want to connect to
#define TEST_HOST "api.weatherlink.com"
int status = WL_IDLE_STATUS;
char server[] = TEST_HOST;    // name address for Google (using DNS)
// For HTTP requests
WiFiClient client;
//Static IP address configuration
IPAddress staticIP(x,x,x,x); //ESP static ip
IPAddress gateway(x,x,x,x);   //IP Address of your WiFi Router (Gateway)
IPAddress subnet(x,x,x,x);  //Subnet mask
IPAddress dns(x,x,x,x);  //DNS
//-----------------------------------------------------------
Serial.begin(115200);
  DBG_STAT("Begin\n");
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }
  // attempt to connect to WiFi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
    Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  if (client.connect(server, 80)) {
    Serial.println("connected to server");
  }
//-----------------------------------------------------------
void makeHTTPRequest() {

  Serial.println("Requesting");

  // Opening connection to server (Use 80 as port if HTTP)
  if (!client.connect(TEST_HOST, 80))
  {
    Serial.println(F("Connection failed"));
    cf_Count ++; //keep track of connection fails
    
    WiFi.end();
    delay(10);
    WiFi.begin(ssid, pass);

    return;
  }

  else
  {
    cf_Count = 0; //connection good - reset count
  }

  Serial.print("Connection Fail Count = ");
  Serial.println(cf_Count);

  // give the esp a breather
  yield();

  // Send HTTP request
  client.print(F("GET "));
  // This is the second half of a request (everything that comes after the base URL)
  client.print(SECRET_KEY);
  client.println(F(" HTTP/1.1"));

  //Headers
  client.print(F("Host: "));
  client.println(TEST_HOST);

  client.println(F("Cache-Control: no-cache"));

  if (client.println() == 0)
  {
    Serial.println(F("Failed to send request"));
    return;
  }
  //delay(100);
  // Check HTTP status
  char status[32] = {0};
  client.readBytesUntil('\r', status, sizeof(status));
  if (strcmp(status, "HTTP/1.1 200 OK") != 0)
  {
    Serial.print(F("Unexpected response: "));
    Serial.println(status);
    return;
  }

  // Skip HTTP headers
  char endOfHeaders[] = "\r\n\r\n";
  if (!client.find(endOfHeaders))
  {
    Serial.println(F("Invalid response"));
    return;
  }

  // This is probably not needed for most, but I had issues
  // with the Tindie api where sometimes there were random
  // characters coming back before the body of the response.
  // This will cause no harm to leave it in
  // peek() will look at the character, but not take it off the queue
  while (client.available() && client.peek() != '{')
  {
    char c = 0;
    client.readBytes(&c, 1);
    Serial.print(c);
    Serial.println("BAD");
  }
  DynamicJsonDocument doc(6144);

  DeserializationError error = deserializeJson(doc, client);

  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return;
  }

Now that we have downloaded this huge amount of data, we have to "parse" it in to a usable format.

// The huge input: an extract from Davis Weather response
const char* credit = doc["credit"]; // "Davis Instruments Corp."
const char* credit_URL = doc["credit_URL"]; // "http://www.davisnet.com"
const char* disclaimer_url = doc["disclaimer_url"]; // "http://www.davisnet.com/about/terms.asp"
const char* copyright_url = doc["copyright_url"]; // "http://www.davisnet.com/about/terms.asp"
const char* privacy_policy_url = doc["privacy_policy_url"];

JsonObject image = doc["image"];
const char* image_url = image["url"]; // "http://www.weatherlink.com/images/Logo_Davis_reflxblu.jpg"
const char* image_title = image["title"]; // "Davis WeatherLink"
const char* image_link = image["link"]; // "http://www.weatherlink.com"

const char* suggested_pickup = doc["suggested_pickup"]; // "15 minutes after the hour"
const char* suggested_pickup_period = doc["suggested_pickup_period"]; // "60"
const char* dewpoint_c = doc["dewpoint_c"]; // "-13.3"
const char* dewpoint_f = doc["dewpoint_f"]; // "8.0"
const char* dewpoint_string = doc["dewpoint_string"]; // "8.0 F (-13.3 C)"
const char* heat_index_c = doc["heat_index_c"]; // "-9.4"
const char* heat_index_f = doc["heat_index_f"]; // "15.0"
const char* heat_index_string = doc["heat_index_string"]; // "15.0 F (-9.4 C)"
const char* location = doc["location"]; // "Pemberville, OH, United States of America"
const char* latitude = doc["latitude"]; // "41.40177"
const char* longitude = doc["longitude"]; // "-83.44719"
const char* observation_time = doc["observation_time"]; // "Last Updated on Jan 25 2022, 9:52 pm EST"
const char* observation_time_rfc822 = doc["observation_time_rfc822"]; // "Tue, 25 Jan 2022 21:52:10 ...
const char* pressure_in = doc["pressure_in"]; // "30.328"
const char* pressure_mb = doc["pressure_mb"]; // "1027.0"
const char* pressure_string = doc["pressure_string"]; // "1027.0 mb"
const char* relative_humidity = doc["relative_humidity"]; // "73"
const char* station_id = doc["station_id"]; // "larryk240"
const char* temp_c = doc["temp_c"]; // "-9.2"
const char* temp_f = doc["temp_f"]; // "15.4"
const char* temperature_string = doc["temperature_string"]; // "15.4 F (-9.2 C)"
const char* wind_degrees = doc["wind_degrees"]; // "271"
const char* wind_dir = doc["wind_dir"]; // "West"
const char* wind_kt = doc["wind_kt"]; // "5.2"
const char* wind_mph = doc["wind_mph"]; // "6.0"
const char* windchill_c = doc["windchill_c"]; // "-10.6"
const char* windchill_f = doc["windchill_f"]; // "13.0"
const char* windchill_string = doc["windchill_string"]; // "13.0 F (-10.6 C)"

JsonObject davis_current_observation = doc["davis_current_observation"];
const char* davis_current_observation_DID = davis_current_observation["DID"]; // "001D0AE07E21"
const char* davis_current_observation_station_name = davis_current_observation["station_name"];
int davis_current_observation_observation_age = davis_current_observation["observation_age"]; // 3
const char* davis_current_observation_dewpoint_day_high_f = davis_current_observation["dewpoint_day_high_f"];
const char* davis_current_observation_dewpoint_day_high_time = davis_current_observation["dewpoint_day_high_time"];
const char* davis_current_observation_dewpoint_day_low_f = davis_current_observation["dewpoint_day_low_f"];
const char* davis_current_observation_dewpoint_day_low_time = davis_current_observation["dewpoint_day_low_time"];
const char* davis_current_observation_dewpoint_month_high_f = davis_current_observation["dewpoint_month_high_f"];
const char* davis_current_observation_dewpoint_month_low_f = davis_current_observation["dewpoint_month_low_f"];
const char* davis_current_observation_dewpoint_year_high_f = davis_current_observation["dewpoint_year_high_f"];
const char* davis_current_observation_dewpoint_year_low_f = davis_current_observation["dewpoint_year_low_f"];
const char* davis_current_observation_heat_index_day_high_f = davis_current_observation["heat_index_day_high_f"];
const char* davis_current_observation_heat_index_day_high_time = davis_current_observation["heat_index_day_high_time"];
const char* davis_current_observation_heat_index_month_high_f = davis_current_observation["heat_index_month_high_f"];
const char* davis_current_observation_heat_index_year_high_f = davis_current_observation["heat_index_year_high_f"];
const char* davis_current_observation_pressure_day_high_in = davis_current_observation["pressure_day_high_in"];
const char* davis_current_observation_pressure_day_high_time = davis_current_observation["pressure_day_high_time"];
const char* davis_current_observation_pressure_day_low_in = davis_current_observation["pressure_day_low_in"];
const char* davis_current_observation_pressure_day_low_time = davis_current_observation["pressure_day_low_time"];
const char* davis_current_observation_pressure_month_high_in = davis_current_observation["pressure_month_high_in"];
const char* davis_current_observation_pressure_month_low_in = davis_current_observation["pressure_month_low_in"];
const char* davis_current_observation_pressure_tendency_string = davis_current_observation["pressure_tendency_string"];
const char* davis_current_observation_pressure_year_high_in = davis_current_observation["pressure_year_high_in"];
const char* davis_current_observation_pressure_year_low_in = davis_current_observation["pressure_year_low_in"];
const char* davis_current_observation_rain_day_in = davis_current_observation["rain_day_in"];
const char* davis_current_observation_rain_month_in = davis_current_observation["rain_month_in"];
const char* davis_current_observation_rain_rate_day_high_in_per_hr = davis_current_observation["rain_rate_day_high_in_per_hr"];
const char* davis_current_observation_rain_rate_hour_high_in_per_hr = davis_current_observation["rain_rate_hour_high_in_per_hr"];
const char* davis_current_observation_rain_rate_in_per_hr = davis_current_observation["rain_rate_in_per_hr"];
const char* davis_current_observation_rain_rate_month_high_in_per_hr = davis_current_observation["rain_rate_month_high_in_per_hr"];
const char* davis_current_observation_rain_rate_year_high_in_per_hr = davis_current_observation["rain_rate_year_high_in_per_hr"];
const char* davis_current_observation_rain_storm_in = davis_current_observation["rain_storm_in"];
const char* davis_current_observation_rain_year_in = davis_current_observation["rain_year_in"];
const char* davis_current_observation_relative_humidity_day_high = davis_current_observation["relative_humidity_day_high"];
const char* davis_current_observation_relative_humidity_day_high_time = davis_current_observation["relative_humidity_day_high_time"];
const char* davis_current_observation_relative_humidity_day_low = davis_current_observation["relative_humidity_day_low"];
const char* davis_current_observation_relative_humidity_day_low_time = davis_current_observation["relative_humidity_day_low_time"];
const char* davis_current_observation_relative_humidity_month_high = davis_current_observation["relative_humidity_month_high"];
const char* davis_current_observation_relative_humidity_month_low = davis_current_observation["relative_humidity_month_low"];
const char* davis_current_observation_relative_humidity_year_high = davis_current_observation["relative_humidity_year_high"];
const char* davis_current_observation_relative_humidity_year_low = davis_current_observation["relative_humidity_year_low"];
const char* davis_current_observation_relative_humidity_in = davis_current_observation["relative_humidity_in"];
const char* davis_current_observation_relative_humidity_in_day_high = davis_current_observation["relative_humidity_in_day_high"];
const char* davis_current_observation_relative_humidity_in_day_high_time = davis_current_observation["relative_humidity_in_day_high_time"];
const char* davis_current_observation_relative_humidity_in_day_low = davis_current_observation["relative_humidity_in_day_low"];
const char* davis_current_observation_relative_humidity_in_day_low_time = davis_current_observation["relative_humidity_in_day_low_time"];
const char* davis_current_observation_relative_humidity_in_month_high = davis_current_observation["relative_humidity_in_month_high"];
const char* davis_current_observation_relative_humidity_in_month_low = davis_current_observation["relative_humidity_in_month_low"];
const char* davis_current_observation_relative_humidity_in_year_high = davis_current_observation["relative_humidity_in_year_high"];
const char* davis_current_observation_relative_humidity_in_year_low = davis_current_observation["relative_humidity_in_year_low"];
const char* davis_current_observation_sunrise = davis_current_observation["sunrise"]; // "7:50am"
const char* davis_current_observation_sunset = davis_current_observation["sunset"]; // "5:41pm"
const char* davis_current_observation_temp_day_high_f = davis_current_observation["temp_day_high_f"];
const char* davis_current_observation_temp_day_high_time = davis_current_observation["temp_day_high_time"];
const char* davis_current_observation_temp_day_low_f = davis_current_observation["temp_day_low_f"];
const char* davis_current_observation_temp_day_low_time = davis_current_observation["temp_day_low_time"];
const char* davis_current_observation_temp_month_high_f = davis_current_observation["temp_month_high_f"];
const char* davis_current_observation_temp_month_low_f = davis_current_observation["temp_month_low_f"];
const char* davis_current_observation_temp_year_high_f = davis_current_observation["temp_year_high_f"];
const char* davis_current_observation_temp_year_low_f = davis_current_observation["temp_year_low_f"];
const char* davis_current_observation_temp_in_day_high_f = davis_current_observation["temp_in_day_high_f"];
const char* davis_current_observation_temp_in_day_high_time = davis_current_observation["temp_in_day_high_time"];
const char* davis_current_observation_temp_in_day_low_f = davis_current_observation["temp_in_day_low_f"];
const char* davis_current_observation_temp_in_day_low_time = davis_current_observation["temp_in_day_low_time"];
const char* davis_current_observation_temp_in_f = davis_current_observation["temp_in_f"]; // "49.6"
const char* davis_current_observation_temp_in_month_high_f = davis_current_observation["temp_in_month_high_f"];
const char* davis_current_observation_temp_in_month_low_f = davis_current_observation["temp_in_month_low_f"];
const char* davis_current_observation_temp_in_year_high_f = davis_current_observation["temp_in_year_high_f"];
const char* davis_current_observation_temp_in_year_low_f = davis_current_observation["temp_in_year_low_f"];
const char* davis_current_observation_wind_day_high_mph = davis_current_observation["wind_day_high_mph"];
const char* davis_current_observation_wind_day_high_time = davis_current_observation["wind_day_high_time"];
const char* davis_current_observation_wind_month_high_mph = davis_current_observation["wind_month_high_mph"];
const char* davis_current_observation_wind_ten_min_avg_mph = davis_current_observation["wind_ten_min_avg_mph"];
const char* davis_current_observation_wind_ten_min_gust_mph = davis_current_observation["wind_ten_min_gust_mph"];
const char* davis_current_observation_wind_year_high_mph = davis_current_observation["wind_year_high_mph"];
const char* davis_current_observation_windchill_day_low_f = davis_current_observation["windchill_day_low_f"];
const char* davis_current_observation_windchill_day_low_time = davis_current_observation["windchill_day_low_time"];
const char* davis_current_observation_windchill_month_low_f = davis_current_observation["windchill_month_low_f"];
const char* davis_current_observation_windchill_year_low_f = davis_current_observation["windchill_year_low_f"];

const char* time_to_generate = doc["time_to_generate"]; // "0.008630 seconds"

   if (!error) {

 // Print the result
 //serializeJsonPretty(doc, Serial);

That is a lot of stuff!. We only need a small portion of it, but the data will have to be conditioned even further so that we can use it.

//local
String freedom_dewpoint_f_x = doc["dewpoint_f"];
//move to global
freedom_dewpoint_f = freedom_dewpoint_f_x;

String freedom_observation_time_x = doc["observation_time"];
freedom_observation_time = freedom_observation_time_x;

String freedom_temp_f_x = doc["temp_f"];
freedom_temp_f = freedom_temp_f_x;

String freedom_wind_mph_x = doc["wind_mph"];
freedom_wind_mph = freedom_wind_mph_x;

String freedom_wind_dir_x = doc["wind_dir"];
freedom_wind_dir = freedom_wind_dir_x;

String freedom_windchill_f_x = doc["windchill_f"];
freedom_windchill_f = freedom_windchill_f_x;

String freedom_pressure_in_x = doc["pressure_in"];
freedom_pressure_in = freedom_pressure_in_x;

String freedom_relative_humidity_x = doc["relative_humidity"];
freedom_relative_humidity = freedom_relative_humidity_x;

String freedom_rain_day_in_x = davis_current_observation["rain_day_in"];
freedom_rain_day_in = freedom_rain_day_in_x;

String freedom_rain_month_in_x = davis_current_observation["rain_month_in"];
freedom_rain_month_in = freedom_rain_month_in_x;

String freedom_rain_year_in_x = davis_current_observation["rain_year_in"];
freedom_rain_year_in = freedom_rain_year_in_x;

String freedom_rain_storm_in_x = davis_current_observation["rain_storm_in"];
freedom_rain_storm_in = freedom_rain_storm_in_x;
//freedom_rain_storm_in = ("0.01");// teesting
freedom_rain_storm_flt = freedom_rain_storm_in.toFloat();
Serial.print("freedom_rain_storm_in = ");
Serial.print(freedom_rain_storm_in);
Serial.print("  ");
Serial.println(freedom_rain_storm_flt);

String freedom_pressure_tendency_string_x = davis_current_observation["pressure_tendency_string"];
freedom_pressure_tendency_string = freedom_pressure_tendency_string_x;

String freedom_sunrise_x = davis_current_observation["sunrise"];
freedom_sunrise = freedom_sunrise_x;

String freedom_sunset_x = davis_current_observation["sunset"];
freedom_sunset = freedom_sunset_x;

Serial.println(freedom_observation_time);
/////////////////////////////////// extract minutes from observation time
int delimiter, delimiter_1 ;
delimiter = freedom_observation_time.indexOf(":");
delimiter_1 = freedom_observation_time.indexOf(" ", delimiter + 1);
String freedom_ob_min = freedom_observation_time.substring(delimiter + 1, delimiter_1); //substring(beginning, end)
ob_min = freedom_ob_min.toInt();
Serial.print("ob_min = ");
Serial.println(ob_min);
Serial.print("mem_ob_min = ");
Serial.println(mem_ob_min);

On to the display........................



Display the Data

final_display.jpg
weather_r5.bmp

I have another Instructable where I used a smaller Crystalfontz display. That Instructable goes in to greater detail about the workings of the EVE display processor.

First off, we need a background for the display. This was created with Corel PaintShop Pro 2019. I started with a new 1024x600 image and added the graphics and general text. The file is saved as a .bmp. Next we have to convert the .bmp to a format that the EVE understands. This is done with the EVE Asset Builder program.

Open the EAB and choose the Image Utilities tab. Click the circle with a cross at the right of the screen to add your background image.

Now set the following:

EVE Chip = BT81X Output Format = COMPRESSED_RGBA_ASTC_8x8_KHR

Check Compressed ASTCPreset = thorough

Now click on Convert at the lower right. Once that is done, you can see your newly converted file by opening the output location. Opening the output location should show 7 files. Copy the .bin file to a uSD card, and then delete (or rename) the file named splash.a8z (if you have already loaded the demo uSD files). Then rename the copied .bin file to splash.a8z. You will get a warning about changing the extension, but you can ignore it.

The BT817 (the chip that drives the display) has 16mb of onboard flash memory. You have to load the flash via a micro SD card. In the CFA10108_defines.h line 67 is the following:

#define PROGRAM_FLASH_FROM_USD (0) //uses uSD

By setting the value to 1, the flash will be loaded from the uSD. Once the flash is loaded, set the value back to 0 and the uSD will no longer be needed. The files that go on the uSD are listed below.

NOTE: Instructables does not support a .a8z file type. The .bmp file used in this project is shown above so you can convert it yourself. The other files used in the Crystalfontz demo are available from Crystalfontz.

Now you need to set your new image file as the background. On line 75 of the CFA10108_defines.h file set the value to 1.

#define BMP_DEMO            (1) //Images must already be programmed into
                                 //flash by using PROGRAM_FLASH_FROM_USD

The rest of the defines should be set to 0.

#define  BMP_SCROLL        (0) //1=scrolling background (cloud.a8z) lektronics
                                 //0=static image (splash.a8z) weather
#define SOUND_DEMO          (0) //Uses uSD
#define  SOUND_VOICE       (0) //1=VOI_8K.RAW, 0=MUS_8K.RAW
#define  SOUND_PLAY_TIMES  (0)
#define LOGO_DEMO           (0) //Rotating logo (the PNG or ARGB image
                                 //data is stored in the Seeeduino's flash)
#define  LOGO_PNG_0_ARGB2_1  (0) //Compressed ARGB is 5408 bytes smaller
#define BOUNCE_DEMO         (0) //Ball-and-rubber-band demo.
#define MARBLE_DEMO         (0) //Marble must already be programmed into
                                 //flash by using PROGRAM_FLASH_FROM_USD
                                 //(bluemarb.a8z)
#define TOUCH_DEMO          (0)
#define VIDEO_DEMO          (0) //Video must already be programmed into
                                 //flash by using PROGRAM_FLASH_FROM_USD
                                 //(Ice_400.avi)

You should now see your background image on the screen. It may take a couple of minutes depending on the value of delay on line 496 of the main.ino file

//++++++++++++++++++++++++++++++++++++++++++++++ HTTP Request +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 //delay(900000); // delay 15 minutes
 //delay(300000); // delay 5 minutes
 delay(60000); //delay 1 minute
 makeHTTPRequest();

Now to fill in the blanks. A little more data formatting is required at this point.

//++++++++++++++ Create Char Arrays ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

       int str_len = freedom_dewpoint_f.length() + 1;
       char dew_F[str_len];
       freedom_dewpoint_f.toCharArray(dew_F, str_len);

       int str_len_0 = freedom_observation_time.length() + 1;
       char ob_Time[str_len_0];
       freedom_observation_time.toCharArray(ob_Time, str_len_0);

       int str_len_1 = freedom_temp_f.length() + 1;
       char temp_F[str_len_1];
       freedom_temp_f.toCharArray(temp_F, str_len_1);

       int str_len_2 = freedom_wind_mph.length() + 1;
       char wind_Mph[str_len_2];
       freedom_wind_mph.toCharArray(wind_Mph, str_len_2);

       int str_len_3 = freedom_wind_dir.length() + 1;
       char wind_Dir[str_len_3];
       freedom_wind_dir.toCharArray(wind_Dir, str_len_3);

       int str_len_4 = freedom_windchill_f.length() + 1;
       char wind_Chill[str_len_4];
       freedom_windchill_f.toCharArray(wind_Chill, str_len_4);

       int str_len_5 = freedom_pressure_in.length() + 1;
       char press_In[str_len_5];
       freedom_pressure_in.toCharArray(press_In, str_len_5);

       int str_len_6 = freedom_relative_humidity.length() + 1;
       char rel_Hum[str_len_6];
       freedom_relative_humidity.toCharArray(rel_Hum, str_len_5);

       int str_len_7 = freedom_rain_day_in.length() + 1;
       char rain_Day[str_len_7];
       freedom_rain_day_in.toCharArray(rain_Day, str_len_7);

       int str_len_8 = freedom_rain_month_in.length() + 1;
       char rain_Mo[str_len_8];
       freedom_rain_month_in.toCharArray(rain_Mo, str_len_8);

       int str_len_9 = freedom_rain_year_in.length() + 1;
       char rain_Yr[str_len_9];
       freedom_rain_year_in.toCharArray(rain_Yr, str_len_9);

       int str_len_10 = freedom_pressure_tendency_string.length() + 1;
       char press_Str[str_len_10];
       freedom_pressure_tendency_string.toCharArray(press_Str, str_len_10);

       int str_len_11 = freedom_sunrise.length() + 1;
       char sun_Rise[str_len_11];
       freedom_sunrise.toCharArray(sun_Rise, str_len_11);

       int str_len_12 = freedom_sunset.length() + 1;
       char sun_Set[str_len_12];
       freedom_sunset.toCharArray(sun_Set, str_len_12);

       String percent_symbol = "%";
       int str_len_14 = percent_symbol.length() + 1;
       char pct_Sym[str_len_14];
       percent_symbol.toCharArray(pct_Sym, str_len_14);
       //Serial.print("str_len_14 = ");
       //Serial.println(str_len_14);

       String degree_symbol = "⁰";
       int str_len_15 = degree_symbol.length() + 1;
       char deg_Sym[str_len_15];
       degree_symbol.toCharArray(deg_Sym, str_len_15);
       //Serial.print("str_len_15 = ");
       //Serial.println(str_len_15);

       int str_len_16 = freedom_rain_storm_in.length() + 1;
       char rain_Storm[str_len_16];
       freedom_rain_storm_in.toCharArray(rain_Storm, str_len_16);

Now, we finally write the data to the screen.

 //========== START THE DISPLAY LIST ======================================================================================================================
         // Set font 16 to 31 size
      FWo=EVE_Cmd_Dat_2(FWo,
                          EVE_ENC_CMD_ROMFONT,16,31); //32 to 34

           // Set font 20 to 34 size
       FWo=EVE_Cmd_Dat_2(FWo,
                          EVE_ENC_CMD_ROMFONT,20,34); //32 to 34                 

       FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0xFF,0xFF,0x00));//yellow font

       FWo=EVE_PrintF(FWo,
                      95, //X horizontal
                      160, //Y vertical
                      30,        //Font
                      EVE_OPT_CENTER, //Options
                      //"%s",//sunrise
                      sun_Rise);

       FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0xFF,0x99,0x00));//orange font

       FWo=EVE_PrintF(FWo,
                      937, //X
                      160, //Y
                      30,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s",//sunset
                      sun_Set);

       FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0x00,0xFF,0x00));//green

       FWo=EVE_PrintF(FWo,
                      310, //X horizontal
                      158, //Y vertical
                      16,        //Font
                      EVE_OPT_RIGHTX, //Options
                     "%s",//temperature
                      temp_F);
/*
       FWo=EVE_PrintF(FWo,
                      370, //X horizontal
                      165, //Y vertical
                      19,        //Font
                      EVE_OPT_CENTER, //Options
                     "\277");//temperature
                     "\F8*");//temperature
*/                     
        FWo=EVE_PrintF(FWo,
                      517, //X
                      158, //Y
                      16,        //Font
                      EVE_OPT_RIGHTX, //Options
                      "%s",//windchill
                      wind_Chill);

        FWo=EVE_PrintF(FWo,
                      755, //X horizontal
                      158, //Y vertical
                      16,        //Font
                      EVE_OPT_RIGHTX, //Options
                      "%s",//dewpoint
                      dew_F);

        FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0x88,0x00,0xFF));//violet

        FWo=EVE_PrintF(FWo,
                      512, //X
                      265, //Y
                      16,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s @ %s MPH",//wind dir
                      wind_Dir,
                      wind_Mph);

        FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0x10,0x00,0xFF));//blue

        FWo=EVE_PrintF(FWo,
                      200, //X horizontal
                      380, //Y vertical
                      16,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s \"Hg",//pressure in.
                      press_In);

        FWo=EVE_PrintF(FWo,
                      512, //X
                      365, //Y
                      25,        //Font
                      EVE_OPT_CENTER, //Options
                      //"%s",//pressure tendency
                      press_Str);

        FWo=EVE_PrintF(FWo,
                      850, //X horizontal
                      380, //Y vertical
                      16,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s %s",//humidity
                      rel_Hum,
                      pct_Sym);

        FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0x42,0xF5,0xCE));//aqua        

        FWo=EVE_PrintF(FWo,
                      225, //X horizontal
                      480, //Y vertical
                      16,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s \"",//rain day
                      rain_Day);

        FWo=EVE_PrintF(FWo,
                      545, //X
                      480, //Y
                      16,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s \"",//rain month
                      rain_Mo);

        FWo=EVE_PrintF(FWo,
                      845, //X horizontal
                      480, //Y vertical
                      16,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s \"",//rain year
                      rain_Yr);                      

       FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0xff,0x00,0x00));//red

       FWo=EVE_PrintF(FWo,
                      512, //X horizontal
                      552, //Y vertical
                      24,        //Font
                      EVE_OPT_CENTER, //Options
                      //"%s",//observation time
                      ob_Time);

     if (ob_min == mem_ob_min) {

       upd_Count ++; //keep track of update fails
       Serial.print("Update failed count = ");
       Serial.println(upd_Count);

        FWo=EVE_PrintF(FWo,
                      10, //X horizontal
                      590, //Y vertical
                      21,        //Font
                      EVE_OPT_CENTER, //Options
                      "%d ",//update not new data
                      ob_min);      
           }
       else
           {
              upd_Count = 0;
              FWo=EVE_PrintF(FWo,
                      10, //X horizontal
                      590, //Y vertical
                      21,        //Font
                      EVE_OPT_CENTER, //Options
                      "OK");//update OK                    
             }

       //cf_Count = 5; //testing     

       if ((cf_Count >= 5)||(upd_Count >= 5)) {
         FWo=EVE_PrintF(FWo,
                      512, //X horizontal
                      265, //Y vertical
                      20,        //Font
                      EVE_OPT_CENTER, //Options
                      "CHECK PC !");//check PC
       }

      FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_COLOR_RGB(0xff,0xff0,0xff));//white

       if (freedom_rain_storm_flt != 0.00) {
         FWo=EVE_PrintF(FWo,
                      300, //X horizontal
                      430, //Y vertical
                      25,        //Font
                      EVE_OPT_CENTER, //Options
                      "Rain Storm");//check PC

           FWo=EVE_PrintF(FWo,
                      680, //X
                      430, //Y
                      25,        //Font
                      EVE_OPT_CENTER, //Options
                      "%s \"",//rain storm
                      rain_Storm);        
              }

        mem_ob_min = ob_min; //store new observation minute
        Serial.print("mem_ob_min_2 = ");
        Serial.println(mem_ob_min);

Display Options

check_pc.jpg
storm.jpg
update.jpg

I have a couple of display options that are specific to my setup.

The first is related to my HP desktop computer. Since it is in the garage, I don't look at it very often. Windows 10 will update itself, and then needs to be logged onto. If the display doesn't respond in 5 requests, "Check PC!" will appear on the screen.

void makeHTTPRequest() {
 Serial.println("Requesting");
 // Opening connection to server (Use 80 as port if HTTP)
 if (!client.connect(TEST_HOST, 80))
 {
   Serial.println(F("Connection failed"));
   cf_Count ++; //keep track of connection fails
   WiFi.end();
   delay(10);
   WiFi.begin(ssid, pass);
   return;
 }
 else
 {
   cf_Count = 0; //connection good - reset count
 }
 Serial.print("Connection Fail Count = ");
 Serial.println(cf_Count);
//---------------------------------
 if (ob_min == mem_ob_min) {      
upd_Count ++; //keep track of update fails
       
Serial.print("Update failed count = ");
        Serial.println(upd_Count);
        
         FWo=EVE_PrintF(FWo,
                       10,  //X horizontal
                       590,  //Y vertical
                       21,         //Font
                       EVE_OPT_CENTER, //Options
                       "%d ",//update not new data
                       ob_min);       
            }
        else
            {
               upd_Count = 0;
               FWo=EVE_PrintF(FWo,
                       10,  //X horizontal
                       590,  //Y vertical
                       21,         //Font
                       EVE_OPT_CENTER, //Options
                       "OK");//update OK                     
              }

        //cf_Count = 5; //testing      

        if ((cf_Count >= 5)||(upd_Count >= 5)) {
          FWo=EVE_PrintF(FWo,
                       512,  //X horizontal
                       265,  //Y vertical
                       20,         //Font
                       EVE_OPT_CENTER, //Options
                       "CHECK PC !");//check PC
        }
mem_ob_min = ob_min; //store new observation minute
         Serial.print("mem_ob_min_2 = ");
         Serial.println(mem_ob_min);

The above code also lets me know if I have a connection problem with my router.

I extract the minutes from the observation time, and see if the time changes for the next request.

Serial.println(freedom_observation_time);
/////////////////////////////////// extract minutes from observation time
int delimiter, delimiter_1 ;
delimiter = freedom_observation_time.indexOf(":");
delimiter_1 = freedom_observation_time.indexOf(" ", delimiter + 1);
String freedom_ob_min = freedom_observation_time.substring(delimiter + 1, delimiter_1); //substring(beginning, end)
ob_min = freedom_ob_min.toInt();
Serial.print("ob_min = ");
Serial.println(ob_min);
Serial.print("mem_ob_min = ");
Serial.println(mem_ob_min);

If the update time changes, the display shows"OK" in the lower left corner. If not, the minutes of the last update are displayed.

The last option is more of a feature than an option.

If the rain storm value is greater than .01", the value is displayed in white on the screen. It goes away when the value is 0.00".

String freedom_rain_storm_in_x = davis_current_observation["rain_storm_in"];
freedom_rain_storm_in = freedom_rain_storm_in_x;
//freedom_rain_storm_in = ("1.23");// teesting
freedom_rain_storm_flt = freedom_rain_storm_in.toFloat();
Serial.print("freedom_rain_storm_in = ");
Serial.print(freedom_rain_storm_in);
Serial.print("  ");
Serial.println(freedom_rain_storm_flt);
//---------------------
 if (freedom_rain_storm_flt != 0.00) {
          FWo=EVE_PrintF(FWo,
                       300,  //X horizontal
                       430,  //Y vertical
                       25,         //Font
                       EVE_OPT_CENTER, //Options
                       "Rain Storm");//check PC
                       
            FWo=EVE_PrintF(FWo,
                       680,  //X
                       430,  //Y
                       25,         //Font
                       EVE_OPT_CENTER, //Options
                       "%s \"",//rain storm
                       rain_Storm);         
               }

I almost forgot to mention the photoresistor.

It measures the amount of ambient light and adjusts the intensity of the display backlight accordingly.

 PR_Value = analogRead(pResistor);
 Serial.print("PR_Value = ");
 Serial.println(int(PR_Value));
 BL_Val = map(analogRead(pResistor), 0, 1023, 1, 128);
 Serial.print("BL_Value = ");
 Serial.println(int(BL_Val));
 if(BL_Val < 1)
   {
   BL_Val=1;
   }
 if(128<BL_Val)
   {
   BL_Val=128;
   }
   //Write the actual value
   EVE_REG_Write_8(EVE_REG_PWM_DUTY,BL_Val);


Construction

assy_2.jpg
finished _frame.jpg
finished_mount.jpg

The first step in the construction is to obtain a suitable frame. I picked up a 5'x7" picture frame at a local craft store.

I then proceeded to design and 3D print a holder for the screen and the proto board.

I mounted all of the components on the board as pictured. Hindsight being 20/20, I would have swapped the positions of the USB socket and the uSD card.

Then I screwed the holder to the frame utilizing my 3d printed mask. The frame opening is a little bit larger than the display area.

I plugged the USB cable into a 5vdc wall transformer, and hung the frame assembly on the wall. Finished ! !

Conclusion

LEKtronics_1024x600.bmp

I hope you enjoyed reading this Instructable. I enjoyed making it. If you have any questions or concerns, you can email me at larry@lektronics.com. You are also invited to visit my website where I have other projects.