Ajout PacoMouse

This commit is contained in:
Serge NOEL
2026-02-10 13:12:27 +01:00
parent c3176e8d79
commit 4fdb0ead68
89 changed files with 21747 additions and 0 deletions

86
PacoMouseCYD/README.md Normal file
View File

@@ -0,0 +1,86 @@
# PacoMouseCYD
Firmware for a WiFi throttle to control locomotives and accessories using a [Cheap Yellow Display](https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display/) 2432S028R.
![FirstVersion](images/PacoMouseCYD.png)
* Protocols: Z21, Xpressnet LAN, Loconet over TCP/IP (LBserver & Binary) and ECoS
* Control of Locomotives
* Rotary encoder for loco speed and direction
* Function icons
* Color image of the locomotive from the SD.
* Extra: Locomotive editor in the SD from the web browser
* Loconet: Command station detection for the correct control of functions F9 to F28.
* Shunting mode for precise stopping.
* Steam locomotive driving simulator
* Control of accesories with 2, 3 or 4 aspects.
* Multiple panels of accessories.
* CV and LNCV programming
* Identify the name of the most common manufacturers when reading the CV8.
* Locking of some features for guest or club use.
* Manually measurement of train speed
* Station Run: Game for children
* WiFi Analyzer
* Menus in different languages:
![Lang](images/languages.png)
---
## Videos
[![PacoMouseRun](https://img.youtube.com/vi/YSfBQpVUhg8/0.jpg)](https://www.youtube.com/watch?v=YSfBQpVUhg8)
[![PacoMouseAutomation](https://img.youtube.com/vi/auRIvvbzx6Q/0.jpg)](https://www.youtube.com/watch?v=auRIvvbzx6Q)
---
## Documentation
- https://usuaris.tinet.cat/fmco/
- https://fmcopaco.github.io/
- Read the [manual](doc/PacoMouseCYD_manual_v0.9.pdf) in the doc directory
- Step-by-step assembly of PacoMouseCYD by [Isaac](https://www.iguadix.es/content/pacomouse-cyd)
---
## Schematics
![Sch](images/schematics.png)
The CYD (Cheap Yellow Display) has the following features:
* ESP32 (With Wifi and Bluetooth)
* 320 x 240 TFT Display (2.8" ILI9341)
* Touch Screen (Resistive XPT2046)
* USB for powering and programming
* SD Card Slot (max. 32Gb FAT32), LED and some additional pins broken out in JST 1.25 connectors.
Just add a rotary encoder type EC-11 or KY-040 with a pushbutton, a battery and its charger to have your **PacoMouseCYD** wireless throttle.
---
## Copyright
Copyright (c) 2025-2026 Paco Cañada, [The Pows](https://usuaris.tinet.cat/fmco/)
All rights reserved.
---
## License
Proprietary.
Sources are only provided to compile and upload to the device.
Modifiyng source code or forking/publishing this project ist not allowed.
Commercial use is forbidden.
---
## Used Libraries
* TFT_eSPI (FreeBSD)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,18 @@
Nombre
Panel 0
Panel 1
Panel 2
Panel 3
Panel 4
Panel 5
Panel 6
Panel 7
Panel 8
Panel 9
Panel 10
Panel 11
Panel 12
Panel 13
Panel 14
Panel 15
1 Nombre
2 Panel 0
3 Panel 1
4 Panel 2
5 Panel 3
6 Panel 4
7 Panel 5
8 Panel 6
9 Panel 7
10 Panel 8
11 Panel 9
12 Panel 10
13 Panel 11
14 Panel 12
15 Panel 13
16 Panel 14
17 Panel 15

BIN
PacoMouseCYD/SD/image/0.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/1.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/2.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/3.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/4.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/5.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/6.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/7.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/8.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
PacoMouseCYD/SD/image/9.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
Ce 6/8 III;1012;75;3;6;16;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 Ce 6/8 III 1012 75 3 6 16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
Talgo;1006;160;3;6;17;9;10;14;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 Talgo 1006 160 3 6 17 9 10 14 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
242F-2209;1008;100;3;6;17;9;10;15;7;8;20;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 242F-2209 1008 100 3 6 17 9 10 15 7 8 20 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
252 Altaria;1009;140;3;6;17;9;10;15;7;8;20;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 252 Altaria 1009 140 3 6 17 9 10 15 7 8 20 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
Platanito;1004;120;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 Platanito 1004 120 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
319-309 Taxi;1003;120;3;6;17;10;9;4;5;8;14;15;20;12;15;16;18;19;2;13;11;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 319-309 Taxi 1003 120 3 6 17 10 9 4 5 8 14 15 20 12 15 16 18 19 2 13 11 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
321 025-9 Taxi;1017;120;3;6;16;17;14;5;33;32;30;6;16;36;26;26;23;28;29;6;6;9;19;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 321 025-9 Taxi 1017 120 3 6 16 17 14 5 33 32 30 6 16 36 26 26 23 28 29 6 6 9 19 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
Vapor 003;1001;80;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 Vapor 003 1001 80 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
269 Cercanias;1000;140;3;2;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 269 Cercanias 1000 140 3 2 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
Camello Cercan.;1005;140;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 Camello Cercan. 1005 140 3 4 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
Mikado;1002;80;3;2;4;6;8;10;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 Mikado 1002 80 3 2 4 6 8 10 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

View File

@@ -0,0 +1,2 @@
Name;Image;Vmax;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;F25;F26;F27;F28
Villacanas;1007;85;3;4;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1
1 Name Image Vmax F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28
2 Villacanas 1007 85 3 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Binary file not shown.

View File

@@ -0,0 +1,44 @@
// USER DEFINED SETTINGS
#define USER_SETUP_INFO "User_Setup CYD 2.8"
#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
#define TFT_HEIGHT 320 // ST7789 240 x 320
#define TFT_INVERSION_ON
#define TFT_BL 21 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
#define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
#define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
#define USE_HSPI_PORT

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

View File

@@ -0,0 +1,383 @@
// USER DEFINED SETTINGS
// Set driver type, fonts to be loaded, pins used and SPI control method etc
//
// See the User_Setup_Select.h file if you wish to be able to define multiple
// setups and then easily select which setup file is used by the compiler.
//
// If this file is edited correctly then all the library example sketches should
// run without the need to make any more changes for a particular hardware setup!
// Note that some sketches are designed for a particular TFT pixel width/height
// User defined information reported by "Read_User_Setup" test & diagnostics example
#define USER_SETUP_INFO "User_Setup CYD 2.4"
// Define to disable all #warnings in library (can be put in User_Setup_Select.h)
//#define DISABLE_ALL_LIBRARY_WARNINGS
// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################
// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32
// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT
// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTB_DATA_BUS
// Tell the library to use parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
//#defined TFT_PARALLEL_16_BIT // **** 16 bit parallel ONLY for RP2040 processor ****
// Display type - only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER // Generic driver for common displays
#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
//#define ST7735_DRIVER // Define additional parameters below for this display
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER
// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only
// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
// #define M5STACK
// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH 80
// #define TFT_WIDTH 128
// #define TFT_WIDTH 172 // ST7789 172 x 320
#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
// #define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
#define TFT_HEIGHT 320 // ST7789 240 x 320
// #define TFT_HEIGHT 240 // GC9A01 240 x 240
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.
#define TFT_INVERSION_ON
// #define TFT_INVERSION_OFF
// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################
// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
#define TFT_BL 27 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED to NodeMCU pin VIN (or 5V, see below)
// Display SCK to NodeMCU pin D5
// Display SDI/MOSI to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET to NodeMCU pin D4 (or RST, see below)
// Display CS to NodeMCU pin D8 (or GND, see below)
// Display GND to NodeMCU pin GND (0V)
// Display VCC to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS PIN_D8 // Chip select control pin D8
//#define TFT_DC PIN_D3 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line when chip select is high!
// Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller
// cannot be connected as well to the same SPI signals.
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined
//#define TFT_SPI_OVERLAP
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS PIN_D3
//#define TFT_DC PIN_D5 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins
//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on.
//#define TFT_SCLK 14
//#define TFT_CS 5 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 22 // LED back-light
#define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 14 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 32 // LED back-light (required for M5Stack)
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!
// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST 32 // Reset pin, toggles on startup
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD 2 // Read strobe control pin
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
//#define TFT_D1 13 // so a single register write sets/clears all bits.
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
//#define TFT_D3 25 // TFT screen update performance.
//#define TFT_D4 17
//#define TFT_D5 16
//#define TFT_D6 27
//#define TFT_D7 14
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS D5 // Chip select control pin to TFT CS
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################
// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################
// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface.
//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface
// For RP2040 processor and 8 or 16 bit parallel displays:
// The parallel interface write cycle period is derived from a division of the CPU clock
// speed so scales with the processor clock. This means that the divider ratio may need
// to be increased when overclocking. I may also need to be adjusted dependant on the
// display controller type (ILI94341, HX8357C etc). If RP2040_PIO_CLK_DIV is not defined
// the library will set default values which may not suit your display.
// The display controller data sheet will specify the minimum write cycle period. The
// controllers often work reliably for shorter periods, however if the period is too short
// the display may not initialise or graphics will become corrupted.
// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV))
//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock
// For the RP2040 processor define the SPI port channel used (default 0 if undefined)
//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used
// For the STM32 processor define the SPI port channel used (default 1 if undefined)
//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.
// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
// #define SPI_FREQUENCY 10000000
// #define SPI_FREQUENCY 20000000
// #define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
#define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// #define SPI_FREQUENCY 80000000
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY 20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY 2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
#define USE_HSPI_PORT
// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect
// #define SUPPORT_TRANSACTIONS

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@@ -0,0 +1,383 @@
// USER DEFINED SETTINGS
// Set driver type, fonts to be loaded, pins used and SPI control method etc
//
// See the User_Setup_Select.h file if you wish to be able to define multiple
// setups and then easily select which setup file is used by the compiler.
//
// If this file is edited correctly then all the library example sketches should
// run without the need to make any more changes for a particular hardware setup!
// Note that some sketches are designed for a particular TFT pixel width/height
// User defined information reported by "Read_User_Setup" test & diagnostics example
#define USER_SETUP_INFO "User_Setup CYD 2.8"
// Define to disable all #warnings in library (can be put in User_Setup_Select.h)
//#define DISABLE_ALL_LIBRARY_WARNINGS
// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################
// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32
// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT
// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTB_DATA_BUS
// Tell the library to use parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
//#defined TFT_PARALLEL_16_BIT // **** 16 bit parallel ONLY for RP2040 processor ****
// Display type - only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER // Generic driver for common displays
#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
//#define ST7735_DRIVER // Define additional parameters below for this display
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER
// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only
// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
// #define M5STACK
// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH 80
// #define TFT_WIDTH 128
// #define TFT_WIDTH 172 // ST7789 172 x 320
#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
// #define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
#define TFT_HEIGHT 320 // ST7789 240 x 320
// #define TFT_HEIGHT 240 // GC9A01 240 x 240
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.
#define TFT_INVERSION_ON
// #define TFT_INVERSION_OFF
// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################
// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
#define TFT_BL 21 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED to NodeMCU pin VIN (or 5V, see below)
// Display SCK to NodeMCU pin D5
// Display SDI/MOSI to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET to NodeMCU pin D4 (or RST, see below)
// Display CS to NodeMCU pin D8 (or GND, see below)
// Display GND to NodeMCU pin GND (0V)
// Display VCC to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS PIN_D8 // Chip select control pin D8
//#define TFT_DC PIN_D3 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line when chip select is high!
// Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller
// cannot be connected as well to the same SPI signals.
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined
//#define TFT_SPI_OVERLAP
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS PIN_D3
//#define TFT_DC PIN_D5 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins
//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on.
//#define TFT_SCLK 14
//#define TFT_CS 5 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 22 // LED back-light
#define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 14 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 32 // LED back-light (required for M5Stack)
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!
// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST 32 // Reset pin, toggles on startup
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD 2 // Read strobe control pin
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
//#define TFT_D1 13 // so a single register write sets/clears all bits.
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
//#define TFT_D3 25 // TFT screen update performance.
//#define TFT_D4 17
//#define TFT_D5 16
//#define TFT_D6 27
//#define TFT_D7 14
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS D5 // Chip select control pin to TFT CS
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################
// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################
// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface.
//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface
// For RP2040 processor and 8 or 16 bit parallel displays:
// The parallel interface write cycle period is derived from a division of the CPU clock
// speed so scales with the processor clock. This means that the divider ratio may need
// to be increased when overclocking. I may also need to be adjusted dependant on the
// display controller type (ILI94341, HX8357C etc). If RP2040_PIO_CLK_DIV is not defined
// the library will set default values which may not suit your display.
// The display controller data sheet will specify the minimum write cycle period. The
// controllers often work reliably for shorter periods, however if the period is too short
// the display may not initialise or graphics will become corrupted.
// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV))
//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock
// For the RP2040 processor define the SPI port channel used (default 0 if undefined)
//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used
// For the STM32 processor define the SPI port channel used (default 1 if undefined)
//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.
// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
// #define SPI_FREQUENCY 10000000
// #define SPI_FREQUENCY 20000000
// #define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
#define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// #define SPI_FREQUENCY 80000000
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY 20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY 2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
#define USE_HSPI_PORT
// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect
// #define SUPPORT_TRANSACTIONS

View File

@@ -0,0 +1,383 @@
// USER DEFINED SETTINGS
// Set driver type, fonts to be loaded, pins used and SPI control method etc
//
// See the User_Setup_Select.h file if you wish to be able to define multiple
// setups and then easily select which setup file is used by the compiler.
//
// If this file is edited correctly then all the library example sketches should
// run without the need to make any more changes for a particular hardware setup!
// Note that some sketches are designed for a particular TFT pixel width/height
// User defined information reported by "Read_User_Setup" test & diagnostics example
#define USER_SETUP_INFO "User_Setup CYD 3.2"
// Define to disable all #warnings in library (can be put in User_Setup_Select.h)
//#define DISABLE_ALL_LIBRARY_WARNINGS
// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################
// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32
// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT
// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTB_DATA_BUS
// Tell the library to use parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
//#defined TFT_PARALLEL_16_BIT // **** 16 bit parallel ONLY for RP2040 processor ****
// Display type - only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER // Generic driver for common displays
#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
//#define ST7735_DRIVER // Define additional parameters below for this display
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER
// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only
// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
// #define M5STACK
// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH 80
// #define TFT_WIDTH 128
// #define TFT_WIDTH 172 // ST7789 172 x 320
#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
// #define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
#define TFT_HEIGHT 320 // ST7789 240 x 320
// #define TFT_HEIGHT 240 // GC9A01 240 x 240
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.
#define TFT_INVERSION_ON
// #define TFT_INVERSION_OFF
// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################
// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
#define TFT_BL 27 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED to NodeMCU pin VIN (or 5V, see below)
// Display SCK to NodeMCU pin D5
// Display SDI/MOSI to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET to NodeMCU pin D4 (or RST, see below)
// Display CS to NodeMCU pin D8 (or GND, see below)
// Display GND to NodeMCU pin GND (0V)
// Display VCC to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS PIN_D8 // Chip select control pin D8
//#define TFT_DC PIN_D3 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line when chip select is high!
// Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller
// cannot be connected as well to the same SPI signals.
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined
//#define TFT_SPI_OVERLAP
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS PIN_D3
//#define TFT_DC PIN_D5 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins
//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on.
//#define TFT_SCLK 14
//#define TFT_CS 5 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 22 // LED back-light
#define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 14 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 32 // LED back-light (required for M5Stack)
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!
// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST 32 // Reset pin, toggles on startup
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD 2 // Read strobe control pin
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
//#define TFT_D1 13 // so a single register write sets/clears all bits.
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
//#define TFT_D3 25 // TFT screen update performance.
//#define TFT_D4 17
//#define TFT_D5 16
//#define TFT_D6 27
//#define TFT_D7 14
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS D5 // Chip select control pin to TFT CS
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################
// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################
// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface.
//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface
// For RP2040 processor and 8 or 16 bit parallel displays:
// The parallel interface write cycle period is derived from a division of the CPU clock
// speed so scales with the processor clock. This means that the divider ratio may need
// to be increased when overclocking. I may also need to be adjusted dependant on the
// display controller type (ILI94341, HX8357C etc). If RP2040_PIO_CLK_DIV is not defined
// the library will set default values which may not suit your display.
// The display controller data sheet will specify the minimum write cycle period. The
// controllers often work reliably for shorter periods, however if the period is too short
// the display may not initialise or graphics will become corrupted.
// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV))
//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock
// For the RP2040 processor define the SPI port channel used (default 0 if undefined)
//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used
// For the STM32 processor define the SPI port channel used (default 1 if undefined)
//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.
// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
// #define SPI_FREQUENCY 10000000
// #define SPI_FREQUENCY 20000000
// #define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
#define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// #define SPI_FREQUENCY 80000000
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY 20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY 2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
#define USE_HSPI_PORT
// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect
// #define SUPPORT_TRANSACTIONS

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -0,0 +1,386 @@
// USER DEFINED SETTINGS
// Set driver type, fonts to be loaded, pins used and SPI control method etc
//
// See the User_Setup_Select.h file if you wish to be able to define multiple
// setups and then easily select which setup file is used by the compiler.
//
// If this file is edited correctly then all the library example sketches should
// run without the need to make any more changes for a particular hardware setup!
// Note that some sketches are designed for a particular TFT pixel width/height
// User defined information reported by "Read_User_Setup" test & diagnostics example
#define USER_SETUP_INFO "User_Setup USER DEFINED - ESP32-035"
// Define to disable all #warnings in library (can be put in User_Setup_Select.h)
//#define DISABLE_ALL_LIBRARY_WARNINGS
// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################
// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32
// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT
// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTB_DATA_BUS
// Tell the library to use parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
//#defined TFT_PARALLEL_16_BIT // **** 16 bit parallel ONLY for RP2040 processor ****
// Display type - only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER // Generic driver for common displays
//#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
//#define ST7735_DRIVER // Define additional parameters below for this display
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER
// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only
// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
// #define M5STACK
// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH 80
// #define TFT_WIDTH 128
// #define TFT_WIDTH 172 // ST7789 172 x 320
// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
#define TFT_WIDTH 320
// #define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
// #define TFT_HEIGHT 320 // ST7789 240 x 320
#define TFT_HEIGHT 480
// #define TFT_HEIGHT 240 // GC9A01 240 x 240
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.
// #define TFT_INVERSION_ON
#define TFT_INVERSION_OFF
// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################
// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
#define TFT_BL 27 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED to NodeMCU pin VIN (or 5V, see below)
// Display SCK to NodeMCU pin D5
// Display SDI/MOSI to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET to NodeMCU pin D4 (or RST, see below)
// Display CS to NodeMCU pin D8 (or GND, see below)
// Display GND to NodeMCU pin GND (0V)
// Display VCC to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS PIN_D8 // Chip select control pin D8
//#define TFT_DC PIN_D3 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line when chip select is high!
// Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller
// cannot be connected as well to the same SPI signals.
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined
//#define TFT_SPI_OVERLAP
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS PIN_D3
//#define TFT_DC PIN_D5 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins
//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on.
//#define TFT_SCLK 14
//#define TFT_CS 5 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 22 // LED back-light
#define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 14 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 32 // LED back-light (required for M5Stack)
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!
// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST 32 // Reset pin, toggles on startup
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD 2 // Read strobe control pin
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
//#define TFT_D1 13 // so a single register write sets/clears all bits.
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
//#define TFT_D3 25 // TFT screen update performance.
//#define TFT_D4 17
//#define TFT_D5 16
//#define TFT_D6 27
//#define TFT_D7 14
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS D5 // Chip select control pin to TFT CS
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################
// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################
// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface.
//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface
// For RP2040 processor and 8 or 16 bit parallel displays:
// The parallel interface write cycle period is derived from a division of the CPU clock
// speed so scales with the processor clock. This means that the divider ratio may need
// to be increased when overclocking. I may also need to be adjusted dependant on the
// display controller type (ILI94341, HX8357C etc). If RP2040_PIO_CLK_DIV is not defined
// the library will set default values which may not suit your display.
// The display controller data sheet will specify the minimum write cycle period. The
// controllers often work reliably for shorter periods, however if the period is too short
// the display may not initialise or graphics will become corrupted.
// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV))
//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock
// For the RP2040 processor define the SPI port channel used (default 0 if undefined)
//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used
// For the STM32 processor define the SPI port channel used (default 1 if undefined)
//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.
// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
// #define SPI_FREQUENCY 10000000
// #define SPI_FREQUENCY 20000000
// #define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
#define SPI_FREQUENCY 65000000
// #define SPI_FREQUENCY 80000000
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY 20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY 2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
#define USE_HSPI_PORT
// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect
// #define SUPPORT_TRANSACTIONS

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -0,0 +1,383 @@
// USER DEFINED SETTINGS
// Set driver type, fonts to be loaded, pins used and SPI control method etc
//
// See the User_Setup_Select.h file if you wish to be able to define multiple
// setups and then easily select which setup file is used by the compiler.
//
// If this file is edited correctly then all the library example sketches should
// run without the need to make any more changes for a particular hardware setup!
// Note that some sketches are designed for a particular TFT pixel width/height
// User defined information reported by "Read_User_Setup" test & diagnostics example
#define USER_SETUP_INFO "User_Setup USER_DEFINED - ESP32-32E"
// Define to disable all #warnings in library (can be put in User_Setup_Select.h)
//#define DISABLE_ALL_LIBRARY_WARNINGS
// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################
// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32
// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT
// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTB_DATA_BUS
// Tell the library to use parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
//#defined TFT_PARALLEL_16_BIT // **** 16 bit parallel ONLY for RP2040 processor ****
// Display type - only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER // Generic driver for common displays
#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
//#define ST7735_DRIVER // Define additional parameters below for this display
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER
// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only
// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
// #define M5STACK
// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH 80
// #define TFT_WIDTH 128
// #define TFT_WIDTH 172 // ST7789 172 x 320
#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
// #define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
#define TFT_HEIGHT 320 // ST7789 240 x 320
// #define TFT_HEIGHT 240 // GC9A01 240 x 240
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.
// #define TFT_INVERSION_ON
#define TFT_INVERSION_OFF
// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################
// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
#define TFT_BL 21 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED to NodeMCU pin VIN (or 5V, see below)
// Display SCK to NodeMCU pin D5
// Display SDI/MOSI to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET to NodeMCU pin D4 (or RST, see below)
// Display CS to NodeMCU pin D8 (or GND, see below)
// Display GND to NodeMCU pin GND (0V)
// Display VCC to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS PIN_D8 // Chip select control pin D8
//#define TFT_DC PIN_D3 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line when chip select is high!
// Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller
// cannot be connected as well to the same SPI signals.
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined
//#define TFT_SPI_OVERLAP
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS PIN_D3
//#define TFT_DC PIN_D5 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins
//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on.
//#define TFT_SCLK 14
//#define TFT_CS 5 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 22 // LED back-light
#define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 14 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 32 // LED back-light (required for M5Stack)
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!
// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST 32 // Reset pin, toggles on startup
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD 2 // Read strobe control pin
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
//#define TFT_D1 13 // so a single register write sets/clears all bits.
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
//#define TFT_D3 25 // TFT screen update performance.
//#define TFT_D4 17
//#define TFT_D5 16
//#define TFT_D6 27
//#define TFT_D7 14
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS D5 // Chip select control pin to TFT CS
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################
// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################
// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface.
//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface
// For RP2040 processor and 8 or 16 bit parallel displays:
// The parallel interface write cycle period is derived from a division of the CPU clock
// speed so scales with the processor clock. This means that the divider ratio may need
// to be increased when overclocking. I may also need to be adjusted dependant on the
// display controller type (ILI94341, HX8357C etc). If RP2040_PIO_CLK_DIV is not defined
// the library will set default values which may not suit your display.
// The display controller data sheet will specify the minimum write cycle period. The
// controllers often work reliably for shorter periods, however if the period is too short
// the display may not initialise or graphics will become corrupted.
// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV))
//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock
// For the RP2040 processor define the SPI port channel used (default 0 if undefined)
//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used
// For the STM32 processor define the SPI port channel used (default 1 if undefined)
//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.
// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
// #define SPI_FREQUENCY 10000000
// #define SPI_FREQUENCY 20000000
// #define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
#define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// #define SPI_FREQUENCY 80000000
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY 20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY 2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
#define USE_HSPI_PORT
// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect
// #define SUPPORT_TRANSACTIONS

View File

@@ -0,0 +1,127 @@
# CYD_USER_DEFINED
> You need to modify the User_Setup.h file according to your board and copy it into the TFT_eSPI library folder.
In the program's config.h file, select CYD_USER_DEFINED as the hardware and modify the USER DEFINED HARDWARE section according to your board.
---
> Tiene que modificar el archivo User_Setup.h de acuerdo a su placa y copiarlo en la carpeta de la biblioteca TFT_eSPI.
En el archivo config.h del programa seleccionar CYD_USER_DEFINED como hardware y modifique la seccion USER DEFINED HARDWARE de acuerdo a su placa.
---
> Heu de modificar el fitxer User_Setup.h segons la vostra placa i copiar-lo a la carpeta de la biblioteca TFT_eSPI.
Al fitxer config.h del programa, seleccioneu CYD_USER_DEFINED com a hardware i modifiqueu la secció USER DEFINED HARDWARE segons la vostra placa.
---
> Sie müssen die Datei User_Setup.h entsprechend Ihrer Platine ändern und in den TFT_eSPI-Bibliotheksordner kopieren.
Wählen Sie in der Datei config.h des Programms CYD_USER_DEFINED als Hardware aus und passen Sie den Abschnitt USER DEFINED HARDWARE entsprechend Ihrem Board an.
---
<br>
<br>
# config.h
////////////////////////////////////////////////////////////<br>
// ***** USER OPTIONS *****<br>
////////////////////////////////////////////////////////////
// Seleccione la version hardware del CYD (Cheap Yellow Display) - Select the hardware version of CYD (Cheap Yellow Display): CYD_TFT_28 / CYD_TFT_24 / CYD_TFT_32 / CYD_USER_DEFINED<br>
// Use el archivo User_Setup.h correcto para la libreria TFT_eSPI - Use the correct User_Setup.h file for library TFT_eSPI
#define CYD_HW_VERSION CYD_USER_DEFINED<br>
## Examples
////////////////////////////////////////////////////////////<br>
// ***** USER DEFINED HARDWARE EXAMPLES *****<br>
////////////////////////////////////////////////////////////<br>
### ESP32-32E
---
//================================================================================================ <br>
// CYD 240x320 2.8" - ESP32-32E (using RGB LED pins for encoder connection)<br>
//================================================================================================ <br>
// Seleccione el modo de acceso al chip XPT2046 - Select XPT2046 chip access mode : MODE_SPI / MODE_BITBANG<br>
#define XPT_MODE MODE_BITBANG<br>
// Seleccione rotacion de la pantalla tactil - Select Touchscreen rotation: 0 / 1 / 2 / 3<br>
#define XPT_ROTATION 0<br>
// Touchscreen <br>
#define XPT2046_IRQ 36 // T_IRQ<br>
#define XPT2046_MOSI 32 // T_DIN<br>
#define XPT2046_MISO 39 // T_OUT<br>
#define XPT2046_CLK 25 // T_CLK<br>
#define XPT2046_CS 33 // T_CS<br>
// Seleccione si usa el LED RGB - Select if use the RGB LED: PRESENT / UNUSED<br>
#define USE_RGB_LED UNUSED<br>
//RGB LED Pins<br>
#define RGB_LED_R 22<br>
#define RGB_LED_G 16<br>
#define RGB_LED_B 17<br>
//SD Pins<br>
#define SD_CS 5<br>
// Encoder Pins<br>
#define ENCODER_A 16<br>
#define ENCODER_B 17<br>
#define ENCODER_SW 22 <br>
<br>
### ESP32-035
---
//================================================================================================<br>
// CYD 320x480 3.5" - ESP32-035<br>
//================================================================================================<br>
// Seleccione el modo de acceso al chip XPT2046 - Select XPT2046 chip access mode : MODE_SPI / MODE_BITBANG<br>
#define XPT_MODE MODE_SPI<br>
// Seleccione rotacion de la pantalla tactil - Select Touchscreen rotation: 0 / 1 / 2 / 3<br>
#define XPT_ROTATION 3<br>
// Touchscreen <br>
#define XPT2046_IRQ 36 // T_IRQ<br>
#define XPT2046_MOSI 13 // T_DIN<br>
#define XPT2046_MISO 12 // T_OUT<br>
#define XPT2046_CLK 14 // T_CLK<br>
#define XPT2046_CS 33 // T_CS<br>
// Seleccione si usa el LED RGB - Select if use the RGB LED: PRESENT / UNUSED<br>
#define USE_RGB_LED PRESENT <br>
//RGB LED Pins<br>
#define RGB_LED_R 4<br>
#define RGB_LED_G 17<br>
#define RGB_LED_B 16<br>
//SD Pins<br>
#define SD_CS 5<br>
// Encoder Pins<br>
#define ENCODER_A 22<br>
#define ENCODER_B 21<br>
#define ENCODER_SW 35<br>

View File

@@ -0,0 +1,383 @@
// USER DEFINED SETTINGS
// Set driver type, fonts to be loaded, pins used and SPI control method etc
//
// See the User_Setup_Select.h file if you wish to be able to define multiple
// setups and then easily select which setup file is used by the compiler.
//
// If this file is edited correctly then all the library example sketches should
// run without the need to make any more changes for a particular hardware setup!
// Note that some sketches are designed for a particular TFT pixel width/height
// User defined information reported by "Read_User_Setup" test & diagnostics example
#define USER_SETUP_INFO "User_Setup USER DEFINED"
// Define to disable all #warnings in library (can be put in User_Setup_Select.h)
//#define DISABLE_ALL_LIBRARY_WARNINGS
// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################
// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32
// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT
// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTB_DATA_BUS
// Tell the library to use parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
//#defined TFT_PARALLEL_16_BIT // **** 16 bit parallel ONLY for RP2040 processor ****
// Display type - only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER // Generic driver for common displays
#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
//#define ST7735_DRIVER // Define additional parameters below for this display
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1351_DRIVER
//#define SSD1963_480_DRIVER
//#define SSD1963_800_DRIVER
//#define SSD1963_800ALT_DRIVER
//#define ILI9225_DRIVER
//#define GC9A01_DRIVER
// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only
// For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
// #define M5STACK
// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation
// #define TFT_WIDTH 80
// #define TFT_WIDTH 128
// #define TFT_WIDTH 172 // ST7789 172 x 320
#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
// #define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
#define TFT_HEIGHT 320 // ST7789 240 x 320
// #define TFT_HEIGHT 240 // GC9A01 240 x 240
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or stray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
// #define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.
#define TFT_INVERSION_ON
// #define TFT_INVERSION_OFF
// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################
// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
#define TFT_BL 21 // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED to NodeMCU pin VIN (or 5V, see below)
// Display SCK to NodeMCU pin D5
// Display SDI/MOSI to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET to NodeMCU pin D4 (or RST, see below)
// Display CS to NodeMCU pin D8 (or GND, see below)
// Display GND to NodeMCU pin GND (0V)
// Display VCC to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
//#define TFT_CS PIN_D8 // Chip select control pin D8
//#define TFT_DC PIN_D3 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line when chip select is high!
// Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller
// cannot be connected as well to the same SPI signals.
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined
//#define TFT_SPI_OVERLAP
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS PIN_D3
//#define TFT_DC PIN_D5 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins
//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on.
//#define TFT_SCLK 14
//#define TFT_CS 5 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 22 // LED back-light
#define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 14 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 32 // LED back-light (required for M5Stack)
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!
// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST 32 // Reset pin, toggles on startup
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD 2 // Read strobe control pin
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
//#define TFT_D1 13 // so a single register write sets/clears all bits.
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
//#define TFT_D3 25 // TFT screen update performance.
//#define TFT_D4 17
//#define TFT_D5 16
//#define TFT_D6 27
//#define TFT_D7 14
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS D5 // Chip select control pin to TFT CS
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################
// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################
// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface.
//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface
// For RP2040 processor and 8 or 16 bit parallel displays:
// The parallel interface write cycle period is derived from a division of the CPU clock
// speed so scales with the processor clock. This means that the divider ratio may need
// to be increased when overclocking. I may also need to be adjusted dependant on the
// display controller type (ILI94341, HX8357C etc). If RP2040_PIO_CLK_DIV is not defined
// the library will set default values which may not suit your display.
// The display controller data sheet will specify the minimum write cycle period. The
// controllers often work reliably for shorter periods, however if the period is too short
// the display may not initialise or graphics will become corrupted.
// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV))
//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock
//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock
// For the RP2040 processor define the SPI port channel used (default 0 if undefined)
//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used
// For the STM32 processor define the SPI port channel used (default 1 if undefined)
//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.
// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
// #define SPI_FREQUENCY 10000000
// #define SPI_FREQUENCY 20000000
// #define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
#define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// #define SPI_FREQUENCY 80000000
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY 20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY 2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
#define USE_HSPI_PORT
// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect
// #define SUPPORT_TRANSACTIONS

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

9
PacoMouseCYD/license.txt Normal file
View File

@@ -0,0 +1,9 @@
## Copyright
Copyright (c) 2025-2026 Paco Cañada, [The Pows](https://usuaris.tinet.cat/fmco/)
All rights reserved.
## License
Proprietary.
Sources are only provided to compile and upload to the device.
Modifiyng source code or forking/publishing this project ist not allowed.
Commercial use is forbidden.

View File

@@ -0,0 +1,164 @@
/* 7pt font https://rop.nl/truetype2gfx/ */
const uint8_t FreeSans7pt7bBitmaps[] = {
0x00, 0xFF, 0x40, 0xB6, 0xD0, 0x12, 0x28, 0xD3, 0xF2, 0x44, 0xBF, 0x94,
0x48, 0x90, 0x10, 0x71, 0x52, 0x95, 0x0E, 0x07, 0x09, 0xD2, 0xA5, 0xF0,
0x81, 0x00, 0x00, 0x87, 0x88, 0x89, 0x08, 0x90, 0x72, 0x00, 0x00, 0x04,
0xE0, 0x91, 0x09, 0x11, 0x0E, 0x30, 0x48, 0x48, 0x48, 0x30, 0x52, 0x9A,
0x8C, 0x8E, 0xFA, 0xF0, 0x29, 0x49, 0x24, 0x99, 0x22, 0x91, 0x24, 0x9A,
0x49, 0x48, 0x4F, 0x4A, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xD8, 0xE0,
0xC0, 0x10, 0x22, 0x04, 0x44, 0x88, 0x33, 0x68, 0xE1, 0x86, 0x18, 0x61,
0x89, 0xE0, 0x13, 0xF1, 0x11, 0x11, 0x11, 0x18, 0xCD, 0x0A, 0x10, 0x61,
0x8C, 0x30, 0x40, 0xFC, 0x38, 0xC9, 0x18, 0x31, 0xC3, 0x80, 0xE1, 0x46,
0xF8, 0x04, 0x18, 0x71, 0xE2, 0xC9, 0xB3, 0x7F, 0x0C, 0x18, 0x7E, 0x81,
0x02, 0x07, 0xC8, 0xC0, 0x81, 0x46, 0xF8, 0x31, 0x28, 0x60, 0xFB, 0x38,
0x61, 0x8D, 0xE0, 0xFC, 0x10, 0x82, 0x10, 0xC2, 0x08, 0x61, 0x00, 0x18,
0xC9, 0x1A, 0x33, 0xCD, 0x90, 0xA1, 0x46, 0xF8, 0x73, 0x68, 0xE1, 0x8F,
0x72, 0x43, 0x8B, 0xE0, 0xC0, 0x0C, 0x40, 0x0D, 0xC0, 0x00, 0x33, 0x30,
0xE0, 0xE0, 0x40, 0xFC, 0x00, 0x00, 0x03, 0x03, 0x03, 0x1D, 0xC8, 0x00,
0x7B, 0x38, 0x43, 0x18, 0x43, 0x00, 0x00, 0xC0, 0x0F, 0xC0, 0xC3, 0x08,
0x04, 0x8E, 0xB4, 0xCC, 0xA4, 0x47, 0x22, 0x29, 0x12, 0x4E, 0xE3, 0x00,
0x0E, 0x00, 0x1F, 0x00, 0x0C, 0x0E, 0x05, 0x02, 0xC2, 0x21, 0x11, 0xFC,
0x82, 0x41, 0x60, 0xC0, 0xFC, 0x86, 0x82, 0x82, 0xFC, 0x86, 0x82, 0x83,
0x82, 0xFC, 0x3E, 0x43, 0xC1, 0x80, 0x80, 0x80, 0x81, 0x81, 0x43, 0x7E,
0xFC, 0xC6, 0xC3, 0xC1, 0xC1, 0xC1, 0xC1, 0xC3, 0xC6, 0xFC, 0xFF, 0x83,
0x06, 0x0F, 0xF8, 0x30, 0x60, 0xC1, 0xFC, 0xFF, 0x83, 0x06, 0x0F, 0xD8,
0x30, 0x60, 0xC1, 0x80, 0x3E, 0x21, 0xB0, 0x50, 0x08, 0x04, 0x3E, 0x03,
0x03, 0x41, 0x9F, 0x40, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81,
0x81, 0x81, 0xFF, 0xFF, 0xF0, 0x04, 0x10, 0x41, 0x04, 0x10, 0x71, 0xCD,
0xE0, 0x82, 0x84, 0x88, 0x90, 0xB0, 0xD8, 0x8C, 0x84, 0x86, 0x83, 0x82,
0x08, 0x20, 0x82, 0x08, 0x20, 0x83, 0xF0, 0xC1, 0xE0, 0xE8, 0x74, 0x7A,
0x2C, 0x96, 0x5B, 0x29, 0x8C, 0xC6, 0x40, 0xC1, 0xC1, 0xA1, 0xB1, 0x91,
0x89, 0x8D, 0x85, 0x87, 0x83, 0x3E, 0x21, 0xA0, 0x70, 0x18, 0x0C, 0x06,
0x03, 0x03, 0x43, 0x1F, 0x00, 0xFD, 0x8F, 0x0E, 0x1C, 0x7F, 0xB0, 0x60,
0xC1, 0x80, 0x3E, 0x21, 0xA0, 0x70, 0x18, 0x0C, 0x06, 0x03, 0x0B, 0x43,
0x1F, 0x80, 0x20, 0xFE, 0xC3, 0xC1, 0xC1, 0xC6, 0xFE, 0xC3, 0xC3, 0xC3,
0xC1, 0x7C, 0xC6, 0x82, 0xC0, 0x70, 0x1E, 0x02, 0x83, 0xC2, 0x7C, 0xFF,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0xC3, 0x7E, 0x41, 0xA0, 0x98, 0x44, 0x62,
0x21, 0x90, 0x58, 0x28, 0x1C, 0x06, 0x00, 0xC2, 0x1A, 0x38, 0x91, 0x44,
0xCA, 0x26, 0x53, 0x14, 0x50, 0xA2, 0x85, 0x14, 0x38, 0xE0, 0x82, 0x00,
0x41, 0x11, 0x8C, 0x82, 0x80, 0xC0, 0xE0, 0x50, 0x44, 0x63, 0x20, 0xC0,
0xC1, 0xB0, 0x88, 0xC6, 0xC1, 0x40, 0x60, 0x20, 0x10, 0x08, 0x04, 0x00,
0x7F, 0x03, 0x06, 0x04, 0x08, 0x18, 0x30, 0x60, 0x40, 0xFF, 0xEA, 0xAA,
0xAA, 0xC0, 0x88, 0x04, 0x40, 0x22, 0x01, 0xE4, 0x92, 0x49, 0x24, 0x9E,
0x23, 0x15, 0x29, 0x00, 0x00, 0x44, 0xFA, 0x20, 0xBE, 0x8A, 0x2F, 0xC0,
0x82, 0x08, 0x3E, 0x8E, 0x18, 0x61, 0x8F, 0xE0, 0x7C, 0x89, 0x06, 0x0C,
0x08, 0x9F, 0x00, 0x02, 0x04, 0x0B, 0xD4, 0x78, 0x70, 0xE1, 0x46, 0xF4,
0x7A, 0x38, 0x7F, 0x82, 0x37, 0x80, 0x34, 0x4F, 0x44, 0x44, 0x44, 0x7A,
0x8F, 0x1E, 0x1C, 0x68, 0xDE, 0x83, 0x44, 0x70, 0x82, 0x08, 0x3E, 0x8E,
0x18, 0x61, 0x86, 0x10, 0x9F, 0xC0, 0x41, 0x55, 0x55, 0xC0, 0x82, 0x08,
0x26, 0xB3, 0x8F, 0x24, 0x8A, 0x30, 0xFF, 0xC0, 0xFF, 0xC6, 0x62, 0x31,
0x18, 0x8C, 0x46, 0x22, 0xFA, 0x38, 0x61, 0x86, 0x18, 0x40, 0x7C, 0x8D,
0x0E, 0x14, 0x28, 0xCF, 0x00, 0xFA, 0x38, 0x61, 0x86, 0x3F, 0xA0, 0x82,
0x00, 0x7E, 0x8F, 0x0E, 0x1C, 0x28, 0xDE, 0x81, 0x02, 0x04, 0xFA, 0x49,
0x20, 0x7D, 0x14, 0x0E, 0x05, 0x17, 0xC0, 0x4B, 0xA4, 0x92, 0x60, 0x8E,
0x38, 0xE3, 0x8E, 0x3F, 0x40, 0xC4, 0x89, 0x33, 0x42, 0x86, 0x0C, 0x00,
0xCC, 0xD3, 0x24, 0xC9, 0x5E, 0x73, 0x0C, 0xC3, 0x30, 0x44, 0xA3, 0x84,
0x29, 0xA4, 0x40, 0x44, 0x89, 0x31, 0x42, 0x86, 0x04, 0x08, 0x20, 0xC0,
0x7C, 0x31, 0x8C, 0x21, 0x0F, 0xC0, 0x69, 0x25, 0xA6, 0x49, 0x26, 0xFF,
0xF8, 0xC6, 0x66, 0x22, 0x12, 0x26, 0x66, 0xC0, 0xE6, 0x70 };
const GFXglyph FreeSans7pt7bGlyphs[] PROGMEM = {
{ 0, 1, 1, 4, 0, 0 }, // 0x20 ' '
{ 1, 1, 10, 4, 2, -9 }, // 0x21 '!'
{ 3, 3, 4, 5, 1, -9 }, // 0x22 '"'
{ 5, 7, 10, 8, 0, -9 }, // 0x23 '#'
{ 14, 7, 13, 8, 0, -10 }, // 0x24 '$'
{ 26, 12, 10, 12, 0, -9 }, // 0x25 '%'
{ 41, 8, 10, 9, 1, -9 }, // 0x26 '&'
{ 51, 1, 4, 3, 1, -9 }, // 0x27 '''
{ 52, 3, 13, 5, 1, -9 }, // 0x28 '('
{ 57, 3, 13, 5, 1, -9 }, // 0x29 ')'
{ 62, 4, 4, 5, 1, -9 }, // 0x2A '*'
{ 64, 6, 7, 8, 1, -6 }, // 0x2B '+'
{ 70, 2, 3, 4, 1, 0 }, // 0x2C ','
{ 71, 3, 1, 5, 1, -3 }, // 0x2D '-'
{ 72, 2, 1, 4, 1, 0 }, // 0x2E '.'
{ 73, 4, 10, 4, 0, -9 }, // 0x2F '/'
{ 78, 6, 10, 8, 1, -9 }, // 0x30 '0'
{ 86, 4, 10, 8, 1, -9 }, // 0x31 '1'
{ 91, 7, 10, 8, 0, -9 }, // 0x32 '2'
{ 100, 7, 10, 8, 0, -9 }, // 0x33 '3'
{ 109, 7, 10, 8, 0, -9 }, // 0x34 '4'
{ 118, 7, 10, 8, 0, -9 }, // 0x35 '5'
{ 127, 6, 10, 8, 1, -9 }, // 0x36 '6'
{ 135, 6, 10, 8, 1, -9 }, // 0x37 '7'
{ 143, 7, 10, 8, 0, -9 }, // 0x38 '8'
{ 152, 6, 10, 8, 1, -9 }, // 0x39 '9'
{ 160, 2, 7, 4, 1, -6 }, // 0x3A ':'
{ 162, 2, 9, 4, 1, -6 }, // 0x3B ';'
{ 165, 6, 7, 8, 1, -6 }, // 0x3C '<'
{ 171, 6, 3, 8, 1, -4 }, // 0x3D '='
{ 174, 6, 7, 8, 1, -6 }, // 0x3E '>'
{ 180, 6, 10, 8, 1, -9 }, // 0x3F '?'
{ 188, 13, 12, 14, 0, -9 }, // 0x40 '@'
{ 208, 9, 10, 9, 0, -9 }, // 0x41 'A'
{ 220, 8, 10, 9, 1, -9 }, // 0x42 'B'
{ 230, 8, 10, 10, 1, -9 }, // 0x43 'C'
{ 240, 8, 10, 10, 1, -9 }, // 0x44 'D'
{ 250, 7, 10, 9, 1, -9 }, // 0x45 'E'
{ 259, 7, 10, 8, 1, -9 }, // 0x46 'F'
{ 268, 9, 10, 11, 1, -9 }, // 0x47 'G'
{ 280, 8, 10, 10, 1, -9 }, // 0x48 'H'
{ 290, 2, 10, 4, 1, -9 }, // 0x49 'I'
{ 293, 6, 10, 7, 0, -9 }, // 0x4A 'J'
{ 301, 8, 10, 9, 1, -9 }, // 0x4B 'K'
{ 311, 6, 10, 8, 1, -9 }, // 0x4C 'L'
{ 319, 9, 10, 11, 1, -9 }, // 0x4D 'M'
{ 331, 8, 10, 10, 1, -9 }, // 0x4E 'N'
{ 341, 9, 10, 11, 1, -9 }, // 0x4F 'O'
{ 353, 7, 10, 9, 1, -9 }, // 0x50 'P'
{ 362, 9, 11, 11, 1, -9 }, // 0x51 'Q'
{ 375, 8, 10, 10, 1, -9 }, // 0x52 'R'
{ 385, 8, 10, 9, 1, -9 }, // 0x53 'S'
{ 395, 8, 10, 8, 0, -9 }, // 0x54 'T'
{ 405, 8, 10, 10, 1, -9 }, // 0x55 'U'
{ 415, 9, 10, 9, 0, -9 }, // 0x56 'V'
{ 427, 13, 10, 13, 0, -9 }, // 0x57 'W'
{ 444, 9, 10, 9, 0, -9 }, // 0x58 'X'
{ 456, 9, 10, 9, 0, -9 }, // 0x59 'Y'
{ 468, 8, 10, 8, 0, -9 }, // 0x5A 'Z'
{ 478, 2, 13, 4, 1, -9 }, // 0x5B '['
{ 482, 4, 10, 4, 0, -9 }, // 0x5C '\'
{ 487, 3, 13, 4, 0, -9 }, // 0x5D ']'
{ 492, 5, 5, 6, 1, -9 }, // 0x5E '^'
{ 496, 8, 1, 8, 0, 3 }, // 0x5F '_'
{ 497, 3, 2, 5, 0, -9 }, // 0x60 '`'
{ 498, 6, 7, 8, 1, -6 }, // 0x61 'a'
{ 504, 6, 10, 8, 1, -9 }, // 0x62 'b'
{ 512, 7, 7, 7, 0, -6 }, // 0x63 'c'
{ 519, 7, 10, 8, 0, -9 }, // 0x64 'd'
{ 528, 6, 7, 8, 1, -6 }, // 0x65 'e'
{ 534, 4, 10, 4, 0, -9 }, // 0x66 'f'
{ 539, 7, 10, 8, 0, -6 }, // 0x67 'g'
{ 548, 6, 10, 8, 1, -9 }, // 0x68 'h'
{ 556, 1, 10, 3, 1, -9 }, // 0x69 'i'
{ 558, 2, 13, 3, 0, -9 }, // 0x6A 'j'
{ 562, 6, 10, 7, 1, -9 }, // 0x6B 'k'
{ 570, 1, 10, 3, 1, -9 }, // 0x6C 'l'
{ 572, 9, 7, 11, 1, -6 }, // 0x6D 'm'
{ 580, 6, 7, 8, 1, -6 }, // 0x6E 'n'
{ 586, 7, 7, 8, 0, -6 }, // 0x6F 'o'
{ 593, 6, 10, 8, 1, -6 }, // 0x70 'p'
{ 601, 7, 10, 8, 0, -6 }, // 0x71 'q'
{ 610, 3, 7, 5, 1, -6 }, // 0x72 'r'
{ 613, 6, 7, 7, 0, -6 }, // 0x73 's'
{ 619, 3, 9, 4, 0, -8 }, // 0x74 't'
{ 623, 6, 7, 8, 1, -6 }, // 0x75 'u'
{ 629, 7, 7, 7, 0, -6 }, // 0x76 'v'
{ 636, 10, 7, 10, 0, -6 }, // 0x77 'w'
{ 645, 6, 7, 7, 0, -6 }, // 0x78 'x'
{ 651, 7, 10, 7, 0, -6 }, // 0x79 'y'
{ 660, 6, 7, 7, 0, -6 }, // 0x7A 'z'
{ 666, 3, 13, 5, 1, -9 }, // 0x7B '{'
{ 671, 1, 13, 4, 1, -9 }, // 0x7C '|'
{ 673, 4, 13, 5, 0, -9 }, // 0x7D '}'
{ 680, 6, 2, 8, 1, -5 } }; // 0x7E '~'
const GFXfont FreeSans7pt7b = {
(uint8_t *)FreeSans7pt7bBitmaps,
(GFXglyph *)FreeSans7pt7bGlyphs,
0x20, 0x7E, 23 };
// Approx. 1354 bytes

View File

@@ -0,0 +1,153 @@
/* 6pt font https://rop.nl/truetype2gfx/ */
const uint8_t FreeSansBold6pt7bBitmaps[] = {
0x00, 0xFF, 0x57, 0xC0, 0xBB, 0x90, 0x2C, 0x5B, 0xFB, 0x44, 0x9F, 0x96,
0x28, 0x11, 0xFD, 0xF4, 0x70, 0x71, 0x77, 0x7C, 0x40, 0x71, 0x36, 0x8D,
0x81, 0xD0, 0x0B, 0xC2, 0x91, 0x24, 0x4F, 0x00, 0xF1, 0x63, 0x87, 0x1E,
0xA7, 0x66, 0xFE, 0xE0, 0x32, 0x64, 0x4C, 0xC4, 0x46, 0x20, 0x89, 0x92,
0x49, 0x69, 0x00, 0x2A, 0x65, 0x21, 0x09, 0xF2, 0x10, 0xF2, 0xFF, 0xF0,
0x24, 0x24, 0x84, 0x01, 0xE4, 0xF3, 0xCF, 0x3C, 0xD3, 0x78, 0x3C, 0x92,
0x49, 0x01, 0xF4, 0xF3, 0x0C, 0x66, 0x18, 0xFC, 0x01, 0xEC, 0xC3, 0x18,
0x30, 0xF3, 0x78, 0x18, 0xA2, 0x92, 0xCB, 0xF0, 0x82, 0x7D, 0x05, 0x1F,
0x0C, 0x3C, 0xDE, 0x01, 0xF4, 0xF0, 0xFF, 0x3C, 0x53, 0x78, 0xFC, 0x31,
0x84, 0x30, 0xC2, 0x08, 0x01, 0xE4, 0xD3, 0x39, 0x3C, 0x73, 0x78, 0x01,
0xEC, 0xF3, 0xCD, 0xF0, 0xD3, 0x78, 0xF0, 0xF0, 0xF0, 0xF7, 0x00, 0x77,
0x30, 0x38, 0x10, 0xFF, 0xC1, 0xF0, 0x83, 0x81, 0xC3, 0x7B, 0x00, 0x73,
0xE8, 0x82, 0x18, 0xC2, 0x0C, 0x30, 0x0F, 0x06, 0x18, 0x81, 0x27, 0x94,
0x93, 0x22, 0x64, 0x4A, 0xFE, 0x60, 0x06, 0x20, 0x38, 0x00, 0x18, 0x1C,
0x1C, 0x34, 0x36, 0x26, 0x7E, 0x63, 0xC3, 0xF9, 0xFB, 0x1E, 0x2F, 0x98,
0xF1, 0xE7, 0xFC, 0x38, 0xFB, 0x1C, 0x08, 0x10, 0x31, 0xE3, 0x7C, 0xF1,
0xFB, 0x1E, 0x3C, 0x78, 0xF1, 0xE6, 0xF8, 0xFF, 0xFC, 0x30, 0xFF, 0x0C,
0x30, 0xFC, 0xFF, 0xFC, 0x30, 0xFB, 0x0C, 0x30, 0xC0, 0x1C, 0x3F, 0x63,
0x40, 0x47, 0x47, 0x61, 0x73, 0x3F, 0xC7, 0x8F, 0x1E, 0x3F, 0xF8, 0xF1,
0xE3, 0xC6, 0xFF, 0xFF, 0xC0, 0x0C, 0x30, 0xC3, 0x0C, 0x3C, 0xF3, 0x78,
0xC7, 0x9B, 0x67, 0x8F, 0x1B, 0x36, 0x66, 0xC6, 0xC3, 0x0C, 0x30, 0xC3,
0x0C, 0x30, 0xFC, 0xC7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xFB, 0xDB, 0xDB,
0xC7, 0x8F, 0x9F, 0x3F, 0x7B, 0xF3, 0xE7, 0xC6, 0x1C, 0x1F, 0x98, 0xC8,
0x3C, 0x1A, 0x0D, 0x84, 0xE6, 0x3E, 0x00, 0xF3, 0xFC, 0x71, 0xFF, 0xEC,
0x30, 0xC0, 0x1C, 0x1F, 0x98, 0xC8, 0x34, 0x1A, 0x0D, 0x94, 0xE6, 0x3F,
0x80, 0x00, 0xF9, 0xFF, 0x1E, 0x3F, 0xDF, 0xB1, 0xE3, 0xC6, 0x38, 0xFD,
0x1B, 0x07, 0xC1, 0xF0, 0xB3, 0x7E, 0xFF, 0xFC, 0x60, 0xC1, 0x83, 0x06,
0x0C, 0x18, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE6, 0x7C, 0xC3, 0x42,
0x66, 0x66, 0x24, 0x3C, 0x3C, 0x18, 0x18, 0xC4, 0x79, 0xCD, 0x39, 0x35,
0x66, 0xAC, 0x57, 0x8E, 0xE1, 0x8C, 0x31, 0x80, 0xC7, 0x66, 0x3C, 0x3C,
0x18, 0x38, 0x3C, 0x66, 0x66, 0xC3, 0x66, 0x66, 0x3C, 0x3C, 0x18, 0x18,
0x18, 0x18, 0xFF, 0xFC, 0x30, 0xE1, 0x86, 0x18, 0x60, 0xFE, 0xFE, 0x49,
0x24, 0x93, 0x80, 0x91, 0x24, 0x49, 0xFC, 0x92, 0x49, 0x27, 0x80, 0x63,
0x95, 0xB8, 0x80, 0xFE, 0x48, 0x7F, 0x33, 0xF3, 0xCD, 0xF0, 0x82, 0x08,
0x3E, 0xCE, 0x38, 0xF2, 0xF8, 0x7D, 0x3C, 0x30, 0x4D, 0xE0, 0x04, 0x10,
0x5F, 0x4F, 0x1C, 0x53, 0x7C, 0x7B, 0x3F, 0xF0, 0x4D, 0xE0, 0x37, 0x6F,
0x66, 0x66, 0x60, 0x7D, 0x3C, 0x71, 0x6D, 0xF0, 0x5F, 0x10, 0x84, 0x21,
0xFC, 0xC6, 0x31, 0x88, 0xDF, 0x80, 0x51, 0x55, 0x5F, 0x84, 0x21, 0x3B,
0x73, 0xD3, 0x98, 0xFF, 0x80, 0xFF, 0x6C, 0xE6, 0x73, 0x39, 0x9C, 0xCC,
0xFE, 0x63, 0x18, 0xC4, 0x7C, 0x8B, 0x1E, 0x36, 0x4F, 0x80, 0xFB, 0x38,
0xE3, 0xCB, 0xE8, 0x20, 0x80, 0x7D, 0x3C, 0x71, 0x4D, 0xF0, 0x41, 0x04,
0xBA, 0x49, 0x00, 0x7D, 0x37, 0x87, 0xCD, 0xF0, 0x66, 0xF6, 0x66, 0x67,
0x8C, 0x63, 0x1D, 0xFC, 0xCD, 0x36, 0x8A, 0x38, 0xC0, 0xCD, 0xAE, 0x95,
0x4E, 0xE7, 0x71, 0xB0, 0x6D, 0xE3, 0x0E, 0x69, 0x30, 0xCD, 0x36, 0x9A,
0x38, 0xC3, 0x1C, 0x40, 0xF8, 0x63, 0x18, 0xC3, 0xF0, 0x37, 0x66, 0x6C,
0x66, 0x66, 0x30, 0xFF, 0xE0, 0x99, 0x24, 0xDA, 0x4B, 0x00, 0xC5, 0xC0 };
const GFXglyph FreeSansBold6pt7bGlyphs[] PROGMEM = {
{ 0, 1, 1, 3, 0, 0 }, // 0x20 ' '
{ 1, 2, 9, 4, 1, -8 }, // 0x21 '!'
{ 4, 4, 3, 6, 1, -8 }, // 0x22 '"'
{ 6, 7, 8, 7, 0, -7 }, // 0x23 '#'
{ 13, 6, 10, 7, 0, -8 }, // 0x24 '$'
{ 21, 10, 8, 10, 0, -7 }, // 0x25 '%'
{ 31, 7, 9, 8, 1, -8 }, // 0x26 '&'
{ 39, 1, 3, 3, 1, -8 }, // 0x27 '''
{ 40, 4, 11, 4, 0, -8 }, // 0x28 '('
{ 46, 3, 11, 4, 0, -8 }, // 0x29 ')'
{ 51, 4, 4, 5, 0, -8 }, // 0x2A '*'
{ 53, 5, 6, 7, 1, -5 }, // 0x2B '+'
{ 57, 2, 4, 3, 1, -1 }, // 0x2C ','
{ 58, 4, 2, 4, 0, -3 }, // 0x2D '-'
{ 59, 2, 2, 3, 1, -1 }, // 0x2E '.'
{ 60, 3, 8, 3, 0, -7 }, // 0x2F '/'
{ 63, 6, 9, 7, 0, -8 }, // 0x30 '0'
{ 70, 3, 8, 7, 1, -7 }, // 0x31 '1'
{ 73, 6, 9, 7, 0, -8 }, // 0x32 '2'
{ 80, 6, 9, 7, 0, -8 }, // 0x33 '3'
{ 87, 6, 8, 7, 0, -7 }, // 0x34 '4'
{ 93, 6, 8, 7, 0, -7 }, // 0x35 '5'
{ 99, 6, 9, 7, 0, -8 }, // 0x36 '6'
{ 106, 6, 8, 7, 0, -7 }, // 0x37 '7'
{ 112, 6, 9, 7, 0, -8 }, // 0x38 '8'
{ 119, 6, 9, 7, 0, -8 }, // 0x39 '9'
{ 126, 2, 6, 4, 1, -5 }, // 0x3A ':'
{ 128, 2, 8, 4, 1, -5 }, // 0x3B ';'
{ 130, 6, 6, 7, 0, -5 }, // 0x3C '<'
{ 135, 5, 4, 7, 1, -4 }, // 0x3D '='
{ 138, 6, 6, 7, 0, -5 }, // 0x3E '>'
{ 143, 6, 9, 7, 1, -8 }, // 0x3F '?'
{ 150, 11, 11, 11, 0, -8 }, // 0x40 '@'
{ 166, 8, 9, 8, 0, -8 }, // 0x41 'A'
{ 175, 7, 9, 8, 1, -8 }, // 0x42 'B'
{ 183, 7, 9, 8, 1, -8 }, // 0x43 'C'
{ 191, 7, 9, 8, 1, -8 }, // 0x44 'D'
{ 199, 6, 9, 8, 1, -8 }, // 0x45 'E'
{ 206, 6, 9, 7, 1, -8 }, // 0x46 'F'
{ 213, 8, 9, 9, 0, -8 }, // 0x47 'G'
{ 222, 7, 9, 8, 1, -8 }, // 0x48 'H'
{ 230, 2, 9, 3, 1, -8 }, // 0x49 'I'
{ 233, 6, 9, 7, 0, -8 }, // 0x4A 'J'
{ 240, 7, 9, 8, 1, -8 }, // 0x4B 'K'
{ 248, 6, 9, 7, 1, -8 }, // 0x4C 'L'
{ 255, 8, 9, 10, 1, -8 }, // 0x4D 'M'
{ 264, 7, 9, 8, 1, -8 }, // 0x4E 'N'
{ 272, 9, 9, 9, 0, -8 }, // 0x4F 'O'
{ 283, 6, 9, 8, 1, -8 }, // 0x50 'P'
{ 290, 9, 10, 9, 0, -8 }, // 0x51 'Q'
{ 302, 7, 9, 8, 1, -8 }, // 0x52 'R'
{ 310, 7, 9, 8, 0, -8 }, // 0x53 'S'
{ 318, 7, 9, 7, 0, -8 }, // 0x54 'T'
{ 326, 7, 9, 8, 1, -8 }, // 0x55 'U'
{ 334, 8, 9, 8, 0, -8 }, // 0x56 'V'
{ 343, 11, 9, 11, 0, -8 }, // 0x57 'W'
{ 356, 8, 9, 8, 0, -8 }, // 0x58 'X'
{ 365, 8, 9, 8, 0, -8 }, // 0x59 'Y'
{ 374, 7, 9, 7, 0, -8 }, // 0x5A 'Z'
{ 382, 3, 11, 4, 1, -8 }, // 0x5B '['
{ 387, 3, 8, 3, 0, -7 }, // 0x5C '\'
{ 390, 3, 11, 4, 0, -8 }, // 0x5D ']'
{ 395, 5, 5, 7, 1, -7 }, // 0x5E '^'
{ 399, 7, 1, 7, 0, 2 }, // 0x5F '_'
{ 400, 3, 2, 4, 0, -8 }, // 0x60 '`'
{ 401, 6, 6, 7, 0, -5 }, // 0x61 'a'
{ 406, 6, 9, 7, 1, -8 }, // 0x62 'b'
{ 413, 6, 6, 7, 0, -5 }, // 0x63 'c'
{ 418, 6, 9, 7, 0, -8 }, // 0x64 'd'
{ 425, 6, 6, 7, 0, -5 }, // 0x65 'e'
{ 430, 4, 9, 4, 0, -8 }, // 0x66 'f'
{ 435, 6, 9, 7, 0, -5 }, // 0x67 'g'
{ 442, 5, 9, 7, 1, -8 }, // 0x68 'h'
{ 448, 1, 9, 3, 1, -8 }, // 0x69 'i'
{ 450, 2, 12, 3, 0, -8 }, // 0x6A 'j'
{ 453, 5, 9, 7, 1, -8 }, // 0x6B 'k'
{ 459, 1, 9, 3, 1, -8 }, // 0x6C 'l'
{ 461, 9, 6, 10, 1, -5 }, // 0x6D 'm'
{ 468, 5, 6, 7, 1, -5 }, // 0x6E 'n'
{ 472, 7, 6, 7, 0, -5 }, // 0x6F 'o'
{ 478, 6, 9, 7, 1, -5 }, // 0x70 'p'
{ 485, 6, 9, 7, 0, -5 }, // 0x71 'q'
{ 492, 3, 6, 5, 1, -5 }, // 0x72 'r'
{ 495, 6, 6, 7, 0, -5 }, // 0x73 's'
{ 500, 4, 8, 4, 0, -7 }, // 0x74 't'
{ 504, 5, 6, 7, 1, -5 }, // 0x75 'u'
{ 508, 6, 6, 7, 0, -5 }, // 0x76 'v'
{ 513, 9, 6, 9, 0, -5 }, // 0x77 'w'
{ 520, 6, 6, 7, 0, -5 }, // 0x78 'x'
{ 525, 6, 9, 7, 0, -5 }, // 0x79 'y'
{ 532, 6, 6, 6, 0, -5 }, // 0x7A 'z'
{ 537, 4, 11, 5, 0, -8 }, // 0x7B '{'
{ 543, 1, 11, 3, 1, -8 }, // 0x7C '|'
{ 545, 3, 11, 5, 1, -8 }, // 0x7D '}'
{ 550, 5, 2, 7, 1, -3 } }; // 0x7E '~'
const GFXfont FreeSansBold6pt7b = {
(uint8_t *)FreeSansBold6pt7bBitmaps,
(GFXglyph *)FreeSansBold6pt7bGlyphs,
0x20, 0x7E, 19 };
// Approx. 1224 bytes

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,197 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
Simple XPT2046 SPI/Bitbang interface for PacoMouseCYD
*/
#include "config.h"
#include "XPT2046.h"
#define Z_THRESHOLD 300
#define MSEC_THRESHOLD 4
XPT2046_TS::XPT2046_TS(uint8_t mosiPin, uint8_t misoPin, uint8_t clkPin, uint8_t csPin) :
_mosiPin(mosiPin), _misoPin(misoPin), _clkPin(clkPin), _csPin(csPin) {
cal = TouchCalibration{0, 4095, 0, 4095, 0}; // other initializations, if required
_msraw = millis();
#ifdef USE_XPT2046_SPI
hspi = new SPIClass(HSPI); // XPT2046 connected to HSPI in CYD 2.4"
hspi->begin();
#endif
}
void XPT2046_TS::begin(uint16_t width, uint16_t height) {
pinMode(_csPin, OUTPUT);
digitalWrite(_csPin, HIGH);
#ifdef USE_XPT2046_BITBANG
pinMode(_clkPin, OUTPUT); // init all pins in bitbang mode only (CYD 2.8")
digitalWrite(_clkPin, LOW);
pinMode(_mosiPin, OUTPUT);
pinMode(_misoPin, INPUT);
#endif
_width = width;
_height = height;
}
void XPT2046_TS::setCalibration(uint16_t xMin, uint16_t xMax, uint16_t yMin, uint16_t yMax) {
cal.xMin = xMin;
cal.xMax = xMax;
cal.yMin = yMin;
cal.yMax = yMax;
}
TouchCalibration XPT2046_TS::getCalibration() {
return cal;
}
void XPT2046_TS::setRotation(uint8_t n) {
cal.rotation = n % 4;
}
bool XPT2046_TS::touched() {
update();
return (_zraw > Z_THRESHOLD);
}
TSPoint XPT2046_TS::getTouch() {
update();
uint16_t x = map(_xraw, cal.xMin, cal.xMax, 0, _width);
uint16_t y = map(_yraw, cal.yMin, cal.yMax, 0, _height);
if ((x >= _width) || (x <= 0) || (y >= _height) || (y <= 0))
_zraw = 0;
return TSPoint{x, y, _zraw};
}
void XPT2046_TS::readData(uint16_t *x, uint16_t *y, uint16_t *z) {
update();
*x = _xraw; // read raw data
*y = _yraw;
*z = _zraw;
}
#ifdef USE_XPT2046_BITBANG
uint16_t XPT2046_TS::readSPI(byte command) {
uint16_t result = 0;
for (int i = 7; i >= 0; i--) {
digitalWrite(_mosiPin, command & (1 << i)); // send command
digitalWrite(_clkPin, HIGH);
delayMicroseconds(7);
digitalWrite(_clkPin, LOW);
delayMicroseconds(7);
}
for (int i = 11; i >= 0; i--) { // read data
digitalWrite(_clkPin, HIGH);
delayMicroseconds(7);
digitalWrite(_clkPin, LOW);
delayMicroseconds(7);
result |= (digitalRead(_misoPin) << i);
}
return result;
}
void XPT2046_TS::update() {
int t;
uint32_t now = millis();
if (now - _msraw < MSEC_THRESHOLD)
return;
digitalWrite(_csPin, LOW);
readSPI(0xB0);
readSPI(0xB0);
readSPI(0xB0);
int z1 = readSPI(0xB0);
_zraw = z1 + 4095;
readSPI(0xC0);
readSPI(0xC0);
readSPI(0xC0);
int z2 = readSPI(0xC0);
_zraw -= z2;
readSPI(0x90);
readSPI(0x90);
readSPI(0x90);
_xraw = readSPI(0x90);
readSPI(0xD0);
readSPI(0xD0);
readSPI(0xD0);
_yraw = readSPI(0xD0);
digitalWrite(_csPin, HIGH);
_msraw = now;
switch (cal.rotation) {
case 0:
t = 4095 - _yraw;
_yraw = _xraw;
_xraw = t;
break;
case 1:
break;
case 2:
t = _xraw;
_xraw = _yraw;
_yraw = 4095 - t;
break;
default:
_xraw = 4095 - _xraw;
_yraw = 4095 - _yraw;
break;
}
}
#endif
#ifdef USE_XPT2046_SPI
void XPT2046_TS::update() {
int t;
uint32_t now = millis();
if (now - _msraw < MSEC_THRESHOLD)
return;
hspi->beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
digitalWrite(_csPin, LOW);
hspi->transfer(0xB0);
hspi->transfer16(0xB0);
hspi->transfer16(0xB0);
hspi->transfer16(0xB0);
int z1 = hspi->transfer16(0xC0) >> 3;
_zraw = z1 + 4095;
hspi->transfer16(0xC0);
hspi->transfer16(0xC0);
hspi->transfer16(0xC0);
int z2 = hspi->transfer16(0x90) >> 3;
_zraw -= z2;
hspi->transfer16(0x90);
hspi->transfer16(0x90);
hspi->transfer16(0x90);
_xraw = hspi->transfer16(0xD0) >> 3;
hspi->transfer16(0xD0);
hspi->transfer16(0xD0);
hspi->transfer16(0xD0);
_yraw = hspi->transfer16(0x0) >> 3;
digitalWrite(_csPin, HIGH);
hspi->endTransaction();
_msraw = now;
switch (cal.rotation) {
case 0:
_xraw = 4095 - _xraw;
break;
case 1:
t = _yraw;
_yraw = _xraw;
_xraw = t;
break;
case 2:
_yraw = 4095 - _yraw;
break;
default:
t = _yraw;
_yraw = 4095 - _xraw;
_xraw = 4095 - t;
break;
}
}
#endif

View File

@@ -0,0 +1,62 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
Simple XPT2046 SPI/Bitbang interface for PacoMouseCYD
*/
#ifndef XPT2046_TS_h
#define XPT2046_TS_h
#include "Arduino.h"
#include "config.h"
#ifdef USE_XPT2046_SPI
#include <SPI.h>
#endif
typedef struct {
uint16_t x;
uint16_t y;
uint16_t z;
} TSPoint;
struct TouchCalibration {
uint16_t xMin;
uint16_t xMax;
uint16_t yMin;
uint16_t yMax;
uint16_t rotation;
};
class XPT2046_TS {
public:
XPT2046_TS(uint8_t mosiPin, uint8_t misoPin, uint8_t clkPin, uint8_t csPin);
void begin(uint16_t width = 240, uint16_t height = 320);
bool touched();
TSPoint getTouch();
void setRotation(uint8_t n);
void setCalibration(uint16_t xMin, uint16_t xMax, uint16_t yMin, uint16_t yMax);
TouchCalibration getCalibration();
void readData(uint16_t *x, uint16_t *y, uint16_t *z);
private:
uint8_t _mosiPin;
uint8_t _misoPin;
uint8_t _clkPin;
uint8_t _csPin;
uint8_t _irqPin;
uint16_t _width;
uint16_t _height;
uint16_t _xraw;
uint16_t _yraw;
uint16_t _zraw;
uint32_t _msraw;
TouchCalibration cal;
#ifdef USE_XPT2046_SPI
SPIClass *hspi = NULL;
#endif
#ifdef USE_XPT2046_BITBANG
uint16_t readSPI(byte command);
#endif
void update();
};
#endif

View File

@@ -0,0 +1,358 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
////////////////////////////////////////////////////////////
// ***** ACCESSORY FIFO *****
////////////////////////////////////////////////////////////
void initFIFO() {
lastInFIFO = 0;
firstOutFIFO = 0;
cntFIFO = 0;
stateFIFO = FIFO_EMPTY;
}
unsigned int readFIFO () {
firstOutFIFO = (firstOutFIFO + 1 ) & 0x1F; // next one (hardcoded)
cntFIFO--;
return (accessoryFIFO[firstOutFIFO]);
}
void writeFIFO (uint16_t FAdr, uint8_t pos) {
lastInFIFO = (lastInFIFO + 1 ) & 0x1F; // next one (hardcoded)
cntFIFO++;
if (pos > 0)
FAdr |= 0x8000;
accessoryFIFO[lastInFIFO] = FAdr; // save in FIFO
}
void sendAccessoryFIFO () {
switch (stateFIFO) {
case FIFO_ACC_CDU: // wait for CDU recharge
case FIFO_EMPTY:
if (cntFIFO > 0) { // activate accessory from FIFO
lastAccessory = readFIFO();
sendAccessory(lastAccessory & 0x7FFF, bitRead(lastAccessory, 15), true);
setTimer(TMR_ACCESSORY, TIME_ACC_ON, TMR_ONESHOT);
stateFIFO = FIFO_ACC_ON;
DEBUG_MSG("Moving acc. %d-%d", lastAccessory & 0x7FFF, lastAccessory >> 15);
}
else
stateFIFO = FIFO_EMPTY;
break;
case FIFO_ACC_ON: // deactivate accessory
sendAccessory(lastAccessory & 0x7FFF, bitRead(lastAccessory, 15), false);
setTimer(TMR_ACCESSORY, TIME_ACC_CDU, TMR_ONESHOT);
stateFIFO = FIFO_ACC_CDU;
break;
}
}
////////////////////////////////////////////////////////////
// ***** ACCESSORY *****
////////////////////////////////////////////////////////////
void moveAccessory(uint16_t FAdr, uint8_t pos) {
writeFIFO(FAdr, pos); // put accessory in FIFO
if (stateFIFO == FIFO_EMPTY) // if not pending accessories, force sending now
sendAccessoryFIFO();
}
void setAccAspect(uint16_t FAdr, uint16_t FAdr2, uint8_t aspect, uint16_t outs) {
uint8_t pos;
pos = aspect * 4;
if (FAdr > 0) {
if (bitRead(outs, pos)) {
moveAccessory(FAdr, 0);
}
else {
if (bitRead(outs, pos + 1))
moveAccessory(FAdr, 1);
}
}
if (FAdr2 > 0) {
if (bitRead(outs, pos + 2)) {
moveAccessory(FAdr2, 0);
}
else {
if (bitRead(outs, pos + 3))
moveAccessory(FAdr2, 1);
}
}
}
////////////////////////////////////////////////////////////
// ***** PANEL *****
////////////////////////////////////////////////////////////
void deleteAccPanelElement(uint8_t pos) {
accPanel[pos].type = ACC_UNDEF;
accPanel[pos].currAspect = 0;
accPanel[pos].addr = 0;
accPanel[pos].addr2 = 0;
accPanel[pos].activeOutput = 0;
accPanel[pos].accName[0] = '\0';
}
void loadDefaultAccPanel() {
uint8_t n;
for (n = 0; n < 16; n++) { // Default panel is all blank with only a keypad button
deleteAccPanelElement(n);
}
accPanel[15].type = ACC_KEYPAD;
}
void updateAccPanel() {
uint8_t n, type, aspect;
snprintf(panelNameBuf, PANEL_LNG + 1, panelNamesBuf[currPanel]);
for (n = 0; n < 16; n++) {
type = accPanel[n].type;
aspect = accPanel[n].currAspect;
snprintf(accNamesBuf[n], ACC_LNG + 1, accPanel[n].accName);
fncData[FNC_ACC0 + n].num = accDef[type].num;
fncData[FNC_ACC0 + n].idIcon = accDef[type].icon[aspect].fncIcon;
fncData[FNC_ACC0 + n].color = accDef[type].icon[aspect].color;
fncData[FNC_ACC0 + n].colorOn = accDef[type].icon[aspect].colorOn;
fncData[FNC_ACC0 + n].backgnd = (type == ACC_UNDEF) ? COLOR_WHITE : COLOR_LIGHTGREY;
buttonData[BUT_ACC_0 + n].backgnd = (type == ACC_UNDEF) ? COLOR_WHITE : COLOR_LIGHTGREY;
}
}
void saveCurrentAspects() {
uint8_t n;
for (n = 0; n < 16; n++)
savedAspect[currPanel][n] = accPanel[n].currAspect;
}
void getLastAspects() {
uint8_t n;
for (n = 0; n < 16; n++)
accPanel[n].currAspect = savedAspect[currPanel][n];
}
void deleteLastAspects() {
uint8_t n;
for (n = 0; n < 16; n++)
accPanel[n].currAspect = 0;
}
void initLastAspects() {
uint8_t i, j;
for (i = 0; i < 16; i++)
for (j = 0; j < 16; j++)
savedAspect[i][j] = 0;;
}
void populateAccPanel() {
loadDefaultAccPanel(); // Load panel data
if (sdDetected) {
if (loadAccPanel(SD))
getLastAspects();
else
deleteLastAspects();
}
else {
if (loadAccPanel(LittleFS))
getLastAspects();
else
deleteLastAspects();
}
updateAccPanel();
}
void accPanelClick(uint8_t pos) {
uint16_t addr, outs;
uint8_t type, aspect;
currPanelAcc = pos;
if (editAccessory) {
paramChild = pos;
type = accPanel[pos].type;
fncData[FNC_ACC_TYPE].num = accDef[type].num;
fncData[FNC_ACC_TYPE].idIcon = accDef[type].icon[0].fncIcon;
fncData[FNC_ACC_TYPE].color = accDef[type].icon[0].color;
fncData[FNC_ACC_TYPE].colorOn = accDef[type].icon[0].colorOn;
encoderValue = type;
encoderMax = ACC_MAX - 1;
openWindow(WIN_ACC_TYPE);
}
else {
type = accPanel[pos].type;
switch (type) {
case ACC_UNDEF:
break;
case ACC_KEYPAD:
openWindow(WIN_ACC_CTRL);
break;
default:
addr = accPanel[pos].addr;
outs = accPanel[pos].activeOutput;
switch (accDef[type].aspects) {
case 2:
aspect = (accPanel[pos].currAspect > 0) ? 0 : 1;
accPanel[pos].currAspect = aspect;
fncData[FNC_ACC0 + pos].idIcon = accDef[type].icon[aspect].fncIcon;
fncData[FNC_ACC0 + pos].color = accDef[type].icon[aspect].color;
fncData[FNC_ACC0 + pos].colorOn = accDef[type].icon[aspect].colorOn;
setAccAspect(addr, 0, aspect, outs);
newEvent(OBJ_BUTTON, BUT_ACC_0 + pos, EVNT_DRAW);
break;
case 3:
currAccAspects = 3;
winData[WIN_ACC_ASPECT].x = 30;
winData[WIN_ACC_ASPECT].w = 180;
buttonData[BUT_ACC_ASPECT0].x = 50;
buttonData[BUT_ACC_ASPECT1].x = 100;
buttonData[BUT_ACC_ASPECT2].x = 150;
fncData[FNC_ASPECT0].x = 54;
fncData[FNC_ASPECT0].idIcon = accDef[type].icon[0].fncIcon;
fncData[FNC_ASPECT0].color = accDef[type].icon[0].color;
fncData[FNC_ASPECT0].colorOn = accDef[type].icon[0].colorOn;
fncData[FNC_ASPECT1].x = 104;
fncData[FNC_ASPECT1].idIcon = accDef[type].icon[1].fncIcon;
fncData[FNC_ASPECT1].color = accDef[type].icon[1].color;
fncData[FNC_ASPECT1].colorOn = accDef[type].icon[1].colorOn;
fncData[FNC_ASPECT2].x = 154;
fncData[FNC_ASPECT2].idIcon = accDef[type].icon[2].fncIcon;
fncData[FNC_ASPECT2].color = accDef[type].icon[2].color;
fncData[FNC_ASPECT2].colorOn = accDef[type].icon[2].colorOn;
openWindow(WIN_ACC_ASPECT);
break;
case 4:
currAccAspects = 4;
winData[WIN_ACC_ASPECT].x = 5;
winData[WIN_ACC_ASPECT].w = 230;
buttonData[BUT_ACC_ASPECT0].x = 25;
buttonData[BUT_ACC_ASPECT1].x = 75;
buttonData[BUT_ACC_ASPECT2].x = 125;
buttonData[BUT_ACC_ASPECT3].x = 175;
fncData[FNC_ASPECT0].x = 29;
fncData[FNC_ASPECT0].idIcon = accDef[type].icon[0].fncIcon;
fncData[FNC_ASPECT0].color = accDef[type].icon[0].color;
fncData[FNC_ASPECT0].colorOn = accDef[type].icon[0].colorOn;
fncData[FNC_ASPECT1].x = 79;
fncData[FNC_ASPECT1].idIcon = accDef[type].icon[1].fncIcon;
fncData[FNC_ASPECT1].color = accDef[type].icon[1].color;
fncData[FNC_ASPECT1].colorOn = accDef[type].icon[1].colorOn;
fncData[FNC_ASPECT2].x = 129;
fncData[FNC_ASPECT2].idIcon = accDef[type].icon[2].fncIcon;
fncData[FNC_ASPECT2].color = accDef[type].icon[2].color;
fncData[FNC_ASPECT2].colorOn = accDef[type].icon[2].colorOn;
fncData[FNC_ASPECT3].x = 179;
fncData[FNC_ASPECT3].idIcon = accDef[type].icon[3].fncIcon;
fncData[FNC_ASPECT3].color = accDef[type].icon[3].color;
fncData[FNC_ASPECT3].colorOn = accDef[type].icon[3].colorOn;
openWindow(WIN_ACC_ASPECT);
break;
default:
buttonData[BUT_ACC_0 + pos].backgnd = COLOR_BLACK;
drawObject(OBJ_BUTTON, BUT_ACC_0 + pos);
buttonData[BUT_ACC_0 + pos].backgnd = COLOR_LIGHTGREY;
setAccAspect(addr, 0, 0, outs);
newEvent(OBJ_BUTTON, BUT_ACC_0 + pos, EVNT_DRAW);
delay(80);
break;
}
break;
}
}
}
void accAspectClick(uint8_t aspect) {
uint16_t addr, addr2, outs;
uint8_t type;
type = accPanel[currPanelAcc].type;
accPanel[currPanelAcc].currAspect = aspect;
fncData[FNC_ACC0 + currPanelAcc].idIcon = accDef[type].icon[aspect].fncIcon;
fncData[FNC_ACC0 + currPanelAcc].color = accDef[type].icon[aspect].color;
fncData[FNC_ACC0 + currPanelAcc].colorOn = accDef[type].icon[aspect].colorOn;
addr = accPanel[currPanelAcc].addr;
addr2 = accPanel[currPanelAcc].addr2;
outs = accPanel[currPanelAcc].activeOutput;
setAccAspect(addr, addr2, aspect, outs);
}
void accTypeClick() {
uint8_t index, n;
index = encoderValue;
switch (index) {
case ACC_UNDEF:
alertWindow(ERR_ASK_SURE);
break;
case ACC_KEYPAD:
editAccessory = false;
winData[WIN_ACCESSORY].backgnd = COLOR_WHITE;
deleteAccPanelElement(paramChild);
accPanel[paramChild].type = ACC_KEYPAD;
updateAccPanel();
updateSpeedHID(); // set encoder
closeWindow(WIN_ACC_TYPE);
break;
default:
if (index != accPanel[paramChild].type) {
currAccEdit.type = (accType)index;
currAccEdit.addr = 0;
currAccEdit.addr2 = 0;
currAccEdit.currAspect = 0;
currAccEdit.activeOutput = accOutDefault[index];
currAccEdit.accName[0] = '\0';
}
else {
currAccEdit = accPanel[paramChild];
}
snprintf(accKeybName, ACC_LNG + 1, currAccEdit.accName);
snprintf(accKeybAddr1, ADDR_LNG + 1, "%d", currAccEdit.addr);
snprintf(accKeybAddr2, ADDR_LNG + 1, "%d", currAccEdit.addr2);
for (n = 0; n < 4; n++) {
fncData[FNC_EDIT_ASPECT0 + n].idIcon = accDef[index].icon[n].fncIcon;
fncData[FNC_EDIT_ASPECT0 + n].color = accDef[index].icon[n].color;
fncData[FNC_EDIT_ASPECT0 + n].colorOn = accDef[index].icon[n].colorOn;
}
accOutUpdate();
closeWindow(WIN_ACC_TYPE);
openWindow(WIN_ACC_EDIT);
break;
}
}
void accOutUpdate() {
uint8_t n;
for (n = 0; n < 16; n += 2) {
buttonData[BUT_ACC_OUT0 + n].backgnd = bitRead(currAccEdit.activeOutput, n) ? COLOR_RED : COLOR_LIGHTBLACK;
buttonData[BUT_ACC_OUT0 + n + 1].backgnd = bitRead(currAccEdit.activeOutput, n + 1) ? COLOR_GREEN : COLOR_LIGHTBLACK;
}
}
void accOutClick(uint8_t out) {
uint8_t outR, outG;
outR = out & 0xFE;
outG = out | 0x01;
currAccEdit.activeOutput ^= bit(out);
if (bitRead(out, 0)) { // green
if (bitRead(currAccEdit.activeOutput, outG))
bitClear(currAccEdit.activeOutput, outR);
}
else { // red
if (bitRead(currAccEdit.activeOutput, outR))
bitClear(currAccEdit.activeOutput, outG);
}
accOutUpdate();
newEvent(OBJ_BUTTON, BUT_ACC_OUT0 + outR, EVNT_DRAW);
newEvent(OBJ_BUTTON, BUT_ACC_OUT0 + outG, EVNT_DRAW);
}
void updateAccChange() {
accPanel[paramChild] = currAccEdit;
updateAccPanel();
accPanelChanged = true;
}

View File

@@ -0,0 +1,184 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
#ifndef PACOMOUSECYD_CFG_H
#define PACOMOUSECYD_CFG_H
#define CYD_TFT_28 0 // Cheap Yellow Display 2.8"
#define CYD_TFT_24 1 // Cheap Yellow Display 2.4"
#define CYD_TFT_32 2 // Cheap Yellow Display 3.2"
#define CYD_USER_DEFINED 3 // User defined board
#define PRESENT 1
#define UNUSED 0
#define MODE_SPI 0
#define MODE_BITBANG 1
////////////////////////////////////////////////////////////
// ***** USER OPTIONS *****
////////////////////////////////////////////////////////////
// Seleccione la version hardware del CYD (Cheap Yellow Display) - Select the hardware version of CYD (Cheap Yellow Display): CYD_TFT_28 / CYD_TFT_24 / CYD_TFT_32 / CYD_USER_DEFINED
// Use el archivo User_Setup.h correcto para la libreria TFT_eSPI - Use the correct User_Setup.h file for library TFT_eSPI
#define CYD_HW_VERSION CYD_TFT_28
// Max. locomotoras guardadas en stack (hasta 254) - Max. locomotives saved in stack (up to 254):
#define LOCOS_IN_STACK 100
// Delimitador en fichero CSV - CSV file delimiter: ';' / ','
#define CSV_FILE_DELIMITER ';'
#if (CYD_HW_VERSION == CYD_USER_DEFINED)
////////////////////////////////////////////////////////////
// ***** USER DEFINED HARDWARE *****
////////////////////////////////////////////////////////////
// Seleccione el modo de acceso al chip XPT2046 - Select XPT2046 chip access mode : MODE_SPI / MODE_BITBANG
#define XPT_MODE MODE_SPI
// Seleccione rotacion de la pantalla tactil - Select Touchscreen rotation: 0 / 1 / 2 / 3
#define XPT_ROTATION 3
// Touchscreen
#define XPT2046_IRQ 36 // T_IRQ
#define XPT2046_MOSI 13 // T_DIN
#define XPT2046_MISO 12 // T_OUT
#define XPT2046_CLK 14 // T_CLK
#define XPT2046_CS 33 // T_CS
// Seleccione si usa el LED RGB - Select if use the RGB LED: PRESENT / UNUSED
#define USE_RGB_LED PRESENT
//RGB LED Pins
#define RGB_LED_R 4
#define RGB_LED_G 17
#define RGB_LED_B 16
//SD Pins
#define SD_CS 5
// Encoder Pins
#define ENCODER_A 22
#define ENCODER_B 21
#define ENCODER_SW 35
#endif
////////////////////////////////////////////////////////////
// ***** END OF USER DEFINED HARDWARE *****
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// ***** END OF USER OPTIONS *****
////////////////////////////////////////////////////////////
#if (CYD_HW_VERSION == CYD_TFT_28)
#define USE_CYD_28 1 // Cheap Yellow Display 2.8" (2432S028R)
#endif
#if (CYD_HW_VERSION == CYD_TFT_24)
#define USE_CYD_24 1 // Cheap Yellow Display 2.4" (2432S024R)
#endif
#if (CYD_HW_VERSION == CYD_TFT_32)
#define USE_CYD_24 1 // Cheap Yellow Display 3.2" (2432S032R)
#endif
#if (CYD_HW_VERSION == CYD_USER_DEFINED)
#if (XPT_MODE == MODE_SPI) // Cheap Yellow Display other type
#define USE_XPT2046_SPI
#endif
#if (XPT_MODE == MODE_BITBANG)
#define USE_XPT2046_BITBANG
#endif
#endif
#if defined(USE_CYD_28) && defined(USE_CYD_24)
#error Seleccione solo un tipo de CYD (Cheap Yellow Display) - Select only one type of CYD (Cheap Yellow Display)
#endif
#define USER_MIN_BL 64 // User min backlight
#define SYS_MIN_BL 32 // System inactivity backlight
#ifdef USE_CYD_28
// Touchscreen pins
#define XPT2046_IRQ 36 // T_IRQ
#define XPT2046_MOSI 32 // T_DIN
#define XPT2046_MISO 39 // T_OUT
#define XPT2046_CLK 25 // T_CLK
#define XPT2046_CS 33 // T_CS
#define USE_XPT2046_BITBANG
#define XPT_ROTATION 0
/*
// I2C pins
#define I2C_SDA 27
#define I2C_SCL 22
*/
//RGB LED
#define RGB_LED_R 4
#define RGB_LED_G 16
#define RGB_LED_B 17
#define USE_RGB_LED PRESENT
//SD Pins
#define SD_CS 5
// Encoder
#define ENCODER_A 22
#define ENCODER_B 27
#define ENCODER_SW 35
#endif
#ifdef USE_CYD_24
// Touchscreen pins
#define XPT2046_IRQ 36 // T_IRQ
#define XPT2046_MOSI 13 // T_DIN
#define XPT2046_MISO 12 // T_OUT
#define XPT2046_CLK 14 // T_CLK
#define XPT2046_CS 33 // T_CS
#define USE_XPT2046_SPI
#define XPT_ROTATION 0
/*
// I2C pins
#define I2C_SDA 21
#define I2C_SCL 22
*/
//RGB LED
#define RGB_LED_R 4
#define RGB_LED_G 17
#define RGB_LED_B 16
#define USE_RGB_LED PRESENT
//SD Pins
#define SD_CS 5
// Encoder
#define ENCODER_A 22
#define ENCODER_B 21
#define ENCODER_SW 35
#endif
#endif

View File

@@ -0,0 +1,956 @@
/* 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.
*/
////////////////////////////////////////////////////////////
// ***** ECoS SOPORTE *****
////////////////////////////////////////////////////////////
void sendMsgECOS (char *buf) {
bool recvAnswer;
uint32_t timeoutECoS;
Client.write(buf);
#ifdef DEBUG
Serial.print(F("Send: "));
Serial.println(buf);
#endif
timeoutECoS = millis();
recvAnswer = false;
while ((millis() - timeoutECoS < 50) && (!recvAnswer)) // wait answer for 50ms
recvAnswer = ECoSProcess();
}
void requestViews () {
snprintf(cmd, 64, "get(%d, info)\n", ID_ECOS); // ECoS info
sendMsgECOS(cmd);
snprintf(cmd, 64, "request(%d, view)\n", ID_ECOS); // ECoS manager (power)
sendMsgECOS(cmd);
snprintf(cmd, 64, "request(%d, view)\n", ID_LOKMANAGER); // Lok manager
sendMsgECOS(cmd);
snprintf(cmd, 64, "request(%d, view)\n", ID_S88FEEDBACK); // S88 feedback
sendMsgECOS(cmd);
getStatusECoS();
}
void getStatusECoS() {
snprintf(cmd, 64, "get(%d, status)\n", ID_ECOS); // Power status
sendMsgECOS(cmd);
}
void requestLocoList() {
snprintf(cmd, 64, "queryObjects(%d, addr, name)\n", ID_LOKMANAGER); // only address and name, protocol not needed at the moment
sendMsgECOS(cmd);
}
void infoLocomotoraECoS (unsigned int ID) {
byte n;
if (ID > 999) {
bitClear(locoData[myLocoData].mySteps, 3);
snprintf(cmd, 64, "request(%d, view, control[events])\n", ID); // View Locomotive data updates
sendMsgECOS(cmd);
snprintf(cmd, 64, "get(%d, speed, dir, speedstep)\n", ID); // Speed, dir & steps
sendMsgECOS(cmd);
for (n = 0; n < 29; n++) {
snprintf(cmd, 64, "get(%d, func[%d])\n", ID, n); // Functions
sendMsgECOS(cmd);
if (appVer > 2)
snprintf(cmd, 64, "get(%d, funcicon[%d])\n", ID, n); // Icon functions
else
snprintf(cmd, 64, "get(%d, funcsymbol[%d])\n", ID, n); // Icon functions
sendMsgECOS(cmd);
}
setTimer (TMR_FNC_ECOS, 10, TMR_ONESHOT);
}
}
void releaseLocoECoS() {
if (locoData[myLocoData].myLocoID) {
snprintf(cmd, 64, "release(%d, control)\n", locoData[myLocoData].myLocoID); // release control
sendMsgECOS(cmd);
snprintf(cmd, 64, "release(%d, view)\n", locoData[myLocoData].myLocoID); // release updates
sendMsgECOS(cmd);
}
}
void checkControlLoco() {
if (bitRead(locoData[myLocoData].mySteps, 3)) {
bitClear(locoData[myLocoData].mySteps, 3);
snprintf(cmd, 64, "request(%d, control[events], force)\n", locoData[myLocoData].myLocoID); // force control
sendMsgECOS(cmd);
}
}
void changeDirectionECoS() {
checkControlLoco();
snprintf(cmd, 64, "set(%d, dir[%d])\n", locoData[myLocoData].myLocoID, (locoData[myLocoData].myDir & 0x80) ? 0 : 1); // direction (1=rückwärts).
sendMsgECOS(cmd);
}
void locoOperationSpeedECoS() {
checkControlLoco();
snprintf(cmd, 64, "set(%d, speed[%d])\n", locoData[myLocoData].myLocoID, locoData[myLocoData].mySpeed); // Speed
sendMsgECOS(cmd);
}
byte getCurrentStepECoS() {
return (mySpeedStep);
}
void funcOperationsECoS (byte fnc) {
byte stat;
checkControlLoco();
stat = (bitRead(locoData[myLocoData].myFunc.Bits, fnc)) ? 1 : 0;
snprintf(cmd, 64, "set(%d, func[%d,%d])\n", locoData[myLocoData].myLocoID, fnc, stat); // Function
sendMsgECOS(cmd);
}
void setAccessoryECoS(unsigned int FAdr, int pair, bool activate) {
char pos;
if (activate) {
pos = (pair > 0) ? 'g' : 'r';
snprintf(cmd, 64, "set(%d, switch[%d%c])\n", ID_SWMANAGER, FAdr, pos); // Directly setting a port.
sendMsgECOS(cmd);
}
}
void resumeOperationsECoS () {
snprintf(cmd, 64, "set(%d, go)\n", ID_ECOS); // Track on
sendMsgECOS(cmd);
}
void emergencyOffECoS() {
snprintf(cmd, 64, "set(%d, stop)\n", ID_ECOS); // Track off
sendMsgECOS(cmd);
}
void showTrkECoS() {
if (csStatus > 0) {
stopTimer (TMR_POWER);
iconData[ICON_POWER].color = COLOR_GREEN;
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
newEvent(OBJ_ICON, ICON_POWER, EVNT_DRAW);
if (isWindow(WIN_STA_PLAY)) {
fncData[FNC_STA_RAYO].state = false;
newEvent(OBJ_FNC, FNC_STA_RAYO, EVNT_DRAW);
}
if (isWindow(WIN_ALERT)) {
switch (errType) {
case ERR_SERV:
case ERR_STOP:
case ERR_CV:
closeWindow(WIN_ALERT);
break;
}
}
}
else {
iconData[ICON_POWER].color = COLOR_RED; // Power off
setTimer (TMR_POWER, 5, TMR_PERIODIC); // Flash power icon
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
newEvent(OBJ_ICON, ICON_POWER, EVNT_DRAW);
if (isWindow(WIN_STA_PLAY)) {
fncData[FNC_STA_RAYO].state = true;
newEvent(OBJ_FNC, FNC_STA_RAYO, EVNT_DRAW);
}
}
}
void readCVECoS (unsigned int adr, byte stepPrg) {
if (!modeProg) { // Read only in Direct mode
snprintf(cmd, 64, "request(%d, view)\n", ID_PRGMANAGER); // Programming manager
sendMsgECOS(cmd);
requestedCV = true;
snprintf(cmd, 64, "set(%d, mode[readdccdirect], cv[%d])\n", ID_PRGMANAGER, adr);
sendMsgECOS(cmd);
}
progStepCV = stepPrg;
}
void writeCVECoS (unsigned int adr, unsigned int data, byte stepPrg) {
if (modeProg) {
snprintf(cmd, 64, "request(%d, view)\n", ID_POMMANAGER); // PoM Programming manager
sendMsgECOS(cmd);
snprintf(cmd, 64, "set(%d, mode[writedccpomloco], addr[%d], cv[%d,%d])\n", ID_POMMANAGER, locoData[myLocoData].myAddr.address, adr, data); // Operations Mode Programming byte mode write request
}
else {
snprintf(cmd, 64, "request(%d,view)\n", ID_PRGMANAGER); // Programming manager
sendMsgECOS(cmd);
snprintf(cmd, 64, "set(%d, mode[writedccdirect], cv[%d,%d])\n", ID_PRGMANAGER, adr, data);
}
requestedCV = true;
sendMsgECOS(cmd);
progStepCV = stepPrg;
DEBUG_MSG("Write CV%d = %d", adr, data);
}
void exitProgrammingECoS() {
if (requestedCV) {
snprintf(cmd, 64, "release(%d,view)\n", ID_PRGMANAGER); // Programming manager
sendMsgECOS(cmd);
snprintf(cmd, 64, "release(%d, view)\n", ID_POMMANAGER); // PoM Programming manager
sendMsgECOS(cmd);
requestedCV = false;
}
}
////////////////////////////////////////////////////////////
// ***** ECoS DECODE *****
////////////////////////////////////////////////////////////
bool ECoSProcess() {
char chr;
bool rcvMsg;
rcvMsg = false;
while (Client.available()) {
chr = Client.read();
if (chr == '\n') {
if (inputLength > 0) {
inputBuffer[inputLength] = 0;
rcvMsg = ECoSDecode();
}
inputLength = 0;
}
else {
inputBuffer[inputLength++] = chr;
if (inputLength >= BUF_LNG) // line too long, discard
inputLength = 0;
}
}
yield();
if (progFinished) { // fin de lectura/programacion CV
progFinished = false;
endProg();
}
return rcvMsg;
}
//============= Scanning ==================
// Simplified scanning based on C compiler method. https://github.com/DoctorWkt/acwj
char getChar () { // get a chr form input buffer
return inputBuffer[posFile++];
}
void putBack(char c) { // Put back an unwanted character
putBackChr = c;
posFile--; // set reading position back
}
char next() { // Get the next character from the input stream.
char c;
if (putBackChr) { // Use the character put back if there is one
c = putBackChr;
putBackChr = 0;
posFile++;
}
else
c = getChar(); // Read from input stream
return c;
}
char skip() { // Skip past input that we don't need to deal with, i.e. whitespace, newlines. Return the first character we do need to deal with.
char c;
c = next();
while (' ' == c || '\t' == c || '\n' == c || '\r' == c) {
c = next();
}
return (c);
}
void discardLine() { // ignore rest of the line
putBackChr = 0;
inputBuffer[posFile] = 0;
}
int scanInt(char c) { // Scan and return an integer literal value from the input stream.
int val;
val = 0;
while (isDigit(c)) { // Convert each character into an int value
val = val * 10 + (c - '0');
c = next();
}
putBack(c); // We hit a non-integer character, put it back.
return val;
}
int scanIdent(int c, char *buf, int lim) { // Scan an identifier from the input file and store it in buf[]. Return the identifier's length
int i = 0;
while (isalpha(c) || isdigit(c) || (c == '-') || (c == '_')) { // Allow digits, alpha and underscore -
if (lim - 1 == i) { // Error if we hit the identifier length limit, else append to buf[] and get next character
return (0);
} else if (i < lim - 1) {
buf[i++] = c;
}
c = next();
}
putBack(c); // We hit a non-valid character, put it back. NUL-terminate the buf[] and return the length
buf[i] = '\0';
return (i);
}
int scanStr (char *buf) { // Scan in a string literal from the input file, and store it in buf[]. Return the length of the string.
int i, c;
for (i = 0; i < TEXTLEN - 1; i++) { // Loop while we have enough buffer space
// Get the next char and append to buf
// Return when we hit the ending double quote
if ((c = next()) == '"') {
buf[i] = 0;
return (i);
}
buf[i] = c;
}
// Ran out of buf[] space
return (0);
}
int ident2HEX (char *buf, int *value) { // convert an idetifier (xNNNN) to hex value
int val, n;
char c;
val = 0;
n = 1;
if (buf[0] == 'x') {
while (buf[n]) {
c = buf[n++];
val *= 16;
if (isDigit(c)) {
val += (c - '0');
}
else {
c &= 0xDF;
val += ((c - 'A') + 10);
}
}
*value = val;
return 1;
}
return 0;
}
// Given a word from the input, return the matching keyword token number or 0 if it's not a keyword.
// Switch on the first letter so that we don't have to waste time strcmp()ing against all the keywords.
int keyword(char *s) {
switch (*s) {
case 'R':
if (!strcmp(s, "REPLY"))
return (T_REPLY);
break;
case 'E':
if (!strcmp(s, "END"))
return (T_END);
if (!strcmp(s, "EVENT"))
return (T_EVENT);
if (!strcmp(s, "ECoS2"))
return (T_ECOS2);
if (!strcmp(s, "ECoS"))
return (T_ECOS);
break;
case 'O':
if (!strcmp(s, "OK"))
return (T_OK);
break;
case 'C':
if (!strcmp(s, "CONTROL_LOST"))
return (T_LOST);
if (!strcmp(s, "CentralStation"))
return (T_CS1);
break;
case 'G':
if (!strcmp(s, "GO"))
return (T_GO);
break;
case 'S':
if (!strcmp(s, "STOP"))
return (T_STOP);
if (!strcmp(s, "SHUTDOWN"))
return (T_SHUTDWN);
break;
case 'A':
if (!strcmp(s, "ApplicationVersion"))
return (T_APPV);
break;
case 'a':
if (!strcmp(s, "addr"))
return (T_ADDR);
break;
case 'c':
if (!strcmp(s, "control"))
return (T_CONTROL);
if (!strcmp(s, "cv"))
return (T_CV);
break;
case 'd':
if (!strcmp(s, "dir"))
return (T_DIR);
break;
case 'e':
if (!strcmp(s, "error"))
return (T_ERROR);
break;
case 'f':
if (!strcmp(s, "funcsymbol")) // ECoS replies 'funcsymbol'
return (T_FSYMBOL);
if (!strcmp(s, "funcsymb")) // CS1 replies 'funcsymb'
return (T_FSYMB);
if (!strcmp(s, "funcicon"))
return (T_FICON);
if (!strcmp(s, "func"))
return (T_FUNC);
if (!strcmp(s, "force"))
return (T_FORCE);
break;
case 'g':
if (!strcmp(s, "get"))
return (T_GET);
break;
case 'm':
if (!strcmp(s, "msg"))
return (T_MSG);
break;
case 'n':
if (!strcmp(s, "name"))
return (T_NAME);
break;
case 'o':
if (!strcmp(s, "ok"))
return (T_CVOK);
if (!strcmp(s, "other"))
return (T_OTHER);
break;
case 'p':
if (!strcmp(s, "protocol"))
return (T_PROT);
break;
case 'q':
if (!strcmp(s, "queryObjects"))
return (T_QOBJ);
break;
case 'r':
if (!strcmp(s, "request"))
return (T_REQ);
if (!strcmp(s, "release"))
return (T_RELEASE);
break;
case 's':
if (!strcmp(s, "status2"))
return (T_STATUS2);
if (!strcmp(s, "status"))
return (T_STATUS);
if (!strcmp(s, "state"))
return (T_STATE);
if (!strcmp(s, "speedstep"))
return (T_STEPS);
if (!strcmp(s, "speed"))
return (T_SPEED);
if (!strcmp(s, "set"))
return (T_SET);
if (!strcmp(s, "switch"))
return (T_SWITCH);
break;
case 'v':
if (!strcmp(s, "view"))
return (T_VIEW);
break;
}
return (0);
}
//=========== Lexical ===============
int scan(struct token *t) { // Scan and return the next token found in the input. Return 1 if token valid, 0 if no tokens left.
char c;
c = skip(); // Skip whitespace
switch (c) { // Determine the token based on the input character
case 0: // EOF
return (0);
case '(':
t->token = T_LPARENT;
t->intvalue = posFile;
break;
case ')':
t->token = T_RPARENT;
t->intvalue = posFile;
break;
case '[':
t->token = T_LBRACKET;
t->intvalue = posFile;
break;
case ']':
t->token = T_RBRACKET;
t->intvalue = posFile;
break;
case '<':
t->token = T_START;
t->intvalue = posFile;
break;
case '>':
t->token = T_ENDB;
t->intvalue = posFile;
break;
case ',':
t->token = T_COMMA;
t->intvalue = posFile;
break;
case '#':
t->token = T_NULL;
t->intvalue = posFile;
discardLine();
break;
case '"':
scanStr(Text);
t->token = T_STRLIT;
break;
default:
if (isDigit(c)) { // If it's a digit, scan the literal integer value in
t->intvalue = scanInt(c);
t->token = T_INTLIT;
break;
}
if (isAlpha(c)) {
scanIdent(c, Text, TEXTLEN);
tokenType = keyword(Text);
if (tokenType) {
t->token = tokenType;
break;
}
t->token = T_IDENT; // Not a recognised keyword, so it must be an identifier
t->intvalue = Text[0];
break;
}
return (0);
break;
}
//DEBUG_MSG("Token found: %s", tokstr[T.token]);
return (1); // We found a token
}
//----------------------------------------------------------
bool ECoSDecode() {
bool endMsg;
#ifdef DEBUG
Serial.print(F("Recv: "));
Serial.println(inputBuffer);
#endif
posFile = 0;
putBackChr = 0;
endMsg = false;
while (scan(&T)) {
switch (msgDecodePhase) {
case MSG_WAIT: // wait '<' for start of msg
if (T.token == T_START)
msgDecodePhase = MSG_START;
break;
case MSG_START:
switch (T.token) {
case T_REPLY: // receiving a REPLY
msgDecodePhase = MSG_REPLY;
break;
case T_EVENT: // receiving an EVENT
msgDecodePhase = MSG_EVENT;
break;
case T_END: // receiving END
msgDecodePhase = MSG_END;
break;
default:
msgDecodePhase = MSG_WAIT; // others not supported
break;
}
idManager = 0;
break;
case MSG_END:
if (T.token == T_INTLIT) { // get the error code
errCode = T.intvalue;
discardLine();
msgDecodePhase = MSG_WAIT; // discard rest of message
switch (errCode) {
case 25: // NERROR_NOCONTROL
bitSet(locoData[myLocoData].mySteps, 3);
break;
case 15: // NERROR_UNKNOWNID
if (requestedCV) {
if (progStepCV != PRG_IDLE) {
CVdata = 0x0600;
progFinished = true;
}
}
break;
}
DEBUG_MSG("END Error code: %d", errCode);
}
endMsg = true;
break;
case MSG_EVENT:
if (T.token == T_INTLIT) // get the event manager
idManager = T.intvalue;
discardLine();
msgDecodePhase = MSG_EVENTBODY; // discard rest of line
DEBUG_MSG("Manager ID: %d", idManager);
if ((idManager == ID_PRGMANAGER) || (idManager == ID_POMMANAGER)) {
lastNumValue = 0x0600;
}
break;
case MSG_REPLY: // <REPLY get(1,status)>
idCommand = T.token; // get ...
scan(&T); // (
scan(&T); // id manager
if (T.token == T_INTLIT) {
idManager = T.intvalue;
//DEBUG_MSG("Reply: %s id: %d", tokstr[idCommand], idManager);
if ((idManager == ID_LOKMANAGER) && (idCommand == T_QOBJ)) { // list of loks
numLoks = 0;
initLocos();
DEBUG_MSG("Roster list cleared");
}
/*
if ((idManager == ID_SWMANAGER) && (idCommand == T_GET)) { // info of turnout <REPLY get(11, switch[12g])>
//DEBUG_MSG("Switch manager get");
scan(&T); // ,
scan(&T); // switch
if (T.token == T_SWITCH) {
//DEBUG_MSG("Get turnout");
scan(&T); // [
scan(&T); // 12g
if (T.token == T_INTLIT) // 12
scan(&T);
if (T.token == T_IDENT) { // Text is "g" or could be "DCC12g"
lastTxtChar = Text[strlen(Text) - 1];
DEBUG_MSG("Info turnout pos: %s - %c", Text, lastTxtChar);
}
}
}
*/
}
discardLine();
msgDecodePhase = MSG_REPLYBODY;
break;
case MSG_EVENTBODY:
switch (T.token) {
case T_START:
msgDecodePhase = MSG_START; // End of body
break;
case T_INTLIT:
switch (idManager) { // id
case ID_ECOS: // ECoS events
decodeEventECoS();
break;
case ID_PRGMANAGER: // Programming events
case ID_POMMANAGER:
decodeEventCV(); // decodes CV answer
break;
default:
if (idManager == locoData[myLocoData].myLocoID) { // current loco events
if (T.intvalue == locoData[myLocoData].myLocoID)
decodeReplyLoco();
}
else {
/*
if ((idManager >= ID_S88FEEDBACK) && (idManager < (ID_S88FEEDBACK + 32))) // S88 events
decodeEventS88Feedback();
else
*/
discardLine();
}
break;
}
break;
default: // other types of start of line not supported
discardLine();
break;
}
break;
case MSG_REPLYBODY:
switch (T.token) {
case T_START:
msgDecodePhase = MSG_START; // End of body
break;
case T_INTLIT:
switch (idManager) { // id
case ID_ECOS: // decodes answer to: get(1,...) / request
if (idCommand == T_GET) {
decodeEventECoS();
}
else
discardLine();
break;
case ID_LOKMANAGER: // decodes answer to: queryObjects(10,...)
decodeLokManager();
break;
case ID_SWMANAGER:
if (idCommand == T_GET) { // decodes answer to: get(11,..)
//decodeSwitchManager();
}
else
discardLine();
break;
default:
/*
if ((idManager >= ID_S88FEEDBACK) && (idManager < (ID_S88FEEDBACK + 32)))
decodeEventS88Feedback();
*/
if (idManager == locoData[myLocoData].myLocoID)
decodeReplyLoco(); // decodes answer to: get(1xxx,...) / set..
else
discardLine();
break;
}
break;
default: // other types of start of line not supported
discardLine();
break;
}
break;
}
}
return endMsg;
}
void decodeEventECoS () {
if (T.intvalue == ID_ECOS) {
scan(&T);
switch (T.token) {
case T_STATUS: // 1 status[GO]
scan(&T); // [
scan(&T); // GO / STOP / SHUTDOWN
if (T.token == T_GO) {
csStatus = 1;
showTrkECoS();
DEBUG_MSG("Power On");
}
else {
csStatus = 0;
showTrkECoS();
DEBUG_MSG("Power Off");
}
break;
case T_APPV: // 1 ApplicationVersion[2.0.4]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
appVer = T.intvalue;
DEBUG_MSG("Version: %d", appVer)
}
break;
/*
case T_CS1: // 1 CentralStation
case T_ECOS: // 1 ECoS
case T_ECOS2: // 1 ECoS2
typeCmdStation = T.token;
DEBUG_MSG("CS Type: %s", tokstr[typeCmdStation]);
break;
*/
}
discardLine();
}
}
void decodeEventCV() {
while (scan(&T)) {
switch (T.token) {
case T_INTLIT:
lastNumValue = T.intvalue;
break;
case T_CVOK:
CVdata = lastNumValue;
progFinished = true;
discardLine();
break;
case T_ERROR:
CVdata = 0x0600;
progFinished = true;
discardLine();
break;
}
}
}
void parseLokAddrName(int pos) {
scan(&T); // 1003 addr[78] name["W. K"]
switch (T.token) {
case T_ADDR:
scan(&T);
scan(&T);
if (T.token == T_INTLIT)
locoData[pos].myAddr.address = T.intvalue;
scan(&T);
DEBUG_MSG("Addr: %d", locoData[pos].myAddr.address);
break;
case T_NAME:
scan(&T);
scan(&T);
if (T.token == T_STRLIT)
snprintf(locoData[pos].myName, NAME_LNG + 1, "%s", Text);
scan(&T);
DEBUG_MSG("Name: %s", locoData[pos].myName);
break;
}
}
void decodeLokManager () {
int id, fnc, num;
switch (idCommand) {
case T_QOBJ:
if (numLoks < LOCOS_IN_STACK) {
locoData[numLoks].myLocoID = T.intvalue; // 1003 addr[78] name["W. K"]
DEBUG_MSG("ID: %d", locoData[numLoks].myLocoID);
parseLokAddrName(numLoks); // addr
parseLokAddrName(numLoks); // name
pushLoco(locoData[numLoks].myLocoID); //.myAddr.address);
numLoks++;
}
discardLine();
break;
default:
discardLine();
break;
}
}
void decodeReplyLoco() {
int numFunc;
scan(&T);
switch (T.token) { // <REPLY get(1xxx,... )> / <EVENT 1xxx>
case T_MSG: // 1018 msg[CONTROL_LOST]
scan(&T);
scan(&T);
if (T.token == T_LOST) {
bitSet(locoData[myLocoData].mySteps, 3);
DEBUG_MSG("Lost control of %d", locoData[myLocoData].myLocoID);
}
break;
case T_CONTROL: // 1000 control[other]
scan(&T);
scan(&T);
if (T.token == T_OTHER) {
bitSet(locoData[myLocoData].mySteps, 3);
DEBUG_MSG("Control %d by other", locoData[myLocoData].myLocoID);
}
break;
case T_SPEED: // 1000 speed[6]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
locoData[myLocoData].mySpeed = T.intvalue;
updateSpeedHID();
DEBUG_MSG("Speed: %d", T.intvalue);
}
break;
case T_STEPS: // 1000 speedstep[2]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
mySpeedStep = T.intvalue;
DEBUG_MSG("Steps: %d", T.intvalue);
}
break;
case T_DIR: // 1000 dir[0]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
locoData[myLocoData].myDir = (T.intvalue > 0) ? 0 : 0x80;
updateSpeedDir();
DEBUG_MSG("Dir: %d", T.intvalue);
}
break;
case T_FUNC: // 1015 func[0, 0]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
numFunc = T.intvalue;
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
if (T.intvalue > 0)
bitSet(locoData[myLocoData].myFunc.Bits, numFunc);
else
bitClear(locoData[myLocoData].myFunc.Bits, numFunc);
DEBUG_MSG("Func%d: %d", numFunc, T.intvalue);
updateFuncState(isWindow(WIN_THROTTLE));
}
}
break;
/*
case T_FSYMBOL: // 1015 funcsymbol[0,2]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
numFunc = T.intvalue;
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
locoData[myLocoData].myFuncIcon[numFunc] = FunktionsTastenSymbole[T.intvalue & 0x7F] << 1;
DEBUG_MSG("Func: %d, Icon: %d", numFunc, T.intvalue)
}
}
break;
*/
case T_FSYMB: // 1015 funcsymb[0,2]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
numFunc = T.intvalue;
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
locoData[myLocoData].myFuncIcon[numFunc] = FunktionsTastenSymboleCS1[T.intvalue & 0x3F] << 1;
DEBUG_MSG("Func: %d, Icon: %d", numFunc, T.intvalue)
}
}
break;
case T_FICON: // 1000 funcicon[0,3,light]
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
numFunc = T.intvalue;
scan(&T);
scan(&T);
if (T.token == T_INTLIT) {
locoData[myLocoData].myFuncIcon[numFunc] = FunktionsTastenSymbole[T.intvalue & 0x7F] << 1;
DEBUG_MSG("Func: %d, Icon: %d", numFunc, T.intvalue)
}
}
break;
}
discardLine();
}

View File

@@ -0,0 +1,197 @@
/* 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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,416 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
bool checkName(char *fileName) {
bool result;
uint16_t lng;
result = false;
lng = strlen(fileName);
if (lng > 4) {
if ((fileName[lng - 4] == '.') && (fileName[lng - 3] == 'c') && (fileName[lng - 2] == 's') && (fileName[lng - 1] == 'v'))
return true;
}
return result;
}
bool saveLocoData(fs::FS &fs, uint16_t pos) { // save loco data in .csv file
char field[30];
uint16_t cnt;
bool dataOK, isDir;
File myFile;
dataOK = false;
myFile = fs.open("/loco");
if (myFile) {
isDir = myFile.isDirectory();
myFile.close();
if (!isDir)
return dataOK;
DEBUG_MSG("/loco is a directory");
}
else {
DEBUG_MSG("Directory /loco not found. Creating...")
fs.mkdir("/loco");
}
sprintf (field, "/loco/%d.csv", locoData[pos].myAddr.address);
myFile = fs.open(field, FILE_WRITE);
if (myFile) {
DEBUG_MSG("File %s opened for writting", field);
getLabelTxt(LBL_NAME, field); // Header
myFile.print(field);
myFile.print(CSV_FILE_DELIMITER);
getLabelTxt(LBL_IMAGE, field);
myFile.print(field);
myFile.print(CSV_FILE_DELIMITER);
getLabelTxt(LBL_VMAX, field);
myFile.print(field);
for (cnt = 0; cnt < 29; cnt++) {
myFile.print(CSV_FILE_DELIMITER);
myFile.print('F');
myFile.print(cnt);
}
myFile.print("\r\n");
myFile.print(locoData[pos].myName); // Loco data
myFile.print(CSV_FILE_DELIMITER);
myFile.print(locoData[pos].myLocoID);
myFile.print(CSV_FILE_DELIMITER);
myFile.print(locoData[pos].myVmax);
for (cnt = 0; cnt < 29; cnt++) {
myFile.print(CSV_FILE_DELIMITER);
myFile.print(locoData[pos].myFuncIcon[cnt] >> 1);
}
myFile.print("\r\n");
myFile.close();
dataOK = true;
}
return dataOK;
}
void deleteLocoData (fs::FS &fs, uint16_t num) {
char line[200];
sprintf (line, "/loco/%d.csv", num);
if (fs.remove(line)) {
DEBUG_MSG("File %s deleted", line);
}
else {
DEBUG_MSG("File %s delete failed", line);
}
}
bool readLocoData(fs::FS &fs, uint16_t num, uint16_t pos) { // read loco data from .csv file
char line[200];
bool dataOK;
uint16_t n;
File myFile;
dataOK = false;
sprintf (line, "/loco/%d.csv", num);
myFile = fs.open(line);
if (myFile) {
if (readCSV(myFile, line, sizeof(line), false)) { // skip header line
if (readCSV(myFile, locoData[pos].myName, sizeof(locoData[pos].myName), true)) { // read data field: Name
if (readCSV(myFile, line, sizeof(line), true)) { // read data field: Picture
locoData[pos].myLocoID = atoi(line);
if (readCSV(myFile, line, sizeof(line), true)) {
locoData[pos].myVmax = atoi(line);
DEBUG_MSG("%d %s %d %d", num, locoData[pos].myName, locoData[pos].myLocoID, locoData[pos].myVmax)
for (n = 0; n < 29; n++) {
if (!readCSV(myFile, line, sizeof(line), true)) { // read data field: Functions
myFile.close();
return false;
}
locoData[pos].myFuncIcon[n] = (uint8_t)(atoi(line) << 1);
//DEBUG_MSG("Func: %d - %d", n, locoData[pos].myFuncIcon[n])
}
locoData[pos].myAddr.address = num;
dataOK = true;
}
}
}
}
myFile.close();
}
return dataOK;
}
bool readCSV(File & f, char* line, uint16_t maxLen, bool getField) {
uint16_t n; // read field or line from CSV file
char chr;
n = 0;
yield();
while (n < maxLen) {
chr = f.read();
switch (chr) {
case 0:
line[n] = '\0';
return false; // EOF
break;
case '\r':
line[n] = '\0';
break;
case '\n':
line[n] = '\0';
return true;
break;
case ',':
case ';':
if (getField) {
line[n] = '\0';
return true;
}
break;
default:
line[n++] = chr;
break;
}
}
return false; // too long
}
void loadLocoFiles(fs::FS &fs, const char * dirname) {
uint16_t pos, adr;
char nameFile[50];
File myFile;
if (wifiSetting.protocol == CLIENT_ECOS)
return;
File root = fs.open(dirname);
if (!root) {
//DEBUG_MSG("Failed to open directory %s", dirname);
return;
}
if (!root.isDirectory()) {
//DEBUG_MSG("%s Not a directory", dirname);
return;
}
pos = 0;
File file = root.openNextFile();
while (file) {
if (! file.isDirectory()) {
if (pos < LOCOS_IN_STACK) {
sprintf(nameFile, "%s", file.name());
adr = atoi(nameFile);
//DEBUG_MSG("%d %s", adr, nameFile);
if (readLocoData(fs, adr, pos++))
pushLoco(adr);
}
}
file = root.openNextFile();
}
}
void initImageList() {
uint16_t n, maxImg;
uint16_t pos, id;
char nameFile[50];
File myFile;
for (n = 0; n < MAX_LOCO_IMAGE; n++) // add to list system images
locoImages[n] = (n < (MAX_SYS_LPIC - 1)) ? n + 1 : 0;
if (wifiSetting.protocol != CLIENT_ECOS) {
if (sdDetected) { // add to list user images from SD
File root = SD.open("/image");
if (root) {
if (root.isDirectory()) {
pos = MAX_SYS_LPIC - 1;
File file = root.openNextFile();
while (file) {
if (! file.isDirectory()) {
if (pos < MAX_LOCO_IMAGE) {
sprintf(nameFile, "%s", file.name());
id = atoi(nameFile);
if (id >= 1000) {
locoImages[pos++] = id;
}
}
}
file = root.openNextFile();
}
}
}
}
}
locoImageIndex = 0;
}
void populateImageList() {
uint16_t n;
for (n = 0; n < 6; n++)
lpicData[LPIC_SEL_IMG1 + n].id = locoImages[locoImageIndex + n];
}
bool saveCurrAccPanel(fs::FS &fs) {
char field[30];
uint16_t cnt;
bool dataOK, isDir;
File myFile;
dataOK = false;
myFile = fs.open("/acc");
if (myFile) {
isDir = myFile.isDirectory();
myFile.close();
if (!isDir)
return dataOK;
DEBUG_MSG("/acc is a directory");
}
else {
DEBUG_MSG("Directory /acc not found. Creating...")
fs.mkdir("/acc");
}
sprintf (field, "/acc/%d.csv", currPanel);
myFile = fs.open(field, FILE_WRITE);
if (myFile) {
DEBUG_MSG("File %s opened for writting", field);
getLabelTxt(LBL_ACC_TYPE, field); // Header
myFile.print(field);
myFile.print(CSV_FILE_DELIMITER);
getLabelTxt(LBL_ACC_ADDR, field);
myFile.print(field);
myFile.print(CSV_FILE_DELIMITER);
getLabelTxt(LBL_ACC_ADDR, field);
myFile.print(field);
myFile.print(CSV_FILE_DELIMITER);
getLabelTxt(LBL_ACC_NAME, field);
myFile.print(field);
myFile.print(CSV_FILE_DELIMITER);
getLabelTxt(LBL_BITS, field);
myFile.print(field);
myFile.print(CSV_FILE_DELIMITER);
myFile.print("\r\n");
for (cnt = 0; cnt < 16; cnt++) {
myFile.print(accPanel[cnt].type); // Acc data
myFile.print(CSV_FILE_DELIMITER);
myFile.print(accPanel[cnt].addr);
myFile.print(CSV_FILE_DELIMITER);
myFile.print(accPanel[cnt].addr2);
myFile.print(CSV_FILE_DELIMITER);
myFile.print(accPanel[cnt].accName);
myFile.print(CSV_FILE_DELIMITER);
myFile.print(accPanel[cnt].activeOutput);
myFile.print(CSV_FILE_DELIMITER);
myFile.print("\r\n");
}
myFile.close();
dataOK = true;
}
return dataOK;
}
bool loadAccPanel(fs::FS & fs) {
char line[200];
bool dataOK;
uint16_t cnt = 0;
File myFile;
dataOK = false;
sprintf (line, "/acc/%d.csv", currPanel);
myFile = fs.open(line);
if (myFile) {
if (readCSV(myFile, line, sizeof(line), false)) { // skip header line
for (cnt = 0; cnt < 16; cnt++) {
readCSV(myFile, line, sizeof(line), true);
accPanel[cnt].type = (accType)atoi(line);
readCSV(myFile, line, sizeof(line), true);
accPanel[cnt].addr = atoi(line);
readCSV(myFile, line, sizeof(line), true);
accPanel[cnt].addr2 = atoi(line);
readCSV(myFile, line, sizeof(line), true);
snprintf(accPanel[cnt].accName, ACC_LNG + 1, line);
readCSV(myFile, line, sizeof(line), false);
accPanel[cnt].activeOutput = atoi(line);
DEBUG_MSG("Line %d: %d, %d, %d, \"%s\", %d", cnt, accPanel[cnt].type, accPanel[cnt].addr, accPanel[cnt].addr2, accPanel[cnt].accName, accPanel[cnt].activeOutput)
}
}
myFile.close();
dataOK = true;
}
return dataOK;
}
bool saveAccPanelNames(fs::FS & fs) {
char field[30];
uint16_t cnt;
bool dataOK, isDir;
File myFile;
dataOK = false;
myFile = fs.open("/acc");
if (myFile) {
isDir = myFile.isDirectory();
myFile.close();
if (!isDir)
return dataOK;
DEBUG_MSG("/acc is a directory");
}
else {
DEBUG_MSG("Directory /acc not found. Creating...")
fs.mkdir("/acc");
}
sprintf (field, "/acc/panel.csv");
myFile = fs.open(field, FILE_WRITE);
if (myFile) {
DEBUG_MSG("File %s opened for writting", field);
getLabelTxt(LBL_NAME, field); // Header
myFile.print(field);
myFile.print("\r\n");
for (cnt = 0; cnt < 16; cnt++) {
myFile.print(panelNamesBuf[cnt]); // Panel names
myFile.print("\r\n");
}
myFile.close();
dataOK = true;
}
return dataOK;
}
bool loadAccPanelNames(fs::FS & fs) {
char line[200];
bool dataOK;
uint16_t n;
File myFile;
dataOK = false;
sprintf (line, "/acc/panel.csv");
myFile = fs.open(line);
if (myFile) {
if (readCSV(myFile, line, sizeof(line), false)) { // skip header line
for (n = 0; n < 16; n++) {
if (readCSV(myFile, line, sizeof(line), true)) { // read data field
snprintf(panelNamesBuf[n], PANEL_LNG + 1, line);
DEBUG_MSG("%s", panelNamesBuf[n])
}
}
}
myFile.close();
}
else {
for (n = 0; n < 16; n++) // default names
snprintf(panelNamesBuf[n], PANEL_LNG + 1, "Panel %d", n);
}
return dataOK;
}
/*
void listDir(fs::FS &fs, const char * dirname, uint8_t levels) {
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(fs, file.path(), levels - 1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
*/

View File

@@ -0,0 +1,151 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
Very basic Graphical User Interface (GUI) for PacoMouseCYD
All data in absolute coordinates
*/
#include <TFT_eSPI.h> // Graphics and font library for ILI9341 driver chip v2.5.43
#include "FreeSans7pt7b.h"
#include "FreeSansBold6pt7b.h"
#define MAX_OBJ_STACK 100
#define MAX_LABEL_LNG 150
#define TMR_RESOLUTION 100
#define NOT_USED 0xFF
#define OBJ_NOT_FOUND 0xFFFF
////////////////////////////////////////////////////////////
// ***** FONTS *****
////////////////////////////////////////////////////////////
#ifndef LOAD_GFXFF
ERROR_Please_enable_LOAD_GFXFF_in_User_Setup!
#endif
#define GFXFF 1
#define FSS7 &FreeSans7pt7b
#define FSS9 &FreeSans9pt7b
#define FSSB6 &FreeSansBold6pt7b
#define FSSB9 &FreeSansBold9pt7b
#define FSSB12 &FreeSansBold12pt7b
////////////////////////////////////////////////////////////
// ***** COLORS *****
////////////////////////////////////////////////////////////
// Colour definitions for 64K colour mode (RGB565)
// Bits 0..4 -> Blue 0..4
// Bits 5..10 -> Green 0..5
// Bits 11..15 -> Red 0..4
// Assign human-readable names to some common 16-bit color values: http://rinkydinkelectronics.com/calc_rgb565.php
// Examples: https://github.com/newdigate/rgb565_colors
#define COLOR_WHITE 0xFFFF
#define COLOR_BLACK 0x0000
#define COLOR_BLUE 0x001F
#define COLOR_NAVY 0x000F
#define COLOR_AQUA 0x5D1C
#define COLOR_SKYBLUE 0x867D
#define COLOR_RED 0xF882
#define COLOR_DARKRED 0x8800
#define COLOR_PINK 0xF97F
#define COLOR_MAGENTA 0xF81F
#define COLOR_GREEN 0x0780
#define COLOR_GREENYELLOW 0xAFE5
#define COLOR_DARKGREEN 0x03E0
#define COLOR_CYAN 0x07FF
#define COLOR_DARKCYAN 0x03EF
#define COLOR_YELLOW 0xFFE0
#define COLOR_GOLD 0xDD24
#define COLOR_LIGHTGREY 0xC618
#define COLOR_DARKGREY 0x7BEF
#define COLOR_LIGHTBLACK 0x4A49
#define COLOR_SMOKYBLACK 0x1061
#define COLOR_CHARCOAL 0x3A2A
#define COLOR_VIOLET 0x9199
#define COLOR_BROWN 0x8200
#define COLOR_ORANGE 0xFD20
#define COLOR_LIME 0x87E0
#define COLOR_MAROON 0x7800
#define COLOR_PURPLE 0x780F
#define COLOR_OLIVE 0x7BE0
#define COLOR_SILVER 0xA510
#define COLOR_CREAM 0xFFF9
#define COLOR_GHOST_WHITE 0xF7BF
#define COLOR_BACKGROUND 0xB5B6 // 0xB6B6B6
#define COLOR_TRANSPARENT TFT_TRANSPARENT
const uint16_t colorDraw[] = {COLOR_BLACK, COLOR_BLUE, COLOR_RED, COLOR_MAGENTA, COLOR_GREEN, COLOR_CYAN, COLOR_YELLOW, COLOR_WHITE,
COLOR_BACKGROUND, COLOR_TRANSPARENT, COLOR_GHOST_WHITE, COLOR_AQUA, COLOR_CREAM, COLOR_SMOKYBLACK, COLOR_SKYBLUE,
COLOR_GOLD,
};
////////////////////////////////////////////////////////////
// ***** OBJECT *****
////////////////////////////////////////////////////////////
enum objTypeGUI {OBJ_UNDEF, OBJ_TIMER, OBJ_WIN, OBJ_BUTTON, OBJ_LABEL, OBJ_TXT, OBJ_BAR, OBJ_DRAWSTR, OBJ_ICON, OBJ_KEYBOARD, OBJ_SWITCH,
OBJ_GAUGE, OBJ_LPIC, OBJ_FNC, OBJ_SLIDER, OBJ_RADIO, OBJ_CHAR,
};
struct wObj { // Graphic objects
uint16_t objType;
uint16_t objID;
};
struct wObj objStack[MAX_OBJ_STACK]; // Object stack
uint16_t endObjStack;
uint16_t lastWinStack;
////////////////////////////////////////////////////////////
// ***** EVENT *****
////////////////////////////////////////////////////////////
enum EventGUI {EVNT_CLICK, EVNT_DRAW, EVNT_WOPEN, EVNT_WCLOSE, EVNT_TIMER, EVNT_BOOT};
typedef struct { // Events
uint16_t objType;
uint16_t objID;
uint16_t eventID;
} wEvent;
wEvent eventStack[32]; // stack for events (size 32, hardcoded in functions. Don't change!)
uint16_t eventIn;
uint16_t eventOut;
uint16_t eventsPending;
uint16_t lastClickX;
uint16_t lastClickY;
////////////////////////////////////////////////////////////
// ***** TIMER *****
////////////////////////////////////////////////////////////
enum timers {TMR_BLIGHT, TMR_END_LOGO, TMR_POWER, TMR_SPEEDO, TMR_INFO, TMR_WAIT, TMR_STEAM, TMR_ACCESSORY, TMR_SCAN, TMR_FNC_ECOS,
TMR_STA_RUN,
MAX_SYS_TIMER
};
enum timerType {TMR_STOP, TMR_ONESHOT, TMR_PERIODIC};
typedef struct {
uint16_t tmrDelay;
uint16_t tmrCount;
uint16_t type;
} wTimer;
wTimer wTimerStack[MAX_SYS_TIMER];
uint32_t timerSys;
#if (TFT_WIDTH == 240)
#include "gui240x320.h"
#endif
#if (TFT_WIDTH == 320)
#include "gui320x480.h"
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,298 @@
/* 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.
*/
// OPCODES
#define OPC_GPOFF 0x82 // GLOBAL power OFF request
#define OPC_GPON 0x83 // GLOBAL power ON request
#define OPC_LOCO_SPD 0xA0 // SET SLOT speed
#define OPC_LOCO_DIRF 0xA1 // SET SLOT dir,F0-4 state
#define OPC_LOCO_SND 0xA2 // SET SLOT sound functions
#define OPC_LOCO_F9F12 0xA3 // Uhlenbrock
#define OPC_SW_REQ 0xB0 // REQ SWITCH function
#define OPC_SW_REP 0xB1 // Turnout SENSOR state REPORT
#define OPC_INPUT_REP 0xB2 // General SENSOR Input codes
#define OPC_LONG_ACK 0xB4 // Long acknowledge
#define OPC_SLOT_STAT1 0xB5 // WRITE slot stat1
#define OPC_MOVE_SLOTS 0xBA // MOVE slot SRC to DEST
#define OPC_RQ_SL_DATA 0xBB // Request SLOT DATA/status block
#define OPC_SW_STATE 0xBC // REQ state of SWITCH
#define OPC_LOCO_ADR_UHLI 0xBE // REQ loco ADR Uhlenbrock
#define OPC_LOCO_ADR 0xBF // REQ loco ADR
#define OPC_UHLI_FUN 0xD4 // Uhlenbrock
#define OPC_PEER_XFER 0xE5 // move 8 bytes PEER to PEER, SRC->DST
#define OPC_SL_RD_UHLI 0xE6 // SLOT DATA return, 21 bytes Uhlenbrock
#define OPC_SL_RD_DATA 0xE7 // SLOT DATA return, 10 bytes
#define OPC_IMM_PACKET 0xED // SEND n-byte packet immediate
#define OPC_WR_SL_UHLI 0xEE // WRITE SLOT DATA, 21 bytes Uhlenbrock
#define OPC_WR_SL_DATA 0xEF // WRITE SLOT DATA, 10 bytes
// BIT MASK
#define OPC_SW_REP_INPUTS 0x40 // sensor inputs, outputs otherwise
#define OPC_SW_REP_SW 0x20 // switch input, aux input otherwise
#define OPC_SW_REP_HI 0x10 // input is HI, LO otherwise
#define OPC_SW_REP_CLOSED 0x20 // 'Closed' line is ON, OFF otherwise
#define OPC_SW_REP_THROWN 0x10 // 'Thrown' line is ON, OFF otherwise
#define OPC_SW_REQ_DIR 0x20 // switch direction - closed/thrown
#define OPC_SW_REQ_OUT 0x10 // output On/Off
#define OPC_INPUT_REP_SW 0x20 // input is switch input, aux otherwise
#define OPC_INPUT_REP_HI 0x10 // input is HI, LO otherwise
#define STAT1_SL_BUSY 0x20 // BUSY/ACTIVE: bit encoding for SLOT activity
#define STAT1_SL_ACTIVE 0x10
#define GTRK_PROG_BUSY 0x08 // programming track is Busy
#define GTRK_IDLE 0x02 // 0 = Track paused, B'cast EMERG STOP, 1 = Power On
#define GTRK_POWER 0x01 // DCC packets are on and global power is up
// VALUES
#define SLOT_0 0x00 // Slot 0. Identifies command station type if implemented
#define SLOT_FC 0x7B // Fast clock slot
#define SLOT_PRG 0x7C // This slot communicates with the programming track
#define UHLI_PRG_START 0x41 // Intellibox II program task
#define UHLI_PRG_END 0x40
#define LNCV_REQID_CFGREAD 0x1F // LNCV task
#define LNCV_REQID_CFGWRITE 0x20
#define LNCV_REQID_CFGREQUEST 0x21
#define LNCV_FLAG_PRON 0x80
#define LNCV_FLAG_PROFF 0x40
// Message structure to determine the size of a message
typedef struct {
uint8_t command; /* LocoNet Op Code */
uint8_t mesg_size; /* size of the message in bytes */
} szMsg;
/* Turnout sensor state report */
typedef struct swrep_t {
uint8_t command;
uint8_t sn1; /* first byte of report */
uint8_t sn2; /* second byte of report */
uint8_t chksum; /* exclusive-or checksum for the message */
} swRepMsg;
/* Request Switch function */
typedef struct swreq_t {
uint8_t command;
uint8_t sw1; /* first byte of request */
uint8_t sw2; /* second byte of request */
uint8_t chksum; /* exclusive-or checksum for the message */
} swReqMsg;
/* Sensor input report */
typedef struct inputrep_t {
uint8_t command;
uint8_t in1; /* first byte of report */
uint8_t in2; /* second byte of report */
uint8_t chksum; /* exclusive-or checksum for the message */
} inputRepMsg;
/* Slot data request */
typedef struct slotreq_t {
uint8_t command;
uint8_t slot; /* slot number for this request */
uint8_t pad; /* should be zero */
uint8_t chksum; /* exclusive-or checksum for the message */
} slotReqMsg;
/* Read/Write Slot data messages */
typedef struct rwslotdata_t {
uint8_t command;
uint8_t mesg_size; /* ummmmm, size of the message in bytes? */
uint8_t slot; /* slot number for this request */
uint8_t stat; /* slot status */
uint8_t adr; /* loco address */
uint8_t spd; /* command speed */
uint8_t dirf; /* direction and F0-F4 bits */
uint8_t trk; /* track status */
uint8_t ss2; /* slot status 2 (tells how to use ID1/ID2 & ADV Consist*/
uint8_t adr2; /* loco address high */
uint8_t snd; /* Sound 1-4 / F5-F8 */
uint8_t id1; /* ls 7 bits of ID code */
uint8_t id2; /* ms 7 bits of ID code */
uint8_t chksum; /* exclusive-or checksum for the message */
} rwSlotDataMsg;
/* Fast Clock Message */
typedef struct fastclock_t {
uint8_t command;
uint8_t mesg_size; /* ummmmm, size of the message in bytes? */
uint8_t slot; /* slot number for this request */
uint8_t clk_rate; /* 0 = Freeze clock, 1 = normal, 10 = 10:1 etc. Max is 0x7f */
uint8_t frac_minsl; /* fractional minutes. not for external use. */
uint8_t frac_minsh;
uint8_t mins_60; /* 256 - minutes */
uint8_t track_stat; /* track status */
uint8_t hours_24; /* 256 - hours */
uint8_t days; /* clock rollovers */
uint8_t clk_cntrl; /* bit 6 = 1; data is valid clock info */
/* " " 0; ignore this reply */
uint8_t id1; /* id1/id2 is device id of last device to set the clock */
uint8_t id2; /* " " = zero shows not set has happened */
uint8_t chksum; /* exclusive-or checksum for the message */
} fastClockMsg;
/* Programmer Task Message (used in Start and Final Reply, both )*/
typedef struct progtask_t {
uint8_t command;
uint8_t mesg_size; /* ummmmm, size of the message in bytes? */
uint8_t slot; /* slot number for this request - slot 124 is programmer */
uint8_t pcmd; /* programmer command */
uint8_t pstat; /* programmer status error flags in reply message */
uint8_t hopsa; /* Ops mode - 7 high address bits of loco to program */
uint8_t lopsa; /* Ops mode - 7 low address bits of loco to program */
uint8_t trk; /* track status. Note: bit 3 shows if prog track is busy */
uint8_t cvh; /* hi 3 bits of CV# and msb of data7 */
uint8_t cvl; /* lo 7 bits of CV# */
uint8_t data7; /* 7 bits of data to program, msb is in cvh above */
uint8_t pad2;
uint8_t pad3;
uint8_t chksum; /* exclusive-or checksum for the message */
} progTaskMsg;
/* Set slot sound functions */
typedef struct locosnd_t {
uint8_t command;
uint8_t slot; /* slot number for this request */
uint8_t snd; /* sound/function request */
uint8_t chksum; /* exclusive-or checksum for the message */
} locoSndMsg;
/* Set slot direction and F0-F4 functions */
typedef struct locodirf_t {
uint8_t command;
uint8_t slot; /* slot number for this request */
uint8_t dirf; /* direction & function request */
uint8_t chksum; /* exclusive-or checksum for the message */
} locoDirfMsg;
/* Set slot speed functions */
typedef struct locospd_t {
uint8_t command;
uint8_t slot; /* slot number for this request */
uint8_t spd; /* speed request */
uint8_t chksum; /* exclusive-or checksum for the message */
} locoSpdMsg;
/* send packet immediate message */
typedef struct sendpkt_t {
uint8_t command;
uint8_t mesg_size; /* ummmmm, size of the message in bytes? */
uint8_t val7f; /* fixed value of 0x7f */
uint8_t reps; /* repeat count */
uint8_t dhi; /* high bits of data bytes */
uint8_t im1;
uint8_t im2;
uint8_t im3;
uint8_t im4;
uint8_t im5;
uint8_t chksum; /* exclusive-or checksum for the message */
} sendPktMsg;
/* Long ACK message */
typedef struct longack_t {
uint8_t command;
uint8_t opcode; /* op-code of message getting the response (msb=0) */
uint8_t ack1; /* response code */
uint8_t chksum; /* exclusive-or checksum for the message */
} longAckMsg;
/* Write slot status message */
typedef struct slotstat_t {
uint8_t command;
uint8_t slot; /* slot number for this request */
uint8_t stat; /* status to be written */
uint8_t chksum; /* exclusive-or checksum for the message */
} slotStatusMsg;
/* Move/Link Slot Message */
typedef struct slotmove_t {
uint8_t command;
uint8_t src; /* source slot number for the move/link */
uint8_t dest; /* destination slot for the move/link */
uint8_t chksum; /* exclusive-or checksum for the message */
} slotMoveMsg;
typedef struct
{
uint8_t command; // OPC_PEER_XFER for replies, OPC_IMM_PACKET for commands
uint8_t mesg_size; // 15 bytes
uint8_t SRC; // source
uint8_t DSTL; // destination, low byte
uint8_t DSTH; // destination, high byte
uint8_t ReqId; // Request ID, distinguishes commands
uint8_t PXCT1; // MSBs of following data
uint8_t D0; // Data Bytes
uint8_t D1;
uint8_t D2;
uint8_t D3;
uint8_t D4;
uint8_t D5;
uint8_t D6;
} UhlenbrockMsg;
typedef struct
{
uint8_t command; // OPC_SL_RD_UHLI for replies, OPC_WR_SL_UHLI for commands
uint8_t mesg_size; // 21 bytes
uint8_t unk0;
uint8_t slot; // slot number
uint8_t stat; // slot status
uint8_t adr; // loco address
uint8_t adr2; // loco address high
uint8_t trk; // track status
uint8_t spd; // command speed
uint8_t fhi; // function high bits: F12,F20,F28
uint8_t dirf; // direction and F0-F4 bits
uint8_t snd2; // F5..F11
uint8_t snd3; // F13..F19
uint8_t snd4; // F21..F27
uint8_t unk1; // steps???
uint8_t unk2;
uint8_t unk3;
uint8_t unk4;
uint8_t unk5;
uint8_t unk6;
} UhliSlotMsg;
typedef union {
szMsg sz ;
swRepMsg srp ;
swReqMsg srq ;
inputRepMsg ir ;
slotReqMsg sr ;
rwSlotDataMsg sd ;
fastClockMsg fc ;
progTaskMsg pt ;
locoSndMsg ls ;
locoDirfMsg ldf ;
locoSpdMsg lsp ;
sendPktMsg sp ;
longAckMsg lack ;
slotStatusMsg ss ;
slotMoveMsg sm ;
UhlenbrockMsg ub;
UhliSlotMsg usd;
uint8_t data[32] ;
} lnMsg ;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,104 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
////////////////////////////////////////////////////////////
// ***** STATION RUN - MODEL TRAIN GAME FOR KIDS *****
////////////////////////////////////////////////////////////
void updateStationTime (uint16_t seconds) {
snprintf(staTimeBuf, ACC_LNG + 1, "%d:%02d", seconds / 60, seconds % 60);
}
void updateStationTarget() {
staStations = staLevel + 4;
if (staStartTime < 10)
staStartTime = 10;
staTime = staStartTime + ((staLevel - 1) * 10);
}
void newStationCounters (bool ini) {
if (ini) {
staStars = 0;
staLevel = 1;
randomSeed(millis());
}
staCurrStation = 0;
updateStationTarget();
}
uint8_t newStation(byte last) {
uint8_t station; // genera numero estacion sin repetir la ultima
do {
station = random (0, staMaxStations);
} while (station == last);
return (station);
}
void updateTargetStations() {
snprintf(staStationsBuf, ACC_LNG + 1, "%d", staStations);
}
void updateCountStations() {
snprintf(staStationsBuf, ACC_LNG + 1, "%d/%d", staCurrStation, staStations);
}
void updateStationLevel() {
snprintf(staLevelBuf, ADDR_LNG + 1, "%d", staLevel);
}
void updateStationStars() {
snprintf(staStarsBuf, ADDR_LNG + 1, "%d", staStars);
}
void setNewTarget() {
uint16_t pos;
staLastStation = newStation(staLastStation);
iconData[ICON_STA_TARGET].color = staColors[staLastStation];
pos = iconData[ICON_STA_TRAIN].x;
iconData[ICON_STA_TRAIN].x = iconData[ICON_STA_TARGET].x;
iconData[ICON_STA_TARGET].x = pos;
iconData[ICON_STA_PIN].x = pos + 8;
}
void clickTargetStation() {
encoderValue = 0;
locoData[myLocoData].mySpeed = 0;
locoOperationSpeed();
updateSpeedDir();
staCurrStation++;
if (staCurrStation == staStations) {
stopTimer(TMR_STA_RUN);
staLevel++;
updateStationLevel();
newStationCounters(false);
updateTargetStations();
updateStationTime(staTime);
closeWindow(WIN_STA_PLAY);
openWindow(WIN_STA_STARS); // well done!
}
else {
updateCountStations();
setNewTarget();
newEvent(OBJ_WIN, WIN_STA_PLAY, EVNT_DRAW);
}
}
uint16_t staGetTurnoutAdr(uint16_t eeAdr, uint16_t defAdr) {
uint16_t adr;
adr = (EEPROM.read(eeAdr) << 8) + EEPROM.read(eeAdr + 1);
if (adr > 2048)
adr = defAdr;
return adr;
}
void updateTurnoutButtons() {
uint16_t n, icon;
for (n = 0; n < 4; n++) {
if (staTurnoutPos[n])
fncData[FNC_STA_ACC0 + n].idIcon = bitRead(staTurnoutDef, n) ? FNC_TURNRD_OFF : FNC_TURNLD_OFF;
else
fncData[FNC_STA_ACC0 + n].idIcon = bitRead(staTurnoutDef, n) ? FNC_TURNRS_OFF : FNC_TURNLS_OFF;
fncData[FNC_STA_ACC0 + n].colorOn = staTurnoutPos[n] ? COLOR_RED : COLOR_GREEN;
}
}

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();
}

View File

@@ -0,0 +1,842 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
////////////////////////////////////////////////////////////
// ***** SYSTEM SUPPORT *****
////////////////////////////////////////////////////////////
void initPins() {
// Set all chip selects high to avoid bus contention during initialisation of each peripheral
digitalWrite(TFT_CS, HIGH); // TFT screen chip select
digitalWrite(SD_CS, HIGH); // SD card chips select
digitalWrite(XPT2046_CS, HIGH); // Touch screen chips select
pinMode (SW_BOOT, INPUT); // Button BOOT
pinMode (ENCODER_A, INPUT); // Encoder
pinMode (ENCODER_B, INPUT);
pinMode (ENCODER_SW, INPUT);
#if (USE_RGB_LED == PRESENT)
pinMode(RGB_LED_R, OUTPUT); // RGB LED
pinMode(RGB_LED_G, OUTPUT);
pinMode(RGB_LED_B, OUTPUT);
setColorRGB(0); // turn off RGB LED
#endif
}
void setBacklight (uint8_t value) { // set PWM backlight
#if (ESP_ARDUINO_VERSION_MAJOR > 2)
// Code for version 3.x
ledcWrite(TFT_BL, value);
#else
// Code for version 2.x
ledcWrite(LEDC_CHANNEL_0, value);
#endif
currBacklight = value;
}
void setRotationDisplay(uint8_t pos) { // Rotate display and touchscreen
tft.setRotation(pos);
touchscreen.setRotation((pos + XPT_ROTATION) & 0x03);
}
void aliveAndKicking() {
setTimer (TMR_BLIGHT, INACT_TIME, TMR_ONESHOT); // reset timeout and restore backlight
if (currBacklight != backlight)
setBacklight(backlight);
}
#if (USE_RGB_LED == PRESENT)
void setColorRGB (uint16_t color) { // set color of RGB LED
int state;
state = (color & 0x04) ? LOW : HIGH;
digitalWrite(RGB_LED_G, state);
state = (color & 0x02) ? LOW : HIGH;
digitalWrite(RGB_LED_R, state);
state = (color & 0x01) ? LOW : HIGH;
digitalWrite(RGB_LED_B, state);
DEBUG_MSG("Color: %d", color & 0x07)
}
#endif
initResult initSequence() { // Performs init sequence
char label[MAX_LABEL_LNG];
char chr;
int n;
initResult result;
result = INIT_OK;
delay(500);
drawObject(OBJ_ICON, ICON_SDCARD); // detecting SD card
if (sdDetected) {
sprintf (FileName, "/image/logo.bmp");
if (tft.width() == 240)
drawBmp (FileName, 0, 180);
else
drawBmp (FileName, 40, 260);
loadLocoFiles(SD, "/loco"); // load loco data & panel names from SD file
loadAccPanelNames(SD);
}
else {
if (LittleFS.begin(false)) {
loadLocoFiles(LittleFS, "/loco"); // load loco data & panel names from FS file
loadAccPanelNames(LittleFS);
}
else {
DEBUG_MSG("LittleFS Mount Failed. Formating....");
LittleFS.format();
}
drawObject(OBJ_ICON, ICON_NO_SD);
result = INIT_NO_SD;
DEBUG_MSG("Total: %ul Used: %ul", LittleFS.totalBytes(), LittleFS.usedBytes())
}
populateAccPanel(); // load first accessory panel
barData[BAR_INIT].value = 10;
drawObject(OBJ_BAR, BAR_INIT);
drawObject(OBJ_ICON, ICON_WIFI); // connecting to WiFi network
drawObject(OBJ_DRAWSTR, DSTR_INIT_STAT);
drawObject(OBJ_LABEL, LBL_CONNECT);
WiFi.mode(WIFI_STA);
WiFi.begin(wifiSetting.ssid, wifiSetting.password);
n = 0;
while ((WiFi.status() != WL_CONNECTED) && n < 80) { // tries to connect to router in 20 seconds
n += 2;
barData[BAR_INIT].value = 10 + n;
drawObject(OBJ_BAR, BAR_INIT);
delay(500);
DEBUG_MSG(".");
}
barData[BAR_INIT].value = 90;
drawObject(OBJ_BAR, BAR_INIT);
if (WiFi.status() == WL_CONNECTED) { // Connect to server with current protocol
drawObject(OBJ_DRAWSTR, DSTR_INIT_STAT); // show Protocol
getLabelTxt(LBL_SEL_Z21 + wifiSetting.protocol, label);
tft.drawString(label, 20, 120, GFXFF);
DEBUG_MSG("Channel: %d", WiFi.channel());
DEBUG_MSG("IP address: %u.%u.%u.%u", WiFi.localIP().operator[](0), WiFi.localIP().operator[](1), WiFi.localIP().operator[](2), WiFi.localIP().operator[](3));
DEBUG_MSG("%s", WiFi.macAddress().c_str())
useID = false;
switch (wifiSetting.protocol) {
case CLIENT_Z21:
WiFi.setSleep(false);
Udp.begin(z21Port);
//wifiSetting.port = z21Port;
DEBUG_MSG("Now listening UDP port %d", z21Port);
getStatusZ21(); // every x seconds
getSerialNumber();
delay(500);
setBroadcastFlags (0x00000013); // Broadcasts and info messages concerning driving and switching, report changes on feedback bus & fast clock
getStatusZ21();
//askZ21begin (LAN_GET_BROADCASTFLAGS);
//sendUDP (0x04);
break;
case CLIENT_LNET:
if (!Client.connect(wifiSetting.CS_IP, wifiSetting.port)) {
DEBUG_MSG("Connection to Loconet over TCP/IP failed");
result = INIT_NO_CONNECT;
}
else {
Client.setNoDelay(true);
rcvStrPhase = WAIT_TOKEN;
getTypeCS();
}
break;
case CLIENT_XNET:
if (!Client.connect(wifiSetting.CS_IP, XnetPort)) {
DEBUG_MSG("Connection to Xpressnet failed");
result = INIT_NO_CONNECT;
}
else {
wifiSetting.port = XnetPort;
Client.setNoDelay(true);
rxIndice = FRAME1;
getVersionXnet(); // pide la version del Xpressnet
getStatusXnet(); // pide estado de la central
}
break;
case CLIENT_ECOS:
useID = true;
if (!Client.connect(wifiSetting.CS_IP, ECoSPort)) {
DEBUG_MSG("Connection to ECoS failed");
result = INIT_NO_CONNECT;
}
else {
wifiSetting.port = ECoSPort;
Client.setNoDelay(true);
requestViews();
requestLocoList();
waitWifiData(500);
}
break;
}
}
else {
drawObject(OBJ_ICON, ICON_NO_WIFI);
result = INIT_NO_WIFI;
}
barData[BAR_INIT].value = 95;
drawObject(OBJ_BAR, BAR_INIT);
drawObject(OBJ_ICON, ICON_INIT_LOCO); // fill image list
initImageList();
barData[BAR_INIT].value = 100;
drawObject(OBJ_BAR, BAR_INIT);
setTimer (TMR_END_LOGO, 7, TMR_ONESHOT); // Wait for answer
return result;
}
bool notLocked () { // check if not locked
if (lockOptions & ((1 << LOCK_SEL_LOCO) | (1 << LOCK_TURNOUT) | (1 << LOCK_PROG)))
return false;
else
return true;
}
bool notLockedOption (byte opt) { // check if option not locked
if (lockOptions & (1 << opt))
return false;
else
return true;
}
////////////////////////////////////////////////////////////
// ***** TOUCHSCREEN *****
////////////////////////////////////////////////////////////
void calibrateTouchscreen(uint16_t colorIn, uint16_t colorOut, uint16_t bg) {
uint16_t TS_TOP, TS_BOT, TS_LEFT, TS_RT;
uint16_t x, y, z;
TSPoint p;
TS_TOP = 4095;
TS_BOT = 0;
TS_LEFT = 4095;
TS_RT = 0;
tft.fillScreen(bg);
for (int i = 0; i < 4; i++) {
tft.fillCircle(0, 0, 15, bg); // delete touch corners points
tft.fillCircle(tft.width(), 0, 15, bg);
tft.fillCircle(0, tft.height(), 15, bg);
tft.fillCircle(tft.width(), tft.height(), 15, bg);
DEBUG_MSG("Calibrate step: %d", i)
switch (i) { // show current touch corner point
case 0:
tft.fillCircle(0, 0, 15, colorOut);
tft.fillCircle(0, 0, 7, colorIn);
break;
case 1:
tft.fillCircle(tft.width(), 0, 15, colorOut);
tft.fillCircle(tft.width(), 0, 7, colorIn);
break;
case 2:
tft.fillCircle(0, tft.height(), 15, colorOut);
tft.fillCircle(0, tft.height(), 7, colorIn);
break;
case 3:
tft.fillCircle(tft.width(), tft.height(), 15, colorOut);
tft.fillCircle(tft.width(), tft.height(), 7, colorIn);
break;
}
while (touchscreen.touched()) // wait to release
delay(0);
DEBUG_MSG("Pen released")
while (!touchscreen.touched()) // wait to touch
delay(0);
DEBUG_MSG("Pen touched")
touchscreen.readData(&x, &y, &z);
if (x < TS_LEFT) {
TS_LEFT = x;
}
if (y < TS_TOP) {
TS_TOP = y;
}
if (x > TS_RT) {
TS_RT = x;
}
if (y > TS_BOT) {
TS_BOT = y;
}
}
tft.fillCircle(tft.width(), tft.height(), 15, bg); // delete last touch corner point
touchscreen.setCalibration(TS_LEFT, TS_RT, TS_TOP, TS_BOT);
DEBUG_MSG("xMin: %d, xMax: %d, yMin: %d, yMax: %d", TS_LEFT, TS_RT, TS_TOP, TS_BOT);
}
void showClockData(uint16_t txtFocus) {
uint16_t n;
for (n = 0; n < 3; n++)
txtData[TXT_HOUR + n].backgnd = COLOR_BACKGROUND;
txtData[txtFocus].backgnd = COLOR_YELLOW; // select focus on selected field
keybData[KEYB_CLOCK].idTextbox = txtFocus;
}
////////////////////////////////////////////////////////////
// ***** WIFI *****
////////////////////////////////////////////////////////////
void scanWiFi() {
networks = 0;
while (networks == 0) {
WiFi.disconnect(true); //DISCONNECT WITH TRUE (SHUOLD TURN OFF THE RADIO)
delay(1000);
WiFi.mode(WIFI_STA); //CALLING THE WIFI MODE AS STATION
WiFi.scanDelete();
networks = WiFi.scanNetworks();
DEBUG_MSG("Networks: %d", networks);
if ((networks > 0) && (networks < 32768)) {
encoderMax = networks - 1;
encoderValue = 0;
scrSSID = 0;
}
else
networks = 0;
}
}
void scanWiFiFill() {
uint16_t n, line;
n = (scrSSID > 5) ? scrSSID - 5 : 0;
snprintf (ssidName1, SSID_LNG, WiFi.SSID(n).c_str());
snprintf (ssidName2, SSID_LNG, WiFi.SSID(n + 1).c_str());
snprintf (ssidName3, SSID_LNG, WiFi.SSID(n + 2).c_str());
snprintf (ssidName4, SSID_LNG, WiFi.SSID(n + 3).c_str());
snprintf (ssidName5, SSID_LNG, WiFi.SSID(n + 4).c_str());
snprintf (ssidName6, SSID_LNG, WiFi.SSID(n + 5).c_str());
line = (scrSSID > 5) ? 5 : scrSSID;
for (n = 0; n < 6; n++) {
txtData[TXT_SSID1 + n].backgnd = (n == line) ? COLOR_BLUE : COLOR_BLACK;
}
}
void wifiAnalyzer() {
int16_t n, i;
char txt[10];
for (n = 0; n < 14; n++) {
ap_count[n] = 0;
max_rssi[n] = RSSI_FLOOR;
}
n = WiFi.scanNetworks();
drawObject(OBJ_DRAWSTR, DSTR_WIFI_SCAN);
drawObject(OBJ_LABEL, LBL_SSID_SCAN);
drawObject(OBJ_FNC, FNC_SCAN_RESET);
tft.setFreeFont(FSSB6);
if ((n > 0) && (n < 32768)) {
for (i = 0; i < n; i++) {
int32_t channel = WiFi.channel(i);
int32_t rssi = WiFi.RSSI(i);
uint16_t color = channel_color[channel - 1];
int height = constrain(map(rssi, RSSI_FLOOR, RSSI_CEILING, 1, GRAPH_HEIGHT), 1, GRAPH_HEIGHT);
// channel stat
ap_count[channel - 1]++;
if (rssi > max_rssi[channel - 1]) {
max_rssi[channel - 1] = rssi;
}
tft.drawLine((channel * CHANNEL_WIDTH) + GRAPH_OFFSET, GRAPH_BASELINE - height, ((channel - 1) * CHANNEL_WIDTH) + GRAPH_OFFSET, GRAPH_BASELINE + 1, color);
tft.drawLine((channel * CHANNEL_WIDTH) + GRAPH_OFFSET, GRAPH_BASELINE - height, ((channel + 1) * CHANNEL_WIDTH) + GRAPH_OFFSET, GRAPH_BASELINE + 1, color);
// Print SSID, signal strengh and if not encrypted
tft.setTextColor(color);
tft.setTextDatum(MC_DATUM);
tft.drawString(WiFi.SSID(i), (channel * CHANNEL_WIDTH) + GRAPH_OFFSET, GRAPH_BASELINE - 10 - height, GFXFF);
// rest for WiFi routine?
delay(10);
}
}
else {
tft.setFreeFont(FSSB9);
tft.setTextColor(COLOR_WHITE);
tft.drawString("SSID = 0", 120 + GRAPH_OFFSET, 120, GFXFF);
}
tft.setFreeFont(FSSB6);
tft.setTextDatum(TC_DATUM);
for (i = 1; i < 15; i++) {
tft.setTextColor(channel_color[i - 1]);
snprintf(txt, 10, "%d", i);
tft.drawString(txt, (i * CHANNEL_WIDTH) + GRAPH_OFFSET, GRAPH_BASELINE + 12, GFXFF);
if (ap_count[i - 1] > 0) {
snprintf(txt, 10, "(%d)", ap_count[i - 1]);
tft.drawString(txt, (i * CHANNEL_WIDTH) + GRAPH_OFFSET, GRAPH_BASELINE + 24, GFXFF);
}
}
setTimer(TMR_SCAN, 50, TMR_ONESHOT);
}
void wifiProcess() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
processZ21();
break;
case CLIENT_XNET:
processXnet();
break;
case CLIENT_LNET:
processLnet();
break;
case CLIENT_ECOS:
ECoSProcess();
break;
}
}
void waitWifiData(uint32_t ms) {
uint32_t now;
now = millis();
while ((millis() - now) < ms)
wifiProcess();
}
void setProtocolData() {
uint16_t n;
useID = false;
switch (wifiSetting.protocol) {
case CLIENT_Z21:
snprintf(keybProtoBuf, PWD_LNG, "Z21");
snprintf(keybPortBuf, 6, "%d", z21Port);
break;
case CLIENT_LNET:
if (wifiSetting.serverType)
snprintf(keybProtoBuf, PWD_LNG, "LBServer");
else
snprintf(keybProtoBuf, PWD_LNG, "Loconet Binary");
snprintf(keybPortBuf, 6, "%d", wifiSetting.port);
break;
case CLIENT_XNET:
snprintf(keybProtoBuf, PWD_LNG, "Xpressnet LAN");
snprintf(keybPortBuf, 6, "%d", XnetPort);
break;
case CLIENT_ECOS:
useID = true;
snprintf(keybProtoBuf, PWD_LNG, "ECoS");
snprintf(keybPortBuf, 6, "%d", ECoSPort);
break;
}
for (n = 0; n < 5; n++)
txtData[TXT_IP1 + n].backgnd = COLOR_BACKGROUND;
txtData[TXT_IP1].backgnd = COLOR_YELLOW; // select focus on first IP byte
keybData[KEYB_IP].idTextbox = TXT_IP1;
}
void setOptionsData() {
switchData[SW_OPT_ADR].state = false; // show disable all as default
switchData[SW_OPT_ADR].colorKnob = COLOR_BACKGROUND;
radioData[RAD_CSTATION].value = radioData[RAD_CSTATION].num;
switch (wifiSetting.protocol) {
case CLIENT_Z21:
switchData[SW_OPT_ADR].colorKnob = COLOR_WHITE;
switchData[SW_OPT_ADR].state = (shortAddress == 99) ? true : false;
break;
case CLIENT_XNET:
break;
case CLIENT_LNET:
radioData[RAD_CSTATION].value = typeCmdStation;
break;
case CLIENT_ECOS:
break;
}
}
////////////////////////////////////////////////////////////
// ***** PROTOCOL COMMON *****
////////////////////////////////////////////////////////////
void infoLocomotora (unsigned int loco) {
DEBUG_MSG("Info Loco % d", loco)
switch (wifiSetting.protocol) {
case CLIENT_Z21:
infoLocomotoraZ21 (loco);
break;
case CLIENT_XNET:
infoLocomotoraXnet (loco);
break;
case CLIENT_LNET:
infoLocomotoraLnet (loco);
break;
case CLIENT_ECOS:
infoLocomotoraECoS (loco); // ID
break;
}
}
void locoOperationSpeed() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
locoOperationSpeedZ21();
break;
case CLIENT_XNET:
locoOperationSpeedXnet();
break;
case CLIENT_LNET:
locoOperationSpeedLnet();
break;
case CLIENT_ECOS:
locoOperationSpeedECoS();
break;
}
}
void changeDirection() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
locoOperationSpeedZ21();
break;
case CLIENT_XNET:
locoOperationSpeedXnet();
break;
case CLIENT_LNET:
changeDirectionF0F4Lnet();
break;
case CLIENT_ECOS:
changeDirectionECoS();
break;
}
}
void funcOperations (uint8_t fnc) {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
funcOperationsZ21 (fnc);
break;
case CLIENT_XNET:
funcOperationsXnet (fnc);
break;
case CLIENT_LNET:
funcOperationsLnet (fnc);
break;
case CLIENT_ECOS:
funcOperationsECoS(fnc);
break;
}
}
byte getCurrentStep() {
byte value;
switch (wifiSetting.protocol) {
case CLIENT_Z21:
value = getCurrentStepZ21();
break;
case CLIENT_XNET:
value = getCurrentStepXnet();
break;
case CLIENT_LNET:
value = getCurrentStepLnet();
break;
case CLIENT_ECOS:
value = getCurrentStepECoS();
break;
}
return value;
}
void releaseLoco() {
switch (wifiSetting.protocol) {
case CLIENT_LNET:
doDispatchGet = false;
doDispatchPut = false;
liberaSlot(); // pasa slot actual a COMMON
break;
case CLIENT_ECOS:
releaseLocoECoS();
break;
}
}
void sendAccessory(unsigned int FAdr, int pair, bool activate) {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
setAccessoryZ21(FAdr, pair, activate);
break;
case CLIENT_XNET:
setAccessoryXnet(FAdr, activate, pair);
break;
case CLIENT_LNET:
lnetRequestSwitch (FAdr, activate, pair);
break;
case CLIENT_ECOS:
setAccessoryECoS(FAdr, pair, activate);
break;
}
}
void resumeOperations() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
resumeOperationsZ21();
break;
case CLIENT_XNET:
resumeOperationsXnet();
break;
case CLIENT_LNET:
resumeOperationsLnet();
break;
case CLIENT_ECOS:
resumeOperationsECoS();
break;
}
}
void emergencyOff() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
emergencyOffZ21();
break;
case CLIENT_XNET:
emergencyOffXnet();
break;
case CLIENT_LNET:
emergencyOffLnet();
break;
case CLIENT_ECOS:
emergencyOffECoS();
break;
}
}
void togglePower() {
if (isTrackOff())
resumeOperations(); // Track Power On
else
emergencyOff(); // Track Power Off
}
bool isTrackOff() {
bool state;
switch (wifiSetting.protocol) {
case CLIENT_Z21:
state = (csStatus & csTrackVoltageOff) ? true : false;
break;
case CLIENT_XNET:
state = (csStatus & csEmergencyOff) ? true : false;
break;
case CLIENT_LNET:
state = (bitRead(mySlot.trk, 0)) ? false : true;
break;
case CLIENT_ECOS:
state = (csStatus > 0) ? false : true;
break;
}
return state;
}
void getStatusCS() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
getStatusZ21();
break;
case CLIENT_XNET:
getStatusXnet();
break;
case CLIENT_LNET:
getTypeCS(); // workaround, not defined for Lnet
break;
case CLIENT_ECOS:
getStatusECoS();
break;
}
}
void setTime(byte hh, byte mm, byte rate) {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
setTimeZ21(hh, mm, rate);
break;
case CLIENT_XNET:
setTimeXnet(hh, mm, rate);
break;
case CLIENT_LNET:
setTimeLnet(hh, mm, rate);
break;
// ECoS not supported
}
}
void readCV (unsigned int adr, byte stepPrg) {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
readCVZ21(adr, stepPrg);
break;
case CLIENT_XNET:
readCVXnet(adr, stepPrg);
break;
case CLIENT_LNET:
readCVLnet(adr, stepPrg);
break;
case CLIENT_ECOS:
readCVECoS(adr, stepPrg);
break;
}
}
void writeCV (unsigned int adr, unsigned int data, byte stepPrg) {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
writeCVZ21(adr, data, stepPrg);
break;
case CLIENT_XNET:
writeCVXnet(adr, data, stepPrg);
break;
case CLIENT_LNET:
writeCVLnet(adr, data, stepPrg);
break;
case CLIENT_ECOS:
writeCVECoS(adr, data, stepPrg);
break;
}
}
void exitProgramming() {
switch (wifiSetting.protocol) {
case CLIENT_Z21:
if (csStatus & csProgrammingModeActive)
resumeOperationsZ21();
break;
case CLIENT_XNET:
if (csStatus & csServiceMode)
resumeOperationsXnet();
break;
case CLIENT_ECOS:
exitProgrammingECoS();
break;
}
}
////////////////////////////////////////////////////////////
// ***** CV PROGRAMMING *****
////////////////////////////////////////////////////////////
void endProg() { // Fin de programcion/lectura CV
DEBUG_MSG("END PROG: CVData - % d Step: % d", CVdata, progStepCV);
if (CVdata > 255) {
if (progStepCV == PRG_RD_CV29) // Si buscaba direccion, muestra CV1 en lugar de CV29
CVaddress = 1;
showDataCV();
}
else {
switch (progStepCV) {
case PRG_CV:
showDataCV();
break;
case PRG_RD_CV29:
cv29 = (byte) CVdata;
if (bitRead(cv29, 5)) {
CVaddress = 17; // Long address
readCV(CVaddress, PRG_RD_CV17);
}
else {
CVaddress = 1; // Short address
readCV(CVaddress, PRG_RD_CV1);
}
break;
case PRG_RD_CV1:
decoAddress = CVdata;
showDirCV();
break;
case PRG_RD_CV17:
cv17 = (byte) CVdata;
CVaddress = 18;
readCV(CVaddress, PRG_RD_CV18);
break;
case PRG_RD_CV18:
cv18 = (byte) CVdata;
decoAddress = ((cv17 & 0x3F) << 8) | cv18;
showDirCV();
break;
case PRG_WR_CV17: // Long address
CVaddress = 18;
writeCV(CVaddress, lowByte(decoAddress), PRG_WR_CV18);
break;
case PRG_WR_CV18:
bitSet(cv29, 5);
CVaddress = 29;
writeCV(CVaddress, cv29, PRG_WR_CV29);
break;
case PRG_WR_CV1: // short address
bitClear(cv29, 5);
CVaddress = 29;
writeCV(CVaddress, cv29, PRG_WR_CV29);
break;
case PRG_WR_CV29:
showDirCV();
break;
}
}
}
void showDataCV() { // muestra valor de la CV
progStepCV = PRG_IDLE;
enterCVdata = (CVdata > 255) ? false : true;
setStatusCV(); // show error / manufacturer / CV / pom
setFieldsCV();
setBitsCV();
if (isWindow(WIN_ALERT))
closeWindow(WIN_ALERT);
if (isWindow(WIN_PROG_CV)) {
showFieldsCV();
newEvent(OBJ_TXT, TXT_CV_STATUS, EVNT_DRAW);
}
if (wifiSetting.protocol == CLIENT_LNET)
progUhli(UHLI_PRG_END);
}
void showDirCV() { // muestra direccion de la locomotora segun sus CV
progStepCV = PRG_IDLE;
setStatusCV(); // show error / manufacturer / CV / pom
setFieldsCV();
setBitsCV();
if (isWindow(WIN_ALERT))
closeWindow(WIN_ALERT);
sprintf(locoEditAddr, " % d", decoAddress);
openWindow(WIN_PROG_ADDR);
if (wifiSetting.protocol == CLIENT_LNET)
progUhli(UHLI_PRG_END);
if (wifiSetting.protocol != CLIENT_ECOS)
pushLoco(decoAddress); // mete esta loco en el stack
}
void readBasicCV (uint16_t num) {
closeWindow(WIN_READ_CV);
if (num == 1) {
readCV(29, PRG_RD_CV29);
}
else {
CVaddress = num;
readCV(num, PRG_CV);
}
alertWindow(ERR_CV);
}
////////////////////////////////////////////////////////////
// ***** EEPROM *****
////////////////////////////////////////////////////////////
void saveCalibrationValues() {
TouchCalibration cal;
cal = touchscreen.getCalibration();
EEPROM.write (EE_XMIN_H, highByte(cal.xMin));
EEPROM.write (EE_XMIN_L, lowByte(cal.xMin));
EEPROM.write (EE_XMAX_H, highByte(cal.xMax));
EEPROM.write (EE_XMAX_L, lowByte(cal.xMax));
EEPROM.write (EE_YMIN_H, highByte(cal.yMin));
EEPROM.write (EE_YMIN_L, lowByte(cal.yMin));
EEPROM.write (EE_YMAX_H, highByte(cal.yMax));
EEPROM.write (EE_YMAX_L, lowByte(cal.yMax));
EEPROM.commit();
}

View File

@@ -0,0 +1,544 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
enum language {LANG_ENGLISH, LANG_SPANISH, LANG_CATALAN, LANG_GERMAN, MAX_LANG};
static const char *translations[][MAX_LANG] = { // DON'T CHANGE ORDER: Same order defined in labelObj. If a complete translation is missing, must be English definition.
{ // LBL_PACO_TXT
[LANG_ENGLISH] = "PacoMouseCYD",
},
{ // LBL_INIT
[LANG_ENGLISH] = "Welcome!",
[LANG_SPANISH] = "Bienvenido!",
[LANG_CATALAN] = "Benvingut!",
[LANG_GERMAN] = "Willkommen!",
},
{ // LBL_CONNECT
[LANG_ENGLISH] = "Connecting...",
[LANG_SPANISH] = "Conectando...",
[LANG_CATALAN] = "Connectant...",
[LANG_GERMAN] = "Verbindung...",
},
{ // LBL_PRESS
[LANG_ENGLISH] = "Touch to start...",
[LANG_SPANISH] = "Toca para empezar...",
[LANG_CATALAN] = "Toca per iniciar...",
[LANG_GERMAN] = "Beruhren Sie den\nBildschirm, um \nzu beginnen...",
},
{ // LBL_CAL
[LANG_ENGLISH] = "To calibrate the\ntouchscreen, touch the\ncorners that appear\nwith the pen.",
[LANG_SPANISH] = "Para calibrar la pantalla,\ntoque las esquinas que\naparecen con el lapiz",
[LANG_CATALAN] = "Per calibrar la pantalla,\ntoqueu les cantonades que\napareixen amb el llapis",
[LANG_GERMAN] = "Um den Touchscreen\nzu kalibrieren, beruhren\nSie die angezeigten Ecken\nmit dem Stift.",
},
{ // LBL_CAL_DONE
[LANG_ENGLISH] = "Calibration done.",
[LANG_SPANISH] = "Calibracion realizada.",
[LANG_CATALAN] = "Calibratge realitzat.",
[LANG_GERMAN] = "Kalibrierung\nabgeschlossen.",
},
{ // LBL_SCAN
[LANG_ENGLISH] = "SSID WiFi\nScanning...",
[LANG_SPANISH] = "SSID WiFi\nBuscando......",
[LANG_CATALAN] = "SSID WiFi\nEscanejant...",
[LANG_GERMAN] = "SSID WiFi\nScannen...",
},
{ // LBL_SSID_SCAN
[LANG_ENGLISH] = "SSID WiFi",
},
{ // LBL_SSID
[LANG_ENGLISH] = "SSID",
},
{ // LBL_IP
[LANG_ENGLISH] = "IP",
},
{ // LBL_PWD_HIDE
[LANG_ENGLISH] = "Password",
[LANG_SPANISH] = NULL,
[LANG_CATALAN] = NULL,
[LANG_GERMAN] = "Passwort",
},
{ // LBL_PORT
[LANG_ENGLISH] = "Port",
[LANG_SPANISH] = "Puerto",
},
{ // LBL_PROTOCOL
[LANG_ENGLISH] = "Protocol",
[LANG_SPANISH] = "Protocolo",
[LANG_CATALAN] = NULL,
[LANG_GERMAN] = "Protokoll",
},
{ // LBL_SEL_PROT
[LANG_ENGLISH] = "Protocol",
[LANG_SPANISH] = "Protocolo",
[LANG_CATALAN] = NULL,
[LANG_GERMAN] = "Protokoll",
},
{ // LBL_SEL_Z21
[LANG_ENGLISH] = "Z21",
},
{ // LBL_SEL_XNET
[LANG_ENGLISH] = "Xpressnet LAN",
},
{ // LBL_SEL_ECOS
[LANG_ENGLISH] = "ECoS",
},
{ // LBL_SEL_LNET
[LANG_ENGLISH] = "Loconet over TCP/IP",
},
{ // LBL_SEL_LBSERVER
[LANG_ENGLISH] = "LBServer",
},
{ // LBL_SEL_BINARY
[LANG_ENGLISH] = "Binary",
},
{ // LBL_OPTIONS
[LANG_ENGLISH] = "Options",
[LANG_SPANISH] = "Opciones",
[LANG_CATALAN] = "Opcions",
[LANG_GERMAN] = "Optionen",
},
{ // LBL_NAME
[LANG_ENGLISH] = "Name",
[LANG_SPANISH] = "Nombre",
[LANG_CATALAN] = "Nom",
[LANG_GERMAN] = NULL,
},
{ // LBL_ADDR
[LANG_ENGLISH] = "Address",
[LANG_SPANISH] = "Direccion",
[LANG_CATALAN] = "Direccio",
[LANG_GERMAN] = "Adresse",
},
{ // LBL_IMAGE
[LANG_ENGLISH] = "Image",
[LANG_SPANISH] = "Imagen",
[LANG_CATALAN] = "Imatge",
[LANG_GERMAN] = "Bild",
},
{ // LBL_VMAX
[LANG_ENGLISH] = "Speed max.",
[LANG_SPANISH] = "Vel. max.",
[LANG_CATALAN] = "Vel. max.",
[LANG_GERMAN] = "Geschw.max.",
},
{ // LBL_FUNC
[LANG_ENGLISH] = "Functions",
[LANG_SPANISH] = "Funciones",
[LANG_CATALAN] = "Funcions",
[LANG_GERMAN] = "Funktionen",
},
{ // LBL_SERVICE
[LANG_ENGLISH] = "Service Mode",
[LANG_SPANISH] = "Modo Servicio",
[LANG_CATALAN] = "Mode Servei",
[LANG_GERMAN] = "Servicemodus",
},
{ // LBL_KMH
[LANG_ENGLISH] = "km/h",
},
{ // LBL_SHUNTING
[LANG_ENGLISH] = "Shunting",
[LANG_SPANISH] = "Maniobras",
[LANG_CATALAN] = "Maniobres",
[LANG_GERMAN] = "Rangieren",
},
{ // LBL_RATE
[LANG_ENGLISH] = "Rate 1:",
[LANG_SPANISH] = "Ratio 1:",
[LANG_CATALAN] = "Ratio 1:",
[LANG_GERMAN] = NULL,
},
{ // LBL_CHG_WIFI
[LANG_ENGLISH] = "Reset to apply\nchanges",
[LANG_SPANISH] = "Reinicie para\naplicar los\ncambios",
[LANG_CATALAN] = "Reiniciar per \naplicar els\ncanvis",
[LANG_GERMAN] = "Neustart, um die\nAnderungen zu\nübernehmen",
},
{ // LBL_EDIT_FUNC
[LANG_ENGLISH] = "Functions",
[LANG_SPANISH] = "Funciones",
[LANG_CATALAN] = "Funcions",
[LANG_GERMAN] = "Funktionen",
},
{ // LBL_STACK_FULL
[LANG_ENGLISH] = "Locomotive stack\nfull!",
[LANG_SPANISH] = "Almacen de\nlocomotoras\nlleno!",
[LANG_CATALAN] = "Magatzem de\nlocomotores\nple!",
[LANG_GERMAN] = "Lokomotivstapel\nvoll!",
},
{ // LBL_STOP_0
[LANG_ENGLISH] = "Speed 0",
[LANG_SPANISH] = "Velocidad 0",
[LANG_CATALAN] = "Velocitat 0",
[LANG_GERMAN] = "Geschw. 0",
},
{ // LBL_STOP_E
[LANG_ENGLISH] = "Emerg. Stop",
[LANG_SPANISH] = "Stop Emerg.",
[LANG_CATALAN] = "Stop Emerg.",
[LANG_GERMAN] = "Not-Halt",
},
{ // LBL_SEL_IMAGE
[LANG_ENGLISH] = "Image",
[LANG_SPANISH] = "Imagen",
[LANG_CATALAN] = "Imatge",
[LANG_GERMAN] = "Bild",
},
{ // LBL_MENU_DRIVE
[LANG_ENGLISH] = "Drive",
[LANG_SPANISH] = "Conducir",
[LANG_CATALAN] = "Conduir",
[LANG_GERMAN] = "Fahren",
},
{ // LBL_MENU_ACC
[LANG_ENGLISH] = "Accesory",
[LANG_SPANISH] = "Accesorios",
[LANG_CATALAN] = "Accesoris",
[LANG_GERMAN] = "Zubehorartikel",
},
{ // LBL_MENU_CV
[LANG_ENGLISH] = "CV Programming",
[LANG_SPANISH] = "Programar CV",
[LANG_CATALAN] = "Programar CV",
[LANG_GERMAN] = "CV-Programmierung",
},
{ // LBL_MENU_CFG
[LANG_ENGLISH] = "Configure",
[LANG_SPANISH] = "Configurar",
[LANG_CATALAN] = "Configurar",
[LANG_GERMAN] = "Einstellungen",
},
{ // LBL_MENU_UTILS
[LANG_ENGLISH] = "Utilities",
[LANG_SPANISH] = "Utilidades",
[LANG_CATALAN] = "Utilitats",
[LANG_GERMAN] = "Dienstprogramme",
},
{ // LBL_CFG_LANG
[LANG_ENGLISH] = "Language",
[LANG_SPANISH] = "Idioma",
[LANG_CATALAN] = "Idioma",
[LANG_GERMAN] = "Sprache",
},
{ // LBL_CFG_SCR
[LANG_ENGLISH] = "Screen",
[LANG_SPANISH] = "Pantalla",
[LANG_CATALAN] = "Pantalla",
[LANG_GERMAN] = "Bildschirm",
},
{ // LBL_CFG_SPD
[LANG_ENGLISH] = "Speed",
[LANG_SPANISH] = "Velocidad",
[LANG_CATALAN] = "Velocitat",
[LANG_GERMAN] = "Geschwindigkeit",
},
{ // LBL_CFG_WIFI
[LANG_ENGLISH] = "WiFi",
},
{ // LBL_CFG_FCLK
[LANG_ENGLISH] = "Fast Clock",
[LANG_SPANISH] = "Reloj",
[LANG_CATALAN] = "Rellotge",
[LANG_GERMAN] = "Uhr",
},
{ // LBL_CFG_LOCK
[LANG_ENGLISH] = "Lock",
[LANG_SPANISH] = "Bloquear",
[LANG_CATALAN] = "Bloquejar",
[LANG_GERMAN] = "Sperre",
},
{ // LBL_CFG_ABOUT
[LANG_ENGLISH] = "About...",
[LANG_SPANISH] = "Acerca...",
[LANG_CATALAN] = "Sobre...",
[LANG_GERMAN] = "Info...",
},
{ // LBL_SCR_ROTATE
[LANG_ENGLISH] = "Rotate",
[LANG_SPANISH] = "Girar",
[LANG_CATALAN] = "Girar",
[LANG_GERMAN] = "Drehen",
},
{ // LBL_PACO_WEB
[LANG_ENGLISH] = "https://usuaris.tinet.cat/fmco",
},
{ // LBL_LOCK_LOK
[LANG_ENGLISH] = "Locomotives",
[LANG_SPANISH] = "Locomotoras",
[LANG_CATALAN] = "Locomotores",
[LANG_GERMAN] = "Lokomotiven",
},
{ // LBL_LOCK_ACC
[LANG_ENGLISH] = "Accesory",
[LANG_SPANISH] = "Accesorios",
[LANG_CATALAN] = "Accesoris",
[LANG_GERMAN] = "Zubehorartikel",
},
{ // LBL_LOCK_PRG
[LANG_ENGLISH] = "Programming",
[LANG_SPANISH] = "Programar",
[LANG_CATALAN] = "Programar",
[LANG_GERMAN] = "Programmierung",
},
/*
{ // LBL_OPT_ROCO
[LANG_ENGLISH] = "Turntable offset",
[LANG_SPANISH] = "Offset Plataforma",
[LANG_CATALAN] = "Offset Plataforma",
[LANG_GERMAN] = "Drehscheibe Versatz",
},
*/
{ // LBL_OPT_ADR
[LANG_ENGLISH] = "Short Addr. (1 to 99)",
[LANG_SPANISH] = "Dir. corta (1 a 99)",
[LANG_CATALAN] = "Dir. curta (1 a 99)",
[LANG_GERMAN] = "Kurze Adr. (1 bis 99)",
},
{ // LBL_OPT_IB2
[LANG_ENGLISH] = "IBII / DR5000",
},
{ // LBL_OPT_UHLI
[LANG_ENGLISH] = "Uhlenbrock",
},
{ // LBL_OPT_DIG
[LANG_ENGLISH] = "Digitrax",
},
{ // LBL_ESTOP
[LANG_ENGLISH] = "Emergency Stop",
[LANG_SPANISH] = "Stop Emergencia",
[LANG_CATALAN] = "Stop Emergencia",
[LANG_GERMAN] = "Nothalt",
},
{ // LBL_SCALE
[LANG_ENGLISH] = "Scale",
[LANG_SPANISH] = "Escala",
[LANG_CATALAN] = "Escala",
[LANG_GERMAN] = "Skala",
},
{ // LBL_MM
[LANG_ENGLISH] = "mm",
},
{ // LBL_SCALE_H0
[LANG_ENGLISH] = "H0",
},
{ // LBL_SCALE_N
[LANG_ENGLISH] = "N",
},
{ // LBL_SCALE_TT
[LANG_ENGLISH] = "TT",
},
{ // LBL_SCALE_Z
[LANG_ENGLISH] = "Z",
},
{ // LBL_SCALE_0
[LANG_ENGLISH] = "0",
},
{ // LBL_MEASURE
[LANG_ENGLISH] = "Measuring",
[LANG_SPANISH] = "Midiendo",
[LANG_CATALAN] = "Mesurant",
[LANG_GERMAN] = "Messung",
},
{ // LBL_CV_ADDR
[LANG_ENGLISH] = "Loco Address",
[LANG_SPANISH] = "Direcc. Loco",
[LANG_CATALAN] = "Direcc. Loco",
[LANG_GERMAN] = "Lokadresse",
},
{ // LBL_CV_SPD_L
[LANG_ENGLISH] = "Speed min.",
[LANG_SPANISH] = "Velocidad min.",
[LANG_CATALAN] = "Velocitat min.",
[LANG_GERMAN] = "Minimale Geschw.",
},
{ // LBL_CV_SPD_M
[LANG_ENGLISH] = "Speed mid.",
[LANG_SPANISH] = "Velocidad media",
[LANG_CATALAN] = "Velocitat mitja",
[LANG_GERMAN] = "Mittlere Geschw.",
},
{ // LBL_CV_SPD_H
[LANG_ENGLISH] = "Speed max.",
[LANG_SPANISH] = "Velocidad max.",
[LANG_CATALAN] = "Velocitat max.",
[LANG_GERMAN] = "Maximale Geschw.",
},
{ // LBL_CV_ACC
[LANG_ENGLISH] = "Acceleration",
[LANG_SPANISH] = "Aceleracion",
[LANG_CATALAN] = "Acceleracio",
[LANG_GERMAN] = "Beschleunig",
},
{ // LBL_CV_DEC
[LANG_ENGLISH] = "Braking",
[LANG_SPANISH] = "Frenado",
[LANG_CATALAN] = "Frenada",
[LANG_GERMAN] = "Bremsen",
},
{ // LBL_CV_CFG
[LANG_ENGLISH] = "Configuration",
[LANG_SPANISH] = "Configuracion",
[LANG_CATALAN] = "Configuracio",
[LANG_GERMAN] = "Konfiguration",
},
{ // LBL_CV_MAN
[LANG_ENGLISH] = "Manufacturer",
[LANG_SPANISH] = "Fabricante",
[LANG_CATALAN] = "Fabricant",
[LANG_GERMAN] = "Hersteller",
},
{ // LBL_CV
[LANG_ENGLISH] = "CV",
},
{ // LBL_LNCV
[LANG_ENGLISH] = "LNCV",
},
{ // LBL_POM
[LANG_ENGLISH] = "PoM",
},
{ // LBL_BITS
[LANG_ENGLISH] = "Bits",
},
{ // LBL_CV_ERROR
[LANG_ENGLISH] = "CV ERROR",
[LANG_SPANISH] = "ERROR CV",
[LANG_CATALAN] = "ERROR CV",
[LANG_GERMAN] = "CV-FEHLER",
},
{ // LBL_UTIL_SPEED
[LANG_ENGLISH] = "Measure speed",
[LANG_SPANISH] = "Medir velocidad",
[LANG_CATALAN] = "Mesurar velocitat",
[LANG_GERMAN] = "Geschw. messen",
},
{ // LBL_UTIL_STEAM
[LANG_ENGLISH] = "Steam locomotive",
[LANG_SPANISH] = "Locomotora de vapor",
[LANG_CATALAN] = "Locomotora de vapor",
[LANG_GERMAN] = "Dampflokomotive",
},
{ // LBL_UTIL_SCAN
[LANG_ENGLISH] = "WiFi Analyzer",
[LANG_SPANISH] = "Analizador de WiFi",
[LANG_CATALAN] = "Analitzador de WiFi",
[LANG_GERMAN] = "WLAN-Analysator",
},
{ // LBL_UTIL_STA
[LANG_ENGLISH] = "Station Run",
[LANG_SPANISH] = "Carrera de estaciones",
[LANG_CATALAN] = "Carrera d'estacions",
[LANG_GERMAN] = "Bahnhofsrennen",
},
{ // LBL_ASK_SURE
[LANG_ENGLISH] = "Are you sure?",
[LANG_SPANISH] = "Estas seguro?",
[LANG_CATALAN] = "Segur?",
[LANG_GERMAN] = "Bist du sicher?",
},
{ // LBL_OPT_DISCOVER
[LANG_ENGLISH] = "Discover",
[LANG_SPANISH] = "Descubrir",
[LANG_CATALAN] = "Descobrir",
[LANG_GERMAN] = "Entdecken",
},
{ // LBL_LNCV_ART
[LANG_ENGLISH] = "Article",
[LANG_SPANISH] = "Articulo",
[LANG_CATALAN] = NULL,
[LANG_GERMAN] = "Artikel",
},
{ // LBL_LNCV_MOD
[LANG_ENGLISH] = "Module",
[LANG_SPANISH] = "Modulo",
[LANG_CATALAN] = "Modul",
[LANG_GERMAN] = "Modul",
},
{ // LBL_LNCV_NUM
[LANG_ENGLISH] = "LNCV",
},
{ // LBL_ACC_TYPE
[LANG_ENGLISH] = "Accessory type",
[LANG_SPANISH] = "Tipo accesorio",
[LANG_CATALAN] = "Tipus accessori",
[LANG_GERMAN] = "Zubehortyp",
},
{ // LBL_ACC_NAME
[LANG_ENGLISH] = "Name",
[LANG_SPANISH] = "Nombre",
[LANG_CATALAN] = "Nom",
[LANG_GERMAN] = NULL,
},
{ // LBL_ACC_ADDR
[LANG_ENGLISH] = "Addr.",
[LANG_SPANISH] = "Dir.",
[LANG_CATALAN] = "Dir.",
[LANG_GERMAN] = "Adr.",
},
{ // LBL_STA_RUN
[LANG_ENGLISH] = "Station Run",
[LANG_SPANISH] = "Carrera de estaciones",
[LANG_CATALAN] = "Carrera d'estacions",
[LANG_GERMAN] = "Bahnhofsrennen",
},
{ // LBL_STA_LEVEL
[LANG_ENGLISH] = "Level:",
[LANG_SPANISH] = "Nivel:",
[LANG_CATALAN] = "Nivell:",
[LANG_GERMAN] = NULL,
},
{ // LBL_STA_START
[LANG_ENGLISH] = "Start",
[LANG_SPANISH] = "Iniciar",
[LANG_CATALAN] = "Iniciar",
[LANG_GERMAN] = "Starten",
},
{ // LBL_STA_INSTR
[LANG_ENGLISH] = "Go to the station of your\ndestination color.\nPress it when you arrive",
[LANG_SPANISH] = "Ve a la estacion del color\nde tu destino.\nPulsala cuando llegues",
[LANG_CATALAN] = "Ves a l'estacio del color\nde la teva destinacio.\nPrem-la quan arribis",
[LANG_GERMAN] = "Gehen Sie sich zum Bahnhof\nIhrer Zielfarbe. Klicken Sie\ndas Symbol, wenn Sie ankommen",
},
{ // LBL_STA_EXCEL
[LANG_ENGLISH] = "Excellent!",
[LANG_SPANISH] = "Excelente!",
[LANG_CATALAN] = "Excel.lent!",
[LANG_GERMAN] = "Exzellent!",
},
{ // LBL_STA_GREAT
[LANG_ENGLISH] = "Great!",
[LANG_SPANISH] = "Muy bien!",
[LANG_CATALAN] = "Molt be!",
[LANG_GERMAN] = "Sehr gut!",
},
{ // LBL_STA_TIMEOUT
[LANG_ENGLISH] = "TIME'S UP\nThanks for playing",
[LANG_SPANISH] = "FIN DEL TIEMPO\nGracias por jugar",
[LANG_CATALAN] = "FINAL DEL TEMPS\nGracies per jugar",
[LANG_GERMAN] = "DIE ZEIT IST UM\nDanke furs Spielen",
},
{ // LBL_STA_STATIONS
[LANG_ENGLISH] = "Stations",
[LANG_SPANISH] = "Estaciones",
[LANG_CATALAN] = "Estacions",
[LANG_GERMAN] = "Bahnhofe",
},
{ // LBL_STA_TURNOUTS
[LANG_ENGLISH] = "Turnouts",
[LANG_SPANISH] = "Desvios",
[LANG_CATALAN] = "Agulles",
[LANG_GERMAN] = "Weichen",
},
{ // LBL_STA_TIME
[LANG_ENGLISH] = "Time",
[LANG_SPANISH] = "Tiempo",
[LANG_CATALAN] = "Temps",
[LANG_GERMAN] = "Zeit",
},
{ // LBL_STA_DESC
[LANG_ENGLISH] = "Address Orientat. Inverted",
[LANG_SPANISH] = "Direcc. Orientac. Invertido",
[LANG_CATALAN] = "Direcc. Orientacio Invertit",
[LANG_GERMAN] = "Adresse Ausricht. Invertiert",
},
};

View File

@@ -0,0 +1,885 @@
/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
*/
////////////////////////////////////////////////////////////
// ***** WINDOW OBJECTS *****
////////////////////////////////////////////////////////////
void openWindow(uint16_t id) {
uint16_t n;
char buf[MAX_LABEL_LNG];
switch (id) {
case WIN_LOGO:
createObject(OBJ_WIN, WIN_LOGO);
createObject(OBJ_DRAWSTR, DSTR_INIT);
createObject(OBJ_LABEL, LBL_PACO_TXT);
posObjStack1 = createObject(OBJ_LABEL, LBL_INIT);
createObject(OBJ_ICON, ICON_PACO);
createObject(OBJ_BAR, BAR_INIT);
drawWindow(WIN_LOGO);
break;
case WIN_CALIBRATE:
createObject(OBJ_WIN, WIN_CALIBRATE);
posObjStack2 = createObject(OBJ_LABEL, LBL_CAL);
posObjStack1 = createObject(OBJ_LABEL, LBL_PRESS);
newEvent(OBJ_WIN, WIN_CALIBRATE, EVNT_DRAW);
break;
case WIN_SSID:
createObject(OBJ_WIN, WIN_SSID);
createObject(OBJ_ICON, ICON_WIFI_SSID);
createObject(OBJ_BUTTON, BUT_SSID_CLOSE);
posObjStack1 = createObject(OBJ_LABEL, LBL_SCAN);
drawWindow(WIN_SSID);
scanWiFi();
objStack[posObjStack1].objID = LBL_SSID_SCAN;
createObject(OBJ_TXT, TXT_SSID1);
createObject(OBJ_TXT, TXT_SSID2);
createObject(OBJ_TXT, TXT_SSID3);
createObject(OBJ_TXT, TXT_SSID4);
createObject(OBJ_TXT, TXT_SSID5);
createObject(OBJ_TXT, TXT_SSID6);
scanWiFiFill();
newEvent(OBJ_WIN, WIN_SSID, EVNT_DRAW);
break;
case WIN_WIFI:
snprintf(ssidName, SSID_LNG, "%s", wifiSetting.ssid);
snprintf(keybPwdHideBuf, 9, "********");
snprintf(keybIP1Buf, 4, "%d", wifiSetting.CS_IP[0]);
snprintf(keybIP2Buf, 4, "%d", wifiSetting.CS_IP[1]);
snprintf(keybIP3Buf, 4, "%d", wifiSetting.CS_IP[2]);
snprintf(keybIP4Buf, 4, "%d", wifiSetting.CS_IP[3]);
setProtocolData();
createObject(OBJ_WIN, WIN_WIFI);
createObject(OBJ_ICON, ICON_WIFI_CFG);
createObject(OBJ_LABEL, LBL_SSID);
createObject(OBJ_TXT, TXT_SSID);
createObject(OBJ_LABEL, LBL_PWD_HIDE);
createObject(OBJ_TXT, TXT_PWD_HIDE);
createObject(OBJ_LABEL, LBL_IP);
createObject(OBJ_TXT, TXT_IP1);
createObject(OBJ_TXT, TXT_IP2);
createObject(OBJ_TXT, TXT_IP3);
createObject(OBJ_TXT, TXT_IP4);
createObject(OBJ_LABEL, LBL_PORT);
createObject(OBJ_TXT, TXT_PORT);
createObject(OBJ_KEYBOARD, KEYB_IP);
createObject(OBJ_LABEL, LBL_PROTOCOL);
createObject(OBJ_TXT, TXT_PROTOCOL);
createObject(OBJ_BUTTON, BUT_WIFI_OK);
newEvent(OBJ_WIN, WIN_WIFI, EVNT_DRAW);
break;
case WIN_WIFI_PWD:
snprintf(keybPwdBuf, PWD_LNG, wifiSetting.password);
createObject(OBJ_WIN, WIN_WIFI_PWD);
createObject(OBJ_TXT, TXT_PWD);
createObject(OBJ_KEYBOARD, KEYB_PWD);
createObject(OBJ_BUTTON, BUT_PWD_OK);
createObject(OBJ_BUTTON, BUT_PWD_CNCL);
newEvent(OBJ_WIN, WIN_WIFI_PWD, EVNT_DRAW);
break;
case WIN_PROTOCOL:
radioData[RAD_PROTOCOL].value = wifiSetting.protocol - CLIENT_Z21;
if (wifiSetting.protocol == CLIENT_LNET)
radioData[RAD_PROTOCOL_LN].value = (wifiSetting.serverType) ? 0 : 1;
else
radioData[RAD_PROTOCOL_LN].value = radioData[RAD_PROTOCOL_LN].num;
createObject(OBJ_WIN, WIN_PROTOCOL);
createObject(OBJ_RADIO, RAD_PROTOCOL);
createObject(OBJ_RADIO, RAD_PROTOCOL_LN);
createObject(OBJ_LABEL, LBL_SEL_PROT);
createObject(OBJ_LABEL, LBL_SEL_Z21);
createObject(OBJ_LABEL, LBL_SEL_XNET);
createObject(OBJ_LABEL, LBL_SEL_ECOS);
createObject(OBJ_LABEL, LBL_SEL_LNET);
createObject(OBJ_LABEL, LBL_SEL_LBSERVER);
createObject(OBJ_LABEL, LBL_SEL_BINARY);
createObject(OBJ_BUTTON, BUT_PROT_OK);
createObject(OBJ_BUTTON, BUT_OPTIONS);
newEvent(OBJ_WIN, WIN_PROTOCOL, EVNT_DRAW);
break;
case WIN_THROTTLE:
iconData[ICON_LOK_EDIT].bitmap = (wifiSetting.protocol == CLIENT_ECOS) ? info24 : wrench;
createObject(OBJ_WIN, WIN_THROTTLE);
createObject(OBJ_ICON, ICON_MENU);
createObject(OBJ_ICON, ICON_POWER);
createObject(OBJ_ICON, ICON_FNEXT);
createObject(OBJ_ICON, ICON_LOK_EDIT);
createObject(OBJ_FNC, FNC_ACC_PANEL);
//createObject(OBJ_ICON, ICON_FWD);
//createObject(OBJ_ICON, ICON_REV);
createObject(OBJ_TXT, TXT_CLOCK);
createObject(OBJ_TXT, TXT_LOCO_NAME);
createObject(OBJ_TXT, TXT_LOCO_ADDR);
createObject(OBJ_LPIC, LPIC_MAIN);
createObject(OBJ_GAUGE, GAUGE_SPEED);
createObject(OBJ_LABEL, LBL_KMH);
createObject(OBJ_FNC, FNC_FX0);
createObject(OBJ_FNC, FNC_FX1);
createObject(OBJ_FNC, FNC_FX2);
createObject(OBJ_FNC, FNC_FX3);
createObject(OBJ_FNC, FNC_FX4);
createObject(OBJ_FNC, FNC_FX5);
createObject(OBJ_FNC, FNC_FX6);
createObject(OBJ_FNC, FNC_FX7);
createObject(OBJ_FNC, FNC_FX8);
createObject(OBJ_FNC, FNC_FX9);
newEvent(OBJ_WIN, WIN_THROTTLE, EVNT_DRAW);
break;
case WIN_CONFIG:
buttonData[BUT_CFG_I_LANG].objID = DSTR_ENGLISH + currLanguage;
lastLanguage = currLanguage;
createObject(OBJ_WIN, WIN_CONFIG);
createObject(OBJ_DRAWSTR, DSTR_CFG_MENU);
createObject(OBJ_BUTTON, BUT_CFG_I_LANG);
createObject(OBJ_BUTTON, BUT_CFG_T_LANG);
createObject(OBJ_BUTTON, BUT_CFG_I_SCR);
createObject(OBJ_BUTTON, BUT_CFG_T_SCR);
createObject(OBJ_BUTTON, BUT_CFG_I_SPD);
createObject(OBJ_BUTTON, BUT_CFG_T_SPD);
createObject(OBJ_BUTTON, BUT_CFG_I_WIFI);
createObject(OBJ_BUTTON, BUT_CFG_T_WIFI);
createObject(OBJ_BUTTON, BUT_CFG_I_FCLK);
createObject(OBJ_BUTTON, BUT_CFG_T_FCLK);
createObject(OBJ_BUTTON, BUT_CFG_I_LOCK);
createObject(OBJ_BUTTON, BUT_CFG_T_LOCK);
createObject(OBJ_BUTTON, BUT_CFG_I_ABOUT);
createObject(OBJ_BUTTON, BUT_CFG_T_ABOUT);
createObject(OBJ_ICON, ICON_CFG_EXIT);
newEvent(OBJ_WIN, WIN_CONFIG, EVNT_DRAW);
break;
case WIN_SCREEN:
barData[BAR_BLIGHT].value = backlight;
switchData[SW_ROTATE].state = (locationUSB == USB_UP);
createObject(OBJ_WIN, WIN_SCREEN);
createObject(OBJ_ICON, ICON_BLIGHT);
createObject(OBJ_BAR, BAR_BLIGHT);
createObject(OBJ_SWITCH, SW_ROTATE);
createObject(OBJ_LABEL, LBL_SCR_ROTATE);
createObject(OBJ_BUTTON, BUT_CFG_TOUCH);
createObject(OBJ_BUTTON, BUT_SCR_OK);
createObject(OBJ_BUTTON, BUT_SCR_CNCL);
newEvent(OBJ_WIN, WIN_SCREEN, EVNT_DRAW);
break;
case WIN_SPEED:
switchData[SW_SHUNTING].state = shuntingMode;
radioData[RAD_STOP_MODE].value = (stopMode > 0) ? 1 : 0;
createObject(OBJ_WIN, WIN_SPEED);
createObject(OBJ_LABEL, LBL_SHUNTING);
createObject(OBJ_SWITCH, SW_SHUNTING);
createObject(OBJ_RADIO, RAD_STOP_MODE);
createObject(OBJ_ICON, ICON_STOP);
createObject(OBJ_LABEL, LBL_STOP_0);
createObject(OBJ_LABEL, LBL_STOP_E);
createObject(OBJ_BUTTON, BUT_SPD_OK);
newEvent(OBJ_WIN, WIN_SPEED, EVNT_DRAW);
break;
case WIN_SET_CLOCK:
snprintf(keybHourBuf, 3, "%d", clockHour);
snprintf(keybMinBuf, 3, "%d", clockMin);
snprintf(keybRateBuf, 4, "%d", clockRate);
showClockData(TXT_HOUR);
createObject(OBJ_WIN, WIN_SET_CLOCK);
createObject(OBJ_DRAWSTR, DSTR_CLOCK);
createObject(OBJ_CHAR, CHAR_CLK_COLON);
createObject(OBJ_ICON, ICON_SET_CLOCK);
createObject(OBJ_TXT, TXT_HOUR);
createObject(OBJ_TXT, TXT_MIN);
createObject(OBJ_TXT, TXT_RATE);
createObject(OBJ_LABEL, LBL_RATE);
createObject(OBJ_KEYBOARD, KEYB_CLOCK);
createObject(OBJ_BUTTON, BUT_CLOCK_OK);
createObject(OBJ_BUTTON, BUT_CLOCK_CNCL);
newEvent(OBJ_WIN, WIN_SET_CLOCK, EVNT_DRAW);
break;
case WIN_LOCK:
switchData[SW_LOCK_LOK].state = (bitRead(lockOptions, LOCK_SEL_LOCO)) ? true : false;
switchData[SW_LOCK_ACC].state = (bitRead(lockOptions, LOCK_TURNOUT)) ? true : false;
switchData[SW_LOCK_PRG].state = (bitRead(lockOptions, LOCK_PROG)) ? true : false;
createObject(OBJ_WIN, WIN_LOCK);
createObject(OBJ_SWITCH, SW_LOCK_LOK);
createObject(OBJ_SWITCH, SW_LOCK_ACC);
createObject(OBJ_SWITCH, SW_LOCK_PRG);
createObject(OBJ_LABEL, LBL_LOCK_LOK);
createObject(OBJ_LABEL, LBL_LOCK_ACC);
createObject(OBJ_LABEL, LBL_LOCK_PRG);
createObject(OBJ_BUTTON, BUT_LOCK);
newEvent(OBJ_WIN, WIN_LOCK, EVNT_DRAW);
break;
case WIN_ABOUT:
snprintf (aboutPacoMouseCYD, PWD_LNG + 1, "v%s.%s%s", VER_H, VER_L, VER_R);
snprintf (aboutIP, PWD_LNG + 1, "IP: %u.%u.%u.%u", WiFi.localIP().operator[](0), WiFi.localIP().operator[](1), WiFi.localIP().operator[](2), WiFi.localIP().operator[](3));
snprintf (aboutMAC, PWD_LNG + 1, "MAC: %s", WiFi.macAddress().c_str());
createObject(OBJ_WIN, WIN_ABOUT);
createObject(OBJ_DRAWSTR, DSTR_ABOUT);
createObject(OBJ_LABEL, LBL_PACO_TXT);
createObject(OBJ_ICON, ICON_ABOUT_PACO);
createObject(OBJ_TXT, TXT_ABOUT);
createObject(OBJ_TXT, TXT_ABOUT_IP);
createObject(OBJ_TXT, TXT_ABOUT_MAC);
createObject(OBJ_LABEL, LBL_PACO_WEB);
newEvent(OBJ_WIN, WIN_ABOUT, EVNT_DRAW);
break;
case WIN_LOK_EDIT:
snprintf (locoEditName, NAME_LNG + 1, "%s", locoData[myLocoData].myName );
sprintf (locoEditAddr, "%d", locoData[myLocoData].myAddr.address);
sprintf (locoEditID, "%d", locoData[myLocoData].myLocoID);
sprintf (locoEditVmax, "%d", locoData[myLocoData].myVmax);
lpicData[LPIC_LOK_EDIT].id = locoData[myLocoData].myLocoID;
for (n = 0; n < 29; n++)
fncData[FNC_F0 + n].idIcon = locoData[myLocoData].myFuncIcon[n];
createObject(OBJ_WIN, WIN_LOK_EDIT);
createObject(OBJ_LABEL, LBL_ADDR);
createObject(OBJ_LABEL, LBL_IMAGE);
createObject(OBJ_LABEL, LBL_NAME);
createObject(OBJ_LABEL, LBL_VMAX);
createObject(OBJ_LPIC, LPIC_LOK_EDIT);
createObject(OBJ_TXT, TXT_EDIT_ADDR);
createObject(OBJ_TXT, TXT_EDIT_NAME);
createObject(OBJ_TXT, TXT_EDIT_IMAGE);
createObject(OBJ_TXT, TXT_EDIT_VMAX);
createObject(OBJ_BUTTON, BUT_EDIT_FUNC);
createObject(OBJ_BUTTON, BUT_EDIT_CNCL);
if (wifiSetting.protocol != CLIENT_ECOS) {
createObject(OBJ_BUTTON, BUT_EDIT_OK);
if ((locoData[myLocoData].mySpeed < 2) && (countLocoInStack() > 1)) // stopped and remaining locos in stack
createObject(OBJ_BUTTON, BUT_EDIT_DEL);
}
newEvent(OBJ_WIN, WIN_LOK_EDIT, EVNT_DRAW);
break;
case WIN_EDIT_NAME:
snprintf(keybNameBuf, NAME_LNG + 1, locoData[myLocoData].myName);
txtData[TXT_NAME].maxLength = NAME_LNG;
createObject(OBJ_WIN, WIN_EDIT_NAME);
createObject(OBJ_TXT, TXT_NAME);
createObject(OBJ_KEYBOARD, KEYB_NAME);
createObject(OBJ_BUTTON, BUT_NAME_OK);
createObject(OBJ_BUTTON, BUT_NAME_CNCL);
newEvent(OBJ_WIN, WIN_EDIT_NAME, EVNT_DRAW);
break;
case WIN_FUNC:
createObject(OBJ_WIN, WIN_FUNC);
for (n = 0; n < 29; n++)
createObject(OBJ_FNC, FNC_F0 + n);
createObject(OBJ_LABEL, LBL_ADDR);
createObject(OBJ_TXT, TXT_EDIT_ADDR);
createObject(OBJ_LABEL, LBL_EDIT_FUNC);
if (wifiSetting.protocol != CLIENT_ECOS)
createObject(OBJ_BUTTON, BUT_FNC_OK);
createObject(OBJ_BUTTON, BUT_FNC_CNCL);
newEvent(OBJ_WIN, WIN_FUNC, EVNT_DRAW);
break;
case WIN_CHG_FUNC:
createObject(OBJ_WIN, WIN_CHG_FUNC);
createObject(OBJ_FNC, FNC_CHG);
createObject(OBJ_TXT, TXT_EDIT_FNC);
newEvent(OBJ_WIN, WIN_CHG_FUNC, EVNT_DRAW);
break;
case WIN_VMAX:
createObject(OBJ_WIN, WIN_VMAX);
createObject(OBJ_TXT, TXT_KEYB_VMAX);
createObject(OBJ_KEYBOARD, KEYB_VMAX);
newEvent(OBJ_WIN, WIN_VMAX, EVNT_DRAW);
break;
case WIN_SEL_LOCO:
createObject(OBJ_WIN, WIN_SEL_LOCO);
createObject(OBJ_DRAWSTR, DSTR_SELLOK);
posObjStack1 = createObject(OBJ_ICON, ICON_LAST_UP);
prepareLocoList();
createObject(OBJ_ICON, ICON_SEL_LOK);
if (wifiSetting.protocol != CLIENT_ECOS)
createObject(OBJ_FNC, FNC_SEL_KEYPAD);
createObject(OBJ_TXT, TXT_SEL_ADDR1);
createObject(OBJ_TXT, TXT_SEL_NAME1);
createObject(OBJ_TXT, TXT_SEL_ADDR2);
createObject(OBJ_TXT, TXT_SEL_NAME2);
createObject(OBJ_TXT, TXT_SEL_ADDR3);
createObject(OBJ_TXT, TXT_SEL_NAME3);
createObject(OBJ_TXT, TXT_SEL_ADDR4);
createObject(OBJ_TXT, TXT_SEL_NAME4);
createObject(OBJ_TXT, TXT_SEL_ADDR5);
createObject(OBJ_TXT, TXT_SEL_NAME5);
createObject(OBJ_TXT, TXT_SEL_ADDR6);
createObject(OBJ_TXT, TXT_SEL_NAME6);
newEvent(OBJ_WIN, WIN_SEL_LOCO, EVNT_DRAW);
break;
case WIN_ENTER_ADDR:
locoKeybAddr[0] = '\0';
createObject(OBJ_WIN, WIN_ENTER_ADDR);
createObject(OBJ_TXT, TXT_KEYB_ADDR);
createObject(OBJ_KEYBOARD, KEYB_ADDR);
newEvent(OBJ_WIN, WIN_ENTER_ADDR, EVNT_DRAW);
break;
case WIN_SEL_IMAGE:
createObject(OBJ_WIN, WIN_SEL_IMAGE);
createObject(OBJ_LABEL, LBL_SEL_IMAGE);
createObject(OBJ_BUTTON, BUT_IMAGE_CNCL);
drawWindow(WIN_SEL_IMAGE);
populateImageList();
createObject(OBJ_LPIC, LPIC_SEL_IMG1);
createObject(OBJ_LPIC, LPIC_SEL_IMG2);
createObject(OBJ_LPIC, LPIC_SEL_IMG3);
createObject(OBJ_LPIC, LPIC_SEL_IMG4);
createObject(OBJ_LPIC, LPIC_SEL_IMG5);
createObject(OBJ_LPIC, LPIC_SEL_IMG6);
createObject(OBJ_ICON, ICON_PREV_IMAGE);
createObject(OBJ_ICON, ICON_NEXT_IMAGE);
newEvent(OBJ_WIN, WIN_SEL_IMAGE, EVNT_DRAW);
break;
case WIN_MENU:
createObject(OBJ_WIN, WIN_MENU);
createObject(OBJ_BUTTON, BUT_MENU_I_DRIVE);
createObject(OBJ_BUTTON, BUT_MENU_T_DRIVE);
createObject(OBJ_BUTTON, BUT_MENU_I_ACC);
createObject(OBJ_BUTTON, BUT_MENU_T_ACC);
createObject(OBJ_BUTTON, BUT_MENU_I_CV);
createObject(OBJ_BUTTON, BUT_MENU_T_CV);
createObject(OBJ_BUTTON, BUT_MENU_I_CFG);
createObject(OBJ_BUTTON, BUT_MENU_T_CFG);
createObject(OBJ_BUTTON, BUT_MENU_I_UTILS);
createObject(OBJ_BUTTON, BUT_MENU_T_UTILS);
createObject(OBJ_DRAWSTR, DSTR_MENU);
newEvent(OBJ_WIN, WIN_MENU, EVNT_DRAW);
break;
case WIN_OPTIONS:
setOptionsData();
createObject(OBJ_WIN, WIN_OPTIONS);
switch (wifiSetting.protocol) {
case CLIENT_Z21:
//createObject(OBJ_SWITCH, SW_OPT_TT_OFFSET);
//createObject(OBJ_LABEL, LBL_OPT_TT_OFFSET);
createObject(OBJ_SWITCH, SW_OPT_ADR);
createObject(OBJ_LABEL, LBL_OPT_ADR);
break;
case CLIENT_XNET:
//createObject(OBJ_SWITCH, SW_OPT_TT_OFFSET);
//createObject(OBJ_LABEL, LBL_OPT_TT_OFFSET);
break;
case CLIENT_LNET:
switchData[SW_OPT_DISCOVER].state = (autoIdentifyCS > 0) ? true : false;
createObject(OBJ_SWITCH, SW_OPT_DISCOVER);
createObject(OBJ_LABEL, LBL_OPT_DISCOVER);
createObject(OBJ_RADIO, RAD_CSTATION);
createObject(OBJ_LABEL, LBL_OPT_IB2);
createObject(OBJ_LABEL, LBL_OPT_UHLI);
createObject(OBJ_LABEL, LBL_OPT_DIG);
break;
case CLIENT_ECOS:
break;
}
createObject(OBJ_BUTTON, BUT_OPT_OK);
newEvent(OBJ_WIN, WIN_OPTIONS, EVNT_DRAW);
break;
case WIN_SPEEDO:
speedoSpeed = 0;
speedoPhase = SPD_WAIT;
setSpeedoPhase(SPD_WAIT);
setTextSpeedo();
snprintf(spdLengthBuf, NAME_LNG + 1, "%d", speedoLength);
snprintf(spdSpeedBuf, NAME_LNG + 1, "%d km/h", speedoSpeed);
iconData[ICON_SPEEDO_LOK].x = 40 + (speedoPhase * 32);
drawStrData[DSTR_SPEEDO_BLANK].x = 40 + (speedoPhase * 32);
lpicData[LPIC_SPEEDO].id = locoData[myLocoData].myLocoID;
createObject(OBJ_WIN, WIN_SPEEDO);
createObject(OBJ_LPIC, LPIC_SPEEDO);
createObject(OBJ_LABEL, LBL_SCALE);
createObject(OBJ_LABEL, LBL_MM);
createObject(OBJ_GAUGE, GAUGE_SPEEDO);
createObject(OBJ_FNC, FNC_SPEEDO_DIR);
createObject(OBJ_DRAWSTR, DSTR_SPEEDO_BLANK);
createObject(OBJ_DRAWSTR, DSTR_SPEEDO_TRK);
createObject(OBJ_ICON, ICON_SPEEDO_LOK);
createObject(OBJ_ICON, ICON_SPEEDO_RADAR);
createObject(OBJ_BUTTON, BUT_SPEEDO_CNCL);
createObject(OBJ_BUTTON, BUT_SPEEDO_CV);
createObject(OBJ_TXT, TXT_SPEEDO_SCALE);
createObject(OBJ_TXT, TXT_SPEEDO_LNG);
createObject(OBJ_TXT, TXT_SPEEDO_SPD);
newEvent(OBJ_WIN, WIN_SPEEDO, EVNT_DRAW);
break;
case WIN_SPEEDO_LNG:
snprintf(speedoKeybLng, PORT_LNG + 1, "%d", speedoLength);
createObject(OBJ_WIN, WIN_SPEEDO_LNG);
createObject(OBJ_TXT, TXT_EDIT_LNG);
createObject(OBJ_KEYBOARD, KEYB_LNG);
newEvent(OBJ_WIN, WIN_SPEEDO_LNG, EVNT_DRAW);
break;
case WIN_SPEEDO_SCALE:
setTextSpeedo();
createObject(OBJ_WIN, WIN_SPEEDO_SCALE);
createObject(OBJ_TXT, TXT_EDIT_SCALE);
createObject(OBJ_TXT, TXT_NUM_SCALE);
createObject(OBJ_KEYBOARD, KEYB_SCALE);
createObject(OBJ_BUTTON, BUT_SPEEDO_H0);
createObject(OBJ_BUTTON, BUT_SPEEDO_N);
createObject(OBJ_BUTTON, BUT_SPEEDO_TT);
createObject(OBJ_BUTTON, BUT_SPEEDO_Z);
createObject(OBJ_BUTTON, BUT_SPEEDO_0);
newEvent(OBJ_WIN, WIN_SPEEDO_SCALE, EVNT_DRAW);
break;
case WIN_READ_CV:
createObject(OBJ_WIN, WIN_READ_CV);
createObject(OBJ_DRAWSTR, DSTR_CFG_MENU);
createObject(OBJ_BUTTON, BUT_CV_ADDR);
createObject(OBJ_BUTTON, BUT_CV_SPD_L);
createObject(OBJ_BUTTON, BUT_CV_SPD_M);
createObject(OBJ_BUTTON, BUT_CV_SPD_H);
createObject(OBJ_BUTTON, BUT_CV_ACC);
createObject(OBJ_BUTTON, BUT_CV_DEC);
createObject(OBJ_BUTTON, BUT_CV_CFG);
createObject(OBJ_BUTTON, BUT_CV_MAN);
newEvent(OBJ_WIN, WIN_READ_CV, EVNT_DRAW);
break;
case WIN_PROG_CV:
//buttonData[BUT_CV_LNCV].backgnd = (wifiSetting.protocol == CLIENT_LNET) ? COLOR_CREAM : COLOR_LIGHTBLACK;
setFieldsCV();
setBitsCV();
setStatusCV();
switchData[SW_POM].state = modeProg;
createObject(OBJ_WIN, WIN_PROG_CV);
createObject(OBJ_LABEL, LBL_CV);
createObject(OBJ_LABEL, LBL_POM);
createObject(OBJ_LABEL, LBL_BITS);
createObject(OBJ_SWITCH, SW_POM);
createObject(OBJ_BUTTON, BUT_CV_READ);
createObject(OBJ_BUTTON, BUT_CV_CNCL);
if (wifiSetting.protocol == CLIENT_LNET)
createObject(OBJ_BUTTON, BUT_CV_LNCV);
createObject(OBJ_KEYBOARD, KEYB_CV);
createObject(OBJ_CHAR, CHAR_CV_EQUAL);
createObject(OBJ_BUTTON, BUT_CV_0);
createObject(OBJ_BUTTON, BUT_CV_1);
createObject(OBJ_BUTTON, BUT_CV_2);
createObject(OBJ_BUTTON, BUT_CV_3);
createObject(OBJ_BUTTON, BUT_CV_4);
createObject(OBJ_BUTTON, BUT_CV_5);
createObject(OBJ_BUTTON, BUT_CV_6);
createObject(OBJ_BUTTON, BUT_CV_7);
createObject(OBJ_TXT, TXT_CV);
createObject(OBJ_TXT, TXT_CV_VAL);
createObject(OBJ_TXT, TXT_CV_STATUS);
newEvent(OBJ_WIN, WIN_PROG_CV, EVNT_DRAW);
break;
case WIN_PROG_ADDR:
getLabelTxt(LBL_CV_ADDR, buf);
snprintf(cvStatusBuf, PWD_LNG + 1, "%s", buf);
createObject(OBJ_WIN, WIN_PROG_ADDR);
createObject(OBJ_TXT, TXT_CV_STATUS);
createObject(OBJ_ICON, ICON_ADDR);
createObject(OBJ_TXT, TXT_CV_ADDR);
createObject(OBJ_KEYBOARD, KEYB_CV_ADDR);
createObject(OBJ_BUTTON, BUT_ADDR_CNCL);
newEvent(OBJ_WIN, WIN_PROG_ADDR, EVNT_DRAW);
break;
case WIN_PROG_LNCV:
setFieldsLNCV();
createObject(OBJ_WIN, WIN_PROG_LNCV);
createObject(OBJ_KEYBOARD, KEYB_LNCV);
createObject(OBJ_LABEL, LBL_LNCV_ART);
createObject(OBJ_LABEL, LBL_LNCV_MOD);
createObject(OBJ_LABEL, LBL_LNCV_NUM);
createObject(OBJ_BUTTON, BUT_LNCV_FIND);
createObject(OBJ_BUTTON, BUT_LNCV_CNCL);
createObject(OBJ_TXT, TXT_LNCV_ART);
createObject(OBJ_TXT, TXT_LNCV_MOD);
createObject(OBJ_TXT, TXT_LNCV_ADR);
createObject(OBJ_TXT, TXT_LNCV_VAL);
createObject(OBJ_CHAR, CHAR_LNCV_EQUAL);
newEvent(OBJ_WIN, WIN_PROG_LNCV, EVNT_DRAW);
break;
case WIN_STEAM:
fncData[FNC_ST_SMOKE].state = false;
createObject(OBJ_WIN, WIN_STEAM);
createObject(OBJ_DRAWSTR, DSTR_STEAM);
createObject(OBJ_ICON, ICON_POWER);
createObject(OBJ_ICON, ICON_MANOMETER);
createObject(OBJ_ICON, ICON_STEAM_EDIT);
createObject(OBJ_BUTTON, BUT_STEAM_CNCL);
createObject(OBJ_FNC, FNC_ST_WATER);
createObject(OBJ_FNC, FNC_ST_TENDER);
createObject(OBJ_FNC, FNC_ST_WHISTLE);
createObject(OBJ_FNC, FNC_ST_FIRE);
createObject(OBJ_FNC, FNC_ST_SMOKE);
createObject(OBJ_BAR, BAR_JOHNSON);
createObject(OBJ_BAR, BAR_WATER);
createObject(OBJ_BAR, BAR_TENDER);
createObject(OBJ_BAR, BAR_BRAKE);
newEvent(OBJ_WIN, WIN_STEAM, EVNT_DRAW);
break;
case WIN_UTIL:
createObject(OBJ_WIN, WIN_UTIL);
createObject(OBJ_BUTTON, BUT_UTL_I_SPEEDO);
createObject(OBJ_BUTTON, BUT_UTL_T_SPEEDO);
createObject(OBJ_BUTTON, BUT_UTL_I_STEAM);
createObject(OBJ_BUTTON, BUT_UTL_T_STEAM);
createObject(OBJ_BUTTON, BUT_UTL_I_SCAN);
createObject(OBJ_BUTTON, BUT_UTL_T_SCAN);
createObject(OBJ_BUTTON, BUT_UTL_I_STA);
createObject(OBJ_BUTTON, BUT_UTL_T_STA);
createObject(OBJ_ICON, ICON_UTL_EXIT);
createObject(OBJ_DRAWSTR, DSTR_UTL_MENU);
newEvent(OBJ_WIN, WIN_UTIL, EVNT_DRAW);
break;
case WIN_ACCESSORY:
editAccessory = false;
winData[WIN_ACCESSORY].backgnd = COLOR_WHITE;
updateAccPanel();
createObject(OBJ_WIN, WIN_ACCESSORY);
createObject(OBJ_BUTTON, BUT_ACC_0);
createObject(OBJ_BUTTON, BUT_ACC_1);
createObject(OBJ_BUTTON, BUT_ACC_2);
createObject(OBJ_BUTTON, BUT_ACC_3);
createObject(OBJ_BUTTON, BUT_ACC_4);
createObject(OBJ_BUTTON, BUT_ACC_5);
createObject(OBJ_BUTTON, BUT_ACC_6);
createObject(OBJ_BUTTON, BUT_ACC_7);
createObject(OBJ_BUTTON, BUT_ACC_8);
createObject(OBJ_BUTTON, BUT_ACC_9);
createObject(OBJ_BUTTON, BUT_ACC_10);
createObject(OBJ_BUTTON, BUT_ACC_11);
createObject(OBJ_BUTTON, BUT_ACC_12);
createObject(OBJ_BUTTON, BUT_ACC_13);
createObject(OBJ_BUTTON, BUT_ACC_14);
createObject(OBJ_BUTTON, BUT_ACC_15);
createObject(OBJ_BUTTON, BUT_ACC_CNCL);
createObject(OBJ_BUTTON, BUT_ACC_EDIT);
createObject(OBJ_TXT, TXT_ACC_0);
createObject(OBJ_TXT, TXT_ACC_1);
createObject(OBJ_TXT, TXT_ACC_2);
createObject(OBJ_TXT, TXT_ACC_3);
createObject(OBJ_TXT, TXT_ACC_4);
createObject(OBJ_TXT, TXT_ACC_5);
createObject(OBJ_TXT, TXT_ACC_6);
createObject(OBJ_TXT, TXT_ACC_7);
createObject(OBJ_TXT, TXT_ACC_8);
createObject(OBJ_TXT, TXT_ACC_9);
createObject(OBJ_TXT, TXT_ACC_10);
createObject(OBJ_TXT, TXT_ACC_11);
createObject(OBJ_TXT, TXT_ACC_12);
createObject(OBJ_TXT, TXT_ACC_13);
createObject(OBJ_TXT, TXT_ACC_14);
createObject(OBJ_TXT, TXT_ACC_15);
createObject(OBJ_TXT, TXT_PANEL);
newEvent(OBJ_WIN, WIN_ACCESSORY, EVNT_DRAW);
break;
case WIN_PANELS:
createObject(OBJ_WIN, WIN_PANELS);
createObject(OBJ_TXT, TXT_PANEL0);
createObject(OBJ_TXT, TXT_PANEL1);
createObject(OBJ_TXT, TXT_PANEL2);
createObject(OBJ_TXT, TXT_PANEL3);
createObject(OBJ_TXT, TXT_PANEL4);
createObject(OBJ_TXT, TXT_PANEL5);
createObject(OBJ_TXT, TXT_PANEL6);
createObject(OBJ_TXT, TXT_PANEL7);
createObject(OBJ_TXT, TXT_PANEL8);
createObject(OBJ_TXT, TXT_PANEL9);
createObject(OBJ_TXT, TXT_PANEL10);
createObject(OBJ_TXT, TXT_PANEL11);
createObject(OBJ_TXT, TXT_PANEL12);
createObject(OBJ_TXT, TXT_PANEL13);
createObject(OBJ_TXT, TXT_PANEL14);
createObject(OBJ_TXT, TXT_PANEL15);
newEvent(OBJ_WIN, WIN_PANELS, EVNT_DRAW);
break;
case WIN_PANEL_NAME:
snprintf(keybNameBuf, PANEL_LNG + 1, panelNameBuf);
txtData[TXT_NAME].maxLength = PANEL_LNG;
createObject(OBJ_WIN, WIN_PANEL_NAME);
createObject(OBJ_TXT, TXT_NAME);
createObject(OBJ_KEYBOARD, KEYB_NAME);
createObject(OBJ_BUTTON, BUT_NAME_OK);
createObject(OBJ_BUTTON, BUT_NAME_CNCL);
newEvent(OBJ_WIN, WIN_PANEL_NAME, EVNT_DRAW);
break;
case WIN_ACC_CTRL:
snprintf(accKeybAddr, ADDR_LNG + 1, "%d", myTurnout);
createObject(OBJ_WIN, WIN_ACC_CTRL);
createObject(OBJ_TXT, TXT_ACC_ADDR);
createObject(OBJ_KEYBOARD, KEYB_ACC);
createObject(OBJ_ICON, ICON_KEYB_ACC);
createObject(OBJ_BUTTON, BUT_ACC_RED);
createObject(OBJ_BUTTON, BUT_ACC_GREEN);
newEvent(OBJ_WIN, WIN_ACC_CTRL, EVNT_DRAW);
break;
case WIN_ACC_ASPECT:
createObject(OBJ_WIN, WIN_ACC_ASPECT);
createObject(OBJ_BUTTON, BUT_ACC_ASPECT0);
createObject(OBJ_BUTTON, BUT_ACC_ASPECT1);
createObject(OBJ_BUTTON, BUT_ACC_ASPECT2);
if (currAccAspects == 4)
createObject(OBJ_BUTTON, BUT_ACC_ASPECT3);
newEvent(OBJ_WIN, WIN_ACC_ASPECT, EVNT_DRAW);
break;
case WIN_ACC_TYPE:
createObject(OBJ_WIN, WIN_ACC_TYPE);
createObject(OBJ_LABEL, LBL_ACC_TYPE);
createObject(OBJ_FNC, FNC_ACC_TYPE);
newEvent(OBJ_WIN, WIN_ACC_TYPE, EVNT_DRAW);
break;
case WIN_ACC_EDIT:
n = accDef[currAccEdit.type].aspects;
winData[WIN_ACC_EDIT].h = 130 + (n * 40);
buttonData[BUT_TYPE_OK].y = 93 + (n * 40);
buttonData[BUT_TYPE_CNCL].y = 93 + (n * 40);
iconData[ICON_TYPE_OK].y = 97 + (n * 40);
iconData[ICON_TYPE_CNCL].y = 97 + (n * 40);
createObject(OBJ_WIN, WIN_ACC_EDIT);
createObject(OBJ_LABEL, LBL_ACC_NAME);
createObject(OBJ_LABEL, LBL_ACC_ADDR);
createObject(OBJ_TXT, TXT_ACC_NAME);
createObject(OBJ_TXT, TXT_ACC_ADDR1);
createObject(OBJ_FNC, FNC_EDIT_ASPECT0);
createObject(OBJ_BUTTON, BUT_ACC_OUT0);
createObject(OBJ_BUTTON, BUT_ACC_OUT1);
if (n > 1) {
createObject(OBJ_FNC, FNC_EDIT_ASPECT1);
createObject(OBJ_BUTTON, BUT_ACC_OUT4);
createObject(OBJ_BUTTON, BUT_ACC_OUT5);
}
if (n > 2) {
createObject(OBJ_TXT, TXT_ACC_ADDR2);
createObject(OBJ_ICON, ICON_PLUS_ONE);
createObject(OBJ_FNC, FNC_EDIT_ASPECT2);
createObject(OBJ_BUTTON, BUT_ACC_OUT2);
createObject(OBJ_BUTTON, BUT_ACC_OUT3);
createObject(OBJ_BUTTON, BUT_ACC_OUT6);
createObject(OBJ_BUTTON, BUT_ACC_OUT7);
createObject(OBJ_BUTTON, BUT_ACC_OUT8);
createObject(OBJ_BUTTON, BUT_ACC_OUT9);
createObject(OBJ_BUTTON, BUT_ACC_OUT10);
createObject(OBJ_BUTTON, BUT_ACC_OUT11);
}
if (n > 3) {
createObject(OBJ_FNC, FNC_EDIT_ASPECT3);
createObject(OBJ_BUTTON, BUT_ACC_OUT12);
createObject(OBJ_BUTTON, BUT_ACC_OUT13);
createObject(OBJ_BUTTON, BUT_ACC_OUT14);
createObject(OBJ_BUTTON, BUT_ACC_OUT15);
}
createObject(OBJ_BUTTON, BUT_TYPE_OK);
createObject(OBJ_BUTTON, BUT_TYPE_CNCL);
newEvent(OBJ_WIN, WIN_ACC_EDIT, EVNT_DRAW);
break;
case WIN_ACC_NAME:
snprintf(keybNameBuf, ACC_LNG + 1, accKeybName);
txtData[TXT_NAME].maxLength = ACC_LNG;
createObject(OBJ_WIN, WIN_ACC_NAME);
createObject(OBJ_TXT, TXT_NAME);
createObject(OBJ_KEYBOARD, KEYB_NAME);
createObject(OBJ_BUTTON, BUT_NAME_OK);
createObject(OBJ_BUTTON, BUT_NAME_CNCL);
newEvent(OBJ_WIN, WIN_ACC_NAME, EVNT_DRAW);
break;
case WIN_ACC_ADDR1:
snprintf(accKeybAdrEdit, ADDR_LNG + 1, "%d", currAccEdit.addr);
createObject(OBJ_WIN, WIN_ACC_ADDR1);
createObject(OBJ_TXT, TXT_ACC_EDIT);
createObject(OBJ_KEYBOARD, KEYB_ACC_ADDR);
newEvent(OBJ_WIN, WIN_ACC_ADDR1, EVNT_DRAW);
break;
case WIN_ACC_ADDR2:
snprintf(accKeybAdrEdit, ADDR_LNG + 1, "%d", currAccEdit.addr2);
createObject(OBJ_WIN, WIN_ACC_ADDR2);
createObject(OBJ_TXT, TXT_ACC_EDIT);
createObject(OBJ_KEYBOARD, KEYB_ACC_ADDR);
newEvent(OBJ_WIN, WIN_ACC_ADDR2, EVNT_DRAW);
break;
case WIN_WIFI_SCAN:
setTimer(TMR_SCAN, 5, TMR_ONESHOT);
createObject(OBJ_WIN, WIN_WIFI_SCAN);
createObject(OBJ_DRAWSTR, DSTR_WIFI_SCAN);
createObject(OBJ_LABEL, LBL_SSID_SCAN);
createObject(OBJ_FNC, FNC_SCAN_RESET);
newEvent(OBJ_WIN, WIN_WIFI_SCAN, EVNT_DRAW);
break;
case WIN_STA_RUN:
updateStationTime(staTime);
updateStationLevel();
updateStationStars();
updateTargetStations();
createObject(OBJ_WIN, WIN_STA_RUN);
createObject(OBJ_LABEL, LBL_STA_RUN);
createObject(OBJ_LABEL, LBL_STA_LEVEL);
createObject(OBJ_LABEL, LBL_STA_INSTR);
createObject(OBJ_FNC, FNC_STA_STARS);
createObject(OBJ_ICON, ICON_STA_CLOCK);
createObject(OBJ_ICON, ICON_STA_STATION);
createObject(OBJ_ICON, ICON_STA_EDIT);
createObject(OBJ_BUTTON, BUT_STA_START);
createObject(OBJ_BUTTON, BUT_STA_CNCL);
createObject(OBJ_TXT, TXT_STA_LEVEL);
createObject(OBJ_TXT, TXT_STA_STARS);
createObject(OBJ_TXT, TXT_STA_STATION);
createObject(OBJ_TXT, TXT_STA_CLOCK);
newEvent(OBJ_WIN, WIN_STA_RUN, EVNT_DRAW);
break;
case WIN_STA_PLAY:
updateTurnoutButtons();
fncData[FNC_STA_RAYO].state = isTrackOff();
createObject(OBJ_WIN, WIN_STA_PLAY);
createObject(OBJ_DRAWSTR, DSTR_STATION_PLAY);
createObject(OBJ_ICON, ICON_STA_TARGET);
createObject(OBJ_ICON, ICON_STA_TRAIN);
createObject(OBJ_ICON, ICON_STA_PIN);
createObject(OBJ_ICON, ICON_STA_TIME);
createObject(OBJ_ICON, ICON_STA_COUNT);
createObject(OBJ_TXT, TXT_STA_TIME);
createObject(OBJ_TXT, TXT_STA_COUNT);
createObject(OBJ_TXT, TXT_STA_STARC);
createObject(OBJ_GAUGE, GAUGE_STATION);
createObject(OBJ_FNC, FNC_STA_DIR);
createObject(OBJ_FNC, FNC_STA_STARC);
createObject(OBJ_FNC, FNC_STA_RAYO);
createObject(OBJ_BUTTON, BUT_STA_STOP);
switch (staMaxTurnout) {
case 1:
fncData[FNC_STA_ACC0].x = 104;
buttonData[BUT_STA_ACC0].x = 100;
createObject(OBJ_BUTTON, BUT_STA_ACC0);
break;
case 2:
fncData[FNC_STA_ACC0].x = 54;
fncData[FNC_STA_ACC1].x = 154;
buttonData[BUT_STA_ACC0].x = 50;
buttonData[BUT_STA_ACC1].x = 150;
createObject(OBJ_BUTTON, BUT_STA_ACC0);
createObject(OBJ_BUTTON, BUT_STA_ACC1);
break;
case 3:
fncData[FNC_STA_ACC0].x = 40;
fncData[FNC_STA_ACC1].x = 104;
fncData[FNC_STA_ACC2].x = 168;
buttonData[BUT_STA_ACC0].x = 36;
buttonData[BUT_STA_ACC1].x = 100;
buttonData[BUT_STA_ACC2].x = 164;
createObject(OBJ_BUTTON, BUT_STA_ACC0);
createObject(OBJ_BUTTON, BUT_STA_ACC1);
createObject(OBJ_BUTTON, BUT_STA_ACC2);
break;
default:
fncData[FNC_STA_ACC0].x = 20;
fncData[FNC_STA_ACC1].x = 76;
fncData[FNC_STA_ACC2].x = 132;
fncData[FNC_STA_ACC3].x = 188;
buttonData[BUT_STA_ACC0].x = 16;
buttonData[BUT_STA_ACC1].x = 72;
buttonData[BUT_STA_ACC2].x = 128;
buttonData[BUT_STA_ACC3].x = 184;
createObject(OBJ_BUTTON, BUT_STA_ACC0);
createObject(OBJ_BUTTON, BUT_STA_ACC1);
createObject(OBJ_BUTTON, BUT_STA_ACC2);
createObject(OBJ_BUTTON, BUT_STA_ACC3);
break;
}
newEvent(OBJ_WIN, WIN_STA_PLAY, EVNT_DRAW);
break;
case WIN_STA_STARS:
createObject(OBJ_WIN, WIN_STA_STARS);
if (staCurrTime > 0) {
staStars++;
createObject(OBJ_FNC, FNC_STA_STAR1);
if (staCurrTime > 10) { // time remaining
staStars++;
createObject(OBJ_FNC, FNC_STA_STAR2);
createObject(OBJ_LABEL, LBL_STA_EXCEL);
}
else {
createObject(OBJ_LABEL, LBL_STA_GREAT);
}
updateStationStars();
}
else {
createObject(OBJ_ICON, ICON_STA_TIMEOUT);
createObject(OBJ_LABEL, LBL_STA_TIMEOUT);
}
newEvent(OBJ_WIN, WIN_STA_STARS, EVNT_DRAW);
break;
case WIN_STA_EDIT:
snprintf(staStartTimeBuf, IP_LNG + 1, "%d", staStartTime);
snprintf(staStatNumBuf, IP_LNG + 1, "%d", staMaxStations);
snprintf(staTurnNumBuf, IP_LNG + 1, "%d", staMaxTurnout);
snprintf(staTurnout1Buf, ADDR_LNG + 1, "%d", staTurnoutAdr1);
snprintf(staTurnout2Buf, ADDR_LNG + 1, "%d", staTurnoutAdr2);
snprintf(staTurnout3Buf, ADDR_LNG + 1, "%d", staTurnoutAdr3);
snprintf(staTurnout4Buf, ADDR_LNG + 1, "%d", staTurnoutAdr4);
for (n = 0; n < 8; n++)
switchData[SW_STA_OR1 + n].state = bitRead(staTurnoutDef, n);
createObject(OBJ_WIN, WIN_STA_EDIT);
createObject(OBJ_LABEL, LBL_STA_STATIONS);
createObject(OBJ_LABEL, LBL_STA_TURNOUTS);
createObject(OBJ_LABEL, LBL_STA_TIME);
createObject(OBJ_LABEL, LBL_STA_DESC);
createObject(OBJ_TXT, TXT_STA_STARTTIME);
createObject(OBJ_TXT, TXT_STA_STATNUM);
createObject(OBJ_TXT, TXT_STA_TURNNUM);
createObject(OBJ_TXT, TXT_STA_TURNOUT1);
createObject(OBJ_TXT, TXT_STA_TURNOUT2);
createObject(OBJ_TXT, TXT_STA_TURNOUT3);
createObject(OBJ_TXT, TXT_STA_TURNOUT4);
createObject(OBJ_BUTTON, BUT_STA_EDIT);
createObject(OBJ_SWITCH, SW_STA_OR1);
createObject(OBJ_SWITCH, SW_STA_OR2);
createObject(OBJ_SWITCH, SW_STA_OR3);
createObject(OBJ_SWITCH, SW_STA_OR4);
createObject(OBJ_SWITCH, SW_STA_INV1);
createObject(OBJ_SWITCH, SW_STA_INV2);
createObject(OBJ_SWITCH, SW_STA_INV3);
createObject(OBJ_SWITCH, SW_STA_INV4);
createObject(OBJ_BUTTON, BUT_STA_STAM);
createObject(OBJ_BUTTON, BUT_STA_STAP);
createObject(OBJ_BUTTON, BUT_STA_TURNM);
createObject(OBJ_BUTTON, BUT_STA_TURNP);
newEvent(OBJ_WIN, WIN_STA_EDIT, EVNT_DRAW);
break;
case WIN_STA_KEYB:
createObject(OBJ_WIN, WIN_STA_KEYB);
createObject(OBJ_KEYBOARD, KEYB_STA);
newEvent(OBJ_WIN, WIN_STA_KEYB, EVNT_DRAW);
break;
}
}
void alertWindow(byte err) {
errType = err;
createObject(OBJ_WIN, WIN_ALERT);
switch (err) {
case ERR_SERV:
createObject(OBJ_ICON, ICON_WARNING);
createObject(OBJ_ICON, ICON_WARNING_ON);
createObject(OBJ_LABEL, LBL_SERVICE);
break;
case ERR_CHG_WIFI:
createObject(OBJ_ICON, ICON_INFO);
createObject(OBJ_LABEL, LBL_CHG_WIFI);
break;
case ERR_FULL:
createObject(OBJ_ICON, ICON_WARNING);
createObject(OBJ_ICON, ICON_WARNING_ON);
createObject(OBJ_LABEL, LBL_STACK_FULL);
break;
case ERR_STOP:
createObject(OBJ_ICON, ICON_ESTOP);
createObject(OBJ_LABEL, LBL_ESTOP);
break;
case ERR_WAIT:
case ERR_CV:
barData[BAR_WAIT].value = 0;
setTimer(TMR_WAIT, 5, TMR_ONESHOT);
if (err == ERR_WAIT)
createObject(OBJ_ICON, ICON_WAIT);
else
createObject(OBJ_ICON, ICON_WAIT_CV);
createObject(OBJ_BAR, BAR_WAIT);
break;
case ERR_ASK_SURE:
createObject(OBJ_ICON, ICON_WARNING);
createObject(OBJ_ICON, ICON_WARNING_ON);
createObject(OBJ_LABEL, LBL_ASK_SURE);
createObject(OBJ_BUTTON, BUT_SURE_OK);
createObject(OBJ_BUTTON, BUT_SURE_CNCL);
break;
}
newEvent(OBJ_WIN, WIN_ALERT, EVNT_DRAW);
}

View File

@@ -0,0 +1,648 @@
/* 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.
*/
////////////////////////////////////////////////////////////
// ***** XPRESSNET LAN SOPORTE *****
////////////////////////////////////////////////////////////
void showErrorXnet() { // muestra pantalla de error
if (csStatus & csEmergencyOff) {
iconData[ICON_POWER].color = COLOR_RED;
setTimer (TMR_POWER, 5, TMR_PERIODIC); // Flash power icon
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
newEvent(OBJ_ICON, ICON_POWER, EVNT_DRAW);
if (isWindow(WIN_STA_PLAY)) {
fncData[FNC_STA_RAYO].state = true;
newEvent(OBJ_FNC, FNC_STA_RAYO, EVNT_DRAW);
}
}
if (csStatus & csServiceMode) {
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
alertWindow(ERR_SERV);
}
if (csStatus & csEmergencyStop) {
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
alertWindow(ERR_STOP);
}
}
void showNormalOpsXnet() {
stopTimer (TMR_POWER);
iconData[ICON_POWER].color = COLOR_GREEN;
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
newEvent(OBJ_ICON, ICON_POWER, EVNT_DRAW);
if (isWindow(WIN_STA_PLAY)) {
fncData[FNC_STA_RAYO].state = false;
newEvent(OBJ_FNC, FNC_STA_RAYO, EVNT_DRAW);
}
if (isWindow(WIN_ALERT)) {
switch (errType) {
case ERR_SERV:
case ERR_STOP:
case ERR_CV:
closeWindow(WIN_ALERT);
break;
}
}
}
uint16_t addrXnet(uint16_t adr) {
if (adr > 99) // Comprueba si es direccion larga
adr |= 0xC000;
return adr;
}
bool isRecentMM () { // Comprueba central Multimaus reciente
if ((xnetCS == 0x10) && (highVerMM > 0) && (lowVerMM > 0x02))
return true;
else
return false;
}
////////////////////////////////////////////////////////////
// ***** XPRESSNET LAN MESSAGES *****
////////////////////////////////////////////////////////////
void getStatusXnet () {
headerXN (0x21); // Command station status request (0x21,0x24,0x05)
dataXN (0x24);
sendXN();
}
void getVersionXnet () {
headerXN (0x21); // Command station software version (0x21,0x21,0x00)
dataXN (0x21);
sendXN();
}
void versionMultimaus() {
headerXN (0xF1); // Multimaus software version (0xF1,0x0A,XOR)
dataXN (0x0A);
sendXN();
}
void getResultsXnet() {
headerXN (0x21); // Request for Service Mode results (0x21,0x10,0x31)
dataXN (0x10);
sendXN();
//getResultsSM = false;
}
void resumeOperationsXnet () {
headerXN (0x21); // Resume operations request (0x21,0x81,0xA0)
dataXN (0x81);
sendXN();
}
void emergencyOffXnet() {
headerXN (0x21); // Stop operations request (emergency off)(0x21,0x80,0xA1)
dataXN (0x80);
sendXN();
}
void infoLocomotoraXnet (unsigned int loco) { // Locomotive information request (0xE3,0x00,ADRH,ADRL,XOR)
uint16_t adr;
adr = addrXnet(loco);
headerXN (0xE3);
dataXN (0x00);
dataXN (highByte(adr));
dataXN (lowByte (adr));
sendXN();
if ((xnetVersion > 0x35) || (xnetCS == 0x10)) {
headerXN (0xE3);
if (xnetCS == 0x10)
dataXN (0xF0); // Locomotive function F13..F20 info MM (0xE3,0xF0,ADRH,ADRL,XOR)
else
dataXN (0x09); // Locomotive function F13..F28 info v3.6 (0xE3,0x09,ADRH,ADRL,XOR)
dataXN (highByte(adr));
dataXN (lowByte (adr));
sendXN();
}
getInfoLoco = false;
}
void locoOperationSpeedXnet() { // Locomotive speed and direction operations (0xE4,ID,ADRH,ADRL,SPD,XOR)
uint16_t adr;
adr = addrXnet(locoData[myLocoData].myAddr.address);
headerXN (0xE4);
if (bitRead(locoData[myLocoData].mySteps, 2)) { // 128 steps
dataXN (0x13);
}
else {
if (bitRead(locoData[myLocoData].mySteps, 1)) { // 28 steps
dataXN (0x12);
}
else {
dataXN (0x10); // 14 steps
}
}
dataXN (highByte(adr));
dataXN (lowByte(adr));
dataXN (locoData[myLocoData].mySpeed | locoData[myLocoData].myDir);
sendXN();
bitClear(locoData[myLocoData].mySteps, 3); // currently operated by me
updateSpeedDir();
}
void funcOperationsXnet (byte fnc) { // Function operation instructions (0xE4,ID,ADRH,ADRL,GRP,XOR)
byte grp, grpID;
uint16_t adr;
adr = addrXnet(locoData[myLocoData].myAddr.address);
if (fnc > 20) {
grpID = 0x28; // F21..F28
grp = ((locoData[myLocoData].myFunc.xFunc[2] >> 5) & 0x07);
grp |= (locoData[myLocoData].myFunc.xFunc[3] << 3);
}
else {
if (fnc > 12) {
if (xnetCS == 0x10)
grpID = 0xF3; // F13..F20 MM (0xE4,0xF3,ADH,ADL,F13F20,XOR)
else
grpID = 0x23; // F13..F20
grp = ((locoData[myLocoData].myFunc.xFunc[1] >> 5) & 0x07);
grp |= (locoData[myLocoData].myFunc.xFunc[2] << 3);
}
else {
if (fnc > 8) {
grpID = 0x22; // F9..F12
grp = ((locoData[myLocoData].myFunc.xFunc[1] >> 1) & 0x0F);
}
else {
if (fnc > 4) {
grpID = 0x21; // F5..F8
grp = ((locoData[myLocoData].myFunc.xFunc[0] >> 5) & 0x07);
if (bitRead(locoData[myLocoData].myFunc.xFunc[1], 0))
grp |= 0x08;
}
else {
grpID = 0x20; // F0..F4
grp = ((locoData[myLocoData].myFunc.xFunc[0] >> 1) & 0x0F);
if (bitRead(locoData[myLocoData].myFunc.xFunc[0], 0))
grp |= 0x10;
}
}
}
}
headerXN (0xE4);
dataXN (grpID);
dataXN (highByte(adr));
dataXN (lowByte(adr));
dataXN (grp);
sendXN();
bitClear(locoData[myLocoData].mySteps, 3); // currently operated by me
}
byte getCurrentStepXnet() {
byte currStep;
if (bitRead(locoData[myLocoData].mySteps, 2)) { // 128 steps -> 0..126
if (locoData[myLocoData].mySpeed > 1)
return (locoData[myLocoData].mySpeed - 1);
}
else {
if (bitRead(locoData[myLocoData].mySteps, 1)) { // 28 steps -> 0..28 '---04321' -> '---43210'
currStep = (locoData[myLocoData].mySpeed << 1) & 0x1F;
bitWrite(currStep, 0, bitRead(locoData[myLocoData].mySpeed, 4));
if (currStep > 3)
return (currStep - 3);
}
else { // 14 steps -> 0..14
if (locoData[myLocoData].mySpeed > 1)
return (locoData[myLocoData].mySpeed - 1);
}
}
return (0);
}
void setAccessoryXnet (unsigned int direccion, bool activa, byte posicion) { // 1..1024
byte adr, dato;
direccion--; // 000000AAAAAAAABB
adr = (direccion >> 2) & 0x00FF; // AAAAAAAA
dato = ((direccion & 0x0003) << 1) | 0x80; // 1000xBBx
if (posicion > 0)
dato |= 0x01;
if (activa) { // 1000dBBD
dato |= 0x08;
}
headerXN (0x52); // Accessory Decoder operation request (0x52,AAAAAAAA,1000dBBD,XOR)
dataXN (adr);
dataXN (dato);
sendXN();
}
void setTimeXnet(byte hh, byte mm, byte rate) {
clockHour = hh;
clockMin = mm;
clockRate = rate;
if (rate > 0) {
headerXN (0x24); // set clock
dataXN (0x2B);
dataXN (hh);
dataXN (mm);
dataXN (rate);
sendXN ();
/*
headerXN (0x21); // start clock
dataXN (0x2C);
sendXN (0x07);
*/
}
else {
headerXN (0x21); // recommended for rate=0. stop clock
dataXN (0x2D);
sendXN ();
}
}
void readCVXnet (unsigned int adr, byte stepPrg) {
if (!modeProg) { // Read only in Direct mode
if (isRecentMM()) {
headerXN (0x23); // Multimaus v1.03
dataXN (0x15);
adr--;
dataXN (highByte(adr) & 0x03);
dataXN (lowByte(adr));
sendXN();
lastCV = lowByte(adr) + 1;
}
else {
headerXN (0x22);
if (xnetVersion > 0x35)
dataXN (0x18 | (highByte(adr) & 0x03)); // v3.6 & up CV1..CV1024
else
dataXN (0x15); // v3.0 CV1..CV256
dataXN (lowByte(adr));
sendXN();
lastCV = lowByte(adr);
}
getResultsSM = true;
infoTimer = millis();
progStepCV = stepPrg;
//DEBUG_MSG("Read CV %d", adr);
}
}
void writeCVXnet (unsigned int adr, unsigned int data, byte stepPrg) {
uint16_t adrLoco;
if (modeProg) {
headerXN (0xE6); // Operations Mode Programming byte mode write request (0xE6,0x30,ADRH,ADRL,0xEC+C,CV,DATA,XOR)
dataXN (0x30);
adrLoco = addrXnet(locoData[myLocoData].myAddr.address);
dataXN (highByte(adrLoco));
dataXN (lowByte(adrLoco));
adr--;
dataXN (0xEC | (highByte(adr) & 0x03));
dataXN (lowByte(adr));
dataXN(data);
sendXN();
}
else {
if (isRecentMM()) {
headerXN (0x24); // Multimaus v1.03
dataXN (0x16);
adr--;
dataXN (highByte(adr) & 0x03);
dataXN (lowByte(adr));
dataXN(data);
sendXN();
lastCV = lowByte(adr) + 1;
}
else {
headerXN (0x23);
if (xnetVersion > 0x35)
dataXN (0x1C | (highByte(adr) & 0x03)); // v3.6 & up CV1..CV1024
else
dataXN (0x16); // v3.0 CV1..CV256
dataXN (lowByte(adr));
dataXN(data);
sendXN();
lastCV = lowByte(adr);
}
getResultsSM = true;
infoTimer = millis();
}
progStepCV = stepPrg;
//DEBUG_MSG("Write CV%d = %d", adr, data);
}
////////////////////////////////////////////////////////////
// ***** XPRESSNET LAN DECODE *****
////////////////////////////////////////////////////////////
void headerXN (byte header) {
txBytes = HEADER; // coloca header en el buffer
txXOR = header;
txBuffer[txBytes++] = header;
txBuffer[FRAME1] = 0xFF;
txBuffer[FRAME2] = 0xFE;
}
void dataXN (byte dato) {
txBuffer[txBytes++] = dato; // coloca dato en el buffer
txXOR ^= dato;
}
void sendXN () {
bool recvAnswer;
txBuffer[txBytes++] = txXOR; // coloca XOR byte en el buffer
#ifdef DEBUG
Serial.print(F("TX: "));
for (uint8_t x = 0; x < txBytes; x++) {
uint8_t val = txBuffer[x];
if (val < 16)
Serial.print('0');
Serial.print(val, HEX);
Serial.print(' ');
}
Serial.println();
#endif
Client.write((byte *)&txBuffer[FRAME1], txBytes); // envia paquete xpressnet
timeoutXnet = millis();
recvAnswer = false;
while ((millis() - timeoutXnet < 500) && (!recvAnswer)) // wait answer for 500ms
recvAnswer = xnetReceive();
}
bool xnetReceive() {
bool getAnswer;
getAnswer = false;
while (Client.available()) {
rxData = Client.read();
//DEBUG_MSG("%d-%02X", rxIndice, rxData);
switch (rxIndice) {
case FRAME1:
rxBufferXN[FRAME1] = rxData;
if (rxData == 0xFF) // 0xFF... Posible inicio de paquete
rxIndice = FRAME2;
break;
case FRAME2:
rxBufferXN[FRAME2] = rxData;
switch (rxData) {
case 0xFF: // 0xFF 0xFF... FRAME2 puede ser FRAME1 (inicio de otro paquete)
break;
case 0xFE: // 0xFF 0xFE... Inicio paquete correcto
case 0xFD: // 0xFF 0xFD... Inicio paquete de broadcast correcto
rxIndice = HEADER;
rxXOR = 0;
break;
default: // 0xFF 0xXX... No es inicio de paquete
rxIndice = FRAME1;
break;
}
break;
default:
rxBufferXN[rxIndice++] = rxData;
rxXOR ^= rxData;
if (((rxBufferXN[HEADER] & 0x0F) + 4) == rxIndice) { // si se han recibido todos los datos indicados en el paquete
if (rxXOR == 0) { // si el paquete es correcto
rxBytes = rxIndice;
#ifdef DEBUG
Serial.print(F("RX: "));
for (uint8_t x = 0; x < rxBytes; x++) {
uint8_t val = rxBufferXN[x];
if (val < 16)
Serial.print('0');
Serial.print(val, HEX);
Serial.print(' ');
}
Serial.println();
#endif
procesaXN(); // nuevo paquete recibido, procesarlo
getAnswer = true;
}
rxIndice = FRAME1; // proximo paquete
}
break;
}
}
return getAnswer;
}
void processXnet () { // procesa Xpressnet
xnetReceive();
if (getInfoLoco && (csStatus == csNormalOps))
infoLocomotoraXnet(addrXnet(locoData[myLocoData].myAddr.address));
if (millis() - infoTimer > 1000UL) { // Cada segundo
infoTimer = millis();
if (getResultsSM) // Resultados de CV pendientes
getResultsXnet(); // pide resultados
else {
if (bitRead(locoData[myLocoData].mySteps, 3)) // Loco controlada por otro mando
getInfoLoco = true; // pide info locomotora
if (askMultimaus) { // pide info Multimaus
askMultimaus = false;
versionMultimaus();
}
}
}
if (progFinished) { // fin de lectura/programacion CV
progFinished = false;
endProg();
}
if (millis() - pingTimer > XNET_PING_INTERVAL) { // Refresca para mantener la conexion
pingTimer = millis();
getStatusXnet(); // pide estado de la central
}
}
void procesaXN () {
byte n, longitud, modulo, dato;
uint16_t adr;
switch (rxBufferXN[HEADER]) { // segun el header byte
case 0x61:
switch (rxBufferXN[DATA1]) {
case 0x01: // Normal operation resumed (0x61,0x01,0x60)
csStatus = csNormalOps;
showNormalOpsXnet();
break;
case 0x08: // Z21 LAN_X_BC_TRACK_SHORT_CIRCUIT (0x61,0x08,XOR)
case 0x00: // Track power off (0x61,0x00,0x61)
csStatus |= csEmergencyOff;
showErrorXnet();
break;
case 0x02: // Service mode entry (0x61,0x02,0x63)
csStatus |= csServiceMode;
if (!getResultsSM) // show 'Service Mode' if we aren't programming CV
showErrorXnet();
break;
case 0x12: // Programming info. "shortcircuit" (0x61,0x12,XOR)
case 0x13: // Programming info. "Data byte not found" (0x61,0x13,XOR)
CVdata = 0x0600;
getResultsSM = false;
progFinished = true;
break;
case 0x81: // Command station busy response (0x61,0x81,XOR)
break;
case 0x1F: // Programming info. "Command station busy" (0x61,0x1F,XOR)
getResultsSM = true;
infoTimer = millis();
break;
case 0x82: // Instruction not supported by command station (0x61,0x82,XOR)
getResultsSM = false;
if (csStatus & csServiceMode) {
CVdata = 0x0600;
progFinished = true;
}
break;
}
break;
case 0x81:
if (rxBufferXN[DATA1] == 0) { // Emergency Stop (0x81,0x00,0x81)
csStatus |= csEmergencyStop;
showErrorXnet();
}
break;
case 0x62:
if (rxBufferXN[DATA1] == 0x22) { // Command station status indication response (0x62,0x22,DATA,XOR)
csStatus = rxBufferXN[DATA2] & (csEmergencyStop | csEmergencyOff | csServiceMode) ;
if ((xnetCS >= 0x10) && (rxBufferXN[DATA2] & csProgrammingModeActive)) // Multimaus/Z21 Service Mode
csStatus |= csServiceMode;
if (csStatus == csNormalOps)
showNormalOpsXnet();
else
showErrorXnet();
}
break;
case 0x63:
switch (rxBufferXN[DATA1]) {
case 0x03: // Broadcast "Modellzeit" (0x63,0x03,dddhhhhh,s0mmmmmm,XOR) (v4.0)
clockHour = rxBufferXN[DATA2] & 0x1F;
clockMin = rxBufferXN[DATA3] & 0x3F;
clockRate = !bitRead(rxBufferXN[DATA3], 7);
updateFastClock();
break;
case 0x14: // Service Mode response for Direct CV mode (0x63,0x1x,CV,DATA,XOR)
case 0x15:
case 0x16:
case 0x17:
if (rxBufferXN[DATA2] == lastCV) { // comprobar CV (DR5000)
lastCV ^= 0x55;
getResultsSM = false;
CVdata = rxBufferXN[DATA3];
progFinished = true;
}
break;
case 0x21: // Command station software version (0x63,0x21,VER,ID,XOR)
xnetVersion = rxBufferXN[DATA2];
xnetCS = rxBufferXN[DATA3];
if (xnetCS == 0x10)
askMultimaus = true;
break;
}
break;
case 0xE3:
if (rxBufferXN[DATA1] == 0x40) { // Locomotive is being operated by another device response (0xE3,0x40,ADRH,ADRL,XOR)
adr = addrXnet(locoData[myLocoData].myAddr.address);
if ((rxBufferXN[DATA3] == lowByte(adr)) && (rxBufferXN[DATA2] == highByte(adr))) { // DR5000 workaround
bitSet(locoData[myLocoData].mySteps, 3);
}
}
if (rxBufferXN[DATA1] == 0x52) { // Locomotive function info F13..F28 (0xE3,0x52,FNC,FNC,XOR)
locoData[myLocoData].myFunc.Bits &= 0xE0001FFF;
locoData[myLocoData].myFunc.Bits |= ((unsigned long)rxBufferXN[DATA2] << 13);
locoData[myLocoData].myFunc.Bits |= ((unsigned long)rxBufferXN[DATA3] << 21);
updateFuncState(isWindow(WIN_THROTTLE));
}
break;
case 0xE4:
if ((rxBufferXN[DATA1] & 0xF0) == 0x00) { // Locomotive information normal locomotive (0xE4,ID,SPD,FKTA,FKTB,XOR)
locoData[myLocoData].mySteps = rxBufferXN[DATA1]; // '0000BFFF'
locoData[myLocoData].myDir = rxBufferXN[DATA2] & 0x80; // 'RVVVVVVV'
locoData[myLocoData].mySpeed = rxBufferXN[DATA2] & 0x7F;
locoData[myLocoData].myFunc.Bits &= 0xFFFFE000; // '000FFFFF','FFFFFFFF'
locoData[myLocoData].myFunc.Bits |= ((unsigned long)rxBufferXN[DATA4] << 5);
locoData[myLocoData].myFunc.xFunc[0] |= ((rxBufferXN[DATA3] & 0x0F) << 1);
bitWrite(locoData[myLocoData].myFunc.xFunc[0], 0, bitRead(rxBufferXN[DATA3], 4));
updateFuncState(isWindow(WIN_THROTTLE));
if (isWindow(WIN_THROTTLE) || isWindow(WIN_SPEEDO))
updateSpeedHID();
}
break;
case 0xE7:
if ((rxBufferXN[DATA1] & 0xF0) == 0x00) { // Locomotive function info F13..F20 MM (0xE7,STP,SPD,FNC,FNC,FNC,0x00,0x00,XOR)
locoData[myLocoData].mySteps = rxBufferXN[DATA1]; // '0000BFFF'
locoData[myLocoData].myDir = rxBufferXN[DATA2] & 0x80; // 'RVVVVVVV'
locoData[myLocoData].mySpeed = rxBufferXN[DATA2] & 0x7F;
locoData[myLocoData].myFunc.Bits &= 0xFE00000;
locoData[myLocoData].myFunc.Bits |= ((unsigned long)rxBufferXN[DATA5] << 13);
locoData[myLocoData].myFunc.Bits |= ((unsigned long)rxBufferXN[DATA4] << 5);
locoData[myLocoData].myFunc.xFunc[0] |= ((rxBufferXN[DATA3] & 0x0F) << 1);
bitWrite(locoData[myLocoData].myFunc.xFunc[0], 0, bitRead(rxBufferXN[DATA3], 4));
updateFuncState(isWindow(WIN_THROTTLE));
if (isWindow(WIN_THROTTLE) || isWindow(WIN_SPEEDO))
updateSpeedHID();
}
break;
case 0xF3:
if (rxBufferXN[DATA1] == 0x0A) { // Multimaus firmware version (0xF3,0x0A,VERH,VERL,XOR)
highVerMM = rxBufferXN[DATA2];
lowVerMM = rxBufferXN[DATA3];
}
break;
default:
if ((rxBufferXN[HEADER] & 0xF0) == 0x40) { // Feedback broadcast / Accessory decoder information response (0x4X,MOD,DATA,...,XOR)
/*
for (n = HEADER; n < (rxBytes - 2); n += 2) {
modulo = rxBufferXN[n + 1];
dato = rxBufferXN[n + 2];
if (modulo == miModulo) { // Si es mi desvio guarda su posicion
if (bitRead(dato, 4) == bitRead(miAccPos, 1)) {
if (bitRead(miAccPos, 0))
myPosTurnout = (dato >> 2) & 0x03;
else
myPosTurnout = dato & 0x03;
if (scrOLED == SCR_TURNOUT)
updateOLED = true;
}
}
#ifdef USE_AUTOMATION
for (byte n = 0; n < MAX_AUTO_SEQ; n++) {
if ((automation[n].opcode & OPC_AUTO_MASK) == OPC_AUTO_FBK) {
if (modulo == automation[n].param) {
unsigned int nibble = (dato & 0x10) ? 0x0F : 0xF0;
automation[n].value &= nibble;
nibble = (dato & 0x10) ? (dato << 4) : (dato & 0x0F);
automation[n].value |= nibble;
}
}
}
#endif
modulo++;
if (modulo == Shuttle.moduleA) // shuttle contacts
updateShuttleStatus(&Shuttle.statusA, dato);
if (modulo == Shuttle.moduleB)
updateShuttleStatus(&Shuttle.statusB, dato);
}
*/
}
break;
}
}

View File

@@ -0,0 +1,581 @@
/* 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.
*/
////////////////////////////////////////////////////////////
// ***** Z21 SOPORTE *****
////////////////////////////////////////////////////////////
void readCVZ21 (unsigned int adr, byte stepPrg) {
if (!modeProg) {
askZ21begin (LAN_X_Header);
askZ21data (0x23);
askZ21data (0x11);
adr--;
askZ21data ((adr >> 8) & 0xFF);
askZ21data (adr & 0xFF);
askZ21xor ();
sendUDP (0x09);
waitResultCV = true;
lastCV = lowByte(adr);
progStepCV = stepPrg;
DEBUG_MSG("Read CV %d", adr + 1);
}
}
void writeCVZ21 (unsigned int adr, unsigned int data, byte stepPrg) {
byte Adr_MSB;
if (modeProg) {
askZ21begin (LAN_X_Header);
askZ21data (0xE6);
askZ21data (0x30);
Adr_MSB = locoData[myLocoData].myAddr.adr[1] & 0x3F;
if (locoData[myLocoData].myAddr.address & 0x3F80)
Adr_MSB |= 0xC0;
askZ21data (Adr_MSB);
askZ21data (locoData[myLocoData].myAddr.adr[0]);
adr--;
askZ21data (0xEC | ((adr >> 8) & 0x03));
askZ21data (adr & 0xFF);
askZ21data (data);
askZ21xor ();
sendUDP (0x0C);
}
else {
askZ21begin (LAN_X_Header);
askZ21data (0x24);
askZ21data (0x12);
adr--;
askZ21data ((adr >> 8) & 0xFF);
askZ21data (adr & 0xFF);
askZ21data (data);
askZ21xor ();
sendUDP (0x0A);
waitResultCV = true;
lastCV = lowByte(adr);
}
progStepCV = stepPrg;
DEBUG_MSG("Write CV%d = %d", adr + 1, data);
}
void showErrorZ21() { // muestra pantalla de error
if (csStatus & csTrackVoltageOff) {
iconData[ICON_POWER].color = COLOR_RED;
setTimer (TMR_POWER, 5, TMR_PERIODIC); // Flash power icon
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
newEvent(OBJ_ICON, ICON_POWER, EVNT_DRAW);
if (isWindow(WIN_STA_PLAY)) {
fncData[FNC_STA_RAYO].state = true;
newEvent(OBJ_FNC, FNC_STA_RAYO, EVNT_DRAW);
}
}
if (csStatus & csProgrammingModeActive) {
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
alertWindow(ERR_SERV);
}
if (csStatus & csEmergencyStop) {
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
alertWindow(ERR_STOP);
}
}
void showNormalOpsZ21() {
stopTimer (TMR_POWER);
iconData[ICON_POWER].color = COLOR_GREEN;
if ((isWindow(WIN_THROTTLE)) || (isWindow(WIN_STEAM)))
newEvent(OBJ_ICON, ICON_POWER, EVNT_DRAW);
if (isWindow(WIN_STA_PLAY)) {
fncData[FNC_STA_RAYO].state = false;
newEvent(OBJ_FNC, FNC_STA_RAYO, EVNT_DRAW);
}
if (isWindow(WIN_ALERT)) {
switch (errType) {
case ERR_SERV:
case ERR_STOP:
case ERR_CV:
closeWindow(WIN_ALERT);
break;
}
}
}
void setTimeZ21(byte hh, byte mm, byte rate) {
clockHour = hh;
clockMin = mm;
clockRate = rate;
askZ21begin (LAN_FAST_CLOCK_CONTROL); // set clock
if (rate > 0) {
askZ21data (0x24);
askZ21data (0x2B);
askZ21data (hh);
askZ21data (mm);
askZ21data (rate);
askZ21xor ();
sendUDP (0x0A);
askZ21begin (LAN_FAST_CLOCK_CONTROL);
askZ21data (0x21); // start clock
askZ21data (0x2C);
askZ21xor ();
sendUDP (0x07);
}
else {
askZ21data (0x21); // recommended for rate=0. stop clock
askZ21data (0x2D);
askZ21xor ();
sendUDP (0x07);
}
}
////////////////////////////////////////////////////////////
// ***** Z21 DECODE *****
////////////////////////////////////////////////////////////
void processZ21() {
int len, packetSize;
packetSize = Udp.parsePacket(); // z21 UDP packet
if (packetSize) {
len = Udp.read(packetBuffer, packetSize); // read the packet into packetBufffer
ReceiveZ21 (len, packetBuffer); // decode received packet
}
delay(0);
if (millis() - infoTimer > 1000UL) { // Cada segundo
infoTimer = millis();
pingTimer++;
if (pingTimer >= Z21_PING_INTERVAL) {
pingTimer = 0;
if (!(csStatus & csProgrammingModeActive))
getStatusZ21();
}
/*
battery = ESP.getVcc (); // Read VCC voltage
if (battery < LowBattADC)
lowBATT = true;
*/
}
if (progFinished) { // fin de lectura/programacion CV
progFinished = false;
endProg();
}
delay(0);
}
// -------------------------------------------------------------------------------------
void setBroadcastFlags (unsigned long bFlags) {
askZ21begin (LAN_SET_BROADCASTFLAGS);
askZ21data (bFlags & 0xFF);
askZ21data ((bFlags >> 8) & 0xFF);
askZ21data ((bFlags >> 16) & 0xFF);
askZ21data ((bFlags >> 24) & 0xFF);
sendUDP (0x08);
}
void resumeOperationsZ21 () { // LAN_X_SET_TRACK_POWER_ON
askZ21begin (LAN_X_Header);
askZ21data (0x21);
askZ21data (0x81);
askZ21xor ();
sendUDP (0x07);
}
void emergencyOffZ21() { // LAN_X_SET_TRACK_POWER_OFF
askZ21begin (LAN_X_Header);
askZ21data (0x21);
askZ21data (0x80);
askZ21xor ();
sendUDP (0x07);
}
void getStatusZ21 () {
askZ21begin (LAN_X_Header);
askZ21data (0x21);
askZ21data (0x24);
askZ21xor ();
sendUDP (0x07);
}
void getSerialNumber () {
askZ21begin (LAN_GET_SERIAL_NUMBER);
sendUDP (0x04);
}
void infoLocomotoraZ21 (unsigned int Adr) {
byte Adr_MSB;
askZ21begin (LAN_X_Header);
askZ21data (0xE3);
askZ21data (0xF0);
Adr_MSB = (Adr >> 8) & 0x3F;
if (Adr & 0x3F80)
Adr_MSB |= 0xC0;
askZ21data (Adr_MSB);
askZ21data (Adr & 0xFF);
askZ21xor ();
sendUDP (0x09);
}
void locoOperationSpeedZ21() {
byte Adr_MSB;
DEBUG_MSG("Loco Operations")
askZ21begin (LAN_X_Header);
askZ21data (0xE4);
if (bitRead(locoData[myLocoData].mySteps, 2)) { // 128 steps
askZ21data (0x13);
}
else {
if (bitRead(locoData[myLocoData].mySteps, 1)) { // 28 steps
askZ21data (0x12);
}
else {
askZ21data (0x10); // 14 steps
}
}
Adr_MSB = locoData[myLocoData].myAddr.adr[1] & 0x3F;
if (locoData[myLocoData].myAddr.address & 0x3F80)
Adr_MSB |= 0xC0;
askZ21data (Adr_MSB);
askZ21data (locoData[myLocoData].myAddr.adr[0]);
askZ21data (locoData[myLocoData].mySpeed | locoData[myLocoData].myDir);
askZ21xor ();
sendUDP (0x0A);
bitClear(locoData[myLocoData].mySteps, 3); // currently operated by me
updateSpeedDir();
}
byte getCurrentStepZ21() {
byte currStep;
DEBUG_MSG("Get Steps: %02X - Speed: %02X", locoData[myLocoData].mySteps, locoData[myLocoData].mySpeed);
if (bitRead(locoData[myLocoData].mySteps, 2)) { // 128 steps -> 0..126
if (locoData[myLocoData].mySpeed > 1)
return (locoData[myLocoData].mySpeed - 1);
}
else {
if (bitRead(locoData[myLocoData].mySteps, 1)) { // 28 steps -> 0..28 '---04321' -> '---43210'
currStep = (locoData[myLocoData].mySpeed << 1) & 0x1F;
bitWrite(currStep, 0, bitRead(locoData[myLocoData].mySpeed, 4));
if (currStep > 3)
return (currStep - 3);
}
else { // 14 steps -> 0..14
if (locoData[myLocoData].mySpeed > 1)
return (locoData[myLocoData].mySpeed - 1);
}
}
return (0);
}
void funcOperationsZ21 (byte fnc) {
byte Adr_MSB;
askZ21begin (LAN_X_Header);
askZ21data (0xE4);
askZ21data (0xF8);
Adr_MSB = locoData[myLocoData].myAddr.adr[1] & 0x3F;
if (locoData[myLocoData].myAddr.address & 0x3F80)
Adr_MSB |= 0xC0;
askZ21data (Adr_MSB);
askZ21data (locoData[myLocoData].myAddr.adr[0]);
if (bitRead(locoData[myLocoData].myFunc.Bits, fnc))
askZ21data (fnc | 0x40);
else
askZ21data (fnc);
askZ21xor ();
sendUDP (0x0A);
bitClear(locoData[myLocoData].mySteps, 3); // currently operated by me
}
void infoDesvio (unsigned int FAdr) {
FAdr--;
askZ21begin (LAN_X_Header);
askZ21data (0x43);
askZ21data ((FAdr >> 8) & 0xFF);
askZ21data (FAdr & 0xFF);
askZ21xor ();
sendUDP (0x08);
}
void setAccessoryZ21 (unsigned int FAdr, int pair, bool active) {
byte db2;
FAdr--;
askZ21begin (LAN_X_Header);
askZ21data (0x53);
askZ21data ((FAdr >> 8) & 0xFF);
askZ21data (FAdr & 0xFF);
db2 = active ? 0x88 : 0x80;
if (pair > 0)
db2 |= 0x01;
askZ21data (db2); // '10Q0A00P'
askZ21xor ();
sendUDP (0x09);
}
void getFeedbackInfo (byte group) {
askZ21begin (LAN_RMBUS_GETDATA);
askZ21data (group);
sendUDP (0x05);
}
void ReceiveZ21 (int len, byte * packet) { // get UDP packet, maybe more than one!!
int DataLen, isPacket;
#ifdef DEBUG____X
Serial.print("\nRX Length: ");
Serial.println (len);
for (int i = 0; i < len; i++) {
Serial.print(packet[i], HEX);
Serial.print(" ");
}
Serial.println();
#endif
isPacket = 1;
while (isPacket) {
DataLen = (packet[DATA_LENH] << 8) + packet[DATA_LENL];
DecodeZ21 (DataLen, packet);
if (DataLen >= len) {
isPacket = 0;
}
else {
packet = packet + DataLen;
len = len - DataLen;
}
delay(0);
}
}
void DecodeZ21 (int len, byte * packet) { // decode z21 UDP packets
int Header, DataLen;
unsigned int FAdr;
byte group;
Header = (packet[DATA_HEADERH] << 8) + packet[DATA_HEADERL];
switch (Header) {
case LAN_GET_SERIAL_NUMBER:
break;
case LAN_GET_CODE: // FW 1.28
break;
case LAN_GET_HWINFO:
break;
case LAN_GET_BROADCASTFLAGS:
break;
case LAN_GET_LOCOMODE:
break;
case LAN_GET_TURNOUTMODE:
break;
case LAN_RMBUS_DATACHANGED:
/*
if (Shuttle.moduleA > 0) { // only check shuttle contacts
if ((packet[4] == 0x01) && (Shuttle.moduleA > 10))
Shuttle.statusA = packet[Shuttle.moduleA - 6];
if ((packet[4] == 0x00) && (Shuttle.moduleA < 11))
Shuttle.statusA = packet[Shuttle.moduleA + 4];
}
if (Shuttle.moduleB > 0) {
if ((packet[4] == 0x01) && (Shuttle.moduleB > 10))
Shuttle.statusB = packet[Shuttle.moduleB - 6];
if ((packet[4] == 0x00) && (Shuttle.moduleB < 11))
Shuttle.statusB = packet[Shuttle.moduleB + 4];
}
#ifdef USE_AUTOMATION
for (byte n = 0; n < MAX_AUTO_SEQ; n++) {
if ((automation[n].opcode & OPC_AUTO_MASK) == OPC_AUTO_FBK) {
if ((packet[4] == 0x01) && (automation[n].param > 9))
automation[n].value = packet[automation[n].param - 5];
if ((packet[4] == 0x00) && (automation[n].param < 10))
automation[n].value = packet[automation[n].param + 5];
DEBUG_MSG("RBUS %d", automation[n].value)
}
}
#endif
*/
break;
case LAN_SYSTEMSTATE_DATACHANGED:
csStatus = packet[16] & (csEmergencyStop | csTrackVoltageOff | csProgrammingModeActive); // CentralState
if (packet[16] & csShortCircuit)
csStatus |= csTrackVoltageOff;
break;
case LAN_RAILCOM_DATACHANGED:
break;
case LAN_LOCONET_Z21_TX: // a message has been written to the LocoNet bus by the Z21.
case LAN_LOCONET_Z21_RX: // a message has been received by the Z21 from the LocoNet bus.
case LAN_LOCONET_FROM_LAN: // another LAN client has written a message to the LocoNet bus via the Z21.
switch (packet[4]) {
case 0x83:
csStatus = csNormalOps; // OPC_GPON
showNormalOpsZ21();
break;
case 0x82:
csStatus |= csTrackVoltageOff; // OPC_GPOFF
showErrorZ21();
break;
}
break;
case LAN_LOCONET_DETECTOR:
break;
case LAN_FAST_CLOCK_DATA: // fast clock data FW 1.43
if (packet[8] & 0x80) { // Stop flag
clockRate = 0;
}
else {
clockHour = packet[6] & 0x1F;
clockMin = packet[7] & 0x3F;
clockRate = packet[9] & 0x3F;
updateFastClock();
}
DEBUG_MSG("Clock: %d:%d %d", clockHour, clockMin, clockRate);
break;
case LAN_X_Header:
switch (packet[XHEADER]) {
case 0x43: // LAN_X_TURNOUT_INFO
FAdr = (packet[DB0] << 8) + packet[DB1] + 1;
/*
if (FAdr == myTurnout) {
myPosTurnout = packet[DB2] & 0x03;
if (scrOLED == SCR_TURNOUT)
updateOLED = true;
}
*/
break;
case 0x61:
switch (packet[DB0]) {
case 0x01: // LAN_X_BC_TRACK_POWER_ON
csStatus = csNormalOps;
showNormalOpsZ21();
break;
case 0x08: // LAN_X_BC_TRACK_SHORT_CIRCUIT
csStatus |= csShortCircuit;
case 0x00: // LAN_X_BC_TRACK_POWER_OFF
csStatus |= csTrackVoltageOff;
showErrorZ21();
break;
case 0x02: // LAN_X_BC_PROGRAMMING_MODE
csStatus |= csProgrammingModeActive;
if (!waitResultCV)
showErrorZ21();
break;
case 0x12: // LAN_X_CV_NACK_SC
case 0x13: // LAN_X_CV_NACK
CVdata = 0x0600;
waitResultCV = false;
progFinished = true;
break;
case 0x82: // LAN_X_UNKNOWN_COMMAND
break;
}
break;
case 0x62:
switch (packet[DB0]) {
case 0x22: // LAN_X_STATUS_CHANGED
csStatus = packet[DB1] & (csEmergencyStop | csTrackVoltageOff | csProgrammingModeActive);
if (packet[DB1] & csShortCircuit)
csStatus |= csTrackVoltageOff;
if (csStatus == csNormalOps)
showNormalOpsZ21();
else
showErrorZ21();
break;
}
break;
case 0x64:
if (packet[DB0] == 0x14) { // LAN_X_CV_RESULT
if (packet[DB2] == lastCV) {
lastCV ^= 0x55;
CVdata = packet[DB3];
waitResultCV = false;
progFinished = true;
}
}
break;
case 0x81:
if (packet[DB0] == 0) { // LAN_X_BC_STOPPED
csStatus |= csEmergencyStop;
showErrorZ21();
}
break;
case 0xEF: // LAN_X_LOCO_INFO
DEBUG_MSG("RX: Loco data")
FAdr = ((packet[DB0] << 8) + packet[DB1]) & 0x3FFF;
if (FAdr == locoData[myLocoData].myAddr.address) {
locoData[myLocoData].mySteps = packet[DB2]; // '0000BFFF'
locoData[myLocoData].myDir = packet[DB3] & 0x80; // 'RVVVVVVV'
locoData[myLocoData].mySpeed = packet[DB3] & 0x7F;
locoData[myLocoData].myFunc.Bits &= 0xE0000000; // '000FFFFF','FFFFFFFF'
locoData[myLocoData].myFunc.xFunc[0] |= ((packet[DB4] & 0x0F) << 1);
bitWrite(locoData[myLocoData].myFunc.xFunc[0], 0, bitRead(packet[DB4], 4));
locoData[myLocoData].myFunc.Bits |= (unsigned long)(packet[DB5] << 5);
locoData[myLocoData].myFunc.Bits |= (unsigned long)(packet[DB6] << 13);
locoData[myLocoData].myFunc.Bits |= (unsigned long)(packet[DB7] << 21);
updateFuncState(isWindow(WIN_THROTTLE));
if (isWindow(WIN_THROTTLE) || isWindow(WIN_SPEEDO))
updateSpeedHID(); // set encoder
}
break;
}
break;
default: // Header other
break;
}
}
void askZ21begin (unsigned int header) {
OutData[DATA_HEADERL] = header & 0xFF;
OutData[DATA_HEADERH] = header >> 8;
OutPos = XHEADER;
OutXOR = 0;
}
void askZ21data (byte data) {
OutData[OutPos++] = data;
OutXOR ^= data;
}
void askZ21xor () {
OutData[OutPos] = OutXOR;
}
void sendUDP (int len) {
OutData[DATA_LENL] = len & 0xFF;
OutData[DATA_LENH] = len >> 8;
Udp.beginPacket(wifiSetting.CS_IP, z21Port);
Udp.write(OutData, len);
Udp.endPacket();
delay(0);
#ifdef DEBUG___X
Serial.print("TX: ");
for (int i = 0; i < len; i++) {
Serial.print(OutData[i], HEX);
Serial.print(" ");
}
Serial.println();
#endif
}