Skip to content

Commit a5339fe

Browse files
authored
Create adafruit_feather_quote.ino
Initial commit of code
1 parent 95aa14a commit a5339fe

1 file changed

Lines changed: 390 additions & 0 deletions

File tree

Lines changed: 390 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,390 @@
1+
/***************************************************
2+
* Quote Display for Adafruit ePaper FeatherWings
3+
* For use with Adafruit tricolor and monochrome ePaper FeatherWings
4+
*
5+
* Adafruit invests time and resources providing this open source code.
6+
* Please support Adafruit and open source hardware by purchasing
7+
* products from Adafruit!
8+
*
9+
* Written by Dan Cogliano for Adafruit Industries
10+
* Copyright (c) 2019 Adafruit Industries
11+
*
12+
* Notes:
13+
* Update the secrets.h file with your WiFi details
14+
* Uncomment the ePaper display type you are using below.
15+
* Change the SLEEP setting to define the time between quotes
16+
*/
17+
18+
#include <Adafruit_GFX.h> // Core graphics library
19+
#include <ESPHTTPClient.h>
20+
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
21+
#include <Adafruit_EPD.h>
22+
#include "secrets.h"
23+
24+
// define the # of seconds to sleep before waking up and getting a new quote
25+
#define SLEEP 3600 // 1 hour in seconds
26+
27+
// WiFi timeout in seconds
28+
#define WIFI_TIMEOUT 30
29+
30+
// What fonts do you want to use?
31+
#include <Fonts/FreeSans9pt7b.h>
32+
//#include <Fonts/FreeSans12pt7b.h> // a font option for larger screens
33+
34+
// qfont is the font for the quote
35+
const GFXfont *qfont = &FreeSans9pt7b;
36+
//const GFXfont *qfont = &FreeSans12pt7b;
37+
38+
// afont is the font for the author's name
39+
const GFXfont *afont = &FreeSans9pt7b;
40+
//const GFXfont *afont = &FreeSans12pt7b;
41+
42+
// ofont is the font for the origin of the feed
43+
const GFXfont *ofont = NULL;
44+
45+
// font to use if qfont is too large
46+
// NULL means use the system font (which is small)
47+
const GFXfont *smallfont = NULL;
48+
49+
50+
// ESP32 settings
51+
#define SD_CS 14
52+
#define SRAM_CS 32
53+
#define EPD_CS 15
54+
#define EPD_DC 33
55+
#define LEDPIN 13
56+
#define LEDPINON HIGH
57+
#define LEDPINOFF LOW
58+
59+
#define EPD_RESET -1 // can set to -1 and share with microcontroller Reset!
60+
#define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay)
61+
62+
/* Uncomment the following line if you are using 2.13" tricolor EPD */
63+
//Adafruit_IL0373 epd(212, 104 ,EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY);
64+
/* Uncomment the following line if you are using 2.13" monochrome 250*122 EPD */
65+
Adafruit_SSD1675 epd(250, 122, EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY);
66+
67+
// get string length in pixels
68+
// set text font prior to calling this
69+
int getStringLength(const char *str, int strlength = 0)
70+
{
71+
char buff[1024];
72+
int16_t x, y;
73+
uint16_t w, h;
74+
if(strlength == 0)
75+
{
76+
strcpy(buff, str);
77+
}
78+
else
79+
{
80+
strncpy(buff, str, strlength);
81+
buff[strlength] = '\0';
82+
}
83+
epd.getTextBounds(buff, 0, 0, &x, &y, &w, &h);
84+
return(w);
85+
}
86+
87+
// word wrap routine
88+
// first time send string to wrap
89+
// 2nd and additional times: use empty string
90+
// returns substring of wrapped text.
91+
char *wrapWord(const char *str, int linesize)
92+
{
93+
static char buff[255];
94+
int linestart = 0;
95+
static int lineend = 0;
96+
static int bufflen = 0;
97+
if(strlen(str) == 0)
98+
{
99+
// additional line from original string
100+
linestart = lineend + 1;
101+
lineend = bufflen;
102+
Serial.println("existing string to wrap, starting at position " + String(linestart) + ": " + String(&buff[linestart]));
103+
}
104+
else
105+
{
106+
Serial.println("new string to wrap: " + String(str));
107+
memset(buff,0,sizeof(buff));
108+
// new string to wrap
109+
linestart = 0;
110+
strcpy(buff,str);
111+
lineend = strlen(buff);
112+
bufflen = strlen(buff);
113+
}
114+
uint16_t w;
115+
int lastwordpos = linestart;
116+
int wordpos = linestart + 1;
117+
while(true)
118+
{
119+
while(buff[wordpos] == ' ' && wordpos < bufflen)
120+
wordpos++;
121+
while(buff[wordpos] != ' ' && wordpos < bufflen)
122+
wordpos++;
123+
if(wordpos < bufflen)
124+
buff[wordpos] = '\0';
125+
w = getStringLength(&buff[linestart]);
126+
if(wordpos < bufflen)
127+
buff[wordpos] = ' ';
128+
if(w > linesize)
129+
{
130+
buff[lastwordpos] = '\0';
131+
lineend = lastwordpos;
132+
return &buff[linestart];
133+
}
134+
else if(wordpos >= bufflen)
135+
{
136+
// first word too long or end of string, send it anyway
137+
buff[wordpos] = '\0';
138+
lineend = wordpos;
139+
return &buff[linestart];
140+
}
141+
lastwordpos = wordpos;
142+
wordpos++;
143+
}
144+
}
145+
146+
// return # of lines created from word wrap
147+
int getLineCount(const char *str, int scrwidth)
148+
{
149+
int linecount = 0;
150+
String line = wrapWord(str,scrwidth);
151+
152+
while(line.length() > 0)
153+
{
154+
linecount++;
155+
line = wrapWord("",scrwidth);
156+
}
157+
return linecount;
158+
}
159+
160+
int getLineHeight(const GFXfont *font = NULL)
161+
{
162+
int height;
163+
if(font == NULL)
164+
{
165+
height = 12;
166+
}
167+
else
168+
{
169+
height = (uint8_t)pgm_read_byte(&font->yAdvance);
170+
}
171+
return height;
172+
}
173+
174+
// Retrieve page response from given URL
175+
String getURLResponse(String url)
176+
{
177+
HTTPClient http;
178+
String jsonstring = "";
179+
Serial.println("getting url: " + url);
180+
if(http.begin(url))
181+
{
182+
Serial.print("[HTTP] GET...\n");
183+
// start connection and send HTTP header
184+
int httpCode = http.GET();
185+
186+
// httpCode will be negative on error
187+
if (httpCode > 0) {
188+
// HTTP header has been sent and Server response header has been handled
189+
Serial.println("[HTTP] GET... code: " + String(httpCode));
190+
191+
// file found at server
192+
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
193+
jsonstring = http.getString();
194+
// use this string for testing very long quotes
195+
//jsonstring = "[{\"text\":\"Don't worry about what anybody else is going to do… The best way to predict the future is to invent it. Really smart people with reasonable funding can do just about anything that doesn't violate too many of Newton's Laws!\",\"author\":\"Alan Kay\"}]";
196+
Serial.println(jsonstring);
197+
}
198+
} else {
199+
Serial.println("[HTTP] GET... failed, error: " + http.errorToString(httpCode));
200+
}
201+
http.end();
202+
}
203+
else {
204+
Serial.println("[HTTP] Unable to connect");
205+
}
206+
return jsonstring;
207+
}
208+
209+
void getQuote(String &quote, String &author)
210+
{
211+
StaticJsonBuffer<1024> jsonBuffer;
212+
String url = "https://www.adafruit.com/api/quotes.php";
213+
String jsonquote = getURLResponse(url);
214+
if(jsonquote.length() > 0)
215+
{
216+
// remove start and end brackets, jsonBuffer is confused by them
217+
jsonquote = jsonquote.substring(1,jsonquote.length()-1);
218+
Serial.println("using: " + jsonquote);
219+
JsonObject &jsonroot = jsonBuffer.parseObject(jsonquote);
220+
if (!jsonroot.success())
221+
{
222+
Serial.println("json parseObject() failed");
223+
Serial.println("bad json: " + jsonquote);
224+
quote = "json parseObject() failed";
225+
}
226+
else
227+
{
228+
String tquote = jsonroot["text"];
229+
String tauthor = jsonroot["author"];
230+
quote = tquote;
231+
author = tauthor;
232+
}
233+
}
234+
else
235+
{
236+
quote = "Error retrieving URL";
237+
}
238+
}
239+
240+
void printQuote(String &quote)
241+
{
242+
int x = 0;
243+
int y = 0;
244+
bool bsmallfont = false;
245+
epd.setTextColor(EPD_BLACK);
246+
epd.setFont(qfont);
247+
epd.setTextSize(1);
248+
249+
int scrwidth = epd.width() - 8;
250+
Serial.println("Screen width is " + String(scrwidth));
251+
Serial.println("Screen height is " + String(epd.height()));
252+
int linecount = getLineCount(quote.c_str(),scrwidth);
253+
int lineheightquote = getLineHeight(qfont);
254+
int lineheightauthor = getLineHeight(afont);
255+
int lineheightother = getLineHeight(ofont);
256+
int maxlines = (epd.height() - (lineheightauthor + lineheightother)) / lineheightquote;
257+
Serial.println("maxlines is " + String(maxlines));
258+
Serial.println("line height is " +String(lineheightquote));
259+
Serial.println("linecount is " +String(linecount));
260+
int topmargin = 0;
261+
if(linecount > maxlines)
262+
{
263+
// too long for default font size
264+
// next attempt, reduce lineheight to .8 size
265+
lineheightquote = .8 * lineheightquote;
266+
maxlines = (epd.height() - (lineheightauthor + lineheightother)) / lineheightquote;
267+
if(linecount > maxlines)
268+
{
269+
// next attempt, use small font
270+
epd.setFont(smallfont);
271+
bsmallfont = true;
272+
epd.setTextSize(1);
273+
lineheightquote = getLineHeight(smallfont);
274+
maxlines = (epd.height() - (lineheightauthor + lineheightother)) / lineheightquote;
275+
linecount = getLineCount(quote.c_str(),scrwidth);
276+
if(linecount > maxlines)
277+
{
278+
// final attempt, last resort is to reduce the lineheight to make it fit
279+
lineheightquote = (epd.height() - (lineheightauthor + lineheightother)) / linecount;
280+
}
281+
}
282+
Serial.println("maxlines has changed to " + String(maxlines));
283+
Serial.println("line height has changed to " +String(lineheightquote));
284+
Serial.println("linecount has changed to " +String(linecount));
285+
}
286+
if(linecount <= maxlines)
287+
{
288+
289+
topmargin = (epd.height() - (lineheightauthor + lineheightother) - linecount*lineheightquote)/2;
290+
if(!bsmallfont)
291+
topmargin+=lineheightquote-4;
292+
//Serial.println("topmargin = " + String(topmargin));
293+
}
294+
String line = wrapWord(quote.c_str(),scrwidth);
295+
296+
int counter = 0;
297+
epd.setTextColor(EPD_BLACK);
298+
while(line.length() > 0)
299+
{
300+
counter++;
301+
Serial.println("printing line " + String(counter) + ": '" + line + String("'"));
302+
epd.setCursor(x +4, y + topmargin);
303+
epd.print(line);
304+
y += lineheightquote;
305+
line = wrapWord("",scrwidth);
306+
}
307+
}
308+
309+
void printAuthor(String author)
310+
{
311+
epd.setTextColor(EPD_BLACK);
312+
epd.setFont(afont);
313+
int lineheightauthor = getLineHeight(afont);
314+
int lineheightother = getLineHeight(ofont);
315+
int x = getStringLength(author.c_str());
316+
// draw line above author
317+
epd.drawLine(epd.width() - x - 10, epd.height() - (lineheightauthor + lineheightother) + 2, epd.width(), epd.height() - (lineheightauthor + lineheightother) + 2, EPD_RED);
318+
epd.drawLine(epd.width() - x - 10, epd.height() - (lineheightauthor + lineheightother) + 2, epd.width() - x - 10,epd.height() - lineheightother - lineheightauthor/3, EPD_RED);
319+
epd.drawLine(0, epd.height() - lineheightother - lineheightauthor/3, epd.width() - x - 10,epd.height() - lineheightother - lineheightauthor/3, EPD_RED);
320+
// draw author text
321+
int cursorx = epd.width() - x - 4;
322+
int cursory = epd.height() - lineheightother - 2;
323+
if(afont == NULL)
324+
{
325+
cursory = epd.height() - lineheightother - lineheightauthor - 2 ;
326+
}
327+
epd.setCursor(cursorx, cursory);
328+
epd.print(author);
329+
}
330+
331+
void printOther(String other)
332+
{
333+
epd.setFont(ofont);
334+
int lineheightother = getLineHeight(ofont);
335+
int ypos = epd.height() - 2;
336+
if (ofont == NULL)
337+
ypos = epd.height() - (lineheightother - 2);
338+
epd.setTextColor(EPD_BLACK);
339+
epd.setCursor(4,ypos);
340+
epd.print(other);
341+
}
342+
343+
void setup() {
344+
Serial.begin(115200);
345+
//while(!Serial);
346+
347+
pinMode(LEDPIN, OUTPUT);
348+
digitalWrite(LEDPIN, LEDPINON);
349+
350+
epd.begin();
351+
Serial.println("ePaper display initialized");
352+
epd.clearBuffer();
353+
epd.setTextWrap(false);
354+
355+
Serial.print("Connecting to WiFi ");
356+
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
357+
int counter = 0;
358+
while (WiFi.status() != WL_CONNECTED && counter < WIFI_TIMEOUT) {
359+
delay(1000);
360+
Serial.print(".");
361+
counter++;
362+
}
363+
364+
String quote, author;
365+
if(WiFi.status() == WL_CONNECTED)
366+
{
367+
Serial.println("connected");
368+
getQuote(quote, author);
369+
}
370+
else
371+
{
372+
quote = "WiFi connection timed out, try again later";
373+
Serial.println(quote);
374+
}
375+
376+
printQuote(String("\"") + quote + String("\""));
377+
printAuthor(author);
378+
printOther("adafruit.com/quotes");
379+
380+
epd.display();
381+
Serial.println("done, going to sleep...");
382+
// power down ePaper display
383+
epd.powerDown();
384+
// put microcontroller to sleep, wake up after specified time
385+
ESP.deepSleep(SLEEP * 1e6);
386+
}
387+
388+
void loop() {
389+
// should never get here, setup() puts the CPU to sleep
390+
}

0 commit comments

Comments
 (0)