M5Core2 library fork that supports multi-touch and provides Arduino-style virtual buttons [update: and gestures and events] [update2: MERGED !]

  • I just pushed a new version that handles two fingers on same button (background is a button) properly.

    With that version, try this (it will be an example later) to nicely visualise what the sensor sees.

     #include <M5Core2.h>
    const int r = 50;
    void setup() {
      M5.Events.addHandler(moveEvent, E_MOVE);
      M5.Events.addHandler(touchEvent, E_TOUCH);
      M5.Events.addHandler(releaseEvent, E_RELEASE);
    void loop() {
    void moveEvent(Event &e) {
      M5.Lcd.drawCircle(e.from.x, e.from.y, r, BLACK);
      M5.Lcd.drawCircle(e.to.x, e.to.y, r, e.finger ? BLUE : RED);
    void touchEvent(Event &e) {
      M5.Lcd.drawCircle(e.to.x, e.to.y, r, e.finger ? BLUE : RED);
    void releaseEvent(Event &e) {
      M5.Lcd.drawCircle(e.to.x, e.to.y, r, BLACK);

  • Or, even shorter (and with slightly thicker and better visible circles):

    #include <M5Core2.h>
    const int r = 50;
    void setup() {
    void loop() {
      Event& e = M5.background.event;
      if (e & (E_MOVE | E_RELEASE)) circle(e & E_MOVE ? e.from : e.to, WHITE);
      if (e & (E_TOUCH | E_MOVE)) circle(e.to, e.finger ? BLUE : RED);
    void circle(Point p, uint16_t c) {
      M5.Lcd.drawCircle(p.x, p.y, r, c);
      M5.Lcd.drawCircle(p.x, p.y, r + 2, c);

  • New version is up. You can now make gestures that have only directions. So that whole bit in the first part of the example is now:

    // Defines gestures
    Gesture swipeRight("swipe right", 160, RIGHT, 30, true);
    Gesture swipeDown("swipe down", 120, DOWN, 30, true);
    Gesture swipeLeft("swipe left", 160, LEFT, 30, true);
    Gesture swipeUp("swipe up", 120, UP, 30, true);
    • The first number is the minimum distance, the RIGHT, LEFT, etc are just #defines for compass directions, the 30 thereafter is 'plus or minus 30 degrees'. The Gesture definitions have the same rot1 flag (the true at the end above) to denote that the direction is relative to the screen in rotation 1. You can still specify two zones for start and finish before the name of the gesture. If you want to specify just one, say ANYWHERE for the other. This direction stuff works because support for it is now in the underlying Point class. You can also say pointA.directionTo(PointB) or if (PointA.isDirectionTo(PointB, 0, 15)) ... to see if it's due north plus or minus 15 deg. Event has the same functions without the To because it has a start and end point in there already. So if you can want to see if a key was dragged upwards you just put something like that in the E_DRAGGED handler.

    • Key repeat with myButton.repeatDelay and repeatInterval, gives repeated E_PRESSING events.

    • The Events object is gone and its (few) functions are now functions of Buttons. Last time I move stuff around like that, I swear.

    • All my code is now formatted (with clang-format) for easier reading and reworked to be maximally understandable. Will work in some more documentation on how I did things, esp. rotation and other tricky bits. (The .clang-format config file is in the root, see the comment in there for usage. I chose Google's style guide with a few minor tweaks, because that was closest to what the rest of the library was doing already anyway.)

    • I expect to be submitting a Pull Request sometime in the next few days after I finish with the documentation.

    • I'd be very happy with any test reports.

  • Rop, M5Button.cpp, line 176 is:


    ...making it hard to catch any of my own debug output.
    It would be much easier to open issues if you enabled Issues on https://github.com/ropg/M5Core2/tree/visual.

  • @vkichline Ooops, further up I had said:

    The version that's online now (I merged 'visual' into master on my repo) has fixes

    but I did not delete the 'visual' branch. My bad. Use the master branch. Much nicer. Some changes, you'll see in the example. (gestures now have directions as well as start and end zones, all optional.)

    Will enable issues also...

  • This won't compile for me, it conflicts with another library I need and admire: ezTime.
    I get this over and over again:

    C:/users/van/.platformio/lib/M5Core2/src/utility/M5Button.h:379:14: error: expected identifier before numeric constant
     #define NONE 0x0120  // Special color value: transparent
    C:/users/van/.platformio/lib/ezTime/src/ezTime.h:69:2: note: in expansion of macro 'NONE'

    If this specific case is fixed, it's likely to cause conflicts elsewhere. Maybe name spacing could be used to make a macro as general as NONE more compatible.

  • Rop, I've opened issues on your fork, and will continue to do so as I work through the library.
    I wrote a goal-based UI evaluation tool for Touch and posted it here: https://github.com/vkichline/TouchGoal . Please give it a try!
    It asks you to accomplish 16 Touch tasks in random order and keeps score, telling you what you missed at the end. You have up to 8 seconds for each task, but they end as soon as you successfully complete them, so it's pretty fast.
    I had written some tools to spew event names, but didn't get a feel for how Touch worked in real life; this test tool provides that feedback.

    I expected to have issues with gestures based on my serial spewing tests, but they seem quite solid. However, I usually fail to get all the double taps. Does double-tapping work better for you?
    Age and fast-twitch motor neurons have a lot of sway over double-click success, it may be a bit too hot for me. Can the delay be adjusted?
    If you can breeze through this test w/o failures, that would tell you the UI is doing what you expect it to do.

  • @vkichline Thanks!! Shouldn't have used NONE, neither here nor in ezTime. Its now NODRAW (again) in this library. i like TouchGoal. Fixed all the issues, I think. The documentation has been completely overhauled.

  • @vkichline In fact, shall we include TouchGoal as an example with the M5Core2 library?

  • You're welcome to include it! Some additional tests for what shouldn't happen and a few comments about the best way to detect things would make it more valuable.
    It's always nice to have a comprehensive suite to walk through after making 'improvements'. I could see TouchGoal being useful for testing subclasses of buttons, zones, etc.
    When you think about it being used for testing subclasses, are there any additions you can think of? It's mostly positive testing now, few negative checks. (For example, you should not get this event before that event in this case...)

  • Tried your changes and I can successfully double-tap every time now. Excellent!
    I will add long presses to TouchGoal as soon as I figure them out. But today's my anniversary, so until later!

  • I think it's a wrap...

    That is: on something this size there will always be more work to do and corners that could be even cleaner. But i think it works, does the job, is well-documented and at the very least doesn't break anything else.

    So please give it a good test and tell me what y'all think, and then I'll make the PRs for M5Core2 (with M5Touch) and M5Stack (just m5Button).

    @vkichline By all means wrap up TouchGoal so it can be included.

    Everyone: please give the documentation a read and maybe write a tiny thing yourself to see if it works like you would expect... I'd love some more really basic simple examples if you can find the time...

  • I've updated TouchGoal with long touches and better syntax. It's ready to go. BTW, I consistently get 100% scores now, so Touch is doing exactly what I want it to do.

    I've added another testing program you may be interested in as well: TouchView.
    It displays events to the screen as well as to the serial port. I like to test in an easy chair.
    The A button turns E_MOVE an and off, B enables/disable long touches, and C toggles key repeat.
    A good way to look for unexpected events. Looks clean to me.

    I did report one bug I'd call a show stopper; #22. (I added a lot of issues, so it could get lost in the soup):

    • Run events_buttons_gestures_rotation
    • Double-tap top-left button. Turns blue. Good.
    • Swipe up. Display inverts. Good.
    • Double-tap button named "top-left" (now at bottom-right.) Button named "bottom-left" (in top-right position) becomes blue. Error.

    Also, the file M5Touch.h didn't get updated; it has at least one example program (the first example in the top level file) which does not compile. I opened detailed issues at https://github.com/ropg/M5Core2/issues. It looks like a lot but most are typos.

    I know I'm a noisy gong, always with the nits and quibbles, but on the whole this is excellent! I'm surprised at how reliably the gestures work; I took a stab at that myself and it was far from trivial. The two-finger support and rotation really go above and beyond as well! A huge leap forward for the Core2 platform.

  • This post is deleted!

  • @Rop I've just started some work porting some old Core 1 M5Stack code to the Core 2 API and found that the 3 bottom buttons don't work quite in the same way.

    For instance this:

    if (M5.BtnA.isPressed() && M5.BtnC.isPressed()) {
    // do something

    doesn't work on the Core 2 despite the multi touch. It only registers the first button to be pressed rather than both.

    Is this something you could fix or provide a hint as to what I need to look at to hack it myself?

  • For anybody else looking at fixing the issue with multiple simultaneous button presses it seems it can't be done.

    Having dived into the M5Core2 source the events generated for two button presses, e.g. A & C, are press for A and move for A.
    This looked like a deeper bug (since the second should have been identified as C), so going further in I found that an else branch wasn't refreshing the currently identified touched button on second press (C) in the method "doEvents" in touch.cpp (it doesn't help the code isn't documented, isn't single responsibility and has an arrow antipattern too).

    Fixing that bug, so the correct button is refreshed, showed that two events together did not produce presses for A & C but instead (I assume - I'm fed up at this point) the hardware averages the presses' coordinates and declares it's button B(!) that is pressed.

    If you have old Core 1 code you're porting that uses two simultaneous button presses I'd suggest changing the code to double taps of a single button instead which is the workaround I'm about to use - to remain consistent this behaviour is both on Core 1 and Core 2

    More formally, I should have read the file header in touch.h earlier:

    the M5Stack Core2 touch display is only
    multi-touch in one dimension. What that means is that it can detect two
    separate touches only if they occur on different vertical positions.
    This has to do with the way the touch screen is wired, it's not
    something that can be changed in software. So you will only ever see
    two points if they do not occur side-by-side. Touches that do happen
    side-by-side blend into one touch that is detected somewhere between
    the actual touches.