ATOM Echoにツイートを喋らせてみました!
ぬいぐるみにうめこんだら、いい感じになりそう。
特定のユーザーのツイートが投稿されるとAtom Echoで内容をしゃべってくれる(ちょっとラグがある🙆♀️ pic.twitter.com/Pg3pNbozoS
— はりねずみ麺 (@hedgehog_noodl) December 20, 2020
enebularでツイートを取得し、テキストをMQTTで送信、最後にATOM Echoで再生します。
MQTTブローカーはshiftr.ioを使用しました。
Atom echo側のコード
はじめはUIFlowでやろうと試みたのですが、M5burnerでATOM Echo TTSのtoken取得ができず断念...
こちらの記事を参考にさせていただきました!
ATOM Echo でTTSを堪能
M5StackとNode-REDをMQTTで連携するメモ
//*-----------------------------------*
// ボード: M5Stick-C
// Upload Speed: 115200
//*-----------------------------------*
#include "M5Atom.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>
#include <google-tts.h>
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2S.h"
#define CONFIG_I2S_BCK_PIN 19
#define CONFIG_I2S_LRCK_PIN 33
#define CONFIG_I2S_DATA_PIN 22
// Wi-FiのSSID
char *ssid = "";
// Wi-Fiのパスワード
char *password = "";
// MQTTの接続先のIP
const char *endpoint = "";
// MQTTのポート
const int port = 1883;
// デバイスID
char *deviceID = "M5Stack"; // デバイスIDは機器ごとにユニークにします
// メッセージを知らせるトピック
char *userName = ""; // MQTTブローカ認証ユーザ名
char *userPass = ""; // MQTTブローカ認証パスワード
char *pubTopic = "/pub/M5Stack";
// メッセージを待つトピック
char *subTopic = "/sub/M5Stack";
TTS tts;
AudioGeneratorMP3 *mp3;
AudioFileSourceICYStream *file;
AudioFileSourceBuffer *buff;
AudioOutputI2S *out;
////////////////////////////////////////////////////////////////////////////////
WiFiClient httpsClient;
PubSubClient mqttClient(httpsClient);
void setup() {
Serial.begin(115200);
M5.begin();
// Start WiFi
Serial.println("Connecting to ");
Serial.print(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// WiFi Connected
Serial.println("\nWiFi Connected.");
mqttClient.setServer(endpoint, port);
mqttClient.setCallback(mqttCallback);
Serial.println(tts.getSpeechUrl("こんにちは、世界!", "ja"));
Serial.println(tts.getSpeechUrl("Hello, World!"));
connectMQTT();
}
void loop() {
mqttLoop();
M5.update();
if (M5.Btn.isPressed()){
speakText("こんにちは、私です");
}
delay(100);
}
void speakText(String text){
Serial.println(text);
String http = "http" + tts.getSpeechUrl(text, "ja").substring(5);
Serial.println(http);
audioLogger = &Serial;
file = new AudioFileSourceICYStream( (const char *)http.c_str() );
file->RegisterMetadataCB(MDCallback, (void*)"ICY");
buff = new AudioFileSourceBuffer(file, 10240);
buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
out = new AudioOutputI2S();
out->SetPinout(CONFIG_I2S_BCK_PIN, CONFIG_I2S_LRCK_PIN, CONFIG_I2S_DATA_PIN);
out->SetChannels(1);
out->SetGain(0.4);
mp3 = new AudioGeneratorMP3();
mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
mp3->begin(buff, out);
while(mp3->isRunning()) {
Serial.println("playing");
if (!mp3->loop()) {
mp3->stop();
break;
}
}
}
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
(void) isUnicode; // Punt this ball for now
// Note that the type and string may be in PROGMEM, so copy them to RAM for printf
char s1[32], s2[64];
strncpy_P(s1, type, sizeof(s1));
s1[sizeof(s1)-1]=0;
strncpy_P(s2, string, sizeof(s2));
s2[sizeof(s2)-1]=0;
Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
Serial.flush();
}
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
// Note that the string may be in PROGMEM, so copy it to RAM for printf
char s1[64];
strncpy_P(s1, string, sizeof(s1));
s1[sizeof(s1)-1]=0;
Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
Serial.flush();
}
void mqttCallback (char* topic, byte* payload, unsigned int length) {
String str = "";
Serial.print("Received. topic=");
Serial.println(topic);
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
str += (char)payload[i];
}
String text = str;
speakText(text);
}
void connectMQTT() {
while (!mqttClient.connected()) {
if (mqttClient.connect(deviceID,userName,userPass)) {
Serial.println("Connected.");
int qos = 0;
mqttClient.subscribe(subTopic, qos);
Serial.println("Subscribed.");
} else {
Serial.print("Failed. Error state=");
Serial.println(mqttClient.state());
errorReport();
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void mqttLoop() {
if (!mqttClient.connected()) {
connectMQTT();
}
mqttClient.loop();
}
void errorReport(){
Serial.print("Failed. Error state = ");
switch (mqttClient.state()) {
case MQTT_CONNECT_UNAUTHORIZED:
Serial.println("MQTT_CONNECT_UNAUTHORIZED");
break;
case MQTT_CONNECT_BAD_CREDENTIALS:
Serial.println("MQTT_CONNECT_BAD_CREDENTIALS");
break;
case MQTT_CONNECT_UNAVAILABLE:
Serial.println("MQTT_CONNECT_UNAVAILABLE");
break;
case MQTT_CONNECT_BAD_CLIENT_ID:
Serial.println("MQTT_CONNECT_BAD_CLIENT_ID");
break;
case MQTT_CONNECT_BAD_PROTOCOL:
Serial.println("MQTT_CONNECT_BAD_PROTOCOL");
break;
case MQTT_CONNECTED:
Serial.println("MQTT_CONNECTED");
break;
case MQTT_DISCONNECTED:
Serial.println("MQTT_DISCONNECTED");
break;
case MQTT_CONNECT_FAILED:
Serial.println("MQTT_CONNECT_FAILED");
break;
case MQTT_CONNECTION_LOST:
Serial.println("MQTT_CONNECTION_LOST");
break;
case MQTT_CONNECTION_TIMEOUT:
Serial.println("MQTT_CONNECTION_TIMEOUT");
break;
}
delay(5000); // Wait 5 seconds before retrying
}
enebular
ツイッターノードをインストール
パレットから、node-red-node-twitter
を追加し、下記のようにつなぎます。
TwitterのDeveloperコンソールから新しくアプリを作成。
developer.twitter.com/en/apps
ニュースのツイッターアカウントなど、早く情報を知りたいアカウントを設定すると便利そうです。