はじめに
お子様や高齢者の外出を見守りたいと思った時に、真っ先に思いつくのはスマホのGPS機能
だと思います。
ただ、スマホも昔ほど安く手に入れる事ができなくなりました。そんな時に外出時にNFCタグや
カードを持ってもらい、外出先のモジュールにタッチしてもらい、居場所をなんとなく把握できる
システムをSpresenseを使って製作してみました。
全体像
お住まいの地域の公園、スーパーマーケットなどに、Spresenseを搭載したNFC読み取り機を設置します。
NFCをそこにタッチすると、LTE経由でNFCの情報がクラウドに集まります。
外出を見守る方は、クラウド情報を確認することで、お出かけの方がどこにいるかが把握できます。
部品リスト
No | 名称 | 入手先 | 備考 |
---|---|---|---|
1 | Spresense メインボード | コンテスト提供品 | |
2 | LTEボード | コンテスト提供品 | |
3 | NFCカードリーダー pn532 | Aliexpress | |
4 | OLED-LCD 128x32 | Aliexpress | ドライバICがSSD1306のもの |
5 | 各種接続ケーブル | 秋月電子など | |
6 | soracom-Simカード | SORACOM | planX3を使用 |
なお、筐体は3Dプリンタで作成
基板接続
LTEボードのピンヘッダのI2C端子をそれぞれのモジュールとつなぎます。
LTEボードのI2CはPWM2,3と排他使用になっています。
LTEボード | NFC address 0x24 | OLED address 0x3c |
---|---|---|
3.3V | VCC | VCC |
GND | GND | GND |
PWM2 | SCL | SCK |
PWM3 | SDA | SDA |
スケッチ
/**************************************************************************/
/*!
This example will wait for any ISO14443A card or tag, and
depending on the size of the UID will attempt to read from it.
If the card has a 4-byte UID it is probably a Mifare
Classic card, and the following steps are taken:
- Authenticate block 4 (the first block of Sector 1) using
the default KEYA of 0XFF 0XFF 0XFF 0XFF 0XFF 0XFF
- If authentication succeeds, we can then read any of the
4 blocks in that sector (though only block 4 is read here)
If the card has a 7-byte UID it is probably a Mifare
Ultralight card, and the 4 byte pages can be read directly.
Page 4 is read by default since this is the first 'general-
purpose' page on the tags.
To enable debug message, define DEBUG in PN532/PN532_debug.h
*/
/**************************************************************************/
/*i2c scanner log
I2C Scanning...
-0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F
0- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
1- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2- : -- -- -- -- 24 -- -- -- -- -- -- -- -- -- -- --
3- : -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
4- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
5- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
6- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
7- : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
the number of found I2C devices is 2.
*/
#if 0
#include <SPI.h>
#include <PN532_SPI.h>
#include "PN532.h"
PN532_SPI pn532spi(SPI, 10);
PN532 nfc(pn532spi);
#elif 0
#include <PN532_HSU.h>
#include <PN532.h>
PN532_HSU pn532hsu(Serial1);
PN532 nfc(pn532hsu);
#else
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
PN532_I2C pn532i2c(Wire1);
PN532 nfc(pn532i2c);
#endif
#include <SPI.h>
// #include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, OLED_RESET);
//LTE setting
#include <LTE.h>
// APN name
#define APP_LTE_APN "soracom.io" // replace your APN
/* APN authentication settings
* Ignore these parameters when setting LTE_NET_AUTHTYPE_NONE.
*/
#define APP_LTE_USER_NAME "sora" // replace with your username
#define APP_LTE_PASSWORD "sora" // replace with your password
// APN IP type
#define APP_LTE_IP_TYPE (LTE_NET_IPTYPE_V4V6) // IP : IPv4v6
// #define APP_LTE_IP_TYPE (LTE_NET_IPTYPE_V4) // IP : IPv4
// #define APP_LTE_IP_TYPE (LTE_NET_IPTYPE_V6) // IP : IPv6
// APN authentication type
#define APP_LTE_AUTH_TYPE (LTE_NET_AUTHTYPE_CHAP) // Authentication : CHAP
// #define APP_LTE_AUTH_TYPE (LTE_NET_AUTHTYPE_PAP) // Authentication : PAP
// #define APP_LTE_AUTH_TYPE (LTE_NET_AUTHTYPE_NONE) // Authentication : NONE
/* RAT to use
* Refer to the cellular carriers information
* to find out which RAT your SIM supports.
* The RAT set on the modem can be checked with LTEModemVerification::getRAT().
*/
#define APP_LTE_RAT (LTE_NET_RAT_CATM) // RAT : LTE-M (LTE Cat-M1)
// #define APP_LTE_RAT (LTE_NET_RAT_NBIOT) // RAT : NB-IoT
// initialize the library instance
LTE lteAccess;
LTEScanner scannerNetworks;
LTEUDP lteUdp;
char host[] = "harvest.soracom.io";
int port = 8514;
int ad_value = 0;
String readFromSerial() {
/* Read String from serial monitor */
String str;
int read_byte = 0;
while (true) {
if (Serial.available() > 0) {
read_byte = Serial.read();
if (read_byte == '\n' || read_byte == '\r') {
Serial.println("");
break;
}
Serial.print((char)read_byte);
str += (char)read_byte;
}
}
return str;
}
void readApnInformation(char apn[], LTENetworkAuthType *authtype,
char user_name[], char password[]) {
/* Set APN parameter to arguments from readFromSerial() */
String read_buf;
while (strlen(apn) == 0) {
Serial.print("Enter Access Point Name:");
readFromSerial().toCharArray(apn, LTE_NET_APN_MAXLEN);
}
while (true) {
Serial.print("Enter APN authentication type(CHAP, PAP, NONE):");
read_buf = readFromSerial();
if (read_buf.equals("NONE") == true) {
*authtype = LTE_NET_AUTHTYPE_NONE;
} else if (read_buf.equals("PAP") == true) {
*authtype = LTE_NET_AUTHTYPE_PAP;
} else if (read_buf.equals("CHAP") == true) {
*authtype = LTE_NET_AUTHTYPE_CHAP;
} else {
/* No match authtype */
Serial.println("No match authtype. type at CHAP, PAP, NONE.");
continue;
}
break;
}
if (*authtype != LTE_NET_AUTHTYPE_NONE) {
while (strlen(user_name)== 0) {
Serial.print("Enter username:");
readFromSerial().toCharArray(user_name, LTE_NET_USER_MAXLEN);
}
while (strlen(password) == 0) {
Serial.print("Enter password:");
readFromSerial().toCharArray(password, LTE_NET_PASSWORD_MAXLEN);
}
}
return;
}
void setup(void) {
// LTE
char apn[LTE_NET_APN_MAXLEN] = APP_LTE_APN;
LTENetworkAuthType authtype = APP_LTE_AUTH_TYPE;
char user_name[LTE_NET_USER_MAXLEN] = APP_LTE_USER_NAME;
char password[LTE_NET_PASSWORD_MAXLEN] = APP_LTE_PASSWORD;
Serial.begin(115200);
Serial.println("Hello!");
nfc.begin();
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(1000); // Pause for 2 seconds
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Didn't find PN53x board");
display.clearDisplay();
// Draw a single pixel in white
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
// display.println(address);
display.println("Didn't find PN53x board");
display.display();
while (1); // halt
}
// Got ok data, print it out!
Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
// configure board to read RFID tags
nfc.SAMConfig();
Serial.println("Waiting for an ISO14443A Card ...");
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
// Draw a single pixel in white
display.drawPixel(10, 10, SSD1306_WHITE);
display.drawPixel(20, 10, SSD1306_WHITE);
display.drawPixel(30, 10, SSD1306_WHITE);
display.display();
delay(2000);
/* Set if Access Point Name is empty */
if (strlen(APP_LTE_APN) == 0) {
Serial.println("This sketch doesn't have a APN information.");
readApnInformation(apn, &authtype, user_name, password);
}
Serial.println("=========== APN information ===========");
Serial.print("Access Point Name : ");
Serial.println(apn);
Serial.print("Authentication Type: ");
Serial.println(authtype == LTE_NET_AUTHTYPE_CHAP ? "CHAP" :
authtype == LTE_NET_AUTHTYPE_NONE ? "NONE" : "PAP");
if (authtype != LTE_NET_AUTHTYPE_NONE) {
Serial.print("User Name : ");
Serial.println(user_name);
Serial.print("Password : ");
Serial.println(password);
}
while (true) {
/* Power on the modem and Enable the radio function. */
if (lteAccess.begin() != LTE_SEARCHING) {
Serial.println("Could not transition to LTE_SEARCHING.");
Serial.println("Please check the status of the LTE board.");
for (;;) {
sleep(1);
}
}
/* The connection process to the APN will start.
* If the synchronous parameter is false,
* the return value will be returned when the connection process is started.
*/
if (lteAccess.attach(APP_LTE_RAT,
apn,
user_name,
password,
authtype,
APP_LTE_IP_TYPE) == LTE_READY) {
Serial.println("attach succeeded.");
break;
}
/* If the following logs occur frequently, one of the following might be a cause:
* - APN settings are incorrect
* - SIM is not inserted correctly
* - If you have specified LTE_NET_RAT_NBIOT for APP_LTE_RAT,
* your LTE board may not support it.
* - Rejected from LTE network
*/
Serial.println("An error has occurred. Shutdown and retry the network attach process after 1 second.");
lteAccess.shutdown();
sleep(1);
}
display.clearDisplay();
// Draw a single pixel in white
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
// display.println(address);
display.println("Setup OK!!");
display.display();
delay(2000);
}
void loop(void) {
uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {
// Display some basic information about the card
Serial.println("Found an ISO14443A card");
Serial.print(" UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
Serial.print(" UID Value: ");
nfc.PrintHex(uid, uidLength);
drawID(uid,uidLength);
udpsend(uid,uidLength);
Serial.println("");
if (uidLength == 4)
{
// We probably have a Mifare Classic card ...
Serial.println("Seems to be a Mifare Classic card (4 byte UID)");
// Now we need to try to authenticate it for read/write access
// Try with the factory default KeyA: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
Serial.println("Trying to authenticate block 4 with default KEYA value");
uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// Start with block 4 (the first block of sector 1) since sector 0
// contains the manufacturer data and it's probably better just
// to leave it alone unless you know what you're doing
success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya);
if (success)
{
Serial.println("Sector 1 (Blocks 4..7) has been authenticated");
uint8_t data[16];
// If you want to write something to block 4 to test with, uncomment
// the following line and this text should be read back in a minute
// data = { 'a', 'd', 'a', 'f', 'r', 'u', 'i', 't', '.', 'c', 'o', 'm', 0, 0, 0, 0};
// success = nfc.mifareclassic_WriteDataBlock (4, data);
// Try to read the contents of block 4
success = nfc.mifareclassic_ReadDataBlock(4, data);
if (success)
{
// Data seems to have been read ... spit it out
Serial.println("Reading Block 4:");
nfc.PrintHexChar(data, 16);
Serial.println("");
// Wait a bit before reading the card again
delay(1000);
}
else
{
Serial.println("Ooops ... unable to read the requested block. Try another key?");
}
}
else
{
Serial.println("Ooops ... authentication failed: Try another key?");
}
}
if (uidLength == 7)
{
// We probably have a Mifare Ultralight card ...
Serial.println("Seems to be a Mifare Ultralight tag (7 byte UID)");
// Try to read the first general-purpose user page (#4)
Serial.println("Reading page 4");
uint8_t data[32];
success = nfc.mifareultralight_ReadPage (4, data);
if (success)
{
// Data seems to have been read ... spit it out
nfc.PrintHexChar(data, 4);
Serial.println("");
// Wait a bit before reading the card again
delay(1000);
}
else
{
Serial.println("Ooops ... unable to read the requested page!?");
}
}
}
drawLTEinfo();
}
void drawLTEinfo() {
// Assigned IP address
IPAddress address = lteAccess.getIPAddress();
Serial.print("IP address: ");
Serial.println(address);
// currently connected carrier
Serial.print("Current carrier: ");
Serial.println(scannerNetworks.getCurrentCarrier());
// return signal strength
Serial.print("Signal Strength: ");
Serial.print(scannerNetworks.getSignalStrength());
Serial.println(" [dBm]");
display.clearDisplay();
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
// display.println(address);
display.setTextSize(2);
display.print("Touch");
display.setTextSize(1);
display.setCursor(64,8);
display.println("Your Card");
display.println(scannerNetworks.getCurrentCarrier());
display.println(scannerNetworks.getSignalStrength());
display.display();
}
void drawID(uint8_t uid[],int uidlength) {
char buf[1];
display.clearDisplay();
display.setTextSize(2); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
// display.println(F(nfc.PrintHex(uid, uidLength)));
display.println(F("touch!"));
String str = (const char*)uid;
display.setTextSize(1); // Normal 1:1 pixel scale
display.print("ID:");
for(int i=0;i<uidlength;i++){
// display.print(String(uid[i],HEX));
sprintf(buf,"%02x",uid[i]);
display.print(buf);
}
// display.println((String)uid);
display.display();
}
void udpsend(uint8_t uid[],int uidlength) {
Serial.println("UDP Send Start");
Serial.print("uidlength:");
Serial.println(uidlength);
if (lteUdp.begin(port) == 1) {
if (lteUdp.beginPacket(host, port) == 1) {
Serial.println("UDP Data make Start");
// uint8ToHexString(uid, uidlength, hexString, sizeof(hexString));
char ad_str[10];
// char * ad_str = (const char *)uid;
sprintf(ad_str, "%02x%02x%02x%02x", uid[0],uid[1],uid[2],uid[3]);
// lteUdp.write(ad_str, uidlength);
lteUdp.write(ad_str, 8);
// lteUdp.write(hexString, 21);
// lteUdp.write(output, 9);
if (lteUdp.endPacket() == 1) {
Serial.println("UDP Data Send OK");
delay(100);
} else {
Serial.println("UDP Data Send NG(endPacket)");
}
} else {
Serial.println("UDP Data make NG(beginPacket)");
}
lteUdp.stop();
Serial.println("UDP Send Stop");
}
}
SORACOM設定
クラウド側でデータを受信するために、SORACOM Harvestの設定を行います。
SORACOMはconsole.ioというサービスで設定がweb上で可能です。
詳細は、SORACOMのページをご確認ください。
-
グループができたら、メニュー画面に戻りSIMグループを選択すると
下の方にある、SORACOM Harvest Data 設定をONにします。
-
SORACOM Harvest Dataの画面でSpresenseLTEから送信されたデータが確認できます。
1次処理済みデータにNFCカードのIDが登録されています。
参照URL
・モーターの音からAIで異常検知 - IoTレシピ
・https://ja.stackoverflow.com/questions/80455/lte拡張ボードの-i2c-を-arduino-で使用するには
・https://docs.neqto.com/docs/ja/spresense-series/hardware/about_spresense#spresense-lte-m
最後に
動作させた動画を共有します。
工夫したいところ
- クラウドへの送信データにGPS情報を載せたい。
- マイク機能やスピーカ機能を使い、タッチした時の音声を出したい。
LTEモジュールのご提供ありがとうございました。
投稿者の人気記事
-
shimodash
さんが
2024/12/15
に
編集
をしました。
(メッセージ: 初版)
-
shimodash
さんが
2024/12/16
に
編集
をしました。
(メッセージ: youtube動画の更新)
-
shimodash
さんが
2024/12/21
に
編集
をしました。
ログインしてコメントを投稿する