-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
display: Create Interfaces for text displays (#42)
* Create Interfaces for text displays * Print errors in interactive mode.
- Loading branch information
Showing
2 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
// Copyright 2024 The Periph Authors. All rights reserved. | ||
// Use of this source code is governed under the Apache License, Version 2.0 | ||
// that can be found in the LICENSE file. | ||
|
||
package displaytest | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"periph.io/x/conn/v3/display" | ||
) | ||
|
||
// TestTextDisplay exercises the methods provided by the interface. It can be | ||
// used interactively as a quick smoke test of an implementation, and from test | ||
// routines. This doesn't test brightness or contrast to avoid EEPROM wear | ||
// issues. | ||
func TestTextDisplay(dev display.TextDisplay, interactive bool) []error { | ||
var result []error | ||
var err error | ||
|
||
pauseTime := time.Duration(0) | ||
if interactive { | ||
pauseTime = 3 * time.Second | ||
} | ||
// Turn the dev on and write the String() value. | ||
if err = dev.Display(true); err != nil { | ||
result = append(result, err) | ||
} | ||
|
||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
|
||
if _, err = dev.WriteString(dev.String()); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
|
||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
_, err = dev.WriteString("Auto Scroll Test") | ||
if err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
err = dev.AutoScroll(true) | ||
if err != nil { | ||
result = append(result, err) | ||
} | ||
// Test Display fill | ||
for line := range dev.Rows() { | ||
c := rune('A') | ||
if err = dev.MoveTo(dev.MinRow()+line, dev.MinCol()); err != nil { | ||
result = append(result, err) | ||
} | ||
for col := range dev.Cols() { | ||
if col%5 == 0 && col > 0 { | ||
_, err = dev.Write([]byte{byte(' ')}) | ||
} else { | ||
_, err = dev.Write([]byte{byte(c)}) | ||
} | ||
if err != nil { | ||
result = append(result, err) | ||
} | ||
c = c + 1 | ||
} | ||
} | ||
// Test AutoScroll working | ||
time.Sleep(pauseTime) | ||
nWritten, err := dev.WriteString("auto scroll happen") | ||
if err != nil { | ||
result = append(result, err) | ||
} | ||
if nWritten != 18 { | ||
result = append(result, fmt.Errorf("dev.WriteString() expected %d chars written, received %d", 18, nWritten)) | ||
} | ||
time.Sleep(pauseTime) | ||
if err = dev.AutoScroll(false); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
|
||
// Test Absolute Positioning | ||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
if _, err = dev.WriteString("Absolute Positioning"); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
for ix := range dev.Rows() { | ||
if err = dev.MoveTo(dev.MinRow()+ix, dev.MinCol()+ix); err != nil { | ||
result = append(result, err) | ||
} | ||
if _, err = dev.WriteString(fmt.Sprintf("(%d,%d)", dev.MinRow()+ix, dev.MinCol()+ix)); err != nil { | ||
result = append(result, err) | ||
} | ||
} | ||
time.Sleep(pauseTime) | ||
|
||
// Test that MoveTo returns error for invalid coordinates | ||
moveCases := []struct { | ||
row int | ||
col int | ||
}{ | ||
{row: dev.MinRow() - 1, col: dev.MinCol()}, | ||
{row: dev.MinRow(), col: dev.MinCol() - 1}, | ||
{row: dev.Rows() + 1, col: dev.Cols()}, | ||
{row: dev.Rows(), col: dev.Cols() + 1}, | ||
} | ||
for _, tc := range moveCases { | ||
if err = dev.MoveTo(tc.row, tc.col); err == nil { | ||
result = append(result, fmt.Errorf("did not receive expected error on MoveTo(%d,%d)", tc.row, tc.col)) | ||
} | ||
} | ||
|
||
// Test Cursor Modes | ||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
modes := []string{"Off", "Underline", "Block", "Blink"} | ||
for ix := display.CursorOff; ix <= display.CursorBlink; ix++ { | ||
if err = dev.MoveTo(dev.MinRow()/2+1, dev.MinCol()); err != nil { | ||
result = append(result, err) | ||
} | ||
if _, err = dev.WriteString("Cursor: " + modes[ix]); err != nil { | ||
result = append(result, err) | ||
} | ||
if err = dev.Cursor(ix); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
if err = dev.Cursor(display.CursorOff); err != nil { | ||
result = append(result, err) | ||
} | ||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
} | ||
if err = dev.Cursor(display.CursorBlink + 1); err == nil { | ||
result = append(result, errors.New("did not receive expected error on Cursor() with invalid value")) | ||
} | ||
|
||
// Test Move Forward and Backward. 2 Should overwrite the 1 | ||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
if _, err = dev.WriteString("Testing >"); err != nil { | ||
result = append(result, err) | ||
} | ||
if err = dev.Move(display.Forward); err != nil { | ||
result = append(result, err) | ||
} | ||
if err = dev.Move(display.Forward); err != nil { | ||
result = append(result, err) | ||
} | ||
for ix := range 10 { | ||
if _, err = dev.WriteString(fmt.Sprintf("%d", ix)); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
if err = dev.Move(display.Backward); err != nil { | ||
result = append(result, err) | ||
} | ||
} | ||
if err = dev.Move(display.Down + 1); err == nil { | ||
result = append(result, errors.New("did not receive expected error on Move() with invalid value")) | ||
} | ||
|
||
// Test Display on/off | ||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
if _, err = dev.WriteString("Set dev off"); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
if err = dev.Display(false); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
if err = dev.Display(true); err != nil { | ||
result = append(result, err) | ||
} | ||
if err = dev.Clear(); err != nil { | ||
result = append(result, err) | ||
} | ||
if _, err = dev.WriteString("Set dev on"); err != nil { | ||
result = append(result, err) | ||
} | ||
time.Sleep(pauseTime) | ||
|
||
if interactive { | ||
for _, e := range result { | ||
if !errors.Is(err, display.ErrNotImplemented) { | ||
fmt.Println(e) | ||
} | ||
} | ||
} | ||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright 2024 The Periph Authors. All rights reserved. | ||
// Use of this source code is governed under the Apache License, Version 2.0 | ||
// that can be found in the LICENSE file. | ||
|
||
package display | ||
|
||
import ( | ||
"errors" | ||
) | ||
|
||
type CursorDirection int | ||
|
||
const ( | ||
// Constants for moving the cursor relative to it's current position. | ||
// | ||
// Move the cursor one unit back. | ||
Backward CursorDirection = iota | ||
// Move the cursor one unit forward. | ||
Forward | ||
Up | ||
Down | ||
) | ||
|
||
type CursorMode int | ||
|
||
const ( | ||
// Turn the cursor Off | ||
CursorOff CursorMode = iota | ||
// Enable Underline Cursor | ||
CursorUnderline | ||
// Enable Block Cursor | ||
CursorBlock | ||
// Blinking | ||
CursorBlink | ||
) | ||
|
||
// TextDisplay represents an interface to a basic character device. It provides | ||
// standard methods implemented by a majority of character LCD devices. Pixel | ||
// type displays can implement this interface if desired. | ||
type TextDisplay interface { | ||
// Enable/Disable auto scroll | ||
AutoScroll(enabled bool) (err error) | ||
// Return the number of columns the display supports | ||
Cols() int | ||
// Clear the display and move the cursor home. | ||
Clear() (err error) | ||
// Set the cursor mode. You can pass multiple arguments. | ||
// Cursor(CursorOff, CursorUnderline) | ||
// | ||
// Implementations should return an error if the value of mode is not | ||
// mode>= CursorOff && mode <= CursorBlink | ||
Cursor(mode ...CursorMode) (err error) | ||
// Move the cursor home (MinRow(),MinCol()) | ||
Home() (err error) | ||
// Return the min column position. | ||
MinCol() int | ||
// Return the min row position. | ||
MinRow() int | ||
// Move the cursor forward or backward. | ||
Move(dir CursorDirection) (err error) | ||
// Move the cursor to arbitrary position. Implementations should return an | ||
// error if row < MinRow() || row > (Rows()-MinRow()), or col < MinCol() | ||
// || col > (Cols()-MinCol()) | ||
MoveTo(row, col int) (err error) | ||
// Return the number of rows the display supports. | ||
Rows() int | ||
// Turn the display on / off | ||
Display(on bool) (err error) | ||
// return info about the display. | ||
String() string | ||
// Write a set of bytes to the display. | ||
Write(p []byte) (n int, err error) | ||
// Write a string output to the display. | ||
WriteString(text string) (n int, err error) | ||
} | ||
|
||
type Intensity int | ||
|
||
// Interface for displays that support a monochrome backlight. Displays that | ||
// support RGB Backlights should implement this as well for maximum | ||
// compatibility. | ||
// | ||
// Many units that support this command write the value to EEPROM, which has a | ||
// finite number of writes. To turn the unit on/off, use TextDisplay.Display() | ||
type DisplayBacklight interface { | ||
Backlight(intensity Intensity) error | ||
} | ||
|
||
// Interface for displays that support a RGB Backlight. E.G. the Sparkfun SerLCD | ||
type DisplayRGBBacklight interface { | ||
RGBBacklight(red, green, blue Intensity) error | ||
} | ||
|
||
type Contrast int | ||
|
||
// Interface for displays that support a programmable contrast adjustment. | ||
// As with SetBacklight(), many devices serialize the value to EEPROM, | ||
// which support only a finite number of writes, so this should be used | ||
// sparingly. | ||
type DisplayContrast interface { | ||
Contrast(contrast Contrast) error | ||
} | ||
|
||
var ErrNotImplemented = errors.New("not implemented") | ||
var ErrInvalidCommand = errors.New("invalid command") |