diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b63b59..8639547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.4.0] - 2025-01-06 +- refactor to sync with rotaryDecoder8 (PCF8575) +- add **reset()** +- add **getRECount()** +- update readme.md +- update unit test + +---- + ## [0.3.1] - 2024-06-20 - changed **uint8_t readInitialState()** to return the read state. - changed **bool setValue(uint8_t re, int32_t value = 0)** to return false diff --git a/LICENSE b/LICENSE index 3afaaed..6e5129e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2024 Rob Tillaart +Copyright (c) 2021-2025 Rob Tillaart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 7782039..7e02ebf 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Arduino library for a PCF8574 based rotary decoder - supports 4 rotary encoders. ## Description -This experimental library uses a PCF8574 to read the pulses of a rotary encoder. +This library uses a PCF8574 to read the pulses of a rotary encoder. As a PCF8574 has 8 lines up to 4 decoders can be read over I2C. The PCF interrupt line can be used to detect changes in the position of the encoders. @@ -24,20 +24,28 @@ If less than 4 rotary encoders are connected one should use the lower bit lines library assumes these are used. Furthermore it is advised to connect the free PCF8574 pins to GND so you will not get unintended interrupts. +Note: This library works (limited) with the PCF8575. -#### Related +As always, feedback is welcome. + +### Related + +- https://github.com/RobTillaart/rotaryDecoder - https://github.com/RobTillaart/rotaryDecoderSwitch +- https://github.com/RobTillaart/rotaryDecoder8 +- https://github.com/RobTillaart/rotaryDecoderSwitch5 - https://github.com/RobTillaart/PCF8574 +- https://github.com/RobTillaart/PCF8575 ## Interface ```cpp -#include "rotaryEncoder.h" +#include "rotaryDecoder.h" ``` -#### Constructor +### Constructor - **rotaryDecoder(const int8_t address, TwoWire \*wire = Wire)** constructor to set the address and optional the Wire bus. @@ -45,63 +53,74 @@ constructor to set the address and optional the Wire bus. count is the number of rotary encoders connected. (Max 4 per PCF8574) Returns true if the PCF8574 is on the I2C bus. - **bool isConnected()** returns true if the PCF8574 is on the I2C bus. +- **uint8_t getRECount()** returns number of RE's from begin(), +convenience e.g. for for loops. +- **void reset()** reset all internal counters to 0. - -#### Core functions +### Core functions - **uint8_t readInitialState()** read the initial state of the 4 rotary encoders. Typically called in setup only, or after a sleep e.g. in combination with **setValue()**. -Since 0.3.1 this function returns the read state, saves an additional read8() call. +This function returns the read state, saves an additional read8() call. - **bool checkChange()** used for polling to see if one or more RE have changed. This function does NOT update the internal counters. - **bool update()** returns true if there is a change detected. It updates the internal counters of the RE. The counters will add +1 or -1 depending on rotation direction. -Need to be called before **getValue()** or before **getKeyPressed()**. +Need to be called before **getValue()**. Note that **update()** must be called as soon as possible after the interrupt occurs or as often as possible when polling. Returns false if there is no change since last read. - **bool updateSingle()** returns true if there is a change detected. It updates the internal counters of the RE. This will add +1, +2 or +3 as it assumes that the rotary encoder -only goes into a single direction. +only goes into a single direction. +Typical use is for a RPM measurement. +Note that the **getValue()** can go 3x as fast if you turn in the other direction. Returns false if there is no change since last read. -#### Counters +### Counters - **int32_t getValue(uint8_r re)** returns the RE counter. If the parameter re > 3 then 0 is returned. +The max value is ± 2147483647. - **bool setValue(uint8_r re, int32_t value = 0)** (re)set the internal counter to value, default 0. If the parameter re > 3 then false is returned, true otherwise. -#### Read1 - Write1 - experimental +### Read1 - Write1 -Warning the **write1(pin, value)** might alter the state of the rotary encoder pins. +**Warning** + +The **write1(pin, value)** might alter the state of the rotary encoder pins. So this functionality should be tested thoroughly for your application. -Especially the **write()** is **experimental**, see issue #10, feedback welcome. +Especially the **write1()** is **experimental**, feedback welcome. -See example **rotaryDecoder_demo_RE_IO.ino** (since 0.3.1). +See example **rotaryDecoder_demo_RE_IO.ino**. **Read1()** and **write1()** are functions to access the pins of the PCF8574 that are not used for rotary encoders. -The user must guard that especially writing the pins do not interfere with the rotary encoder pins. +The user must guard that especially writing the pins do not interfere with the +rotary encoder pins. - **uint8_t read1(uint8_t pin)** reads a single pin (0..7). Returns HIGH or LOW. - **bool write1(uint8_t pin, uint8_t value)** writes a single pin (0..7). Value should be LOW (0) or HIGH (other values). +As said before the user must guard not to interfere with the +rotary encoder pins. - **uint8_t read8()** read all pins in one I2C IO action. When one need to access multiple input pins this is faster but need some bit masking. -- **bool write8(uint8_t value)** writes to multiple pins at once, e.g. to control multiple +- **bool write8(uint8_t bitmask)** writes to multiple pins at once, e.g. to control multiple LEDs in one IO action. As said before the user must guard not to interfere with the rotary encoder pins. -#### Debugging +### Debugging -- **int8_t getLastPosition(uint8_r re)** Returns last position. +- **int8_t getLastPosition(uint8_r re)** Returns last position. +Returns 0 if re > 3. ## Performance @@ -143,6 +162,7 @@ assuming 4 pulses == 360 - update documentation - picture how to connect e.g two rotary encoders which pins to used +- keep in sync with rotaryEncoder8 (PCF8575) class. #### Should @@ -153,6 +173,7 @@ assuming 4 pulses == 360 - invert flag to adjust to RE that give their pulse just the other way around? - setInvert(bool); getInvert(); - per channel / all? +- split **reset()** per var? #### Wont diff --git a/examples/rotaryDecoder_performance/rotaryDecoder_performance.ino b/examples/rotaryDecoder_performance/rotaryDecoder_performance.ino index 6fe992b..2397a51 100644 --- a/examples/rotaryDecoder_performance/rotaryDecoder_performance.ino +++ b/examples/rotaryDecoder_performance/rotaryDecoder_performance.ino @@ -48,6 +48,7 @@ void setup() Serial.print("\t"); Serial.print((stop - start) * 0.1); Serial.print("\n"); + delay(100); } } diff --git a/keywords.txt b/keywords.txt index ef31482..782a482 100644 --- a/keywords.txt +++ b/keywords.txt @@ -7,7 +7,10 @@ rotaryDecoder KEYWORD1 begin KEYWORD2 isConnected KEYWORD2 +getRECount KEYWORD2 +reset KEYWORD2 readInitialState KEYWORD2 + checkChange KEYWORD2 update KEYWORD2 updateSingle KEYWORD2 @@ -25,3 +28,4 @@ getLastPosition KEYWORD2 # Constants (LITERAL1) ROTARY_DECODER_LIB_VERSION LITERAL1 + diff --git a/library.json b/library.json index c6bfe83..7c45caf 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "rotaryDecoder", "keywords": "rotary,encoder,decoder,PCF8574", - "description": "Arduino library for rotary decoder with a PCF8574\nSupports up to 4 rotary encoders.", + "description": "Arduino library for a PCF8574 rotary decoder.\nSupports up to 4 rotary encoders.", "authors": [ { @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/rotaryDecoder.git" }, - "version": "0.3.1", + "version": "0.4.0", "license": "MIT", "frameworks": "*", "platforms": "*", diff --git a/library.properties b/library.properties index 19ce601..c5cbf8b 100644 --- a/library.properties +++ b/library.properties @@ -1,8 +1,8 @@ name=rotaryDecoder -version=0.3.1 +version=0.4.0 author=Rob Tillaart maintainer=Rob Tillaart -sentence=Arduino library for rotary decoder with a PCF8574. +sentence=Arduino library for a PCF8574 based rotary decoder. paragraph=Supports up to 4 rotary encoders. category=Data Processing url=https://github.com/RobTillaart/rotaryDecoder diff --git a/rotaryDecoder.cpp b/rotaryDecoder.cpp index ea1c7ab..c176cbc 100644 --- a/rotaryDecoder.cpp +++ b/rotaryDecoder.cpp @@ -1,9 +1,9 @@ // // FILE: rotaryDecoder.cpp // AUTHOR: Rob Tillaart -// VERSION: 0.3.1 +// VERSION: 0.4.0 // DATE: 2021-05-08 -// PURPOSE: Arduino library for rotary decoder +// PURPOSE: Arduino library for a PCF8574 based rotary decoder // URL: https://github.com/RobTillaart/rotaryDecoder @@ -24,7 +24,7 @@ rotaryDecoder::rotaryDecoder(const int8_t address, TwoWire *wire) bool rotaryDecoder::begin(uint8_t count) { _count = count; - if (_count > 4) _count = 4; + if (_count > ROTDEC_MAX_COUNT) _count = ROTDEC_MAX_COUNT; if (! isConnected()) return false; return true; @@ -38,6 +38,23 @@ bool rotaryDecoder::isConnected() } +uint8_t rotaryDecoder::getRECount() +{ + return _count; +} + + +void rotaryDecoder::reset() +{ + for (int i = 0 ; i < ROTDEC_MAX_COUNT; i++) + { + _lastPos[i] = 0; + _encoder[i] = 0; + } + _lastValue = 0; +} + + uint8_t rotaryDecoder::readInitialState() { uint8_t value = read8(); @@ -69,8 +86,8 @@ bool rotaryDecoder::update() _lastValue = value; for (uint8_t i = 0; i < _count; i++, value >>= 2) { - uint8_t currentpos = (value & 0x03); - uint8_t change = (_lastPos[i] << 2) | currentpos; + uint8_t currentPos = (value & 0x03); + uint8_t change = (_lastPos[i] << 2) | currentPos; switch (change) { case 0b0001: // fall through.. @@ -86,7 +103,7 @@ bool rotaryDecoder::update() _encoder[i]--; break; } - _lastPos[i] = currentpos; + _lastPos[i] = currentPos; } return true; } @@ -103,8 +120,8 @@ bool rotaryDecoder::updateSingle() _lastValue = value; for (uint8_t i = 0; i < _count; i++, value >>= 2) { - uint8_t currentpos = (value & 0x03); - uint8_t change = (_lastPos[i] << 2) | currentpos; + uint8_t currentPos = (value & 0x03); + uint8_t change = (_lastPos[i] << 2) | currentPos; switch (change) { case 0b0001: // fall through.. @@ -126,7 +143,7 @@ bool rotaryDecoder::updateSingle() _encoder[i] += 3; break; } - _lastPos[i] = currentpos; + _lastPos[i] = currentPos; } return true; } @@ -134,14 +151,14 @@ bool rotaryDecoder::updateSingle() int32_t rotaryDecoder::getValue(uint8_t re) { - if (re > 3) return 0; + if (re >= ROTDEC_MAX_COUNT) return 0; return _encoder[re]; } bool rotaryDecoder::setValue(uint8_t re, int32_t value) { - if (re > 3) return false; + if (re >= ROTDEC_MAX_COUNT) return false; _encoder[re] = value; return true; } @@ -172,16 +189,15 @@ bool rotaryDecoder::write1(uint8_t pin, uint8_t value) uint8_t rotaryDecoder::read8() { _wire->requestFrom(_address, (uint8_t)1); - uint8_t x = _wire->read(); - // Serial.println(x, HEX); - return x; + uint8_t a = _wire->read(); + return a; } -bool rotaryDecoder::write8(uint8_t value) +bool rotaryDecoder::write8(uint8_t bitmask) { _wire->beginTransmission(_address); - _wire->write(value); + _wire->write(bitmask); return (_wire->endTransmission() == 0); } @@ -192,6 +208,7 @@ bool rotaryDecoder::write8(uint8_t value) // uint8_t rotaryDecoder::getLastPosition(uint8_t re) { + if (re >= ROTDEC_MAX_COUNT) return 0; return _lastPos[re]; } diff --git a/rotaryDecoder.h b/rotaryDecoder.h index 4d100d8..6f8be63 100644 --- a/rotaryDecoder.h +++ b/rotaryDecoder.h @@ -2,16 +2,18 @@ // // FILE: rotaryDecoder.h // AUTHOR: Rob Tillaart -// VERSION: 0.3.1 +// VERSION: 0.4.0 // DATE: 2021-05-08 -// PURPOSE: Arduino library for rotary decoder +// PURPOSE: Arduino library for a PCF8574 based rotary decoder // URL: https://github.com/RobTillaart/rotaryDecoder #include "Arduino.h" #include "Wire.h" -#define ROTARY_DECODER_LIB_VERSION (F("0.3.1")) +#define ROTARY_DECODER_LIB_VERSION (F("0.4.0")) + +const uint8_t ROTDEC_MAX_COUNT = 4; class rotaryDecoder @@ -19,42 +21,46 @@ class rotaryDecoder public: explicit rotaryDecoder(const int8_t address, TwoWire *wire = &Wire); - bool begin(uint8_t count = 4); - bool isConnected(); + bool begin(uint8_t count = ROTDEC_MAX_COUNT); + bool isConnected(); + + uint8_t getRECount(); + void reset(); - uint8_t readInitialState(); + uint8_t readInitialState(); // for polling version, // checkChange is bit faster than a call to update // so useful if there are only a few updates - bool checkChange(); + bool checkChange(); // read and update the counters - bool update(); // assumes two directions => +1 and -1 - bool updateSingle(); // assumes single direction => + ++ +++ + bool update(); // assumes two directions => +1 and -1 + bool updateSingle(); // assumes single direction => + ++ +++ - // re = rotary encoder + // re = rotary encoder 0..3 // returns 0, false if re > 3. - int32_t getValue(uint8_t re); - bool setValue(uint8_t re, int32_t value = 0); + int32_t getValue(uint8_t re); + bool setValue(uint8_t re, int32_t value = 0); // READ - WRITE interface - uint8_t read1(uint8_t pin); - bool write1(uint8_t pin, uint8_t value); - uint8_t read8(); - bool write8(uint8_t value); + uint8_t read1(uint8_t pin); + bool write1(uint8_t pin, uint8_t value); + uint8_t read8(); + bool write8(uint8_t bitmask); // DEBUG - uint8_t getLastPosition(uint8_t re); + // re = rotary encoder 0..3 + uint8_t getLastPosition(uint8_t re); protected: uint8_t _count = 0; uint8_t _lastValue = 0; - uint8_t _lastPos[4] = { 0, 0, 0, 0 }; - int32_t _encoder[4] = { 0, 0, 0, 0 }; + uint8_t _lastPos[ROTDEC_MAX_COUNT] = { 0, 0, 0, 0 }; + int32_t _encoder[ROTDEC_MAX_COUNT] = { 0, 0, 0, 0 }; uint8_t _address; TwoWire * _wire; diff --git a/test/unit_test_001.cpp b/test/unit_test_001.cpp index 3edd913..cd32a03 100644 --- a/test/unit_test_001.cpp +++ b/test/unit_test_001.cpp @@ -48,9 +48,48 @@ unittest_teardown() unittest(test_constructor) { - rotaryDecoder(0x20); + rotaryDecoder RE(0x20); - fprintf(stderr, "no tests yet"); + Wire.begin(); + + RE.begin(4); + assertEqual(4, RE.getRECount()); + + RE.begin(10); + assertEqual(4, RE.getRECount()); +} + + +unittest(test_getValue) +{ + rotaryDecoder RE(0x20); + + Wire.begin(); + + RE.begin(4); + for (int i = 0; i < 4; i++) + { + assertEqual(0, RE.getValue(i)); + } + for (int i = 0; i < 4; i++) + { + RE.setValue(i, i * 100); + assertEqual(i * 100, RE.getValue(i)); + } +} + + +unittest(test_getLastPosition) +{ + rotaryDecoder RE(0x20); + + Wire.begin(); + + RE.begin(4); + for (int i = 0; i < 4; i++) + { + assertEqual(0, RE.getLastPosition(i)); + } }