This commit is contained in:
e2002
2022-05-24 10:09:19 +03:00
parent 4f6d131cf5
commit 90d97af3e8
12 changed files with 310 additions and 41 deletions

View File

@@ -45,5 +45,7 @@
\ \
![ёRadio](images/img21.jpg)\ ![ёRadio](images/img21.jpg)\
\ \
![ёRadio](images/img22.jpg) ![ёRadio](images/img22.jpg)\
\
![ёRadio](images/img23.jpg)

View File

@@ -140,7 +140,7 @@ _\** GPIO 16 and 17 are used by PSRAM on the WROVER modules._
--- ---
## Dependencies ## Dependencies
#### Libraries: #### Libraries:
**Library Manager**: Adafruit_GFX, Adafruit_ST7735\*, Adafruit_SSD1306\*, Adafruit_PCD8544\*, Adafruit_SH110X\*, Adafruit_SSD1327\*, Adafruit_ILI9341\*, Adafruit_SSD1305\*, (\* depending on display model), ESP32Encoder, OneButton, IRremoteESP8266, XPT2046_Touchscreen \ **Library Manager**: Adafruit_GFX, Adafruit_ST7735\*, Adafruit_SSD1306\*, Adafruit_PCD8544\*, Adafruit_SH110X\*, Adafruit_SSD1327\*, Adafruit_ILI9341\*, Adafruit_SSD1305\*, (\* depending on display model), OneButton, IRremoteESP8266, XPT2046_Touchscreen \
**Github**: [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer), [AsyncTCP](https://github.com/me-no-dev/AsyncTCP), [async-mqtt-client](https://github.com/marvinroger/async-mqtt-client)* \ **Github**: [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer), [AsyncTCP](https://github.com/me-no-dev/AsyncTCP), [async-mqtt-client](https://github.com/marvinroger/async-mqtt-client)* \
\* _if you need MQTT support_ \* _if you need MQTT support_
@@ -291,6 +291,12 @@ Work is in progress...
--- ---
## Version history ## Version history
#### v0.6.262
- change encoder library to [ai-esp32-rotary-encoder](https://github.com/igorantolic/ai-esp32-rotary-encoder) (injected to project)
- added new option VOL_ACCELERATION - volume adjustment acceleration by encoder (see [myoptions.h](exsamples/myoptions.h) for exsample)
- fixed connection error with http-stations on esp32-core v2.0.3
- fixed css errors (a [full update](#update-over-web-interface) is required)
#### v0.6.250 #### v0.6.250
- added update via web-interface \ - added update via web-interface \
**Attention! Full firmware with chip re-partitioning is required!** see [board setup example](#quick-start) **Attention! Full firmware with chip re-partitioning is required!** see [board setup example](#quick-start)

View File

@@ -54,13 +54,15 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
//#define ENC2_BTNB 255 /* Encoder button */ //#define ENC2_BTNB 255 /* Encoder button */
//#define ENC2_BTNR 255 /* Right rotation */ //#define ENC2_BTNR 255 /* Right rotation */
//#define ENC2_INTERNALPULLUP true /* Enable the weak pull up resistors */ //#define ENC2_INTERNALPULLUP true /* Enable the weak pull up resistors */
//#define ENC2_HALFQUARD true /* Experiment with it */ //#define ENC2_HALFQUARD false /* (true, false, 255) Experiment with it */
/******************************************/ /******************************************/
/* BUTTONS */ /* BUTTONS */
//#define BTN_LEFT 255 /* VolDown, Prev */ //#define BTN_LEFT 255 /* VolDown, Prev */
//#define BTN_CENTER 255 /* Play, Stop, Show playlist */ //#define BTN_CENTER 255 /* Play, Stop, Show playlist */
//#define BTN_RIGHT 255 /* VolUp, Next */ //#define BTN_RIGHT 255 /* VolUp, Next */
//#define BTN_UP 255 /* Prev, Move Up */
//#define BTN_DOWN 255 /* Next, Move Down */
//#define BTN_INTERNALPULLUP true /* Enable the weak pull up resistors */ //#define BTN_INTERNALPULLUP true /* Enable the weak pull up resistors */
//#define BTN_LONGPRESS_LOOP_DELAY 200 /* Delay between calling DuringLongPress event */ //#define BTN_LONGPRESS_LOOP_DELAY 200 /* Delay between calling DuringLongPress event */
//#define BTN_CLICK_TICKS 300 /* Event Timing https://github.com/mathertel/OneButton#event-timing */ //#define BTN_CLICK_TICKS 300 /* Event Timing https://github.com/mathertel/OneButton#event-timing */
@@ -100,6 +102,7 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
//#define TFT_CONTRAST 55 /* Nokia 5110 contrast */ //#define TFT_CONTRAST 55 /* Nokia 5110 contrast */
//#define TFT_INVERT true /* Invert the display colors (usually true) */ //#define TFT_INVERT true /* Invert the display colors (usually true) */
//#define VOL_STEP 1 /* Volume control step */ //#define VOL_STEP 1 /* Volume control step */
//#define VOL_ACCELERATION 200 /* Encoder vol acceleration; 0 or 1 means disabled acceleration */
//#define MUTE_PIN 255 /* MUTE Pin */ //#define MUTE_PIN 255 /* MUTE Pin */
//#define MUTE_VAL HIGH /* Write this to MUTE_PIN when player is stopped */ //#define MUTE_VAL HIGH /* Write this to MUTE_PIN when player is stopped */
//#define PL_WITH_NUMBERS /* show the number of station in the playlist */ //#define PL_WITH_NUMBERS /* show the number of station in the playlist */
@@ -129,4 +132,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
//#define IR_CODE_NUM9 0xFF5AA5 //#define IR_CODE_NUM9 0xFF5AA5
//#define IR_CODE_HASH 0xFF52AD /* Toggle playlist mode */ //#define IR_CODE_HASH 0xFF52AD /* Toggle playlist mode */
//#define IR_CODE_AST 0xFF42BD /* Not used */ //#define IR_CODE_AST 0xFF42BD /* Not used */
/******************************************/
#endif #endif

BIN
images/img23.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@@ -16,13 +16,28 @@ OneButton button[] {{BTN_LEFT, true, BTN_INTERNALPULLUP}, {BTN_CENTER, true, BTN
constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]); constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]);
#endif #endif
#if ENC_HALFQUARD==false
#define ENCODER_STEPS 4
#elif ENC_HALFQUARD==true
#define ENCODER_STEPS 2
#elif ENC_HALFQUARD==255
#define ENCODER_STEPS 1
#endif
#if ENC2_HALFQUARD==false
#define ENCODER2_STEPS 4
#elif ENC2_HALFQUARD==true
#define ENCODER2_STEPS 2
#elif ENC2_HALFQUARD==255
#define ENCODER2_STEPS 1
#endif
#if (ENC_BTNL!=255 && ENC_BTNR!=255) || (ENC2_BTNL!=255 && ENC2_BTNR!=255) #if (ENC_BTNL!=255 && ENC_BTNR!=255) || (ENC2_BTNL!=255 && ENC2_BTNR!=255)
#include <ESP32Encoder.h> #include "src/yoEncoder/yoEncoder.h"
#if (ENC_BTNL!=255 && ENC_BTNR!=255) #if (ENC_BTNL!=255 && ENC_BTNR!=255)
ESP32Encoder encoder; yoEncoder encoder = yoEncoder(ENC_BTNL, ENC_BTNR, ENCODER_STEPS, ENC_INTERNALPULLUP);
#endif #endif
#if (ENC2_BTNL!=255 && ENC2_BTNR!=255) #if (ENC2_BTNL!=255 && ENC2_BTNR!=255)
ESP32Encoder encoder2; yoEncoder encoder2 = yoEncoder(ENC2_BTNL, ENC2_BTNR, ENCODER2_STEPS, ENC2_INTERNALPULLUP);
#endif #endif
#endif #endif
@@ -50,23 +65,34 @@ IRrecv irrecv(IR_PIN, kCaptureBufferSize, kTimeout, true);
decode_results irResults; decode_results irResults;
#endif #endif
void initControls() {
#if ENC_BTNL!=255 #if ENC_BTNL!=255
encoder.useInternalWeakPullResistors = ENC_INTERNALPULLUP ? UP : DOWN; void IRAM_ATTR readEncoderISR()
if (ENC_HALFQUARD) { {
encoder.attachHalfQuad(ENC_BTNL, ENC_BTNR); encoder.readEncoder_ISR();
} else { }
encoder.attachFullQuad(ENC_BTNL, ENC_BTNR);
}
#endif #endif
#if ENC2_BTNL!=255 #if ENC2_BTNL!=255
encoder2.useInternalWeakPullResistors = ENC2_INTERNALPULLUP ? UP : DOWN; void IRAM_ATTR readEncoder2ISR()
if (ENC2_HALFQUARD) { {
encoder2.attachHalfQuad(ENC2_BTNL, ENC2_BTNR); encoder2.readEncoder_ISR();
} else { }
encoder2.attachFullQuad(ENC2_BTNL, ENC2_BTNR);
}
#endif #endif
void initControls() {
#if ENC_BTNL!=255
encoder.begin();
encoder.setup(readEncoderISR);
encoder.setBoundaries(0, 254, true);
encoder.setAcceleration(VOL_ACCELERATION);
#endif
#if ENC2_BTNL!=255
encoder2.begin();
encoder2.setup(readEncoder2ISR);
encoder2.setBoundaries(0, 254, true);
encoder2.setAcceleration(VOL_ACCELERATION);
#endif
#if ISPUSHBUTTONS #if ISPUSHBUTTONS
for (int i = 0; i < nrOfButtons; i++) for (int i = 0; i < nrOfButtons; i++)
{ {
@@ -134,30 +160,28 @@ void loopControls() {
#if ENC_BTNL!=255 #if ENC_BTNL!=255
void encoderLoop() { void encoderLoop() {
long encNewPosition = encoder.getCount() / 2; int8_t encoderDelta = encoder.encoderChanged();
if (encNewPosition != 0 && encNewPosition != encOldPosition) { if (encoderDelta!=0)
encOldPosition = encNewPosition; {
encoder.setCount(0); controlsEvent(encoderDelta > 0, encoderDelta);
controlsEvent(encNewPosition > 0);
} }
} }
#endif #endif
#if ENC2_BTNL!=255 #if ENC2_BTNL!=255
void encoder2Loop() { void encoder2Loop() {
long encNewPosition = encoder2.getCount() / 2; int8_t encoderDelta = encoder2.encoderChanged();
if (encNewPosition != 0 && encNewPosition != enc2OldPosition) { if (encoderDelta!=0)
enc2OldPosition = encNewPosition; {
encoder2.setCount(0);
uint8_t bp = 2; uint8_t bp = 2;
if (ENC2_BTNB != 255) { if (ENC2_BTNB != 255) {
bp = digitalRead(ENC2_BTNB); bp = digitalRead(ENC2_BTNB);
} }
if (bp == HIGH && display.mode == PLAYER) { if (bp == HIGH && display.mode == PLAYER) {
display.putRequest({NEWMODE, STATIONS}); display.putRequest({NEWMODE, STATIONS});
while(display.mode != STATIONS) {delay(5);} while(display.mode != STATIONS) {delay(10);}
} }
controlsEvent(encNewPosition > 0); controlsEvent(encoderDelta > 0, encoderDelta);
} }
} }
#endif #endif
@@ -474,14 +498,21 @@ void onBtnDuringLongPress(int id) {
} }
} }
void controlsEvent(bool toRight) { void controlsEvent(bool toRight, int8_t volDelta) {
if (display.mode == NUMBERS) { if (display.mode == NUMBERS) {
display.numOfNextStation = 0; display.numOfNextStation = 0;
display.putRequest({NEWMODE, PLAYER}); display.putRequest({NEWMODE, PLAYER});
} }
if (display.mode != STATIONS) { if (display.mode != STATIONS) {
display.putRequest({NEWMODE, VOL}); display.putRequest({NEWMODE, VOL});
player.stepVol(toRight); if(volDelta!=0){
int nv = config.store.volume+volDelta;
if(nv<0) nv=0;
if(nv>254) nv=254;
player.setVol((byte)nv, false);
}else{
player.stepVol(toRight);
}
} }
if (display.mode == STATIONS) { if (display.mode == STATIONS) {
display.resetQueue(); display.resetQueue();

View File

@@ -16,7 +16,7 @@ void irLoop();
void touchLoop(); void touchLoop();
void irNum(byte num); void irNum(byte num);
void irBlink(); void irBlink();
void controlsEvent(bool toRight); void controlsEvent(bool toRight, int8_t volDelta = 0);
void onBtnClick(int id); void onBtnClick(int id);
void onBtnDoubleClick(int id); void onBtnDoubleClick(int id);

Binary file not shown.

View File

@@ -1,7 +1,7 @@
#ifndef options_h #ifndef options_h
#define options_h #define options_h
#define VERSION "0.6.250" #define VERSION "0.6.262"
/******************************************************* /*******************************************************
DO NOT EDIT THIS FILE. DO NOT EDIT THIS FILE.
@@ -100,7 +100,7 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#define ENC_INTERNALPULLUP true #define ENC_INTERNALPULLUP true
#endif #endif
#ifndef ENC_HALFQUARD #ifndef ENC_HALFQUARD
#define ENC_HALFQUARD true #define ENC_HALFQUARD false
#endif #endif
#ifndef ENC2_BTNL #ifndef ENC2_BTNL
@@ -116,7 +116,7 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#define ENC2_INTERNALPULLUP true #define ENC2_INTERNALPULLUP true
#endif #endif
#ifndef ENC2_HALFQUARD #ifndef ENC2_HALFQUARD
#define ENC2_HALFQUARD true #define ENC2_HALFQUARD false
#endif #endif
/* BUTTONS */ /* BUTTONS */
@@ -197,6 +197,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#ifndef VOL_STEP #ifndef VOL_STEP
#define VOL_STEP 1 // Encoder vol step #define VOL_STEP 1 // Encoder vol step
#endif #endif
#ifndef VOL_ACCELERATION
#define VOL_ACCELERATION 200 // Encoder vol acceleration; 0 or 1 means disabled acceleration
#endif
#ifndef MUTE_PIN #ifndef MUTE_PIN
#define MUTE_PIN 255 // MUTE Pin #define MUTE_PIN 255 // MUTE Pin
#endif #endif

View File

@@ -232,7 +232,7 @@ esp_err_t Audio::I2Sstop(uint8_t i2s_num) {
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
esp_err_t Audio::i2s_mclk_pin_select(const uint8_t pin) { esp_err_t Audio::i2s_mclk_pin_select(const uint8_t pin) {
if(pin != 0 && pin != 1 && pin != 3) { if(pin != 0 && pin != 1 && pin != 3) {
ESP_LOGE(TAG, "Only support GPIO0/GPIO1/GPIO3, gpio_num:%d", pin); //ESP_LOGE(TAG, "Only support GPIO0/GPIO1/GPIO3, gpio_num:%d", pin);
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
switch(pin){ switch(pin){
@@ -348,7 +348,7 @@ void Audio::httpPrint(const char* url) {
strcat(resp, " HTTP/1.1\r\n"); strcat(resp, " HTTP/1.1\r\n");
strcat(resp, "Host: "); strcat(resp, "Host: ");
strcat(resp, host); strcat(resp, host);
strcat(resp, "\r\nUser-Agent: ESP32 audioI2S\r\n"); strcat(resp, "\r\nUser-Agent: Mozilla/5.0\r\n");
strcat(resp, "icy-metadata: 1\r\n"); strcat(resp, "icy-metadata: 1\r\n");
strcat(resp, "Accept-Encoding: identity\r\n"); strcat(resp, "Accept-Encoding: identity\r\n");
strcat(resp, "Connection: Keep-Alive\r\n\r\n"); strcat(resp, "Connection: Keep-Alive\r\n\r\n");
@@ -522,7 +522,7 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
// strcat(resp, "Transfer-Encoding: \r\n"); // otherwise the server assumes gzip compression // strcat(resp, "Transfer-Encoding: \r\n"); // otherwise the server assumes gzip compression
strcat(resp, "Connection: keep-alive\r\n\r\n"); strcat(resp, "Connection: keep-alive\r\n\r\n");
const uint32_t TIMEOUT_MS{350}; const uint32_t TIMEOUT_MS{3700};
uint32_t wtf; uint32_t wtf;
if(m_f_ssl == false) { if(m_f_ssl == false) {
uint32_t t = millis(); uint32_t t = millis();

View File

@@ -1640,7 +1640,7 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) {
strcat(resp, "\r\n"); strcat(resp, "\r\n");
strcat(resp, "Connection: keep-alive\r\n\r\n"); strcat(resp, "Connection: keep-alive\r\n\r\n");
const uint32_t TIMEOUT_MS{350}; const uint32_t TIMEOUT_MS{3700};
uint32_t wtf; uint32_t wtf;
if(m_f_ssl == false) { if(m_f_ssl == false) {
uint32_t t = millis(); uint32_t t = millis();

View File

@@ -0,0 +1,151 @@
// based on https://github.com/igorantolic/ai-esp32-rotary-encoder code
//
//
#include "esp_log.h"
#define LOG_TAG "yoEncoder"
#include "yoEncoder.h"
void IRAM_ATTR yoEncoder::readEncoder_ISR()
{
unsigned long now = millis();
portENTER_CRITICAL_ISR(&(this->mux));
if (this->isEnabled)
{
// code from https://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino/
/**/
this->old_AB <<= 2; //remember previous state
int8_t ENC_PORT = ((digitalRead(this->encoderBPin)) ? (1 << 1) : 0) | ((digitalRead(this->encoderAPin)) ? (1 << 0) : 0);
this->old_AB |= (ENC_PORT & 0x03); //add current state
//this->encoder0Pos += ( this->enc_states[( this->old_AB & 0x0f )]);
int8_t currentDirection = (this->enc_states[(this->old_AB & 0x0f)]); //-1,0 or 1
if (currentDirection != 0)
{
long prevRotaryPosition = this->encoder0Pos / this->encoderSteps;
this->encoder0Pos += currentDirection;
long newRotaryPosition = this->encoder0Pos / this->encoderSteps;
if (newRotaryPosition != prevRotaryPosition && rotaryAccelerationCoef > 1)
{
//additional movements cause acceleration?
// at X ms, there should be no acceleration.
unsigned long accelerationLongCutoffMillis = 200;
// at Y ms, we want to have maximum acceleration
unsigned long accelerationShortCutffMillis = 4;
// compute linear acceleration
if (currentDirection == lastMovementDirection &&
currentDirection != 0 &&
lastMovementDirection != 0)
{
// ... but only of the direction of rotation matched and there
// actually was a previous rotation.
unsigned long millisAfterLastMotion = now - lastMovementAt;
if (millisAfterLastMotion < accelerationLongCutoffMillis)
{
if (millisAfterLastMotion < accelerationShortCutffMillis)
{
millisAfterLastMotion = accelerationShortCutffMillis; // limit to maximum acceleration
}
if (currentDirection > 0)
{
this->encoder0Pos += rotaryAccelerationCoef / millisAfterLastMotion;
}
else
{
this->encoder0Pos -= rotaryAccelerationCoef / millisAfterLastMotion;
}
}
}
this->lastMovementAt = now;
this->lastMovementDirection = currentDirection;
}
//respect limits
if (this->encoder0Pos > (this->_maxEncoderValue))
this->encoder0Pos = this->_circleValues ? this->_minEncoderValue : this->_maxEncoderValue;
if (this->encoder0Pos < (this->_minEncoderValue))
this->encoder0Pos = this->_circleValues ? this->_maxEncoderValue : this->_minEncoderValue;
}
}
portEXIT_CRITICAL_ISR(&(this->mux));
}
yoEncoder::yoEncoder(uint8_t encoder_APin, uint8_t encoder_BPin, uint8_t encoderSteps, bool internalPullup)
{
this->old_AB = 0;
this->encoderAPin = encoder_APin;
this->encoderBPin = encoder_BPin;
this->encoderSteps = encoderSteps;
pinMode(this->encoderAPin, internalPullup?INPUT_PULLUP:INPUT_PULLDOWN);
pinMode(this->encoderBPin, internalPullup?INPUT_PULLUP:INPUT_PULLDOWN);
}
void yoEncoder::setBoundaries(long minEncoderValue, long maxEncoderValue, bool circleValues)
{
this->_minEncoderValue = minEncoderValue * this->encoderSteps;
this->_maxEncoderValue = maxEncoderValue * this->encoderSteps;
this->_circleValues = circleValues;
}
long yoEncoder::readEncoder()
{
return (this->encoder0Pos / this->encoderSteps);
}
void yoEncoder::setEncoderValue(long newValue)
{
reset(newValue);
}
long yoEncoder::encoderChanged()
{
long _encoder0Pos = readEncoder();
long encoder0Diff = _encoder0Pos - this->lastReadEncoder0Pos;
this->lastReadEncoder0Pos = _encoder0Pos;
return encoder0Diff;
}
void yoEncoder::setup(void (*ISR_callback)(void))
{
attachInterrupt(digitalPinToInterrupt(this->encoderAPin), ISR_callback, CHANGE);
attachInterrupt(digitalPinToInterrupt(this->encoderBPin), ISR_callback, CHANGE);
}
void yoEncoder::begin()
{
this->lastReadEncoder0Pos = 0;
}
void yoEncoder::reset(long newValue_)
{
newValue_ = newValue_ * this->encoderSteps;
this->encoder0Pos = newValue_;
this->lastReadEncoder0Pos = this->encoder0Pos;
if (this->encoder0Pos > this->_maxEncoderValue)
this->encoder0Pos = this->_circleValues ? this->_minEncoderValue : this->_maxEncoderValue;
if (this->encoder0Pos < this->_minEncoderValue)
this->encoder0Pos = this->_circleValues ? this->_maxEncoderValue : this->_minEncoderValue;
}
void yoEncoder::enable()
{
this->isEnabled = true;
}
void yoEncoder::disable()
{
this->isEnabled = false;
}

View File

@@ -0,0 +1,68 @@
// yoEncoder.h
// based on https://github.com/igorantolic/ai-esp32-rotary-encoder code
#ifndef _YOENCODER_h
#define _YOENCODER_h
#include "Arduino.h"
typedef enum
{
BUT_DOWN = 0,
BUT_PUSHED = 1,
BUT_UP = 2,
BUT_RELEASED = 3,
BUT_DISABLED = 99,
} ButtonState;
class yoEncoder
{
private:
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE buttonMux = portMUX_INITIALIZER_UNLOCKED;
volatile long encoder0Pos = 0;
volatile int8_t lastMovementDirection = 0; //1 right; -1 left
volatile unsigned long lastMovementAt = 0;
unsigned long rotaryAccelerationCoef = 150;
bool _circleValues = false;
bool isEnabled = true;
uint8_t encoderAPin;
uint8_t encoderBPin;
long encoderSteps;
long _minEncoderValue = -1 << 15;
long _maxEncoderValue = 1 << 15;
uint8_t old_AB;
long lastReadEncoder0Pos;
int8_t enc_states[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
void (*ISR_callback)();
public:
yoEncoder(
uint8_t encoderAPin,
uint8_t encoderBPin,
uint8_t encoderSteps,
bool internalPullup = true);
void setBoundaries(long minValue = -100, long maxValue = 100, bool circleValues = false);
void IRAM_ATTR readEncoder_ISR();
void setup(void (*ISR_callback)(void));
void begin();
void reset(long newValue = 0);
void enable();
void disable();
long readEncoder();
void setEncoderValue(long newValue);
long encoderChanged();
unsigned long getAcceleration() { return this->rotaryAccelerationCoef; }
void setAcceleration(unsigned long acceleration) { this->rotaryAccelerationCoef = acceleration; }
void disableAcceleration() { setAcceleration(0); }
};
#endif