001package jmri.jmrix.marklin; 002 003/** 004 * Encodes a message to a Marklin command station. 005 * <p> 006 * The {@link MarklinReply} class handles the response from the command station. 007 * Packages of length 13 are interpreted as can-bus packages: 008 * 4 bytes Can-bus-ID (BigEndian or network order), 009 * 1-byte length and 010 * 8 bytes of data, if necessary with null bytes to fill in. 011 * <p> 012 * The message ID is divided into the areas of lower priority (priority), 013 * command (command), response and hash. 014 * The communication is based on the following format: 015 * Prio - 2 +2bit 016 * Command 8 bit 017 * Resp - 1 bit 018 * Hash - 16bit 019 * DLC - 4bit (ie CAN message length) 020 * CAN message 8 BYTES 021 * Can Message Bytes 0 to 3 are the address bytes, with byte 0 High, byte 3 low 022 * @author Kevin Dickerson Copyright (C) 2001, 2008 023 */ 024public class MarklinMessage extends jmri.jmrix.AbstractMRMessage { 025 026 static int MY_UID = 0x12345678; 027 028 MarklinMessage() { 029 _dataChars = new int[13]; 030 _nDataChars = 13; 031 setBinary(true); 032 for (int i = 0; i < 13; i++) { 033 _dataChars[i] = 0x00; 034 } 035 } 036 037 // create a new one from an array 038 public MarklinMessage(int[] d) { 039 this(); 040 System.arraycopy(d, 0, _dataChars, 0, d.length); 041 } 042 043 // create a new one from a byte array, as a service 044 public MarklinMessage(byte[] d) { 045 this(); 046 for (int i = 0; i < d.length; i++) { 047 _dataChars[i] = d[i] & 0xFF; 048 } 049 } 050 051 // create a new one 052 public MarklinMessage(int i) { 053 this(); 054 } 055 056 // copy one 057 public MarklinMessage(MarklinMessage m) { 058 super(m); 059 } 060 061 // static methods to return a formatted message 062 public static MarklinMessage getEnableMain() { 063 MarklinMessage m = new MarklinMessage(); 064 m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF); 065 m.setElement(1, 0x00 & 0xFF); 066 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 067 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 068 m.setElement(4, 0x05 & 0xFF); // DLC = 5 (five data bytes) 069 // Elements 5-8: Address bytes (0x00 for global command) 070 m.setElement(9, MarklinConstants.CMDGOSYS & 0xFF); // Data byte 0: Turn main on (0x01) 071 return m; 072 } 073 074 public static MarklinMessage getKillMain() { 075 MarklinMessage m = new MarklinMessage(); 076 m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF); 077 m.setElement(1, 0x00 & 0xFF); 078 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 079 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 080 m.setElement(4, 0x05 & 0xFF); // DLC = 5 (five data bytes) 081 // Elements 5-8: Address bytes (0x00 for global command) 082 m.setElement(9, MarklinConstants.CMDSTOPSYS & 0xFF); // Data byte 0: Turn main off (0x00) 083 return m; 084 } 085 086 /** 087 * Generate CAN BOOT command (0x1B) for Gleisbox activation. 088 * <p> 089 * This command resets the Gleisbox/trackbox and initiates it to start 090 * passing commands to locos and accessories on the rails. Without this 091 * command on startup, the hardware does not respond to subsequent commands. 092 * This variant is used for normal operational startup of standalone 093 * Gleisbox devices when no CS2/MS2 is attached. 094 * <p> 095 * The packet uses DLC=5 with data byte 0 set to 0x11, which is the 096 * "magic value" that activates the Gleisbox for normal operations. 097 * This matches the behavior of Rocrail and the can2udp reference 098 * implementation (M_GLEISBOX_MAGIC_START_SEQUENCE). 099 * 100 * @return MarklinMessage containing the CAN BOOT activation command 101 * @see <a href="https://github.com/GBert/railroad/can2udp">can2udp reference implementation</a> 102 * @see #getCanBootloaderMode() 103 */ 104 public static MarklinMessage getCanBoot() { 105 MarklinMessage m = new MarklinMessage(); 106 m.setElement(0, (0x1B >> 7) & 0xFF); // Command 0x1B high bits (encodes to 0x00) 107 m.setElement(1, (0x1B << 1) & 0xFF); // Command 0x1B low bits (encodes to 0x36) 108 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 109 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 110 m.setElement(4, 0x05 & 0xFF); // DLC = 5 (five data bytes) 111 // Elements 5-8: Address bytes (0x00 for broadcast) 112 m.setElement(9, 0x11 & 0xFF); // Data byte 0: Magic value 0x11 to activate Gleisbox 113 // Elements 10-12 remain as 0x00 (initialized in constructor) 114 return m; 115 } 116 117 /** 118 * Generate CAN BOOT command (0x1B) for entering bootloader mode. 119 * <p> 120 * This variant of the CAN BOOT command invokes the bootloader update 121 * sequence on Märklin devices for firmware updates. It is sent after 122 * a system reset with approximately 400ms wait time, putting the device 123 * into bootloader mode ready to receive firmware data. 124 * <p> 125 * This command uses DLC=0 (no data bytes) and should be followed by 126 * firmware data transfer packets if performing an actual firmware update. 127 * This is different from {@link #getCanBoot()} which activates the device 128 * for normal operations. 129 * <p> 130 * <strong>Note:</strong> This command is intended for firmware update 131 * operations. For normal Gleisbox activation to run trains, use 132 * {@link #getCanBoot()} instead. 133 * 134 * @return MarklinMessage containing the CAN BOOT bootloader invocation command 135 * @see <a href="https://www.stummiforum.de/t122854f7-M-rklin-CAN-Protokoll-x-B-commands-updates.html">Märklin CAN Protokoll 0x1B commands documentation</a> 136 * @see #getCanBoot() 137 */ 138 public static MarklinMessage getCanBootloaderMode() { 139 MarklinMessage m = new MarklinMessage(); 140 m.setElement(0, (0x1B >> 7) & 0xFF); // Command 0x1B high bits (encodes to 0x00) 141 m.setElement(1, (0x1B << 1) & 0xFF); // Command 0x1B low bits (encodes to 0x36) 142 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 143 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 144 m.setElement(4, 0x00 & 0xFF); // DLC = 0 (no data bytes - bootloader invocation) 145 // Elements 5-12 remain as 0x00 (initialized in constructor) 146 return m; 147 } 148 149 //static public MarklinMessage get 150 public static MarklinMessage getSetTurnout(int addr, int state, int power) { 151 MarklinMessage m = new MarklinMessage(); 152 m.setElement(0, (MarklinConstants.ACCCOMMANDSTART >> 7) & 0xFF); 153 m.setElement(1, (MarklinConstants.ACCCOMMANDSTART << 1) & 0xFF); 154 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 155 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 156 m.setElement(4, 0x06 & 0xFF); // DLC = 6 (six data bytes) 157 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 158 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 159 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 160 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 161 m.setElement(9, state & 0xff); 162 m.setElement(10, power & 0xff); 163 return m; 164 } 165 166 public static MarklinMessage getQryLocoSpeed(int addr) { 167 MarklinMessage m = new MarklinMessage(); 168 m.setElement(0, (MarklinConstants.LOCOSPEED >> 7) & 0xFF); 169 m.setElement(1, (MarklinConstants.LOCOSPEED << 1) & 0xFF); 170 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 171 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 172 m.setElement(4, 0x04 & 0xFF); 173 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 174 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 175 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 176 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 177 return m; 178 } 179 180 public static MarklinMessage setLocoSpeed(int addr, int speed) { 181 MarklinMessage m = new MarklinMessage(); 182 m.setElement(0, (MarklinConstants.LOCOSPEED >> 7) & 0xFF); 183 m.setElement(1, (MarklinConstants.LOCOSPEED << 1) & 0xFF); 184 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 185 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 186 m.setElement(4, 0x06 & 0xFF); 187 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 188 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 189 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 190 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 191 m.setElement(9, (speed >> 8) & 0xff); 192 m.setElement(10, speed & 0xff); 193 return m; 194 } 195 196 public static MarklinMessage setLocoEmergencyStop(int addr) { 197 MarklinMessage m = new MarklinMessage(); 198 m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF); 199 m.setElement(1, 0x00 & 0xFF); 200 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 201 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 202 m.setElement(4, 0x05 & 0xFF); // DLC = 5 (five data bytes) 203 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 204 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 205 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 206 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 207 m.setElement(9, MarklinConstants.LOCOEMERGENCYSTOP & 0xFF); 208 return m; 209 } 210 211 public static MarklinMessage setLocoSpeedSteps(int addr, int step) { 212 MarklinMessage m = new MarklinMessage(); 213 m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF); 214 m.setElement(1, 0x00 & 0xFF); 215 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 216 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 217 m.setElement(4, 0x05 & 0xFF); // DLC = 5 (five data bytes) 218 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 219 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 220 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 221 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 222 m.setElement(9, 0x05 & 0xFF); 223 m.setElement(10, step & 0xFF); 224 return m; 225 } 226 227 public static MarklinMessage getQryLocoDirection(int addr) { 228 MarklinMessage m = new MarklinMessage(); 229 m.setElement(0, (MarklinConstants.LOCODIRECTION >> 7) & 0xFF); 230 m.setElement(1, (MarklinConstants.LOCODIRECTION << 1) & 0xFF); 231 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 232 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 233 m.setElement(4, 0x04 & 0xFF); 234 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 235 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 236 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 237 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 238 return m; 239 } 240 241 public static MarklinMessage setLocoDirection(int addr, int dir) { 242 MarklinMessage m = new MarklinMessage(); 243 m.setElement(0, (MarklinConstants.LOCODIRECTION >> 7) & 0xFF); 244 m.setElement(1, (MarklinConstants.LOCODIRECTION << 1) & 0xFF); 245 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 246 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 247 m.setElement(4, 0x05 & 0xFF); 248 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 249 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 250 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 251 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 252 m.setElement(9, dir & 0xff); 253 return m; 254 } 255 256 public static MarklinMessage getQryLocoFunction(int addr, int funct) { 257 MarklinMessage m = new MarklinMessage(); 258 m.setElement(0, (MarklinConstants.LOCOFUNCTION >> 7) & 0xFF); 259 m.setElement(1, (MarklinConstants.LOCOFUNCTION << 1) & 0xFF); 260 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 261 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 262 m.setElement(4, 0x05 & 0xFF); 263 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 264 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 265 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 266 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 267 m.setElement(9, (funct) & 0xFF); 268 return m; 269 } 270 271 public static MarklinMessage setLocoFunction(int addr, int funct, int state) { 272 MarklinMessage m = new MarklinMessage(); 273 m.setElement(0, (MarklinConstants.LOCOFUNCTION >> 7) & 0xFF); 274 m.setElement(1, (MarklinConstants.LOCOFUNCTION << 1) & 0xFF); 275 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 276 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 277 m.setElement(4, 0x06 & 0xFF); 278 m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF); 279 m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF); 280 m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF); 281 m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF); 282 m.setElement(9, funct & 0xff); 283 m.setElement(10, state & 0xff); 284 m.getAddress(); 285 return m; 286 } 287 288 public static MarklinMessage sensorPollMessage(int module) { 289 MarklinMessage m = new MarklinMessage(); 290 m.setElement(0, (MarklinConstants.FEECOMMANDSTART >> 7) & 0xFF); 291 m.setElement(1, (MarklinConstants.FEECOMMANDSTART << 1) & 0xFF); 292 m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF); 293 m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF); 294 m.setElement(4, 0x05 & 0xFF); // DLC = 5 (five data bytes) 295 m.setElement(MarklinConstants.CANADDRESSBYTE1, (MY_UID >> 24) & 0xFF); 296 m.setElement(MarklinConstants.CANADDRESSBYTE2, (MY_UID >> 16) & 0xFF); 297 m.setElement(MarklinConstants.CANADDRESSBYTE3, (MY_UID >> 8) & 0xFF); 298 m.setElement(MarklinConstants.CANADDRESSBYTE4, (MY_UID) & 0xFF); 299 m.setElement(9, module & 0xFF); 300 return m; 301 } 302 303 public long getAddress() { 304 long addr = getElement(MarklinConstants.CANADDRESSBYTE1); 305 addr = (addr << 8) + getElement(MarklinConstants.CANADDRESSBYTE2); 306 addr = (addr << 8) + getElement(MarklinConstants.CANADDRESSBYTE3); 307 addr = (addr << 8) + getElement(MarklinConstants.CANADDRESSBYTE4); 308 309 return addr; 310 } 311 312 public static MarklinMessage getProgMode() { 313 return null; 314 } 315 316 public static MarklinMessage getExitProgMode() { 317 return null; 318 } 319 320 public static MarklinMessage getReadPagedCV(int cv) { //Rxxx 321 return new MarklinMessage(); 322 } 323 324 public static MarklinMessage getWritePagedCV(int cv, int val) { //Pxxx xxx 325 return new MarklinMessage(); 326 } 327 328 public static MarklinMessage getReadRegister(int reg) { //Vx 329 return new MarklinMessage(); 330 } 331 332 public static MarklinMessage getWriteRegister(int reg, int val) { //Sx xxx 333 return new MarklinMessage(); 334 } 335 336 public static MarklinMessage getReadDirectCV(int cv) { //Rxxx 337 return new MarklinMessage(); 338 } 339 340 public static MarklinMessage getWriteDirectCV(int cv, int val) { //Pxxx xxx 341 return new MarklinMessage(); 342 } 343}