วันพฤหัสบดีที่ 20 เมษายน พ.ศ. 2560

Week03: Arduino IoTs – ThingSpeak Interface


Week03: Arduino IoTs – ThingSpeak Interface

ทฤษฏี เนื้อหาที่เกี่ยวข้อง


Thingspeak เป็นเว็ปที่ให้การบริการในการเก็บข้อมูล และสามารถแสดงข้อมูลแบบ real-time ได้ ซึ้งเราสามารถ update ข้อมูล หรือจะเรียกดูข้อมูลได้ตลอดเวลา ที่ไหนก็ได้ เพราะทำงานบน cloud ซึ่ง thingspeak สร้างมาเพื่อต้องการให้ตอบโจทย์ของ IoT อยู่แล้ว ส่วนข้อมูลที่เก็บอยู่บน cloud นั้นก็ขึ้นอยู่กับเราว่าจะใช้ยังไง รูปแบบไหน ในการที่จะส่งข้อมูล data ไปไว้บน cloud นั้น ทาง thingspeak ได้มี api ในการติดต่อไว้เรียบร้อยแล้ว


การทดลอง
การเปิดใช้งาน Thinkspeak
·        อันดับแรกเลย ให้ทำการสมัครสมาชิกให้เรียบร้อย
·        จากนั้นก็สร้าง channel ขึ้นมา โดยให้เรากดไปที่ My channel แล้วก็ new channel ขึ้นมา



·        หลังจากที่ new channel ขึ้นมาแล้ว ไปที่ channel setting ก็ป้อนข้อมูลเข้าไป
·        อย่าลืมเลือก þ Make Public
·        เสร็จแล้วก็กด save channel




·        เมื่อสร้างเสร็จแล้ว ก็จะแสดงหน้าต่าง ในหน้าต่างนี้จะแสดงข้อมูลเป็นแบบเส้นกราฟ ซึ่งตอนนี้ยังไม่มีข้อมูลใด ๆ ส่งมาจึงไม่เกิดอะไรขึ้น




การ update ข้อมูลไปยัง cloud ผ่าน api ของ thingspeak
·        อันดับแรกให้ดูที่ API KEY ของเราว่ามันคืออะไร
o   Write API Key               4741AGU8WGCJP0J5
o   Read API Keys            TYJ1J2AU2OV7Q0U8


·        ในการ update ข้อมูลจะเป็นการส่ง HTTP Request ไปยัง server เพื่อ update ข้อมูลที่ต้องการตาม field ต่าง ๆ ที่เรากำหนด วิธีการ update โดยเราจะส่งข้อมูลแบบนี้

https://api.thingspeak.com/update?key=4741AGU8WGCJP0J5&field1=0

ที่ขีดเส้นไว้คือ
4741AGU8WGCJP0J5     คือ API KEY
field1=0              คือ field ที่เราต้องการ update ในตัวอย่างคือ field ที่ 1 และกำหนดให้มีค่าเท่ากับ 0

·        มาดูผลลัพธ์กันที่แท็บ Private View

จากรูปตัวอย่างฝั่งขวาคือเจ้าของบล็อกได้ทำการ Update ข้อมูล โดยกำหนดให้ field ที่ 1 มีค่าเท่ากับ 0 และฝั่งซ้ายนั้นได้มีจุดเพิ่มมา 1 จุดก็คือค่าที่เราเพิ่มไปเมื่อซักครู่นั่นเอง


เขียนโปรแกรมให้กับ NodeMcu ให้ส่งข้อมูลไป update
·        Test Code on Arduino IDE

#include <ESP8266WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS D3

const char* ssid = "testVirus";                            // กำหนด SSID (อย่าลืมแก้เป็นของตัวเอง)
const char* password = "1510031510";                            // กำหนด Password(อย่าลืมแก้เป็นของตัวเอง)";

String apiKey = "4741AGU8WGCJP0J5";
const char* server = "api.thingspeak.com";

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
WiFiClient client;

void setup()
{ Serial.begin(115200);
  delay(1000);
  sensors.begin();
  Serial.println();             Serial.println();
  Serial.print("Connecting to ");   Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  { delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop()
{ Serial.println("-------------------------");
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures();                                   // Send the command to get temperatures
  float cTemp = sensors.getTempCByIndex(0);
  Serial.print("Temperature is: "); Serial.println(cTemp, 4);            // Display Temperature
  if (client.connect(server, 80))                           // "184.106.153.149" or api.thingspeak.com
  { String postStr = apiKey;
    postStr += "&field1=";
    postStr += String(cTemp);
    postStr += "\r\n\r\n";
    client.print("POST /update HTTP/1.1\n");
    client.print("Host: api.thingspeak.com\n");
    client.print("Connection: close\n");
    client.print("X-THINGSPEAKAPIKEY: " + apiKey + "\n");
    client.print("Content-Type: application/x-www-form-urlencoded\n");
    client.print("Content-Length: ");
    client.print(postStr.length());
    client.print("\n\n");
    client.print(postStr);
    Serial.print("Temperature('C)= ");
    Serial.println(cTemp, 4);
  }
  client.stop();
  Serial.println("Waiting...");
  delay(20000);                                      // thingspeak needs minimum 15 sec delay between updates
}



·        ผลการทำงาน





·       NodeMcu ส่ง http request ไปที่ server เพื่อ update ข้อมูล ซึ่งฝั่ง thingspeak ก็ทำการ update แบบ real-time และพลอตออกมาในรูปแบบกราฟ ซึ่งทาง thingspeak สามารถบันทึกข้อมูลได้ทุก ๆ 15 วินาที ซึ่งหากว่าเราสามารถบันทึกค่าข้อมูลลง cloud ได้แล้ว ก็เหมือนกับว่าเรามีค่าอะไรบางอย่างที่เราพร้อมเรียกตลอดเวลาไม่ว่าจะอยู่ที่ไหนก็ตาม

·       วิดีโอการทำงานของแหล่งข้อมูลต้นฉบับ https://youtu.be/_ZF71GhXCjc

·       ส่วนของการส่งข้อมูล ก็จะเป็นการส่งแบบ http request ซึ่งเราส่งออกไปว่า
GET /update?key=4741AGU8WGCJP0J5&field1=0 HTTP/1.1\r\n
Host: api.thingspeak.com\r\n
Connection: close\r\n\r\n


4.  การมอนิเตอร์ การสั่งงานด้วย Hercules ดังนี้
POST Data to ThinkSpeak






Hercules Setup: TCP Client
§  Module IP à api.thingspeak.com
§  Port à 80
§  Connect
§  Send à GET /update?key=4741AGU8WGCJP0J5&field1=1234 HTTP/1.1
§  Send à 0A
§  Send à Host: api.thingspeak.com
§  Send à 0A
§  Send à 0A




5.  การนำไฟล์ออกมาเพื่อทำรายงาน
·       จากข้อมูลสามารถ Export CSV ไฟล์เพื่อทำรายงานต่างๆได้





การควบคุมเปิดปิด LED ผ่าน Internet ด้วย NodeMcu
·        ต่อวงจร LED ที่ D1, D2, D5, D6 และทดสอบการทำงาน




#define LED_1 D1
#define LED_2 D2
#define LED_3 D5
#define LED_4 D6

void setup()
{ pinMode(LED_1, OUTPUT);      digitalWrite(LED_1, LOW);
  pinMode(LED_2, OUTPUT);      digitalWrite(LED_2, LOW);
  pinMode(LED_3, OUTPUT);      digitalWrite(LED_3, LOW);
  pinMode(LED_4, OUTPUT);      digitalWrite(LED_4, LOW);
}

void loop()
{ digitalWrite(LED_1, HIGH);    delay(200);      digitalWrite(LED_1, LOW);   delay(100);
  digitalWrite(LED_2, HIGH);    delay(200);      digitalWrite(LED_2, LOW);   delay(100);
  digitalWrite(LED_3, HIGH);    delay(200);      digitalWrite(LED_3, LOW);   delay(100);
  digitalWrite(LED_4, HIGH);    delay(200);      digitalWrite(LED_4, LOW);   delay(100);
}

·        วิธีที่เราจะควบคุม LED นั้นเราจำเป็นต้องมีข้อมูลเพื่อนำไปเช็คเป็นเงื่อนไขว่า ถ้าเกิดเป็นค่านี้ให้ LED เปิด ถ้าเป็นอีกค่านึงให้ LED ปิด ดังนั้นหากเราต้องการสั่งให้ LED เปิดหรือปิดผ่าน Internet นั้น เราจึงจำเป็นต้องมีข้อมูลบน cloud โดยเราจะทำการส่ง Http request ไปยัง server เพื่อร้องขอข้อมูลที่เราต้องการ ถ้าเราส่ง  http request ได้อย่างถูกต้องก็จะมี http response ตอบกลับจาก server โดยส่งมาพร้อมกับข้อมูลที่เราต้องการ และเราก็ทำการเขียนโปรแกรมเพื่อใช้ค่าที่ server ตอบกลับมานั้น มาเป็นค่าในการควบคุม LED เพื่อเปิดหรือปิดนั่นเอง





·        การ get data หรือจะเป็นการ update data เราสามารถศึกษา หรือดู Example ได้จาก Documentation จาก Support ของ ThingSpeak
·        ในส่วนของการ fill data จะใช้การเขียนทั่วไป
https://api.thingspeak.com/update?key=4741AGU8WGCJP0J5&field1=9



·        ในการ get data จะใช้ url
https://api.thingspeak.com/channels/110910/fields/1/last




o   ที่ขีดเส้นใต้ไว้นั้น   110910 คือช่อง channels ของเรา
o   ส่วน 1 คือ ต้องการดูที่ field ไหน ส่วนเจ้าของบล็อกขอเลือกดูที่ field 1 แล้วกันเพราะเจ้าของบล็อกบันทึกค่าไว้ที่ field นี้
o   ค่า response ตอบกลับมาเป็นเลข 9 นั่นก็คือค่าที่เราต้องการที่จะทราบว่า ค่าที่บันทึกล่าสุดของ field ที่ 1 คือค่าอะไร ก็มีการ response กลับมาเป็นค่าดังกล่าว

·        ตัวอย่างนี้เป็นเพียงการทดสอบดูว่าสามารถ get ค่าจาก server ได้หรือไม่ ต่อมาเราก็มาดูกันว่า เราจะเขียนให้ NodeMcu ของเรานั้นไป get ค่ามาแล้วมาควบคุม LED ได้อย่างไร

#include <ESP8266WiFi.h>
#define LED_1 D1
#define LED_2 D2
#define LED_3 D5
#define LED_4 D6

const char* ssid = "testVirus";    // กำหนด SSID (อย่าลืมแก้เป็นของตัวเอง)
const char* password = "1510031510";    // กำหนด Password(อย่าลืมแก้เป็นของตัวเอง)";

const char* host = "api.thingspeak.com"; // ชื่อ host ที่ต้องการติดต่อ
const char* url = "/channels/110910/fields/1/last"; // path ที่ต้องการเรียก

String line = ""; // ตัวแปรนี้จะเก็บค่าทั้งหมดที่อ่านได้จาก Server

const int PORT = 80; // ใช้ port 80

WiFiServer server(80); // สร้าง object จาก คลาส WiFiServer กำหนด port 80

void setup() {
  Serial.begin(115200); //ใช้ buadrate 115200
  delay(10);

  pinMode(LED_1, OUTPUT); //
  pinMode(LED_2, OUTPUT);  //
  pinMode(LED_3, OUTPUT);  // กำหนด ขา ให้เป็น ขา OUTPUT
  pinMode(LED_4, OUTPUT); //

  Serial.println("\n\n");
  Serial.print("Connecting to ");
  Serial.println(ssid);  // แสดงข้อความออกทาง Serial ว่า Connecting to (SSID ที่เชื่อมต่อ)
  WiFi.begin(ssid, password);  // เชื่อมต่อ wifi จาก SSID และ password ที่เรากำหนด

  while (WiFi.status() != WL_CONNECTED) { // ถ้าหากยังเชื่อมต่อกับ wifi ยังไม่ได้ก็จะแสดงข้อความ ... ไปเรื่อย ๆ
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi Connected!!");
  Serial.print("IP address : ");
  Serial.println(WiFi.localIP());  // แสดง IP address ที่ได้รับจาก wifi ที่เราเชื่อมต่อ
}

void loop()
{ delay(5000);
  WiFiClient client = server.available();
  Serial.print("Connecting to ");     Serial.println(host);
  Serial.print("Requesting URL : ");  Serial.println(url);

  if (!client.connect(host, PORT))
  { Serial.print(".");
    return;
  }
  else
  { client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
    delay(50);
    Serial.println("SEND !!");  // เมื่อส่งไปแล้วให้แสดงข้อความ SEND !! ออกมา
  }
  while (!client.available())  //ถ้าหากไม่มี response ตอบกลับมา ก็จะให้วนลูป จนกว่าจะมี responce ตอบกลับมา
  {}

  while (client.available())  // และเมื่อมี response ตอบกลับมาก็จะให้เข้า loop while แล้วทำการเก็บค่าทั้งหมดไว้ที่ line
  { line += (char)client.read();
  }

  Serial.println();
  Serial.println("GET SUCCESSFULLY !!");
  Serial.print("###### rawData  >> ");  Serial.println(line);
  int lengthData = line.length();
  char data = line[lengthData - 1];
  String s = (String)"Position " + lengthData + (String)", Data " + data;
  Serial.print("###### splitted >> ");  Serial.println(s);

  switch (data) { 
    case '1':
      digitalWrite(LED_1, HIGH); // ถ้าเป็น 1 led1 ติด
      break;
    case '2':
      digitalWrite(LED_1, LOW); // ถ้าเป็น 2 led1 ดับ
      break;
    case '3':
      digitalWrite(LED_2, HIGH); // ถ้าเป็น 3 led2 ติด
      break;
    case '4':
      digitalWrite(LED_2, LOW); // ถ้าเป็น 4 led2 ดับ
      break;
    case '5':
      digitalWrite(LED_3, HIGH); // ถ้าเป็น 5 led3 ติด
      break;
    case '6':
      digitalWrite(LED_3, LOW); // ถ้าเป็น 6 led3 ดับ
      break;
    case '7':
      digitalWrite(LED_4, HIGH); // ถ้าเป็น 7 led4 ติด
      break;
    case '8':
      digitalWrite(LED_4, LOW); // ถ้าเป็น 8 led4 ดับ
      break;
    case '9':
      digitalWrite(LED_1, HIGH); // ถ้าเป็น 9 All LED On
      digitalWrite(LED_2, HIGH);
      digitalWrite(LED_3, HIGH);
      digitalWrite(LED_4, HIGH);
      break;
    case '0':
      digitalWrite(LED_1, LOW); // ถ้าเป็น 0 All LED Off
      digitalWrite(LED_2, LOW);
      digitalWrite(LED_3, LOW);
      digitalWrite(LED_4, LOW);
      break;
  }
  line = ""; // เคลียข้อมูลในตัวแปร line ให้เท่ากับ "" เพื่อรอรับค่าใหม่ใน loop หน้า
  Serial.println();
  Serial.println("Closing Connection"); 
}



·       เป็นการ get ค่าจาก Server โดยส่ง http request ขอดูในข้อมูลล่าสุดของ field 1 ถ้าส่ง request ถูกต้อง Server ก็จะส่ง http response กลับมาเป็นค่าที่เราต้องการ
·       เมื่อได้ค่าที่ต้องการแล้วก็ทำการ substring ก็คือตัดเอาข้อความเอาเฉพาะที่จำเป็นมาใช้งาน
·       วิดีโอการทำงานของแหล่งข้อมูลต้นฉบับ https://youtu.be/wUZR-jAaeKs


7.  การมอนิเตอร์ การสั่งงานด้วย Hercules ดังนี้
POST Data to ThinkSpeak



GET Data from ThinkSpeak





TCP Client
§  Module IP à api.thingspeak.com
§  Port à 80
§  Connect
§  Send à GET /channels/110910/fields/1/last HTTP/1.1
§  Send à 0A
§  Send à Host: api.thingspeak.com
§  Send à 0A
§  Send à 0A




คำถามท้ายการทดลอง



เอกสาร แหล่งข้อมูลอ้างอิง


โปรแกรมทดสอบ
Example1 – Read Weather Station
§  Request – SSID, Password
/*
  ReadWeatherStation

  Reads the latest weather data every 60 seconds from the public MathWorks
  weather station in Natick, MA https://thingspeak.com/channels/12397 on ThingSpeak.

  ThingSpeak ( https://www.thingspeak.com ) is an analytic IoT platform service that allows you to aggregate, visualize and
  analyze live data streams in the cloud.

  Copyright 2017, The MathWorks, Inc.

  Documentation for the ThingSpeak Communication Library for Arduino is in the extras/documentation folder where the library was installed.
  See the accompaning licence file for licensing information.
*/

#include <ESP8266WiFi.h>
#include "ThingSpeak.h"

WiFiClient  client;
char ssid[] = "testVirus";    //  your network SSID (name)
char pass[] = "1510031510";   // your network password

/*
  This is the ThingSpeak channel number for the MathwWorks weather station
  https://thingspeak.com/channels/12397.  It senses a number of things and puts them in the eight
  field of the channel:
  Field 1 - Wind Direction (degrees where 0 is North)
  Field 2 - Wind Speed (MPH)
  Field 3 - Humidity (%RH)
  Field 4 - Temperature (Degrees F)
  Field 5 - Rainfall (inches since last measurement)
  Field 6 - Atmospheric Pressure (inHg)
*/
unsigned long weatherStationChannelNumber = 12397;

void setup()
{ Serial.begin(9600);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED)
  { delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  ThingSpeak.begin(client);
}

void loop() {
  float windDirection = ThingSpeak.readFloatField(weatherStationChannelNumber, 1);
  float windSpeed = ThingSpeak.readFloatField(weatherStationChannelNumber, 2);
  float humidity = ThingSpeak.readFloatField(weatherStationChannelNumber, 3);
  float temperature = ThingSpeak.readFloatField(weatherStationChannelNumber, 4);
  float rainfall = ThingSpeak.readFloatField(weatherStationChannelNumber, 5);
  float pressure = ThingSpeak.readFloatField(weatherStationChannelNumber, 6);

  Serial.println("======================================");
  Serial.println("Current weather conditions in Natick: ");
  Serial.print(temperature);                      Serial.print(" degrees F, ");
  Serial.print(humidity);                           Serial.println("% humidity");
  Serial.print("Wind at ");                         Serial.print(windSpeed);
  Serial.print(" MPH at ");                         Serial.print(windDirection);
  Serial.println(" degrees");                      Serial.print("Pressure is ");
  Serial.print(pressure);                            Serial.print(" inHg");
  if (rainfall > 0)
  {
    Serial.print(", and it's raining");
  }
  Serial.println();

  delay(60000); // Note that the weather station only updates once a minute

}



Example2 – Write Voltage to thingspeak
§  Request – SSID, Password
§  Request – thingspeak Channel, thingspeak API Write Key
/*
  WriteVoltage

  Reads an analog voltage from pin 0, and writes it to a channel on ThingSpeak every 20 seconds.

  ThingSpeak ( https://www.thingspeak.com ) is an analytic IoT platform service that allows you to aggregate, visualize and
  analyze live data streams in the cloud.

  Copyright 2017, The MathWorks, Inc.

  Documentation for the ThingSpeak Communication Library for Arduino is in the extras/documentation folder where the library was installed.
  See the accompaning licence file for licensing information.
*/

#include <ESP8266WiFi.h>
#include "ThingSpeak.h"

WiFiClient  client;

/*
  *****************************************************************************************
  **** Visit https://www.thingspeak.com to sign up for a free account and create
  **** a channel.  The video tutorial http://community.thingspeak.com/tutorials/thingspeak-channels/
  **** has more information. You need to change this to your channel, and your write API key
  **** IF YOU SHARE YOUR CODE WITH OTHERS, MAKE SURE YOU REMOVE YOUR WRITE API KEY!!
  *****************************************************************************************/
unsigned long myChannelNumber = 110910;
const char * myWriteAPIKey = "CC9IVV278SU09QFL";
char ssid[] = "testVirus";    //  your network SSID (name)
char pass[] = "1510031510";   // your network password

void setup()
{ Serial.begin(9600);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED)
  { delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  ThingSpeak.begin(client);
}

void loop()
{ float voltage;
  Serial.print("Data is = ");
  for (int i = 0; i < 15; i++)// ThingSpeak will only accept updates every 15 seconds.
  { int sensorValue = analogRead(A0); // read the input on analog pin 0:
    voltage = sensorValue * 3.3 / 1023.0; // 0 - 1023 maps to 0 - 3.3 volts
    Serial.print(voltage, 2);
    Serial.print(",");
    delay(1000);
  }
  // Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different
  // pieces of information in a channel.  Here, we write to field 1.
  ThingSpeak.writeField(myChannelNumber, 1, voltage, myWriteAPIKey);
  Serial.print("\nSend Data to Thinkspeak = ");
  Serial.println(voltage);
}





Example2 – Read private channel from thingspeak
§  Request – SSID, Password
§  Request – thingspeak Channel, thingspeak API Write Key, thingspeak API Read Key

/*
  ReadPrivateChannel

  Reads the latest voltage value from a private channel on ThingSpeak every 30 seconds
  and prints to the serial port debug window.

  For an example of how to read from a public channel, see ReadChannel example

  ThingSpeak ( https://www.thingspeak.com ) is an analytic IoT platform service that allows you to aggregate, visualize and
  analyze live data streams in the cloud.

  Copyright 2017, The MathWorks, Inc.

  Documentation for the ThingSpeak Communication Library for Arduino is in the extras/documentation folder where the library was installed.
  See the accompaning licence file for licensing information.
*/


#include <ESP8266WiFi.h>
#include "ThingSpeak.h"
WiFiClient  client;

/*
  *****************************************************************************************
  **** Visit https://www.thingspeak.com to sign up for a free account and create
  **** a channel.  The video tutorial http://community.thingspeak.com/tutorials/thingspeak-channels/
  **** has more information. You need to change this to your channel, and your read API key
  **** IF YOU SHARE YOUR CODE WITH OTHERS, MAKE SURE YOU REMOVE YOUR READ API KEY!!
  *****************************************************************************************

  This is the ThingSpeak channel used in the write examples (31461).  It is private, and requires a
  read API key to access it.  We'll read from the first field.
*/
unsigned long myChannelNumber = 110910;
const char * myWriteAPIKey    = "CC9IVV278SU09QFL";
const char * myReadAPIKey     = "TYJ1J2AU2OV7Q0U8";
char ssid[] = "testVirus";    //  your network SSID (name)
char pass[] = "1510031510";   // your network password

void setup()
{ Serial.begin(9600);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED)
  { delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  ThingSpeak.begin(client);
}

void loop() {
  // Read the latest value from field 1 of channel 31461
  float voltage = ThingSpeak.readFloatField(myChannelNumber, 1, myReadAPIKey);

  Serial.print("Latest voltage is: ");
  Serial.print(voltage);
  Serial.println("V");

  delay(30000);
}