Skip to content

Commit e01555d

Browse files
authored
display: Create Interfaces for text displays (#42)
* Create Interfaces for text displays * Print errors in interactive mode.
1 parent 1a89f8f commit e01555d

File tree

2 files changed

+312
-0
lines changed

2 files changed

+312
-0
lines changed

display/displaytest/text.go

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Copyright 2024 The Periph Authors. All rights reserved.
2+
// Use of this source code is governed under the Apache License, Version 2.0
3+
// that can be found in the LICENSE file.
4+
5+
package displaytest
6+
7+
import (
8+
"errors"
9+
"fmt"
10+
"time"
11+
12+
"periph.io/x/conn/v3/display"
13+
)
14+
15+
// TestTextDisplay exercises the methods provided by the interface. It can be
16+
// used interactively as a quick smoke test of an implementation, and from test
17+
// routines. This doesn't test brightness or contrast to avoid EEPROM wear
18+
// issues.
19+
func TestTextDisplay(dev display.TextDisplay, interactive bool) []error {
20+
var result []error
21+
var err error
22+
23+
pauseTime := time.Duration(0)
24+
if interactive {
25+
pauseTime = 3 * time.Second
26+
}
27+
// Turn the dev on and write the String() value.
28+
if err = dev.Display(true); err != nil {
29+
result = append(result, err)
30+
}
31+
32+
if err = dev.Clear(); err != nil {
33+
result = append(result, err)
34+
}
35+
36+
if _, err = dev.WriteString(dev.String()); err != nil {
37+
result = append(result, err)
38+
}
39+
time.Sleep(pauseTime)
40+
41+
if err = dev.Clear(); err != nil {
42+
result = append(result, err)
43+
}
44+
_, err = dev.WriteString("Auto Scroll Test")
45+
if err != nil {
46+
result = append(result, err)
47+
}
48+
time.Sleep(pauseTime)
49+
err = dev.AutoScroll(true)
50+
if err != nil {
51+
result = append(result, err)
52+
}
53+
// Test Display fill
54+
for line := range dev.Rows() {
55+
c := rune('A')
56+
if err = dev.MoveTo(dev.MinRow()+line, dev.MinCol()); err != nil {
57+
result = append(result, err)
58+
}
59+
for col := range dev.Cols() {
60+
if col%5 == 0 && col > 0 {
61+
_, err = dev.Write([]byte{byte(' ')})
62+
} else {
63+
_, err = dev.Write([]byte{byte(c)})
64+
}
65+
if err != nil {
66+
result = append(result, err)
67+
}
68+
c = c + 1
69+
}
70+
}
71+
// Test AutoScroll working
72+
time.Sleep(pauseTime)
73+
nWritten, err := dev.WriteString("auto scroll happen")
74+
if err != nil {
75+
result = append(result, err)
76+
}
77+
if nWritten != 18 {
78+
result = append(result, fmt.Errorf("dev.WriteString() expected %d chars written, received %d", 18, nWritten))
79+
}
80+
time.Sleep(pauseTime)
81+
if err = dev.AutoScroll(false); err != nil {
82+
result = append(result, err)
83+
}
84+
time.Sleep(pauseTime)
85+
86+
// Test Absolute Positioning
87+
if err = dev.Clear(); err != nil {
88+
result = append(result, err)
89+
}
90+
if _, err = dev.WriteString("Absolute Positioning"); err != nil {
91+
result = append(result, err)
92+
}
93+
time.Sleep(pauseTime)
94+
if err = dev.Clear(); err != nil {
95+
result = append(result, err)
96+
}
97+
for ix := range dev.Rows() {
98+
if err = dev.MoveTo(dev.MinRow()+ix, dev.MinCol()+ix); err != nil {
99+
result = append(result, err)
100+
}
101+
if _, err = dev.WriteString(fmt.Sprintf("(%d,%d)", dev.MinRow()+ix, dev.MinCol()+ix)); err != nil {
102+
result = append(result, err)
103+
}
104+
}
105+
time.Sleep(pauseTime)
106+
107+
// Test that MoveTo returns error for invalid coordinates
108+
moveCases := []struct {
109+
row int
110+
col int
111+
}{
112+
{row: dev.MinRow() - 1, col: dev.MinCol()},
113+
{row: dev.MinRow(), col: dev.MinCol() - 1},
114+
{row: dev.Rows() + 1, col: dev.Cols()},
115+
{row: dev.Rows(), col: dev.Cols() + 1},
116+
}
117+
for _, tc := range moveCases {
118+
if err = dev.MoveTo(tc.row, tc.col); err == nil {
119+
result = append(result, fmt.Errorf("did not receive expected error on MoveTo(%d,%d)", tc.row, tc.col))
120+
}
121+
}
122+
123+
// Test Cursor Modes
124+
if err = dev.Clear(); err != nil {
125+
result = append(result, err)
126+
}
127+
modes := []string{"Off", "Underline", "Block", "Blink"}
128+
for ix := display.CursorOff; ix <= display.CursorBlink; ix++ {
129+
if err = dev.MoveTo(dev.MinRow()/2+1, dev.MinCol()); err != nil {
130+
result = append(result, err)
131+
}
132+
if _, err = dev.WriteString("Cursor: " + modes[ix]); err != nil {
133+
result = append(result, err)
134+
}
135+
if err = dev.Cursor(ix); err != nil {
136+
result = append(result, err)
137+
}
138+
time.Sleep(pauseTime)
139+
if err = dev.Cursor(display.CursorOff); err != nil {
140+
result = append(result, err)
141+
}
142+
if err = dev.Clear(); err != nil {
143+
result = append(result, err)
144+
}
145+
}
146+
if err = dev.Cursor(display.CursorBlink + 1); err == nil {
147+
result = append(result, errors.New("did not receive expected error on Cursor() with invalid value"))
148+
}
149+
150+
// Test Move Forward and Backward. 2 Should overwrite the 1
151+
if err = dev.Clear(); err != nil {
152+
result = append(result, err)
153+
}
154+
if _, err = dev.WriteString("Testing >"); err != nil {
155+
result = append(result, err)
156+
}
157+
if err = dev.Move(display.Forward); err != nil {
158+
result = append(result, err)
159+
}
160+
if err = dev.Move(display.Forward); err != nil {
161+
result = append(result, err)
162+
}
163+
for ix := range 10 {
164+
if _, err = dev.WriteString(fmt.Sprintf("%d", ix)); err != nil {
165+
result = append(result, err)
166+
}
167+
time.Sleep(pauseTime)
168+
if err = dev.Move(display.Backward); err != nil {
169+
result = append(result, err)
170+
}
171+
}
172+
if err = dev.Move(display.Down + 1); err == nil {
173+
result = append(result, errors.New("did not receive expected error on Move() with invalid value"))
174+
}
175+
176+
// Test Display on/off
177+
if err = dev.Clear(); err != nil {
178+
result = append(result, err)
179+
}
180+
if _, err = dev.WriteString("Set dev off"); err != nil {
181+
result = append(result, err)
182+
}
183+
time.Sleep(pauseTime)
184+
if err = dev.Display(false); err != nil {
185+
result = append(result, err)
186+
}
187+
time.Sleep(pauseTime)
188+
if err = dev.Display(true); err != nil {
189+
result = append(result, err)
190+
}
191+
if err = dev.Clear(); err != nil {
192+
result = append(result, err)
193+
}
194+
if _, err = dev.WriteString("Set dev on"); err != nil {
195+
result = append(result, err)
196+
}
197+
time.Sleep(pauseTime)
198+
199+
if interactive {
200+
for _, e := range result {
201+
if !errors.Is(err, display.ErrNotImplemented) {
202+
fmt.Println(e)
203+
}
204+
}
205+
}
206+
return result
207+
}

display/text_display.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2024 The Periph Authors. All rights reserved.
2+
// Use of this source code is governed under the Apache License, Version 2.0
3+
// that can be found in the LICENSE file.
4+
5+
package display
6+
7+
import (
8+
"errors"
9+
)
10+
11+
type CursorDirection int
12+
13+
const (
14+
// Constants for moving the cursor relative to it's current position.
15+
//
16+
// Move the cursor one unit back.
17+
Backward CursorDirection = iota
18+
// Move the cursor one unit forward.
19+
Forward
20+
Up
21+
Down
22+
)
23+
24+
type CursorMode int
25+
26+
const (
27+
// Turn the cursor Off
28+
CursorOff CursorMode = iota
29+
// Enable Underline Cursor
30+
CursorUnderline
31+
// Enable Block Cursor
32+
CursorBlock
33+
// Blinking
34+
CursorBlink
35+
)
36+
37+
// TextDisplay represents an interface to a basic character device. It provides
38+
// standard methods implemented by a majority of character LCD devices. Pixel
39+
// type displays can implement this interface if desired.
40+
type TextDisplay interface {
41+
// Enable/Disable auto scroll
42+
AutoScroll(enabled bool) (err error)
43+
// Return the number of columns the display supports
44+
Cols() int
45+
// Clear the display and move the cursor home.
46+
Clear() (err error)
47+
// Set the cursor mode. You can pass multiple arguments.
48+
// Cursor(CursorOff, CursorUnderline)
49+
//
50+
// Implementations should return an error if the value of mode is not
51+
// mode>= CursorOff && mode <= CursorBlink
52+
Cursor(mode ...CursorMode) (err error)
53+
// Move the cursor home (MinRow(),MinCol())
54+
Home() (err error)
55+
// Return the min column position.
56+
MinCol() int
57+
// Return the min row position.
58+
MinRow() int
59+
// Move the cursor forward or backward.
60+
Move(dir CursorDirection) (err error)
61+
// Move the cursor to arbitrary position. Implementations should return an
62+
// error if row < MinRow() || row > (Rows()-MinRow()), or col < MinCol()
63+
// || col > (Cols()-MinCol())
64+
MoveTo(row, col int) (err error)
65+
// Return the number of rows the display supports.
66+
Rows() int
67+
// Turn the display on / off
68+
Display(on bool) (err error)
69+
// return info about the display.
70+
String() string
71+
// Write a set of bytes to the display.
72+
Write(p []byte) (n int, err error)
73+
// Write a string output to the display.
74+
WriteString(text string) (n int, err error)
75+
}
76+
77+
type Intensity int
78+
79+
// Interface for displays that support a monochrome backlight. Displays that
80+
// support RGB Backlights should implement this as well for maximum
81+
// compatibility.
82+
//
83+
// Many units that support this command write the value to EEPROM, which has a
84+
// finite number of writes. To turn the unit on/off, use TextDisplay.Display()
85+
type DisplayBacklight interface {
86+
Backlight(intensity Intensity) error
87+
}
88+
89+
// Interface for displays that support a RGB Backlight. E.G. the Sparkfun SerLCD
90+
type DisplayRGBBacklight interface {
91+
RGBBacklight(red, green, blue Intensity) error
92+
}
93+
94+
type Contrast int
95+
96+
// Interface for displays that support a programmable contrast adjustment.
97+
// As with SetBacklight(), many devices serialize the value to EEPROM,
98+
// which support only a finite number of writes, so this should be used
99+
// sparingly.
100+
type DisplayContrast interface {
101+
Contrast(contrast Contrast) error
102+
}
103+
104+
var ErrNotImplemented = errors.New("not implemented")
105+
var ErrInvalidCommand = errors.New("invalid command")

0 commit comments

Comments
 (0)