Skip to content

Instantly share code, notes, and snippets.

@cowboy
Last active June 1, 2022 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cowboy/1af3a69d4b6cae6a099fcf76431bda02 to your computer and use it in GitHub Desktop.
Save cowboy/1af3a69d4b6cae6a099fcf76431bda02 to your computer and use it in GitHub Desktop.
// ===============================================================
// MIDI to 24 PPQN Clock + Run (USB MIDI) - Teensy LC Firmware
// "Cowboy" Ben Alman, 2022
// https://gist.github.com/cowboy/1af3a69d4b6cae6a099fcf76431bda02
// ===============================================================
int PPQN = 24;
int RUN_PIN = 26;
int RUN_LED_PIN = 20;
int CLOCK_PIN = 17;
int CLOCK_LED_PIN = 5;
int counter = 0;
void setup() {
// Setup pins
pinMode(RUN_PIN, OUTPUT);
pinMode(RUN_LED_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(CLOCK_LED_PIN, OUTPUT);
// MIDI message handlers
usbMIDI.setHandleClock(onClock);
usbMIDI.setHandleStart(onStart);
usbMIDI.setHandleStop(onStop);
blink(3);
}
void blink(int count) {
for (int i = 0; i < count; i++) {
digitalWrite(RUN_LED_PIN, HIGH);
digitalWrite(CLOCK_LED_PIN, LOW);
delay(250);
digitalWrite(RUN_LED_PIN, LOW);
digitalWrite(CLOCK_LED_PIN, HIGH);
delay(250);
}
digitalWrite(CLOCK_LED_PIN, LOW);
}
void loop() {
usbMIDI.read();
}
void onClock() {
digitalWrite(CLOCK_PIN, HIGH);
delay(5);
digitalWrite(CLOCK_PIN, LOW);
if (counter == 0) {
digitalWrite(CLOCK_LED_PIN, HIGH);
} else if (counter == (PPQN / 2)) {
digitalWrite(CLOCK_LED_PIN, LOW);
}
if (++counter == PPQN) {
counter = 0;
}
}
void onStart() {
counter = 0;
digitalWrite(RUN_PIN, HIGH);
digitalWrite(RUN_LED_PIN, HIGH);
}
void onStop() {
digitalWrite(RUN_PIN, LOW);
digitalWrite(RUN_LED_PIN, LOW);
digitalWrite(CLOCK_PIN, LOW);
digitalWrite(CLOCK_LED_PIN, LOW);
}
// ===============================================================
// MIDI to 24 PPQN Clock + Run (USB MIDI) - OpenSCAD Enclosure
// "Cowboy" Ben Alman, 2022
// https://gist.github.com/cowboy/1af3a69d4b6cae6a099fcf76431bda02
// ===============================================================
$fs=0.1;
o=0.001;
outer_border=1.5;
pcb_w=35.75;
pcb_d=18;
pcb_h=1.75;
pcb_y_offset=1.2;
pcb_z_offset=7;
pcb_top=pcb_z_offset+pcb_h;
pcb_jack_divider=4.5;
pcb_guide_w=3;
pcb_guide_h=2;
pcb_button_h=3.1;
button_x=33.25;
button_r=1;
button_cut=0.4;
button_w=5;
button_indent=0.4;
button_ramp_offset=1;
usb_cutout_w=9;
usb_cutout_h=3.7;
usb_cutout_z_offset=0.5;
led_offset_x=19;
led_cutout_r=1.8;
led_base_h=2;
led_base_r=3.1;
jack_x=outer_border+pcb_w+pcb_jack_divider;
jack_w=10.5;
jack_d=8.75;
jack_h=10.5;
jack_metal_r=3.25;
jack_metal_z=4.5;
jack_divider=1;
jack_y_offset=(jack_d+jack_divider)/2;
lid_lip_w=pcb_w-4;
lid_lip_x_offset=(pcb_w-lid_lip_w)/2;
lid_pcb_holder_w=pcb_w*0.7;
lid_pcb_holder_x_offset=(pcb_w-lid_pcb_holder_w)/2;
lid_pcb_holder_z=pcb_top+outer_border;
length=2*outer_border+pcb_w+pcb_jack_divider+jack_w;
depth=2*outer_border+(pcb_d/2+pcb_y_offset)*2;
height=2*outer_border+jack_h;
module box(w,d,h) {
cube([w,d,h]);
}
module center_box(w,d,h) {
translate([0,-d/2,0])
box(w,d,h);
}
module right_triangle(x,y) {
polygon([[0,0],[x,0],[x,y]]);
}
module lid() {
lid_w=length-2*outer_border-0.1;
lid_d=depth-2*outer_border-0.1;
notch_w=1;
notch_d=5;
rotate(180, [30,0,0])
translate([outer_border,30,-height]) {
difference() {
// lid
translate([0,0,height-outer_border])
center_box(lid_w,lid_d,outer_border);
// notch
translate([lid_w-notch_w+o,0,height-outer_border-o])
center_box(notch_w,notch_d,outer_border+2*o);
}
// lip
translate([lid_lip_x_offset,0,height-2]) {
translate([0,(depth-2*outer_border+1-0.2)/2,0])
lid_lip(1);
translate([0,-(depth-2*outer_border+1-0.2)/2,0])
lid_lip(-1);
}
// pcb holder
translate([lid_pcb_holder_x_offset,0,lid_pcb_holder_z])
center_box(lid_pcb_holder_w,2,height-lid_pcb_holder_z-outer_border+o);
}
}
module lid_lip(dir) {
translate([0,0,1])
rotate(270,[0,0,1])
rotate(270,[1,0,0])
linear_extrude(lid_lip_w)
polygon([[0,0],[1.5*dir,0],[1.5*dir,1],[0.5*dir,1]]);
}
module body() {
difference() {
union() {
difference() {
center_box(length, depth, height);
translate([outer_border,0,outer_border])
center_box(length-2*outer_border+o, depth-2*outer_border+o, height-outer_border+o);
jacks();
lid_cutout();
usb_cutout();
button_cutout();
labels();
}
jack_guides();
pcb_guides();
button();
leds_base();
}
leds_cutout();
}
}
module lid_cutout() {
translate([outer_border,0,height-2])
center_box(pcb_w,depth-2*outer_border+1,1.2);
}
module usb_cutout() {
translate([-o,pcb_y_offset,outer_border+pcb_z_offset+usb_cutout_z_offset-usb_cutout_h])
center_box(outer_border+2*o,usb_cutout_w,usb_cutout_h);
}
module button_cutout() {
translate([button_x,pcb_y_offset,-o]) {
cylinder(outer_border+o*2,button_r+button_cut,button_r+button_cut);
translate([-button_w,0,0])
center_box(button_w,2*button_r+button_cut*2,outer_border+o*2);
}
}
module button() {
translate([button_x,pcb_y_offset,0]) {
difference() {
union() {
cylinder(outer_border+pcb_z_offset-pcb_button_h,button_r,button_r);
translate([-button_w,0,0]) {
center_box(button_w,2*button_r,outer_border);
translate([button_ramp_offset,button_r,outer_border])
rotate(90, [1,0,0])
linear_extrude(2*button_r)
right_triangle(button_w-button_ramp_offset,pcb_z_offset-pcb_button_h);
}
}
sphere(button_indent);
}
}
}
module jack_guides() {
difference() {
translate([jack_x-pcb_jack_divider,0,outer_border-o])
center_box(jack_w+pcb_jack_divider+o,depth-2*outer_border+o,height-2*outer_border);
translate([jack_x-pcb_jack_divider,0,0])
resize([20,0,0])
translate([-jack_x,0,0])
jacks();
}
}
module jack(idx) {
y_offset=[-jack_y_offset, jack_y_offset][idx];
translate([jack_x,y_offset,outer_border]) {
color("#000",0.5)
center_box(jack_w,jack_d,jack_h);
color("#fff",0.5)
translate([jack_w,0,jack_metal_z])
rotate([0,90,0])
cylinder(2+2*o,jack_metal_r,jack_metal_r);
}
}
module jacks() {
jack(0);
jack(1);
}
module led_cutout(idx) {
y_offset=[-jack_y_offset, jack_y_offset][idx];
translate([led_offset_x,y_offset,0-o])
cylinder(outer_border+led_base_h+2*o,led_cutout_r,led_cutout_r);
}
module leds_cutout() {
led_cutout(0);
led_cutout(1);
}
module led_base(idx) {
y_offset=[-jack_y_offset, jack_y_offset][idx];
translate([led_offset_x,y_offset,outer_border])
cylinder(led_base_h,led_base_r,led_base_r);
}
module leds_base() {
led_base(0);
led_base(1);
}
module pcb() {
color("#0a0", 0.5)
translate([outer_border,pcb_y_offset,outer_border+pcb_z_offset])
center_box(pcb_w,pcb_d,pcb_h);
}
module pcb_guide() {
translate([outer_border,0,outer_border-o])
center_box(pcb_guide_w,pcb_guide_h,pcb_z_offset);
}
module pcb_guides() {
mid_x=(pcb_w-pcb_guide_w)/2.3;
top_y=(depth-2*outer_border-pcb_guide_h)/2;
bot_y=pcb_y_offset*2-top_y;
translate([mid_x*2,top_y,0])
pcb_guide();
translate([mid_x,top_y,0])
pcb_guide();
translate([0,top_y,0])
pcb_guide();
translate([mid_x*2,bot_y,0])
pcb_guide();
translate([mid_x-2.5,bot_y,0])
pcb_guide();
translate([0,bot_y,0])
pcb_guide();
translate([outer_border,pcb_y_offset+outer_border-depth/2,outer_border-o])
center_box(mid_x*2+pcb_guide_w,pcb_y_offset*2,pcb_top);
}
module labels() {
translate([16,0,0-o]) {
linear_extrude(height = 0.5) {
mirror([0,1,0])
translate([0,-jack_y_offset,0])
text("CLOCK", size=3, valign="center", halign="right", font="Liberation Sans:style=Bold");
mirror([0,1,0])
translate([0,jack_y_offset,0])
text("RUN", size=3, valign="center", halign="right", font="Liberation Sans:style=Bold");
translate([6.3,-jack_y_offset-0.25,0])
square([26,0.5]);
translate([6.3,jack_y_offset-0.25,0])
square([26,0.5]);
}
}
}
body();
//jacks();
//pcb();
lid();
// ===============================================================
// MIDI to 24 PPQN Clock + Run (USB MIDI) - Teensy LC Firmware
// "Cowboy" Ben Alman, 2022
// https://gist.github.com/cowboy/1af3a69d4b6cae6a099fcf76431bda02
// ===============================================================
// To give your project a unique name, this code must be
// placed into a .c file (its own tab). It can not be in
// a .cpp file or your main sketch (the .ino file).
#include "usb_names.h"
// Edit these lines to create your own name. The length must
// match the number of characters in your custom name.
#define MIDI_NAME {'C', 'o', 'w', 'b', 'o', 'y', ' ', 'C', 'l', 'o', 'c', 'k', ' ', '+', ' ', 'R', 'u', 'n'}
#define MIDI_NAME_LEN 18
// Do not change this part. This exact format is required by USB.
struct usb_string_descriptor_struct usb_string_product_name = {
2 + MIDI_NAME_LEN * 2,
3,
MIDI_NAME
};
@cowboy
Copy link
Author

cowboy commented May 28, 2022

Videos

Prototype: https://www.instagram.com/p/CeG2_JkDAA0/
Final version: https://www.instagram.com/p/CePUMbxDVG4/

BOM

1x Teensy LC
2x PJ301CM 3.5mm jacks (not using them as stereo, just mono)
1x 3mm Green LED
1x 3mm Orange LED
1x 47 ohm resistor
1x 330 ohm resistor

Bottom, showing Teensy LC + 2x 3.5mm jacks

2022-05-31 18 02 53

Top, showing 2x 3mm LEDs

2022-05-31 18 03 22

Iterations...

2022-05-31 18 03 05

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment