Controlling a Nextion touch display


#1

The code below works well to control a Nextion 3.2" display with a Photon. When I load the same code on the BluzDK, I get certain LED blinking patterns indicating a problem.
The OTA update seems to go well, but then the LED blinks slow green. If I try a second time, I might get blinking yellow or magenta. The Bluz never comes online and the only way to recover is a factory reset.
The debug messages from the gateway shield do not show any handshake with the BluzDK, only the cloud and BLE 3.
The header files come from the ITEADLIB_Nextion library on WebIDE.
Has anyone tried this library on a BluzDK? What could be hanging up the BluzDK and not the Photon?

/**
 * @example CompButton.ino
 *
 * @par How to Use
 * This example shows that when the button component on the Nextion screen is released,
 * the text of this button will plus one every time.
 *
 * @author  Wu Pengfei (email:<pengfei.wu@itead.cc>)
 * @date    2015/7/10
 * @copyright
 * Copyright (C) 2014-2015 ITEAD Intelligent Systems Co., Ltd. \n
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 */

 /**
  * Define DEBUG_SERIAL_ENABLE to enable debug serial.
  * Comment it to disable debug serial.
  */
 //#define DEBUG_SERIAL_ENABLE  // include this in your sketch to activate debug output

#include "Nextion.h"
USARTSerial& nexSerial = Serial1;

// Declare a button object [page id:0,component id:1, component name: "b0"].
NexButton UP = NexButton(0, 4, "UpButt");
NexButton DOWN = NexButton(0, 3, "DownButt");
NexButton HOLD = NexButton(0, 9, "Hold");

// Declare a text object [page id:0,component id:2, component name: "textNumber"].
NexText SetTempText = NexText(0, 2, "SetTemp");

int number = 50;
String HeatMode = "AUTO";
char buffer[30] = {0};

/*
 * Register a button object to the touch event list.
 */
NexTouch *nex_listen_list[] =
{
    &UP,
    &DOWN,
    &HOLD,
    NULL
};

/*
   Button component push callback function.
   These functions increment the value of the text component when the button is pressed.
   The press or release event for the button is set with the Nextion editor by checking the box
   "Send Component ID" for the touch and/or release event.
*/
void UPbuttonPushCallback(void *ptr)
{
//  dbSerialPrintln("UPbuttonPushCallback");
//  dbSerialPrint("ptr=");
//  dbSerialPrintln((uint32_t)ptr);

  if (number < 90)
  {
    number += 1;
    memset(buffer, 0, sizeof(buffer)); // clear buffer
    itoa(number, buffer, 10);
    SetTempText.setText(buffer);
  }
}

void DOWNbuttonPushCallback(void *ptr)
{
//  dbSerialPrintln("DOWNbuttonPushCallback");
//  dbSerialPrint("ptr=");
//  dbSerialPrintln((uint32_t)ptr);

  if (number > 50)
  {
    number -= 1;
    memset(buffer, 0, sizeof(buffer)); // clear buffer
    itoa(number, buffer, 10);
    SetTempText.setText(buffer);
  }
}

void HOLDbuttonPushCallback(void *ptr)
{
//  dbSerialPrintln("HOLDbuttonPushCallback");
//  dbSerialPrint("ptr=");
//  dbSerialPrintln((uint32_t)ptr);

  if (HeatMode == "AUTO")
  {
    HeatMode = "HOLD";
    digitalWrite(D7, HIGH);
  }
  else
  {
    HeatMode = "AUTO";
    digitalWrite(D7, LOW);
  }

//    memset(buffer, 0, sizeof(buffer)); // clear buffer
//    itoa(number, buffer, 10);
    HOLD.setText(HeatMode);
}

void setup()
{
    /* Set the baudrate which is for debug and communicate with Nextion screen. */
    nexInit();

    /* Register the push event callback function of the current button component. */
    UP.attachPush(UPbuttonPushCallback, &UP);
    DOWN.attachPush(DOWNbuttonPushCallback, &DOWN);
    HOLD.attachPush(HOLDbuttonPushCallback, &HOLD);

    pinMode(D7, OUTPUT);

//    dbSerialPrintln("setup done");
}

void loop()
{
    /*
     * When a pop or push event occured every time,
     * the corresponding component[right page id and component id] in touch event list will be asked.
     */
    nexLoop(nex_listen_list);
    delay(1000);
}


#2

I didn’t look at this library, but you can go through the code and try to find commands that are specific to the Photon. In particular, the library may contain assembly for the specific processor on the Photon. A lot of times it has to do with timing specific code or IO control. You can look for code that checks for the PLATFORM type, that is one obvious way that may help find it.

A lot of the libraries in the IDE were written for a specific platform, so they may not immediately work with bluz. However, with a little tweaking, a lot of them can work.


#3

Thanks for responding, Eric.
I did not find a PLATFORM statement, but there are checks for #if defined(SPARK) to insert Particle.process statements. This directive was not defined anywhere and defining it did not solve the problem.
The NexHardware.cpp file also has delays sprinkled throughout.
There are lots of debug statements directed to the Serial port (not Serial1), but BluxDK does not support it. I changed the code to send everything to Serial1. The code compiles, but I wonder if the same serial port can be defined with two different references.
I will try the Photon method for reading the serial port (still haven’t gotten around to buying a FTDI adapter).

Thanks,
Pescatore


#4

I have been debugging this issue by sending messages to the serial line. It’s a delicate dance of managing the Serial and Serial1 messages. Now I wish BluzDK supported the Serial port.

What I found is that the code running on the BluzDK gets stuck (infinite loop) in the function sendCommand() in the NexHardware.cpp library.
This routine first clears the serial port buffer with:

while (nexSerial.read() >= 0); // flush RX buffer only

According to the docs, Serial.read() returns -1 if the buffer is empty. I verified this to be true with a Photon by sending the result of nexSerial.read() to the Serial port, so the Photon does not get stuck here and continues to run. However, with the BluzDK I get a zero for an empty buffer.
So I removed the “=” sign and got past this point. Now the while statement looks like:

while (nexSerial.read() > 0); // flush RX buffer only

I don’t know if this is a good fix. Does checking for zero bytes have the same outcome as testing for -1?
BTW, there is another place in the same .cpp file in function sendSkript() that needs the same fix.

Next issue. It seems the BluzDK does not have the power to process the serial port and stay connected to the gateway shield. It can detect button presses and update the touch display, but it loses the BLE connection when it has to process data from the display. It reconnects every time, but it takes a few seconds and the display is not functional in that time.

I found that most functions in this library have a “housekeeping” code block. I am guessing that @ScruffR added these to avoid losing the cloud connection:

#if defined(SPARK)
    Particle.process();
#endif
    delay(50); // was 10

I defined SPARK, tried increasing the delay and added a few more of these blocks where the serial port is handled. They don’t help the BluzDK.
One thing I am not clear on (excuse my software ignorance) is where should SPARK be defined. I put it in all the .cpp files that have this code block.

What else can I try? Is the BluzDK not a good platform for this application?

Thanks for reading!
Pescatore


#5

First, good catch on the serial.read() issue! That is a bug in our firmware and I just checked in a fix for it. Your fix will work just fine both now and after the fix, so you can leave that as-is.

SPARK is defined as a compile-time flag by the firmware system, so it will be defined for you.

As for the disconnects, that is usually caused by blocking code. My guess is this can be fixed as well, but the thing to keep in mind about bluz is that the CPU processes your code, the Particle firmware code, and handles the BLE processing. The latter operates in the highest level of interrupts, so the BLE connection shouldn’t easily be lost by blocking code, but the cloud connection could.

My guess is it would just require some tweaks to work, bluz is a unique platform as it is based on a completely different processor than the Core/Photon, so the libraries sometimes just need to take those differences into consideration. One thing you can try to verify is to put the DK into local communication mode. This will turn off the cloud connection, so it shouldn’t be interrupted. You could start there and verify everything works, then try turning it back on and debug why it disconnects.


#6

Sometimes the blind squirrel finds a nut.
Where will this fix be released? My BluzDK runs 2.1.50. Any reasons to upgrade?

I smell another nut in the serial port handshake. The mere checking for presence of data in the serial port can make the BluzDK blink green.
The library uses while (nexSerial.available()) and nexSerial.read(); to read the event from the display.
I inserted Particle.process() statements before and inside this loop, but no success. Is this the right way to avoid blocking the operating system? I have not found any other blocking code.

Thanks,
Pescatore


#7

One other question I have is regarding the debug print statements (again software ignorance).
If the variable DEBUG_SERIAL_ENABLE is not defined, the dbSerial port is not initialized. What does the compiler do with the statements in the code? I am not sure if the #else statement below makes the compiler skip the while line, or it creates bogus instructions that end up occupying the processor.
Sample code:

//#define DEBUG_SERIAL_ENABLE  // include this in your sketch to activate debug output

#define dbSerial Serial
#ifdef DEBUG_SERIAL_ENABLE
#define dbSerialPrint(a)    dbSerial.print(a)
#define dbSerialPrintln(a)  dbSerial.println(a)
#define dbSerialBegin(a)    dbSerial.begin(a)
#else
#define dbSerialPrint(a)    ; // used to be do {} while(0)
#define dbSerialPrintln(a)  ; // used to be do {} while(0)
#define dbSerialBegin(a)    ; // used to be do {} while(0)
#endif

//use model example
  dbSerialPrintln("HOLDbuttonPushCallback");
  dbSerialPrint("ptr=");
  dbSerialPrintln((uint32_t)ptr);

#8

Looks like I fixed it. The Particle.process() calls disconnect the cloud!?!?!?
I created a simplified program by copying pieces of the giant library. It checks for serial data and reads it into a variable. This would also lose the connection, until I removed Particle.process (I had copied it from the library).

I think the original library might work by first changing a few things.
First change the serial port from Serial to Serial1 in the main .ino file.

USARTSerial& nexSerial = Serial1;

Then, depending on the bug fix mentioned above, change all

  while (nexSerial.read() >= 0); // flush RX buffer only

to

  while (nexSerial.read() > 0); // flush RX buffer only

Last, remove all Particle.process(); statements in all the .cpp files. There are a few. I would also remove the 10ms delays.

I should take a new snapshot of the library and make these changes to create a BluzDK specific one. I haven’t tested all the functionality, but button presses are processed correctly and text fields can be written to. Perhaps there are other issues.

Pescatore


#9

Glad it is working! Not sure about the Particle.process() call, that clearly shouldn’t cause disconnects, but I will look into it.


#10

Sorry, late to the party here - didn’t get a notification about the pending message.

About the “fix” while (nexSerial.read() > 0);
This would not consume all data in the buffer . it would leave trailing \0 bytes after the first one found.
A correct workaround for the underlying firmware bug would be this

  while (nexSerial.available()) nexSerial.read();

And this would only work for controllers that can flush the RX buffer quicker than the incoming data arrives. For slower controllers you’d need to set a timeout to allow for the rest of your code to run.


#11

Thanks for the reply @ScruffR - better late than never. I had not thought about the end character in the buffer.
Once I got the code working I stopped “fixing” it, but the library you ported has been valuable in understanding
how these displays work.
Thanks,
Pescatore