Skip to content

Instantly share code, notes, and snippets.

@bobmcwhirter
Created November 18, 2019 19:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bobmcwhirter/236dd816a743180b13a1e6cd2365e4c9 to your computer and use it in GitHub Desktop.
Save bobmcwhirter/236dd816a743180b13a1e6cd2365e4c9 to your computer and use it in GitHub Desktop.
/*
* Project mw
* Description:
* Author:
* Date:
*/
#include "application.h"
#include <GxEPD.h>
#include <GxGDEW0583T7/GxGDEW0583T7.h> // 5.83" b/w
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>
//#include "fonts/Roboto-Bold8pt7b.h"
//#include "fonts/Roboto-Black12pt7b.h"
//#include "fonts/Roboto-Black24pt7b.h"
//#include "fonts/Roboto-Black48pt7b.h"
//#define FONT_S &Roboto_Bold8pt7b
//#define FONT_M &Roboto_Black12pt7b
//#define FONT_L &Roboto_Black24pt7b
//#define FONT_XL &Roboto_Black48pt7b
#include "fonts/Courier8pt7b.h"
#include "fonts/Courier-Bold12pt7b.h"
#include "fonts/Courier-Bold24pt7b.h"
#include "fonts/Courier-Bold48pt7b.h"
#include "fonts/FFF_Tusj36pt7b.h"
#define FONT_S &Courier8pt7b
#define FONT_M &Courier_Bold12pt7b
#define FONT_L &Courier_Bold24pt7b
#define FONT_XL &Courier_Bold48pt7b
#define FONT_SPLASH &FFF_Tusj36pt7b
#include "icons/cloud-sun.h"
#include "icons/cloud.h"
#include "icons/fog.h"
#include "icons/rain.h"
#include "icons/snow.h"
#include "icons/sun.h"
#include "icons/wind.h"
GxIO_Class io(SPI, A3, D9, A1); // arbitrary selection of 17, 16
//GxEPD_Class display(io, A1, A0); // arbitrary selection of (16), 4
GxEPD_Class display(io, A1, D7); // arbitrary selection of (16), 4
struct forecast {
char day[5] = "-";
char icon[20];
char high[4];
char low[4];
char precip[4];
};
struct Dashboard {
char temperature[20] = "0.0";
char temperature_trend[10] = "stable";
char temperature_min[20] = "0.0";
char temperature_max[20] = "0.0";
char humidity[10] = "0";
char pressure[10] = "0";
char rain_hour[10] = "0.0";
char rain_day[10] = "0.0";
char wind_speed[5] = "0";
char wind_gust[5] = "0";
char wind_max[5] = "0";
char wind_direction[5] = "";
char wind_angle[5] = "0";
forecast forecast0;
forecast forecast1;
forecast forecast2;
forecast forecast3;
forecast forecast4;
char words[400] = "";
int changed = 0;
time_t lastUpdateReceived = 0;
};
Dashboard current = {};
//SYSTEM_THREAD(ENABLED);
// setup() runs once, when the device is first turned on.
void setup() {
Serial.begin(115200);
delay(3000);
display.init(115200); // enable diagnostic output on Serial
Serial.printf( "%s %s %s %s\n", current.temperature, current.rain_day, current.wind_speed, current.wind_direction );
Particle.subscribe("mrweathery.", handleMessage, MY_DEVICES);
}
int first = 1;
// loop() runs over and over again, as quickly as it can execute.
void loop() {
//delay(5000);
if ( first ) {
display.init();
showWhiteout();
//delay(2000);
showSplash();
first = 0;
}
delay(3000);
showDashboard();
delay(4000);
}
void showWhiteout() {
display.fillScreen(GxEPD_WHITE);
display.update();
}
void showSplash() {
display.fillScreen(GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
display.setFont(FONT_SPLASH);
display.setCursor( 300 - (widthOf( "Mr. Weathery", FONT_SPLASH)/2), 150);
display.print("mr. weathery");
display.setFont(FONT_M);
display.setCursor( 300 - (widthOf( "starting up.", FONT_M)/2), 250);
display.print("starting up.");
display.drawBitmap(IconWind, 170, 300, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
display.drawBitmap(IconSun, 270, 300, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
display.drawBitmap(IconSnow, 370, 300, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
display.update();
}
void showDashboard() {
Serial.printlnf( "start change flag now: %d", current.changed );
if ( ! current.changed ) {
Serial.println( "no change");
return;
} else {
Serial.println( "has change");
if ( ( Time.now() - current.lastUpdateReceived ) < 10 ) {
Serial.println( "has change but delaying");
return;
}
}
display.fillScreen(GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
int startY = 100;
showTemperatureHumidity(&display, 150, startY);
showWind( &display, 450, startY );
showForecastBar( &display, 50, startY+130);
showWords( &display, 300, startY+270);
showRain( &display, 300, startY+280);
display.update();
Serial.printlnf( "end change flag now: %d", current.changed );
current.changed = 0;
}
#define ICON_OFFSET 1
void showForecast(GxEPD_Class *display, forecast *f, int x, int y) {
if ( strcmp(f->icon, "clear" ) == 0 ) {
display->drawBitmap(IconSun, x+20, y+ICON_OFFSET, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
} else if ( strcmp(f->icon, "rain" ) == 0 ) {
display->drawBitmap(IconRain, x+20, y+ICON_OFFSET, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
} else if ( strcmp(f->icon, "snow" ) == 0 ) {
display->drawBitmap(IconSnow, x+20, y+ICON_OFFSET, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
} else if ( strcmp(f->icon, "wind" ) == 0 ) {
display->drawBitmap(IconWind, x+20, y+ICON_OFFSET, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
} else if ( strcmp(f->icon, "fog" ) == 0 ) {
display->drawBitmap(IconFog, x+20, y+ICON_OFFSET, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
} else if ( strcmp(f->icon, "cloudy" ) == 0 ) {
display->drawBitmap(IconCloud, x+20, y+ICON_OFFSET, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
} else if ( strcmp(f->icon, "partly-cloudy" ) == 0 ) {
display->drawBitmap(IconCloudSun, x+20, y+ICON_OFFSET, 60, 60, GxEPD_BLACK, GxEPD::bm_invert);
} else {
Serial.printlnf( "******** unhandled %s", f->icon);
}
display->setCursor(x+50-( widthOf(f->day, FONT_M)/2), y);
display->setFont( FONT_M );
display->print( f->day );
char str[20];
sprintf( str, "%s-%s", f->low, f->high);
display->setCursor(x+50-( widthOf(str, FONT_M )/2), y + 72);
display->setFont( FONT_M );
display->print( str );
sprintf( str, "%s%%", f->precip);
display->setCursor(x+50-( widthOf(f->precip, FONT_M)/2), y + 98);
display->setFont( FONT_M );
display->print(str);
}
void showForecastBar(GxEPD_Class *display, int x, int y) {
showForecast( display, &(current.forecast0), x, y );
showForecast( display, &(current.forecast1), x + 100, y );
showForecast( display, &(current.forecast2), x + 200, y );
showForecast( display, &(current.forecast3), x + 300, y );
showForecast( display, &(current.forecast4), x + 400, y );
}
void showWords(GxEPD_Class *display, int x, int y) {
//display->setCursor(x-( widthOf(current.words, FONT_M)/2), y);
//display->setFont( FONT_M );
//display->print(current.words);
int fastWidth = widthOf(current.words, FONT_M);
if ( fastWidth < 600 ) {
display->setCursor( 300 - (fastWidth/2), y + 20);
display->print( current.words );
return;
}
String allWords = String(current.words);
String word;
int curX = 20;
int curY = y;
int cur = 0;
int spaceLoc = 0;
while( ( spaceLoc = allWords.indexOf( ' ', cur) ) >= 0 ) {
word = allWords.substring(cur, spaceLoc);
cur = spaceLoc + 1;
int width = widthOf(word.c_str(), FONT_M);
Serial.printlnf( "word %s %d at %d", word.c_str(), width, curX);
if ( curX + width > 580 ) {
curX = 20;
curY = curY + 20;
}
display->setCursor(curX, curY);
display->print(word.c_str());
curX = curX + width + 15;
}
display->setCursor(curX, curY);
display->print(allWords.substring(cur).c_str());
}
void showRain(GxEPD_Class *display, int x, int y) {
char str[20];
sprintf(str, "%s\"", current.rain_day);
display->setCursor(x - ( widthOf( str, FONT_L ) / 2 ), y+50);
display->setFont(FONT_L);
display->print(str);
}
void showTemperatureHumidity(GxEPD_Class *display, int x, int y) {
display->setFont(FONT_XL);
char str[40];
sprintf(str, "%s", current.temperature);
display->setCursor(x - ( widthOf( str, FONT_XL ) / 2 ), y);
display->print(str);
drawTemperatureTrend(display, x + ( widthOf( str, FONT_XL ) / 2 ) + 20, y );
display->setFont(FONT_M);
sprintf(str, "%s - %s", current.temperature_min, current.temperature_max);
display->setCursor(x - ( widthOf( str, FONT_M ) / 2 ), y + 35 );
display->print(str);
display->setFont(FONT_M);
sprintf(str, "%s%%/%s\"Hg", current.humidity, current.pressure);
display->setCursor(x - ( widthOf( str, FONT_M ) / 2 ), y + 70);
display->print(str);
}
void drawTemperatureTrend(GxEPD_Class *display, int x, int y) {
if ( strcmp( current.temperature_trend, "up" ) == 0 ) {
showUpTrend(display, x, y - 32 );
} else if ( strcmp( current.temperature_trend, "down" ) == 0 ) {
showDownTrend(display, x, y - 30 );
} else {
showStableTrend(display, x, y - 28);
}
}
void showUpTrend(GxEPD_Class *display, int x, int y) {
display->fillTriangle( x, y, x+40, y, x+20, y-20, GxEPD_BLACK);
}
void showDownTrend(GxEPD_Class *display, int x, int y) {
display->fillTriangle( x, y, x+40, y, x+20, y+20, GxEPD_BLACK);
display->fillTriangle( x, y, x+40, y, x+20, y+20, GxEPD_BLACK);
}
void showStableTrend(GxEPD_Class *display, int x, int y) {
display->drawLine( x, y, x+40, y, GxEPD_BLACK);
display->drawLine( x, y+1, x+40, y+1, GxEPD_BLACK);
display->drawLine( x, y-1, x+40, y-1, GxEPD_BLACK);
}
#define COMPASS_SIZE 80
void showWind(GxEPD_Class *display, int x, int y) {
Serial.printf("wind angle str [%s]\n", current.wind_angle);
int angle = atoi( current.wind_angle );
display->fillCircle(x, y, COMPASS_SIZE, GxEPD_BLACK );
display->fillCircle(x, y, COMPASS_SIZE - 5, GxEPD_WHITE );
Serial.printf( "wind angle: %d\n", angle );
int windSpeed = atoi( current.wind_speed );
int gustSpeed = atoi( current.wind_gust );
if ( windSpeed > 0 || gustSpeed >= 2 ) {
drawWindDireciontIndicator( display, x, y, COMPASS_SIZE - 2, angle, GxEPD_BLACK );
}
int textOffsetY = y - 10;
char str[30];
sprintf( str, "%s-%s", current.wind_speed, current.wind_gust);
display->setCursor( x - (widthOf( str, FONT_L) / 2), textOffsetY );
display->setFont(FONT_L);
display->print( str );
sprintf( str, "max: %s", current.wind_max);
display->setCursor( x - ( widthOf( str, FONT_M)/2), textOffsetY + 32 );
display->setFont(FONT_M);
display->print(str);
display->setCursor( x - ( widthOf( "mph", FONT_M)/2), textOffsetY + 64 );
display->setFont(FONT_M);
display->print("mph");
}
int widthOf(const char *str, const GFXfont *font) {
int w = 0;
int i = 0;
while ( str[i] != 0 ) {
int c = str[i] - ' ';
if ( str[i + 1] != 0 ) {
w += font->glyph[c].xAdvance;
} else {
w += font->glyph[c].width;
}
++i;
}
return w;
}
void handleMessage(const char *event, const char *data) {
Serial.printf("handle message %s = %s\n", event, data);
if ( strcmp( event, "mrweathery.temperature" ) == 0 ) {
setData( current.temperature, data );
} else if ( strcmp( event, "mrweathery.temperature.trend" ) == 0 ) {
setData( current.temperature_trend, data );
} else if ( strcmp( event, "mrweathery.temperature.max" ) == 0 ) {
setData( current.temperature_max, data );
} else if ( strcmp( event, "mrweathery.temperature.min" ) == 0 ) {
setData( current.temperature_min, data );
} else if ( strcmp( event, "mrweathery.humidity" ) == 0 ) {
setData( current.humidity, data );
} else if ( strcmp( event, "mrweathery.pressure" ) == 0 ) {
setData( current.pressure, data );
} else if ( strcmp( event, "mrweathery.wind.strength" ) == 0 ) {
setData( current.wind_speed, data );
} else if ( strcmp( event, "mrweathery.wind.strength.max" ) == 0 ) {
setData( current.wind_max, data );
} else if ( strcmp( event, "mrweathery.wind.angle" ) == 0 ) {
setData( current.wind_angle, data );
} else if ( strcmp( event, "mrweathery.gust.strength" ) == 0 ) {
setData( current.wind_gust, data );
} else if ( strcmp( event, "mrweathery.rain.hour") == 0 ) {
setData( current.rain_hour, data );
} else if ( strcmp( event, "mrweathery.rain.day" ) == 0 ) {
setData( current.rain_day, data );
} else if ( strcmp( event, "mrweathery.words" ) == 0 ) {
setData( current.words, data );
} else {
String eventStr = String(event);
if ( eventStr.startsWith("mrweathery.forecast")) {
handleForecast(eventStr, String(data));
}
}
}
void processForecast(forecast *f, String data) {
//strcpy(f->day, data.substring(0,3).c_str());
setData(f->day, String( data.substring(0,3).toLowerCase() + "." ).c_str());
//String daystr = String( data.substring(0,3).c_str() );
//daystr.concat(".");
//daystr.toLowerCase();
//char daystr[5];
//strcpy(daystr, data.substring(0,3).c_str());
//daystr[3] = '.';
//setData( f->day, daystr);
int colon1 = data.indexOf(':');
int colon2 = data.indexOf(':', colon1+1);
//strcpy(f->icon, data.substring(colon1+1, colon2).c_str());
setData(f->icon, data.substring(colon1+1, colon2).c_str());
colon1 = colon2;
colon2 = data.indexOf(':', colon1+1);
setData(f->high, data.substring(colon1+1, colon2).c_str());
colon1 = colon2;
colon2 = data.indexOf(':', colon1+1);
setData(f->low, data.substring(colon1+1, colon2).c_str());
colon1 = colon2;
setData(f->precip, data.substring(colon1+1).c_str());
Serial.printlnf( "forecast result: %s // %s // %s // %s // %s", f->day, f->icon, f->high, f->low, f->precip);
}
void handleForecast(String event, String data) {
Serial.printlnf("%s == %s", event.c_str(), data.c_str());
if ( event.endsWith(".0") ) {
processForecast( &(current.forecast0), data );
} else if ( event.endsWith( ".1" ) ) {
processForecast( &(current.forecast1), data );
} else if ( event.endsWith( ".2" ) ) {
processForecast( &(current.forecast2), data );
} else if ( event.endsWith( ".3" ) ) {
processForecast( &(current.forecast3), data );
} else if ( event.endsWith( ".4" ) ) {
processForecast( &(current.forecast4), data );
}
}
void setData(char *dest, const char *data) {
if ( strcmp( dest, data ) != 0 ) {
strcpy(dest, data );
Serial.printf( "marking as changed due to %s\n", data);
current.changed = 1;
current.lastUpdateReceived = Time.now();
}
}
void drawWindDireciontIndicator(GxEPD_Class *display, int cx, int cy, int r, int angle, int color) {
double leftRadians = ( ( angle - 90) - 12 ) * 3.1415 / 180;
double rightRadians = ( ( angle - 90 ) + 12 ) * 3.1415 / 180;
double centerRadians = ( ( angle - 90 ) ) * 3.1415 / 180;
int leftX = cx + ((r+10) * cos(leftRadians));
int leftY = cy + ((r+10) * sin(leftRadians));
int rightX = cx + ((r+10) * cos(rightRadians));
int rightY = cy + ((r+10) * sin(rightRadians));
int centerX = cx + ((r-20) * cos(centerRadians));
int centerY = cy + ((r-20) * sin(centerRadians));
display->fillTriangle( leftX, leftY, rightX, rightY, centerX, centerY, color);
leftRadians = ( ( angle - 90) - 7 ) * 3.1415 / 180;
rightRadians = ( ( angle - 90 ) + 7 ) * 3.1415 / 180;
leftX = cx + ((r+4) * cos(leftRadians));
leftY = cy + ((r+4) * sin(leftRadians));
rightX = cx + ((r+4) * cos(rightRadians));
rightY = cy + ((r+4) * sin(rightRadians));
centerX = cx + ((r-12) * cos(centerRadians));
centerY = cy + ((r-12) * sin(centerRadians));
display->fillTriangle( leftX, leftY, rightX, rightY, centerX, centerY, GxEPD_WHITE);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment