2022. 11. 22. 21:48ㆍ개발/Google
유튜브 링크
https://www.youtube.com/watch?v=ENuUDIMJ7fs
Google에서 제공하는 Google Home app을 사용해서 PLC를 제어한다.
위 어플에서 원격으로 PLC를 제어할 수 있도록 연동한다.
Google Action
https://console.actions.google.com/u/0/
1. Google action으로 이동
2. 새 프로젝트 생성
3.Smart Home 선택 후 Start Building
4. Build 되면 Quick setup에서 'Name your Smart Home action' 클릭
5. Develop / Invocation 에서 이름 설정
이 이름은 Google Home 앱에서 사용할 이름
6. Develop / Actions 에서 Fulfillment URL 입력
https://us-central1-<project-id>.cloudfunctions.net/smarthome
<project-id> 대신 해당 프로젝트 id로 수정
7. Develop / Account linking 에서 인증값 입력
공식 프로젝트가 아닌 개인이 사용하는 테스트 프로젝트이므로
Auth, Token 등 인증서들을 default 값으로 설정
- Client ID : ABC123
- Client secret : DEF456
- Auth URL : https://us-central1-<project-id>.cloudfunctions.net/fakeauth
- Token URL : https://us-central1-<project-id>.cloudfunctions.net/faketoken
Google Cloud Platform
https://console.cloud.google.com/
1. Google Action에서 생성했던 프로젝트를 선택
2. 메뉴에서 APIs & Services > Library 로 이동
3. Homegraph 검색 후 ENABLE 버튼 클릭
4. APIs & Services > Credentials 로 이동 후
인증서를 Service account로 생성
5. Service name을 지정 후 DONE 클릭
6. 생성된 Service Accounts 클릭
7. Keys 로 이동 후 JSON 형식의 키 생성
다운받은 키 파일명을 smart-home-key.json 로 수정
Google Home 앱
등록한 Google action을 클릭하면 연동된다.
연동이 되지 않고 오류가 발생하면
Google action 의 이름을 변경해주면 해결된다.
Firebase Deploy
https://github.com/choiprin/Arduino/tree/main/Google%20Smart%20Home
1. 위 github링크에 있는 폴더를 다운받은 후
functions 디렉토리에 방금 다운받은 smart-home-key.json 파일 이동
2. 다운받은 경로에 맞춰서 Google Smart Home/firebase/functions 디렉토리로 이동
3. 디렉토리에서 cmd 실행 및 명령어 순서대로 입력
npm install -g firebase-tools
npm install
firebase login
firebase projects:list
4. 리스트를 확인 후
firebase use project-id
5. project-id 대신 사용할 프로젝트의 id를 입력 후 실행
firebase deploy
5. Firebase 에 배포
"engines": {
"node": "10"
}
6. firebase deploy 단계에서 에러 발생 시
해당 디렉토리의 package.json Node버전을 10으로 변경
위와 같이 firebase 의 Realtime database에 데이터가 입력됨
Arduino
//FirebaseESP8266.h must be included before ESP8266WiFi.h
#include "FirebaseESP8266.h"
#include <ESP8266WiFi.h>
#include <SoftwareSerial.h>
#include "CRC.h"
#define FIREBASE_HOST " firebase Host 값 입력 "
#define FIREBASE_AUTH " firebase Auth 값 입력 " // 자세한 내용은 이전영상 참고
#define WIFI_SSID " Wifi SSID 입력 "
#define WIFI_PASSWORD " Wifi PW 입력 "
//#define WIFI_SSID "mecha1203"
//#define WIFI_PASSWORD "mecha1203"
//Define FirebaseESP8266 data object
FirebaseData firebaseData1;
FirebaseData firebaseData2;
FirebaseData firebaseData;
unsigned long sendDataPrevMillis = 0;
String path = "/6/OnOff";
uint16_t count = 0;
SoftwareSerial mySerial(D7, D4); // RX,Tx
int act=0,outPlc=0;
int Out[8]={0},In[10]={0}; // plc 입력과 출력 저장
String inputString = ""; // 받은 문자열
String sIn="",sInPre=""; // 입력값이 달라질 때만 mqtt로 송신
unsigned long previousMillis = 0;
const long interval = 1000;
void doTick();
void printResult(FirebaseData &data);
void printResult(StreamData &data);
void outResult(StreamData &data);
void crd16Rtu();
void plcInData();
void serialEvent();
void streamCallback(StreamData data)
{
Serial.println("Stream Data1 available...");
Serial.println("STREAM PATH: " + data.streamPath());
Serial.println("EVENT PATH: " + data.dataPath());
Serial.println("DATA TYPE: " + data.dataType());
Serial.println("EVENT TYPE: " + data.eventType());
Serial.print("VALUE: ");
printResult(data);
Serial.println();
outResult(data);
}
void streamTimeoutCallback(bool timeout)
{
if (timeout)
{
Serial.println();
Serial.println("Stream timeout, resume streaming...");
Serial.println();
}
}
void setup()
{
Serial.begin(19200);
mySerial.begin(19200);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(300);
}
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP());
Serial.println();
Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
Firebase.reconnectWiFi(true);
//Set the size of WiFi rx/tx buffers in the case where we want to work with large data.
firebaseData1.setBSSLBufferSize(1024, 1024);
//Set the size of HTTP response buffers in the case where we want to work with large data.
firebaseData1.setResponseSize(1024);
//Set the size of WiFi rx/tx buffers in the case where we want to work with large data.
firebaseData2.setBSSLBufferSize(1024, 1024);
//Set the size of HTTP response buffers in the case where we want to work with large data.
firebaseData2.setResponseSize(1024);
if (!Firebase.beginStream(firebaseData1, path))
{
Serial.println("------------------------------------");
Serial.println("Can't begin stream connection...");
Serial.println("REASON: " + firebaseData1.errorReason());
Serial.println("------------------------------------");
Serial.println();
}
Firebase.setStreamCallback(firebaseData1, streamCallback, streamTimeoutCallback);
plcInData();
}
void loop()
{
doTick();
serialEvent();
}
//1초 마다 실행되는 시간함수
void doTick() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
crd16Rtu();
}
}
void printResult(FirebaseData &data)
{
if (data.dataType() == "int")
Serial.println(data.intData());
else if (data.dataType() == "float")
Serial.println(data.floatData(), 5);
else if (data.dataType() == "double")
printf("%.9lf\n", data.doubleData());
else if (data.dataType() == "boolean")
Serial.println(data.boolData() == 1 ? "true" : "false");
else if (data.dataType() == "string")
Serial.println(data.stringData());
else if (data.dataType() == "json")
{
Serial.println();
FirebaseJson &json = data.jsonObject();
//Print all object data
Serial.println("Pretty printed JSON data:");
String jsonStr;
json.toString(jsonStr, true);
Serial.println(jsonStr);
Serial.println();
Serial.println("Iterate JSON data:");
Serial.println();
size_t len = json.iteratorBegin();
String key, value = "";
int type = 0;
for (size_t i = 0; i < len; i++)
{
json.iteratorGet(i, type, key, value);
Serial.print(i);
Serial.print(", ");
Serial.print("Type: ");
Serial.print(type == FirebaseJson::JSON_OBJECT ? "object" : "array");
if (type == FirebaseJson::JSON_OBJECT)
{
Serial.print(", Key: ");
Serial.print(key);
}
Serial.print(", Value: ");
Serial.println(value);
}
json.iteratorEnd();
}
else if (data.dataType() == "array")
{
Serial.println();
//get array data from FirebaseData using FirebaseJsonArray object
FirebaseJsonArray &arr = data.jsonArray();
//Print all array values
Serial.println("Pretty printed Array:");
String arrStr;
arr.toString(arrStr, true);
Serial.println(arrStr);
Serial.println();
Serial.println("Iterate array values:");
Serial.println();
for (size_t i = 0; i < arr.size(); i++)
{
Serial.print(i);
Serial.print(", Value: ");
FirebaseJsonData &jsonData = data.jsonData();
//Get the result data from FirebaseJsonArray object
arr.get(jsonData, i);
if (jsonData.typeNum == FirebaseJson::JSON_BOOL)
Serial.println(jsonData.boolValue ? "true" : "false");
else if (jsonData.typeNum == FirebaseJson::JSON_INT)
Serial.println(jsonData.intValue);
else if (jsonData.typeNum == FirebaseJson::JSON_DOUBLE)
printf("%.9lf\n", jsonData.doubleValue);
else if (jsonData.typeNum == FirebaseJson::JSON_STRING ||
jsonData.typeNum == FirebaseJson::JSON_NULL ||
jsonData.typeNum == FirebaseJson::JSON_OBJECT ||
jsonData.typeNum == FirebaseJson::JSON_ARRAY)
Serial.println(jsonData.stringValue);
}
}
}
void printResult(StreamData &data)
{
if (data.dataType() == "int")
Serial.println(data.intData());
else if (data.dataType() == "float")
Serial.println(data.floatData(), 5);
else if (data.dataType() == "double")
printf("%.9lf\n", data.doubleData());
else if (data.dataType() == "boolean")
Serial.println(data.boolData() == 1 ? "true" : "false");
else if (data.dataType() == "string")
Serial.println(data.stringData());
else if (data.dataType() == "json")
{
Serial.println();
FirebaseJson *json = data.jsonObjectPtr();
//Print all object data
Serial.println("Pretty printed JSON data:");
String jsonStr;
json->toString(jsonStr, true);
Serial.println(jsonStr);
Serial.println();
Serial.println("Iterate JSON data:");
Serial.println();
size_t len = json->iteratorBegin();
String key, value = "";
int type = 0;
for (size_t i = 0; i < len; i++)
{
json->iteratorGet(i, type, key, value);
Serial.print(i);
Serial.print(", ");
Serial.print("Type: ");
Serial.print(type == FirebaseJson::JSON_OBJECT ? "object" : "array");
if (type == FirebaseJson::JSON_OBJECT)
{
Serial.print(", Key: ");
Serial.print(key);
}
Serial.print(", Value: ");
Serial.println(value);
}
json->iteratorEnd();
}
else if (data.dataType() == "array")
{
Serial.println();
//get array data from FirebaseData using FirebaseJsonArray object
FirebaseJsonArray *arr = data.jsonArrayPtr();
//Print all array values
Serial.println("Pretty printed Array:");
String arrStr;
arr->toString(arrStr, true);
Serial.println(arrStr);
Serial.println();
Serial.println("Iterate array values:");
Serial.println();
for (size_t i = 0; i < arr->size(); i++)
{
Serial.print(i);
Serial.print(", Value: ");
FirebaseJsonData *jsonData = data.jsonDataPtr();
//Get the result data from FirebaseJsonArray object
arr->get(*jsonData, i);
if (jsonData->typeNum == FirebaseJson::JSON_BOOL)
Serial.println(jsonData->boolValue ? "true" : "false");
else if (jsonData->typeNum == FirebaseJson::JSON_INT)
Serial.println(jsonData->intValue);
else if (jsonData->typeNum == FirebaseJson::JSON_DOUBLE)
printf("%.9lf\n", jsonData->doubleValue);
else if (jsonData->typeNum == FirebaseJson::JSON_STRING ||
jsonData->typeNum == FirebaseJson::JSON_NULL ||
jsonData->typeNum == FirebaseJson::JSON_OBJECT ||
jsonData->typeNum == FirebaseJson::JSON_ARRAY)
Serial.println(jsonData->stringValue);
}
}
}
void outResult(StreamData &data)
{
int onValue=1;
int noPlc=0;
if (data.dataType() == "int"){
onValue=data.intData();
}
else if (data.dataType() == "string") {
Serial.println("string data");
if(data.stringData() != "1")
onValue=0;
Serial.println(data.stringData());
}
else if (data.dataType() == "json")
{
Serial.println("json data");
FirebaseJson *json = data.jsonObjectPtr();
String jsonStr;
json->toString(jsonStr, true);
FirebaseJsonData jsonObj;
json->get(jsonObj,"on");
onValue=jsonObj.intValue;
//json->get(jsonObj,"no");
//noPlc=jsonObj.intValue;
}
Serial.println("---data received---");
//Serial.println(noPlc);
Serial.println(onValue);
Serial.println("-------------");
outPlc=1;
noPlc=1;
Out[noPlc]=onValue;
crd16Rtu();
}
// 아두이노에서 RS485 출력을 내보낸다.
void crd16Rtu() {
String s;
int si,sj,len;
char str[24];
if(outPlc == 1) { //출력
//str[24] = {0x00,0x0f,0x00,0x00,0x00,0x0a,0x02,0xff,0x00,0x00,0x00}; //비트연속출력 len=9
str[0]=0x01; str[1]=0x0f; str[2]=0x00; str[3]=0x40; str[4]=0x00;
str[5]=0x0a; str[6]=0x02; str[7]=0xff; str[8]=0x00; str[9]=0x00; str[10]=0x00;
len=9;
str[7]=Out[0]+Out[1]*2+Out[2]*4+Out[3]*8;
outPlc=0;
}
else { //입력
//str[24] = {0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x00}; // 비트 입력영역 읽기 len=6
str[0]=0x01; str[1]=0x02; str[2]=0x00; str[3]=0x00; str[4]=0x00;
str[5]=0x08; str[6]=0x00; str[7]=0x00;
len=6;
}
inputString = "";
uint8_t * data = (uint8_t *) &str[0];
si=crc16(data, len, 0x8005, 0xFFFF, 0x0000, true, true );
sj=si&0xff;
str[len]=sj;
sj=si>>8;
str[len+1]=sj;
for(int i=0;i<len+2;i++)
mySerial.print(str[i]);
}
void serialEvent() {
if(mySerial.available() == false)
return;
while (mySerial.available()) {
// get the new byte:
char inChar = (char)mySerial.read();
//Serial.print(inChar,HEX);
// add it to the inputString:
inputString += inChar;
}
//Serial.println("");
if(outPlc!=1 && inputString.length() >= 6) {
int b=1;
sIn="";
for(int i=1;i<=6;i++) {
int c=inputString.charAt(3)&b;
if(c!=0)
c=0x01;
In[i-1]=c;
sIn+=c;
//Serial.print(c,HEX);
//Serial.print(" ");
b*=2;
}
inputString="";
if(sIn!=sInPre) {
Serial.println(sIn);
plcInData();
}
sInPre=sIn;
}
}
void plcInData() {
//FirebaseData firebaseData;
String pathIn = "read";
String jsonStr = "";
FirebaseJson json1;
FirebaseJsonData jsonObj;
json1.set("led0", In[0]);
json1.set("led1", In[1]);
json1.set("led2", In[2]);
json1.set("led3", In[3]);
json1.set("led4", In[4]);
json1.set("led5", In[5]);
json1.set("led6", In[6]);
json1.set("led7", In[7]);
json1.toString(jsonStr, true);
if (Firebase.set(firebaseData, pathIn, json1))
Serial.println("Sucess");
else
Serial.println("FAILED");
}
String path = "/6/OnOff";
if (!Firebase.beginStream(firebaseData1, path))
{
Serial.println("------------------------------------");
Serial.println("Can't begin stream connection...");
Serial.println("REASON: " + firebaseData1.errorReason());
Serial.println("------------------------------------");
Serial.println();
}
Firebase 를 확인해보면 6/OnOff 가 true/false 로 토글된다.
따라서 아두이노 소스에서도 같은 경로를 설정해서
값을 받아오도록 했다.
Google Home -> Firebase Realtime Database 값 set
Arduino -> Firebase Realtime Database 실시간 구독
'개발 > Google' 카테고리의 다른 글
[Google] Dialogflow messenger (0) | 2022.11.19 |
---|