少し進む
M5Stackで試作していたが音を出してみるとノイズまみれ。内蔵DACは使えなさそう。本番ではコンパクトなDevKitCを使うつもりなので構成を変更。
部品 | 購入先 |
---|---|
ESP32-DevKitC-32D | 秋月 |
LPS33HW 【防水】気圧センサモジュール | Strawberry Linux |
MAX98357A搭載 I2S 3W D級アンプボード | スイッチサイエンス |
スピーカー 直径27mm | amazon |
ピアニカ吹口(ヤマハPMP32C) | 近所の楽器屋さん |
シリコーンチューブ 内径12mm | amazon |
ESP32のcore1で気圧センサの値を読み、core0で音を出してみた。I2SはMSBから送出しているらしい。なのにlittle endianなESP32のint16_tをそのままi2s_write()して音が出るのが不思議…?
気圧センサは前回と同じものを利用。シリコンチューブに穴を空けて、センサの出っ張り部分を穴に入れて、結束バンドで固定した。チューブの反対側には栓をした。I2Cのプルアップ抵抗は手元にあった2.2KΩ。はじめは10KΩだった。でもスピーカーをつなぐとI2C読み取りに失敗する。しばらく悩んで抵抗値を変えたら動いた。いくつが適切なのかよくわからない。
アンプボードのゲインは6dbに変更。9dbだと音が割れるようだ。原因はスピーカー?電源?よくわからない。スピーカーにはエンクロージャーが必要なことを初めて知った。音が小さいな?とは感じていた。大きなスピーカーをつなげたら大きな音で鳴るのだろうか?
今後の予定:
- 音階を出す
- 気圧は毎日異なるので、息を吹いてないときの気圧値のキャリブレーション
/** * Devkit-Cとmax98357aで音出しする */ #include <driver/i2s.h> #include <Wire.h> /** http://akiracing.com/2018/02/05/how_to_use_lps22hb/ */ ////////////////////////LPS22HB//////////////////////////// #define LPS22HB_ADDRESS 0x5c//I2Cでの気圧センサのスレーブアドレス #define CTRL_REG1 0x10//設定用のレジスタのアドレス #define CTRL_REG1_POWER_DOWN 0x00//データ出力モード(0Hz) ////////LPS22HBの設定//////// #define CTRL_REG1_1HZ 0x10//データ出力モード(1Hz) #define CTRL_REG1_10HZ 0x20//データ出力モード(10Hz) #define CTRL_REG1_25HZ 0x30//データ出力モード(35Hz) #define CTRL_REG1_50HZ 0x40//データ出力モード(50Hz) #define CTRL_REG1_75HZ 0x50//データ出力モード(75Hz)//akiracing.com #define CTRL_REG1_LPF_9_1HZ 0x18//データ出力モード(1Hz)//LPF, Freq/9 #define CTRL_REG1_LPF_9_10HZ 0x28//データ出力モード(10Hz)//LPF, Freq/9 #define CTRL_REG1_LPF_9_25HZ 0x38//データ出力モード(35Hz)//LPF, Freq/9 #define CTRL_REG1_LPF_9_50HZ 0x48//データ出力モード(50Hz)//LPF, Freq/9 #define CTRL_REG1_LPF_9_75HZ 0x58//データ出力モード(75Hz)//LPF, Freq/9 #define CTRL_REG1_LPF_20_1HZ 0x1C//データ出力モード(1Hz)//LPF, Freq/20 #define CTRL_REG1_LPF_20_10HZ 0x2C//データ出力モード(10Hz)//LPF, Freq/20 #define CTRL_REG1_LPF_20_25HZ 0x3C//データ出力モード(35Hz)//LPF, Freq/20 #define CTRL_REG1_LPF_20_50HZ 0x4C//データ出力モード(50Hz)//LPF, Freq/20 #define CTRL_REG1_LPF_20_75HZ 0x5C8//データ出力モード(75Hz)//LPF, Freq/20 uint8_t barometerData[3];//8ビット出力データの保存用配列 volatile int32_t pressureBIN = 0;//2進数での気圧データ格納用 volatile float pressure = 0;//10進数での気圧データ格納用 //////////////////////////////////akiracing.com/////////////// #define SAMPLE_RATE (44100) int16_t sinTable[100]; void setup() { Serial.begin(115200); Wire.begin(21, 22); for (int i=0; i<100; i++) { sinTable[i] = (int16_t)(32767 * sin(PI * 2 * i / 100) *0.7); // 歪?を抑えるための0.7倍 } i2cWriteByte(LPS22HB_ADDRESS, CTRL_REG1, CTRL_REG1_LPF_9_75HZ);//測定を開始する//アドレスCTRL_REG1にレジスタCTRL_REG1_75HZを書き込む xTaskCreatePinnedToCore(play, "Task0", 4096, NULL, 1, NULL, 0); } int nobress = 4149000; // 息を吹いていない場合の数値 int lower = nobress - 5000; // シリアルプロッタの下限 int upper = nobress + 10000; // シリアルプロッタの上限 float vol = 0; void loop() { barometer(); //気圧データの取得 Serial.print(lower); Serial.print(" "); //4120000 - 4130000 Serial.print(upper); Serial.print(" "); //4140000 Serial.println(pressureBIN); float v = 0; if (pressureBIN < lower + 8000) { v = 0; } else { v = (pressureBIN - (lower + 8000)) / (upper - (lower + 8000.0)); if (v > 1) v = 1; } vol = v; delay(1000/75); } void barometer() { i2cRead(LPS22HB_ADDRESS, 0x28, 3, barometerData); //0x28から,3バイト分をbarometerDataにいれる pressureBIN = (uint32_t)barometerData[2] << 16 | (uint16_t)barometerData[1] << 8 | barometerData[0]; //barometerData[2]を左に16シフトし(<<),barometerData[1]を左に8シフトし(<<),barometerData[0]を足し合わせる(|) pressure = pressureBIN / 4096.000000;//[hPa]に変換 } void i2cRead(uint8_t Address, uint8_t Register, uint8_t NBytes, volatile uint8_t* Data) {//指定したアドレスのデータを読む関数 Wire.beginTransmission(Address);//指定したアドレスと通信を始める Wire.write(Register);//レジスタを書き込む Wire.endTransmission(false);//通信を終了する Wire.requestFrom(Address, NBytes);//スレーブからNByteのデータを要求する uint8_t index = 0; while (Wire.available()) { Data[index++] = Wire.read();//データを読み込む } Wire.endTransmission(true); //Serial.print(index); Serial.print(" "); } void i2cWriteByte(uint8_t Address, uint8_t Register, volatile uint8_t Data) {//指定したアドレスにデータを書き込む関数 Wire.beginTransmission(Address);//指定したアドレスと通信を始める Wire.write(Register);//指定するレジスタを書き込む Wire.write(Data);//データを書き込む Wire.endTransmission(true);//通信を終了する } void I2S_Init() { i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), .sample_rate = SAMPLE_RATE, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), .intr_alloc_flags = 0, .dma_buf_count = 16, .dma_buf_len = 60 }; i2s_pin_config_t pin_config = { .bck_io_num = 26, // BCLK .ws_io_num = 25, //23, // LRC .data_out_num = 23,// 25, // DIN .data_in_num = -1 // Not used }; i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); i2s_set_pin((i2s_port_t)0, &pin_config); i2s_set_clk((i2s_port_t)0, SAMPLE_RATE, (i2s_bits_per_sample_t)16, (i2s_channel_t)2); } int DAC_Write_int16(int16_t val) { uint16_t d[2]; // 0:RIGHT 1:LEFT size_t written; d[1] = d[1] = val; //i2s_push_sample((i2s_port_t)0, (const char *)d, 100); i2s_write((i2s_port_t)0, (const void *)d, (size_t)4, &written, 100); } int DAC_Write_char(int8_t *addr) { int8_t sample[4]; sample[0] = *(addr+0); sample[1] = *(addr+1); sample[2] = *(addr+0); sample[3] = *(addr+1); size_t written; i2s_write((i2s_port_t)0, (const void *)sample, (size_t)4, &written, 100); } float volume = 0; void play(void* param) { I2S_Init(); int count = 0; while (1) { for (int i=0; i<50000; i++) { volume = volume * 0.99 + vol * 0.01; // 滑らかに変化させる // 正弦波 DAC_Write_int16(sinTable[count] * volume); // 配列要素が0.7倍してある /* int16_t sample = sinTable[count]; DAC_Write_char((int8_t *)&sample); DAC_Write_int16(sinTable[count]); */ // 矩形波 //DAC_Write_int16((int16_t)((count < 50 ? -32767 : 32767) * volume * 0.7)); // のこぎり波 //DAC_Write_int16((int16_t)((-30000 + count * 600) * volume * 0.7)); // 三角波 //DAC_Write_int16((int16_t)((count<50 ? -30000 + count * 1200 : 30000 - (count-50) * 1200) * volume * 0.7)); count+=1; if (count ==100) count = 0; } vTaskDelay(1); // wdt対策 } }