Here’s some source code and background information how I used my old Nextion display (with serial interface) with the Meadow.
The Nextion has many different capabilities, like built-in scripting, adaptable widgets that can be controlled dynamically. To use this in real world applications, a serial two-ways-communication needs to be implemented, like it is in TCP. One side sends a request and expects an answer, often within some time limit.
Luckily, for my use case (doing some tests with the meadow), this wasn’t necessary, I just wanted to display strings with information on the display and needed some buttons to press. For this, a one-way-communication is sufficient, I just sent some strings to the display for setting the labels, or, after a button on the display was pressed, the display sent some special chars to my program. No answer was necessary.
The approach I used is very simple, I’m hesitating to show it here, because it is so poor. But the purpose was only to make it work for me, not to write some Nextion C# library or adapt an existing one to C#. If you should have the need to implement two-way-communication (and this need will come sooner as you expect), I suggest to take a look at the great ITEAD C++ library on Github, which I mentioned also in my post some days ago: GitHub - itead/ITEADLIB_Arduino_Nextion
Maybe it would make sense to get in touch with the guys there, maybe they would be interested in adapting it, I don’t know. If not, or if you want to write some small functions for getting some answering strings from the nextion by yourself, it is worth to take a look at the methods recvRetNumber() and recvRetString() in the source file NexHardware.cpp there, to get inspired.
Concerning my use case: I made a small program, consisting in two source files, MeadowApp.cs and NextionDisplay.cs.
On the display, there are some labels to display the program version, some status information, and the current time. There are three buttons to turn the LED on the meadow to red, green or off. That’s all.
The Nextion display gets its power via the 5V pin on the nextion, which (hopefully, I don’t know for sure) is directly connected to the 5V from the USB. Its RX and TX lines are connected to the appropriate (opposite) COM4 Pins on the Meadow.
Here’s the code. I wish you much fun and success, it’s great how easy even complex interfaces can be built with the Nextion.
==== Begin MeadowApp.cs ====
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using Meadow;
using Meadow.Devices;
using Meadow.Hardware;
using Nextion;
namespace NextionSimpleTest
{
// Change F7FeatherV2 to F7FeatherV1 for V1.x boards
public class MeadowApp : App<F7FeatherV2>
{
// consts etc.
const string Version = "1.10";
const string NextionPortName = "COM4";
const int NextionBaudRate = 115200;
// to deal with our current mode
enum Modes { Off, Red, Green }
Modes mode = Modes.Off;
// indexes of nextion pages (we only have one...)
const int Page_Main = 0;
// names of nextion labels
const string Name_tVersLabel = "tVersLabel";
const string Name_tVersValue = "tVersValue";
const string Name_tStatus = "tStatus";
const string Name_tTime = "tTime";
// ids of nextion controls
const int Id_tVersLabel = 2;
const int Id_tVersValue = 3;
const int Id_tStatus = 4;
const int Id_tTime = 7;
const int Id_bRed = 5;
const int Id_bGreen = 1;
const int Id_bOff = 6;
// hardware
IDigitalOutputPort redLed;
IDigitalOutputPort greenLed;
NextionDisplay nextionDisplay;
// set up Meadow and Nextion
public override Task Initialize()
{
Console.WriteLine(new string('-', 30));
Console.WriteLine("Initialize, Version " + Version);
Console.WriteLine(new string('=', 30));
redLed = Device.CreateDigitalOutputPort(Device.Pins.OnboardLedRed);
greenLed = Device.CreateDigitalOutputPort(Device.Pins.OnboardLedGreen);
try
{
var port = Device.CreateSerialPort(Device.PlatformOS.GetSerialPortName(NextionPortName), NextionBaudRate, 8, Meadow.Hardware.Parity.None, Meadow.Hardware.StopBits.One, 512);
nextionDisplay = new NextionDisplay(port, NextionCommandHandler);
Console.WriteLine(string.Format("Nextion display on port {0} successfully connected.", NextionPortName));
}
catch (Exception ex)
{
Console.WriteLine(string.Format("ERROR: could not connect to Nextion Display on serial port {0}. Exception: {1}", NextionPortName, ex.Message));
}
nextionDisplay.SendCommand_SetPage(Page_Main);
nextionDisplay.SendCommand_SetText(Name_tVersValue, Version);
RefreshStatusText();
return base.Initialize();
}
// show on the display what is going on
void RefreshStatusText()
{
string text;
switch (mode)
{
case Modes.Red:
text = "red led is on";
break;
case Modes.Green:
text = "green led is on";
break;
case Modes.Off:
text = "led is off";
break;
default:
text = "(unknown mode)";
break;
}
nextionDisplay.SendCommand_SetText(Name_tStatus, text);
}
// for debugging
void ComSerialPort_MessageReceived(object sender, SerialMessageData e)
{
Console.WriteLine($"Message received: {e.GetMessageString(Encoding.ASCII)}");
}
// e.g. key pressed on Nextion
void NextionCommandHandler(int pageId, int controlId, int data)
{
Console.WriteLine(string.Format("Nextion command received, pageId: {0}, controlId: {1}", pageId, controlId));
if (pageId == Page_Main)
{
switch (controlId)
{
case Id_bRed:
mode = Modes.Red;
Console.WriteLine("Mode set to red");
break;
case Id_bGreen:
mode = Modes.Green;
Console.WriteLine("Mode set to green");
break;
case Id_bOff:
mode = Modes.Off;
Console.WriteLine("Mode set to off");
break;
default:
// unknown id (not yet handled)
break;
}
RefreshStatusText();
}
else
{
// handle other pages here
}
}
public override async Task Run()
{
Console.WriteLine("Run...");
await Work();
}
// loop for refreshing the Nextion ports etc. due to the current mode
Modes oldMode = Modes.Off;
string oldTimeString = null;
Task Work()
{
Console.WriteLine("Working started...");
while (true)
{
// evtl. change leds state
if (mode != oldMode)
{
switch (mode)
{
case Modes.Red:
redLed.State = true;
greenLed.State = false;
break;
case Modes.Green:
redLed.State = false;
greenLed.State = true;
break;
default:
redLed.State = false;
greenLed.State = false;
break;
}
oldMode = mode;
}
// evtl. change displayed time
var timeString = DateTime.Now.ToString("HH:mm:ss");
if (timeString != oldTimeString)
{
nextionDisplay.SendCommand_SetText(Name_tTime, timeString, false);
oldTimeString = timeString;
}
// give (e.g. serial) events some time
Thread.Sleep(100);
}
}
}
}
==== End MeadowApp.cs ====
==== Begin NextionDisplay.cs ====
using System;
using System.Collections.Generic;
using System.Text;
using Meadow;
using Meadow.Hardware;
namespace Nextion
{
public class NextionDisplay : IDisposable
{
ISerialPort Port { get; set; }
List<byte> ReceivedBytes { get; set; } = new List<byte>();
public delegate void OnCommand(int pageId, int controlId, int data);
event OnCommand OnCommandHandler;
public NextionDisplay(ISerialPort port, OnCommand onCommandHandler)
{
OnCommandHandler = onCommandHandler;
this.Port = port;
try
{
Port.Open();
Port.DataReceived += SerialPort_DataReceived;
Console.WriteLine(string.Format("Serial port {0} for NEXTION successfully opened.", Port));
}
catch (Exception ex)
{
Console.WriteLine(string.Format("ERROR: could not open serial port {0} for NEXTION. Exception: {1}", Port, ex.Message));
throw;
}
}
void Disconnect()
{
if (Port?.IsOpen ?? false)
Port?.Close();
Port = null;
}
private void SerialPort_DataReceived(object sender, Meadow.Hardware.SerialDataReceivedEventArgs e)
{
Console.WriteLine("Stop 11");
var buffer = new byte[1];
var sp = (ISerialPort)sender;
while (sp.BytesToRead > 0)
{
var nb = sp.Read(buffer, 0, 1);
if (nb != 1)
{
Console.WriteLine("Read-Error: " + nb);
throw new Exception("0805-1811-01-933");
}
var b = buffer[0];
Console.WriteLine(string.Format("Read: {0} ({0:x})", b));
ReceivedBytes.Add(b);
if (CommandReceived(out var commandBytes))
{
ExecCommand(commandBytes);
ReceivedBytes.Clear();
}
}
Console.WriteLine("Stop 19");
}
// eAAAyyy
// 0123456
bool CommandReceived(out List<byte> commandBytes)
{
Console.WriteLine("Stop 21");
commandBytes = null;
var len = ReceivedBytes.Count;
if (len < 4)
return false;
if (ReceivedBytes[len - 3] != 0xff || ReceivedBytes[len - 2] != 0xff || ReceivedBytes[len - 1] != 0xff)
return false;
commandBytes = new List<byte>();
for (var i = 1; i < len - 3; i++)
commandBytes.Add(ReceivedBytes[i]);
Console.WriteLine("Stop 29");
return true;
}
void ExecCommand(List<byte> commandBytes)
{
var len = commandBytes.Count;
Console.WriteLine("Stop 31, len=" + len);
if (len < 2) // happens if the communication with the Nextion was started, and he sends 26 and 3 times 0xff
return;
var page = (int)commandBytes[0];
var control = (int)commandBytes[1];
var data = len >= 3 ? (int)commandBytes[2] : 0;
Console.WriteLine(string.Format("Stop 33, page={0}, control={1}, data={2}", page, control, data));
OnCommandHandler?.Invoke(page, control, data);
}
public void Dispose()
{
Disconnect();
}
public void SendCommand(string s, bool writeInfoToConsole = true)
{
if (Port != null)
{
if (Port.IsOpen)
{
Port.Write(Encoding.ASCII.GetBytes(s));
Port.Write(new byte[] { 0xff, 0xff, 0xff }, 0, 3);
if (writeInfoToConsole)
Console.WriteLine(string.Format("Sent to Nextion: \"{0}\"", s));
}
else
Console.WriteLine("ERROR: Port not open");
}
else
Console.WriteLine("ERROR: Port not initialized");
}
public void SendCommand_SetPage(int page, bool writeInfoToConsole = true)
{
SendCommand(string.Format("page {0}", page), writeInfoToConsole);
}
public void SendCommand_SetText(string controlName, string newValue, bool writeInfoToConsole = true)
{
SendCommand(string.Format("{0}.txt=\"{1}\"", controlName, newValue), writeInfoToConsole);
}
public void SendCommand_ShowHide(int control, bool show, bool writeInfoToConsole = true)
{
SendCommand(string.Format("vis {0},{1}", control, show ? 1 : 0), writeInfoToConsole);
}
}
}
==== End NextionDisplay.cs ====