Files
Maison/PacoMouseCYD/Platformio/Arduino.old/main.ino
2026-02-13 08:49:53 +01:00

1734 lines
66 KiB
C++

/**
* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*
* This software and associated files are a DIY project that is not intended for commercial use.
* This software uses libraries with different licenses, follow all their different terms included.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
*
* Sources are only provided for building and uploading to the device.
* You are not allowed to modify the source code or fork/publish this project.
* Commercial use is forbidden.
*
* --------------------------------------------------------------------------------------------------
*
* Use 2.8" Cheap Yellow Display ESP32-2432S028 (CYD)
* - ILI9341 driver chip (320x240)
* - XPT2046 chip for touch screen
*
* CYD Also available in 2.4" and 3.2" (Use Resistive touch)
*
* Select ESP32 Dev Module in Arduino IDE
*
* SD Card. IMPORTANT!!!: use FAT32 SD card (max. 32GB)
*
* --------------------------------------------------------------------------------------------------
*
* v0.1 24feb25 Start writting code
* v0.2 07mar25 GUI, SD Pictures, Wifi configuration and loco throttle on Z21 working
* v0.3 21mar25 Added loco list sorting and loco image selection. Added internal file system for loco data.
* v0.4 19apr25 Added configuration menu screen. Corrected touch rotation for CYD 2.4". Changed translations files. Added programming CV. Added speedometer.
* v0.5 02jun25 Added steam loco throttle. Adding more function icons. Added Xpressnet LAN and Loconet over TCP protocols. Added experimental identify command station for Loconet.
* v0.6 08oct25 Added Loconet programming. New LocoEditor for browser on SD.
* v0.7 23nov25 Corrected bugs on loconet steam direction. Added accessory panels. Added WiFi analyzer.
* v0.8 15dec25 Added ECoS/CS1 protocol. Updated user defined CYDs. Changes in modal windows.
* v0.9 03jan26 Added Station Run for kids. Corrected minor bugs on loconet
*/
// PacoMouseCYD program version
#define VER_H "0"
#define VER_L "9"
#define VER_R "c"
// #define DEBUG to enable debug messages on Serial0. Remember to set baudrate to 115200 bps in console.
#define DEBUG
// Libraries
#include <Arduino.h>
// #include "config.h" // PacoMouseCYD config file
// #include "icon.h" // PacoMouseCYD icons
// #include "gui.h" // PacoMouseCYD Graphic User Interface
// #include "translations.h" // PacoMouseCYD languages
// #include <FS.h> // SD Card. IMPORTANT!!!: use FAT32 SD card (max. 32GB)
// #include <SD.h>
// #include <LittleFS.h>
// #include <SPI.h>
// #include <TFT_eSPI.h> // Graphics and font library for ILI9341 driver chip v2.5.43
// #include "XPT2046.h" // PacoMouseCYD touch screen XPT2046 (2.8": bit bang, 2.4": SPI)
// #include <EEPROM.h>
// #include <WiFi.h>
// #include <WiFiUdp.h>
// #include "lnet.h" // PacoMouseCYD LNTCP
#ifdef DEBUG
char output[80];
#define DEBUG_MSG(...) snprintf(output,80, __VA_ARGS__ ); \
Serial.println(output);
#else
#define DEBUG_MSG(...)
#endif
/*********************************************************
***************** SD CARD *******************************
*********************************************************/
// bool sdDetected;
// File root;
// char folderSel[32];
// int dirCount = 1;
// char DirAndFile[15];
// char FileName[32];
// #define FORMAT_LITTLEFS_IF_FAILED true
/*********************************************************
* *************** TFT *******************************
*********************************************************/
// TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
// TFT_eSprite sprite = TFT_eSprite(&tft);
// // backlight
// #define LEDC_CHANNEL_0 0 // use first channel of 16 channels (started from zero)
// #define LEDC_RESOLUTION 8 // use 8 (12) bit precission for LEDC timer
// #define LEDC_BASE_FREQ 5000 // 5000 Hz as a LEDC base frequency
// #define INACT_TIME 1200 // 2 minutes inactivity (in 100ms) -> lower bright
// uint8_t backlight;
// uint8_t currBacklight;
// uint16_t oldNeedle;
// #define USB_UP 2 // Display USB up
// #define USB_DOWN 0 // Display USB down
// uint8_t locationUSB;
////////////////////////////////////////////////////////////
// ***** TOUCHSCREEN *****
////////////////////////////////////////////////////////////
// XPT2046_TS touchscreen(XPT2046_MOSI, XPT2046_MISO, XPT2046_CLK, XPT2046_CS);
// #define SW_BOOT 0 // BOOT button used to enter touchscreen calibration window
// bool bootPressed;
// bool calibrationPending;
// bool clickDetected = false; // pulsacion detectada en panel tactil
////////////////////////////////////////////////////////////
// ***** ENCODER *****
////////////////////////////////////////////////////////////
// volatile int outA; // encoder input used by ISR
// volatile int outB;
// volatile int copyOutA;
// volatile int copyOutB;
// volatile uint32_t lastTimeEncoder;
// volatile byte encoderValue; // encoder shared values (ISR & program)
// volatile byte encoderMax;
// volatile bool encoderChange;
// volatile bool encoderNeedService;
// #define ENC_DEBOUNCE 3
// byte statusSwitch;
// bool switchOn;
// const unsigned long timeoutButtons = 50; // temporizador antirebote
// unsigned long timeButtons;
////////////////////////////////////////////////////////////
// ***** EEPROM *****
////////////////////////////////////////////////////////////
// #define EEPROM_SIZE 512
// enum Settings {
// EE_XMIN_H, EE_XMIN_L, EE_XMAX_H, EE_XMAX_L, EE_YMIN_H, EE_YMIN_L, EE_YMAX_H, EE_YMAX_L, EE_BACKLIGHT, EE_LANGUAGE,
// EE_ADRH, EE_ADRL, EE_STOP_MODE, EE_SHUNTING, EE_ROCO, EE_LOCK, EE_SHORT, EE_USB_LOCATION, EE_CMD_STA, EE_CMD_AUTO,
// EE_STA_ADRH1, EE_STA_ADRL1, EE_STA_ADRH2, EE_STA_ADRL2, EE_STA_ADRH3, EE_STA_ADRL3, EE_STA_ADRH4, EE_STA_ADRL4,
// EE_STA_TRNDEF, EE_STA_TRNNUM, EE_STA_NUM, EE_STA_TIME,
// EE_WIFI, // datos WiFi. (Tiene que ser el ultimo)
// }; // EEPROM settings
// bool eepromChanged;
////////////////////////////////////////////////////////////
// ***** WiFi *****
////////////////////////////////////////////////////////////
struct {
char ssid[33]; // SSID
char password[65]; // Password
IPAddress CS_IP; // IP
uint16_t port; // Port
bool serverType; // Server type
byte protocol; // protocol
int ok;
} wifiSetting;
enum typeProto {CLIENT_Z21, CLIENT_XNET, CLIENT_ECOS, CLIENT_LNET};
WiFiClient Client;
WiFiUDP Udp;
#define z21Port 21105 // local port to listen on command station
#define XnetPort 5550
#define ECoSPort 15471
uint16_t networks;
uint8_t scrSSID;
enum cmdStation {CMD_DR, CMD_ULI, CMD_DIG, CMD_UNKNOW};
byte typeCmdStation; // tipo de central Loconet para funciones
// RSSI RANGE
#define RSSI_CEILING -40
#define RSSI_FLOOR -100
#define NEAR_CHANNEL_RSSI_ALLOW -70
#define CHANNEL_WIDTH 15
#define GRAPH_HEIGHT 188
#if (TFT_WIDTH == 240)
#define GRAPH_OFFSET 0
#define GRAPH_BASELINE 222
#else
#define GRAPH_OFFSET 40
#define GRAPH_BASELINE 302
#endif
// Channel color mapping from channel 1 to 14
// uint16_t channel_color[] = {
// COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_MAGENTA, COLOR_RED,
// COLOR_ORANGE, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_MAGENTA, COLOR_RED, COLOR_ORANGE,
// };
// uint8_t scan_count = 0;
// uint8_t ap_count[14];
// int32_t max_rssi[14];
////////////////////////////////////////////////////////////
// ***** GUI *****
////////////////////////////////////////////////////////////
// uint16_t posObjStack1;
// uint16_t posObjStack2;
// uint16_t paramChild;
// uint8_t lastLanguage;
////////////////////////////////////////////////////////////
// ***** PACOMOUSE *****
////////////////////////////////////////////////////////////
// enum initResult {INIT_OK, INIT_NO_SD, INIT_NO_WIFI, INIT_NO_CONNECT};
// enum Err {NO_ERROR, ERR_OFF, ERR_STOP, ERR_SERV, ERR_WAIT, ERR_FULL, ERR_CHG_WIFI, ERR_CV, ERR_ASK_SURE};
// byte errType;
// initResult initStatus;
// byte csStatus = 0;
// #define DEFAULT_STEPS 4 // 128 steps
// byte stopMode;
// bool shuntingMode;
// byte shortAddress;
// byte scrHour, scrMin, scrRate, scrPosTime;
// byte clockHour, clockMin, clockRate;
// unsigned long clockTimer, clockInterval; // Internal fast clock calculation
// enum prgCV {PRG_IDLE, PRG_CV, PRG_RD_CV29, PRG_RD_CV1, PRG_RD_CV17, PRG_RD_CV18, PRG_WR_CV1, PRG_WR_CV17, PRG_WR_CV18, PRG_WR_CV29};
// unsigned int CVaddress, CVdata, CVmax; // programacion CV
// bool modeProg;
// bool enterCVdata;
// bool progFinished;
// byte progStepCV, lastCV;
// byte cv29, cv17, cv18;
// unsigned int decoAddress;
// enum lock {LOCK_SEL_LOCO, LOCK_TURNOUT, LOCK_PROG};
// byte lockOptions;
////////////////////////////////////////////////////////////
// ***** LOCOMOTIVES *****
////////////////////////////////////////////////////////////
// unsigned long infoTimer;
// unsigned long pingTimer;
// byte myLocoData; // current loco data index
// typedef union { // Loco address
// uint8_t adr[2];
// uint16_t address;
// } lokAddr;
// typedef union {
// uint8_t xFunc[4]; // loco functions, F0F4, F5F12, F13F20 y F21F28
// uint32_t Bits; // long para acceder a los bits
// } lokFunc;
// typedef struct {
// lokAddr myAddr;
// lokFunc myFunc;
// uint8_t myDir;
// uint8_t mySpeed;
// uint8_t mySteps;
// uint16_t myVmax;
// char myName[NAME_LNG + 1];
// uint16_t myLocoID; // ID / picture
// uint8_t myFuncIcon[30];
// } lokData;
// lokData locoData[LOCOS_IN_STACK]; // Loco data
// uint16_t locoStack[LOCOS_IN_STACK]; // stack para ultimas locos accedidas
// uint16_t sortedLocoStack[LOCOS_IN_STACK]; // lista ordenada de locomotoras
// uint16_t locoImages[LOCOS_IN_STACK + 20]; // lista de imagenes en el sistema y SD
// bool useID;
// #define MAX_LOCO_IMAGE sizeof(locoImages) / sizeof(locoImages[0])
// uint16_t locoImageIndex;
// enum locoSort {SORT_LAST, SORT_NUM_UP, SORT_NUM_DWN, SORT_NAME_UP, SORT_NAME_DWN};
// uint16_t currOrder;
// struct {
// unsigned int id; // stack loco data
// unsigned int locoAddress;
// char locoName[NAME_LNG + 1];
// } rosterList[LOCOS_IN_STACK];
////////////////////////////////////////////////////////////
// ***** ACCESSORIES *****
////////////////////////////////////////////////////////////
#define AUTO_INTERVAL 100UL // Timer interval (100ms)
#define TIME_ACC_ON 2 // accessory activation time accessory (200ms)
#define TIME_ACC_CDU 2 // time to wait after accessory deactivated for CDU (200ms)
// enum fifo {FIFO_EMPTY, FIFO_ACC_ON, FIFO_ACC_CDU};
// uint16_t accessoryFIFO[32]; // FIFO 32 elements. Hardcoded in functions.
// uint16_t lastInFIFO;
// uint16_t firstOutFIFO;
// uint16_t cntFIFO;
// uint16_t lastAccessory;
// uint8_t accessoryTimer;
// uint8_t stateFIFO;
// DESVIADO: ROJO - > links thrown
// RECTO: VERDE + < rechts closed
// enum posDesvio {NO_MOVIDO, DESVIADO, RECTO, INVALIDO};
// bool editAccessory;
// uint8_t currPanel;
// uint8_t currPanelAcc;
// uint8_t currAccAspects;
// bool accPanelChanged;
// uint16_t myTurnout;
// typedef enum accType {ACC_UNDEF, ACC_TURN_L, ACC_TURN_R, ACC_TRIPLE, ACC_CROSSING, ACC_DCROSS, ACC_BETRELLE, ACC_SIGNAL2, ACC_SIGNAL3, ACC_SIGNAL4, ACC_SEM2, ACC_SEM3,
// ACC_PAN, ACC_TT_L, ACC_TT_R, ACC_TT_TRK, ACC_TT_TURN, ACC_LIGHT, ACC_SOUND, ACC_POWER, ACC_KEYPAD, ACC_MAX,
// };
// typedef struct {
// uint16_t fncIcon;
// uint16_t color;
// uint16_t colorOn;
// } accAspect;
// typedef struct {
// uint8_t aspects;
// uint8_t num;
// accAspect icon[4];
// } accObj;
// const accObj accDef[ACC_MAX] = {
// { 1, 99, {{FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_UNDEF
// { 2, 99, {{FNC_TURNLD_OFF, COLOR_BLACK, COLOR_RED}, {FNC_TURNLS_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_TURN_L
// { 2, 99, {{FNC_TURNRD_OFF, COLOR_BLACK, COLOR_RED}, {FNC_TURNRS_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_TURN_R
// { 3, 99, {{FNC_TURN3L_OFF, COLOR_BLACK, COLOR_RED}, {FNC_TURN3S_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_TURN3R_OFF, COLOR_BLACK, COLOR_RED}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_TRIPLE
// { 2, 99, {{FNC_CROSD_OFF, COLOR_BLACK, COLOR_RED}, {FNC_CROSS_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_CROSSING
// { 4, 4, {{FNC_DCROSSD1_OFF, COLOR_BLACK, COLOR_RED}, {FNC_DCROSSS1_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_DCROSSD2_OFF, COLOR_BLACK, COLOR_RED}, {FNC_DCROSSS2_OFF, COLOR_BLACK, COLOR_GREEN}}}, // ACC_DCROSS
// { 2, 99, {{FNC_BRETELLED_OFF, COLOR_BLACK, COLOR_RED}, {FNC_BRETELLE_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_BETRELLE
// { 2, 99, {{FNC_SIGRY_OFF, COLOR_BLACK, COLOR_RED}, {FNC_SIGGW_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_SIGNAL2
// { 3, 3, {{FNC_SIGRY_OFF, COLOR_BLACK, COLOR_RED}, {FNC_SIGGW_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_SIGRY_OFF, COLOR_BLACK, COLOR_YELLOW}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_SIGNAL3
// { 4, 4, {{FNC_SIGRY_OFF, COLOR_BLACK, COLOR_RED}, {FNC_SIGGW_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_SIGRY_OFF, COLOR_BLACK, COLOR_YELLOW}, {FNC_SIGGW_OFF, COLOR_BLACK, COLOR_WHITE}}}, // ACC_SIGNAL4
// { 2, 99, {{FNC_SEMR_OFF, COLOR_BLACK, COLOR_RED}, {FNC_SEMG_OFF, COLOR_BLACK, COLOR_RED}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_SEM2
// { 3, 3, {{FNC_SEMR_OFF, COLOR_BLACK, COLOR_RED}, {FNC_SEMG_OFF, COLOR_BLACK, COLOR_RED}, {FNC_SEMY_OFF, COLOR_BLACK, COLOR_RED}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_SEM3
// { 2, 99, {{FNC_PANR_OFF, COLOR_BLACK, COLOR_RED}, {FNC_PANG_OFF, COLOR_BLACK, COLOR_RED}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_PAN
// { 1, 99, {{FNC_TTL_OFF, COLOR_BLACK, COLOR_RED}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_TT_L
// { 1, 99, {{FNC_TTR_OFF, COLOR_BLACK, COLOR_GREEN}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_TT_R
// { 1, 99, {{FNC_TTTRK_OFF, COLOR_BLACK, COLOR_BLACK}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_TT_TRK
// { 1, 99, {{FNC_TTROT_OFF, COLOR_BLACK, COLOR_YELLOW}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_TT_TURN
// { 2, 99, {{FNC_LIGHT_OFF, COLOR_BLACK, COLOR_LIGHTGREY}, {FNC_LIGHT_OFF, COLOR_BLACK, COLOR_YELLOW}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_LIGHT
// { 2, 99, {{FNC_SOUND_OFF, COLOR_BLACK, COLOR_LIGHTGREY}, {FNC_SOUND_OFF, COLOR_BLACK, COLOR_YELLOW}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_SOUND
// { 2, 99, {{FNC_POWER_OFF, COLOR_RED, COLOR_RED}, {FNC_POWER_OFF, COLOR_GREEN, COLOR_GREEN}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_POWER
// { 1, 99, {{FNC_KEYPAD_OFF, COLOR_BLACK, COLOR_YELLOW}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}, {FNC_BLANK_OFF, COLOR_LIGHTGREY, COLOR_LIGHTGREY}}}, // ACC_KEYPAD
// };
// typedef struct {
// accType type;
// uint16_t addr;
// uint16_t addr2;
// char accName[ACC_LNG + 1];
// uint8_t currAspect;
// uint16_t activeOutput; // '3A2G 3A2R 3A1G 3A1R 2A2G 2A2R 2A1G 2A1R 1A2G 1A2R 1A1G 1A1R 0A2G 0A2R 0A1G 0A1R'
// } panelElement;
// const uint16_t accOutDefault[ACC_MAX] = {0x0000, 0x0021, 0x0021, 0x0521, 0x0021, 0x4521, 0x0021, 0x0021, 0x0421, 0x8421, 0x0021, 0x0421, 0x0021, 0x0002, 0x0001, 0x0001, 0x0002, 0x0021, 0x0021, 0x0021, 0x0000};
// panelElement accPanel[16];
// panelElement currAccEdit;
// uint8_t savedAspect[16][16];
////////////////////////////////////////////////////////////
// ***** SPEEDOMETER *****
////////////////////////////////////////////////////////////
// uint32_t speedoStartTime;
// uint32_t speedoEndTime;
// uint32_t speedoSpeed;
// uint16_t speedoScale;
// uint16_t speedoLength;
// enum speedo {SPD_WAIT, SPD_BEGIN, SPD_COUNT, SPD_ARRIVE, SPD_END};
// uint16_t speedoPhase;
////////////////////////////////////////////////////////////
// ***** STEAM *****
////////////////////////////////////////////////////////////
// uint16_t oldSteamLoco; // steam loco address
// uint16_t oldPressure; // 0..270 manometer needle angle
// uint16_t oldSpeedSteam; // 240..305 throttle bar angle
// uint16_t oldLevelBoiler; // 0..50 water level bar
// uint16_t oldLevelTender; // 0..500 tender level bar
// uint8_t steamDir; // 0x80: Forward, 0x00: Backward
// uint16_t targetSpeedSteam; // 0..127 calculated target speed
// uint16_t currentSteamSpeed; // 0..127 current speed
// uint16_t steamPressure; // 0..100 boiler pressure
// uint16_t waterLevelBoiler; // 0..100 boiler water
// uint16_t waterLevelTender; // 0..500 tender water
// bool fillTender; // Tender water filling
// bool waterInjection; // Boiler water injection
// bool shovelCoal; // Shoveling coal to firebox
// #define STEAM_LOAD_TIME 250 // Basic Timeout for water & steam
// #define STEAM_SMOKE_SLOW 3200 // Chimney smoke at slow speed
// #define STEAM_SMOKE_FAST 600 // Chimney smoke at fast speed
// #define STEAM_JOHNSON_NEUTRAL 3 // Johnson bar neutral position
// uint32_t currentSteamTime;
// uint32_t steamTimeSpeed;
// uint32_t steamTimeSteam;
// uint32_t steamTimeWater;
// uint32_t steamTimeLoad;
// uint32_t steamTimeSmoke;
// uint32_t changeSteam;
// uint32_t changeWater;
// uint32_t changeSpeed;
// uint32_t changeSmoke;
// enum steamLimits {LIMIT_THROTTLE, LIMIT_JOHNSON, LIMIT_PRESSURE, LIMIT_WATER, LIMIT_TENDER, LIMIT_BRAKE, MAX_LIMIT};
// #define LIMIT_NONE 127
// uint16_t steamSpeedLimit[MAX_LIMIT];
////////////////////////////////////////////////////////////
// ***** STATION RUN *****
////////////////////////////////////////////////////////////
// uint16_t staTime;
// uint16_t staCurrTime;
// uint16_t staStartTime;
// uint8_t staLevel;
// uint8_t staStars;
// uint8_t staStations; // stations target
// uint8_t staCurrStation; // stations counter
// uint8_t staLastStation; // last color station
// uint8_t staMaxStations;
// uint8_t staMaxTurnout;
// #define MAX_STATIONS 5
// const uint16_t staColors[MAX_STATIONS] = {COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_WHITE, COLOR_CYAN};
// uint16_t staTurnoutAdr1;
// uint16_t staTurnoutAdr2;
// uint16_t staTurnoutAdr3;
// uint16_t staTurnoutAdr4;
// uint8_t staTurnoutDef;
// bool staTurnoutPos[4];
////////////////////////////////////////////////////////////
// ***** Z21 *****
////////////////////////////////////////////////////////////
// #define csNormalOps 0x00 // Normal Operation Resumed
// #define csEmergencyStop 0x01 // Emergency Stop
// #define csEmergencyOff 0x02 // Emergency off. Xpressnet
// #define csTrackVoltageOff 0x02 // Emergency off. Z21
// #define csStartMode 0x04 // Start mode. Xpressnet
// #define csShortCircuit 0x04 // ShortCircuit
// #define csServiceMode 0x08 // Service mode. Xpressnet
// #define csReserved 0x10
// #define csProgrammingModeActive 0x20 // Service Mode. Z21
// #define csPowerUp 0x40 // Xpressnet
// #define csErrorRAM 0x80 // Error RAM. Xpressnet
// byte packetBuffer[1500]; // buffer to hold incoming packet, max. 1472 bytes
// byte OutData[80];
// byte OutPos = 0;
// byte OutXOR;
// // Z21 white message header
// #define LAN_GET_SERIAL_NUMBER 0x10
// #define LAN_GET_CODE 0x18
// #define LAN_GET_HWINFO 0x1A
// #define LAN_LOGOFF 0x30
// #define LAN_X_Header 0x40
// #define LAN_SET_BROADCASTFLAGS 0x50
// #define LAN_GET_BROADCASTFLAGS 0x51
// #define LAN_GET_LOCOMODE 0x60
// #define LAN_SET_LOCOMODE 0x61 // Not implemented
// #define LAN_GET_TURNOUTMODE 0x70
// #define LAN_SET_TURNOUTMODE 0x71 // Not implemented
// #define LAN_RMBUS_DATACHANGED 0x80
// #define LAN_RMBUS_GETDATA 0x81
// #define LAN_RMBUS_PROGRAMMODULE 0x82 // Not implemented
// #define LAN_SYSTEMSTATE_DATACHANGED 0x84
// #define LAN_SYSTEMSTATE_GETDATA 0x85
// #define LAN_RAILCOM_DATACHANGED 0x88
// #define LAN_RAILCOM_GETDATA 0x89
// #define LAN_LOCONET_Z21_RX 0xA0
// #define LAN_LOCONET_Z21_TX 0xA1
// #define LAN_LOCONET_FROM_LAN 0xA2
// #define LAN_LOCONET_DISPATCH_ADDR 0xA3 // unused
// #define LAN_LOCONET_DETECTOR 0xA4
// #define LAN_FAST_CLOCK_CONTROL 0xCC
// #define LAN_FAST_CLOCK_DATA 0xCD
// #define LAN_FAST_CLOCK_SETTINGS_GET 0xCE // unused
// #define LAN_FAST_CLOCK_SETTINGS_SET 0xCF // unused
// enum xAnswer {DATA_LENL, DATA_LENH, DATA_HEADERL, DATA_HEADERH, XHEADER, DB0, DB1, DB2, DB3, DB4, DB5, DB6, DB7, DB8};
// #define Z21_PING_INTERVAL 5 // Prevent automatic LOGOFF (5s)
// bool waitResultCV;
////////////////////////////////////////////////////////////
// ***** XPRESSNET LAN *****
////////////////////////////////////////////////////////////
// enum xPacket {FRAME1, FRAME2, HEADER, DATA1, DATA2, DATA3, DATA4, DATA5};
// #define XNET_PING_INTERVAL 10000UL // Prevent closing the connection
// volatile byte rxBufferXN[20]; // Comunicacion Xpressnet
// volatile byte txBuffer[14];
// volatile byte txBytes;
// volatile byte rxBytes;
// byte rxXOR, rxIndice, txXOR, rxData;
// byte xnetVersion, xnetCS;
// bool getInfoLoco, getResultsSM;
// bool askMultimaus; // Multimaus
// byte highVerMM, lowVerMM;
// unsigned long timeoutXnet;
////////////////////////////////////////////////////////////
// ***** LOCONET OVER TCP *****
////////////////////////////////////////////////////////////
// lnMsg SendPacket; // Paquete a enviar por Loconet WiFi
// lnMsg RecvPacket; // Paquete recibido por Loconet WiFi
// char rcvStr[10];
// byte rcvStrPos;
// enum rcvPhase {WAIT_TOKEN, RECV_TOKEN, RECV_PARAM, SENT_TOKEN, SENT_PARAM};
// byte rcvStrPhase;
// bool sentOK;
// unsigned long timeoutSend;
// enum statusSlot {STAT_FREE, STAT_COMMON, STAT_IDLE, STAT_IN_USE};
// struct slot {
// byte num; // slot number: 1..120 (0x01..0x78)
// byte state; // state: in_use/idle/common/free
// byte trk; // track status
// } mySlot;
// const byte stepsLN[] = {28, 28, 14, 128, 28, 28, 14, 128};
// #define LNET_PING_INTERVAL 55000UL // Prevent PURGE of slot (55s)
// bool doDispatchGet, doDispatchPut; // dispatching
// uint8_t lastTrk; // last received global track status
// uint8_t autoIdentifyCS;
// bool lnetProg; // programando CV o LNCV
// byte ulhiProg; // Programming track off (UHLI)
// enum lncv {LNCV_ART, LNCV_MOD, LNCV_ADR, LNCV_VAL};
// unsigned int artNum;
// unsigned int modNum;
// unsigned int numLNCV;
// unsigned int valLNCV;
// byte optLNCV;
////////////////////////////////////////////////////////////
// ***** ECoS *****
////////////////////////////////////////////////////////////
// #define ID_ECOS 1 // Base objects ID
// #define ID_PRGMANAGER 5
// #define ID_POMMANAGER 7
// #define ID_LOKMANAGER 10
// #define ID_SWMANAGER 11
// #define ID_SNIFFERMANAGER 25
// #define ID_S88MANAGER 26
// #define ID_BOOSTERMANAGER 27
// #define ID_S88FEEDBACK 100
// #define ID_INTBOOSTER 65000
// char cmd[64]; // send buffer
// #define NAME_LNG 16 // loco names length
// #define BUF_LNG 1024
// char inputBuffer[BUF_LNG];
// unsigned int inputLength;
// #define TEXTLEN 32 // Length of symbols in input
// char putBackChr;
// int posFile;
// // Tokens
// enum {
// T_NULL, T_START, T_ENDB, T_REPLY, T_EVENT, T_END, T_COMMA, T_INTLIT, T_SEMI, T_LPARENT, T_RPARENT,
// T_LBRACKET, T_RBRACKET, T_STRLIT, T_IDENT, T_PRINT, T_INT,
// T_GET, T_SET, T_QOBJ, T_ADDR, T_NAME, T_PROT, T_STATUS, T_STATUS2, T_STATE,
// T_REQ, T_VIEW, T_CONTROL, T_RELEASE, T_FORCE,
// T_GO, T_STOP, T_SHUTDWN, T_OK, T_MSG, T_SPEED, T_STEPS, T_DIR, T_FUNC, T_FSYMBOL, T_FSYMB, T_FICON,
// T_LOST, T_OTHER, T_CV, T_CVOK, T_ERROR, T_SWITCH,
// T_CS1, T_ECOS, T_ECOS2, T_APPV,
// };
// // Token structure
// struct token {
// char token;
// int intvalue;
// };
// struct token T;
// int tokenType;
// char Text[TEXTLEN + 1];
// int errCode;
// int idManager;
// int idCommand;
// int numLoks;
// int lastNumValue;
// bool requestedCV;
// int appVer;
// byte msgDecodePhase;
// enum {MSG_WAIT, MSG_START, MSG_REPLY, MSG_EVENT, MSG_END, MSG_REPLYBODY, MSG_EVENTBODY};
// uint8_t mySpeedStep;
// const uint8_t FunktionsTastenSymbole[] = { // Conversion table from ECoS v4.2.9 to PacoMouseCYD function icons
// 1, 1, 2, 3, 4, 5, 6, 7,
// 8, 9, 10, 11, 12, 13, 14, 15,
// 16, 17, 18, 19, 20, 21, 22, 23,
// 2, 2, 24, 2, 2, 25, 26, 27, //31
// 28, 29, 30, 2, 31, 2, 32, 2,
// 2, 2, 2, 2, 33, 34, 2, 2, //47
// 2, 2, 2, 2, 2, 2, 2, 35,
// 2, 36, 2, 2, 2, 2, 2, 2, //63
// 2, 2, 2, 2, 2, 2, 2, 2,
// 2, 2, 2, 2, 2, 2, 2, 2, //79
// 2, 2, 2, 2, 37, 2, 2, 38,
// 39, 2, 2, 2, 2, 2, 2, 2, //95
// 2, 2, 2, 2, 2, 39, 2, 2, //103
// 2, 2, 2, 2, 2, 2, 2, 2,
// 2, 2, 2, 2, 2, 2, 2, 2,
// 2, 2, 2, 2, 40, 2, 2, 2, //127
// };
// const uint8_t FunktionsTastenSymboleCS1[] = { // Conversion table from CS1 v2.0.4 to PacoMouseCYD function icons
// 2, 10, 3, 14, 5, 31, 15, 16,
// 11, 13, 17, 9, 12, 6, 7, 23,
// 4, 2, 25, 26, 22, 2, 30, 27,
// 2, 2, 2, 28, 29, 20, 21, 8, //31
// 18, 2, 2, 2, 2, 2, 2, 2,
// 2, 2, 2, 2, 2, 2, 2, 2, //47
// 2, 2, 2, 2, 2, 2, 2, 2,
// 2, 2, 2, 2, 2, 2, 2, 2, //63
// };
////////////////////////////////////////////////////////////
// ***** MAIN PROGRAM *****
////////////////////////////////////////////////////////////
// Arduino setup function: runs once at boot
void setup() {
uint16_t posLabel; // Variable for label positioning (if used)
// initPins(); // Initialize GPIO pins
delay(500); // Power-up safety delay
// Initialize serial for debug output if enabled
#ifdef DEBUG
Serial.begin(115200); // Debug messages, serial at 115200b
DEBUG_MSG("\n\nPacoMouseCYD v%s.%s", VER_H, VER_L);
DEBUG_MSG(USER_SETUP_INFO);
DEBUG_MSG("Chip Model: %s \nFlash Chip Size: %lu", ESP.getChipModel(), ESP.getFlashChipSize())
DEBUG_MSG("ESP_ARDUINO_VERSION: %d.%d.%d", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
#endif
// initGUI(); // Initialize graphical user interface
// initVariables(); // Initialize global variables and settings
// sdDetected = SD.begin(SD_CS) ? true : false; // Initialize SD card
DEBUG_MSG("SD Card %s", sdDetected ? "found" : "begin failed")
// tft.init(); // Initialize TFT display
// tft.fillScreen(COLOR_BLACK); // Clear display
#ifdef DEBUG
DEBUG_MSG("Display driver: %X %dx%d", TFT_DRIVER, TFT_WIDTH, TFT_HEIGHT)
DEBUG_MSG("TFT BL pin: %d", TFT_BL)
DEBUG_MSG("GUI Usage:\n Timers: %d\n Windows: %d\n Labels: %d", MAX_SYS_TIMER, MAX_WIN_OBJ, MAX_LABEL_OBJ)
DEBUG_MSG(" Draw Strings: %d\n Chars: %d\n Function Icons: %d", MAX_DRAWSTR_OBJ, MAX_CHAR_OBJ, MAX_FNC_OBJ)
DEBUG_MSG(" Icons: %d\n Buttons: %d\n Radio Buttons: %d", MAX_ICON_OBJ, MAX_BUT_OBJ, MAX_RAD_OBJ)
DEBUG_MSG(" Progress Bar: %d\n Loco Pictures: %d\n Gauges: %d", MAX_BAR_OBJ, MAX_LPIC_OBJ, MAX_GAUGE_OBJ)
DEBUG_MSG(" Text Box: %d\n Switch: %d\n Keyboard: %d", MAX_TXT_OBJ, MAX_SWITCH_OBJ, MAX_KEYB_OBJ)
#endif
// Setting up the LEDC and configuring the Back light pin
// NOTE: this needs to be done after tft.init()
#if (ESP_ARDUINO_VERSION_MAJOR > 2)
DEBUG_MSG("PWM LED code for version 3.x")
ledcAttach(TFT_BL, LEDC_BASE_FREQ, LEDC_RESOLUTION);
#else
DEBUG_MSG("PWM LED code for version 2.x")
// ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_RESOLUTION); // backlight PWM
// ledcAttachPin(TFT_BL, LEDC_CHANNEL_0);
#endif
// setBacklight (backlight); // Set initial backlight level
//
// // Setup the touchscreen (must be after tft.init())
// touchscreen.begin(TFT_WIDTH, TFT_HEIGHT); // Initialize touchscreen
// setRotationDisplay(locationUSB); // Set display rotation based on USB location
// copyOutA = digitalRead (ENCODER_A); // Init encoder ISR
// copyOutB = 0x80;
// attachInterrupt (digitalPinToInterrupt (ENCODER_A), encoderISR, CHANGE); // Attach encoder interrupt
// openWindow(WIN_LOGO); // Show logo window (no events, just draw)
// initStatus = initSequence(); // Run initialization sequence
// getLastLoco(); // Load last used locomotive
}
// Arduino main loop: runs repeatedly after setup()
void loop() {
// timerProcess(); // Handle timers
// hidProcess(); // Handle human interface devices (buttons, encoders, etc.)
// wifiProcess(); // Handle WiFi events and communication
// if (isWindow(WIN_STEAM))
// steamProcess(); // If steam window is active, process steam logic
// if (eventsPending > 0) { // Execute pending events
// eventProcess();
// DEBUG_MSG("New Event...")
// }
// // Check BOOT button to enter touchscreen calibration
// if (! bootPressed) {
// if (digitalRead(SW_BOOT) == LOW) {
// bootPressed = true;
// newEvent(OBJ_WIN, WIN_CALIBRATE, EVNT_BOOT);
// DEBUG_MSG("BOOT switch pressed...")
// }
// }
// // Handle touchscreen click detection
// if (clickDetected) {
// if (! touchscreen.touched())
// clickDetected = false;
// }
// else {
// if (touchscreen.touched()) {
// TSPoint p = touchscreen.getTouch();
// if (p.z > 350) {
// DEBUG_MSG("X: %d, Y: %d, Z: %d", p.x, p.y, p.z);
// sendClickEvent(p.x, p.y);
// clickDetected = true;
// }
// }
// }
}
////////////////////////////////////////////////////////////
// ***** SUPPORT *****
////////////////////////////////////////////////////////////
void initVariables()
{
byte pos;
uint16_t xm, xM, ym, yM, value;
// EEPROM.begin(EEPROM_SIZE);
// eepromChanged = false;
// currLanguage = EEPROM.read(EE_LANGUAGE);
// if (currLanguage >= MAX_LANG)
// currLanguage = LANG_ENGLISH;
// bootPressed = false;
// calibrationPending = false;
// EEPROM.get (EE_WIFI, wifiSetting); // read WiFi settings
// if (wifiSetting.ok != 0x464D) { // check correct EEPROM signature
// snprintf (wifiSetting.ssid, 32, ""); // init WiFi settings
// snprintf (wifiSetting.password, 64, "12345678");
// wifiSetting.CS_IP = IPAddress(192, 168, 0, 111);
// wifiSetting.port = 1234;
// wifiSetting.serverType = true;
// wifiSetting.protocol = CLIENT_Z21;
// wifiSetting.ok = 0x464D;
// EEPROM.put(EE_WIFI, wifiSetting); // also init calibration values
// touchscreen.setCalibration(0, 4095, 0, 4095); // set default calibration values
// saveCalibrationValues(); // save all
// DEBUG_MSG("Setting default WiFi & calibration values");
// }
// xm = (EEPROM.read(EE_XMIN_H) << 8) + EEPROM.read(EE_XMIN_L);
// xM = (EEPROM.read(EE_XMAX_H) << 8) + EEPROM.read(EE_XMAX_L);
// ym = (EEPROM.read(EE_YMIN_H) << 8) + EEPROM.read(EE_YMIN_L);
// yM = (EEPROM.read(EE_YMAX_H) << 8) + EEPROM.read(EE_YMAX_L);
// touchscreen.setCalibration(xm, xM, ym, yM); // set calibration values
// DEBUG_MSG("xMin: %d, xMax: %d, yMin: %d, yMax: %d", xm, xM, ym, yM);
// backlight = EEPROM.read(EE_BACKLIGHT);
// if (backlight < USER_MIN_BL)
// backlight = USER_MIN_BL;
// locationUSB = EEPROM.read(EE_USB_LOCATION);
// if (locationUSB > 0)
// locationUSB = USB_UP;
// timeButtons = millis();
// lastTimeEncoder = millis();
// encoderValue = 0;
// encoderMax = 2;
// encoderNeedService = false;
// oldNeedle = 0;
// initFIFO(); // accessories
// editAccessory = false;
// accPanelChanged = false;
// currPanel = 0;
// myTurnout = 1;
// initLastAspects();
// CVdata = 3; // CV programming
// CVaddress = 1;
// modeProg = false;
// enterCVdata = false;
// progFinished = false;
// progStepCV = PRG_IDLE;
// clockRate = 0; // fast clock internal
// clockHour = 0;
// clockMin = 0;
// updateFastClock();
// initLocos();
// stopMode = EEPROM.read(EE_STOP_MODE);
// shuntingMode = (EEPROM.read(EE_SHUNTING) > 0) ? true : false;
// lockOptions = EEPROM.read(EE_LOCK) & 0x07;
// shortAddress = EEPROM.read(EE_SHORT);
// if ((shortAddress != 99) && (shortAddress != 127))
// shortAddress = 99;
// typeCmdStation = EEPROM.read(EE_CMD_STA);
// if (typeCmdStation > CMD_DIG)
// typeCmdStation = CMD_DR;
// autoIdentifyCS = EEPROM.read(EE_CMD_AUTO);
// speedoPhase = SPD_WAIT;
// speedoScale = 87;
// speedoLength = 1000;
// speedoSpeed = 0;
// oldSteamLoco = 0;
// mySlot.num = 0; // Loconet slots
// mySlot.trk = 0x01; // Power on
// doDispatchGet = false;
// doDispatchPut = false;
// lnetProg = false;
// ulhiProg = UHLI_PRG_END;
// lastTrk = 0x80;
// msgDecodePhase = MSG_WAIT; // ECoS
// requestedCV = false;
// appVer = 4;
// staTurnoutAdr1 = staGetTurnoutAdr(EE_STA_ADRH1, 1); // Station Run
// staTurnoutAdr2 = staGetTurnoutAdr(EE_STA_ADRH2, 2);
// staTurnoutAdr3 = staGetTurnoutAdr(EE_STA_ADRH3, 3);
// staTurnoutAdr4 = staGetTurnoutAdr(EE_STA_ADRH4, 4);
// staTurnoutDef = EEPROM.read(EE_STA_TRNDEF);
// staMaxTurnout = EEPROM.read(EE_STA_TRNNUM);
// staMaxTurnout = constrain(staMaxTurnout, 1, 4);
// staMaxStations = EEPROM.read(EE_STA_NUM);
// staMaxStations = constrain(staMaxStations, 3, 5);
// staStartTime = EEPROM.read(EE_STA_TIME);
}
void hidProcess() {
// if (encoderNeedService) // detectado cambio en pines del encoder
// encoderService();
// if (encoderChange) // se ha movido el encoder
// controlEncoder();
// if (switchOn) // se ha pulsado el boton del encoder
// controlSwitch();
// if (millis() - timeButtons > timeoutButtons) // lectura de boton
// readButtons();
}
void updateFastClock() {
// if (clockRate > 0)
// sprintf(clockBuf, "%02d:%02d", clockHour, clockMin);
// else
// sprintf(clockBuf, "");
// if (isWindow(WIN_THROTTLE))
// newEvent(OBJ_TXT, TXT_CLOCK, EVNT_DRAW);
}
void setSpeedoPhase(uint8_t phase) {
// drawStrData[DSTR_SPEEDO_BLANK].x = 40 + (speedoPhase * 32);
// speedoPhase = phase;
// iconData[ICON_SPEEDO_LOK].x = 40 + (speedoPhase * 32);
// drawObject(OBJ_DRAWSTR, DSTR_SPEEDO_BLANK);
// drawObject(OBJ_ICON, ICON_SPEEDO_LOK);
}
////////////////////////////////////////////////////////////
// ***** SOPORTE LOCOMOTORAS *****
////////////////////////////////////////////////////////////
// uint16_t checkLocoAddress(uint16_t loco) {
// if (useID) {
// }
// else {
// loco &= 0x3FFF;
// if ((loco > 9999) || (loco == 0)) // Comprueba que este entre 1 y 9999
// loco = 3;
// }
// //DEBUG_MSG("Loco: %d", loco);
// return loco;
// }
// void pushLoco(unsigned int loco) { // mete locomotora en el stack
// byte pos;
// unsigned int adr;
// for (pos = 0; pos < LOCOS_IN_STACK; pos++) { // busca loco en stack
// if (locoStack[pos] == loco)
// locoStack[pos] = 0; // evita que se repita
// }
// pos = 0;
// do {
// adr = locoStack[pos]; // push the loco in stack
// locoStack[pos] = loco;
// loco = adr;
// pos++;
// } while ((adr > 0) && (pos < LOCOS_IN_STACK));
// #ifdef DEBUG
// Serial.print(F("STACK: "));
// for (pos = 0; pos < LOCOS_IN_STACK; pos++) {
// Serial.print(locoStack[pos]);
// Serial.print(' ');
// }
// Serial.println();
// #endif
// }
// void popLoco(unsigned int loco) { // elimina locomotora del stack
// byte pos, n;
// unsigned int adr;
// pos = LOCOS_IN_STACK;
// for (n = 0; n < LOCOS_IN_STACK; n++) { // busca loco en stack
// if (locoStack[n] == loco)
// pos = n; // save position
// }
// if (pos != LOCOS_IN_STACK) {
// for (n = pos; n < (LOCOS_IN_STACK - 1); n++) {
// locoStack[n] = locoStack[n + 1];
// }
// locoStack[LOCOS_IN_STACK - 1] = 0;
// }
// }
// void initLocos() {
// uint16_t pos;
// myLocoData = 0;
// for (pos = 0; pos < LOCOS_IN_STACK; pos++) {
// locoStack[pos] = 0; // clear loco stack
// clearLocoData(pos);
// }
// }
// void clearLocoData (uint16_t pos) {
// uint16_t cnt;
// locoData[pos].myAddr.address = 0; // clear loco data
// locoData[pos].myName[0] = '\0';
// locoData[pos].myFunc.Bits = 0;
// locoData[pos].myDir = 0x80;
// locoData[pos].mySpeed = 0;
// locoData[pos].mySteps = DEFAULT_STEPS;
// locoData[pos].myVmax = 100;
// locoData[pos].myLocoID = SYS_NO_LOK;
// locoData[pos].myFuncIcon[0] = FNC_LIGHT_OFF;
// for (cnt = 1; cnt < 29; cnt++)
// locoData[pos].myFuncIcon[cnt] = FNC_FUNC_OFF;
// locoData[pos].myFuncIcon[cnt] = FNC_BLANK_OFF;
// }
// void getLastLoco() {
// uint16_t loco;
// loco = locoStack[0]; // get most recent loco in stack (filesystem)
// loco = checkLocoAddress(loco); // avoid empty stack
// findLocoData(loco); // get pos in stack also check stack full
// loadThrottleData(); // load data to throttle
// updateSpeedHID(); // set encoder
// setTimer(TMR_INFO, 10, TMR_ONESHOT);
// }
// void getNewLoco(uint16_t loco) {
// if (useID) {
// if (loco != locoData[myLocoData].myLocoID) {
// releaseLoco();
// }
// }
// else {
// if (loco != locoData[myLocoData].myAddr.address) {
// releaseLoco();
// }
// }
// loco = checkLocoAddress(loco);
// findLocoData(loco); // get pos in stack also check stack full
// loadThrottleData(); // load data to throttle
// updateSpeedHID(); // set encoder
// infoLocomotora(loco); // ask current values
// setTimer(TMR_INFO, 5, TMR_ONESHOT);
// }
// void findLocoData(uint16_t loco) {
// uint16_t pos, cnt;
// pos = 0;
// if (useID) {
// while (pos < LOCOS_IN_STACK) { // find ID loco data
// if (locoData[pos].myLocoID == loco) {
// myLocoData = pos;
// pushLoco(loco);
// DEBUG_MSG("Find ID%d data in %d", loco, pos);
// return;
// }
// pos++;
// }
// }
// else {
// while (pos < LOCOS_IN_STACK) { // find address loco data
// if (locoData[pos].myAddr.address == loco) {
// myLocoData = pos;
// pushLoco(loco);
// DEBUG_MSG("Find %d data in %d", loco, pos);
// return;
// }
// pos++;
// }
// DEBUG_MSG("New Loco")
// pos = 0; // new loco
// while (pos < LOCOS_IN_STACK) {
// if (locoData[pos].myAddr.address == 0) {
// myLocoData = pos;
// locoData[pos].myAddr.address = loco;
// locoData[pos].myLocoID = SYS_ELOK;
// //locoData[pos].mySteps = DEFAULT_STEPS;
// pushLoco(loco);
// return;
// }
// pos++;
// }
// }
// myLocoData = 0; // stack full
// alertWindow(ERR_FULL);
// }
// void loadThrottleData() {
// uint16_t n;
// snprintf (locoName, NAME_LNG + 1, "%s", locoData[myLocoData].myName );
// txtData[TXT_LOCO_NAME].font = (strlen(locoName) > 13) ? FSS7 : FSS9;
// sprintf (locoAddr, "%d", locoData[myLocoData].myAddr.address);
// lpicData[LPIC_MAIN].id = locoData[myLocoData].myLocoID;
// iconData[ICON_FWD].color = (locoData[myLocoData].myDir & 0x80) ? COLOR_NAVY : COLOR_DARKGREY;
// iconData[ICON_REV].color = (locoData[myLocoData].myDir & 0x80) ? COLOR_DARKGREY : COLOR_NAVY;
// for (n = 0; n < 10; n++) {
// fncData[FNC_FX0 + n].num = n;
// fncData[FNC_FX0 + n].idIcon = locoData[myLocoData].myFuncIcon[n];
// }
// updateFuncState(false);
// }
// void updateFuncState(bool show) {
// byte n;
// bool state;
// for (n = 0; n < 10; n++) {
// state = bitRead(locoData[myLocoData].myFunc.Bits, fncData[FNC_FX0 + n].num);
// if (fncData[FNC_FX0 + n].state != state) {
// fncData[FNC_FX0 + n].state = state;
// if (show)
// newEvent(OBJ_FNC, FNC_FX0 + n, EVNT_DRAW);
// DEBUG_MSG("Change in F%d", fncData[FNC_FX0 + n].num);
// }
// }
// }
// void toggleFunction(uint8_t fnc, uint8_t id) {
// locoData[myLocoData].myFunc.Bits ^= bit(fnc);
// fncData[id].state = bitRead(locoData[myLocoData].myFunc.Bits, fnc);
// funcOperations(fnc);
// newEvent(OBJ_FNC, id, EVNT_DRAW);
// }
// void showFuncBlock(uint8_t fncOffset) {
// uint16_t ini, cnt;
// for (ini = 0; ini < 10; ini++) {
// cnt = ini + FNC_FX0;
// fncData[cnt].idIcon = locoData[myLocoData].myFuncIcon[ini + fncOffset];
// fncData[cnt].num = ini + fncOffset;
// fncData[cnt].state = bitRead(locoData[myLocoData].myFunc.Bits, fncData[cnt].num);
// drawObject(OBJ_FNC, cnt);
// }
// }
// void showNextFuncBlock() {
// uint16_t ini, fncOffset;
// ini = fncData[FNC_FX0].num;
// fncOffset = (ini == 20) ? 0 : ini + 10;
// showFuncBlock(fncOffset);
// }
// void updateSpeedHID() {
// byte spd, steps;
// switch (wifiSetting.protocol) {
// case CLIENT_Z21:
// case CLIENT_XNET:
// if (bitRead(locoData[myLocoData].mySteps, 2)) { // 0..127 -> 0..63
// encoderMax = 63;
// encoderValue = (locoData[myLocoData].mySpeed > 1) ? (locoData[myLocoData].mySpeed >> 1) : 0;
// }
// else {
// if (bitRead(locoData[myLocoData].mySteps, 1)) { // 0..31
// encoderMax = 31;
// spd = (locoData[myLocoData].mySpeed & 0x0F) << 1;
// if (bitRead(locoData[myLocoData].mySpeed, 4))
// bitSet(spd, 0);;
// encoderValue = (spd > 3) ? spd : 0;
// }
// else { // 0..15
// encoderMax = 15;
// spd = locoData[myLocoData].mySpeed & 0x0F;
// encoderValue = (spd > 1) ? spd : 0;
// }
// }
// break;
// case CLIENT_LNET:
// steps = getMaxStepLnet();
// encoderMax = (steps == 128) ? 63 : ((steps == 28) ? 31 : 15);
// if (locoData[myLocoData].mySpeed > 1) {
// if (steps == 128) { // Max 100% speed (64 pasos, compatible con 14, 28 y 128 pasos)
// encoderValue = locoData[myLocoData].mySpeed >> 1; // 0..127 -> 0..63
// }
// else {
// if (steps == 28) { // 0..31
// spd = (((locoData[myLocoData].mySpeed - 2) << 1) / 9);
// encoderValue = spd + 4;
// }
// else {
// spd = (locoData[myLocoData].mySpeed - 2) / 9;
// encoderValue = spd + 2;
// }
// }
// }
// else
// encoderValue = 0;
// //DEBUG_MSG("HID Enc:%d Spd:%d", encoderValue, mySpeed);
// break;
// case CLIENT_ECOS:
// encoderMax = 63; // 0..127 -> 0..63
// encoderValue = (locoData[myLocoData].mySpeed > 1) ? (locoData[myLocoData].mySpeed >> 1) : 0;
// break;
// }
// updateSpeedDir();
// }
// void updateMySpeed() {
// byte spd, steps;
// switch (wifiSetting.protocol) {
// case CLIENT_Z21:
// case CLIENT_XNET:
// if (bitRead(locoData[myLocoData].mySteps, 2)) { // 0..63 -> 0..127
// if ((encoderValue == 0) && shuntingMode) // comprueba Modo maniobras
// encoderValue = 1;
// locoData[myLocoData].mySpeed = encoderValue << 1;
// }
// else {
// if (bitRead(locoData[myLocoData].mySteps, 1)) { // 0..31 -> 0..31 '---43210' -> '---04321'
// encoderValue = shuntingSpeed (encoderValue, 4);
// if (encoderValue > 3) {
// locoData[myLocoData].mySpeed = (encoderValue >> 1) & 0x0F;
// bitWrite(locoData[myLocoData].mySpeed, 4, bitRead(encoderValue, 0));
// }
// else {
// switch (encoderValue) {
// case 0:
// case 3:
// locoData[myLocoData].mySpeed = 0;
// encoderValue = 0;
// break;
// case 1:
// case 2:
// locoData[myLocoData].mySpeed = 2;
// encoderValue = 4;
// break;
// }
// }
// }
// else {
// encoderValue = shuntingSpeed (encoderValue, 2);
// locoData[myLocoData].mySpeed = (encoderValue > 1) ? encoderValue : 0; // 0..15 -> 0..15
// }
// }
// break;
// case CLIENT_LNET:
// steps = getMaxStepLnet();
// if (steps == 128) {
// if ((encoderValue == 0) && shuntingMode) // Modo maniobras
// encoderValue = 1;
// locoData[myLocoData].mySpeed = (encoderValue << 1); // 0..63 -> 0..127
// if (encoderValue > 61)
// locoData[myLocoData].mySpeed++;
// }
// else {
// if (steps == 28) {
// encoderValue = shuntingSpeed (encoderValue, 4);
// if (encoderValue > 3) {
// spd = ((encoderValue - 3) * 9) + 1; // 0..31 -> 0..127
// locoData[myLocoData].mySpeed = (spd >> 1) + 1;
// }
// else {
// switch (encoderValue) {
// case 0:
// case 3:
// locoData[myLocoData].mySpeed = 0;
// encoderValue = 0;
// break;
// case 1:
// case 2:
// locoData[myLocoData].mySpeed = 5;
// encoderValue = 4;
// break;
// }
// }
// }
// else {
// encoderValue = shuntingSpeed (encoderValue, 2);
// if (encoderValue == 1)
// return;
// locoData[myLocoData].mySpeed = (encoderValue > 1) ? ((encoderValue - 2) * 9) + 2 : 0; // 0..15 -> 0..127
// }
// }
// //DEBUG_MSG("LN Enc:%d Spd:%d", encoderValue, mySpeed);
// break;
// case CLIENT_ECOS:
// if ((encoderValue == 0) && shuntingMode) // Modo maniobras
// encoderValue = 1;
// locoData[myLocoData].mySpeed = (encoderValue << 1); // 0..63 -> 0..127
// if (encoderValue > 61)
// locoData[myLocoData].mySpeed++;
// break;
// }
// locoOperationSpeed();
// }
// byte shuntingSpeed (byte encoder, byte stepMin) { // comprueba Modo maniobras
// if (encoder < stepMin) {
// if (shuntingMode)
// return stepMin;
// }
// return encoder;
// }
// void updateSpeedDir() {
// uint16_t angle, spd, stp;
// iconData[ICON_FWD].color = (locoData[myLocoData].myDir & 0x80) ? COLOR_NAVY : COLOR_DARKGREY;
// iconData[ICON_REV].color = (locoData[myLocoData].myDir & 0x80) ? COLOR_DARKGREY : COLOR_NAVY;
// if (isWindow(WIN_THROTTLE)) {
// angle = map(encoderValue, 0, encoderMax, 0, 255);
// // if ((encoderMax == 15) && (encoderValue < 2))
// // angle = 0;
// drawObject(OBJ_ICON, ICON_FWD);
// drawObject(OBJ_ICON, ICON_REV);
// spd = map(angle, 0, 255, 0, locoData[myLocoData].myVmax);
// stp = getCurrentStep();
// drawSpeed(angle, spd, stp);
// DEBUG_MSG("Enc: %d-%d Spd: %d Stp: %d", encoderValue, encoderMax, spd, stp)
// }
// if (isWindow(WIN_SPEEDO)) {
// gaugeData[GAUGE_SPEEDO].value = map(encoderValue, 0, encoderMax, 0, 255);
// fncData[FNC_SPEEDO_DIR].idIcon = (locoData[myLocoData].myDir & 0x80) ? FNC_NEXT_OFF : FNC_PREV_OFF;
// drawObject(OBJ_GAUGE, GAUGE_SPEEDO);
// drawObject(OBJ_FNC, FNC_SPEEDO_DIR);
// drawSpeedoStep();
// }
// if (isWindow(WIN_STA_PLAY)) {
// gaugeData[GAUGE_STATION].value = map(encoderValue, 0, encoderMax, 0, 255);
// fncData[FNC_STA_DIR].idIcon = (locoData[myLocoData].myDir & 0x80) ? FNC_NEXT_OFF : FNC_PREV_OFF;
// drawObject(OBJ_GAUGE, GAUGE_STATION);
// drawObject(OBJ_FNC, FNC_STA_DIR);
// }
// }
// void drawSpeed(uint16_t angle, uint16_t spd, uint16_t stp) {
// angle = (angle < 30) ? 330 + angle : angle - 30; // convert 0..255 to drawing angles -30..225
// tft.setPivot(gaugeData[GAUGE_SPEED].x, gaugeData[GAUGE_SPEED].y);
// sprite.setColorDepth(8); // Create an 8bpp Sprite
// sprite.createSprite(36, 16); // 8bpp requires 36 * 16 = 576 bytes
// sprite.setPivot(56, 7); // Set pivot relative to top left corner of Sprite
// sprite.fillSprite(COLOR_BACKGROUND); // Fill the Sprite with background
// sprite.pushRotated(oldNeedle); // Delete needle
// sprite.drawBitmap(0, 0, needle, 36, 15, COLOR_RED); // Draw new needle
// sprite.drawFastHLine(4, 7, 29, COLOR_PINK);
// sprite.pushRotated(angle, COLOR_BACKGROUND);
// oldNeedle = angle;
// sprite.fillSprite(gaugeData[GAUGE_SPEED].color); // Fill the Sprite with black //gaugeData[GAUGE_SPEED].color
// sprite.setFreeFont(FSSB9);
// sprite.setTextColor(COLOR_WHITE);
// sprite.setTextDatum(MC_DATUM);
// sprite.drawNumber(spd, 18, 8); // Draw current speed in km/h
// sprite.pushSprite(gaugeData[GAUGE_SPEED].x - 18, gaugeData[GAUGE_SPEED].y - 8, COLOR_TRANSPARENT);
// sprite.fillSprite(COLOR_BACKGROUND); // Draw current step
// sprite.setFreeFont(FSS7);
// sprite.drawNumber(stp, 18, 8);
// sprite.pushSprite(gaugeData[GAUGE_SPEED].x - 18, gaugeData[GAUGE_SPEED].y + 44, COLOR_TRANSPARENT);
// sprite.deleteSprite();
// }
// void drawSpeedoStep() {
// uint16_t stp;
// stp = getCurrentStep();
// sprite.setColorDepth(16); // Create an 16bpp Sprite
// sprite.createSprite(36, 16); // 8bpp requires 36 * 16 = 576 bytes * 2
// sprite.fillSprite(COLOR_BACKGROUND); // Draw current step
// sprite.setFreeFont(FSS7);
// sprite.setTextColor(COLOR_WHITE);
// sprite.setTextDatum(MC_DATUM);
// sprite.drawNumber(stp, 18, 8);
// sprite.pushSprite(gaugeData[GAUGE_SPEEDO].x - 18, gaugeData[GAUGE_SPEEDO].y + 20, COLOR_TRANSPARENT);
// sprite.deleteSprite();
// }
// uint16_t countLocoInStack() {
// uint16_t pos, total;
// total = 0;
// pos = 0;
// while ((locoStack[pos++] > 0) && (pos < LOCOS_IN_STACK))
// total++;
// return total;
// }
// void prepareLocoList() {
// uint16_t pos;
// for (pos = 0; pos < 6; pos++) { // delete list
// txtData[TXT_SEL_ADDR1 + pos].buf[0] = '\0';
// txtData[TXT_SEL_NAME1 + pos].buf[0] = '\0';
// }
// encoderValue = 0; // count locos in stack
// encoderMax = countLocoInStack();
// DEBUG_MSG("Locos in list: %d", encoderMax);
// if (encoderMax > 0)
// encoderMax--;
// sortLocoList(SORT_LAST);
// populateLocoList();
// }
// void populateLocoList() {
// uint16_t posName;
// uint16_t line, adr, n;
// for (n = 0; n < 6; n++) {
// if (n < encoderMax + 1) {
// line = (encoderValue > 5) ? encoderValue - 5 : 0;
// adr = sortedLocoStack[line + n];
// posName = findLocoPos(adr);
// //DEBUG_MSG("Enc: %d Line: %d Adr: %d", encoderValue, line, adr);
// if (useID)
// snprintf(txtData[TXT_SEL_ADDR1 + n].buf, ADDR_LNG + 1, "%d", locoData[posName].myAddr.address);
// else
// snprintf(txtData[TXT_SEL_ADDR1 + n].buf, ADDR_LNG + 1, "%d", adr);
// if (posName != LOCOS_IN_STACK)
// snprintf(txtData[TXT_SEL_NAME1 + n].buf, NAME_LNG + 1, locoData[posName].myName);
// }
// }
// line = (encoderValue > 5) ? 5 : encoderValue;
// for (n = 0; n < 6; n++) {
// txtData[TXT_SEL_ADDR1 + n].backgnd = (n == line) ? COLOR_YELLOW : COLOR_WHITE;
// txtData[TXT_SEL_NAME1 + n].backgnd = (n == line) ? COLOR_YELLOW : COLOR_WHITE;
// }
// }
// uint16_t findLocoPos(uint16_t loco) {
// uint16_t pos, n;
// pos = LOCOS_IN_STACK;
// for (n = 0; n < LOCOS_IN_STACK; n++) {
// if (useID) {
// if (locoData[n].myLocoID == loco) // search ID in loco stack
// pos = n;
// }
// else {
// if (locoData[n].myAddr.address == loco) // search address in loco stack
// pos = n;
// }
// }
// return pos;
// }
// void sortLocoList (uint16_t order) {
// uint16_t tmp, n, i, j, total, pos;
// bool reverse;
// #ifdef DEBUG
// Serial.print(F("INI.STACK: "));
// for (pos = 0; pos < LOCOS_IN_STACK; pos++) {
// Serial.print(sortedLocoStack[pos]);
// Serial.print(' ');
// }
// Serial.println();
// #endif
// objStack[posObjStack1].objID = ICON_LAST_UP + order;
// currOrder = order;
// total = countLocoInStack();
// reverse = false;
// //DEBUG_MSG("Order %d Total %d", currOrder, total)
// switch (order) {
// case SORT_LAST:
// for (n = 0; n < LOCOS_IN_STACK; n++)
// sortedLocoStack[n] = locoStack[n];
// break;
// case SORT_NUM_DWN:
// reverse = true;
// case SORT_NUM_UP:
// for (i = 1; i < total; i++) {
// if (useID) {
// for (j = i; j > 0 && (idOrder(sortedLocoStack[j - 1], sortedLocoStack[j]) != reverse); j--) {
// tmp = sortedLocoStack[j - 1];
// sortedLocoStack[j - 1] = sortedLocoStack[j];
// sortedLocoStack[j] = tmp;
// }
// }
// else {
// for (j = i; j > 0 && ((sortedLocoStack[j - 1] > sortedLocoStack[j]) != reverse); j--) {
// tmp = sortedLocoStack[j - 1];
// sortedLocoStack[j - 1] = sortedLocoStack[j];
// sortedLocoStack[j] = tmp;
// }
// }
// }
// break;
// case SORT_NAME_DWN:
// reverse = true;
// case SORT_NAME_UP:
// for (i = 1; i < total; i++) {
// for (j = i; j > 0 && (nameOrder(sortedLocoStack[j - 1], sortedLocoStack[j]) != reverse); j--) {
// tmp = sortedLocoStack[j - 1];
// sortedLocoStack[j - 1] = sortedLocoStack[j];
// sortedLocoStack[j] = tmp;
// }
// }
// break;
// }
// #ifdef DEBUG
// Serial.print(F("END.STACK: "));
// for (pos = 0; pos < LOCOS_IN_STACK; pos++) {
// Serial.print(sortedLocoStack[pos]);
// Serial.print(' ');
// }
// Serial.println();
// #endif
// }
// bool nameOrder(uint16_t first, uint16_t second) {
// uint16_t posFirst, posSecond;
// posFirst = findLocoPos(first);
// posSecond = findLocoPos(second);
// return strcmp(locoData[posFirst].myName, locoData[posSecond].myName) > 0;
// }
// bool idOrder(uint16_t first, uint16_t second) {
// uint16_t posFirst, posSecond;
// posFirst = findLocoPos(first);
// posSecond = findLocoPos(second);
// return (locoData[posFirst].myAddr.address > locoData[posSecond].myAddr.address);
// }
// ////////////////////////////////////////////////////////////
// // ***** SOPORTE GUI *****
// ////////////////////////////////////////////////////////////
// void setBitsCV() {
// uint16_t n;
// for (n = 0; n < 8; n++) {
// buttonData[BUT_CV_0 + n].backgnd = bitRead(CVdata, n) ? COLOR_BLUE : COLOR_DARKGREY;
// charData[CHAR_CV_0 + n].color = bitRead(CVdata, n) ? COLOR_WHITE : COLOR_BROWN;
// }
// }
// void setFieldsCV() {
// txtData[TXT_CV].backgnd = enterCVdata ? COLOR_WHITE : COLOR_YELLOW;
// txtData[TXT_CV_VAL].backgnd = enterCVdata ? COLOR_YELLOW : COLOR_WHITE;
// keybData[KEYB_CV].idTextbox = enterCVdata ? TXT_CV_VAL : TXT_CV;
// snprintf(keybCvBuf, ADDR_LNG + 1, "%d", CVaddress);
// if (CVdata > 255) {
// keybCvValBuf[0] = '\0';
// CVdata = 0;
// txtData[TXT_CV_VAL].backgnd = COLOR_PINK;
// }
// else
// snprintf(keybCvValBuf, IP_LNG + 1, "%d", CVdata);
// }
// void showFieldsCV() {
// uint16_t n;
// newEvent(OBJ_TXT, TXT_CV, EVNT_DRAW);
// newEvent(OBJ_TXT, TXT_CV_VAL, EVNT_DRAW);
// for (n = 0; n < 8; n++)
// newEvent(OBJ_BUTTON, BUT_CV_0 + n, EVNT_DRAW);
// }
// void setFieldsLNCV() {
// txtData[TXT_LNCV_ART].backgnd = (optLNCV == LNCV_ART) ? COLOR_YELLOW : COLOR_WHITE;
// txtData[TXT_LNCV_MOD].backgnd = (optLNCV == LNCV_MOD) ? COLOR_YELLOW : COLOR_WHITE;
// txtData[TXT_LNCV_ADR].backgnd = (optLNCV == LNCV_ADR) ? COLOR_YELLOW : COLOR_WHITE;
// txtData[TXT_LNCV_VAL].backgnd = (optLNCV == LNCV_VAL) ? COLOR_YELLOW : COLOR_WHITE;
// switch (optLNCV) {
// case LNCV_ART:
// keybData[KEYB_LNCV].idTextbox = TXT_LNCV_ART;
// break;
// case LNCV_MOD:
// keybData[KEYB_LNCV].idTextbox = TXT_LNCV_MOD;
// break;
// case LNCV_ADR:
// keybData[KEYB_LNCV].idTextbox = TXT_LNCV_ADR;
// break;
// case LNCV_VAL:
// keybData[KEYB_LNCV].idTextbox = TXT_LNCV_VAL;
// break;
// }
// snprintf(keybLncvArtBuf, PORT_LNG + 1, "%d", artNum);
// snprintf(keybLncvModBuf, PORT_LNG + 1, "%d", modNum);
// snprintf(keybLncvAdrBuf, PORT_LNG + 1, "%d", numLNCV);
// snprintf(keybLncvValBuf, PORT_LNG + 1, "%d", valLNCV);
// }
// void showFieldsLNCV() {
// newEvent(OBJ_TXT, TXT_LNCV_ART, EVNT_DRAW);
// newEvent(OBJ_TXT, TXT_LNCV_MOD, EVNT_DRAW);
// newEvent(OBJ_TXT, TXT_LNCV_ADR, EVNT_DRAW);
// newEvent(OBJ_TXT, TXT_LNCV_VAL, EVNT_DRAW);
// }
// void setStatusCV() {
// uint16_t num;
// char buf[MAX_LABEL_LNG];
// num = 0;
// if (CVdata > 255) {
// num = LBL_CV_ERROR;
// txtData[TXT_CV_STATUS].color = COLOR_RED;
// }
// else {
// txtData[TXT_CV_STATUS].color = COLOR_BLUE;
// switch (CVaddress) {
// case 1:
// case 17:
// case 18:
// case 19:
// num = LBL_CV_ADDR;
// break;
// case 2:
// num = LBL_CV_SPD_L;
// break;
// case 3:
// num = LBL_CV_ACC;
// break;
// case 4:
// num = LBL_CV_DEC;
// break;
// case 5:
// num = LBL_CV_SPD_H;
// break;
// case 6:
// num = LBL_CV_SPD_M;
// break;
// case 541:
// case 29:
// num = LBL_CV_CFG;
// break;
// case 520:
// case 8:
// switch (CVdata) { // imprime el fabricante conocido
// case 13:
// sprintf(buf, "DIY");
// break;
// case 74:
// sprintf(buf, "PpP");
// break;
// case 42:
// sprintf(buf, "Digikeijs");
// break;
// case 151:
// sprintf(buf, "ESU");
// break;
// case 145:
// sprintf(buf, "Zimo");
// break;
// case 99:
// sprintf(buf, "Lenz");
// break;
// case 97:
// sprintf(buf, "D&H");
// break;
// case 157:
// sprintf(buf, "Kuehn");
// break;
// case 62:
// sprintf(buf, "Tams");
// break;
// case 85:
// sprintf(buf, "Uhlenbrock");
// break;
// case 134:
// sprintf(buf, "Lais");
// break;
// case 129:
// sprintf(buf, "Digitrax");
// break;
// case 161:
// sprintf(buf, "Roco");
// break;
// case 109:
// sprintf(buf, "Viessmann");
// break;
// case 78:
// sprintf(buf, "Train-O-matic");
// break;
// case 117:
// sprintf(buf, "CT Elektronik");
// break;
// default:
// num = LBL_CV_MAN;
// break;
// }
// break;
// default:
// num = LBL_MENU_CV;
// break;
// }
// }
// if (num > 0)
// getLabelTxt(num, buf);
// snprintf(cvStatusBuf, PWD_LNG + 1, "%s", buf);
// }
// void setTextSpeedo() {
// uint8_t index;
// char scaleName[5];
// switch (speedoScale) {
// case 87:
// index = LBL_SCALE_H0;
// break;
// case 160:
// index = LBL_SCALE_N;
// break;
// case 120:
// index = LBL_SCALE_TT;
// break;
// case 220:
// index = LBL_SCALE_Z;
// break;
// case 45:
// index = LBL_SCALE_0;
// break;
// default:
// index = 0;
// }
// scaleName[0] = '\0';
// if (index > 0)
// getLabelTxt(index, scaleName);
// snprintf(spdScaleBuf, NAME_LNG + 1, "%s 1:%d", scaleName, speedoScale);
// snprintf(spdSelScaleBuf, NAME_LNG + 1, "%s 1:", scaleName);
// snprintf(spdSelScaleNumBuf, ADDR_LNG + 1, "%d", speedoScale);
// }
// void setScaleSpeedo(uint16_t value) {
// speedoScale = value;
// setTextSpeedo();
// drawObject(OBJ_TXT, TXT_EDIT_SCALE);
// drawObject(OBJ_TXT, TXT_NUM_SCALE);
// }