音声による信号通信 Chirp -M5StickC編-
面白いものを見つけてしまいました。
.@arduino_fans listen up! We’ve partnered up with Arduino to offer the first official data-over-sound integration for the platform. Get building!👨💻 https://t.co/1qcf7bQAl7 pic.twitter.com/i7KEqO7VfT
— Chirp (@chirp) August 13, 2019
Chirpという音声による信号通信ソリューションを提供するサービスがArm Cortex-M4系のArduinoマイコンにも対応するというニュースでした。
従来からAndroidやiOSなどのSDKを提供していたようです。
いろいろ調べてみるとESP32のサンプルコードも公開されていたので、ここではM5StickCを使用してChirpの音声による信号通信を試してみました!
WiFiやBLEに加えて音声による無線通信をさりげなく使えるようになれば、かなりのジェントルといえるでしょうから。
目次
サンプルコード
以下にArm Cortex-M4系のArduinoマイコンとESP32用のChirpライブラリが公開されております。
https://github.com/chirp/chirp-arduino
Android IDEのライブラリマネージャからもインストール可能です。
ESP32につきましては音声(信号)送信と受信のサンプルコードが公開されています。
ここでは受信用のコードをM5StickC用にカスタマイズします。
受信のサンプルコードはESP32にI2SマイクSPH0645を接続して音声信号を受信してChirpによるアルゴリズムで信号処理してシリアルモニタに信号(文字列)を表示するという簡単なものです。
送信にはChirpの音声信号送受信スマホアプリが公開されておりますのでスマホを使用します。
- Android:https://play.google.com/store/apps/details?id=io.chirp.messenger
- iOS;https://apps.apple.com/jp/app/chirp-messenger/id1438119896
Chirp開発者登録
Chirpのコード使用する際には開発者キーが必要になるため登録が必要です。以下から登録ください。無料です。
https://developers.chirp.io/applications
登録してログインするとMy Sonic Application画面が開きます。
Configurationでは “16khz-mono-embedded”を選択しSAVEします。アプリケーションとしては超音波なども選択できるようです。
SAVEされるとAPP_KEY、APP_SECRET、APP_CONFIGが発行されます。コード作成の際に使用しますのでコピーしておきます。
M5StickC用にコードをカスタマイズ
受信用のコードをM5StickC用にカスタマイズします。
主な変更点は以下の通りです。
- マイクをSPH0645からM5StickC内蔵のSPM1423に変更
ピンアサイン、I2Sの設定を修正 - 音声によって受信した信号(文字列)をディスプレイに表示
Arduinoコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
#include <M5StickC.h> #include <driver/i2s.h> #include "efontEnableAll.h" #include "efont.h" #include "efontM5StickC.h" #include "chirp_connect.h" #include "credentials.h" #define PIN_CLK 0 #define PIN_DATA 34 #define LED_PIN 10 // Pin number for on-board LED //#define SWITCH_PIN 0 // Pin number for on-board switch #define BUFFER_SIZE 512 // Audio buffer size #define SAMPLE_RATE 16000 // Audio sample rate #define MIC_CALIBRATION 13125 #define CONVERT_INPUT(sample) (((int32_t)(sample) >> 14) + MIC_CALIBRATION) // Global variables ------------------------------------------------------------ static chirp_connect_t *chirp = NULL; static chirp_connect_state_t currentState = CHIRP_CONNECT_STATE_NOT_CREATED; static bool startTasks = false; // Function definitions -------------------------------------------------------- void setupChirp(); void chirpErrorHandler(chirp_connect_error_code_t code); void setupAudioInput(int sample_rate); // Function declarations ------------------------------------------------------- void setup() { M5.begin(); M5.Lcd.setRotation(1); M5.Lcd.fillScreen(BLACK); delay(500); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); Serial.begin(115200); Serial.printf("Heap size: %u\n", ESP.getFreeHeap()); xTaskCreate(initTask, "initTask", 16384, NULL, 1, NULL); } void loop() { esp_err_t audioError; chirp_connect_error_code_t chirpError; if (startTasks) { xTaskCreate(processInputTask, "processInputTask", 16384, NULL, 5, NULL); startTasks = false; } } // RTOS Tasks ------------------------------------------------------------------ void initTask(void *parameter) { setupChirp(); chirp_connect_error_code_t chirpError = chirp_connect_set_input_sample_rate(chirp, SAMPLE_RATE); chirpErrorHandler(chirpError); setupAudioInput(SAMPLE_RATE); Serial.printf("Heap size: %u\n", ESP.getFreeHeap()); startTasks = true; vTaskDelete(NULL); } void processInputTask(void *parameter) { esp_err_t audioError; chirp_connect_error_code_t chirpError; size_t bytesLength = 0; float buffer[BUFFER_SIZE] = {0}; int32_t ibuffer[BUFFER_SIZE] = {0}; while (currentState >= CHIRP_CONNECT_STATE_RUNNING) { audioError = i2s_read(I2S_NUM_0, ibuffer, BUFFER_SIZE * 4, &bytesLength, portMAX_DELAY); if (bytesLength) { for (int i = 0; i < bytesLength / 4; i++) { buffer[i] = (float) CONVERT_INPUT(ibuffer[i]); } chirpError = chirp_connect_process_input(chirp, buffer, bytesLength / 4); chirpErrorHandler(chirpError); } } vTaskDelete(NULL); } // Chirp ----------------------------------------------------------------------- void onStateChangedCallback(void *chirp, chirp_connect_state_t previous, chirp_connect_state_t current) { currentState = current; Serial.printf("State changed from %d to %d\n", previous, current); } void onReceivingCallback(void *chirp, uint8_t *payload, size_t length, uint8_t channel) { Serial.println("Receiving data..."); digitalWrite(LED_PIN, HIGH); } void onReceivedCallback(void *chirp, uint8_t *payload, size_t length, uint8_t channel) { if (payload) { char *data = (char *)calloc(length + 1, sizeof(uint8_t)); memcpy(data, payload, length * sizeof(uint8_t)); Serial.print("Received data: "); Serial.println(data); // text print M5.Lcd.fillScreen(BLACK); M5.Lcd.setTextSize(2); printEfont(data, 5, 10); free(data); } else { Serial.println("Decode failed."); } } void setupChirp() { chirp = new_chirp_connect(CHIRP_APP_KEY, CHIRP_APP_SECRET); if (chirp == NULL) { Serial.println("Chirp initialisation failed."); return; } chirp_connect_error_code_t err = chirp_connect_set_config(chirp, CHIRP_APP_CONFIG); chirpErrorHandler(err); chirp_connect_callback_set_t callbacks = {0}; callbacks.on_state_changed = onStateChangedCallback; callbacks.on_receiving = onReceivingCallback; callbacks.on_received = onReceivedCallback; err = chirp_connect_set_callbacks(chirp, callbacks); chirpErrorHandler(err); err = chirp_connect_set_callback_ptr(chirp, chirp); chirpErrorHandler(err); err = chirp_connect_start(chirp); chirpErrorHandler(err); Serial.println("Chirp Connect initialised."); } void chirpErrorHandler(chirp_connect_error_code_t code) { if (code != CHIRP_CONNECT_OK) { const char *error_string = chirp_connect_error_code_to_string(code); Serial.println(error_string); exit(42); } } // I2S Audio ------------------------------------------------------------------- void setupAudioInput(int sample_rate) { i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM), .sample_rate = sample_rate, .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, // is fixed at 12bit, stereo, MSB .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, .communication_format = I2S_COMM_FORMAT_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, .dma_buf_len = 64, }; i2s_pin_config_t pin_config; pin_config.bck_io_num = I2S_PIN_NO_CHANGE; pin_config.ws_io_num = PIN_CLK; pin_config.data_out_num = I2S_PIN_NO_CHANGE; pin_config.data_in_num = PIN_DATA; i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); i2s_set_pin(I2S_NUM_0, &pin_config); i2s_set_sample_rates(I2S_NUM_0, sample_rate); i2s_set_clk(I2S_NUM_0, sample_rate, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_MONO); } |
驚くべきことにChirpの通信は日本語にも対応しているので以下を参考にライブラリを導入し、日本語フォントも表示できるようにしています。
https://github.com/tanakamasayuki/efont
https://lang-ship.com/blog/?p=646
動作
音声による信号通信ソリューションを提供するChirp
いいですねぇ。
M5StickCのマイクでもできました!超音波も試したいですねぇ。https://t.co/l5TiVWtYdj pic.twitter.com/3jcznrf42E
— HomeMadeGarbage (@H0meMadeGarbage) August 13, 2019
音がR2-D2みたいでめちゃくちゃ可愛いです!
信号は2~7kHzに分布されて通信しているようです。下は”hello”の信号周波数分布(スペクトログラム)。
今後、マイコンでも超音波の対応もするというニュースでしたので是非 超音波も試してみたいです。
音楽に超音波混ぜてM5StickCのディスプレイや接続したLEDを遠隔かつ同期操作とかできたらメチャメチャ楽しいよね。
続報を待ちます!
追記
ディスプレイとLED制御(19/8/15)
カラーコードを音声で送ってディスプレイとLEDテープの色を制御してみました♪
私が知っているモテる男の人たちに共通してるのは、WiFi、BLE、音声の3軒くらいを事前に実装しておき、環境に「(実装していることは言わず)どれがマッチする?」と聞いて、スマートに通信できるようにしておくということ。#Chirp #M5StickC pic.twitter.com/fakXCNS65t
— HomeMadeGarbage (@H0meMadeGarbage) August 15, 2019
カラーコード(0x000000~0xffffff)を送信してLEDテープとディスプレイの色を変えてます。
ディスプレイはRGB565に変換して色指定しています。
Chirp買収
Chirpは2020年2月に買収されてしまい、サービス終了となりました 😥 。
We are excited to announce that Chirp has been acquired by @Sonos.
We’ve loved seeing developers apply our SDKs to such an imaginative range of applications. Our thanks to everyone who has supported us on our mission over the last 9 years 🙌
More info: https://t.co/U5GTeuaTzd pic.twitter.com/XKYM9ooDFu
— Chirp (@chirp) February 13, 2020