6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

IoTLTAdvent Calendar 2020

Day 8

リモート発表の寂しさ問題を解決するNIGIYAKASHI

Last updated at Posted at 2020-12-07

はじめに

今年も始まりました。IoTLTのアドベントカレンダー!。この投稿のついでに前から作ってみようと思っていたものをつくってみます

つくるもの

だいたいのイメージはこんな感じです。

NIGIYAKASHI.jpg

やりたいことは以下です。

  1. みんなのメッセージをPCやスマホから受け取ります
  2. メッセージをクラウドで集めます
  3. LEDデバイスで表示します

LEDデバイスは@kitazakiさんよりいただいたMINI LED BADGEを使います。
とりあえずこのアプリケーションをNIGIYAKASHIと名付けました。最近はオンラインでの勉強会での発表がメインになってきましたが、やはりオフラインと比べて賑やかし要素が少なく、寂しく感じる場面が多々あったのでこれを作ってみようと思いました。

完成品

とりあえず動くものから見せます。

IMAGE ALT TEXT HERE

机の上若干ごちゃついているのは無視でw

つかったもの

PubNub

詳しくは公式サイトで見てもらった方が良いかと思いますが、端的に説明するとリアルタイムメッセージチャットやMQTT用のブローカーのサーバーをすべて肩代わりしてくれるサービスです。このサービスを使うとメッセージ送信・受信用のコードを書くだけでリアルタイムメッセージのやり取りができるアプリケーションが作成できます。

UIアプリの作成

コードの全体はこちらで公開しています。ReactでPubNubをつかうためのパッケージpubnub-reactを使うと簡単にPubNubを使うことができます。実装したコードは↓のような感じで、公式ドキュメントのサンプルコードをフォークするだけで簡単に実装することができました。

src/App.js
import React, { useCallback, useState } from 'react'
import PubNub from 'pubnub'
import { PubNubProvider, usePubNub } from 'pubnub-react'
import Button from '@material-ui/core/Button'
import Input from '@material-ui/core/Input'
import { makeStyles } from '@material-ui/core/styles'
import logo from './logo.png'

const pubnub = new PubNub({
  publishKey: process.env.REACT_APP_PUB_KEY,
  subscribeKey: process.env.REACT_APP_SUB_KEY,
})

const useStyles = makeStyles({
  container: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  preparedMessage: {
    flexDirection: 'row',
  },
  customMessage: {
    flexDirection: 'row',
  },
  logo: {
    width: 400,
  },
})

const simpleMessages = ['わーい!', 'すごーい!', 'たのしー!']

const Messenger = () => {
  const pubnub = usePubNub()
  const [input, setInput] = useState('')
  const classes = useStyles()

  const sendMessage = useCallback(
    async message => {
      await pubnub.publish({
        channel: 'nigiyakashi',
        message: { message },
      })

      setInput('')
    },
    [pubnub, setInput]
  )

  return (
    <div className={classes.container}>
      <img src={logo} alt='logo' className={classes.logo}/>
      <div className={classes.preparedMessage}>
        {simpleMessages.map(simpleMessage =>
          <Button
            onClick={e => {
              e.preventDefault()
              sendMessage(simpleMessage)
            }}
            variant="outlined"
          >{simpleMessage}</Button>
        )}
      </div>
      <div className={classes.customMessage}>
        <Input
          type="text"
          placeholder="自由記入欄"
          value={input}
          onChange={e => setInput(e.target.value)}
        />
        <Button
          onClick={e => {
            e.preventDefault()
            sendMessage(input)
          }}
          variant="contained"
          color="primary"
        >
          送信
      </Button>
      </div>
    </div>
  )
}

const App = () => {
  return (
    <PubNubProvider client={pubnub}>
      <Messenger />
    </PubNubProvider>
  )
}

export default App

受信デバイスの実装

MINI LED BADGEのM5Stack用のサンプルコードをベースに実装しました。PubNubとのやりとりはn0bisukeさんの記事を参考に実装してみました。
またPubNubから送られてくるメッセージはJSONの形式となっているので、それをパースするためにArduino_JSONを使いました。

まずベースのサンプルコードのTestLEDMatrix()をいじって、任意の長さの文字をスクロール表示するための関数を実装しました。

void displayLEDMatrix(char *str){
  uint8_t buf[8];
  for (int x=17; x>= -1 * (int(String(str).length()) / 3 * 8) - 16; x--) {
    char *ptr = str;
    uint16_t n = 0;
    matrix.clear();
    matrix1.clear();
    matrix.setCursor(x,0);
    matrix1.setCursor(x+16,0);
    while(*ptr){
      ptr = getFontData(buf,ptr,true);
      if(!ptr)
        break;
      matrix.drawBitmap(x+n,0,buf,8,8,1);
      matrix1.drawBitmap(x+16+n,0,buf,8,8,1);
      n+=8;
    }
    matrix.writeDisplay();
    matrix1.writeDisplay();
  }
}

ただこれは全角の文字列が前提なので半角には対応してないですwあとはメッセージをSubscribeしたときのコールバック関数でJSON形式のメッセージをパースしてdisplayLEDMatrix()に送るための文字列を取り出します。

void callback(char* topic, byte* payload, unsigned int length){
  String msg = (char*) payload;
  msg = msg.substring(0, length);
  JSONVar msgObj = JSON.parse((char*) msg.c_str());
  displayLEDMatrix((char*) ((const char*) msgObj["message"]));
}

コード全体はこんな感じになりました。

#include <M5Stack.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Arduino_JSON.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>
#include "misakiUTF16.h"

Adafruit_8x16matrix matrix = Adafruit_8x16matrix();
Adafruit_8x16matrix matrix1 = Adafruit_8x16matrix();

const char* ssid = "ssid";
const char* password = "password";
const char* mqttServer = "mqtt.pndsn.com";
const char* pubnubid = "pub-c-xxx/sub-c-xxx/ufoo68";
const int mqttPort = 1883;

WiFiClient espClient;
PubSubClient client(espClient);

void callback(char* topic, byte* payload, unsigned int length){
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  Serial.print("Message:");
  String msg = (char*) payload;
  msg = msg.substring(0, length);
  Serial.println(msg);
  JSONVar msgObj = JSON.parse((char*) msg.c_str());
  displayLEDMatrix((char*) ((const char*) msgObj["message"]));
  Serial.println("-----------------------");
}

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");

  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
    if (client.connect(pubnubid)){
      Serial.println("connected");
    }else{
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
  }
  client.subscribe("nigiyakashi");

  Wire.begin(21, 22, 1000);  // M5Stack Grove G21: SDA, G22: SCL
  matrix.begin(0x71);
  matrix1.begin(0x70);
  matrix.setBrightness(0);
  matrix1.setBrightness(0);
  matrix.setTextWrap(false);
  matrix1.setTextWrap(false);
  matrix.setTextColor(LED_ON);
  matrix1.setTextColor(LED_ON);
  matrix.setRotation(1);
  matrix1.setRotation(1);
}

void loop() {
  client.loop();
}

void displayLEDMatrix(char *str){
  uint8_t buf[8];
  for (int x=17; x>= -1 * (int(String(str).length()) / 3 * 8) - 16; x--) {
    char *ptr = str;
    uint16_t n = 0;
    matrix.clear();
    matrix1.clear();
    matrix.setCursor(x,0);
    matrix1.setCursor(x+16,0);
    while(*ptr){
      ptr = getFontData(buf,ptr,true);
      if(!ptr)
        break;
      matrix.drawBitmap(x+n,0,buf,8,8,1);
      matrix1.drawBitmap(x+16+n,0,buf,8,8,1);
      n+=8;
    }
    matrix.writeDisplay();
    matrix1.writeDisplay();
  }
}

さいごに

今回は色んなサンプルコードを組み合わせたような実装になりましたが、Arduinoを使ったリアルタイムメッセージのやりとりをPubNubを使って簡単に実装することができました。かなりシンプルなものができましたが、実際に自分のリモート発表に使ってみようかなと思います。

次回は@wicketさんの投稿です。お楽しみに!

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?