Copyright | © Patrick Pelletier 2017 |
---|---|
License | BSD3 |
Maintainer | code@funwithsoftware.org |
Portability | Linux |
Safe Haskell | None |
Language | Haskell2010 |
This module contains everything you need to use an Adafruit character LCD and keypad kit from Haskell. The kit has a 16x2 character LCD, an RGB backlight which can produce 8 possible colors, and five buttons: Up, Down, Left, Right, and Select.
Since the physical LCD+Keypad Kit is a global resource, only one
PiLcd
should exist at a time. (Except in the very exceptional case
where you have more than one LCD+Keypad Kit connected to your
Raspberry Pi, at different addresses on the I²C bus.) If you create
more than one PiLcd
at once when you only have one physical
LCD+Keypad Kit, things will get very confused. Also, PiLcd
is not
threadsafe, so if you want to use the PiLcd
from more than one
thread, you will need to handle locking yourself.
However, PiLcd
is safe in the presence of async exceptions. (In
other words, updateDisplay
and other operations will either
atomically occur or not occur; they will not be interrupted in the
middle and leave the hardware in an inconsistent state.)
- openPiLcd :: LcdAddress -> LcdOptions -> IO PiLcd
- closePiLcd :: PiLcd -> IO ()
- turnOffAndClosePiLcd :: PiLcd -> IO ()
- data PiLcd
- data LcdAddress = LcdAddress {}
- defaultLcdAddress :: LcdAddress
- data LcdOptions = LcdOptions {}
- data RomCode
- defaultLcdOptions :: LcdOptions
- data Color
- setBacklightColor :: PiLcd -> Color -> IO ()
- data Button
- data ButtonDirection
- data ButtonEvent = ButtonEvent Button ButtonDirection
- getButtonEvent :: PiLcd -> IO (Maybe ButtonEvent)
- getButtons :: PiLcd -> IO Word8
- buttonSelect :: Word8
- buttonRight :: Word8
- buttonDown :: Word8
- buttonUp :: Word8
- buttonLeft :: Word8
- updateDisplay :: PiLcd -> [Text] -> IO ()
- charFromAsciiArt :: [String] -> [Word8]
- nativeChar :: Word8 -> Char
- data UiData = UiData {}
- data UiState = UiState {
- usList :: !Int
- usButtons :: !Int
- usInternal :: !InternalState
- data InternalState
- defaultUiState :: UiState
- runUi :: PiLcd -> UiData -> UiState -> IO (UiState, Bool)
- runUiUntilDone :: PiLcd -> UiData -> UiState -> IO UiState
- withPiLcd :: LcdAddress -> LcdOptions -> (PiLcd -> IO a) -> IO a
- withPiLcdThenTurnOff :: LcdAddress -> LcdOptions -> (PiLcd -> IO a) -> IO a
Creating a PiLcd
openPiLcd :: LcdAddress -> LcdOptions -> IO PiLcd Source #
Opens the LCD+keypad kit, at the specified address, with the specified options.
closePiLcd :: PiLcd -> IO () Source #
Closes the PiLcd
, leaving the display contents and
backlight setting untouched.
turnOffAndClosePiLcd :: PiLcd -> IO () Source #
Like closePiLcd
, but clears the display, turns off the
display, and turns off the backlight before closing the PiLcd
.
data LcdAddress Source #
Specifies how to connect to the LCD+Keypad Kit. laBus
should be 1 for
revision 2 Raspberry Pis and later. For revision 1 Pis (those with 256 MB
of RAM), the bus should be 0. If you need a way to automatically detect
this, consider using the piBoardRev
function in the
wiringPi package.
On the other hand, there is not much reason to ever change
laAddr
from the default 0x20. (The only reason would be if you changed
the address of your LCD+Keypad Kit by messing with the address bit
solder pads on the back. And probably the only reason you'd want to do
that is to connect more than one LCD+Keypad Kit. That should work, in
theory, but hasn't been tested.)
defaultLcdAddress :: LcdAddress Source #
Default values for LcdAddress
. Defaults to bus 1 and address 0x20.
The HD44780U LCD controller comes in two different variants with different character ROMs. (See Table 4 on pages 17-18 of the HD44780U datasheet.) Unfortunately, as best as I can interpret this exchange with Adafruit customer support, Adafruit ships a mixture of A00 ROMs and A02 ROMs, depending on what's available at the moment. ("We take what's available or we don't sell LCDs.") This is a bit annoying, since there doesn't seem to be any way to query the HD44780U to find out which ROM it has. So, the user has to test their LCD and then specify which ROM they have.
defaultLcdOptions :: LcdOptions Source #
Defaults to 2 lines, 16 columns, ROM code A00, and no additional custom characters.
Backlight color
The kit lacks PWM, so each of the red, green, and blue LEDs can be either on or off, yielding 8 possible colors.
Buttons
Indicates one of the five buttons on the LCD+Keypad kit.
data ButtonDirection Source #
Indicates whether a button was pressed or released.
data ButtonEvent Source #
Indicates a press or release of one of the five buttons on the LCD+Keypad kit.
getButtonEvent :: PiLcd -> IO (Maybe ButtonEvent) Source #
If a button has been pressed or released since the last call to
getButtonEvent
, returns information on that press or release as
a ButtonEvent
.
getButtons :: PiLcd -> IO Word8 Source #
Returns all of the buttons which are currently depressed, as a
bitwise "or" of buttonSelect
, buttonRight
, buttonDown
,
buttonUp
, and buttonLeft
.
Button bitmask values
buttonSelect :: Word8 Source #
buttonRight :: Word8 Source #
buttonDown :: Word8 Source #
buttonLeft :: Word8 Source #
Display
Displays Unicode text on the LCD. Only updates the parts of the LCD which have changed. Automatically manages custom characters, using the 5x8 fixed font for characters which are not built-in to the the LCD controller's ROM. Only eight distinct non-built-in characters can be on the display at any one time.
Only supports characters which are made up of a single code point. (In other words, combining marks are not supported.) If your input contains decomposed characters, consider using the unicode-transforms package to convert to Normalization Form C.
updateDisplay :: PiLcd -> [Text] -> IO () Source #
Update the display to contain the specified lines of text. This is done intelligently; i. e. only the characters which have changed are rewritten. The lines are truncated or padded with spaces to make them the width of the display. Similarly, the list of lines is truncated or padded with blank lines to make it the height of the display.
charFromAsciiArt :: [String] -> [Word8] Source #
Converts a glyph from ASCII art to binary representation. Expects a list of eight lines, where each line contains five characters. A dot is considered "off" if the character is a space, or "on" if it is any other character. Returns the glyph in the format expected for custom characters: eight bytes where each byte contains data in the least significant five bits.
nativeChar :: Word8 -> Char Source #
If for some reason you want to specify a character in the native
8-bit encoding of the LCD, instead of in Unicode, just call
nativeChar
on the 8-bit character value. This maps it to a
region of the Private Use Area which is treated specially.
User Interface
Displays a simple user interface. The first line of the display is used as a "list box", where the user can scroll through a list of items one at a time using the up and down buttons. The second line of the display is used for virtual "buttons", such as "OK" and "Cancel". The user uses the left and right buttons to select a virtual "button". When the user presses the Select button, the interaction is considered done, and the calling program is given the list item and button selection that the user made.
If there is only one item in the list, the up and down buttons won't do anything, and the "↕" indicator will not be displayed. So, if you want to display a static line of text and some buttons, just create a single-element list containing the line of text.
The data to be displayed in the user interface.
The current state of the user interaction.
UiState | |
|
data InternalState Source #
Opaque data.
defaultUiState :: UiState Source #
Defaults to displaying the first list item (index 0) and highlighting the first button (index 0).
:: PiLcd | |
-> UiData | Data to display in the UI |
-> UiState | Current state of the interaction |
-> IO (UiState, Bool) | New UI state, and a flag indicating whether the interaction is done (i. e. user has pressed and released the "Select" button) |
Updates the display based on the given UI state, and updates the UI state based on a button press or release which may have occurred since the last call.
:: PiLcd | |
-> UiData | Data to display in the UI |
-> UiState | Initial state (i. e. which list item and button start out highlighted) |
-> IO UiState | Final state (selections user made) |
Calls runUi
repeatedly, with a short delay in between, feeding back
the state, until the interaction is "done". (i. e. user has
pressed and released the "Select" button) The final state is
returned, which indicates the selection the user has made.
Exception handling
These are specialized forms of bracket
, where an uncaught
exception causes the backlight to turn red and the exception to
be displayed on the LCD. Then the exception is rethrown. This
is useful for headless setups, where the LCD is the primary
means of user interface.
:: LcdAddress | |
-> LcdOptions | |
-> (PiLcd -> IO a) | Body computation |
-> IO a | Result returned by body |
Opens a PiLcd
with the given LcdAddress
and LcdOptions
, passes
the PiLcd
to the body computation, and then closes the PiLcd
,
regardless of whether the body exited normally or exceptionally.
If an exception occurred, the exception is shown on the LCD.
:: LcdAddress | |
-> LcdOptions | |
-> (PiLcd -> IO a) | Body computation |
-> IO a | Result returned by body |
Like withPiLcd
, but in the non-exceptional case, the display is
cleared and the backlight is turned off.