guile-inkplate/inkplate.scm
Tom Willemse b86754cb92 Add ‘guile-termios’ dependency to set the speed of the inkplate TTY
There seems to be magic going on here. Once the speed has been set for the
device once the OS (at least GNU/Linux) remembers the setting and it doesn't
have to happen again after. Since I was porting from my Emacs module, I kept
running Emacs and it kept setting the speed, which meant that my tests with
Guile kept working.

After a reboot, it stopped.

This change makes sure that the speed is set. I'm not sure if the raw is
necessary, but it's in the example, and it seems to work for now, so I'll keep
it around.
2023-07-12 15:08:31 -07:00

298 lines
11 KiB
Scheme
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(define-module (inkplate)
#:use-module ((ice-9 format) #:select (format))
#:use-module ((srfi srfi-9) #:select (define-record-type))
#:use-module ((termios system) #:select (termios-B115200))
#:use-module ((termios with-exceptions)
#:select (make-termios-struct
tc-get-attr!
tc-set-attr
cf-make-raw!
cf-set-speed!))
#:export (<inkplate>
make-inkplate
inkplate?
inkplate-input-port
inkplate-output-port
convert-string-to-hex
open
close
echo
draw-pixel
draw-line
draw-fast-vertical-line
draw-fast-horizontal-line
draw-rectangle
draw-circle
draw-triangle
draw-rounded-rectangle
fill-rectangle
fill-circle
fill-triangle
fill-rounded-rectangle
print
set-text-size
set-cursor
set-text-wrap
set-rotation
draw-bitmap
set-display-mode
get-display-mode
clear-screen
update
partial-update
read-temperature
read-touchpad
read-battery
panel-supply
get-panel-state
draw-image
draw-thick-line
draw-ellipse
fill-ellipse
send))
(define-record-type <inkplate>
(make-inkplate input-port output-port)
inkplate?
(input-port inkplate-input-port)
(output-port inkplate-output-port))
(define (convert-string-to-hex string)
"Convert STRING to a hexadecimal representation of the text."
(string-join
(map (lambda (x) (format #f "~2,'0x" (char->integer x)))
(string->list string))
""))
(define (open device-path)
"Open a connection to an Inkplate on the named DEVICE-PATH."
(let ((port (open-file device-path "r+"))
(termios (make-termios-struct)))
(tc-get-attr! port termios)
(cf-make-raw! termios)
(cf-set-speed! termios termios-B115200)
(tc-set-attr port termios)
(make-inkplate port port)))
(define (close device)
"Close a connection to an Inkplate in DEVICE."
(close-port (inkplate-output-port device))
(close-port (inkplate-input-port device)))
(define (echo device)
"Send a command to check if the DEVICE is receiving commands."
(display "#?*" (inkplate-output-port device)))
(define (draw-pixel device x y color)
"Draw a pixel on DEVICE at coordinates X and Y in COLOR."
(format (inkplate-output-port device) "#0(~3,'0d,~3,'0d,~2,'0d)*" x y color))
(define (draw-line device x1 y1 x2 y2 color)
"Draw a line on DEVICE from coordinates X1,Y1 to X2,Y2 in COLOR."
(format (inkplate-output-port device)
"#1(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x1 y1 x2 y2 color))
(define (draw-fast-vertical-line device x y length color)
"Draw a vertical line on DEVICE from X,Y for LENGTH pixels in COLOR."
(format (inkplate-output-port device)
"#2(~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y length color))
(define (draw-fast-horizontal-line device x y length color)
"Draw a horizontal line on DEVICE from X,Y for LENGTH pixels in COLOR."
(format (inkplate-output-port device)
"#3(~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y length color))
(define (draw-rectangle device x y width height color)
"Draw a rectangle on DEVICE at X,Y WIDTH wide by HEIGHT high in COLOR.
This function draws an outline of a rectangle whereas fill-rectangle fills the
rectangle with the specified color."
(format (inkplate-output-port device)
"#4(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y width height color))
(define (draw-circle device x y radius color)
"Draw a circle on DEVICE at X,Y with RADIUS in COLOR.
This function draws an outline of a circle whereas fill-circle fills the
circle with the specified color."
(format (inkplate-output-port device)
"#5(~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y radius color))
(define (draw-triangle device x1 y1 x2 y2 x3 y3 color)
"Draw a triangle on DEVICE at points X1,Y1, X2,Y2, and X3,Y3 in COLOR.
This function draws an outline of a triangle whereas fill-triangle fills the
triangle with the specified color."
(format (inkplate-output-port device)
"#6(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*"
x1 y1 x2 y2 x3 y3 color))
(define (draw-rounded-rectangle device x y width height radius color)
"Draw a rounded rectangle on DEVICE at X,Y WIDTH wide by HEIGHT high.
RADIUS is the radius of the rounded corners. Draw the rectangle in COLOR."
(format (inkplate-output-port device)
"#7(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*"
x y width height radius color))
(define (fill-rectangle device x y width height color)
"Draw a rectangle on DEVICE at X,Y WIDTH wide by HEIGHT high in COLOR.
This function fills the entire rectangle with COLOR while draw-rectangle only
draws an outline."
(format (inkplate-output-port device)
"#8(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y width height color))
(define (fill-circle device x y radius color)
"Draw a circle on DEVICE at X,Y with RADIUS.
This function fills the entire circle with COLOR while draw-circle only draws
an outline."
(format (inkplate-output-port device)
"#9(~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y radius color))
(define (fill-triangle device x1 y1 x2 y2 x3 y3 color)
"Draw a triangle on DEVICE at points X1,Y1, X2,Y2, and X3,Y3 in COLOR.
This function fills the entire triangle with COLOR while draw-triangle only
draws an outline."
(format (inkplate-output-port device)
"#A(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*"
x1 y1 x2 y2 x3 y3 color))
(define (fill-rounded-rectangle device x y width height radius color)
"Draw a rounded rectangle on DEVICE at X,Y WIDTH wide by HEIGHT high.
CORNER-RADIUS is the radius of the rounded corners. Fill the rectangle with
COLOR."
(format (inkplate-output-port device)
"#B(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*"
x y width height radius color))
(define (print device text)
"Print the TEXT onto the screen of DEVICE.
TEXT should be a hex string as produced by convert-string-to-hex."
(format (inkplate-output-port device) "#C(~s)*" text))
(define (set-text-size device size)
"Set the text size for the next print command to SIZE for DEVICE."
(format (inkplate-output-port device) "#D(~2,'0d)*" size))
(define (set-cursor device x y)
"Move the cursor on DEVICE to the X and Y coordinates."
(format (inkplate-output-port device) "#E(~3,'0d,~3,'0d)*" x y))
(define (set-text-wrap device enable)
"Enable or disable text wrapping on DEVICE depending on the value of ENABLE.
If ENABLE is #t, enable text wrapping. Otherwise disable it."
(format (inkplate-output-port device) "#F(~a)*" (if enable "T" "F")))
(define (set-rotation device rotation)
"Set the screen rotation on DEVICE.
ROTATION can be one of:
- 0: 0 degrees rotation
- 1: 90 degrees rotation
- 2: 180 degrees rotation
- 3: 270 degrees rotation"
(format (inkplate-output-port device) "#G(~3,'0d)*" rotation))
(define (draw-bitmap device x y path)
"Draw a bitmap on DEVICE at coordinates X,Y.
PATH should be a path on the SD card and should be a hex string
as produced by inkplate--convert-string-to-hex. This command
puts a response in the output buffer. It should be one of:
- #H(0)*: Image load failed
- #H(1)*: Image loaded successfully
- #H(-1)*: SD Card Init Error"
(format (inkplate-output-port device) "#H(~3,'0d,~3,'0d,~s)*" x y path))
(define (set-display-mode device mode)
"Set the display mode for DEVICE to MODE.
Mode can be one of:
- 1: 1-bit mode
- 3: 3-bit mode"
(format (inkplate-output-port device) "#I(~d)*" mode))
(define (get-display-mode device)
"Query the DEVICE for which display mode is active.
This puts one of the following responses into the output buffer:
- #J(0): 1-bit mode
- #J(1): 3-bit mode"
(display "#J(?)*" (inkplate-output-port device)))
(define (clear-screen device)
"Send the command to clear the screen to DEVICE."
(display "#K(1)*" (inkplate-output-port device)))
(define (update device)
"Send the command to update the display to DEVICE."
(display "#L(1)*" (inkplate-output-port device)))
(define (partial-update device yy1 xx2 yy2)
"Send the command to update the display to DEVICE.
Tell it to only do a partial update."
;; I guess that xx1 is implicitly 0? It's not mentioned in the documentation.
(format (inkplate-output-port device) "#M(~3,'0d,~3,'0d,~3,'0d)*" yy1 xx2 yy2))
(define (read-temperature device)
"Query DEVICE for its current temperature.
This will produce a response in the output buffer in the form or #N(22)* which
means its temperature is 22 degrees Celsius."
(display "#N(?)*" (inkplate-output-port device)))
(define (read-touchpad device touchpad)
"Query DEVICE for the state of TOUCHPAD.
TOUCHPAD can be 1, 2, or 3. This will produce a response in the
output buffer which can be read with inkplate-read-output.
Possible responses are:
- #O(1)*: The touch pad is in the high state
- #O(0)*: The touch pad is in the low state"
(format (inkplate-output-port device) "#O(~d)*" touchpad))
(define (read-battery device)
"Query DEVICE for the current voltage on the battery.
This produces a response in the output buffer which can be read using
read-output. The response is in the form of #P(3.65)* meaning the measured
voltage on the battery is 3.65VDC."
(display "#P(?)*" (inkplate-output-port device)))
(define (panel-supply device enable)
"Turn DEVICE on or off depending on the value of ENABLE.
If ENABLE is #t turn DEVICE on, otherwise turn it off."
(format (inkplate-output-port device) "#Q(~d)*" (if enable 1 0)))
(define (get-panel-state device)
"Query DEVICE for the state of the panel.
This command produces a response in the output buffer which can be read using
read-output. The response can be one of:
- #R(1)*: Panel has power
- #R(0)*: Panel has no power"
(display "#R(?)*" (inkplate-output-port device)))
(define (draw-image device x y path)
"Draw an image on DEVICE at coordinates X,Y.
PATH should be a path on the SD card and a hex string as produced by
convert-string-to-hex."
(format (inkplate-output-port device) "#S(~3,'0d,~3,'0d,~s)*" x y path))
(define (draw-thick-line device x1 y1 x2 y2 thickness color)
"Draw a line on DEVICE from X1,Y1 to X2,Y2 in THICKNESS and COLOR."
(format (inkplate-output-port device)
"#T(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d,~2,'0d)*"
x1 y1 x2 y2 thickness color))
(define (draw-ellipse device x y x-radius y-radius color)
"Draw an ellipse on DEVICE at coordinates X,Y.
X-RADIUS and Y-RADIUS specify the x and y radius of the ellipse. Draw it in
COLOR."
(format (inkplate-output-port device)
"#U(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y x-radius y-radius color))
(define (fill-ellipse device x y x-radius y-radius color)
"Draw and fill an ellipse on DEVICE at coordinates X,Y.
X-RADIUS and Y-RADIUS specify the x and y radius of the ellipse. Draw it in
COLOR."
(format (inkplate-output-port device)
"#V(~3,'0d,~3,'0d,~3,'0d,~3,'0d,~2,'0d)*" x y x-radius y-radius color))
(define (send device)
"Send the accumulated commands to DEVICE."
(force-output (inkplate-output-port device)))