0.4.0 M5ROTATE8

This commit is contained in:
Rob Tillaart 2024-06-16 17:31:42 +02:00
parent 6d8d7ee3af
commit 12257c41f9
15 changed files with 445 additions and 85 deletions

View File

@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.4.0] 2024-06-15
- add **uint32_t readRGB(uint8_t channel)**
- add firmware V2 functions
- **void setButtonToggleCount(uint8_t channel, uint8_t value = 0)**
- **uint8_t getButtonToggleCount(uint8_t channel)**
- **uint8_t getButtonRegister()**
- **uint8_t getEncoderChangeFlag()**
- improved **setAddress()**
- add examples
- update readme.md
- minor edits
----
## [0.3.0] 2023-12-05
- refactor API
- update readme

View File

@ -18,32 +18,46 @@ Arduino library for M5 8ROTATE 8x rotary encoders.
**Experimental**
M5ROTATE8 is an Arduino class to read the 8 rotary encoders of the
M5 8ENCODER module.
It also provides means to write RGB values to the 8 LED's in the same module.
M5ROTATE8 is an Arduino library to read the 8 rotary encoders of the
M5 8ENCODER device.
It also provides means to write RGB values to the 9 LED's in the same module.
The rotary encoders can be read as an absolute counter (since start or reset) or as an relative counter (since last time read).
The value can be both positive and negative depending on the direction of turns.
The absolute counters can be given an initial value.
The counters can be reset per channel.
The library can also read the key pressed status of every rotary encoder.
The rotary encoders can be read as an absolute counter, since start or reset,
or as a relative counter since last time read.
These values can be both positive and negative depending on the direction of the turns.
The absolute counters can be given an initial value e.g. to match some value in your project.
These counters can be reset per channel.
The library can read the state of the mini switch.
Furthermore the library can read the key pressed status of every rotary encoder.
These can be used e.g. to switch mode from fine to coarse and back.
The library can set the RGB value of the 9 LEDS.
The RotaryEncoder module has no brightness, like the 8ANGLe unit does.
Finally the library can read the state of the mini switch.
First tests with hardware have been done.
**Warning:** One strange observation, the RE makes steps of size 2 and occasionally step size 1.
This needs further investigation, so use with care.
Missing in the device is an interrupt signal e.g. on change.
One has to poll all channels for changes which is not efficient.
A single byte register that showed change since last read would allow to monitor
all 8 rotary encoders in one call.
Feedback is welcome!
#### Missing in V1
The device has no interrupt signal e.g. on change. However since firmware version V2
the device allows to read one register to see changes on all rotary encoders and one
registers to see all the button states. These enhancements improve interaction most
of the time.
Note: V2 is not tested with hardware yet.
#### Breaking change
Version 0.4.0 added support for Firmware V2 functions. See below.
If your hardware has firmware V1 these functions won't work.
#### Breaking change
Version 0.3.0 introduced a breaking change.
@ -56,7 +70,9 @@ before calling **begin()**.
#### I2C
The address range is in theory from 0..127, however the I2C specification
states it should be between 8 and 119 as some addresses are reserved.
states it should be between 8 and 119 as some addresses are reserved,
or have special meaning.
The default address is **0x41** or **65**.
| clock | works | notes |
@ -79,6 +95,12 @@ The step size needs investigation as I would expect step size 1, always.
#### Related
Manufacturer
- https://github.com/m5stack/M5Unit-8Encoder
- https://github.com/m5stack/M5Unit-8Encoder/issues/1 (V2 extensions)
- https://github.com/m5stack/M5Unit-8Encoder-Internal-FW (firmware)
Libraries
- https://github.com/RobTillaart/M5ANGLE8
- https://github.com/RobTillaart/M5ROTATE8
- https://github.com/RobTillaart/rotaryDecoder
@ -97,38 +119,67 @@ The step size needs investigation as I would expect step size 1, always.
Default address = 0x41, default Wire.
- **bool begin()** checks if address is on the I2C bus.
User must call wire.begin() before this one.
- **bool isConnected()** checks if address is on the I2C bus.
- **bool isConnected()** checks if the address given in the constructor, or by setAddress(),
can be found on the I2C bus.
- **bool setAddress(uint8_t address = M5ROTATE8_DEFAULT_ADDRESS)** set a new address for the device.
Default address = 0x41.
- **uint8_t getAddress()** convenience function.
- **uint8_t getVersion()** get firmware version from device.
Returns false if address below 8 or above 119.
- **uint8_t getAddress()** convenience function to get the set address.
- **uint8_t getVersion()** get the firmware version from device.
#### Rotary encoder part
- **int32_t getAbsCounter(uint8_t channel)**
Read a absolute position of the rotary encoder since reset or start.
- **void setAbsCounter(uint8_t channel, int32_t value);
Read the absolute or cumulative position of the rotary encoder since reset or start.
Note this can be positive or negative or zero.
- **void setAbsCounter(uint8_t channel, int32_t value)** allows to set an initial value
e.g. so it matches a setting or value in your application.
- **int32_t getRelCounter(uint8_t channel)**
Read a relative position of the rotary encoder since reset.
Note this can be positive or negative or zero.
Note: this counter will reset after each read.
- **bool getKeyPressed(uint8_t channel)** get key status of the rotary encoder.
True is pressed.
- **bool getKeyPressed(uint8_t channel)** get the status of the key of the rotary encoder.
True (1) is pressed, False (0) is not pressed.
- **bool resetCounter(uint8_t channel)** reset a rotary encoder.
- **void resetAll()** reset all counters to 0.
- **void resetAll()** reset all rotary encoder counters to 0.
#### Input switch part
- **uint8_t inputSwitch()** read status of the switch.
- **uint8_t inputSwitch()** read the status of the micro switch.
#### LED part
- **bool writeRGB(uint8_t channel, uint8_t R, uint8_t G, uint8_t B)** Set the RGB value of a specific LED.
channel = 0..8
- **bool setAll(uint8_t R, uint8_t G, uint8_t B)** set all LEDs.
- **bool allOff()** switches all LEDs off.
channel = 0..8 as there is one more LED than rotary encoders.
- **uint32_t readRGB(uint8_t channel)** read back the RGB value of specified LED as an 32 bits integer.
The value is 0x00RRGGBB.
- **bool setAll(uint8_t R, uint8_t G, uint8_t B)** set all LEDs to the specified RGB value.
- **bool allOff()** switches all LEDs off, RGB = (0,0,0).
#### Firmware V2 functions
New content in registers 0x58 - 0x5F, 0x61, 0x62.
Needs testing with hardware.
- **void setButtonToggleCount(uint8_t channel, uint8_t value = 0)**
Write a starting value to the toggle counters.
- **uint8_t getButtonToggleCount(uint8_t channel)** Button toggle counting.
To be used to see if button has been pressed and released, optionally multiple times.
Reset to zero after reading.
- **uint8_t getButtonChangeMask()** returns a bit mask for all 8 buttons,
bit 0 = not pressed, bit 1 = pressed.
To be used to check all 8 buttons in just one call.
This is much faster than reading them separately one by one.
Note that this function inverts the datasheetV2 specification as it seems more logical.
- **uint8_t getEncoderChangeMask()** returns a bit mask for all 8 rotary encoders.
Bit 0 = no change, bit 1 = changed.
To be used to check all 8 encoders in one call.
This is much faster than reading them separately one by one.
Resets the whole mask (register) to zero after reading.
## Future
@ -140,19 +191,16 @@ channel = 0..8
- investigate step size 2 / 1.
- An easy patch: divide by 2 resulting in step size 1 or 0
#### Should
- test firmware V2 functions with hardware.
- error handling
- optimize low level calls
- merge into two functions => read/write array + length.
- resetAll() could be "one call"
- **bool readRGB(channel, &R, &G, &B)**
- or **uint32_t readRGB(uint8_t channel)**
- improve on return values of functions.
- improve performance
- investigate address changes
- does this affect performance?
- improve performance (how?)
- extend performance example with V2 functions
#### Could
@ -160,9 +208,9 @@ channel = 0..8
- digital lock
- add unit tests
#### Wont (unless)
- **bool readRGB(channel, &R, &G, &B)**
## Support

View File

@ -1,7 +1,7 @@
//
// FILE: M5ROTATE8_performance.ino
// AUTHOR: Rob Tillaart
// PURPOSE: meansurement performance
// PURPOSE: measurement performance
// URL: https://github.com/RobTillaart/M5ROTATE8
@ -13,26 +13,6 @@ M5ROTATE8 MM;
uint32_t start, stop;
uint16_t x;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("M5ROTATE8_LIB_VERSION: ");
Serial.println(M5ROTATE8_LIB_VERSION);
delay(100);
Wire.begin();
MM.begin();
for (uint32_t speed = 100000; speed <= 400000; speed += 100000)
{
Wire.setClock(speed);
Serial.println();
Serial.print("I2C:\t");
Serial.println(speed);
performance();
}
}
void performance()
{
@ -124,6 +104,28 @@ void performance()
}
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("M5ROTATE8_LIB_VERSION: ");
Serial.println(M5ROTATE8_LIB_VERSION);
delay(100);
Wire.begin();
MM.begin();
for (uint32_t speed = 100000; speed <= 400000; speed += 100000)
{
Wire.setClock(speed);
Serial.println();
Serial.print("I2C:\t");
Serial.println(speed);
performance();
}
}
void loop()
{
}

View File

@ -10,6 +10,7 @@
M5ROTATE8 MM;
void setup()
{
Serial.begin(115200);

View File

@ -0,0 +1,49 @@
//
// FILE: M5ROTATE8_address_change.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/M5ROTATE8
#include "m5rotate8.h"
M5ROTATE8 MM;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("M5ROTATE8_LIB_VERSION: ");
Serial.println(M5ROTATE8_LIB_VERSION);
delay(100);
Wire.begin();
MM.begin();
MM.resetAll();
Serial.print("Before: \t");
Serial.println(MM.getAddress());
Serial.print("Connect: \t");
Serial.println(MM.isConnected());
// assumes not out of range.
MM.setAddress(MM.getAddress() + 2);
Serial.print("After: \t");
Serial.println(MM.getAddress());
Serial.print("Connect: \t");
Serial.println(MM.isConnected());
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,44 @@
//
// FILE: M5ROTATE8_button_mask.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo firmware V2 function
// URL: https://github.com/RobTillaart/M5ROTATE8
#include "m5rotate8.h"
M5ROTATE8 MM;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("M5ROTATE8_LIB_VERSION: ");
Serial.println(M5ROTATE8_LIB_VERSION);
delay(100);
Wire.begin();
MM.begin();
}
void loop()
{
// Note: mask is inverted register.
uint8_t mask = MM.getButtonChangeMask();
if (mask != 0)
{
for (int button = 0; button < 8; button++)
{
Serial.print("Button ");
Serial.print(button);
Serial.print(" has state ");
Serial.println((mask & (1 << button)) > 0);
}
}
}
// -- END OF FILE --

View File

@ -0,0 +1,52 @@
//
// FILE: M5ROTATE8_button_toggle_count.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo firmware V2 function
// URL: https://github.com/RobTillaart/M5ROTATE8
#include "m5rotate8.h"
M5ROTATE8 MM;
int sum = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("M5ROTATE8_LIB_VERSION: ");
Serial.println(M5ROTATE8_LIB_VERSION);
delay(100);
Wire.begin();
MM.begin();
}
void loop()
{
for (int channel = 0; channel < 8; channel++)
{
int x = MM.getButtonToggleCount(channel);
sum += x;
Serial.print(x);
Serial.print("\t");
delay(125);
}
Serial.println();
// time to reset?
if (sum >= 25) // arbitrary condition.
{
sum = 0;
for (int channel = 0; channel < 8; channel++)
{
MM.setButtonToggleCount(channel, 0); // explicit value
}
}
}
// -- END OF FILE --

View File

@ -0,0 +1,48 @@
//
// FILE: M5ROTATE8_encoder_mask.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo firmware V2 function
// URL: https://github.com/RobTillaart/M5ROTATE8
#include "m5rotate8.h"
M5ROTATE8 MM;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("M5ROTATE8_LIB_VERSION: ");
Serial.println(M5ROTATE8_LIB_VERSION);
delay(100);
Wire.begin();
MM.begin();
}
void loop()
{
uint8_t mask = MM.getEncoderChangeMask();
if (mask != 0)
{
for (int encoder = 0; encoder < 8; encoder++)
{
if (mask & (1 << encoder))
{
Serial.print("Encoder ");
Serial.print(encoder);
Serial.print(" moved to ");
// read and process encoder.
Serial.println(MM.getAbsCounter(encoder));
}
}
}
}
// -- END OF FILE --

View File

@ -26,6 +26,13 @@ setAll KEYWORD2
allOff KEYWORD2
# FIRMWARE V2
setButtonToggleCount KEYWORD2
getButtonToggleCount KEYWORD2
getButtonChangeMask KEYWORD2
getEncoderChangeMask KEYWORD2
# Constants (LITERAL1)
M5ROTATE8_LIB_VERSION LITERAL1

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/M5ROTATE8"
},
"version": "0.3.0",
"version": "0.4.0",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=M5ROTATE8
version=0.3.0
version=0.4.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for M5 8ROTATE 8x rotary encoders

View File

@ -1,23 +1,29 @@
//
// FILE: m5rotate8.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.3.0
// VERSION: 0.4.0
// PURPOSE: Arduino library for M5 8ROTATE 8x rotary encoders
// URL: https://github.com/RobTillaart/M5ROTATE8
#include "m5rotate8.h"
// FIRMWARE V1 REGISTERS
#define M5ROTATE8_REG_ADDRESS 0xFF
#define M5ROTATE8_REG_VERSION 0xFE
#define M5ROTATE8_REG_BASE_ABS 0x00
#define M5ROTATE8_REG_BASE_REL 0x20
#define M5ROTATE8_REG_BASE_RESET 0x40
#define M5ROTATE8_REG_BASE_BUTTON 0x50
#define M5ROTATE8_REG_BASE_BUTTON_VALUE 0x50
#define M5ROTATE8_REG_SWITCH 0x60
#define M5ROTATE8_REG_RGB 0x70
// FIRMWARE V2 REGISTERS
#define M5ROTATE8_REG_BASE_BUTTON_TOGGLE 0x58
#define M5ROTATE8_REG_ENCODER_MASK 0x61
#define M5ROTATE8_REG_BUTTON_MASK 0x62
M5ROTATE8::M5ROTATE8(uint8_t address, TwoWire *wire)
{
@ -42,6 +48,7 @@ bool M5ROTATE8::isConnected()
bool M5ROTATE8::setAddress(uint8_t address)
{
if ((address < 8) || (address > 119)) return false;
_address = address;
write8(M5ROTATE8_REG_ADDRESS, _address);
return isConnected();
@ -60,7 +67,6 @@ uint8_t M5ROTATE8::getVersion()
}
////////////////////////////////////////////////
//
// ROTARY ENCODER PART
//
@ -88,7 +94,7 @@ bool M5ROTATE8::getKeyPressed(uint8_t channel)
{
return false;
}
return (0 == read8(M5ROTATE8_REG_BASE_BUTTON + channel));
return (0 == read8(M5ROTATE8_REG_BASE_BUTTON_VALUE + channel));
}
@ -112,7 +118,6 @@ void M5ROTATE8::resetAll()
}
////////////////////////////////////////////////
//
// INPUT SWITCH PART
//
@ -122,7 +127,6 @@ uint8_t M5ROTATE8::inputSwitch()
}
////////////////////////////////////////////////
//
// LED PART
//
@ -137,6 +141,12 @@ bool M5ROTATE8::writeRGB(uint8_t channel, uint8_t R, uint8_t G, uint8_t B)
}
uint32_t M5ROTATE8::readRGB(uint8_t channel)
{
return read24(M5ROTATE8_REG_RGB + (channel * 3));
}
bool M5ROTATE8::setAll(uint8_t R, uint8_t G, uint8_t B)
{
for (uint8_t ch = 0; ch < 9; ch++)
@ -153,7 +163,47 @@ bool M5ROTATE8::allOff()
}
////////////////////////////////////////////////
//
// FIRMWARE V2
//
bool M5ROTATE8::setButtonToggleCount(uint8_t channel, uint8_t value)
{
if (channel > 7)
{
return false;
}
return write8(M5ROTATE8_REG_BASE_BUTTON_TOGGLE + channel, value);
}
uint8_t M5ROTATE8::getButtonToggleCount(uint8_t channel)
{
if (channel > 7)
{
return 0;
}
return read8(M5ROTATE8_REG_BASE_BUTTON_TOGGLE + channel);
}
// 0 = no change, 1 = changed
uint8_t M5ROTATE8::getEncoderChangeMask()
{
return read8(M5ROTATE8_REG_ENCODER_MASK);
}
// 0 = not pressed, 1 = pressed (inverted the datasheetV2 specification)
// seems more logical
uint8_t M5ROTATE8::getButtonChangeMask()
{
// invert register to be more logical IMHO.
return read8(M5ROTATE8_REG_BUTTON_MASK) ^ 0xFF;
}
//////////////////////////////////////////////////////////////////////////////
//
// PRIVATE
//
@ -198,6 +248,31 @@ bool M5ROTATE8::write24(uint8_t reg, uint8_t R, uint8_t G, uint8_t B)
}
uint32_t M5ROTATE8::read24(uint8_t reg)
{
_wire->beginTransmission(_address);
_wire->write(reg);
_error = _wire->endTransmission();
if (_error != 0)
{
// error handling
return 0;
}
if (_wire->requestFrom(_address, (uint8_t)3) != 3)
{
// error handling
return 0;
}
uint32_t value = 0;
value += _wire->read();
value <<= 8;
value += _wire->read();
value <<= 8;
value += _wire->read();
return value;
}
bool M5ROTATE8::write32(uint8_t reg, uint32_t value)
{
_wire->beginTransmission(_address);

View File

@ -2,7 +2,7 @@
//
// FILE: m5rotate8.h
// AUTHOR: Rob Tillaart
// VERSION: 0.3.0
// VERSION: 0.4.0
// PURPOSE: Arduino library for M5 8ROTATE 8x rotary encoders
// URL: https://github.com/RobTillaart/M5ROTATE8
@ -10,7 +10,7 @@
#include "Arduino.h"
#include "Wire.h"
#define M5ROTATE8_LIB_VERSION (F("0.3.0"))
#define M5ROTATE8_LIB_VERSION (F("0.4.0"))
#define M5ROTATE8_DEFAULT_ADDRESS 0x41
@ -28,6 +28,7 @@ public:
bool begin();
bool isConnected();
// META
bool setAddress(uint8_t address = M5ROTATE8_DEFAULT_ADDRESS);
uint8_t getAddress();
uint8_t getVersion();
@ -48,10 +49,26 @@ public:
// channel = 0..7
// R,G,B = 0..255
bool writeRGB(uint8_t channel, uint8_t R, uint8_t G, uint8_t B);
uint32_t readRGB(uint8_t channel);
bool setAll(uint8_t R, uint8_t G, uint8_t B);
bool allOff();
// FIRMWARE V2 functions (to be verified)
// use getVersion() to check.
// channel = 0..7
// value = 0..255
// register 0x58..0x5F
bool setButtonToggleCount(uint8_t channel, uint8_t value = 0);
uint8_t getButtonToggleCount(uint8_t channel);
// register 0x61, 0x62
// 0 = no change, 1 = changed
uint8_t getEncoderChangeMask();
// 0 = not pressed, 1 = pressed (inverted the datasheetV2 specification)
// seems to be more logical.
uint8_t getButtonChangeMask();
private:
uint8_t _address;
@ -61,7 +78,10 @@ private:
bool write8(uint8_t reg, uint8_t value);
uint8_t read8(uint8_t reg);
bool write24(uint8_t reg, uint8_t R, uint8_t G, uint8_t B);
uint32_t read24(uint8_t reg);
bool write32(uint8_t reg, uint32_t value);
uint32_t read32(uint8_t reg);
};