TFT ILI9806 on ESP32 don't work


On diagnose_TFT_support I dissable tft.readID() in loop().

After this, I see all screen rotate correctly, only that on REV mode I see black bar too

Can readID at end it expecting next command or data? If this, next command from next function can corrupt the registers?

I attach new photos

I am VERY confused. I understood that your ILI9806 had wrong SS, RGB, and geometry. I/You had fixed it.
So the diagnose_TFT_controller photos say odd things:
green photo says 864x480 but wrong rotation.
blue photo shows what looks like a 480x854 panel but the geometry is not printed
grey 864x480 for landscape when I thought you had corrected it to 854

If the geometry is correct, you will see two white rectangles. It is very obvious how many pixels are missing. It is a good way to identify a 272x480 panel with a 320x480 controller

I have made these corrections and posted a "test_9806" Branch on GitHub.

I suggest that:

  1. you leave the IDE
  2. delete the current libraries/MCUFRIEND_kbv directories.
  3. select "test_9806" Branch on GitHub
  4. Download Zip to your PC
  5. Start IDE
  6. Install MCUFRIEND_kbv from the ZIP
  7. run graphictest_kbv.ino
  8. run diagnose_TFT_controller.ino

If you have a GitHub account, you still need to check that you do not have multiple libraries.
You only "change Branch" in your GitHub DeskTop

Life would be simpler if I knew whether you have a GitHub account. It is easy to track any edits or corrections.

Quite honestly, the edits are so trivial that you should have little problem doing them manually.
You can always look at the GitHub site and compare history there.

I am sure that there must be a way to write the PIN_LOW() macro efficiently for ESP32.
Meanwhile I suggest that you stick with digitalWrite() for the moment.

Did you need a delay(1) or yield() with your ESP32 ? Did the WatchDog get you?

If you make your own edit to a GitHub file, please describe what you have done.
The whole beauty of GitHub is that remote colleagues can see what each other has done.



Did you need a delay(1) or yield() with your ESP32 ? Did the WatchDog get you?

Not is necessary in ESP32, the problem of reset not’s from watchdog, this problem I have localized in:

for (i = 0; i < 12; i++) {
    PGM_P str = result[i].msg;
    char c;
    if (len > 24) {
      if (i < 10) tft.print(" ");
      tft.print(": ");
    uint8_t cnt = len;

    while ((c = pgm_read_byte(str++)) && cnt--) tft.print(c); <-- This line produce a Core panic

    tft.print(" ");
    total += result[i].ms;
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC      : 0x400d1406  PS      : 0x00060730  A0      : 0x800d1541  A1      : 0x3ffb1ef0
A2      : 0x3ffc26e0  A3      : 0x3ffc240c  A4      : 0x00000018  A5      : 0x00000000
A6      : 0x00000002  A7      : 0x001a22f0  A8      : 0x00000018  A9      : 0x3ffb1ed0
A10     : 0x00000007  A11     : 0x00000005  A12     : 0x0000000a  A13     : 0x4007eb85
A14     : 0x00000002  A15     : 0x000000ec  SAR     : 0x0000001f  EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000000  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xffffffff

Backtrace: 0x400d1406:0x3ffb1ef0 0x400d153e:0x3ffb1f20 0x400eb1b6:0x3ffb1fa0


At the moment simply I comment this line, more advanced time I’ll debug this, at this moment not’s my preoccupation.

I clone your new branch and installed on IDE.

First attempt produce this results.

At continuation I’ll modificate step by step and posting the results here.

PD. I have an Github account but I’m not used Github, I need create a pull request?

I added this to readID()

uint16_t MCUFRIEND_kbv::readID(void)
    uint16_t ret, ret2;
    uint8_t msb;
    #ifdef SUPPORT_9806
      ret = readReg32(0xD3);           //Obtain ID
      //Serial.print("RET ");printhex(ret);Serial.println();
      if (ret == 0x9806){             //Check if 0x9806
          //Serial.println("Return ILI9806");
          readReg32(0xD3);           //forces a read again, I observed that if not, don't work propperly
          return 0x9806;
    ret = readReg(0);           //forces a reset() if called before begin()
    if (ret == 0x5408)          //the SPFD5408 fails the 0xD3D3 test.
... ... ...

after this modification I obtain this. attached photos.

Blue and green screen I can see the two white frames.
Red and grey screen Are corrupted.

And in the graphictest, scroll vertical up and down, and scroll Bar, don’t see any movement.
and software scroll not clean.

It all seems rather odd. I have been running the Branch today on a RM68140 shield mounted on the TTGO ESP32 board.

You can see the edits that I made. Only to define SUPPORT_9806, add SS, RGB attribute and change the geometry.

You were doing quite nicely yesterday. Or did you change some other registers.

If you like, I can make another Branch but from the v2.9.8 Release instead of the current Beta.
I have done a lot of STM32 work and macro style but nothing that changes the library for ESP32.

It would be best to get to the bottom of this e.g. your initial reports about SS and RGB.


You were doing quite nicely yesterday. Or did you change some other registers

I continued from your branch test_9806.

I'll go step by step, I have all past work wrote on paper.

I know that the end are near :smiley:

Looking at your last photos. Blue and Green show perfect 480x854 geometry.

The Red and Grey show a missing area. And corruption of what you do see.
I would do a bit of experimenting. e.g. writing 0x80 to reg(0xF7) which will make the hardware think that it has a 480x864 panel.

Since you have told it the HEIGHT is 854 it should look after the graphics ok. And hopefully avoid the "missing black band"

There are several approaches to the "missing band" problem. e.g. OFFSET_9327 conditional with _lcd_ID==0x9806

I would expect readID() to get 0x9806 at first go.

    ret = readReg32(0xD3);      //for ILI9488, 9486, 9340, 9341
    msb = ret >> 8;
    if (msb == 0x93 || msb == 0x94 || msb == 0x98 || msb == 0x77 || msb == 0x16)
        return ret;             //0x9488, 9486, 9340, 9341, 7796

After all, it should recognise 0x98xx just like 0x9320, 0x9325, 0x9328, ... all match 0x93xx

Only you can see what is happening. But there is no harm in just calling readReg32(0xD3) twice.
Your snippet is only going to return 0x9806 if it found 0x9806 in the fist place. The dummy call does not return anything.


Only changing this

(0xF7), 1, /* 480x864*/0x81,
(0xF7), 1, /* 480x864*/0x80,

All screen in diagnose_TFT_support are see cleanly, but red and grey see scrolled some pixels as you see another times from first time (fixed black bar).

But int graphictest_kbv don't work vertical scroll screen up and down, and scroll of color bar, don't work.

only after change

table8_ads = ILI9806_regValues, table_size = sizeof(ILI9806_regValues);
p16 = (int16_t *) & HEIGHT;

   *p16 = 854;   //panels can be 480x864, 480x854, 480x800, 480x840, 480x720
   *p16 = 864;   //panels can be 480x864, 480x854, 480x800, 480x840, 480x720

p16 = (int16_t *) & WIDTH;
        *p16 = 480;

vertical scroll screen up and down, and scroll of color bar work correctly.

I see Software scroll corrupt yet

At this moment all functions work about 80% I thinking.

Only you can see what is happening. But there is no harm in just calling readReg32(0xD3) twice.
Your snippet is only going to return 0x9806 if it found 0x9806 in the fist place. The dummy call does not return anything.

If I call readreg32(0xD3) one time, yes, I received the register correctly, but screen are crazy, half screen only, inverted color, ...
and if I dummy re-call readreg32 only one more time, you see all as you see in last photos. Attention, if I call readreg32 more times, the screen are crazy again.

Only call two times readreg32 for work well. It's curious, I don't explanation for this, I thinking now on speed of states read write transitions.

I activated direct I/O only for WR pin and "software scroll" is see clean now, all are much quickly too.

Yes, if the arithmetic does not work, the scroll does not happen e.g. TOP+BAND+BOT must equal the GateScan.

It is not uncommon for controllers to go wrong in REV. They sometimes need a kludge. To get the GateScan to line up properly AND to keep the scroll arithmetic happy.

The important thing is that you KNOW it is physically 480x854.
People seldom use the REV rotations. So it does not matter if there is a kludge.

reg(0xF7) = 0x80 gives a clear display.
I will kludge vertScroll() and setAddrWindow() arithmetic to cope with black bands.

It is my bedtime.


Edit. Crazy behaviour is probably because readID() got a different ID. i.e. tft.begin(wrong_ID)
Please stick with a known PIN_LOW(). I want to get this working properly first. Before we worry about performance.

Good night, and very thank you :slight_smile:

I have updated the Branch on GitHub.


Until now, I was not able to see testline(), and others as triangles or circles because it’s crashed.

Now I can see the whole test with out fails.

I'm away from home, but tonight I'll try to make a report with the changes made.

Thank you very much

I am doing something else at the moment.

Your 480x854 is 2.67x the pixels of my 320x480 displays. I will try to get an ESP32 to crash by trebling the size of those Adafruit Tests.

The ESP8266 is a pain with WatchDog. I have always found ESP32 kinder.


Here is a video from this


I am more confused.

  1. The Adafruit Tests appear to work.
  2. The Adafruit Report is normally Green text on Black background with visible text messages.
  3. Your Total is 11.22 sec. My ESP32 takes 3.55 sec on a 320x480. So I would expect you to take 9.48 sec.
  4. Your Vertical Scroll is very fast. Software Scroll is the same as mine.
  5. The Red penguin screen looks as if it has a “missing” 96 pixels on the bottom of the screen (right on video)
  6. Your previous Red screen looks like 64 pixels missing.
  7. since the Band scroll is working, my vertScroll() arithmetic kludge works.
  8. the OFFSET definitely does not work.

If you make changes to the graphictest_kbv example, please say so. I am guessing that you have reduced the delays to make a shorter video. An excellent strategy but it would be nice to be told.

I have updated the GitHub Branch. Adjust the OFFSET_9806 to see what works. Personally I would expect the value to be 10 i.e. to account for 854 vs 864 scanlines.
I have set it to 96 because of what the video looks like.


Oh, excuse me, have you right, I modidfied the delays, and other things, but the changes are not important, I think.

The total time of test can be affected by the pinLow and pinHigh, I’m in digitalWrite(), because with your change don’t work for me. The screen don’t start.

#define PIN_LOW(p, b) ((&p+2)) = (1<<(b&31))
#define PIN_HIGH(p, b) (
(&p+1)) = (1<<(b&31))

No problem. It is often wise to reduce delays for a video. I can stop and start a video. In fact I tend to just view stopped frames when I want to study something.

I was amazed that that “p” macro worked at all without a lot of casts. It does not surprise me that you had a problem. The Expressif toolchain is very fussy about certain things.
Yes, it is safer to stick with digitalWrite() for the PIN_LOW() macro. The ESP32 compiler does an incredible job in generating efficient code.

The most important task is to get the screen rendering correctly in XXX_REV rotations.
I presume that the Adafruit Report screen “oddity” is due to your edit.


Hello, I am also using esp32 to drive ili9806 lcd,
My lcd is 800 * 480 resolution,
Can you share your code and library files?