-
Notifications
You must be signed in to change notification settings - Fork 15
display: Create Interfaces for text displays #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
af2f803
Create Interfaces for text displays
gsexton e37225a
Add copyright
gsexton 621c0de
Fix lint errors, and record errors in tests.
gsexton 08c802a
Fix shadow lint
gsexton 0214171
Print errors in interactive mode.
gsexton 3b04f68
Add errors
gsexton a728b10
Update error dump to skip not implemented errors.
gsexton 908582a
Lint errors
gsexton 6f1c295
Lint errors
gsexton a849c37
PR Review Feedback
gsexton File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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") | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.