Add files

This commit is contained in:
likeablob 2019-06-10 02:39:04 +09:00
parent 682e21c48c
commit 8d3458a207
51 changed files with 112102 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.wav

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "openscad/scad-utils"]
path = openscad/scad-utils
url = https://github.com/OskarLinde/scad-utils

View File

@ -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
View 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
View 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
View File

@ -0,0 +1,6 @@
Section "Device"
Identifier "myfb"
Driver "fbdev"
Option "fbdev" "/dev/fb0"
EndSection

9
config/asound.conf Normal file
View File

@ -0,0 +1,9 @@
pcm.!default {
type hw
card 0
}
ctl.!default {
type hw
card 0
}

1
config/fbtft.conf Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
images/mm1_sd.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

BIN
images/mm2_sd.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

BIN
images/mm3_sd.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

BIN
images/mm_s_10.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

BIN
images/mm_s_14.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 KiB

BIN
images/mm_s_20.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 KiB

BIN
images/mm_s_23.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 KiB

BIN
images/mm_s_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 KiB

BIN
images/mm_s_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

BIN
images/mm_s_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 KiB

BIN
images/mm_s_8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 KiB

BIN
images/mm_s_9.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

32
install.sh Executable file
View 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
View File

@ -0,0 +1,7 @@
*.dsk
*.DSK
*.zip
*.ZIP
*.rom
*.ROM

BIN
minivmac/minivmac.320 Executable file

Binary file not shown.

BIN
minivmac/minivmac.512 Executable file

Binary file not shown.

31
openscad/Makefile Normal file
View 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
View 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
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
openscad/images/main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
openscad/images/models.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View 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);
}

View File

@ -0,0 +1,3 @@
include <macintosh-mini-base.scad>
partA();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
include <macintosh-mini-base.scad>
partB();

File diff suppressed because it is too large Load Diff

1
openscad/scad-utils Submodule

@ -0,0 +1 @@
Subproject commit 484d39bc2fd19cf6903c8e92e01af40a77276e66

24
switcher/.eslintrc.js Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

23
switcher/package.json Normal file
View 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"
}
}

View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
xterm

View 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
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
chromium-browser --kiosk --app=http://simpleclock.surge.sh