Skip to content

Commit b7bbd5b

Browse files
committed
Merge branch 'master' into 2025.12-maintenance
2 parents f7337a8 + 41cb8c1 commit b7bbd5b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2044
-562
lines changed

.github/workflows/deploy.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,49 @@ jobs:
3737
- run: yarn install
3838
- run: yarn build
3939

40+
- name: Generate Capacitor Config
41+
run: node capacitor.config.generator.mjs
42+
43+
- name: Sync Capacitor Android Project
44+
run: npx cap sync android
45+
46+
- name: Set up Java
47+
uses: actions/setup-java@v4
48+
with:
49+
distribution: temurin
50+
java-version: '21'
51+
cache: gradle
52+
53+
- name: Build and Sign Android APK
54+
env:
55+
RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE }}
56+
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
57+
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
58+
RELEASE_KEY_ALIAS_PASSWORD: ${{ secrets.RELEASE_KEY_ALIAS_PASSWORD }}
59+
run: |
60+
: "${RELEASE_KEYSTORE_BASE64:?Set the RELEASE_KEYSTORE secret with your base64-encoded keystore}"
61+
: "${RELEASE_KEYSTORE_PASSWORD:?Set the RELEASE_KEYSTORE_PASSWORD secret with your keystore password}"
62+
: "${RELEASE_KEY_ALIAS:?Set the RELEASE_KEY_ALIAS secret with your key alias}"
63+
: "${RELEASE_KEY_ALIAS_PASSWORD:?Set the RELEASE_KEY_ALIAS_PASSWORD secret with your key alias password}"
64+
65+
mkdir -p android/keystore
66+
echo "$RELEASE_KEYSTORE_BASE64" | base64 --decode > android/keystore/release.jks
67+
68+
pushd android >/dev/null
69+
./gradlew clean
70+
./gradlew assembleRelease \
71+
-Pandroid.injected.signing.store.file="$PWD/keystore/release.jks" \
72+
-Pandroid.injected.signing.store.password="$RELEASE_KEYSTORE_PASSWORD" \
73+
-Pandroid.injected.signing.key.alias="$RELEASE_KEY_ALIAS" \
74+
-Pandroid.injected.signing.key.password="$RELEASE_KEY_ALIAS_PASSWORD"
75+
popd >/dev/null
76+
77+
- name: Upload APK as Artifact
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: betaflight-configurator-apk
81+
path: android/app/build/outputs/apk/release/*.apk
82+
4083
- name: Upload build artifact
4184
uses: actions/upload-artifact@v4
4285
with:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,7 @@ capacitor.config.json
4747

4848
# Ignore Gradle build output directory
4949
build
50+
51+
# Ignore Capacitor Android build output directories for local plugins
52+
capacitor-plugins/*/android/bin/
53+

CAPACITOR_SERIAL_IMPLEMENTATION.md

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
src/js/protocols/CapacitorSerial.js # Protocol adapter
2+
3+
# Betaflight Configurator - Custom Capacitor USB Serial Plugin
4+
5+
## Overview
6+
7+
This implementation creates a **custom Capacitor USB Serial plugin** specifically designed for Betaflight Configurator, replacing the patched `capacitor-plugin-usb-serial` with a clean, purpose-built solution.
8+
9+
## 🎯 Key Improvements
10+
11+
### ✅ Problems Solved
12+
13+
1. **USB Permission Issues** - Native Android permission handling works correctly
14+
2. **Binary Protocol Support** - Built-in hex string encoding/decoding for MSP protocol
15+
3. **No Patches Required** - Clean implementation without `patch-package` workarounds
16+
4. **Better Device Detection** - Automatic USB attach/detach events
17+
5. **Simplified API** - Designed specifically for Betaflight's architecture
18+
19+
### 🏗️ Architecture
20+
21+
```text
22+
betaflight-configurator/
23+
├── android/app/src/main/java/
24+
│ ├── betaflight/configurator/MainActivity.java # Registers the plugin
25+
│ └── betaflight/configurator/protocols/serial/ # Native plugin source
26+
│ ├── BetaflightSerialPlugin.java
27+
│ └── UsbPermissionReceiver.java
28+
29+
├── src/js/protocols/
30+
│ └── CapacitorSerial.js # Protocol adapter
31+
32+
└── android/app/src/main/res/xml/
33+
└── device_filter.xml # USB device filters
34+
```
35+
36+
## 📦 Plugin Structure
37+
38+
### Native Android Layer
39+
40+
**File**: `android/app/src/main/java/betaflight/configurator/protocols/serial/BetaflightSerialPlugin.java`
41+
42+
**Key Features**:
43+
- Uses `usb-serial-for-android` library (proven, mature library)
44+
- Supports all major USB-to-serial chipsets (FTDI, CP210x, CH34x, STM32, etc.)
45+
- Automatic permission request handling
46+
- Binary data transmission via hex strings
47+
- Real-time data reception through event listeners
48+
- Device attach/detach detection
49+
50+
**Methods**:
51+
- `requestPermission()` - Request USB device permissions
52+
- `getDevices()` - Get list of permitted devices
53+
- `connect(options)` - Connect to a device
54+
- `disconnect()` - Disconnect from device
55+
- `write(options)` - Write hex string data
56+
- `read()` - Read available data as hex string
57+
58+
**Events**:
59+
- `dataReceived` - Emitted when data is received
60+
- `deviceAttached` - Emitted when USB device is attached
61+
- `deviceDetached` - Emitted when USB device is detached
62+
63+
### TypeScript/JavaScript Layer
64+
65+
**File**: `src/js/protocols/CapacitorSerial.js`
66+
67+
**Purpose**: Protocol adapter that integrates the native plugin into Betaflight's serial architecture
68+
69+
**Key Features**:
70+
- Implements the same interface as WebSerial, WebBluetooth, etc.
71+
- Automatic hex string ↔ Uint8Array conversion
72+
- Event forwarding to the serial system
73+
- Android platform detection
74+
75+
## 🔗 Integration Points
76+
77+
### 1. Serial System (`src/js/serial.js`)
78+
79+
80+
**Protocol Registration:**
81+
82+
CapacitorSerial is registered in the protocol list alongside WebSerial, WebBluetooth, WebSocket, and VirtualSerial. The protocol is selected automatically for ports with the `capacitor-` prefix.
83+
84+
```javascript
85+
this._protocols = [
86+
{ name: "webserial", instance: new WebSerial() },
87+
{ name: "webbluetooth", instance: new WebBluetooth() },
88+
{ name: "capacitorserial", instance: new CapacitorSerial() },
89+
{ name: "websocket", instance: new Websocket() },
90+
{ name: "virtual", instance: new VirtualSerial() },
91+
];
92+
```
93+
94+
**Selection Logic:**
95+
The port handler chooses the protocol based on the port path prefix, e.g. `capacitor-<deviceId>`. This ensures Android USB devices are routed to the CapacitorSerial implementation.
96+
97+
### 2. Port Handler (`src/js/port_handler.js`)
98+
99+
100+
**Port Handler Updates:**
101+
Detects available Capacitor serial ports dynamically from the protocol layer
102+
- `showCapacitorOption` controls UI visibility for Capacitor ports
103+
- Device list refresh includes Capacitor devices when available
104+
- Handles attach/detach events and permission requests for Capacitor devices
105+
106+
### 3. Compatibility Checks (`src/js/utils/checkCompatibility.js`)
107+
108+
109+
**Compatibility Check:**
110+
Serial protocol compatibility is determined by `checkSerialSupport()` in `src/js/utils/checkCompatibility.js`. This function returns `true` for Android/Capacitor environments and for browsers supporting the Web Serial API. It is used to conditionally enable CapacitorSerial in the UI and protocol selection logic.
111+
112+
### 4. Android Configuration
113+
114+
**AndroidManifest.xml**:
115+
```xml
116+
<intent-filter>
117+
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
118+
</intent-filter>
119+
120+
<meta-data
121+
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
122+
android:resource="@xml/device_filter" />
123+
124+
<uses-feature android:name="android.hardware.usb.host" android:required="true" />
125+
```
126+
127+
**device_filter.xml**: Defines all supported USB devices (FTDI, STM32, CP210x, etc.)
128+
129+
## 🚀 Usage Flow
130+
131+
### Initial Setup
132+
133+
1. App launches on Android
134+
2. CapacitorSerial protocol is initialized
135+
3. Compatibility check detects Android platform
136+
4. Port handler includes Capacitor Serial in device refresh
137+
138+
### Device Connection
139+
140+
1. User requests USB permission → `requestPermission()`
141+
2. Android shows permission dialog for each device
142+
3. Granted devices appear in port list
143+
4. User selects device and connects → `connect(deviceId, baudRate)`
144+
5. Native plugin opens USB connection
145+
6. Data flows through hex string encoding/decoding
146+
147+
### Data Transmission
148+
149+
**Sending** (MSP request):
150+
```javascript
151+
// JavaScript: Uint8Array → hex string
152+
const data = new Uint8Array([0x24, 0x58, 0x00, 0x00, 0xfb]);
153+
await serial.send(data); // CapacitorSerial protocol
154+
155+
// CapacitorSerial: Uint8Array → "24580000fb"
156+
// Native: "24580000fb" → byte array → USB
157+
```
158+
159+
**Receiving** (MSP response):
160+
```javascript
161+
// Native: USB → byte array → "24580d00..." hex string
162+
// Event: dataReceived { data: "24580d00..." }
163+
// CapacitorSerial: "24580d00..." → Uint8Array
164+
// JavaScript: Uint8Array received via 'receive' event
165+
```
166+
167+
## 📚 Supported USB Chipsets
168+
169+
Via `usb-serial-for-android` library:
170+
171+
- **CDC-ACM** - USB Communication Device Class
172+
- **CP210x** - Silicon Labs (CP2102, CP2105, etc.)
173+
- **FTDI** - Future Technology Devices (FT232, FT2232, FT4232, etc.)
174+
- **PL2303** - Prolific Technology
175+
- **CH34x** - WinChipHead (CH340, CH341)
176+
- **STM32** - ST Microelectronics Virtual COM Port
177+
- **GD32** - GigaDevice Virtual COM Port
178+
- **AT32** - ArteryTek Virtual COM Port
179+
- **APM32** - Geehy APM32 Virtual COM Port
180+
- **Raspberry Pi Pico** - RP2040 USB Serial
181+
182+
All the Betaflight-compatible devices listed in `device_filter.xml`.
183+
184+
## 🔄 Migration from PR #4698
185+
186+
### What Was Removed
187+
188+
-`capacitor-plugin-usb-serial` dependency
189+
-`patch-package` dependency
190+
-`patches/capacitor-plugin-usb-serial+0.0.6.patch`
191+
192+
### What Was Added
193+
194+
- ✅ Native Android USB serial implementation embedded inside the app module
195+
- ✅ CapacitorSerial protocol adapter
196+
- ✅ Enhanced port handler support
197+
- ✅ Device filter XML configuration
198+
199+
### What Stayed the Same
200+
201+
- ✅ WebSerial protocol (for desktop browsers)
202+
- ✅ WebBluetooth protocol (for Bluetooth connections)
203+
- ✅ Overall serial architecture
204+
- ✅ MSP protocol implementation
205+
- ✅ User interface
206+
207+
## 🧪 Testing Checklist
208+
209+
### Initial Testing
210+
211+
- [ ] Install dependencies: `yarn install`
212+
- [ ] Sync Capacitor: `npx cap sync android`
213+
- [ ] Build Android app: `yarn android:run`
214+
215+
### Device Detection
216+
217+
- [ ] Connect USB OTG adapter with flight controller
218+
- [ ] App should detect USB device attach
219+
- [ ] Request permission should show Android dialog
220+
- [ ] Granted device should appear in port list
221+
222+
### Connection
223+
224+
- [ ] Select Capacitor Serial device from port list
225+
- [ ] Click Connect
226+
- [ ] Connection should establish at 115200 baud
227+
- [ ] Status should show "Connected"
228+
229+
### Communication
230+
231+
- [ ] MSP data should be sent/received correctly
232+
- [ ] Configuration should load from flight controller
233+
- [ ] Can read/write settings
234+
- [ ] Can flash firmware
235+
- [ ] Can view sensor data in real-time
236+
237+
### Stability
238+
239+
- [ ] Disconnect/reconnect multiple times
240+
- [ ] Physical device disconnect/reconnect
241+
- [ ] No memory leaks during extended use
242+
- [ ] Clean connection closure
243+
244+
## 🐛 Troubleshooting
245+
246+
### Permission Not Granted
247+
248+
**Symptom**: Permission dialog doesn't appear or permission denied
249+
250+
**Solutions**:
251+
1. Check `device_filter.xml` includes your device's VID/PID
252+
2. Verify AndroidManifest.xml has USB intent filter
253+
3. Check Android settings → Apps → Betaflight → Permissions
254+
4. Try manually revoking USB permissions and reconnecting
255+
256+
### Device Not Detected
257+
258+
**Symptom**: USB device connected but not showing in port list
259+
260+
**Solutions**:
261+
1. Verify USB OTG adapter is working (test with other apps)
262+
2. Check device is in `device_filter.xml`
263+
3. Look for logs: `adb logcat | grep BetaflightSerial`
264+
4. Ensure `usb.host` feature is declared in manifest
265+
266+
### Connection Fails
267+
268+
**Symptom**: Connect button pressed but connection doesn't establish
269+
270+
**Solutions**:
271+
1. Check USB cable quality (data lines, not just power)
272+
2. Try different baud rate (115200, 57600, 9600)
273+
3. Verify flight controller is powered properly
274+
4. Check for conflicting apps using USB device
275+
276+
### Data Not Received
277+
278+
**Symptom**: Connected but no MSP data flows
279+
280+
**Solutions**:
281+
1. Verify hex string encoding/decoding is correct
282+
2. Check event listeners are set up properly
283+
3. Monitor native logs for I/O errors
284+
4. Test with simple MSP commands first
285+
286+
## 📖 Additional Resources
287+
288+
### USB Serial for Android Library
289+
290+
- GitHub: [mik3y/usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android)
291+
- Documentation: Comprehensive driver support
292+
- License: MIT
293+
294+
### Capacitor Documentation
295+
296+
- Plugins: https://capacitorjs.com/docs/plugins
297+
- Android: https://capacitorjs.com/docs/android
298+
- Custom Plugins: https://capacitorjs.com/docs/plugins/creating-plugins
299+
300+
### Betaflight MSP Protocol
301+
302+
- MSP Protocol: Binary protocol for flight controller communication
303+
- Hex String Format: Two hex digits per byte (e.g., "24" = 0x24 = 36 decimal)
304+
305+
## 🎉 Benefits Summary
306+
307+
1. **Clean Implementation** - No patches, no workarounds
308+
2. **Better Permissions** - Native Android permission handling
309+
3. **Binary Protocol** - Built for MSP from the ground up
310+
4. **Maintainable** - All code is yours to modify
311+
5. **Extensible** - Easy to add features or fix issues
312+
6. **Documented** - Comprehensive comments and documentation
313+
7. **Tested** - Built on proven `usb-serial-for-android` library
314+
315+
## 📅 Next Steps
316+
317+
1. **Testing** - Comprehensive testing on various Android devices
318+
2. **Documentation** - User guide for Android version
319+
3. **CI/CD** - Automated Android builds
320+
4. **Release** - Beta release for Android testers
321+
5. **Feedback** - Gather user feedback and iterate
322+
323+
---
324+
325+
**Created**: November 2025
326+
**Author**: AI Assistant for Betaflight Team
327+
**License**: GPL-3.0 (same as Betaflight Configurator)

android/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ dependencies {
3535
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
3636
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
3737
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
38+
implementation 'com.github.mik3y:usb-serial-for-android:3.9.0'
3839
implementation project(':capacitor-android')
3940
testImplementation "junit:junit:$junitVersion"
4041
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"

0 commit comments

Comments
 (0)