🎄 Happy Holidays!
kintone Advent Calendar 2020 20日目の記事です🎉
はじめに
昨年は M5StickC 開発にチャレンジしたのですが、その時買った Dual Button Unit を使ってみたい!ということで、今年は**「ぽちぽちボタンでタイムカード登録する」**プログラムを開発しました٩( ᐛ )و
会社でも kintone でタイムカード管理しているのですが、よく忘れる & 時間を入力するのが手間...
はい、物理ボタンの出番ですね!
(押し心地が良いんで、ちょっとしたストレス解消グッズにもなります笑)
作ったもの
デモです🎥
#kintone #M5StickC でタイムカードぽちぽち🔘 pic.twitter.com/sOixMDlmat
— Tomoko Miyake (@tomo63ko) 2020年12月20日
仕組み
セットアップ
kintone アプリ作成
必須項目はこちらです。
フィールド名 | フィールドタイプ | フィールドコード |
---|---|---|
日付 | 日付 | Date |
出勤時刻 | 時刻 | In |
退勤時刻 | 時刻 | Out |
次に、Arduino プログラムから HTTP リクエストするための API トークン(認証情報)
を生成します。
以下のアクセス権をつけてください。
- レコード閲覧
- レコード追加
- レコード編集
M5StickC 開発環境準備
前回書いたので、こちらの記事を参考にしてください。
最近 Mac OSX を Big Sur にアップデートしてしまったが故に、ESP32 のセットアップに苦戦しました^^;
トラブルシュートで参考にした記事も参考リンク欄
に載せてます。
プログラム開発
下準備ができたら、Arduino IDE で以下のスケッチ
を作成して保存します。
書き終わったらコンパイル
して、マイコンボードに書き込み
をして完成です!👏
(開発中はシリアルモニタ
を使うとデバッグしやすいのでおすすめ)
※ プログラム内こちらの変数の値は書き換えてください。
- ドメイン名
- アプリ ID
- API トークン
- WiFi SSID
- WiFi PASSWORD
#include <M5StickC.h>
#include <WiFi.h>
#include <time.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <stdio.h>
#include <string.h>
#define LED_PIN 10
#define LED_ON LOW
#define LED_OFF HIGH
// kintone setting
const char* domain = "[ドメイン名]";
const int appId = [アプリ ID];
const char* token = "[API トークン]";
char url[128];
char json[256];
// WiFi setting
const char* ssid = "[WiFi SSID]";
const char* password = "[WiFi PASSWORD]";
// Time setting
const char* ntpServer = "ntp.jst.mfeed.ad.jp";
const long gmtOffset_sec = 9 * 3600;
const int daylightOffset_sec = 0;
struct tm timeinfo;
char s[20];
// Button setting
int lastValueRed = 0;
int curValueRed = 0;
int lastValueBlue = 0;
int curValueBlue = 0;
// LED function
void blinkLed() {
digitalWrite(LED_PIN, LED_ON);
delay(500);
digitalWrite(LED_PIN, LED_OFF);
delay(500);
digitalWrite(LED_PIN, LED_ON);
delay(500);
digitalWrite(LED_PIN, LED_OFF);
delay(500);
}
void setup() {
M5.begin();
Serial.begin(115200);
Serial.println("\n*********************\nStart processing...\n*********************");
M5.Axp.ScreenBreath(9);
M5.Lcd.setRotation(3);
pinMode(32, INPUT);
pinMode(33, INPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LED_OFF);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setCursor(15, 2);
M5.Lcd.println("Timecard PochiPochi");
M5.Lcd.setTextColor(WHITE);
// Check WiFi connection
WiFi.begin(ssid, password);
int i = 0;
while (WiFi.status() != WL_CONNECTED) {
if (i > 10) break;
delay(100);
Serial.println("Connecting to WiFi...");
i++;
}
Serial.println("Successfully connected to the WiFi\n");
delay(2000);
}
void loop() {
curValueRed = digitalRead(32);
curValueBlue = digitalRead(33);
M5.Lcd.setCursor(0,15); M5.Lcd.print("Blue Status: ");
M5.Lcd.setCursor(0,30); M5.Lcd.print("IN Time: ");
M5.Lcd.setCursor(0,45); M5.Lcd.print("Red Status: ");
M5.Lcd.setCursor(0,60); M5.Lcd.print("OUT Time: ");
// Click blue button (In time)
if (curValueBlue != lastValueBlue) {
M5.Lcd.fillRect(95,15,100,15,BLACK);
M5.Lcd.fillRect(95,30,100,15,BLACK);
if (curValueBlue==0) {
M5.Lcd.setCursor(95,15); M5.Lcd.print("pressed");
Serial.println("Button Status: blue pressed");
Serial.println(" value: 0");
// Get the current time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
if (!getLocalTime(&timeinfo)) {
Serial.println("Error getting current time\n");
} else {
sprintf(s, "%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min);
Serial.printf("\nCurrent Time = %s \n", s);
}
// GET Request to check today's record
int respCode = 0;
HTTPClient http;
sprintf(url, "https://%s.cybozu.com/k/v1/records.json?app=%d&query=Date=TODAY()&totalCount=true", domain, appId);
http.begin(url);
http.addHeader("X-Cybozu-API-Token", token);
respCode = http.GET();
Serial.printf("\n------ GET records request ------\n");
Serial.printf("HTTP Response Code = %d \n", respCode);
String payload = http.getString();
Serial.println(payload);
DynamicJsonDocument doc(50000);
deserializeJson(doc, payload);
// Check totalCount
const char* totalCount = doc["totalCount"];
Serial.printf("totalCount = %s \n", totalCount);
int numTotalCount = atoi(totalCount);
if (numTotalCount == 0) {
Serial.println("No today's record yet\n");
// POST request to register IN time
Serial.printf("\n------ POST record request ------\n");
sprintf(json, "{\"app\":\"%d\",\"record\":{\"In\":{\"value\":\"%s\"}}}", appId, s);
Serial.printf("Request body = %s \n", json);
int postRespCode = 0;
sprintf(url, "https://%s.cybozu.com/k/v1/record.json", domain);
http.begin(url);
http.addHeader("X-Cybozu-API-Token", token);
http.addHeader("Content-Type", "application/json");
postRespCode = http.POST(json);
Serial.printf("HTTP Response Code = %d \n", postRespCode);
String payload = http.getString();
Serial.println(payload);
Serial.println("----------");
M5.Lcd.setCursor(95,30); M5.Lcd.println(s);
blinkLed();
M5.Lcd.setTextColor(GREEN);
M5.Lcd.setCursor(15,70); M5.Lcd.println("Have a nice day!");
M5.Lcd.setTextColor(WHITE);
delay(5000);
M5.Lcd.fillRect(15,70,100,70,BLACK);
} else if (numTotalCount == 1) {
// Check the record number
JsonObject obj = doc.as<JsonObject>();
String getRecords = obj[String("records")];
Serial.println(getRecords);
DynamicJsonDocument doc(50000);
deserializeJson(doc, getRecords);
JsonObject datas = doc[0];
const char* recordNum = datas["RecordNumber"]["value"];
Serial.printf("Today's record number = %s \n", recordNum);
// PUT Request to update IN time
Serial.printf("\n------ PUT record request ------\n");
sprintf(json, "{\"app\":\"%d\",\"id\":\"%s\",\"record\":{\"In\":{\"value\":\"%s\"}}}", appId, recordNum, s);
Serial.printf("Request body = %s \n", json);
int putRespCode = 0;
sprintf(url, "https://%s.cybozu.com/k/v1/record.json", domain);
http.begin(url);
http.addHeader("X-Cybozu-API-Token", token);
http.addHeader("Content-Type", "application/json");
putRespCode = http.PUT(json);
Serial.printf("HTTP Response Code = %d \n", putRespCode);
String payload = http.getString();
Serial.println(payload);
Serial.println("----------");
M5.Lcd.setCursor(95,30); M5.Lcd.println(s);
blinkLed();
M5.Lcd.setTextColor(GREEN);
M5.Lcd.setCursor(15,70); M5.Lcd.println("Have a nice day!");
M5.Lcd.setTextColor(WHITE);
delay(5000);
M5.Lcd.fillRect(15,70,100,70,BLACK);
} else {
Serial.println("Error! Today's record is duplicated\n----------");
}
}
else{
M5.Lcd.setCursor(95,15); M5.Lcd.print("released");
M5.Lcd.setCursor(95,30); M5.Lcd.println(s);
Serial.println("Button Status: blue released");
Serial.println(" value: 1");
}
lastValueBlue = curValueBlue;
}
// Click red button (Out time)
if (curValueRed != lastValueRed) {
M5.Lcd.fillRect(95,45,100,15,BLACK);
M5.Lcd.fillRect(95,60,100,15,BLACK);
if (curValueRed==0) {
M5.Lcd.setCursor(95,45); M5.Lcd.print("pressed");
Serial.println("Button Status: red pressed");
Serial.println(" value: 0");
// Get the current time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
if (!getLocalTime(&timeinfo)) {
Serial.println("Error getting current time\n");
} else {
sprintf(s, "%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min);
Serial.printf("\nCurrent Time = %s \n", s);
}
// GET Request to check today's record
int respCode = 0;
HTTPClient http;
sprintf(url, "https://%s.cybozu.com/k/v1/records.json?app=%d&query=Date=TODAY()&totalCount=true", domain, appId);
http.begin(url);
http.addHeader("X-Cybozu-API-Token", token);
respCode = http.GET();
Serial.printf("\n------ GET records request ------\n");
Serial.printf("HTTP GET Response Code = %d \n", respCode);
String payload = http.getString();
Serial.println(payload);
DynamicJsonDocument doc(50000);
deserializeJson(doc, payload);
// Check totalCount
const char* totalCount = doc["totalCount"];
Serial.printf("totalCount = %s \n", totalCount);
int numTotalCount = atoi(totalCount);
if (numTotalCount == 0) {
Serial.println("No today's record yet\n");
// POST request to register OUT time
sprintf(json, "{\"app\":\"%d\",\"record\":{\"Out\":{\"value\":\"%s\"}}}", appId, s);
Serial.printf("Request body = %s \n", json);
int postRespCode = 0;
sprintf(url, "https://%s.cybozu.com/k/v1/record.json", domain);
http.begin(url);
http.addHeader("X-Cybozu-API-Token", token);
http.addHeader("Content-Type", "application/json");
postRespCode = http.POST(json);
Serial.printf("HTTP Response Code = %d \n", postRespCode);
String payload = http.getString();
Serial.println(payload);
Serial.println("----------");
M5.Lcd.setCursor(95,60); M5.Lcd.println(s);
blinkLed();
M5.Lcd.setTextColor(GREEN);
M5.Lcd.setCursor(15,70); M5.Lcd.println("Good job!");
M5.Lcd.setTextColor(WHITE);
delay(5000);
M5.Lcd.fillRect(15,70,100,70,BLACK);
} else if (numTotalCount == 1) {
// Check the record number
JsonObject obj = doc.as<JsonObject>();
String getRecords = obj[String("records")];
Serial.println(getRecords);
DynamicJsonDocument doc(50000);
deserializeJson(doc, getRecords);
JsonObject datas = doc[0];
const char* recordNum = datas["RecordNumber"]["value"];
Serial.printf("Today's record number = %s \n", recordNum);
// PUT Request to update OUT time
Serial.printf("\n------ PUT record request ------\n");
sprintf(json, "{\"app\":\"%d\",\"id\":\"%s\",\"record\":{\"Out\":{\"value\":\"%s\"}}}", appId, recordNum, s);
Serial.printf("Request body = %s \n", json);
int putRespCode = 0;
sprintf(url, "https://%s.cybozu.com/k/v1/record.json", domain);
http.begin(url);
http.addHeader("X-Cybozu-API-Token", token);
http.addHeader("Content-Type", "application/json");
putRespCode = http.PUT(json);
Serial.printf("HTTP Response Code = %d \n", putRespCode);
String payload = http.getString();
Serial.println(payload);
Serial.println("----------");
M5.Lcd.setCursor(95,60); M5.Lcd.println(s);
blinkLed();
M5.Lcd.setTextColor(GREEN);
M5.Lcd.setCursor(15,70); M5.Lcd.println("Good job!");
M5.Lcd.setTextColor(WHITE);
delay(5000);
M5.Lcd.fillRect(15,70,100,70,BLACK);
} else {
Serial.println("Error! Today's record is duplicated\n----------");
}
} else {
M5.Lcd.setCursor(95,45); M5.Lcd.print("released");
M5.Lcd.setCursor(95,60); M5.Lcd.println(s);
Serial.println("Button Status: red released");
Serial.println(" value: 1");
}
lastValueRed = curValueRed;
}
M5.update();
}
※ HTTP リクエストの部分は同じような処理が複数あるのでほんとは独自関数作って呼び出すのが良いですが、途中の型変換がうまくいかず冗長な書き方になってしまいました。良きようにアレンジしてください。(๑˃̵ᴗ˂̵)
参考リンク
- macOS Big SurでArduino IDEからESP32の書き込みができない件
- macOS CatalinaのArduino IDEでM5StickCに書き込みができない問題の解決方法
- M5Stick-CでJsonをPOSTする
- ArduinoJson Assistant
- M5StickCであそぶ 〜ボタンとLEDを使う〜
- Dual_Button example
最後まで読んでくださりありがとうございます。
みなさん良いお年を🎍