M5Stackを使い非常に安価なクラウド連携(kintone)タイムレコーダーを作る

土曜日 , 29, 1月 2022 1 Comment

M5Stackを使うと非常に安価にクラウド連携のタイムレコーダーを作成することができます。
今回は、RFIDユニットと非接触ICカードを組み合わせてカードをピッとすると打刻データがkintoneに即アップロードされる方式のタイムレコーダーを作成しました。
アップロードしたデータを使いkintone上で勤務簿作成や給与処理などの就業管理の自動化ができると思います。20220128_145421282_iOS

非接触ICカードとは、日本ではSuicaやnanacoとして知られているものです。
下図はM5Stack直販で購入した際におまけとしていただいていたICカードで、よく知られているICカードとまったく同じ形状をしていることがわかります。
カードごとにユニークなIDが付与されており、これを社員マスタなどと関連付けることでカードだけで社員の特定ができるようになります。

20220128_150609241_iOS


使い方

  • kintone上にアプリ(データベース)を作成し、外部からアクセスするためのAPIトークンを発行、および適切なアクセス権限を設定します。
    サンプルプログラムでは、「m5timecard」というアプリを作成し、下表のようにフィールドを定義しています。

スクリーンショット 2022-01-29 0.15.16

フィールド名

データ型

フィールドコード

打刻時刻 文字列(1行) hms
カードID 文字列(1行) card_id
打刻モード 文字列(1行) mode

※APIからアップロードする際に使用するのは、kintone上の「フィールド名」(設定画面一番上の項目)ではなく「フィールドコード」(設定画面一番下の項目)である点に注意が必要です。

  • kintoneのURL、APIトークン、アプリIDをソースコードの冒頭にある変数に設定します。
  • M5Burnerなどを使ってM5Stackシリーズのデバイスに転送します。
  • 電源を投入すると所定のWi-Fiに接続を試みます。
    接続が確立するとNTPサーバと同期をとり時刻が表示されます。
    この状態でRFIDユニットにカードを近づけ読み取りに成功するとIDを読み取りkintoneに送信します。
    M5StackのAボタンまたはCボタンを押すと出勤/退勤モードが切り替わります。
  • kintoneへ送信に成功するとピッとビープが鳴ります。
    失敗するとブーと低音長めのビープが鳴り画面に「エラー」と表示されます。
  • cybozuへアップロードされたデータは下図のように記録されます。

スクリーンショット 2022-01-28 23.56.36


ソースコード


スクリーンショット 2022-01-29 0.01.48

from m5stack import *
from m5ui import *
from uiflow import *
import urequests
import wifiCfg
import ntptime
import time
import unit

setScreenColor(0x222222)
rfid0 = unit.get(unit.RFID, unit.PORTA)


x = None
hms = None
id2 = None
mode = None
API_URL = None
xx = None
API_TOKEN = None
lastid = None
APP_ID = None
STATUS_TEXT = None

wifiCfg.autoConnect(lcdShow=True)
wifiCfg.reconnect()


# Describe this function...
def format2digits(x):
  global hms, id2, mode, API_URL, xx, API_TOKEN, lastid, APP_ID, STATUS_TEXT
  if x <= 9:
    xx = (str('0') + str(str(x)))
  else:
    xx = str(x)
  return xx

# この関数の説明…
def sendToKintone(hms, id2, mode):
  global x, API_URL, xx, API_TOKEN, lastid, APP_ID, STATUS_TEXT
  try:
    req = urequests.request(method='POST', url=API_URL,json={'app':APP_ID,'record':({'hms':({'value':hms}),'card_id':({'value':id2}),'mode':({'value':mode})})}, headers={'X-Cybozu-API-Token':API_TOKEN,'Content-Type':'application/json'})
    speaker.tone(1200, 10)
    STATUS_TEXT = ''
  except:
    speaker.tone(400, 3000)
    STATUS_TEXT = 'エラー'


def buttonA_wasPressed():
  global hms, API_URL, xx, mode, id2, API_TOKEN, lastid, APP_ID, x, STATUS_TEXT
  setScreenColor(0x000066)
  mode = 0
  lastid = '__timecard__'
  speaker.tone(1800, 200)
  pass
btnA.wasPressed(buttonA_wasPressed)

def buttonC_wasPressed():
  global hms, API_URL, xx, mode, id2, API_TOKEN, lastid, APP_ID, x, STATUS_TEXT
  setScreenColor(0x663300)
  mode = 1
  lastid = '__timecard__'
  speaker.tone(1800, 200)
  pass
btnC.wasPressed(buttonC_wasPressed)

@timerSch.event('rfid')
def trfid():
  global hms, API_URL, xx, mode, id2, API_TOKEN, lastid, APP_ID, x, STATUS_TEXT
  if rfid0.isCardOn():
    id2 = rfid0.readUid()
    if id2 != '' and id2 != lastid:
      sendToKintone(hms, id2, mode)
      lastid = id2
  else:
    pass
  pass

@timerSch.event('clock')
def tclock():
  global hms, API_URL, xx, mode, id2, API_TOKEN, lastid, APP_ID, x, STATUS_TEXT
  hms = (str(str(format2digits(ntp.hour()))) + str(((str(':') + str(((str(str(format2digits(ntp.minute()))) + str(((str(':') + str(str(format2digits(ntp.second())))))))))))))
  print(hms)
  lcd.clear()
  lcd.font(lcd.FONT_DejaVu72)
  lcd.print(str(hms), 0, 0, 0xffffff)
  lcd.font(lcd.FONT_DejaVu24)
  lcd.print(str(id2), 0, 80, 0xffffff)
  lcd.font(lcd.FONT_UNICODE)
  lcd.print(str(STATUS_TEXT), 0, 120, 0xffffff)
  lcd.font(lcd.FONT_UNICODE)
  if mode == 0:
    lcd.rect(10, 185, 105, 45, color=0xffff00)
  elif mode == 1:
    lcd.rect(200, 185, 105, 45, color=0xffff00)
  else:
    lcd.rect(10, 185, 105, 45, color=0xffffff)
    lcd.rect(200, 185, 105, 45, color=0xffffff)
  lcd.print('出勤', 40, 195, 0xffffff)
  lcd.print('退勤', 225, 195, 0xffffff)
  lcd.print('', 0, 200, 0xffffff)
  pass


API_URL = 'YOUR_API_URL'
API_TOKEN = 'YOUR_API_TOKEN'
APP_ID = 'YOUR_APP_ID'
while not (wifiCfg.wlan_sta.isconnected()):
  wait(1)
setScreenColor(0x000000)
ntp = ntptime.client(host='ntp.nict.jp', timezone=9)
id2 = ''
mode = 0
lastid = '__timecard__'
STATUS_TEXT = ''
setScreenColor(0x000066)
timerSch.run('clock', 1000, 0x00)
timerSch.run('rfid', 500, 0x00)



まとめ

作成していて気づいたのですが、連続して打刻データを飛ばさないように同一ID、同一モードの時はkintoneにデータを送信しないように工夫したもののときどき変なデータがアップロードされてきました。
どうもIDの読み取りに失敗するとブランクや桁数に満たない変なデータが送られてしまう時があるようです。
本格的な運用の際には桁数チェックなどを入れるともよいかもしれません。

それにしても、今回作成した組み合わせではだいたい6000円くらいでクラウド連携のタイムレコーダーができてしまい驚きます。ICカードもM5Stackで5枚で2ドルと大変リーズナブルに入手できるので大量配布しても導入コストをかなり抑えたものができそうです。

One thought on “ : M5Stackを使い非常に安価なクラウド連携(kintone)タイムレコーダーを作る”
  • […] 前回『M5Stackを使い非常に安価なクラウド連携(kintone)タイムレコーダーを作…では、M5Stackを使ってタイムレコーダーを作ってみましたが、今回はRaspberry PiとICカードリーダ、kintoneを使ってクラウド連携のタイムレコーダーが作れるか実験してみました。Raspberry Piの場合、USB機器やHATを介してGROVE規格のセンサーやディスプレイ、制御装置などと連携し複雑な処理を行うことができるようになります。 […]

  • Please give us your valuable comment

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です

    このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください