English description below.
実装することで実際にドローンとして飛行できる,ドローン名刺を作ってみました.
長い間,バグっててあんまりきれいに飛ばなかったのですが息抜きに飛ぶように修正してみました. ESP32をコアとしており,6軸IMUのセンシングとモーターのPID制御します.スマホをプロポとするためにESP32上でWebサーバーを立て,Joystickを動かすとWebSocket通信で入力値をドローンに送信します.また,ドローンのRoll pitch yawの角度値や電圧といったテレメトリをスマホ側で見れるようにしています. イメージとしてはCrazyfileのようなフライトコントローラとフレーム兼用の基板を目指して作っています.リアルで私に会うことがあればお渡しします!
名刺完成しました!(形だけ) pic.twitter.com/beSWObaRMu
— fumi (@fumi_maker) August 20, 2024
概要
実際に動いている様子は以下をご覧ください.
この名刺は部品を実装することで手のひらサイズの小型ドローンとして飛行させることができます。また、ダックスの首輪のLEDを実装しスマホのNFCアンテナ部を近づけるとLEDが点滅する機能があります。




ドローン名刺について
ESP32を搭載し、基板自体がドローンになります。以下の機能を持っています。
- 6軸IMUとの通信と姿勢推定
- PID制御による姿勢制御
- ESP32自身がWi-Fiアクセスポイントとなり、スマホをコントローラーとしたWebSocket通信による機体操縦
- 機体情報テレメトリ通信
- PIDゲインやキャリブレーションが調整可能なWebUI
- 角度制御飛行(Angle)
必要なもの
ドローン名刺基板 BOM表のパーツ 3Dプリンタ
ハードウェアの組み立て
以下のBOMと回路図を参考に実装してください。Kicadで設計しました.プロジェクトデータは名刺のため個人情報などがあり気になる方は個別に連絡してください. Githubで回路図とBOMを公開してます.


| Item | Qty | Reference(s) | Value | Footprint | 備考 |
|---|---|---|---|---|---|
| 1 | 1 | C1 | DNP | Capacitor_SMD:C_0603_1608Metric | |
| 2 | 10 | C2, C5, C7, C8, C9, C10, C11, C12, C13, C14 | 0.1u | Capacitor_SMD:C_0603_1608Metric | お好みで |
| 3 | 3 | C3, C4, C6 | 10u | Capacitor_SMD:C_0603_1608Metric | お好みで |
| 4 | 1 | D1 | MBR0520 | Diode_SMD:D_SOD-123 | SS2040など |
| 5 | 1 | D2 | blue | LED_SMD:LED_0603_1608Metric | お好みで |
| 6 | 2 | D3, D4 | red | LED_SMD:LED_0603_1608Metric | お好みで |
| 7 | 1 | D5 | green | LED_SMD:LED_0603_1608Metric | お好みで |
| 8 | 4 | D6, D7, D8, D9 | 1N4148W | Diode_SMD:D_SOD-123 | https://akizukidenshi.com/catalog/g/g107084/ |
| 9 | 1 | FB1 | FerriteBead | Inductor_SMD:L_0603_1608Metric | https://akizukidenshi.com/catalog/g/g104053/ |
| 10 | 1 | J1 | LIPO | JST PH | https://akizukidenshi.com/catalog/g/g112802/ |
| 11 | 1 | J2 | DNP | Connector_PinSocket_1.27mm:PinSocket_1x03_P1.27mm_Vertical | 拡張用 |
| 16 | 1 | P1 | USB_C_Plug_USB2.0 | Connector_USB:USB_C_Receptacle_HCTL_HC-TYPE-C-16P-01A | https://akizukidenshi.com/catalog/g/g114356/ |
| 17 | 1 | Q1 | AO3401A | Package_TO_SOT_SMD:SOT-23 | https://akizukidenshi.com/catalog/g/g114654/ |
| 18 | 3 | Q2, Q3, Q4 | MMBT3904 | Package_TO_SOT_SMD:SOT-23 | https://akizukidenshi.com/catalog/g/g105969/ |
| 19 | 4 | Q5, Q6, Q7, Q8 | AO3400A | Package_TO_SOT_SMD:SOT-23 | https://akizukidenshi.com/catalog/g/g114653/ |
| 20 | 2 | R1, R2 | 2.2k | Resistor_SMD:R_0603_1608Metric | お好みで |
| 21 | 15 | R3, R4, R5, R6, R7, R8, R9, R11, R12, R14, R15, R21, R22, R26, R27 | 10k | Resistor_SMD:R_0603_1608Metric | お好みで |
| 22 | 1 | R10 | 5.1k | Resistor_SMD:R_0603_1608Metric | お好みで |
| 23 | 9 | R13, R16, R17, R18, R19, R20, R23, R24, R25 | 1k | Resistor_SMD:R_0603_1608Metric | お好みで |
| 24 | 2 | SW1, SW2 | SKRPACE010 | module:SKRPACE010 | https://akizukidenshi.com/catalog/g/g106185/ |
| 25 | 1 | U1 | ADP3338AKCZ-3.3 | Package_TO_SOT_SMD:SOT-223-3_TabPin2 | https://akizukidenshi.com/catalog/g/g112635/ |
| 26 | 1 | U2 | ESP32-WROOM-32E | RF_Module:ESP32-WROOM-32E | https://akizukidenshi.com/catalog/g/g115675/ |
| 27 | 1 | U3 | MPU-6050 | Sensor_Motion:InvenSense_QFN-24_4x4mm_P0.5mm | https://www.mouser.jp/ProductDetail/TDK-InvenSense/MPU-6050?qs=u4fy%2FsgLU9O14B5JgyQFvg%3D%3D |
| 28 | 1 | U4 | FT231XQ | Package_DFN_QFN:QFN-20-1EP_4x4mm_P0.5mm_EP2.5x2.5mm | https://www.digikey.jp/ja/products/detail/ftdi-future-technology-devices-international-ltd/FT231XQ-R/3029140 |
モーターホルダーの印刷
ドローンの脚兼モーターホルダーの印刷をします。GitHubにあるのでダウンロードしてください。筆者はBambuLab X1 carbonのBasic PLAで印刷していますが、精度によってはモーターが入らない場合があるのでその場合はstepファイルを編集してちょうどいい物に調整してください。なお、緩い場合は最悪テープで止めると良いです(自分も結局テープで止めてます)。
ソフトウェア
GitHubにソースコードを公開しています。Arduino CoreとESP32ライブラリを使用しています。
システムアーキテクチャ
システムアーキテクチャは以下の通りです.ドローン上ESP32にWebサーバーを立てて、そこにスマホのブラウザからアクセスすることでドローン制御用のコントローラーと調整用のWebUIがあります。 制御は500HzでIMU読み込み、姿勢推定計算、PID計算、モーター出力をしています。モーター制御は20khzです。ドローンの操縦を行うWebsocket通信は50Hzです。Webサーバー、Websocket周りはESPAsyncWebServer、AsyncWebSocketによるイベント駆動です。 ArduinoCoreによる実装で、Wi-FiやWebサーバー、Websocketなどは別コアで動いているので制御と通信周りは別コアによるDual Core構成になっています。


プロペラ配置はX QuadでM0,M1がFrontとなっています.

書き込み
ArduinoとESP32ライブラリ、をインストールします。
- ESPAsyncWebServer
- AsyncTCP
BoardはESP32 Devkitに設定し書き込みます。
飛行方法
- ESP32が起動するとWiFi APが立ち上がる
- SSID:
ESP32-DRONE - パスワード:
12345678
- SSID:
- スマホで接続し、ブラウザで
http://192.168.4.1/を開く - Web UIでテレメトリが送られていることを確認する。Websocket通信が確立されると素早い点滅(Failsafe状態)から蛍(通常状態)のようになります。
- SETTINGSでGYROのキャリブレーションを実行、その後にACCのキャリブレーションをします。機体を動かし正しく姿勢推定できていることを確認してください。
- 必要に応じてPIDゲインを調整します。
- 必要に応じて、MOTOR TESTでプロペラ番号が合っていることを確認してください。
- 横画面にし、ARMチェックボックスを入れると800ms後にARMします。Joystickで操縦してください。
- 機体に対して揚力がかなり大きいので飛び上がりに注意してください。
- Joystickをあげた瞬間にWIFIが切れ、テレメトリがとまり高速点滅を始める場合バッテリーの電圧が足りないかバッテリーが十分に電流を流せないものの可能性があります。(安全装置がついてるバッテリーは過電流でよく止まります)
- DISARMボタンもしくはARMチェックボックス外すと即時DISARMします。機体が80度以上傾くとクラッシュ検知となりDISARMします。
CONTROL
横画面でJoystickによる操縦ができます。縦画面だとテレメトリが見えます。ARM Requestのチェックボックスを入れることでARMします。ARMできない場合はFSにARMできない理由が書いてあります。
横画面にしたところ

SETTINGS
PIDゲインの調整とセンサのキャリブレーションができます。Applyを押して反映してください。良ければSAVEを押すと不揮発領域に書き込まれ、次回電源を入れた際に読み出されます。
MOTOR TEST
プロペラを外してください。回る場所が違う場合、kMotor0Pinなどをソースコードで調整してください。
飛行している様子
note:
現状姿勢推定はMahonyフィルタとしていますが、試験的にMadgwickフィルタも実装しています。動いているようですがどちらでもあまりパフォーマンスは変わりませんでした。
static const FilterMode kFilterMode = FILTER_MAHONY; をFILTER_MADGWICKにすることで変更できます。
制御ループは500Hzです.プロポ通信は50Hzになってます.

WebSocket メッセージ仕様について
Client → Drone
| Type | Format | 用途 | レート |
|---|---|---|---|
C |
C,throttle,roll,pitch,yaw,arm |
操縦入力 | 50 Hz |
PID |
PID,ANGLE|RATE|YAW,kp,ki,kd |
PIDゲイン更新 | 任意 |
CFG |
CFG,KEY,VALUE |
設定変更 | 任意 |
CAL |
CAL,LEVEL|GYRO |
キャリブレーション要求 | 任意 |
MTEST |
MTEST,idx,thr,dur_ms / MTEST,STOP |
モータテスト | 任意 |
GET |
GET |
設定同期要求 | 接続時 |
SAVE |
SAVE |
NVSへ保存 | 任意 |
K |
K |
強制Disarm | 任意 |
Drone → Client (JSON)
| Type | 主なフィールド | 用途 | レート |
|---|---|---|---|
tel |
t, armed, roll, pitch, yaw, m0..m3, vbatt, loop_hz, fs, cmd_age |
テレメトリ | 20 Hz |
cfg |
PID/制限/オフセット一式 | 接続時の設定通知 | 接続時 |
ack |
op, tag, ok, msg |
コマンド応答 | 都度 |
ピン/ポート割当
| 機能 | 信号 | GPIO / ポート | 備考 |
|---|---|---|---|
| I2C IMU | SDA | GPIO 21 | 400 kHz |
| I2C IMU | SCL | GPIO 22 | addr 0x68 |
| Motor M0 (FL) | PWM | GPIO 15 | LEDC ch0 |
| Motor M1 (FR) | PWM | GPIO 13 | LEDC ch1 |
| Motor M2 (RR) | PWM | GPIO 12 | LEDC ch2 |
| Motor M3 (RL) | PWM | GPIO 14 | LEDC ch3 |
| Web Server | TCP | :80 (HTTP / WS) | ESPAsyncWebServer |
| Wi-Fi | SoftAP | 2.4GHz | SSID ESP32-DRONE / IP 192.168.4.1 |
| Debug | UART0 | USB | 115200 bps |
フェイルセーフ
| トリガ | 動作 | 備考 |
|---|---|---|
| WebSocket切断 | Disarm | FS_WS_DISCONNECT |
| 操縦コマンド未受信 > 300ms | Disarm | cmd_to で変更可能 |
| 機体傾き > 80° のとき | Auto Disarm | tilt_dis で変更可能 |
| IMU異常 | Disarm | 姿勢推定値が不正な場合 |
| Armラッチ後の再Arm抑止 | キル状態保持 | 明示的にDisarm要求が必要 |
English
I made a Drone Business Card that can actually fly as a drone when its components are assembled.
It was buggy and didn't fly very well for a long time, but I fixed it up for a change of pace so it can fly now. It uses an ESP32 as its core, performing 6-axis IMU sensing and PID control for the motors. To use a smartphone as a transmitter, a web server is set up on the ESP32. When you move the joystick, the input values are sent to the drone via WebSocket communication. Additionally, telemetry data such as the drone's roll, pitch, and yaw angle values, as well as voltage, can be viewed on the smartphone. The concept aims for a PCB that doubles as a flight controller and frame, similar to the Crazyflie.
名刺完成しました!(形だけ) pic.twitter.com/beSWObaRMu
— fumi (@fumi_maker) August 20, 2024
Overview
Please check out the video below to see it in action.
By assembling the components, this business card can be flown as a palm-sized miniature drone. Additionally, it has a feature where mounting a Dachshund collar LED will cause it to flash when a smartphone's NFC antenna is brought close to it.




About the Drone Business Card
Equipped with an ESP32, the PCB itself becomes the drone. It has the following features:
- Communication with a 6-axis IMU and attitude estimation
- Attitude control via PID control
- The ESP32 itself acts as a Wi-Fi access point, enabling aircraft piloting via WebSocket communication using a smartphone as a controller
- Aircraft telemetry communication
- A Web UI for adjusting PID gains and calibration
- Angle mode flight (Angle control)
What You Need
Drone Business Card PCB Parts from the BOM 3D Printer
8520 Motors (something around these)
Propellers (around 50-55mm)
1S LiPo Battery (around 300-400mAh)
A charger set is convenient.
Hardware Assembly
Please assemble it by referring to the BOM and schematic below. I designed it using KiCad. Since the project data is for a business card and contains personal information, please contact me directly if you are interested. The schematic and BOM are available on GitHub.


Printing the Motor Holders
Print the motor holders, which also serve as the drone's legs. They are available on GitHub, so please download them. I printed them using Basic PLA on a Bambu Lab X1 Carbon, but depending on the printing precision, the motors might not fit. In that case, edit the step file to adjust it to the right size. If it's too loose, taping it as a last resort works well (I ended up securing mine with tape too).
Software
The source code is published on GitHub. It uses Arduino Core and the ESP32 library.
System Architecture
The system architecture is as follows. A web server is set up on the ESP32 aboard the drone, and by accessing it from a smartphone browser, you get a controller for operating the drone and a Web UI for adjustments. The control loop runs at 500Hz, handling IMU reading, attitude estimation calculations, PID calculations, and motor output. Motor control operates at 20kHz. WebSocket communication for piloting the drone runs at 50Hz. The web server and WebSocket components are event-driven, utilizing ESPAsyncWebServer and AsyncWebSocket. Built with Arduino Core, the Wi-Fi, web server, and WebSockets run on a separate core, creating a Dual Core configuration that isolates the control and communication processes.

The piloting sequence is as follows.

The propeller layout is an X Quad configuration, with M0 and M1 at the front.

Flashing
Install Arduino and the ESP32 library.
- ESPAsyncWebServer
- AsyncTCP
Set the board to ESP32 Devkit and flash it.
How to Fly
- Once the ESP32 boots up, the WiFi AP will start.
- SSID:
ESP32-DRONE - Password:
12345678
- SSID:
- Connect via smartphone and open
http://192.168.4.1/in your browser. - Verify that telemetry is being sent on the Web UI. When the WebSocket communication is established, the LED will change from rapid flashing (Failsafe state) to glowing like a firefly (Normal state).
- In SETTINGS, perform the GYRO calibration, followed by the ACC calibration. Move the drone around to confirm that the attitude estimation is working correctly.
- Adjust the PID gains as needed.
- If necessary, use the MOTOR TEST to confirm that the propeller numbers are correct.
- Switch to landscape mode and check the ARM checkbox; it will ARM after 800ms. Pilot it using the Joystick.
- The lift is quite strong relative to the drone's weight, so be careful of it jumping up.
- If the Wi-Fi disconnects, telemetry stops, and rapid flashing begins the moment you raise the joystick, the battery voltage might be insufficient, or the battery cannot supply enough current. (Batteries with safety circuits often shut off due to overcurrent.)
- Pressing the DISARM button or unchecking the ARM checkbox will instantly DISARM the drone. If the drone tilts more than 80 degrees, it will detect a crash and DISARM automatically.
CONTROL
You can pilot it using the Joystick in landscape mode. In portrait mode, you can see the telemetry. Checking the ARM Request box will ARM the drone. If it fails to ARM, the reason will be displayed in the FS section.
Landscape view

SETTINGS
You can adjust PID gains and calibrate the sensors. Press Apply to reflect the changes. If it looks good, press SAVE to write the settings to non-volatile memory, which will be loaded the next time it's powered on.
MOTOR TEST
Please remove the propellers. If the wrong motor spins, adjust kMotor0Pin, etc., in the source code.
Flight Footage
note:
Currently, the attitude estimation uses a Mahony filter, but I've also experimentally implemented a Madgwick filter. It seems to work, but the performance didn't change much with either.
You can change it by setting static const FilterMode kFilterMode = FILTER_MAHONY; to FILTER_MADGWICK.
The control loop runs at 500Hz. Transmitter communication operates at 50Hz.

WebSocket Message Specifications
Client → Drone
| Type | Format | Purpose | Rate |
|---|---|---|---|
C |
C,throttle,roll,pitch,yaw,arm |
Control Input | 50 Hz |
PID |
PID,ANGLE|RATE|YAW,kp,ki,kd |
PID Gain Update | Any |
CFG |
CFG,KEY,VALUE |
Configuration Change | Any |
CAL |
CAL,LEVEL|GYRO |
Calibration Request | Any |
MTEST |
MTEST,idx,thr,dur_ms / MTEST,STOP |
Motor Test | Any |
GET |
GET |
Settings Sync Request | On Conn |
SAVE |
SAVE |
Save to NVS | Any |
K |
K |
Force Disarm | Any |
Drone → Client (JSON)
| Type | Main Fields | Purpose | Rate |
|---|---|---|---|
tel |
t, armed, roll, pitch, yaw, m0..m3, vbatt, loop_hz, fs, cmd_age |
Telemetry | 20 Hz |
cfg |
Full set of PID / Limits / Offsets | Settings Notification on Conn | On Conn |
ack |
op, tag, ok, msg |
Command Response | Each time |
Pin/Port Assignments
| Function | Signal | GPIO / Port | Notes |
|---|---|---|---|
| I2C IMU | SDA | GPIO 21 | 400 kHz |
| I2C IMU | SCL | GPIO 22 | addr 0x68 |
| Motor M0 (FL) | PWM | GPIO 15 | LEDC ch0 |
| Motor M1 (FR) | PWM | GPIO 13 | LEDC ch1 |
| Motor M2 (RR) | PWM | GPIO 12 | LEDC ch2 |
| Motor M3 (RL) | PWM | GPIO 14 | LEDC ch3 |
| Web Server | TCP | :80 (HTTP / WS) | ESPAsyncWebServer |
| Wi-Fi | SoftAP | 2.4GHz | SSID ESP32-DRONE / IP 192.168.4.1 |
| Debug | UART0 | USB | 115200 bps |
Failsafe
| Trigger | Action | Notes |
|---|---|---|
| WebSocket Disconnect | Disarm | FS_WS_DISCONNECT |
| Control command not received > 300ms | Disarm | Configurable via cmd_to |
| Aircraft tilt > 80° | Auto Disarm | Configurable via tilt_dis |
| IMU Error | Disarm | When attitude estimation values are invalid |
| Prevent Re-Arming after Arm Latch | Maintain Kill State | Requires an explicit Disarm request |




