1
0

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.

M5StickCPlusからCloud Functions経由でツイートした日記

Last updated at Posted at 2022-04-16

日記です。

見たところESP32からTwitterに低めのやる気で投稿する際に、AWS LambdaやCloudFunctionを噛ませている例がなかったので、書いておきます。

はじめに

この度、初めてESP32関連のデバイスに触れました。やりたかったこととしては、物理ボタンを押すとツイートを投稿することです。

先例を探していくと以下の手法が見つかりました

  • 外部の中継サービスにHTTPでツイート文を送信し、中継サービスがツイートする
  • IFTTTにHTTPSで送信し、IFTTTがツイートする
  • ESP32からツイートする。ライブラリあんまりないのでゴリゴリ実装する。

まず、HTTPは個人的には使いたくありません。IFTTTなどのサービスも費用がかかるので避けています。

 ESP32から直接ツイートを投稿するのは、これができるのがベストなのですが、既存のライブラリもろくになさそうです。Twitter API から Arduino – ESP32 を使ってトレンドツイートを取得してみたという先行事例はあるのですが、ここまでの気合は今日はありません。
 Espruino on ESP32 で Twitter(その3) - いっぺーちゃんの いろいろやってみよ~はJavaScriptでツイート投稿をしています。終わってから見つけましたが、これでもよかったな:thinking:

というわけで、次のようなフローで組みました。M5StickからCloudFunctionにはお気楽HTTPSリクエストを投げ、CloudFunctionからTwitterに投稿してもらいます。先の例でいう中継サービスを自分で作る感じですね。
やる気のない図.drawio.png

なんとなく多数派っぽいArduinoので書いていましたが、JavaScriptやPythonでも書けるらしいですね。

ボタンを押すとCloudFunctionを発火させる

HTTPSを叩く方法です。先例がいろいろあります。

画面の表示のリファレンスです。

ボタンの押し方です。

いい感じに足し合わせるとこんな感じになります

スケッチ
#include <M5StickCPlus.h>

#include <Arduino.h>

#include <WiFi.h>

#include <WiFiMulti.h>

#include <HTTPClient.h>

#include <WiFiClientSecure.h>


// Googleのルート証明書 Googleでない場合は異なる
const char * rootCACertificate = \
  "-----BEGIN CERTIFICATE-----\n"\
"MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw\n"\
"CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n"\
"MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n"\
"MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n"\
"Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA\n"\
"A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo\n"\
"27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w\n"\
"Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw\n"\
"TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl\n"\
"qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH\n"\
"szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8\n"\
"Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk\n"\
"MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92\n"\
"wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p\n"\
"aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN\n"\
"VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID\n"\
"AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\n"\
"FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb\n"\
"C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe\n"\
"QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy\n"\
"h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4\n"\
"7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J\n"\
"ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef\n"\
"MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/\n"\
"Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT\n"\
"6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ\n"\
"0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm\n"\
"2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb\n"\
"bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c\n"\
"-----END CERTIFICATE-----\n";

WiFiMulti WiFiMulti;

void setup() {

  Serial.begin(115200);

  Serial.println();
  // WiFiの初期設定・接続
  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP("{WiFiのSSID}", "{WiFiのパスワード}");

  M5.begin();
  Serial.print("Waiting for WiFi to connect...");
  while ((WiFiMulti.run() != WL_CONNECTED)) {
    Serial.print(".");
    delay(250);
  }
  // 画面とシリアルにIPアドレスを出しておく
  Serial.println(WiFi.localIP());
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println(WiFi.localIP());
}

void tweet(int button) {
  WiFiClientSecure client;
  client.setCACert(rootCACertificate);

  // 接続先
  const char * server = "asia-northeast1-your-project-name.cloudfunctions.net";

  if (!client.connect(server, 443))
    Serial.println("Connection failed");
  else {
    Serial.println("Connected to server");
    client.println(String("GET https://") + String(server) + String("/your-function-name?auth=12345&button=") + String(button == 0 ? "A" : "B") + String(" HTTP/1.0\r\n"));
    client.println(String("Host: ") + String(server));
    client.println("User-Agent: ESP32");
    client.println("Connection: close");
    client.println();
  }
  int status_code = 0;
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    Serial.println(line);
    if (status_code == 0) {
      Serial.println(line.substring(9, 12));
      status_code = line.substring(9, 12).toInt();
    }
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }

  client.stop();
  M5.Lcd.setCursor(0, 0, 4);
  M5.Lcd.setTextSize(1);

  if (status_code == 200) {
    // ツイートをして色が変わると楽しい
    M5.Lcd.fillScreen(NAVY);
    M5.Lcd.setTextColor(WHITE, NAVY);
    M5.Lcd.println(String("Tweeted!\n") + String(button == 0 ? "A" : "B"));
  } else {
    M5.Lcd.fillScreen(PURPLE);
    M5.Lcd.setTextColor(WHITE, PURPLE);
    M5.Lcd.println("Tweet failed.");
  }
  delay(3000);

  M5.Lcd.fillScreen(BLACK);
}

void loop() {
  M5.update();

  M5.Lcd.setCursor(0, 12, 4);
  M5.Lcd.setTextColor(WHITE, BLACK);

  M5.Lcd.println("ready");

  if (M5.BtnA.wasPressed()) {
    Serial.println("BtnA.wasPressed() == TRUE");
    M5.Lcd.println("Sending...");
    tweet(0);
  } else if (M5.BtnB.wasPressed()) {
    Serial.println("BtnB.wasPressed() == TRUE");
    M5.Lcd.println("Sending...");
    tweet(1);
  }
  delay(200);

}

CloudFunctionからツイートを送る

TwitterのAPIキーやアクセストークンなどを取得する必要があります。色々なサイトで紹介されています。Read/Write権限を貰うところまで忘れずにやりましょう。

ライブラリや実装はなんでもいいんですがnode-twitter-api-v2を使いました。CloudFunctionは次の設定にしました。

title 設定
環境 第1世代
トリガー HTTP, 未認証の呼び出しを許可, HTTPSが必須
メモリ 128MB
タイムアウト 20秒
最小インスタンス数 0
最大インスタンス数 3
CloudFunctionsのコード
package.json
{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
    "twitter-api-v2": "^1.12.0"
  }
}
index.js
/**
 * Responds to any HTTP request.
 *
 * @param {!express:Request} req HTTP request context.
 * @param {!express:Response} res HTTP response context.
 */
const TwitterApiV2 = require('twitter-api-v2');

exports.helloWorld = async (req, res) => {
    if (req.query.auth === undefined || req.query.auth !== "12345qwerty") {
        res.status(403).send("")
        return
    }


    const appKey = "12345"
    const appSecret = "67890"
    const accessToken = "233456-abcde"
    const accessSecret = "abcdefg"

    const twitterClient = new TwitterApiV2.TwitterApi({
        appKey,
        appSecret,
        accessToken,
        accessSecret
    });

    const rwClient = twitterClient.readWrite;
    let tweetText = ""
    const jstNow = new Date((new Date()).getTime() + 9 * 3600 * 1000).toISOString().split('.')[0]

    const textList = [
        "にゃーん", "わんわん", "がおー"
    ]

    tweetText += textList[~~(Math.random() * textList.length)]
    tweetText += ` ${jstNow}`
    const {
        data: createdTweet
    } = await rwClient.v2.tweet(tweetText);
    console.log('Tweet', createdTweet.id, ':', createdTweet.text);
    let message = req.query.message || req.body.message || 'Hello World!';
    res.status(200).send(message);
};

おわりに

お手軽に直接ツイートしたいのですが、M5Stick自体も初心者だったので、HTTPSを送信するところだけ頑張りました。面倒くさそうなところはCloudFunctionsに丸投げしています。AWS Lambdaとかでもできると思います。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?