239 lines
7.2 KiB
C++
239 lines
7.2 KiB
C++
|
|
/**
|
|
* @file encoder.ino
|
|
* @brief Rotary encoder and button handling for PacoMouseCYD throttle.
|
|
* @author F. Cañada
|
|
* @date 2025-2026
|
|
* @copyright https://usuaris.tinet.cat/fmco/
|
|
*
|
|
* This file contains interrupt service routines and functions for reading and processing
|
|
* rotary encoder and button input, including debouncing and value management.
|
|
*/
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// API Documentation
|
|
////////////////////////////////////////////////////////////
|
|
/**
|
|
* @brief Interrupt Service Routine for the encoder.
|
|
*
|
|
* Sets a flag indicating the encoder needs service.
|
|
*/
|
|
void IRAM_ATTR encoderISR();
|
|
|
|
/**
|
|
* @brief Handles encoder state changes and updates encoder value.
|
|
*/
|
|
void encoderService();
|
|
|
|
/**
|
|
* @brief Reads the encoder button and updates its state.
|
|
*/
|
|
void readButtons();
|
|
|
|
/**
|
|
* @brief Processes encoder movement and updates UI or state accordingly.
|
|
*/
|
|
void controlEncoder();
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// End API Documentation
|
|
////////////////////////////////////////////////////////////
|
|
|
|
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
|
|
*/
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// ***** ENCODER *****
|
|
////////////////////////////////////////////////////////////
|
|
|
|
IRAM_ATTR void encoderISR () { // Encoder interrupt
|
|
encoderNeedService = true;
|
|
}
|
|
|
|
|
|
void encoderService () { // Encoder interrupt service
|
|
encoderNeedService = false;
|
|
lastTimeEncoder = millis();
|
|
outA = digitalRead (ENCODER_A);
|
|
outB = digitalRead (ENCODER_B);
|
|
if (outA != copyOutA) { // evitamos rebotes
|
|
copyOutA = outA;
|
|
if (copyOutB == 0x80) {
|
|
copyOutB = outB;
|
|
}
|
|
else {
|
|
if ( outB != copyOutB) {
|
|
copyOutB = 0x80;
|
|
if (outA == outB) // comprueba sentido de giro
|
|
encoderValue = (encoderValue < encoderMax) ? ++encoderValue : encoderMax ; // CW, hasta maximo
|
|
else
|
|
encoderValue = (encoderValue > 0) ? --encoderValue : 0; // CCW, hasta 0
|
|
encoderChange = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void readButtons () {
|
|
byte inputButton;
|
|
|
|
timeButtons = millis(); // lee cada cierto tiempo
|
|
inputButton = digitalRead (ENCODER_SW); // comprueba cambio en boton del encoder
|
|
if (statusSwitch != inputButton) {
|
|
statusSwitch = inputButton;
|
|
if (statusSwitch == LOW)
|
|
switchOn = true;
|
|
}
|
|
}
|
|
|
|
|
|
void controlEncoder() { // encoder movement
|
|
encoderChange = false;
|
|
aliveAndKicking();
|
|
DEBUG_MSG("Encoder: %d", encoderValue);
|
|
switch (objStack[lastWinStack].objID) {
|
|
case WIN_SSID:
|
|
scrSSID = encoderValue;
|
|
scanWiFiFill();
|
|
drawObject(OBJ_TXT, TXT_SSID1);
|
|
drawObject(OBJ_TXT, TXT_SSID2);
|
|
drawObject(OBJ_TXT, TXT_SSID3);
|
|
drawObject(OBJ_TXT, TXT_SSID4);
|
|
drawObject(OBJ_TXT, TXT_SSID5);
|
|
drawObject(OBJ_TXT, TXT_SSID6);
|
|
break;
|
|
case WIN_THROTTLE:
|
|
case WIN_SPEEDO:
|
|
case WIN_STA_PLAY:
|
|
updateMySpeed();
|
|
break;
|
|
case WIN_CHG_FUNC:
|
|
fncData[FNC_CHG].idIcon = encoderValue * 2;
|
|
drawObject(OBJ_FNC, FNC_CHG);
|
|
break;
|
|
case WIN_SEL_LOCO:
|
|
populateLocoList();
|
|
drawObject(OBJ_TXT, TXT_SEL_ADDR1);
|
|
drawObject(OBJ_TXT, TXT_SEL_NAME1);
|
|
drawObject(OBJ_TXT, TXT_SEL_ADDR2);
|
|
drawObject(OBJ_TXT, TXT_SEL_NAME2);
|
|
drawObject(OBJ_TXT, TXT_SEL_ADDR3);
|
|
drawObject(OBJ_TXT, TXT_SEL_NAME3);
|
|
drawObject(OBJ_TXT, TXT_SEL_ADDR4);
|
|
drawObject(OBJ_TXT, TXT_SEL_NAME4);
|
|
drawObject(OBJ_TXT, TXT_SEL_ADDR5);
|
|
drawObject(OBJ_TXT, TXT_SEL_NAME5);
|
|
drawObject(OBJ_TXT, TXT_SEL_ADDR6);
|
|
drawObject(OBJ_TXT, TXT_SEL_NAME6);
|
|
break;
|
|
case WIN_STEAM:
|
|
showSpeedSteam((encoderValue << 1) + 240);
|
|
break;
|
|
case WIN_ACC_TYPE:
|
|
fncData[FNC_ACC_TYPE].num = accDef[encoderValue].num;
|
|
fncData[FNC_ACC_TYPE].idIcon = accDef[encoderValue].icon[0].fncIcon;
|
|
fncData[FNC_ACC_TYPE].color = accDef[encoderValue].icon[0].color;
|
|
fncData[FNC_ACC_TYPE].colorOn = accDef[encoderValue].icon[0].colorOn;
|
|
drawObject(OBJ_FNC, FNC_ACC_TYPE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void controlSwitch() { // encoder switch
|
|
uint16_t value, value2, txtID;
|
|
uint32_t delta, dist;
|
|
char msg[NAME_LNG + 1];
|
|
switchOn = false;
|
|
aliveAndKicking();
|
|
DEBUG_MSG("Encoder Switch");
|
|
switch (objStack[lastWinStack].objID) {
|
|
case WIN_SSID:
|
|
snprintf (wifiSetting.ssid, 32, WiFi.SSID(scrSSID).c_str()); //saveSSID(scrSSID);
|
|
DEBUG_MSG("New SSID: %s", wifiSetting.ssid);
|
|
eepromChanged = true;
|
|
closeWindow(WIN_SSID);
|
|
openWindow(WIN_WIFI);
|
|
break;
|
|
case WIN_THROTTLE:
|
|
case WIN_STA_PLAY:
|
|
if (encoderValue > 0) {
|
|
encoderValue = 0;
|
|
if (stopMode > 0)
|
|
locoData[myLocoData].mySpeed = 1;
|
|
else
|
|
locoData[myLocoData].mySpeed = 0;
|
|
locoOperationSpeed();
|
|
}
|
|
else {
|
|
locoData[myLocoData].myDir ^= 0x80;
|
|
changeDirection();
|
|
}
|
|
updateSpeedDir();
|
|
break;
|
|
case WIN_CHG_FUNC:
|
|
fncData[FNC_F0 + paramChild].idIcon = fncData[FNC_CHG].idIcon;
|
|
closeWindow(WIN_CHG_FUNC);
|
|
break;
|
|
case WIN_SEL_LOCO:
|
|
releaseLoco();
|
|
txtID = (encoderValue > 5) ? 5 : encoderValue;
|
|
if (useID) {
|
|
value2 = (encoderValue > 5) ? encoderValue - 5 : 0;
|
|
value = sortedLocoStack[value2 + txtID];
|
|
}
|
|
else {
|
|
value = atoi(txtData[TXT_SEL_ADDR1 + txtID].buf);
|
|
}
|
|
//value = atoi(txtData[TXT_SEL_ADDR1 + txtID].buf);
|
|
DEBUG_MSG("Selected Loco %d", value);
|
|
closeWindow(WIN_SEL_LOCO);
|
|
getNewLoco(value);
|
|
break;
|
|
case WIN_SPEEDO:
|
|
switch (speedoPhase) { //enum speedo {SPD_WAIT, SPD_BEGIN, SPD_COUNT, SPD_ARRIVE, SPD_END};
|
|
case SPD_WAIT:
|
|
if (getCurrentStep() > 0) {
|
|
speedoStartTime = millis();
|
|
setSpeedoPhase(SPD_BEGIN);
|
|
getLabelTxt(LBL_MEASURE, msg);
|
|
snprintf(spdSpeedBuf, NAME_LNG + 1, "%s", msg);
|
|
drawObject(OBJ_TXT, TXT_SPEEDO_SPD);
|
|
setTimer(TMR_SPEEDO, 5, TMR_ONESHOT);
|
|
}
|
|
else {
|
|
locoData[myLocoData].myDir ^= 0x80;
|
|
changeDirection();
|
|
updateSpeedDir();
|
|
}
|
|
break;
|
|
case SPD_BEGIN:
|
|
case SPD_COUNT:
|
|
speedoEndTime = millis();
|
|
setSpeedoPhase(SPD_ARRIVE);
|
|
setTimer(TMR_SPEEDO, 5, TMR_ONESHOT);
|
|
dist = speedoLength * 36 * speedoScale;
|
|
delta = (speedoEndTime - speedoStartTime) * 10;
|
|
speedoSpeed = dist / delta;
|
|
snprintf(spdSpeedBuf, NAME_LNG + 1, "%d km/h", speedoSpeed);
|
|
drawObject(OBJ_TXT, TXT_SPEEDO_SPD);
|
|
break;
|
|
case SPD_ARRIVE:
|
|
break;
|
|
case SPD_END:
|
|
break;
|
|
}
|
|
break;
|
|
case WIN_STEAM:
|
|
steamThrottleStop();
|
|
currentSteamSpeed = 0;
|
|
locoData[myLocoData].mySpeed = 0;
|
|
locoOperationSpeed();
|
|
break;
|
|
case WIN_ACC_TYPE:
|
|
accTypeClick();
|
|
break;
|
|
}
|
|
}
|