ESP32 から Google Sheet に放射線量をアップ(放射線測定マップの作成 Ⅴ)

「安価な中華ガイガーカウンターをIoT化(放射線測定マップの作成 Ⅰ)」
https://qiita.com/nanbuwks/items/7ee0bf85be3ff3730405

では、測定値をスマホで閲覧したりガイガーカウンターを WiFi に接続したりして、最後に以下のように記しました。

> ## ここから先
> サーバにデータを保管したりするのは上記のプログラムを元にがんばってみよう！

「ここから先」をやってみます。

Google Spreadsheet  にテンプレートを作ってみました。

https://docs.google.com/spreadsheets/d/1SSg2Jcz3jP_HCPYQoH5F1X9ZNy8kx5VZK17pr6CSsUc/edit?pli=1#gid=0

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/ee70d7b1-ee7e-74f0-b06b-f62169c9d04a.png)

この、テンプレートをコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/444c988b-4eeb-5e12-a143-d0ffa6188a88.png)

名前を自分のわかりやすい名前にして、「コピーを作成」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/3ca724a4-aa5d-eae8-82c4-50358089a708.png)

「data」シートに入っているサンプルデータを消します。
1〜9行目の間には何を書いても構いません。
ここに写真や説明などを入力しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/12ff99a8-ca4c-7c2b-b1ee-722ab23bf082.png)

:::note
テンプレートには、「説明」「data」「changelog」のシートがあります。
「説明」、「cgabgelog」  シートは間違い防止の為削除しておくのがいいでしょう。
:::
:::note warn
また、「data」シートはIDを 0 に設定しています。コピーや新規作成したシートだと IDが 別の値になってしまいますので、0 の ID のシートをそのままお使いください。
シートIDは、シートを表示したときの URL の末尾に `gid=` の形で表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/3320a6cb-749b-a463-a0d5-4a2c5430ec9c.png)

:::

# スクリプトを設定する

URL の、この部分がシートのIDになります。これをコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/1d370776-1f40-3cb8-32b1-3f18ea59347d.png)

Apps Script を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/184ea4fd-e46c-86f5-4636-19ba4052d375.png)

設定されているスクリプトは以下の通りです。


```
function doGet(e) {
    let id = '  -- please replace your sheet ID -- ';
    let sheetName = 'data'
    var result;
    // e.parameter from GET request
    if (e.parameter == undefined) {
        result = 'Parameter undefined';
    } else {
        var sheet = SpreadsheetApp.openById(id).getSheetByName(sheetName);
        const testRange = sheet.getRange("C11:D11");
        var onedata = [];
        testRange.insertCells(SpreadsheetApp.Dimension.ROWS);
        onedata[0] = new Date();
        onedata[1] = e.parameter.value;
        sheet.getRange(11,3).setValue(onedata[0]);
        sheet.getRange(11,4).setValue(onedata[1]);
    }
    return ContentService.createTextOutput('saved');   
}

```

ここの '  -- please replace your sheet ID --  ' を コピーしたIDに入れ替えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/3f11a35f-97d8-5eb4-5499-a3d8853ab3c4.png)
↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/330c380a-765c-e144-6f41-94d04b9c0a73.png)

「デプロイ」-「新しいデプロイ」して

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/5bbfbd0b-2a50-b6de-cddc-e0651ed68561.png)

種類を「ウェブアプリ」とします。説明は適当に。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/56d55737-0741-53cf-983b-496e9f6f004e.png)
実行ユーザーは自分として、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/e95f373e-e081-7787-4b14-c428c7550eed.png)

アクセスできるユーザを「全員」として「デプロイ」します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/4a9162ac-87ce-0b04-a5af-76bc91afe6a2.png)

「アクセスを承認」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/8ffdb831-96e5-7923-e35a-677ecb74019d.png)

URLを知っている人は無条件でスクリプトを実行できる危険性があるので、この後に出るURLは自分だけが知るようにしないといけません。「Advanced」を押して許可をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/ded2b734-d1ac-b7f9-ed73-8efe478cd85b.png)
「Allow」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/ecd73cd4-0f5b-0457-408f-2f2d9255e1df.png)



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/153f566e-3237-7c5f-6e3f-d12689155519.png)

この、スクリプト実行 URL をコピーしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/3e0162f9-5dcd-875b-ff83-c7ab2abdd06e.png)

# スクリプトを試しに実行する


先程のURLが、例えば `https://script.google.com/macros/s/hogehogefugafuga/exec`
とすると、後ろに `?value=222` を追加して
`https://script.google.com/macros/s/hogehogefugafuga/exec?value=222` をブラウザで開きます。


C11 に実行日時、D11 に`222`と入力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/5430fb3b-9155-616e-8368-2d6f71fcd965.png)

## トラブルシューティング

以下のようになったときは、スクリプトのファンクション名が doGet になっていないので直します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/553e863b-5f95-13da-ec71-900fcc83c4a6.png)


# ESP32 からデータを投げる

先の内容を少し変更して、このようにしました。
```
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <SPIFFS.h>
#include <FS.h>

const int WiFiSetPin = 16; // GPIO16  push with boot : AP mode on
const char* settings = "/wifi_settings.txt";
// AP mode WiFi Setting
const char* defaultWifiMode = "APMode";
const char* defaultSsid = "ORION";
const char* defaultEssKey = "11111111";
String wifiMode;
String ssid;
String essKey;
String channel;
String AppScriptURL=" -- Please replace your script URL --";
char *sensorModeStr;
int WiFiSet = 0;  // 0 ... normal operation / 1 ... WiFi Setting Mode
int count;
int beforecpm,cpm;
float microSvph;

WebServer server(80);

void handleRoot(){
  char temp[500];
  snprintf ( temp, 500,
"<html>\
<head>\
<title>Geiger Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1> %f uSv/h (%d cpm )</h1>\
<br>%d :geiger count in %d sec.\
<hr> \
<a href=\"\/wifiInput\">wifi settings</a> \
</body>\
</html>",microSvph,cpm,count,millis()/1000);
  server.send ( 200, "text/html", temp );
}

void wifiInput() {
  String html = "";
  html += "<h1>WiFi Settings</h1>";
  html += "<form method='post' action='/wifiSet'>";
  html += "  <br>";
  html += "  <input type='text' name='essid' >ESSID<br>";
  html += "  <input type='text' name='key' >KEY(pass)<br>";
  html += "  <br>";
  html += "  <input type='submit'><br>";
  html += "</form>";
  server.send(200, "text/html", html);
}

void wifiSet() {
  String wifiMode = "clientMode";
  String setSsid = server.arg("essid");
  String setKey = server.arg("key");
  String setChannel = server.arg("0");

  File f = SPIFFS.open(settings, "w");
  f.println(wifiMode);
  f.println(setSsid);
  f.println(setKey);
  f.println(channel);
  f.close();

  String html = "";
  html += "<h1>WiFi Settings</h1>";
  html += "ESSID:" + setSsid + "<br>";
  html += "key(pass):" + setKey + "<br>";
  html += "<hr>";
  html += "<h1>Please Reset!</h1>";

  server.send(200, "text/html", html);
}

void handleNotFound(){
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  server.send(404, "text/plain", message);
}
void googleSheet(){
    HTTPClient http;
    String URL = AppScriptURL;
    URL += "?";
    URL += "value=";
    URL += microSvph;

    Serial.println("HTTP access to:");
    Serial.println(URL);
    http.begin(URL);
    int httpCode = http.GET();
    if(httpCode > 0) {
        Serial.println(httpCode);
        if(httpCode == HTTP_CODE_OK) {
            String payload = http.getString();
            Serial.println(payload);
        }
    } else {
        Serial.println(http.errorToString(httpCode).c_str());
    }

}

void setup(void){

  pinMode(27, INPUT);
    
  Serial.begin(115200);
  Serial.println("");
  Serial.println("ESXAR Program Start");
  delay(1000);
  pinMode ( WiFiSetPin, INPUT_PULLUP );
  WiFiSet=digitalRead(WiFiSetPin);
//   init filesystem
  bool res = SPIFFS.begin(true); // FORMAT_SPIFFS_IF_FAILED
  if (!res) {
      Serial.println("SPIFFS.begin fail");
  }
  // check port
  int i;
  delay(1000);
  // settings read
  if ((1 ==digitalRead(WiFiSetPin)) && (1 ==WiFiSet)){
    WiFiSet=1; // boot on default  
  } else {
    WiFiSet=0;
  }
  // set file read
  File fp = SPIFFS.open(settings, "r");
  if (!fp) {
      Serial.println("open error");
  }
  wifiMode = fp.readStringUntil('\n'); // always "clientMode"
  ssid = fp.readStringUntil('\n');
  essKey = fp.readStringUntil('\n');
  channel = fp.readStringUntil('\n');  // no use
  Serial.print("wifiMode:");
  Serial.println(wifiMode);
  Serial.print("ssid:");
  Serial.println(ssid);
  Serial.print("essKey:");
  Serial.println(essKey);
  fp.close();
  wifiMode.trim();
  ssid.trim();
  essKey.trim();
  channel.trim();
  if ( 0 == wifiMode.compareTo("clientMode") ){
  } else if ( 0 == wifiMode.compareTo("APMode") ) {
  } else {
       Serial.print("SPIFFS data seems clash. Default load...");
       wifiMode=defaultWifiMode;
       ssid=defaultSsid;
       essKey=defaultEssKey;
       File f = SPIFFS.open(settings, "w");
       f.println(wifiMode);
       f.println(ssid);
       f.println(essKey);
       f.close();
  }
  if ( 0 == WiFiSet ) { 
    Serial.println("WiFiMode is ON");
    //----------WiFi access point---------
    Serial.println();
    Serial.print("Configuring access point...");
  /* You can remove the password parameter if you want the AP to be open. */
    if ( 0 == wifiMode.compareTo("APMode") ){
      WiFi.softAP(ssid.c_str(), essKey.c_str());
      IPAddress myIP = WiFi.softAPIP();
      Serial.print("AP IP address: ");
      Serial.println(myIP);
    }
    //----- WiFi client --------------
    if ( 0 == wifiMode.compareTo("clientMode") ){
      WiFi.begin(ssid.c_str(), essKey.c_str());
      Serial.println("");
    // Wait for connection
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
      Serial.println("");
      Serial.print("Connected to ");
      Serial.println(ssid);
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
    }
  }
  if (1 == WiFiSet) { 
    Serial.println("WiFiMode default boot");
    //----------WiFi access point---------
    Serial.println();
    Serial.print("Configuring access point...");
  /* You can remove the password parameter if you want the AP to be open. */
    WiFi.softAP(defaultSsid, defaultEssKey);
    IPAddress myIP = WiFi.softAPIP();
    Serial.print("AP IP address: ");
    Serial.println(myIP);
  }
  server.on("/",     handleRoot);
  server.on("/wifiInput",  wifiInput);
  server.on("/wifiSet", wifiSet);
  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");  
}




void loop() {
   int check=digitalRead(27);
   int beforemillis=millis();
   int counter=0;
   int timecounter=millis()-beforemillis;
   while ( 60000 > (millis()-beforemillis) ){
     int check=digitalRead(27);
     if ( 0 == check ) {
       count=count+1;
       cpm=cpm+1;
       Serial.println(count);
       delayMicroseconds(200);
      }
   }
   
   beforemillis=millis();
   beforecpm=cpm;
   microSvph = cpm / 150.0;
   Serial.print("count:");
   Serial.print(count);
   Serial.print("cpm:");
   Serial.print(cpm);
   Serial.print("microSvph:");
   Serial.print(microSvph);
   cpm=0;
   googleSheet();
   // Handle incoming connections 
   server.handleClient();
   
}

```

1分ごとに登録できました!


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/2d288cc9-4743-19a0-4ce2-05c229f660ff.png)


# 登録間隔の変更

Google Sheet に登録されたデータは最新100値をグラフ化し、マッピングに使用します。なので登録間隔は数時間に一度ぐらいがいいかな? 例えば2時間に1回、(100計測=200時間=8.3日分)にする場合は、

```
   while ( 60000 > (millis()-beforemillis) ){
```

を

```
   while ( 60000*60*2 > (millis()-beforemillis) ){
```
とします。

# マッピングに登録

「Cloudcande」という名前で uMap 上に測定マップを作っています。上記で設定したものをマップ上にグラフ図示する場合は、こちらの Issue に登録してください。
https://github.com/nanbuwks/GeigerMap/issues/1


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/a2bc6c63-9f17-2e73-e3f3-443b3007f585.png)

## 登録方法

Google Sheet の 「共有」を押して、「リンクを知っている全員」が「閲覧者」となるようにして「リンクをコピー」します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/4e6d9851-1fd2-7f87-4a24-9af15f678312.png)

その URL を Issue に貼ると、レポジトリの Author が追加していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/f7690d17-3fc5-d367-9b42-c31c9348950c.png)
# おまけ

以下は ESSID  と ESSKEY をスケッチに直接書くやり方です。

```
const char* defaultSsid = "ORION";
const char* defaultEssKey = "11111111";
```
の箇所を書き換えてください。

```

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <SPIFFS.h>
#include <FS.h>

const int WiFiSetPin = 16; // GPIO16  push with boot : AP mode on
const char* settings = "/wifi_settings.txt";
// AP mode WiFi Setting
// const char* defaultWifiMode = "APMode";
const char* defaultSsid = "ORION";
const char* defaultEssKey = "11111111";
String wifiMode;
String ssid;
String essKey;
String channel;
String AppScriptURL=" -- Please replace your script URL --";
char *sensorModeStr;
int WiFiSet = 0;  // 0 ... normal operation / 1 ... WiFi Setting Mode
int count;
int beforecpm,cpm;
float microSvph;

WebServer server(80);

void handleRoot(){
  char temp[500];
  snprintf ( temp, 500,
"<html>\
<head>\
<title>Geiger Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1> %f uSv/h (%d cpm )</h1>\
<br>%d :geiger count in %d sec.\
<hr> \
<a href=\"\/wifiInput\">wifi settings</a> \
</body>\
</html>",microSvph,cpm,count,millis()/1000);
  server.send ( 200, "text/html", temp );
}

void wifiInput() {
  String html = "";
  html += "<h1>WiFi Settings</h1>";
  html += "<form method='post' action='/wifiSet'>";
  html += "  <br>";
  html += "  <input type='text' name='essid' >ESSID<br>";
  html += "  <input type='text' name='key' >KEY(pass)<br>";
  html += "  <br>";
  html += "  <input type='submit'><br>";
  html += "</form>";
  server.send(200, "text/html", html);
}

void wifiSet() {
  String wifiMode = "clientMode";
  String setSsid = server.arg("essid");
  String setKey = server.arg("key");
  String setChannel = server.arg("0");

  File f = SPIFFS.open(settings, "w");
  f.println(wifiMode);
  f.println(setSsid);
  f.println(setKey);
  f.println(channel);
  f.close();

  String html = "";
  html += "<h1>WiFi Settings</h1>";
  html += "ESSID:" + setSsid + "<br>";
  html += "key(pass):" + setKey + "<br>";
  html += "<hr>";
  html += "<h1>Please Reset!</h1>";

  server.send(200, "text/html", html);
}

void handleNotFound(){
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  server.send(404, "text/plain", message);
}
void googleSheet(){
    HTTPClient http;
    String URL = AppScriptURL;
    URL += "?";
    URL += "value=";
    URL += microSvph;

    Serial.println("HTTP access to:");
    Serial.println(URL);
    http.begin(URL);
    int httpCode = http.GET();
    if(httpCode > 0) {
        Serial.println(httpCode);
        if(httpCode == HTTP_CODE_OK) {
            String payload = http.getString();
            Serial.println(payload);
        }
    } else {
        Serial.println(http.errorToString(httpCode).c_str());
    }

}

void setup(void){

  pinMode(27, INPUT);
    
  Serial.begin(115200);
  Serial.println("");
  Serial.println("ESXAR Program Start");
  delay(1000);

  
  
  ssid=defaultSsid;
  essKey=defaultEssKey;
  Serial.print("ssid:");
  Serial.println(ssid);
  Serial.print("essKey:");
  Serial.println(essKey);
// WiFi client mode
  WiFi.begin(ssid.c_str(), essKey.c_str());
      Serial.println("");
// Wait for connection
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
      Serial.println("");
      Serial.print("Connected to ");
      Serial.println(ssid);
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
    }
  }

  server.on("/",     handleRoot);
  server.on("/wifiInput",  wifiInput);
  server.on("/wifiSet", wifiSet);
  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");  
}




void loop() {
   int check=digitalRead(27);
   int beforemillis=millis();
   int counter=0;
   int timecounter=millis()-beforemillis;
   while ( 60000 > (millis()-beforemillis) ){
     int check=digitalRead(27);
     if ( 0 == check ) {
       count=count+1;
       cpm=cpm+1;
       Serial.println(count);
       delayMicroseconds(200);
      }
   }
   
   beforemillis=millis();
   beforecpm=cpm;
   microSvph = cpm / 150.0;
   Serial.print("count:");
   Serial.print(count);
   Serial.print("cpm:");
   Serial.print(cpm);
   Serial.print("microSvph:");
   Serial.print(microSvph);
   cpm=0;
   googleSheet();
   // Handle incoming connections 
   server.handleClient();
   
}

```