🚗

NFCタグでトミカ読み上げアプリを作ってみた

2024/09/20に公開

やってみた

この動画が全てですが、ちょっと説明します。

https://www.youtube.com/watch?v=WG6mibocQM4

経緯

子がトミカ好きだと、車名を覚える必要にかられます。

初期

👶「これなんて車?」
🧑「消防車だよー」

中期

👶「これなんて車?」
🧑「ポンプ消防車」

末期

👶「これなんて車?」
😰「災害、、、対策用、、、救助車?」

そう、トミカは消防車だけで何十種類もあるのです。

正直覚えきれないので、技術で解決できないか考えることにしました。

下準備

トミカ辞書を作る

まず、トミカの車種はどうやったら特定できるのかを調べました。トミカの箱には3桁の数字が書いてあり、同じ数字が車の裏にも表記されています。例えば、日野 はしご付消防車は、No.108です。

https://www.amazon.co.jp/dp/B0002U3LS6/

この数字がIDとして使えるかも!と思いましたが、そんな甘くはありません。この数字は新商品が発売されるとランダムに上書きされます。現在、No.108はしご付消防車も、その前は動物運搬車(ゴリラ)、さらに前はキャンピングカーと一貫性皆無です。

そこで、トミカダスを参考に親番号-子番号の辞書を作ります。

tomica.dart
const Map<String, Map<String, String>> tomica = {
  "1": {
    "1-7": "日産 スカイライン(BNR34) パトロールカー",
    "1-6": "日産 エクストレイル 消防指揮車",
    "1-5": "三菱ふそう エアロクィーン",
    "1-4": "三菱ふそう エアロクイーン",
    "1-3": "日野 グランビューバス",
    "1-2": "ふそう はとバス",
    "1-1": "ブルーバードSSSクーペ"
  },
  ...
  "108": {
    "108-4": "日野 はしご付消防車 (モリタ・スーパージャイロラダー)",
    "108-3": "三菱ふそう 動物運搬車(ゴリラ)",
    "108-2": "トヨタ ハイラックス キャンピングカー",
    "108-1": "トヨタ カローラ 30 レビン"
  },
  ...
}

NFCタグ

Amazonで色々売ってますが、トミカに貼れるサイズのものを選びました。

冷静に考えると1枚50円は割高...

https://www.amazon.co.jp/dp/B0C8HMW9GC/

トミカに貼る

トミカの背面は凹凸がありますが、粘着力があって結構しっかり貼れます。

キーを書き込む

NFC Toolsを使って、トミカの親番号-子番号をテキストで書き込みます。

https://play.google.com/store/apps/details?id=com.wakdev.wdnfc

上の写真のトミカは、No.115のスバルフォレスターで、115-6を書き込みました。

Flutterで読み上げアプリを作る

使用プラグイン

NFCタグを読み取るnfc_managerと、テキストを読み上げるflutter_ttsを使います。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_tts: ^4.0.0  # テキスト読み上げ
  nfc_manager: ^3.5.0  # NFC読み取り

コード(一部略)

main.dart
main.dart
import 'package:flutter_tts/flutter_tts.dart';
import 'package:nfc_manager/nfc_manager.dart';
import './tomica.dart';

class _MyHomePageState extends State<MyHomePage> {
  late FlutterTts tts;
  String? _newVoiceText;

  
  void initState() {
    super.initState();

    NfcManager.instance.startSession(
      onDiscovered: (NfcTag tag) async {
        Ndef? ndef = Ndef.from(tag);
        if (ndef == null) {
          // エラー処理
          return;
        }
        try {
          // NFCタグのテキスト取得
          NdefMessage message = await ndef.read();
          String str = '';
          for (NdefRecord record in message.records) {
            Uint8List payload = record.payload;
            str += utf8.decode(payload);
          }
          // Language Codeのenを無視
          var nono = str.substring(str.indexOf('en') + 2);  
          // 親番号切り出し
          var no = nono.substring(0, nono.indexOf("-"));

          setState(() {
            // 読み上げ
            _newVoiceText = tomica[no]?[nono];
            _speak();
          });
        } catch (e) {
          logger.e(e);
        }
      },
    );
  }

  dynamic initTts() {
    tts = FlutterTts();
    _setAwaitOptions();
    if (Platform.isAndroid) {
      _getDefaultEngine();
      _getDefaultVoice();
    }
  }

  Future<void> _setAwaitOptions() async {
    await tts.awaitSpeakCompletion(true);
  }

  Future<void> _getDefaultEngine() async {
    var engine = await tts.getDefaultEngine;
  }

  Future<void> _getDefaultVoice() async {
    var voice = await tts.getDefaultVoice;
  }

  Future<void> _speak() async {
    await tts.setLanguage('ja-JP'); // 読み上げる言語を日本語に設定
    await tts.setSpeechRate(0.7);
    await tts.setVolume(1.0);
    await tts.setPitch(1.0);
    await tts.setVoice({'name': 'Karen', 'locale': 'ja-JP'});

    if (_newVoiceText != null) {
      if (_newVoiceText!.isNotEmpty) {
        await tts.speak(_newVoiceText!);
      }
    }
  }
}

オチ

🧑「ここにトミカ置いてみて」
👶「はい🚗」
📱「災害対策用救助車 V型」
🧑「(ドヤ)」
👶「・・・」
👶「パパが言ってよ」
😰「・・・」

お蔵になりました。

Discussion