🤖Have you ever tried Chat.M5Stack.com before asking??😎
    M5Stack Community
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Register
    • Login

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

    Core 2
    5
    55
    209.3k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • RopR
      Rop
      last edited by

      It got merged... Yay!

      1 Reply Last reply Reply Quote 1
      • felmueF
        felmue
        last edited by

        Hey @Rop

        congratulation. This is great news!

        Cheers
        Felix

        GPIO translation table M5Stack / M5Core2
        Information about various M5Stack products.
        Code examples

        1 Reply Last reply Reply Quote 0
        • vkichlineV
          vkichline
          last edited by

          Congrats, Rop, but I have bad news, too.
          They did not bump the version number, so Arduino or PlatformIO install the original release.
          I manually downloaded and installed the update; verified the touch example was updated, and it works fine.
          However, I can no longer run the factory test at all. The old hack that made it work no longer works, and Felix's suggestions don't help either.
          So unfortunately, there are still issues to be addressed.

          1 Reply Last reply Reply Quote 0
          • felmueF
            felmue
            last edited by

            Hi guys

            in the M5Core2 library all I2C devices are using the internal I2C bus on Wire1 (GPIO 21 and 22).

            That actually hasn't changed with Rop's touch pull-request - all that has changed is that Wire now is setup to use GPIO 32 and 33 which is the external I2C bus.

            Factory test source code was and is still wrongly using Wire for scanning and testing of internal I2C devices.

            @vkichline - to fix factory test open Core2_Factory_test.ino and change the four occurrences of Wire to Wire1.

            Happy Stacking!
            Felix

            GPIO translation table M5Stack / M5Core2
            Information about various M5Stack products.
            Code examples

            1 Reply Last reply Reply Quote 0
            • vkichlineV
              vkichline
              last edited by

              @felmue, confirmed that the correct fix for Core2_Factory_test is to change lines 446 & 447 from Wire. ... to Wire1. ...
              With that, @Rop's update works fine.
              Thanks to both of you!

              1 Reply Last reply Reply Quote 0
              • vkichlineV
                vkichline
                last edited by

                @rop, perhaps this topic should be closed since it's part of the standard lib now, but I have a question about TouchButtons and don't know where else to post it.
                With your change checked in, I feel that I can start to develop some Core2 code at last. I am trying to get M5Stack-SD-Updater working with your addition to touch, and I have found a difference between the simulated and physical BtnA/B/C behavior which is problematic.
                If you run this brief sketch on an M5Stack Fire and on a Core2 (chainging the header as required)

                #include <M5Core2.h>
                void setup() { M5.begin(); }
                void loop() {
                  M5.update();
                  if(M5.BtnA.isPressed()) M5.Lcd.fillScreen(WHITE);
                  else M5.Lcd.fillScreen(BLACK);
                  delay(100);
                }
                

                ...you will see that on the Fire, if you reset the device with BtnA pressed, the screen will immediately turn white.
                On the Core2, if you reset with the simulated BtnA pressed, the screen will remain black until you release and press the button again.
                This makes it tricky to use the M5Stack-SD-Updater gesture of resetting with A pressed to load the menu application.

                Can you think of some work-around I could insert, perhaps in the setup() function, to make an already pressed button read as pressed in loop()?

                vkichlineV 1 Reply Last reply Reply Quote 0
                • vkichlineV
                  vkichline @vkichline
                  last edited by

                  I find that touch::ispressed() does not return true until you release and press again if you reset with the screen pressed.
                  So this may be quite difficult...

                  RopR 1 Reply Last reply Reply Quote 0
                  • felmueF
                    felmue
                    last edited by

                    Hi @vkichline

                    hmm, I think that is a tricky one. I tried a few things but have not found a solution.

                    I believe the touch IC constantly monitors the electrostatic field and automatically adapts to slow changes to get a 'baseline'. And only big changes in a short period of time will be taken into account for touch.

                    So when the finger already is on the screen when the device boots that is simply taken as baseline and only lifting and touching the screen again is a big enough change to count as touch.

                    Thanks
                    Felix

                    GPIO translation table M5Stack / M5Core2
                    Information about various M5Stack products.
                    Code examples

                    1 Reply Last reply Reply Quote 0
                    • RopR
                      Rop @vkichline
                      last edited by

                      @vkichline Booting with the screen pressed. I had not thought of that... It will be fixed.

                      1 Reply Last reply Reply Quote 0
                      • vkichlineV
                        vkichline
                        last edited by

                        I find that if you boot with the screen touched, M5.Touch.ft6336(0x02, 11, data) returns nothing but 11 zeros until you release-retouch the screen.
                        You get the same stream of zeros if you boot without touching the screen, until it is touched.
                        Frankly I doubt if there's a solution. The screen knows knows no "untouched state", it has to figure it out each time its booted. But perhaps some arcane stream of I2C commands con compensate.

                        I think the only "special boot condition" one might detect with the Core2 will be positional; the device is upside down, or standing on one particular edge. The presence/absence of an SD card could be used as well, but then you'd be much more likely to encounter unintentional special boot conditions.

                        Or perhaps a special condition could be detected just before reboot and written to NVS.

                        RopR 1 Reply Last reply Reply Quote 0
                        • RopR
                          Rop
                          last edited by

                          @vkichline Ah, OK. So it's not only my state-machine... Hmmm... I like the upside down idea, that would be pretty untypical in the real world.

                          BTW: I made a new version that does pretty things on the screen. Check out the example with the 'visual' branch of my fork. Please test. I think you'll like it. Will document it soon and submit another PR.

                          1 Reply Last reply Reply Quote 0
                          • RopR
                            Rop @vkichline
                            last edited by

                            @vkichline Hey, there's also a RTC. So you could check whether a second user reset happens soon after. That's pretty intuitive.

                            1 Reply Last reply Reply Quote 0
                            • vkichlineV
                              vkichline
                              last edited by

                              @rop said:

                              Check out the example with the 'visual' branch of my fork. Please test. I think you'll like it.

                              I do like it! The results the touch demo gets from a single page of code are remarkable!
                              I do have some feedback though. (If you ask me to test something, you will always get bug reports.)

                              Could you enable Issues for https://github.com/ropg/M5Core2/tree/visual? Better to keep a discussion issues on that branch with the repo.

                              1 Reply Last reply Reply Quote 0
                              • vkichlineV
                                vkichline
                                last edited by

                                @rop,
                                In TouchButton::pressedFor(uint32_t ms),
                                _time - _lastChange is always equal to zero. pressedFor(n) always returns false.
                                It looks like in some of these TouchButton routines, _time should be replaced with millis().

                                1 Reply Last reply Reply Quote 0
                                • RopR
                                  Rop
                                  last edited by

                                  I did some work the past few days. :)

                                  First of all my stuff is now three parts. M5Touch provides the Touch library and nothing else. It will talk to M5Button (if that's present') which provides Buttons, Gestures and Events. PointAndZone provides the Point and Zone primitives that they both depend on. M5Button's Button also does hardware buttons that can have a zone on the screen to draw their buttons in and it can be used independently from M5Touch. Yes, of course that means Button and the whole Events thing are coming to the M5Stack classic.

                                  Zones (and thus Buttons) have a rot1 flag that makes them stay in rotation 1, meaning that they stay in the same place even if you rotate the screen. To see that demoed, compile the example app, hold the device in the desired direction and swipe from what's then the top to the bottom of the screen. Also try pressing the off-screen circles in different rotations. (Yes, when the screen is upside down they have negative y coords... :)

                                  Much has changed internally and I will document everything painstakingly over the next days. It is now much easier to write code for the library itself because objects have an instance pointer so you can reference the screen from some other object without #including the whole M5Core2.h kitchen sink or doing other ugly things. All my changes to existing files such as M5Display compile on both classic and Core2: all the changes are behind #ifdefs, #define in Config.h.

                                  Everything is super-efficient, such that Button's visual capabilities are not in the way if you're not using them, etc etc.

                                  As for existing code:

                                  • M5.Touch has been split up: some functionality (addHandler, etc) now lives in M5.Event
                                  • TouchPoint -> Point, TouchZone -> Zone, TouchButton -> Button, TouchEvent -> Event
                                  • Event names start with E_ not TE_

                                  It's all in the visual branch on my fork

                                   

                                   

                                  @vkichline The pressedFor bug is also fixed...

                                  1 Reply Last reply Reply Quote 0
                                  • felmueF
                                    felmue
                                    last edited by

                                    Hi @Rop

                                    I am trying to dynamically create (and delete) a button, but I get a crash when the button is about to be deleted. (When I use a static button it works.)

                                    The code creates a MsgBox and upon pressing button B a new empty screen with a fullscreen button is opened and when I touch it I get the crash.

                                    Guru Meditation Error: Core 1 panic'ed (InstrFetchProhibited). Exception was unhandled.

                                    I am sure I am overlooking something so if you have a minute I'd appreciate your input.

                                    Thanks
                                    Felix

                                    #include <M5ez.h>
                                    
                                    #define STATIC_BUTTON
                                    
                                    void buttonCallback(Event& e);
                                    
                                    bool page_done = false;
                                    
                                    #ifdef STATIC_BUTTON
                                    Button button1(0, 0, 320, 240, false ,"button1");
                                    
                                    void showPage() {
                                      button1.addHandler(buttonCallback, E_TOUCH + E_BTNONLY);
                                      page_done = false;
                                      while(page_done == false) {
                                        M5.Touch.update();
                                      }
                                      button1.delHandlers(buttonCallback);
                                    }
                                    
                                    #else
                                    
                                    Button *button1 = NULL;
                                    
                                    void showPage() {
                                      button1 = new Button(0, 0, 320, 240, false, "button1");
                                      button1->addHandler(buttonCallback, E_TOUCH + E_BTNONLY);
                                      page_done = false;
                                      while(page_done == false) {
                                        M5.Touch.update();
                                      }
                                      button1->delHandlers(buttonCallback);
                                      delete(button1);
                                    }
                                    #endif
                                    
                                    void buttonCallback(Event& e) {
                                      page_done = true;
                                    }
                                    
                                    void setup() {
                                      ez.begin();
                                    }
                                    
                                    void loop() {
                                      ez.msgBox("M5ez button test", "Button test !", "showPage");
                                      showPage();
                                    }
                                    

                                    GPIO translation table M5Stack / M5Core2
                                    Information about various M5Stack products.
                                    Code examples

                                    1 Reply Last reply Reply Quote 0
                                    • RopR
                                      Rop
                                      last edited by Rop

                                      I've worked extensively on the events system, so that there is now only one event per call to M5.update(). That means you can now also check for events in loops, no need for handler functions if you don't want them. Get the new library and try this:

                                      #include <M5Core2.h>
                                      
                                      ButtonColors on = {RED, WHITE, WHITE};
                                      ButtonColors off = {BLACK, WHITE, WHITE};
                                      
                                      void setup() {
                                        M5.begin();
                                        while (true) {
                                          screen1();
                                          screen2();
                                        }
                                      }
                                      
                                      // Never comes here
                                      void loop() {
                                      	M5.update();
                                      }
                                      
                                      void screen1() {
                                        M5.Lcd.clearDisplay(YELLOW);
                                        Button tl(5, 5, 150, 110, false ,"top-left", off, on, TL_DATUM);
                                        while (true) {
                                          M5.update();
                                          if (tl.isPressed()) return;
                                        }
                                      }
                                      
                                      
                                      void screen2() {
                                        M5.Lcd.clearDisplay(BLUE);
                                        Button tr(165, 5, 150, 110, false, "top-right", off, on, TR_DATUM);
                                        while (true) {
                                          M5.update();
                                          if (tr.event.type == E_DBLTAP) return;
                                        }
                                      }
                                      

                                      As you can see you don't need to orry about manually deleting the buttons, they go away as the function ends. But you cannot call one function from the other, because that leaves the variables in existence. Then you'd have to 'new' and 'delete' them indeed.

                                      1 Reply Last reply Reply Quote 0
                                      • felmueF
                                        felmue
                                        last edited by

                                        Hello @Rop

                                        Thank you for the update. Unfortunately no luck for me. When I run your code with the latest M5Core2 from your visual branch I get a crash as soon as the code tries to instantiate the button in screen1() function.

                                        When I declare the buttons globally the code works, but as you mentioned they don't go away.

                                        I wonder if the button object is too large to be declared in a function?

                                        Thanks
                                        Felix

                                        GPIO translation table M5Stack / M5Core2
                                        Information about various M5Stack products.
                                        Code examples

                                        1 Reply Last reply Reply Quote 0
                                        • felmueF
                                          felmue
                                          last edited by felmue

                                          Hi @Rop

                                          Update2: I've created a pull-request with below fixes for your convenience.

                                          https://github.com/ropg/M5Core2/pull/2

                                          I have found the reason for the crash: drawFn is not NULL, yet not set correctly which causes the crash when calling drawFn(this, bc) on line 212.

                                          void Button::draw(ButtonColors bc) {
                                            // use locally set draw function if aplicable, global one otherwise
                                            if (drawFn) {
                                              drawFn(this, bc);
                                          

                                          I've fixed it by modifying two lines (137, 157) in M5Button.h and initialising drawFn with nullptr.

                                          void (*drawFn)(Button* b, ButtonColors bc) = nullptr;
                                          

                                          Update: Just found another crash when using new and delete. Fixed by setting _freeFont to nullptr (lines 108 and 125).

                                          const GFXfont* _freeFont = nullptr;
                                          

                                          Thanks
                                          Felix

                                          GPIO translation table M5Stack / M5Core2
                                          Information about various M5Stack products.
                                          Code examples

                                          1 Reply Last reply Reply Quote 0
                                          • RopR
                                            Rop
                                            last edited by

                                            @felmue Thanks! I fixed it but in the cpp and not in the .h. I prefer to not have too much in the header files.

                                            The version that's online now (I merged 'visual' into master on my repo) has fixes. Also there's now a copy of the original M5Stack library on my Github as well, it has the identical M5Button library file with Buttons and Events. Allows you to compile

                                            #include <M5Stack.h>
                                            
                                            void setup() {
                                              M5.begin();
                                              M5.BtnA.setLabel("Test");
                                              M5.BtnB.setLabel("Wow !");
                                              M5.BtnC.setLabel("Amazing !");
                                              M5.BtnA.off = M5.BtnB.off = M5.BtnC.off = {BLUE, WHITE, NODRAW};
                                              M5.BtnA.on = M5.BtnB.on = M5.BtnC.on = {RED, WHITE, NODRAW};
                                              M5.Events.addHandler(eventDisplay);
                                              M5.Buttons.draw();
                                            }
                                            
                                            void loop() {
                                              M5.update();
                                            }
                                            
                                            void eventDisplay(Event& e) {
                                              Serial.printf("\n%-12s %-18s", e.typeName(), e.objName());
                                              if (e.type == E_RELEASE || e.type == E_PRESSED) Serial.printf("%5d ms", e.duration);
                                            }
                                            

                                            (I'll post this wider after a bit more testing and documenting.)

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post