Compare commits
11 Commits
master
...
wifi-contr
Author | SHA1 | Date |
---|---|---|
yuno | 399a8fa5b7 | |
yuno | 9e27dc59fe | |
yuno | 894e6da897 | |
yuno | b4f16a0468 | |
yuno | 5115aa179a | |
yuno | db6216a6bc | |
yuno | a382866c4a | |
yuno | 38854b6122 | |
yuno | 691c6af0ef | |
yuno | 6549041895 | |
yuno | fadd940816 |
|
@ -0,0 +1,116 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
@font-face {font-family: 'ChicagoFont';src: url('https://dangasson.github.io/Resources/Fonts/ChicagoFLF.ttf') format('truetype');font-weight: normal;font-style: normal;}.centered {top: 50%;left: 50%;transform: translate(-50%, -50%);}.container {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}.ipod {width: 415px;height: 692px;border: 1px solid transparent;border-radius: 38px;background: linear-gradient(45deg, #E3E4E5, #FFFFFF);box-shadow: inset 5px -5px 15px 0px grey;}.ipod.small {transform: scale(0.67, 0.67);}.screen {position: relative;margin: 30px auto 0px auto;width: 284px;height: 230px;background: linear-gradient(135deg, #A5A59B, #D6D5D0);border-radius: 10px;box-shadow: inset 0px 0px 10px 2px #4D4D4D;font-family: "ChicagoFont", "Arial";font-size: 130%;color: #484647;}.title {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}.title-bar {position: absolute;left: 5px;right: 5px;height: 16%;border-bottom: 2px solid #484647;text-align: center;}.play-icon {position: absolute;display: inline-block;top: 50%;left: 10px;transform: translate(0, -50%);border-left: 18px solid #484647;border-top: 9px solid transparent;border-bottom: 9px solid transparent;height: 0;width: 0;}.battery {position: absolute;right: 5px;height: 50px;width: 100px;background-color: #C1C1BA;border: 5px solid #484647;}.battery:before {content: '';position: absolute;top: 50%;right: -12px;transform: translate(0, -50%);height: 33%;width: 7px;background-color: #C1C1BA;border-right: 5px solid #484647;border-top: 5px solid #484647;border-bottom: 5px solid #484647;}.battery:after {content: '';position: absolute;top: 5px;bottom: 5px;left: 5px;width: 70px;background: repeating-linear-gradient(to right, #484647, #484647 20px, #C1C1BA 20px, #C1C1BA 25px, #484647 25px, #484647 45px, #C1C1BA 45px, #C1C1BA 50px, #484647 50px, #484647 70px);}.battery.small {transform: scale(0.3, 0.3);transform-origin: 100% 25%;}.menu-options {display: flex;flex-direction: column;justify-content: space-around;position: relative;top: 17%;height: 81%;}.option {padding-left: 12px;}.option:after {content: '';position: absolute;right: 18px;border-bottom: 3px solid #484647;border-right: 3px solid #484647;width: 8px;height: 8px;transform: rotate(-45deg) skew(7deg, 7deg);transform-origin: 180%;}.option.selected {background-color: #484647;border-bottom: 2px solid #484647;color: #C1C1BA;}.option.selected:after {border-bottom: 3px solid #C1C1BA;border-right: 3px solid #C1C1BA;}.outer-ring {position: relative;margin: 0 auto;top: 30px;height: 350px;width: 350px;border-radius: 50%;background-color: #FFFFFF;box-shadow: inset 5px -5px 30px -7px #595959;}.outer-ring:before, .outer-ring:after {content: '';position: absolute;width: 0;height: 99%;border: 1px solid #4D4D4D;}.outer-ring:before {left: 50%;transform: rotate(45deg);}.outer-ring:after {top: 0;left: 50%;transform: rotate(135deg);}.touch-wheel {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);height: 276px;width: 276px;border: 2px solid #4D4D4D;border-radius: 50%;background: radial-gradient(farthest-side at 90% -70%, #999999, #F2F2F2);box-shadow: 5px -5px 30px -7px #595959;z-index: 1;}.center-button {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);height: 100px;width: 100px;border: 2px solid #BABDC1;border-radius: 50%;background-color: #CBCCCE;background: radial-gradient(farthest-side at -90% 80%, #999999, #F2F2F2);}text {font-family: "Arial";font-size: 110%;font-weight: bold;fill: #BABDC1;}path {fill: transparent;}.skip {position: absolute;top: 50%;transform: translate(0, -50%);background-color: #BABDC1;height: 12px;width: 4px;}.skip:before, .skip:after {content: '';position: absolute;border-left: 9px solid #BABDC1;border-top: 6px solid transparent;border-bottom: 6px solid transparent;}.skip:before {left: -9px;}.skip:after {left: -18px;}.skip.forward {right: 7px;}.skip.back {left: 7px;transform: rotate(180deg) translate(0, 50%);}.play-pause {position: absolute;bottom: 12px;left: 50%;height: 0;width: 0;transform: translate(-13px, 0);border-left: 12px solid #BABDC1;border-top: 6px solid transparent;border-bottom: 6px solid transparent;}.play-pause:before {content: '';position: absolute;bottom: -6px;left: 4px;height: 12px;width: 10px;background: repeating-linear-gradient(to right, #BABDC1, #BABDC1 4px, transparent 4px, transparent 6px, #BABDC1 6px, #BABDC1 10px);}_:-ms-lang(x), .ipod {box-shadow: inset 5px -5px 25px 3px #999999;}_:-ms-lang(x), .screen {box-shadow: inset 0px 0px 20px 1px #595959;}_:-ms-lang(x), .outer-ring {box-shadow: inset 5px -5px 50px -7px #999999;}_:-ms-lang(x), .touch-wheel {box-shadow: 5px -5px 50px -7px #999999;}
|
||||
</style>
|
||||
<script>
|
||||
function action(name) {
|
||||
fetch("/?pod="+name)
|
||||
}
|
||||
|
||||
function Touchwheel(selector) {
|
||||
this.elem = document.querySelector(selector);
|
||||
|
||||
this.calc_pos = (ev) => {
|
||||
let w = ev.target.clientWidth;
|
||||
let h = ev.target.clientHeight;
|
||||
let x = ev.offsetX / w - 0.5;
|
||||
let y = ev.offsetY / h - 0.5;
|
||||
let d = (Math.atan2(y, x) * (180 / Math.PI)) + 90;
|
||||
return d;
|
||||
}
|
||||
|
||||
this.last_pos = null;
|
||||
this.last_seg = 0;
|
||||
this.down_time = null;
|
||||
this.mousedown = (ev) => {
|
||||
if(ev.target != this.elem) return;
|
||||
this.last_pos = this.calc_pos(ev);
|
||||
this.down_time = ev.timeStamp;
|
||||
}
|
||||
|
||||
this.mouseup = (ev) => {
|
||||
if(ev.target != this.elem) return;
|
||||
this.last_pos = null;
|
||||
|
||||
if(ev.type == "mouseup") {
|
||||
let dt = ev.timeStamp - this.down_time;
|
||||
if(dt < 100) {
|
||||
console.log("clicked");
|
||||
if(ev.offsetY / ev.target.clientHeight < 0.5) {
|
||||
action("scrollup")
|
||||
} else {
|
||||
action("scrolldown")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.mousemove = (ev) => {
|
||||
if(ev.target != this.elem) return;
|
||||
if(this.last_pos === null) return;
|
||||
|
||||
let pos = this.calc_pos(ev) - this.last_pos;
|
||||
let seg = Math.round(pos / 16);
|
||||
|
||||
if(seg != this.last_seg) {
|
||||
console.log(seg);
|
||||
if(seg > this.last_seg) {
|
||||
action("volup");
|
||||
} else if(seg < this.last_seg) {
|
||||
action("voldown");
|
||||
}
|
||||
this.last_seg = seg;
|
||||
}
|
||||
}
|
||||
|
||||
this.elem.onmousedown = this.mousedown;
|
||||
this.elem.onmousemove = this.mousemove;
|
||||
this.elem.onmouseup = this.mouseup;
|
||||
this.elem.onmouseleave = this.mouseup;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="ipod small">
|
||||
<div class="screen">
|
||||
<div class="title-bar">
|
||||
<div class="play-icon"></div>
|
||||
<div class="title">iPod</div>
|
||||
<div class="battery small"></div>
|
||||
</div>
|
||||
<div class="menu-options">
|
||||
<div class="option">Playlists</div>
|
||||
<div class="option">Artists</div>
|
||||
<div class="option selected">Songs</div>
|
||||
<div class="option">Settings</div>
|
||||
<div class="option">About</div>
|
||||
<div class="option">Now Playing</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="outer-ring">
|
||||
<svg viewBox="-150 5 350 350">
|
||||
<path id="curve" d="m0,30 c16,-4 32,-4 48,0" />
|
||||
<text onclick="action('menu')">
|
||||
<textPath xlink:href="#curve">menu</textPath>
|
||||
</text>
|
||||
</svg>
|
||||
<div class="skip forward" onclick="action('next')"></div>
|
||||
<div class="skip back" onclick="action('prev')"></div>
|
||||
<div class="play-pause" onclick="action('play')"></div>
|
||||
<div class="touch-wheel">
|
||||
<div class="center-button" onclick="action('ok')"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const touchwheel = new Touchwheel(".touch-wheel");
|
||||
</script>
|
||||
<a href="/config.html">Config
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,236 @@
|
|||
from machine import Pin
|
||||
from machine import UART
|
||||
import network
|
||||
import time
|
||||
import socket
|
||||
# config option
|
||||
|
||||
network.hostname("opendocks.local")
|
||||
ui = "index.html"
|
||||
config = "config.html"
|
||||
port = 80 #dont change this shit brakes
|
||||
|
||||
# ssid and pass
|
||||
ssid = 'kainet'
|
||||
password = 'peepeepoopoo'
|
||||
|
||||
page = open(ui, "r")
|
||||
html = page.read()
|
||||
|
||||
# aap commands start here
|
||||
class Modes:
|
||||
Switch = 0x00
|
||||
Voice = 0x01
|
||||
Simple = 0x02
|
||||
Request = 0x03
|
||||
AiR = 0x04
|
||||
|
||||
|
||||
_test_sw_mode_simple = [0xFF, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA]
|
||||
_test_button_up = [0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB]
|
||||
_test_play = _test_sw_mode_simple + [0xFF, 0x55, 0x03, 0x02, 0x00, 0x01, 0xFA] + _test_button_up
|
||||
|
||||
# generate a checksum for a request
|
||||
def req_checksum(length, mode, command, parameter):
|
||||
return 0x100 - ((length + mode + sum(int(a) for a in command) + sum(int(a) for a in parameter)) & 0xff)
|
||||
|
||||
def make_request(mode: int, command: list[int], parameter=[]):
|
||||
length = 1+len(command)+len(parameter)
|
||||
ret = []
|
||||
|
||||
if mode != Modes.Switch: # add mode switch
|
||||
ret += make_request(0, [0x01, mode])
|
||||
|
||||
ret += [0xFF, 0x55, length, mode] + command + parameter + [req_checksum(length, mode, command, parameter)]
|
||||
|
||||
if mode == Modes.Simple: # add button up
|
||||
ret += [0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB] # button up
|
||||
return ret
|
||||
|
||||
class Requests:
|
||||
#ButtonReleased = make_request(Modes.Simple, [0x0, 0x0])
|
||||
PlayToggle = make_request(Modes.Simple, [0x0, 0x01])
|
||||
VolUp = make_request(Modes.Simple, [0x0, 0x02])
|
||||
VolDown = make_request(Modes.Simple, [0x0, 0x04])
|
||||
Next = make_request(Modes.Simple, [0x0, 0x08])
|
||||
Prev = make_request(Modes.Simple, [0x0, 0x10])
|
||||
NextAlbum = make_request(Modes.Simple, [0x0, 0x20])
|
||||
PrevAlbum = make_request(Modes.Simple, [0x0, 0x40])
|
||||
Stop = make_request(Modes.Simple, [0x0, 0x80])
|
||||
Play = make_request(Modes.Simple, [0x0, 0x0, 0x01])
|
||||
Pause = make_request(Modes.Simple, [0x0, 0x0, 0x02])
|
||||
MuteToggle = make_request(Modes.Simple, [0x0, 0x0, 0x04])
|
||||
NextPlaylist = make_request(Modes.Simple, [0x0, 0x0, 0x20])
|
||||
PrevPlaylist = make_request(Modes.Simple, [0x0, 0x0, 0x40])
|
||||
ShuffleToggle = make_request(Modes.Simple, [0x0, 0x0, 0x80])
|
||||
RepeatToggle = make_request(Modes.Simple, [0x0, 0x0, 0x0, 0x01])
|
||||
Off = make_request(Modes.Simple, [0x0, 0x0, 0x0, 0x04])
|
||||
On = make_request(Modes.Simple, [0x0, 0x0, 0x0, 0x08])
|
||||
Menu = make_request(Modes.Simple, [0x0, 0x0, 0x0, 0x40])
|
||||
Ok = make_request(Modes.Simple, [0x0, 0x0, 0x0, 0x80])
|
||||
ScrollUp = make_request(Modes.Simple, [0x0, 0x0, 0x0, 0x0, 0x01])
|
||||
ScrollDown = make_request(Modes.Simple, [0x0, 0x0, 0x0, 0x0, 0x02])
|
||||
|
||||
assert Requests.PlayToggle == _test_play, "invalid make_request"
|
||||
|
||||
# aap commands end here
|
||||
|
||||
"""
|
||||
class AiRRequests:
|
||||
GetIpodName = make_request(Modes.AiR, [0x0, 0x14])
|
||||
|
||||
def read_response(uart):
|
||||
head = uart.read(2 + 1)
|
||||
length = int(head[2])
|
||||
packet = uart.read(length)
|
||||
mode = packet[0]
|
||||
command = [int(a) for a in packet[1:3]]
|
||||
param = packet[3:-1]
|
||||
return command, param
|
||||
|
||||
class AiR:
|
||||
@staticmethod
|
||||
def GetIpodName(uart):
|
||||
uart.write(bytes(AiRRequests.GetIpodName))
|
||||
command, param = read_response(uart)
|
||||
assert command == [0x0, 0x15]
|
||||
assert len(param) == 1
|
||||
name = uart.read(int.from_bytes(param, 'little'))
|
||||
return name.decode("utf8")
|
||||
"""
|
||||
|
||||
class IPOD:
|
||||
def __init__(self, uart) -> None:
|
||||
self.uart = uart
|
||||
|
||||
def _cmd(self, cmd: list[int]):
|
||||
self.uart.write(bytes(cmd))
|
||||
|
||||
def play(self): self._cmd(Requests.PlayToggle)
|
||||
def next(self): self._cmd(Requests.Next)
|
||||
def prev(self): self._cmd(Requests.Prev)
|
||||
def menu(self): self._cmd(Requests.Menu)
|
||||
def ok(self): self._cmd(Requests.Ok)
|
||||
def volup(self): self._cmd(Requests.VolUp)
|
||||
def voldown(self): self._cmd(Requests.VolDown)
|
||||
def scrollup(self): self._cmd(Requests.ScrollUp)
|
||||
def scrolldown(self): self._cmd(Requests.ScrollDown)
|
||||
|
||||
def init():
|
||||
|
||||
|
||||
led = Pin("LED", Pin.OUT, value=1)
|
||||
|
||||
# network.hostname("opendock.local")
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
wlan.config(pm = 0xa11140) # Diable powersave mode
|
||||
wlan.connect(ssid, password)
|
||||
name = wlan.config("hostname")
|
||||
# wlan.ifconfig(('192.168.1.69', '255.255.255.0', '192.168.1.5', '192.168.1.5'))
|
||||
|
||||
print("hostname is: " + name)
|
||||
|
||||
def blink_led(frequency = 0.5, num_blinks = 3):
|
||||
for _ in range(num_blinks):
|
||||
led.on()
|
||||
time.sleep(frequency)
|
||||
led.off()
|
||||
time.sleep(frequency)
|
||||
|
||||
# Wait for connect or fail
|
||||
max_wait = 10
|
||||
while max_wait > 0:
|
||||
if wlan.status() < 0 or wlan.status() >= 3:
|
||||
break
|
||||
max_wait -= 1
|
||||
print('waiting for connection...')
|
||||
time.sleep(1)
|
||||
|
||||
# Handle connection error
|
||||
if wlan.status() != 3:
|
||||
blink_led(0.2, 5)
|
||||
raise RuntimeError('wifi connection failed')
|
||||
else:
|
||||
blink_led(1, 2)
|
||||
print('connected')
|
||||
status = wlan.ifconfig()
|
||||
print('ip = ' + status[0], ':' ,port ,sep ='')
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
|
||||
# Open socket
|
||||
addr = socket.getaddrinfo('0.0.0.0', port)[0][-1]
|
||||
|
||||
s = socket.socket()
|
||||
s.bind(addr)
|
||||
s.listen(1)
|
||||
|
||||
print('listening on', addr)
|
||||
|
||||
uart = UART(0, 19200)
|
||||
uart.init(19200, bits=8, parity=None, stop=1)
|
||||
|
||||
ipod = IPOD(uart)
|
||||
|
||||
ipod._cmd(_test_play)
|
||||
|
||||
actions = {
|
||||
"play": ipod.play,
|
||||
"next": ipod.next,
|
||||
"prev": ipod.prev,
|
||||
"volup": ipod.volup,
|
||||
"voldown": ipod.voldown,
|
||||
"menu": ipod.menu,
|
||||
"ok": ipod.ok,
|
||||
"scrollup": ipod.scrollup,
|
||||
"scrolldown": ipod.scrolldown
|
||||
}
|
||||
def nop():
|
||||
pass
|
||||
|
||||
# Listen for connections
|
||||
while True:
|
||||
try:
|
||||
cl, addr = s.accept()
|
||||
print('Client connected from', addr)
|
||||
r = cl.recv(16000)
|
||||
lines = r.splitlines()
|
||||
req = lines[0].decode("utf8").split(" ")
|
||||
|
||||
method = req[0]
|
||||
path = req[1]
|
||||
ver = req[2]
|
||||
print(f"method: {method} path: {path}")
|
||||
|
||||
_search = path.split("?")
|
||||
if len(_search) > 1:
|
||||
search = _search[1]
|
||||
print(f"search: {search}")
|
||||
for param in search.split("&"):
|
||||
k,v = param.split("=")
|
||||
if k == "pod":
|
||||
actions.get(v, nop)()
|
||||
print(f"executed: {v}")
|
||||
|
||||
head = 'HTTP/1.0 200 OK\r\n'
|
||||
head += "Content-type: text/html\r\n"
|
||||
head += f"Content-Length: {len(html)}\r\n"
|
||||
head += "\r\n"
|
||||
|
||||
off = 0
|
||||
SEG_SIZE = 1024
|
||||
while off < len(html):
|
||||
cl.send(html[off:off+SEG_SIZE])
|
||||
off += SEG_SIZE
|
||||
|
||||
cl.close()
|
||||
except Exception as e:
|
||||
print("Exception", e)
|
||||
cl.close()
|
||||
print('Connection closed')
|
||||
|
||||
init()
|
||||
main()
|
16
opendock.py
16
opendock.py
|
@ -1,16 +0,0 @@
|
|||
#import crap
|
||||
from machine import UART, Pin
|
||||
|
||||
# init ipod uart
|
||||
uart = UART(0, 19200)
|
||||
uart.init(19200, bits=8, parity=None, stop=1)
|
||||
|
||||
#command table
|
||||
play = bytes([0xFF, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x01, 0xFA, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB])
|
||||
next = bytes([0xFF, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x08, 0xF3, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB])
|
||||
prev = bytes([0xFF, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x10, 0xEB, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB])
|
||||
vup = bytes([0xFF, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x02, 0xF9, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB])
|
||||
vdown = bytes([0xFF, 0x55, 0x03, 0x00, 0x01, 0x02, 0xFA, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x04, 0xF7, 0xFF, 0x55, 0x03, 0x02, 0x00, 0x00, 0xFB])
|
||||
|
||||
#code
|
||||
uart.write(play) # it works yey
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue