├── OpenSCAD ├── MS-Gothic-SFD.ttf ├── Carousel_left.scad ├── Carousel_right.scad ├── Chassis.scad ├── Overallview.scad ├── PVC_Plate.scad ├── Printview.scad ├── Split_Flap_Module.scad ├── 2D_library.scad ├── _settings.scad ├── _font_setup.scad └── 3D_library.scad ├── Arduino ├── pins_v2.ino ├── inputs_v2.ino └── Split_Flap.ino ├── Raspberry_Pi ├── html │ ├── text.php │ ├── assets │ │ ├── dist │ │ │ └── css │ │ │ │ └── cover.css │ │ └── js │ │ │ └── color-modes.js │ └── index.php ├── i2c_transmit.php └── split-flap.php ├── PCB ├── Module │ ├── Module.kicad_prl │ ├── SplitFlap.kicad_prl │ ├── Module.kicad_pro │ └── SplitFlap.kicad_pro └── Backplane │ ├── Backplane.kicad_prl │ └── Backplane.kicad_pro ├── LICENSE └── README.md /OpenSCAD/MS-Gothic-SFD.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g000ze/Split-Flap-Display/HEAD/OpenSCAD/MS-Gothic-SFD.ttf -------------------------------------------------------------------------------- /Arduino/pins_v2.ino: -------------------------------------------------------------------------------- 1 | const byte hall_pin_0 = 18; 2 | const byte hall_pin_1 = 19; 3 | const byte hall_pin_2 = 20; 4 | const byte hall_pin_3 = 21; 5 | const byte hall_pin_4 = 9; 6 | const byte hall_pin_5 = 10; 7 | const byte hall_pin_6 = 11; 8 | const byte hall_pin_7 = 12; 9 | 10 | const byte step_pin_0 = 15; 11 | const byte step_pin_1 = 14; 12 | const byte step_pin_2 = 23; 13 | const byte step_pin_3 = 22; 14 | const byte step_pin_4 = 5; 15 | const byte step_pin_5 = 4; 16 | const byte step_pin_6 = 17; 17 | const byte step_pin_7 = 16; 18 | 19 | -------------------------------------------------------------------------------- /Raspberry_Pi/html/text.php: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /OpenSCAD/Carousel_left.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<3D_library.scad>; 19 | 20 | // Hier wird das Modul "projection" verwendet, um 2D 21 | // darzustellen und danach nach "dfx" zu exportieren. 22 | //projection() karussell_scheibe_links(); 23 | karussell_scheibe_links(); 24 | -------------------------------------------------------------------------------- /OpenSCAD/Carousel_right.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<3D_library.scad>; 19 | 20 | // Mit "projection" wird ein Shape von 3D zu 2D umgewandelt 21 | // damit es danach nach dfx exportiert werden kann. 22 | //projection() karussell_scheibe_rechts(); 23 | karussell_scheibe_rechts(); 24 | -------------------------------------------------------------------------------- /Raspberry_Pi/html/assets/dist/css/cover.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | 6 | /* Custom default button */ 7 | .btn-light, 8 | .btn-light:hover, 9 | .btn-light:focus { 10 | color: #333; 11 | text-shadow: none; /* Prevent inheritance from `body` */ 12 | } 13 | 14 | 15 | /* 16 | * Base structure 17 | */ 18 | 19 | body { 20 | text-shadow: 0 .05rem .1rem rgba(0, 0, 0, .5); 21 | box-shadow: inset 0 0 5rem rgba(0, 0, 0, .5); 22 | } 23 | 24 | .cover-container { 25 | max-width: 42em; 26 | } 27 | 28 | 29 | /* 30 | * Header 31 | */ 32 | 33 | .nav-masthead .nav-link { 34 | color: rgba(255, 255, 255, .5); 35 | border-bottom: .25rem solid transparent; 36 | } 37 | 38 | .nav-masthead .nav-link:hover, 39 | .nav-masthead .nav-link:focus { 40 | border-bottom-color: rgba(255, 255, 255, .25); 41 | } 42 | 43 | .nav-masthead .nav-link + .nav-link { 44 | margin-left: 1rem; 45 | } 46 | 47 | .nav-masthead .active { 48 | color: #fff; 49 | border-bottom-color: #fff; 50 | } 51 | -------------------------------------------------------------------------------- /OpenSCAD/Chassis.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<3D_library.scad>; 19 | 20 | module chassis(){ 21 | // Gehäuse Seiten: 22 | union() 23 | { 24 | color("grey") 25 | { 26 | // Rechts mit grossem Ausschnitt 27 | chassis_right(); 28 | 29 | // Links mit Löcher für Motor 30 | chassis_left(); 31 | 32 | // Front 33 | chassis_front(); 34 | } 35 | } 36 | } 37 | 38 | rotate([-90,0,0]) chassis(); 39 | -------------------------------------------------------------------------------- /Raspberry_Pi/i2c_transmit.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | ; 19 | 20 | cols = 10; 21 | rows = 5; 22 | 23 | for(row=[0:1:rows-1]) 24 | { 25 | for(col=[0:1:cols-1]) 26 | { 27 | // this translate shifts the objects in rows and cols 28 | translate([((flap_width + (2 * flap_pin) + h_space_between_flaps) * col), -((flap_height + v_space_between_flaps) * (row * 2))]) 29 | { 30 | // XXX das hier ist falsch beim letzten Durchgang. 31 | rotate([0, 0, 90]) flap_with_char(((cols * row) + col) + 1); 32 | } 33 | translate([((flap_width + (2 * flap_pin) + h_space_between_flaps) * col), -((flap_height + v_space_between_flaps) * (row * 2) + (flap_height + 1))]) 34 | { 35 | rotate([0, 180, 90]) flap_with_char(((cols * row) + col)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /OpenSCAD/PVC_Plate.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<3D_library.scad>; 19 | 20 | 21 | cols = 6; 22 | rows = 6; 23 | space = 1; 24 | 25 | sheet_width = 500; 26 | sheet_height = 500; 27 | 28 | difference() 29 | { 30 | square(size = [sheet_width, sheet_height], center = false); 31 | for(col=[0:1:cols-1]) 32 | { 33 | for(row=[0:1:rows-1]) 34 | { 35 | // verschieben auf dem Quadrat 36 | // v v 37 | translate([(carousel_diameter / 2) + ((carousel_diameter + space) * row) + 1.5, (carousel_diameter / 2) + ((carousel_diameter + space) * col) + 1.5, 0]) 38 | if((col + row) % 2 == 0) 39 | { 40 | // render() makes the preview faster 41 | render() karussell_scheibe_rechts(); 42 | } 43 | else 44 | { 45 | render() karussell_scheibe_links(); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /OpenSCAD/Printview.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<2D_library.scad>; 19 | 20 | // es fehlen noch die indicators 21 | 22 | // 1 - 5 or 6 - 10 23 | start_row = 1; 24 | end_row = 10; 25 | 26 | // front or back 27 | side = "front"; 28 | // zoom for better resolution in the output picture 29 | zoom = 10; 30 | 31 | // Das ist die totale Breite sämtlicher Flaps inkl. Nasen & Abstände 32 | total_width_flaps = ((flap_total_width) * cols) + (h_space_between_flaps * (cols - 1)); 33 | // Das ist die totale Höhe sämtlicher Flaps inkl. Abstände 34 | total_height_flaps = (flap_height * (end_row - (start_row + 1))) + (v_space_between_flaps * ((end_row - (start_row + 1)) - 1)); 35 | 36 | // dieses render() hilft gegen die Fehlermeldung 37 | // Normalized tree is growing past 200000 elements. Aborting normalization 38 | render() 39 | { 40 | scale([zoom, zoom]) 41 | { 42 | difference() 43 | { 44 | foil(); 45 | 46 | // two translates, for better understanding: 47 | // 1) move by half flap in width and height, due to center = true 48 | // 2) move to center of foil 49 | translate([(flap_width / 2) + flap_pin, -(flap_height/2)]) 50 | { 51 | translate([-total_width_flaps/2, total_height_flaps/2]) 52 | { 53 | cols_and_rows(); 54 | } 55 | } 56 | 57 | indicators(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Arduino/inputs_v2.ino: -------------------------------------------------------------------------------- 1 | // https://de.wikipedia.org/wiki/American_Standard_Code_for_Information_Interchange 2 | // Col Dez 3 | 4 | /* 5 | "0", "A", "B", "C", "D", 6 | "1", "E", "F", "G", "H", 7 | "2", "I", "J", "K", "L", 8 | "3", "M", "N", "O", "P", 9 | "4", "Q", "R", "S", "T", 10 | "5", "U", "V", "W", "X", 11 | "6", "Y", "Z", "+", "-", 12 | "7", "?", "!", ".", ",", 13 | "8", "@", ":", ";", "#", 14 | "9", "*", "$", "/", " ", 15 | */ 16 | 17 | int get_position(int input){ 18 | // convert lowercase to uppercase letters. 19 | if ((input >= 'a' && input <= 'z') || (input >= 'A' && input <= 'Z')) { 20 | input = input & ~32; 21 | } 22 | 23 | switch (input) { 24 | case 48: return 1; // 0 25 | case 65: return 2; // A 26 | case 66: return 3; // B 27 | case 67: return 4; // C 28 | case 68: return 5; // D 29 | case 49: return 6; // 1 30 | case 69: return 7; // E 31 | case 70: return 8; // F 32 | case 71: return 9; // G 33 | case 72: return 10; // H 34 | case 50: return 11; // 2 35 | case 73: return 12; // I 36 | case 74: return 13; // J 37 | case 75: return 14; // K 38 | case 76: return 15; // L 39 | case 51: return 16; // 3 40 | case 77: return 17; // M 41 | case 78: return 18; // N 42 | case 79: return 19; // O 43 | case 80: return 20; // P 44 | case 52: return 21; // 4 45 | case 81: return 22; // Q 46 | case 82: return 23; // R 47 | case 83: return 24; // S 48 | case 84: return 25; // T 49 | case 53: return 26; // 5 50 | case 85: return 27; // U 51 | case 86: return 28; // V 52 | case 87: return 29; // W 53 | case 88: return 30; // X 54 | case 54: return 31; // 6 55 | case 89: return 32; // Y 56 | case 90: return 33; // Z 57 | case 43: return 34; // + 58 | case 45: return 35; // - 59 | case 55: return 36; // 7 60 | case 63: return 37; // ? 61 | case 33: return 38; // ! 62 | case 46: return 39; // . 63 | case 44: return 40; // , 64 | case 56: return 41; // 8 65 | case 64: return 42; // @ 66 | case 58: return 43; // : 67 | case 59: return 44; // ; 68 | case 35: return 45; // # 69 | case 57: return 46; // 9 70 | case 42: return 47; // * 71 | case 36: return 48; // $ 72 | case 47: return 49; // / 73 | case 32: return 50; // " " (space) 74 | default: return 0; // Default 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Raspberry_Pi/html/assets/js/color-modes.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) 3 | * Copyright 2011-2024 The Bootstrap Authors 4 | * Licensed under the Creative Commons Attribution 3.0 Unported License. 5 | */ 6 | 7 | (() => { 8 | 'use strict' 9 | 10 | const getStoredTheme = () => localStorage.getItem('theme') 11 | const setStoredTheme = theme => localStorage.setItem('theme', theme) 12 | 13 | const getPreferredTheme = () => { 14 | const storedTheme = getStoredTheme() 15 | if (storedTheme) { 16 | return storedTheme 17 | } 18 | 19 | return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' 20 | } 21 | 22 | const setTheme = theme => { 23 | if (theme === 'auto') { 24 | document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')) 25 | } else { 26 | document.documentElement.setAttribute('data-bs-theme', theme) 27 | } 28 | } 29 | 30 | setTheme(getPreferredTheme()) 31 | 32 | const showActiveTheme = (theme, focus = false) => { 33 | const themeSwitcher = document.querySelector('#bd-theme') 34 | 35 | if (!themeSwitcher) { 36 | return 37 | } 38 | 39 | const themeSwitcherText = document.querySelector('#bd-theme-text') 40 | const activeThemeIcon = document.querySelector('.theme-icon-active use') 41 | const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`) 42 | const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href') 43 | 44 | document.querySelectorAll('[data-bs-theme-value]').forEach(element => { 45 | element.classList.remove('active') 46 | element.setAttribute('aria-pressed', 'false') 47 | }) 48 | 49 | btnToActive.classList.add('active') 50 | btnToActive.setAttribute('aria-pressed', 'true') 51 | activeThemeIcon.setAttribute('href', svgOfActiveBtn) 52 | const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})` 53 | themeSwitcher.setAttribute('aria-label', themeSwitcherLabel) 54 | 55 | if (focus) { 56 | themeSwitcher.focus() 57 | } 58 | } 59 | 60 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { 61 | const storedTheme = getStoredTheme() 62 | if (storedTheme !== 'light' && storedTheme !== 'dark') { 63 | setTheme(getPreferredTheme()) 64 | } 65 | }) 66 | 67 | window.addEventListener('DOMContentLoaded', () => { 68 | showActiveTheme(getPreferredTheme()) 69 | 70 | document.querySelectorAll('[data-bs-theme-value]') 71 | .forEach(toggle => { 72 | toggle.addEventListener('click', () => { 73 | const theme = toggle.getAttribute('data-bs-theme-value') 74 | setStoredTheme(theme) 75 | setTheme(theme) 76 | showActiveTheme(theme, true) 77 | }) 78 | }) 79 | }) 80 | })() 81 | -------------------------------------------------------------------------------- /PCB/Module/Module.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 1, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "shapes": 1.0, 14 | "tracks": 1.0, 15 | "vias": 1.0, 16 | "zones": 0.6 17 | }, 18 | "selection_filter": { 19 | "dimensions": true, 20 | "footprints": true, 21 | "graphics": true, 22 | "keepouts": true, 23 | "lockedItems": false, 24 | "otherItems": true, 25 | "pads": true, 26 | "text": true, 27 | "tracks": true, 28 | "vias": true, 29 | "zones": true 30 | }, 31 | "visible_items": [ 32 | "vias", 33 | "footprint_text", 34 | "footprint_anchors", 35 | "ratsnest", 36 | "grid", 37 | "footprints_front", 38 | "footprints_back", 39 | "footprint_values", 40 | "footprint_references", 41 | "tracks", 42 | "drc_errors", 43 | "drawing_sheet", 44 | "bitmaps", 45 | "pads", 46 | "zones", 47 | "drc_warnings", 48 | "locked_item_shadows", 49 | "conflict_shadows", 50 | "shapes" 51 | ], 52 | "visible_layers": "00000000_00000000_0ffc3320_d77fff75", 53 | "zone_display_mode": 0 54 | }, 55 | "git": { 56 | "repo_password": "", 57 | "repo_type": "", 58 | "repo_username": "", 59 | "ssh_key": "" 60 | }, 61 | "meta": { 62 | "filename": "SplitFlap.kicad_prl", 63 | "version": 5 64 | }, 65 | "net_inspector_panel": { 66 | "col_hidden": [ 67 | false, 68 | false, 69 | false, 70 | false, 71 | false, 72 | false, 73 | false, 74 | false, 75 | false, 76 | false 77 | ], 78 | "col_order": [ 79 | 0, 80 | 1, 81 | 2, 82 | 3, 83 | 4, 84 | 5, 85 | 6, 86 | 7, 87 | 8, 88 | 9 89 | ], 90 | "col_widths": [ 91 | 156, 92 | 141, 93 | 103, 94 | 77, 95 | 103, 96 | 106, 97 | 103, 98 | 81, 99 | 103, 100 | 103 101 | ], 102 | "custom_group_rules": [], 103 | "expanded_rows": [], 104 | "filter_by_net_name": true, 105 | "filter_by_netclass": true, 106 | "filter_text": "", 107 | "group_by_constraint": false, 108 | "group_by_netclass": false, 109 | "show_unconnected_nets": false, 110 | "show_zero_pad_nets": false, 111 | "sort_ascending": true, 112 | "sorting_column": 0 113 | }, 114 | "open_jobsets": [], 115 | "project": { 116 | "files": [] 117 | }, 118 | "schematic": { 119 | "selection_filter": { 120 | "graphics": true, 121 | "images": true, 122 | "labels": true, 123 | "lockedItems": false, 124 | "otherItems": true, 125 | "pins": true, 126 | "symbols": true, 127 | "text": true, 128 | "wires": true 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /PCB/Module/SplitFlap.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 1, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "shapes": 1.0, 14 | "tracks": 1.0, 15 | "vias": 1.0, 16 | "zones": 0.6 17 | }, 18 | "selection_filter": { 19 | "dimensions": true, 20 | "footprints": true, 21 | "graphics": true, 22 | "keepouts": true, 23 | "lockedItems": false, 24 | "otherItems": true, 25 | "pads": true, 26 | "text": true, 27 | "tracks": true, 28 | "vias": true, 29 | "zones": true 30 | }, 31 | "visible_items": [ 32 | "vias", 33 | "footprint_text", 34 | "footprint_anchors", 35 | "ratsnest", 36 | "grid", 37 | "footprints_front", 38 | "footprints_back", 39 | "footprint_values", 40 | "footprint_references", 41 | "tracks", 42 | "drc_errors", 43 | "drawing_sheet", 44 | "bitmaps", 45 | "pads", 46 | "zones", 47 | "drc_warnings", 48 | "locked_item_shadows", 49 | "conflict_shadows", 50 | "shapes" 51 | ], 52 | "visible_layers": "00000000_00000000_0ffc3320_d77fff75", 53 | "zone_display_mode": 0 54 | }, 55 | "git": { 56 | "repo_password": "", 57 | "repo_type": "", 58 | "repo_username": "", 59 | "ssh_key": "" 60 | }, 61 | "meta": { 62 | "filename": "SplitFlap.kicad_prl", 63 | "version": 5 64 | }, 65 | "net_inspector_panel": { 66 | "col_hidden": [ 67 | false, 68 | false, 69 | false, 70 | false, 71 | false, 72 | false, 73 | false, 74 | false, 75 | false, 76 | false 77 | ], 78 | "col_order": [ 79 | 0, 80 | 1, 81 | 2, 82 | 3, 83 | 4, 84 | 5, 85 | 6, 86 | 7, 87 | 8, 88 | 9 89 | ], 90 | "col_widths": [ 91 | 156, 92 | 141, 93 | 103, 94 | 77, 95 | 103, 96 | 106, 97 | 103, 98 | 81, 99 | 103, 100 | 10 101 | ], 102 | "custom_group_rules": [], 103 | "expanded_rows": [], 104 | "filter_by_net_name": true, 105 | "filter_by_netclass": true, 106 | "filter_text": "", 107 | "group_by_constraint": false, 108 | "group_by_netclass": false, 109 | "show_unconnected_nets": false, 110 | "show_zero_pad_nets": false, 111 | "sort_ascending": true, 112 | "sorting_column": 0 113 | }, 114 | "open_jobsets": [], 115 | "project": { 116 | "files": [] 117 | }, 118 | "schematic": { 119 | "selection_filter": { 120 | "graphics": true, 121 | "images": true, 122 | "labels": true, 123 | "lockedItems": false, 124 | "otherItems": true, 125 | "pins": true, 126 | "symbols": true, 127 | "text": true, 128 | "wires": true 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /PCB/Backplane/Backplane.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "", 5 | "auto_track_width": false, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 1, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "shapes": 1.0, 14 | "tracks": 1.0, 15 | "vias": 1.0, 16 | "zones": 0.5899999737739563 17 | }, 18 | "ratsnest_display_mode": 0, 19 | "selection_filter": { 20 | "dimensions": true, 21 | "footprints": true, 22 | "graphics": true, 23 | "keepouts": true, 24 | "lockedItems": true, 25 | "otherItems": true, 26 | "pads": true, 27 | "text": true, 28 | "tracks": true, 29 | "vias": true, 30 | "zones": true 31 | }, 32 | "visible_items": [ 33 | "vias", 34 | "footprint_text", 35 | "footprint_anchors", 36 | "ratsnest", 37 | "grid", 38 | "footprints_front", 39 | "footprints_back", 40 | "footprint_values", 41 | "footprint_references", 42 | "tracks", 43 | "drc_errors", 44 | "drawing_sheet", 45 | "bitmaps", 46 | "pads", 47 | "zones", 48 | "drc_warnings", 49 | "drc_exclusions", 50 | "locked_item_shadows", 51 | "conflict_shadows", 52 | "shapes" 53 | ], 54 | "visible_layers": "00000000_00000000_0000b030_fffffdff", 55 | "zone_display_mode": 0 56 | }, 57 | "git": { 58 | "repo_password": "", 59 | "repo_type": "", 60 | "repo_username": "", 61 | "ssh_key": "" 62 | }, 63 | "meta": { 64 | "filename": "Backplane.kicad_prl", 65 | "version": 5 66 | }, 67 | "net_inspector_panel": { 68 | "col_hidden": [ 69 | false, 70 | false, 71 | false, 72 | false, 73 | false, 74 | false, 75 | false, 76 | false, 77 | false, 78 | false 79 | ], 80 | "col_order": [ 81 | 0, 82 | 1, 83 | 2, 84 | 3, 85 | 4, 86 | 5, 87 | 6, 88 | 7, 89 | 8, 90 | 9 91 | ], 92 | "col_widths": [ 93 | 156, 94 | 141, 95 | 103, 96 | 77, 97 | 103, 98 | 106, 99 | 103, 100 | 81, 101 | 103, 102 | 10 103 | ], 104 | "custom_group_rules": [], 105 | "expanded_rows": [], 106 | "filter_by_net_name": true, 107 | "filter_by_netclass": true, 108 | "filter_text": "", 109 | "group_by_constraint": false, 110 | "group_by_netclass": false, 111 | "show_unconnected_nets": false, 112 | "show_zero_pad_nets": false, 113 | "sort_ascending": true, 114 | "sorting_column": 0 115 | }, 116 | "open_jobsets": [], 117 | "project": { 118 | "files": [] 119 | }, 120 | "schematic": { 121 | "selection_filter": { 122 | "graphics": true, 123 | "images": true, 124 | "labels": true, 125 | "lockedItems": false, 126 | "otherItems": true, 127 | "pins": true, 128 | "symbols": true, 129 | "text": true, 130 | "wires": true 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /OpenSCAD/Split_Flap_Module.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<3D_library.scad>; 19 | 20 | // explode 12 to show exploded view and spin the view 21 | //explode = 12; 22 | // explode 0 to show compact view and spin the carousel 23 | explode = 0; 24 | module split_flap() 25 | { 26 | translate(carousel_pos) 27 | { 28 | //rotate([90, 0, 0]) translate(housing_virt_bolt_pos) rotate([90, 0, 0]) circle(); 29 | 30 | rotate([270, 0, 270]) 31 | { 32 | rotate([0, explode == 0 ? -360 * $t : 0, 0]) 33 | { 34 | translate([0, -(explode * 10), 0]) color("#333") karussell_spacer(); 35 | translate([0, -(explode * 2), 0]) color("#888") karussell_schrauben(); 36 | translate([0, -(explode * 13.5), 0]) color("#888") karussell_muttern(); 37 | translate([0, -(explode * 8), 0]) color("#555") karussell_scheibe_position("rechts"); 38 | translate([0, -(explode * 12.5), 0]) color("#555") karussell_scheibe_position("links"); 39 | 40 | // use this trick to speed up things: export rendered carousel wheels to .stl and import it here again. 41 | rotate([90, 90, 0]) translate([0, 0, ((carousel_inner_distance/2) + (carousel_thickness/2) + (explode * 12.5))]) color("#555") import("Carousel_left.stl"); 42 | rotate([90, 90, 0]) translate([0, 0, -((carousel_inner_distance/2) + (carousel_thickness/2) - (explode * 9))]) color("#555") import("Carousel_right.stl"); 43 | 44 | // The Pouley 45 | translate([0, -(explode * 11.5), 0]) color("grey") stepper_pouley(); 46 | translate([0, -(explode * 14), 0]) color("#888") pouley_schrauben(); 47 | 48 | // The flaps 49 | if (explode == 0) 50 | translate([0, -(explode * 10), 0]) rotating_carousel(); 51 | 52 | } 53 | } 54 | 55 | if (explode > 0) 56 | translate([0, 0, (explode * 10)]) example_flap_bottom(28); 57 | 58 | // Stepper Motor, Schrittmotor 59 | translate([0, 0, (explode * 10)]) color("silver") stepper_motor(); 60 | translate([0, 0, -(explode * 3)]) color("#888") motor_schrauben(); 61 | 62 | // Spacer für Motor 63 | translate([0, 0, (explode * 8)]) color("#333") stepper_spacer(); 64 | 65 | // PCB 66 | // OpenSCAD generated PCB 67 | translate([0, 0, (explode * 1)]) pcb(); 68 | translate([0, 0, -(explode * 1.2)]) color("#888") pcb_schrauben(); 69 | translate([0, 0, (explode * 2)]) color("#888") pcb_muttern(); 70 | translate([0, 0, (explode * 0.5)]) color("#333") pcb_spacer(); 71 | 72 | } 73 | 74 | // Gehäuse Seiten: 75 | color([0.85, 0.85, 0.85, 0.6]) 76 | { 77 | // Rechts mit grossem Ausschnitt 78 | %chassis_right(); 79 | 80 | // Links mit Löcher für Motor 81 | %chassis_left(); 82 | 83 | // Front 84 | %chassis_front(); 85 | } 86 | 87 | // Bolt für die Flaps 88 | translate([0, 0, -(explode * 3)]) color("#888") chassis_bolt_screw(); 89 | translate([0, 0, (explode * 2)]) color("#888") chassis_bolt_nut(); 90 | } 91 | 92 | rotate([270, 0, explode > 0 ? -360 * $t : 0]) split_flap(); 93 | 94 | -------------------------------------------------------------------------------- /OpenSCAD/2D_library.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<_settings.scad>; 19 | 20 | 21 | module base_flap(overlap = 0) 22 | { 23 | square([flap_width, flap_height + overlap], center = true); 24 | } 25 | 26 | module flap() 27 | { 28 | base_flap(); 29 | pins(); 30 | } 31 | 32 | module flap_outline() 33 | { 34 | difference() 35 | { 36 | offset(delta = +outline_weight) flap(); 37 | flap(); 38 | } 39 | } 40 | 41 | module pins() 42 | { 43 | translate([ ((flap_width/2) + (flap_pin/2)), -((flap_height/2) - (flap_pin/2))]) 44 | { 45 | square([flap_pin,flap_pin], center = true); 46 | } 47 | translate([-((flap_width/2) + (flap_pin/2)), -((flap_height/2) - (flap_pin/2))]) 48 | { 49 | square([flap_pin,flap_pin], center = true); 50 | } 51 | } 52 | 53 | module pins_outline() 54 | { 55 | difference() 56 | { 57 | offset(delta = +outline_weight) pins(); 58 | pins(); 59 | // lets make the inner line disapear with an additional square 60 | translate([0, -((flap_height/2) - (flap_pin/2))]) square([flap_width, flap_pin + (outline_weight * 2)], center = true); 61 | } 62 | } 63 | 64 | module foil() 65 | { 66 | square([foil_width, foil_height], center = true); 67 | } 68 | 69 | module indicator_cross() 70 | { 71 | square([width_marker, length_marker], center = true); 72 | square([length_marker, width_marker], center = true); 73 | } 74 | 75 | module indicators(){ 76 | // Indicators for CAD initialisation 77 | h_pos = (foil_width / 2) - indicator_position; 78 | v_pos = (foil_height / 2) - indicator_position; 79 | 80 | translate([ h_pos, v_pos, 0]) indicator_cross(); 81 | translate([-h_pos, v_pos, 0]) indicator_cross(); 82 | translate([-h_pos, -v_pos, 0]) indicator_cross(); 83 | translate([ h_pos, -v_pos, 0]) indicator_cross(); 84 | } 85 | 86 | module half_letter(col, row) 87 | { 88 | difference() 89 | { 90 | intersection() 91 | { 92 | // this does the overlap of the letter 93 | //flap(); 94 | base_flap(overlap); 95 | 96 | // mind front and back as well as last character 97 | if(side == "front") 98 | { 99 | rotate([0, 0]) translate([0, -(flap_height / 2)]) 100 | draw_letter(characters[(cols * row) + col]); 101 | } 102 | else 103 | { 104 | character = (cols * row) - col + cols == len(characters) ? 0 : (cols * row) - col + cols ; 105 | rotate([180, 180]) translate([0, (flap_height / 2)]) 106 | draw_letter(characters[character]); 107 | } 108 | } 109 | // Das ist der Balken, der unten alles Überstehende abschneidet 110 | if(side != "front") translate([0, (flap_height/2) - (cut_off_blind/2)]) square([flap_width, cut_off_blind], center = true); 111 | } 112 | } 113 | 114 | module cols_and_rows() 115 | { 116 | for(col=[0:1:cols-1]) 117 | { 118 | for(row=[start_row-1:1:end_row-1]) 119 | { 120 | // this translate shifts the objects in rows and cols 121 | translate([(flap_total_width + h_space_between_flaps) * col, -(flap_height + v_space_between_flaps) * (row - start_row)]) 122 | { 123 | half_letter(col, row); 124 | flap_outline(); 125 | } 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /Raspberry_Pi/html/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Split Flap Display 9 | 10 | 11 | 12 | 13 | 21 | 22 | 41 | 42 | 43 | 44 |
45 |
46 |
47 |

Split Flap Display

48 |
49 |
50 | 51 | 58 |
59 | 60 |
61 |
62 |

Feed Your Text

63 | 64 |
65 |
66 | 67 |
68 | 69 |
70 | 71 | 86 |
87 | 88 |
89 | 90 | 91 |
92 | 93 |
94 | 95 | 96 |
97 | 98 |
99 | 100 | 101 |
102 |
103 | 104 |
105 |
106 | 107 | 111 | 112 | 113 | 114 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /OpenSCAD/_settings.scad: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 4 | Projekt Split-Flap Display Module 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | $fn=100; 20 | 21 | nr_of_flaps = 50; 22 | 23 | // Flaps 24 | flap_width = 50; 25 | flap_height = 40; 26 | flap_thickness = 0.76; 27 | flap_pin = 3; 28 | flap_total_width = flap_width + (2 * flap_pin); 29 | 30 | // Karussell 31 | carousel_diameter = 82; 32 | carousel_thickness = 3; 33 | carousel_inner_distance = 51; 34 | carousel_flap_holes_diameter = 3.4; 35 | carousel_flap_path_radius = 40 - (carousel_flap_holes_diameter/2); 36 | carousel_axis_diameter = 5; 37 | carousel_inner_circle = 24; 38 | carousel_spokes_width = 10; 39 | carousel_pos_x = 20.8; // um 0.5mm nach vorne korrigiert am 08.08.2025, damit die Flaps etwas besser klemmen. 40 | // um 0.2mm nach hinten korrigiert am 31.08.2025, weil das Gehäuse nun 1.0 und nicht mehr 0.8mm ist. 41 | carousel_pos_y = -13.5; 42 | carousel_pos = [carousel_pos_x,carousel_pos_y,0]; 43 | 44 | // Die Löcher werden etwas länger, damit die Darstellung von OpenSCAD stimmt 45 | carousel_thickness_holes = carousel_thickness + 0.01; 46 | 47 | // Ausschnitt 48 | carousel_diameter_cutout = 59; 49 | 50 | // Poulley 51 | pulley_nr_of_holes = 4; 52 | pulley_holes_diameter = 3; 53 | pulley_holes_path_radius = 6.35; 54 | pulley_diameter = 19; 55 | pulley_thickness = 5; 56 | pulley_axis_diameter = carousel_axis_diameter; 57 | 58 | // Spacers Karussell 59 | carousel_spacer_outer_diameter = 6; 60 | carousel_spacer_inner_diameter = 3.2; 61 | carousel_spacer_length = carousel_inner_distance; 62 | carousel_spacer_nr = 4; 63 | carousel_spacer_holes_diameter = 3.2; 64 | carousel_spacer_path_radius = 33; 65 | 66 | // Magnet 67 | magnet_hole_diameter = 1.9; 68 | magnet_hole_path_radius = carousel_spacer_path_radius; 69 | 70 | // Die Schrauben durch die Karussell Spacer 71 | carousel_spacer_screw_length = 60; 72 | carousel_spacer_screw_diameter = 3; 73 | 74 | // Stepper Motor 75 | motor_width = 42; 76 | motor_height = 42; 77 | motor_depth = 34; 78 | motor_corners = 5.5; 79 | motor_axis_diameter = carousel_axis_diameter; 80 | motor_axis_shift = -5; 81 | motor_axis_length = 64; 82 | motor_attachment_depth = 2; 83 | motor_attachment_diameter = 22; 84 | motor_winding_distance = 31; 85 | // Spacer für Motor 86 | motor_spacer_outer_diameter = 6; 87 | motor_spacer_inner_diameter = 3.2; 88 | motor_spacer_length = 20; 89 | // Schrauben für die Befestigung des Motors 90 | motor_screw_diameter = 3; 91 | motor_screw_length = 25; 92 | 93 | // Gehäuse 94 | housing_width = 120; 95 | housing_height = 150; 96 | housing_outer_distance = 70; 97 | //housing_thickness = 0.8; 98 | housing_thickness = 1; 99 | housing_inner_distance = housing_outer_distance - (2 * housing_thickness); 100 | 101 | housing_cutout_height = 104; 102 | housing_cutout_width = 53; 103 | housing_cutout_small_height = 35; 104 | housing_cutout_small_width = 3; 105 | housing_cutout_position_y = 3; 106 | 107 | // Das Loch für den Bolzen 108 | housing_bolt_v = 26; 109 | housing_bolt_h = 16; 110 | housing_virt_bolt_pos = [37, 0, -71]; 111 | 112 | 113 | // PCB 114 | pcb_width = 42.67; 115 | pcb_length = 72; 116 | pcb_edge_width = 17.2; 117 | pcb_edge_length = 7.4; 118 | pcb_thickness = 1.6; 119 | pcb_copper_width = 1.7; 120 | 121 | pcb_hole_diameter = 3.2; 122 | // Die Position der Löcher im Gehäuse für das PCB sind jeweils 123 | // gemessen an den hinteren Löchern für den Motor. 124 | // also: pcb_hole_top_from_left = 52 bedeutet, dass das obere Loch 125 | // für das PCB 52mm vom hinteren Loch des Motors weg ist. 126 | pcb_hole_top_from_left_housing = 52; 127 | pcb_hole_bottom_from_left_housing = 27; 128 | 129 | // Die Position der Löcher im PCB sind jeweils gemessen 130 | // am linken Rand des PCB. 131 | // pcb_hole_top_from_left_housing - pcb_hole_top_from_left_pcb = (motor_spacer_outer_diameter/2) + 0.5 132 | // 52 - 48.5 = (6/2) + 0.5 133 | pcb_hole_top_from_left_pcb = 48.5; 134 | pcb_hole_bottom_from_left_pcb = 23.5; 135 | 136 | // Hall Sensor 137 | pcb_hall_top_from_left = 12.25; 138 | pcb_hall_top_from_top = 10.85; 139 | pcb_hall_width = 2.6; 140 | pcb_hall_length = 3.1; 141 | pcb_hall_height = 0.7; 142 | 143 | // Das PCB soll mit Spacern etwas vom Gehäuse abstehen. 144 | pcb_spacer_length = 2; 145 | pcb_spacer_outer_diameter = 6; 146 | pcb_spacer_inner_diameter = 3.2; 147 | 148 | // always adjust overrides in _font_setup.scad accordingly 149 | 150 | 151 | characters = [ 152 | "0", "A", "B", "C", "D", 153 | "1", "E", "F", "G", "H", 154 | "2", "I", "J", "K", "L", 155 | "3", "M", "N", "O", "P", 156 | "4", "Q", "R", "S", "T", 157 | "5", "U", "V", "W", "X", 158 | "6", "Y", "Z", "+", "-", 159 | "7", "?", "!", ".", ",", 160 | "8", "@", ":", ";", "#", 161 | "9", "*", "$", "/", " ", 162 | ]; 163 | 164 | /* 165 | characters = [ 166 | "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 167 | "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", 168 | "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", 169 | "U", "V", "W", "X", "Y", "Z", "!", "%", "&", "*", 170 | "+", "-", ".", "/", ":", "?", "$", "@", "#", " ", 171 | ]; 172 | */ 173 | 174 | 175 | /* 176 | Settings for the flaps sheet 177 | These settings below take place at 178 | the following files: 179 | - Overallview.scad 180 | - Printview.scad 181 | - _font_setup.scad 182 | - 2D_library.scad 183 | */ 184 | 185 | include<_font_setup.scad>; 186 | 187 | font_preset = "msgothicsfd"; 188 | 189 | letter_facet_number = 50; 190 | 191 | // how many columns and rows to display 192 | cols = 5; 193 | //rows = 10; 194 | rows = ceil(len(characters) / cols); 195 | 196 | // horizontal space between flaps 197 | h_space_between_flaps = 8; 198 | // vertical space between flaps 199 | v_space_between_flaps = 8; 200 | 201 | outline_weight = 0.1; 202 | 203 | // overlap letter for cutting tolerance 204 | overlap = 4; 205 | 206 | // Der Balken, der alles Überstehende abschneidet 207 | cut_off_blind = 4.5; 208 | 209 | // the size of the foil 210 | foil_width = 360; 211 | foil_height = 510; 212 | 213 | // font size of description text 214 | font_size_desc = 3; 215 | 216 | // marker sizes 217 | length_marker = 8; 218 | width_marker = 0.2; 219 | 220 | indicator_position = 14; 221 | 222 | 223 | -------------------------------------------------------------------------------- /Arduino/Split_Flap.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // motor and flap settings 4 | const byte microsteps = 16; // Microsteps for the driver 5 | const byte motorsteps = 200; // Stepper motor takes 200 positions per revolution 6 | const byte flaps = 50; // number of flaps on carrousel 7 | const int ms_per_revolution = motorsteps * microsteps; // microsteps performed by carousel revolution 8 | const int ms_per_flap = ms_per_revolution / flaps; // microsteps performed by flap position 9 | 10 | /* 11 | Speed: The greater the number the slower it turns. 12 | 28800 / microsteps is approximately 6 seconds per revolution. 13 | 14 | grundsätzlich ist jede Geschwindigkeit möglich, solange 15 | sie sich durch 32 (Anz. max. Microsteps) teilen lässt. 16 | sneak: 128000 17 | slow: 38400 18 | normal: 28800 19 | fast: 19200 20 | stress: 9600 21 | */ 22 | const unsigned long speed = 28800 / microsteps; 23 | 24 | // Number of modules 25 | const byte modules = 8; 26 | 27 | // Arduino pin settings 28 | // as of Backplane V3 PCB 29 | #include 30 | // input settings 31 | #include 32 | 33 | const byte hall_pin[modules] = 34 | { 35 | hall_pin_0, 36 | hall_pin_1, 37 | hall_pin_2, 38 | hall_pin_3, 39 | hall_pin_4, 40 | hall_pin_5, 41 | hall_pin_6, 42 | hall_pin_7, 43 | }; 44 | 45 | const byte step_pin[modules] = 46 | { 47 | step_pin_0, 48 | step_pin_1, 49 | step_pin_2, 50 | step_pin_3, 51 | step_pin_4, 52 | step_pin_5, 53 | step_pin_6, 54 | step_pin_7, 55 | }; 56 | 57 | // some other things 58 | unsigned long curr_time = 0; 59 | unsigned long prev_time = 0; 60 | 61 | int cur_ms_pos[modules] = {}; 62 | int new_ms_pos[modules] = {}; 63 | int ms_to_go[modules] = {}; 64 | char new_char[modules] = {}; 65 | bool set_ms_pos[modules] = {}; 66 | 67 | // Feineinstellung 68 | const int UNSET = -1; 69 | int init_ms_pos[modules] = {UNSET, UNSET, UNSET, UNSET, UNSET, UNSET, UNSET, UNSET}; 70 | //const int init_ms_pos[modules] = {20, 20, 20, 20, 20, 20, 20, 20}; 71 | 72 | // read values out of EEPROM 73 | #include 74 | const int i2c_addr = EEPROM.read(0); 75 | const int default_init_ms_pos = EEPROM.read(1); 76 | 77 | void setup() { 78 | Serial.begin(9600); 79 | 80 | // Join I2C bus as slave with uniqe address 81 | Wire.begin(i2c_addr); 82 | // set I2C receive and request functions 83 | Wire.onReceive(receive_i2c_event); 84 | Wire.onRequest(request_i2c_event); 85 | 86 | // setup pins and variables 87 | for(byte module = 0; module < modules; module++){ 88 | pinMode(step_pin[module], OUTPUT); 89 | pinMode(hall_pin[module], INPUT); 90 | 91 | // ascii 32 is space character 92 | new_char[module] = 32; 93 | cur_ms_pos[module] = 0; 94 | set_ms_pos[module] = false; 95 | set_ms_to_go(module); 96 | 97 | if (init_ms_pos[module] == UNSET) { 98 | init_ms_pos[module] = default_init_ms_pos; 99 | } 100 | } 101 | } 102 | 103 | // ToDo XXX 104 | #include 105 | 106 | void loop() { 107 | motor_run(); 108 | receive_serial_event(); 109 | } 110 | 111 | void motor_run(){ 112 | curr_time = micros(); 113 | if((curr_time - prev_time) >= speed){ 114 | prev_time = curr_time; 115 | for(byte module = 0; module < modules; module++){ 116 | if(ms_to_go[module] > 0){ 117 | digitalWrite(step_pin[module], HIGH); 118 | digitalWrite(step_pin[module], LOW); 119 | update_ms_pos(module); 120 | } 121 | } 122 | } 123 | } 124 | 125 | void update_ms_pos(byte module){ 126 | cur_ms_pos[module]++; 127 | ms_to_go[module]--; 128 | 129 | // reset cur_ms_pos only once if magnet is at home place 130 | if(is_magnet_home(module)){ 131 | if(set_ms_pos[module]){ 132 | // uncomment for debugging purposes. 133 | /* 134 | if(cur_ms_pos[module] > ms_per_revolution){ 135 | Serial.print("magnet behind counter "); 136 | } 137 | if(cur_ms_pos[module] < ms_per_revolution - 1){ // weshalb - 1 ? 138 | Serial.print("magnet before counter "); 139 | } 140 | */ 141 | 142 | cur_ms_pos[module] = 0; 143 | 144 | // instant adjustment of microsteps to go 145 | if(new_ms_pos[module] != ms_per_revolution){ 146 | set_ms_to_go(module); 147 | }else{ 148 | ms_to_go[module] = init_ms_pos[module]; 149 | } 150 | } 151 | set_ms_pos[module] = false; 152 | }else{ 153 | /* 154 | // wait for magnet after one revolution 155 | if(cur_ms_pos[module] >= ms_per_revolution){ 156 | if(cur_ms_pos[module] <= (ms_per_revolution * 3)){ 157 | ms_to_go[module] = 1; 158 | } 159 | } 160 | */ 161 | set_ms_pos[module] = true; 162 | } 163 | } 164 | 165 | /* 166 | case 1: 167 | if new position is greater than current position, go straight forward 168 | positions to walk = new - cur 169 | cur = 6 170 | new = 30 171 | 30 - 6 = 24 172 | 173 | case 2: 174 | if new position is smaller than current position, recalculate respecting flap positions 175 | positions to walk = (flaps - cur) + new 176 | cur = 44 177 | new = 30 178 | (50 - 44) + 30 = 36 179 | */ 180 | void set_ms_to_go(byte module){ 181 | unsigned int new_flap_pos = get_position(new_char[module]); 182 | // proceed only if a valid character has been provided 183 | if(new_flap_pos == 0){ 184 | return; 185 | } 186 | 187 | new_ms_pos[module] = new_flap_pos * ms_per_flap; 188 | 189 | // pretend to be on correct position, even if magnet has not been spotted 190 | int position = cur_ms_pos[module] % ms_per_revolution; 191 | 192 | // do the magic! 193 | if(new_ms_pos[module] > position){ 194 | ms_to_go[module] = new_ms_pos[module] + init_ms_pos[module] - position; 195 | }else{ 196 | ms_to_go[module] = new_ms_pos[module] + init_ms_pos[module] + (ms_per_revolution - position); 197 | } 198 | } 199 | 200 | bool is_magnet_home(byte module) { 201 | return (digitalRead(hall_pin[module]) == 0); 202 | } 203 | 204 | bool is_running(byte module) { 205 | return (ms_to_go[module] > 0); 206 | } 207 | 208 | /* 209 | * serial events 210 | */ 211 | 212 | void receive_serial_event() { 213 | if (!Serial.available()) { 214 | return; 215 | } 216 | 217 | String content = Serial.readStringUntil('\n'); 218 | for (byte module = 0; module < modules && module < content.length(); module++) { 219 | new_char[module] = content.charAt(module); 220 | set_ms_to_go(module); 221 | } 222 | } 223 | 224 | /* 225 | * I2C events 226 | */ 227 | void receive_i2c_event(int bytes) { 228 | // register byte verwerfen 229 | Wire.read(); 230 | 231 | // Maximal nur `modules` Bytes einlesen 232 | for (byte module = 0; module < modules && Wire.available(); module++) { 233 | byte b = Wire.read(); 234 | 235 | // nur druckbare ASCII-Zeichen akzeptieren 236 | // https://www.eso.org/~ndelmott/ascii.html 237 | if (b >= 0x20 && b <= 0x7E) { 238 | new_char[module] = b; 239 | set_ms_to_go(module); 240 | } 241 | } 242 | 243 | // Rest verwerfen falls mehr als 8 Bytes geschickt wurden 244 | while (Wire.available()) Wire.read(); 245 | } 246 | 247 | void request_i2c_event(){ 248 | for(byte module = 0; module < modules; module++){ 249 | if(ms_to_go[module] > 0){ 250 | Wire.write(255); 251 | }else{ 252 | //round, in case something ran out of count 253 | Wire.write(round((float)(cur_ms_pos[module] - init_ms_pos[module]) / ms_per_flap)); 254 | //Wire.write((cur_ms_pos[module] - init_ms_pos[module]) / ms_per_flap); 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /OpenSCAD/_font_setup.scad: -------------------------------------------------------------------------------- 1 | /* 2 | This code has originaly been created by Scott. 3 | For details see https://github.com/scottbez1/splitflap/tree/master 4 | Many thanks Scott for your big effort in your project! 5 | */ 6 | 7 | /* 8 | Configure font presets below. Each font has the following settings: 9 | 'array_name' 10 | This is the name to which you point to in the code. 11 | 12 | 'font' 13 | Font name - see https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Text#Using_Fonts_&_Styles 14 | Hint. Select "Help > Fontlist" to get a list of all included fonts. You will finde the appropriate names there. 15 | 16 | 'height' 17 | Font size (height) relative to flaps. i.e. a value of 1 sets the font size equal to the height of the top and bottom flaps. 18 | 19 | 'width' 20 | Width scale factor. 1 = use default font width. 21 | 22 | 'offset_x' 23 | Horizontal offset, in mm, of letters within flaps. A value of 0 uses default font centering. 24 | 25 | 'offset_y' 26 | Vertical offset, in mm, of letters within flaps. A value of 0 uses default font centering. 27 | 28 | 'color' 29 | the default color of the character, used in 3D_library.scad 30 | 31 | 'type' 32 | whether the given font or a white square should be drawn. Valid values are "square" or "font" 33 | 34 | 'overrides' 35 | Array of position/size overrides for specific letters. Each entry is a set of overrides for a single letter, 36 | specified as an array with the following entries: 37 | - Letter to override (e.g. "M"). Case must match for the override to apply. 38 | - Additional X position offset, in mm (e.g. -5). Can be undef or 0 to omit. 39 | - Additional Y position offset, in mm (e.g. 2.5). Can be undef or 0 to omit. 40 | - Height override, as a value relative to flap height (e.g. 0.7). Replaces letter_height for this letter. Can be undef to omit. 41 | - Width override, as a value relative to default font width (e.g. 0.7). Replaces letter_width for this letter. Can be undef to omit. 42 | - Color override. Can be undef or 0 to omit. 43 | 44 | */ 45 | 46 | 47 | use; 48 | 49 | 50 | _font_settings = [ 51 | "msgothicsfd", [ 52 | "font", "MS Gothic SFD", 53 | "height", 0.9, 54 | "width", 0.92, 55 | "offset_x", 0, 56 | "offset_y", 1, 57 | "color", "white", 58 | "type", "letter", 59 | "overrides", [ 60 | ["-", 0, -8, 1, 0.8], 61 | ["+", 0, -8, 0.9, 0.9], 62 | [".", 13, 7, 0.9, 0.9], 63 | [",", 13, 10, 0.9, 0.9], 64 | [":", 0, 7, 0.9, 0.9], 65 | [";", 0, 7, 0.9, 0.9], 66 | ["$", 0, 0, 0.75, 1.1], 67 | ["/", 0, 2.9, 0.95, 1, "white", "square"], 68 | ], 69 | ], 70 | ]; 71 | 72 | /* 73 | // try this if you wanna have fun with colors :-) 74 | _font_settings = [ 75 | "msgothicsfd", [ 76 | "font", "MS Gothic SFD", 77 | "height", 0.9, 78 | "width", 0.92, 79 | "offset_x", 0, 80 | "offset_y", 1, 81 | "color", "white", 82 | "type", "letter", 83 | "overrides", [ 84 | ["0", 1, 1, 1, 1, [1.000, 0.000, 0.000], "square"], 85 | ["A", 1, 1, 1, 1, [1.000, 0.120, 0.000], "square"], 86 | ["B", 1, 1, 1, 1, [1.000, 0.240, 0.000], "square"], 87 | ["C", 1, 1, 1, 1, [1.000, 0.360, 0.000], "square"], 88 | ["D", 1, 1, 1, 1, [1.000, 0.480, 0.000], "square"], 89 | ["1", 1, 1, 1, 1, [1.000, 0.600, 0.000], "square"], 90 | ["E", 1, 1, 1, 1, [1.000, 0.720, 0.000], "square"], 91 | ["F", 1, 1, 1, 1, [1.000, 0.840, 0.000], "square"], 92 | ["G", 1, 1, 1, 1, [1.000, 0.960, 0.000], "square"], 93 | ["H", 1, 1, 1, 1, [0.960, 1.000, 0.000], "square"], 94 | ["2", 1, 1, 1, 1, [0.840, 1.000, 0.000], "square"], 95 | ["I", 1, 1, 1, 1, [0.720, 1.000, 0.000], "square"], 96 | ["J", 1, 1, 1, 1, [0.600, 1.000, 0.000], "square"], 97 | ["K", 1, 1, 1, 1, [0.480, 1.000, 0.000], "square"], 98 | ["L", 1, 1, 1, 1, [0.360, 1.000, 0.000], "square"], 99 | ["3", 1, 1, 1, 1, [0.240, 1.000, 0.000], "square"], 100 | ["M", 1, 1, 1, 1, [0.120, 1.000, 0.000], "square"], 101 | ["N", 1, 1, 1, 1, [0.000, 1.000, 0.000], "square"], 102 | ["O", 1, 1, 1, 1, [0.000, 1.000, 0.120], "square"], 103 | ["P", 1, 1, 1, 1, [0.000, 1.000, 0.240], "square"], 104 | ["4", 1, 1, 1, 1, [0.000, 1.000, 0.360], "square"], 105 | ["Q", 1, 1, 1, 1, [0.000, 1.000, 0.480], "square"], 106 | ["R", 1, 1, 1, 1, [0.000, 1.000, 0.600], "square"], 107 | ["S", 1, 1, 1, 1, [0.000, 1.000, 0.720], "square"], 108 | ["T", 1, 1, 1, 1, [0.000, 1.000, 0.840], "square"], 109 | ["5", 1, 1, 1, 1, [0.000, 1.000, 0.960], "square"], 110 | ["U", 1, 1, 1, 1, [0.000, 0.960, 1.000], "square"], 111 | ["V", 1, 1, 1, 1, [0.000, 0.840, 1.000], "square"], 112 | ["W", 1, 1, 1, 1, [0.000, 0.720, 1.000], "square"], 113 | ["X", 1, 1, 1, 1, [0.000, 0.600, 1.000], "square"], 114 | ["6", 1, 1, 1, 1, [0.000, 0.480, 1.000], "square"], 115 | ["Y", 1, 1, 1, 1, [0.000, 0.360, 1.000], "square"], 116 | ["Z", 1, 1, 1, 1, [0.000, 0.240, 1.000], "square"], 117 | ["+", 1, 1, 1, 1, [0.000, 0.120, 1.000], "square"], 118 | ["-", 1, 1, 1, 1, [0.000, 0.000, 1.000], "square"], 119 | ["7", 1, 1, 1, 1, [0.120, 0.000, 1.000], "square"], 120 | ["?", 1, 1, 1, 1, [0.240, 0.000, 1.000], "square"], 121 | ["!", 1, 1, 1, 1, [0.360, 0.000, 1.000], "square"], 122 | [".", 1, 1, 1, 1, [0.480, 0.000, 1.000], "square"], 123 | [",", 1, 1, 1, 1, [0.600, 0.000, 1.000], "square"], 124 | ["8", 1, 1, 1, 1, [0.720, 0.000, 1.000], "square"], 125 | ["@", 1, 1, 1, 1, [0.840, 0.000, 1.000], "square"], 126 | [":", 1, 1, 1, 1, [0.960, 0.000, 1.000], "square"], 127 | [";", 1, 1, 1, 1, [1.000, 0.000, 0.960], "square"], 128 | ["#", 1, 1, 1, 1, [1.000, 0.000, 0.840], "square"], 129 | ["9", 1, 1, 1, 1, [1.000, 0.000, 0.720], "square"], 130 | ["*", 1, 1, 1, 1, [1.000, 0.000, 0.600], "square"], 131 | ["$", 1, 1, 1, 1, [1.000, 0.000, 0.480], "square"], 132 | ["/", 1, 1, 1, 1, [1.000, 0.000, 0.360], "square"], 133 | [" ", 1, 1, 1, 1, [1.000, 0.000, 0.240], "square"], 134 | ], 135 | ], 136 | ]; 137 | */ 138 | 139 | 140 | 141 | // Private functions 142 | function _get_entry_in_dict_array(arr, key) = arr[search([key], arr)[0] + 1]; 143 | function _get_font_settings() = _get_entry_in_dict_array(_font_settings, font_preset); 144 | 145 | // Public functions 146 | function get_font_setting(key) = _get_entry_in_dict_array(_get_font_settings(), key); 147 | function get_letter_overrides(letter) = get_font_setting("overrides")[search([letter], get_font_setting("overrides"))[0]]; 148 | 149 | /* 150 | Public modules 151 | */ 152 | module draw_letter(letter) { 153 | overrides = get_letter_overrides(letter); 154 | height = is_undef(overrides[3]) ? get_font_setting("height") : overrides[3]; 155 | width = is_undef(overrides[4]) ? get_font_setting("width") : overrides[4]; 156 | type = is_undef(overrides[6]) ? get_font_setting("type") : overrides[6]; 157 | 158 | translate([0, -flap_height * height, 0]) 159 | { 160 | scale([width, 1, 1]) 161 | { 162 | offset_x = is_undef(overrides[1]) ? get_font_setting("offset_x") : get_font_setting("offset_x") + overrides[1]; 163 | offset_y = is_undef(overrides[2]) ? get_font_setting("offset_y") : get_font_setting("offset_y") + overrides[2]; 164 | translate([offset_x, offset_y]) 165 | { 166 | if (type == "square") 167 | { 168 | translate([0, flap_height * height]) square([flap_width, (flap_height * height * 2)], center = true); 169 | } 170 | else 171 | { 172 | text(text=letter, size=flap_height * height * 2, font=get_font_setting("font"), halign="center", $fn=letter_facet_number); 173 | } 174 | } 175 | } 176 | } 177 | } 178 | 179 | -------------------------------------------------------------------------------- /PCB/Module/Module.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.0, 41 | "height": 5.0, 42 | "width": 2.7 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.45 52 | } 53 | }, 54 | "diff_pair_dimensions": [], 55 | "drc_exclusions": [], 56 | "meta": { 57 | "version": 2 58 | }, 59 | "rule_severities": { 60 | "annular_width": "error", 61 | "clearance": "error", 62 | "connection_width": "warning", 63 | "copper_edge_clearance": "error", 64 | "copper_sliver": "warning", 65 | "courtyards_overlap": "error", 66 | "creepage": "error", 67 | "diff_pair_gap_out_of_range": "error", 68 | "diff_pair_uncoupled_length_too_long": "error", 69 | "drill_out_of_range": "error", 70 | "duplicate_footprints": "warning", 71 | "extra_footprint": "warning", 72 | "footprint": "error", 73 | "footprint_filters_mismatch": "ignore", 74 | "footprint_symbol_mismatch": "warning", 75 | "footprint_type_mismatch": "warning", 76 | "hole_clearance": "error", 77 | "hole_near_hole": "error", 78 | "hole_to_hole": "error", 79 | "holes_co_located": "warning", 80 | "invalid_outline": "error", 81 | "isolated_copper": "warning", 82 | "item_on_disabled_layer": "error", 83 | "items_not_allowed": "error", 84 | "length_out_of_range": "error", 85 | "lib_footprint_issues": "warning", 86 | "lib_footprint_mismatch": "warning", 87 | "malformed_courtyard": "error", 88 | "microvia_drill_out_of_range": "error", 89 | "mirrored_text_on_front_layer": "warning", 90 | "missing_courtyard": "warning", 91 | "missing_footprint": "warning", 92 | "net_conflict": "warning", 93 | "nonmirrored_text_on_back_layer": "warning", 94 | "npth_inside_courtyard": "warning", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "warning", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_on_edge_cuts": "error", 106 | "text_thickness": "warning", 107 | "through_hole_pad_without_hole": "error", 108 | "too_many_vias": "error", 109 | "track_angle": "error", 110 | "track_dangling": "warning", 111 | "track_segment_length": "error", 112 | "track_width": "error", 113 | "tracks_crossing": "error", 114 | "unconnected_items": "error", 115 | "unresolved_variable": "error", 116 | "via_dangling": "warning", 117 | "zones_intersect": "error" 118 | }, 119 | "rules": { 120 | "max_error": 0.005, 121 | "min_clearance": 0.0, 122 | "min_connection": 0.0, 123 | "min_copper_edge_clearance": 0.5, 124 | "min_groove_width": 0.0, 125 | "min_hole_clearance": 0.25, 126 | "min_hole_to_hole": 0.25, 127 | "min_microvia_diameter": 0.2, 128 | "min_microvia_drill": 0.1, 129 | "min_resolved_spokes": 2, 130 | "min_silk_clearance": 0.0, 131 | "min_text_height": 0.8, 132 | "min_text_thickness": 0.08, 133 | "min_through_hole_diameter": 0.3, 134 | "min_track_width": 0.0, 135 | "min_via_annular_width": 0.1, 136 | "min_via_diameter": 0.5, 137 | "solder_mask_to_copper_clearance": 0.0, 138 | "use_height_for_length_calcs": true 139 | }, 140 | "teardrop_options": [ 141 | { 142 | "td_onpthpad": true, 143 | "td_onroundshapesonly": false, 144 | "td_onsmdpad": true, 145 | "td_ontrackend": false, 146 | "td_onvia": true 147 | } 148 | ], 149 | "teardrop_parameters": [ 150 | { 151 | "td_allow_use_two_tracks": true, 152 | "td_curve_segcount": 0, 153 | "td_height_ratio": 1.0, 154 | "td_length_ratio": 0.5, 155 | "td_maxheight": 2.0, 156 | "td_maxlen": 1.0, 157 | "td_on_pad_in_zone": false, 158 | "td_target_name": "td_round_shape", 159 | "td_width_to_size_filter_ratio": 0.9 160 | }, 161 | { 162 | "td_allow_use_two_tracks": true, 163 | "td_curve_segcount": 0, 164 | "td_height_ratio": 1.0, 165 | "td_length_ratio": 0.5, 166 | "td_maxheight": 2.0, 167 | "td_maxlen": 1.0, 168 | "td_on_pad_in_zone": false, 169 | "td_target_name": "td_rect_shape", 170 | "td_width_to_size_filter_ratio": 0.9 171 | }, 172 | { 173 | "td_allow_use_two_tracks": true, 174 | "td_curve_segcount": 0, 175 | "td_height_ratio": 1.0, 176 | "td_length_ratio": 0.5, 177 | "td_maxheight": 2.0, 178 | "td_maxlen": 1.0, 179 | "td_on_pad_in_zone": false, 180 | "td_target_name": "td_track_end", 181 | "td_width_to_size_filter_ratio": 0.9 182 | } 183 | ], 184 | "track_widths": [], 185 | "tuning_pattern_settings": { 186 | "diff_pair_defaults": { 187 | "corner_radius_percentage": 80, 188 | "corner_style": 1, 189 | "max_amplitude": 1.0, 190 | "min_amplitude": 0.2, 191 | "single_sided": false, 192 | "spacing": 1.0 193 | }, 194 | "diff_pair_skew_defaults": { 195 | "corner_radius_percentage": 80, 196 | "corner_style": 1, 197 | "max_amplitude": 1.0, 198 | "min_amplitude": 0.2, 199 | "single_sided": false, 200 | "spacing": 0.6 201 | }, 202 | "single_track_defaults": { 203 | "corner_radius_percentage": 80, 204 | "corner_style": 1, 205 | "max_amplitude": 1.0, 206 | "min_amplitude": 0.2, 207 | "single_sided": false, 208 | "spacing": 0.6 209 | } 210 | }, 211 | "via_dimensions": [], 212 | "zones_allow_external_fillets": false 213 | }, 214 | "ipc2581": { 215 | "dist": "", 216 | "distpn": "", 217 | "internal_id": "", 218 | "mfg": "", 219 | "mpn": "" 220 | }, 221 | "layer_pairs": [], 222 | "layer_presets": [], 223 | "viewports": [] 224 | }, 225 | "boards": [], 226 | "cvpcb": { 227 | "equivalence_files": [] 228 | }, 229 | "libraries": { 230 | "pinned_footprint_libs": [], 231 | "pinned_symbol_libs": [] 232 | }, 233 | "meta": { 234 | "filename": "SplitFlap.kicad_pro", 235 | "version": 3 236 | }, 237 | "net_settings": { 238 | "classes": [ 239 | { 240 | "bus_width": 12, 241 | "clearance": 0.2, 242 | "diff_pair_gap": 0.25, 243 | "diff_pair_via_gap": 0.25, 244 | "diff_pair_width": 0.2, 245 | "line_style": 0, 246 | "microvia_diameter": 0.3, 247 | "microvia_drill": 0.1, 248 | "name": "Default", 249 | "pcb_color": "rgba(0, 0, 0, 0.000)", 250 | "priority": 2147483647, 251 | "schematic_color": "rgba(0, 0, 0, 0.000)", 252 | "track_width": 0.2, 253 | "via_diameter": 0.6, 254 | "via_drill": 0.3, 255 | "wire_width": 6 256 | } 257 | ], 258 | "meta": { 259 | "version": 4 260 | }, 261 | "net_colors": null, 262 | "netclass_assignments": null, 263 | "netclass_patterns": [] 264 | }, 265 | "pcbnew": { 266 | "last_paths": { 267 | "gencad": "", 268 | "idf": "", 269 | "netlist": "", 270 | "plot": "", 271 | "pos_files": "", 272 | "specctra_dsn": "", 273 | "step": "", 274 | "svg": "", 275 | "vrml": "" 276 | }, 277 | "page_layout_descr_file": "" 278 | }, 279 | "schematic": { 280 | "legacy_lib_dir": "", 281 | "legacy_lib_list": [] 282 | }, 283 | "sheets": [], 284 | "text_variables": {} 285 | } 286 | -------------------------------------------------------------------------------- /PCB/Module/SplitFlap.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.0, 41 | "height": 1.0, 42 | "width": 3.0 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.45 52 | } 53 | }, 54 | "diff_pair_dimensions": [], 55 | "drc_exclusions": [], 56 | "meta": { 57 | "version": 2 58 | }, 59 | "rule_severities": { 60 | "annular_width": "error", 61 | "clearance": "error", 62 | "connection_width": "warning", 63 | "copper_edge_clearance": "error", 64 | "copper_sliver": "warning", 65 | "courtyards_overlap": "error", 66 | "creepage": "error", 67 | "diff_pair_gap_out_of_range": "error", 68 | "diff_pair_uncoupled_length_too_long": "error", 69 | "drill_out_of_range": "error", 70 | "duplicate_footprints": "warning", 71 | "extra_footprint": "warning", 72 | "footprint": "error", 73 | "footprint_filters_mismatch": "ignore", 74 | "footprint_symbol_mismatch": "warning", 75 | "footprint_type_mismatch": "warning", 76 | "hole_clearance": "error", 77 | "hole_near_hole": "error", 78 | "hole_to_hole": "error", 79 | "holes_co_located": "warning", 80 | "invalid_outline": "error", 81 | "isolated_copper": "warning", 82 | "item_on_disabled_layer": "error", 83 | "items_not_allowed": "error", 84 | "length_out_of_range": "error", 85 | "lib_footprint_issues": "warning", 86 | "lib_footprint_mismatch": "warning", 87 | "malformed_courtyard": "error", 88 | "microvia_drill_out_of_range": "error", 89 | "mirrored_text_on_front_layer": "warning", 90 | "missing_courtyard": "warning", 91 | "missing_footprint": "warning", 92 | "net_conflict": "warning", 93 | "nonmirrored_text_on_back_layer": "warning", 94 | "npth_inside_courtyard": "warning", 95 | "padstack": "warning", 96 | "pth_inside_courtyard": "warning", 97 | "shorting_items": "error", 98 | "silk_edge_clearance": "warning", 99 | "silk_over_copper": "warning", 100 | "silk_overlap": "warning", 101 | "skew_out_of_range": "error", 102 | "solder_mask_bridge": "error", 103 | "starved_thermal": "error", 104 | "text_height": "warning", 105 | "text_on_edge_cuts": "error", 106 | "text_thickness": "warning", 107 | "through_hole_pad_without_hole": "error", 108 | "too_many_vias": "error", 109 | "track_angle": "error", 110 | "track_dangling": "warning", 111 | "track_segment_length": "error", 112 | "track_width": "error", 113 | "tracks_crossing": "error", 114 | "unconnected_items": "error", 115 | "unresolved_variable": "error", 116 | "via_dangling": "warning", 117 | "zones_intersect": "error" 118 | }, 119 | "rules": { 120 | "max_error": 0.005, 121 | "min_clearance": 0.0, 122 | "min_connection": 0.0, 123 | "min_copper_edge_clearance": 0.5, 124 | "min_groove_width": 0.0, 125 | "min_hole_clearance": 0.25, 126 | "min_hole_to_hole": 0.25, 127 | "min_microvia_diameter": 0.2, 128 | "min_microvia_drill": 0.1, 129 | "min_resolved_spokes": 2, 130 | "min_silk_clearance": 0.0, 131 | "min_text_height": 0.8, 132 | "min_text_thickness": 0.08, 133 | "min_through_hole_diameter": 0.3, 134 | "min_track_width": 0.0, 135 | "min_via_annular_width": 0.1, 136 | "min_via_diameter": 0.5, 137 | "solder_mask_to_copper_clearance": 0.0, 138 | "use_height_for_length_calcs": true 139 | }, 140 | "teardrop_options": [ 141 | { 142 | "td_onpthpad": true, 143 | "td_onroundshapesonly": false, 144 | "td_onsmdpad": true, 145 | "td_ontrackend": false, 146 | "td_onvia": true 147 | } 148 | ], 149 | "teardrop_parameters": [ 150 | { 151 | "td_allow_use_two_tracks": true, 152 | "td_curve_segcount": 0, 153 | "td_height_ratio": 1.0, 154 | "td_length_ratio": 0.5, 155 | "td_maxheight": 2.0, 156 | "td_maxlen": 1.0, 157 | "td_on_pad_in_zone": false, 158 | "td_target_name": "td_round_shape", 159 | "td_width_to_size_filter_ratio": 0.9 160 | }, 161 | { 162 | "td_allow_use_two_tracks": true, 163 | "td_curve_segcount": 0, 164 | "td_height_ratio": 1.0, 165 | "td_length_ratio": 0.5, 166 | "td_maxheight": 2.0, 167 | "td_maxlen": 1.0, 168 | "td_on_pad_in_zone": false, 169 | "td_target_name": "td_rect_shape", 170 | "td_width_to_size_filter_ratio": 0.9 171 | }, 172 | { 173 | "td_allow_use_two_tracks": true, 174 | "td_curve_segcount": 0, 175 | "td_height_ratio": 1.0, 176 | "td_length_ratio": 0.5, 177 | "td_maxheight": 2.0, 178 | "td_maxlen": 1.0, 179 | "td_on_pad_in_zone": false, 180 | "td_target_name": "td_track_end", 181 | "td_width_to_size_filter_ratio": 0.9 182 | } 183 | ], 184 | "track_widths": [], 185 | "tuning_pattern_settings": { 186 | "diff_pair_defaults": { 187 | "corner_radius_percentage": 80, 188 | "corner_style": 1, 189 | "max_amplitude": 1.0, 190 | "min_amplitude": 0.2, 191 | "single_sided": false, 192 | "spacing": 1.0 193 | }, 194 | "diff_pair_skew_defaults": { 195 | "corner_radius_percentage": 80, 196 | "corner_style": 1, 197 | "max_amplitude": 1.0, 198 | "min_amplitude": 0.2, 199 | "single_sided": false, 200 | "spacing": 0.6 201 | }, 202 | "single_track_defaults": { 203 | "corner_radius_percentage": 80, 204 | "corner_style": 1, 205 | "max_amplitude": 1.0, 206 | "min_amplitude": 0.2, 207 | "single_sided": false, 208 | "spacing": 0.6 209 | } 210 | }, 211 | "via_dimensions": [], 212 | "zones_allow_external_fillets": false 213 | }, 214 | "ipc2581": { 215 | "dist": "", 216 | "distpn": "", 217 | "internal_id": "", 218 | "mfg": "", 219 | "mpn": "" 220 | }, 221 | "layer_pairs": [], 222 | "layer_presets": [], 223 | "viewports": [] 224 | }, 225 | "boards": [], 226 | "cvpcb": { 227 | "equivalence_files": [] 228 | }, 229 | "libraries": { 230 | "pinned_footprint_libs": [], 231 | "pinned_symbol_libs": [] 232 | }, 233 | "meta": { 234 | "filename": "SplitFlap.kicad_pro", 235 | "version": 3 236 | }, 237 | "net_settings": { 238 | "classes": [ 239 | { 240 | "bus_width": 12, 241 | "clearance": 0.2, 242 | "diff_pair_gap": 0.25, 243 | "diff_pair_via_gap": 0.25, 244 | "diff_pair_width": 0.2, 245 | "line_style": 0, 246 | "microvia_diameter": 0.3, 247 | "microvia_drill": 0.1, 248 | "name": "Default", 249 | "pcb_color": "rgba(0, 0, 0, 0.000)", 250 | "priority": 2147483647, 251 | "schematic_color": "rgba(0, 0, 0, 0.000)", 252 | "track_width": 0.2, 253 | "via_diameter": 0.6, 254 | "via_drill": 0.3, 255 | "wire_width": 6 256 | } 257 | ], 258 | "meta": { 259 | "version": 4 260 | }, 261 | "net_colors": null, 262 | "netclass_assignments": null, 263 | "netclass_patterns": [] 264 | }, 265 | "pcbnew": { 266 | "last_paths": { 267 | "gencad": "", 268 | "idf": "", 269 | "netlist": "", 270 | "plot": "", 271 | "pos_files": "", 272 | "specctra_dsn": "", 273 | "step": "", 274 | "svg": "", 275 | "vrml": "" 276 | }, 277 | "page_layout_descr_file": "" 278 | }, 279 | "schematic": { 280 | "legacy_lib_dir": "", 281 | "legacy_lib_list": [] 282 | }, 283 | "sheets": [], 284 | "text_variables": {} 285 | } 286 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /PCB/Backplane/Backplane.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 1.2192, 41 | "height": 1.7272, 42 | "width": 1.7272 43 | }, 44 | "silk_line_width": 0.12, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 1.0, 47 | "silk_text_size_v": 1.0, 48 | "silk_text_thickness": 0.15, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "45_degree_only": false, 52 | "min_clearance": 0.508 53 | } 54 | }, 55 | "diff_pair_dimensions": [ 56 | { 57 | "gap": 0.0, 58 | "via_gap": 0.0, 59 | "width": 0.0 60 | } 61 | ], 62 | "drc_exclusions": [], 63 | "meta": { 64 | "version": 2 65 | }, 66 | "rule_severities": { 67 | "annular_width": "error", 68 | "clearance": "error", 69 | "connection_width": "warning", 70 | "copper_edge_clearance": "error", 71 | "copper_sliver": "warning", 72 | "courtyards_overlap": "error", 73 | "creepage": "error", 74 | "diff_pair_gap_out_of_range": "error", 75 | "diff_pair_uncoupled_length_too_long": "error", 76 | "drill_out_of_range": "error", 77 | "duplicate_footprints": "warning", 78 | "extra_footprint": "warning", 79 | "footprint": "error", 80 | "footprint_filters_mismatch": "ignore", 81 | "footprint_symbol_mismatch": "warning", 82 | "footprint_type_mismatch": "error", 83 | "hole_clearance": "error", 84 | "hole_near_hole": "error", 85 | "hole_to_hole": "error", 86 | "holes_co_located": "warning", 87 | "invalid_outline": "error", 88 | "isolated_copper": "warning", 89 | "item_on_disabled_layer": "error", 90 | "items_not_allowed": "error", 91 | "length_out_of_range": "error", 92 | "lib_footprint_issues": "warning", 93 | "lib_footprint_mismatch": "warning", 94 | "malformed_courtyard": "error", 95 | "microvia_drill_out_of_range": "error", 96 | "mirrored_text_on_front_layer": "warning", 97 | "missing_courtyard": "error", 98 | "missing_footprint": "warning", 99 | "net_conflict": "warning", 100 | "nonmirrored_text_on_back_layer": "warning", 101 | "npth_inside_courtyard": "error", 102 | "padstack": "error", 103 | "pth_inside_courtyard": "error", 104 | "shorting_items": "error", 105 | "silk_edge_clearance": "warning", 106 | "silk_over_copper": "warning", 107 | "silk_overlap": "warning", 108 | "skew_out_of_range": "error", 109 | "solder_mask_bridge": "warning", 110 | "starved_thermal": "error", 111 | "text_height": "warning", 112 | "text_on_edge_cuts": "error", 113 | "text_thickness": "warning", 114 | "through_hole_pad_without_hole": "error", 115 | "too_many_vias": "error", 116 | "track_angle": "error", 117 | "track_dangling": "warning", 118 | "track_segment_length": "error", 119 | "track_width": "error", 120 | "tracks_crossing": "error", 121 | "unconnected_items": "error", 122 | "unresolved_variable": "error", 123 | "via_dangling": "warning", 124 | "zones_intersect": "error" 125 | }, 126 | "rules": { 127 | "allow_blind_buried_vias": false, 128 | "allow_microvias": false, 129 | "max_error": 0.005, 130 | "min_clearance": 0.0, 131 | "min_connection": 0.0, 132 | "min_copper_edge_clearance": 0.01, 133 | "min_groove_width": 0.0, 134 | "min_hole_clearance": 0.25, 135 | "min_hole_to_hole": 0.25, 136 | "min_microvia_diameter": 0.2, 137 | "min_microvia_drill": 0.1, 138 | "min_resolved_spokes": 2, 139 | "min_silk_clearance": 0.0, 140 | "min_text_height": 0.8, 141 | "min_text_thickness": 0.08, 142 | "min_through_hole_diameter": 0.3, 143 | "min_track_width": 0.2, 144 | "min_via_annular_width": 0.05, 145 | "min_via_diameter": 0.4, 146 | "solder_mask_to_copper_clearance": 0.0, 147 | "use_height_for_length_calcs": true 148 | }, 149 | "teardrop_options": [ 150 | { 151 | "td_onpthpad": true, 152 | "td_onroundshapesonly": false, 153 | "td_onsmdpad": true, 154 | "td_ontrackend": false, 155 | "td_onvia": true 156 | } 157 | ], 158 | "teardrop_parameters": [ 159 | { 160 | "td_allow_use_two_tracks": true, 161 | "td_curve_segcount": 0, 162 | "td_height_ratio": 1.0, 163 | "td_length_ratio": 0.5, 164 | "td_maxheight": 2.0, 165 | "td_maxlen": 1.0, 166 | "td_on_pad_in_zone": false, 167 | "td_target_name": "td_round_shape", 168 | "td_width_to_size_filter_ratio": 0.9 169 | }, 170 | { 171 | "td_allow_use_two_tracks": true, 172 | "td_curve_segcount": 0, 173 | "td_height_ratio": 1.0, 174 | "td_length_ratio": 0.5, 175 | "td_maxheight": 2.0, 176 | "td_maxlen": 1.0, 177 | "td_on_pad_in_zone": false, 178 | "td_target_name": "td_rect_shape", 179 | "td_width_to_size_filter_ratio": 0.9 180 | }, 181 | { 182 | "td_allow_use_two_tracks": true, 183 | "td_curve_segcount": 0, 184 | "td_height_ratio": 1.0, 185 | "td_length_ratio": 0.5, 186 | "td_maxheight": 2.0, 187 | "td_maxlen": 1.0, 188 | "td_on_pad_in_zone": false, 189 | "td_target_name": "td_track_end", 190 | "td_width_to_size_filter_ratio": 0.9 191 | } 192 | ], 193 | "track_widths": [ 194 | 0.0 195 | ], 196 | "tuning_pattern_settings": { 197 | "diff_pair_defaults": { 198 | "corner_radius_percentage": 80, 199 | "corner_style": 1, 200 | "max_amplitude": 1.0, 201 | "min_amplitude": 0.2, 202 | "single_sided": false, 203 | "spacing": 1.0 204 | }, 205 | "diff_pair_skew_defaults": { 206 | "corner_radius_percentage": 80, 207 | "corner_style": 1, 208 | "max_amplitude": 1.0, 209 | "min_amplitude": 0.2, 210 | "single_sided": false, 211 | "spacing": 0.6 212 | }, 213 | "single_track_defaults": { 214 | "corner_radius_percentage": 80, 215 | "corner_style": 1, 216 | "max_amplitude": 1.0, 217 | "min_amplitude": 0.2, 218 | "single_sided": false, 219 | "spacing": 0.6 220 | } 221 | }, 222 | "via_dimensions": [ 223 | { 224 | "diameter": 0.0, 225 | "drill": 0.0 226 | } 227 | ], 228 | "zones_allow_external_fillets": false, 229 | "zones_use_no_outline": true 230 | }, 231 | "ipc2581": { 232 | "dist": "", 233 | "distpn": "", 234 | "internal_id": "", 235 | "mfg": "", 236 | "mpn": "" 237 | }, 238 | "layer_pairs": [], 239 | "layer_presets": [], 240 | "viewports": [] 241 | }, 242 | "boards": [], 243 | "cvpcb": { 244 | "equivalence_files": [] 245 | }, 246 | "libraries": { 247 | "pinned_footprint_libs": [], 248 | "pinned_symbol_libs": [] 249 | }, 250 | "meta": { 251 | "filename": "Backplane.kicad_pro", 252 | "version": 3 253 | }, 254 | "net_settings": { 255 | "classes": [ 256 | { 257 | "bus_width": 12, 258 | "clearance": 0.2, 259 | "diff_pair_gap": 0.25, 260 | "diff_pair_via_gap": 0.25, 261 | "diff_pair_width": 0.2, 262 | "line_style": 0, 263 | "microvia_diameter": 0.3, 264 | "microvia_drill": 0.1, 265 | "name": "Default", 266 | "pcb_color": "rgba(0, 0, 0, 0.000)", 267 | "priority": 2147483647, 268 | "schematic_color": "rgba(0, 0, 0, 0.000)", 269 | "track_width": 0.25, 270 | "via_diameter": 0.8, 271 | "via_drill": 0.4, 272 | "wire_width": 6 273 | }, 274 | { 275 | "bus_width": 12, 276 | "clearance": 0.2, 277 | "diff_pair_gap": 0.25, 278 | "diff_pair_via_gap": 0.25, 279 | "diff_pair_width": 0.2, 280 | "line_style": 0, 281 | "microvia_diameter": 0.3, 282 | "microvia_drill": 0.1, 283 | "name": "+12V", 284 | "pcb_color": "rgba(0, 0, 0, 0.000)", 285 | "priority": 0, 286 | "schematic_color": "rgba(0, 0, 0, 0.000)", 287 | "track_width": 0.5, 288 | "via_diameter": 0.8, 289 | "via_drill": 0.5, 290 | "wire_width": 6 291 | }, 292 | { 293 | "bus_width": 12, 294 | "clearance": 0.2, 295 | "diff_pair_gap": 0.25, 296 | "diff_pair_via_gap": 0.25, 297 | "diff_pair_width": 0.2, 298 | "line_style": 0, 299 | "microvia_diameter": 0.3, 300 | "microvia_drill": 0.1, 301 | "name": "+5V", 302 | "pcb_color": "rgba(0, 0, 0, 0.000)", 303 | "priority": 1, 304 | "schematic_color": "rgba(0, 0, 0, 0.000)", 305 | "track_width": 0.25, 306 | "via_diameter": 0.8, 307 | "via_drill": 0.4, 308 | "wire_width": 6 309 | }, 310 | { 311 | "bus_width": 12, 312 | "clearance": 0.2, 313 | "diff_pair_gap": 0.25, 314 | "diff_pair_via_gap": 0.25, 315 | "diff_pair_width": 0.2, 316 | "line_style": 0, 317 | "microvia_diameter": 0.3, 318 | "microvia_drill": 0.1, 319 | "name": "GND", 320 | "pcb_color": "rgba(0, 0, 0, 0.000)", 321 | "priority": 2, 322 | "schematic_color": "rgba(0, 0, 0, 0.000)", 323 | "track_width": 0.25, 324 | "via_diameter": 0.8, 325 | "via_drill": 0.4, 326 | "wire_width": 6 327 | } 328 | ], 329 | "meta": { 330 | "version": 4 331 | }, 332 | "net_colors": null, 333 | "netclass_assignments": null, 334 | "netclass_patterns": [ 335 | { 336 | "netclass": "GND", 337 | "pattern": "GND" 338 | }, 339 | { 340 | "netclass": "+12V", 341 | "pattern": "+12V" 342 | }, 343 | { 344 | "netclass": "+5V", 345 | "pattern": "+5V" 346 | } 347 | ] 348 | }, 349 | "pcbnew": { 350 | "last_paths": { 351 | "gencad": "", 352 | "idf": "", 353 | "netlist": "../../../../../../Applications/KiCad/KiCad.app/Contents/Applications/", 354 | "plot": "gerbers", 355 | "pos_files": "", 356 | "specctra_dsn": "", 357 | "step": "", 358 | "svg": "", 359 | "vrml": "" 360 | }, 361 | "page_layout_descr_file": "" 362 | }, 363 | "schematic": { 364 | "legacy_lib_dir": "", 365 | "legacy_lib_list": [] 366 | }, 367 | "sheets": [], 368 | "text_variables": {} 369 | } 370 | -------------------------------------------------------------------------------- /Raspberry_Pi/split-flap.php: -------------------------------------------------------------------------------- 1 | '0', 'A', 'B', 'C', 'D', // 5 15 | '1', 'E', 'F', 'G', 'H', // 10 16 | '2', 'I', 'J', 'K', 'L', // 15 17 | '3', 'M', 'N', 'O', 'P', // 20 18 | '4', 'Q', 'R', 'S', 'T', // 25 19 | '5', 'U', 'V', 'W', 'X', // 30 20 | '6', 'Y', 'Z', '+', '-', // 35 21 | '7', '?', '!', '.', ',', // 40 22 | '8', '@', ':', ';', '#', // 45 23 | '9', '*', '$', '/', ' ', // 50 24 | ); 25 | 26 | /** @var $targets */ 27 | $targets = array( 28 | # lines 29 | 0 => array( 30 | # arduinos or units 31 | 0 => array( 32 | 'size' => 8, // number of modules 33 | 'i2c' => 0x0a, // i2c address of arduino 34 | ), 35 | 1 => array( 36 | 'size' => 8, // number of modules 37 | 'i2c' => 0x0b, // i2c address of arduino 38 | ), 39 | ), 40 | 1 => array( 41 | 0 => array( 42 | 'size' => 8, 43 | 'i2c' => 0x0c, 44 | ), 45 | 1 => array( 46 | 'size' => 8, 47 | 'i2c' => 0x0d, 48 | ), 49 | ), 50 | 2 => array( 51 | 0 => array( 52 | 'size' => 8, 53 | 'i2c' => 0x0e, 54 | ), 55 | 1 => array( 56 | 'size' => 8, 57 | 'i2c' => 0x0f, 58 | ), 59 | ), 60 | ); 61 | 62 | // count $rows, $cols and $modules 63 | $rows = count($targets); 64 | $cols = 0; 65 | foreach ($targets[0] as $arduino) 66 | { 67 | $cols += $arduino['size']; 68 | } 69 | $modules = $rows * $cols; 70 | 71 | /** 72 | * @param array $options 73 | * @return array|false|null 74 | */ 75 | function filter_options (array $options) 76 | { 77 | $filters = [ 78 | 'same' => [ 79 | 'filter' => FILTER_VALIDATE_BOOLEAN, 80 | 'options' => [ 81 | 'default' => false, 82 | ], 83 | ], 84 | 'noblock' => [ 85 | 'filter' => FILTER_VALIDATE_BOOLEAN, 86 | 'options' => [ 87 | 'default' => false, 88 | ], 89 | ], 90 | 'wipe' => [ 91 | 'filter' => FILTER_VALIDATE_BOOLEAN, 92 | 'options' => [ 93 | 'default' => false, 94 | ], 95 | ], 96 | 'animation' => [ 97 | 'filter' => FILTER_VALIDATE_REGEXP, 98 | 'options' => [ 99 | 'default' => "start", 100 | 'regexp' => "/^(start|stop|right|left|top|bottom|random|snake1|snake2|snake3|snake4|cross|diagonal|round)$/", 101 | ], 102 | ], 103 | 'text' => [ 104 | 'filter' => FILTER_DEFAULT, 105 | #'flags' => FILTER_REQUIRE_ARRAY, 106 | ], 107 | 'slowdown' => [ 108 | 'filter' => FILTER_VALIDATE_INT, 109 | 'options' => [ 110 | 'default' => 1, 111 | 'min_range' => 1, 112 | 'max_range' => 10, 113 | ], 114 | ], 115 | ]; 116 | return (filter_var_array($options, $filters)); 117 | } 118 | 119 | 120 | /** 121 | * @param string $string 122 | * @return string 123 | */ 124 | 125 | function sanitize_string(string $string, $wipe = false): string 126 | { 127 | global $characters, $placeholder, $modules; 128 | 129 | # Unicode-Normalisierung in Form KD (Kompatibilitätszerlegung) 130 | $string = Normalizer::normalize($string, Normalizer::FORM_KD); 131 | 132 | # replace whitespaces 133 | $string = preg_replace('/\r\n|\r|\n|\t|\v|\f/u', ' ', $string); 134 | 135 | # Entfernt diakritische Zeichen (Akzente) 136 | $string = preg_replace('/\p{Mn}/u', '', $string); 137 | 138 | $string = iconv('UTF-8', 'ASCII//TRANSLIT', $string); 139 | 140 | # max length to number of modules 141 | $string = substr($string, 0, $modules); 142 | 143 | # string to uppercase 144 | $string = strtoupper($string); 145 | 146 | # fill up string with spaces 147 | if ($wipe) { 148 | if (strlen($string) < $modules) { 149 | for ($i = strlen($string); $i < $modules; $i++) { 150 | $string[$i] = " "; 151 | } 152 | } 153 | } 154 | 155 | # replace any character with placeholder, if it isn't part of the $characters array 156 | for ($i = 0; $i < strlen($string); $i++) { 157 | if (!in_array($string[$i], $characters, true)) { 158 | $string[$i] = $placeholder; 159 | } 160 | } 161 | return ($string); 162 | } 163 | 164 | /** 165 | * @param string $text 166 | * @return array $targets 167 | * merges a sanitized text string into the three-dimensional array 168 | * that has been specified above. 169 | * also specify another array structure in order to be more 170 | * intuitive. we change it from: 171 | * array 172 | * rows 173 | * arduinos 174 | * modules 175 | * 176 | * to: 177 | * array 178 | * rows 179 | * cols 180 | */ 181 | function config_to_grid($text, $same = false): array 182 | { 183 | global $targets, $placeholder, $characters; 184 | $array = array(); 185 | $current_positions = get_current_positions(); 186 | 187 | $index = 0; 188 | foreach ($targets as $row => $line) { 189 | $col = 0; 190 | foreach ($line as $arduino) { 191 | for ($i = 0; $i < $arduino['size']; $i++) { 192 | $array[$row][$col]['i2c'] = $arduino['i2c']; 193 | $array[$row][$col]['module'] = $i; 194 | $array[$row][$col]['size'] = $arduino['size']; 195 | $array[$row][$col]['char'] = isset($text[$index]) ? $text[$index] : $placeholder; 196 | # pos 0 in Arduino is the same as pos 50 in PHP 197 | $array[$row][$col]['cur_pos'] = ($current_positions[$row][$col] == 0) ? 50 : $current_positions[$row][$col]; 198 | $array[$row][$col]['new_pos'] = array_search($array[$row][$col]['char'], $characters); 199 | $array[$row][$col]['walk'] = pos_to_walk($array[$row][$col]['cur_pos'], $array[$row][$col]['new_pos'], $same); 200 | $array[$row][$col]['timer'] = 0; 201 | $index++; 202 | $col++; 203 | } 204 | } 205 | } 206 | return ($array); 207 | } 208 | 209 | /** 210 | * @param $targets 211 | * @return array $delay 212 | * 213 | * this function sets the "timer" values according to the 214 | * animation and changes the array structure 215 | * from: 216 | * array 217 | * rows 218 | * cols 219 | * values ... 220 | * 221 | * to: 222 | * array 223 | * timer 224 | * i2c 225 | * module 226 | * values ... 227 | */ 228 | function set_delay ($array, $animation = "start", $slowdown = 1): array 229 | { 230 | global $rows, $cols; 231 | 232 | $modules = $rows * $cols; 233 | $counter = 0; 234 | $timer = 0; 235 | $return = array(); 236 | 237 | if ($animation === "random") { 238 | $randomized = range(0, $modules - 1); 239 | shuffle($randomized); 240 | } 241 | 242 | for($row = 0; $row < $rows; $row++){ 243 | for($col = 0; $col < $cols; $col++){ 244 | switch ($animation) { 245 | case "start": 246 | $timer = 0; 247 | break; 248 | case "stop": 249 | $timer = $array[$row][$col]['walk']; 250 | break; 251 | case "left": 252 | $timer = ($col % $cols === 0) ? $cols : --$timer; 253 | break; 254 | case "right": 255 | $timer = ($col % $cols === 0) ? 0 : ++$timer; 256 | break; 257 | case "top": 258 | $slowdown = 3; 259 | $timer = ($rows - 1) - $row; 260 | break; 261 | case "bottom": 262 | $slowdown = 3; 263 | $timer = $row; 264 | break; 265 | case "random": 266 | $timer = $randomized[$counter]; 267 | break; 268 | case "snake1": 269 | $timer = ($modules - 1) - ($row % 2 === 0 ? (($row + 1) * $cols - $col - 1) : $counter); 270 | break; 271 | case "snake2": 272 | $timer = ($modules - 1) - ($row % 2 === 1 ? (($row + 1) * $cols - $col - 1) : $counter); 273 | break; 274 | case "snake3": 275 | $timer = $row % 2 === 0 ? (($row + 1) * $cols - $col - 1) : $counter; 276 | break; 277 | case "snake4": 278 | $timer = $row % 2 === 1 ? (($row + 1) * $cols - $col - 1) : $counter; 279 | break; 280 | case "cross": 281 | ($row % 2 === 0) ? $timer++ : $timer--; 282 | break; 283 | case "round": 284 | if ($row == 0) { $timer = ($timer + 1); } 285 | else if($row == ($rows - 1)){ $timer = ((($cols - 1) + ($rows - 1)) + ($cols - $col)); } 286 | else 287 | { 288 | if ($col == 0) { $timer = ((($cols - 1) + ($rows - 1)) + ($cols + $row)); } 289 | else if($col == ($cols - 1)){ $timer = $cols + $row; } 290 | } 291 | break; 292 | case "diagonal": 293 | $slowdown = 2; 294 | $timer = ($row * 2) + $col; 295 | break; 296 | } 297 | $counter++; 298 | 299 | # only add module to array, if it is meant to be running 300 | if($array[$row][$col]['walk'] > 0){ 301 | $array[$row][$col]['timer'] = $timer * $slowdown; 302 | 303 | $module = $array[$row][$col]['module']; 304 | $i2c = $array[$row][$col]['i2c']; 305 | $timeout = $timer * $slowdown; 306 | 307 | $return[$timeout][$i2c]['size'] = $array[$row][$col]['size']; 308 | $return[$timeout][$i2c][$module] = $array[$row][$col]; 309 | } 310 | } 311 | } 312 | 313 | krsort($return, SORT_NUMERIC); 314 | return ($return); 315 | } 316 | 317 | /** 318 | * @return array 319 | */ 320 | function get_current_positions () : array 321 | { 322 | global $fd, $targets; 323 | $array = array(); 324 | foreach($targets as $row => $line) { 325 | $col = 0; 326 | foreach($line as $arduino) { 327 | if(i2c_select($fd, $arduino['i2c'])) { 328 | $positions = i2c_read($fd, $arduino['size']); 329 | for ($i = 0; $i < $arduino['size']; $i++) { 330 | $array[$row][$col] = $positions[$i]; 331 | $col++; 332 | } 333 | } 334 | } 335 | } 336 | return $array; 337 | } 338 | 339 | /** 340 | * @param $targets 341 | * @return void 342 | */ 343 | function run_carrousel($targets): void 344 | { 345 | global $fd, $delay_per_position, $placeholder; 346 | $prev_time = 0; 347 | $counter = 0; 348 | 349 | foreach ($targets as $time => $delay) { 350 | $sleep = $counter == 0 ? 0 : ($prev_time - $time) * $delay_per_position; 351 | usleep($sleep); 352 | $counter++; 353 | $prev_time = $time; 354 | foreach ($delay as $i2c => $address) { 355 | unset($text); 356 | for ($i = 0; $i < $address['size']; $i++) { 357 | $text[$i] = ord($address[$i]['char'] ?? $placeholder); 358 | } 359 | 360 | if (i2c_select($fd, $i2c)) { 361 | i2c_write($fd, 0, $text); 362 | } 363 | } 364 | } 365 | } 366 | 367 | 368 | /** 369 | * @param $cur 370 | * @param $new 371 | * @param bool $same 372 | * @return int 373 | */ 374 | function pos_to_walk($cur, $new, $same = false): int 375 | { 376 | global $flaps; 377 | if(is_int($cur) && is_int($new)) 378 | { 379 | if($new === $cur) 380 | { 381 | if($same) 382 | { 383 | return (0); 384 | } else 385 | { 386 | return ($flaps); 387 | } 388 | } 389 | else if($cur >= 255){ 390 | // XXX ist das richtig??? 391 | return (255); 392 | } 393 | else if($new > $cur) 394 | { 395 | return ($new - $cur); 396 | } else 397 | { 398 | return ($flaps - $cur) + $new; 399 | } 400 | } else 401 | { 402 | return (0); 403 | } 404 | } 405 | 406 | 407 | function block(){ 408 | global $delay_per_position; 409 | while(is_running(get_current_positions())){ 410 | # pause for a short time 411 | usleep($delay_per_position); 412 | } 413 | } 414 | 415 | 416 | /** 417 | * @param array $array 418 | * @return bool 419 | */ 420 | function is_running(array $array) { 421 | foreach ($array as $value) { 422 | if (is_array($value)) { 423 | if (is_running($value)) { 424 | return true; 425 | } 426 | }else{ 427 | if ($value >= 255) { 428 | return true; 429 | } 430 | } 431 | } 432 | return false; 433 | } 434 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Intro 2 | Older people (like me) look back with nostalgia at train stations and airports, middle-aged people are captivated by technology, and the younger generation is excited by the clattering sound combined with the non-virtual movement. The split flap display remains a fascinating display panel for text and small animations. 3 | 4 | One day, while watching a video about split flap displays, I suddenly felt the desire to own one. After a relatively short research, I realized that only a few manufacturers build such displays. There are, for example, [Oat Foundry](https://www.oatfoundry.com/), [Vestaboard](https://www.vestaboard.com/), [Scott Bezek](https://github.com/scottbez1/splitflap) or the one called "Split Flap Display Manufacturer." No matter which manufacturer you choose, the displays are either very expensive or don't meet my requirements. So I decided to try building one myself. 5 | 6 | [![DIY Split Flap Display on YouTube](https://i.ytimg.com/vi/Lept-73BvrE/hq720.jpg)](https://www.youtube.com/watch?v=Lept-73BvrE) 7 | 8 | A typical split flap display looks best when it can display text. This text is naturally composed of characters, i.e., letters, numbers, and special characters. Optionally, symbols, colors, or images can also be displayed. A character is represented on the display by a so-called module. A module consists of a housing, a stepper motor, a motor driver, a PCB that connects the driver and the motor, a carousel, 50 flaps for the characters, and, of course, various spacers and screws that hold the whole thing together. A magnetic sensor (Hall effect sensor) is also integrated into the carousel, which tells the microprocessor when position 0 or the home position has been reached. 9 | 10 | As already mentioned, a single module produces little effect, so we want multiple modules for a display to achieve an intense visual effect. I decided to control eight modules with an [Arduino Micro](https://docs.arduino.cc/hardware/micro/). The modules therefore always come in units of eight. The Micro offers 20 digital input/output pins. Two of these are required per module: an output pin as a stepper for the driver and an input pin for the Hall effect sensor to determine the feedback for a full rotation of the carousel. This means a total of 16 Arduino pins are used for eight modules. The Arduino, in turn, is placed on the backplane PCB, where the modules are plugged into so-called card edge connectors. This PCB primarily controls two things: 11 | - The digital communication between the Arduino and the modules (drivers & Hall sensors) 12 | - The power supply for all electronics, including the motors 13 | 14 | **Animation of spinning spool using OpenSCAD** 15 | 16 | [![Spinning Carousel](https://g000ze.github.io/docs/index.html)](https://github.com/user-attachments/assets/af98eea6-6dcd-4d9f-962c-459cd9d655be) 17 | 18 | [![Exploded View]](https://github.com/user-attachments/assets/ecffe0bf-9c8d-47f0-b8ce-c02ede4a916f) 19 | 20 | ## The Housing 21 | The housing is used to position the motor, along with the carousel and flaps, as well as the PCB with the Hall sensor. If everything is exactly right, the flaps will drop at the right time and the magnet will move correctly over the sensor. I used 0.8 mm sheet steel for the housing. The 3D OpenSCAD drawing can be found [here](https://github.com/g000ze/Split-Flap-Display/blob/main/OpenSCAD/Chassis.scad). I found a company in Switzerland called [Blexon](https://blexon.com), that will punch and bend the housing according to my specifications. 22 | 23 | Housing 24 | 25 | ## The Stepper Motor 26 | Normally, Nema 17 stepper motors are mounted on the side where the shaft is located. This display is different; the motor must be mounted on one side, and the shaft, where the carousel is screwed to, must be on the opposite side. The [17HS13-0404D](https://www.omc-stepperonline.com/de/doppel-schacht-nema-17-bipolar-1-8deg-26ncm-36-8oz-in-0-4a-12v-42x34mm-4-draehte-17hs13-0404d) is the only Nema 17 I could find with the shaft on the opposite side from where it is mounted. This motor is actually a "dual shaft" motor. The 17HS13-0404D is powered by +12 volts. 27 | 28 | 17HS13-0404D 29 | 30 | ## The Driver 31 | To operate a stepper motor, you need a driver. There are many. I experimented with stepper motor drivers for a long time until I finally came across the [TMC2208](https://www.digikey.ch/de/products/detail/watterott-electronic-gmbh/20170003-002/10071142) "Silent Step Stick" from [Watterott](https://learn.watterott.com/de/silentstepstick/). This driver allows the 17HS13-0404D to run almost silently. Finally, the sound of a split-flap display plays a crucial role. We want to hear only the flaps clattering, not the motors. 32 | 33 | ## The PCBs 34 | Two different types of PCBs are required. One is a small PCB that is attached to each module. It essentially carries the driver and the Hall sensor. The other is the backplane PCB, which connects the Arduino's electronic wires to the eight modules via card-edge connectors. The six connections that run via the card-edge connectors are: 35 | - Hall sensor 36 | - GND 37 | - +5V 38 | - Step 39 | - GND 40 | - +12V 41 | 42 | The TMC2208 and the Hall sensor on the module PCB each operate at +5 VCC, while the motor naturally requires 12 volts. 43 | 44 | PCB Module 45 | PCB Backplane 46 | 47 | ## The Carousel 48 | The Carousel is attached to the pouley on the motor shaft so that it can be rotated by the steppermotor. It also holds the 50 flaps in position. The stepper motor is located inside the carousel. With its 82mm diameter, the carousel fits perfectly around the motor and there's enough space for the 50 flap positions. The two wheels are different, as the left wheel contains spokes, while the right wheel is open. 49 | 50 | Carousel 51 | 52 | I decided to use [3mm PVC plates](https://www.vink.ch/wa_files/Produktblatt_20PVC-U_20TROVIDUR_20EC_205_3_2015_20Ind.pdf) for the carousel. A company named [Waterjet](https://www.waterjet.ch/) located in Switzerland did the water cutting to cut out the carousel wheels. 53 | 54 | ## The Pouley 55 | The pouley connects the motor and the carousel together. [This](https://www.pololu.com/product/1998) one was actually the only one I found to suit the motor shaft. So I bought it, even though it is insanely overpriced. 56 | 57 | ## The Flaps 58 | The flaps are the essential parts, since they show the text that you want to see. Basically almost all kind of fonts, characters, signs and pictures are possible. After endless searching, trying and disapointments, I found a company named [Ren Peck](http://www.ren-peck.com/) in China who was willing to create what I am looking for. In fact it is the very same company that created the flaps for [Scott Bezek](https://github.com/scottbez1/splitflap)s Split Flap Display. 59 | 60 | Flaps 61 | 62 | One flap has basically the size of 40mm height and 50mm width. There are two little notches on each side in order to hold them in the carousel and to make them actually flip down. 63 | 64 | ![Flaps Small](https://github.com/user-attachments/assets/0acedc98-daa6-42de-a18b-f75c15f40719) 65 | 66 | ## The Software 67 | The software was mainly written by me. See GitHub repository for details and ask me if you get any questions or suggestions regarding that. 68 | 69 | ## Other 70 | 71 | ## Tools Used 72 | - OpenSCAD: https://openscad.org/ 73 | - KiCad: https://www.kicad.org/ 74 | - FreeCAD: https://www.freecad.org/ 75 | - For the Raspberry Pi to communicate with Arduino using I2C, I used this extension: https://github.com/tasoftch/php-i2c-extension 76 | 77 | ## Parts 78 | ### Module 79 | Exploded_Split_Flap_Module 80 | 81 | 82 | | Part | Nr. | Provider | Model | Link | 83 | |-----------------------------------|-----|---------------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------| 84 | | Housing | 1 | Blexon Galgenen | | https://blexon.com/ | 85 | | Stepper Motor | 2 | Stepper Online | 17HS13-0404D | https://www.omc-stepperonline.com/de/doppel-schacht-nema-17-bipolar-1-8deg-26ncm-36-8oz-in-0-4a-12v-42x34mm-4-draehte-17hs13-0404d | 86 | | Wheels for the Carousel | 3 | Waterjet Mörschwil | | https://www.waterjet.ch/ | 87 | | Flaps | 4 | Ren Peck China | | https://www.alibaba.com/product-detail/China-Ren-peck-supply-emotion-cards-60650877066.html | 88 | | Pouley 5mm Shaft, M3 Holes | 5 | Eckstein | | https://eckstein-shop.de/PololuUniversalAluminumMountingHubfor5mmShaft2CM3Holes2-Pack or https://www.pololu.com/product/1998 | 89 | | PCB | 6 | JLCPCB | | https://jlcpcb.com/ | 90 | | Stepper Driver | 7 | Digikey | TMC2208 | https://www.digikey.ch/de/products/detail/analog-devices-inc-maxim-integrated/TMC2208SILENTSTEPSTICK/6873626 | 91 | | Spacer Motor 20mm | 8 | Axxatronic | OG-PA-6-3,2-20 | https://www.axxatronic.de/distanzhuelsen/kunststoff/OG-PA-0060-0032.html | 92 | | Spacer Carousel 51mm | 8 | Axxatronic | OG-PA-6-3,2-51 | https://www.axxatronic.de/distanzhuelsen/kunststoff/OG-PA-0060-0032.html | 93 | | Spacer PCB 2mm | 8 | Axxatronic | OG-PA-6-3,2-02 | https://www.axxatronic.de/distanzhuelsen/kunststoff/OG-PA-0060-0032.html | 94 | | Magnet | 9 | supermagnete.ch | S-02-02-N | https://www.supermagnete.ch/scheibenmagnete-neodym/scheibenmagnet-2mm-2mm_S-02-02-N | 95 | | Screws | 10 | Schraubenking | | https://www.schraubenking.ch/ | 96 | 97 | ### Module PCB 98 | PCB Module 99 | 100 | 101 | | Part | Nr. | Provider | Model | Link | 102 | |-----------------------------------|-----|---------------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------| 103 | | 10K Resistor | 1 | Digikey | CRGCQ1210F10K | https://www.digikey.ch/en/products/detail/te-connectivity-passive-product/CRGCQ1210F10K/8576485 | 104 | | Header Connector | 2 | Digikey | NPPC082KFMS-RC | https://www.digikey.ch/en/products/detail/sullins-connector-solutions/NPPC082KFMS-RC/776176 | 105 | | Hall Sensor | 3 | Digikey | 620-1002-1-ND | https://www.digikey.ch/de/products/detail/allegro-microsystems/A1102LLHLT-T/1006577 | 106 | | 47 µF Capacitor | 4 | Digikey | APA0606470M025R | https://www.digikey.ch/en/products/detail/kyocera-avx/APA0606470M025R/15195370 | 107 | | Wires Connector | 5 | Digikey | 009176004873906 | https://www.digikey.ch/en/products/detail/kyocera-avx/009176004873906/8540378 | 108 | 109 | ### Backplane PCB 110 | 111 | | Part | Nr. | Provider | Model | Link | 112 | |-----------------------------------|-----|---------------------|-----------------|------------------------------------------------------------------------------------------------------------------------------------| 113 | | Arduino Micro | | Digikey | A000053 | https://www.digikey.ch/de/products/detail/arduino/A000053/4486332 | 114 | | Voltage Converter | | Digikey | R-78K5.0-1.0 | https://www.digikey.ch/en/products/detail/recom-power/R-78K5-0-1-0/18093047 | 115 | | Card Edge Connector (old version) | | Digikey | EDC305122-ND | https://www.digikey.ch/de/products/detail/edac-inc/305-012-520-202/107502 | 116 | | Card Edge Connector (new version) | | Digikey | EBC06DRXH | https://www.digikey.ch/de/products/detail/sullins-connector-solutions/EBC06DRXH/927248 | 117 | | Connector Header for Arduino | | Digikey | 1-215307-7 | https://www.digikey.ch/en/products/detail/te-connectivity-amp-connectors/1-215307-7/2278494 | 118 | | Screw Terminal for 12V Power | | Digikey | 282836-1 | https://www.digikey.ch/en/products/detail/te-connectivity-amp-connectors/282836-1/2222325 | 119 | 120 | 121 | Generated by https://www.tablesgenerator.com/markdown_tables 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /OpenSCAD/3D_library.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Stefan Frech, 10.12.2021 - 28.09.2024 3 | Projekt Split-Flap Display Module 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | include<_settings.scad>; 19 | 20 | module senk_schraube_m3(length) { 21 | // Schrauben Kopf Imbus (Sechskant) 22 | difference() 23 | { 24 | cylinder(h=1.5, r1=3, r2=1.5, $fn=24*3); 25 | cylinder(h=0.8*3, r=1.15, $fn=6, center=true); 26 | } 27 | // Schraube ohne Gewinde, ist schneller. 28 | translate([0, 0, 1.5]) cylinder(d=3, h=length, center=false); 29 | } 30 | 31 | module mutter_m3(){ 32 | difference(){ 33 | cylinder(h=2.2, r=3, $fn=6, center=true); 34 | cylinder(h=2.2 + 0.1, r=1.5, center=true); 35 | } 36 | } 37 | 38 | module spacer(length, outer, inner) 39 | { 40 | difference(){ 41 | cylinder(d=outer, h=length, center = true); 42 | cylinder(d=inner, h=length + 0.1, center = true); 43 | } 44 | } 45 | 46 | module karussell_scheibe_rechts(){ 47 | difference(){ 48 | cylinder(d=carousel_diameter, h=carousel_thickness, center = true); 49 | 50 | // Ring 51 | cylinder(d=carousel_diameter_cutout, h=carousel_thickness_holes, center = true); 52 | 53 | // Löcher für Flaps 54 | for(i=[1:nr_of_flaps]){ 55 | translate([carousel_flap_path_radius*cos(i*(360/nr_of_flaps)),carousel_flap_path_radius*sin(i*(360/nr_of_flaps)),0]) cylinder(d=carousel_flap_holes_diameter, h=carousel_thickness_holes, center = true); 56 | } 57 | 58 | // Löcher Spacers 59 | for(i=[1:carousel_spacer_nr]){ 60 | translate([carousel_spacer_path_radius*cos(i*(360/carousel_spacer_nr)),carousel_spacer_path_radius*sin(i*(360/carousel_spacer_nr)),0]) cylinder(d=carousel_spacer_holes_diameter, h=carousel_thickness_holes, center = true); 61 | } 62 | 63 | // 2mm Loch für das Magnet und als Alignement 64 | x=(360 / nr_of_flaps * 7); 65 | translate([magnet_hole_path_radius*cos(x),magnet_hole_path_radius*sin(x),0]){ 66 | cylinder(d=magnet_hole_diameter, h=carousel_thickness_holes, center = true); 67 | } 68 | } 69 | } 70 | 71 | module karussell_scheibe_links() 72 | { 73 | difference() 74 | { 75 | union() 76 | { 77 | karussell_scheibe_rechts(); 78 | // Die beiden Speichen 79 | cube([carousel_spokes_width, carousel_diameter_cutout, carousel_thickness], center = true); 80 | cube([carousel_diameter_cutout, carousel_spokes_width, carousel_thickness], center = true); 81 | // Der Kreis wo die Löcher für das Poulley und die Achse rein kommen 82 | cylinder(d=carousel_inner_circle, h=carousel_thickness, center = true); 83 | } 84 | 85 | // Loch für Achse 86 | cylinder(d=carousel_axis_diameter, h=carousel_thickness_holes, center = true); 87 | 88 | for(i=[1:pulley_nr_of_holes]) 89 | { 90 | // Löcher für Motor Poulley 91 | translate([ 92 | pulley_holes_path_radius * cos(i * (360 / pulley_nr_of_holes)), 93 | pulley_holes_path_radius * sin(i * (360 / pulley_nr_of_holes)), 94 | 0 95 | ]) cylinder(d=pulley_holes_diameter, h=carousel_thickness_holes, center = true); 96 | } 97 | } 98 | } 99 | 100 | module karussell_scheibe_position(side = "rechts") 101 | { 102 | // z-Offset 103 | zoffset = (carousel_inner_distance/2 + carousel_thickness/2) * (side == "rechts" ? -1 : 1); 104 | 105 | rotate([90,90,0]) 106 | translate([0,0,zoffset]) { 107 | if (side == "rechts") karussell_scheibe_rechts(); 108 | else karussell_scheibe_links(); 109 | } 110 | } 111 | 112 | module karussell_schrauben(){ 113 | rotate([90, 0, 0]){ 114 | for(i=[1:carousel_spacer_nr]){ 115 | translate([carousel_spacer_path_radius*cos(i*(360/carousel_spacer_nr)), carousel_spacer_path_radius*sin(i*(360/carousel_spacer_nr)), -((carousel_inner_distance/2)+carousel_thickness+0.2)]){ 116 | senk_schraube_m3(carousel_spacer_screw_length); 117 | } 118 | } 119 | } 120 | } 121 | 122 | module karussell_muttern(){ 123 | rotate([90, 0, 0]){ 124 | for(i=[1:carousel_spacer_nr]){ 125 | translate([carousel_spacer_path_radius*cos(i*(360/carousel_spacer_nr)), carousel_spacer_path_radius*sin(i*(360/carousel_spacer_nr)), ((carousel_inner_distance/2)+carousel_thickness+1.1)]){ 126 | mutter_m3(); 127 | } 128 | } 129 | } 130 | } 131 | 132 | module karussell_spacer(){ 133 | rotate([90,0,0]){ 134 | for(i=[1:carousel_spacer_nr]){ 135 | translate([carousel_spacer_path_radius*cos(i*(360/carousel_spacer_nr)),carousel_spacer_path_radius*sin(i*(360/carousel_spacer_nr)),0]){ 136 | spacer(carousel_spacer_length, carousel_spacer_outer_diameter, carousel_spacer_inner_diameter); 137 | } 138 | } 139 | } 140 | } 141 | 142 | module odd_flap_bottom(){ 143 | rotate([0,0,51*1.2]){ 144 | translate([(carousel_flap_path_radius+(flap_height/2)-1.5),0,0]){ 145 | rotate([90,0,0]){ 146 | flap(); 147 | } 148 | } 149 | } 150 | } 151 | 152 | module example_flap_front_top(){ 153 | translate([carousel_flap_path_radius, -((flap_height/2)-1.5), 0]){ 154 | rotate([90,0,270]){ 155 | flap(); 156 | } 157 | } 158 | } 159 | 160 | module example_flap_front_bottom(){ 161 | translate([(carousel_flap_path_radius - (flap_thickness/2)),((flap_height/2) + carousel_flap_holes_diameter),0]){ 162 | rotate([90,0,90]){ 163 | flap(); 164 | } 165 | } 166 | } 167 | 168 | module example_flap_bottom(number){ 169 | translate([0, (flap_height/2) + (carousel_flap_path_radius - (carousel_flap_holes_diameter / 2)) , 0]){ 170 | rotate([90,0,90]){ 171 | flap_with_char(number); 172 | } 173 | } 174 | } 175 | 176 | module stepper_motor(){ 177 | translate([0,0,-((housing_inner_distance/2)-(motor_depth/2)-motor_spacer_length)]){ 178 | difference(){ 179 | cube([motor_width,motor_height,motor_depth], center = true); 180 | // Ecken 181 | translate([motor_width/2,motor_height/2,0]) rotate([0,0,45]) cube([motor_corners,motor_corners,motor_depth+0.1], center = true); 182 | translate([-motor_width/2,motor_height/2,0]) rotate([0,0,45]) cube([motor_corners,motor_corners,motor_depth+0.1], center = true); 183 | translate([motor_width/2,-motor_height/2,0]) rotate([0,0,45]) cube([motor_corners,motor_corners,motor_depth+0.1], center = true); 184 | translate([-motor_width/2,-motor_height/2,0]) rotate([0,0,45]) cube([motor_corners,motor_corners,motor_depth+0.1], center = true); 185 | 186 | // Die Befestigungsgewinde 187 | translate([ motor_winding_distance/2, motor_winding_distance/2, 0]) rotate([0,0,45]) cylinder(d=motor_screw_diameter, h=motor_depth + 0.1, center = true); 188 | translate([-motor_winding_distance/2, motor_winding_distance/2, 0]) rotate([0,0,45]) cylinder(d=motor_screw_diameter, h=motor_depth + 0.1, center = true); 189 | translate([ motor_winding_distance/2, -motor_winding_distance/2, 0]) rotate([0,0,45]) cylinder(d=motor_screw_diameter, h=motor_depth + 0.1, center = true); 190 | translate([-motor_winding_distance/2, -motor_winding_distance/2, 0]) rotate([0,0,45]) cylinder(d=motor_screw_diameter, h=motor_depth + 0.1, center = true); 191 | } 192 | 193 | // Aufsatz 194 | translate([0,0,-((motor_depth/2)+(motor_attachment_depth/2))]) cylinder(d=motor_attachment_diameter, h=motor_attachment_depth, center = true); 195 | 196 | //Achse 197 | translate([0,0,motor_axis_shift]) cylinder(d=motor_axis_diameter, h=motor_axis_length, center = true); 198 | } 199 | } 200 | 201 | module stepper_spacer(){ 202 | spacer_pos = ((housing_inner_distance/2)-(motor_spacer_length/2)); 203 | translate([motor_winding_distance/2,motor_winding_distance/2,-spacer_pos]){ 204 | spacer(motor_spacer_length, motor_spacer_outer_diameter, motor_spacer_inner_diameter); 205 | } 206 | translate([-motor_winding_distance/2,motor_winding_distance/2,-spacer_pos]){ 207 | spacer(motor_spacer_length, motor_spacer_outer_diameter, motor_spacer_inner_diameter); 208 | } 209 | translate([motor_winding_distance/2,-motor_winding_distance/2,-spacer_pos]){ 210 | spacer(motor_spacer_length, motor_spacer_outer_diameter, motor_spacer_inner_diameter); 211 | } 212 | translate([-motor_winding_distance/2,-motor_winding_distance/2,-spacer_pos]){ 213 | spacer(motor_spacer_length, motor_spacer_outer_diameter, motor_spacer_inner_diameter); 214 | } 215 | } 216 | 217 | module motor_schrauben(){ 218 | translate([0,0,-(housing_inner_distance/2 + housing_thickness + 0.2)]){ 219 | translate([ motor_winding_distance/2, motor_winding_distance/2, 0]) rotate([0,0,45]) senk_schraube_m3(motor_screw_length); 220 | translate([-motor_winding_distance/2, motor_winding_distance/2, 0]) rotate([0,0,45]) senk_schraube_m3(motor_screw_length); 221 | translate([ motor_winding_distance/2, -motor_winding_distance/2, 0]) rotate([0,0,45]) senk_schraube_m3(motor_screw_length); 222 | translate([-motor_winding_distance/2, -motor_winding_distance/2, 0]) rotate([0,0,45]) senk_schraube_m3(motor_screw_length); 223 | } 224 | } 225 | 226 | module stepper_pouley(){ 227 | translate([0, -(carousel_inner_distance/2)+(pulley_thickness/2), 0]) 228 | { 229 | rotate([90, 0, 0]) 230 | { 231 | difference(){ 232 | cylinder(d=pulley_diameter, h=pulley_thickness, center = true); 233 | 234 | // Achse 235 | cylinder(d=pulley_axis_diameter, h=pulley_thickness + 1, center = true); 236 | 237 | // Löcher für Motor Poulley 238 | for(i=[1:pulley_nr_of_holes]){ 239 | translate([ 240 | pulley_holes_path_radius*cos(i*(360/pulley_nr_of_holes)), 241 | pulley_holes_path_radius*sin(i*(360/pulley_nr_of_holes)), 242 | 0 243 | ]) 244 | cylinder(d=pulley_holes_diameter, h=pulley_thickness + 0.1, center = true); 245 | } 246 | } 247 | } 248 | } 249 | } 250 | 251 | module pouley_schrauben(){ 252 | translate([0, -(carousel_inner_distance/2 + carousel_thickness + 0.1)]) 253 | { 254 | rotate([90, 0, 180]) 255 | { 256 | for(i=[1:pulley_nr_of_holes]) 257 | { 258 | translate([ 259 | pulley_holes_path_radius*cos(i*(360/pulley_nr_of_holes)), 260 | pulley_holes_path_radius*sin(i*(360/pulley_nr_of_holes)), 261 | 0 262 | ]) 263 | senk_schraube_m3(6); 264 | } 265 | } 266 | } 267 | } 268 | 269 | module chassis_front(){ 270 | difference(){ 271 | geh_front_pos = (housing_width/2)-(housing_thickness/2); 272 | translate([geh_front_pos,0,0]) cube([housing_thickness, housing_height, housing_inner_distance], center = true); 273 | // Ausschnitt 274 | translate([geh_front_pos,housing_cutout_position_y,0]) cube([housing_thickness+0.1,housing_cutout_height,housing_cutout_width], center = true); 275 | 276 | // Die kleinen Ausschnitte für die Räder des Karussells 277 | // tolerance ist die Korrektur, damit OpenSCAD in der Vorschau keine Fehler anzeigt 278 | tolerance = 0.1; 279 | geh_aus_kar = (housing_cutout_width/2) + (housing_cutout_small_width/2); 280 | translate([geh_front_pos, carousel_pos_y, (geh_aus_kar - tolerance)]) cube([housing_thickness + tolerance, housing_cutout_small_height, housing_cutout_small_width + (tolerance * 2)], center = true); 281 | translate([geh_front_pos, carousel_pos_y, -(geh_aus_kar - tolerance)]) cube([housing_thickness + tolerance, housing_cutout_small_height, housing_cutout_small_width + (tolerance * 2)], center = true); 282 | } 283 | } 284 | 285 | module chassis_right(){ 286 | translate([0,0,-((housing_inner_distance/2)+(housing_thickness/2))]){ 287 | difference(){ 288 | cube([housing_width,housing_height,housing_thickness], center = true); 289 | translate(carousel_pos){ 290 | // Die Achse des Motors kommt dem Gehäuse sehr nahe. Daher machen wir ein Loch ins Gehäuse. 291 | translate([0, 0, 0]) cylinder(d=motor_axis_diameter + 1, h=housing_thickness + 1, center = true); 292 | 293 | // Löcher für Motor-Spacer 294 | translate([motor_winding_distance/2,motor_winding_distance/2,0]) cylinder(d=motor_spacer_inner_diameter, h=housing_thickness + 0.01, center = true); 295 | translate([-motor_winding_distance/2,motor_winding_distance/2,0]) cylinder(d=motor_spacer_inner_diameter, h=housing_thickness + 0.01, center = true); 296 | translate([motor_winding_distance/2,-motor_winding_distance/2,0]) cylinder(d=motor_spacer_inner_diameter, h=housing_thickness + 0.01, center = true); 297 | translate([-motor_winding_distance/2,-motor_winding_distance/2,0]) cylinder(d=motor_spacer_inner_diameter, h=housing_thickness + 0.01, center = true); 298 | 299 | // Löcher für PCB 300 | translate([-((motor_winding_distance/2) + pcb_hole_top_from_left_housing), -(motor_winding_distance/2), 0]) cylinder(d=pcb_hole_diameter, h=housing_thickness + 0.01, center = true); 301 | translate([-((motor_winding_distance/2) + pcb_hole_bottom_from_left_housing), (motor_winding_distance/2), 0]) cylinder(d=pcb_hole_diameter, h=housing_thickness + 0.01, center = true); 302 | } 303 | 304 | // Und hier noch das Loch für den Bolzen 305 | translate([(housing_width/2) - housing_bolt_h, (housing_height/2) - housing_bolt_v, 0]) cylinder(d=motor_spacer_inner_diameter, h=housing_thickness + 1, center = true); 306 | } 307 | } 308 | } 309 | 310 | module chassis_left(){ 311 | // mit Ausschnitt für Karussell 312 | translate([0,0,((housing_inner_distance/2)+(housing_thickness/2))]){ 313 | difference(){ 314 | cube([housing_width,housing_height,housing_thickness], center = true); 315 | cube([housing_width-20,housing_height-20,housing_thickness+0.01], center = true); 316 | } 317 | } 318 | } 319 | 320 | module chassis_bolt_screw(){ 321 | translate([0,0,-((housing_inner_distance/2)+(housing_thickness/2))]){ 322 | translate([(housing_width/2) - housing_bolt_h, (housing_height/2) - housing_bolt_v, -(housing_thickness/2 + 0.2)]) senk_schraube_m3(25); 323 | } 324 | } 325 | 326 | module chassis_bolt_nut(){ 327 | translate([0,0,-((housing_inner_distance/2)+(housing_thickness/2))]){ 328 | // 1.1 is the height of the nut, it currently is hard coded 329 | translate([(housing_width/2) - housing_bolt_h, (housing_height/2) - housing_bolt_v, (housing_thickness/2 + 1.1)]) mutter_m3(); 330 | } 331 | } 332 | 333 | module pcb() 334 | { 335 | translate([-((pcb_length/2) + (motor_winding_distance/2) + (motor_spacer_outer_diameter/2) + 0.5), 0, -((housing_inner_distance/2) - (pcb_thickness/2)) + pcb_spacer_length]) 336 | { 337 | color("#1F6239") 338 | { 339 | // Basic PCB 340 | difference() 341 | { 342 | cube([pcb_length, pcb_width, pcb_thickness], center = true); 343 | translate([((pcb_length/2) - pcb_hole_top_from_left_pcb), -(motor_winding_distance/2),0]) cylinder(d=pcb_hole_diameter, h=pcb_thickness + 0.01, center = true); 344 | translate([((pcb_length/2) - pcb_hole_bottom_from_left_pcb), (motor_winding_distance/2),0]) cylinder(d=pcb_hole_diameter, h=pcb_thickness + 0.01, center = true); 345 | } 346 | } 347 | 348 | // Card Edge Connector 349 | translate([-((pcb_length / 2) + (pcb_edge_length / 2)), 0, 0]) 350 | { 351 | color("#1F6239") cube([pcb_edge_length, pcb_edge_width, pcb_thickness], center = true); 352 | color("orange") 353 | for (i = [0:6-1]) 354 | { 355 | translate([0, ((pcb_edge_width / 2) - (pcb_copper_width / 2) - ((pcb_copper_width + 0.84) * i) - 1.4), 0]) cube([pcb_edge_length, pcb_copper_width, pcb_thickness + 0.01], center = true); 356 | } 357 | } 358 | 359 | // hall sensor 360 | color("red"){ 361 | translate([((pcb_length/2) - pcb_hall_top_from_left), (-(pcb_width/2) + pcb_hall_top_from_top), ((pcb_thickness/2) + (pcb_hall_height/2))]){ 362 | cube([pcb_hall_length, pcb_hall_width, pcb_hall_height], center = true); 363 | } 364 | } 365 | } 366 | } 367 | 368 | module pcb_schrauben(){ 369 | translate([-((pcb_length/2) + (motor_winding_distance/2) + (motor_spacer_outer_diameter/2) + 0.5), 0, -(housing_inner_distance/2 + housing_thickness + 0.4)]) 370 | { 371 | translate([((pcb_length/2) - pcb_hole_top_from_left_pcb), -(motor_winding_distance/2), 0]) senk_schraube_m3(6); 372 | translate([((pcb_length/2) - pcb_hole_bottom_from_left_pcb), (motor_winding_distance/2), 0]) senk_schraube_m3(6); 373 | } 374 | } 375 | 376 | module pcb_muttern(){ 377 | translate([ 378 | -((pcb_length/2) + (motor_winding_distance/2) + (motor_spacer_outer_diameter/2) + 0.5), 379 | 0, 380 | -(housing_inner_distance/2 - pcb_spacer_length - pcb_thickness - (2.2/2)) 381 | ]) 382 | { 383 | translate([((pcb_length/2) - pcb_hole_top_from_left_pcb), -(motor_winding_distance/2), 0]) mutter_m3(); 384 | translate([((pcb_length/2) - pcb_hole_bottom_from_left_pcb), (motor_winding_distance/2), 0]) mutter_m3(); 385 | } 386 | } 387 | 388 | module pcb_spacer(){ 389 | translate([-((pcb_length/2) + (motor_winding_distance/2) + (motor_spacer_outer_diameter/2) + 0.5), 0, -((housing_inner_distance/2) - (pcb_spacer_length/2))]) 390 | { 391 | translate([((pcb_length/2) - pcb_hole_top_from_left_pcb), -(motor_winding_distance/2), 0]) 392 | { 393 | spacer(pcb_spacer_length, pcb_spacer_outer_diameter, pcb_spacer_inner_diameter); 394 | } 395 | translate([((pcb_length/2) - pcb_hole_bottom_from_left_pcb), (motor_winding_distance/2), 0]) 396 | { 397 | spacer(pcb_spacer_length, pcb_spacer_outer_diameter, pcb_spacer_inner_diameter); 398 | } 399 | } 400 | 401 | } 402 | 403 | module flap() 404 | { 405 | color("#333") union() 406 | { 407 | cube([flap_height,flap_width, flap_thickness], center = true); 408 | translate([-((flap_height/2) - (flap_pin/2)), -((flap_width/2) + (flap_pin/2)), 0]) 409 | { 410 | cube([flap_pin,flap_pin,flap_thickness], center = true); 411 | } 412 | translate([-((flap_height/2) - (flap_pin/2)), ((flap_width/2) + (flap_pin/2)), 0]) 413 | { 414 | cube([flap_pin,flap_pin,flap_thickness], center = true); 415 | } 416 | } 417 | } 418 | 419 | module rotating_carousel() 420 | { 421 | for (i = [0:nr_of_flaps-1]) 422 | { 423 | position = i * (360 / nr_of_flaps); 424 | rotated_angle = (position - (360 * $t) + 360) % 360; 425 | dx = (housing_virt_bolt_pos[0]) - (carousel_flap_path_radius * cos(rotated_angle)); 426 | dy = (housing_virt_bolt_pos[2]) - (carousel_flap_path_radius * sin(rotated_angle)); 427 | 428 | translate([ 429 | carousel_flap_path_radius * sin(position), 430 | 0, 431 | carousel_flap_path_radius * cos(position) 432 | ]) 433 | { 434 | rotate([0, 360 * $t, 0]) 435 | { 436 | // this version shows the rotating flaps without the bolt 437 | //angle = (rotated_angle > 0 && rotated_angle < 180) ? rotated_angle : 180 ; 438 | 439 | // this version shows the flaps as if there where a bolt 440 | angle = (rotated_angle > 260 && rotated_angle < 360) ? 270 + atan2(dy, dx) : (rotated_angle > 0 && rotated_angle < 180) ? rotated_angle : 180 ; 441 | 442 | rotate([0, angle, 0]) 443 | { 444 | translate([(flap_height / 2) - (flap_pin / 2), 0, 0]) 445 | { 446 | flap_with_char(i); 447 | } 448 | } 449 | } 450 | } 451 | } 452 | } 453 | 454 | module draw_half_letter(char, flip = false) 455 | { 456 | overrides = get_letter_overrides(characters[char]); 457 | color = is_undef(overrides[5]) ? get_font_setting("color") : overrides[5]; 458 | rotate_letter = flip ? [0, 180, 90] : [0, 0, 270]; 459 | lift_letter = flip ? -0.01 : 0.02; 460 | move_letter = flip ? (flap_height/2) : -(flap_height/2); 461 | 462 | translate([0, 0, lift_letter]) color(color) intersection() 463 | { 464 | cube([flap_height - 0.1, flap_width - 0.1, flap_thickness], center = true); 465 | rotate(rotate_letter) translate([0, move_letter, 0]) linear_extrude(height = flap_thickness) 466 | difference() 467 | { 468 | draw_letter(characters[char]); 469 | if(flip) translate([0, -((flap_height) - (cut_off_blind/2))]) square([flap_width, cut_off_blind], center = true); 470 | } 471 | } 472 | } 473 | 474 | module flap_with_char(char) 475 | { 476 | flap(); 477 | prev_char = char <= 0 ? nr_of_flaps-1 : char-1; 478 | draw_half_letter(prev_char, flip = false); 479 | draw_half_letter(char, flip = true); 480 | } 481 | --------------------------------------------------------------------------------