mirror of
https://github.com/phooky/Apple-410.git
synced 2024-06-11 08:29:34 +00:00
initial checkin of Apple 410 doc and driver
This commit is contained in:
commit
5aa05091c7
19
10print.py
Executable file
19
10print.py
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import apple410
|
||||||
|
import random
|
||||||
|
import math
|
||||||
|
|
||||||
|
lineht=100
|
||||||
|
# 2394 x 1700
|
||||||
|
w=2394
|
||||||
|
h=1700
|
||||||
|
|
||||||
|
a = apple410.Apple410('/dev/ttyUSB0')
|
||||||
|
a.send("LS{}".format(lineht))
|
||||||
|
ydim = math.floor(h/lineht)
|
||||||
|
xdim = math.floor(w/lineht)
|
||||||
|
for y in range(ydim):
|
||||||
|
a.send("MA0,{}".format(lineht*y))
|
||||||
|
a.send("PL"+''.join(map(lambda _:random.choice('\\/'),range(xdim))))
|
||||||
|
a.send("CH")
|
50
apple410.py
Executable file
50
apple410.py
Executable file
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import serial
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class Apple410:
|
||||||
|
"""A simple class for queing up commands for the Apple 410"""
|
||||||
|
def __init__(self, portname, baud=1200):
|
||||||
|
self.serial = serial.Serial(portname, baud, rtscts=True, dsrdtr=True, timeout=0.1)
|
||||||
|
self.pos = (0,0)
|
||||||
|
self.wd = self.vp = (0,0,2394,1759)
|
||||||
|
|
||||||
|
def sendchar(self, c):
|
||||||
|
self.serial.flush()
|
||||||
|
while not self.serial.cts:
|
||||||
|
time.sleep(0.1)
|
||||||
|
while not self.serial.dsr:
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.serial.write(c.encode('ascii'))
|
||||||
|
|
||||||
|
def send(self, command):
|
||||||
|
for c in command:
|
||||||
|
self.sendchar(c)
|
||||||
|
self.sendchar('\x03')
|
||||||
|
|
||||||
|
def move_to(self, coords):
|
||||||
|
self.send('MA{},{}'.format(coords[0],coords[1]))
|
||||||
|
self.pos = coords
|
||||||
|
|
||||||
|
def draw_to(self, coords):
|
||||||
|
self.send('DA{:.2f},{:.2f}'.format(coords[0],coords[1]))
|
||||||
|
self.pos = coords
|
||||||
|
|
||||||
|
def pen_select(self, index):
|
||||||
|
self.send('PS{}'.format(index))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
scr = 'test_script.cmds'
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
scr = sys.argv[1]
|
||||||
|
print("Running file {}".format(scr))
|
||||||
|
a = Apple410('/dev/ttyUSB0')
|
||||||
|
if scr == "-":
|
||||||
|
f = sys.stdin
|
||||||
|
else:
|
||||||
|
f = open(scr)
|
||||||
|
for line in f.readlines():
|
||||||
|
print("SENDING: {}".format(line.strip()))
|
||||||
|
a.send(line.strip())
|
||||||
|
|
153
doc/Apple 401 Plotter.md
Normal file
153
doc/Apple 401 Plotter.md
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
# The Apple 401 Color Plotter
|
||||||
|
|
||||||
|
## General concepts
|
||||||
|
|
||||||
|
The way the 401 interprets a coordinate at any given time depends on the current
|
||||||
|
viewport and window settings. The "viewport" is a rectangle specified in hardware
|
||||||
|
absolute coordinates and defines the physical area in which coordinates are resolved.
|
||||||
|
The "window" defines the virtual coordinates of the drawing area, which are then
|
||||||
|
mapped to the viewport.
|
||||||
|
|
||||||
|
Commands sent to the 401 are terminated by the ASCII "end of text" delimiter, 0x03.
|
||||||
|
|
||||||
|
### Serial connection
|
||||||
|
|
||||||
|
The 8 DIP switches (SW1-SW8) on the back of the plotter are used to configure the
|
||||||
|
serial interface.
|
||||||
|
|
||||||
|
SW1 selects the data bits. ON = 7 bits, OFF = 8 bits.
|
||||||
|
|
||||||
|
SW2 selects whether parity is used. ON = no parity bit, OFF = use a parity bit.
|
||||||
|
If parity is selected, SW3 determines the type of parity bit. ON = odd parity,
|
||||||
|
OFF = even parity.
|
||||||
|
|
||||||
|
SW4 and SW5 select the number of stop bits:
|
||||||
|
| SW4 | SW5 | Stop bits |
|
||||||
|
|------|------|---------------|
|
||||||
|
| OFF | OFF | 2 stop bits |
|
||||||
|
| ON | OFF | 1.5 stop bits |
|
||||||
|
| OFF | ON | 1 stop bit |
|
||||||
|
| ON | ON | invalid |
|
||||||
|
|
||||||
|
SW6, SW7, and SW8 select the baud rate:
|
||||||
|
| SW6 | SW7 | SW8 | Baud |
|
||||||
|
|-----|-----|-----|------------|
|
||||||
|
| OFF | OFF | OFF | 75 baud |
|
||||||
|
| OFF | OFF | ON | 150 baud |
|
||||||
|
| OFF | ON | OFF | 300 baud |
|
||||||
|
| OFF | ON | ON | 600 baud |
|
||||||
|
| ON | OFF | OFF | 1200 baud |
|
||||||
|
| ON | OFF | ON | 2400 baud |
|
||||||
|
| ON | ON | OFF | 4800 baud |
|
||||||
|
| ON | ON | ON | 9600 baud |
|
||||||
|
|
||||||
|
**The 401 uses hardware handshaking.** It never returns data to the computer
|
||||||
|
over the serial line and sets DSR when its internal buffer is full. Sending even
|
||||||
|
one byte after the DSR line has been set will corrupt the 401's command buffer
|
||||||
|
and trigger an error. Most USB to RS232 cables will attempt to bundle up several
|
||||||
|
bytes before transmission, so unless you're sure that your cable can handle it
|
||||||
|
I recommend flushing the serial connection after every byte and checking the DSR
|
||||||
|
line yourself.
|
||||||
|
|
||||||
|
## Command Reference
|
||||||
|
|
||||||
|
### Move Absolute (MA)
|
||||||
|
|
||||||
|
```
|
||||||
|
MAx,y
|
||||||
|
```
|
||||||
|
|
||||||
|
Raise the pen and move the plotter head to the position specified by the x,y coordinates.
|
||||||
|
|
||||||
|
Params: 2
|
||||||
|
|
||||||
|
### ??Move relative (MR)??
|
||||||
|
|
||||||
|
```
|
||||||
|
MRx,y
|
||||||
|
```
|
||||||
|
|
||||||
|
Raise the pen and move the plotter head to a position offset x,y from its
|
||||||
|
current position.
|
||||||
|
|
||||||
|
Params: 2
|
||||||
|
### Draw absolute (DA)
|
||||||
|
|
||||||
|
```
|
||||||
|
DAx,y(,x,y..)
|
||||||
|
```
|
||||||
|
|
||||||
|
Lower the pen and draw a line from the current position to the position
|
||||||
|
specified by the x,y coordinates. Continues
|
||||||
|
|
||||||
|
Params: arb.
|
||||||
|
|
||||||
|
==??Draw relative (DR)??==
|
||||||
|
|
||||||
|
DRx,y(,x,y)*
|
||||||
|
|
||||||
|
lower pen and draw line from current position to an x,y offset. Multiple
|
||||||
|
offsets may be specified in a single command.
|
||||||
|
|
||||||
|
Params: arb.
|
||||||
|
|
||||||
|
==Circle (CA)==
|
||||||
|
|
||||||
|
CAr,x,y
|
||||||
|
|
||||||
|
lower pen and draw a circle of radius r centered at x,y.
|
||||||
|
|
||||||
|
Params: 3
|
||||||
|
|
||||||
|
==Letter size (LS)==
|
||||||
|
|
||||||
|
LSs
|
||||||
|
|
||||||
|
sets the font size to s
|
||||||
|
|
||||||
|
==Letter rotation (LR)==
|
||||||
|
|
||||||
|
LRtheta
|
||||||
|
|
||||||
|
draw following text rotated by theta
|
||||||
|
in degrees, clockwise
|
||||||
|
|
||||||
|
==Print letters (PL)==
|
||||||
|
|
||||||
|
PLtext
|
||||||
|
|
||||||
|
lower pen and draw specified text at current position
|
||||||
|
|
||||||
|
==Pen select (PS)==
|
||||||
|
|
||||||
|
PSi
|
||||||
|
|
||||||
|
select pen i (where i is in 1-4)
|
||||||
|
|
||||||
|
|
||||||
|
LT - Line type -
|
||||||
|
1 solid
|
||||||
|
2 ??
|
||||||
|
3 ??
|
||||||
|
4 ??
|
||||||
|
5 ?? param
|
||||||
|
6 ??
|
||||||
|
|
||||||
|
XT - ? xticks ? -- 5 params
|
||||||
|
YT - ? yticks ? -- 5 params
|
||||||
|
CH - ??? -- no params
|
||||||
|
PM - Point Marking -- 1 param
|
||||||
|
PS - pen select
|
||||||
|
PV - ??? - 1 param
|
||||||
|
WD - window - 4 params
|
||||||
|
AC - arc - 5 params
|
||||||
|
LI - ??? - no params/arb ???
|
||||||
|
IM - ??? - 2 params
|
||||||
|
PK - ??? - no params/arb
|
||||||
|
RS - ??? - ?0x80 as params?
|
||||||
|
VP - Viewport - 4 params
|
||||||
|
UL - ??? - no params/arb ? 1 param 0-9?
|
||||||
|
SP - ??? - 1 param ??
|
||||||
|
LF - ??? - 1 param ? 0-9
|
||||||
|
SL - ??? - 1 param -- Slanted lettering (by %?)
|
||||||
|
|
54
moore.py
Executable file
54
moore.py
Executable file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
from itertools import chain
|
||||||
|
from math import sin,cos,pi
|
||||||
|
import apple410
|
||||||
|
|
||||||
|
axiom = list("LFL+F+LFL")
|
||||||
|
|
||||||
|
rules = { 'L' : list("-RF+LFL+FR-"),
|
||||||
|
'R' : list("+LF-RFR-FL+") }
|
||||||
|
|
||||||
|
def L_repl(c):
|
||||||
|
global rules
|
||||||
|
r = rules.get(c)
|
||||||
|
if r:
|
||||||
|
return r
|
||||||
|
return [c]
|
||||||
|
|
||||||
|
def L_iter(system):
|
||||||
|
a = map(L_repl,system)
|
||||||
|
return list(chain.from_iterable(a))
|
||||||
|
|
||||||
|
def L_sys(system,depth):
|
||||||
|
l = axiom
|
||||||
|
for i in range(depth):
|
||||||
|
l = L_iter(l)
|
||||||
|
return l
|
||||||
|
|
||||||
|
seq=L_sys(axiom,5)
|
||||||
|
|
||||||
|
direction = 0.0
|
||||||
|
theta = (pi/2)
|
||||||
|
distance = 16
|
||||||
|
location = (452.0,451.0)
|
||||||
|
|
||||||
|
def move(pos,angle,distance):
|
||||||
|
nx = pos[0] + sin(angle)*distance
|
||||||
|
ny = pos[1] + cos(angle)*distance
|
||||||
|
return (nx,ny)
|
||||||
|
|
||||||
|
a = apple410.Apple410('/dev/ttyUSB0')
|
||||||
|
a.pen_select(2)
|
||||||
|
a.move_to(location)
|
||||||
|
|
||||||
|
for e in seq:
|
||||||
|
if e == 'F':
|
||||||
|
nl = move(location, direction, distance)
|
||||||
|
a.draw_to(nl)
|
||||||
|
print("DA {}".format(nl))
|
||||||
|
location = nl
|
||||||
|
elif e == '+':
|
||||||
|
direction = direction + theta
|
||||||
|
elif e == '-':
|
||||||
|
direction = direction - theta
|
||||||
|
|
105
plot_scripts/self_text.scr
Normal file
105
plot_scripts/self_text.scr
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
MA0,0
|
||||||
|
DA2394,0,2394,1700,0,1700,0,0
|
||||||
|
PS2
|
||||||
|
WD0,0,150,140
|
||||||
|
LS4
|
||||||
|
MA7,6.6
|
||||||
|
PLApple
|
||||||
|
LS2
|
||||||
|
PS3
|
||||||
|
MA20,19.2
|
||||||
|
LR30
|
||||||
|
PLApple
|
||||||
|
PS4
|
||||||
|
MA30,28.8
|
||||||
|
LR60
|
||||||
|
PLApple
|
||||||
|
PS1
|
||||||
|
MA40,38.4
|
||||||
|
LR90
|
||||||
|
PLApple
|
||||||
|
PS2
|
||||||
|
MA50,48
|
||||||
|
LR120
|
||||||
|
PLApple
|
||||||
|
PS3
|
||||||
|
MA60,57.6
|
||||||
|
LR150
|
||||||
|
PLApple
|
||||||
|
PS4
|
||||||
|
MA70,67.2
|
||||||
|
LR180
|
||||||
|
PLApple
|
||||||
|
PS1
|
||||||
|
MA80,76.8
|
||||||
|
LR210
|
||||||
|
PLApple
|
||||||
|
PS2
|
||||||
|
MA90,86.4
|
||||||
|
LR240
|
||||||
|
PLApple
|
||||||
|
PS3
|
||||||
|
MA100,96
|
||||||
|
LR270
|
||||||
|
PLApple
|
||||||
|
PS4
|
||||||
|
MA110,105.6
|
||||||
|
LR300
|
||||||
|
PLApple
|
||||||
|
PS1
|
||||||
|
MA120,115.2
|
||||||
|
LR330
|
||||||
|
PLApple
|
||||||
|
LS4
|
||||||
|
PS2
|
||||||
|
MA127,121.8
|
||||||
|
LR360
|
||||||
|
PLApple
|
||||||
|
VP1500,250,2300,1050
|
||||||
|
WD0,0,100,100
|
||||||
|
PS1
|
||||||
|
CA50,50,50
|
||||||
|
CA10,50,50
|
||||||
|
PS2
|
||||||
|
CA25,75,50
|
||||||
|
CA25,25,50
|
||||||
|
AC25,0,180,50,75
|
||||||
|
AC25,180,360,50,25
|
||||||
|
PS3
|
||||||
|
CA15,85,50
|
||||||
|
CA15,50,15
|
||||||
|
CA15,15,50
|
||||||
|
CA15,50,85
|
||||||
|
PS4
|
||||||
|
CA10,90,50
|
||||||
|
CA10,50,10
|
||||||
|
CA10,10,50
|
||||||
|
CA 10,50,90
|
||||||
|
VP0,821,1197,1700
|
||||||
|
WD0,0,140,100
|
||||||
|
MA10,50
|
||||||
|
XT3,120,12,2,2
|
||||||
|
MA10,10
|
||||||
|
YT1,80,8,4,0
|
||||||
|
PS1
|
||||||
|
LT5,10
|
||||||
|
MA20,60
|
||||||
|
DA30,90,40,20,50,20,60,10,70,80
|
||||||
|
LT6
|
||||||
|
PS2
|
||||||
|
DA80,90,90,40,100,60,120,60,130,70
|
||||||
|
VP900,100,1400,600
|
||||||
|
WD0,0,100,100
|
||||||
|
PS3
|
||||||
|
LT1
|
||||||
|
MA0,0
|
||||||
|
DA0,100,10,100,0,0,20,100,30,100,0,0,40,100,50,100,0,0
|
||||||
|
DA60,100,70,100,0,0,80,100,90,100,0,0,100,100,100,90
|
||||||
|
DA0,0,100,80,100,70,0,0,100,60,100,50,0,0,100,40,100,30
|
||||||
|
DA0,0,100,20,100,10,0,0,100,0
|
||||||
|
VP0,0,2394,1759
|
||||||
|
WD0,0,2394,1759
|
||||||
|
MA1550,110
|
||||||
|
LS30
|
||||||
|
PLApple Color Plotter Test
|
||||||
|
CH
|
112
plot_to_svg.py
Executable file
112
plot_to_svg.py
Executable file
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import sys
|
||||||
|
import svgwrite
|
||||||
|
import math
|
||||||
|
|
||||||
|
d = svgwrite.Drawing(profile='tiny')
|
||||||
|
|
||||||
|
|
||||||
|
def getnum(line):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def coordlist(line,expected=-1):
|
||||||
|
l = list(map(float,line.split(',')))
|
||||||
|
if expected > -1:
|
||||||
|
assert len(l) == expected
|
||||||
|
return l
|
||||||
|
|
||||||
|
pens = [
|
||||||
|
svgwrite.rgb(0, 0, 0, '%'),
|
||||||
|
svgwrite.rgb(100, 0, 0, '%'),
|
||||||
|
svgwrite.rgb(0, 100, 0, '%'),
|
||||||
|
svgwrite.rgb(0, 0, 100, '%') ]
|
||||||
|
|
||||||
|
pennum = 0
|
||||||
|
pos = (0.0,0.0)
|
||||||
|
text_rotation = 0.0
|
||||||
|
text_size = 1
|
||||||
|
# "viewport"? part of the page to plot on
|
||||||
|
vp = (0.0,0.0,2394,1759)
|
||||||
|
# virtual size for viewport
|
||||||
|
wd = (0.0,0.0,2394,1759)
|
||||||
|
|
||||||
|
def tcoord(x, vpl, vph, wdl, wdh):
|
||||||
|
vpw = vph-vpl
|
||||||
|
wdw = wdh-wdl
|
||||||
|
nx = vpw*(x/wdw)
|
||||||
|
return nx + vpl
|
||||||
|
|
||||||
|
def transform1(x):
|
||||||
|
vpw = vp[2]-vp[0]
|
||||||
|
wdw = wd[2]-wd[0]
|
||||||
|
nx = vpw*(x/wdw)
|
||||||
|
return nx
|
||||||
|
|
||||||
|
def transform(x,y):
|
||||||
|
nx = tcoord(x,vp[0],vp[2],wd[0],wd[2])
|
||||||
|
ny = tcoord(y,vp[1],vp[3],wd[1],wd[3])
|
||||||
|
return nx, ny
|
||||||
|
|
||||||
|
for line in sys.stdin.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
cmd = line[0:2]
|
||||||
|
line = line[2:].strip()
|
||||||
|
if cmd == 'MA':
|
||||||
|
l=coordlist(line,2)
|
||||||
|
pos=transform(l[0],l[1])
|
||||||
|
elif cmd == 'VP': #viewport?
|
||||||
|
l=coordlist(line,4)
|
||||||
|
vp=(l[0],l[1],l[2],l[3])
|
||||||
|
elif cmd == 'WD':
|
||||||
|
l=coordlist(line,4)
|
||||||
|
wd=(l[0],l[1],l[2],l[3])
|
||||||
|
elif cmd == 'DA':
|
||||||
|
l=coordlist(line)
|
||||||
|
while len(l) > 0:
|
||||||
|
nextp = transform(l[0],l[1])
|
||||||
|
l = l[2:]
|
||||||
|
d.add(d.line(pos,nextp,stroke=pens[pennum]))
|
||||||
|
pos = nextp
|
||||||
|
elif cmd == 'CA': #circle
|
||||||
|
l=coordlist(line)
|
||||||
|
r=transform1(l[0])
|
||||||
|
p=transform(l[1],l[2])
|
||||||
|
c=d.circle(center=p,r=r)
|
||||||
|
c.fill(opacity=0)
|
||||||
|
c.stroke(color=pens[pennum],opacity=100,width=2)
|
||||||
|
d.add(c)
|
||||||
|
elif cmd == 'AC': #arc
|
||||||
|
l=coordlist(line)
|
||||||
|
r=transform1(l[0])
|
||||||
|
p=transform(l[3],l[4])
|
||||||
|
t2, t1 = math.radians(l[1]),math.radians(l[2])
|
||||||
|
x1, y1 = p[0] + (r*math.cos(t1)), p[1] + (r*math.sin(t1))
|
||||||
|
x2, y2 = p[0] + (r*math.cos(t2)), p[1] + (r*math.sin(t2))
|
||||||
|
ds="M {} {} A {} {} 0 0 0 {} {}".format(x1,y1,r,r,x2,y2)
|
||||||
|
path=d.path(
|
||||||
|
d=ds,
|
||||||
|
stroke=pens[pennum])
|
||||||
|
path.fill(opacity=0)
|
||||||
|
d.add(path)
|
||||||
|
elif cmd == 'LR':
|
||||||
|
text_rotation = float(line)
|
||||||
|
elif cmd == 'LS':
|
||||||
|
text_size = float(line)
|
||||||
|
elif cmd == 'PS':
|
||||||
|
pennum = int(line) - 1
|
||||||
|
#print("Switching to pen {}".format(pennum))
|
||||||
|
elif cmd == 'PL':
|
||||||
|
t = d.text(line,insert=pos)
|
||||||
|
t.rotate(text_rotation,center=pos)
|
||||||
|
d.add(t)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
#print("Command {}".format(cmd))
|
||||||
|
pass
|
||||||
|
|
||||||
|
d.write(sys.stdout)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
roms/B9801YL.bin
Normal file
BIN
roms/B9801YL.bin
Normal file
Binary file not shown.
BIN
roms/B9801YM.bin
Normal file
BIN
roms/B9801YM.bin
Normal file
Binary file not shown.
BIN
roms/B9801YN.bin
Normal file
BIN
roms/B9801YN.bin
Normal file
Binary file not shown.
10
roms/README.md
Normal file
10
roms/README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
## Apple 410 ROM dumps
|
||||||
|
|
||||||
|
This directory contains the raw ROM dumps from the Apple 410 Color Plotter.
|
||||||
|
|
||||||
|
`B9801Y[LMN].bin` are images the three 8K 27C64 chips installed on the board. `ROM.bin` is the
|
||||||
|
consolidated ROM image built by concatenating the three individual roms.
|
||||||
|
|
||||||
|
`test_extractor.py` is a script for extracting the self-test commands from the consolidated
|
||||||
|
ROM file.
|
||||||
|
|
BIN
roms/ROM.bin
Normal file
BIN
roms/ROM.bin
Normal file
Binary file not shown.
20
roms/test_extractor.py
Executable file
20
roms/test_extractor.py
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import sys
|
||||||
|
|
||||||
|
st_start = 0x94b
|
||||||
|
|
||||||
|
assert len(sys.stdin.buffer.read(st_start)) == st_start
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
cmdlen = ord(sys.stdin.buffer.read(1))
|
||||||
|
if cmdlen == 0:
|
||||||
|
break
|
||||||
|
cmd = sys.stdin.buffer.read(cmdlen)
|
||||||
|
if cmd[-1] != 3:
|
||||||
|
print("Invalid entry")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(cmd[0:-1].decode('ascii'))
|
||||||
|
|
||||||
|
|
||||||
|
|
9
send_to_plotter.py
Executable file
9
send_to_plotter.py
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
import serial
|
||||||
|
|
||||||
|
print("opening port")
|
||||||
|
s = serial.Serial("/dev/ttyUSB0", 9600, timeout=1)
|
||||||
|
print("opened")
|
||||||
|
s.write(b'PS4\x03')
|
||||||
|
print("Sent pen select\n")
|
||||||
|
print("recv {}\n".format(s.read(100)))
|
Loading…
Reference in New Issue
Block a user