diff --git a/sanro/sanro.ino b/ATmega32U4/ATmega32U4.ino similarity index 100% rename from sanro/sanro.ino rename to ATmega32U4/ATmega32U4.ino diff --git a/sanro/cache.h b/ATmega32U4/cache.h similarity index 100% rename from sanro/cache.h rename to ATmega32U4/cache.h diff --git a/ESP32/ESP32.ino b/ESP32/ESP32.ino new file mode 100644 index 0000000..4b43a85 --- /dev/null +++ b/ESP32/ESP32.ino @@ -0,0 +1,90 @@ +#define CHANNELS 4 +#define SAMPLE_CACHE_LENGTH 32 // Must be power of 2 (8, 16, etc.); See cache.h for implementation +#define HIT_THRES 750 // The thresholds are also dependent on SAMPLE_CACHE_LENGTH, if you +#define RESET_THRES 300 // changed SAMPLE_CACHE_LENGTH, you must also adjust thresholds +#define SAMPLING_PERIOD 1000 // Sampling period in microseconds (us), 1000us = 1ms = 0.001s + +#define DEBUG 1 + +#include "cache.h" +#include "keyboard.h" + +Cache inputWindow[CHANNELS]; +unsigned long power[CHANNELS]; +unsigned long lastPower[CHANNELS]; + +bool triggered; +unsigned long triggeredTime[CHANNELS]; + +const byte inPins[] = {35, 34, 39, 36}; // L don, L kat, R don, R kat +const byte outPins[] = {25, 26, 27, 14}; // LED visualization (optional) +const char* outKeys[] = {"f", "d", "j", "k"}; // L don, L kat, R don, R kat + +float sensitivity[] = {1.0, 1.0, 1.0, 1.0}; + +short maxIndex; +float maxPower; + +void bluetoothTask(void*); +void typeText(const char* text); + +unsigned long lastTime; + +void setup() { + Serial.begin(250000); + for (byte i = 0; i < CHANNELS; i++) { + power[i] = 0; + lastPower[i] = 0; + triggered = false; + pinMode(outPins[i], OUTPUT); + } + lastTime = 0; + maxIndex = -1; + maxPower = 0; + xTaskCreate(bluetoothTask, "bluetooth", 20000, NULL, 5, NULL); +} + +void loop() { + + if (maxIndex != -1 && lastPower[maxIndex] < RESET_THRES) { + triggered = false; + digitalWrite(outPins[maxIndex], LOW); + maxIndex = -1; + maxPower = 0; + } + + for (byte i = 0; i < CHANNELS; i++) { + + inputWindow[i].put(analogRead(inPins[i])); + power[i] = sensitivity[i] * (power[i] - inputWindow[i].get(1) + inputWindow[i].get()); + + if (lastPower[i] > maxPower && power[i] < lastPower[i]) { + maxPower = lastPower[i]; + maxIndex = i; + } + lastPower[i] = power[i]; +#if DEBUG + Serial.print(power[i]); + Serial.print(" "); +#endif + } + + if (!triggered && maxPower >= HIT_THRES) { + triggered = true; + digitalWrite(outPins[maxIndex], HIGH); +#if !DEBUG + if (isBleConnected) { + typeText(outKeys[maxIndex]); + } +#endif + } +#if DEBUG + Serial.print("\n"); +#endif + + // unsigned int frameTime = micros() - lastTime; + // lastTime = micros(); + // if (frameTime < SAMPLING_PERIOD) { + // delayMicroseconds(SAMPLING_PERIOD - frameTime); + // } +} diff --git a/ESP32/cache.h b/ESP32/cache.h new file mode 100644 index 0000000..5d73956 --- /dev/null +++ b/ESP32/cache.h @@ -0,0 +1,31 @@ +/*************************************************************** + * * + * Taiko Sanro - Arduino * + * Cache data structure * + * * + * Chris * + * wisaly@gmail.com * + * * + ***************************************************************/ + +#ifndef CACHE_H +#define CACHE_H + +template +class Cache { +public: + Cache() { memset(data_, 0, sizeof(data_)); } + inline void put(T value) { + current_ = (current_ + 1) & (L - 1); + data_[current_] = value; + } + inline T get(int offset = 0) const { + return data_[(current_ + offset) & (L - 1)]; + } + +private: + T data_[L]; + int current_ = 0; +}; + +#endif // CACHE_H \ No newline at end of file diff --git a/ESP32/keyboard.h b/ESP32/keyboard.h new file mode 100644 index 0000000..15d719f --- /dev/null +++ b/ESP32/keyboard.h @@ -0,0 +1,219 @@ + +// Bluetooth keyboard implemetation by manuelbl: +// https://gist.github.com/manuelbl/66f059effc8a7be148adb1f104666467 + +#include "BLEDevice.h" +#include "BLEHIDDevice.h" +#include "HIDKeyboardTypes.h" +#include "HIDTypes.h" + +#define DEVICE_NAME "ESP32 Bluetooth Keyboard" + +bool isBleConnected = false; + +// Message (report) sent when a key is pressed or released +struct InputReport { + uint8_t modifiers; // bitmask: CTRL = 1, SHIFT = 2, ALT = 4 + uint8_t reserved; // must be 0 + uint8_t pressedKeys[6]; // up to six concurrenlty pressed keys +}; + +// Message (report) received when an LED's state changed +struct OutputReport { + uint8_t leds; // bitmask: num lock = 1, caps lock = 2, scroll lock = 4, + // compose = 8, kana = 16 +}; + +// The report map describes the HID device (a keyboard in this case) and +// the messages (reports in HID terms) sent and received. +static const uint8_t REPORT_MAP[] = { + USAGE_PAGE(1), + 0x01, // Generic Desktop Controls + USAGE(1), + 0x06, // Keyboard + COLLECTION(1), + 0x01, // Application + REPORT_ID(1), + 0x01, // Report ID (1) + USAGE_PAGE(1), + 0x07, // Keyboard/Keypad + USAGE_MINIMUM(1), + 0xE0, // Keyboard Left Control + USAGE_MAXIMUM(1), + 0xE7, // Keyboard Right Control + LOGICAL_MINIMUM(1), + 0x00, // Each bit is either 0 or 1 + LOGICAL_MAXIMUM(1), + 0x01, + REPORT_COUNT(1), + 0x08, // 8 bits for the modifier keys + REPORT_SIZE(1), + 0x01, + HIDINPUT(1), + 0x02, // Data, Var, Abs + REPORT_COUNT(1), + 0x01, // 1 byte (unused) + REPORT_SIZE(1), + 0x08, + HIDINPUT(1), + 0x01, // Const, Array, Abs + REPORT_COUNT(1), + 0x06, // 6 bytes (for up to 6 concurrently pressed keys) + REPORT_SIZE(1), + 0x08, + LOGICAL_MINIMUM(1), + 0x00, + LOGICAL_MAXIMUM(1), + 0x65, // 101 keys + USAGE_MINIMUM(1), + 0x00, + USAGE_MAXIMUM(1), + 0x65, + HIDINPUT(1), + 0x00, // Data, Array, Abs + REPORT_COUNT(1), + 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) + REPORT_SIZE(1), + 0x01, + USAGE_PAGE(1), + 0x08, // LEDs + USAGE_MINIMUM(1), + 0x01, // Num Lock + USAGE_MAXIMUM(1), + 0x05, // Kana + LOGICAL_MINIMUM(1), + 0x00, + LOGICAL_MAXIMUM(1), + 0x01, + HIDOUTPUT(1), + 0x02, // Data, Var, Abs + REPORT_COUNT(1), + 0x01, // 3 bits (Padding) + REPORT_SIZE(1), + 0x03, + HIDOUTPUT(1), + 0x01, // Const, Array, Abs + END_COLLECTION(0) // End application collection +}; + +BLEHIDDevice* hid; +BLECharacteristic* input; +BLECharacteristic* output; + +const InputReport NO_KEY_PRESSED = {}; + +/* + * Callbacks related to BLE connection + */ +class BleKeyboardCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer* server) { + isBleConnected = true; + + // Allow notifications for characteristics + BLE2902* cccDesc = + (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); + cccDesc->setNotifications(true); + + Serial.println("Client has connected"); + } + + void onDisconnect(BLEServer* server) { + isBleConnected = false; + + // Disallow notifications for characteristics + BLE2902* cccDesc = + (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); + cccDesc->setNotifications(false); + + Serial.println("Client has disconnected"); + } +}; + +/* + * Called when the client (computer, smart phone) wants to turn on or off + * the LEDs in the keyboard. + * + * bit 0 - NUM LOCK + * bit 1 - CAPS LOCK + * bit 2 - SCROLL LOCK + */ +class OutputCallbacks : public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic* characteristic) { + OutputReport* report = (OutputReport*)characteristic->getData(); + Serial.print("LED state: "); + Serial.print((int)report->leds); + Serial.println(); + } +}; + +void bluetoothTask(void*) { + // initialize the device + BLEDevice::init(DEVICE_NAME); + BLEServer* server = BLEDevice::createServer(); + server->setCallbacks(new BleKeyboardCallbacks()); + + // create an HID device + hid = new BLEHIDDevice(server); + input = hid->inputReport(1); // report ID + output = hid->outputReport(1); // report ID + output->setCallbacks(new OutputCallbacks()); + + // set manufacturer name + hid->manufacturer()->setValue("Maker Community"); + // set USB vendor and product ID + hid->pnp(0x02, 0xe502, 0xa111, 0x0210); + // information about HID device: device is not localized, device can be + // connected + hid->hidInfo(0x00, 0x02); + + // Security: device requires bonding + BLESecurity* security = new BLESecurity(); + security->setAuthenticationMode(ESP_LE_AUTH_BOND); + + // set report map + hid->reportMap((uint8_t*)REPORT_MAP, sizeof(REPORT_MAP)); + hid->startServices(); + + // set battery level to 100% + hid->setBatteryLevel(100); + + // advertise the services + BLEAdvertising* advertising = server->getAdvertising(); + advertising->setAppearance(HID_KEYBOARD); + advertising->addServiceUUID(hid->hidService()->getUUID()); + advertising->addServiceUUID(hid->deviceInfo()->getUUID()); + advertising->addServiceUUID(hid->batteryService()->getUUID()); + advertising->start(); + + Serial.println("BLE ready"); + delay(portMAX_DELAY); +}; + +void typeText(const char* text) { + int len = strlen(text); + for (int i = 0; i < len; i++) { + // translate character to key combination + uint8_t val = (uint8_t)text[i]; + if (val > KEYMAP_SIZE) + continue; // character not available on keyboard - skip + KEYMAP map = keymap[val]; + + // create input report + InputReport report = {.modifiers = map.modifier, + .reserved = 0, + .pressedKeys = {map.usage, 0, 0, 0, 0, 0}}; + + // send the input report + input->setValue((uint8_t*)&report, sizeof(report)); + input->notify(); + + delay(5); + + // release all keys between two characters; otherwise two identical + // consecutive characters are treated as just one key press + input->setValue((uint8_t*)&NO_KEY_PRESSED, sizeof(NO_KEY_PRESSED)); + input->notify(); + + delay(5); + } +} \ No newline at end of file diff --git a/README.md b/README.md index 5be048b..81add3b 100644 --- a/README.md +++ b/README.md @@ -4,38 +4,52 @@ ![Taiko Drum Controller](./images/banner-taiko.png) -# Taiko Drum Controller - Arduino/ESP32 +# Taiko Drum Controller - Arduino (ATmega32U4/ESP32) Open-source hardware program to make your own Taiko no Tatsujin PC controller. ## About this Project -This project aims to help you develop your own **hardware taiko** at home. +This project aims to help you develop your own hardware taiko at home. -*This program is for personal and non-commercial use only.* +**This program is for personal and non-commercial use only.** ## What You Need -1. An Arduino Micro/Leonardo microcontroller (other compatible boards might work, but you need to verify that they support keyboard emulation); +1. An Arduino Micro/Leonardo (ATmega32U4) board or an Arduino Nano ESP (ESP32) board. - Checkout to the "ESP32" branch if you use an ESP32 board. ESP32 is significantly faster than ATmega32 and can connect to the computer as a bluetooth keyboard. + Most ATmega32U4 boards work, but you need to verify that they support keyboard emulation; ATmega328P boards like Arduino Uno don't work. + + ESP32 is strongly recommended because it's significantly more powerful than ATmega32U4. This project uses an ESP32-WROOM-32 board. -2. 4 piezoelectric sensors; +2. 4 piezoelectric sensors. -3. Necessary electronic components (breadboards, resistors, LEDs, jumper wires, etc.); +3. 8 100kΩ resistors. -4. Wood planks and cutting tools if you need to make your drum from scratch. If you have a aftermarket taiko or a Big Power Lv. 5 drum, you can use them directly. +4. (Optional) 4 bridge rectifier chips such as [DB107](https://www.diodes.com/assets/Datasheets/products_inactive_data/ds21211_R5.pdf) (see the Additional Notes section for details). + +5. (Optional) Some red and blue LEDs. + +6. Necessary electronic components (breadboards and jumper wires, etc.). + +7. Wood planks and cutting tools (only if you need to make your physical taiko drum from scratch). If you have an aftermarket taiko or a Big Power Lv. 5 drum, you can use them directly. ## Steps to Make the Controller 1. Make the drum and firmly glue the 4 piezoelectric sensors to the drum. Refer to the image for preferred locations of the sensors. - ![Controller scheme](./images/piezo_locations.png) + ![Sensor Setup](./images/piezo_locations.png) 2. Connect the piezoelectric sensors and other components to the controller as follows (the polarity of the piezoelectric sensors don't matter); + + The following schemes are for Arduino Micro boards. If you use a different board, refer to its documentations for the connection. ![Controller scheme](./images/scheme.png) + If you choose to add the bridge rectifiers, use the following scheme: + + ![Controller scheme with bridge rectifiers](./images/scheme_bridge.png) + 3. Flash the firmware to the board. You may need to fine-tune some parameters like `SAMPLE_CACHE_LENGTH`, `HIT_THRES`, `RESET_THRES`, and `sensitivity`. See the following section for details. @@ -46,11 +60,11 @@ This project aims to help you develop your own **hardware taiko** at home. 1. Hit and reset threshold - Set `DEBUG 1` (this disables the keyboard output and sends signal values from the serial port), flash the firmware, roll on one of the 4 areas of the drum, and visualize the graph from the output of the serial monitor. The hit threshold should be smaller than your heaviest hit on the drum, and the reset threshold should be greater than the trough between roll hits. The reset value should also below the hit value. + Set `DEBUG 1` (this disables the keyboard output and sends signal values from the serial port), flash the firmware, roll on one of the 4 areas of the drum, and visualize the graph from the output of the serial monitor. The hit threshold should be lower than your heaviest hit on the drum, and the reset threshold should be greater than the trough between roll hits. The reset value should also be below the hit value. Repeat the process for the rest 3 areas and find the best one that fits all. - ![Controller scheme](./images/tune_hit_reset.png) + ![Setting hit and reset values](./images/tune_hit_reset.png) 2. Sampling length @@ -60,6 +74,20 @@ This project aims to help you develop your own **hardware taiko** at home. Not all piezoelectric sensors are the same, and due to installation errors, the captured signals from the 4 sensors may vary significantly. The sensitivity values are multipliers to normalize the differences. In the following example, the right-don area generates a much higher value than the rest 3, so you can adjust `sensitivity` to `{1.0, 1.0, 0.5, 1.0}` to eliminate the issue. - ![Controller scheme](./images/tune_sensitivities.png) + ![Setting sensitivity values](./images/tune_sensitivities.png) Note that the installation of the sensors is very critical. You should make sure that the sensors are firmly attached on the wood and located properly. + +## Additional Notes + +1. Why using bridge rectifiers + + Without biasing the voltage of the piezoelectric sensors, their output voltage range is about -5V to +5V. However, the ADCs of the analog inputs only accepts positive voltage values (0-3.3V for ESP32 and 0-5V for ATmega32U4). When they receive a negative voltage, it's simply truncated to 0. + + It's usually okay for normal electronic drums because we're just losing half of the input energy and it doesn't influence how we calculate the hitting time. But it can cause problems for *taiko* drums, especially with slow processors like ATmega32U4. + + In a taiko drum, all the 4 vibrating pieces are connected together, meaning that if you hit left-don, the processor also receives signals from left-kat, right-don, and right-kat. If the left-don piezoelectric sensor generates a negative voltage at the beginning and is truncated by the ADC, it will cause a minor "delay" of about 3 to 4 milliseconds, and the processor could incorrectly treat this hit as a right-don, a left-kat, or even a right-kat, whichever sends a highest positive value. + + Using a bridge rectifier, all negative values are converted to positive. In other words, it's like the `abs()` function, ensuring that we don't lose any negative voltages. + + ![Why using bridge rectifiers](./images/bridge_signal.png) diff --git a/README_ja-JP.md b/README_ja-JP.md index c38324b..1505d7a 100644 --- a/README_ja-JP.md +++ b/README_ja-JP.md @@ -2,43 +2,57 @@ [中文文档在这里](README_zh-CN.md) -![Taiko Drum Controller](./images/banner-taiko.png) +![太鼓コントローラ](./images/banner-taiko.png) -# 太鼓ドラムコントローラ - Arduino/ESP32 +# Taiko Drum Controller - Arduino (ATmega32U4/ESP32) -オープンソースのハードウェアプログラムで、自分の「太鼓の達人」PCコントローラーを作りましょう。 +自分の太鼓の達人PCコントローラを作るためのオープンソースハードウェアプログラム。 ## このプロジェクトについて -このプロジェクトは、自宅で自分の**ハードウェア太鼓**を開発するのを助けることを目的としています。 +このプロジェクトは、自宅で自分のハードウェア太鼓を開発するのを助けることを目指しています。 -*このプログラムは個人的かつ非商用のみで使用してください。* +**このプログラムは個人的かつ非商用のみに限ります。** ## 必要なもの -1. Arduino MicroまたはLeonardoマイクロコントローラ(他の互換ボードも動作する可能性がありますが、キーボードエミュレーションをサポートしているか確認する必要があります); +1. Arduino Micro/Leonardo (ATmega32U4) ボードまたは Arduino Nano ESP (ESP32) ボード。 + + ほとんどのATmega32U4ボードは動作しますが、キーボードエミュレーションをサポートしているかを確認する必要があります。Arduino UnoのようなATmega328Pボードは動作しません。 + + ESP32はATmega32U4よりも格段にパワフルなため、強く推奨されます。このプロジェクトではESP32-WROOM-32ボードを使用しています。 - チェックアウトしてください "ESP32" ブランチを使う場合はESP32ボードを使用します。ESP32はATmega32よりも大幅に高速で、Bluetoothキーボードとしてコンピュータに接続することができます。 +2. 4つの圧電センサ。 -2. 4つの圧電センサー; +3. 8つの100kΩ抵抗。 -3. 必要な電子部品(ブレッドボード、抵抗器、LED、ジャンパーワイヤーなど); - -4. 太鼓を一から作る場合は木材と切削ツール。市販の太鼓やビッグパワーLv.5ドラムを持っている場合は、それらを直接使用できます。 +4. (任意) DB107などのブリッジ整流器チップ(詳細は追加ノートセクションを参照)。 -## コントローラーを作る手順 - -1. 太鼓を作り、4つの圧電センサーを太鼓にしっかりと接着してください。センサーの好ましい位置については、画像を参照してください。 +5. (任意) 赤と青のLEDいくつか。 - ![コントローラースキーム](./images/piezo_locations.png) - -2. 圧電センサーと他の部品を次のようにコントローラーに接続します(圧電センサーの極性は関係ありません); +6. 必要な電子部品(ブレッドボード、ジャンパーワイヤーなど)。 - ![コントローラースキーム](./images/scheme.png) +7. 木板と切削工具(自分で物理的な太鼓を一から作る必要がある場合のみ)。アフターマーケットの太鼓やBig Power Lv.5ドラムを持っている場合は、それらを直接使用できます。 + +## コントローラを作る手順 + +1. 太鼓を作り、4つの圧電センサを太鼓にしっかりと接着します。センサの好ましい位置については画像を参照してください。 + + ![センサ設定](./images/piezo_locations.png) + +2. 圧電センサと他のコンポーネントを以下の通りコントローラに接続します(圧電センサの極性は問いません); + + 以下のスキームはArduino Microボード用です。異なるボードを使用する場合は、接続についてそのドキュメンテーションを参照してください。 + + ![コントローラスキーム](./images/scheme.png) + + ブリッジ整流器を追加することを選択した場合、以下のスキームを使用してください: + + ![ブリッジ整流器付きコントローラスキーム](./images/scheme_bridge.png) 3. ファームウェアをボードにフラッシュします。 - `SAMPLE_CACHE_LENGTH`、`HIT_THRES`、`RESET_THRES`、`sensitivity`などのパラメータを微調整する必要があるかもしれません。詳細は次のセクションを参照してください。 + `SAMPLE_CACHE_LENGTH`、`HIT_THRES`、`RESET_THRES`、`sensitivity`などのパラメータを微調整する必要があるかもしれません。詳細は以下のセクションを参照してください。 4. 楽しんでください! @@ -46,20 +60,34 @@ 1. ヒットとリセットの閾値 - `DEBUG 1`を設定してください(これによりキーボード出力が無効になり、シリアルポートからの信号値が送信されます)、ファームウェアをフラッシュし、太鼓の4つのエリアの1つでロールし、シリアルモニターからの出力グラフを視覚化します。ヒット閾値は太鼓での最も重いヒットより小さくなければならず、リセット閾値はロールヒットの間の波底より大きくなければなりません。リセット値もヒット値より下である必要があります。 + `DEBUG 1`を設定し(これによりキーボード出力が無効になり、シリアルポートから信号値が送信されます)、ファームウェアをフラッシュし、太鼓の4つの領域のいずれかでロールして、シリアルモニターの出力からグラフを視覚化します。ヒット閾値は太鼓の最も重いヒットよりも低く、リセット閾値はロールヒットの間のトラフよりも大きくする必要があります。リセット値はまた、ヒット値よりも低くする必要があります。 - 残りの3つのエリアについてもプロセスを繰り返し、すべてに適合する最適な値を見つけてください。 + 残りの3つの領域についてもプロセスを繰り返し、すべてに適合する最適なものを見つけます。 - ![コントローラースキーム](./images/tune_hit_reset.png) + ![ヒットとリセット値の設定](./images/tune_hit_reset.png) 2. サンプリング長 - 最大のランタイム速度のために、`cache.h`ライブラリは2のべき乗の`SAMPLE_CACHE_LENGTH`ウィンドウサイズで動作するように最適化されています。つまり、2、8、16、32などです。実際にはArduinoにとって16が最適な値ですが、少なくとも4000Hz以上の速度で入力をサンプリングする強力なマイクロコントローラを持っている場合は、よりスムーズ(つまり、ノイズが少ない)曲線のために値を32に変更できます。 + 最大の実行時間速度のために、`cache.h`ライブラリは2のべき乗の`SAMPLE_CACHE_LENGTH`ウィンドウサイズで動作するように最適化されています。つまり2、8、16、32などです。実用的には16がArduinoにとって最適な値ですが、少なくとも4000Hz以上の速度で入力をサンプリングするパワフルなマイクロコントローラを持っている場合は、よりスムーズな(つまり、ノイズが少ない)カーブのために値を32に変更することができます。 3. 感度 - すべての圧電センサーが同じではなく、設置エラーのために4つのセンサーから捉えられる信号は大きく異なる可能性があります。感度値は違いを正規化するための乗数です。次の例では、右ドンエリアが他の3つよりもはるかに高い値を生成しているため、`sensitivity`を`{1.0, 1.0, 0.5, 1.0}`に調整して問題を解消できます。 + すべての圧電センサが同じではなく、取り付けエラーにより、4つのセンサからキャプチャされる信号は大きく異なることがあります。感度値は、差異を正規化するための乗数です。以下の例では、右のドンエリアが他の3つよりもはるかに高い値を生成しているため、`sensitivity`を`{1.0, 1.0, 0.5, 1.0}`に調整して問題を解決できます。 - ![コントローラースキーム](./images/tune_sensitivities.png) + ![感度値の設定](./images/tune_sensitivities.png) - センサーの設置が非常に重要です。センサーが木材にしっかりと取り付けられ、適切な位置にあることを確認してください。 + センサの取り付けは非常に重要です。センサが木にしっかりと取り付けられ、適切な位置にあることを確認してください。 + +## 追加ノート + +1. ブリッジ整流器の使用理由 + + 圧電センサの電圧を偏らせない場合、その出力電圧範囲は約-5Vから+5Vです。しかし、アナログ入力のADCは正の電圧値(ESP32の場合は0-3.3V、ATmega32U4の場合は0-5V)のみを受け付けます。負の電圧を受け取ると、単に0に切り捨てられます。 + + 通常の電子ドラムでは、入力エネルギーの半分を失っても、ヒットタイミングの計算には影響しませんので問題ありません。しかし、*太鼓*のドラムでは、特にATmega32U4のような遅いプロセッサを使用すると問題が発生することがあります。 + + 太鼓のドラムでは、4つの振動する部分がすべて繋がっているため、左のドンを叩くと、プロセッサは左のカツ、右のドン、右のカツからも信号を受信します。左のドンの圧電センサが最初に負の電圧を生成し、ADCによって切り捨てられると、約3〜4ミリ秒のわずかな「遅延」が発生し、プロセッサはこのヒットを誤って右のドン、左のカツ、または右のカツとして処理する可能性があります。どちらかが最高の正の値を送信した場合です。 + + ブリッジ整流器を使用すると、すべての負の値が正の値に変換されます。言い換えれば、それは`abs()`関数のようなもので、負の電圧を失わないことを保証します。 + + ![ブリッジ整流器の使用理由](./images/bridge_signal.png) \ No newline at end of file diff --git a/README_zh-CN.md b/README_zh-CN.md index eca5df6..3ae3edf 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -4,41 +4,55 @@ ![Taiko Drum Controller](./images/banner-taiko.png) -# 太鼓达人控制器 - Arduino/ESP32 +# Taiko Drum Controller - Arduino (ATmega32U4/ESP32) -开源硬件程序,用于制作自己的太鼓达人PC控制器。 +开源硬件程序,帮助你制作自己的太鼓达人PC控制器。 -## 关于这个项目 +## 关于本项目 -该项目旨在帮助您在家中开发自己的**硬件太鼓**。 +本项目旨在帮助你在家制作自己的硬件太鼓。 -*本程序仅限个人和非商业用途。* +**该程序仅供个人和非商业用途。** -## 你需要什么 +## 你需要准备 -1. 一个Arduino Micro或Leonardo微控制器(其他兼容板可能也可以工作,但您需要验证它们是否支持键盘模拟); +1. 一个Arduino Micro/Leonardo (ATmega32U4) 板或一个Arduino Nano ESP (ESP32) 板。 - 如果你用的是ESP32开发板,请切换到“ESP32”分枝。ESP32相比ATmega32要快得多,而且可以通过蓝牙无线连接电脑。 + 大多数ATmega32U4板都可以工作,但你需要验证它们是否支持键盘模拟;像Arduino Uno这样的ATmega328P板则不行。 -2. 4个压电传感器; + 强烈推荐使用ESP32,因为它比ATmega32U4强大得多。该项目使用了ESP32-WROOM-32板。 + +2. 4个压电传感器。 -3. 必要的电子组件(面包板、电阻、LED、跳线等); +3. 8个100kΩ电阻。 -4. 如果您需要从头开始制作鼓,还需要木板和切割工具。如果您有一个市售的太鼓或Big Power Lv.5鼓,您可以直接使用它们。 +4. (可选)4个桥式整流器芯片,比如[DB107](https://www.diodes.com/assets/Datasheets/products_inactive_data/ds21211_R5.pdf)(详见附加说明部分)。 + +5. (可选)一些红色和蓝色的LED灯。 + +6. 必要的电子组件(面包板、LED灯、跳线等)。 + +7. 木板和切割工具(仅在你需要从头开始制作实体太鼓时使用)。如果你有市售太鼓或大力鼓Lv.5,可以直接使用。 ## 制作控制器的步骤 -1. 制作鼓,并将4个压电传感器牢固地粘贴到鼓上。参考图片以了解传感器的首选位置。 +1. 制作鼓,并将4个压电传感器牢固地粘贴在鼓上。参考图片以了解传感器的首选位置。 - ![控制器方案](./images/piezo_locations.png) + ![传感器设置](./images/piezo_locations.png) -2. 按照以下方式将压电传感器和其他组件连接到控制器(压电传感器的极性无关紧要); +2. 按照以下方案将压电传感器和其他组件连接到控制器(压电传感器的极性无关紧要); + + 以下方案适用于Arduino Micro板。如果你使用不同的板,请参考其文档了解连接信息。 ![控制器方案](./images/scheme.png) -3. 将固件刷新到板上。 + 如果你选择添加桥式整流器,请使用以下方案: - 您可能需要微调一些参数,如`SAMPLE_CACHE_LENGTH`、`HIT_THRES`、`RESET_THRES`和`sensitivity`。有关详细信息,请参见下一节。 + ![带桥式整流器的控制器方案](./images/scheme_bridge.png) + +3. 将固件刷写到板上。 + + 你可能需要微调一些参数,如`SAMPLE_CACHE_LENGTH`、`HIT_THRES`、`RESET_THRES`和`sensitivity`。详见下一节。 4. 玩得开心! @@ -46,20 +60,34 @@ 1. 击打和重置阈值 - 设置`DEBUG 1`(这将禁用键盘输出并从串行端口发送信号值),刷新固件,在鼓的4个区域之一上滚奏,并从串行监视器的输出中可视化图表。击打阈值应小于鼓上的最重击打,重置阈值应大于两个波峰之间的波谷值。重置值也应低于击打值。 + 设置`DEBUG 1`(这会禁用键盘输出并从串行端口发送信号值),刷写固件,在鼓的4个区域之一上滚动,然后从串行监视器的输出中查看图表。击打阈值应低于你在鼓上的最重击打,重置阈值应高于连续击打之间的低谷。重置值也应低于击打值。 - 为其余3个区域重复此过程,并找到适合所有区域的最佳值。 + 为剩下的3个区域重复此过程,并找到适合所有区域的最佳值。 - ![控制器方案](./images/tune_hit_reset.png) + ![设置击打和重置值](./images/tune_hit_reset.png) 2. 采样长度 - 为了最大运行速度,`cache.h`库已优化,可与2的幂次方的`SAMPLE_CACHE_LENGTH`窗口大小一起工作。这意味着2、8、16和32等。实际上,16对于Arduino来说是最佳值,但如果您有一个强大的微控制器,其采样输入速度至少为4000Hz或更高,则可以将值更改为32,以获得更平滑(换句话说,更少噪声)的曲线。 + 为了最大化运行速度,`cache.h`库已优化为支持2的幂次方窗口大小的`SAMPLE_CACHE_LENGTH`。这意味着2、8、16、32等。实际上,对于Arduino来说16是最佳值,但如果你有一个能以至少4000Hz或更高速度采样输入的强大微控制器,你可以将值改为32,以获得更平滑(换句话说,更少噪声)的曲线。 3. 灵敏度 - 并非所有压电传感器都相同,由于安装错误,4个传感器捕获的信号可能会有显著差异。灵敏度值是用来标准化差异的乘数。在以下示例中,右边的“咚”区域产生的值比其余3个区域高得多,因此您可以调整`sensitivity`为`{1.0, 1.0, 0.5, 1.0}`以消除这个问题。 + 并非所有压电传感器都是相同的,由于安装错误,4个传感器捕获的信号可能有显著差异。灵敏度值是用来规范这些差异的乘数。在以下示例中,右边的don区域产生的值比其余3个区域高得多,所以你可以调整`sensitivity`为`{1.0, 1.0, 0.5, 1.0}`来解决这个问题。 - ![控制器方案](./images/tune_sensitivities.png) + ![设置灵敏度值](./images/tune_sensitivities.png) - 请注意,传感器的安装非常关键。您应确保传感器牢固地附着在木头上并放置在适当的位置。 + 请注意,传感器的安装非常关键。你应该确保传感器牢固地贴在木头上,并且位置适当。 + +## 附加说明 + +1. 为什么使用桥式整流器 + + 如果不对压电传感器的电压进行偏置,它们的输出电压范围约为-5V到+5V。然而,模拟输入的ADC只接受正电压值(ESP32为0-3.3V,ATmega32U4为0-5V)。当它们接收到负电压时,通常会被简单地截断为0。 + + 对于普通的电子鼓来说,这通常没问题,因为我们只是失去了一半的输入能量,而这并不影响我们计算击打时间的方式。但是对于*太鼓*鼓来说,特别是像ATmega32U4这样的慢处理器,可能会造成问题。 + + 在太鼓鼓中,所有4个振动部件都连接在一起,这意味着如果你击打左边的don,处理器也会接收到左边的kat、右边的don和右边的kat的信号。如果左边的don压电传感器一开始就产生负电压,并被ADC截断,它将导致约3到4毫秒的轻微“延迟”,处理器可能会错误地将这次击打视为右边的don、左边的kat甚至右边的kat,取决于哪个发送了最高的正值。 + + 使用桥式整流器,所有负值都转换为正值。换句话说,就像`abs()`函数,确保我们不会丢失任何负电压。 + + ![为什么使用桥式整流器](./images/bridge_signal.png) \ No newline at end of file diff --git a/images/bridge_signal.png b/images/bridge_signal.png new file mode 100644 index 0000000..45cccde Binary files /dev/null and b/images/bridge_signal.png differ diff --git a/images/scheme_bridge.png b/images/scheme_bridge.png new file mode 100644 index 0000000..e139518 Binary files /dev/null and b/images/scheme_bridge.png differ