mirror of
https://github.com/0015/ThatProject.git
synced 2026-01-12 09:17:42 +03:00
NodeMCU(ESP8266) | GPS | ILI9488 - Showing Google Map with Device Position on Display🗺️
This commit is contained in:
@@ -0,0 +1,408 @@
|
|||||||
|
#define WIFI_SSID "<YOUR_WIFI_SSID_HERE>"
|
||||||
|
#define PASSWORD "<YOUR_WIFI_PW_HERE>"
|
||||||
|
|
||||||
|
#include "SPI.h"
|
||||||
|
#include <TFT_eSPI.h>
|
||||||
|
#include <TJpg_Decoder.h>
|
||||||
|
#include <CertStoreBearSSL.h>
|
||||||
|
#include <TinyGPS++.h>
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#include <TimeLib.h>
|
||||||
|
#include "List_SPIFFS.h"
|
||||||
|
|
||||||
|
// Include SPIFFS
|
||||||
|
#define FS_NO_GLOBALS
|
||||||
|
#include <FS.h>
|
||||||
|
|
||||||
|
// Include WiFi and http client
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266HTTPClient.h>
|
||||||
|
#else
|
||||||
|
#include "SPIFFS.h" // Required for ESP32 only
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define BUTTONSIZE 50
|
||||||
|
#define PButtonFrame_X 20
|
||||||
|
#define PButtonFrame_Y 240
|
||||||
|
#define NButtonFrame_X 90
|
||||||
|
#define NButtonFrame_Y 240
|
||||||
|
|
||||||
|
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
||||||
|
BearSSL::CertStore certStore;
|
||||||
|
BearSSL::WiFiClientSecure *client;
|
||||||
|
|
||||||
|
const char * host = "maps.googleapis.com";
|
||||||
|
const String defaultPath = "/maps/api/staticmap?center=";
|
||||||
|
const String API_KEY = "<YOUR_API_KEY_HERE>";
|
||||||
|
const char * mapFile = "/map.jpg";
|
||||||
|
|
||||||
|
const long buttonInterval = 500;
|
||||||
|
const int maxZoomLevel = 21;
|
||||||
|
const int minZoomLevel = 1;
|
||||||
|
|
||||||
|
static const int RXPin = 5, TXPin = 16;
|
||||||
|
static const uint32_t GPSBaud = 9600;
|
||||||
|
|
||||||
|
const int offset = -8; // Pacific Standard Time
|
||||||
|
time_t prevDisplay = 0;
|
||||||
|
TinyGPSPlus gps;
|
||||||
|
SoftwareSerial ss(RXPin, TXPin);
|
||||||
|
|
||||||
|
char timeBuffer[16];
|
||||||
|
char dateBuffer[16];
|
||||||
|
char zoomBuffer[10];
|
||||||
|
|
||||||
|
String prevCoarseLatitude, prevCoarseLongitude;
|
||||||
|
String latitude = "";
|
||||||
|
String longitude = "";
|
||||||
|
int zoomLevel = 17;
|
||||||
|
|
||||||
|
long previousMillis = 0;
|
||||||
|
bool buttonAvailable = false;
|
||||||
|
|
||||||
|
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
|
||||||
|
{
|
||||||
|
if ( y >= tft.height() ) return 0;
|
||||||
|
tft.pushImage(x, y, w, h, bitmap);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
ss.begin(GPSBaud);
|
||||||
|
|
||||||
|
Serial.println("\n\n");
|
||||||
|
|
||||||
|
// Initialise SPIFFS
|
||||||
|
if (!SPIFFS.begin()) {
|
||||||
|
Serial.println("SPIFFS initialisation failed!");
|
||||||
|
while (1) yield(); // Stay here twiddling thumbs waiting
|
||||||
|
}
|
||||||
|
Serial.println("\r\nInitialisation done.");
|
||||||
|
|
||||||
|
// Initialise the TFT
|
||||||
|
tft.begin();
|
||||||
|
tft.fillScreen(TFT_BLACK);
|
||||||
|
tft.setRotation(3);
|
||||||
|
uint16_t calData[5] = {277, 3614, 317, 3509, 1};
|
||||||
|
tft.setTouch(calData);
|
||||||
|
initScreen();
|
||||||
|
|
||||||
|
TJpgDec.setJpgScale(1);
|
||||||
|
TJpgDec.setSwapBytes(true);
|
||||||
|
TJpgDec.setCallback(tft_output);
|
||||||
|
|
||||||
|
WiFi.begin(WIFI_SSID, PASSWORD);
|
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
if(!hasCerts){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listSPIFFS();
|
||||||
|
menuScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
buttonEvent();
|
||||||
|
GPSEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasCerts(){
|
||||||
|
setClock(); // Required for X.509 validation
|
||||||
|
|
||||||
|
int numCerts = certStore.initCertStore(SPIFFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
|
||||||
|
Serial.printf("Number of CA certs read: %d\n", numCerts);
|
||||||
|
if (numCerts == 0) {
|
||||||
|
Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the SPIFFS directory before running?\n");
|
||||||
|
return false; // Can't connect to anything w/o certs!
|
||||||
|
}
|
||||||
|
|
||||||
|
client = new BearSSL::WiFiClientSecure();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set time via NTP, as required for x.509 validation
|
||||||
|
void setClock() {
|
||||||
|
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||||
|
|
||||||
|
Serial.print("Waiting for NTP time sync: ");
|
||||||
|
time_t now = time(nullptr);
|
||||||
|
while (now < 8 * 3600 * 2) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
now = time(nullptr);
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
struct tm timeinfo;
|
||||||
|
gmtime_r(&now, &timeinfo);
|
||||||
|
Serial.print("Current time: ");
|
||||||
|
Serial.print(asctime(&timeinfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getStaticMapImage(const char *host, const char *path, String fileName){
|
||||||
|
if(WiFi.status() != WL_CONNECTED){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int contentLength = 0;
|
||||||
|
int httpCode;
|
||||||
|
|
||||||
|
|
||||||
|
// Integrate the cert store with this connection
|
||||||
|
client->setCertStore(&certStore);
|
||||||
|
client->connect(host, 443); //HTTPS
|
||||||
|
|
||||||
|
Serial.printf("Trying: %s:443...", host);
|
||||||
|
|
||||||
|
if(!client->connected()){
|
||||||
|
client->stop();
|
||||||
|
Serial.printf("*** Can't connect. ***\n-------\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("HTTPS Connected!");
|
||||||
|
client->write("GET ");
|
||||||
|
client->write(path);
|
||||||
|
client->write(" HTTP/1.0\r\nHost: ");
|
||||||
|
client->write(host);
|
||||||
|
client->write("\r\nUser-Agent: ESP8266\r\n");
|
||||||
|
client->write("\r\n");
|
||||||
|
|
||||||
|
while(client->connected()){
|
||||||
|
String header = client-> readStringUntil('\n');
|
||||||
|
if(header.startsWith(F("HTTP/1."))){
|
||||||
|
httpCode = header.substring(9, 12).toInt();
|
||||||
|
if(httpCode != 200){
|
||||||
|
client->stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(header.startsWith(F("Content-Length: "))){
|
||||||
|
contentLength = header.substring(15).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(header == F("\r")){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(contentLength > 0)){
|
||||||
|
client->stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::File f = SPIFFS.open(fileName, "w");
|
||||||
|
if(!f){
|
||||||
|
Serial.println(F("FILE OPEN FAILED"));
|
||||||
|
client->stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int remaining = contentLength;
|
||||||
|
int received;
|
||||||
|
uint8_t buff[512] = {0};
|
||||||
|
|
||||||
|
while(client->available() && remaining > 0){
|
||||||
|
received = client->readBytes(buff, ((remaining > sizeof(buff)) ? sizeof(buff) : remaining));
|
||||||
|
f.write(buff, received);
|
||||||
|
|
||||||
|
if(remaining > 0){
|
||||||
|
remaining -= received;
|
||||||
|
}
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
f.close();
|
||||||
|
client->stop();
|
||||||
|
Serial.println("DOWNLOAD END");
|
||||||
|
return (remaining == 0 ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPath(){
|
||||||
|
String newPath = defaultPath;
|
||||||
|
newPath += latitude;
|
||||||
|
newPath += ",";
|
||||||
|
newPath += longitude;
|
||||||
|
newPath += "&zoom=";
|
||||||
|
newPath += String(zoomLevel);
|
||||||
|
newPath += "&size=320x320";
|
||||||
|
newPath += "&maptype=roadmap";
|
||||||
|
newPath += "markers=color:red%7C";
|
||||||
|
newPath += latitude;
|
||||||
|
newPath += ",";
|
||||||
|
newPath += longitude;
|
||||||
|
newPath += "&format=jpg-baseline";
|
||||||
|
newPath += API_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPSEvent()
|
||||||
|
{
|
||||||
|
while (ss.available() > 0){
|
||||||
|
bool isMapNeedUpdate = false;
|
||||||
|
|
||||||
|
if (gps.encode(ss.read())){
|
||||||
|
|
||||||
|
int Year = gps.date.year();
|
||||||
|
byte Month = gps.date.month();
|
||||||
|
byte Day = gps.date.day();
|
||||||
|
byte Hour = gps.time.hour();
|
||||||
|
byte Minute = gps.time.minute();
|
||||||
|
byte Second = gps.time.second();
|
||||||
|
|
||||||
|
setTime(Hour, Minute, Second, Day, Month, Year);
|
||||||
|
adjustTime(offset * SECS_PER_HOUR);
|
||||||
|
|
||||||
|
if(gps.location.isValid()){
|
||||||
|
tft.setTextDatum(TL_DATUM);
|
||||||
|
tft.setTextSize(1);
|
||||||
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
|
|
||||||
|
latitude = String(gps.location.lat(), 6);
|
||||||
|
tft.drawString(latitude, 0, 120, 4);
|
||||||
|
String coarseLatitude = String(gps.location.lat(), 3);
|
||||||
|
|
||||||
|
if(coarseLatitude != prevCoarseLatitude){
|
||||||
|
isMapNeedUpdate = true;
|
||||||
|
}
|
||||||
|
prevCoarseLatitude = coarseLatitude;
|
||||||
|
|
||||||
|
longitude = String(gps.location.lng(), 6);
|
||||||
|
tft.drawString(longitude, 0, 150, 4);
|
||||||
|
String coarseLongitude = String(gps.location.lng(), 3);
|
||||||
|
|
||||||
|
if(coarseLongitude != prevCoarseLongitude){
|
||||||
|
isMapNeedUpdate = true;
|
||||||
|
}
|
||||||
|
prevCoarseLongitude = coarseLongitude;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(timeStatus() != timeNotSet){
|
||||||
|
if(now() != prevDisplay){
|
||||||
|
prevDisplay = now();
|
||||||
|
dateTimeDisplay();
|
||||||
|
|
||||||
|
if(isMapNeedUpdate){
|
||||||
|
if(getStaticMapImage(host, getPath().c_str(), mapFile)){
|
||||||
|
TJpgDec.drawFsJpg(160, 0, mapFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() > 5000 && gps.charsProcessed() < 10)
|
||||||
|
{
|
||||||
|
Serial.println(F("No GPS detected: check wiring."));
|
||||||
|
errorScreen();
|
||||||
|
while(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initScreen(){
|
||||||
|
tft.fillScreen(TFT_WHITE);
|
||||||
|
tft.setTextSize(1);
|
||||||
|
tft.setTextColor(TFT_RED, TFT_WHITE);
|
||||||
|
tft.setTextDatum(TC_DATUM);
|
||||||
|
tft.drawString("Waiting for GPS...", 240, 140, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorScreen(){
|
||||||
|
tft.fillScreen(TFT_RED);
|
||||||
|
tft.setTextSize(1);
|
||||||
|
tft.setTextColor(TFT_WHITE, TFT_RED);
|
||||||
|
tft.setTextDatum(TC_DATUM);
|
||||||
|
tft.drawString("No GPS detected: Check Wiring!", 240, 140, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dateTimeDisplay(){
|
||||||
|
sprintf(timeBuffer, "%02u:%02u:%02u", hour(), minute(), second());
|
||||||
|
tft.setTextSize(1);
|
||||||
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||||
|
tft.drawString(timeBuffer, 0, 30, 4);
|
||||||
|
|
||||||
|
sprintf(dateBuffer, "%02u/%02u/%04u", month(), day(), year());
|
||||||
|
tft.drawString(dateBuffer, 0, 60, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void menuScreen(){
|
||||||
|
tft.fillScreen(TFT_BLACK);
|
||||||
|
plusBtn();
|
||||||
|
negativeBtn();
|
||||||
|
showingZoomLevel();
|
||||||
|
TJpgDec.drawFsJpg(160, 0, mapFile); //previous saved image
|
||||||
|
}
|
||||||
|
|
||||||
|
void plusBtn(){
|
||||||
|
tft.fillRect(PButtonFrame_X, PButtonFrame_Y, BUTTONSIZE, BUTTONSIZE, TFT_GREEN);
|
||||||
|
tft.setTextColor(TFT_WHITE);
|
||||||
|
tft.setTextSize(2);
|
||||||
|
tft.setTextDatum(MC_DATUM);
|
||||||
|
tft.drawString("+", PButtonFrame_X + (BUTTONSIZE / 2) + 1, PButtonFrame_Y + (BUTTONSIZE / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void negativeBtn(){
|
||||||
|
tft.fillRect(NButtonFrame_X, NButtonFrame_Y, BUTTONSIZE, BUTTONSIZE, TFT_RED);
|
||||||
|
tft.setTextColor(TFT_WHITE);
|
||||||
|
tft.setTextSize(2);
|
||||||
|
tft.setTextDatum(MC_DATUM);
|
||||||
|
tft.drawString("-", NButtonFrame_X + (BUTTONSIZE / 2) + 1, NButtonFrame_Y + (BUTTONSIZE / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void showingZoomLevel(){
|
||||||
|
sprintf(zoomBuffer, "Zoom : %02d", zoomLevel);
|
||||||
|
|
||||||
|
tft.setTextDatum(TL_DATUM);
|
||||||
|
tft.setTextSize(2);
|
||||||
|
tft.drawString(zoomBuffer, 20, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonEvent(){
|
||||||
|
uint16_t x, y;
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
if(currentMillis - previousMillis > buttonInterval){
|
||||||
|
previousMillis = currentMillis;
|
||||||
|
buttonAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tft.getTouch(&x, &y) && !buttonAvailable){
|
||||||
|
if((x > PButtonFrame_X) && (x < (PButtonFrame_X + BUTTONSIZE))){
|
||||||
|
if((y > PButtonFrame_Y) && (x < (PButtonFrame_Y + BUTTONSIZE))){
|
||||||
|
buttonAvailable = true;
|
||||||
|
|
||||||
|
zoomLevel++;
|
||||||
|
if(zoomLevel > maxZoomLevel){
|
||||||
|
zoomLevel = maxZoomLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if((x > NButtonFrame_X) && (x < (NButtonFrame_X + BUTTONSIZE))){
|
||||||
|
if((y > NButtonFrame_Y) && (x < (NButtonFrame_Y + BUTTONSIZE))){
|
||||||
|
buttonAvailable = true;
|
||||||
|
|
||||||
|
zoomLevel--;
|
||||||
|
if(zoomLevel < minZoomLevel){
|
||||||
|
zoomLevel = minZoomLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showingZoomLevel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/***************************************************************************************
|
||||||
|
** Function name: listSPIFFS
|
||||||
|
** Description: Listing SPIFFS files
|
||||||
|
***************************************************************************************/
|
||||||
|
#ifdef ESP8266
|
||||||
|
void listSPIFFS(void) {
|
||||||
|
Serial.println(F("\r\nListing SPIFFS files:"));
|
||||||
|
|
||||||
|
fs::Dir dir = SPIFFS.openDir("/"); // Root directory
|
||||||
|
|
||||||
|
static const char line[] PROGMEM = "=================================================";
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println(F(" File name Size"));
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
|
||||||
|
while (dir.next()) {
|
||||||
|
String fileName = dir.fileName();
|
||||||
|
Serial.print(fileName);
|
||||||
|
int spaces = 33 - fileName.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
|
||||||
|
fs::File f = dir.openFile("r");
|
||||||
|
String fileSize = (String) f.size();
|
||||||
|
spaces = 10 - fileSize.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
Serial.println(fileSize + " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println();
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================================
|
||||||
|
|
||||||
|
#elif defined ESP32
|
||||||
|
|
||||||
|
void listSPIFFS(void) {
|
||||||
|
Serial.println(F("\r\nListing SPIFFS files:"));
|
||||||
|
static const char line[] PROGMEM = "=================================================";
|
||||||
|
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println(F(" File name Size"));
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
|
||||||
|
fs::File root = SPIFFS.open("/");
|
||||||
|
if (!root) {
|
||||||
|
Serial.println(F("Failed to open directory"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!root.isDirectory()) {
|
||||||
|
Serial.println(F("Not a directory"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::File file = root.openNextFile();
|
||||||
|
while (file) {
|
||||||
|
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
Serial.print("DIR : ");
|
||||||
|
String fileName = file.name();
|
||||||
|
Serial.print(fileName);
|
||||||
|
} else {
|
||||||
|
String fileName = file.name();
|
||||||
|
Serial.print(" " + fileName);
|
||||||
|
// File path can be 31 characters maximum in SPIFFS
|
||||||
|
int spaces = 33 - fileName.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
String fileSize = (String) file.size();
|
||||||
|
spaces = 10 - fileSize.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
Serial.println(fileSize + " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
file = root.openNextFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println();
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#include "FS.h"
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <TFT_eSPI.h>
|
||||||
|
TFT_eSPI tft = TFT_eSPI();
|
||||||
|
|
||||||
|
#define CALIBRATION_FILE "/calibrationData"
|
||||||
|
|
||||||
|
void setup(void) {
|
||||||
|
uint16_t calibrationData[5];
|
||||||
|
uint8_t calDataOK = 0;
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("starting");
|
||||||
|
|
||||||
|
tft.init();
|
||||||
|
|
||||||
|
tft.setRotation(3);
|
||||||
|
tft.fillScreen((0xFFFF));
|
||||||
|
|
||||||
|
tft.setCursor(20, 0, 2);
|
||||||
|
tft.setTextColor(TFT_BLACK, TFT_WHITE); tft.setTextSize(1);
|
||||||
|
tft.println("calibration run");
|
||||||
|
|
||||||
|
// check file system
|
||||||
|
if (!SPIFFS.begin()) {
|
||||||
|
Serial.println("formating file system");
|
||||||
|
|
||||||
|
SPIFFS.format();
|
||||||
|
SPIFFS.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPIFFS.format(); //Always start calibration
|
||||||
|
|
||||||
|
// check if calibration file exists
|
||||||
|
if (SPIFFS.exists(CALIBRATION_FILE)) {
|
||||||
|
File f = SPIFFS.open(CALIBRATION_FILE, "r");
|
||||||
|
if (f) {
|
||||||
|
if (f.readBytes((char *)calibrationData, 14) == 14)
|
||||||
|
calDataOK = 1;
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (calDataOK) {
|
||||||
|
// calibration data valid
|
||||||
|
tft.setTouch(calibrationData);
|
||||||
|
} else {
|
||||||
|
// data not valid. recalibrate
|
||||||
|
tft.calibrateTouch(calibrationData, TFT_WHITE, TFT_RED, 15);
|
||||||
|
// store data
|
||||||
|
File f = SPIFFS.open(CALIBRATION_FILE, "w");
|
||||||
|
if (f) {
|
||||||
|
f.write((const unsigned char *)calibrationData, 14);
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<5; i++){
|
||||||
|
Serial.printf("%d,%d\n", i, calibrationData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tft.fillScreen((0xFFFF));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
uint16_t x, y;
|
||||||
|
static uint16_t color;
|
||||||
|
|
||||||
|
if (tft.getTouch(&x, &y)) {
|
||||||
|
|
||||||
|
tft.setCursor(5, 5, 2);
|
||||||
|
tft.printf("x: %i ", x);
|
||||||
|
tft.setCursor(5, 20, 2);
|
||||||
|
tft.printf("y: %i ", y);
|
||||||
|
|
||||||
|
tft.drawPixel(x, y, color);
|
||||||
|
color += 155;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
#define WIFI_SSID "<YOUR_WIFI_SSID_HERE>"
|
||||||
|
#define PASSWORD "<YOUR_WIFI_PW_HERE>"
|
||||||
|
#include <TJpg_Decoder.h>
|
||||||
|
#include <CertStoreBearSSL.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
// Include SPIFFS
|
||||||
|
#define FS_NO_GLOBALS
|
||||||
|
#include <FS.h>
|
||||||
|
|
||||||
|
// Include WiFi and http client
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266HTTPClient.h>
|
||||||
|
#else
|
||||||
|
#include "SPIFFS.h" // Required for ESP32 only
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Load tabs attached to this sketch
|
||||||
|
#include "List_SPIFFS.h"
|
||||||
|
#include "Web_Fetch.h"
|
||||||
|
#include "SPI.h"
|
||||||
|
#include <TFT_eSPI.h> // Hardware-specific library
|
||||||
|
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
||||||
|
|
||||||
|
BearSSL::CertStore certStore;
|
||||||
|
|
||||||
|
const char * host = "maps.googleapis.com";
|
||||||
|
const char * path = "/maps/api/staticmap?center=40.714728,-73.998672&zoom=14&size=480x320&format=jpg-baseline&key=<YOUR_API_KEY_HERE>";
|
||||||
|
const char * mapFile = "/map.jpg";
|
||||||
|
|
||||||
|
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
|
||||||
|
{
|
||||||
|
// Stop further decoding as image is running off bottom of screen
|
||||||
|
if ( y >= tft.height() ) return 0;
|
||||||
|
|
||||||
|
// This function will clip the image block rendering automatically at the TFT boundaries
|
||||||
|
tft.pushImage(x, y, w, h, bitmap);
|
||||||
|
|
||||||
|
// Return 1 to decode next block
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("\n\n");
|
||||||
|
|
||||||
|
// Initialise SPIFFS
|
||||||
|
if (!SPIFFS.begin()) {
|
||||||
|
Serial.println("SPIFFS initialisation failed!");
|
||||||
|
while (1) yield(); // Stay here twiddling thumbs waiting
|
||||||
|
}
|
||||||
|
Serial.println("\r\nInitialisation done.");
|
||||||
|
|
||||||
|
|
||||||
|
// Initialise the TFT
|
||||||
|
tft.begin();
|
||||||
|
tft.fillScreen(TFT_BLACK);
|
||||||
|
tft.setRotation(3);
|
||||||
|
|
||||||
|
// The jpeg image can be scaled by a factor of 1, 2, 4, or 8
|
||||||
|
TJpgDec.setJpgScale(1);
|
||||||
|
|
||||||
|
// The byte order can be swapped (set true for TFT_eSPI)
|
||||||
|
TJpgDec.setSwapBytes(true);
|
||||||
|
|
||||||
|
// The decoder must be given the exact name of the rendering function above
|
||||||
|
TJpgDec.setCallback(tft_output);
|
||||||
|
|
||||||
|
WiFi.begin(WIFI_SSID, PASSWORD);
|
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(1000);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
setClock(); // Required for X.509 validation
|
||||||
|
|
||||||
|
int numCerts = certStore.initCertStore(SPIFFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
|
||||||
|
Serial.printf("Number of CA certs read: %d\n", numCerts);
|
||||||
|
if (numCerts == 0) {
|
||||||
|
Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the SPIFFS directory before running?\n");
|
||||||
|
return; // Can't connect to anything w/o certs!
|
||||||
|
}
|
||||||
|
|
||||||
|
listSPIFFS();
|
||||||
|
|
||||||
|
if(getStaticMapImage(host, path, mapFile)){
|
||||||
|
listSPIFFS();
|
||||||
|
TJpgDec.drawFsJpg(0, 0, mapFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set time via NTP, as required for x.509 validation
|
||||||
|
void setClock() {
|
||||||
|
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||||
|
|
||||||
|
Serial.print("Waiting for NTP time sync: ");
|
||||||
|
time_t now = time(nullptr);
|
||||||
|
while (now < 8 * 3600 * 2) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
now = time(nullptr);
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
struct tm timeinfo;
|
||||||
|
gmtime_r(&now, &timeinfo);
|
||||||
|
Serial.print("Current time: ");
|
||||||
|
Serial.print(asctime(&timeinfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getStaticMapImage(const char *host, const char *path, String fileName){
|
||||||
|
if(WiFi.status() != WL_CONNECTED){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int contentLength = 0;
|
||||||
|
int httpCode;
|
||||||
|
|
||||||
|
BearSSL::WiFiClientSecure *client = new BearSSL::WiFiClientSecure();
|
||||||
|
// Integrate the cert store with this connection
|
||||||
|
client->setCertStore(&certStore);
|
||||||
|
client->connect(host, 443); //HTTPS
|
||||||
|
|
||||||
|
Serial.printf("Trying: %s:443...", host);
|
||||||
|
|
||||||
|
if(!client->connected()){
|
||||||
|
client->stop();
|
||||||
|
Serial.printf("*** Can't connect. ***\n-------\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("HTTPS Connected!");
|
||||||
|
client->write("GET ");
|
||||||
|
client->write(path);
|
||||||
|
client->write(" HTTP/1.0\r\nHost: ");
|
||||||
|
client->write(host);
|
||||||
|
client->write("\r\nUser-Agent: ESP8266\r\n");
|
||||||
|
client->write("\r\n");
|
||||||
|
|
||||||
|
while(client->connected()){
|
||||||
|
String header = client-> readStringUntil('\n');
|
||||||
|
if(header.startsWith(F("HTTP/1."))){
|
||||||
|
httpCode = header.substring(9, 12).toInt();
|
||||||
|
if(httpCode != 200){
|
||||||
|
client->stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(header.startsWith(F("Content-Length: "))){
|
||||||
|
contentLength = header.substring(15).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(header == F("\r")){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(contentLength > 0)){
|
||||||
|
client->stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::File f = SPIFFS.open(fileName, "w");
|
||||||
|
if(!f){
|
||||||
|
Serial.println(F("FILE OPEN FAILED"));
|
||||||
|
client->stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int remaining = contentLength;
|
||||||
|
int received;
|
||||||
|
uint8_t buff[512] = {0};
|
||||||
|
|
||||||
|
while(client->available() && remaining > 0){
|
||||||
|
received = client->readBytes(buff, ((remaining > sizeof(buff)) ? sizeof(buff) : remaining));
|
||||||
|
f.write(buff, received);
|
||||||
|
|
||||||
|
if(remaining > 0){
|
||||||
|
remaining -= received;
|
||||||
|
}
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
f.close();
|
||||||
|
client->stop();
|
||||||
|
Serial.println("DOWNLOAD END");
|
||||||
|
return (remaining == 0 ? true : false);
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/***************************************************************************************
|
||||||
|
** Function name: listSPIFFS
|
||||||
|
** Description: Listing SPIFFS files
|
||||||
|
***************************************************************************************/
|
||||||
|
#ifdef ESP8266
|
||||||
|
void listSPIFFS(void) {
|
||||||
|
Serial.println(F("\r\nListing SPIFFS files:"));
|
||||||
|
|
||||||
|
fs::Dir dir = SPIFFS.openDir("/"); // Root directory
|
||||||
|
|
||||||
|
static const char line[] PROGMEM = "=================================================";
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println(F(" File name Size"));
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
|
||||||
|
while (dir.next()) {
|
||||||
|
String fileName = dir.fileName();
|
||||||
|
Serial.print(fileName);
|
||||||
|
int spaces = 33 - fileName.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
|
||||||
|
fs::File f = dir.openFile("r");
|
||||||
|
String fileSize = (String) f.size();
|
||||||
|
spaces = 10 - fileSize.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
Serial.println(fileSize + " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println();
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================================
|
||||||
|
|
||||||
|
#elif defined ESP32
|
||||||
|
|
||||||
|
void listSPIFFS(void) {
|
||||||
|
Serial.println(F("\r\nListing SPIFFS files:"));
|
||||||
|
static const char line[] PROGMEM = "=================================================";
|
||||||
|
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println(F(" File name Size"));
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
|
||||||
|
fs::File root = SPIFFS.open("/");
|
||||||
|
if (!root) {
|
||||||
|
Serial.println(F("Failed to open directory"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!root.isDirectory()) {
|
||||||
|
Serial.println(F("Not a directory"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::File file = root.openNextFile();
|
||||||
|
while (file) {
|
||||||
|
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
Serial.print("DIR : ");
|
||||||
|
String fileName = file.name();
|
||||||
|
Serial.print(fileName);
|
||||||
|
} else {
|
||||||
|
String fileName = file.name();
|
||||||
|
Serial.print(" " + fileName);
|
||||||
|
// File path can be 31 characters maximum in SPIFFS
|
||||||
|
int spaces = 33 - fileName.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
String fileSize = (String) file.size();
|
||||||
|
spaces = 10 - fileSize.length(); // Tabulate nicely
|
||||||
|
if (spaces < 1) spaces = 1;
|
||||||
|
while (spaces--) Serial.print(" ");
|
||||||
|
Serial.println(fileSize + " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
file = root.openNextFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println(FPSTR(line));
|
||||||
|
Serial.println();
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// Fetch a file from the URL given and save it in SPIFFS
|
||||||
|
// Return 1 if a web fetch was needed or 0 if file already exists
|
||||||
|
bool getFile(String url, String filename) {
|
||||||
|
|
||||||
|
// If it exists then no need to fetch it
|
||||||
|
if (SPIFFS.exists(filename) == true) {
|
||||||
|
Serial.println("Found " + filename);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Downloading " + filename + " from " + url);
|
||||||
|
|
||||||
|
// Check WiFi connection
|
||||||
|
if ((WiFi.status() == WL_CONNECTED)) {
|
||||||
|
HTTPClient http;
|
||||||
|
|
||||||
|
Serial.print("[HTTP] begin...\n");
|
||||||
|
|
||||||
|
// Configure server and url
|
||||||
|
http.begin(url);
|
||||||
|
|
||||||
|
Serial.print("[HTTP] GET...\n");
|
||||||
|
// Start connection and send HTTP header
|
||||||
|
int httpCode = http.GET();
|
||||||
|
if (httpCode > 0) {
|
||||||
|
fs::File f = SPIFFS.open(filename, "w+");
|
||||||
|
if (!f) {
|
||||||
|
Serial.println("file open failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// HTTP header has been send and Server response header has been handled
|
||||||
|
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
|
||||||
|
|
||||||
|
// File found at server
|
||||||
|
if (httpCode == HTTP_CODE_OK) {
|
||||||
|
|
||||||
|
// Get length of document (is -1 when Server sends no Content-Length header)
|
||||||
|
int total = http.getSize();
|
||||||
|
int len = total;
|
||||||
|
|
||||||
|
// Create buffer for read
|
||||||
|
uint8_t buff[128] = { 0 };
|
||||||
|
|
||||||
|
// Get tcp stream
|
||||||
|
WiFiClient * stream = http.getStreamPtr();
|
||||||
|
|
||||||
|
// Read all data from server
|
||||||
|
while (http.connected() && (len > 0 || len == -1)) {
|
||||||
|
// Get available data size
|
||||||
|
size_t size = stream->available();
|
||||||
|
|
||||||
|
if (size) {
|
||||||
|
// Read up to 128 bytes
|
||||||
|
int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
|
||||||
|
|
||||||
|
// Write it to file
|
||||||
|
f.write(buff, c);
|
||||||
|
|
||||||
|
// Calculate remaining bytes
|
||||||
|
if (len > 0) {
|
||||||
|
len -= c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
Serial.print("[HTTP] connection closed or file end.\n");
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
}
|
||||||
|
return 1; // File was fetched from web
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user