The last article in the series presented how the user interface should look like and behave. This article takes the first step towards the implementation. First it will describe the Daisy Patch Hardware Abstraction Layer which provides a simplyfied way to interact with the hardware. Afterwards we will use the abstraction layer to implement a small example application reading the encoder and writing on the screen.
Daisy Patch Abstraction Layer#
Electrosmith provides libraries with abstraction layers for the different daisy hardware variants. The hardware abstraction for the daisy patch is implemented in the class daisy::DaisyPatch
. It has the attribute display
of type OledDisplay<SSD130x4WireSpi128x64Driver>
that allows control of the display on the daisy patch. Additionally it has an attribute encoder
of type Encoder
that provides the mean to read the recent changes of the encoder.
The Class OledDisplay
#
The display is monochrome, that means it only supports two states for each pixel, on and off. All drawing methods in OledDisplay
take a boolean parameter for the color, true for white or on and false for black or off. For example, calling Fill(False)
clears the display, while Fill(true)
turns every pixel on the display on.
OledDisplay
don’t communicate with the display immediately. Calling Update
finalizes drawing and sends the new data to the display.The drawing methods used for the implementation are summarized in the table below:
Method | Description | Arguments |
---|---|---|
Fill | Fills the screen with the given color | bool on - color |
SetCursor | Moves the cursor for text output to the given coordinates | uint_16t x - x coordinateuint_16t y - y coordinate |
WriteString | Outputs text starting at the current cursor position | const char * str - string to writeFontDef font - definition of the fontbool on - color |
DrawRect | Draws the rectangle defined by the four coordinates | uint_fast8_t x1 - x coordinate for the upper left pointuint_fast8_t y1 - y coordinate for the upper left pointuint_fast8_t x2 - x coordinate for the lower left pointuint_fast8_t y2 - y coordinate for the lower left point |
Update | Update the screen |
The Class Encoder
#
OledDisplay
provides the methods to draw on the screen, but we also need to be able to read the changes of the encoder. This functionality is available in the class Encoder
.
Encoder
doesn’t get automatically updated on every change, similar to display updates. Debounce
receives the changes to the encoder hardware and updates the class. To update all inputs, call ProcessAllControls
provided by the class DaisyPatch
.Encoder
has several methods to read the encoder state. The methods used in the implementation are summarized below:
Method | Description |
---|---|
RisingEdge | Returns true if the encoder was just pressed |
Increment | Returns 1 if the encoder was turned clockwisereturns -1 if the encoder was turned counter-clockwisereturns 0 if it was not turned |
Debounce | Reads and interprets the raw input. Called by ProcessAllControls |
A Simple Example User Interface#
The prerequisite for following this article is that your development environment for daisy is already set up. If not, follow the setup instructions in the Daisy Wiki. Once the developement environment is set up, we create a new project using the helper.py
script from the daisy examples repository.
python ./helper.py -b patch create MyProjects/example_ui
The parameter -b
determines the daisy board, in our case that is patch
for daisy patch and create
is the command which tells the script to create a new project in the given directory. The helper script create a daisy patch program that copies the input from the audio ins to the audio outs.
First we need to change where the inputs are processed. The example application processes inputs in the audio callback, which we want to move from the audio handler to the loop in main
. Delete the line hw.ProcessAllControls();
from the function AudioCallback
. Next we’ll change the endless loop so that it first processes the controls, then draws on the display and finally waits for 1 ms.
Add the following in the while loop in main
:
// Read the input from the patch and update the hardware abstraction layer
hw.ProcessAllControls();
// Move the cursor to position 0, 0
hw.display.SetCursor(0, 0);
// Check the direction of the controller
if (hw.encoder.Increment() == -1) {
// Write counter-clockwise because the controller was rotated
// counter-clockwise
hw.display.WriteString("counter-clockwise", Font_7x10, true);
} else if (hw.encoder.Increment() == 1) {
// Write clockwise because the controller was rotated clockwise.
// Note that we pad the string with spaces so that it is the same
// length as counter-clockwise because we do not clear the display.
hw.display.WriteString("clockwise ", Font_7x10, true);
}
// Check if button was pressed
if (hw.encoder.RisingEdge()) {
// Move below the other text
hw.display.SetCursor(0, 11);
// Print button pressed
hw.display.WriteString("Button pressed", Font_7x10, true);
}
// Paint
hw.display.Update();
// Wait for 1 ms
hw.DelayMs(1);
Afterwards prepare the daisy for flashing by pressing the reset and the boot button and the excute the task build_and_program_dfu
in vs code. Initially the screen will be blank. When rotating the encoder, the screen will display clockwise
or counter-clockwise
depending on the direction the encoder was rotated recently. When the encoder is clicked the screen will also display Button pressed
.
The code can be found on github.