Add files
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.wav
|
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "openscad/scad-utils"]
|
||||
path = openscad/scad-utils
|
||||
url = https://github.com/OskarLinde/scad-utils
|
99
README.md
@ -0,0 +1,99 @@
|
||||
<p align="center">
|
||||
<img align="center" width="300px" height="auto" src="./images/mm2_sd.gif">
|
||||
</p>
|
||||
|
||||
# Mac(intosh) mini
|
||||
|
||||
## Build Instruction
|
||||
- Print the outer shell.
|
||||
- PLA, 0.2 mm, 180 ℃ (See also [./openscad/README.md](./openscad/README.md))
|
||||
- `macintosh-mini-part-a.stl` ([stl](./openscad/macintosh-mini-part-a.stl))
|
||||
- `macintosh-mini-part-b.stl` ([stl](./openscad/macintosh-mini-part-b.stl))
|
||||
|
||||
<img align="center" width="60%" height="auto" src="./openscad/images/models.png">
|
||||
|
||||
- Setup your Orange Pi Zero Plus 2 (H3)
|
||||
- Install Armbian Bionic. https://www.armbian.com/orange-pi-zero-plus-2-h3/
|
||||
- Then SSH into the device.
|
||||
```bash
|
||||
$ sudo apt install -y git xtightvncserver
|
||||
|
||||
# Install Node.js
|
||||
$ git clone https://github.com/tj/n /tmp/n && cd /tmp/n && sudo make install
|
||||
$ sudo n v10
|
||||
|
||||
# Install https://github.com/xpertsavenue/WiringOP-Zero
|
||||
$ git clone https://github.com/xpertsavenue/WiringOP-Zero
|
||||
## Please refer to https://github.com/xpertsavenue/WiringOP-Zero
|
||||
|
||||
# Setup vncserver
|
||||
$ mkdir .vnc
|
||||
$ vncpasswd .vnc/passwd
|
||||
|
||||
# Install the dependencies & config files
|
||||
$ git clone https://github.com/likeablob/macmini ~/macmini
|
||||
$ cd ~/macmini
|
||||
$ sudo ./install.sh
|
||||
|
||||
# copy ypur assests
|
||||
$ cp /path/to/vmac.rom ~/macmini/minivmac/vmac.rom # Mini vMac ROM file
|
||||
$ cp /path/to/vmac.dsk ~/macmini/minivmac/system.dsk # Mini vMac disk file
|
||||
$ cp /path/to/.basilisk_ii_prefs ~/.basilisk_ii_prefs # Basilisk II config file
|
||||
$ cp /path/to/macboot.wav ~/macmini/macboot.wav # Boot sound (optional)
|
||||
```
|
||||
- [Tips]: Here is my working [.basilisk_ii_prefs](basilisk_ii_prefs.md).
|
||||
|
||||
- Wire the display & USB boards to the OPi
|
||||
|
||||
<img align="center" width="auto" height="400px" src="./images/mm_s_8.jpg">
|
||||
|
||||
| OPi | Peripherals |
|
||||
| -------------- | -------------------- |
|
||||
| PA15/SPI1_MOSI | LCD MOSI |
|
||||
| PA16/SPI1_MISO | LCD MISO |
|
||||
| PA14/SPI1_CLK | LCD CLK |
|
||||
| PA13/SPI1_CS | LCD CS |
|
||||
| PS12 | LCD RESET |
|
||||
| PA11 | LCD DC |
|
||||
| PA06 | Push Switch |
|
||||
| USB-DP2 | USB (Front) Data+ |
|
||||
| USB-DM2 | USB (Front) Data- |
|
||||
| 5V | USB (Front/Rear) 5V |
|
||||
| GND | USB (Front/Rear) GND |
|
||||
|
||||
## Basic Concepts
|
||||
1. At system boot, [`fbtft_device`](./config/fbtft.conf) kernel module is loaded and it initializes the LCD as `/dev/fb0`.
|
||||
2. `nodm` starts an X session and `~/.xinitrc` get executed.
|
||||
3. `~/.xinitrc` launchs `matchbox-window-manager` and `~/macmini/switcher/*js`.
|
||||
4. `~/macmini/switcher/index.js` reads the scripts [`~/macmini/switcher/sh.d/*sh`](./switcher/sh.d/) and spawns them one by one when there is a HTTP GET request at `localhost:5501/`.
|
||||
5. `~/macmini/switcher/gpio-reader.js` watchs the push SW at GPIO7(PA06) and send a request to `localhost:5501/`.
|
||||
|
||||
### How does the low-res LCD(320x240) handle 512x384 resolution?
|
||||
Internally `xthighvncserver` and `ssvncviewer` are used to scale the display.
|
||||
- e.g. [`1_large_minimac.sh`]((./switcher/sh.d/1_large_minimac.sh)) launches:
|
||||
- `xthighvncserver` to create a virtual display (512x384) at `DISPLAY=:1` (`:5901`)
|
||||
- `minivmac` in `DISPLAY:1`
|
||||
- `ssvncviewer -scale 0.625` in `DISPLAY:0` (By the `-scale` option, low-res LED get upscaled)
|
||||
## Gallery
|
||||
| <img align="center" width="auto" height="200px" src="./images/mm_s_5.jpg"> | <img align="center" width="auto" height="200px" src="./images/mm_s_7.jpg"> |
|
||||
| :-------------------------------------------------------------------------: | :-------------------------------------------------------------------------: |
|
||||
| <img align="center" width="auto" height="200px" src="./images/mm_s_20.jpg"> | <img align="center" width="auto" height="200px" src="./images/mm_s_14.jpg"> |
|
||||
| <img align="center" width="auto" height="200px" src="./images/mm_s_10.jpg"> | <img align="center" width="auto" height="200px" src="./images/mm_s_23.jpg"> |
|
||||
| <img align="center" width="auto" height="200px" src="./images/mm_s_9.jpg"> | <img align="center" width="auto" height="200px" src="./images/mm3_sd.gif"> |
|
||||
|
||||
|
||||
## BOM
|
||||
| Name | Quantity | Descriptions |
|
||||
| ------------------------------------------------------- | :------: | ----------------------- |
|
||||
| OrangePi Zero Plus 2 (H3) | 1 | H5 ver. might work too. |
|
||||
| 2.4inch SPI Display (ili9340, 320x240, with flat cable) | 1 | |
|
||||
| Generic micro-USB Breakout Board | 2 | |
|
||||
| M2 x 6 mm Self Tapping Screw | 16 | |
|
||||
| D10mm Rounding Magnet | 2 | |
|
||||
| 7x7 mm Push Switch | 1 | |
|
||||
|
||||
## Acknowledgment
|
||||
- Mini vMac https://www.gryphel.com/c/minivmac/
|
||||
- Basilisk II https://basilisk.cebix.net/
|
||||
- Kudos to all the devs!s
|
||||
|
37
basilisk_ii_prefs.md
Normal file
@ -0,0 +1,37 @@
|
||||
~/.basilisk_ii_prefs
|
||||
```bash
|
||||
disk ${/path/to/disk1.dsk}
|
||||
disk ${/path/to/disk2.dsk}
|
||||
extfs ${/path/to/your/home/dir}
|
||||
screen win/512/384
|
||||
seriala /dev/ttyS5
|
||||
serialb /dev/ttyS6
|
||||
ether slirp
|
||||
udptunnel false
|
||||
udpport 6066
|
||||
rom ${/path/to/mac.rom}
|
||||
bootdrive 0
|
||||
bootdriver 0
|
||||
ramsize 33554432
|
||||
frameskip 0
|
||||
modelid 5
|
||||
cpu 3
|
||||
fpu false
|
||||
nocdrom true
|
||||
nosound false
|
||||
noclipconversion false
|
||||
nogui false
|
||||
jit true
|
||||
jitfpu true
|
||||
jitdebug false
|
||||
jitcachesize 2048
|
||||
jitlazyflush true
|
||||
jitinline true
|
||||
keyboardtype 5
|
||||
keycodes false
|
||||
mousewheelmode 1
|
||||
mousewheellines 3
|
||||
dsp /dev/dsp
|
||||
mixer /dev/mixer
|
||||
idlewait true
|
||||
```
|
12
config/.xinitrc
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
MACMINI_DIR=~/macmini
|
||||
xloadimage ${MACMINI_DIR}/images/macface.png -fullscreen -onroot
|
||||
[ -e ${MACMINI_DIR}/macboot.wav ] && mplayer -af volume=10 ${MACMINI_DIR}/macboot.wav &
|
||||
|
||||
if [[ $DISPLAY == :0 ]];then
|
||||
(cd ${MACMINI_DIR}/switcher && padsp npm start)&
|
||||
(cd ${MACMINI_DIR}/switcher && gpio mode 7 up && npm run start:gpio)&
|
||||
fi
|
||||
|
||||
matchbox-window-manager -use_titlebar no
|
6
config/99-fbdev.conf
Normal file
@ -0,0 +1,6 @@
|
||||
Section "Device"
|
||||
Identifier "myfb"
|
||||
Driver "fbdev"
|
||||
Option "fbdev" "/dev/fb0"
|
||||
EndSection
|
||||
|
9
config/asound.conf
Normal file
@ -0,0 +1,9 @@
|
||||
pcm.!default {
|
||||
type hw
|
||||
card 0
|
||||
}
|
||||
|
||||
ctl.!default {
|
||||
type hw
|
||||
card 0
|
||||
}
|
1
config/fbtft.conf
Normal file
@ -0,0 +1 @@
|
||||
options fbtft_device custom name=fb_ili9340 gpios=dc:11,reset:12 speed=64000000 busnum=1 rotate=270 bgr=1
|
14
config/rc.local
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh -e
|
||||
#
|
||||
# rc.local
|
||||
#
|
||||
# This script is executed at the end of each multiuser runlevel.
|
||||
# Make sure that the script will "exit 0" on success or any other
|
||||
# value on error.
|
||||
#
|
||||
# In order to enable or disable this script just change the execution
|
||||
# bits.
|
||||
#
|
||||
# By default this script does nothing.
|
||||
|
||||
echo none | tee /sys/class/leds/orangepi\:red\:status/trigger # disable LED
|
5
config/xstartup
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Fix to make GNOME work
|
||||
export XKL_XMODMAP_DISABLE=1
|
||||
/etc/X11/Xsession
|
BIN
images/macface.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
images/mm1_sd.gif
Normal file
After Width: | Height: | Size: 3.9 MiB |
BIN
images/mm2_sd.gif
Normal file
After Width: | Height: | Size: 3.7 MiB |
BIN
images/mm3_sd.gif
Normal file
After Width: | Height: | Size: 3.9 MiB |
BIN
images/mm_s_10.jpg
Normal file
After Width: | Height: | Size: 652 KiB |
BIN
images/mm_s_14.jpg
Normal file
After Width: | Height: | Size: 720 KiB |
BIN
images/mm_s_20.jpg
Normal file
After Width: | Height: | Size: 709 KiB |
BIN
images/mm_s_23.jpg
Normal file
After Width: | Height: | Size: 612 KiB |
BIN
images/mm_s_5.jpg
Normal file
After Width: | Height: | Size: 669 KiB |
BIN
images/mm_s_6.jpg
Normal file
After Width: | Height: | Size: 654 KiB |
BIN
images/mm_s_7.jpg
Normal file
After Width: | Height: | Size: 671 KiB |
BIN
images/mm_s_8.jpg
Normal file
After Width: | Height: | Size: 684 KiB |
BIN
images/mm_s_9.jpg
Normal file
After Width: | Height: | Size: 623 KiB |
32
install.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#! /bin/bash
|
||||
|
||||
[ $(id -u) -gt 0 ] && echo Please run as root! && exit 1
|
||||
|
||||
set +x
|
||||
|
||||
apt install -y -q \
|
||||
bluez \
|
||||
tightvncserver \
|
||||
ssvnc \
|
||||
mplayer \
|
||||
chromium-browser \
|
||||
basilisk2 \
|
||||
matchbox-window-manager \
|
||||
nodm \
|
||||
xserver-xorg-input-libinput \
|
||||
xloadimage \
|
||||
pulseaudio \
|
||||
pulseaudio-esound-compat \
|
||||
pulseaudio-utils \
|
||||
pavucontrol
|
||||
|
||||
cp -a config/.xinitrc ~/.xinitrc
|
||||
cp -a config/xstartup ~/.vnc/
|
||||
cp config/99-fbdev.conf /etc/X11/xorg.conf.d/
|
||||
cp config/fbtft.conf /etc/modprobe.d/
|
||||
cp config/asound.conf /etc/
|
||||
cp config/rc.local /etc/
|
||||
|
||||
cd switcher && npm install
|
||||
|
||||
echo Done!
|
7
minivmac/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
*.dsk
|
||||
*.DSK
|
||||
*.zip
|
||||
*.ZIP
|
||||
*.rom
|
||||
*.ROM
|
||||
|
BIN
minivmac/minivmac.320
Executable file
BIN
minivmac/minivmac.512
Executable file
31
openscad/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
OPENSCAD := openscad-nightly
|
||||
MONTAGE := montage
|
||||
targets := $(wildcard macintosh-mini-part*.scad)
|
||||
stls := $(targets:.scad=.stl)
|
||||
image_dir := images
|
||||
thumbnails := $(targets:%.scad=${image_dir}/%_s.png)
|
||||
img_models := ${image_dir}/models.png
|
||||
|
||||
.PHONY: all clean images
|
||||
all: ${stls}
|
||||
@echo done
|
||||
|
||||
${stls}: %.stl: %.scad
|
||||
@echo Building $@ from $<
|
||||
${OPENSCAD} -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f ${stls}
|
||||
|
||||
images: $(thumbnails) ${img_models}
|
||||
@echo done
|
||||
|
||||
$(thumbnails): ${image_dir}/%_s.png: %.scad
|
||||
@echo Generating $@ from $<
|
||||
${OPENSCAD} -o $@ \
|
||||
--imgsize=640,480 --colorscheme=Tomorrow \
|
||||
--projection o --camera -200,-200,280,0,0,40 $<
|
||||
|
||||
${img_models}: ${thumbnails}
|
||||
@echo Generating $@ from $^
|
||||
${MONTAGE} -label '%t' -geometry 320x240 $(sort $^) $@
|
21
openscad/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
<p align="center">
|
||||
<img align="center" width="80%" height="auto" src="./images/main.png">
|
||||
</p>
|
||||
|
||||
## Print conditions
|
||||
- PLA, 0.2 mm, 180 ℃
|
||||
- `macintosh-mini-part-a`: Rotate so that the -X(front) side is facing down.
|
||||
|
||||
<img align="center" width="80%" height="auto" src="./images/models.png">
|
||||
|
||||
|
||||
## Dependencies
|
||||
- [OskarLinde]'s [scad-utils](https://github.com/OskarLinde/scad-utils)
|
||||
- openscad-nightly (`20181009T230242.gitf3de0e27+2416.1`)
|
||||
|
||||
## Build
|
||||
```sh
|
||||
$ git submodule update --init --recursive # or git clone --recursive
|
||||
$ make -j4
|
||||
```
|
||||
|
BIN
openscad/images/macintosh-mini-part-a_s.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
openscad/images/macintosh-mini-part-b_s.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
openscad/images/main.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
openscad/images/models.png
Normal file
After Width: | Height: | Size: 127 KiB |
717
openscad/macintosh-mini-base.scad
Normal file
@ -0,0 +1,717 @@
|
||||
$fn=40;
|
||||
include <mylib.scad>
|
||||
include <scad-utils/morphology.scad>
|
||||
|
||||
WH_RATIO = 22.7/29.2;
|
||||
DH_RATIO = 46.0/63.4;
|
||||
|
||||
BODY_Z = 90;
|
||||
BODY_X = BODY_Z * WH_RATIO;
|
||||
BODY_Y = BODY_Z * DH_RATIO;
|
||||
|
||||
module copy_mirror(vec=[0,1,0]){
|
||||
children();
|
||||
mirror(vec) children();
|
||||
}
|
||||
|
||||
module base(mscale=1) {
|
||||
difference() {
|
||||
hull() {
|
||||
// upper
|
||||
translate([0, 0, (BODY_X)/2 + (BODY_Z-BODY_X)/2])
|
||||
rotate([0, 5, 0])
|
||||
cube(size=[BODY_X, BODY_Y, BODY_X], center=true);
|
||||
|
||||
// lower
|
||||
translate([0, 0, (BODY_Z-BODY_X)/2])
|
||||
cube(size=[BODY_X, BODY_Y, BODY_Z-BODY_X], center=true);
|
||||
|
||||
// display
|
||||
rotate([0, 5, 0])
|
||||
translate([-BODY_X/2-6, 0, (BODY_Z-BODY_X)/2-1])
|
||||
translate([0, 0, BODY_X/2])
|
||||
cube(size=[0.1, BODY_Y*0.97, BODY_X*0.93], center=true);
|
||||
}
|
||||
|
||||
// under the display
|
||||
minkowski() {
|
||||
UD_Z = (BODY_Z-BODY_X)/2*0.6;
|
||||
translate([-BODY_Y/2-10/2, 0, UD_Z-1.5])
|
||||
hull() {
|
||||
rotate([0, 5, 0])
|
||||
cube(size=[BODY_Y*0.2+10, BODY_Y+1, UD_Z*2], center=true);
|
||||
|
||||
/* translate([BODY_Y*0.2/2+8, 0, -UD_Z])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d=0.01, h=BODY_Y+1, center=true); */
|
||||
translate([BODY_Y*0.2/2+1, 0, -UD_Z])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d=0.01, h=BODY_Y+1, center=true);
|
||||
}
|
||||
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d=1, h=10, center=true);
|
||||
}
|
||||
|
||||
// top mizo
|
||||
translate([BODY_X*0.4, 0, BODY_Z-BODY_Z*0.27])
|
||||
rotate([0, 5, 0])
|
||||
scale([mscale, mscale, mscale])
|
||||
linear_extrude(height=BODY_Z*0.3, scale=1.1, center=false, convexity=10, twist=0)
|
||||
square(size=[BODY_X, BODY_Y/2], center=true);
|
||||
|
||||
// back edge
|
||||
translate([BODY_Y, 0, BODY_X/2 + BODY_Z*0.5])
|
||||
rotate([0, 60, 0])
|
||||
cube(size=[BODY_X, BODY_Y+3, BODY_X], center=true);
|
||||
|
||||
// back
|
||||
translate([BODY_X/2+BODY_X*0.1/2-1, 0, (BODY_Z)/2])
|
||||
cube(size=[BODY_X*0.1, BODY_Y*1.2, BODY_Z], center=true);
|
||||
|
||||
// side edge
|
||||
*copy_mirror([0,1,0])
|
||||
translate([-BODY_X/2, BODY_Y/2, 0])
|
||||
translate([0, 0, BODY_Z/2*1.1])
|
||||
rotate([0, 5, 0])
|
||||
rotate([0, 0, 60])
|
||||
cube(size=[5, 5, BODY_Z], center=true);
|
||||
}
|
||||
}
|
||||
|
||||
THICK=-5;
|
||||
|
||||
// variables for partB
|
||||
OPIZ_X = 48;
|
||||
OPIZ_Y = 46;
|
||||
OPIZ_Z = 10;
|
||||
|
||||
OPI_HOLE_POS = 2.8;
|
||||
|
||||
HDMI_Z=5.5;
|
||||
HDMI_Y=15;
|
||||
|
||||
OPIZ_HLD_Z = BODY_Z*0.6;
|
||||
OPIZ_HLD_Y = 5;
|
||||
OPIZ_HLD_X = 10;
|
||||
|
||||
OPIZ_OFFSET_Z = 6;
|
||||
OPIZ_OFFSET_X = -1.5;
|
||||
|
||||
BACKPANEL_X = 1.0;
|
||||
|
||||
module partB() {
|
||||
difference() {
|
||||
union() {
|
||||
difference() {
|
||||
intersection(){
|
||||
difference(){
|
||||
minkowski() {
|
||||
base();
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d=5, h=0.01, center=true);
|
||||
}
|
||||
// inner space
|
||||
difference() {
|
||||
translate([-0.5, 0, 0])
|
||||
scale([0.95, 1, 1])
|
||||
resize([BODY_X-THICK, 0, 0], auto=[false, true, true])
|
||||
base(1.15);
|
||||
|
||||
translate([BODY_X/2+10/2-3, 0, 0])
|
||||
cube(size=[10, BODY_Y*2, BODY_Z*2], center=true);
|
||||
}
|
||||
|
||||
copy_mirror([0,1,0])
|
||||
translate([BODY_X/2-10, BODY_Y*0.91/2, BODY_Z*0.68])
|
||||
rotate([0, 90, 0]){
|
||||
screw(2);
|
||||
translate([0, 0, 8.5])
|
||||
screw(4);
|
||||
}
|
||||
|
||||
copy_mirror([0,1,0])
|
||||
translate([BODY_X/2-10, BODY_Y*0.91/2, BODY_Z*0.1])
|
||||
rotate([0, 90, 0]){
|
||||
screw(2);
|
||||
translate([0, 0, 8.5])
|
||||
screw(4);
|
||||
}
|
||||
|
||||
copy_mirror([0,1,0])
|
||||
translate([-BODY_X*0.2, BODY_Y*0.91/2, BODY_Z*0.1/3])
|
||||
union(){
|
||||
screwB(3);
|
||||
translate([0, 0, -15])
|
||||
screwB(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// partB
|
||||
|
||||
union() {
|
||||
H2 = (BODY_Z-BODY_X);
|
||||
Y=BODY_Y-THICK-4-1;
|
||||
// front
|
||||
hull() {
|
||||
translate([-(BODY_X/2 + BODY_Y*0.2)/2-1, 0, H2/2-2])
|
||||
cube(size=[1, Y+8, 2], center=true);
|
||||
|
||||
translate([-(BODY_X/2 + BODY_Y*0.2)/2-1.3, 0, -2])
|
||||
cube(size=[2, Y+8, 2], center=true);
|
||||
|
||||
translate([-(BODY_X/2)/2, 0, -4])
|
||||
cube(size=[2, Y+8, 2], center=true);
|
||||
}
|
||||
|
||||
// bottom
|
||||
cube(size=[BODY_X, Y, BODY_X*0.1], center=true);
|
||||
|
||||
// back
|
||||
translate([(BODY_X/2), 0, BODY_Z*0.75/2-1-0.3])
|
||||
cube(size=[BODY_X*0.2, Y, BODY_Z*0.70], center=true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// hdmi
|
||||
color("red")
|
||||
translate([BODY_X/2-OPIZ_X/2-3, BODY_Y/3.5, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0])
|
||||
translate([OPIZ_X/2+3, OPIZ_Y/2-HDMI_Y/2-6.5, 2.5])
|
||||
hdmi(0.3);
|
||||
|
||||
// micro usb for power
|
||||
translate([BODY_X/2-USBM_BOARD[0]/2+OPIZ_OFFSET_X-1.1, -BODY_Y/4, 2])
|
||||
rotate([0, 0, 180])
|
||||
usbm_board(0.2);
|
||||
|
||||
// micro usb for keyboards
|
||||
translate([-BODY_X*0.1-USBM_BOARD[0]/2-2, -BODY_Y/4,1])
|
||||
usbm_board(0.2);
|
||||
|
||||
// switch
|
||||
translate([BODY_X/2, -BODY_Y/4, BODY_Z*0.6])
|
||||
rotate([0, 90, 0])
|
||||
linear_extrude(height=10, center=true, convexity=10, twist=0)
|
||||
circle(d=4);
|
||||
|
||||
} // diff
|
||||
|
||||
// opi holder
|
||||
translate([OPIZ_OFFSET_X, 0, 0])
|
||||
difference() {
|
||||
union() {
|
||||
translate([BODY_X/2-OPIZ_HLD_X/2-1, OPIZ_HLD_Y/2 + BODY_Y/3.5 + 1, OPIZ_HLD_Z/2])
|
||||
cube(size=[OPIZ_HLD_X, OPIZ_HLD_Y, OPIZ_HLD_Z], center=true);
|
||||
|
||||
translate([BODY_X/2+OPIZ_HLD_X/2-5-OPIZ_X, OPIZ_HLD_Y/2 + BODY_Y/3.5 + 1, OPIZ_HLD_Z/4.5/2])
|
||||
cube(size=[OPIZ_HLD_X, OPIZ_HLD_Y, OPIZ_HLD_Z/4], center=true);
|
||||
|
||||
// screw base A
|
||||
translate([BODY_X/2-OPIZ_X/2-3, BODY_Y/3.5, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0])
|
||||
mirror_y()
|
||||
translate([OPIZ_X/2-OPI_HOLE_POS, OPIZ_Y/2-OPI_HOLE_POS, -1])
|
||||
cylinder(d=6, h=1, d2=5, center=!true);
|
||||
|
||||
// screw base B
|
||||
translate([BODY_X/2-OPIZ_X/2-3, BODY_Y/3.5, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0])
|
||||
mirror([1, 0, 0])
|
||||
mirror([0, 1, 0])
|
||||
translate([OPIZ_X/2-OPI_HOLE_POS, OPIZ_Y/2-OPI_HOLE_POS, -1])
|
||||
cylinder(d=6, h=1, d2=5, center=!true);
|
||||
}
|
||||
|
||||
// screws A
|
||||
translate([BODY_X/2-OPIZ_X/2-3, BODY_Y/3.5, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0])
|
||||
mirror_y()
|
||||
translate([OPIZ_X/2-OPI_HOLE_POS, OPIZ_Y/2-OPI_HOLE_POS, -1])
|
||||
cylinder(d=2, h=10, center=true);
|
||||
|
||||
// screws B
|
||||
translate([BODY_X/2-OPIZ_X/2-3, BODY_Y/3.5, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0])
|
||||
mirror([1, 0, 0])
|
||||
mirror([0, 1, 0])
|
||||
translate([OPIZ_X/2-OPI_HOLE_POS, OPIZ_Y/2-OPI_HOLE_POS, -1])
|
||||
cylinder(d=2, h=10, center=true);
|
||||
|
||||
}
|
||||
|
||||
// micro usb for power - screws
|
||||
translate([BODY_X/2-USBM_BOARD[0]/2+OPIZ_OFFSET_X-1.1, -BODY_Y/4, 0])
|
||||
rotate([0, 0, 180])
|
||||
usbm_board_screws(d=8, h=2, center=false);
|
||||
|
||||
// micro usb for keyboards - screws
|
||||
translate([-BODY_X*0.1-USBM_BOARD[0]/2-2, -BODY_Y/4, 0])
|
||||
usbm_board_screws(d=8, h=1, center=false);
|
||||
|
||||
// magnets - holder
|
||||
mirror_y()
|
||||
translate([7, BODY_Y/2.6, 0])
|
||||
cylinder(d=12, h=4, center=!true);
|
||||
} // union
|
||||
|
||||
// micro usb for power
|
||||
translate([BODY_X/2-USBM_BOARD[0]/2+OPIZ_OFFSET_X-1.1, -BODY_Y/4, -2.2])
|
||||
rotate([0, 0, 180])
|
||||
usbm_board_screws(d=2, h=5, center=false);
|
||||
|
||||
// micro usb for keyboards
|
||||
translate([-BODY_X*0.1-USBM_BOARD[0]/2-2, -BODY_Y/4, -2.2])
|
||||
usbm_board_screws(d=2, h=5, center=false);
|
||||
|
||||
// magnets - holder
|
||||
mirror_y()
|
||||
translate([7, BODY_Y/2.6, -2.6]) {
|
||||
cylinder(d=11, h=3+0.01, center=!true);
|
||||
translate([0, 0, 3])
|
||||
cylinder(d=11, d2=3, h=2, center=!true);
|
||||
}
|
||||
|
||||
// magnets - holder
|
||||
mirror_y()
|
||||
translate([7, BODY_Y/2.6, -2.6]) {
|
||||
cylinder(d=1.5, h=10, center=!true);
|
||||
}
|
||||
|
||||
// back panel A
|
||||
color("red")
|
||||
translate([BODY_X/2+10/2-1-BACKPANEL_X, BODY_Y/3.5-5, OPIZ_Y/2+14])
|
||||
rotate([0, 90, 0])
|
||||
linear_extrude(height=10, center=true, convexity=10, twist=0)
|
||||
rounding(0.8)
|
||||
square(size=[35, 8], center=true);
|
||||
|
||||
// back panel B
|
||||
color("red")
|
||||
translate([BODY_X/2+10/2-1-BACKPANEL_X, 0, 2])
|
||||
rotate([0, 90, 0])
|
||||
linear_extrude(height=10, center=true, convexity=10, twist=0, scale=1.2)
|
||||
rounding(0.8)
|
||||
square(size=[20, 60], center=true);
|
||||
|
||||
// back panel C
|
||||
color("red")
|
||||
translate([BODY_X/2+10/2-1.5, -5, BODY_Z*0.35])
|
||||
rotate([0, 90, 0])
|
||||
linear_extrude(height=10, center=true, convexity=10, twist=0)
|
||||
rounding(0.8)
|
||||
square(size=[35, 25], center=true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
module pport(y=10, z=3) {
|
||||
rotate([0, 90, 0])
|
||||
linear_extrude(height=1.0, center=true, convexity=10, twist=0)
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
rounding(0.8)
|
||||
hull() {
|
||||
square(size=[0.1, y], center=true);
|
||||
translate([z, 0, 0])
|
||||
square(size=[0.1, y*0.85], center=true);
|
||||
}
|
||||
mirror_y()
|
||||
translate([z/2, y/2+0.5, 0])
|
||||
circle(d=1.9);
|
||||
}
|
||||
for (i=[-y/3:y/3]) {
|
||||
translate([z/3, i*1.2, 0])
|
||||
circle(d=1);
|
||||
}
|
||||
for (i=[-y/4:y/4+1]) {
|
||||
translate([z/1.5, i*1.2-1.2/2, 0])
|
||||
circle(d=1);
|
||||
}
|
||||
mirror_y()
|
||||
translate([z/2, y/2+0.5, 0])
|
||||
circle(d=1);
|
||||
}
|
||||
}
|
||||
|
||||
// parallel port
|
||||
color("green")
|
||||
translate([BODY_X/2-1-BACKPANEL_X, 1, 7])
|
||||
pport(14, 4);
|
||||
|
||||
// ps/2 port
|
||||
color("green")
|
||||
translate([BODY_X/2-1-BACKPANEL_X, BODY_Y/4, 5])
|
||||
rotate([0, 90, 0])
|
||||
linear_extrude(height=1, center=true, convexity=10, twist=0)
|
||||
union() {
|
||||
shell(0.3)
|
||||
circle(d=3);
|
||||
translate([-3/2+1/2, 0, 0])
|
||||
square(size=[1, 1], center=true);
|
||||
}
|
||||
|
||||
// ps/2 port
|
||||
color("green")
|
||||
translate([BODY_X/2-1-BACKPANEL_X, BODY_Y/4+6, 5])
|
||||
rotate([0, 90, 0])
|
||||
linear_extrude(height=1.0, center=true, convexity=10, twist=0)
|
||||
union() {
|
||||
shell(0.3)
|
||||
circle(d=3);
|
||||
translate([-3/2+1/2, 0, 0])
|
||||
square(size=[1, 1], center=true);
|
||||
}
|
||||
|
||||
// display holder
|
||||
difference() {
|
||||
hull() {
|
||||
translate([-(BODY_X-THICK)*0.9/2+3, 0, BODY_Z/1.6])
|
||||
rotate([0, 5, 0])
|
||||
cube(size=[1, 15, 15], center=true);
|
||||
|
||||
translate([-BODY_X*0.1, 0, 0])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d=1, h=15, center=true);
|
||||
|
||||
translate([-BODY_X*0.27, 0, 0])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d=1, h=12, center=true);
|
||||
}
|
||||
|
||||
translate([2, 0, 0])
|
||||
scale([1, 0.7, 1])
|
||||
hull() {
|
||||
translate([-(BODY_X-THICK)*0.9/2+3, 0, BODY_Z/1.6])
|
||||
rotate([0, 5, 0])
|
||||
cube(size=[1, 15, 15], center=true);
|
||||
|
||||
translate([-BODY_X*0.1, 0, 0])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d=1, h=15, center=true);
|
||||
|
||||
translate([-BODY_X*0.27, 0, 0])
|
||||
rotate([90, 0, 0])
|
||||
cylinder(d=1, h=12, center=true);
|
||||
}
|
||||
|
||||
// screw base
|
||||
translate([BODY_X/2-OPIZ_X/2-3 + OPIZ_OFFSET_X, 0, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0])
|
||||
mirror([1, 0, 0])
|
||||
mirror([0, 1, 0])
|
||||
translate([OPIZ_X/2-OPI_HOLE_POS, OPIZ_Y/2-OPI_HOLE_POS, -1])
|
||||
cylinder(d=6, h=20, center=true);
|
||||
|
||||
// pin header
|
||||
translate([BODY_X/2-OPIZ_X/2-3 + OPIZ_OFFSET_X, 0, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0])
|
||||
translate([-OPIZ_X/2+33/2+7.7, -OPIZ_Y/2+5/2, -1])
|
||||
cube(size=[33, 5, 20], center=true);
|
||||
}
|
||||
|
||||
// switch holder
|
||||
translate([BODY_X/2-10/2-3, -BODY_Y/4, BODY_Z*0.6-5.5])
|
||||
difference() {
|
||||
cube(size=[10, 5, 15], center=true);
|
||||
|
||||
translate([0, 0, -10])
|
||||
rotate([0, -60, 0])
|
||||
cube(size=[10, 5+1, 25], center=true);
|
||||
|
||||
translate([-6/2+10/2, 0, 15-5])
|
||||
cube(size=[6, 5+1, 15], center=true);
|
||||
}
|
||||
}
|
||||
|
||||
module hdmi(offset=0.1) {
|
||||
translate([0, 0, HDMI_Z]) {
|
||||
rotate([-90, 0, 90])
|
||||
linear_extrude(height=10, center=!true, convexity=10, twist=0)
|
||||
outset(offset)
|
||||
hull() {
|
||||
translate([0, 4/2, 0])
|
||||
square(size=[HDMI_Y, 4], center=true);
|
||||
translate([0, HDMI_Z/2, 0])
|
||||
square(size=[11, HDMI_Z], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// opi
|
||||
%translate([BODY_X/2-OPIZ_X/2-3+OPIZ_OFFSET_X, BODY_Y/3.5, OPIZ_Y/2 + OPIZ_OFFSET_Z])
|
||||
rotate([90, 0, 0]){
|
||||
translate([0, 0, OPIZ_Z/2])
|
||||
cube(size=[OPIZ_X, OPIZ_Y, OPIZ_Z], center=true);
|
||||
|
||||
//screw
|
||||
mirror_x()
|
||||
mirror_y()
|
||||
translate([OPIZ_X/2-OPI_HOLE_POS, OPIZ_Y/2-OPI_HOLE_POS, 0])
|
||||
cylinder(d=3, h=10, center=true);
|
||||
|
||||
// hdmi
|
||||
translate([OPIZ_X/2+3, OPIZ_Y/2-HDMI_Y/2-6.5, 2])
|
||||
hdmi();
|
||||
}
|
||||
|
||||
// partA
|
||||
module partA() {
|
||||
difference() {
|
||||
minkowski() {
|
||||
base();
|
||||
rotate([0, 95, 0])
|
||||
cylinder(d=5, h=0.01, center=true);
|
||||
}
|
||||
|
||||
// display
|
||||
translate([0, 0, BODY_X/2 + (BODY_Z-BODY_X)/2*1.8])
|
||||
rotate([0, 5, 0])
|
||||
hull() {
|
||||
DISP_Y = 72.6;
|
||||
translate([-BODY_X/2-4, 0, 0])
|
||||
cube(size=[0.1, DISP_Y*0.9, DISP_Y*0.9*0.75], center=true);
|
||||
|
||||
translate([-30, 0, 0])
|
||||
sphere(d=20);
|
||||
}
|
||||
|
||||
// inner space
|
||||
scale([0.9, 1, 1])
|
||||
resize([BODY_X-THICK, 0, 0], auto=[false, true, true])
|
||||
base(1.15);
|
||||
|
||||
// floppy
|
||||
floppy();
|
||||
|
||||
// partB
|
||||
union() {
|
||||
H2 = (BODY_Z-BODY_X);
|
||||
Y=BODY_Y-THICK-4;
|
||||
// front
|
||||
hull() {
|
||||
translate([-(BODY_X/2 + BODY_Y*0.2)/2-1, 0, H2/2-2])
|
||||
cube(size=[2, Y+8, 2], center=true);
|
||||
|
||||
translate([-(BODY_X/2 + BODY_Y*0.2)/2-1.2, 0, -2])
|
||||
cube(size=[2, Y+8, 2], center=true);
|
||||
|
||||
translate([-(BODY_X/2)/2, 0, -4])
|
||||
cube(size=[2, Y+8, 2], center=true);
|
||||
}
|
||||
|
||||
// bottom
|
||||
cube(size=[BODY_X, Y, BODY_X*0.1], center=true);
|
||||
|
||||
// back
|
||||
translate([(BODY_X/2), 0, BODY_Z*0.75/2-1])
|
||||
cube(size=[BODY_X*0.2, Y, BODY_Z*0.70], center=true);
|
||||
}
|
||||
|
||||
// air intake
|
||||
copy_mirror([0,1,0])
|
||||
translate([BODY_X/2-BODY_X*0.2/2, BODY_Y/2-BODY_Y/8.5, BODY_Z-BODY_Z*0.141])
|
||||
for (i=[0:4]) {
|
||||
translate([0, 0, -i*0.6*3])
|
||||
cube(size=[BODY_X*0.2, BODY_Y/5, 0.9], center=true);
|
||||
}
|
||||
|
||||
// air intake
|
||||
translate([-BODY_X*0.1, 0, BODY_Z-BODY_Z*0.175])
|
||||
for (i=[-4:4]) {
|
||||
translate([0, -i*0.8*3, 0])
|
||||
rotate([90, 0, 0])
|
||||
cube(size=[BODY_X*0.2, BODY_Y/12, 0.8], center=true);
|
||||
}
|
||||
|
||||
/* translate([-BODY_X/2+4, 0, BODY_Z/2*0.88])
|
||||
rotate([0, 5, 0])
|
||||
rotate([90, 0, 90])
|
||||
linear_extrude(height=0.5, center=true, convexity=10, twist=0)
|
||||
shell(d=1)
|
||||
square(size=[BODY_Y+4, BODY_Z], center=true); */
|
||||
|
||||
}
|
||||
|
||||
|
||||
// top cover
|
||||
translate([-3, 0, BODY_Z-BODY_Z*0.105])
|
||||
union() {
|
||||
rotate([0, 5, 0])
|
||||
cube(size=[BODY_X*0.18, BODY_Y/2, 2], center=true);
|
||||
|
||||
translate([0, 0, -1])
|
||||
rotate([0, 5, 0])
|
||||
hull() {
|
||||
cube(size=[BODY_X*0.18, BODY_Y/3, 0.01], center=true);
|
||||
translate([0, 0, -2])
|
||||
cube(size=[BODY_X*0.18, BODY_Y/4, 0.01], center=true);
|
||||
}
|
||||
}
|
||||
|
||||
copy_mirror([0,1,0])
|
||||
translate([BODY_X/2-10-3.5, BODY_Y*0.91/2, BODY_Z*0.68])
|
||||
rotate([0, 90, 0])
|
||||
screwBase();
|
||||
|
||||
copy_mirror([0,1,0])
|
||||
translate([BODY_X/2-10-3.5, BODY_Y*0.91/2, BODY_Z*0.1])
|
||||
rotate([0, 90, 0])
|
||||
screwBase();
|
||||
|
||||
copy_mirror([0,1,0])
|
||||
translate([-BODY_X*0.2, BODY_Y*0.91/2, BODY_Z*0.1/3])
|
||||
screwBaseB();
|
||||
}
|
||||
|
||||
|
||||
USBM_BOARD = [15, 14.3, 1.5];
|
||||
module usbm_board(soffset=0.1) {
|
||||
difference() {
|
||||
translate([0, 0, USBM_BOARD[2]/2])
|
||||
cube(size=USBM_BOARD, center=true);
|
||||
|
||||
// holes
|
||||
mirror_y()
|
||||
translate([USBM_BOARD[0]/2-5.5, USBM_BOARD[1]/2-2.5, 0])
|
||||
cylinder(d=3, h=10, center=true);
|
||||
}
|
||||
|
||||
// socket
|
||||
translate([0, 0, USBM_BOARD[2]+0.3])
|
||||
rotate([90, 0, -90])
|
||||
linear_extrude(height=10, center=!true, convexity=10, twist=0)
|
||||
offset(soffset)
|
||||
hull() {
|
||||
translate([0, 2.7, 0])
|
||||
square(size=[6.8, 0.1], center=true);
|
||||
translate([0, 1.4, 0])
|
||||
square(size=[7.8, 0.1], center=true);
|
||||
square(size=[5.3, 0.1], center=true);
|
||||
}
|
||||
}
|
||||
|
||||
module usbm_board_screws(d=3, h=10, center=true) {
|
||||
USBM_BOARD = [15, 14.3, 1.5];
|
||||
|
||||
// holes
|
||||
mirror_y()
|
||||
translate([USBM_BOARD[0]/2-5.5, USBM_BOARD[1]/2-2.5, 0])
|
||||
cylinder(d=d, h=h, center=center);
|
||||
}
|
||||
|
||||
module floppy(args) {
|
||||
// body...
|
||||
module floppy_baseA() {
|
||||
square(size=[BODY_Y/2.5, BODY_Z*0.01], center=true);
|
||||
}
|
||||
module floppy_baseB() {
|
||||
translate([-BODY_Y/2.5/2+BODY_Y/9/2, -BODY_Z*0.01, 0])
|
||||
square(size=[BODY_Y/9, BODY_Z*0.03], center=true);
|
||||
}
|
||||
module floppy_baseC() {
|
||||
/* offset(r=2) */
|
||||
translate([-BODY_Y/2.5/2+BODY_Y/9/2, BODY_Z*0.01, 0])
|
||||
square(size=[BODY_Y/9, BODY_Z*0.02], center=true);
|
||||
}
|
||||
module floppy_baseD() {
|
||||
translate([-BODY_Y/2.5/2+BODY_Y/9/2, 0, 0])
|
||||
square(size=[BODY_Y/9, BODY_Z*0.02*2], center=true);
|
||||
}
|
||||
|
||||
FLOPPY_Z = BODY_Z*0.26;
|
||||
|
||||
translate([-BODY_X/2, -BODY_Y*0.2, FLOPPY_Z])
|
||||
rotate([0, 5, 0])
|
||||
rotate([90, 0, 90])
|
||||
linear_extrude(height=7, center=true, convexity=10, twist=0){
|
||||
floppy_baseA();
|
||||
floppy_baseB();
|
||||
}
|
||||
|
||||
translate([-BODY_X/2-3, -BODY_Y*0.2, FLOPPY_Z])
|
||||
rotate([0, 5, 0])
|
||||
rotate([90, 0, 90])
|
||||
offset(r=2)
|
||||
linear_extrude(height=5, center=true, convexity=10, twist=0)
|
||||
floppy_baseC();
|
||||
|
||||
// front
|
||||
translate([-BODY_X/2-2, -BODY_Y*0.2, FLOPPY_Z])
|
||||
rotate([0, 5, 0])
|
||||
rotate([90, 0, 90]){
|
||||
hull() {
|
||||
linear_extrude(height=1, center=true, convexity=10, twist=0)
|
||||
floppy_baseA();
|
||||
|
||||
translate([0, 0, -2])
|
||||
linear_extrude(height=1, center=true, convexity=10, twist=0, scale=1.1)
|
||||
offset(delta=1)
|
||||
offset(r=0.1)
|
||||
floppy_baseA();
|
||||
}
|
||||
hull() {
|
||||
linear_extrude(height=1, center=true, convexity=10, twist=0)
|
||||
offset(r=0.1)
|
||||
floppy_baseD();
|
||||
|
||||
translate([0, 0, -2])
|
||||
linear_extrude(height=1, center=true, convexity=10, twist=0, scale=1.1)
|
||||
offset(delta=1)
|
||||
offset(r=0.1)
|
||||
floppy_baseD();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module screwBase() {
|
||||
XY = 8;
|
||||
Z = 10;
|
||||
|
||||
difference() {
|
||||
translate([0, 0, Z/2])
|
||||
cube(size=[XY, XY, 10], center=true);
|
||||
|
||||
cylinder(d=2, h=Z+1, center=!true);
|
||||
|
||||
rotate([-40, 0, 0])
|
||||
translate([0, 0, -2])
|
||||
cube(size=[XY*2, XY*3, XY], center=true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module screwBaseB() {
|
||||
XY = 8;
|
||||
Z = 10;
|
||||
translate([0, 0, -XY/2])
|
||||
difference() {
|
||||
translate([0, 0, Z/2])
|
||||
cube(size=[XY*2, XY, XY], center=true);
|
||||
|
||||
translate([XY/2, 0, 0])
|
||||
cylinder(d=2, h=Z+1, center=!true);
|
||||
|
||||
rotate([0, 0, -90-60])
|
||||
translate([XY, 0, Z/2])
|
||||
cube(size=[XY*2, XY*3, Z+1], center=true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module screwB(d=2) {
|
||||
XY = 8;
|
||||
Z = 10;
|
||||
translate([XY/2, 0, 0])
|
||||
cylinder(d=d, h=Z*2, center=true);
|
||||
}
|
||||
|
||||
module screw(d=2) {
|
||||
XY = 8;
|
||||
Z = 10;
|
||||
|
||||
cylinder(d=d, h=Z+1, center=!true);
|
||||
}
|
3
openscad/macintosh-mini-part-a.scad
Normal file
@ -0,0 +1,3 @@
|
||||
include <macintosh-mini-base.scad>
|
||||
|
||||
partA();
|
24754
openscad/macintosh-mini-part-a.stl
Normal file
3
openscad/macintosh-mini-part-b.scad
Normal file
@ -0,0 +1,3 @@
|
||||
include <macintosh-mini-base.scad>
|
||||
|
||||
partB();
|
84688
openscad/macintosh-mini-part-b.stl
Normal file
1
openscad/scad-utils
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 484d39bc2fd19cf6903c8e92e01af40a77276e66
|
24
switcher/.eslintrc.js
Normal file
@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
commonjs: true,
|
||||
es6: true,
|
||||
node: true
|
||||
},
|
||||
extends: ["eslint:recommended", "plugin:prettier/recommended"],
|
||||
globals: {
|
||||
Atomics: "readonly",
|
||||
SharedArrayBuffer: "readonly"
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018
|
||||
},
|
||||
rules: {
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
singleQuote: true,
|
||||
trailingComma: "es5"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
80
switcher/.gitignore
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
# Created by https://www.gitignore.io/api/node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
|
||||
# End of https://www.gitignore.io/api/node
|
17
switcher/gpio-reader.js
Normal file
@ -0,0 +1,17 @@
|
||||
const Gpio = require('orange-pi-gpio');
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
let gpio5 = new Gpio({pin:7, mode: 'in'});
|
||||
|
||||
prev = 0
|
||||
setInterval(function () {
|
||||
gpio5.read()
|
||||
.then((state)=>{
|
||||
state = parseInt(state)
|
||||
if (state !== prev && state === 0) {
|
||||
console.log("push");
|
||||
fetch("http://localhost:5501/").catch((err) => {console.log(err);})
|
||||
}
|
||||
prev = state
|
||||
});
|
||||
}, 100);
|
89
switcher/index.js
Normal file
@ -0,0 +1,89 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
const shPath = './sh.d';
|
||||
|
||||
class Runner {
|
||||
constructor() {
|
||||
this.files = fs
|
||||
.readdirSync(shPath)
|
||||
.filter(v => /.sh$/.test(v))
|
||||
.map(v => path.join(shPath, v));
|
||||
console.log('loaded scripts:', this.files);
|
||||
this.currentTarget = null;
|
||||
}
|
||||
psleep(ms) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
kill(sig = 'SIGINT') {
|
||||
try {
|
||||
process.kill(-this.currentTarget.pid, sig || 'SIGINT');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
spawn(target) {
|
||||
return new Promise(resolve => {
|
||||
const child = spawn(target, {
|
||||
sid: true,
|
||||
detached: true,
|
||||
});
|
||||
const tag = `${target}:`;
|
||||
child.stdout.on('data', data => {
|
||||
console.log(tag, data.toString().trim());
|
||||
});
|
||||
child.stderr.on('data', data => {
|
||||
console.log(tag, data.toString().trim());
|
||||
});
|
||||
child.on('close', code => {
|
||||
console.log(tag, 'close');
|
||||
resolve(code);
|
||||
});
|
||||
child.on('exit', code => {
|
||||
console.log(tag, 'exit');
|
||||
this.kill();
|
||||
resolve(code);
|
||||
});
|
||||
this.currentTarget = child;
|
||||
});
|
||||
}
|
||||
async runner() {
|
||||
let counter = 0;
|
||||
while (true) {
|
||||
const target = this.files[counter++ % this.files.length];
|
||||
console.log('running', target);
|
||||
await this.spawn(target).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
start() {
|
||||
this.runner();
|
||||
}
|
||||
}
|
||||
|
||||
const runner = new Runner();
|
||||
runner.start();
|
||||
|
||||
const onExit = () => {
|
||||
console.log('exitting');
|
||||
runner.kill();
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on('SIGTERM', onExit);
|
||||
process.on('SIGINT', onExit);
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
const sig = req.query.force ? 'SIGKILL' : null;
|
||||
runner.kill(sig);
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 5501;
|
||||
app.listen(port, 'localhost', () => console.log('listening on', port));
|
1390
switcher/package-lock.json
generated
Normal file
23
switcher/package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "mm-switcher",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"start:gpio": "node gpio-reader.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.16.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^6.5.1",
|
||||
"eslint-config-prettier": "^6.3.0",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"prettier": "^1.18.2"
|
||||
}
|
||||
}
|
8
switcher/sh.d/1_large_minimac.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
vncserver -kill :1
|
||||
vncserver -geometry 512x384
|
||||
ssvncviewer -scale 0.625 :1 -fullscreen -passwd ~/.vnc/passwd &
|
||||
DISPLAY=:1
|
||||
cd ~/macmini/minivmac
|
||||
./minivmac.512 vmac.rom system.dsk
|
8
switcher/sh.d/2_minimac.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
vncserver -kill :1
|
||||
vncserver -geometry 320x240
|
||||
ssvncviewer -scale 1 :1 -fullscreen -passwd ~/.vnc/passwd &
|
||||
DISPLAY=:1
|
||||
cd ~/macmini/minivmac
|
||||
./minivmac.320 vmac.rom system.dsk
|
6
switcher/sh.d/3_basilisk.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
vncserver -kill :1
|
||||
vncserver -geometry 512x384
|
||||
ssvncviewer -scale 0.625 :1 -fullscreen -passwd ~/.vnc/passwd &
|
||||
DISPLAY=:1 BasiliskII --nogui true
|
3
switcher/sh.d/4_terminal.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
xterm
|
7
switcher/sh.d/5_large_terminal.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
vncserver -kill :1
|
||||
vncserver -geometry 512x384
|
||||
ssvncviewer -scale 0.625 :1 -fullscreen -passwd ~/.vnc/passwd &
|
||||
DISPLAY=:1
|
||||
xterm
|
3
switcher/sh.d/6_clock.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
chromium-browser --kiosk --app=http://simpleclock.surge.sh
|