Compare commits
25 Commits
Author | SHA1 | Date |
---|---|---|
Neale Pickett | bc1f181963 | |
Neale Pickett | 0f59df655f | |
Neale Pickett | d1066752be | |
Neale Pickett | 533dd49bdc | |
Neale Pickett | 36cc138443 | |
Neale Pickett | 31bfadd71d | |
Neale Pickett | b04428276d | |
Neale Pickett | e95233c120 | |
Neale Pickett | 1f61b21051 | |
Neale Pickett | 94b4c7f917 | |
Neale Pickett | 9aed973bb5 | |
Neale Pickett | bcce75feac | |
Neale Pickett | e9584169d1 | |
Neale Pickett | 2f8eab0b3b | |
Neale Pickett | e60c7bce05 | |
Neale Pickett | ac984445f9 | |
Neale Pickett | a5e691eab3 | |
Neale Pickett | afe8b66261 | |
Neale Pickett | c2b8b96578 | |
Neale Pickett | bf1e62ec0e | |
Neale Pickett | b67252e994 | |
Neale Pickett | d99f59cffa | |
Neale Pickett | a6eaae4da1 | |
Neale Pickett | d8a615813e | |
Neale Pickett | 012ee5ae31 |
|
@ -0,0 +1,10 @@
|
|||
CompileFlags:
|
||||
Add:
|
||||
- "--include-directory=/opt/arduino/hardware/arduino/avr/cores/arduino"
|
||||
- "--include-directory=/opt/arduino/hardware/arduino/avr/variants/standard"
|
||||
- "--include-directory=/opt/arduino/hardware/arduino/avr/libraries/HID/src"
|
||||
- "--include-directory=/opt/arduino/hardware/tools/avr/avr/include"
|
||||
- "--include-directory=/opt/arduino/libraries/Keyboard/src"
|
||||
- "--include-directory=/opt/arduino/libraries/HID/src"
|
||||
- "--include-directory=/home/dartcatcher/Arduino/libraries/MIDIUSB/src"
|
||||
|
|
@ -1 +1,3 @@
|
|||
build/
|
||||
case/adapter-case.stl
|
||||
pcb/pcb-backups
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"clangd.arguments": [
|
||||
"--enable-config"
|
||||
]
|
||||
}
|
27
Makefile
27
Makefile
|
@ -1,11 +1,19 @@
|
|||
FQBN = Seeeduino:samd:seeed_XIAO_m0
|
||||
FQBN_qtpy = adafruit:samd:adafruit_qtpy_m0
|
||||
FQBN_xiao = Seeeduino:samd:seeed_XIAO_m0
|
||||
UF2_MOUNT = /mnt/chromeos/removable/Arduino
|
||||
ARDUINO_DIR = /app/Arduino
|
||||
BUILDER = flatpak run --command ${ARDUINO_DIR}/arduino-builder cc.arduino.arduinoide
|
||||
|
||||
default: build/vail-adapter.xiao.uf2
|
||||
install: build/vail-adapter.xiao.uf2
|
||||
default: build/vail-adapter.qtpy.uf2 build/vail-adapter.xiao.uf2
|
||||
|
||||
install: install-xiao
|
||||
install-xiao: build/vail-adapter.xiao.uf2
|
||||
./install.sh $< $(UF2_MOUNT)
|
||||
install-qtpy: build/vail-adapter.qtpy.uf2
|
||||
./install.sh $< /mnt/chromeos/removable/QTPY_BOOT
|
||||
|
||||
clean:
|
||||
rm -rf build/*
|
||||
|
||||
# uf2conv.py is covered by an MIT license.
|
||||
build/uf2conv.py: build/uf2families.json
|
||||
|
@ -16,13 +24,19 @@ build/uf2families.json:
|
|||
mkdir -p build
|
||||
curl -L https://raw.githubusercontent.com/microsoft/uf2/master/utils/$(@F) > $@
|
||||
|
||||
%.xiao.uf2: %.ino.bin build/uf2conv.py
|
||||
%.xiao.uf2: %.xiao.bin build/uf2conv.py
|
||||
build/uf2conv.py -b 0x2000 -c -o $@ $<
|
||||
|
||||
build/%.bin: % *.cpp *.h
|
||||
%.qtpy.uf2: %.qtpy.bin build/uf2conv.py
|
||||
build/uf2conv.py -b 0x2000 -c -o $@ $<
|
||||
|
||||
build/%.qtpy.bin: FQBN = adafruit:samd:adafruit_qtpy_m0
|
||||
build/%.xiao.bin: FQBN = Seeeduino:samd:seeed_XIAO_m0
|
||||
build/vail-adapter.%.bin: vail-adapter.ino *.cpp *.h
|
||||
mkdir -p build/$*
|
||||
arduino-builder \
|
||||
-build-cache ~/.cache/arduino \
|
||||
-build-path build \
|
||||
-build-path build/$* \
|
||||
-core-api-version 10813 \
|
||||
-fqbn $(FQBN) \
|
||||
-hardware ~/.arduino15/packages \
|
||||
|
@ -34,6 +48,7 @@ build/%.bin: % *.cpp *.h
|
|||
-libraries ~/Arduino/libraries \
|
||||
-compile \
|
||||
$<
|
||||
mv build/$*/vail-adapter.ino.bin $@
|
||||
|
||||
upload: vail-adapter.ino
|
||||
arduino --upload --board $(FQBN) $<
|
||||
|
|
14
README.md
14
README.md
|
@ -13,8 +13,10 @@ License: MIT
|
|||
|
||||
* Lets you key even if you move focus to another window
|
||||
* Works with [Vail](https://vail.woozle.org/)
|
||||
* Works with [VBand](https://hamradio.solutions/vband/)
|
||||
* Optional sidetone generator for straight keying, which helps with latency
|
||||
* Works with [VBand](https://hamradio.solutions/vband/), but the window has to remain focused
|
||||
* Optional sidetone generator, which helps with latency
|
||||
* Implements all nine keyer modes from Vail, in the adapter, so you lunatics can try to key at 50WPM with no latency issues
|
||||
* Plays received signals in the adapter, so you can turn off your computer speaker
|
||||
* Free firmware updates for life
|
||||
* Can be wired up in about 5 minutes
|
||||
|
||||
|
@ -30,10 +32,10 @@ License: MIT
|
|||
|
||||
Things I plan to add:
|
||||
|
||||
* Local keyer logic for Ultimatic, Iambic, etc.
|
||||
* Local keyer generating sidetones
|
||||
* Vail site sends MIDI note events to the adapter,
|
||||
so you don't need your computer speaker on to listen.
|
||||
* [x] PCB to ease assembly and make a more robust shippable product
|
||||
* [ ] Debug tone changes
|
||||
* [ ] PCB v2 to get the speaker on pin 10 instead of pin 9
|
||||
* [ ] Unplug detection: send a pulse out one pin and detect it on the T pin to reset straight-key detection
|
||||
|
||||
|
||||
# Contributing
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
#include <Arduino.h>
|
||||
#include <Keyboard.h>
|
||||
#include <MIDIUSB.h>
|
||||
#include <cstddef>
|
||||
#include "keyers.h"
|
||||
#include "adapter.h"
|
||||
#include "polybuzzer.h"
|
||||
|
||||
#define MILLISECOND 1
|
||||
#define SECOND (1000 * MILLISECOND)
|
||||
|
||||
VailAdapter::VailAdapter(unsigned int PiezoPin) {
|
||||
this->buzzer = new PolyBuzzer(PiezoPin);
|
||||
}
|
||||
|
||||
bool VailAdapter::KeyboardMode() {
|
||||
return this->keyboardMode;
|
||||
}
|
||||
|
||||
// Send a MIDI Key Event
|
||||
void VailAdapter::midiKey(uint8_t key, bool down) {
|
||||
midiEventPacket_t event = {uint8_t(down?9:8), uint8_t(down?0x90:0x80), key, 0x7f};
|
||||
MidiUSB.sendMIDI(event);
|
||||
MidiUSB.flush();
|
||||
}
|
||||
|
||||
// Send a keyboard key event
|
||||
void VailAdapter::keyboardKey(uint8_t key, bool down) {
|
||||
if (down) {
|
||||
Keyboard.press(key);
|
||||
} else {
|
||||
Keyboard.release(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Begin transmitting
|
||||
void VailAdapter::BeginTx() {
|
||||
this->buzzer->Note(0, this->txNote);
|
||||
if (this->keyboardMode) {
|
||||
this->keyboardKey(KEY_LEFT_CTRL, true);
|
||||
} else {
|
||||
this->midiKey(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop transmitting
|
||||
void VailAdapter::EndTx() {
|
||||
this->buzzer->NoTone(0);
|
||||
if (this->keyboardMode) {
|
||||
this->keyboardKey(KEY_LEFT_CTRL, false);
|
||||
} else {
|
||||
this->midiKey(0, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a paddle being pressed.
|
||||
//
|
||||
// The caller needs to debounce keys and deal with keys wired in parallel.
|
||||
void VailAdapter::HandlePaddle(Paddle paddle, bool pressed) {
|
||||
switch (paddle) {
|
||||
case PADDLE_STRAIGHT:
|
||||
if (pressed) {
|
||||
this->BeginTx();
|
||||
} else {
|
||||
this->EndTx();
|
||||
}
|
||||
return;
|
||||
case PADDLE_DIT:
|
||||
if (this->keyer) {
|
||||
this->keyer->Key(paddle, pressed);
|
||||
} else if (this->keyboardMode) {
|
||||
this->keyboardKey(KEY_LEFT_CTRL, pressed);
|
||||
} else {
|
||||
this->midiKey(1, pressed);
|
||||
}
|
||||
break;
|
||||
case PADDLE_DAH:
|
||||
if (this->keyer) {
|
||||
this->keyer->Key(paddle, pressed);
|
||||
} else if (this->keyboardMode) {
|
||||
this->keyboardKey(KEY_RIGHT_CTRL, pressed);
|
||||
} else {
|
||||
this->midiKey(2, pressed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a MIDI event.
|
||||
//
|
||||
// We act as a MIDI
|
||||
void VailAdapter::HandleMIDI(midiEventPacket_t event) {
|
||||
uint16_t msg = (event.byte1 << 8) | (event.byte2 << 0);
|
||||
switch (event.byte1) {
|
||||
case 0xB0: // Controller Change
|
||||
switch (event.byte2) {
|
||||
case 0: // turn keyboard mode on/off
|
||||
this->keyboardMode = (event.byte3 > 0x3f);
|
||||
MidiUSB.sendMIDI(event); // Send it back to acknowledge
|
||||
break;
|
||||
case 1: // set dit duration (0-254) *2ms
|
||||
this->ditDuration = event.byte3 * 2 * MILLISECOND;
|
||||
if (this->keyer) {
|
||||
this->keyer->SetDitDuration(this->ditDuration);
|
||||
}
|
||||
break;
|
||||
case 2: // set tx note
|
||||
this->txNote = event.byte3;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xC0: // Program Change
|
||||
if (this->keyer) {
|
||||
this->keyer->Release();
|
||||
}
|
||||
this->keyer = GetKeyerByNumber(event.byte2, this);
|
||||
this->keyer->SetDitDuration(this->ditDuration);
|
||||
break;
|
||||
case 0x80: // Note off
|
||||
this->buzzer->NoTone(1);
|
||||
break;
|
||||
case 0x90: // Note on
|
||||
this->buzzer->Note(1, event.byte2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VailAdapter::Tick(unsigned millis) {
|
||||
if (this->keyer) {
|
||||
this->keyer->Tick(millis);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <MIDIUSB.h>
|
||||
#include "keyers.h"
|
||||
#include "polybuzzer.h"
|
||||
|
||||
class VailAdapter: public Transmitter {
|
||||
private:
|
||||
unsigned int txNote = 69;
|
||||
unsigned int ditDuration = 100;
|
||||
bool keyboardMode = true;
|
||||
Keyer *keyer = NULL;
|
||||
PolyBuzzer *buzzer = NULL;
|
||||
|
||||
void midiKey(uint8_t key, bool down);
|
||||
void keyboardKey(uint8_t key, bool down);
|
||||
|
||||
|
||||
public:
|
||||
VailAdapter(unsigned int PiezoPin);
|
||||
bool KeyboardMode();
|
||||
void HandlePaddle(Paddle key, bool pressed);
|
||||
void HandleMIDI(midiEventPacket_t event);
|
||||
void BeginTx();
|
||||
void EndTx();
|
||||
void Tick(unsigned millis);
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
default: body.stl cap.stl
|
||||
|
||||
%.stl: case.scad
|
||||
openscad -o $@ -D TARGET=\"$*\" $<
|
|
@ -1,16 +0,0 @@
|
|||
union() {
|
||||
import("seeed_xaio_case.stl", convexity=3);
|
||||
translate([-12.1, -2, 6]) cube([1.3, 4, 4]); // Plug up the component hole in the back
|
||||
|
||||
// Fill in the curved sides in front
|
||||
translate([-13.5, -10.485, 0]) cube([5, 1.30, 10]);
|
||||
translate([-13.5, 9.185, 0]) cube([5, 1.30, 10]);
|
||||
|
||||
// Big block for key
|
||||
translate([-40, -10.485, 0]) cube([29, 10.485*2, 10]);
|
||||
|
||||
difference() {
|
||||
translate([-40, -10.485, 0]) cube([28, 10.485*2, 30]);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,178 @@
|
|||
// Make circles lovely and round
|
||||
$fa = 1; $fs = 0.1;
|
||||
|
||||
outer_dimensions = [24.2, 21, 25];
|
||||
outer_radius = 3.7;
|
||||
wall_width = [1.3, 1.3, 1.3];
|
||||
inner_dimensions = outer_dimensions - (wall_width * 2);
|
||||
inner_radius = outer_radius * 0.64;
|
||||
z_elevate = [[0, 0, 0], [0, 0, 0], [0, 0, 0.5]]; // Multiply by this to bring bottom to z=0
|
||||
center = [[-0.5, 0, 0], [0, -0.5, 0], [0, 0, 0]]; // Multiply by this to center something on the xy axes
|
||||
xiao_elevation = 5.2; // Center of the PCB
|
||||
pcb_thickness = 1.8;
|
||||
channel = [16, 1, 1]; // Difensions of cap channel
|
||||
channel_height = 2; // How far into the case the clips need to go
|
||||
cap_thickness = 1.3;
|
||||
|
||||
module roundedcube(size=[1, 1, 1], center=false, r=0.5) {
|
||||
size = (size[0]==undef) ? [size, size, size] : size;
|
||||
cubeSize = [size[0] - 2*r, size[1] - 2*r, size[2]];
|
||||
|
||||
translate(center ? [0,0,0] : size/2) {
|
||||
union() {
|
||||
cube([size[0], cubeSize[1], size[2]], center=true);
|
||||
cube([cubeSize[0], size[1], size[2]], center=true);
|
||||
for (ymul = [-0.5, 0.5]) {
|
||||
for (xmul = [-0.5, 0.5]) {
|
||||
translate([cubeSize[0]*xmul, cubeSize[1]*ymul, 0]) cylinder(h=size[2], r=r, center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module trs_support() {
|
||||
intersection() {
|
||||
cube([10, 30, 12], center=true);
|
||||
rotate(-45, [0, 1, 0]) cube([10, 30, 12], center=true);
|
||||
}
|
||||
}
|
||||
|
||||
module usb_c() {
|
||||
cube([10, 6.5, 3.15], center=true);
|
||||
translate([0, -3.25, 0]) rotate(90, [0, 1, 0]) cylinder(d=3.15, h=10, center=true);
|
||||
translate([0, 3.25, 0]) rotate(90, [0, 1, 0]) cylinder(d=3.15, h=10, center=true);
|
||||
}
|
||||
|
||||
|
||||
module case() {
|
||||
difference() {
|
||||
translate((outer_dimensions * z_elevate) + ([0, 0, -1] * cap_thickness)) {
|
||||
difference() {
|
||||
roundedcube(outer_dimensions, center=true, r=outer_radius);
|
||||
roundedcube(inner_dimensions, center=true, r=inner_radius);
|
||||
translate(-outer_dimensions/2) cube([100, 100, cap_thickness]);
|
||||
}
|
||||
}
|
||||
|
||||
// USB C port
|
||||
translate([inner_dimensions[0]/2, 0, 0]) {
|
||||
translate([0, 0, xiao_elevation - 3.15/2]) usb_c();
|
||||
cube([10, 9.65, xiao_elevation*2 - 3.15], center=true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module body() {
|
||||
difference() {
|
||||
union() {
|
||||
case();
|
||||
|
||||
// Add some supports
|
||||
intersection() {
|
||||
union() {
|
||||
translate([13, 10, 13]) rotate(60, [0, 0, 1]) trs_support();
|
||||
translate([13, -10, 13]) rotate(-60, [0, 0, 1]) trs_support();
|
||||
|
||||
// Shelf for board to rest against
|
||||
difference() {
|
||||
translate([-8, 0, xiao_elevation]) cube([6, 19, 4], center=true);
|
||||
translate([-6, 0, xiao_elevation]) rotate(45, [0, 0, 1]) cube(15, center=true);
|
||||
}
|
||||
}
|
||||
translate(outer_dimensions * center) roundedcube(outer_dimensions, r=outer_radius);
|
||||
}
|
||||
|
||||
|
||||
// Eyes
|
||||
translate([-12, 4, 18]) sphere(r=1.2);
|
||||
translate([-12, -4, 18]) sphere(r=1.2);
|
||||
}
|
||||
|
||||
// Smirk grin
|
||||
intersection() {
|
||||
translate([0, 0, 11]) rotate(90, [0, 1, 0]) {
|
||||
difference() {
|
||||
cylinder(h=50, d=20, center=true);
|
||||
cylinder(h=50, d=19, center=true);
|
||||
}
|
||||
}
|
||||
translate([-18.7, 0, 0]) cube(7);
|
||||
}
|
||||
|
||||
// Channel in the back to make it easier to squeeze in the XIAO
|
||||
translate([-4.5, 0, xiao_elevation]) intersection() {
|
||||
cube([13, 100, pcb_thickness], center=true);
|
||||
roundedcube(inner_dimensions, center=true, r=cap_thickness);
|
||||
}
|
||||
|
||||
// Channels for bottom insertion part thingy
|
||||
for (i = [-0.5, 0.5]) {
|
||||
translate([0, inner_dimensions[1]*i, channel_height-channel[2]/2]) {
|
||||
cube(channel, center=true);
|
||||
}
|
||||
}
|
||||
|
||||
// Paddle contacts
|
||||
translate([0, 0, 10]) {
|
||||
// 8-32x 1/4 screws
|
||||
rotate(90, [1, 0, 0]) cylinder(h=30, d=4.0, center=true);
|
||||
|
||||
// Hookup wire
|
||||
translate([0, 0, -2.5]) cube([1, 30, 0.8], center=true);
|
||||
}
|
||||
|
||||
// 3.5mm TRS jack
|
||||
translate([10, 0, 14]) rotate(90, [0, 1, 0]) cylinder(h=3, d=6.0);
|
||||
|
||||
// Piezo buzzer
|
||||
translate([0, 0, 10]) {
|
||||
translate([-12, 3.5, 0]) cube([5, 1, 1], center=true);
|
||||
translate([-12, -3.5, 0]) cube([5, 1, 1], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module cap() {
|
||||
inner = inner_dimensions * 0.97; // leave 5% slop
|
||||
union() {
|
||||
intersection() {
|
||||
roundedcube(outer_dimensions, center=true, r=outer_radius);
|
||||
translate([0, 0, cap_thickness/2]) cube([100, 100, cap_thickness], center=true);
|
||||
}
|
||||
intersection() {
|
||||
roundedcube(inner, center=true, r=inner_radius);
|
||||
translate(center * inner) cube([100, 100, cap_thickness+2.2]);
|
||||
}
|
||||
difference() {
|
||||
union() {
|
||||
for (i = [-0.5, 0.5]) {
|
||||
translate([0, inner[1]*i, cap_thickness + channel_height - channel[2]*0.4]) { // 40% of channel z for slop
|
||||
difference() {
|
||||
cube(channel, center=true);
|
||||
translate([0, channel[1]*i, channel[2]/2]) rotate(90*i, [1, 0, 0]) {
|
||||
cube([channel[0], channel[1], channel[2]], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cube([5, 200, 200], center=true);
|
||||
for (i = [-1, 1]) {
|
||||
translate([9.5*i, 0, 0]) cube([5, 200, 200], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TARGET == "body") {
|
||||
body();
|
||||
} else if (TARGET == "cap") {
|
||||
cap();
|
||||
} else if (true == true) {
|
||||
color([0, 0.5, 0.5]) body();
|
||||
color([0.5, 0, 0.5]) translate([0, 0, -cap_thickness]) cap();
|
||||
} else {
|
||||
roundedcube(10);
|
||||
}
|
BIN
case/case.stl
BIN
case/case.stl
Binary file not shown.
|
@ -1,521 +0,0 @@
|
|||
|
||||
|
||||
/*
|
||||
>>> COMPACT 3D-PRINTED PADDLES <<<
|
||||
Torbjørn Skauli, LA4ZCA (tskauli@gmail.com)
|
||||
v2.0, December 2018
|
||||
Iambic paddles designed for 3D-printing. The design is simple, but provides precise movement with adjustable force and travel. Design features include a printed rocker hinge, force adjustment by a sliding spring, travel adjustment using a modified screw, ergonomic grip and general simplicity and precision.
|
||||
Changes in v2.0: The paddles have been made narrower, and the base is thinner. There is an option to remove the bottom mounting holes. The base extends under the paddles to protect them from being pushed upwards. The cable exit is on the side, to fit mounting on the QCX transceiver.
|
||||
TODO:
|
||||
-Arms 1-2 mm higher, spring channel longer towards contacts?
|
||||
Materials:
|
||||
- 3 printed parts
|
||||
- 2 screws M3x5mm, cylinder head, with washers for adjustment if needed
|
||||
- 1 screw M4 x 18-20mm, cylinder head, with lockwasher and nut
|
||||
- Compression spring, 6-8 mm in diameter
|
||||
- Compression spring, 4-5 mm in diameter
|
||||
- Cable with plug as required, up to 3.5 mm diameter
|
||||
Note: Nickel plated brass screws have been found to give the most reliable contact operation. Dimensions of screws, springs and cable can be changed in the code.
|
||||
Assembly:
|
||||
- First, prepare the 3D-printed parts by removing support material in the arm spring well and in the ends of the cable holes. Also remove any protuding edges and bumps by gently filing the surfaces.
|
||||
- Place the large spring so that it is held between the paddles approximately in the middle of the spring well. Also place the small spring in the holes at the hinge. Temporarily slide the two paddles in place. Check the spring force on the paddles and adjust as desired by either moving the spring along the well or by bending the spring to change its length. Make sure that the small spring keeps the arms in place at the hinges during use.
|
||||
- Remove 6-8 cm of the outer isolation (if present) of the cable and 1 cm of the inner isolation of each wire. Insert the cable from the back through the diagonal hole, and temporarily pull it out from the side "window". Insert the cable back into the other hole and press the cable bend into the window so that the outer isolation ends in the interior wiring well. This forms a strain relief.
|
||||
- Prepare a 18-20 mm M4 screw with cylindrical head by grinding the outer 5 mm to flatten two opposing sides. Preferably align the flattened screw end with the slot in the screw head.
|
||||
- Enter two M3 screws with cylindrical heads into the paddle arms, with the heads facing inwards. Clamp the dot and dash wire ends under these screw heads.
|
||||
- Enter the M4 screw from the bottom and clamp the ground wire underneath the lockwasher. Tighten the nut quite firmly, while allowing a small amount of adjustment of the screw angle to set the travel distance.
|
||||
- Place the spring between the paddles and slide them in place. Adjust travel by rotating the M4 screw. If the travel is asymmetric, it may be necessary to correct the difference by placing a washer under the head of one of the M3 screws in the paddle arms.
|
||||
*/
|
||||
|
||||
//*************** Rendering output control
|
||||
mountholes=true;//Whether to have mounting holes in bottom
|
||||
cacc=4; // accuracy of circles, multiplier for $fn. Use cacc=1 for dev/debug, =2-4 for final.
|
||||
preview=0; // =0 for print layout,
|
||||
// =1 or =2 for 1- or 2-arm assembly preview,
|
||||
// =3 for base only
|
||||
// =4 for arms print layout,
|
||||
// =5 base+attachment preview
|
||||
// =6 base modified for attachment to other cabinet
|
||||
// =7 attachment for inclusion in other cabinet
|
||||
// =8 rotation stopper for inclusion in other cabinet
|
||||
|
||||
//**************** main parameters of the design
|
||||
wxbase=24; // overall width, sets arm thickness etc.
|
||||
lybasemin=40; // length excl. knobs (normally 40, made longer below for QCX)
|
||||
hzarm=20; // height of main part of arm
|
||||
wallt=2; // wall thikckness
|
||||
wallmin=2; // min wall thickness under cable holes
|
||||
|
||||
dfinger=30; // approx diameter of finger for curved knob. Also knob length.
|
||||
lyknob=25; // total length of knob
|
||||
rround=1; // radius of rounded edges on knob
|
||||
txknobmin=2; // min. thickness of knob
|
||||
|
||||
yhinge=6.5; // y position of hinge relative to back
|
||||
dxwedge=4; // height of hinge wedge
|
||||
ahinginn=50; // angles for inner and outer part of hinges
|
||||
ahingout=80;
|
||||
dstopper=1.5; // diameter of stopper on top of hinge that keeps arm down
|
||||
|
||||
armsep=0.75; // arm separation from all walls
|
||||
dyarmrests=2; // y width of resting and bounding surfaces for arms
|
||||
minstroke=0.25; // minimum stroke length (at full dia of center screw)
|
||||
|
||||
//**************** parameters for non-printed parts
|
||||
dscrew1=4; // screw dia, also scales screw head height
|
||||
dscrew2=3; // screw dia, also scales screw head height
|
||||
hscrhrel=2/3; // screw head height and diam rel to diameter
|
||||
dscrhrel=7/4;
|
||||
dzcontact=0.1; // extra height of all screws
|
||||
dzsprwell=-1; // height adjustment of spring well
|
||||
dcable=4; // cable diameter
|
||||
dspring=9.5; // spring diameter
|
||||
dhingespring=5; // diameter of spring keeping hinge in place
|
||||
|
||||
//*************** Parameters for cabinet attachment, needed to make integrated paddle
|
||||
daxis=6; // Diameter of rotation axis tube
|
||||
lyflange=22; // Length and height of flange on axis tube
|
||||
hzflange=13;
|
||||
atthick=1.5;
|
||||
rotsnaph=1; // Height of snaps for paddle attachment rotation
|
||||
rotstop=7; // Size of rotation stopper shelf
|
||||
|
||||
//**************** parameters for rendering
|
||||
gap=0.2; // gap for loose fit
|
||||
tol=0.025; // general tolerance
|
||||
nil=0.001; // Negligible distance, to correct rendering
|
||||
|
||||
|
||||
lybase=lybasemin; // length of basee ex. knobs
|
||||
echo("Length of base (mm):",lybase);
|
||||
hzwall=hzarm+armsep; // total height of walls
|
||||
hzbase=2*wallmin+dcable; // height of base under arms
|
||||
ycontact=lybase-1*dscrew1; // position of contact screw
|
||||
|
||||
wxarm=wxbase/2-wallt-armsep-hscrhrel*dscrew2-minstroke-dscrew1/2; // arm thickness is the remaining space after removing many contributions to total width
|
||||
echo("Total height (mm):",hzwall+hzbase);
|
||||
echo("Total width for QCX (mm):",wxbase+dcable);
|
||||
echo("Max length of contact screws in arms (mm):",wxarm);
|
||||
echo("Min length of contact screw in base (mm):",hzbase-hscrhrel*dscrew1+armsep+hzarm/2+dscrew2*dscrhrel/2);
|
||||
echo("Min length to flatten contact screw in base (mm):",dscrew2*dscrhrel);
|
||||
wxknob=wxarm+wallt+armsep+hscrhrel*dscrew2;
|
||||
dxknob=hscrhrel*dscrew2; // x offset toward center rel to main arm
|
||||
hzknob=hzarm; // height of knobs
|
||||
lyarm=lybase-wallt-armsep/2; // knob spaced armsep/2 from base front
|
||||
|
||||
y0sprwell=yhinge-wallt-armsep+dxwedge*tan(ahingout/2); // starting pos of spring channel
|
||||
sprfloort=wallt; // thickness of floor underneath spring
|
||||
sprlen=wxbase-2*(wallt+armsep+sprfloort); // length of spring
|
||||
|
||||
dxtip=wxbase/4; // diameter of rounded tip with attachment hole
|
||||
dytip=(wxbase-dxtip)/2; // length of tip ex rouned part
|
||||
rtip=dxtip*sqrt(2)/2; // radius of tip
|
||||
ycentip=lybase+dytip-rtip/sqrt(2); // center of rounded tip
|
||||
|
||||
module teardropHole(lh,rh){ // Hole with 45-degree teardrop shape
|
||||
rotate([90,0,0])
|
||||
rotate([0,0,45])
|
||||
union(){
|
||||
cylinder(h=lh,r1=rh,r2=rh,$fn=8*cacc);
|
||||
cube([rh,rh,lh]);
|
||||
};
|
||||
};
|
||||
module snap45(snaph,snapl){ // bumps to snap parts together, max angle 45 degrees
|
||||
// snapl is length of bump, passed as parameter to allow tolerance
|
||||
difference(){
|
||||
rsnap=snaph/(1-1/sqrt(2));
|
||||
translate([rsnap-snaph,-snapl/2,0])
|
||||
rotate([-90,0,0])
|
||||
cylinder(h=snapl,r=rsnap,$fn=5*cacc);
|
||||
translate([nil,-snapl/2,-rsnap])
|
||||
cube([2*rsnap,snapl,2*rsnap]);
|
||||
};
|
||||
}; // end snap
|
||||
module wedge(a,wx,hz){ // Equilateral triangular block
|
||||
//top angle a(deg), length hz and triangle height wx in x direction
|
||||
linear_extrude(height=hz)
|
||||
polygon([[0,0],[wx,wx*tan(a/2)],[wx,-wx*tan(a/2)]]);
|
||||
};
|
||||
module cone45(dc){ // cone for stopper that keeps arm down, 45-degree slope
|
||||
rotate([180,0,0])
|
||||
cylinder(d1=dc,d2=0,h=dc*1.0,$fn=8*cacc);
|
||||
};
|
||||
module wedge_hinge(){ //wedge on wall for hinge, with cutout so that arm rests on top and bottom
|
||||
// cutout for stopper cone
|
||||
translate([0,0,hzwall])
|
||||
cone45(dstopper);
|
||||
// wedge with cutout, centered on the edge between cone and armrest,
|
||||
// to make arm better supported against up-down tilt
|
||||
difference(){
|
||||
wedge(a=ahinginn,wx=dxwedge,hz=hzwall);
|
||||
translate([-hzarm/2/sqrt(2),hzarm/2,hzarm/2+armsep-dstopper*0.75/2])
|
||||
rotate([90,0,0])
|
||||
cylinder(h=hzarm,d=hzarm,$fn=12*cacc);
|
||||
};
|
||||
};
|
||||
module bump45(bumph){ // Spherical bump with at most 45 degree angle
|
||||
rotate([0,180,0])
|
||||
difference(){
|
||||
amax=60; // max overhang angle on bump, not necessarily 45 degerees
|
||||
rsnap=bumph/(1-cos(amax));
|
||||
translate([rsnap-bumph,0,0])
|
||||
sphere(r=rsnap,$fn=5*cacc);
|
||||
translate([nil,-rsnap,-rsnap])
|
||||
cube([2*rsnap,2*rsnap,2*rsnap]);
|
||||
};
|
||||
};
|
||||
module rotsnaps(snaph){ // Rotation snaps for paddle
|
||||
rrot=(hzbase+hzwall)/2*sqrt(2)-snaph/(1-1/sqrt(2))-wallt;
|
||||
for (a=[45:90:360])
|
||||
rotate([a,0,0])
|
||||
translate([0,rrot,0])
|
||||
bump45(snaph);
|
||||
};
|
||||
module base_add(){ // parts of base that add to shape
|
||||
// base plate
|
||||
translate([-wxbase/2,0,0])
|
||||
cube([wxbase,lybase+lyknob-rround,hzbase]);
|
||||
|
||||
//rounded front
|
||||
translate([-wxbase/2+rround,lybase+lyknob-rround,0])
|
||||
minkowski(){
|
||||
cube([wxbase-2*rround,tol,hzbase]);
|
||||
// rounding cy
|
||||
cylinder(r=rround,h=tol,$fn=cacc*4);
|
||||
};
|
||||
|
||||
// walls
|
||||
translate([0,0,hzbase])
|
||||
difference(){
|
||||
translate([-wxbase/2,0,0])
|
||||
cube([wxbase,lybase,hzwall]);
|
||||
translate([-wxbase/2+wallt,wallt,0])
|
||||
cube([wxbase-2*wallt,lybase,hzwall+tol]);
|
||||
}
|
||||
|
||||
//wedge 1
|
||||
translate([wxbase/2-wallt-dxwedge,yhinge,hzbase])
|
||||
wedge_hinge();
|
||||
//wedge 2
|
||||
translate([-(wxbase/2-wallt-dxwedge),yhinge,hzbase])
|
||||
rotate([0,0,180])
|
||||
wedge_hinge();
|
||||
|
||||
// bottom arm resting surface, height armsep above base top
|
||||
translate([0,yhinge,hzbase])
|
||||
cube([wxbase-2*wallt-2*dxwedge-2,dyarmrests,2*armsep],center=true);
|
||||
|
||||
// front arm lower resting surface, normally with 2*gap airgap
|
||||
translate([-wxbase/2,lybase-dyarmrests,hzbase])
|
||||
cube([(wxbase-2*dscrew1)/2,dyarmrests,armsep-2*gap]);
|
||||
translate([wxbase/2,lybase,hzbase])
|
||||
rotate([0,0,180])
|
||||
cube([(wxbase-2*dscrew1)/2,dyarmrests,armsep-2*gap]);
|
||||
|
||||
// outer end stops, 2mm wide
|
||||
translate([-(wxbase/2-wallt),lybase-dyarmrests,hzbase])
|
||||
cube([armsep,dyarmrests,hzwall]);
|
||||
translate([(wxbase/2-wallt)-armsep,lybase-dyarmrests,hzbase])
|
||||
cube([armsep,dyarmrests,hzwall]);
|
||||
|
||||
// extra column for center screw stability
|
||||
translate([0,ycontact,hzbase])
|
||||
cylinder(d=dscrew1*dscrhrel*1.5,h=dscrew1/2+dzcontact,$fn=8*cacc);
|
||||
|
||||
// QCX attachment: add 1x cable dia of wall thickness and rotation axis
|
||||
if (preview>=5){
|
||||
|
||||
// Thicker wall
|
||||
translate([wxbase/2,0,0])
|
||||
cube([dcable,lybase,hzbase+hzwall]);
|
||||
|
||||
// Rotation stopper
|
||||
translate([wxbase/2,lybase,0])
|
||||
cube([dcable,rotstop,hzbase]);
|
||||
|
||||
// snaps for paddle rotation
|
||||
translate([wxbase/2+dcable,lybase/2,(hzbase+hzwall)/2])
|
||||
rotsnaps(rotsnaph);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
module base_sub(){ // parts of base that cut away from shape
|
||||
|
||||
// center contact screw hole
|
||||
translate([0,ycontact,0])
|
||||
cylinder(d=dscrew1,h=hzbase*9,$fn=8*cacc);
|
||||
|
||||
// center contact screw head recess (20% enlarged) filled in by cylinder to avoid need for support
|
||||
translate([0,ycontact,0])
|
||||
cylinder(d=dscrew1*dscrhrel*1.2,h=dscrew1*hscrhrel*1.2,$fn=8*cacc);
|
||||
|
||||
// Front mounting screw hole with recess
|
||||
if (mountholes && preview<5){
|
||||
translate([0,ycentip,0])
|
||||
cylinder(d=dscrew1+gap,h=hzbase,$fn=8*cacc);
|
||||
translate([0,ycentip,wallt])
|
||||
cylinder(d=dscrew1*dscrhrel*1.2,h=hzbase,$fn=8*cacc);
|
||||
};
|
||||
|
||||
// wire well
|
||||
wellw=wxbase/2; // wire well width and length
|
||||
translate([wellw/2,ycontact-2*dscrew1,wallt])
|
||||
rotate([0,0,180])
|
||||
cube([wellw,wellw,hzbase]);
|
||||
|
||||
// Back mounting screw hole in well
|
||||
if (mountholes && preview<5){
|
||||
translate([0,ycontact-2*dscrew1-wellw/2,0])
|
||||
cylinder(d=dscrew1+gap,h=hzbase,$fn=8*cacc);
|
||||
};
|
||||
|
||||
//cable holes
|
||||
if (preview<5) // Free standing paddle, no attachment
|
||||
translate([-(wxbase/2-dcable/4),ycontact-2*dscrew1-wellw-dcable/2,dcable/2+wallmin]){
|
||||
// cable hole 1
|
||||
translate([0,-dcable/2,0])
|
||||
rotate([0,0,90])
|
||||
teardropHole(lh=wxbase*2,rh=dcable/2);
|
||||
// cable hole 2
|
||||
rotate([0,0,45+90])
|
||||
teardropHole(lh=wxbase/sqrt(2)/2,rh=dcable/2);
|
||||
//cable access opening
|
||||
cube([dcable,2*dcable,dcable],center=true);
|
||||
}
|
||||
else{ // Cable routing for QCX
|
||||
// cable hole from well
|
||||
translate([0,lybase/2+dcable,dcable/2+wallmin])
|
||||
rotate([0,0,90])
|
||||
teardropHole(lh=wxbase,rh=dcable/2);
|
||||
|
||||
//cable access opening, printable without support, using breakaway wall
|
||||
for (i=[-1,1])
|
||||
translate([wxbase/2+0*wallt/2,lybase/2+i*(gap+dcable/2), wallmin]){
|
||||
cube([dcable,dcable,dcable]);
|
||||
translate([0,dcable/2,1.5*dcable])rotate([0,90,0])wedge(90,dcable/2,dcable);
|
||||
};
|
||||
|
||||
//vertical cable hole
|
||||
translate([wxbase/2+dcable-(wallt+dcable)/2,lybase/2,wallmin]){
|
||||
hhole=hzbase+hzwall/2;
|
||||
cylinder(d=dcable,h=hhole); // main hole
|
||||
translate([0,0,hhole])
|
||||
cylinder(d1=dcable,d2=0,h=dcable); // tapered top to avoid support
|
||||
};
|
||||
|
||||
// rotation axis
|
||||
translate([wxbase/2-dcable,lybase/2,(hzbase+hzwall)/2]){
|
||||
// Axis hole
|
||||
rotate([0,90,0])
|
||||
cylinder(d=daxis+gap,h=99,$fn=8*cacc);
|
||||
// Rotation snap
|
||||
};
|
||||
|
||||
// Room for square flange soldered onto rotation axis
|
||||
translate([wxbase/2-wallt,(lybase-lyflange)/2,(hzbase+hzwall-hzflange)/2]){
|
||||
cube([dcable,lyflange,hzflange]);
|
||||
translate([-tol,0,hzflange-tol])
|
||||
rotate([90,-135,180])
|
||||
wedge(90,dcable/sqrt(2),lyflange);
|
||||
};
|
||||
|
||||
}; // End QCX attachment
|
||||
|
||||
};
|
||||
module base(){ // complete base part
|
||||
difference(){
|
||||
base_add();
|
||||
base_sub();
|
||||
};
|
||||
};
|
||||
module attachment(){ // attachment that can be included in QCX (or other) cabinet
|
||||
difference(){
|
||||
union(){
|
||||
|
||||
// Attachment part of wall
|
||||
translate([0,-2*rotstop,0])
|
||||
cube([atthick,2*rotstop+lybase,hzbase+hzwall]);
|
||||
|
||||
}; // end union
|
||||
|
||||
// Axis hole
|
||||
translate([0,lybase/2,(hzbase+hzwall)/2])
|
||||
rotate([0,90,0])
|
||||
cylinder(d=daxis+gap,h=99,$fn=8*cacc);
|
||||
|
||||
// snaps for paddle rotation, slightly tight to avoid play
|
||||
translate([0,lybase/2,(hzbase+hzwall)/2])
|
||||
rotsnaps(rotsnaph-gap/3);
|
||||
|
||||
}; // end difference
|
||||
};
|
||||
module rotlimit(){ // Rotation stopper for attachment, to be included in cabinet
|
||||
|
||||
limdim=wallt+dcable; // width of stopper cube, equal to stopper on paddle base
|
||||
|
||||
// stopper knob
|
||||
translate([-dcable,lybase/2-limdim/2,(hzbase+hzwall)/2-limdim/2])// move to hole
|
||||
rotate([0*30,0,0]) // rotate if desired
|
||||
translate([0,-lybase/2-limdim/2-rotstop,(hzbase+hzwall)/2-limdim/2])
|
||||
cube([dcable,limdim,limdim]);
|
||||
|
||||
};
|
||||
module rotlimitEXPORT(){ // Rotation stopper for attachment, to be included in cabinet
|
||||
translate([0,-lybase,-hzbase-hzwall])
|
||||
rotlimit();
|
||||
};
|
||||
module attachmentEXPORT(){ // Rotation stopper for attachment, to be included in cabinet
|
||||
translate([0,-lybase,-hzbase-hzwall])
|
||||
attachment();
|
||||
};
|
||||
function attachHeightEXPORT()=hzbase+hzwall; // Export total height
|
||||
module arm_add(){ // arm base shape, without knob
|
||||
|
||||
// main arm
|
||||
cube([wxarm,lyarm,hzarm]);
|
||||
|
||||
// extra material for supporting hinge spring
|
||||
translate([-dxknob,0,0])
|
||||
cube([dxknob,y0sprwell,hzarm]);
|
||||
|
||||
// extra material near top of arm, for stiffness and appearance
|
||||
hzarmextra=hzarm/2-dzcontact-(dscrew2*dscrhrel*1.3);
|
||||
translate([-dxknob,0,hzarm-(hzarmextra-dxknob)]){
|
||||
cube([dxknob,lyarm,hzarmextra-dxknob]);
|
||||
|
||||
// 45 deg underside of extra material, to avoid generation of support
|
||||
translate([dxknob,0,0])
|
||||
rotate([90,45,180])
|
||||
wedge(90,dxknob/sqrt(2),lyarm);
|
||||
}
|
||||
|
||||
};
|
||||
module arm_sub(){ // arm shaping
|
||||
// hinge groove
|
||||
translate([wxarm-dxwedge+armsep,yhinge-wallt-armsep,0])
|
||||
wedge(a=ahingout,wx=dxwedge,hz=hzarm);
|
||||
|
||||
// hinge stopper cutout
|
||||
translate([wxarm-dxwedge+armsep,yhinge-wallt-armsep,hzarm])
|
||||
cone45(dstopper+2*gap);
|
||||
|
||||
// tension spring channel
|
||||
translate([0,y0sprwell,(hzarm-dspring)/2+dzsprwell]){
|
||||
cube([wxarm-sprfloort,ycontact-y0sprwell-2.5*dscrew1,dspring]);
|
||||
translate([-tol,0,dspring-tol])
|
||||
rotate([90,-135,180])
|
||||
wedge(90,(wxarm-sprfloort)/sqrt(2),ycontact-y0sprwell-2.5*dscrew1);
|
||||
};
|
||||
|
||||
// contact screw hole
|
||||
translate([-tol,ycontact-wallt-armsep,hzarm/2+dzcontact])
|
||||
rotate([0,0,90])
|
||||
teardropHole(lh=wxarm*2,rh=dscrew2/2-gap);
|
||||
|
||||
// hole for spring keeping hinge in place
|
||||
translate([-dxknob-tol,yhinge-wallt-armsep,hzarm/2])
|
||||
rotate([0,0,90])
|
||||
teardropHole(lh=dxknob+wxarm-dxwedge+armsep-sprfloort+tol,rh=dhingespring/2);
|
||||
|
||||
// extra space for center screw column
|
||||
translate([-(wxbase/2-wallt-armsep-wxarm),ycontact-wallt-armsep,0])
|
||||
cylinder(d=dscrew1*dscrhrel*1.5+2*minstroke+dscrew1,h=dscrew1/2+dzcontact+dscrew1*hscrhrel*1.25,$fn=8*cacc);
|
||||
|
||||
|
||||
};
|
||||
|
||||
module knob_curved(){ // finger-curved and rounded knob
|
||||
intersection(){ // cutting to outer shape
|
||||
minkowski(){ // rounding of edges
|
||||
|
||||
// un-rounded knob shrunk by rounding radius
|
||||
difference(){ // shaping finger rest
|
||||
|
||||
// knob, to be shaped by subtraction
|
||||
translate([-dxknob,lyarm,0])
|
||||
cube([wxknob-rround,lyknob-rround,hzknob-rround]);
|
||||
|
||||
// shaping of knob
|
||||
translate([-dxknob+txknobmin+dfinger/2,lyarm+0*wallt+dfinger/2,dfinger/2+0*wallt])
|
||||
minkowski(){
|
||||
cube([tol,wxbase,wxbase]);
|
||||
sphere(r=dfinger/2,$fn=8*cacc);
|
||||
};
|
||||
};
|
||||
// rounding sphere
|
||||
sphere(r=rround,$fn=cacc*4);
|
||||
};
|
||||
// outer bound of knob
|
||||
translate([-dxknob,lyarm,0])
|
||||
cube([wxknob,dfinger,hzknob]);
|
||||
};
|
||||
|
||||
};
|
||||
module arm(){ // Generate final shape according to preview and cacc settings
|
||||
difference(){
|
||||
union(){
|
||||
knob_curved();
|
||||
arm_add();
|
||||
};
|
||||
arm_sub();
|
||||
};
|
||||
};
|
||||
module build_all(){
|
||||
|
||||
if(preview==1){
|
||||
base();
|
||||
translate([wxbase/2-wallt-armsep-wxarm,wallt+armsep,hzbase+armsep])
|
||||
arm();
|
||||
}
|
||||
else if (preview==2){
|
||||
base();
|
||||
translate([wxbase/2-wallt-armsep-wxarm,wallt+armsep,hzbase+armsep])
|
||||
arm();
|
||||
|
||||
translate([-(wxbase/2-wallt-armsep-wxarm),wallt+armsep,hzbase+armsep])
|
||||
scale([-1,1,1])
|
||||
arm();
|
||||
}
|
||||
else if (preview==3){
|
||||
base();
|
||||
}
|
||||
else if (preview==4){
|
||||
translate([wxbase/2+wxknob+1,0,0])
|
||||
scale([-1,1,1])
|
||||
arm();
|
||||
|
||||
translate([wxbase/2+2*wxknob+2,0,0])
|
||||
arm();
|
||||
}
|
||||
else if (preview==5){
|
||||
base();
|
||||
translate([wxbase/2+dcable+10,0,0]){ // spacing for rendering, for preview of fit
|
||||
attachment();
|
||||
rotlimit();
|
||||
};
|
||||
}
|
||||
else if (preview==6){ // Base with additions for attachment
|
||||
base();
|
||||
}
|
||||
else if (preview==7){ // Attachment for inclusion in cabinet
|
||||
attachmentEXPORT();
|
||||
}
|
||||
else if (preview==8){ // Rotation stopper for inclusion in cabinet
|
||||
rotlimitEXPORT();
|
||||
}
|
||||
else if (preview==9){ // Rotation stopper for inclusion in cabinet
|
||||
attachmentEXPORT();
|
||||
rotlimitEXPORT();
|
||||
}
|
||||
else{
|
||||
base();
|
||||
translate([wxbase/2+wxknob+1,0,0])
|
||||
scale([-1,1,1])
|
||||
arm();
|
||||
|
||||
translate([wxbase/2+2*wxknob+2,0,0])
|
||||
arm();
|
||||
|
||||
};
|
||||
};
|
||||
build_all();
|
Binary file not shown.
|
@ -0,0 +1,55 @@
|
|||
# Vail MIDI Protocol
|
||||
|
||||
When it boots,
|
||||
the Vail adapter sends left and right Control keyboard key up and down events.
|
||||
It also shows up as a MIDI device.
|
||||
|
||||
The Vail web site sends MIDI control commands to enable MIDI keyer mode,
|
||||
tells the keyer what sideband pitch to generate,
|
||||
and can set the keyer mode.
|
||||
|
||||
|
||||
## Controller 0 - MIDI Mode
|
||||
|
||||
`b0 00 ff` will enable MIDI mode and disable Keyboard mode
|
||||
|
||||
`b0 00 00` will enable Keyboard mode and disable MIDI mode
|
||||
|
||||
|
||||
## Controller 1 - dit length
|
||||
|
||||
`b0 00 xx` will set the dit duration to `xx` times 2 milliseconds
|
||||
|
||||
|
||||
## Controller 2 - sidetone note
|
||||
|
||||
`b0 00 xx` will play note `xx` as the sidetone note
|
||||
|
||||
|
||||
## Program Change
|
||||
|
||||
`c0 xx` will change the keyer mode to `xx`.
|
||||
|
||||
|
||||
### Keyer Modes
|
||||
|
||||
* 0: passthrough (sends C# and D for dit and dah)
|
||||
* 1: cootie / straight key
|
||||
* 2: bug
|
||||
* 3: electric bug
|
||||
* 4: single dot
|
||||
* 5: ultimatic
|
||||
* 6: plain iambic
|
||||
* 7: iambic a
|
||||
* 8: iambic b
|
||||
* 9: keyahead
|
||||
|
||||
Any other mode will set to passthrough.
|
||||
|
||||
|
||||
## Notes (key down / key up)
|
||||
|
||||
`90 00 xx` will begin playing note `xx`
|
||||
`80 00 xx` will end playing note `xx`
|
||||
|
||||
These work just like a regular MIDI synthesizer.
|
|
@ -11,8 +11,8 @@ Then compile and upload the sketch.
|
|||
|
||||
## Works with no source code changes
|
||||
|
||||
* Seeeduino Xiao
|
||||
* Adafruit Qt Py
|
||||
* Seeeduino Xiao SAMD21
|
||||
* Adafruit Qt Py SAMD21
|
||||
|
||||
## Known to work with source code changes
|
||||
|
||||
|
@ -24,6 +24,18 @@ Then compile and upload the sketch.
|
|||
* Adafruit GEMMA M0
|
||||
* Adafruit Feather M0
|
||||
|
||||
## Will Not Work!
|
||||
|
||||
The RP2040 chip will not work, because it lacks a USB MCU,
|
||||
needed by the MIDIUSB library.
|
||||
I'm listing specific devices here
|
||||
in the hopes that seeing them crossed out will prevent people from
|
||||
making a purchasing mistake!
|
||||
|
||||
* ~~Seeeduino Xiao RP2040~~ Will not work!
|
||||
* ~~Adafruit Qt Pi RP2040~~ Will not work!
|
||||
* ~~Any RP2040 Device~~ Will not work!
|
||||
|
||||
|
||||
# Advanced Wiring
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# Easy Installation
|
||||
|
||||
1. Get a Seeeduino XIAO
|
||||
2. Download the most recent xiao firmware from
|
||||
1. Get a Seeeduino XIAO SAMD21
|
||||
2. Download the most recent XIAO SAMD21 firmware from
|
||||
[releases](https://github.com/nealey/vail-adapter/releases)
|
||||
3. [Enter bootloader mode](https://wiki.seeedstudio.com/Seeeduino-XIAO/#enter-bootloader-mode)
|
||||
4. Copy the firmware onto the XIAO
|
||||
4. Copy the firmware onto the XIAO SAMD21
|
||||
|
||||
[Wideo walkthrough of firmware upload](https://www.youtube.com/watch?v=IgOdkUe5SMY) (3:07)
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# MIDI Negotiation
|
||||
|
||||
Morse code keyers are very simple devices,
|
||||
they just connect two wires together.
|
||||
You could use a button if you wanted to,
|
||||
or even touch wires together.
|
||||
|
||||
The only real complication here is that some browsers
|
||||
need to get keyboard events instead of musical instrument events.
|
||||
|
||||
The Vail adapter boots into a mode that sends both keyboard events
|
||||
and MIDI messages.
|
||||
If it receives a MIDI key release event
|
||||
on channel 0
|
||||
for note C0,
|
||||
it will disable keyboard events.
|
||||
|
||||
Vail sends this "disable keyboard" MIDI event, so as soon as you
|
||||
load up Vail, the keyboard events are disabled, and your adapter
|
||||
will no longer interfere with your typing.
|
||||
If your browser doesn't support MIDI,
|
||||
the disable command can't be sent,
|
||||
and it keeps on sending keystrokes.
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
#pragma once
|
||||
|
||||
const int equalTemperamentNote[] = {
|
||||
8, // 0
|
||||
8, // 1
|
||||
9, // 2
|
||||
9, // 3
|
||||
10, // 4
|
||||
10, // 5
|
||||
11, // 6
|
||||
12, // 7
|
||||
12, // 8
|
||||
13, // 9
|
||||
14, // 10
|
||||
15, // 11
|
||||
16, // 12
|
||||
17, // 13
|
||||
18, // 14
|
||||
19, // 15
|
||||
20, // 16
|
||||
21, // 17
|
||||
23, // 18
|
||||
24, // 19
|
||||
25, // 20
|
||||
27, // 21
|
||||
29, // 22
|
||||
30, // 23
|
||||
32, // 24
|
||||
34, // 25
|
||||
36, // 26
|
||||
38, // 27
|
||||
41, // 28
|
||||
43, // 29
|
||||
46, // 30
|
||||
49, // 31
|
||||
51, // 32
|
||||
55, // 33
|
||||
58, // 34
|
||||
61, // 35
|
||||
65, // 36
|
||||
69, // 37
|
||||
73, // 38
|
||||
77, // 39
|
||||
82, // 40
|
||||
87, // 41
|
||||
92, // 42
|
||||
98, // 43
|
||||
103, // 44
|
||||
110, // 45
|
||||
116, // 46
|
||||
123, // 47
|
||||
130, // 48
|
||||
138, // 49
|
||||
146, // 50
|
||||
155, // 51
|
||||
164, // 52
|
||||
174, // 53
|
||||
185, // 54
|
||||
196, // 55
|
||||
207, // 56
|
||||
220, // 57
|
||||
233, // 58
|
||||
247, // 59
|
||||
261, // 60
|
||||
277, // 61
|
||||
293, // 62
|
||||
311, // 63
|
||||
329, // 64
|
||||
349, // 65
|
||||
370, // 66
|
||||
392, // 67
|
||||
415, // 68
|
||||
440, // 69
|
||||
466, // 70
|
||||
494, // 71
|
||||
523, // 72
|
||||
554, // 73
|
||||
587, // 74
|
||||
622, // 75
|
||||
659, // 76
|
||||
698, // 77
|
||||
740, // 78
|
||||
784, // 79
|
||||
831, // 80
|
||||
880, // 81
|
||||
932, // 82
|
||||
988, // 83
|
||||
1047, // 84
|
||||
1109, // 85
|
||||
1175, // 86
|
||||
1245, // 87
|
||||
1319, // 88
|
||||
1397, // 89
|
||||
1480, // 90
|
||||
1568, // 91
|
||||
1662, // 92
|
||||
1760, // 93
|
||||
1865, // 94
|
||||
1976, // 95
|
||||
2094, // 96
|
||||
2218, // 97
|
||||
2350, // 98
|
||||
2490, // 99
|
||||
2638, // 100
|
||||
2795, // 101
|
||||
2961, // 102
|
||||
3137, // 103
|
||||
3324, // 104
|
||||
3521, // 105
|
||||
3731, // 106
|
||||
3953, // 107
|
||||
4188, // 108
|
||||
4437, // 109
|
||||
4701, // 110
|
||||
4980, // 111
|
||||
5276, // 112
|
||||
5590, // 113
|
||||
5922, // 114
|
||||
6275, // 115
|
||||
6648, // 116
|
||||
7043, // 117
|
||||
7462, // 118
|
||||
7906, // 119
|
||||
8376, // 120
|
||||
8874, // 121
|
||||
9402, // 122
|
||||
9961, // 123
|
||||
10553, // 124
|
||||
11181, // 125
|
||||
11845, // 126
|
||||
12550, // 127
|
||||
};
|
|
@ -0,0 +1,405 @@
|
|||
#include <stddef.h>
|
||||
#include "keyers.h"
|
||||
|
||||
#define len(t) (sizeof(t)/sizeof(*t))
|
||||
|
||||
// Queue Set: A Set you can shift and pop.
|
||||
class QSet {
|
||||
int arr[MAX_KEYER_QUEUE];
|
||||
unsigned int arrlen = 0;
|
||||
|
||||
public:
|
||||
int shift() {
|
||||
if (arrlen == 0) {
|
||||
return -1;
|
||||
}
|
||||
int ret = arr[0];
|
||||
arrlen--;
|
||||
for (int i = 0; i < arrlen; i++) {
|
||||
arr[i] = arr[i+1];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pop() {
|
||||
if (arrlen == 0) {
|
||||
return -1;
|
||||
}
|
||||
int ret = arr[arrlen];
|
||||
arrlen--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void add(int val) {
|
||||
if (arrlen == MAX_KEYER_QUEUE) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < arrlen; i++) {
|
||||
if (arr[i] == val) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
arr[arrlen] = val;
|
||||
arrlen++;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class StraightKeyer: public Keyer {
|
||||
public:
|
||||
Transmitter *output;
|
||||
unsigned int ditDuration;
|
||||
bool txRelays[2];
|
||||
|
||||
StraightKeyer() {
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
void SetOutput(Transmitter *output) {
|
||||
this->output = output;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if (this->output) {
|
||||
this->output->EndTx();
|
||||
}
|
||||
this->ditDuration = 100;
|
||||
}
|
||||
|
||||
void SetDitDuration(unsigned int duration) {
|
||||
this->ditDuration = duration;
|
||||
}
|
||||
|
||||
void Release() {
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
bool TxClosed() {
|
||||
for (int i = 0; i < len(this->txRelays); i++) {
|
||||
if (this->TxClosed(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TxClosed(int relay) {
|
||||
return this->txRelays[relay];
|
||||
}
|
||||
|
||||
void Tx(int relay, bool closed) {
|
||||
bool wasClosed = this->TxClosed();
|
||||
this->txRelays[relay] = closed;
|
||||
bool nowClosed = this->TxClosed();
|
||||
|
||||
if (wasClosed != nowClosed) {
|
||||
if (nowClosed) {
|
||||
this->output->BeginTx();
|
||||
} else {
|
||||
this->output->EndTx();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
this->Tx(key, pressed);
|
||||
}
|
||||
|
||||
void Tick(unsigned int millis) {};
|
||||
};
|
||||
|
||||
class BugKeyer: public StraightKeyer {
|
||||
public:
|
||||
unsigned int nextPulse = 0;
|
||||
bool keyPressed[2];
|
||||
|
||||
using StraightKeyer::StraightKeyer;
|
||||
|
||||
void Reset() {
|
||||
StraightKeyer::Reset();
|
||||
this->nextPulse = 0;
|
||||
this->keyPressed[0] = false;
|
||||
this->keyPressed[1] = false;
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
this->keyPressed[key] = pressed;
|
||||
if (key == 0) {
|
||||
this->beginPulsing();
|
||||
} else {
|
||||
StraightKeyer::Key(key, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void Tick(unsigned int millis) {
|
||||
if (this->nextPulse && (millis >= this->nextPulse)) {
|
||||
this->pulse(millis);
|
||||
}
|
||||
}
|
||||
|
||||
void beginPulsing() {
|
||||
if (!this->nextPulse) {
|
||||
this->nextPulse = 1;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void pulse(unsigned int millis) {
|
||||
if (this->TxClosed(0)) {
|
||||
this->Tx(0, false);
|
||||
} else if (this->keyPressed[0]) {
|
||||
this->Tx(0, true);
|
||||
} else {
|
||||
this->nextPulse = 0;
|
||||
return;
|
||||
}
|
||||
this->nextPulse = millis + this->ditDuration;
|
||||
}
|
||||
};
|
||||
|
||||
class ElBugKeyer: public BugKeyer {
|
||||
public:
|
||||
unsigned int nextRepeat;
|
||||
|
||||
using BugKeyer::BugKeyer;
|
||||
|
||||
void Reset() {
|
||||
BugKeyer::Reset();
|
||||
this->nextRepeat = -1;
|
||||
}
|
||||
|
||||
// Return which key is pressed. If none, return -1.
|
||||
int whichKeyPressed() {
|
||||
for (int i = 0; i < len(this->keyPressed); i++) {
|
||||
if (this->keyPressed[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
this->keyPressed[key] = pressed;
|
||||
if (pressed) {
|
||||
this->nextRepeat = key;
|
||||
this->beginPulsing();
|
||||
} else {
|
||||
this->nextRepeat = this->whichKeyPressed();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int keyDuration(int key) {
|
||||
switch (key) {
|
||||
case PADDLE_DIT:
|
||||
return this->ditDuration;
|
||||
case PADDLE_DAH:
|
||||
return 3 * (this->ditDuration);
|
||||
}
|
||||
return this->ditDuration; // XXX
|
||||
}
|
||||
|
||||
virtual int nextTx() {
|
||||
if (this->whichKeyPressed() == -1) {
|
||||
return -1;
|
||||
}
|
||||
return this->nextRepeat;
|
||||
}
|
||||
|
||||
virtual void pulse(unsigned int millis) {
|
||||
int nextPulse = 0;
|
||||
if (this->TxClosed(0)) {
|
||||
// Pause if we're currently transmitting
|
||||
nextPulse = this->keyDuration(PADDLE_DIT);
|
||||
this->Tx(0, false);
|
||||
} else {
|
||||
int next = this->nextTx();
|
||||
if (next >= 0) {
|
||||
nextPulse = this->keyDuration(next);
|
||||
this->Tx(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextPulse) {
|
||||
this->nextPulse = millis + nextPulse;
|
||||
} else {
|
||||
this->nextPulse = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class UltimaticKeyer: public ElBugKeyer {
|
||||
public:
|
||||
QSet queue;
|
||||
|
||||
using ElBugKeyer::ElBugKeyer;
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed) {
|
||||
this->queue.add(key);
|
||||
}
|
||||
ElBugKeyer::Key(key, pressed);
|
||||
}
|
||||
|
||||
virtual int nextTx() {
|
||||
int key = this->queue.shift();
|
||||
if (key != -1) {
|
||||
return key;
|
||||
}
|
||||
return ElBugKeyer::nextTx();
|
||||
}
|
||||
};
|
||||
|
||||
class SingleDotKeyer: public ElBugKeyer {
|
||||
public:
|
||||
QSet queue;
|
||||
|
||||
using ElBugKeyer::ElBugKeyer;
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed && (key == PADDLE_DIT)) {
|
||||
this->queue.add(key);
|
||||
}
|
||||
ElBugKeyer::Key(key, pressed);
|
||||
}
|
||||
|
||||
virtual int nextTx() {
|
||||
int key = this->queue.shift();
|
||||
if (key != -1) {
|
||||
return key;
|
||||
}
|
||||
if (this->keyPressed[1]) return 1;
|
||||
if (this->keyPressed[0]) return 0;
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
class IambicKeyer: public ElBugKeyer {
|
||||
public:
|
||||
|
||||
using ElBugKeyer::ElBugKeyer;
|
||||
|
||||
virtual int nextTx() {
|
||||
int next = ElBugKeyer::nextTx();
|
||||
if (this->keyPressed[PADDLE_DIT] && this->keyPressed[PADDLE_DAH]) {
|
||||
this->nextRepeat = 1 - this->nextRepeat;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
};
|
||||
|
||||
class IambicAKeyer: public IambicKeyer {
|
||||
public:
|
||||
QSet queue;
|
||||
|
||||
using IambicKeyer::IambicKeyer;
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed && (key == PADDLE_DIT)) {
|
||||
this->queue.add(key);
|
||||
}
|
||||
IambicKeyer::Key(key, pressed);
|
||||
}
|
||||
|
||||
virtual int nextTx() {
|
||||
int next = IambicKeyer::nextTx();
|
||||
int key = this->queue.shift();
|
||||
if (key != -1) {
|
||||
return key;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
};
|
||||
|
||||
class IambicBKeyer: public IambicKeyer {
|
||||
public:
|
||||
QSet queue;
|
||||
|
||||
using IambicKeyer::IambicKeyer;
|
||||
|
||||
void Reset() {
|
||||
IambicKeyer::Reset();
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed) {
|
||||
this->queue.add(key);
|
||||
}
|
||||
IambicKeyer::Key(key, pressed);
|
||||
}
|
||||
|
||||
virtual int nextTx() {
|
||||
for (int key = 0; key < len(this->keyPressed); key++) {
|
||||
if (this->keyPressed[key]) {
|
||||
this->queue.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
return this->queue.shift();
|
||||
}
|
||||
};
|
||||
|
||||
class KeyaheadKeyer: public ElBugKeyer {
|
||||
public:
|
||||
int queue[MAX_KEYER_QUEUE];
|
||||
unsigned int qlen;
|
||||
|
||||
using ElBugKeyer::ElBugKeyer;
|
||||
|
||||
void Reset() {
|
||||
ElBugKeyer::Reset();
|
||||
this->qlen = 0;
|
||||
}
|
||||
|
||||
void Key(Paddle key, bool pressed) {
|
||||
if (pressed) {
|
||||
if (this->qlen < MAX_KEYER_QUEUE) {
|
||||
this->queue[this->qlen++] = key;
|
||||
}
|
||||
}
|
||||
ElBugKeyer::Key(key, pressed);
|
||||
}
|
||||
|
||||
virtual int nextTx() {
|
||||
if (this->qlen > 0) {
|
||||
int next = this->queue[0];
|
||||
this->qlen--;
|
||||
for (int i = 0; i < this->qlen; i++) {
|
||||
this->queue[i] = this->queue[i+1];
|
||||
}
|
||||
return next;
|
||||
}
|
||||
return ElBugKeyer::nextTx();
|
||||
}
|
||||
};
|
||||
|
||||
StraightKeyer straightKeyer = StraightKeyer();
|
||||
BugKeyer bugKeyer = BugKeyer();
|
||||
ElBugKeyer elBugKeyer = ElBugKeyer();
|
||||
SingleDotKeyer singleDotKeyer = SingleDotKeyer();
|
||||
UltimaticKeyer ultimaticKeyer = UltimaticKeyer();
|
||||
IambicKeyer iambicKeyer = IambicKeyer();
|
||||
IambicAKeyer iambicAKeyer = IambicAKeyer();
|
||||
IambicBKeyer iambicBKeyer = IambicBKeyer();
|
||||
KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
|
||||
|
||||
Keyer *keyers[] = {
|
||||
NULL,
|
||||
&straightKeyer,
|
||||
&bugKeyer,
|
||||
&elBugKeyer,
|
||||
&singleDotKeyer,
|
||||
&ultimaticKeyer,
|
||||
&iambicKeyer,
|
||||
&iambicAKeyer,
|
||||
&iambicBKeyer,
|
||||
&keyaheadKeyer,
|
||||
};
|
||||
|
||||
Keyer *GetKeyerByNumber(int n, Transmitter *output) {
|
||||
if (n >= len(keyers)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Keyer *k = keyers[n];
|
||||
k->SetOutput(output);
|
||||
return k;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#define MAX_KEYER_QUEUE 5
|
||||
|
||||
typedef enum {
|
||||
PADDLE_DIT = 0,
|
||||
PADDLE_DAH = 1,
|
||||
PADDLE_STRAIGHT,
|
||||
} Paddle;
|
||||
|
||||
class Transmitter {
|
||||
public:
|
||||
virtual void BeginTx();
|
||||
virtual void EndTx();
|
||||
};
|
||||
|
||||
class Keyer {
|
||||
public:
|
||||
virtual void SetOutput(Transmitter *output);
|
||||
virtual void Reset();
|
||||
virtual void SetDitDuration(unsigned int d);
|
||||
virtual void Release();
|
||||
virtual bool TxClosed();
|
||||
virtual bool TxClosed(int relay);
|
||||
virtual void Tx(int relay, bool closed);
|
||||
virtual void Key(Paddle key, bool pressed);
|
||||
virtual void Tick(unsigned int millis);
|
||||
};
|
||||
|
||||
Keyer *GetKeyerByNumber(int n, Transmitter *output);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
(fp_lib_table
|
||||
(lib (name "Seeeduino XIAO")(type "KiCad")(uri "/home/dartcatcher/.var/app/org.kicad.KiCad/data/kicad/6.0/footprints/Seeeduino XIAO KICAD")(options "")(descr ""))
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"board": {
|
||||
"active_layer": 33,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": true,
|
||||
"hidden_netclasses": [],
|
||||
"hidden_nets": [],
|
||||
"high_contrast_mode": 0,
|
||||
"net_color_mode": 1,
|
||||
"opacity": {
|
||||
"images": 0.6,
|
||||
"pads": 1.0,
|
||||
"tracks": 1.0,
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"ratsnest_display_mode": 0,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
"graphics": true,
|
||||
"keepouts": true,
|
||||
"lockedItems": true,
|
||||
"otherItems": true,
|
||||
"pads": true,
|
||||
"text": true,
|
||||
"tracks": true,
|
||||
"vias": true,
|
||||
"zones": true
|
||||
},
|
||||
"visible_items": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36
|
||||
],
|
||||
"visible_layers": "fffffff_ffffffff",
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
"repo_password": "",
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
"ssh_key": ""
|
||||
},
|
||||
"meta": {
|
||||
"filename": "pcb.kicad_prl",
|
||||
"version": 3
|
||||
},
|
||||
"project": {
|
||||
"files": []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,652 @@
|
|||
{
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"apply_defaults_to_fp_fields": false,
|
||||
"apply_defaults_to_fp_shapes": false,
|
||||
"apply_defaults_to_fp_text": false,
|
||||
"board_outline_line_width": 0.09999999999999999,
|
||||
"copper_line_width": 0.19999999999999998,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.049999999999999996,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": false,
|
||||
"text_position": 0,
|
||||
"units_format": 1
|
||||
},
|
||||
"fab_line_width": 0.09999999999999999,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.15,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 0.4,
|
||||
"height": 2.2,
|
||||
"width": 1.2
|
||||
},
|
||||
"silk_line_width": 0.15,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.15,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"45_degree_only": false,
|
||||
"min_clearance": 0.508
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [
|
||||
{
|
||||
"gap": 0.0,
|
||||
"via_gap": 0.0,
|
||||
"width": 0.0
|
||||
}
|
||||
],
|
||||
"drc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_symbol_mismatch": "warning",
|
||||
"footprint_type_mismatch": "error",
|
||||
"hole_clearance": "error",
|
||||
"hole_near_hole": "error",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"net_conflict": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
"padstack": "error",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zone_has_empty_net": "error",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"allow_blind_buried_vias": false,
|
||||
"allow_microvias": false,
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.0,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.0,
|
||||
"min_hole_clearance": 0.25,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.19999999999999998,
|
||||
"min_microvia_drill": 0.09999999999999999,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.7999999999999999,
|
||||
"min_text_thickness": 0.08,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.19999999999999998,
|
||||
"min_via_annular_width": 0.049999999999999996,
|
||||
"min_via_diameter": 0.39999999999999997,
|
||||
"solder_mask_clearance": 0.0,
|
||||
"solder_mask_min_width": 0.0,
|
||||
"solder_mask_to_copper_clearance": 0.0,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_onpadsmd": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_ontrackend": false,
|
||||
"td_onviapad": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [
|
||||
0.0
|
||||
],
|
||||
"tuning_pattern_settings": {
|
||||
"diff_pair_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 1.0
|
||||
},
|
||||
"diff_pair_skew_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
},
|
||||
"single_track_defaults": {
|
||||
"corner_radius_percentage": 80,
|
||||
"corner_style": 1,
|
||||
"max_amplitude": 1.0,
|
||||
"min_amplitude": 0.2,
|
||||
"single_sided": false,
|
||||
"spacing": 0.6
|
||||
}
|
||||
},
|
||||
"via_dimensions": [
|
||||
{
|
||||
"diameter": 0.0,
|
||||
"drill": 0.0
|
||||
}
|
||||
],
|
||||
"zones_allow_external_fillets": false,
|
||||
"zones_use_no_outline": true
|
||||
},
|
||||
"ipc2581": {
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": ""
|
||||
},
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_label_syntax": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"conflicting_netclasses": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"global_label_dangling": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"label_dangling": "error",
|
||||
"lib_symbol_issues": "warning",
|
||||
"missing_bidi_pin": "warning",
|
||||
"missing_input_pin": "warning",
|
||||
"missing_power_pin": "error",
|
||||
"missing_unit": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"similar_labels": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"unannotated": "error",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "pcb.kicad_pro",
|
||||
"version": 1
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.5,
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 6
|
||||
},
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "I2C",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.5,
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 6
|
||||
},
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Power",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.8,
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 3
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
"netclass_patterns": [
|
||||
{
|
||||
"netclass": "Power",
|
||||
"pattern": "GND"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"gencad": "",
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"pos_files": "",
|
||||
"specctra_dsn": "",
|
||||
"step": "",
|
||||
"svg": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
"field_delimiter": ",",
|
||||
"keep_line_breaks": false,
|
||||
"keep_tabs": false,
|
||||
"name": "CSV",
|
||||
"ref_delimiter": ",",
|
||||
"ref_range_delimiter": "",
|
||||
"string_delimiter": "\""
|
||||
},
|
||||
"bom_presets": [],
|
||||
"bom_settings": {
|
||||
"exclude_dnp": false,
|
||||
"fields_ordered": [
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reference",
|
||||
"name": "Reference",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Value",
|
||||
"name": "Value",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Footprint",
|
||||
"name": "Footprint",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Qty",
|
||||
"name": "${QUANTITY}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "DNP",
|
||||
"name": "${DNP}",
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"name": "Grouped By Value",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
"dashed_lines_gap_length_ratio": 3.0,
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"operating_point_overlay_i_precision": 3,
|
||||
"operating_point_overlay_i_range": "~A",
|
||||
"operating_point_overlay_v_precision": 3,
|
||||
"operating_point_overlay_v_range": "~V",
|
||||
"overbar_offset_ratio": 1.23,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"net_format_name": "",
|
||||
"ngspice": {
|
||||
"fix_include_paths": true,
|
||||
"fix_passive_vals": false,
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"model_mode": 0,
|
||||
"workbook_filename": ""
|
||||
},
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"spice_adjust_passive_values": false,
|
||||
"spice_current_sheet_as_root": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
"spice_model_current_sheet_as_root": true,
|
||||
"spice_save_all_currents": false,
|
||||
"spice_save_all_dissipations": false,
|
||||
"spice_save_all_voltages": false,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"bc199109-bc84-4207-bd79-7d43ff8dd6f2",
|
||||
"Root"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,559 @@
|
|||
(kicad_sch (version 20211123) (generator eeschema)
|
||||
|
||||
(uuid bc199109-bc84-4207-bd79-7d43ff8dd6f2)
|
||||
|
||||
(paper "A4")
|
||||
|
||||
(lib_symbols
|
||||
(symbol "Connector:AudioJack3" (in_bom yes) (on_board yes)
|
||||
(property "Reference" "J" (id 0) (at 0 8.89 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Value" "AudioJack3" (id 1) (at 0 6.35 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Footprint" "" (id 2) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "~" (id 3) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_keywords" "audio jack receptacle stereo headphones phones TRS connector" (id 4) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_description" "Audio Jack, 3 Poles (Stereo / TRS)" (id 5) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_fp_filters" "Jack*" (id 6) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(symbol "AudioJack3_0_1"
|
||||
(rectangle (start -5.08 -5.08) (end -6.35 -2.54)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type outline))
|
||||
)
|
||||
(polyline
|
||||
(pts
|
||||
(xy 0 -2.54)
|
||||
(xy 0.635 -3.175)
|
||||
(xy 1.27 -2.54)
|
||||
(xy 2.54 -2.54)
|
||||
)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
(polyline
|
||||
(pts
|
||||
(xy -1.905 -2.54)
|
||||
(xy -1.27 -3.175)
|
||||
(xy -0.635 -2.54)
|
||||
(xy -0.635 0)
|
||||
(xy 2.54 0)
|
||||
)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
(polyline
|
||||
(pts
|
||||
(xy 2.54 2.54)
|
||||
(xy -2.54 2.54)
|
||||
(xy -2.54 -2.54)
|
||||
(xy -3.175 -3.175)
|
||||
(xy -3.81 -2.54)
|
||||
)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
(rectangle (start 2.54 3.81) (end -5.08 -5.08)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type background))
|
||||
)
|
||||
)
|
||||
(symbol "AudioJack3_1_1"
|
||||
(pin passive line (at 5.08 0 180) (length 2.54)
|
||||
(name "~" (effects (font (size 1.27 1.27))))
|
||||
(number "R" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin passive line (at 5.08 2.54 180) (length 2.54)
|
||||
(name "~" (effects (font (size 1.27 1.27))))
|
||||
(number "S" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin passive line (at 5.08 -2.54 180) (length 2.54)
|
||||
(name "~" (effects (font (size 1.27 1.27))))
|
||||
(number "T" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol "Connector_Generic:Conn_01x03" (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "J" (id 0) (at 0 5.08 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Value" "Conn_01x03" (id 1) (at 0 -5.08 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Footprint" "" (id 2) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "~" (id 3) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_keywords" "connector" (id 4) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_description" "Generic connector, single row, 01x03, script generated (kicad-library-utils/schlib/autogen/connector/)" (id 5) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_fp_filters" "Connector*:*_1x??_*" (id 6) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(symbol "Conn_01x03_1_1"
|
||||
(rectangle (start -1.27 -2.413) (end 0 -2.667)
|
||||
(stroke (width 0.1524) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
(rectangle (start -1.27 0.127) (end 0 -0.127)
|
||||
(stroke (width 0.1524) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
(rectangle (start -1.27 2.667) (end 0 2.413)
|
||||
(stroke (width 0.1524) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
(rectangle (start -1.27 3.81) (end 1.27 -3.81)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type background))
|
||||
)
|
||||
(pin passive line (at -5.08 2.54 0) (length 3.81)
|
||||
(name "Pin_1" (effects (font (size 1.27 1.27))))
|
||||
(number "1" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin passive line (at -5.08 0 0) (length 3.81)
|
||||
(name "Pin_2" (effects (font (size 1.27 1.27))))
|
||||
(number "2" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin passive line (at -5.08 -2.54 0) (length 3.81)
|
||||
(name "Pin_3" (effects (font (size 1.27 1.27))))
|
||||
(number "3" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol "Device:Speaker" (pin_names (offset 0) hide) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "LS" (id 0) (at 1.27 5.715 0)
|
||||
(effects (font (size 1.27 1.27)) (justify right))
|
||||
)
|
||||
(property "Value" "Speaker" (id 1) (at 1.27 3.81 0)
|
||||
(effects (font (size 1.27 1.27)) (justify right))
|
||||
)
|
||||
(property "Footprint" "" (id 2) (at 0 -5.08 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "~" (id 3) (at -0.254 -1.27 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_keywords" "speaker sound" (id 4) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_description" "Speaker" (id 5) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(symbol "Speaker_0_0"
|
||||
(rectangle (start -2.54 1.27) (end 1.016 -3.81)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
(polyline
|
||||
(pts
|
||||
(xy 1.016 1.27)
|
||||
(xy 3.556 3.81)
|
||||
(xy 3.556 -6.35)
|
||||
(xy 1.016 -3.81)
|
||||
)
|
||||
(stroke (width 0.254) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
)
|
||||
(symbol "Speaker_1_1"
|
||||
(pin input line (at -5.08 0 0) (length 2.54)
|
||||
(name "1" (effects (font (size 1.27 1.27))))
|
||||
(number "1" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at -5.08 -2.54 0) (length 2.54)
|
||||
(name "2" (effects (font (size 1.27 1.27))))
|
||||
(number "2" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol "Seeeduino XIAO:SeeeduinoXIAO" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "U" (id 0) (at -19.05 22.86 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Value" "SeeeduinoXIAO" (id 1) (at -12.7 21.59 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Footprint" "" (id 2) (at -8.89 5.08 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "" (id 3) (at -8.89 5.08 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(symbol "SeeeduinoXIAO_0_1"
|
||||
(rectangle (start -19.05 20.32) (end 17.78 -19.05)
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
)
|
||||
(symbol "SeeeduinoXIAO_1_1"
|
||||
(pin unspecified line (at -21.59 11.43 0) (length 2.54)
|
||||
(name "PA02_A0_D0" (effects (font (size 1.27 1.27))))
|
||||
(number "1" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at 20.32 -3.81 180) (length 2.54)
|
||||
(name "PA5_A9_D9_MISO" (effects (font (size 1.27 1.27))))
|
||||
(number "10" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at 20.32 0 180) (length 2.54)
|
||||
(name "PA6_A10_D10_MOSI" (effects (font (size 1.27 1.27))))
|
||||
(number "11" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at 20.32 3.81 180) (length 2.54)
|
||||
(name "3V3" (effects (font (size 1.27 1.27))))
|
||||
(number "12" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at 20.32 7.62 180) (length 2.54)
|
||||
(name "GND" (effects (font (size 1.27 1.27))))
|
||||
(number "13" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at 20.32 11.43 180) (length 2.54)
|
||||
(name "5V" (effects (font (size 1.27 1.27))))
|
||||
(number "14" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at -2.54 -21.59 90) (length 2.54)
|
||||
(name "5V" (effects (font (size 1.27 1.27))))
|
||||
(number "15" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at 2.54 -21.59 90) (length 2.54)
|
||||
(name "GND" (effects (font (size 1.27 1.27))))
|
||||
(number "16" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at -5.08 22.86 270) (length 2.54)
|
||||
(name "PA31_SWDIO" (effects (font (size 1.27 1.27))))
|
||||
(number "17" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at -1.27 22.86 270) (length 2.54)
|
||||
(name "PA30_SWCLK" (effects (font (size 1.27 1.27))))
|
||||
(number "18" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at 2.54 22.86 270) (length 2.54)
|
||||
(name "RESET" (effects (font (size 1.27 1.27))))
|
||||
(number "19" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at -21.59 7.62 0) (length 2.54)
|
||||
(name "PA4_A1_D1" (effects (font (size 1.27 1.27))))
|
||||
(number "2" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin input line (at 6.35 22.86 270) (length 2.54)
|
||||
(name "GND" (effects (font (size 1.27 1.27))))
|
||||
(number "20" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at -21.59 3.81 0) (length 2.54)
|
||||
(name "PA10_A2_D2" (effects (font (size 1.27 1.27))))
|
||||
(number "3" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at -21.59 0 0) (length 2.54)
|
||||
(name "PA11_A3_D3" (effects (font (size 1.27 1.27))))
|
||||
(number "4" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at -21.59 -3.81 0) (length 2.54)
|
||||
(name "PA8_A4_D4_SDA" (effects (font (size 1.27 1.27))))
|
||||
(number "5" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at -21.59 -7.62 0) (length 2.54)
|
||||
(name "PA9_A5_D5_SCL" (effects (font (size 1.27 1.27))))
|
||||
(number "6" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at -21.59 -11.43 0) (length 2.54)
|
||||
(name "PB08_A6_D6_TX" (effects (font (size 1.27 1.27))))
|
||||
(number "7" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at 20.32 -11.43 180) (length 2.54)
|
||||
(name "PB09_A7_D7_RX" (effects (font (size 1.27 1.27))))
|
||||
(number "8" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
(pin unspecified line (at 20.32 -7.62 180) (length 2.54)
|
||||
(name "PA7_A8_D8_SCK" (effects (font (size 1.27 1.27))))
|
||||
(number "9" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
)
|
||||
)
|
||||
(symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
|
||||
(property "Reference" "#PWR" (id 0) (at 0 -6.35 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Value" "GND" (id 1) (at 0 -3.81 0)
|
||||
(effects (font (size 1.27 1.27)))
|
||||
)
|
||||
(property "Footprint" "" (id 2) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "" (id 3) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_keywords" "power-flag" (id 4) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (id 5) (at 0 0 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(symbol "GND_0_1"
|
||||
(polyline
|
||||
(pts
|
||||
(xy 0 0)
|
||||
(xy 0 -1.27)
|
||||
(xy 1.27 -1.27)
|
||||
(xy 0 -2.54)
|
||||
(xy -1.27 -1.27)
|
||||
(xy 0 -1.27)
|
||||
)
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(fill (type none))
|
||||
)
|
||||
)
|
||||
(symbol "GND_1_1"
|
||||
(pin power_in line (at 0 0 270) (length 0) hide
|
||||
(name "GND" (effects (font (size 1.27 1.27))))
|
||||
(number "1" (effects (font (size 1.27 1.27))))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(junction (at 179.07 71.12) (diameter 0) (color 0 0 0 0)
|
||||
(uuid 96702e3d-509f-4a69-ac95-a9b197fc14de)
|
||||
)
|
||||
|
||||
(wire (pts (xy 179.07 64.77) (xy 184.15 64.77))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 11ab5539-9afc-424d-a214-d23a8391cc2e)
|
||||
)
|
||||
(wire (pts (xy 176.53 92.71) (xy 176.53 110.49))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 259e20f0-df3e-4bdb-a78f-e183d985aa97)
|
||||
)
|
||||
(wire (pts (xy 115.57 71.12) (xy 124.46 71.12))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 42fa935b-b02e-446c-867d-a2a4383098a5)
|
||||
)
|
||||
(wire (pts (xy 115.57 66.04) (xy 115.57 58.42))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 5d9d222f-5c52-4809-8ee3-d095863a93b7)
|
||||
)
|
||||
(wire (pts (xy 119.38 67.31) (xy 119.38 68.58))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 78ce755e-88f6-4ea7-9add-f1f118d33ce9)
|
||||
)
|
||||
(wire (pts (xy 166.37 78.74) (xy 179.07 78.74))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 820fc704-964e-4f67-8158-e70a72bf2047)
|
||||
)
|
||||
(wire (pts (xy 119.38 90.17) (xy 124.46 90.17))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 88d19543-1cfe-4048-b1fa-b6ff80cefb7c)
|
||||
)
|
||||
(wire (pts (xy 173.99 87.63) (xy 182.88 87.63))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 9aee4852-816c-460e-b2ee-62fcf3d5cc6e)
|
||||
)
|
||||
(wire (pts (xy 166.37 86.36) (xy 173.99 86.36))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 9c196354-4c24-4cfb-9763-670e19c76460)
|
||||
)
|
||||
(wire (pts (xy 179.07 76.2) (xy 179.07 71.12))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid 9eed885e-c80c-4839-a800-4fa88c39e5c3)
|
||||
)
|
||||
(wire (pts (xy 166.37 71.12) (xy 179.07 71.12))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid a2c04bf2-9ffb-49fd-a19d-c8bf1403cb15)
|
||||
)
|
||||
(wire (pts (xy 115.57 58.42) (xy 120.65 58.42))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid a9121f20-eef1-4824-a5a7-97be54cf7b03)
|
||||
)
|
||||
(wire (pts (xy 179.07 71.12) (xy 179.07 64.77))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid c87486aa-c692-42aa-9b06-8f3cc9614c1d)
|
||||
)
|
||||
(wire (pts (xy 166.37 90.17) (xy 182.88 90.17))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid cde43b1a-0a41-4215-9117-85257cc02e6c)
|
||||
)
|
||||
(wire (pts (xy 119.38 110.49) (xy 119.38 90.17))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid d7b056b3-9bc6-4e05-a3b4-b63cb4b0295d)
|
||||
)
|
||||
(wire (pts (xy 119.38 68.58) (xy 115.57 68.58))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid ee72e577-6a60-4cf3-8fdb-1a6ff0fae0c0)
|
||||
)
|
||||
(wire (pts (xy 124.46 67.31) (xy 119.38 67.31))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid f43e0a7c-e10c-4a2a-966d-dcfa64c28cc0)
|
||||
)
|
||||
(wire (pts (xy 173.99 86.36) (xy 173.99 87.63))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid f544b0d7-a59d-4d16-874a-887827d61d81)
|
||||
)
|
||||
(wire (pts (xy 176.53 110.49) (xy 119.38 110.49))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid fdc06068-6d59-4d1e-ab39-15e6bceaed6b)
|
||||
)
|
||||
(wire (pts (xy 182.88 92.71) (xy 176.53 92.71))
|
||||
(stroke (width 0) (type default) (color 0 0 0 0))
|
||||
(uuid fee40df5-d1e4-4668-80b3-be2cfdc5df44)
|
||||
)
|
||||
|
||||
(symbol (lib_id "Seeeduino XIAO:SeeeduinoXIAO") (at 146.05 78.74 0) (unit 1)
|
||||
(in_bom yes) (on_board yes) (fields_autoplaced)
|
||||
(uuid 09be1eba-cac4-4f6b-8eb4-87d819840140)
|
||||
(property "Reference" "U1" (id 0) (at 150.6094 99.6934 0)
|
||||
(effects (font (size 1.27 1.27)) (justify left))
|
||||
)
|
||||
(property "Value" "SeeeduinoXIAO" (id 1) (at 150.6094 102.2303 0)
|
||||
(effects (font (size 1.27 1.27)) (justify left))
|
||||
)
|
||||
(property "Footprint" "Seeeduino XIAO:Seeeduino XIAO-MOUDLE14P-2.54-21X17.8MM" (id 2) (at 137.16 73.66 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "" (id 3) (at 137.16 73.66 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(pin "1" (uuid fa8ff7bd-280a-430a-90ce-4566eeaa888e))
|
||||
(pin "10" (uuid bafa1981-6fa5-42d5-a6e9-bf4d117a6565))
|
||||
(pin "11" (uuid e163a356-5a18-4550-b2f8-04426edfa537))
|
||||
(pin "12" (uuid 645209ba-000d-4161-8079-a90c48f21972))
|
||||
(pin "13" (uuid de14dca8-bf3f-497e-816c-11f9cf81219b))
|
||||
(pin "14" (uuid a4692f34-d532-4459-88e3-b0550c8a2eee))
|
||||
(pin "15" (uuid 496a7bd6-ee6c-43c8-8ebc-939c96d755f3))
|
||||
(pin "16" (uuid 1284f43c-755b-46d4-8f24-a61527f2e347))
|
||||
(pin "17" (uuid 50f5671f-9d26-467f-bcde-f5b9d8fc54df))
|
||||
(pin "18" (uuid d372df45-d835-49ee-b010-2ee64c196b50))
|
||||
(pin "19" (uuid 8dfae23e-614b-46bb-bdae-f341197dce2f))
|
||||
(pin "2" (uuid 97a9a8d0-7188-48a2-8c2f-46c8abe86dd6))
|
||||
(pin "20" (uuid bec1f1dd-62f3-4b41-b807-7d06f8d6cd2a))
|
||||
(pin "3" (uuid 6417cc3b-7235-4a80-a2c1-d13faf64b1d7))
|
||||
(pin "4" (uuid 9ed46697-310b-4a89-9326-ff97b2100523))
|
||||
(pin "5" (uuid c40c67a9-b68b-499e-ab8a-2d082d8d0b7d))
|
||||
(pin "6" (uuid 17515fd1-db3b-40e0-9d89-f8de0bd2313d))
|
||||
(pin "7" (uuid 6be5e90f-7745-4b4f-a246-cdf69a470372))
|
||||
(pin "8" (uuid ad72a37e-a57a-4468-bc8e-2de8107460b1))
|
||||
(pin "9" (uuid 7b7ef057-2eaa-4d86-af51-083f44f7903a))
|
||||
)
|
||||
|
||||
(symbol (lib_id "power:GND") (at 120.65 58.42 0) (unit 1)
|
||||
(in_bom yes) (on_board yes) (fields_autoplaced)
|
||||
(uuid 21b4a75e-d3ec-42e3-bfa0-19b1fa944cd6)
|
||||
(property "Reference" "#PWR0104" (id 0) (at 120.65 64.77 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Value" "GND" (id 1) (at 120.65 62.8634 0))
|
||||
(property "Footprint" "" (id 2) (at 120.65 58.42 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "" (id 3) (at 120.65 58.42 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(pin "1" (uuid ab5f9a41-c6e0-47f7-af1b-c452558850d3))
|
||||
)
|
||||
|
||||
(symbol (lib_id "power:GND") (at 184.15 64.77 0) (unit 1)
|
||||
(in_bom yes) (on_board yes) (fields_autoplaced)
|
||||
(uuid 523cd503-035b-4793-9ef9-a9ffc5f08847)
|
||||
(property "Reference" "#PWR0102" (id 0) (at 184.15 71.12 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Value" "GND" (id 1) (at 184.15 69.2134 0))
|
||||
(property "Footprint" "" (id 2) (at 184.15 64.77 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "" (id 3) (at 184.15 64.77 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(pin "1" (uuid 8a4098fa-48a9-49ec-80d8-dec1073eed68))
|
||||
)
|
||||
|
||||
(symbol (lib_id "Device:Speaker") (at 184.15 76.2 0) (unit 1)
|
||||
(in_bom yes) (on_board yes) (fields_autoplaced)
|
||||
(uuid 950312c9-0e5c-4da0-91fd-b7338a801460)
|
||||
(property "Reference" "LS1" (id 0) (at 188.468 76.6353 0)
|
||||
(effects (font (size 1.27 1.27)) (justify left))
|
||||
)
|
||||
(property "Value" "Speaker" (id 1) (at 188.468 79.1722 0)
|
||||
(effects (font (size 1.27 1.27)) (justify left))
|
||||
)
|
||||
(property "Footprint" "Connector_Wire:SolderWire-1sqmm_1x02_P5.4mm_D1.4mm_OD2.7mm" (id 2) (at 184.15 81.28 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "~" (id 3) (at 183.896 77.47 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(pin "1" (uuid cb10816e-6377-4a4b-b040-bd2614edb9b9))
|
||||
(pin "2" (uuid 10868984-bd98-4ad9-8184-0bb81f331140))
|
||||
)
|
||||
|
||||
(symbol (lib_id "Connector:AudioJack3") (at 110.49 68.58 0) (unit 1)
|
||||
(in_bom yes) (on_board yes) (fields_autoplaced)
|
||||
(uuid e3e5b867-9078-406e-a424-61c9632af7f6)
|
||||
(property "Reference" "J1" (id 0) (at 108.585 61.0702 0))
|
||||
(property "Value" "AudioJack3" (id 1) (at 108.585 63.6071 0))
|
||||
(property "Footprint" "Connector_Audio:Jack_3.5mm_PJ320D_Horizontal" (id 2) (at 110.49 68.58 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "~" (id 3) (at 110.49 68.58 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(pin "R" (uuid fabe6c60-bf57-479b-a5cc-b51e4ab4f9ab))
|
||||
(pin "S" (uuid 8df3a703-5274-4af0-8e99-d46d16dd32b2))
|
||||
(pin "T" (uuid 44db45bf-8f3e-43c3-a852-3c688a22c4a9))
|
||||
)
|
||||
|
||||
(symbol (lib_id "Connector_Generic:Conn_01x03") (at 187.96 90.17 0) (mirror x) (unit 1)
|
||||
(in_bom yes) (on_board yes) (fields_autoplaced)
|
||||
(uuid ea989905-0b3c-45d4-b789-9cb8ba99b588)
|
||||
(property "Reference" "J?" (id 0) (at 187.96 82.6602 0))
|
||||
(property "Value" "Conn_01x03" (id 1) (at 187.96 85.1971 0))
|
||||
(property "Footprint" "Connector_Wire:SolderWire-0.5sqmm_1x03_P4.8mm_D0.9mm_OD2.3mm" (id 2) (at 187.96 90.17 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(property "Datasheet" "~" (id 3) (at 187.96 90.17 0)
|
||||
(effects (font (size 1.27 1.27)) hide)
|
||||
)
|
||||
(pin "1" (uuid a7cb4827-649e-4a1f-b100-7bc64f6b49b3))
|
||||
(pin "2" (uuid 8592b91a-c3a1-459e-a8f5-48b29ec88b66))
|
||||
(pin "3" (uuid 8dedf67b-e6ba-4f01-84b9-4abd8a8a6d94))
|
||||
)
|
||||
|
||||
(sheet_instances
|
||||
(path "/" (page "1"))
|
||||
)
|
||||
|
||||
(symbol_instances
|
||||
(path "/523cd503-035b-4793-9ef9-a9ffc5f08847"
|
||||
(reference "#PWR0102") (unit 1) (value "GND") (footprint "")
|
||||
)
|
||||
(path "/21b4a75e-d3ec-42e3-bfa0-19b1fa944cd6"
|
||||
(reference "#PWR0104") (unit 1) (value "GND") (footprint "")
|
||||
)
|
||||
(path "/e3e5b867-9078-406e-a424-61c9632af7f6"
|
||||
(reference "J1") (unit 1) (value "AudioJack3") (footprint "Connector_Audio:Jack_3.5mm_PJ320D_Horizontal")
|
||||
)
|
||||
(path "/ea989905-0b3c-45d4-b789-9cb8ba99b588"
|
||||
(reference "J?") (unit 1) (value "Conn_01x03") (footprint "Connector_Wire:SolderWire-0.5sqmm_1x03_P4.8mm_D0.9mm_OD2.3mm")
|
||||
)
|
||||
(path "/950312c9-0e5c-4da0-91fd-b7338a801460"
|
||||
(reference "LS1") (unit 1) (value "Speaker") (footprint "Connector_Wire:SolderWire-1sqmm_1x02_P5.4mm_D1.4mm_OD2.7mm")
|
||||
)
|
||||
(path "/09be1eba-cac4-4f6b-8eb4-87d819840140"
|
||||
(reference "U1") (unit 1) (value "SeeeduinoXIAO") (footprint "Seeeduino XIAO:Seeeduino XIAO-MOUDLE14P-2.54-21X17.8MM")
|
||||
)
|
||||
)
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
(sym_lib_table
|
||||
(lib (name "Seeeduino XIAO")(type "Legacy")(uri "/home/dartcatcher/.var/app/org.kicad.KiCad/data/kicad/6.0/symbols/Seeeduino XIAO.lib")(options "")(descr ""))
|
||||
)
|
|
@ -0,0 +1,44 @@
|
|||
#include <Arduino.h>
|
||||
#include "polybuzzer.h"
|
||||
#include "equal_temperament.h"
|
||||
|
||||
PolyBuzzer::PolyBuzzer(uint8_t pin) {
|
||||
for (int i = 0; i < POLYBUZZER_MAX_TONES; i++) {
|
||||
this->tones[i] = 0;
|
||||
}
|
||||
this->playing = 0;
|
||||
this->pin = pin;
|
||||
pinMode(pin, OUTPUT);
|
||||
}
|
||||
|
||||
void PolyBuzzer::update() {
|
||||
for (int i = 0; i < POLYBUZZER_MAX_TONES; i++) {
|
||||
if (this->tones[i]) {
|
||||
if (this->playing != this->tones[i]) {
|
||||
this->playing = this->tones[i];
|
||||
tone(this->pin, this->tones[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->playing = 0;
|
||||
noTone(this->pin);
|
||||
}
|
||||
|
||||
void PolyBuzzer::Tone(int slot, unsigned int frequency) {
|
||||
this->tones[slot] = frequency;
|
||||
this->update();
|
||||
}
|
||||
|
||||
void PolyBuzzer::Note(int slot, uint8_t note) {
|
||||
if (note > 127) {
|
||||
note = 127;
|
||||
}
|
||||
this->Tone(slot, equalTemperamentNote[note]);
|
||||
}
|
||||
|
||||
void PolyBuzzer::NoTone(int slot) {
|
||||
tones[slot] = 0;
|
||||
this->update();
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#define POLYBUZZER_MAX_TONES 2
|
||||
|
||||
// PolyBuzzer provides a proritized monophonic buzzer.
|
||||
//
|
||||
// A given tone will only be played when all higher priority tones have stopped.
|
||||
class PolyBuzzer {
|
||||
public:
|
||||
unsigned int tones[POLYBUZZER_MAX_TONES];
|
||||
unsigned int playing;
|
||||
uint8_t pin;
|
||||
|
||||
PolyBuzzer(uint8_t pin);
|
||||
void update();
|
||||
void Tone(int slot, unsigned int frequency);
|
||||
void Note(int slot, uint8_t note);
|
||||
void NoTone(int slot);
|
||||
};
|
|
@ -1,11 +1,11 @@
|
|||
#include "touchbounce.h"
|
||||
|
||||
void TouchBounce::attach(int pin) {
|
||||
this->qt = Adafruit_FreeTouch(pin);
|
||||
this->qt = Adafruit_FreeTouch(pin, OVERSAMPLE_2, RESISTOR_0, FREQ_MODE_SPREAD);
|
||||
this->qt.begin();
|
||||
}
|
||||
|
||||
bool TouchBounce::readCurrentState() {
|
||||
int val = this->qt.measure();
|
||||
return val < QT_THRESHOLD;
|
||||
return val > QT_THRESHOLD;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <Adafruit_FreeTouch.h>
|
||||
#include "bounce2.h"
|
||||
|
||||
#define QT_THRESHOLD 850
|
||||
#define QT_THRESHOLD 450
|
||||
|
||||
class TouchBounce: public Bounce {
|
||||
public:
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <Adafruit_FreeTouch.h>
|
||||
#include "bounce2.h"
|
||||
#include "touchbounce.h"
|
||||
#include "adapter.h"
|
||||
|
||||
#define DIT_PIN 2
|
||||
#define DAH_PIN 1
|
||||
|
@ -19,14 +20,13 @@
|
|||
#define LED_ON false // Xiao inverts this logic for some reason
|
||||
#define LED_OFF (!LED_ON)
|
||||
|
||||
#define DIT_KEY KEY_LEFT_CTRL
|
||||
#define DAH_KEY KEY_RIGHT_CTRL
|
||||
#define TONE 550
|
||||
#define DIT_KEYBOARD_KEY KEY_LEFT_CTRL
|
||||
#define DAH_KEYBOARD_KEY KEY_RIGHT_CTRL
|
||||
#define TONE 3000
|
||||
|
||||
#define MILLISECOND 1
|
||||
#define SECOND (1 * MILLISECOND)
|
||||
|
||||
bool keyboard = true;
|
||||
bool trs = false; // true if a TRS plug is in a TRRS jack
|
||||
uint16_t iambicDelay = 80 * MILLISECOND;
|
||||
Bounce dit = Bounce();
|
||||
|
@ -35,10 +35,10 @@ Bounce key = Bounce();
|
|||
TouchBounce qt_dit = TouchBounce();
|
||||
TouchBounce qt_dah = TouchBounce();
|
||||
TouchBounce qt_key = TouchBounce();
|
||||
VailAdapter adapter = VailAdapter(PIEZO);
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
pinMode(PIEZO, OUTPUT);
|
||||
dit.attach(DIT_PIN, INPUT_PULLUP);
|
||||
dah.attach(DAH_PIN, INPUT_PULLUP);
|
||||
key.attach(KEY_PIN, INPUT_PULLUP);
|
||||
|
@ -68,7 +68,7 @@ void setup() {
|
|||
void setLED() {
|
||||
static bool beepin = false;
|
||||
int beat = millis() / iambicDelay;
|
||||
bool on = keyboard; // If we're not in intro, display status of keyboard
|
||||
bool on = adapter.KeyboardMode(); // If we're not in intro, display status of keyboard
|
||||
|
||||
if (beat < 16) {
|
||||
on = HELLO_BITS & (1 << (15-beat));
|
||||
|
@ -85,76 +85,36 @@ void setLED() {
|
|||
digitalWrite(LED_BUILTIN, on?LED_ON:LED_OFF);
|
||||
}
|
||||
|
||||
void midiKey(bool down, uint8_t key) {
|
||||
midiEventPacket_t event = {down?9:8, down?0x90:0x80, key, 0x7f};
|
||||
MidiUSB.sendMIDI(event);
|
||||
MidiUSB.flush();
|
||||
}
|
||||
|
||||
void midiProbe() {
|
||||
void loop() {
|
||||
unsigned now = millis();
|
||||
midiEventPacket_t event = MidiUSB.read();
|
||||
|
||||
uint16_t msg = (event.byte1 << 8) | (event.byte2 << 0);
|
||||
switch (msg) {
|
||||
case 0x8B00: // Controller 0: turn keyboard mode on/off
|
||||
keyboard = (event.byte3 > 0x3f);
|
||||
break;
|
||||
case 0x8B01: // Controller 1: set iambic speed (0-254)
|
||||
// I am probably never going to use this,
|
||||
// because as soon as I implement it,
|
||||
// people are going to want a way to select mode A or B,
|
||||
// or typeahead,
|
||||
// or some other thing that I don't want to maintain
|
||||
// simultaneously in both C and JavaScript
|
||||
iambicDelay = event.byte3 << 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
midiProbe();
|
||||
setLED();
|
||||
adapter.Tick(now);
|
||||
|
||||
if (event.header) {
|
||||
adapter.HandleMIDI(event);
|
||||
}
|
||||
|
||||
// Monitor straight key pin
|
||||
if (key.update() || qt_key.update()) {
|
||||
bool fell = key.fell() || qt_key.fell();
|
||||
midiKey(fell, 0);
|
||||
if (fell) {
|
||||
tone(PIEZO, TONE);
|
||||
} else {
|
||||
noTone(PIEZO);
|
||||
}
|
||||
bool pressed = !key.read() || qt_key.read();
|
||||
adapter.HandlePaddle(PADDLE_STRAIGHT, pressed);
|
||||
}
|
||||
|
||||
// If we made dit = dah, we have a straight key on the dit pin,
|
||||
// so we skip iambic polling.
|
||||
// so we skip other keys polling.
|
||||
if (trs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dit.update() || qt_dit.update()) {
|
||||
bool fell = dit.fell() || qt_dit.fell();
|
||||
midiKey(fell, 1);
|
||||
if (keyboard) {
|
||||
if (fell) {
|
||||
Keyboard.press(DIT_KEY);
|
||||
} else {
|
||||
Keyboard.release(DIT_KEY);
|
||||
}
|
||||
}
|
||||
bool pressed = !dit.read() || qt_dit.read();
|
||||
adapter.HandlePaddle(PADDLE_DIT, pressed);
|
||||
}
|
||||
|
||||
// Monitor dah pin
|
||||
if (dah.update() || qt_dah.update()) {
|
||||
bool fell = dah.fell() || qt_dah.fell();
|
||||
midiKey(fell, 2);
|
||||
|
||||
if (keyboard) {
|
||||
if (fell) {
|
||||
Keyboard.press(DAH_KEY);
|
||||
} else {
|
||||
Keyboard.release(DAH_KEY);
|
||||
}
|
||||
}
|
||||
bool pressed = !dah.read() || qt_dah.read();
|
||||
adapter.HandlePaddle(PADDLE_DAH, pressed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
let deviceFilter = { vendorId: 0x1234, productId: 0xabcd };
|
||||
let requestParams = { filters: [deviceFilter] };
|
||||
let outputReportId = 0x01;
|
||||
let outputReport = new Uint8Array([42]);
|
||||
|
||||
function handleConnectedDevice(e) {
|
||||
console.log("Device connected: " + e.device.productName);
|
||||
}
|
||||
|
||||
function handleDisconnectedDevice(e) {
|
||||
console.log("Device disconnected: " + e.device.productName);
|
||||
}
|
||||
|
||||
function handleInputReport(e) {
|
||||
console.log(e.device.productName + ": got input report " + e.reportId);
|
||||
console.log(new Uint8Array(e.data.buffer));
|
||||
}
|
||||
|
||||
navigator.hid.addEventListener("connect", handleConnectedDevice);
|
||||
navigator.hid.addEventListener("disconnect", handleDisconnectedDevice);
|
||||
|
||||
function listen() {
|
||||
navigator.hid.requestDevice(requestParams).then((devices) => {
|
||||
if (devices.length == 0) return;
|
||||
devices[0].open().then(() => {
|
||||
console.log("Opened device: " + device.productName);
|
||||
device.addEventListener("inputreport", handleInputReport);
|
||||
device.sendReport(outputReportId, outputReport).then(() => {
|
||||
console.log("Sent output report " + outputReportId);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
document.querySelector("#moo").addEventListener("click", listen)
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", init)
|
||||
} else {
|
||||
init()
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="moo">start</button>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue