Initialisation depot
This commit is contained in:
87
README.md
Normal file
87
README.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# HW 364A ESP8266 DCC Command Station with XpressNet
|
||||||
|
|
||||||
|
This is a minimal HW 364A ESP8266 project for a DCC Command Station using the LM18200 motor driver, now with XpressNet support for communication with throttles and other XpressNet devices.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
- `src/main.cpp`: Main application entry point
|
||||||
|
- `src/DCC.cpp` / `src/DCC.h`: DCC signal generation and handling
|
||||||
|
- `src/XpressNet.cpp` / `src/XpressNet.h`: XpressNet protocol interface (stubs)
|
||||||
|
- `src/WiFiManagerHelper.cpp` / `src/WiFiManagerHelper.h`: WiFiManager-based configuration
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
1. Open the project in PlatformIO or Arduino IDE.
|
||||||
|
2. Select the HW 364A ESP8266 as your target board.
|
||||||
|
3. Connect the LM18200 and XpressNet bus to the HW 364A ESP8266 as per your hardware setup.
|
||||||
|
4. Build and upload to your HW 364A ESP8266 board.
|
||||||
|
5. On first boot, connect to the WiFi AP named `DCC-CommandStation` to configure your WiFi credentials using the captive portal.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Wire the LM18200 motor driver to the ESP32 according to your motor control and DCC output requirements. Refer to the LM18200 datasheet for correct pinout and power handling.
|
||||||
|
- Use of included display
|
||||||
|
- This is a minimal template. You must implement the DCC signal generation logic for your hardware.
|
||||||
|
- XpressNet protocol functions are provided as stubs for further development.
|
||||||
|
- WiFiManager is used for easy WiFi configuration by the user.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Hardware Compatibility: HW 364A ESP8266
|
||||||
|
|
||||||
|
This project is designed for the HW 364A ESP8266 board. Ensure you select this board in PlatformIO or Arduino IDE for correct pin mapping and features.
|
||||||
|
|
||||||
|
### Connecting the LM18200 Motor Driver
|
||||||
|
|
||||||
|
The LM18200 is used to drive the DCC signal to the track. For RailCom cutout, you must implement a hardware or software cut-off during the RailCom window.
|
||||||
|
|
||||||
|
**Basic Wiring:**
|
||||||
|
- **IN1/IN2:** Connect to ESP8266 GPIOs for DCC signal generation (e.g., GPIO 5 and GPIO 4).
|
||||||
|
- **PWM:** Connect to a PWM-capable GPIO if needed for DCC modulation.
|
||||||
|
- **VCC/GND:** Connect to your power supply and ground.
|
||||||
|
- **OUT1/OUT2:** Connect to the track.
|
||||||
|
- **RailCom Cutout:**
|
||||||
|
- To implement RailCom cutout, use a MOSFET or relay to disconnect the track output during the RailCom window. Control the MOSFET/relay from an ESP8266 GPIO.
|
||||||
|
- Alternatively, use a dedicated circuit to blank the DCC signal during the cutout period.
|
||||||
|
|
||||||
|
**Example RailCom Cutout Circuit:**
|
||||||
|
```
|
||||||
|
ESP8266 GPIO ----> MOSFET Gate
|
||||||
|
MOSFET Drain ----> Track Output
|
||||||
|
MOSFET Source ---> GND
|
||||||
|
```
|
||||||
|
- Set the GPIO HIGH to enable track power, LOW to cut off for RailCom.
|
||||||
|
|
||||||
|
### XpressNet Bus
|
||||||
|
- Connect the XpressNet bus (RS485 or TTL UART) to the ESP8266 UART pins.
|
||||||
|
- Use a level shifter if required for voltage compatibility.
|
||||||
|
|
||||||
|
### Display
|
||||||
|
- Use an I2C OLED display (e.g., SSD1306).
|
||||||
|
- Connect SDA to GPIO 14 (D6), SCL to GPIO 12 (D5) as per sample code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Software Features
|
||||||
|
- DCC signal generation (implement in DCC.cpp)
|
||||||
|
- RailCom cutout support (add cutout logic in DCC routines)
|
||||||
|
- XpressNet protocol (see xpressnet.md for commands and custom extensions)
|
||||||
|
- WiFi configuration via captive portal
|
||||||
|
- Optional XpressNet over IP (WebSocket)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
- LM18200 Datasheet
|
||||||
|
- HW 364A ESP8266 Pinout
|
||||||
|
- [RailCom Protocol Overview](https://www.opendcc.de/elektronik/railcom/railcom_e.html)
|
||||||
|
- [XpressNet Protocol](https://www.opendcc.de/elektronik/xpressnet/xpressnet_e.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For further details, see xpressnet.md and source files.
|
||||||
19
include/pio_config_template.ini
Normal file
19
include/pio_config_template.ini
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
; See https://docs.platformio.org/en/latest/projectconf.html
|
||||||
|
|
||||||
|
[platformio]
|
||||||
|
default_envs = d1_mini32
|
||||||
|
|
||||||
|
[env:d1_mini32]
|
||||||
|
platform = espressif32
|
||||||
|
board = d1_mini32
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
lib_deps =
|
||||||
|
tzapu/WiFiManager
|
||||||
|
Wire
|
||||||
|
LiquidCrystal_I2C
|
||||||
|
|
||||||
|
; You can add custom build flags, upload options, etc. below
|
||||||
|
; build_flags =
|
||||||
|
; upload_speed =
|
||||||
9
platformio.ini
Normal file
9
platformio.ini
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[env:d1_mini]
|
||||||
|
platform = espressif8266
|
||||||
|
board = d1_mini
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
lib_deps =
|
||||||
|
tzapu/WiFiManager
|
||||||
|
Wire
|
||||||
|
adafruit/Adafruit SSD1306
|
||||||
9
src/DCC.cpp
Normal file
9
src/DCC.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include "DCC.h"
|
||||||
|
|
||||||
|
void setupDCC() {
|
||||||
|
// Initialize DCC output pins and timers
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendDCCPacket(const uint8_t* packet, size_t length) {
|
||||||
|
// Generate DCC signal using LM18200
|
||||||
|
}
|
||||||
5
src/DCC.h
Normal file
5
src/DCC.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
void setupDCC();
|
||||||
|
void sendDCCPacket(const uint8_t* packet, size_t length);
|
||||||
61
src/DisplayHelper.cpp
Normal file
61
src/DisplayHelper.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include "DisplayHelper.h"
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 128
|
||||||
|
#define SCREEN_HEIGHT 64
|
||||||
|
#define OLED_RESET -1
|
||||||
|
#define SCREEN_ADDRESS 0x3C // If not work please scan the bus
|
||||||
|
#define OLED_SDA 14 // D6
|
||||||
|
#define OLED_SCL 12 // D5
|
||||||
|
|
||||||
|
// Adafruit_SSD1306 *display;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
|
||||||
|
void displayInit() {
|
||||||
|
display = new Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
Wire.begin(OLED_SDA, OLED_SCL);
|
||||||
|
display->begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
|
||||||
|
display->clearDisplay();
|
||||||
|
display->setTextSize(1);
|
||||||
|
display->setTextColor(SSD1306_WHITE);
|
||||||
|
display->setCursor(0, 0);
|
||||||
|
display->println("DCC Cmd Station");
|
||||||
|
display->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayShowIP(const String& ip) {
|
||||||
|
display->clearDisplay();
|
||||||
|
display->setCursor(0, 0);
|
||||||
|
display->println("DCC Cmd Station");
|
||||||
|
display->setCursor(0, 16);
|
||||||
|
display->println("WiFi Connected!");
|
||||||
|
display->setCursor(0, 32);
|
||||||
|
display->print("IP: ");
|
||||||
|
display->println(ip);
|
||||||
|
display->display();
|
||||||
|
delay(3000); // Show IP for 5 seconds before clearing
|
||||||
|
display->clearDisplay();
|
||||||
|
display->setCursor(0, 0);
|
||||||
|
display->println("DCC Cmd Station");
|
||||||
|
display->setCursor(0, 16);
|
||||||
|
display->print("IP: ");
|
||||||
|
display->println(ip);
|
||||||
|
display->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayShowPortal() {
|
||||||
|
display->clearDisplay();
|
||||||
|
display->setCursor(0, 0);
|
||||||
|
display->println("DCC Cmd Station");
|
||||||
|
display->setCursor(0, 16);
|
||||||
|
display->println("Please Connect to");
|
||||||
|
display->setCursor(0, 32);
|
||||||
|
display->println("DCC-CommandStation");
|
||||||
|
display->display();
|
||||||
|
}
|
||||||
9
src/DisplayHelper.h
Normal file
9
src/DisplayHelper.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
|
||||||
|
// extern Adafruit_SSD1306 display;
|
||||||
|
|
||||||
|
void displayInit();
|
||||||
|
void displayShowIP(const String& ip);
|
||||||
|
void displayShowPortal();
|
||||||
97
src/WiFiManagerHelper.cpp
Normal file
97
src/WiFiManagerHelper.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
#include "WiFiManagerHelper.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#include "DisplayHelper.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#define EEPROM_SIZE 96
|
||||||
|
#define SSID_ADDR 0
|
||||||
|
#define PASS_ADDR 32
|
||||||
|
#define MAX_CRED_LEN 32
|
||||||
|
|
||||||
|
|
||||||
|
// Save WiFi credentials to EEPROM
|
||||||
|
void saveCredentialsToEEPROM(const char* ssid, const char* pass) {
|
||||||
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
|
for (int i = 0; i < MAX_CRED_LEN; i++) {
|
||||||
|
EEPROM.write(SSID_ADDR + i, i < strlen(ssid) ? ssid[i] : 0);
|
||||||
|
EEPROM.write(PASS_ADDR + i, i < strlen(pass) ? pass[i] : 0);
|
||||||
|
}
|
||||||
|
EEPROM.commit();
|
||||||
|
EEPROM.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load WiFi credentials from EEPROM
|
||||||
|
void loadCredentialsFromEEPROM(char* ssid, char* pass) {
|
||||||
|
EEPROM.begin(EEPROM_SIZE);
|
||||||
|
for (int i = 0; i < MAX_CRED_LEN; i++) {
|
||||||
|
ssid[i] = EEPROM.read(SSID_ADDR + i);
|
||||||
|
pass[i] = EEPROM.read(PASS_ADDR + i);
|
||||||
|
}
|
||||||
|
ssid[MAX_CRED_LEN - 1] = 0;
|
||||||
|
pass[MAX_CRED_LEN - 1] = 0;
|
||||||
|
EEPROM.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback for WiFiManager to save credentials
|
||||||
|
void saveWifiCallback() {
|
||||||
|
Serial.println("[WiFiManager] Settings saved.");
|
||||||
|
WiFiManager wifiManager;
|
||||||
|
const char* ssid = WiFi.SSID().c_str();
|
||||||
|
const char* pass = WiFi.psk().c_str();
|
||||||
|
saveCredentialsToEEPROM(ssid, pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setupWiFiManager() {
|
||||||
|
char ssid[MAX_CRED_LEN] = {0};
|
||||||
|
char pass[MAX_CRED_LEN] = {0};
|
||||||
|
|
||||||
|
loadCredentialsFromEEPROM(ssid, pass);
|
||||||
|
// displayInit();
|
||||||
|
|
||||||
|
if (strlen(ssid) > 0) {
|
||||||
|
Serial.print("[WiFiManager] Trying stored credentials: ");
|
||||||
|
display->setCursor(0, 16);
|
||||||
|
display->print("Connecting to Wifi ");
|
||||||
|
// display->println(ssid);
|
||||||
|
display->display();
|
||||||
|
Serial.println(ssid);
|
||||||
|
WiFi.begin(ssid, pass);
|
||||||
|
unsigned long start = millis();
|
||||||
|
while (WiFi.status() != WL_CONNECTED && millis() - start < 10000) {
|
||||||
|
delay(500);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Serial.print("[WiFiManager] Connected with stored credentials! IP: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
displayShowIP(WiFi.localIP().toString());
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Serial.println("[WiFiManager] Failed to connect with stored credentials.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFiManager wifiManager;
|
||||||
|
wifiManager.setSaveConfigCallback(saveWifiCallback);
|
||||||
|
displayShowPortal();
|
||||||
|
// Uncomment to reset saved settings for testing
|
||||||
|
// wifiManager.resetSettings();
|
||||||
|
if (!wifiManager.autoConnect("DCC-CommandStation")) {
|
||||||
|
Serial.println("[WiFiManager] Failed to connect and hit timeout. Restarting...");
|
||||||
|
delay(3000);
|
||||||
|
ESP.restart();
|
||||||
|
delay(5000);
|
||||||
|
}
|
||||||
|
// Save credentials after successful connection
|
||||||
|
saveCredentialsToEEPROM(WiFi.SSID().c_str(), WiFi.psk().c_str());
|
||||||
|
Serial.print("[WiFiManager] Connected! IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
// displayShowIP(WiFi.localIP().toString());
|
||||||
|
}
|
||||||
4
src/WiFiManagerHelper.h
Normal file
4
src/WiFiManagerHelper.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
void setupWiFiManager();
|
||||||
13
src/XpressNet.cpp
Normal file
13
src/XpressNet.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "XpressNet.h"
|
||||||
|
|
||||||
|
void setupXpressNet() {
|
||||||
|
// Initialize XpressNet communication (e.g., Serial, pins)
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleXpressNet() {
|
||||||
|
// Poll and process XpressNet messages
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendXpressNetMessage(const uint8_t* data, size_t length) {
|
||||||
|
// Send a message to the XpressNet bus
|
||||||
|
}
|
||||||
6
src/XpressNet.h
Normal file
6
src/XpressNet.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
void setupXpressNet();
|
||||||
|
void handleXpressNet();
|
||||||
|
void sendXpressNetMessage(const uint8_t* data, size_t length);
|
||||||
72
src/main.cpp
Normal file
72
src/main.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "DisplayHelper.h"
|
||||||
|
#include "WiFiManagerHelper.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include <ESP8266WebServer.h>
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 128 // OLED display width, in pixels
|
||||||
|
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
|
||||||
|
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
#define SCREEN_ADDRESS 0x3C // If not work please scan the bus
|
||||||
|
#define OLED_SDA 14 // D6
|
||||||
|
#define OLED_SCL 12 // D5
|
||||||
|
|
||||||
|
// Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
Adafruit_SSD1306 *display;
|
||||||
|
|
||||||
|
// Mode for command station operation
|
||||||
|
// Remove conflicting definition, use extern from main.h
|
||||||
|
// Mode currentMode = MODE_OFF;
|
||||||
|
|
||||||
|
// Create web server instance on port 80
|
||||||
|
ESP8266WebServer server(80);
|
||||||
|
|
||||||
|
// HTML content for welcome page
|
||||||
|
const char* welcome_html = R"HTML(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang=\"en\">
|
||||||
|
<head>
|
||||||
|
<meta charset=\"UTF-8\">
|
||||||
|
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
|
||||||
|
<title>Welcome to ESP8266 DCC Command Station</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; background: #f4f4f4; color: #333; margin: 0; padding: 0; }
|
||||||
|
.container { max-width: 600px; margin: 40px auto; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 32px; }
|
||||||
|
h1 { color: #0077cc; }
|
||||||
|
p { font-size: 1.2em; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class=\"container\">
|
||||||
|
<h1>Welcome!</h1>
|
||||||
|
<p>This is your ESP8266 DCC Command Station.</p>
|
||||||
|
<p>Configure WiFi, manage XpressNet, and control your layout from here.</p>
|
||||||
|
<p>For documentation, see <a href=\"/xpressnet.md\">XpressNet Commands</a> and <a href=\"/README.md\">Hardware Setup</a>.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)HTML";
|
||||||
|
|
||||||
|
void handleWelcome() {
|
||||||
|
server.send(200, "text/html", welcome_html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(100);
|
||||||
|
Serial.println("[DEBUG] Serial started at 115200 baud");
|
||||||
|
displayInit();
|
||||||
|
Serial.println("[DEBUG] OLED display initialized");
|
||||||
|
setupWiFiManager();
|
||||||
|
server.on("/", handleWelcome);
|
||||||
|
server.begin();
|
||||||
|
Serial.println("[DEBUG] Web server started");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
server.handleClient();
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
33
src/main.h
Normal file
33
src/main.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// expose display and other shared resources
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
|
||||||
|
extern Adafruit_SSD1306 *display;
|
||||||
|
extern int currentMode;
|
||||||
|
|
||||||
|
// static const uint8_t D0 = 16;
|
||||||
|
// static const uint8_t D1 = 5;
|
||||||
|
// static const uint8_t D2 = 4;
|
||||||
|
// static const uint8_t D3 = 0;
|
||||||
|
// static const uint8_t D4 = 2;
|
||||||
|
// static const uint8_t D5 = 14;
|
||||||
|
// static const uint8_t D6 = 12;
|
||||||
|
// static const uint8_t D7 = 13;
|
||||||
|
// static const uint8_t D8 = 15;
|
||||||
|
// static const uint8_t D9 = 3;
|
||||||
|
// static const uint8_t D10 = 1;
|
||||||
|
|
||||||
|
// LM18200 pin definitions
|
||||||
|
#define PWM_PIN 5 // D1
|
||||||
|
#define DIR_PIN 4 // D2
|
||||||
|
#define BRAKE_PIN 0 // D3
|
||||||
|
#define MARKLIN_RELAY_PIN 2 // D4 // Relay to swith setup (28V) to reverse polarity for Marklin relays
|
||||||
|
|
||||||
|
// Mode for command station operation
|
||||||
|
enum Mode {
|
||||||
|
MODE_OFF, // No operation
|
||||||
|
MODE_DCC, // DCC command station mode
|
||||||
|
MODE_ANA, // Analog command station mode (PWM)
|
||||||
|
MODE_MAR // Märklin command station mode (PWM with setup for Marklin relays)
|
||||||
|
};
|
||||||
22
welcome.html
Normal file
22
welcome.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Welcome to ESP8266 DCC Command Station</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; background: #f4f4f4; color: #333; margin: 0; padding: 0; }
|
||||||
|
.container { max-width: 600px; margin: 40px auto; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 32px; }
|
||||||
|
h1 { color: #0077cc; }
|
||||||
|
p { font-size: 1.2em; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Welcome!</h1>
|
||||||
|
<p>This is your ESP8266 DCC Command Station.</p>
|
||||||
|
<p>Configure WiFi, manage XpressNet, and control your layout from here.</p>
|
||||||
|
<p>For documentation, see <a href="/xpressnet.md">XpressNet Commands</a> and <a href="/README.md">Hardware Setup</a>.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
132
xpressnet.md
Normal file
132
xpressnet.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# XpressNet Command Reference
|
||||||
|
|
||||||
|
This document describes the available XpressNet commands and how to implement them in your command station firmware.
|
||||||
|
|
||||||
|
## XpressNet Command Table
|
||||||
|
|
||||||
|
| Command Name | Code (Hex) | Description | Typical Usage |
|
||||||
|
|-----------------------|------------|---------------------------------------------|------------------------------|
|
||||||
|
| Locomotive Speed | 0xE4 | Set speed and direction for a locomotive | Control train movement |
|
||||||
|
| Function Group 1 | 0xE4 | Control F0-F4 functions | Lights, sound, etc. |
|
||||||
|
| Function Group 2 | 0xE4 | Control F5-F8 functions | Extended functions |
|
||||||
|
| Accessory Control | 0x90 | Switch turnouts, signals, etc. | Control layout accessories |
|
||||||
|
| Read Feedback | 0xF2 | Query feedback modules | Detect occupancy, sensors |
|
||||||
|
| Programming on Main | 0xEF | Write CVs to decoders on main track | Decoder configuration |
|
||||||
|
| Programming Track | 0xED | Write/read CVs on programming track | Safe decoder programming |
|
||||||
|
| Request Version | 0x21 | Request command station version | Handshake, diagnostics |
|
||||||
|
| Emergency Stop | 0x80 | Stop all locomotives immediately | Safety/emergency |
|
||||||
|
|
||||||
|
*Note: Actual codes and command structure may vary by implementation. Refer to the official XpressNet protocol documentation for full details.*
|
||||||
|
|
||||||
|
## Implementing XpressNet in Your Command Station
|
||||||
|
|
||||||
|
1. **Serial Communication**: XpressNet typically uses RS485 or TTL UART. Set up a serial interface at the required baud rate (commonly 62500 baud).
|
||||||
|
|
||||||
|
2. **Command Parsing**: In your firmware, implement a parser that reads incoming bytes and matches them to the command table above. Use a state machine to handle multi-byte commands.
|
||||||
|
|
||||||
|
3. **Command Handling**:
|
||||||
|
- For each recognized command, implement a handler function (e.g., `handleLocomotiveSpeed()`, `handleAccessoryControl()`).
|
||||||
|
- Extract parameters (address, speed, function bits, etc.) from the command bytes.
|
||||||
|
- Update your internal state or send DCC packets as needed.
|
||||||
|
|
||||||
|
4. **Response Generation**: Some commands require a response (e.g., version request, feedback read). Format and send the appropriate reply bytes.
|
||||||
|
|
||||||
|
5. **Error Handling**: Implement checks for invalid or unsupported commands and respond with error codes if required by the protocol.
|
||||||
|
|
||||||
|
6. **Integration with DCC**: For commands that affect trains or accessories, translate XpressNet commands into DCC packets and send them to the track using your DCC output routines.
|
||||||
|
|
||||||
|
### Example: Handling a Locomotive Speed Command
|
||||||
|
```cpp
|
||||||
|
void handleLocomotiveSpeed(const uint8_t* data, size_t len) {
|
||||||
|
// Parse address, speed, direction from data
|
||||||
|
// Update DCC packet buffer
|
||||||
|
// Send DCC packet to track
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### References
|
||||||
|
- [XpressNet Protocol Specification](https://www.opendcc.de/elektronik/xpressnet/xpressnet_e.html)
|
||||||
|
- [DCC Protocol Overview](https://www.nmra.org/index-nmra-standards-and-recommended-practices)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom (DIY) XpressNet Extensions
|
||||||
|
|
||||||
|
You can add your own special commands to XpressNet by using unused command codes. Below are examples for switching mode and power control.
|
||||||
|
|
||||||
|
| Command Name | Code (Hex) | Payload Example | Description |
|
||||||
|
|---------------|------------|----------------|----------------------------|
|
||||||
|
| Switch Mode | 0xF0 | 0x00 | Switch to Analog mode |
|
||||||
|
| | | 0x01 | Switch to DCC mode |
|
||||||
|
| | | 0x02 | Switch to Marklin mode |
|
||||||
|
| Power Track | 0xF1 | 0x00 | Power OFF |
|
||||||
|
| | | 0x01 | Power ON |
|
||||||
|
|
||||||
|
### Example Implementation in Firmware
|
||||||
|
```cpp
|
||||||
|
// Handle custom XpressNet commands
|
||||||
|
void handleCustomXpressNet(const uint8_t* data, size_t len) {
|
||||||
|
uint8_t cmd = data[0];
|
||||||
|
switch (cmd) {
|
||||||
|
case 0xF0: // Switch Mode
|
||||||
|
if (len > 1) {
|
||||||
|
uint8_t mode = data[1];
|
||||||
|
switch (mode) {
|
||||||
|
case 0x00: /* setAnalogMode(); */ break;
|
||||||
|
case 0x01: /* setDCCMode(); */ break;
|
||||||
|
case 0x02: /* setMarklinMode(); */ break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xF1: // Power Track
|
||||||
|
if (len > 1) {
|
||||||
|
if (data[1] == 0x01) {
|
||||||
|
// powerOnTrack();
|
||||||
|
} else {
|
||||||
|
// powerOffTrack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// ... handle other custom commands ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## XpressNet over IP (WebSocket)
|
||||||
|
|
||||||
|
You can encapsulate XpressNet packets in WebSocket frames for remote control over IP. On the ESP8266, use a WebSocket server library (e.g., arduinoWebSockets).
|
||||||
|
|
||||||
|
**Basic Steps:**
|
||||||
|
1. Start a WebSocket server on the ESP8266.
|
||||||
|
2. On message received, treat the payload as an XpressNet packet and process it.
|
||||||
|
3. Send any response packets back over WebSocket.
|
||||||
|
|
||||||
|
**Example (pseudo-code):**
|
||||||
|
```cpp
|
||||||
|
#include <WebSocketsServer.h>
|
||||||
|
WebSocketsServer webSocket = WebSocketsServer(81);
|
||||||
|
|
||||||
|
void onWebSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||||
|
if (type == WStype_BIN) {
|
||||||
|
// Process XpressNet packet
|
||||||
|
handleXpressNet(payload, length);
|
||||||
|
// Optionally send response:
|
||||||
|
// webSocket.sendBIN(num, response, responseLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// ... WiFi setup ...
|
||||||
|
webSocket.begin();
|
||||||
|
webSocket.onEvent(onWebSocketEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
webSocket.loop();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
These extensions allow you to add custom features and remote control to your XpressNet command station.
|
||||||
Reference in New Issue
Block a user