init: initial commit
Some checks failed
CI Checks / Building (macOS-latest, stable) (push) Has been cancelled
CI Checks / Building (true, macOS-latest, nightly) (push) Has been cancelled
CI Checks / Building (true, ubuntu-latest, nightly) (push) Has been cancelled
CI Checks / Building (true, windows-latest, nightly) (push) Has been cancelled
CI Checks / Building (ubuntu-latest, stable) (push) Has been cancelled
CI Checks / Building (windows-latest, stable) (push) Has been cancelled
CI Checks / Linting (push) Has been cancelled
CI Checks / Formatting (push) Has been cancelled
Some checks failed
CI Checks / Building (macOS-latest, stable) (push) Has been cancelled
CI Checks / Building (true, macOS-latest, nightly) (push) Has been cancelled
CI Checks / Building (true, ubuntu-latest, nightly) (push) Has been cancelled
CI Checks / Building (true, windows-latest, nightly) (push) Has been cancelled
CI Checks / Building (ubuntu-latest, stable) (push) Has been cancelled
CI Checks / Building (windows-latest, stable) (push) Has been cancelled
CI Checks / Linting (push) Has been cancelled
CI Checks / Formatting (push) Has been cancelled
This commit is contained in:
29
.cargo/config.toml
Normal file
29
.cargo/config.toml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# Choose a default "cargo run" tool (see README for more info)
|
||||||
|
# - `probe-rs` provides flashing and defmt via a hardware debugger, and stack unwind on panic
|
||||||
|
# - elf2uf2-rs loads firmware over USB when the rp2040 is in boot mode
|
||||||
|
# runner = "probe-rs run --chip RP2040 --protocol swd"
|
||||||
|
# runner = "elf2uf2-rs -d"
|
||||||
|
runner = "picotool load -x -v -t elf"
|
||||||
|
linker = "flip-link"
|
||||||
|
rustflags = [
|
||||||
|
"-C",
|
||||||
|
"link-arg=--nmagic",
|
||||||
|
"-C",
|
||||||
|
"link-arg=-Tlink.x",
|
||||||
|
"-C",
|
||||||
|
"link-arg=-Tdefmt.x",
|
||||||
|
|
||||||
|
# Code-size optimizations.
|
||||||
|
# trap unreachable can save a lot of space, but requires nightly compiler.
|
||||||
|
# uncomment the next line if you wish to enable it
|
||||||
|
# "-Z", "trap-unreachable=no",
|
||||||
|
"-C",
|
||||||
|
"no-vectorize-loops",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv6m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "debug"
|
||||||
59
.github/workflows/ci_checks.yml
vendored
Normal file
59
.github/workflows/ci_checks.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name: CI Checks
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
building:
|
||||||
|
name: Building
|
||||||
|
continue-on-error: ${{ matrix.experimental || false }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# All generated code should be running on stable now
|
||||||
|
rust: [nightly, stable]
|
||||||
|
include:
|
||||||
|
# Nightly is only for reference and allowed to fail
|
||||||
|
- rust: nightly
|
||||||
|
experimental: true
|
||||||
|
os:
|
||||||
|
# Check compilation works on common OSes
|
||||||
|
# (i.e. no path issues)
|
||||||
|
- ubuntu-latest
|
||||||
|
- macOS-latest
|
||||||
|
- windows-latest
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
- run: cargo install flip-link
|
||||||
|
- run: cargo build --all
|
||||||
|
- run: cargo build --all --release
|
||||||
|
linting:
|
||||||
|
name: Linting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: clippy
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
- run: cargo clippy --all-features -- --deny=warnings
|
||||||
|
formatting:
|
||||||
|
name: Formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
- run: cargo fmt -- --check
|
||||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
*.uf2
|
||||||
|
|
||||||
|
**/*.rs.bk
|
||||||
|
.#*
|
||||||
|
.gdb_history
|
||||||
|
Cargo.lock
|
||||||
|
target/
|
||||||
|
|
||||||
|
# editor files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/*.md
|
||||||
|
!.vscode/*.svd
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/settings.json
|
||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.cargo.target": "thumbv6m-none-eabi",
|
||||||
|
"rust-analyzer.check.allTargets": false,
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
}
|
||||||
165
COPYING.LESSER
Normal file
165
COPYING.LESSER
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
84
Cargo.toml
Normal file
84
Cargo.toml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "muse2040"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "LGPL-3.0-or-later"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = { version = "1.0.0" }
|
||||||
|
|
||||||
|
defmt = "1"
|
||||||
|
defmt-rtt = "1"
|
||||||
|
format_no_std = "1.2.0"
|
||||||
|
heapless = "0.9.2"
|
||||||
|
panic-probe = { version = "1", features = ["print-defmt"] }
|
||||||
|
portable-atomic = { version = "1.11.1", features = ["critical-section"] }
|
||||||
|
static_cell = "2.1.1"
|
||||||
|
|
||||||
|
rp-pico = "0.9"
|
||||||
|
|
||||||
|
embassy-executor = { version = "0.8.0", features = ["arch-cortex-m", "executor-thread"] }
|
||||||
|
embassy-futures = "0.1.1"
|
||||||
|
embassy-time = { version = "0.4.0" }
|
||||||
|
embassy-rp = { version = "0.7.0", features = ["rp2040", "time-driver", "critical-section-impl"] }
|
||||||
|
embassy-sync = "0.7.1"
|
||||||
|
embassy-usb = "0.5.1"
|
||||||
|
|
||||||
|
cyw43 = { version = "0.4.0", features = ["bluetooth"]}
|
||||||
|
cyw43-firmware = "0.1.0"
|
||||||
|
cyw43-pio = "0.6.0"
|
||||||
|
trouble-host = { version = "0.2.4", features = ["central", "default-packet-pool"]}
|
||||||
|
|
||||||
|
# cargo build/run
|
||||||
|
[profile.dev]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true
|
||||||
|
incremental = false
|
||||||
|
opt-level = 3
|
||||||
|
overflow-checks = true
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3
|
||||||
|
overflow-checks = false
|
||||||
|
|
||||||
|
# do not optimize proc-macro crates = faster builds from scratch
|
||||||
|
[profile.dev.build-override]
|
||||||
|
codegen-units = 8
|
||||||
|
debug = false
|
||||||
|
debug-assertions = false
|
||||||
|
opt-level = 0
|
||||||
|
overflow-checks = false
|
||||||
|
|
||||||
|
[profile.release.build-override]
|
||||||
|
codegen-units = 8
|
||||||
|
debug = false
|
||||||
|
debug-assertions = false
|
||||||
|
opt-level = 0
|
||||||
|
overflow-checks = false
|
||||||
|
|
||||||
|
# cargo test
|
||||||
|
[profile.test]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true
|
||||||
|
incremental = false
|
||||||
|
opt-level = 3
|
||||||
|
overflow-checks = true
|
||||||
|
|
||||||
|
# cargo test --release
|
||||||
|
[profile.bench]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3
|
||||||
39
Embed.toml
Normal file
39
Embed.toml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
[default.probe]
|
||||||
|
protocol = "Swd"
|
||||||
|
speed = 20000
|
||||||
|
# If you only have one probe cargo embed will pick automatically
|
||||||
|
# Otherwise: add your probe's VID/PID/serial to filter
|
||||||
|
|
||||||
|
## rust-dap
|
||||||
|
# usb_vid = "6666"
|
||||||
|
# usb_pid = "4444"
|
||||||
|
# serial = "test"
|
||||||
|
|
||||||
|
|
||||||
|
[default.flashing]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[default.reset]
|
||||||
|
enabled = true
|
||||||
|
halt_afterwards = false
|
||||||
|
|
||||||
|
[default.general]
|
||||||
|
chip = "RP2040"
|
||||||
|
log_level = "WARN"
|
||||||
|
# RP2040 does not support connect_under_reset
|
||||||
|
connect_under_reset = false
|
||||||
|
|
||||||
|
[default.rtt]
|
||||||
|
enabled = true
|
||||||
|
up_mode = "NoBlockSkip"
|
||||||
|
channels = [
|
||||||
|
{ up = 0, down = 0, name = "name", up_mode = "NoBlockSkip", format = "Defmt" },
|
||||||
|
]
|
||||||
|
timeout = 3000
|
||||||
|
show_timestamps = true
|
||||||
|
log_enabled = false
|
||||||
|
log_path = "./logs"
|
||||||
|
|
||||||
|
[default.gdb]
|
||||||
|
enabled = false
|
||||||
|
gdb_connection_string = "127.0.0.1:2345"
|
||||||
13
IMPLEMENTATION.md
Normal file
13
IMPLEMENTATION.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Implementation
|
||||||
|
|
||||||
|
## Serial Packets
|
||||||
|
|
||||||
|
If you would like to control muse2040 without buttplug.io, you can open a serial connection over USB (Typically `COM3` on Windows and `/dev/ttyACM0` on Linux) and control it directly.
|
||||||
|
|
||||||
|
Since the program takes a long time to control channels (the connection with the toy is 50Hz), the serial connection only allows one channel to change at a time to increase speed.
|
||||||
|
|
||||||
|
Currently, serialising channel and intensity information for muse2040 is done through the following operation:
|
||||||
|
|
||||||
|
```python
|
||||||
|
packet = (channel << 2) + intensity # assuming channel is limited to 0-2 and intensity is limited to 0-3
|
||||||
|
```
|
||||||
100
README.md
Normal file
100
README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# muse2040
|
||||||
|
|
||||||
|
This is a Love Spouse/MuSe sex toy driver using the CYW43 wireless adaptor on the Raspberry Pi Pico W.
|
||||||
|
|
||||||
|
A patch for buttplug.io support can be found in the `patches` directory.
|
||||||
|
|
||||||
|
# Requirements
|
||||||
|
|
||||||
|
- a Raspberry Pi Pico W (unsure about the Pico 2 W)
|
||||||
|
- program requirements listed below
|
||||||
|
- a will to live
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
## Binaries
|
||||||
|
|
||||||
|
Since I lack a USB Vendor ID, I cannot distribute the binaries of muse2040 as it uses stock Pi Pico values. I am also most likely not permitted to distribute the patched version of buttplug.io and Intiface, so you'll have to compile that yourself.
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
[Git](https://git-scm.com) is required to clone the Git repositories required and apply the muse2040 patches.
|
||||||
|
[Rust](https://rust-lang.org/learn/get-started/) is required to compile muse2040, buttplug.io and Intiface.
|
||||||
|
[Flutter](https://flutter.dev/) is required to compile Intiface.
|
||||||
|
|
||||||
|
### muse2040
|
||||||
|
|
||||||
|
The `thumbv6m-none-eabi` target is required to build muse2040. You can add it by running the command `rustup target add thumbv6m-none-eabi`.
|
||||||
|
|
||||||
|
Clone the repository with `https://git.girlcock.wang/may/muse2040` and enter it with `cd muse2040`.
|
||||||
|
|
||||||
|
#### Direct Flashing
|
||||||
|
|
||||||
|
You'll need PicoTool installed. You can install it via the package manager of your choice — but since there aren't many distributions of it, you may need to build it from source. **[ [AUR](https://aur.archlinux.org/packages/picotool) | [PicoTool GitHub](https://github.com/raspberrypi/picotool) ]**
|
||||||
|
|
||||||
|
Plug your Pi Pico into your machine via USB while [holding the BOOTSEL button](https://projects-static.raspberrypi.org/projects/getting-started-with-the-pico/f67470613439ea4dc7996f9f4ce83b34c2103fb4/en/images/Pico-bootsel.png). A USB drive should appear called `RPI-RP2`, but disregard it. Run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --release
|
||||||
|
```
|
||||||
|
|
||||||
|
#### UF2 File
|
||||||
|
|
||||||
|
You'll need `elf2uf2-rs` installed. You can install it by running `cargo install elf2uf2-rs`.
|
||||||
|
|
||||||
|
Run the following to build a UF2 of muse2040:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --release
|
||||||
|
elf2uf2-rs ./target/thumbv6m-none-eabi/release/muse2040 muse2040.uf2
|
||||||
|
```
|
||||||
|
|
||||||
|
The final UF2 file can be found at the root of the repository.
|
||||||
|
|
||||||
|
## Verifying Installation
|
||||||
|
|
||||||
|
Check that the green LED on the Pico turns solid. Run `lsusb` or check the USB section of Device Manager. You should see a device called `Love Spouse/MuSe Generic Device (muse2040)` with the ID `2e8a:6969`.
|
||||||
|
|
||||||
|
If your toy only uses 1 channel, you can test it by running `python tests/test_vibration.py`. Note that you will need `pyserial` installed for this to work (`pip install pyserial`).
|
||||||
|
|
||||||
|
### buttplug.io + Intiface with muse2040 support
|
||||||
|
|
||||||
|
Go to the parent directory of muse2040 (if you're in the muse2040 directory run `cd ..`) and run the following commands to clone buttplug.io:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/buttplugio/buttplug
|
||||||
|
git clone https://github.com/intiface/intiface-central
|
||||||
|
```
|
||||||
|
|
||||||
|
To patch buttplug.io, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd buttplug
|
||||||
|
git apply ../muse2040/patches/buttplugio.diff
|
||||||
|
cargo build
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
To compile Intiface, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd intiface-central
|
||||||
|
flutter build linux # you may need to configure JAVA_HOME before executing this!
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the patched Intiface, run `build/linux/x64/release/bundle/intiface_central` in the `intiface-central` directory.
|
||||||
|
|
||||||
|
# Usage with buttplug.io
|
||||||
|
|
||||||
|
Enable **Experimental Settings** in the **App Modes** tab, then scroll down and enable **Serial Port**. Then, go to **Devices** and add a serial device with the following settings:
|
||||||
|
|
||||||
|
| | Linux | Windows |
|
||||||
|
|---------------|--------------|----------|
|
||||||
|
| Protocol Type | muse2040 | muse2040 |
|
||||||
|
| Port Name | /dev/ttyACM0 | COM3 |
|
||||||
|
| Baud Rate | 115200 | 115200 |
|
||||||
|
| Data Bits | 8 | 8 |
|
||||||
|
| Parity | N | N |
|
||||||
|
| Stop Bits | 1 | 1 |
|
||||||
|
|
||||||
|
Turning on the server and starting a scan should (hopefully) show the device.
|
||||||
201
THIRDPARTY/rp-rs/LICENSE-APACHE-2.0
Normal file
201
THIRDPARTY/rp-rs/LICENSE-APACHE-2.0
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2021 rp-rs organization
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
21
THIRDPARTY/rp-rs/LICENSE-MIT
Normal file
21
THIRDPARTY/rp-rs/LICENSE-MIT
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 rp-rs organization
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
31
build.rs
Normal file
31
build.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
// By default, Cargo will re-run a build script whenever
|
||||||
|
// any file in the project changes. By specifying `memory.x`
|
||||||
|
// here, we ensure the build script is only re-run when
|
||||||
|
// `memory.x` is changed.
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
}
|
||||||
BIN
firmware/cyw43/43439A0.bin
Normal file
BIN
firmware/cyw43/43439A0.bin
Normal file
Binary file not shown.
BIN
firmware/cyw43/43439A0_btfw.bin
Normal file
BIN
firmware/cyw43/43439A0_btfw.bin
Normal file
Binary file not shown.
BIN
firmware/cyw43/43439A0_clm.bin
Normal file
BIN
firmware/cyw43/43439A0_clm.bin
Normal file
Binary file not shown.
49
firmware/cyw43/LICENSE-permissive-binary-license-1.0.txt
Normal file
49
firmware/cyw43/LICENSE-permissive-binary-license-1.0.txt
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
Permissive Binary License
|
||||||
|
|
||||||
|
Version 1.0, July 2019
|
||||||
|
|
||||||
|
Redistribution. Redistribution and use in binary form, without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
1) Redistributions must reproduce the above copyright notice and the
|
||||||
|
following disclaimer in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
2) Unless to the extent explicitly permitted by law, no reverse
|
||||||
|
engineering, decompilation, or disassembly of this software is
|
||||||
|
permitted.
|
||||||
|
|
||||||
|
3) Redistribution as part of a software development kit must include the
|
||||||
|
accompanying file named <20>DEPENDENCIES<45> and any dependencies listed in
|
||||||
|
that file.
|
||||||
|
|
||||||
|
4) Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
Limited patent license. The copyright holders (and contributors) grant a
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free patent license to
|
||||||
|
make, have made, use, offer to sell, sell, import, and otherwise
|
||||||
|
transfer this software, where such license applies only to those patent
|
||||||
|
claims licensable by the copyright holders (and contributors) that are
|
||||||
|
necessarily infringed by this software. This patent license shall not
|
||||||
|
apply to any combinations that include this software. No hardware is
|
||||||
|
licensed hereunder.
|
||||||
|
|
||||||
|
If you institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the software
|
||||||
|
itself infringes your patent(s), then your rights granted under this
|
||||||
|
license shall terminate as of the date such litigation is filed.
|
||||||
|
|
||||||
|
DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
CONTRIBUTORS "AS IS." ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
14
firmware/cyw43/README.md
Normal file
14
firmware/cyw43/README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# WiFi + Bluetooth firmware blobs
|
||||||
|
|
||||||
|
Firmware obtained from https://github.com/georgerobotics/cyw43-driver/tree/main/firmware
|
||||||
|
|
||||||
|
Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt)
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
* 2023-08-21: synced with `a1dc885` - Update 43439 fw + clm to come from `wb43439A0_7_95_49_00_combined.h` + add Bluetooth firmware
|
||||||
|
* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 to 7.95.62
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
If you update these files, please update the lengths in the `tests/rp/src/bin/cyw43_perf.rs` test (which relies on these files running from RAM).
|
||||||
15
memory.x
Normal file
15
memory.x
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
MEMORY {
|
||||||
|
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
||||||
|
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN(BOOT2_FIRMWARE)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/* ### Boot loader */
|
||||||
|
.boot2 ORIGIN(BOOT2) :
|
||||||
|
{
|
||||||
|
KEEP(*(.boot2));
|
||||||
|
} > BOOT2
|
||||||
|
} INSERT BEFORE .text;
|
||||||
180
patches/buttplugio.diff
Normal file
180
patches/buttplugio.diff
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
diff --git a/crates/buttplug_server/src/device/protocol_impl/mod.rs b/crates/buttplug_server/src/device/protocol_impl/mod.rs
|
||||||
|
index 4e3a275d..677eb8f6 100644
|
||||||
|
--- a/crates/buttplug_server/src/device/protocol_impl/mod.rs
|
||||||
|
+++ b/crates/buttplug_server/src/device/protocol_impl/mod.rs
|
||||||
|
@@ -69,6 +69,7 @@ pub mod mizzzee_v2;
|
||||||
|
pub mod mizzzee_v3;
|
||||||
|
pub mod monsterpub;
|
||||||
|
pub mod motorbunny;
|
||||||
|
+pub mod muse2040;
|
||||||
|
pub mod mysteryvibe;
|
||||||
|
pub mod mysteryvibe_v2;
|
||||||
|
pub mod nextlevelracing;
|
||||||
|
@@ -359,6 +360,10 @@ pub fn get_default_protocol_map() -> HashMap<String, Arc<dyn ProtocolIdentifierF
|
||||||
|
&mut map,
|
||||||
|
motorbunny::setup::MotorbunnyIdentifierFactory::default(),
|
||||||
|
);
|
||||||
|
+ add_to_protocol_map(
|
||||||
|
+ &mut map,
|
||||||
|
+ muse2040::setup::MuSe2040IdentifierFactory::default(),
|
||||||
|
+ );
|
||||||
|
add_to_protocol_map(
|
||||||
|
&mut map,
|
||||||
|
mysteryvibe::setup::MysteryVibeIdentifierFactory::default(),
|
||||||
|
diff --git a/crates/buttplug_server/src/device/protocol_impl/muse2040.rs b/crates/buttplug_server/src/device/protocol_impl/muse2040.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..e267457b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/crates/buttplug_server/src/device/protocol_impl/muse2040.rs
|
||||||
|
@@ -0,0 +1,39 @@
|
||||||
|
+// Buttplug Rust Source Code File - See https://buttplug.io for more info.
|
||||||
|
+//
|
||||||
|
+// Source protected under Copyright 2025 girlcock (dot) wang. All rights reserved.
|
||||||
|
+//
|
||||||
|
+// Integration is licensed under the BSD 3-Clause license. See LICENSE file in
|
||||||
|
+// the project root for full license information.
|
||||||
|
+//
|
||||||
|
+// Driver (muse2040) is licensed under LGPLv3. See LICENSE file in the muse2040
|
||||||
|
+// project root @ https://git.girlcock.wang/may/muse2040 for full license
|
||||||
|
+// information.
|
||||||
|
+
|
||||||
|
+use crate::device::{
|
||||||
|
+ hardware::{HardwareCommand, HardwareWriteCmd},
|
||||||
|
+ protocol::{ProtocolHandler, generic_protocol_setup},
|
||||||
|
+};
|
||||||
|
+use buttplug_core::errors::ButtplugDeviceError;
|
||||||
|
+use buttplug_server_device_config::Endpoint;
|
||||||
|
+use uuid::Uuid;
|
||||||
|
+
|
||||||
|
+generic_protocol_setup!(MuSe2040, "muse2040");
|
||||||
|
+
|
||||||
|
+#[derive(Default)]
|
||||||
|
+pub struct MuSe2040 {}
|
||||||
|
+
|
||||||
|
+impl ProtocolHandler for MuSe2040 {
|
||||||
|
+ fn handle_output_vibrate_cmd(
|
||||||
|
+ &self,
|
||||||
|
+ _feature_index: u32,
|
||||||
|
+ feature_id: Uuid,
|
||||||
|
+ speed: u32,
|
||||||
|
+ ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
|
||||||
|
+
|
||||||
|
+ let data = ((_feature_index << 2) + speed) as u8;
|
||||||
|
+
|
||||||
|
+ Ok(vec![
|
||||||
|
+ HardwareWriteCmd::new(&[feature_id], Endpoint::Tx, vec![data], true).into(),
|
||||||
|
+ ])
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json b/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json
|
||||||
|
index b4f91519..95a2e108 100644
|
||||||
|
--- a/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json
|
||||||
|
+++ b/crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json
|
||||||
|
@@ -13321,6 +13321,61 @@
|
||||||
|
"name": "Muse Device"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
+ "muse2040": {
|
||||||
|
+ "communication": [
|
||||||
|
+ {
|
||||||
|
+ "serial": {
|
||||||
|
+ "baud_rate": 115200,
|
||||||
|
+ "data_bits": 8,
|
||||||
|
+ "parity": "N",
|
||||||
|
+ "port": "default",
|
||||||
|
+ "stop_bits": 1
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ ],
|
||||||
|
+ "defaults": {
|
||||||
|
+ "features": [
|
||||||
|
+ {
|
||||||
|
+ "description": "Channel 0 (single-channel devices)",
|
||||||
|
+ "id": "e9594130-280a-4ec0-aff9-c799f35321e6",
|
||||||
|
+ "output": {
|
||||||
|
+ "vibrate": {
|
||||||
|
+ "value": [
|
||||||
|
+ 0,
|
||||||
|
+ 3
|
||||||
|
+ ]
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ "description": "Channel 1",
|
||||||
|
+ "id": "12c1a484-8aae-48e2-a041-099d59f86ef6",
|
||||||
|
+ "output": {
|
||||||
|
+ "vibrate": {
|
||||||
|
+ "value": [
|
||||||
|
+ 0,
|
||||||
|
+ 3
|
||||||
|
+ ]
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ "description": "Channel 2",
|
||||||
|
+ "id": "4d541da0-f607-43b4-a4ab-90e52c3677c3",
|
||||||
|
+ "output": {
|
||||||
|
+ "vibrate": {
|
||||||
|
+ "value": [
|
||||||
|
+ 0,
|
||||||
|
+ 3
|
||||||
|
+ ]
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ ],
|
||||||
|
+ "id": "eed7545b-3016-494a-b611-77941455dfed",
|
||||||
|
+ "name": "Love Spouse/MuSe Device (muse2040)"
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
"mysteryvibe": {
|
||||||
|
"communication": [
|
||||||
|
{
|
||||||
|
diff --git a/crates/buttplug_server_device_config/device-config-v4/protocols/muse2040.yml b/crates/buttplug_server_device_config/device-config-v4/protocols/muse2040.yml
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..1fc4523e
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/crates/buttplug_server_device_config/device-config-v4/protocols/muse2040.yml
|
||||||
|
@@ -0,0 +1,39 @@
|
||||||
|
+# Integration is licensed under the BSD 3-Clause license. See LICENSE file in
|
||||||
|
+# the project root for full license information.
|
||||||
|
+#
|
||||||
|
+# Driver (muse2040) is licensed under LGPLv3. See LICENSE file in the muse2040
|
||||||
|
+# project root @ https://git.girlcock.wang/may/muse2040 for full license
|
||||||
|
+# information.
|
||||||
|
+
|
||||||
|
+defaults:
|
||||||
|
+ name: Love Spouse/MuSe Device (muse2040)
|
||||||
|
+ features:
|
||||||
|
+ - description: Channel 0 (single-channel devices)
|
||||||
|
+ id: e9594130-280a-4ec0-aff9-c799f35321e6
|
||||||
|
+ output:
|
||||||
|
+ vibrate:
|
||||||
|
+ value:
|
||||||
|
+ - 0
|
||||||
|
+ - 3
|
||||||
|
+ - description: Channel 1
|
||||||
|
+ id: 12c1a484-8aae-48e2-a041-099d59f86ef6
|
||||||
|
+ output:
|
||||||
|
+ vibrate:
|
||||||
|
+ value:
|
||||||
|
+ - 0
|
||||||
|
+ - 3
|
||||||
|
+ - description: Channel 2
|
||||||
|
+ id: 4d541da0-f607-43b4-a4ab-90e52c3677c3
|
||||||
|
+ output:
|
||||||
|
+ vibrate:
|
||||||
|
+ value:
|
||||||
|
+ - 0
|
||||||
|
+ - 3
|
||||||
|
+ id: eed7545b-3016-494a-b611-77941455dfed
|
||||||
|
+communication:
|
||||||
|
+ - serial:
|
||||||
|
+ port: default
|
||||||
|
+ baud_rate: 115200
|
||||||
|
+ data_bits: 8
|
||||||
|
+ parity: 'N'
|
||||||
|
+ stop_bits: 1
|
||||||
|
\ No newline at end of file
|
||||||
36
src/advert.rs
Normal file
36
src/advert.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (C) 2025 girlcock (dot) wang
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use trouble_host::prelude::{AdStructure, LE_GENERAL_DISCOVERABLE};
|
||||||
|
|
||||||
|
use crate::packets::MuSePacket;
|
||||||
|
|
||||||
|
pub fn build_packet(command: MuSePacket) -> ([u8; 64], usize) {
|
||||||
|
let mut adv_data = [0; 64];
|
||||||
|
|
||||||
|
let len = AdStructure::encode_slice(
|
||||||
|
&[
|
||||||
|
AdStructure::Flags(LE_GENERAL_DISCOVERABLE),
|
||||||
|
AdStructure::ManufacturerSpecificData {
|
||||||
|
company_identifier: 0xFFF0,
|
||||||
|
payload: &command,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
&mut adv_data[..],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
return (adv_data, len);
|
||||||
|
}
|
||||||
21
src/macros.rs
Normal file
21
src/macros.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (C) 2025 girlcock (dot) wang
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! muse_cmd {
|
||||||
|
($a:expr, $b:expr, $c:expr) => {
|
||||||
|
[0x6D, 0xB6, 0x43, 0xCE, 0x97, 0xFE, 0x42, 0x7C, $a, $b, $c]
|
||||||
|
};
|
||||||
|
}
|
||||||
237
src/main.rs
Normal file
237
src/main.rs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
// Copyright (C) 2025 girlcock (dot) wang
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER};
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_futures::join as future_join;
|
||||||
|
use embassy_rp::{
|
||||||
|
bind_interrupts,
|
||||||
|
gpio::{Level, Output},
|
||||||
|
peripherals::{DMA_CH0, PIO0, USB},
|
||||||
|
pio::{InterruptHandler as PIOInterruptHandler, Pio},
|
||||||
|
usb::{Driver, InterruptHandler as USBInterruptHandler},
|
||||||
|
};
|
||||||
|
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel};
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use embassy_usb::{self, class::cdc_acm::CdcAcmClass, UsbDevice};
|
||||||
|
// use heapless::String;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
use trouble_host::{
|
||||||
|
prelude::{AdvertisementParameters, DefaultPacketPool, ExternalController},
|
||||||
|
Address, Host, HostResources,
|
||||||
|
};
|
||||||
|
|
||||||
|
use embassy_usb::class::cdc_acm::State as USBState;
|
||||||
|
|
||||||
|
use crate::{advert::build_packet, packets::*};
|
||||||
|
|
||||||
|
use {defmt_rtt as _, embassy_time as _, panic_probe as _};
|
||||||
|
|
||||||
|
mod advert;
|
||||||
|
mod macros;
|
||||||
|
mod packets;
|
||||||
|
|
||||||
|
type USBDriver = Driver<'static, USB>;
|
||||||
|
type USBDevice = UsbDevice<'static, USBDriver>;
|
||||||
|
|
||||||
|
bind_interrupts!(
|
||||||
|
struct Irqs {
|
||||||
|
PIO0_IRQ_0 => PIOInterruptHandler<PIO0>;
|
||||||
|
USBCTRL_IRQ => USBInterruptHandler<USB>;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// dummy task to run the cyw event handler
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn cyw43_task(
|
||||||
|
runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>,
|
||||||
|
) -> ! {
|
||||||
|
runner.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
// dummy task to run the USB stack
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usb_task(mut usb: USBDevice) -> ! {
|
||||||
|
usb.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
|
// set up USB serial
|
||||||
|
let usb_driver = Driver::new(p.USB, Irqs);
|
||||||
|
|
||||||
|
let usb_config = {
|
||||||
|
let mut config = embassy_usb::Config::new(0x2E8A, 0x6969); // ! DEVELOPMENT ONLY! change the VID/PID if binaries are being distributed!
|
||||||
|
config.manufacturer = Some("Love Spouse/MuSe");
|
||||||
|
config.product = Some("Generic Device (muse2040)");
|
||||||
|
config.serial_number = Some("2040990000");
|
||||||
|
config.max_power = 100;
|
||||||
|
config.max_packet_size_0 = 64;
|
||||||
|
config
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut usb_builder = {
|
||||||
|
static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
|
||||||
|
static BOS_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
|
||||||
|
static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new();
|
||||||
|
|
||||||
|
let builder = embassy_usb::Builder::new(
|
||||||
|
usb_driver,
|
||||||
|
usb_config,
|
||||||
|
CONFIG_DESCRIPTOR.init([0; 256]),
|
||||||
|
BOS_DESCRIPTOR.init([0; 256]),
|
||||||
|
&mut [], // no msos descriptors
|
||||||
|
CONTROL_BUF.init([0; 64]),
|
||||||
|
);
|
||||||
|
builder
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut usb_class = {
|
||||||
|
static STATE: StaticCell<USBState> = StaticCell::new();
|
||||||
|
let state = STATE.init(USBState::new());
|
||||||
|
CdcAcmClass::new(&mut usb_builder, state, 64)
|
||||||
|
};
|
||||||
|
|
||||||
|
let usb = usb_builder.build();
|
||||||
|
|
||||||
|
unwrap!(spawner.spawn(usb_task(usb)), "usb task spawn failed");
|
||||||
|
|
||||||
|
// ~ INCLUDE FIRMWARE ~
|
||||||
|
let fw = include_bytes!("../firmware/cyw43/43439A0.bin");
|
||||||
|
let clm = include_bytes!("../firmware/cyw43/43439A0_clm.bin");
|
||||||
|
let btfw = include_bytes!("../firmware/cyw43/43439A0_btfw.bin");
|
||||||
|
|
||||||
|
// initialise cyw43 spi interface
|
||||||
|
let pwr = Output::new(p.PIN_23, Level::Low);
|
||||||
|
let cs = Output::new(p.PIN_25, Level::High);
|
||||||
|
let mut pio = Pio::new(p.PIO0, Irqs);
|
||||||
|
let spi = PioSpi::new(
|
||||||
|
&mut pio.common,
|
||||||
|
pio.sm0,
|
||||||
|
DEFAULT_CLOCK_DIVIDER,
|
||||||
|
pio.irq0,
|
||||||
|
cs,
|
||||||
|
p.PIN_24,
|
||||||
|
p.PIN_29,
|
||||||
|
p.DMA_CH0,
|
||||||
|
);
|
||||||
|
|
||||||
|
static STATE: StaticCell<cyw43::State> = StaticCell::new();
|
||||||
|
let state: &'static mut cyw43::State = STATE.init(cyw43::State::new());
|
||||||
|
|
||||||
|
// initialise the bluetooth card
|
||||||
|
let (_net_device, bt_device, mut control, runner) =
|
||||||
|
cyw43::new_with_bluetooth(state, pwr, spi, fw, btfw).await;
|
||||||
|
unwrap!(spawner.spawn(cyw43_task(runner)), "spawn failed");
|
||||||
|
control.init(clm).await;
|
||||||
|
|
||||||
|
let controller: ExternalController<_, 10> = ExternalController::new(bt_device);
|
||||||
|
|
||||||
|
let address = Address::random([0x0, 0x50, 0x1e, 0x0b, 0xb0, 0x00]); // i have to put a null byte at the end (well, start since it's little endian) for some reason
|
||||||
|
info!("[stack] ble address: {:?}", Debug2Format(&address));
|
||||||
|
|
||||||
|
// build a BLE stack
|
||||||
|
let mut resources: HostResources<DefaultPacketPool, 0, 0, 27> = HostResources::new();
|
||||||
|
|
||||||
|
let stack = trouble_host::new(controller, &mut resources).set_random_address(address);
|
||||||
|
|
||||||
|
let Host {
|
||||||
|
mut peripheral,
|
||||||
|
mut runner,
|
||||||
|
..
|
||||||
|
} = stack.build();
|
||||||
|
|
||||||
|
info!("[stack] built stack");
|
||||||
|
|
||||||
|
let mut params = AdvertisementParameters::default();
|
||||||
|
params.interval_min = Duration::from_millis(20); // 50Hz
|
||||||
|
params.interval_max = Duration::from_millis(20);
|
||||||
|
|
||||||
|
control.gpio_set(0, true).await; // turn on to indicate the driver has initialised
|
||||||
|
|
||||||
|
let channel: Channel<NoopRawMutex, u8, 1> = Channel::new();
|
||||||
|
|
||||||
|
let _ = future_join::join3(
|
||||||
|
runner.run(),
|
||||||
|
async {
|
||||||
|
let (adv, adv_len) = build_packet(C0S0); // initial state
|
||||||
|
|
||||||
|
let adata = trouble_host::prelude::Advertisement::ConnectableScannableUndirected {
|
||||||
|
adv_data: &adv[..adv_len],
|
||||||
|
scan_data: &[],
|
||||||
|
};
|
||||||
|
|
||||||
|
let _advertiser = peripheral.advertise(¶ms, adata).await.unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let bit_state = channel.receive().await;
|
||||||
|
|
||||||
|
control.gpio_set(0, false).await;
|
||||||
|
|
||||||
|
// the intensity is stored as the last two bits of the byte, with the channel being the fifth and sixth bits.
|
||||||
|
let channel = (bit_state >> 2 & 0b11) as usize;
|
||||||
|
let intensity = (bit_state >> 0 & 0b11) as usize;
|
||||||
|
|
||||||
|
let (adv, adv_len) = build_packet(LUT[channel][intensity]);
|
||||||
|
|
||||||
|
peripheral
|
||||||
|
.update_adv_data(
|
||||||
|
trouble_host::prelude::Advertisement::ConnectableScannableUndirected {
|
||||||
|
adv_data: &adv[..adv_len],
|
||||||
|
scan_data: &[],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Timer::after(Duration::from_millis(20)).await; // ! potential race condition? the bt driver advertisement timer and this run on two different clocks...
|
||||||
|
|
||||||
|
control.gpio_set(0, true).await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async {
|
||||||
|
loop {
|
||||||
|
usb_class.wait_connection().await;
|
||||||
|
|
||||||
|
let mut buf = [0; 1];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let _len = unwrap!(
|
||||||
|
usb_class.read_packet(&mut buf).await,
|
||||||
|
"could not read packet"
|
||||||
|
);
|
||||||
|
|
||||||
|
channel.send(buf[0]).await;
|
||||||
|
|
||||||
|
// // * just a shitty failsafe to get data through serial
|
||||||
|
|
||||||
|
// let mut buffer: String<64> = String::new();
|
||||||
|
// core::fmt::write(&mut buffer, format_args!("data: {}", buf[0]));
|
||||||
|
|
||||||
|
// usb_class.write_packet(buffer.as_bytes()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End of file
|
||||||
44
src/packets.rs
Normal file
44
src/packets.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (C) 2025 girlcock (dot) wang
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::muse_cmd;
|
||||||
|
|
||||||
|
pub type MuSePacket = [u8; 11];
|
||||||
|
|
||||||
|
// C<channel>S<speed>
|
||||||
|
|
||||||
|
// 0 - "all" channels
|
||||||
|
pub const C0S0: MuSePacket = muse_cmd!(0xE5, 0x15, 0x7D);
|
||||||
|
pub const C0S1: MuSePacket = muse_cmd!(0xE4, 0x9C, 0x6C);
|
||||||
|
pub const C0S2: MuSePacket = muse_cmd!(0xE7, 0x07, 0x5E);
|
||||||
|
pub const C0S3: MuSePacket = muse_cmd!(0xE6, 0x8E, 0x4F);
|
||||||
|
|
||||||
|
// 1 - Channel 1
|
||||||
|
pub const C1S0: MuSePacket = muse_cmd!(0xD5, 0x96, 0x4C);
|
||||||
|
pub const C1S1: MuSePacket = muse_cmd!(0xD4, 0x1F, 0x5D);
|
||||||
|
pub const C1S2: MuSePacket = muse_cmd!(0xD7, 0x84, 0x6F);
|
||||||
|
pub const C1S3: MuSePacket = muse_cmd!(0xD6, 0x0D, 0x7E);
|
||||||
|
|
||||||
|
// 2 - Channel 2
|
||||||
|
pub const C2S0: MuSePacket = muse_cmd!(0xA5, 0x11, 0x3F);
|
||||||
|
pub const C2S1: MuSePacket = muse_cmd!(0xA4, 0x98, 0x2E);
|
||||||
|
pub const C2S2: MuSePacket = muse_cmd!(0xA7, 0x03, 0x1C);
|
||||||
|
pub const C2S3: MuSePacket = muse_cmd!(0xA6, 0x8A, 0x0D);
|
||||||
|
|
||||||
|
pub const LUT: [[MuSePacket; 4]; 3] = [
|
||||||
|
[C0S0, C0S1, C0S2, C0S3],
|
||||||
|
[C1S0, C1S1, C1S2, C1S3],
|
||||||
|
[C2S0, C2S1, C2S2, C2S3],
|
||||||
|
];
|
||||||
22
tests/test_vibration.py
Normal file
22
tests/test_vibration.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
import serial
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
|
||||||
|
conn = serial.Serial("COM3" if os.name == "nt" else "/dev/ttyACM0")
|
||||||
|
|
||||||
|
def change(channel, intensity):
|
||||||
|
return struct.pack("B", (channel << 2) + intensity)
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("Blinking (1sec intervals)... [Press ^C to exit]\x1b[?25l")
|
||||||
|
while True:
|
||||||
|
print("\r\x1b[32m- vibrating -\x1b[0m", end="", flush=True)
|
||||||
|
conn.write(change(0, 1))
|
||||||
|
time.sleep(1)
|
||||||
|
print("\r\x1b[35m- sleeping - \x1b[0m", end="", flush=True)
|
||||||
|
conn.write(change(0, 0))
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
conn.write(change(0, 0))
|
||||||
|
print("\x1b[?25h")
|
||||||
Reference in New Issue
Block a user