Graphic User Interface Part 2

The Nextion provides a serial port to interface to a host processor. The host processor (in our case, the PSOC) can communicate with the code running on the Nextion, or with the flash loading program residing on the Nextion.

Communicating with the flash loading program residing on the Nextion is not being addressed in our project. Communicating with our code is part of what we are concerned with.

Grunt Work

In order to understand the Nextion, you need to study its command language. The Nextion uses an interpreted language that appears to be a hybrid between C and Basic. It can be confusing at first. In addition, the Nextion syntax is very unforgiving. Braces “{” are only allowed at certain places, and sometimes can only be on a line alone. Using an “else” with a brace “}” requires no space, like this: “}else”. In addition, you cannot put the other brace “{” on the same line as the “}else”. Fortunately, the compiler is good about pointing the line with the error, but not much good about explanations.

You really need to study the language available here: https://nextion.tech/instruction-set/. Once you see all the commands available to you, you will appreciate how powerful the Nextion is.

A PDF of a slightly earlier version of this instruction set can be downloaded with the LTS support editor here: https://nextion.tech/nextion-editor/#_section2 . Unzip the file and look for the PDF of the instruction set in the directory. It will allow you to do offline study. All the commands will work, but the updated commands are not part of the PDF file, so you will need to go online for the additional information.

GUI Design Language

I am sure you are familiar with GUIs (Graphic User Interfaces). Android devices, iDevices, and even thermostats use GUI interfaces to a large extent today. The confusion for most developers happens when they first start with a project.

There is an old “Life in these United States” story (Reader’s Digest, now almost defunct) where a man drove his MG down a street every afternoon with its top down, and the neighborhood St. Bernard would always chase the tiny car. The man suddenly stopped, sat on the top of the back seat and said “Now that you have it, what are you going to do with it?”

Often, we feel the same way when approaching a design. The thing that has been hidden from the modern generation of developers is that GUI design tries to imitate real world physical items. When done properly, no one has to be told how a GUI design operates — it is intuitive.

Unfortunately, shortly after the death of Steve Jobs, Apple fired their hugely successful design language architect. They then put their chief hardware architect in charge for a brief period of time. He nearly destroyed the foundation of GUI design.

That engineer divorced real world physical concepts from on-screen design, and made almost all of today’s GUI design language unintuitive. Due to Apple’s influence in the design world, this new language spread all the way down to universities. As a result, someone now has to explain to you how a GUI control works, or even the fact that it is a control. That never happened before. I am not sure GUI design language will ever fully recover from this tragic mistake.

GUI Design Lesson To Learn

When you do a design, start with how you would do it with physical items (buttons, switches, meters, etc.). Once that is done, translate that into a screen presentation. At that point you can have a good looking design. The operation of the device you are creating will be intuitive once you have finished your work.

Next is the hard part. Computers do not see. They only interact with data in the form of electrical impulses. The key here is to translate GUI interface manipulation into something a microprocessor can use.

Events

When you read a News Site webpage, the most terrifying event that has occurred will be given you you immediately. In the news industry, “If it bleeds, it leads.” This event is something you pay attention to. In some cases it is important enough to cause you to immediately act upon the information.

In the GUI design world, an user interaction with an on screen GUI device is called an event. It must be handled immediately, even if the event causes nothing more than the setting of a variable in the processor’s memory.

Events mimic real world happenings on the physical plane. For example, wen you push a button, the push causes something to happen in real life. When you “un-push” that same button the release causes something to happen. (I.E. On/Off) These real world concepts are translated in the GUI design language as “touch” and “release” events. These events carry different names based on the toolkit used, but they always have the same concepts behind them.

A mouse click is a “touch” event. The release of the mouse button (and un-touching the section of the screen) is a “release” event. The same thing happens with a finger on a screen.

Another GUI event is a “move.” If the mouse stays clicked, it will “drag” the control on the screen, and the computer code causes the screen to imitate a real world item and move it with the mouse cursor (or finger). Releasing the mouse button, or lifting of the finger, “drops” the item at that location.

Finally, you have to understand that GUI events are random. Humans are random. They rarely go down a street at the same speed each time. They rarely open a door by touching the exact same spot on the door handle at the beginning of the move. User interaction with GUI interface elements is entirely random. That means that events occur randomly, with random amounts of time between the events, sometimes days or months apart.

Your design must understand that events are random, sporadic, unpredictable. You have to handle these events in any order, any sequence, without crashing. The computer device must perform as close as possible to how a device consisting of buttons and switches would perform in the physical world.

This means you have to write the code to handle each event as if each event ran a stand alone software program, that manipulates memory or hardware inside your processor. Once you get into that “modular” approach, the design, programming, testing, and eventual functionality of your project becomes relatively easy.

So easy, in fact, that from time to time you will be absolutely bored with working on your code. This is because you have to do the exact same patterns with each one of the dozens of design elements. It is mind numbing.

I could go on, but I expect you understand the concepts. So, how does this event get to my PSOC, Arduino, or PIC? Through a formatted message sent over a communication port!

Serial Data Ports, PSOCs and Nextions

The Nextion display has a single TTL (5V) UART Serial Port. This port is used to both program the display and communicate with the program running on the Nextion. The key to all of this is to get the event into your PSOC code in such a way that you can understand it.

Fortunately, the Nextion has this covered. You get internal events and can send information over the serial port to your processor. That is exactly how this project works.

Now for some PSOC Code

I have implemented the Nextion interface in several stages. First, I created an interface header file where I explained to my future self (who may not have any of the documentation from Nextion available) what is being used in the program. Here is the file:

nextion_interface.h


#ifndef _NEXTION_INSTRUCTIONS_H_
    #define _NEXTION_INSTRUCTIONS_H_
/* ========================================
 * Copyright Wade Maxfield, 2020
 * All Rights Reserved
 * LICENSED SOFTWARE.
 *  Under the GPL v3 license
 * This license does not override previous licenses
 * Some information may be Proprietary to 
 * Cypress (http://www.cypress.com) for their
 * PSoC 5LP®--Cypress Semiconductor and
 * only usable on their devices.
 * PROPERTY OF Wade Maxfield.
 * Commercial license available
 * ========================================
*/
// ALL "BINARY" or HEXADECIMAL values are BIG ENDIAN

// information:
//  a # in a string is to be replaced by the number of the item you are working with
// example: "set b#" --> change to "set b0", where both the b and the 0 are human readable ascii.

#define END_OF_COMMAND  0xffffff  // three bytes to be sent (NOT 4 bytes!!!) at end of cmd

#define RESET_NEXTION "rest"
#define CLEAR_SCREEN_EX "cls #"  //clear to color
#define CLEAR_SCREEN_NAME "cls str"  //clear to color cls RED




#define REFRESH_PAGE "page #"
#define REFRESH_PAGE_MAIN "page main"

#define REFRESH_COMPONENT "ref "   // ie: "ref t0" to refresh component t0
#define REFRESH_COMPONENT_EX "ref @"  // the @ is the component id
//1. The default loading mode is automatically load when you create and edit 
//a component in Nextion Editor. If set it as manually load, you should use 
//ref command to load the component. Or when the component is covered by the 
//other components, you can use this command to refresh the covered component.

//2. Component auto refresh when attribute changes only valid for those 
//attributes in green bold font. The other attributes not in this format 
//can only be refreshed and displayed by the command of ref.

//3.When creating a component in Nextion editor, the default loading mode is 
//auto loading. When the loading mode is set as manual loading, it requires to 
//use ref to load. When the component is sheltered by GUI command mapping, or 
//when the component is sheltered by other manual loading components, you can 
//use ref to refresh the component. 

#define ACTIVATE_CLICK "click "
#define ACTIVATE_CLICK_EX "click @, #"  // click b0,0 == release, click b0,1 == press

#define REFRESH_STOP  "ref_stop" // stop refreshing screen

#define REFRESH_RESUME "ref_star" // start refreshing (use after ref_stop)

#define GET_VARIABLE "get "
#define GET_VARIABLE_EX "get @"  // get t0.txt, get j0.val, get "123", get 123
//1. When returned value is a string, the returned 
//  data is 0X70+ASCII code+0xff 0xff 0xff.
// 2. When returned value is numerical, returned 
// data is 0X71+4 byte binary data+0xff 0xff 0xff.
//The data storage mode is little-endian mode (namely, low-order in front, 
//and high-order at back).


#define GET_PAGE_ID "sendme"

#define CONVERT_VAR "cov "
#define CONVERT_VAR_EX "cov @,@,#"  // cov att1,att2,length
// cov h0.val,to.txt,0 //convert the value variable of slider h0 val into decimal 
//string and assign the txt variable of t0, the length is automatic
//cov t0.txt,j0.val,0  //convert the string variable of t0 txt into value and 
//assign the variable of slider h0 val, the length is automatic

#define CALIBRATE_TOUCH  "touch_j"  // enter calibrate function


#define SET_VISIBILITY "vis "
#define SET_VISIBILITY_EX "vis @,#"  // vis b0,1 == show, vis b0,0 == hide
#define SET_VISIBILITY_ID_EX "vis #,#" // vis 1,1 == show component ID 1, vis 1,0 == hide component ID 1

#define SET_TOUCH_ABILITY "tsw "
#define SET_TOUCH_ABILITY_EX "tsw @,#"  // tsw b0,1 == enable touch, tsw b0,0 == disable
#define SET_TOUCH_ABILITY_ID_EX "tsw #,#" // tsw 1,1 == enable touch component ID 1


#define COM_EXECUTION_STOP "com_stop"  // stop executing received commands
#define COM_EXECUTION_START "com_star"  // start executing received commands
#define EXECUTION_BUFFER_CLEAR "code_c"

#define RANDOM_INITIALIZE_EX "ranset #,#" // set value randomly generated from # to #

#define PRINT_RAW "print "
#define PRINT_RAW_EX "print @"   // print t0.txt  (string), print j0.val  (4 byte hex value)

#define PRINT_HEX "printh "


//-------------------------------------------
// Waveform components
//-------------------------------------------
#define ADD_DATA_TO_WAVEFORM_COMPONENT "add "
#define ADD_DATA_TO_WAVEFORM_COMPONENT_EX "add #, #, #" // data -- 0 to 255
//add 1, 0, 30  //add data 30 to channel 0 of the Waveform component which ID number is 1
//add 1, 1, 50  //add data 50 to channel 1 of the Waveform component which ID number is 1


#define ADD_DATA_TO_WAVEFORM_COMPONENT_IN_VOLUME "addt "
/*
addt: Add data to waveform component in volume

addt objid, ch, qty

objid: waveform component ID

ch: channel number of waveform component

qty: adopted point quantity of the data

    Example:

addt 1, 0, 100  //waveform component whose ID is 1 enter into data pass through mode, pass 
through adopted point quantity is 100

Remarks:

1.Waveform component only support 8-bit values, 0 minimum, 255 maximum. Single pass through 
is 1024 bits maximum.

2.After sending waveform data pass through command, it will take some time to get the device 
responded and started passing through values. This period takes about 5ms(it will take longer
if there are other commands to be executed in the buffer zone before executing data pass through 
command). Following this period, it will send a data pass through ready data(0XFE+Terminator) to 
user, then it will start sending pass through data. The data being passed are only hexadecimal 
numbers. It recovers to command receiving state only after the device has adopted specified data.

3.Waveform will not refresh until specified data has been passed through completely. 
*/

#define CLEAR_WAVEFORM_DATA_IN_COMPONENT "cle "
#define CLEAR_WAVEFORM_DATA_IN_COMPONENT_EX "cle #,#" //
//cle 1, 0   //clear the data of waveform component ID=1, channel=0;
//cle 1, 255   //clear all the data of waveform component ID=1 



//-------------------------------------------
// EEPROM
//-------------------------------------------

#define WRITE_EPROM "wepo "
#define WRITE_EPROM_FROM_COMPONENT "wepo @,#"
// wepo t0.txt,10 // write value of t0.txt to EEPROM, start from address #10. storage size is (t0.txt-mal)+1 bytes
#define WRITE_EPROM_FROM_STRING "wepo S,#" 
// wepo "abcd",10 // write string "abcd" to EEPROM, start from address #10. storage size is 5 bytes
//wepo 11,10  //write constant 11 to EEPROM, start from address #10. storage size 4 bytes

#define WRITE_EEPROM_FROM_UART "wept "
/*
wept: write data in hex to EEPROM through UART - Enhanced Model Only

wept add, length

add: start address in EEPROM

length: length of data


Example:

wept 30, 20 // write 20 bytes of hex data from UART to EERPOM, start address is 30 

Remark:

1, When wept instruction received, Nextion device will take around 5ms to initiate 
and send ready signal “0xFE 0xFF 0xFF 0xFF"

2, Before you send data from UART, you should wait until you get ready signal from 
Nextion device

3, wept can not be terminated. 
*/

#define READ_EEPROM_TO_UART "rept "
/*
rept: read data in hex from EEPROM to UART - Enhanced Model Only

rept add, lenght

add: start address in EEPROM

length: length of data

Example:

rept 30, 20 // read 20 bytes of hex data from EEPROM to UART, start address is 30 
*/

//-------------------------------------------------------
// GPIO
//-------------------------------------------------------
/*
cfgpio: configure GPIO - Enhanced Model Only

cfgpio id,state,cmp

id: I/O pin number

state: work state of the GPIO

  0 - pull up input mode
  1 - input binding mode
  2 - push pull output mode
  3 - PWM output mode
  4 - open drain output mode

cmp: binding component, available in work state 2

Example:

cfgpio 0, 0, 0    //Configure GPIO0 as pull up input mode, you can read the input status from system variable pio0, 
                       //such as: n0.val = pio0

cfgpio 1, 2, 0   //Configure GPIO1 as push pull output mode, you can control the output level 
                 //by system variable pio1, such as: pio1 = 1

cfgpio 2, 1, b0   //Configure GPIO2 as input binding mode, the binding component is button b0. 
                  //Falling edge of GPIO2 will trigger b0's button press event, rising edge of 
                  //GPIO2 will trigger bo's button release event.

cfgpio 4, 3, 0   //Configure GPIO4 as PWM output mode, you should use system variable pwm4 
                 //to set duty cycle before you use the instruction

Remark:

1, only GPIO4 to GPIO7 support PWM function

2, When configure a GPIO to input binding mode, only components in current page can be bind. It is recommended 
to put the instruction in the preinitialize event, because binding event wont not be triggered after page refreshing or switching. 

*/


//-------------------------------------------------------
// response table
//--------------------------------------------------------------
// all responses are terminated with 0xff 0xff 0xff
// for example, invalid instruction returns 0x00 0xff 0xff 0xff
// Nextion responses
#define INVALID_INSTRUCTION     0x00
#define INSTRUCTION_EXECUTED    0x01
#define COMPONENT_ID_INVALID    0x02
#define PAGE_ID_INVALID         0x03
#define PICTURE_ID_INVALID      0x04
#define FONT_ID_INVALID         0x05
#define BAUD_RATE_SET_INVALID   0x11
#define CURVE_CTL_ID_INVALID    0x12
#define VAR_NAME_INVALID        0X1A
#define VAR_OPERATION_INVALID   0X1B
#define FAILED_TO_ASSIGN        0X1C
#define EEPROM_OPERATION_FAILED 0X1D
#define PARAMETER_QUANTITY_BAD  0X1E
#define UNDEFINED_ESCAPE_CHAR   0X20
#define VARIABLE_NAME_TOO_LONG  0X23  // MAXIMUM 29 CHARS


// Nextion EVENT Data 

#define TOUCH_EVENT             0x65
#define CURRENT_PAGE_ID         0x66
#define TOUCH_COORDINATES       0x67
#define TOUCHED_WHILE_ASLEEP    0x68
#define STRING_DATA             0x70
#define NUMBER_DATA             0x71
#define AUTO_ENTERED_INTO_SLEEP 0x86
#define AUTO_WOKE_UP            0x87
#define STARTUP_SUCCESSFUL      0x88
#define DATA_TRANSPARENT_TRANSMIT_FINISHED  0xFD
#define DATA_TRANSPARENT_TRANSMIT_READY     0xFE


// Color code list
#define COLOR_RED   63488
#define COLOR_BLUE  31
#define COLOR_GRAY  33840   
#define COLOR_BLACK 0
#define COLOR_WHITE 65535
#define COLOR_GREEN 2016
#define COLOR_BROWN 48192
#define COLOR_YELLOW 65504

#endif
/* [] END OF FILE */

Next I created a small header file defines some things I like to use, such as TRUE, FALSE, YES, NO, along with some of the functions that will be used. The file is here:

nextion_interface.h

#ifndef _NEXTION_INTERFACE_H_
#define _NEXTION_INTERFACE_H_
/* ========================================
 * Copyright Wade Maxfield, 2020
 * All Rights Reserved
 * LICENSED SOFTWARE.
 *  Under the GPL v3 license
 * This license does not override previous licenses
 * Some information may be Proprietary to 
 * Cypress (http://www.cypress.com) for their
 * PSoC 5LP®--Cypress Semiconductor and
 * only usable on their devices.
 * PROPERTY OF Wade Maxfield.
 * Commercial license available
 * ========================================
*/
#include <project.h>

void Nextion_interface_init();  // start up the nextion driver
void handle_Nextion_interface();

#define IN_BUFF_SZ 64
#define OUT_BUFF_SZ 64

#ifndef FALSE
    #define FALSE 0
#endif
#ifndef TRUE
    #define TRUE 1
#endif
#ifndef YES
    #define YES TRUE
#endif
#ifndef NO
    #define NO FALSE
#endif


#define bool uint8

void sendTextToNextion(char *text);
void sendDebugTextToNextion(char *msg);
void sendCountDownTextToNextion(int16 count);

#endif
/* [] END OF FILE */

The next file is the C file that contains the code to talk to the Nextion. In this file, we reference a UART, which must be added to the project. It is separate from the USBUart, and must be connected to certain pins. It’s default name is UART_1, and is good enough for this project. I have often renamed the UART so it was more descriptive.

One thing to note, if your UART is named UART_1, don’t name your files uart_1.c, uart_1.h. In the PSOC IDE, it will not cause an error, but your project will not work. Always give your source files a slightly different name from the device name in the Configuration dialogs. That is because those names are already used in the generated source code, and the compiler and linker can’t handle identical names even though the function names are different.

I will cover the UART in more detail on the next post. (You probably have enough experience now to add a UART to your project and hook it up to the pins, but I will cover it anyway.) Here is a screen capture of the schematic entry:

UART Communication to Nextion

Finally, I created the interface file that communicates over the serial port (UART_1). Here is the file for talking to the nextion:

nextion_interface.c

/* ========================================
 * Copyright Wade Maxfield, 2020
 * All Rights Reserved
 * LICENSED SOFTWARE.
 *  Under the GPL v3 license
 * This license does not override previous licenses
 * Some information may be Proprietary to 
 * Cypress (http://www.cypress.com) for their
 * PSoC 5LP®--Cypress Semiconductor and
 * only usable on their devices.
 * PROPERTY OF Wade Maxfield.
 * Commercial license available
 * ========================================
*/
/*
  Note: in "c" all hexadecimal numbers are preceded by 0x (or 0X) 
  Order of events:

  0) psoc comes up and puts out a message: "global.psRebooted.val=1" 0xff 0xff 0xff
  1) Nextion puts out a message: "|a0xff 0xff 0xff "
  2) psoc puts out a request for information: "global.SystemChange.val=1" 0xff 0xff 0xff

    note: all info sent to nextion over UART is terminated with 
      0xff 0xff 0xff ( 3 bytes all ones)

  todo add automatic/manual
*/
#include "main.h"
#include <stdio.h>
#include "playIR.h"



// IF USING SPRINTF, 
//#if defined (__GNUC__)
// in Menu Project->Build Settings ->
// in pop-up dialog box, click on "ARM GCC"
// 
// use default libraries true
// use newlibnano true
// use newlibnano floating point format true

// in previous versions, we had to Add an explicit reference to the floating 
// point printf library
// to allow the usage of floating point conversion specifiers. 
// This was not linked in by default with the newlib-nano library. 
// add this entry (maybe not needed today, but does not hurt)
asm (".global _printf_float");
//
// !! DO THE FOLLOWING IN ALL CASES!!
//  in CYDWR->System, change stack size to 4k (0x1000), and Heap size to 2k (0x7ff)
//  **sprintf uses a lot of stack space **
//#endif



#define millis milliseconds

enum captureTypesEnum manualHC=coolType;// default to cool

uint8 nextionWoke;
int16 returnAfterWaitStateState;

unsigned long waitStateStartTime;

int16 commandInWork;

enum nextionStates {
    nextionNoState=0
    ,nextionConfigure
    ,nextionSetPSOCAwake    
    ,nextionWaitmsec     // waitxxxmsec for message
    ,SYS_STARTUP_MSG
    ,SYS_REPORTING
    ,SYS_IDLE
};

enum nextionStates SystemState;

#define NUMBER_OF_IN_BUFS 4
int16 head,tail;
uint8 inBuff[NUMBER_OF_IN_BUFS][IN_BUFF_SZ];
uint8  outBuff[OUT_BUFF_SZ];
int16 bufIndex;
uint8 *curBuff;
int16 cbIndex;

float coolTemp=68.0f;
float heatTemp=72.0f;
int16 onOff=1; // if YES (1) is on.  control the aircon
int16 operatingMode; // if 1, heat, if 0 cool
int16 recordingFinished;
int16 manualMode ; // 1== manual, 0== automatic

// point to the isr
CY_ISR_PROTO(Nextion_rx_data);

//-----------------------------------------
//-----------------------------------------
void Nextion_interface_init(){         
    isr_UartRX_StartEx(Nextion_rx_data);
    UART_1_Start();// the NEXTION display communication interface
    UART_1_ClearTxBuffer();
    UART_1_ClearRxBuffer();

    SystemState = nextionConfigure;
    head=tail=0;
    curBuff = inBuff[head]; //toggle between buffers
}

//-----------------------------------------
// convert to ascii, return length
//-----------------------------------------
int itoa(int number, unsigned char* buf)
{
	int sum = number;
	int i = 0;
	int digit;
    int negative=0;
    
    if (sum < 0 ) {
        negative = 1;
        sum=-number;
    }
    
	do
	{
		digit = sum % 10;
		if (digit < 0xA)
			buf[i++] = '0' + digit;
		else
			buf[i++] = ('A'- 0xA) + digit ;
		sum /= 10;
	}while (sum);
    
    if (negative){
        buf[i++]='-';
    }
	
	buf[i] = '\0';
	return i;
}
//-----------------------------------------
// A short atoi() function
//-----------------------------------------
int atoi(char *str)
{
    int result = 0;     // Initialize result
    int16 sign = 1;     // Initialize sign as positive
    int16 index = 0;    // Initialize index of first digit or sign
      
    // If number is negative, then update sign
    if (str[0] == '-'){
        sign = -1;  
        index++;  // move past sign
    }
      
    // go left to right, multiply by 10 each time
    // will screw up on anything over 0x39...
    // will stop on 0x0d, 0x0a, or anything
    // less than ascii 0.
    for (; str[index] && str[index]>0x2f; ++index)
        result = result*10 + str[index] - '0';
    
    // Return result with sign
    return sign*result;
}


uint8 swallowFFs; // when true, we must clear out FF's before looking for other data
uint8 zeroDepth;

//--------------------------------------------------
enum psocCmds {
    REQUEST_CONFIG= 0
    ,PSOC_REBOOTED 
    ,PSOC_AWAKE
    ,INC_MSG_COUNTER
    ,DEBUG_TXT_MSG
};
//--------------------------------------------------
char *NextionCommands[] = {
    "global.SystemChange.val=1"
    ,"global.psRebooted.val=1"
    ,"global.psoc_awake.val=1"
    ,"global.PsocMsgCounter.val++"
    ,"main.tDebugText.txt="
};
//--------------------------------------------------
//--------------------------------------------------
void sendTextToNextion(char *text){
    // make a copy of the text in case 
    // this function is interrupted.
    strcpy((char*)outBuff,text);
    char *p=(char*)outBuff;
    while (*p) {
        UART_1_PutChar(*p++);
    }
    // write command terminator
    UART_1_PutChar(0xff);
    UART_1_PutChar(0xff);
    UART_1_PutChar(0xff);
    
}
//--------------------------------------------------
// set countdown text variable in main screen
//--------------------------------------------------
void sendCountDownTextToNextion(int16 count){
    static char n[4]={'\"',0x30,'\"',0};  
    n[1]=(count&0xf)+0x30;   
    strcpy((char*)outBuff,"main.tCountDown.txt=");
    strcat((char*)outBuff,n);
    sendTextToNextion((char*)outBuff);
}
//--------------------------------------------------
//--------------------------------------------------
void sendDebugTextToNextion(char *msg){
    static char q[2]="\"";
    strcpy((char*)outBuff,NextionCommands[DEBUG_TXT_MSG]);
    strcat((void*)outBuff,q);
    strcat((void*)outBuff,msg);
    strcat((void*)outBuff,q);
    sendTextToNextion((void*)outBuff);
}

//--------------------------------------------------
//--------------------------------------------------
void sendCommandToNextion(int16 CommandNumber){
    sendTextToNextion(NextionCommands[CommandNumber]);
}

//-----------------------------------------
// update the main display temperature
// text field.
//-----------------------------------------
void sendTemperatureToNextion() {
    extern float temperature;

    sprintf((char*)outBuff,"main.curTemp.txt=\"%3.1f\"",temperature);
    sendTextToNextion((char*)outBuff);
}
//--------------------------------------------------
// Record UP    "|ru"
// Record DOWN  "|rd"
//--------------------------------------------------
void handleRecordCommands(uint8 *bufPtr){

    switch(bufPtr[1]){
        case 'u':
            captureType = upType;
        break;
        
        case 'd':
            captureType = downType;
        break;
        
        case 's':
                recordingFinished=TRUE;
        break;
        
        default:
         return;
    }
    
}
//--------------------------------------------------
// temperature set commands
// Cool Temp    "|tc"  -- followed by temp i.e. "ct72"
// Heat Temp    "|th"  -- followed by temp i.e. "ht72"
// the "|" is removed in the serial stream
//--------------------------------------------------
void handleTempSetCommands(uint8 *bufPtr){
 //   (void)bufPtr;
    switch(bufPtr[1]){
        case 'c': {// set cool temperature
           int16 ct = atoi((char*)&bufPtr[2]);
            coolTemp = (float)ct;
        }break;
            
        case 'h':{
          int16 ht   = atoi((char*)&bufPtr[2]);
            heatTemp=(float)ht;
        }break;
        default:
            break;
    }
}
//--------------------------------------------------
//--------------------------------------------------
void handleOnOffIR(uint8 *bufPtr){
      switch(bufPtr[1]){
        case '0': // set on/off status
            onOff = atoi((char*)&bufPtr[2]);
        break;
            
        case '1':// 0== cool 1== heat
            operatingMode = atoi((char*)&bufPtr[2]);
        break;
        default:
            break;
    }
}
//--------------------------------------------------
//--------------------------------------------------
void handleOpStateCommands(uint8 *bufPtr){
      switch(bufPtr[1]){
        case 'o': // set on/off status
            onOff = atoi((char*)&bufPtr[2]);
        break;
            
        case 'm':// 0== cool 1== heat
            operatingMode = atoi((char*)&bufPtr[2]);
        break;
        default:
            break;
    }
}
//--------------------------------------------------
//   fan up     "|fu"  // cycle through fan settings
//   fan down   "|fd"
//   fan auto   "|fa"
//--------------------------------------------------
void handleFanChoice(uint8 *bufPtr){
     switch(bufPtr[1]){
        case 'u': // fan faster
            addPlayIRToQueue(fanFasterType,CurrentEquipmentID);
        break;
            
        case 'd':
            addPlayIRToQueue(fanSlowerType,CurrentEquipmentID);
        break;
        
        case 'a':
            addPlayIRToQueue(fanAutoType,CurrentEquipmentID);
        break;            
        default:
            break;
    }
}
//--------------------------------------------------
//--------------------------------------------------
void handleSendIRByCmd(uint8 *bufPtr){
    switch(bufPtr[1]){
        case 'u': // play up
            addPlayIRToQueue(upType,CurrentEquipmentID);
        break;
            
        case 'd':// play down
            addPlayIRToQueue(downType,CurrentEquipmentID);
        break;
            
        case 'e': // energy saver
            addPlayIRToQueue(energySaverType,CurrentEquipmentID);
        break;
            
        case 'c': // cool (forced by choosing this)
            addPlayIRToQueue(coolType,CurrentEquipmentID);
        break;
            
            
        default:
            break;
    }
}
//--------------------------------------------------
//--------------------------------------------------
void handleSetEquipmentType(uint8 *bufPtr){
    switch(bufPtr[1]){
        case 't': // set type
            CurrentEquipmentID = (enum equipEnum) atoi((char*)&bufPtr[2]);
        break;
            
        default:
            break;
    }
}
//--------------------------------------------------
// send commands.  Note that the addPlayIRToQueue()
// system will space the IR commands appropriately
//---
// Manual
//   manual on  "|m1"
//   manual off "|m0"
//    temp up   "|mu"
//    temp down "|md"
//    heat      "|mh"
//    cool      "|mc"
// energy saver "|me"
//---
// GE Commands
//   select     "|mm"  rotate through the selections
//--------------------------------------------------
void handleManualCommands(uint8 *bufPtr){
    //int16 waitCount=0;
    switch(bufPtr[1]){
        case 'u':  // temperature change
            addPlayIRToQueue(upType,CurrentEquipmentID);
        break;
            
        case 'd':
            addPlayIRToQueue(downType,CurrentEquipmentID);
        break;
            
            
        case 'h': // mode select
            manualHC=heatType;
            addPlayIRToQueue(manualHC,CurrentEquipmentID);
        break;
            
        case 'c': // mode select
            manualHC=coolType;
            addPlayIRToQueue(manualHC,CurrentEquipmentID);
        break;
            
        case 'e': 
            manualHC=energySaverType;
            addPlayIRToQueue(manualHC,CurrentEquipmentID);

        break;
            
        case '0':// manual mode being turned off
            manualMode=0;// turn on temperature control
        break;
            
        case '1':// manual mode on
            manualMode=1;// turn off temperature control
        break;
            // GE Equipment Mode select
        case 'm': // manual mode "Select" "Next" operating mode
            addPlayIRToQueue(modeType,CurrentEquipmentID);
        break;
        
            
        default:
            break;
    }
}
            
//--------------------------------------------------
// commands to psoc
// Nextion Awake "|a"
// Set Equipment "|et"
//---
// Manual
//   manual on  "|m1"
//   manual off "|m0"
//    temp up   "|mu"
//    temp down "|md"
//    heat      "|mh"
//    cool      "|mc"
// energy saver "|me"
// following permanent choice
//   fan up     "|fu"  // cycle through fan settings
//   fan down   "|fd"
//   fan auto   "|fa"
//---
// Power Toggle "|pt"
// Record COOL  "|rc"
// Record HEAT  "|rh"
// stop Record  "|rs"
// Record UP    "|ru"
// Record DOWN  "|rd"
// On State     "|S1"
// Off State    "|S0"
// Cool Temp    "|tc"  -- followed by temp i.e. "ct72"
// Heat Temp    "|th"  -- followed by temp i.e. "ht72"
// OnOff        "|oo"  -- followed by "1" (on) or "0" (off)
// Operate Mode "|om" - followed by "0" (cool) or "1" (heat)
//----------
// commands from psoc
// curTemp.txt="72.5" 0xff 0xff 0xff
// main.psoc_awake.val=1 0xff 0xff 0xff
//
// SLEEP:
//  AUTO SLEEP STARTED: 0x86 0xff 0xff 0xff
//       WAKUP        : 0x87 0xff 0xff 0xff
//--------------------------------------------------
void handle_Nextion_interface() {
    
    
    
    while (head != tail ){
        
        curBuff=inBuff[tail];
        switch(curBuff[0]){
            case '1':
            // received nextion awake
            case 'a':{
                sendCommandToNextion(PSOC_AWAKE);
                CyDelay(10); // wait 10 milliseconds
                sendCommandToNextion(REQUEST_CONFIG);
                nextionWoke=1;// start reporting state machine.
            }break;
            case 'e':{
                CyDelay(2);
                handleSetEquipmentType(curBuff);
            }break;
            
            case 'f':{
                CyDelay(2);
                handleFanChoice(curBuff);
            }break;
            case 'm':{
               // manualMode=1;
                CyDelay(2);// allow the rest to come in
                // handle manual command
                handleManualCommands(curBuff);
            }break;
            case 'o':{
                // handle operational state commands
                handleOpStateCommands(curBuff);
            }break;
            case 'p':{ // Power toggle
                 CyDelay(2);// allow the rest to come in
                 if (curBuff[1]=='t'){
                    addPlayIRToQueue(powerType,CurrentEquipmentID);
                }
            } break;
            // received record command
            case 'r':{
                // these are "record" commands
                //  the user will point to the receiver and
                // press the appropriate key
                CyDelay(2);// allow the rest to come in
                 handleRecordCommands(curBuff);
            }break;
            case 's':{
                // handle OnOff IR command.
                CyDelay(2);// allow the rest to come in
                 handleOnOffIR(curBuff);
            }break;
            case 't':{
                // temperature set commands
                CyDelay(2);// allow the rest to come in
                 handleTempSetCommands(curBuff);
            }break;     
            
            case 'u':{
                // auto wakeup.
                msCounter1=10; // take temperature and update display
                decisionTimer=0;// update display
            }break;
            
        }
        
        if (++tail >= NUMBER_OF_IN_BUFS)
            tail=0;
    }
    
    
    // if nextion rebooted, we may have to reconfigure.
    // so go through process again.
    if (nextionWoke==1)
        SystemState=SYS_STARTUP_MSG;
   
    
    switch(SystemState)
    {
    /*****************/
        // once per second update the current temperature
    case SYS_REPORTING:{
        SystemState = nextionWaitmsec;
        returnAfterWaitStateState = SYS_REPORTING;
        waitStateStartTime = millis +1000;
        
        if (nextionWoke){
            // if a reading is there, give to psoc
            extern int16 TemperatureReadingAvailable;
            if (TemperatureReadingAvailable){
                sendTemperatureToNextion();
                TemperatureReadingAvailable=FALSE;
                sendCommandToNextion(INC_MSG_COUNTER);
                
            }
            
        }else{
            sendCommandToNextion(PSOC_REBOOTED);            
            sendCommandToNextion(INC_MSG_COUNTER);
            sendDebugTextToNextion("PSOC Awake");

        }
        
	}break;
        
    /*******************/
    case SYS_STARTUP_MSG:
        if (nextionWoke==1) {
    		SystemState = nextionConfigure;
            nextionWoke=2; // set to next state, so we don't keep returning here
        }else {
            SystemState = nextionWaitmsec;
            returnAfterWaitStateState = SYS_STARTUP_MSG;
            waitStateStartTime = millis +1000;    // wait 1 second            
        }
    break;
        
    case nextionConfigure: {
       // nextionSendPsocIsAwake();
        SystemState = nextionWaitmsec;
        returnAfterWaitStateState = SYS_REPORTING;
        waitStateStartTime = millis +100;  // wait 1/10th second
      }break;
        
    case nextionWaitmsec:{            
        if (millis >= waitStateStartTime) 
            SystemState = returnAfterWaitStateState;
    }break;
        
    /********************/
    case SYS_IDLE:
    default:
        
    break;
    }

}
//------------------------------------------------------
// the interrupt for RS232 data from the Nextion
//------------------------------------------------------
 CY_ISR(Nextion_rx_data){
     uint8 temp ;
    static int16 BeginFound;
    
    do {
        temp = UART_1_GetChar();
        
        // by convention, our nextion code
        // does not send binary numbers, only 
        // ascii numbers, so we can safely ignore 0xff.
        if (temp==0xff)
            continue;
        if (temp=='|'){
            BeginFound=YES;
            cbIndex=0;
            continue;
        }
        
        if (temp==0x87){ // auto wakeup
            // trigger the psoc to send wakeup 
            BeginFound=YES;
            cbIndex=0;
            temp=(uint8)'u'; // trigger auto wakeup.
            inBuff[head][cbIndex++]=temp;
            temp=0xa;// trigger command 
        }
        
        if (!BeginFound)
            continue; // throw away garbage
        
        inBuff[head][cbIndex++]=temp;
        // we ask the nextion to send an '\r' in the text, so it
        // also sends an '\n'.  key on that.
        if (temp==0xa){            
            if (++head >= NUMBER_OF_IN_BUFS)
                head=0;            
            cbIndex=0;
        }
             
        if (cbIndex >= IN_BUFF_SZ)
            cbIndex=0; // something bad is happening, clear the rx buffer pointer
    } while (UART_1_GetRxBufferSize());
    
}



/* [] END OF FILE */

In main.c, add the following initialization line to initialize the Nextion before the main for loop. Inside the main loop, add the “handle_Nextion_interface(); ” line. Everything should compile and link.

    Nextion_interface_init();// intialize RS232 for nextion

for(;;)
{
        //======================
        // nextion
        //======================
        handle_Nextion_interface(); // update the display if needed

Testing

At this point, if everything was done right, you should be able to test the project by connecting the UART on the USB Debug Finger stick on the PSOC to the Nextion IDE using the selection interface on the IDE.

In the next post, I will walk through this interface with screen shots and the like. Remember, No Pain, No Gain! If it does not work, keep pounding on it until it gives its information up or your head fails.

From the Past:

A reader from Australia gave feedback that the information for the USBUart did not work. After a few emails were exchanged, he traced it to a bad PSOC development stick. Once replaced, everything worked. That is the first time one of those has been bad in my limited experience.

I’m glad to know people are using this information.

Enjoy!

Add a Comment

Your email address will not be published. Required fields are marked *