繼續上一篇

ESP8266 一次就上手 / Google Script + Google試算表 =股票機 (二)

處理好 GoogleScript 以及Google試算表,接下來就是把程式寫到ESP8266

由於使用 GoogleScript 取資料必須走https的協議,所以Arduino IDE 要安裝HTTPSRedirect 這個程式庫(必須手動安裝)

可以從這下載最新的:HTTPSRedirect

HTTPSRedirect.png

手動將.ZIP加入程式庫內

addzip.png

好了這樣就可以正常使用了

完整的code 如下,很多是許多段加在一起的,沒有特別在整理過,所以有可能很多是贅述的,畢竟花了很多時間反覆在測試,請大家包涵一下


#include "ESP8266WiFi.h"
#include "HTTPSRedirect.h"
#include "DNSServer.h" //Local DNS Server used for redirecting all rs to the configuration portal
#include "ESP8266WebServer.h" //Local WebServer used to serve the configuration portal
#include "WiFiManager.h"
#include "ArduinoJson.h"
#include "DebugMacros.h"
#include "string.h"
#include "WiFiUdp.h"
#include "Time.h"
#include "TimeAlarms.h"
char copy[512];
unsigned int localPort=2390; //local port to listen for UDP packets
IPAddress timeServerIP; //time.nist.gov NTP server address
const char* ntpServerName="time.nist.gov"; //NTP Server host name
const int NTP_PACKET_SIZE=48; //NTP timestamp resides in the first 48 bytes of packets
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
WiFiUDP udp; //UDP instance to let us send and receive packets over UDP
int count=0;
int w2=0;
const int pressPin = 0;
String msg1="歡迎使用股票機";
static const unsigned char PROGMEM signal0[] =
{
 0x7F, 0x00, 0x00, 0x49, 0x00, 0x04, 0x2A, 0x00, 0x04, 0x9C, 0x64, 0x04, 
 0x88, 0x95, 0x04, 0x88, 0x96, 0x00, 0x88, 0x64, 0x04, 0x00, 0x00, 0x00, 
};
static const unsigned char PROGMEM signal1[] =
{
 0x7F, 0x00, 0x00, 0x49, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x1C, 0x00, 0x00, 
 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 
};
static const unsigned char PROGMEM signal2[] =
{
 0x7F, 0x00, 0x00, 0x49, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x1C, 0x00, 0x00, 
 0x08, 0x0C, 0x00, 0x08, 0x0C, 0x00, 0xC8, 0x0C, 0x00, 0x00, 0x00, 0x00, 
};
static const unsigned char PROGMEM signal3[] =
{
 0x7F, 0x00, 0x00, 0x49, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x1C, 0xC0, 0x00, 
 0x08, 0xCC, 0x00, 0x08, 0xCC, 0x00, 0xC8, 0xCC, 0x00, 0x00, 0x00, 0x00, 
};
static const unsigned char PROGMEM signal4[] =
{
 0x7F, 0x00, 0x00, 0x49, 0x00, 0x00, 0x2A, 0x00, 0x0C, 0x1C, 0xC0, 0x0C, 
 0x08, 0xCC, 0x0C, 0x08, 0xCC, 0x0C, 0xC8, 0xCC, 0x0C, 0x00, 0x00, 0x00, 
};
static const unsigned char PROGMEM signal5[] =
{
 0x7F, 0x00, 0x00, 0x49, 0x00, 0xC0, 0x2A, 0x00, 0xCC, 0x1C, 0xC0, 0xCC, 
 0x08, 0xCC, 0xCC, 0x08, 0xCC, 0xCC, 0xC8, 0xCC, 0xCC, 0x00, 0x00, 0x00, 
};

const char* host = "script.google.com";
const char *GScriptId = "xxxxxxxxxx"; // Google script ID
const char *SheetId = "xxxxxxxxxx";// Google sheet ID
const int httpsPort = 443;
int stocknumber = 2;
int totalnumber = 13;//設定幾檔股票

HTTPSRedirect* client = nullptr ;
unsigned int free_heap_before = 0;
unsigned int free_stack_before = 0;

//U8g2
#include 
#ifdef U8X8_HAVE_HW_SPI
#include 
#endif
#ifdef U8X8_HAVE_HW_I2C
#include 
#endif
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/16, /* clock=*/ 5, /* data=*/ 4); // pin remapping with ESP8266 HW I2C

void setup() {
// initialize the Serial
Serial.begin(115200);
 Serial.flush();
 pinMode(pressPin, INPUT_PULLUP);
 free_heap_before = ESP.getFreeHeap();
 free_stack_before = ESP.getFreeContStack();
 Serial.printf("Free heap: %u\n", free_heap_before);
 Serial.printf("Free stack: %u\n", free_stack_before);
 Serial.flush();

 //U8g2
 u8g2.begin();
 u8g2.enableUTF8Print();
 u8g2.setFont(u8g2_font_unifont_t_chinese1);
 u8g2.setFontDirection(0);
 u8g2.clearBuffer();
 u8g2.setCursor(0, 15);
 u8g2.print("Startup");
 u8g2.setCursor(0, 31);
 u8g2.print("股票機啟動中...");
 u8g2.sendBuffer();
 //wifi連線
 delay(1000);
 u8g2.clearBuffer();
 u8g2.setCursor(0, 15);
 u8g2.print("WIFI連線中...");
 u8g2.setCursor(0, 31);
 u8g2.print("請連結STOCK-BOT");
 u8g2.sendBuffer();
 WiFiManager wifiManager;
 wifiManager.autoConnect("STOCK-BOT");
 Serial.println("");
 Serial.println("WiFi connected");
 Serial.println("IP address: ");
 IPAddress ip = WiFi.localIP();
 Serial.println(ip);
 
 // Use HTTPSRedirect class to create a new TLS connection
 client = new HTTPSRedirect(httpsPort);
 client->setInsecure();
 client->setPrintResponseBody(true);
 client->setContentTypeHeader("application/json");
 Serial.print("Connecting to ");
 Serial.println(host);
 
 //Start UDP
 Serial.println("Starting UDP");
 udp.begin(localPort);
 Serial.print("Local port: ");
 Serial.println(udp.localPort());
 sync_clock(); //以 NTP 時間初始設定內部時鐘
 delete client;
 client = nullptr;
}

void loop() {
 String d=getDate();
 String t=getTime();
 String w=getWeek();
 int Hm=hour()*100 + minute(); //時:分整數, 02359
 Serial.print(d + " " + t + " " + w + " ");
 Serial.println(Hm);
 static bool flag = false;
 if (!flag){
 free_heap_before = ESP.getFreeHeap();
 free_stack_before = ESP.getFreeContStack();
 client = new HTTPSRedirect(httpsPort);
 client->setInsecure();
 flag = true;
 client->setPrintResponseBody(true);
 client->setContentTypeHeader("application/json");
 }
 if (client != nullptr){
 if (!client->connected()){
 client->connect(host, httpsPort);
 }
 }
 getstock();
 if(w2>128)
 longtext();
 ++count;
 ++stocknumber;
 if (count >= 30) { //每 30 次迴圈與 NTP 同步一次
 sync_clock();
 count=0;
 }
 if (stocknumber > totalnumber) { //設定幾檔股票
 stocknumber=2;
 } 
 
 delay(1000);

}
 
 
void sync_clock() {
 unsigned long GMT=getUnixTime();
 if (GMT != 0) { //有得到 NTP 回應才更新 ESP8266 內建 RTC
 setTime(GMT + 28800L); //以台灣時間更新內部時鐘
 }
 }
 
void longtext(){
 String d=getDate();
 String t=getTime();
 int rssi = WiFi.RSSI();
 for(int i=0 ; i>127-w2; --i){
 u8g2.clearBuffer();
 u8g2.setFont(u8g2_font_pxplusibmcgathin_8r);
 u8g2.setCursor(0, 8);
 u8g2.print(d);u8g2.print(" ");u8g2.println(t);
 u8g2.setCursor(i, 26);
 u8g2.setFont(u8g2_font_unifont_t_chinese1);
 u8g2.println(msg1);
 if(rssi<-20 && rssi>=-40)
 u8g2.drawXBMP(104, 0, 24, 8, signal5);
 else if(rssi<-40 && rssi>=-50)
 u8g2.drawXBMP(104, 0, 24, 8, signal4);
 else if(rssi<-50 && rssi>=-60)
 u8g2.drawXBMP(104, 0, 24, 8, signal3); 
 else if(rssi<-60 && rssi>=-70)
 u8g2.drawXBMP(104, 0, 24, 8, signal2);
 else if(rssi<-70 && rssi>=-80)
 u8g2.drawXBMP(104, 0, 24, 8, signal1);
 else
 u8g2.drawXBMP(104, 0, 24, 8, signal0);
 u8g2.sendBuffer();
 delay(10);
 }
}

void winfi_signal(){
 int rssi = WiFi.RSSI();
 if(rssi<-20 && rssi>=-40)
 u8g2.drawXBMP(104, 0, 24, 8, signal5);
 else if(rssi<-40 && rssi>=-50)
 u8g2.drawXBMP(104, 0, 24, 8, signal4);
 else if(rssi<-50 && rssi>=-60)
 u8g2.drawXBMP(104, 0, 24, 8, signal3); 
 else if(rssi<-60 && rssi>=-70)
 u8g2.drawXBMP(104, 0, 24, 8, signal2);
 else if(rssi<-70 && rssi>=-80)
 u8g2.drawXBMP(104, 0, 24, 8, signal1);
 else
 u8g2.drawXBMP(104, 0, 24, 8, signal0);
}
String getDate() {
 String d="";
 byte M=month();
 if (M < 10) {d.concat('0');}
 d.concat(M);
 d.concat('/');
 byte D=day();
 if (D < 10) {d.concat('0');}
 d.concat(D);
 return d;
 }

String getTime() {
 String t="";
 byte h=hour();
 if (h < 10) {t.concat('0');}
 t.concat(h);
 t.concat(':');
 byte m=minute();
 if (m < 10) {t.concat('0');}
 t.concat(m);
 //t.concat(':');
 //byte s=second();
 //if (s < 10) {t.concat('0');}
 //t.concat(s);
 return t;
 }

String getWeek() {
 String w[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
 return w[weekday()-1];
 }

String getDateTime() { //傳回日期時間
 String dt=(String)year() + "-";
 byte M=month();
 if (M < 10) {dt.concat('0');}
 dt.concat(M);
 dt.concat('-');
 byte d=day();
 if (d < 10) {dt.concat('0');}
 dt.concat(d);
 dt.concat(' ');
 byte h=hour();
 if (h < 10) {dt.concat('0');}
 dt.concat(h);
 dt.concat(':');
 byte m=minute();
 if (m < 10) {dt.concat('0');}
 dt.concat(m);
 dt.concat(':');
 byte s=second();
 if (s < 10) {dt.concat('0');}
 dt.concat(s);
 return dt; //傳回格式如 2016-07-16 16:09:23 的日期時間字串
 }

unsigned long getUnixTime() {
 WiFi.hostByName(ntpServerName, timeServerIP); //get a random server from the pool
 sendNTPpacket(timeServerIP); //send an NTP packet to a time server
 delay(1000); // wait to see if a reply is available

 int cb=udp.parsePacket(); //return bytes received
 unsigned long unix_time=0; //預設傳回 0, 表示未收到 NTP 回應
 if (!cb) {Serial.println("no packet yet");}
 else { //received a packet, read the data from the buffer
 Serial.print("packet received, length=");
 Serial.println(cb); //=48
 udp.read(packetBuffer, NTP_PACKET_SIZE); //read the packet into the buffer

 //the timestamp starts at byte 40 of the received packet and is four bytes,
 //or two words, long. First, esxtract the two words:
 unsigned long highWord=word(packetBuffer[40], packetBuffer[41]);
 unsigned long lowWord=word(packetBuffer[42], packetBuffer[43]);
 //combine the four bytes (two words) into a long integer
 //this is NTP time (seconds since Jan 1 1900):
 unsigned long secsSince1900=highWord << 16 lowWord;
 Serial.print("Seconds since Jan 1 1900=" );
 Serial.println(secsSince1900);
 Serial.print("Unix time=");
 //Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
 unix_time=secsSince1900 - 2208988800UL; //更新 unix_time
 Serial.print(F("Unix time stamp (seconds since 1970-01-01)="));
 Serial.println(unix_time); //print Unix time
 } 
 return unix_time; //return seconds since 1970-01-01
 }

unsigned long sendNTPpacket(IPAddress& address) {
 Serial.println("sending NTP packet...");
 // set all bytes in the buffer to 0
 memset(packetBuffer, 0, NTP_PACKET_SIZE);
 //Initialize values needed to form NTP request
 //(see URL above for details on the packets)
 packetBuffer[0]=0b11100011; // LI, Version, Mode
 packetBuffer[1]=0; // Stratum, or type of clock
 packetBuffer[2]=6; // Polling Interval
 packetBuffer[3]=0xEC; // Peer Clock Precision
 //8 bytes of zero for Root Delay & Root Dispersion
 packetBuffer[12]=49;
 packetBuffer[13]=0x4E;
 packetBuffer[14]=49;
 packetBuffer[15]=52;
 // all NTP fields have been given values, now
 // you can send a packet requesting a timestamp:
 udp.beginPacket(address, 123); //NTP requests are to port 123
 udp.write(packetBuffer, NTP_PACKET_SIZE);
 udp.endPacket();
 }

void getstock() {
 String url3 = String("/macros/s/") + GScriptId + "/exec?id=" + SheetId + "&number=" + stocknumber;
 Serial.println(url3);
 Serial.println("GET Data");
 if (client->GET(url3, host)){
 Serial.println("GET SUCCESS");
 }
 String aLine = client->getResponseBody();
 Serial.print("aLine:");Serial.println(aLine); 
 Serial.print("aLineLength:");Serial.println(aLine.length());
 aLine.replace("records","");
 aLine.replace("\"","");
 aLine.replace("{","");
 aLine.replace("}","");
 aLine.replace("[","");
 aLine.replace("]","");
 aLine.replace(","," ");
 aLine.replace(":"," ");
 aLine.replace("TPE","");
 msg1 = aLine;
 msg1.toCharArray(copy, 512);
 w2=u8g2.getUTF8Width(copy);
 Serial.print("w2:");Serial.println(w2);
}


 

在程式碼第5459行的位置

const char* host = "script.google.com";

const char *GScriptId = "xxxxxxxxxx"; // 輸入你的GoogleScript ID

const char *SheetId = "xxxxxxxxxx";// 輸入你的Google試算表 ID 

const int httpsPort = 443;

int stocknumber = 2;

int totalnumber = 13;//設定幾檔股票

這3個地方要輸入你的資料,然後上傳就可以了

st01.png

st04.png

st03.png

st02.png

 

 

大功告成


補充說明

這邊用到幾個程式庫,其中 WiFimanager 是一個方便好用的 WiFi 連線程式庫,設定很簡單,好處是你不用常常在程式裡修改你的WiFi SSID及密碼,當裝置連不上WiFi時就會自動產生熱點,你在連到熱點選擇可以使用的WiFi 輸入密碼即可連上網,程式庫可以在Arduino IDE 管理程式庫搜尋即可安裝。

程式第61行,wifiManager.autoConnect("STOCK-BOT");你可以修改自己喜歡的名稱,在搜尋裝置 WiFi 名稱時就可以輕易地辨識。

wm04.pngwm03.png

wm02.pngwm01.png

 

U8g2 程式庫自訂字褲的功能很強大,基本上UTF8碼找得到的字都可以用,只是要考量裝置 Flash 的大小是否能把字庫都裝進去。

自訂圖形部分U8g2 支援XBM的格式,你可以用小畫家等繪圖軟體畫好你要的圖形,存成BMP檔,利用線上轉圖程式:XBM線上轉圖

xbm01.png

轉檔後下載的 *.XBM 用純文字編輯器打開,裡面的程式碼貼到程式內就可以用了

xbm02.png

程式碼332355行,是透過HTTPS協議取值,取回的值是JSON格式,但是我不會使用JSON取值方式,所以就把回傳的資料當作字串處理,把一些標點符號,括號用空白字取代了,如果有高手也請幫忙教學一下吧

 

文章標籤
全站熱搜
創作者介紹
創作者 terrywu5 的頭像
terrywu5

IoT 智慧生活的異想世界

terrywu5 發表在 痞客邦 留言(0) 人氣(820)