繼續上一篇~
ESP8266 一次就上手 / Google Script + Google試算表 =股票機 (二)
處理好 GoogleScript 以及Google試算表,接下來就是把程式寫到ESP8266
由於使用 GoogleScript 取資料必須走https的協議,所以Arduino IDE 要安裝HTTPSRedirect 這個程式庫(必須手動安裝)
可以從這下載最新的:HTTPSRedirect
手動將.ZIP加入程式庫內
好了這樣就可以正常使用了~~
完整的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(); //時:分整數, 0~2359
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);
}
在程式碼第54~59行的位置
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個地方要輸入你的資料,然後上傳就可以了~~
大功告成~
補充說明~
這邊用到幾個程式庫,其中 WiFimanager 是一個方便好用的 WiFi 連線程式庫,設定很簡單,好處是你不用常常在程式裡修改你的WiFi SSID及密碼,當裝置連不上WiFi時就會自動產生熱點,你在連到熱點選擇可以使用的WiFi 輸入密碼即可連上網,程式庫可以在Arduino IDE 管理程式庫搜尋即可安裝。
程式第61行,wifiManager.autoConnect("STOCK-BOT");
你可以修改自己喜歡的名稱,在搜尋裝置 WiFi 名稱時就可以輕易地辨識。
U8g2 程式庫自訂字褲的功能很強大,基本上UTF8碼找得到的字都可以用,只是要考量裝置 Flash 的大小是否能把字庫都裝進去。
自訂圖形部分U8g2 支援XBM的格式,你可以用小畫家等繪圖軟體畫好你要的圖形,存成BMP檔,利用線上轉圖程式:XBM線上轉圖
轉檔後下載的 *.XBM 用純文字編輯器打開,裡面的程式碼貼到程式內就可以用了~~
程式碼332~355行,是透過HTTPS協議取值,取回的值是JSON格式,但是我不會使用JSON取值方式,所以就把回傳的資料當作字串處理,把一些標點符號,括號用空白字取代了,如果有高手也請幫忙教學一下吧~~~
留言列表