Reorganisation fichiers

This commit is contained in:
Serge NOEL
2026-02-13 08:49:53 +01:00
parent ec9957d5b1
commit 758f73bc0e
33 changed files with 977 additions and 499 deletions

View File

@@ -0,0 +1,399 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
////////////////////////////////////////////////////////////
// ***** STEAM THROTTLE *****
////////////////////////////////////////////////////////////
void initSteamThrottle () {
uint16_t n;
if (oldSteamLoco != locoData[myLocoData].myAddr.address) {
oldSteamLoco = locoData[myLocoData].myAddr.address;
steamPressure = 80;
waterLevelBoiler = 80;
waterLevelTender = 350;
oldPressure = 0;
oldLevelBoiler = 0;
oldLevelTender = 0;
barData[BAR_JOHNSON].value = STEAM_JOHNSON_NEUTRAL; // neutral position
barData[BAR_BRAKE].value = 0;
steamDir = locoData[myLocoData].myDir;
for (n = 0; n < MAX_LIMIT; n++)
steamSpeedLimit[n] = LIMIT_NONE;
}
currentSteamTime = millis();
steamTimeSpeed = currentSteamTime;
steamTimeSteam = currentSteamTime;
steamTimeWater = currentSteamTime;
steamTimeLoad = currentSteamTime;
steamTimeSmoke = currentSteamTime;
shovelCoal = false;
setFirebox();
endWaterInjection();
endTenderFill();
setTimer(TMR_STEAM, 5, TMR_ONESHOT);
changeSmoke = STEAM_SMOKE_FAST;
oldPressure = 0;
oldSpeedSteam = 305;
encoderMax = 31; // set throttle to current speed
updateSteamThrottle();
if (steamDir != locoData[myLocoData].myDir) { // check Johnson bar position
steamDir = locoData[myLocoData].myDir;
barData[BAR_JOHNSON].value = 6 - barData[BAR_JOHNSON].value;
}
}
void updateSteamThrottle() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
case CLIENT_XNET:
if (bitRead(locoData[myLocoData].mySteps, 2)) { // 0..127
currentSteamSpeed = locoData[myLocoData].mySpeed;
encoderValue = currentSteamSpeed >> 2;
}
else {
if (bitRead(locoData[myLocoData].mySteps, 1)) { // 0..31
encoderValue = (locoData[myLocoData].mySpeed & 0x0F) << 1;
if (bitRead(locoData[myLocoData].mySpeed, 4))
bitSet(encoderValue, 0);
currentSteamSpeed = (encoderValue > 3) ? (encoderValue << 2) + (encoderValue >> 3) : 0;
}
else { // 0..15
encoderValue = locoData[myLocoData].mySpeed & 0x0F;
encoderValue = (encoderValue > 1) ? encoderValue << 1 : 0;
currentSteamSpeed = (encoderValue << 2) + (encoderValue >> 3);
}
}
break;
case CLIENT_LNET:
case CLIENT_ECOS:
currentSteamSpeed = locoData[myLocoData].mySpeed;
encoderValue = currentSteamSpeed >> 2;
break;
}
}
void steamProcess() {
uint16_t value, newSpeed, mappedThrottle, jFactor;
steamSpeedLimit[LIMIT_THROTTLE] = (encoderValue << 2) + (encoderValue >> 3); // Read Throtle Speed (0..31 -> 0..127)
steamSpeedLimit[LIMIT_JOHNSON] = LIMIT_NONE;
switch (barData[BAR_JOHNSON].value) { // Read Johnson Bar
case 0: // full reverse
jFactor = 3;
changeSpeed = 80;
steamDir = 0x00;
break;
case 1:
jFactor = 2;
changeSpeed = 200;
steamDir = 0x00;
break;
case 2:
jFactor = 1;
changeSpeed = 500;
steamDir = 0x00;
break;
case STEAM_JOHNSON_NEUTRAL: // Neutral position of Johnson Bar
jFactor = 1;
changeSpeed = 1000;
steamSpeedLimit[LIMIT_JOHNSON] = 0;
break;
case 4:
jFactor = 1;
changeSpeed = 500;
steamDir = 0x80;
break;
case 5:
jFactor = 2;
changeSpeed = 200;
steamDir = 0x80;
break;
case 6: // full forward
jFactor = 3;
changeSpeed = 80;
steamDir = 0x80;
break;
}
if (steamDir != locoData[myLocoData].myDir) { // Check direction
locoData[myLocoData].myDir = steamDir;
changeDirection();
DEBUG_MSG("STEAM: Change direction")
}
value = steamSpeedLimit[LIMIT_THROTTLE];
changeSteam = 10000 - ((value * 9) * jFactor); // Steam timeout: 6571 to 10000
changeWater = 14000 - ((value * 27) * jFactor); // Water timeout: 3713 to 14000
if (barData[BAR_BRAKE].value > 0) { // Brake bar: 300, 150, 100
changeSpeed = 300 / barData[BAR_BRAKE].value;
}
currentSteamTime = millis();
if (currentSteamTime > (steamTimeWater + changeWater)) { // Water consumption
steamTimeWater = currentSteamTime;
if (waterLevelBoiler > 0) {
waterLevelBoiler--;
DEBUG_MSG("Boiler Level: %d", waterLevelBoiler)
}
}
if (waterLevelBoiler < 10) { // Stop loco if not enough water
steamSpeedLimit[LIMIT_WATER] = 0;
steamThrottleStop();
}
else {
steamSpeedLimit[LIMIT_WATER] = LIMIT_NONE;
}
if (currentSteamTime > (steamTimeSteam + changeSteam)) { // Steam consumption
steamTimeSteam = currentSteamTime;
if (steamPressure > 0)
steamPressure--;
}
if (steamPressure < 50) { // Limit speed based on steam level
value = (steamPressure < 20) ? 0 : map(steamPressure, 20, 50, 20, 120);
steamSpeedLimit[LIMIT_PRESSURE] = value;
}
else {
steamSpeedLimit[LIMIT_PRESSURE] = LIMIT_NONE;
}
if (currentSteamTime > (steamTimeLoad + STEAM_LOAD_TIME)) { // Load coal and water
steamTimeLoad = currentSteamTime;
if (shovelCoal) { // Fire open for shoveling coal
if (steamPressure > 96) {
shovelCoal = false;
setFirebox();
newEvent(OBJ_FNC, FNC_ST_FIRE, EVNT_DRAW);
}
else {
if (steamPressure < 20) // slowly pressure up at beginning
steamPressure += 1;
else
steamPressure += 2;
}
}
if (waterInjection) { // Water injector open
if (waterLevelTender > 0) { // Inject water with water from tender
if (waterLevelBoiler > 95) {
endWaterInjection();
}
else {
waterLevelBoiler += 2;
waterLevelTender--;
}
steamSpeedLimit[LIMIT_TENDER] = LIMIT_NONE;
}
else {
endWaterInjection(); // Stop locomotive if tender empty
steamSpeedLimit[LIMIT_TENDER] = 0;
steamThrottleStop();
}
}
if (fillTender) {
if ((waterLevelTender > 495) || (currentSteamSpeed != 0)) { // Only fill tender when stopped
endTenderFill();
}
else {
waterLevelTender++;
if (waterLevelTender > 6) // Minimum level to run again
steamSpeedLimit[LIMIT_TENDER] = LIMIT_NONE;
}
}
}
if (currentSteamTime > (steamTimeSmoke + changeSmoke)) { // Chimney smoke
steamTimeSmoke = currentSteamTime;
if (currentSteamSpeed > 0) {
fncData[FNC_ST_SMOKE].state = !fncData[FNC_ST_SMOKE].state;
changeSmoke = map(currentSteamSpeed, 0, 127, STEAM_SMOKE_SLOW, STEAM_SMOKE_FAST);
}
else {
fncData[FNC_ST_SMOKE].state = false;
changeSmoke = STEAM_SMOKE_SLOW + STEAM_SMOKE_SLOW;
}
newEvent(OBJ_FNC, FNC_ST_SMOKE, EVNT_DRAW);
}
if (barData[BAR_BRAKE].value > 0) { // Braking
value = barData[BAR_BRAKE].value * 8;
value = (currentSteamSpeed > value) ? (currentSteamSpeed - value) : 0;
steamSpeedLimit[LIMIT_BRAKE] = value;
}
else {
steamSpeedLimit[LIMIT_BRAKE] = LIMIT_NONE;
}
targetSpeedSteam = LIMIT_NONE; // Find lower limit
for (value = 0; value < MAX_LIMIT; value++) {
if (steamSpeedLimit[value] < targetSpeedSteam)
targetSpeedSteam = steamSpeedLimit[value];
}
newSpeed = currentSteamSpeed;
if (currentSteamTime > (steamTimeSpeed + changeSpeed)) { // Speed acceleration
steamTimeSpeed = currentSteamTime;
//DEBUG_MSG("Target: %d Current: %d New: %d", targetSpeedSteam, currentSteamSpeed, newSpeed)
if (targetSpeedSteam > currentSteamSpeed) {
newSpeed = currentSteamSpeed + 1;
DEBUG_MSG("Inc New: %d", newSpeed)
}
if (targetSpeedSteam < currentSteamSpeed) {
newSpeed = currentSteamSpeed - 1;
DEBUG_MSG("Dec New: %d", newSpeed)
}
//DEBUG_MSG("New acc: %d", newSpeed)
}
if (currentSteamSpeed != newSpeed) { // changes in speed
DEBUG_MSG("Step: %d - New: %d LIMITS ", currentSteamSpeed, newSpeed)
#ifdef DEBUG
for (value = 0; value < MAX_LIMIT; value++) {
Serial.print(steamSpeedLimit[value]);
Serial.print(" ");
}
Serial.println();
#endif
currentSteamSpeed = newSpeed;
switch (wifiSetting.protocol) {
case CLIENT_Z21:
case CLIENT_XNET:
if (bitRead(locoData[myLocoData].mySteps, 2)) { // 128 steps
mappedThrottle = (currentSteamSpeed > 1) ? currentSteamSpeed : 0;
}
else {
if (bitRead(locoData[myLocoData].mySteps, 1)) { // 28 steps
if (currentSteamSpeed > 15) {
mappedThrottle = currentSteamSpeed >> 3;
bitWrite(mappedThrottle, 4, bitRead(currentSteamSpeed, 2));
}
else {
mappedThrottle = 0;
}
}
else { // 14 steps
mappedThrottle = (currentSteamSpeed > 15) ? currentSteamSpeed >> 3 : 0;
}
}
break;
case CLIENT_LNET:
case CLIENT_ECOS:
mappedThrottle = (currentSteamSpeed > 1) ? currentSteamSpeed : 0;
break;
}
locoData[myLocoData].mySpeed = mappedThrottle;
locoOperationSpeed();
DEBUG_MSG("Steam step: %d", currentSteamSpeed)
if ((currentSteamSpeed > 0) && (changeSmoke > STEAM_SMOKE_SLOW)) { // initial chuff
changeSmoke = 0;
fncData[FNC_ST_SMOKE].state = false;
}
}
if ((oldLevelTender / 10) != (waterLevelTender / 10)) {
oldLevelTender = waterLevelTender;
barData[BAR_TENDER].value = waterLevelTender;
newEvent(OBJ_BAR, BAR_TENDER, EVNT_DRAW);
}
value = waterLevelBoiler / 2;
if (oldLevelBoiler != value ) {
oldLevelBoiler = value;
barData[BAR_WATER].value = value;
newEvent(OBJ_BAR, BAR_WATER, EVNT_DRAW);
}
value = steamPressure * 270 / 100;
if (oldPressure != value) {
showPressure(value);
DEBUG_MSG("Pressure: %d", steamPressure)
}
}
void startWaterInjection () {
waterInjection = true;
fncData[FNC_ST_WATER].colorOn = COLOR_DARKGREEN;
drawObject(OBJ_FNC, FNC_ST_WATER);
}
void endWaterInjection () {
waterInjection = false;
fncData[FNC_ST_WATER].colorOn = COLOR_RED;
drawObject(OBJ_FNC, FNC_ST_WATER);
}
void startTenderFill() {
fillTender = true;
//fncData[FNC_ST_TENDER].color = COLOR_RED;
fncData[FNC_ST_TENDER].colorOn = COLOR_DARKGREEN;
drawObject(OBJ_FNC, FNC_ST_TENDER);
}
void endTenderFill() {
fillTender = false;
//fncData[FNC_ST_TENDER].color = COLOR_WHITE;
fncData[FNC_ST_TENDER].colorOn = COLOR_RED;
drawObject(OBJ_FNC, FNC_ST_TENDER);
}
void setFirebox() {
if (shovelCoal) {
fncData[FNC_ST_FIRE].idIcon = FNC_FIRE_OP_OFF;
fncData[FNC_ST_FIRE].color = COLOR_ORANGE;
fncData[FNC_ST_FIRE].colorOn = COLOR_YELLOW;
}
else {
fncData[FNC_ST_FIRE].idIcon = FNC_FIRE_CL_OFF;
fncData[FNC_ST_FIRE].color = COLOR_SILVER;
fncData[FNC_ST_FIRE].colorOn = COLOR_RED;
}
}
void steamThrottleStop() { // set controls for stop
if (encoderValue > 0) {
encoderValue = 0;
showSpeedSteam(240);
}
if (barData[BAR_JOHNSON].value != STEAM_JOHNSON_NEUTRAL) {
barData[BAR_JOHNSON].value = STEAM_JOHNSON_NEUTRAL; // Neutral
drawObject(OBJ_BAR, BAR_JOHNSON);
}
}
void showPressure(uint16_t angle) {
tft.setPivot(140, 105); // Set pivot to center of manometer in TFT screen
sprite.setColorDepth(8); // Create an 8bpp Sprite
sprite.createSprite(19, 19); // 8bpp requires 19 * 19 = 361 bytes
sprite.setPivot(9, 9); // Set pivot relative to top left corner of Sprite
sprite.fillSprite(COLOR_WHITE); // Fill the Sprite with background
sprite.pushRotated(oldPressure);
oldPressure = angle;
sprite.drawBitmap(0, 0, needle_bar, 19, 19, COLOR_BLUE);
sprite.pushRotated(angle);
sprite.deleteSprite();
}
void showSpeedSteam(uint16_t angle) {
tft.setPivot(120, 170); // Set pivot to center of bar in TFT screen
sprite.setColorDepth(8); // Create an 8bpp Sprite
sprite.createSprite(83, 15); // 8bpp requires 83 * 15 = 1245 bytes
sprite.setPivot(76, 7); // Set pivot relative to top left corner of Sprite
sprite.fillSprite(COLOR_BLACK); // Fill the Sprite with background
sprite.pushRotated(oldSpeedSteam);
oldSpeedSteam = angle;
sprite.drawBitmap(0, 0, speed_steam, 83, 15, COLOR_RED);
sprite.pushRotated(angle);
tft.drawArc(120, 170, 27, 22, 315, 45, COLOR_RED, COLOR_BLACK, false);
sprite.deleteSprite();
}