close

繼續上一篇~

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();   //時:分整數, 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個地方要輸入你的資料,然後上傳就可以了~~

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

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

 

arrow
arrow
    創作者介紹
    創作者 terrywu5 的頭像
    terrywu5

    IoT 智慧生活的異想世界

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