├── .gitignore ├── apc-10-60-838-450-32.gif ├── assembly_guide ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── 3d_printing.md │ ├── bom.md │ ├── final_assembly.md │ ├── hacks.md │ ├── inventory.md │ ├── pcb-bigger_components.md │ ├── pcb-general_tips.md │ ├── pcb-power.md │ ├── pcb-small_components.md │ ├── pcb-test.md │ ├── pcb-troubleshooting.md │ ├── schematic.md │ ├── source-and-license.md │ └── what_youll_be_making.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ └── css │ │ └── custom.css └── static │ ├── .nojekyll │ └── img │ ├── 3d-printed-parts.png │ ├── apc-10-60-838-450-32.gif │ ├── assembly │ ├── 9v-relief.jpg │ ├── 9v-wires.jpg │ ├── apc.jpg │ ├── battery.jpg │ ├── c101.jpg │ ├── c102.jpg │ ├── c103.jpg │ ├── chip-insert.jpg │ ├── chip-pins.jpg │ ├── enclosure-bend.jpg │ ├── enclosure-slide.jpg │ ├── ic-pins.jpg │ ├── label.jpg │ ├── led-flat.jpg │ ├── pcb.jpg │ ├── pot-tabs.jpg │ ├── power.jpg │ ├── r101.jpg │ ├── r102.jpg │ ├── socket-placement.jpg │ ├── socket-solder.jpg │ ├── speaker-flat.jpg │ ├── speaker-solder.jpg │ ├── switch-flat.jpg │ ├── switch_clutch.jpg │ ├── vol-flat.jpg │ ├── vol-test.jpg │ └── wheel.jpg │ ├── favicon.ico │ └── schematic.svg ├── license.txt ├── make_stls.sh ├── openscad ├── apc.scad ├── battery.scad ├── cross_section.scad ├── enclosure.scad ├── floating_hole_cavity.scad ├── pcb.scad ├── rib_cavities.scad ├── shared_constants.scad ├── switch_clutch.scad └── wheels.scad ├── pcb.svg ├── print ├── inserts.pdf ├── inserts.svg ├── label_inserts.pdf ├── label_single-2.4.svg └── labels.svg └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | local/ 3 | 4 | ## DOCUSAURUS 5 | 6 | # Dependencies 7 | assembly_guide/node_modules 8 | 9 | # Production 10 | assembly_guide/build 11 | 12 | # Generated files 13 | .docusaurus 14 | .cache-loader 15 | 16 | # Misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /apc-10-60-838-450-32.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/apc-10-60-838-450-32.gif -------------------------------------------------------------------------------- /assembly_guide/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /assembly_guide/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /assembly_guide/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /assembly_guide/docs/3d_printing.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 3d-printing 3 | title: 3D-Printing 4 | sidebar_label: 3D-Printing 5 | description: How to 3D-print your APC's parts 6 | slug: 3d-printing 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | :::note 11 | If you bought a kit with 3D-printed parts included, you can skip this step. 12 | ::: 13 | 14 | :::info 15 | If you don't have access to a 3D printer and can't get the parts made, you can still assemble the kit's electronics without them. Continue on to the next step. 16 | ::: 17 | 18 | ## Models 19 | 20 | Download STLs of the models from: 21 | [https://www.printables.com/model/224313-apc-atari-punk-console](https://www.printables.com/model/224313-apc-atari-punk-console) 22 | 23 | There are four files to print: 24 | 25 | ![Exploded CAD view of the four models](/img/3d-printed-parts.png) 26 | 27 | | Part | Count | Layer Height | Supports? | Estimated Time | 28 | | ---------------- | ----- | ------------ | --------- | -------------- | 29 | | Wheels | 2 | .2mm | No | 1hr 8min | 30 | | Enclosure top | 1 | .2mm | No | 4hr | 31 | | Switch clutch | 1 | .2mm | No | 20min | 32 | | Enclosure bottom | 1 | .2mm | No | 48min | 33 | 34 | ## Notes 35 | 36 | - Models assume Fused Deposition Modeling with a standard .4mm nozzle. Using a bigger nozzle will likely result in a loss of detail and possibly missing internal walls. 37 | - The 3D-printed parts were designed using PLA. Other filament types like ABS are not recommended and will likely have fit or tolerance issues. (If you find that you need to drill or file your prints, that's a good sign there'll be other problems too.) 38 | - They also don't need supports and should already be rotated to the correct orientation for printing. 39 | - Watch the first couple layers of the enclosure pieces while printing, especially around the text engravings — if you see bad adhesion, stop the print to remedy the situation and start again. 40 | - If the prints aren't fitting together well, check to see that the corners aren't bulging. See if your slicer has settings for "coasting" or "linear advance." 41 | - The switch clutch has two narrow support walls that will [break off when it's done printing](https://twitter.com/oskitone/status/1367957529406316545). 42 | -------------------------------------------------------------------------------- /assembly_guide/docs/bom.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: bom 3 | title: BOM (Bill of Materials) 4 | sidebar_label: BOM 5 | description: BOM (Bill of Materials) 6 | slug: bom 7 | hide_table_of_contents: true 8 | image: /img/assembly/apc.jpg 9 | --- 10 | 11 | | Part | Package | Quantity | Value | Marking | 12 | | ------------ | ----------------------------------- | -------- | ------------ | ------------------- | 13 | | BT101 | Battery snap | 1 | 9v | | 14 | | C101 | Ceramic disc D5.0mm_W2.5mm_P5.00mm | 1 | .01uF / 10nF | 103 | 15 | | C102 | Ceramic disc D5.0mm_W2.5mm_P5.00mm | 1 | .1uF | 104 | 16 | | C103 | CP_Radial_D4.0mm_P2.00mm | 1 | 10uF | | 17 | | D101 | LED D5.0mm | 1 | LED | | 18 | | LS101 | Speaker 30mm_36MS30008-PN | 1 | Speaker | | 19 | | R101 | Resistor | 1 | 330 | Orange Orange Brown | 20 | | R102 | Resistor | 1 | 1k | Brown Black Red | 21 | | RV101, RV102 | PTV09 Vertical Pot | 2 | 500k-1M | | 22 | | RV103 | Potentiometer Piher_PT-6-V_Vertical | 1 | 1k | 102 | 23 | | S101 | EG1218 slider switch | 2 | | | 24 | | U101 | DIP-14_W7.62mm_LongPads | 1 | LM556 | | 25 | -------------------------------------------------------------------------------- /assembly_guide/docs/final_assembly.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: final_assembly 3 | title: Final Assembly 4 | sidebar_label: Final Assembly 5 | description: Final assembly steps for the APC 6 | slug: final_assembly 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | 1. Insert **switch clutch** into its cavity in the **enclosure top**. 11 | - It will fit in with room to slide back and forth. 12 | ![Insert switch clutch](/img/assembly/switch_clutch.jpg) 13 | 2. Insert **PCB** into **enclosure top**. 14 | - It should fit perfectly without too much force. Wiggle the **switch clutch** around if it's not in the right place. 15 | ![Insert PCB](/img/assembly/pcb.jpg) 16 | 3. Insert **battery** into its spot in the **enclosure bottom**. 17 | - Its wires can be tucked under the PCB 18 | ![Insert battery ](/img/assembly/battery.jpg) 19 | 4. Slide **enclosure bottom** onto **enclosure top**. 20 | - This will hold the battery and **PCB** in place. 21 | ![Slide enclosure halves together](/img/assembly/enclosure-slide.jpg) 22 | - **Troubleshooting: enclosure halves are getting stuck and can't slide together all the way** 23 | - Make sure the **PCB** is pushed into the enclosure top all the way. Then, when sliding the enclosure bottom on, try bending it slightly _out_ where its rails meet the **PCB**. 24 | ![Bend enclosure top up if necessary](/img/assembly/enclosure-bend.jpg) 25 | 26 | 5. Fit **wheels** into their cavity wells on the **enclosure top** and onto the pot shafts. 27 | - They should also fit perfectly without too much force. 28 | ![Add wheels](/img/assembly/wheel.jpg) 29 | - **Troubleshooting: Wheels aren't popping on all the way:** 30 | - Use a butter knife (or similar flat tool like a flathead screwdriver or paint spatula) to pop the **wheel** off its pot, then press it back on. Sometimes it just takes another try! 31 | 32 | You're done! Excellent work! 33 | 34 | ![An assembled Oskitone APC](/img/assembly/apc.jpg) 35 | -------------------------------------------------------------------------------- /assembly_guide/docs/hacks.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hacks 3 | title: Hacks 4 | sidebar_label: Hacks 5 | description: Different hacks you can try on the APC 6 | slug: hacks 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | The Oskitone APC wants to be hacked and modified! 11 | 12 | Here are some ideas: 13 | 14 | - Add a quick-and-dirty interrupt button by wiring a switch between the battery's positive wire and the PCB "**+**" pad. [It's fun](https://www.instagram.com/p/CMz4yeIjg4J/)! 15 | - Want an output jack? Try connecting the speaker pins to a 1/8" or 1/4" jack, but be careful about connecting directly to sensitive equipment — the output voltage is the battery's 9v, which is much higher than a typical line-level 1v! 16 | - [There _is_ a way to make an Atari Punk Console have line-level output](https://compiler.kaustic.net/machines/apc.html), but it doesn't account for a built-in speaker, so you may have to sacrifice it. 17 | -------------------------------------------------------------------------------- /assembly_guide/docs/inventory.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: inventory 3 | title: Inventory 4 | sidebar_label: Inventory 5 | description: What you'll need to assembly your APC 6 | slug: inventory 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | ## What You'll Need 11 | 12 | - The [Oskitone APC DIY Electronics Kit](https://www.oskitone.com/product/apc-diy-electronics-kit)! 13 | - Soldering iron and solder for electronics 14 | - Wire stripper or cutters 15 | - 9v battery 16 | 17 | ## Good to have 18 | 19 | While not required, it'd be good to have these tools around too. 20 | 21 | - Multimeter for debugging 22 | - PCB vice or “helping hands” holder 23 | - “Solder sucker” or desoldering braid 24 | - Patience, patience, patience 25 | -------------------------------------------------------------------------------- /assembly_guide/docs/pcb-bigger_components.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: pcb-bigger_components 3 | title: Bigger components 4 | sidebar_label: 3. Bigger components 5 | description: How to solder the APC's pots, speaker, and IC socket 6 | slug: pcb-bigger_components 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | :::note 11 | Take your time and make sure the pots and speaker are perfectly flat against the PCB before soldering all of their pins. 12 | ::: 13 | 14 | Last is the bigger components: pots, speaker, and IC. 15 | 16 | 1. Push the two **500k pots** ("pot" is short for "potentiometer"!) into their footprints at **RV101** and **RV102** 17 | 1. A trick to get the pot to snap in better is to _gently_ push its mounting tabs inward before popping it onto the PCB. 18 | ![Bending the potentiometer tabs](/img/assembly/pot-tabs.jpg) 19 | 2. Check that they're flat against the PCB, then solder into place. 20 | 3. You can solder the mounting tabs on the side too, if you want! 21 | 2. Fit the **speaker** into **LS101**, matching its **+** and **-** pins to the right holes. 22 | 1. Don't bend its leads! That can break it. 23 | 2. Hold in place and solder. 24 | ![Soldering the APC speaker at LS101](/img/assembly/speaker-solder.jpg) 25 | 3. Check that the **speaker** is flat against the PCB before continuing. 26 | ![Speaker, flat against PCB](/img/assembly/speaker-flat.jpg) 27 | 3. And the absolute last component to solder is the **IC socket** 28 | 1. Place the **IC socket** into **U101**, matching its little indentation to the footprint. 29 | ![Placing the IC socket at U101](/img/assembly/socket-placement.jpg) 30 | 2. Hold in place and solder. 31 | ![Soldering the socket](/img/assembly/socket-solder.jpg) 32 | 4. With its socket soldered, we can add its **556 chip**. 33 | 1. In order to fit well into its **socket**, the pins of the **556** need to point straight down from the chip's body. You can use a [pin straightener tool](https://www.jameco.com/z/ICS-01-R-Jameco-Benchpro-IC-Pin-Straightener-for-0-300-and-0-600-Wide-ICs_99363.html) or simply bend them against any flat surface. 34 | ![The IC chip's pins, straightened](/img/assembly/ic-pins.jpg) 35 | 2. Carefully insert the **556 chip** into the **socket** at **U101**, matching its indentation. If its pins don't seem to go in well, try the previous step again. 36 | ![556 chip into socket at U101](/img/assembly/chip-insert.jpg) 37 | 38 | 39 | -------------------------------------------------------------------------------- /assembly_guide/docs/pcb-general_tips.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: general-tips 3 | title: PCB general tips 4 | description: Things to keep in mind as you solder your APC. 5 | sidebar_label: General tips 6 | image: /img/assembly/apc.jpg 7 | slug: /general-tips 8 | --- 9 | 10 | :::note 11 | Take your time and be patient! If you run into a problem, try to keep a cool head and refer to the [PCB troubleshooting](pcb-troubleshooting.md) section. If your problem remains unfixed and you can't figure it out, [please _email_ me to let me know](https://www.oskitone.com/contact)! I'll do my best to help you, and your feedback will help improve the guide and other future Oskitone designs. 12 | ::: 13 | 14 | 15 | 16 | ## General tips 17 | 18 | - **Component colors**
19 | This guide's components' brands and body colors (and even the PCB color itself) may look different from yours, and that's okay! What's important is that the part types and values are in the right spots. 20 | - **Resistors are labeled with colors, capacitors with numbers**
21 | Resistors' values are marked as colored bands on their body. Ceramic capacitors use a number system to denote their value. 22 | - **Ceramic and electrolytic capacitors**
23 | There are two kinds of “caps” used in this kit. Ceramic capacitors are small, circular, and have no polarity; they can be placed in either direction. Electrolytic caps are bigger, cylindrical, and have marked +/- polarities. 24 | - **IC chip is static-sensitive**
25 | The included IC chip can be damaged by static electricity. Leave it in its packaging until ready to install. Before handling, discharge any static electricity on your body by touching a large piece of metal. You can even use an anti-static mat and/or wrist strap for extra caution. 26 | - **IC in sockets**
27 | The IC chip comes with a corresponding socket with the same number of pins. You will solder the socket to the PCB, not the chip itself. This prevents overheating the IC with the soldering iron and makes it easier to switch a faulty one out. 28 | - **Component polarities**
29 | LEDs, batteries, and electrolytic capacitors have positive and negative leads. Where applicable, the PCB will be labeled where each lead goes or a component outline to denote orientation. 30 | - **IC orientation**
31 | The IC chip also has an orientation, marked by a notch at its top. Make sure these line up when soldering the sockets and again when inserting the chips. A chip can be permanently damaged if inserted incorrectly! 32 | - **Get tricky/sticky with short-lead components**
33 | Components with short leads can be hard to get to stay on the PCB, because you can't really bend their leads to get them to stay put. But there are tricks! Try using tape or "Blu-Tack" adhesive to hold them. Or clip the solder into your "holding hands", and try bringing the _board to the solder_ (instead of the typical reverse of solder to board). 34 | -------------------------------------------------------------------------------- /assembly_guide/docs/pcb-power.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: pcb-power 3 | title: Power up 4 | sidebar_label: 1. Power up 5 | description: How to get power to the APC PCB 6 | slug: pcb-power 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | :::info 11 | This guide's components' brands and body colors (and even the PCB color itself) may look different from yours, and that's okay! What's important is that the part types and values are in the right spots. 12 | ::: 13 | 14 | :::note 15 | Take your time and make sure the LED and switch are perfectly flat against the PCB before soldering all of their pins. 16 | ::: 17 | 18 | First, we'll get power to the board and make sure our battery is working. 19 | 20 | 1. Find the **330 ohm resistor**; its color bands are _Orange Orange Brown_. Solder it into **R101**. 21 | - ![330 resistor to R101](/img/assembly/r101.jpg) 22 | 2. The **LED** goes to **D101**. 23 | 1. Note that the footprint on the PCB is a circle with one flat side. Insert the **LED** so its flat side matches that footprint. 24 | 2. Hold in place and solder. Make sure it's flat against the PCB. 25 | ![LED, perfectly flat against PCB, at D101](/img/assembly/led-flat.jpg) 26 | 3. The **slider switch** goes to **S101**. 27 | 1. It doesn't have polarity and can go in either direction, but, just like the **LED**, it does need to be perfectly flat against the PCB. 28 | 2. Confirm it's flat before soldering soldering all of its pads. 29 | ![Switch, perfectly flat against PCB, at S101](/img/assembly/switch-flat.jpg) 30 | 4. Next up is the **9v battery snap**. 31 | 1. Feed its wires through the hole by **BT101**. This acts as a stress relief, preventing strain at the solder joints whenever you replace the battery. 32 | ![9v snap wires in their relief hole](/img/assembly/9v-relief.jpg) 33 | 2. Insert and solder wires into place: red to **+** and black to **-** 34 | ![9v snap wires in place at BT101](/img/assembly/9v-wires.jpg) 35 | 5. **Test it!** 36 | 1. Connect a **battery** to the **9v snap**, and slide the **switch** back and forth. You should see the **LED** turn on and off. Nice! 37 | ![Testing power](/img/assembly/power.jpg) 38 | 2. If you don't, don't worry; it's just time to debug. _Don't move on to the next step until you've got this working._ 39 | - Check all your solder joints. 40 | - Verify **LED** is placed correctly and matches its footprint. 41 | - Is the **battery** dead? 42 | - Are the **battery** wires in the right spots? 43 | 6. Trim leads if you haven't already, and remove the **battery** before continuing. 44 | 45 | 46 | -------------------------------------------------------------------------------- /assembly_guide/docs/pcb-small_components.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: pcb-small_components 3 | title: Small components 4 | sidebar_label: 2. Small components 5 | description: Soldering the APC's small components 6 | slug: pcb-small_components 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | :::note 11 | Take your time and make sure the 1k trim pot is perfectly flat against the PCB before soldering all of its pins. 12 | ::: 13 | 14 | Next we'll do the remaining small, passive components. 15 | 16 | 1. There are two small ceramic capacitors: 17 | 1. The one marked _103_ is for **.01uF** (also known as 10nF). It goes to **C101**. 18 | ![.01uF to C101](/img/assembly/c101.jpg) 19 | 2. The other's marked _104_, for **.1uF**, and goes to **C102**. 20 | ![.1uF to C102](/img/assembly/c102.jpg) 21 | 2. The last cap is an electrolytic **10uF** capacitor at **C103**. 22 | - Match the capacitor's white stripe to the white part of the footprint. 23 | ![10uF to C103](/img/assembly/c103.jpg) 24 | 3. The remaining resistor is **1k ohms**, colored _Brown Black Red_. 25 | - Solder to **R102**. 26 | ![1k ohms to R102](/img/assembly/r102.jpg) 27 | 4. The small, blue **1k trim potentiometer** goes to **RV103**. It's marked _102_. Make sure it's flat against the PCB. 28 | - ![1k trim pot to 102](/img/assembly/vol-flat.jpg) 29 | 5. Nothing to test here, but check all solder joints and trim leads before moving on. We're almost done! 30 | 31 | 32 | -------------------------------------------------------------------------------- /assembly_guide/docs/pcb-test.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: pcb-test 3 | title: Test 4 | sidebar_label: 4. Test 5 | description: Testing the APC PCB after soldering 6 | slug: pcb-test 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | All done soldering! Let's test it. 11 | 12 | 1. Reconnect the **battery**. 13 | 2. Slide power **switch**. 14 | 3. Use a screwdriver to adjust the volume at **RV103** and the two bigger **potentiometers** until you hear noises coming out of the **speaker**. 15 | - If you hear noise, congrats! You successfully soldered your **Oskitone APC**! 16 | ![A successfully soldered and working Oskitone APC](/img/assembly/vol-test.jpg) 17 | 4. If not, don't worry. It's time to debug: 18 | - Check all your solder joints again 19 | - Are the components with polarity (**+** and **-** sides) right? 20 | - Is the **556 chip** pushed all they way into the socket? And none of its legs are bent? 21 | - Try using a multimeter to confirm power and ground are at all the pins you'd expect, referring to the schematic. 22 | 23 | 24 | 25 | Next, let's get the soldered PCB assembled into its enclosure... 26 | -------------------------------------------------------------------------------- /assembly_guide/docs/pcb-troubleshooting.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: pcb-troubleshooting 3 | title: PCB troubleshooting 4 | description: Common problems that come up when soldering the APC PCB. 5 | sidebar_label: PCB troubleshooting 6 | image: /img/assembly/apc.jpg 7 | slug: /pcb-troubleshooting 8 | --- 9 | 10 | ## General tips 11 | 12 | Any of these problems can cause a variety of "just not working right" errors in a circuit. Familiarize yourself with these troubleshooting checks and do them regularly. 13 | 14 | - Turn the PCB over and check all solder joints. A majority of problems are caused by insufficient or errant soldering. Familiarize yourself with what a good joint looks like in the [Adafruit Guide To Excellent Soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering). 15 | - Is the chip in the right orientation? It will have a notch/dimple that should match the footprint outline on the PCB. 16 | - Does the battery have enough power? It should measure 9 volts. Try replacing with a fresh battery and see if that solves your issue. 17 | 18 | ## Specific issues 19 | 20 | - If there’s buzzing, check for any metal scraps stuck to the speaker. 21 | -------------------------------------------------------------------------------- /assembly_guide/docs/schematic.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: schematic 3 | title: Schematic 4 | sidebar_label: Schematic 5 | description: Oskitone APC Schematic 6 | slug: schematic 7 | hide_table_of_contents: true 8 | image: /img/assembly/apc.jpg 9 | --- 10 | 11 | [![APC Schematic](/img/schematic.svg)](/img/schematic.svg) 12 | -------------------------------------------------------------------------------- /assembly_guide/docs/source-and-license.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: source-and-license 3 | title: "Source and License" 4 | description: The APC is open-source hardware. 5 | sidebar_label: Source and License 6 | image: /img/assembly/apc.jpg 7 | --- 8 | 9 | ## Source 10 | 11 | The APC is open-source hardware. 12 | 13 | [https://github.com/oskitone/apc](https://github.com/oskitone/apc) 14 | 15 | ## License 16 | 17 | > Designed by Oskitone. Please support future synth projects by purchasing from [Oskitone](https://www.oskitone.com/). 18 | > 19 | > Creative Commons Attribution/Share-Alike, all text above must be included in any redistribution. See license.txt for additional details. 20 | -------------------------------------------------------------------------------- /assembly_guide/docs/what_youll_be_making.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: what-youll-be-making 3 | title: Oskitone APC Assembly Guide 4 | sidebar_label: What You'll Be Making 5 | description: How to solder and assemble the Oskitone APC Electronics Kit 6 | slug: / 7 | image: /img/assembly/apc.jpg 8 | --- 9 | 10 | [![APC](/img/apc-10-60-838-450-32.gif)](/img/apc-10-60-838-450-32.gif) 11 | 12 | A decapitated robot head? An extraterrestrial communicator? A specter summoner? 13 | 14 | Nope (and yep!), it's an [APC](https://en.wikipedia.org/wiki/Atari_Punk_Console)! 15 | 16 | **Demo:** [https://vimeo.com/518375593](https://vimeo.com/518375593)
17 | **Purchase:** [APC (fully assembled)](https://www.oskitone.com/product/apc), [APC DIY Electronics Kit](https://www.oskitone.com/product/apc-diy-electronics-kit)
18 | **Blog post:** [https://blog.tommy.sh/posts/oskitone-makes-an-atari-punk-console/](https://blog.tommy.sh/posts/oskitone-makes-an-atari-punk-console/)
19 | **Source code:** [https://github.com/oskitone/apc](https://github.com/oskitone/apc) 20 | 21 | ## Um, WHAT 22 | 23 | The APC (Atari Punk Console) is some kind of noise toy. Its circuit was originally described in the early 80s but it was such a hit that it's become something of a go-to project for folks new to electronics and soldering! There are _tons_ of versions of it but they all share the same basic parts. 24 | 25 | ![An assembled Oskitone APC](/img/assembly/apc.jpg) 26 | 27 | The Oskitone APC is the function of the classic circuit in an Oskitone form. It's not very useful or musically pleasant, but it's fun! 28 | 29 | I wrote a lot of words about this particular Atari Punk Console here, its design and history with the POLY555, and some other stuff here: [Oskitone Makes an Atari Punk Console](https://blog.tommy.sh/posts/oskitone-makes-an-atari-punk-console/) 30 | 31 | 32 | -------------------------------------------------------------------------------- /assembly_guide/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const lightCodeTheme = require("prism-react-renderer/themes/github"); 5 | const darkCodeTheme = require("prism-react-renderer/themes/dracula"); 6 | 7 | /** @type {import('@docusaurus/types').Config} */ 8 | const config = { 9 | title: "Oskitone APC Assembly Guide", 10 | // tagline: 'Dinosaurs are cool', 11 | url: "https://oskitone.github.io", 12 | baseUrl: "/apc/", 13 | onBrokenLinks: "throw", 14 | onBrokenMarkdownLinks: "warn", 15 | favicon: "img/favicon.ico", 16 | 17 | organizationName: "oskitone", 18 | projectName: "apc", 19 | 20 | i18n: { 21 | defaultLocale: "en", 22 | locales: ["en"] 23 | }, 24 | 25 | presets: [ 26 | [ 27 | "classic", 28 | /** @type {import('@docusaurus/preset-classic').Options} */ 29 | ({ 30 | docs: { 31 | path: "./docs", 32 | routeBasePath: "/", 33 | sidebarPath: require.resolve("./sidebars.js") 34 | }, 35 | // blog: { 36 | // showReadingTime: true, 37 | // }, 38 | theme: { 39 | customCss: require.resolve("./src/css/custom.css") 40 | } 41 | }) 42 | ] 43 | ], 44 | 45 | themeConfig: 46 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 47 | ({ 48 | navbar: { 49 | title: "Oskitone APC Assembly Guide" 50 | }, 51 | footer: { 52 | style: "dark", 53 | copyright: `Copyright © ${new Date().getFullYear()} Oskitone. Built with Docusaurus.` 54 | }, 55 | 56 | prism: { 57 | theme: lightCodeTheme, 58 | darkTheme: darkCodeTheme 59 | } 60 | }) 61 | }; 62 | 63 | module.exports = config; 64 | -------------------------------------------------------------------------------- /assembly_guide/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assembly-guide", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.0.0-rc.1", 18 | "@docusaurus/preset-classic": "2.0.0-rc.1", 19 | "@mdx-js/react": "^1.6.22", 20 | "clsx": "^1.2.1", 21 | "prism-react-renderer": "^1.3.5", 22 | "react": "^17.0.2", 23 | "react-dom": "^17.0.2" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "2.0.0-rc.1" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "engines": { 41 | "node": ">=16.14" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /assembly_guide/sidebars.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 4 | 5 | const _doc = id => ({ 6 | type: "doc", 7 | id 8 | }); 9 | 10 | const _category = (label, docIds = []) => ({ 11 | label, 12 | type: "category", 13 | items: docIds.map(id => _doc(id)) 14 | }); 15 | 16 | const sidebars = { 17 | mySidebar: [ 18 | _category("Getting Started", [ 19 | "what-youll-be-making", 20 | "inventory" 21 | // "how-does-it-work" 22 | ]), 23 | _doc("3d-printing"), 24 | _category("PCB Assembly", [ 25 | "general-tips", 26 | "pcb-power", 27 | "pcb-small_components", 28 | "pcb-bigger_components", 29 | "pcb-test" 30 | ]), 31 | _doc("final_assembly"), // "care" 32 | _category("Appendix", [ 33 | "hacks", 34 | "bom", 35 | "schematic", 36 | "pcb-troubleshooting", 37 | "source-and-license" 38 | ]) 39 | ] 40 | }; 41 | 42 | module.exports = sidebars; 43 | -------------------------------------------------------------------------------- /assembly_guide/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /assembly_guide/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/.nojekyll -------------------------------------------------------------------------------- /assembly_guide/static/img/3d-printed-parts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/3d-printed-parts.png -------------------------------------------------------------------------------- /assembly_guide/static/img/apc-10-60-838-450-32.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/apc-10-60-838-450-32.gif -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/9v-relief.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/9v-relief.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/9v-wires.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/9v-wires.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/apc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/apc.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/battery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/battery.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/c101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/c101.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/c102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/c102.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/c103.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/c103.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/chip-insert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/chip-insert.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/chip-pins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/chip-pins.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/enclosure-bend.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/enclosure-bend.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/enclosure-slide.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/enclosure-slide.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/ic-pins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/ic-pins.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/label.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/label.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/led-flat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/led-flat.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/pcb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/pcb.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/pot-tabs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/pot-tabs.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/power.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/power.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/r101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/r101.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/r102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/r102.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/socket-placement.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/socket-placement.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/socket-solder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/socket-solder.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/speaker-flat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/speaker-flat.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/speaker-solder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/speaker-solder.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/switch-flat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/switch-flat.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/switch_clutch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/switch_clutch.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/vol-flat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/vol-flat.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/vol-test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/vol-test.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/assembly/wheel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/assembly/wheel.jpg -------------------------------------------------------------------------------- /assembly_guide/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/assembly_guide/static/img/favicon.ico -------------------------------------------------------------------------------- /assembly_guide/static/img/schematic.svg: -------------------------------------------------------------------------------- 1 | +9V123RV1031kC10310uF123RV102500k-1mVCC14GND7DIS1THR2CV3R4Q5TR6U101ALM556123S101EG1218R101330123RV101500k-1mD101LEDBT1019vVCC14GND7R10CV11THR12DIS13TR8Q9U101BLM55612LS101SpeakerC102.1uFC101.01uFR1021k -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | Attribution-ShareAlike 3.0 Unported 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR 10 | DAMAGES RESULTING FROM ITS USE. 11 | 12 | License 13 | 14 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE 15 | COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY 16 | COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS 17 | AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. 18 | 19 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE 20 | TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY 21 | BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS 22 | CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND 23 | CONDITIONS. 24 | 25 | 1. Definitions 26 | 27 | a. "Adaptation" means a work based upon the Work, or upon the Work and 28 | other pre-existing works, such as a translation, adaptation, 29 | derivative work, arrangement of music or other alterations of a 30 | literary or artistic work, or phonogram or performance and includes 31 | cinematographic adaptations or any other form in which the Work may be 32 | recast, transformed, or adapted including in any form recognizably 33 | derived from the original, except that a work that constitutes a 34 | Collection will not be considered an Adaptation for the purpose of 35 | this License. For the avoidance of doubt, where the Work is a musical 36 | work, performance or phonogram, the synchronization of the Work in 37 | timed-relation with a moving image ("synching") will be considered an 38 | Adaptation for the purpose of this License. 39 | b. "Collection" means a collection of literary or artistic works, such as 40 | encyclopedias and anthologies, or performances, phonograms or 41 | broadcasts, or other works or subject matter other than works listed 42 | in Section 1(f) below, which, by reason of the selection and 43 | arrangement of their contents, constitute intellectual creations, in 44 | which the Work is included in its entirety in unmodified form along 45 | with one or more other contributions, each constituting separate and 46 | independent works in themselves, which together are assembled into a 47 | collective whole. A work that constitutes a Collection will not be 48 | considered an Adaptation (as defined below) for the purposes of this 49 | License. 50 | c. "Creative Commons Compatible License" means a license that is listed 51 | at https://creativecommons.org/compatiblelicenses that has been 52 | approved by Creative Commons as being essentially equivalent to this 53 | License, including, at a minimum, because that license: (i) contains 54 | terms that have the same purpose, meaning and effect as the License 55 | Elements of this License; and, (ii) explicitly permits the relicensing 56 | of adaptations of works made available under that license under this 57 | License or a Creative Commons jurisdiction license with the same 58 | License Elements as this License. 59 | d. "Distribute" means to make available to the public the original and 60 | copies of the Work or Adaptation, as appropriate, through sale or 61 | other transfer of ownership. 62 | e. "License Elements" means the following high-level license attributes 63 | as selected by Licensor and indicated in the title of this License: 64 | Attribution, ShareAlike. 65 | f. "Licensor" means the individual, individuals, entity or entities that 66 | offer(s) the Work under the terms of this License. 67 | g. "Original Author" means, in the case of a literary or artistic work, 68 | the individual, individuals, entity or entities who created the Work 69 | or if no individual or entity can be identified, the publisher; and in 70 | addition (i) in the case of a performance the actors, singers, 71 | musicians, dancers, and other persons who act, sing, deliver, declaim, 72 | play in, interpret or otherwise perform literary or artistic works or 73 | expressions of folklore; (ii) in the case of a phonogram the producer 74 | being the person or legal entity who first fixes the sounds of a 75 | performance or other sounds; and, (iii) in the case of broadcasts, the 76 | organization that transmits the broadcast. 77 | h. "Work" means the literary and/or artistic work offered under the terms 78 | of this License including without limitation any production in the 79 | literary, scientific and artistic domain, whatever may be the mode or 80 | form of its expression including digital form, such as a book, 81 | pamphlet and other writing; a lecture, address, sermon or other work 82 | of the same nature; a dramatic or dramatico-musical work; a 83 | choreographic work or entertainment in dumb show; a musical 84 | composition with or without words; a cinematographic work to which are 85 | assimilated works expressed by a process analogous to cinematography; 86 | a work of drawing, painting, architecture, sculpture, engraving or 87 | lithography; a photographic work to which are assimilated works 88 | expressed by a process analogous to photography; a work of applied 89 | art; an illustration, map, plan, sketch or three-dimensional work 90 | relative to geography, topography, architecture or science; a 91 | performance; a broadcast; a phonogram; a compilation of data to the 92 | extent it is protected as a copyrightable work; or a work performed by 93 | a variety or circus performer to the extent it is not otherwise 94 | considered a literary or artistic work. 95 | i. "You" means an individual or entity exercising rights under this 96 | License who has not previously violated the terms of this License with 97 | respect to the Work, or who has received express permission from the 98 | Licensor to exercise rights under this License despite a previous 99 | violation. 100 | j. "Publicly Perform" means to perform public recitations of the Work and 101 | to communicate to the public those public recitations, by any means or 102 | process, including by wire or wireless means or public digital 103 | performances; to make available to the public Works in such a way that 104 | members of the public may access these Works from a place and at a 105 | place individually chosen by them; to perform the Work to the public 106 | by any means or process and the communication to the public of the 107 | performances of the Work, including by public digital performance; to 108 | broadcast and rebroadcast the Work by any means including signs, 109 | sounds or images. 110 | k. "Reproduce" means to make copies of the Work by any means including 111 | without limitation by sound or visual recordings and the right of 112 | fixation and reproducing fixations of the Work, including storage of a 113 | protected performance or phonogram in digital form or other electronic 114 | medium. 115 | 116 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce, 117 | limit, or restrict any uses free from copyright or rights arising from 118 | limitations or exceptions that are provided for in connection with the 119 | copyright protection under copyright law or other applicable laws. 120 | 121 | 3. License Grant. Subject to the terms and conditions of this License, 122 | Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 123 | perpetual (for the duration of the applicable copyright) license to 124 | exercise the rights in the Work as stated below: 125 | 126 | a. to Reproduce the Work, to incorporate the Work into one or more 127 | Collections, and to Reproduce the Work as incorporated in the 128 | Collections; 129 | b. to create and Reproduce Adaptations provided that any such Adaptation, 130 | including any translation in any medium, takes reasonable steps to 131 | clearly label, demarcate or otherwise identify that changes were made 132 | to the original Work. For example, a translation could be marked "The 133 | original work was translated from English to Spanish," or a 134 | modification could indicate "The original work has been modified."; 135 | c. to Distribute and Publicly Perform the Work including as incorporated 136 | in Collections; and, 137 | d. to Distribute and Publicly Perform Adaptations. 138 | e. For the avoidance of doubt: 139 | 140 | i. Non-waivable Compulsory License Schemes. In those jurisdictions in 141 | which the right to collect royalties through any statutory or 142 | compulsory licensing scheme cannot be waived, the Licensor 143 | reserves the exclusive right to collect such royalties for any 144 | exercise by You of the rights granted under this License; 145 | ii. Waivable Compulsory License Schemes. In those jurisdictions in 146 | which the right to collect royalties through any statutory or 147 | compulsory licensing scheme can be waived, the Licensor waives the 148 | exclusive right to collect such royalties for any exercise by You 149 | of the rights granted under this License; and, 150 | iii. Voluntary License Schemes. The Licensor waives the right to 151 | collect royalties, whether individually or, in the event that the 152 | Licensor is a member of a collecting society that administers 153 | voluntary licensing schemes, via that society, from any exercise 154 | by You of the rights granted under this License. 155 | 156 | The above rights may be exercised in all media and formats whether now 157 | known or hereafter devised. The above rights include the right to make 158 | such modifications as are technically necessary to exercise the rights in 159 | other media and formats. Subject to Section 8(f), all rights not expressly 160 | granted by Licensor are hereby reserved. 161 | 162 | 4. Restrictions. The license granted in Section 3 above is expressly made 163 | subject to and limited by the following restrictions: 164 | 165 | a. You may Distribute or Publicly Perform the Work only under the terms 166 | of this License. You must include a copy of, or the Uniform Resource 167 | Identifier (URI) for, this License with every copy of the Work You 168 | Distribute or Publicly Perform. You may not offer or impose any terms 169 | on the Work that restrict the terms of this License or the ability of 170 | the recipient of the Work to exercise the rights granted to that 171 | recipient under the terms of the License. You may not sublicense the 172 | Work. You must keep intact all notices that refer to this License and 173 | to the disclaimer of warranties with every copy of the Work You 174 | Distribute or Publicly Perform. When You Distribute or Publicly 175 | Perform the Work, You may not impose any effective technological 176 | measures on the Work that restrict the ability of a recipient of the 177 | Work from You to exercise the rights granted to that recipient under 178 | the terms of the License. This Section 4(a) applies to the Work as 179 | incorporated in a Collection, but this does not require the Collection 180 | apart from the Work itself to be made subject to the terms of this 181 | License. If You create a Collection, upon notice from any Licensor You 182 | must, to the extent practicable, remove from the Collection any credit 183 | as required by Section 4(c), as requested. If You create an 184 | Adaptation, upon notice from any Licensor You must, to the extent 185 | practicable, remove from the Adaptation any credit as required by 186 | Section 4(c), as requested. 187 | b. You may Distribute or Publicly Perform an Adaptation only under the 188 | terms of: (i) this License; (ii) a later version of this License with 189 | the same License Elements as this License; (iii) a Creative Commons 190 | jurisdiction license (either this or a later license version) that 191 | contains the same License Elements as this License (e.g., 192 | Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible 193 | License. If you license the Adaptation under one of the licenses 194 | mentioned in (iv), you must comply with the terms of that license. If 195 | you license the Adaptation under the terms of any of the licenses 196 | mentioned in (i), (ii) or (iii) (the "Applicable License"), you must 197 | comply with the terms of the Applicable License generally and the 198 | following provisions: (I) You must include a copy of, or the URI for, 199 | the Applicable License with every copy of each Adaptation You 200 | Distribute or Publicly Perform; (II) You may not offer or impose any 201 | terms on the Adaptation that restrict the terms of the Applicable 202 | License or the ability of the recipient of the Adaptation to exercise 203 | the rights granted to that recipient under the terms of the Applicable 204 | License; (III) You must keep intact all notices that refer to the 205 | Applicable License and to the disclaimer of warranties with every copy 206 | of the Work as included in the Adaptation You Distribute or Publicly 207 | Perform; (IV) when You Distribute or Publicly Perform the Adaptation, 208 | You may not impose any effective technological measures on the 209 | Adaptation that restrict the ability of a recipient of the Adaptation 210 | from You to exercise the rights granted to that recipient under the 211 | terms of the Applicable License. This Section 4(b) applies to the 212 | Adaptation as incorporated in a Collection, but this does not require 213 | the Collection apart from the Adaptation itself to be made subject to 214 | the terms of the Applicable License. 215 | c. If You Distribute, or Publicly Perform the Work or any Adaptations or 216 | Collections, You must, unless a request has been made pursuant to 217 | Section 4(a), keep intact all copyright notices for the Work and 218 | provide, reasonable to the medium or means You are utilizing: (i) the 219 | name of the Original Author (or pseudonym, if applicable) if supplied, 220 | and/or if the Original Author and/or Licensor designate another party 221 | or parties (e.g., a sponsor institute, publishing entity, journal) for 222 | attribution ("Attribution Parties") in Licensor's copyright notice, 223 | terms of service or by other reasonable means, the name of such party 224 | or parties; (ii) the title of the Work if supplied; (iii) to the 225 | extent reasonably practicable, the URI, if any, that Licensor 226 | specifies to be associated with the Work, unless such URI does not 227 | refer to the copyright notice or licensing information for the Work; 228 | and (iv) , consistent with Ssection 3(b), in the case of an 229 | Adaptation, a credit identifying the use of the Work in the Adaptation 230 | (e.g., "French translation of the Work by Original Author," or 231 | "Screenplay based on original Work by Original Author"). The credit 232 | required by this Section 4(c) may be implemented in any reasonable 233 | manner; provided, however, that in the case of a Adaptation or 234 | Collection, at a minimum such credit will appear, if a credit for all 235 | contributing authors of the Adaptation or Collection appears, then as 236 | part of these credits and in a manner at least as prominent as the 237 | credits for the other contributing authors. For the avoidance of 238 | doubt, You may only use the credit required by this Section for the 239 | purpose of attribution in the manner set out above and, by exercising 240 | Your rights under this License, You may not implicitly or explicitly 241 | assert or imply any connection with, sponsorship or endorsement by the 242 | Original Author, Licensor and/or Attribution Parties, as appropriate, 243 | of You or Your use of the Work, without the separate, express prior 244 | written permission of the Original Author, Licensor and/or Attribution 245 | Parties. 246 | d. Except as otherwise agreed in writing by the Licensor or as may be 247 | otherwise permitted by applicable law, if You Reproduce, Distribute or 248 | Publicly Perform the Work either by itself or as part of any 249 | Adaptations or Collections, You must not distort, mutilate, modify or 250 | take other derogatory action in relation to the Work which would be 251 | prejudicial to the Original Author's honor or reputation. Licensor 252 | agrees that in those jurisdictions (e.g. Japan), in which any exercise 253 | of the right granted in Section 3(b) of this License (the right to 254 | make Adaptations) would be deemed to be a distortion, mutilation, 255 | modification or other derogatory action prejudicial to the Original 256 | Author's honor and reputation, the Licensor will waive or not assert, 257 | as appropriate, this Section, to the fullest extent permitted by the 258 | applicable national law, to enable You to reasonably exercise Your 259 | right under Section 3(b) of this License (right to make Adaptations) 260 | but not otherwise. 261 | 262 | 5. Representations, Warranties and Disclaimer 263 | 264 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR 265 | OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY 266 | KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, 267 | INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, 268 | FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF 269 | LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, 270 | WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION 271 | OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 272 | 273 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE 274 | LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR 275 | ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES 276 | ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS 277 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 278 | 279 | 7. Termination 280 | 281 | a. This License and the rights granted hereunder will terminate 282 | automatically upon any breach by You of the terms of this License. 283 | Individuals or entities who have received Adaptations or Collections 284 | from You under this License, however, will not have their licenses 285 | terminated provided such individuals or entities remain in full 286 | compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will 287 | survive any termination of this License. 288 | b. Subject to the above terms and conditions, the license granted here is 289 | perpetual (for the duration of the applicable copyright in the Work). 290 | Notwithstanding the above, Licensor reserves the right to release the 291 | Work under different license terms or to stop distributing the Work at 292 | any time; provided, however that any such election will not serve to 293 | withdraw this License (or any other license that has been, or is 294 | required to be, granted under the terms of this License), and this 295 | License will continue in full force and effect unless terminated as 296 | stated above. 297 | 298 | 8. Miscellaneous 299 | 300 | a. Each time You Distribute or Publicly Perform the Work or a Collection, 301 | the Licensor offers to the recipient a license to the Work on the same 302 | terms and conditions as the license granted to You under this License. 303 | b. Each time You Distribute or Publicly Perform an Adaptation, Licensor 304 | offers to the recipient a license to the original Work on the same 305 | terms and conditions as the license granted to You under this License. 306 | c. If any provision of this License is invalid or unenforceable under 307 | applicable law, it shall not affect the validity or enforceability of 308 | the remainder of the terms of this License, and without further action 309 | by the parties to this agreement, such provision shall be reformed to 310 | the minimum extent necessary to make such provision valid and 311 | enforceable. 312 | d. No term or provision of this License shall be deemed waived and no 313 | breach consented to unless such waiver or consent shall be in writing 314 | and signed by the party to be charged with such waiver or consent. 315 | e. This License constitutes the entire agreement between the parties with 316 | respect to the Work licensed here. There are no understandings, 317 | agreements or representations with respect to the Work not specified 318 | here. Licensor shall not be bound by any additional provisions that 319 | may appear in any communication from You. This License may not be 320 | modified without the mutual written agreement of the Licensor and You. 321 | f. The rights granted under, and the subject matter referenced, in this 322 | License were drafted utilizing the terminology of the Berne Convention 323 | for the Protection of Literary and Artistic Works (as amended on 324 | September 28, 1979), the Rome Convention of 1961, the WIPO Copyright 325 | Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 326 | and the Universal Copyright Convention (as revised on July 24, 1971). 327 | These rights and subject matter take effect in the relevant 328 | jurisdiction in which the License terms are sought to be enforced 329 | according to the corresponding provisions of the implementation of 330 | those treaty provisions in the applicable national law. If the 331 | standard suite of rights granted under applicable copyright law 332 | includes additional rights not granted under this License, such 333 | additional rights are deemed to be included in the License; this 334 | License is not intended to restrict the license of any rights under 335 | applicable law. 336 | 337 | 338 | Creative Commons Notice 339 | 340 | Creative Commons is not a party to this License, and makes no warranty 341 | whatsoever in connection with the Work. Creative Commons will not be 342 | liable to You or any party on any legal theory for any damages 343 | whatsoever, including without limitation any general, special, 344 | incidental or consequential damages arising in connection to this 345 | license. Notwithstanding the foregoing two (2) sentences, if Creative 346 | Commons has expressly identified itself as the Licensor hereunder, it 347 | shall have all rights and obligations of Licensor. 348 | 349 | Except for the limited purpose of indicating to the public that the 350 | Work is licensed under the CCPL, Creative Commons does not authorize 351 | the use by either party of the trademark "Creative Commons" or any 352 | related trademark or logo of Creative Commons without the prior 353 | written consent of Creative Commons. Any permitted use will be in 354 | compliance with Creative Commons' then-current trademark usage 355 | guidelines, as may be published on its website or otherwise made 356 | available upon request from time to time. For the avoidance of doubt, 357 | this trademark restriction does not form part of the License. 358 | 359 | Creative Commons may be contacted at https://creativecommons.org/. 360 | -------------------------------------------------------------------------------- /make_stls.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | { 4 | 5 | # Exit on error 6 | set -o errexit 7 | set -o errtrace 8 | 9 | # Constants 10 | openscad="/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD" 11 | timestamp=$(git log -n1 --date=unix --format="%ad" openscad) 12 | commit_hash=$(git log -n1 --format="%h" openscad) 13 | 14 | # Flags 15 | bonk= 16 | prefix="oskitone-apc" 17 | dir="local/3d-models/$prefix-$timestamp-$commit_hash" 18 | query= 19 | 20 | # Internal variables 21 | _found_matches= 22 | 23 | function help() { 24 | echo "\ 25 | Renders APC STL models. 26 | 27 | Usage: 28 | ./make_stls.sh [-hectb] [-p PREFIX] [-d DIRECTORY] [-q COMMA,SEPARATED,QUERY] 29 | 30 | Usage: 31 | ./make_stls.sh Export all STLs 32 | ./make_stls.sh -h Show this message and quit 33 | ./make_stls.sh -e Echo out output directory and quit 34 | ./make_stls.sh -c Echo out commit hash and quit 35 | ./make_stls.sh -t Echo out timestamp and quit 36 | ./make_stls.sh -b Bonk and open folder when done 37 | ./make_stls.sh -p Set filename prefix 38 | Default is 'oskitone-apc' 39 | ./make_stls.sh -d Set output directory 40 | Default is local/3d-models/... 41 | ./make_stls.sh -q Export only STLs whose filename stubs match 42 | comma-separated query 43 | 44 | Examples: 45 | ./make_stls.sh -p test -q switch Exports test-...-switch_clutch.stl 46 | ./make_stls.sh -p wheels,enc Exports oskitone-apc-...-wheels.stl, 47 | oskitone-apc-...-enclosure_bottom.stl, 48 | and oskitone-apc-...-enclosure_top.stl 49 | " 50 | } 51 | 52 | function note_poly555_branch() { 53 | pushd ../poly555 > /dev/null 54 | poly555_branch=$(git branch --show-current) 55 | popd > /dev/null 56 | 57 | echo "NOTE: poly555 is on branch '$poly555_branch'." 58 | echo 59 | } 60 | 61 | function export_stl() { 62 | stub="$1" 63 | override="$2" 64 | flip_vertically="$3" 65 | 66 | function _run() { 67 | filename="$dir/$prefix-$timestamp-$commit_hash-$stub.stl" 68 | 69 | echo "Exporting $filename..." 70 | 71 | # The "& \" at the end runs everything in parallel! 72 | $openscad "openscad/apc.scad" \ 73 | --quiet \ 74 | -o "$filename" \ 75 | --export-format "binstl" \ 76 | -D 'SHOW_ENCLOSURE_TOP=false' \ 77 | -D 'SHOW_ENCLOSURE_BOTTOM=false' \ 78 | -D 'SHOW_PCB=false' \ 79 | -D 'SHOW_WHEELS=false' \ 80 | -D 'SHOW_SWITCH_CLUTCH=false' \ 81 | -D 'SHOW_BATTERY=false' \ 82 | -D 'SHOW_DFM=true' \ 83 | -D 'WHEELS_COUNT=1' \ 84 | -D "CENTER=true" \ 85 | -D "FLIP_VERTICALLY=$flip_vertically" \ 86 | -D "$override=true" \ 87 | & \ 88 | } 89 | 90 | if [[ -z "$query" ]]; then 91 | _run 92 | else 93 | for query_iterm in "${query[@]}"; do 94 | if [[ "$stub" == *"$query_iterm"* ]]; then 95 | _found_matches=true 96 | _run 97 | fi 98 | done 99 | fi 100 | } 101 | 102 | function create_zip() { 103 | if [[ -z "$query" ]]; then 104 | echo 105 | echo "Creating zip" 106 | pushd $dir 107 | zip "$prefix-$timestamp-$commit_hash-ALL.zip" *.stl 108 | popd > /dev/null 109 | fi 110 | } 111 | 112 | function run() { 113 | mkdir -pv $dir 114 | 115 | function finish() { 116 | # Kill descendent processes 117 | pkill -P "$$" 118 | } 119 | trap finish EXIT 120 | 121 | note_poly555_branch 122 | 123 | start=`date +%s` 124 | 125 | export_stl 'enclosure_bottom' 'SHOW_ENCLOSURE_BOTTOM' 'false' 126 | export_stl 'enclosure_top' 'SHOW_ENCLOSURE_TOP' 'true' 127 | export_stl 'switch_clutch' 'SHOW_SWITCH_CLUTCH' 'false' 128 | export_stl 'wheels' 'SHOW_WHEELS' 'false' 129 | wait 130 | 131 | end=`date +%s` 132 | runtime=$((end-start)) 133 | 134 | if [[ "$query" && -z $_found_matches ]]; then 135 | echo "Found no matches for query '$query'" 136 | else 137 | create_zip 138 | 139 | if [[ $bonk ]]; then 140 | printf "\a" 141 | open $dir 142 | fi 143 | fi 144 | 145 | echo 146 | echo "Finished in $runtime seconds" 147 | } 148 | 149 | while getopts "h?b?p:d:e?c?t?q:" opt; do 150 | case "$opt" in 151 | h) help; exit ;; 152 | b) bonk=true ;; 153 | p) prefix="$OPTARG" ;; 154 | d) dir="$OPTARG" ;; 155 | e) echo "$dir"; exit ;; 156 | c) echo "$commit_hash"; exit ;; 157 | t) echo "$timestamp"; exit ;; 158 | q) IFS="," read -r -a query <<< "$OPTARG" ;; 159 | *) help; exit ;; 160 | esac 161 | done 162 | 163 | run "${query[@]}" 164 | 165 | } 166 | -------------------------------------------------------------------------------- /openscad/apc.scad: -------------------------------------------------------------------------------- 1 | include ; 2 | include ; 3 | include ; 4 | include ; 5 | include ; 6 | 7 | module apc( 8 | show_enclosure_top = true, 9 | show_enclosure_bottom = true, 10 | 11 | show_pcb = true, 12 | show_wheels = true, 13 | show_switch_clutch = true, 14 | show_battery = true, 15 | 16 | show_dfm = true, 17 | 18 | wheels_count = undef, 19 | 20 | enclosure_color = "hotpink", 21 | wheels_color = "white", 22 | switch_clutch_color = "white", 23 | 24 | switch_position = round($t), 25 | enclosure_bottom_position = 0 // abs($t - .5) * 2 26 | ) { 27 | e = .0123; 28 | 29 | color(enclosure_color) { 30 | if (show_enclosure_bottom) { 31 | enclosure_bottom( 32 | enclosure_bottom_position = enclosure_bottom_position 33 | ); 34 | } 35 | 36 | if (show_enclosure_top) { 37 | enclosure_top( 38 | show_dfm = show_dfm 39 | ); 40 | } 41 | } 42 | 43 | if (show_pcb) { 44 | translate([ 45 | ENCLOSURE_WALL + ENCLOSURE_INTERNAL_GUTTER, 46 | ENCLOSURE_LENGTH - ENCLOSURE_WALL - ENCLOSURE_INTERNAL_GUTTER 47 | - PCB_LENGTH, 48 | PCB_Z - e 49 | ]) { 50 | # pcb( 51 | show_board = true, 52 | show_speaker = true, 53 | show_pots = true, 54 | show_led = true, 55 | show_switch = true, 56 | show_volume_pot = true, 57 | switch_position = switch_position 58 | ); 59 | } 60 | } 61 | 62 | color (wheels_color) { 63 | if (show_wheels) { 64 | wheels( 65 | diameter = WHEEL_DIAMETER, 66 | height = WHEEL_HEIGHT, 67 | y = PCB_Y, 68 | z = ENCLOSURE_HEIGHT - WHEEL_HEIGHT + WHEEL_VERTICAL_EXPOSURE 69 | - e, 70 | count = wheels_count 71 | ); 72 | } 73 | } 74 | 75 | if (show_switch_clutch) { 76 | color(switch_clutch_color) { 77 | switch_clutch( 78 | position = switch_position, 79 | show_dfm = show_dfm 80 | ); 81 | } 82 | } 83 | 84 | if (show_battery) { 85 | translate([ 86 | ENCLOSURE_WALL + ENCLOSURE_INTERNAL_GUTTER, 87 | ENCLOSURE_WALL + ENCLOSURE_INTERNAL_GUTTER, 88 | ENCLOSURE_FLOOR_CEILING 89 | ]) { 90 | battery(); 91 | } 92 | } 93 | } 94 | 95 | SHOW_ENCLOSURE_TOP = true; 96 | SHOW_ENCLOSURE_BOTTOM = true; 97 | SHOW_PCB = true; 98 | SHOW_WHEELS = true; 99 | SHOW_SWITCH_CLUTCH = true; 100 | SHOW_BATTERY = true; 101 | SHOW_DFM = false; 102 | 103 | WHEELS_COUNT = undef; 104 | CROSS_SECTION = undef; 105 | 106 | FLIP_VERTICALLY = false; 107 | CENTER = false; 108 | 109 | position = CENTER 110 | ? [ 111 | ENCLOSURE_WIDTH / -2, 112 | ENCLOSURE_LENGTH / -2, 113 | ENCLOSURE_HEIGHT / -2 114 | ] : [0, 0, 0]; 115 | rotation = FLIP_VERTICALLY ? [0, 180, 0] : [0, 0, 0]; 116 | 117 | translate(position) rotate(rotation) { 118 | intersection() { 119 | apc( 120 | show_enclosure_top = SHOW_ENCLOSURE_TOP, 121 | show_enclosure_bottom = SHOW_ENCLOSURE_BOTTOM, 122 | show_pcb = SHOW_PCB, 123 | show_wheels = SHOW_WHEELS, 124 | show_switch_clutch = SHOW_SWITCH_CLUTCH, 125 | show_battery = SHOW_BATTERY, 126 | show_dfm = SHOW_DFM, 127 | wheels_count = WHEELS_COUNT 128 | ); 129 | 130 | if (CROSS_SECTION) { 131 | cross_section(CROSS_SECTION); 132 | } 133 | } 134 | } 135 | 136 | echo(str("ENCLOSURE: ", ENCLOSURE_WIDTH, " x ", ENCLOSURE_LENGTH, " x ", ENCLOSURE_HEIGHT)); 137 | echo(str("WHEELS: ", WHEEL_DIAMETER, " diameter, ", WHEEL_HEIGHT, " height")); 138 | -------------------------------------------------------------------------------- /openscad/battery.scad: -------------------------------------------------------------------------------- 1 | BATTERY_WIDTH = 48.6; 2 | BATTERY_LENGTH = 17.1; 3 | BATTERY_HEIGHT = 25.8; 4 | BATTERY_SNAP_WIDTH = 4.2; 5 | BATTERY_SNAP_CLEARANCE = 3; 6 | 7 | module battery(include_snap = true) { 8 | e = .021; 9 | 10 | cube([BATTERY_WIDTH, BATTERY_LENGTH, BATTERY_HEIGHT]); 11 | 12 | if (include_snap) { 13 | translate([BATTERY_WIDTH - e, 0, 0]) { 14 | # cube([BATTERY_SNAP_WIDTH + e * 2, BATTERY_LENGTH, BATTERY_HEIGHT]); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /openscad/cross_section.scad: -------------------------------------------------------------------------------- 1 | include ; 2 | 3 | CROSS_SECTION_WIDTH = "cross_section_width"; 4 | CROSS_SECTION_LENGTH = "cross_section_length"; 5 | CROSS_SECTION_BRANDING = "cross_section_branding"; 6 | CROSS_SECTION_WHEEL = "cross_section_wheel"; 7 | CROSS_SECTION_TOP_Z = "cross_section_top_z"; 8 | 9 | module cross_section(cross_section_name) { 10 | if (cross_section_name == CROSS_SECTION_WIDTH) { 11 | cube([ENCLOSURE_WIDTH / 2, ENCLOSURE_LENGTH * 2, ENCLOSURE_HEIGHT]); 12 | } else if (cross_section_name == CROSS_SECTION_LENGTH) { 13 | cube([ENCLOSURE_WIDTH, ENCLOSURE_LENGTH * .8, ENCLOSURE_HEIGHT]); 14 | } else if (cross_section_name == CROSS_SECTION_BRANDING) { 15 | translate([ 16 | ENCLOSURE_WIDTH / 5 * 1, 17 | ENCLOSURE_GRILL_GUTTER * 1, 18 | ENCLOSURE_HEIGHT - ENCLOSURE_FLOOR_CEILING 19 | ]) { 20 | cube([ 21 | ENCLOSURE_WIDTH / 5 * (5 - 2), 22 | ENCLOSURE_LENGTH - GRILL_LENGTH - ENCLOSURE_GRILL_GUTTER * 2.5, 23 | ENCLOSURE_FLOOR_CEILING 24 | ]); 25 | } 26 | } else if (cross_section_name == CROSS_SECTION_WHEEL) { 27 | width = PCB_X + PCB_POT_POSITIONS[1][0]; 28 | cube([width, ENCLOSURE_LENGTH, ENCLOSURE_HEIGHT + 10]); 29 | } else if (cross_section_name == CROSS_SECTION_TOP_Z) { 30 | z = 13.2; 31 | offset = 5; 32 | 33 | translate([ 34 | -offset, 35 | -offset, 36 | ENCLOSURE_HEIGHT - z 37 | ]) { 38 | cube([ 39 | ENCLOSURE_WIDTH + offset * 2, 40 | ENCLOSURE_LENGTH + offset * 2, 41 | z + offset 42 | ]); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /openscad/enclosure.scad: -------------------------------------------------------------------------------- 1 | // TODO: extract parts to common repo 2 | use <../../poly555/openscad/lib/basic_shapes.scad>; 3 | use <../../poly555/openscad/lib/enclosure.scad>; 4 | use <../../poly555/openscad/lib/engraving.scad>; 5 | use <../../poly555/openscad/lib/diagonal_grill.scad>; 6 | 7 | include ; 8 | include ; 9 | 10 | ENCLOSURE_ENGRAVING_DEPTH = 1.2; 11 | ENCLOSURE_SIDE_ENGRAVING_SIZE = 3; 12 | ENCLOSURE_CHAMFER = .8; 13 | 14 | PCB_RAILS_TOTAL_WIDTH = 25 + ENCLOSURE_INNER_WALL; 15 | 16 | module _pot_walls( 17 | diameter_bleed = 0, 18 | height_bleed = 0 19 | ) { 20 | e = .0321; 21 | 22 | y = PCB_Y + PCB_POT_POSITIONS[0][1]; 23 | 24 | module _well() { 25 | diameter = WELL_DIAMETER + diameter_bleed * 2; 26 | z = ENCLOSURE_HEIGHT - WHEEL_HEIGHT + WHEEL_VERTICAL_EXPOSURE 27 | - ENCLOSURE_FLOOR_CEILING - height_bleed; 28 | 29 | intersection() { 30 | translate([ENCLOSURE_WALL - e, y - diameter / 2 - e, 0]) { 31 | cube([ 32 | ENCLOSURE_WIDTH - ENCLOSURE_WALL * 2 - e * 2, 33 | diameter + e * 2, 34 | 100 35 | ]); 36 | } 37 | 38 | for (xy = PCB_POT_POSITIONS) { 39 | translate([PCB_X + xy.x, y, z]) { 40 | cylinder( 41 | d = diameter, 42 | h = ENCLOSURE_HEIGHT - z - ENCLOSURE_FLOOR_CEILING + e 43 | ); 44 | } 45 | }; 46 | } 47 | } 48 | 49 | module _shaft_to_base() { 50 | z = PCB_Z + PCB_HEIGHT + PTV09A_POT_BASE_HEIGHT; 51 | 52 | for (xy = PCB_POT_POSITIONS) { 53 | translate([PCB_X + xy.x, y, z]) { 54 | cylinder( 55 | d = PTV09A_POT_ACTUATOR_DIAMETER + diameter_bleed * 2, 56 | h = ENCLOSURE_HEIGHT - z - ENCLOSURE_FLOOR_CEILING + e 57 | ); 58 | } 59 | }; 60 | } 61 | 62 | _well(); 63 | _shaft_to_base(); 64 | } 65 | 66 | OSKITONE_LENGTH_WIDTH_RATIO = 4.6 / 28; // TODO: extract 67 | module _brand_engraving( 68 | width = 10, 69 | height = ENCLOSURE_ENGRAVING_DEPTH, 70 | tolerance = DEFAULT_TOLERANCE, 71 | resize = undef 72 | ) { 73 | e = .049; 74 | 75 | engraving( 76 | svg = "../../branding.svg", 77 | resize = resize !=undef 78 | ? resize 79 | : [width, width * OSKITONE_LENGTH_WIDTH_RATIO], 80 | height = height + e, 81 | bleed = -tolerance, 82 | chamfer = $preview ? 0 : .2 // engraving_chamfer 83 | ); 84 | } 85 | 86 | module _text_engraving( 87 | string, 88 | size = ENCLOSURE_SIDE_ENGRAVING_SIZE, 89 | tolerance = DEFAULT_TOLERANCE, 90 | depth = ENCLOSURE_ENGRAVING_DEPTH, 91 | bleed = .2, 92 | resize = undef 93 | ) { 94 | e = .09421; 95 | 96 | engraving( 97 | string = string, 98 | font = "Orbitron:style=Black", 99 | size = size, 100 | bleed = -tolerance + bleed, 101 | height = depth + e, 102 | center = true, 103 | chamfer = $preview ? 0 : .2, 104 | resize = resize 105 | ); 106 | } 107 | 108 | module _half(h, lip) { 109 | enclosure_half( 110 | width = ENCLOSURE_WIDTH, length = ENCLOSURE_LENGTH, height = h, 111 | wall = ENCLOSURE_WALL, 112 | floor_ceiling = ENCLOSURE_FLOOR_CEILING, 113 | add_lip = lip, 114 | remove_lip = !lip, 115 | fillet = ENCLOSURE_FILLET, 116 | tolerance = DEFAULT_TOLERANCE, 117 | include_tongue_and_groove = true, 118 | tongue_and_groove_endstop_height = ENCLOSURE_BOTTOM_HEIGHT 119 | - (ENCLOSURE_FLOOR_CEILING + LIP_BOX_DEFAULT_LIP_HEIGHT), 120 | $fn = DEFAULT_ROUNDING 121 | ); 122 | } 123 | 124 | module enclosure_bottom( 125 | width = ENCLOSURE_WIDTH, 126 | length = ENCLOSURE_LENGTH, 127 | height = ENCLOSURE_HEIGHT, 128 | 129 | wall = ENCLOSURE_WALL, 130 | inner_wall = ENCLOSURE_INNER_WALL, 131 | floor_ceiling = ENCLOSURE_FLOOR_CEILING, 132 | gutter = ENCLOSURE_INTERNAL_GUTTER, 133 | 134 | tolerance = DEFAULT_TOLERANCE, 135 | 136 | enclosure_bottom_position = 0 137 | ) { 138 | e = 0.0321; 139 | 140 | module _pcb_rails() { 141 | _length = 30; 142 | _height = PCB_Z - floor_ceiling; 143 | _gutter = PCB_RAILS_TOTAL_WIDTH - inner_wall; 144 | 145 | y = PCB_Y + PCB_LENGTH - _length; 146 | 147 | module _rail() { 148 | cube([inner_wall, _length, _height + e]); 149 | 150 | translate([0, -_height, 0]) { 151 | flat_top_rectangular_pyramid( 152 | top_width = inner_wall, 153 | top_length = 0, 154 | bottom_width = inner_wall, 155 | bottom_length = _height + e, 156 | height = _height + e, 157 | top_weight_y = 1 158 | ); 159 | } 160 | } 161 | 162 | for (x = [ 163 | (width - inner_wall - _gutter) / 2, 164 | (width - inner_wall + _gutter) / 2 165 | ]) { 166 | translate([x, y, floor_ceiling - e]) { 167 | _rail(); 168 | } 169 | } 170 | } 171 | 172 | module _output() { 173 | translate([0, ENCLOSURE_LENGTH * enclosure_bottom_position, 0]) { 174 | _pcb_rails(); 175 | 176 | difference() { 177 | _half(ENCLOSURE_BOTTOM_HEIGHT, false); 178 | 179 | translate([ 180 | width / 2, 181 | length * .67, 182 | ENCLOSURE_ENGRAVING_DEPTH 183 | ]) { 184 | rotate([0, 180, 0]) { 185 | _brand_engraving(width * .67); 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | _output(); 193 | } 194 | 195 | module enclosure_top( 196 | width = ENCLOSURE_WIDTH, 197 | length = ENCLOSURE_LENGTH, 198 | height = ENCLOSURE_HEIGHT, 199 | 200 | wall = ENCLOSURE_WALL, 201 | inner_wall = ENCLOSURE_INNER_WALL, 202 | floor_ceiling = ENCLOSURE_FLOOR_CEILING, 203 | gutter = ENCLOSURE_INTERNAL_GUTTER, 204 | 205 | grill_depth = ENCLOSURE_GRILL_DEPTH, 206 | grill_gutter = ENCLOSURE_GRILL_GUTTER, 207 | grill_ring = ENCLOSURE_GRILL_RING, 208 | grill_coverage = ENCLOSURE_GRILL_COVERAGE, 209 | grill_fillet = ACCESSORY_FILLET, 210 | 211 | side_overexposure = ENCLOSURE_SIDE_OVEREXPOSURE, 212 | 213 | fillet = ENCLOSURE_FILLET, 214 | tolerance = DEFAULT_TOLERANCE, 215 | 216 | show_dfm = true 217 | ) { 218 | e = 0.0321; 219 | 220 | switch_clutch_cavity_length = SWITCH_CLUTCH_LENGTH 221 | + SWITCH_ACTUATOR_TRAVEL 222 | + SWITCH_CLUTCH_SLIDE_CLEARANCE * 2; 223 | 224 | module _chamfer(z, diameter) { 225 | translate([0, 0, z - ENCLOSURE_CHAMFER]) { 226 | cylinder( 227 | d1 = diameter, 228 | d2 = diameter + ENCLOSURE_CHAMFER * 2, 229 | h = ENCLOSURE_CHAMFER + e 230 | ); 231 | } 232 | } 233 | 234 | module _component_walls(is_cavity = false) { 235 | $fn = is_cavity ? HIDEF_ROUNDING : DEFAULT_ROUNDING; 236 | 237 | pcb_top_z = Z_PCB_TOP + (is_cavity ? -e : 0); 238 | volume_pot_access_z = Z_PCB_TOP + VOLUME_POT_ACTUATOR_HEIGHT 239 | + MISC_CLEARANCE + (is_cavity ? -e : 0); 240 | 241 | led_bleed = is_cavity ? tolerance : 3; 242 | default_bleed = is_cavity ? tolerance : inner_wall; 243 | 244 | function get_height(z, expose = is_cavity) = 245 | height - z + (expose ? e : e - floor_ceiling); 246 | 247 | translate([ 248 | PCB_X + PCB_LED_POSITION.x, 249 | PCB_Y + PCB_LED_POSITION.y, 250 | pcb_top_z 251 | ]) { 252 | cylinder( 253 | d = LED_BASE_DIAMETER + led_bleed * 2, 254 | h = get_height(pcb_top_z) 255 | ); 256 | 257 | if (is_cavity) { 258 | _chamfer( 259 | get_height(pcb_top_z), 260 | LED_BASE_DIAMETER + led_bleed * 2 261 | ); 262 | } 263 | } 264 | 265 | translate([ 266 | PCB_X + PCB_VOLUME_POT_POSITION.x, 267 | PCB_Y + PCB_VOLUME_POT_POSITION.y, 268 | volume_pot_access_z 269 | ]) { 270 | cylinder( 271 | d = VOLUME_POT_SCREWDRIVER_ACCESS_DIAMETER + default_bleed * 2, 272 | h = get_height(volume_pot_access_z) 273 | ); 274 | 275 | if (is_cavity) { 276 | _chamfer( 277 | get_height(volume_pot_access_z), 278 | VOLUME_POT_SCREWDRIVER_ACCESS_DIAMETER + default_bleed * 2 279 | ); 280 | } 281 | } 282 | 283 | if (!is_cavity) { 284 | translate([ 285 | PCB_X + PCB_SPEAKER_POSITION.x, 286 | PCB_Y + PCB_SPEAKER_POSITION.y, 287 | pcb_top_z 288 | ]) { 289 | cylinder( 290 | d = SPEAKER_DIAMETER + default_bleed * 2, 291 | h = get_height(pcb_top_z) - grill_depth 292 | ); 293 | } 294 | 295 | _pot_walls(inner_wall); 296 | } 297 | } 298 | 299 | module _speaker_cavity() { 300 | full_height = height - grill_depth - floor_ceiling - Z_PCB_TOP; 301 | 302 | module _cavity(height, diameter) { 303 | translate([ 304 | PCB_X + PCB_SPEAKER_POSITION.x, 305 | PCB_Y + PCB_SPEAKER_POSITION.y, 306 | Z_PCB_TOP - e 307 | ]) { 308 | cylinder( 309 | d = diameter + tolerance * 2, 310 | h = height + e 311 | ); 312 | } 313 | } 314 | 315 | _cavity(SPEAKER_HEIGHT, SPEAKER_DIAMETER); 316 | _cavity(full_height, SPEAKER_DIAMETER - SPEAKER_RIM * 2); 317 | } 318 | 319 | module _grill_cavities_plate() { 320 | x = wall - e; 321 | _width = width - x * 2; 322 | _length = GRILL_LENGTH - wall + grill_gutter * 2 + e; 323 | y = length - _length - wall + e; 324 | z = height - grill_depth - floor_ceiling; 325 | 326 | translate([x, y, z]) { 327 | cube([_width, _length, grill_depth + e]); 328 | } 329 | } 330 | 331 | // TODO: extend grill to full speaker cavity height 332 | module _grill_cavities() { 333 | _depth = grill_depth + e; 334 | _length = GRILL_LENGTH; 335 | 336 | y = length - _length - grill_gutter; 337 | z = height - grill_depth; 338 | 339 | module _rounding(height = _depth) { 340 | rounded_xy_cube( 341 | [width - grill_gutter * 2, _length, height], 342 | radius = grill_fillet, 343 | $fn = DEFAULT_ROUNDING 344 | ); 345 | } 346 | 347 | module _diagonal_grill(height = _depth, angle = 45) { 348 | diagonal_grill( 349 | width - grill_gutter * 2, _length, height, 350 | size = 2, 351 | angle = angle 352 | ); 353 | } 354 | 355 | module _ring(xy, diameter) { 356 | translate([xy.x, xy.y, z - e]) { 357 | cylinder( 358 | d = diameter + grill_ring * 2, 359 | h = _depth + e * 2 360 | ); 361 | } 362 | } 363 | 364 | intersection() { 365 | translate([grill_gutter, y, z - floor_ceiling - e]) { 366 | _diagonal_grill(floor_ceiling + e * 2, angle = 0); 367 | } 368 | 369 | translate([ 370 | PCB_X + PCB_SPEAKER_POSITION.x, 371 | PCB_Y + PCB_SPEAKER_POSITION.y, 372 | z - floor_ceiling - e 373 | ]) { 374 | cylinder( 375 | d = SPEAKER_DIAMETER - SPEAKER_RIM * 2, 376 | h = floor_ceiling + e * 4 377 | ); 378 | } 379 | } 380 | 381 | difference() { 382 | translate([grill_gutter, y, z]) { 383 | intersection() { 384 | _rounding(); 385 | translate([0, 0, -e]) _diagonal_grill(_depth + e * 2); 386 | } 387 | } 388 | 389 | translate([PCB_X, PCB_Y, 0]) { 390 | for (xy = PCB_POT_POSITIONS) { 391 | _ring(xy, WELL_DIAMETER); 392 | }; 393 | _ring(PCB_LED_POSITION, LED_BASE_DIAMETER); 394 | _ring(PCB_VOLUME_POT_POSITION, 395 | VOLUME_POT_SCREWDRIVER_ACCESS_DIAMETER); 396 | } 397 | } 398 | } 399 | 400 | module _wheel_cavities() { 401 | well_z = ENCLOSURE_HEIGHT - WHEEL_HEIGHT + WHEEL_VERTICAL_EXPOSURE - e; 402 | shaft_to_base_z = PCB_Z + PCB_HEIGHT + PTV09A_POT_BASE_HEIGHT - e; 403 | 404 | exposure_diameter = PTV09A_POT_ACTUATOR_DIAMETER + tolerance * 2; 405 | 406 | module _well(_height = height - well_z + e) { 407 | cylinder( 408 | d = WELL_DIAMETER, 409 | h = _height, 410 | $fn = HIDEF_ROUNDING 411 | ); 412 | 413 | _chamfer(_height, WELL_DIAMETER); 414 | } 415 | 416 | for (xy = PCB_POT_POSITIONS) { 417 | translate([wall + gutter + xy.x, PCB_Y + xy.y, 0]) { 418 | translate([0, 0, well_z]) { 419 | _well(); 420 | 421 | if (show_dfm) { 422 | floating_hole_cavity( 423 | exposure_diameter, 424 | WELL_DIAMETER 425 | ); 426 | } 427 | } 428 | 429 | translate([0, 0, shaft_to_base_z]) { 430 | cylinder( 431 | d = exposure_diameter, 432 | h = height - shaft_to_base_z + e, 433 | $fn = HIDEF_ROUNDING 434 | ); 435 | 436 | cylinder( 437 | d = PTV09A_POT_ACTUATOR_BASE_DIAMETER + tolerance * 2, 438 | h = PTV09A_POT_ACTUATOR_BASE_HEIGHT, 439 | $fn = HIDEF_ROUNDING 440 | ); 441 | } 442 | }; 443 | } 444 | } 445 | 446 | module _switch_clutch_cavity() { 447 | y = get_switch_clutch_y(0) - SWITCH_CLUTCH_SLIDE_CLEARANCE; 448 | 449 | height = SWITCH_CLUTCH_HEIGHT + Z_PCB_TOP 450 | + SWITCH_CLUTCH_TOP_CLEARANCE 451 | + e; 452 | 453 | translate([-e, y, -e]) { 454 | cube([wall + e * 2, switch_clutch_cavity_length, height]); 455 | } 456 | } 457 | 458 | module _switch_clutch_web_wall() { 459 | width = ENCLOSURE_INNER_WALL; 460 | length = SWITCH_CLUTCH_WEB_LENGTH + SWITCH_ACTUATOR_TRAVEL; 461 | height = SWITCH_CLUTCH_WEB_HEIGHT; 462 | 463 | cavity_length = SWITCH_BASE_LENGTH + SWITCH_CLUTCH_SLIDE_CLEARANCE * 2; 464 | 465 | translate([ 466 | SWITCH_CLUTCH_WEB_X - ENCLOSURE_SIDE_OVEREXPOSURE 467 | + SWITCH_CLUTCH_WEB_WIDTH + MISC_CLEARANCE, 468 | get_switch_clutch_y(.5) - (length - SWITCH_CLUTCH_LENGTH) / 2, 469 | ENCLOSURE_HEIGHT - ENCLOSURE_FLOOR_CEILING - height 470 | - ENCLOSURE_GRILL_DEPTH 471 | ]) { 472 | difference() { 473 | cube([width, length, height + e]); 474 | 475 | translate([-e, (length - cavity_length) / 2, -e]) { 476 | cube([width + e * 2, cavity_length, height + e * 3]); 477 | } 478 | } 479 | } 480 | } 481 | 482 | module _pcb_rails_lip_cavity() { 483 | _width = PCB_RAILS_TOTAL_WIDTH + MISC_CLEARANCE * 2; 484 | 485 | translate([ 486 | (width - _width) / 2, 487 | length - wall - e, 488 | ENCLOSURE_BOTTOM_HEIGHT - LIP_BOX_DEFAULT_LIP_HEIGHT - e 489 | ]) { 490 | cube([_width, wall + e * 2, LIP_BOX_DEFAULT_LIP_HEIGHT + e]); 491 | } 492 | } 493 | 494 | module _top_engraving( 495 | depth = ENCLOSURE_ENGRAVING_DEPTH, 496 | top_outer_gutter = grill_gutter, 497 | bottom_outer_gutter = grill_gutter * 1.5, 498 | side_outer_gutter = grill_gutter * 1.5, 499 | inner_gutter = grill_gutter / 3, 500 | debug = false 501 | ) { 502 | area_width = width - side_outer_gutter * 2; 503 | area_length = length - GRILL_LENGTH - grill_gutter 504 | - top_outer_gutter - bottom_outer_gutter; 505 | 506 | brand_width = 35; 507 | brand_length = brand_width * OSKITONE_LENGTH_WIDTH_RATIO; 508 | 509 | MODEL_LENGTH_WIDTH_RATIO = .25; 510 | model_length = area_length - brand_length - inner_gutter; 511 | model_width = model_length / MODEL_LENGTH_WIDTH_RATIO; 512 | 513 | if (debug) { 514 | translate([side_outer_gutter, bottom_outer_gutter, height]) { 515 | # cube([area_width, area_length, e]); 516 | } 517 | } 518 | 519 | translate([ 520 | width / 2, 521 | bottom_outer_gutter + area_length / 2, 522 | height - depth - e 523 | ]) { 524 | translate([0, area_length / 2 - brand_length / 2, 0]) { 525 | _brand_engraving( 526 | width = brand_width, 527 | height = ENCLOSURE_ENGRAVING_DEPTH + e 528 | ); 529 | } 530 | 531 | translate([0, area_length / -2 + model_length / 2, 0]) { 532 | _text_engraving( 533 | "APC", 534 | area_length / 2, 535 | bleed = 0, 536 | resize = [model_width, model_length] 537 | ); 538 | } 539 | } 540 | } 541 | 542 | module _power_switch_engraving(depth = ENCLOSURE_ENGRAVING_DEPTH) { 543 | ys = [ 544 | get_switch_clutch_y(0) - ENCLOSURE_SIDE_ENGRAVING_SIZE * 1.5, 545 | get_switch_clutch_y(0) + switch_clutch_cavity_length 546 | + ENCLOSURE_SIDE_ENGRAVING_SIZE / 2 547 | ]; 548 | z = PCB_Z + SWITCH_CLUTCH_HEIGHT / 2; 549 | 550 | for (i = [0 : len(ys) - 1]) { 551 | translate([depth - e, ys[i], z]) { 552 | rotate([270, 180, 90]) { 553 | _text_engraving(["0", "1"][i]); 554 | } 555 | } 556 | } 557 | } 558 | 559 | module _volume_engraving(depth = ENCLOSURE_ENGRAVING_DEPTH) { 560 | y = PCB_Y + PCB_VOLUME_POT_POSITION[1]; 561 | z = height - ENCLOSURE_GRILL_GUTTER - ENCLOSURE_SIDE_ENGRAVING_SIZE / 2; 562 | 563 | translate([width - depth, y, z]) { 564 | rotate([270, 180, 270]) { 565 | _text_engraving("V"); 566 | } 567 | } 568 | } 569 | 570 | module _output() { 571 | // TODO: endstop tabs for PCB -- component walls aren't enough 572 | // TODO: hold battery into place 573 | 574 | difference() { 575 | union() { 576 | translate([0, 0, height]) { 577 | mirror([0, 0, 1]) { 578 | _half(ENCLSOURE_TOP_HEIGHT, true); 579 | } 580 | } 581 | 582 | _component_walls(); 583 | _grill_cavities_plate(); 584 | _switch_clutch_web_wall(); 585 | } 586 | 587 | _component_walls(is_cavity = true); 588 | render() _grill_cavities(); 589 | _wheel_cavities(); 590 | _speaker_cavity(); 591 | _switch_clutch_cavity(); 592 | _pcb_rails_lip_cavity(); 593 | _top_engraving(); 594 | _power_switch_engraving(); 595 | _volume_engraving(); 596 | } 597 | } 598 | 599 | _output(); 600 | } 601 | -------------------------------------------------------------------------------- /openscad/floating_hole_cavity.scad: -------------------------------------------------------------------------------- 1 | include ; 2 | 3 | module floating_hole_cavity( 4 | minimum_diameter, 5 | maximum_diameter, 6 | 7 | coverages = [1, .5, 0, 0], 8 | layer_height = DEFAULT_DFM_LAYER_HEIGHT, 9 | 10 | $fn = HIDEF_ROUNDING 11 | ) { 12 | e = .0245; 13 | 14 | function get_span( 15 | coverage = 0, 16 | minimum = minimum_diameter, 17 | maximum = maximum_diameter 18 | ) = ( 19 | minimum + coverage * (maximum - minimum) 20 | ); 21 | 22 | intersection() { 23 | translate([0, 0, layer_height * -len(coverages) - e]) { 24 | cylinder( 25 | d = maximum_diameter + e * 2, 26 | h = layer_height * len(coverages) + e * 2 27 | ); 28 | } 29 | 30 | for (i = [0 : len(coverages) - 1]) { 31 | width = get_span(coverage = coverages[max(0, i - 1)]); 32 | length = get_span(coverage = coverages[i]); 33 | 34 | translate([0, 0, (i + 1) * -layer_height]) { 35 | rotate([0, 0, (i % 2) * 90]) { 36 | translate([width / -2, length / -2, 0]) { 37 | cube([width, length, layer_height + e]); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /openscad/pcb.scad: -------------------------------------------------------------------------------- 1 | // TODO: extract parts to common repo 2 | use <../../poly555/openscad/lib/switch.scad>; 3 | 4 | PCB_WIDTH = 63.2714; 5 | PCB_LENGTH = 46.0756; 6 | PCB_HEIGHT = 1.6; 7 | 8 | PCB_BOTTOM_CLEARANCE = 4; 9 | 10 | PCB_SPEAKER_POSITION = [PCB_WIDTH / 2, 31.046]; 11 | SPEAKER_DIAMETER = 29.85; 12 | SPEAKER_HEIGHT = 12.7; 13 | SPEAKER_RIM = 2; 14 | 15 | PCB_POT_POSITIONS = [ 16 | [9.51 - 2.52, 7.551 + 7], 17 | [58.736 - 2.52, 7.551 + 7] 18 | ]; 19 | 20 | PTV09A_POT_BASE_WIDTH = 10; 21 | PTV09A_POT_BASE_HEIGHT = 6.8; 22 | PTV09A_POT_ACTUATOR_DIAMETER = 6.1; 23 | PTV09A_POT_ACTUATOR_BASE_DIAMETER = 6.9; 24 | PTV09A_POT_ACTUATOR_BASE_HEIGHT = 2; 25 | PTV09A_POT_ACTUATOR_HEIGHT = 20 - PTV09A_POT_BASE_HEIGHT; 26 | PTV09A_POT_ACTUATOR_D_SHAFT_HEIGHT = 7; 27 | PTV09A_POT_ACTUATOR_D_SHAFT_DEPTH = PTV09A_POT_ACTUATOR_DIAMETER - 4.5; 28 | 29 | PCB_LED_POSITION = [13.716 - 2.54 / 2, 40.698]; 30 | LED_DIAMETER = 5; 31 | LED_BASE_DIAMETER = 6; 32 | LED_HEIGHT = 8.6; 33 | 34 | PCB_SWITCH_POSITION = [3.556, 34.602]; 35 | 36 | VOLUME_POT_SCREWDRIVER_ACCESS_DIAMETER = 5; 37 | PCB_VOLUME_POT_POSITION = [59.944 - 2.54, 30.919 + 2.54]; 38 | VOLUME_POT_ACTUATOR_HEIGHT = 7.8; 39 | VOLUME_POT_ACTUATOR_DIAMETER = 6.15; 40 | 41 | // TODO: extract from poly555 42 | SWITCH_BASE_WIDTH = 4.4; 43 | SWITCH_BASE_LENGTH = 8.7; 44 | SWITCH_BASE_HEIGHT = 4.7; 45 | SWITCH_ACTUATOR_WIDTH = 2; 46 | SWITCH_ACTUATOR_LENGTH = 2.1; 47 | SWITCH_ACTUATOR_HEIGHT = 3.8; 48 | SWITCH_ACTUATOR_TRAVEL = 1.5; 49 | SWITCH_ORIGIN = [SWITCH_BASE_WIDTH / 2, 6.36]; 50 | 51 | POT_SHAFT_TYPE_SPLINED = "pot_shaft_type_splined"; 52 | POT_SHAFT_TYPE_PLAIN = "pot_shaft_type_plain"; 53 | POT_SHAFT_TYPE_FLATTED = "pot_shaft_type_flatted"; 54 | POT_SHAFT_TYPE_DEFAULT = POT_SHAFT_TYPE_SPLINED; 55 | 56 | module pot( 57 | show_base = true, 58 | show_actator = true, 59 | 60 | base_width = PTV09A_POT_BASE_WIDTH, 61 | base_height = PTV09A_POT_BASE_HEIGHT, 62 | 63 | actuator_diameter = PTV09A_POT_ACTUATOR_DIAMETER, 64 | actuator_height = PTV09A_POT_ACTUATOR_HEIGHT, 65 | 66 | shaft_type = POT_SHAFT_TYPE_DEFAULT, 67 | actuator_d_shaft_height = PTV09A_POT_ACTUATOR_D_SHAFT_HEIGHT, 68 | actuator_d_shaft_depth = PTV09A_POT_ACTUATOR_D_SHAFT_DEPTH, 69 | 70 | diameter_bleed = 0, 71 | actuator_height_bleed = 0 72 | ) { 73 | e = .0421; 74 | 75 | if (show_base) { 76 | translate([-7.35 + 2.52, + 1.5 - 7, 0]) { 77 | cube([base_width, 11, base_height]); 78 | } 79 | } 80 | 81 | if (show_actator) { 82 | translate([0, 0, base_height - e]) { 83 | difference() { 84 | cylinder( 85 | d = actuator_diameter + diameter_bleed * 2, 86 | h = actuator_height + actuator_height_bleed + e 87 | ); 88 | 89 | if (shaft_type == POT_SHAFT_TYPE_FLATTED) { 90 | translate([ 91 | actuator_diameter / -2 - diameter_bleed, 92 | actuator_diameter / -2 - e - diameter_bleed, 93 | actuator_height - actuator_d_shaft_height 94 | ]) { 95 | cube([ 96 | actuator_diameter + diameter_bleed * 2, 97 | actuator_d_shaft_depth + e, 98 | actuator_d_shaft_height + actuator_height_bleed + e 99 | ]); 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | module pcb( 108 | show_board = true, 109 | show_speaker = true, 110 | show_pots = true, 111 | show_led = true, 112 | show_switch = true, 113 | show_volume_pot = true, 114 | show_bottom_clearance = true, 115 | 116 | switch_position = 0 117 | ) { 118 | e = .031; 119 | silkscreen_height = e; 120 | 121 | e_z = PCB_HEIGHT - e; 122 | 123 | if (show_board) { 124 | cube([PCB_WIDTH, PCB_LENGTH, PCB_HEIGHT]); 125 | 126 | % translate([0, 0, e_z]) { 127 | linear_extrude(silkscreen_height + e) offset(delta = .2) { 128 | import("../pcb.svg"); 129 | } 130 | } 131 | } 132 | 133 | // 36MS30008-PN 134 | if (show_speaker) { 135 | translate([PCB_SPEAKER_POSITION.x, PCB_SPEAKER_POSITION.y, e_z]) { 136 | % cylinder(d = SPEAKER_DIAMETER, h = 12.7 + e); 137 | } 138 | } 139 | 140 | if (show_pots) { 141 | for (xy = PCB_POT_POSITIONS) { 142 | translate([xy.x, xy.y, e_z]) { 143 | % pot(); 144 | } 145 | } 146 | } 147 | 148 | if (show_led) { 149 | translate([PCB_LED_POSITION.x, PCB_LED_POSITION.y, e_z]) { 150 | % cylinder(d = LED_DIAMETER, h = LED_HEIGHT + e); 151 | } 152 | } 153 | 154 | if (show_switch) { 155 | translate([PCB_SWITCH_POSITION.x, PCB_SWITCH_POSITION.y, e_z]) { 156 | % switch(switch_position); 157 | } 158 | } 159 | 160 | if (show_volume_pot) { 161 | translate([PCB_VOLUME_POT_POSITION.x, PCB_VOLUME_POT_POSITION.y, e_z]) { 162 | % cylinder( 163 | d = VOLUME_POT_ACTUATOR_DIAMETER, 164 | h = VOLUME_POT_ACTUATOR_HEIGHT 165 | ); 166 | } 167 | } 168 | 169 | if (show_bottom_clearance) { 170 | translate([0, 0, -PCB_BOTTOM_CLEARANCE - e]) { 171 | % cube([PCB_WIDTH, PCB_LENGTH, PCB_BOTTOM_CLEARANCE]); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /openscad/rib_cavities.scad: -------------------------------------------------------------------------------- 1 | include ; 2 | 3 | module rib_cavities( 4 | length, 5 | height, 6 | 7 | rib_length = DEFAULT_RIB_LENGTH, 8 | rib_gutter = DEFAULT_RIB_GUTTER, 9 | depth = DEFAULT_RIB_LENGTH, 10 | ) { 11 | e = .02581; 12 | 13 | available_length = length - rib_gutter * 2 - rib_length; 14 | count = round(available_length / (rib_length + rib_gutter)); 15 | 16 | for (i = [0 : count - 0]) { 17 | y = rib_gutter + i * (available_length / count); 18 | 19 | translate([-e, y, -e]) { 20 | cube([depth + e, rib_length, height + e * 2]); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /openscad/shared_constants.scad: -------------------------------------------------------------------------------- 1 | // TODO: remove any includes. this file should have no dependencies... 2 | include ; 3 | include ; 4 | 5 | FIXED_LODEF_ROUNDING = 6; 6 | DEFAULT_ROUNDING = $preview ? undef : 24; 7 | HIDEF_ROUNDING = $preview ? undef : 120; 8 | 9 | DEFAULT_DFM_LAYER_HEIGHT = .2; 10 | BREAKAWAY_SUPPORT_DEPTH = .5; 11 | 12 | DEFAULT_TOLERANCE = .1; 13 | 14 | ENCLOSURE_WALL = 2.4; 15 | ENCLOSURE_INNER_WALL = 1.2; 16 | ENCLOSURE_FLOOR_CEILING = 1.8; 17 | ENCLOSURE_INTERNAL_GUTTER = 2; 18 | ENCLOSURE_SIDE_OVEREXPOSURE = 1.4; 19 | 20 | ENCLOSURE_GRILL_DEPTH = 6; 21 | ENCLOSURE_GRILL_GUTTER = 4; 22 | ENCLOSURE_GRILL_RING = 3; 23 | ENCLOSURE_GRILL_COVERAGE = .667; 24 | 25 | ENCLOSURE_FILLET = 2; 26 | ACCESSORY_FILLET = 1; 27 | 28 | ENCLOSURE_WIDTH = PCB_WIDTH 29 | + ENCLOSURE_INTERNAL_GUTTER * 2 30 | + ENCLOSURE_WALL * 2; 31 | ENCLOSURE_LENGTH = PCB_LENGTH 32 | + BATTERY_LENGTH 33 | + ENCLOSURE_INTERNAL_GUTTER * 3 34 | + ENCLOSURE_WALL * 2; 35 | 36 | LIP_BOX_DEFAULT_LIP_HEIGHT = 3; // TODO: expose 37 | ENCLOSURE_BOTTOM_HEIGHT = max( 38 | ENCLOSURE_FLOOR_CEILING + LIP_BOX_DEFAULT_LIP_HEIGHT, 39 | ENCLOSURE_FLOOR_CEILING + PCB_BOTTOM_CLEARANCE 40 | ); 41 | 42 | SWITCH_CLUTCH_TOP_CLEARANCE = PCB_HEIGHT; 43 | SWITCH_CLUTCH_SLIDE_CLEARANCE = 1.2; 44 | 45 | MISC_CLEARANCE = 1; 46 | 47 | PCB_X = ENCLOSURE_WALL + ENCLOSURE_INTERNAL_GUTTER; 48 | PCB_Y = ENCLOSURE_WALL + ENCLOSURE_INTERNAL_GUTTER * 2 + BATTERY_LENGTH; 49 | PCB_Z = max( 50 | ENCLOSURE_BOTTOM_HEIGHT - PCB_HEIGHT, 51 | ENCLOSURE_FLOOR_CEILING + PCB_BOTTOM_CLEARANCE 52 | ); 53 | 54 | GRILL_LENGTH = (ENCLOSURE_LENGTH - ENCLOSURE_GRILL_GUTTER * 2) 55 | * ENCLOSURE_GRILL_COVERAGE; 56 | 57 | // This is imperfect: 58 | // 1) The plate is actually longer than grill. 59 | // 2) Wheels may extend beyond grill plate. 60 | BATTERY_FITS_BEYOND_GRILL = (ENCLOSURE_LENGTH - GRILL_LENGTH 61 | - ENCLOSURE_GRILL_GUTTER * 2) > BATTERY_LENGTH; 62 | 63 | ENCLOSURE_HEIGHT = 64 | max( 65 | BATTERY_FITS_BEYOND_GRILL 66 | ? BATTERY_HEIGHT + ENCLOSURE_FLOOR_CEILING + BATTERY_SNAP_CLEARANCE 67 | : BATTERY_HEIGHT + ENCLOSURE_GRILL_DEPTH + ENCLOSURE_FLOOR_CEILING, 68 | PCB_Z + PCB_HEIGHT + PTV09A_POT_BASE_HEIGHT + PTV09A_POT_ACTUATOR_HEIGHT 69 | ) 70 | + ENCLOSURE_FLOOR_CEILING; 71 | 72 | ENCLSOURE_TOP_HEIGHT = ENCLOSURE_HEIGHT - ENCLOSURE_BOTTOM_HEIGHT; 73 | 74 | Z_PCB_TOP = PCB_Z + PCB_HEIGHT; 75 | 76 | DEFAULT_RIB_LENGTH = .8; 77 | DEFAULT_RIB_GUTTER = 1.234; 78 | -------------------------------------------------------------------------------- /openscad/switch_clutch.scad: -------------------------------------------------------------------------------- 1 | // TODO: extract parts to common repo 2 | use <../../poly555/openscad/lib/basic_shapes.scad>; 3 | 4 | include ; 5 | include ; 6 | include ; 7 | 8 | // Length is arbitrary but height's intentional -- middle of clutch should align 9 | // to the switch's actuator. 10 | SWITCH_CLUTCH_WIDTH = PCB_X + PCB_SWITCH_POSITION[0] 11 | + SWITCH_BASE_WIDTH / 2 12 | + ENCLOSURE_SIDE_OVEREXPOSURE; 13 | SWITCH_CLUTCH_LENGTH = SWITCH_ACTUATOR_LENGTH + ENCLOSURE_WALL * 2; 14 | SWITCH_CLUTCH_HEIGHT = SWITCH_BASE_HEIGHT * 2 + SWITCH_ACTUATOR_HEIGHT; 15 | 16 | SWITCH_CLUTCH_WEB_X = ENCLOSURE_SIDE_OVEREXPOSURE + ENCLOSURE_WALL 17 | + DEFAULT_TOLERANCE * 2; 18 | 19 | SWITCH_CLUTCH_WEB_WIDTH = SWITCH_CLUTCH_WIDTH 20 | - SWITCH_BASE_WIDTH - DEFAULT_TOLERANCE - SWITCH_CLUTCH_WEB_X; 21 | SWITCH_CLUTCH_WEB_LENGTH = SWITCH_BASE_LENGTH + SWITCH_ACTUATOR_TRAVEL 22 | + 4 * 2; 23 | SWITCH_CLUTCH_WEB_HEIGHT = ENCLOSURE_HEIGHT - Z_PCB_TOP - ENCLOSURE_FLOOR_CEILING 24 | - ENCLOSURE_GRILL_DEPTH 25 | - DEFAULT_DFM_LAYER_HEIGHT; // just to be safe 26 | 27 | function get_switch_clutch_y(position = 0) = ( 28 | PCB_Y + PCB_SWITCH_POSITION[1] - SWITCH_ORIGIN[1] 29 | + SWITCH_BASE_LENGTH / 2 30 | - SWITCH_ACTUATOR_TRAVEL / 2 31 | + SWITCH_ACTUATOR_TRAVEL * position 32 | - SWITCH_CLUTCH_LENGTH / 2 33 | ); 34 | 35 | module switch_clutch( 36 | position = 0, 37 | fillet = ACCESSORY_FILLET, 38 | side_overexposure = ENCLOSURE_SIDE_OVEREXPOSURE, 39 | tolerance = DEFAULT_TOLERANCE, 40 | floor_ceiling = ENCLOSURE_FLOOR_CEILING, 41 | brim_height = DEFAULT_DFM_LAYER_HEIGHT, 42 | brim_depth = 1, 43 | show_dfm = true 44 | ) { 45 | e = .045321; 46 | 47 | skirt_width = side_overexposure + PCB_X - SWITCH_CLUTCH_WEB_X 48 | - tolerance * 2; 49 | skirt_height = PCB_HEIGHT + MISC_CLEARANCE; 50 | 51 | module _breakaway_support(height) { 52 | cube([ 53 | BREAKAWAY_SUPPORT_DEPTH, 54 | SWITCH_CLUTCH_LENGTH, 55 | height - DEFAULT_DFM_LAYER_HEIGHT 56 | ]); 57 | 58 | translate([-brim_depth, -brim_depth, 0]) { 59 | cube([ 60 | BREAKAWAY_SUPPORT_DEPTH + brim_depth * 2, 61 | SWITCH_CLUTCH_LENGTH + brim_depth * 2, 62 | brim_height 63 | ]); 64 | } 65 | } 66 | 67 | module _clutch() { 68 | module _web() { 69 | dfm_cavity_depth = SWITCH_CLUTCH_WEB_WIDTH - skirt_width; 70 | 71 | translate([ 72 | SWITCH_CLUTCH_WEB_X, 73 | (SWITCH_CLUTCH_LENGTH - SWITCH_CLUTCH_WEB_LENGTH) / 2, 74 | 0 75 | ]) { 76 | difference() { 77 | cube([ 78 | SWITCH_CLUTCH_WEB_WIDTH, 79 | SWITCH_CLUTCH_WEB_LENGTH, 80 | SWITCH_CLUTCH_WEB_HEIGHT 81 | ]); 82 | 83 | if (show_dfm) { 84 | translate([skirt_width, -e, -e]) { 85 | flat_top_rectangular_pyramid( 86 | top_width = 0, 87 | top_length = SWITCH_CLUTCH_WEB_LENGTH + e * e, 88 | bottom_width = dfm_cavity_depth + e, 89 | bottom_length = SWITCH_CLUTCH_WEB_LENGTH + e * e, 90 | height = dfm_cavity_depth + e, 91 | top_weight_x = 1 92 | ); 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | module _exposed_grip() { 100 | width = SWITCH_CLUTCH_WIDTH - SWITCH_CLUTCH_WEB_WIDTH 101 | - SWITCH_BASE_WIDTH; 102 | 103 | if (show_dfm) { 104 | translate([ 105 | fillet, 106 | 0, 107 | -skirt_height 108 | ]) { 109 | _breakaway_support(skirt_height); 110 | } 111 | } 112 | 113 | difference() { 114 | rounded_cube( 115 | [ 116 | width + fillet, 117 | SWITCH_CLUTCH_LENGTH, 118 | SWITCH_CLUTCH_HEIGHT 119 | ], 120 | radius = fillet, 121 | $fn = DEFAULT_ROUNDING 122 | ); 123 | 124 | rib_cavities( 125 | length = SWITCH_CLUTCH_LENGTH, 126 | height = SWITCH_CLUTCH_HEIGHT 127 | ); 128 | } 129 | } 130 | 131 | module _actuator_clutch() { 132 | actuator_cavity_length = SWITCH_ACTUATOR_LENGTH + tolerance * 2; 133 | width = SWITCH_BASE_WIDTH + e; 134 | 135 | cavity_x = SWITCH_CLUTCH_WIDTH - width - e; 136 | cavity_width = SWITCH_BASE_WIDTH + DEFAULT_TOLERANCE * 2; 137 | 138 | difference() { 139 | translate([SWITCH_CLUTCH_WIDTH - width - e, 0, 0]) { 140 | cube([ 141 | width, 142 | SWITCH_CLUTCH_LENGTH, 143 | SWITCH_CLUTCH_HEIGHT 144 | ]); 145 | } 146 | 147 | translate([cavity_x, -e, -e]) { 148 | cube([ 149 | cavity_width, 150 | SWITCH_CLUTCH_LENGTH + e * 2, 151 | SWITCH_BASE_HEIGHT + e 152 | ]); 153 | } 154 | 155 | translate([ 156 | cavity_x, 157 | (SWITCH_CLUTCH_LENGTH - actuator_cavity_length) / 2, 158 | -e 159 | ]) { 160 | cube([ 161 | cavity_width, 162 | actuator_cavity_length, 163 | SWITCH_ACTUATOR_HEIGHT + SWITCH_BASE_HEIGHT + e 164 | + tolerance // just in case 165 | ]); 166 | } 167 | } 168 | 169 | if (show_dfm) { 170 | inset = 1; // try to prevent loose hangs 171 | x = SWITCH_CLUTCH_WIDTH - BREAKAWAY_SUPPORT_DEPTH - inset; 172 | 173 | translate([x, 0, -skirt_height]) { 174 | _breakaway_support(SWITCH_BASE_HEIGHT + skirt_height); 175 | } 176 | } 177 | } 178 | 179 | module _skirt() { 180 | length = SWITCH_CLUTCH_WEB_LENGTH; 181 | 182 | translate([ 183 | SWITCH_CLUTCH_WEB_X, 184 | (SWITCH_CLUTCH_WEB_LENGTH - SWITCH_CLUTCH_LENGTH) / -2, 185 | -skirt_height 186 | ]) { 187 | cube([skirt_width, length, skirt_height + e]); 188 | } 189 | } 190 | 191 | module _output() { 192 | _web(); 193 | _exposed_grip(); 194 | _actuator_clutch(); 195 | _skirt(); 196 | } 197 | 198 | _output(); 199 | } 200 | 201 | translate([0, position * SWITCH_ACTUATOR_TRAVEL, 0]) { 202 | difference() { 203 | translate([ 204 | -side_overexposure, 205 | get_switch_clutch_y(0), 206 | Z_PCB_TOP 207 | ]) { 208 | _clutch(); 209 | } 210 | 211 | _pot_walls( 212 | diameter_bleed = ENCLOSURE_INNER_WALL 213 | + SWITCH_CLUTCH_SLIDE_CLEARANCE, 214 | height_bleed = tolerance * 2 215 | ); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /openscad/wheels.scad: -------------------------------------------------------------------------------- 1 | // TODO: extract parts to common repo 2 | use <../../poly555/openscad/lib/basic_shapes.scad>; 3 | 4 | include ; 5 | include ; 6 | 7 | WHEEL_DIAMETER = 2 * 8 | (PCB_X + PCB_POT_POSITIONS[0][0] + ENCLOSURE_SIDE_OVEREXPOSURE); 9 | WHEEL_VERTICAL_EXPOSURE = ENCLOSURE_SIDE_OVEREXPOSURE; 10 | WHEEL_RECESSED_HEIGHT = 13; 11 | WHEEL_HEIGHT = WHEEL_RECESSED_HEIGHT + WHEEL_VERTICAL_EXPOSURE; 12 | WHEEL_DIAMETER_CLEARANCE = 1.2; 13 | WELL_DIAMETER = WHEEL_DIAMETER + WHEEL_DIAMETER_CLEARANCE * 2; 14 | WHEEL_RING = ENCLOSURE_FILLET * 2; 15 | 16 | module wheel( 17 | diameter = WHEEL_DIAMETER, 18 | height = WHEEL_HEIGHT, 19 | ring = WHEEL_RING, 20 | 21 | hub_ceiling = ENCLOSURE_FLOOR_CEILING, 22 | hub_diameter = PTV09A_POT_ACTUATOR_DIAMETER + ENCLOSURE_INNER_WALL * 2, 23 | 24 | brodie_knob_diameter = WHEEL_RING, 25 | brodie_knob_stilt = 0, 26 | brodie_knob_count = 1, 27 | brodie_knob_angle_offset = 0, 28 | 29 | dimple_count = 0, 30 | dimple_depth = 1, 31 | dimple_y = undef, 32 | 33 | spokes_count = 6, 34 | spokes_width = 2, 35 | spokes_height = WHEEL_HEIGHT * .5, 36 | 37 | chamfer = ENCLOSURE_INNER_WALL - BREAKAWAY_SUPPORT_DEPTH, 38 | shim_size = .6, 39 | shim_count = 5, 40 | 41 | shaft_type = POT_SHAFT_TYPE_DEFAULT, 42 | 43 | grip_count = undef, 44 | 45 | test_fit = false, 46 | 47 | color = undef, 48 | cavity_color = undef, 49 | 50 | tolerance = DEFAULT_TOLERANCE, 51 | $fn = DEFAULT_ROUNDING 52 | ) { 53 | e = 0.043; 54 | 55 | grip_count = grip_count != undef 56 | ? grip_count 57 | : round( 58 | diameter * PI / (DEFAULT_RIB_LENGTH + DEFAULT_RIB_GUTTER) 59 | ); 60 | 61 | module _hub() { 62 | if (!test_fit) { 63 | translate([0, 0, height - ring / 2]) { 64 | hull() { 65 | donut( 66 | diameter = hub_diameter, 67 | thickness = ring, 68 | segments = $preview ? 24 : 36 69 | ); 70 | } 71 | } 72 | } 73 | 74 | cylinder( 75 | d = hub_diameter, 76 | h = height - ring / 2 77 | ); 78 | } 79 | 80 | module _tire() { 81 | module _donuts() { 82 | for (z = [ring / 2, height - ring / 2]) { 83 | translate([0, 0, z]) { 84 | donut( 85 | diameter = diameter, 86 | thickness = ring, 87 | segments = $preview ? 24 : 36 88 | ); 89 | } 90 | } 91 | } 92 | 93 | difference() { 94 | union() { 95 | if (spokes_count > 0) { 96 | _donuts(); 97 | } else { 98 | hull() { 99 | _donuts(); 100 | } 101 | } 102 | 103 | translate([0, 0, ring / 2]) { 104 | ring( 105 | diameter = diameter, 106 | height = height - ring, 107 | thickness = ring 108 | ); 109 | } 110 | } 111 | 112 | cylinder_grip( 113 | diameter = diameter, 114 | height = height, 115 | count = grip_count, 116 | size = .8, 117 | $fn = FIXED_LODEF_ROUNDING 118 | ); 119 | } 120 | } 121 | 122 | module _pot_cavity() { 123 | module _chamfer() { 124 | translate([0, 0, -e]) { 125 | cylinder( 126 | d1 = PTV09A_POT_ACTUATOR_DIAMETER + tolerance * 2 127 | + chamfer * 2, 128 | d2 = PTV09A_POT_ACTUATOR_DIAMETER + tolerance * 2 129 | - PTV09A_POT_ACTUATOR_D_SHAFT_DEPTH * 2, 130 | h = chamfer + PTV09A_POT_ACTUATOR_D_SHAFT_DEPTH + e 131 | ); 132 | } 133 | } 134 | 135 | module _grips() { 136 | cylinder_grip( 137 | diameter = PTV09A_POT_ACTUATOR_DIAMETER + tolerance * 2, 138 | height = height - hub_ceiling, 139 | count = shim_count, 140 | rotation_offset = 180, 141 | size = shim_size 142 | ); 143 | } 144 | 145 | // Cavity is full available height, regardless of actual usage 146 | pot_cavity_z = height - hub_ceiling 147 | - PTV09A_POT_BASE_HEIGHT - PTV09A_POT_ACTUATOR_HEIGHT; 148 | 149 | _chamfer(); 150 | difference() { 151 | translate([0, 0, pot_cavity_z]) { 152 | pot( 153 | show_base = false, 154 | diameter_bleed = tolerance, 155 | shaft_type = shaft_type, 156 | $fn = HIDEF_ROUNDING 157 | ); 158 | } 159 | 160 | _grips(); 161 | } 162 | } 163 | 164 | module _spokes() { 165 | overlap = ring / 2; 166 | 167 | x = spokes_width / -2; 168 | y = hub_diameter / 2 - overlap; 169 | 170 | length = diameter / 2 - y - ring + overlap; 171 | 172 | for (i = [0 : spokes_count - 1]) { 173 | rotate([0, 0, (i / spokes_count) * 360]) { 174 | translate([x, y, 0]) { 175 | cube([spokes_width, length, spokes_height]); 176 | } 177 | } 178 | } 179 | } 180 | 181 | module _brodie_knobs() { 182 | for (i = [0 : brodie_knob_count - 1]) { 183 | rotation = brodie_knob_angle_offset + i * (360 / brodie_knob_count); 184 | 185 | rotate([0, 0, rotation]) { 186 | translate([0, diameter / 2 - brodie_knob_diameter / 2, 0]) { 187 | cylinder( 188 | h = height + brodie_knob_stilt, 189 | d1 = 0, 190 | d2 = brodie_knob_diameter 191 | ); 192 | 193 | translate([0, 0, height + brodie_knob_stilt]) { 194 | sphere( 195 | d = brodie_knob_diameter 196 | ); 197 | } 198 | } 199 | } 200 | } 201 | } 202 | 203 | module _dimple_cavities(dimple_diameter = diameter / 3) { 204 | assert( 205 | brodie_knob_count == 0, 206 | "Dimples and brodie knobs can't be used together. Set brodie_knob_count to 0." 207 | ); 208 | 209 | assert( 210 | spokes_count == 0, 211 | "Dimples and spokes can't be used together. Set spokes_count to 0." 212 | ); 213 | 214 | for (i = [0 : dimple_count - 1]) { 215 | y = dimple_y != undef 216 | ? dimple_y 217 | : diameter / 2 - dimple_diameter / 2 - ring / 2; 218 | rotation = i * (360 / dimple_count); 219 | 220 | rotate([0, 0, rotation]) { 221 | translate([0, y, height - dimple_depth]) { 222 | cylinder( 223 | h = dimple_depth + e, 224 | d = dimple_diameter 225 | ); 226 | } 227 | } 228 | } 229 | } 230 | 231 | difference() { 232 | color(color) { 233 | union() { 234 | if (spokes_count > 0) { 235 | _hub(); 236 | } 237 | 238 | if (!test_fit) { 239 | _tire(); 240 | 241 | if (spokes_count > 0) { 242 | _spokes(); 243 | } 244 | 245 | if (brodie_knob_count > 0) { 246 | _brodie_knobs(); 247 | } 248 | } 249 | } 250 | } 251 | 252 | color(cavity_color) { 253 | _pot_cavity(); 254 | 255 | if (dimple_count > 0) { 256 | _dimple_cavities(); 257 | } 258 | } 259 | } 260 | } 261 | 262 | function slice(list, start = 0, end) = 263 | end == 0 264 | ? [] 265 | : [for (i = [start : (end == undef ? len(list) : end) - 1]) list[i]] 266 | ; 267 | 268 | module wheels( 269 | diameter = WHEEL_DIAMETER, 270 | height = WHEEL_HEIGHT, 271 | shaft_type = POT_SHAFT_TYPE_DEFAULT, 272 | y = 0, 273 | z = 0, 274 | test_fit = false, 275 | count = undef 276 | ) { 277 | e = .04321; 278 | 279 | for (xy = slice(PCB_POT_POSITIONS, 0, count)) { 280 | translate([ 281 | ENCLOSURE_WALL + ENCLOSURE_INTERNAL_GUTTER + xy.x, 282 | y + xy.y, 283 | z 284 | ]) { 285 | wheel( 286 | shaft_type = shaft_type, 287 | test_fit = test_fit 288 | ); 289 | }; 290 | } 291 | } 292 | 293 | // shim .5 * 3 is still good 294 | // .6 * 3 could be good w/ bigger chamfer 295 | // .6 * 4 feels even better 296 | // 5 shims is whatever and 6 is too many 297 | // .8 is too big, regardless 298 | 299 | // all of these work 300 | /* shim_sizes = [.5, .6]; 301 | shim_counts = [3, 5]; 302 | 303 | plot = 7.8; 304 | 305 | for (i = [0 : len(shim_sizes) - 1]) { 306 | for (ii = [0 : len(shim_counts) - 1]) { 307 | shim_size = shim_sizes[i]; 308 | shim_count = shim_counts[ii]; 309 | 310 | is_needle = shim_size == .5 && shim_count == 3; 311 | 312 | translate([i * plot, ii * plot, 0]) { 313 | color(is_needle ? "red" : undef) { 314 | wheel( 315 | height = 8, 316 | shim_size = shim_size, 317 | shim_count = shim_count, 318 | test_fit = true, 319 | shaft_type = POT_SHAFT_TYPE_SPLINED, 320 | chamfer = .8 321 | ); 322 | } 323 | } 324 | } 325 | } */ 326 | -------------------------------------------------------------------------------- /print/inserts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/print/inserts.pdf -------------------------------------------------------------------------------- /print/inserts.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 25 | 31 | 37 | 43 | 49 | 55 | 61 | 67 | 73 | 79 | 85 | 91 | 97 | 103 | 109 | 115 | 121 | 127 | 133 | 139 | 145 | 151 | 157 | 163 | 169 | 175 | 181 | 187 | 193 | 199 | 205 | 211 | 217 | 223 | 229 | 235 | 241 | 247 | 253 | 259 | 265 | 271 | 277 | 283 | 289 | 290 | 312 | 319 | 326 | 333 | 340 | 347 | 354 | 361 | 368 | 375 | 382 | 383 | 385 | 386 | 388 | image/svg+xml 389 | 391 | 392 | 393 | 394 | 395 | 399 | Thank you and congrats on your new APC from Oskitone! 414 | 418 | Find the assembly instructions online at oskitone.com/guides 428 | 439 | Thank you and congrats on your new APC from Oskitone! 454 | 458 | Find the assembly instructions online at oskitone.com/guides 468 | Thank you and congrats on your new APC from Oskitone! 483 | 487 | Find the assembly instructions online at oskitone.com/guides 497 | Thank you and congrats on your new APC from Oskitone! 512 | 516 | Find the assembly instructions online at oskitone.com/guides 526 | Thank you and congrats on your new APC from Oskitone! 541 | 545 | Find the assembly instructions online at oskitone.com/guides 555 | Thank you and congrats on your new APC from Oskitone! 570 | 574 | Find the assembly instructions online at oskitone.com/guides 584 | Thank you and congrats on your new APC from Oskitone! 599 | 603 | Find the assembly instructions online at oskitone.com/guides 613 | Thank you and congrats on your new APC from Oskitone! 628 | 632 | Find the assembly instructions online at oskitone.com/guides 642 | Thank you and congrats on your new APC from Oskitone! 657 | 661 | Find the assembly instructions online at oskitone.com/guides 671 | Thank you and congrats on your new APC from Oskitone! 686 | 690 | Find the assembly instructions online at oskitone.com/guides 700 | Thank you and congrats on your new APC from Oskitone! 715 | 719 | Find the assembly instructions online at oskitone.com/guides 729 | Thank you and congrats on your new APC from Oskitone! 744 | 748 | Find the assembly instructions online at oskitone.com/guides 758 | 759 | 760 | -------------------------------------------------------------------------------- /print/label_inserts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskitone/apc/4bfc36b0f834bbb9172182348f5815221b57c65a/print/label_inserts.pdf -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # APC 2 | 3 | [![APC](apc-10-60-838-450-32.gif)](apc-10-60-838-450-32.gif) 4 | 5 | A decapitated robot head? An extraterrestrial communicator? A specter summoner? 6 | 7 | Nope (and yep!), it's an [APC](https://en.wikipedia.org/wiki/Atari_Punk_Console)! 8 | 9 | **Assembly Guide:** [https://oskitone.github.io/apc/](https://oskitone.github.io/apc/)
10 | **Demo:** [https://vimeo.com/518375593](https://vimeo.com/518375593)
11 | **Purchase:** [APC (fully assembled)](https://www.oskitone.com/product/apc), [APC DIY Electronics Kit](https://www.oskitone.com/product/apc-diy-electronics-kit)
12 | **Blog post:** [https://blog.tommy.sh/posts/oskitone-makes-an-atari-punk-console/](https://blog.tommy.sh/posts/oskitone-makes-an-atari-punk-console/) 13 | 14 | ## KiCad Source 15 | 16 | The PCB and its schematic are actually part of [poly555](https://github.com/oskitone/poly555), so head over there to its KiCad project to dig deeper into the electronics. (That the APC is a part of a bigger project may also help to explain why the component numbers start at 100!) 17 | 18 | ## 3D Models 19 | 20 | The APC's 3D-printed models are written in OpenSCAD. 21 | 22 | ### Changelog 23 | 24 | - **September 6, 2021:** Add brim to switch_clutch for better DFM (2d0a1d0), Loosen wheel fit (7e2dab7) 25 | - **February 25, 2021:** Init (8df1dc4) 26 | 27 | ### Dependencies 28 | 29 | Assumes poly555 repo is in a sibling directory. Here's how I've got it: 30 | 31 | \ oskitone 32 | \ apc 33 | \ poly555 34 | 35 | ### Mods 36 | 37 | - For pots with flatted D shafts, use `shaft_type = POT_SHAFT_TYPE_FLATTED` in the call to `wheels()` for a better fit. 38 | 39 | ### Building 40 | 41 | STLs are generated with `make_stls.sh`. Run `./make_stls.sh -h` for full flags list. 42 | 43 | ## License 44 | 45 | Designed by Oskitone. Please support future synth projects by purchasing from [Oskitone](https://www.oskitone.com/). 46 | 47 | Creative Commons Attribution/Share-Alike, all text above must be included in any redistribution. See license.txt for additional details. 48 | --------------------------------------------------------------------------------