Serial and i2c?

I gather from casual skimming of threads that there are issues to be wary of if I want the same Arduino to participate in both a serial data interchange with another device, and an i2c bus. I'll describe specifically what I'm wanting to do, and maybe someone can steer me away from the dead ends and towards a sensible approach.

I have a fairly complex game controller (first revision) with almost 40 buttons (!). At present it consists of 2 Arduinos, one to monitor sensors in real-time & emulate a joystick, and one to interact with the user via all those buttons (and some knobs, and leds and digital displays and stuff) & emulate a keyboard. The whole thing is glued together with i2c (button pads from Adafruit, sensors, the 2 Arduinos, display backpacks...) and it's been working great, very solid.

I'm thinking that Version 2 would be far more compact and attractive with a multipage touch screen. The obvious first choice for such a touch screen (at least so it seemed to me) was a Nextion, because of its low serial overhead & ease of gui-building.

So I would like to maintain a live serial connection to the Nextion, listening for output from that device, while also participating in the i2c bus that connects all the sensors and micros in my project.

A primitive test sketch for the display is working ok -- I can detect button-release events and send some status data from the screen to the Arduino. However, my test sketch is doing absolutely nothing but listen to the Nextion. So I started googling for "serial and i2c, arduino" (etc) and found rather troubling comments about interrupts conflicting, losing bytes, etc. I'm not technical enough to follow all the details, but the fact that this is even a topic of discussion worries me :slight_smile:

Here are my (somewhat naive, sorry) questions (I'm not expert enough to dive into the i2c libs, or the serial lib, and just by reading the code understand what's really going on in there).

  1. does the i2c bus master secretly use an ISR deep down in the library, or is its 'read slave' and 'write slave' functionality in the main execution loop where it appears to be? I know the slave sketch uses a visible ISR which I had to customise. The aim of this question is to find out whether the i2c bus master can afford an ISR serving the serial display interface, because there is no other ISR to muck up the timing.

  2. is there any other recommended & graceful (non i2c, non-ISR) way of sharing data between 2 arduini? Can 2 arduini share (I'm speculating wildly here) some kind of serial device with two ports and two buffers, each being able only to read one, and only to write the other? So they can "check the mailbox" at their own discretion? Alternatively is there a device I could put between the display and the Arduino, with a large buffer, that would store up the serial output until I feel like reading it, so as to eliminate the risk of dropping bytes while distracted?

Sorry to be so vague. Serial comms is not my thing, as you can tell, and before getting into a lot of painful head-bashing I thought I would ask for some guidance & see what handy gizmos might be out there whose names I don't know and therefore can't google :slight_smile:

I gather from casual skimming of threads that there are issues to be wary of if I want the same Arduino to participate in both a serial data interchange with another device, and an i2c bus

No?

  1. does the i2c bus master secretly use an ISR deep down in the library,

Yes.

The aim of this question is to find out whether the i2c bus master can afford an ISR serving the serial display interface, because there is no other ISR to muck up the timing.

Both processes use internal hardware so they will not conflict with each other.

If you go with Software Serial however, your mileage may vary.

O, o, I see a lot of problems ahead :o

Can you do everything with a single Arduino board ?
An Arduino board can be a I2C Slave, but the examples for that are often wrong.
It can be done, and it can be reliable. It is reliable when the Slave does not turn off the interrupts. Some libraries turn of interrupts for a special timing protocol.

The onRequest and onReceive functions for a Arduino as I2C Slave are called from a real ISR. Those functions are therefor real interrupt functions. Those functions should be as short as possible and should not call Serial, delay, or something like that.
There is no problem to use I2C and Serial, but you better not call a Serial functions from the onRequest and onReceive functions.

The Nextion display library for Arduino is a pain. It sends a command and waits for it. Receiving data when pushing a button on the screen in done in a strange way as well. A lot of time is wasted in that library. The flow of the data is not smooth.
The library for a Nextion display uses a class for every item. That might seem modern, but I think it is over the top.
The commands for the Nextion are simple, therefor using the Nextion without the library is easier, more straightforward and faster. There are alternative libraries, but they don't support everything, and I don't like those libraries either.

Never use a SoftwareSerial with a Nextion display.
Don't tell anyone, but I use AltSoftSerial with a Nextion display on a Arduino Uno at 38400 baud. It works, but it is only a clock.The Arduino Uno has nothing else to do. I also send a command to the Nextion display so that it not responds to a normal command. Okay, yeah, it looks really great :wink:

Have you build a project with a Nextion display ?

Hmm well! right now I am in fact using a Uno for basic checkout of my new Nextion, and I am using Software Serial. Seems OK so far... why should I not use it?

I have been using a simplified Nextion library because after a quick sniff I agreed immediately w/Koepel that the ITEAD library was very heavy indeed -- over-engineered for what seems a fairly simple protocol. The simpler library by crcibernetica seems a bit rough around the edges but it's working OK for me so far and is easy enough to modify. I'm ignoring all their clever image masking tricks and talking directly to the basic ITEAD widgets -- just reading and writing.

I was definitely not planning to attach the Nextion to a slave i2c device :slight_smile: I figured it had to be attached to the bus master if at all.

Right now the question is open for v2 of my controller, whether to continue with 2 arduini or to use a single, faster device like a Due (I do need the native USB support for HID libraries). I am leaning towards the Due with its zillions of io ports and blinding speed and gobs of memory, but they are a bit pricey... and they are 3.3v which gets me into the level conversion nightmare. I've been reluctant to go there.

So I've got a bunch of level converters (including special i2c level converters) on order, but who knows when the boat will get here from China :slight_smile: -- also got a cheap Zero to try them out on so I don't blow up my one and only brand-new never-opened Due. The Due is the right form factor to play well in a nice compact case with the Nextion display, for sure... that may be where I'm headed. There would still be i2c, of course, but not between Arduini.

BTW I found the I2C_Anything library worked like a charm for bidirectional data transfer between a bus master duino and 1 or more slave duini.

Oh yes, and like I said, this is my very first Nextion. Quite a step up from 7 seg displays :slight_smile:

I can't help with the decision which board to use. That is up to you.

You can use SoftwareSerial to test the display. But if you really use that, don't expect the Arduino Uno to be able to run other things.

This alternative ? GitHub - bborncr/nextion: A simple Nextion HMI library for Arduino.
That is not even half way there :drooling_face:
There are too many timeouts and delays in there.
After a sendCommand() there is still waiting for the response. The sendCommand() can not accept flash text, for example when the F() macro is used.
There is also no good solution for the ::listen() function in that library. As soon as something is available, a delay(10) is done.
A good solution would be to accept an incoming character and put it in a buffer and let the loop() run. Once enough characters are received for a valid command, then process the command. That requires that the incoming and outgoing streams of data have to be split.

That means that when you use a faster board for the Nextion display, you still end up with code that is stumbling over all those delays.

Cool, they make Zero clones for 14 dollars now. I see a Zero without extra hardware USB serial port. I think you should install the extra boards and select the "Arduino M0". The "Arduino M0" was developed by Arduino.org and that is a simple board with just the SAMD M0+ processor. To burn a new bootloader, you need something to plug onto the header with the fine pins.

You could buy an ATMega1284P and program that using one of your regular Arduinos.

Second example covers the 1284P: Gammon Forum : Electronics : Microprocessors : How to make an Arduino-compatible minimal board

The 1284P's ran me $5.50 each through Mouser, down from $7 before. I got 328P's (the chip in the socket of original Unos) for like $2.15 ea on the same order just last summer.

The 1284P is a 40 pin AVR with 36 IO pins. It's got 16K RAM, 4K EEPROM and 128K flash. It's got 2 serial ports and the usual others. It is community supported for the Arduino IDE, referred to as The Mighty 1284P for good reason.

Does the Nextion have an SPI interface?

No SPI interface from Nextion, nor any indication that they plan one.

I'm enjoying Nick Gammon's writeups (in GoForSmoke's .sig or ps). Very interesting, and given Nextion's very rigid message format the "buffer and check for EOM" method might just be the ticket.

I must confess Koepel is crediting me with more Arduino savvy than I actually have -- I have never burned a bootloader and wouldn't know how to begin :slight_smile: nor at this point would I know how to add an extra USB port to a duino. Of course, there is always a good moment to learn new things :slight_smile: but right now my Nextion is the new thing I'm learning... that'll keep me busy for a while. I have a 6 page UI to design, wheeee.

Have to say I do appreciate Arduino forum because it's generally tolerant of n00bs and amateurs (i.e. non-EE types like me). Thanks everyone! Every time I ask a question I get some answers that go a bit over my head, and others that expand my world and give me hope that eventually, I may even understand the "over the head" answers one day.

Start with one page on the Nextion display.

When you add a page, don't update the items on a page that is not active. Only deal with the current page on the display.
I have only done one project with a Nextion display, and I have not figured out a straightforward way to deal with more pages. I have not seen an elegant way by others yet.

The easiest way for a second page is to have only buttons, and use the events from the buttons to update things on the display.

However, when the main page has a clock and another page has a running clock as well, then you must know which page is the active page on the screen and only update the clock of the active page. I have a variable for the active page and every page has an initialization function and a update function.

There are many ways to use the Nextion display. Some prefer to do as much as possible in the Arduino, others prefer to do things in the display. It is even possible to ignore the Nextion functions and send low level commands to print text and lines. You have to find what suits you best.

@Koepel

Thanks for tips! Yes I had thought about this; fortunately I intend to use the Nextion pages to replace 2 Adafruit Trellis devices in the current version (plus a few additional pushbuttons and switches). So most of the pages would be "write only", sending touch events/button values to the duino, unidirectionally. And most buttons are "command only" (no state value).

I did plan a couple of pages that would receive and display data from the duino: a splash screen visible during device initialisation, which would log the init phases, display any errors, etc -- and a "dashboard" or home page with a few analog widgets for RPM, RTC, etc. So I had realised the necessity of a state variable to remember which page the user is currently viewing... easy enough, since they have to press fwd/back buttons or nav bar buttons to move between pages.

You mentioned the need for an initialisation function per Nextion page. This caused me to do a quick experiment and verify that indeed, the Nextion pages are stateless; they have no memory of their previous invocation! Thanks for the warning. I see that I'll have to re-establish state for any widgets such as checkbox, radiobutton, on/off, etc. (Sigh). I'd be very interested to have a look at your code, if you're willing to share, to see how you approached the problem w/o going super-heavy like ITEAD lib.

What seems particularly tedious is maintaining the Waveform widgets across page changes... a lot of data to re-send to restore their state :frowning:

As to the question of where to invest the coding -- on the Nextion or the duino -- I think it might be an interesting one, if all other things were equal. But the workflow in the Nextion Editor is so cumbersome -- repeatedly manipulating the uSD card -- that I think the decision is made for me. I plan to do as little coding as possible on the Nextion and invest all the smarts in the duino which has a much more reasonable and efficient IDE and quick debug cycle.

I've read that it is possible to skip the uSD card and upload tft file to the Nextion via USB adapter, but also that this is so darned slow that most people just give up and use the uSD card method. If you know of a better way, I'd be very interested to hear about it!

Uploading at the highest baud rate of 115200 is fast enough. You could buy a seperate usb-serial module for that. The Nextion accepts 5V and 3.3V serial signals.

My use of multiple pages is still a big mess and experimental.
These are the steps I took:
I do as much as possible in the Arduino. Via serial to the display is slower, but the Arduino is easier to modify.
I have created a new tab in the Arduino IDE for each Nextion page. No matter how small the code is. There is a init function and a update function.

When a button is pressed for another page, I call the init function of that page.
In the init function I send the command to the nextion display to show that page. I also set a global variable 'NextionPage' to that page.

The update of the pages in the loop() is like this:

  // Keep the page updated
  switch( NextionPage)
  {
  case 0:
    Page0_update();
    break;
  case 1:
    Page1_update();
    break;
  case 2:
    Page2_update();
    break;
  }

I want to change that someday. It should be more automatic.

To be able to use the F() macro, I have this:

#define sendCOMMAND(a) \
  nexSerial.print(a); \
  nexSerial.print(F("\xFF\xFF\xFF"))

To have each page in a seperate 'tab' in the Arduino IDE was a good choice. To create the macro to use the F() macro is handy. I'm not sure about the rest.

@Koepel

Thanks for the snippets. Did you discover how to get/set Nextion variables (attributes, whatever) directly, by a binary message, instead of sending the ascii command string? I ask because it seems to me simpler on the duino side to forget the widget names and just refer to them by page number and object id number. I think I would rather construct a hex cmd

0x?? (set command) 0x00 (page num) 0x08 (widget id) 0x10 0xff 0xff 0xff

to set widget 8 on page 0 to a value of 16, than go through the overhead of mapping the widget page/id num to a name string on the duino, and constructing a String "thingy.val=16" to send to the Nextion. Fewer bytes of message, fewer bytes of data storage on MCU, easier management of the widget set. Or so it seems to me; but I haven't yet found any indication that I can send "set" or "get" commands in binary format. Looks like the Nextion is listening only for ascii cmd strings.

It's a funny animal, the Nextion -- seems very sophisticated in some ways and kinda crude in others :slight_smile: still, pretty darned good for the price.

Looking at the datasheet for the 5" screen and noting that all models has 8 GPIO pins with 5V tolerant INPUT and 3V OUTPUT, why not program the Nextion an SPI slave? It would take blinking 1 pin and reading another on a provided clock pulse, 4MHz by default on Arduino = 500,000 cps bidirectional xfer rate.

Bidirectional SPI communicates both ways at once, but most apps can't use it. However an output shift register SPI-daisy-chained to an input shift register could turn 8 things ON/OFF and read 8 binary inputs in the same SPI CLK ticks.

@GoForSmoke, that should be done in the firmware. The firmware is not open. It is not only not open, it is sometimes annoying. There are for example a number of tricks, but they keep it a mystery because they want to get money for it some day. When someone asks on the Nextion forum how to do those tricks, the answer is that they should search the forum or that they don't want to tell it.

A trick is for example to create a custom gauge with custom needle. That is done by uploading many pictures to the Nextion display (for each needle position) and selecting which picture to show.

@Tazling, that would require a database or list of all objects on all pages. That can grow very large very quickly.
There is some inconsistancy with the names and the "id" of the objects. Sometimes the "id" must be used and sometimes the name must be used. I have heard that they are working on that, but I don't know if that is already fixed.

At this moment I have the name of an object directly in the sketch. Since I have every page in a seperate 'tab' in the Arduino IDE, I keep it small and locally. For me that works at the moment.
For example my page to change the clock has also an alarm setting and a few buttons. That results into 27 objects. Hour up, hour down, and so on.

TIP1: Add a LDR. I set the backlight between 30% and 100% according to the LDR value. That is visually very nice, I don't even notice it.

TIP2: Add a speaker. A small beep or click sound makes pressing the buttons a lot easier. I have added that to every "on push" callback function.

TIP3: You can make a small button easier to press, by not using the button, but by adding a larger 'touch' field on top of the small button.

Hmmmm

http://support.iteadstudio.com/support/discussions/topics/11000006164

Hi I couldn't find info about using gpio pins. Can we turn on a led by using these pins? What are the codes of nextion to use gpio? I have 3,5" ehanced version. Thanks.
1 Comment
Patrick Martin
said over 1 year ago

answer:

See CFGPIO and System Variables in the Nextion Instruction Set

https://www.itead.cc/wiki/Nextion_Instruction_Set#cfgpio:_configure_GPIO_-_Enhanced_Model_Only

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.

Just at first glance I'd say that the regular Nextions are more appliance than dev platform.

Oh well, you're stuck with few options if you didn't get the enhanced model.

you're stuck with few options if you didn't get the enhanced model.

Yes, I fear that is the Nextion business model, kind of an IGP approach :slight_smile: Make an attractive but slightly clunky product at a bargain-basement price, then charge the user for "enhancements" that in some cases seem like they should have been part of the basic kit. Want decent proportional fonts? $150 extra. Want a little patch to the s/w or any detailed tech advice? That'll be $50 please. Same with the hardware -- the basic model is semi-crippled, because (imnsho) they want you to get just frustrated enough with it that you'll buy the "enhanced" model next time :slight_smile:

I guess it's a workable business model, and it does keep the base unit super cheap. But imho these policies plus their jealously guarded proprietary file formats limit the reach and popularity of the device -- if opened up to community development I suspect it would take off like a rocket and possibly render most other small-format touch screens obsolete. The potential is there. And the quality of the editor if opened to community dev would be miles ahead of where it is now (just compare to, say, Blender or Arduino IDE or Platformio or other O/S dev tools).

Their forum in my limited experience is not a very friendly place; the main moderator seems kinda cranky and in trawling the old threads I've seen him more than once waste way more of his time scolding a newbie -- at great length -- for asking a naive question, than he would have spent on a 1-line answer to the naive question :slight_smile: Kinda funny, really, but a little hard on the person being scolded. I don't think casual 'duino hobbyists, or amateurs of any kind, are the intended market.

I still think it's worth using -- I'm just lowering my expectations gradually as I realise how limited the "economy" package is. It will work for my project, it's the right form factor, it will look (reasonably) pretty -- it's all good, I'm just toning down my original Gee Wow excitement as I realise that I'm not their target market and they have no motivation to be any friendlier or more open.

Koepel:
@GoForSmoke, that should be done in the firmware. The firmware is not open. It is not only not open, it is sometimes annoying. There are for example a number of tricks, but they keep it a mystery because they want to get money for it some day. When someone asks on the Nextion forum how to do those tricks, the answer is that they should search the forum or that they don't want to tell it.

:slight_smile: yeah, see my notes on "Nextion culture" above.

A trick is for example to create a custom gauge with custom needle. That is done by uploading many pictures to the Nextion display (for each needle position) and selecting which picture to show.

Yes this masking and image shuffling seems to be "the right way" to go about anything clever on the screen. Fortunately I don't need to be clever -- mostly just buttons.

@Tazling, that would require a database or list of all objects on all pages. That can grow very large very quickly.
There is some inconsistancy with the names and the "id" of the objects. Sometimes the "id" must be used and sometimes the name must be used. I have heard that they are working on that, but I don't know if that is already fixed.

In my version of the editor, iirc, the id is only unique to the page.

At this moment I have the name of an object directly in the sketch. Since I have every page in a seperate 'tab' in the Arduino IDE, I keep it small and locally. For me that works at the moment.
For example my page to change the clock has also an alarm setting and a few buttons. That results into 27 objects. Hour up, hour down, and so on.

Yes I see what you mean. Initially I'm thinking of a generable object name, like "w" + 100* pgid + objid. So when I get pg and object ids in the Nextion event message, I automatically know what the associated widget name is. I just have to play by the rules on the Nextion Editor side. The absence of a scripting language, data export in an open format, or other automation resources (not to mention any introspective functions) is disheartening; it's not a dev tool in which I want to spend any more time than need be. Cheap and cheerful is the motto, I suppose.

TIP1: Add a LDR. I set the backlight between 30% and 100% according to the LDR value. That is visually very nice, I don't even notice it.

TIP2: Add a speaker. A small beep or click sound makes pressing the buttons a lot easier. I have added that to every "on push" callback function.

TIP3: You can make a small button easier to press, by not using the button, but by adding a larger 'touch' field on top of the small button.

Nice tips! I think I have a stray piezo kicking around that I could use to "chirp" for positive touch feedback. I find the touch screen rather temperamental -- it seems to respond better to a fingernail or stylus than to a finger tip. Haven't yet investigated any sensitivity adjustments. An LDR, good thought. I was just going to have a touch-drag area of the screen, as in many tablet apps.

Tip3 I had not thought of, but it makes perfect sense. I could create oversize touch areas where real estate permits. Thanks for the idea.

In fact it occurred to me that one could ignore all the device's built in widgets and define an entire UI with just "touch areas" and clever swapping around of underlying images. However, my main focus is not to build the world's prettiest UI, just to save real estate by using multiple screens to present 40+ buttons.

Have to say it's very exciting to contemplate a UI without any restrictions on button and slider/pot count. I've been doing it the hard way with mechanical input devices forever (linear and rotary pots, buttons, switches), so this is a revolutionary departure for my projects! Despite all the disappointing limitations, the Nextion is still a pretty kewl toy and I don't regret the purchase one bit.

UPDATE: char-at-a-time receiving of Nextion messages (nonblocking) working fine, easy to implement. Ditched the Nextion library -- too cumbersome dealing with classes, I'm only using a few very simple functions. Now have a 5 page draft UI on the Nextion that is sending events and data correctly to Uno. Waiting for my Zero :slight_smile: expect a period of headbanging to get a SerialN set up dedicated to the Nextion, then I'm cookin' with gas.

Many thanks to all for the educational discussion! Going on vacation now for a couple of weeks, no more hacking till I return.

Wow, that was fast. It took me months to improve the functions and I finally noticed that I was no longer using the Arduino Nextion library.

I have pushed the strings into Flash with the F() and PSTR() macro and I use a global buffer to build the text. I also write the FF FF FF directly with the text.
This is for example my function to select a page:

void showPage( int page)
{
  sprintf_P( buffer, PSTR( "page page%d\xFF\xFF\xFF"), page);
  nexSerial.write( buffer);
}

That is short, isn't it ?