Forum Replies Created

Viewing 1 post (of 1 total)
  • Author
    Posts
  • in reply to: PCF8574 how to add / use encoder with multiclick #29086
    MAWMN
    Participant

      Hello,
      just finnished my demo project, see the code below, perhaps also nice to use as an example :

      thanks and bye..

      
      /*
       * PCF8574 GPIO Port Expander
       * https://www.mischianti.org/2020/03/13/pcf8574-i2c-digital-i-o-expander-rotary-encoder-part-2/
       *
       * Connection Overview
       * PCF8574 —- WeMos-D1
       * A0 —- GND
       * A1 —- GND
       * A2 —- GND
       * VSS —- GND
       * VDD —- 5V/3.3V
       * SDA —- D1(PullUp) // GEEL
       * SCL —- D2(PullUp) // GROEN
       * INT —- INT(PullUp) // WIT
       *
       * PCF8574 —- Rot.Encoder
       * P0 —- ENCODER PIN A
       * P1 —- ENCODER PIN B
       * P2 —- ENCODER BUTTON
       *
       */
      #include "Arduino.h"
      #include "PCF8574.h"
      
      // * Wemos-D1 defines
      #define sdaPin D1 // This is the default Pin for Wemos-D1
      #define sclPin D2 // This is the default Pin for Wemos-D1
      // #define sdaPin D5 // ! NOT the default Pin - NOT WORKING @ Wemos-D1 ???
      // #define sclPin D6 // ! NOT the default Pin - NOT WORKING @ Wemos-D1 ???
      #define INTERRUPTED_PIN D3 // Wemos-D1 pin (D3 = Interrupt pin for Encoder)
      
      // * RotaryEncoder defines (Connected to PCF8574)
      #define encoderPinA P0
      #define encoderPinB P1
      #define switchPin P2
      
      // Pre formatted Header for Interrupt (See Initialisation of updateEncoder Interrupt Below),
      // necessary here because of the Interrupt Initialisation in the next Line
      void IRAM_ATTR updateEncoder(); // ! *** IRAM_ATTR because this is the ISR !!!
      
      // Initialize PCF-library Interrupt
      PCF8574 pcf8574(0x38, INTERRUPTED_PIN, updateEncoder); // for Default I2C Pins
      // PCF8574 pcf8574(0x38, sdaPin, sclPin, INTERRUPTED_PIN, updateEncoder); // ! NOT the default Pins - NOT WORKING @ Wemos-D1 ???
      
      // Initialize Rot.Encoder Global variables
      volatile long encoderValue = 0;
      uint8_t encoderButtonVal = HIGH;
      
      bool changed = false;
      
      /*****************************************/
      /***** MULTICLICK_DEBUG_PRINT MACRO *****/
      /*****************************************/
      // ? Uncomment to enable printing out nice debug messages.
      // ? #define MULTICLICK_DEBUG
      #define MULTICLICK_DEBUG
      
      // Define where debug output will be printed.
      #define MULTICLICK_PRINTER Serial
      
      // Setup DEBUG printing MACRO
      #ifdef MULTICLICK_DEBUG
      #define MULTICLICK_DEBUG_PRINT(...) { MULTICLICK_PRINTER.print(__VA_ARGS__); }
      #define MULTICLICK_DEBUG_PRINTLN(...) { MULTICLICK_PRINTER.println(__VA_ARGS__); }
      #else
      #define MULTICLICK_DEBUG_PRINT(...) {}
      #define MULTICLICK_DEBUG_PRINTLN(...) {}
      #endif
      
      //**********************************************************************/
      //! *** Additions tbv MULTI-CLICK: One Button with Multiple Events !!! */
      //**********************************************************************/
      #define maxMulticlickGap 1000 // Time where in between the multiclick must remain
      #define maxLongPress 2000 // Max time in ms. to wait berfore clearing the firstPressTS
      
      bool btnPressed = false;
      bool btnReleased = true;
      int lastBtnState = 0;
      uint32 firstPressTS = 0; // Timestamp of the first btnPress moment
      int clicksCnt = -1; // Count the number of Clicks before the firstPressTS Reset occurs (-1 = OFF)
      bool clickHandled = false;
      //**********************************************************************/
      
      //******************************************************************/
      //! ***** !!! ISR Functie tbv Encoder ButtonTurn & ButtonPress !!! */
      //******************************************************************/
      uint8_t encoderPinALast = LOW;
      uint8_t valPrecEncoderButton = LOW;
      
      void updateEncoder() { // ! *** IRAM_ATTR NOT necessary here for ISR !!!
      // Encoder management
      	uint8_t n = pcf8574.digitalRead(encoderPinA);
      	if ((encoderPinALast == LOW) && (n == HIGH)) {
      		if (pcf8574.digitalRead(encoderPinB) == LOW) {
      // * ClockWise
      			encoderValue++; // ! if we need to change counting for CW & CCW, we need to do it here
      			changed = true; // Changed the value of the encoder
      		} else {
      // * Counter ClockWise
      			encoderValue--; // ! if we need to change counting for CW & CCW, we need to do it here
      			changed = true; // Changed the value of the encoder
      		}
      	}
      	encoderPinALast = n;
      
      // Button management
      	encoderButtonVal = pcf8574.digitalRead(switchPin);
      	if (encoderButtonVal != valPrecEncoderButton) {
      // Changed the value of button
      		changed = true;
      		valPrecEncoderButton = encoderButtonVal;
      	}
      }
      
      //**********************************************************************/
      //! *** Additions tbv MULTI-CLICK: One Button with Multiple Events !!! */
      //**********************************************************************/
      void checkButton(bool buttonVal) {
      	if (buttonVal == 0) {
      		btnPressed = true;
      		btnReleased = false;
      	} else {
      		btnPressed = false;
      		btnReleased = true;
      	}
      
      	if (btnPressed == true) {
      		MULTICLICK_DEBUG_PRINTLN("btnPressed..");
      		if (firstPressTS == 0) {
      			firstPressTS = millis();
      			MULTICLICK_DEBUG_PRINT("firstPressTS set to - ");
      			MULTICLICK_DEBUG_PRINTLN(firstPressTS);
      			clicksCnt = 0;
      		}
      	} else if (btnReleased == true) {
      		MULTICLICK_DEBUG_PRINTLN("btnReleased..");
      	}
      
      	if (lastBtnState != buttonVal) {
      // There is a change in State
      		if (btnReleased == true) {
      // Take action when the button is being Released
      			if ((millis() - firstPressTS) < maxMulticlickGap) {
      				clicksCnt += 1;
      			}
      		}
      	}
      
      	lastBtnState = buttonVal;
      }
      
      // MultiClick Events to trigger
      void singleClickEvent() {
      	MULTICLICK_DEBUG_PRINTLN("Perform singleClickEvent");
      	clickHandled = true;
      }
      
      void doubleClickEvent() {
      	MULTICLICK_DEBUG_PRINTLN("Perform doubleClickEvent");
      	clickHandled = true;
      }
      
      void tripleClickEvent() {
      	MULTICLICK_DEBUG_PRINTLN("Perform tripleClickEvent");
      	clickHandled = true;
      }
      
      void longPressEvent() {
      	MULTICLICK_DEBUG_PRINTLN("Perform longPressEvent");
      	clickHandled = true;
      }
      //**********************************************************************/
      
      //******************************************************/
      //***** Application Setup.. *****/
      //******************************************************/
      void setup() {
      	/**************************/
      	/***** StartUp Serial *****/
      	/**************************/
      	Serial.begin(115200);
      	Serial.println();
      	Serial.flush();
      	delay(1000);
      
      	/*****************************************************/
      	/***** I2C Setup, Encoder Setup *****/
      	/*****************************************************/
      // pcf8574.begin(); // ! ???? DO we NEED This, works also without defining for using the default I2C Pins ????
      // Setup PCF8574 pins
      	pcf8574.pinMode(encoderPinA, INPUT_PULLUP);
      	pcf8574.pinMode(encoderPinB, INPUT_PULLUP);
      
      // Setup Encoder Switch button
      	pcf8574.pinMode(switchPin, INPUT_PULLUP);
      
      // Set low latency with this method or uncomment LOW_LATENCY define in the library
      // Needed for the Encoder
      	pcf8574.setLatency(0);
      
      // Start the PCF8574 library
      	MULTICLICK_DEBUG_PRINT("Init pcf8574…");
      	if (pcf8574.begin()) {
      // Board/Chip is Acknowledging the I2C Commands
      		MULTICLICK_DEBUG_PRINTLN("OK");
      	} else {
      // Board/Chip is NOT Acknowledging the I2C Commands
      		MULTICLICK_DEBUG_PRINTLN("NOT OK");
      	}
      }
      
      //******************************************************/
      //***** Application MAIN Loop.. *****/
      //******************************************************/
      void loop() {
      	if (changed) {
      		MULTICLICK_DEBUG_PRINT("ENCODER -> ");
      		MULTICLICK_DEBUG_PRINT(encoderValue);
      		MULTICLICK_DEBUG_PRINT(" - BUTTON -> ");
      		MULTICLICK_DEBUG_PRINT(encoderButtonVal ? "HIGH -> " : "LOW -> ");
      
      // Get button event and act accordingly
      		checkButton(encoderButtonVal);
      
      		changed = false;
      	}
      
      // handling of the checkButton() function - firstPressTS is running..
      	if (firstPressTS != 0) {
      // Perform ClickEvents based on maxMulticlickGap
      		if ((millis() - maxMulticlickGap) >= firstPressTS) {
      			if (clicksCnt == 1)
      				singleClickEvent();
      			if (clicksCnt == 2)
      				doubleClickEvent();
      			if (clicksCnt == 3)
      				tripleClickEvent();
      			if (clicksCnt <= 3) {
      				clicksCnt = 0;
      			}
      		}
      
      		if ((millis() - maxLongPress) >= firstPressTS) {
      // MULTICLICK_DEBUG_PRINT("numClicks : ");
      // MULTICLICK_DEBUG_PRINTLN(clicksCnt);
      			if (clicksCnt >= 4) {
      // number of clicks out of range
      				MULTICLICK_DEBUG_PRINTLN("numClicks Out of Range..");
      			} else if ((clicksCnt == 0) && (clickHandled == false))
      				longPressEvent();
      
      // Clear firstPressTS to stopped..
      			MULTICLICK_DEBUG_PRINT("firstPressTS cleared @ ");
      			MULTICLICK_DEBUG_PRINTLN(millis());
      			firstPressTS = 0;
      			clicksCnt = 0;
      			clickHandled = false;
      		}
      	}
      }
      
      
      • This reply was modified 11 months, 2 weeks ago by Renzo Mischianti. Reason: Format code
    Viewing 1 post (of 1 total)