In this project we're going to learn how easy is to use TextDisplayMenu, a new Meadow.Foundation library to make interactive menus with very little code. With this library, all we need to provide is the display the menu will be rendered on, and a menu structure which can be written in C# or load a Json file and the menu will be generated for you. To interact with it, we can use push buttons, analog joystick, even rotary encoders!
Meadow.Foundationa platform for quickly and easily building connected things using.NET on Meadow. Created by Wilderness Labs, it's completely open source and maintained by the Wilderness Labs community.
If you're new working with Meadow, I suggest you go to the Getting Started w/ Meadow by Controlling the Onboard RGB LEDproject to properly set up your development environment.
Step 1 - Assemble the circuitWire your project like this:
Create a new Meadow Application project in Visual Studio 2019 for Windows or macOS and name it MeadowMenu.
Step 3 - Add the required NuGet packagesFor this project, search and install the following NuGet packages:
Step 4 - Add the menu Json fileAs I've mentioned before, you can design your menu in a json file, and passing an array of bytes of this file to TextDisplayMenu's constructor it will generate the menu by itself. Download this menu.json file and be sure to set the Build Action to Embedded Resource.
Step 5 - Write the code for MeadowMenuCopy the following code below:
public class MeadowApp : App<F7Micro, MeadowApp>
{
Menu menu;
St7789 st7789;
RgbPwmLed onboardLed;
GraphicsLibrary graphics;
PushButton next, previous, select;
public MeadowApp()
{
Initialize();
}
void Initialize()
{
onboardLed = new RgbPwmLed(device: Device,
redPwmPin: Device.Pins.OnboardLedRed,
greenPwmPin: Device.Pins.OnboardLedGreen,
bluePwmPin: Device.Pins.OnboardLedBlue,
3.3f, 3.3f, 3.3f,
Meadow.Peripherals.Leds.IRgbLed.CommonType.CommonAnode);
onboardLed.SetColor(Color.Red);
var config = new SpiClockConfiguration(
speedKHz: 12000,
mode: SpiClockConfiguration.Mode.Mode3);
var spiBus = Device.CreateSpiBus(
clock: Device.Pins.SCK,
mosi: Device.Pins.MOSI,
miso: Device.Pins.MISO,
config: config);
st7789 = new St7789
(
device: Device,
spiBus: spiBus,
chipSelectPin: null,
dcPin: Device.Pins.D01,
resetPin: Device.Pins.D00,
width: 240, height: 240
);
graphics = new GraphicsLibrary(st7789)
{
CurrentFont = new Font12x20(),
};
graphics.Clear();
var menuData = LoadFromJson("menu.json");
//var menuData = LoadFromCode();
menu = new Menu(graphics, menuData, false);
next = new PushButton(Device, Device.Pins.D03, ResistorMode.PullUp);
next.Clicked += (s, e) => { menu.Next(); };
select = new PushButton(Device, Device.Pins.D04, ResistorMode.PullUp);
select.Clicked += (s, e) => { menu.Select(); };
previous = new PushButton(Device, Device.Pins.D02, ResistorMode.PullUp);
previous.Clicked += (s, e) => { menu.Previous(); };
onboardLed.SetColor(Color.Green);
menu.Enable();
}
byte[] LoadFromJson(string filename)
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = $"MeadowMenu.{filename}";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
return ms.ToArray();
}
}
}
}
In this meadow App, everything happens in the Initialize()
method.
- First we initialize an
RgbPwmLed
object using the onboardLED which we can use to give us feedback for when the app is ready to receive IO. You'll see the LED turns Red first to indicate the initialization process has begun. - The ST7789 display is initialized specifying the pins which are connected to Meadow. Right away we also instantiate a
GraphicsLibrary
object passing the display object we just created. We'll use this to create theTextDisplayMenu
object. Also notice that we're setting theCurrentFont
property so the menu's text is displayed with that font size. - Next is loading the Menu. Notice we call
LoadFromJson()
function which is a function that opens the menu.json file in a MemoryStream, and returns an array of bytes which is then passed in the Menu's constructor along with the graphics object. - The push buttons next, select and previous are all initialized in pins D03, D04 and D02 respectively. We're using ResistorMode.PullUp so we don't need to use an external resistors, and we're all registering the
Clicked()
event. In each event handler, we simply call the correspondingMenu
public method for interaction:Next()
,Select()
andPrevious()
. - Finally, we turn the onboard LED to glow Green and we call
menu.Enable()
so the menu is ready to receive user interaction with the push buttons.
Click the Run button in Visual Studio. It should look like to the following GIF:
Use the white Push Buttons to move the cursor (represented as an asterisk on the right side of the selected item) up and down the menu. You can see that submenus are indicated with a > character on the right side of the row of that item. Once the cursor is on an item, push the yellow push button to select it to either edit a value or navigate inside a sub menu.
Once inside a sub menu, you can select the top item to go back to the parent menu (item represented as < Back)
.
As we mentioned before, we can create our menu in C# code if we prefer, depending on our use case. Simply comment the LoadFromJson("menu.json")
instruction and uncomment LoadFromCode()
instruction instead in the Initialize method:
//var menuData = LoadFromJson("menu.json");
var menuData = LoadFromCode(); // <=== Optional Step
menu = new Menu(graphics, menuData, false);
Now we copy the LoadFromCode()
function:
MenuItem[] LoadFromCode()
{
var subMenuItems2 = new List<MenuItem>();
subMenuItems2.Add(new MenuItem(
text: "Sub Item C"));
subMenuItems2.Add(new MenuItem(
text: "Sub Item D"));
subMenuItems2.Add(new MenuItem(
text: "Sub Item E"));
var subMenuItems1 = new List<MenuItem>();
subMenuItems1.Add(new MenuItem(
text: "Sub Item A"));
subMenuItems1.Add(new MenuItem(
text: "Sub Item B"));
subMenuItems1.Add(new MenuItem(
text: "Submenu 2",
subItems: subMenuItems2.ToArray()));
var menuItems = new List<MenuItem>();
menuItems.Add(new MenuItem(
text: "Value 1: {value}",
id: "displayValue1",
value: 77));
menuItems.Add(new MenuItem(
text: "Edit Value 1",
id: "myValue1",
type: "Value",
value: 77));
menuItems.Add(new MenuItem(
text: "Value 2: {value}",
id: "displayValue1",
value: 25));
menuItems.Add(new MenuItem(
text: "Edit Value 2",
id: "myValue2",
type: "Value",
value: 25));
menuItems.Add(new MenuItem(
text: "submenu 1",
subItems: subMenuItems1.ToArray()));
return menuItems.ToArray();
}
The menu designed here is the equivalent with the Json file, so when we run the app again, we should see the same exact menu is generated, only difference is that this menu will be loaded much faster than using the menu.json
file.
This project is only the tip of the iceberg in terms of the extensive exciting things you can do with Meadow.Foundation.
- It comes with a huge peripheral driver library with drivers for the most common sensors and peripherals.
- The peripheral drivers encapsulate the core logic and expose a simple, clean, modern API.
- This project is backed by a growing community that is constantly working on building cool connected things and are always excited to help new-comers and discuss new projects.
Comments