In chapter 2.2 “Creating a Button with Events” there are a few errors, due to which the published sketch doesn’t work (at least not on my CYD). I’ve described the problem – and the solution – in detail in this document: https://drive.google.com/file/d/1-IW4Uwl_bA2k_kA8upkVc76PjYsPQbEN/view?usp=sharing.
The short version: the Width and Height of the screen are #defined for a portrait orientation, while the sketch is intended for a landscape orientation. Moreover, the functions touchscreen.setRotation(2) and lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270) have contradicting parameters: value 2 is for portrait, while value LV_DISPLAY_ROTATION_270 = 3 is for landscape.
Finally the function lv_display_set_rotation() doesn’t work. When I replace this function with its TFT_eSPI equivalent tft.setRotation(), then I get what I expect. I’ve read somewhere that there may be issues related to rotating “in hardware” vs rotating using LVGL. Maybe that’s the case here?
Hi.
Thanks for letting me know.
Can you tell me the version of the TFT_eSPI and LVGL libraries that you’re using?
Also, tell me the version of the ESP32 boards you have installed.
regards,
Sara
Sara,
version information is in the doc which I posted on Google Drive. I believen Thatcher ESP IDF is Version 5, but I need to check.
Gilbert.
Board selected: ESP32-2432S028R CYD, partition schem: HUGE APP
Arduino ESP32 IDF board package: 3.0.7
Arduino ESP IDF version: v5.1.4-972-g632e0c2a9f-dirty
This is what the compiler reports:
FQBN: esp32:esp32:jczn_2432s028r:PartitionScheme=huge_app
Using board ‘jczn_2432s028r’ from platform in folder: C:\Users\Gilbert\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7
Using core ‘esp32’ from platform in folder: C:\Users\Gilbert\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7
Saudações,
Gilbert
Thanks for letting me know.
I tested our codes and they work as expected for us. I believe you might have a slightly different version of the display.
But, your explanation makes much more sense.
Can you share your complete code with the changes so that I can test it on my display?
Regards,
Sara
Wow – the sketch as published in the book works out of the box? Amazing – despite the fact that conflicting orientation settings are used for touchscreen and display?
I can’t check if my display controller is really ILI9341 because the controller chip is sandwiched between the display and the PCB. The shop on AliExpress (Sunton Store) doesn’t provide those details. The shop site provides a link to the manufacturer’s site (http://pan.jczn1688.com/directlink/1/ESP32%20module/2.8inch_ESP32-2432S028R.rar), but you never know if the board is really coming from there. You’ll get a warning that the site is not safe because it uses http, but you can ignore that.
In the manufacturer’s documentation it is mentioned that the controller is ILI9341. Other than orientation I have not (yet) encountered issues which suggest that something is wrong with the display. The manufacturer’s doc also contains the schematics. Looks pretty straightforward to me.
I’ll clean up the code and post it asap.
Regards,
Gilbert
Yes.
After a library update, that was the only way I could get my CYD to properly display the orientation I wanted. We tried many different combinations.
Thanks, then share it with us so that I can also test it.
Regards,
Sara
Still messing with my code. Rotations performed by LVGL, TFT and Touch are driving me nuts…
But I’m getting somewhere.
Gilbert
I’m onto something – I think…
The example in chapter 1.9 (Testing the touchscreen) seems to work, but it isn’t.
The results on the display suggest that all is well. But the LGVL lib is going crazy.
Set rotation to landscape (1 or 3 – problem remains the same). Compile and upload the code.
Tap the display initially about halfway the screen with. Then move to the right until the x-value approaches 240.
Remember: the max x-value is 320 in landscape. So far everything is going well.
Now move further to the right. As soon as the x-value exceeds 240, you still get correct values on the display, but LGVL goes in a loop and keeps spitting ou this error message:
[Warn] (323.970, +30) indev_pointer_proc: Y is -6 which is smaller than zero lv_indev.c:695
I did put in some debug code to see what’s going on, and the interesting thing is that y is nowhere near the limit, while x has exceeded the maximum valye for y.
Sounds crazy? agree.
Here is the serial output at the point where it goes wrong:
Raw touchscreen x = 3006, y = 1252 LV rotation set to 3 Mapping touch point p.x = 3006 to display range 0..320 Resulting value x = 245 After constraining x = 245, y = 67 X = 245 | Y = 67 | Pressure = 2323 [Warn] (323.970, +30) indev_pointer_proc: Y is -6 which is smaller than zero lv_indev.c:695 [Warn] (323.970, +30) indev_pointer_proc: Y is -6 which is smaller than zero lv_indev.c:695
LVGL continues to crank out those error messages, even after I stopped touching the screen.
At the same time there is nothing unusual going on on the display of CYD. In this case, the display will just show x=245, y=67, z=2149.
X = 245 is a correct value, but LVGL swaps X and Y and concludes thet the resulting value for Y is out of range (greater than the max. height of the screen in landscape).
Constraining the x/y value doesn’t help either, because they will be constrained to remain within the dimensions of the display (in this case the max allowed x value is 319)
In the LVGL library code lvgl/src/indev the function indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
is swapping x and y if rotation is set for landscape. That’s probably where things go wrong.
I would be surprised if this was a bug in LVGL, otherwise the internet would be screaming…
So, where am I going wrong? I have posted my sketch here: https://drive.google.com/file/d/1-L6ipaOU3-uMXMWvtFTFmDbONyG3_adx/view?usp=sharing
Hi.
I’m sorry. But I’m not sure what might be wrong.
Did you run the calibration sketch?
Regards,
Sara
Hi Sara, thanks for following up.
I started from scratch again. First simple CYD sketches. All OK. Then the LVGL sketch which is included in the Arduino IDE under File/examples/lvgl/arduino/LGVL_Arduino. That sketch starts as a template to which allows to add LGVL functions litllt by little. Up to the point where I read my touchscreen values in function
my_touchpad_read()
everything works as expected. I can rotate my screen and every time the origin is in the upper left corner; when I tap near the origin I get near-zero x and y values, when I tap in the lower right corner I get near-max values for x and y.
Next step: I pass the x/y values which I obtain from mapping the touch points to display points and then things go the wrong way. The x/y values are picked up by the function indev_pointer_proc() in the LGVL library. Depending on the rotation value x and y coordinates are swapped and/or mirrorred (why ???). The the function is doing a ‘sanity check’ on the result of that process and updates the state of the input device. I haven’t followed the further processing of the input, but IMHO the x/y data are processed incorrectly or (more likely) I’m feeding LVGL with incorrect data.
This is what I’m doing:
/*Read the touchpad*/
void my_touchpad_read( lv_indev_t * indev, lv_indev_data_t * data )
{
// Checks if Touchscreen was touched, and reports Display.x, Display.y and Touch.Pressure (z)
if(touchscreen.tirqTouched() && touchscreen.touched()) {// Get RAW Touchscreen points
TS_Point p = touchscreen.getPoint();
// translate RAW TS points to calibrated Display points
switch(TFT_ROTATION) {
case LV_DISPLAY_ROTATION_0 : // rotation = 0 – portrait, USB @ bottom side
x = map(p.x, 280, 3850, 0, TFT_WIDTH);
y = map(p.y, 200, 3730, 0, TFT_HEIGHT);
x = constrain(x, 0, TFT_WIDTH – 1);
y = constrain(y, 0, TFT_HEIGHT – 1);
break;case LV_DISPLAY_ROTATION_90: // rotation = 1 – landscape, USB @ right side
x = map(p.x, 180, 3750, 0, TFT_HEIGHT);
y = map(p.y, 275, 3850, 0, TFT_WIDTH);
x = constrain(x, 0, TFT_HEIGHT – 1);
y = constrain(y, 0, TFT_WIDTH – 1);
break;case LV_DISPLAY_ROTATION_180: // rotation = 2 – portrait, USB @ top side
x = map(p.x, 275, 3850, 0, TFT_WIDTH);
y = map(p.y, 350, 3900, 0, TFT_HEIGHT);
x = constrain(x, 0, TFT_WIDTH – 1);
y = constrain(y, 0, TFT_HEIGHT – 1);
break;case LV_DISPLAY_ROTATION_270: // rotation = 3 – landscape, USB @ left side
x = map(p.x, 380, 3800, 0, TFT_HEIGHT);
y = map(p.y, 260, 3780, 0, TFT_WIDTH);
x = constrain(x, 0, TFT_HEIGHT – 1);
y = constrain(y, 0, TFT_WIDTH – 1);
break;default:
Serial.printf(“Error: invalid rotation value = %d”, TFT_ROTATION);
x = 0;
y = 0;
break;
}Serial.printf(“\ntouch points p.x, p.y mapped to x = %d, y = %d\n”, x, y);
// pass those DP points to indev.data
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = x;
data->point.y = y;
}
else{
data->state = LV_INDEV_STATE_RELEASED;
}
}
I’ll post the issue on the LGVL forum and see what’s going on there.
Unfortunately the LGVL documentation is not very clear for an LGVL beginnner.
Response to your question if I ran the calibration sketch: I did at first. Then I realized that the sketch was designed for landscape orientation and I was looking for ways to adapt it for portrait orientation. For that point forward, everything turned into a rotation quagmire ;-(. Frankly, I don’t think that rotation and calibration are related somehow. I understand that using the touchscreen w/o calibration or with somewhat imprecise calibration data will produce imprecise coordinates, but never so bad that x/y will be off the chart, so to speak. Worst I have seen so far is that x/y are slightly off-display when I touch the screen near the edge of the display. For the same reason the sketch may think that I’m touching the screen outside the button area when I hit the screen inside the button, close to the border of that button. But that’s not the issue I’m chasing.
Looks like I found the root cause. I may be using LVGL the wrong way (after all I’m an LVGL noob) but the examples in the LVGL documentation don’t work – at least not ona CYD.
I modified lvgl_indev.c (located in “C:\Users\myUserName\Documents\Arduino\libraries\lvgl\src\indev” folder by adding a macro to the corresponding header file like “#define LV_INDEV_POINTER_ROTATED 1” to indicate that the input data are already rotated by the touchscreen driver and then bypass the code which rotates the input data once more like this (file lvgl_indev.c, lines 679 …)
/* rotate input values if input device doesn’t rotate */
#ifndef LV_INDEV_POINTER_ROTATED
if(disp->rotation == LV_DISPLAY_ROTATION_180 || disp->rotation == LV_DISPLAY_ROTATION_270) { data->point.x = disp->hor_res – data->point.x – 1;
data->point.y = disp->ver_res – data->point.y – 1; }
if(disp->rotation == LV_DISPLAY_ROTATION_90 || disp->rotation == LV_DISPLAY_ROTATION_270) { int32_t tmp = data->point.y; data->point.y = data->point.x; // swap x, y data->point.x = disp->ver_res – tmp – 1; } #endif
This prevents that lv_indev rotates the x/y values one more time – they are already rotated!
I tried this approach with a few examples from the eBook and the result is exactly what I hoped for.
feeding the touch values for x/y to the data->point struct in lv_indev doesn’t work – at least not for CYD. If I do that I get an endless stream of warning from lvgl that the point is out of range.
Unless someone tells me that there is a better way (maybe the intended way) to handle this, I consider the case as closed.
Hi.
Thanks for your detailed explanation.
It makes sense.
I’ll try your approach and implement any necessary changes to face this subject in the right and best way.
Thank you so much for taking the time to look into this.
Regards,
Sara
Couldn’t let it go…
But now I nailed it (I’m sure ;-)).
It took me so long to figure out what’s causing the rotation problem because it’s a mix of issues:
(1) The eBook examples rotate the touch screen to LV_DISPLAY_ROTATION_180 (=2) to compensate for the “upside-down” orientation of the touch screen relative to the display. My CYD looks exactly the same as yours, but has the touch screen oriented the same way as the display, hence no rotation works for me.
I noticed right away that the rotation of the touch screen was different from the rotation of the display, which seemed incorrect to me. In hindsight you did the right thing, but failed to provide a bit more background (paricularly for CYD noobs like me). If you had explained that all rotations (touch and display) are done by LVGL and we’re not supposed to change the touch rotation ever, regardless of the display rotation – except for the case where the thouch screen appears to be upside down – that would have helped a lot.
(2) but we’re not home free yet. LVGL input device (= touch screen) lib code for indev rotation is flawed. And testing was the proof of the pudding. This is the current code in function
static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
The function takes the (x,y) values obtained from the touch – display mapped in the user code and rotates those coordinates according to the rotation set for the display. Well, it tries, but fails. I was surprised that the LVGL forum was not on fire because of this, but later I found out that important changes were made in the update to LVGL version 9.2 in August 2024. Maybe not that many people have updated their library to the latest and greatest?
Anyway, my first test (as described in previous posts related to this issue) I bypassed the rotation in this function and took care of the rotation in my own user code. Problem solved – I thought…-.
And then I kept thinking “how is this suppposed to work in the first place?”. I then realized that the user code is not supposed to rotate the input device data and should leave this to LVGL. I looked at the code in file “lvgl/src/indev/indev.c” and found that the rotation is not performed correctly. To confuse us poor sods even more there is another error in the documentation about this feature. On the LVGL page “https://docs.lvgl.io/master/details/main-components/indev.html” the example suggests that the data points (x,y) are touchpad points. This is absolutely incorrect. They are display points!
https://drive.google.com/file/d/1-NoC6CJ0HhXZ5w7RqdRlewjloqCgDj4l/view?usp=sharing
This is the faulty code:
if(disp->rotation == LV_DISPLAY_ROTATION_180 || disp->rotation == LV_DISPLAY_ROTATION_270) { // mirror x,y data->point.x = disp->hor_res – data->point.x – 1; data->point.y = disp->ver_res – data->point.y – 1; } if(disp->rotation == LV_DISPLAY_ROTATION_90 || disp->rotation == LV_DISPLAY_ROTATION_270) { // swap x, y int32_t tmp = data->point.y; data->point.y = data->point.x; data->point.x = disp->ver_res – tmp – 1; }
This is the fix (tested):
switch(disp->rotation) { case LV_DISPLAY_ROTATION_90: tmp = data->point.y; data->point.y = disp->hor_res – data->point.x – 1; data->point.x = tmp; break; case LV_DISPLAY_ROTATION_180: data->point.x = disp->hor_res – data->point.x – 1; data->point.y = disp->ver_res – data->point.y – 1; break; case LV_DISPLAY_ROTATION_270: tmp = data->point.y; data->point.y = data->point.x; data->point.x = disp->ver_res – tmp – 1; break; }
The fix only works when both the display and the touch screen have their origin in the upper left corner. How do we know where the upper left corner is? Show a label uing LVGL (like text “x” or whatever) close to the origin of the display. That’s where the upper left corner is. Now tap on the screen near that marker. If (x,y) produced by reading the touch screen values has low values (less than +/- 500 is OK) then the touch screen has its origin in the same corner. Otherwise the touch screen has its origin most likely in the opposit corner and needs to be rotated 180°.