-
Notifications
You must be signed in to change notification settings - Fork 32
/
LocoNet.h
640 lines (550 loc) · 24.9 KB
/
LocoNet.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
#ifndef LOCONET_INCLUDED
#define LOCONET_INCLUDED
/****************************************************************************
* Copyright (C) 2009 to 2013 Alex Shepherd
* Copyright (C) 2020 Damian Philipp
*
* Portions Copyright (C) Digitrax Inc.
* Portions Copyright (C) Uhlenbrock Elektronik GmbH
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*****************************************************************************
*
* IMPORTANT:
*
* Some of the message formats used in this code are Copyright Digitrax, Inc.
* and are used with permission as part of the MRRwA (previously EmbeddedLocoNet) project.
* That permission does not extend to uses in other software products. If you wish
* to use this code, algorithm or these message formats outside of
* MRRwA, please contact Digitrax Inc, for specific permission.
*
* Note: The sale any LocoNet device hardware (including bare PCB's) that
* uses this or any other LocoNet software, requires testing and certification
* by Digitrax Inc. and will be subject to a licensing agreement.
*
* Please contact Digitrax Inc. for details.
*
*****************************************************************************
*
* IMPORTANT:
*
* Some of the message formats used in this code are Copyright Uhlenbrock Elektronik GmbH
* and are used with permission as part of the MRRwA (previously EmbeddedLocoNet) project.
* That permission does not extend to uses in other software products. If you wish
* to use this code, algorithm or these message formats outside of
* MRRwA, please contact Copyright Uhlenbrock Elektronik GmbH, for specific permission.
*
*****************************************************************************
* DESCRIPTION
* This module provides functions that manage the sending and receiving of LocoNet packets.
*
* As bytes are received from the LocoNet, they are stored in a circular
* buffer and after a valid packet has been received it can be read out.
*
* When packets are sent successfully, they are also appended to the Receive
* circular buffer so they can be handled like they had been received from
* another device.
*
* Statistics are maintained for both the send and receiving of packets.
*
* Any invalid packets that are received are discarded and the stats are
* updated approproately.
*
*****************************************************************************/
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "utility/ln_buf.h"
#include "utility/ln_opc.h"
#include "utility/utils.h"
typedef enum
{
LN_CD_BACKOFF = 0,
LN_PRIO_BACKOFF,
LN_NETWORK_BUSY,
LN_DONE,
LN_COLLISION,
LN_UNKNOWN_ERROR,
LN_RETRY_ERROR
} LN_STATUS;
typedef enum
{
LN_ST_IDLE = 0, // net is free for anyone to start transmission
LN_ST_CD_BACKOFF = 1, // timer interrupt is counting backoff bits
LN_ST_TX_COLLISION = 2, // just sending break after creating a collision
LN_ST_TX = 3, // transmitting a packet
LN_ST_RX = 4 // receiving bytes
} LN_UART_STATE;
// CD Backoff starts after the Stop Bit (Bit 9) and has a minimum or 20 Bit Times
// but initially starts with an additional 20 Bit Times
#define LN_CARRIER_TICKS 20 // carrier detect backoff - all devices have to wait this
#define LN_MASTER_DELAY 6 // non master devices have to wait this additionally
#define LN_INITIAL_PRIO_DELAY 20 // initial attempt adds priority delay
#define LN_BACKOFF_MIN (LN_CARRIER_TICKS + LN_MASTER_DELAY) // not going below this
#define LN_BACKOFF_INITIAL (LN_BACKOFF_MIN + LN_INITIAL_PRIO_DELAY) // for the first normal tx attempt
#define LN_BACKOFF_MAX (LN_BACKOFF_INITIAL + 10) // lower priority is not supported
#define LN_DEFERRED_SPEED_NOOP -1
//
// LNCV error codes
// Used by the LNCV callbacks to signal what kind of error has occurred.
//
// Error-codes for write-requests
#define LNCV_LACK_ERROR_GENERIC (0)
// Unsupported/non-existing CV
#define LNCV_LACK_ERROR_UNSUPPORTED (1)
// CV is read only
#define LNCV_LACK_ERROR_READONLY (2)
// Value out of range
#define LNCV_LACK_ERROR_OUTOFRANGE (3)
// Everything OK
#define LNCV_LACK_OK (127)
// the valid range for module addresses (CV0) as per the LNCV spec.
#define LNCV_MIN_MODULEADDR (0)
#define LNCV_MAX_MODULEADDR (65534)
class LocoNetClass
{
private:
LnBuf LnBuffer;
void setTxPin(uint8_t txPin);
public:
LocoNetClass();
void init(void);
void init(uint8_t txPin);
bool available(void);
uint8_t length(void);
lnMsg* receive(void);
LN_STATUS send(lnMsg* TxPacket);
LN_STATUS send(lnMsg* TxPacket, uint8_t PrioDelay);
LN_STATUS send(uint8_t OpCode, uint8_t Data1, uint8_t Data2);
LN_STATUS send(uint8_t OpCode, uint8_t Data1, uint8_t Data2, uint8_t PrioDelay);
LN_STATUS sendLongAck(uint8_t ucCode);
LN_UART_STATE uartState();
LnBufStats* getStats(void);
const char* getStatusStr(LN_STATUS Status);
uint8_t processSwitchSensorMessage(lnMsg* LnPacket);
uint8_t processPowerTransponderMessage(lnMsg* LnPacket);
LN_STATUS requestSwitch(uint16_t Address, uint8_t Output, uint8_t Direction);
LN_STATUS reportSwitch(uint16_t Address);
LN_STATUS reportSensor(uint16_t Address, uint8_t State);
LN_STATUS reportPower(uint8_t State);
};
extern LocoNetClass LocoNet;
typedef enum
{
TH_ST_FREE = 0,
TH_ST_IDLE,
TH_ST_RELEASE,
TH_ST_ACQUIRE,
TH_ST_SELECT,
TH_ST_DISPATCH,
TH_ST_SLOT_MOVE,
TH_ST_SLOT_FORCE_FREE,
TH_ST_SLOT_RESUME,
TH_ST_SLOT_STEAL,
TH_ST_IN_USE
} TH_STATE;
typedef enum
{
TH_ER_OK = 0,
TH_ER_SLOT_IN_USE,
TH_ER_BUSY,
TH_ER_NOT_SELECTED,
TH_ER_NO_LOCO,
TH_ER_NO_SLOTS
} TH_ERROR;
#define TH_OP_DEFERRED_SPEED 0x01
typedef enum
{
TH_SP_ST_28 = 0, // 000=28 step/ 3 BYTE PKT regular mode
TH_SP_ST_28_TRI = 1, // 001=28 step. Generate Trinary packets for this Mobile ADR
TH_SP_ST_14 = 2, // 010=14 step MODE
TH_SP_ST_128 = 3, // 011=send 128 speed mode packets
TH_SP_ST_28_ADV = 4, // 100=28 Step decoder ,Allow Advanced DCC consisting
TH_SP_ST_128_ADV = 7 // 111=128 Step decoder, Allow Advanced DCC consisting
} TH_SPEED_STEPS;
class LocoNetThrottleClass
{
private:
TH_STATE myState; // State of throttle
// Sometimes reduces frequency of sending speed update packets by deferring them to process100msActions(), and
// helps to automatically send a speed packet periodically, if no speed or function changes have been made for
// a certain duration (based on SLOT_REFRESH_TICKS), to prevent slot timeout.
uint16_t myTicksSinceLastAction;
uint16_t myThrottleId; // Id of throttle
uint8_t mySlot; // Master Slot index
uint16_t myAddress; // Decoder Address
uint8_t mySpeed; // Loco Speed
int16_t myDeferredSpeed; // Deferred Loco Speed setting
uint8_t myStatus1; // Stat1
uint8_t myDirFunc0to4; // Direction
uint8_t myFunc5to8; // Direction
uint8_t myUserData;
uint8_t myOptions;
uint32_t myLastTimerMillis;
TH_SPEED_STEPS mySpeedSteps;
void updateAddress(uint16_t Address, uint8_t ForceNotify);
void updateSpeed(uint8_t Speed, uint8_t ForceNotify);
void updateState(TH_STATE State, uint8_t ForceNotify);
void updateStatus1(uint8_t Status, uint8_t ForceNotify);
void updateDirectionAndFunctions(uint8_t DirFunc0to4, uint8_t ForceNotify);
void updateFunctions5to8(uint8_t Func5to8, uint8_t ForceNotify);
void updateSpeedSteps(TH_SPEED_STEPS SpeedSteps, uint8_t ForceNotify);
public:
void init(uint8_t UserData, uint8_t Options, uint16_t ThrottleId);
void processMessage(lnMsg* LnPacket);
void process100msActions(void);
uint16_t getAddress(void);
TH_ERROR setAddress(uint16_t Address);
TH_ERROR stealAddress(uint16_t Address);
TH_ERROR resumeAddress(uint16_t Address, uint8_t LastSlot);
TH_ERROR dispatchAddress(void);
TH_ERROR acquireAddress(void);
TH_ERROR releaseAddress(void);
TH_ERROR idleAddress(void);
TH_ERROR freeAddress(void);
TH_ERROR dispatchAddress(uint16_t Address);
TH_ERROR freeAddressForce(uint16_t Address);
uint8_t getSpeed(void);
TH_ERROR setSpeed(uint8_t Speed);
uint8_t getDirection(void);
TH_ERROR setDirection(uint8_t Direction);
uint8_t getFunction(uint8_t Function);
TH_ERROR setFunction(uint8_t Function, uint8_t Value);
TH_ERROR setDirFunc0to4Direct(uint8_t Value);
TH_ERROR setFunc5to8Direct(uint8_t Value);
TH_SPEED_STEPS getSpeedSteps(void);
void setSpeedSteps(TH_SPEED_STEPS newSpeedSteps);
TH_STATE getState(void);
uint8_t getSlot() { return mySlot; }
const char* getStateStr(TH_STATE State);
const char* getErrorStr(TH_ERROR Error);
const char* getSpeedStepStr(TH_SPEED_STEPS speedStep);
};
/************************************************************************************
The LocoNet fast clock in the Command Station is driven from a 65.535 ms
time base. A normal minute takes approximately 915 x 65.535 ms ticks.
The LocoNet fast clock values are stored in a special slot in the Command
Station called the fast clock slot which is slot number 0x7B or 123
Each of the fields in the slot are supposed to count up until the most significant bit
is 0x80 and then rollover the appropriate values and reset however this behaviour
does not seem to hold for all fields and so some corrction factors are needed
An important part of syncing to the Fast Clock master is to interpret the current
FRAC_MINS fields so that a Fast Clock Slave can sync to the part minute and then
rollover it's accumulators in sync with the master. The FRAC_MINS counter is a
14 bit counter that is stored in the two 7 bit FRAC_MINSL & FRAC_MINSH fields.
It counts up the FRAC_MINSL field until it rolls over to 0x80 and then increments
the FRAC_MINSH high field until it rolls over to 0x80 and then increments the minute,
hour and day fields as appropriate and then resets the FRAC_MINS fields to 0x4000 - 915
which is stored in each of the 7 bit fields.
HOWEVER when the DCS100 resets FRAC_MINS fields to 0x4000 - 915, it then immediately
rolls over a 128 count and so the minute is short by 915 - 128 65.535 ms ticks, so it
runs too fast. To correct this problem the fast clock slot can be overwritten with
corrected FRAC_MINS field values that the DCS100 will then increment correctly.
This implementation of a LocoNet Fast Clock Slave has two features to correct these
short commings:
A) It has the option to reduce the FRAC_MINS count by 128 so that it keeps in step with
the DCS100 Fast Clock which normally runs too fast. This is enabled by passing in the
FC_FLAG_DCS100_COMPATIBLE_SPEED flag bit to the init() function.
B) It has the option to overwrite the LocoNet Fast Clock Master slot values with corrected
FRAC_MINS fields imediately after it rolls-over the fast minute, to make the DCS100 not run
too fast as it normally does.
There also seems to be problems with the hours field not rolling over correctly from 23
back to 0 and so there is extra processing to work out the hours when it has rolled over
to 0x80 or 0x00 by the time the bit 7 is cleared. This seems to cause the DT400 throttle
problems as well and so when running in FC_FLAG_MINUTE_ROLLOVER_SYNC mode, this should
be corrected.
The DT400 throttle display seems to decode the minutes incorrectly by 1 count and so we
have to make the same interpretation here which is why there is a 127 and not a 128
roll-over for the minutes.
***********************************************************************************************/
typedef enum
{
FC_ST_IDLE,
FC_ST_REQ_TIME,
FC_ST_READY,
FC_ST_DISABLED,
} FC_STATE;
class LocoNetFastClockClass
{
private:
FC_STATE fcState; // State of the Fast Clock Slave
uint8_t fcFlags; // Storage of the option flags passed into initFastClock()
fastClockMsg fcSlotData; // Primary storage for the Fast Clock slot data
uint8_t fcLastPeriod; // Period of last tick so we can alternate between
void doNotify(uint8_t Sync);
public:
void init(uint8_t DCS100CompatibleSpeed, uint8_t CorrectDCS100Clock, uint8_t NotifyFracMin);
void poll(void);
void processMessage(lnMsg* LnPacket);
void process66msActions(void);
};
/************************************************************************************
SV (System Variable Handling
************************************************************************************/
#if defined(STM32F1)
// STM31F1 has no flash.
#else
typedef enum
{
SV_EE_SZ_256 = 0,
SV_EE_SZ_512 = 1,
SV_EE_SZ_1024 = 2,
SV_EE_SZ_2048 = 3,
SV_EE_SZ_4096 = 4,
SV_EE_SZ_8192 = 5
} SV_EE_SIZE;
typedef enum
{
SV_WRITE_SINGLE = 0x01,
SV_READ_SINGLE = 0x02,
SV_WRITE_MASKED = 0x03,
SV_WRITE_QUAD = 0x05,
SV_READ_QUAD = 0x06,
SV_DISCOVER = 0x07,
SV_IDENTIFY = 0x08,
SV_CHANGE_ADDRESS = 0x09,
SV_RECONFIGURE = 0x0F
} SV_CMD;
typedef enum
{
SV_ADDR_EEPROM_SIZE = 1,
SV_ADDR_SW_VERSION = 2,
SV_ADDR_NODE_ID_L = 3,
SV_ADDR_NODE_ID_H = 4,
SV_ADDR_SERIAL_NUMBER_L = 5,
SV_ADDR_SERIAL_NUMBER_H = 6,
SV_ADDR_USER_BASE = 7,
} SV_ADDR;
typedef enum
{
SV_OK = 0,
SV_ERROR = 1,
SV_DEFERRED_PROCESSING_NEEDED = 2
} SV_STATUS;
class LocoNetSystemVariableClass
{
private:
uint8_t mfgId;
uint8_t devId;
uint16_t productId;
uint8_t swVersion;
uint8_t DeferredProcessingRequired;
uint8_t DeferredSrcAddr;
public:
/** Checks whether the given Offset is a valid value.
*
* Returns:
* True - if the given Offset is valid. False Otherwise.
*/
uint8_t isSVStorageValid(uint16_t Offset);
/** Read the NodeId (Address) for SV programming of this module.
*
* This method accesses multiple special EEPROM locations.
*/
uint16_t readSVNodeId(void);
/** Write the NodeId (Address) for SV programming of this module.
*
* This method accesses multiple special EEPROM locations.
*/
uint16_t writeSVNodeId(uint16_t newNodeId);
/**
* Checks whether all addresses of an address range are valid (defers to
* isSVStorageValid()). Sends a notification for the first invalid address
* (long Ack with a value of 42).
*
* TODO: There is a Type error in this method. Return type is bool, but
* actual returned values are Integer.
*
* Returns:
* 0 if at least one address of the range is not valid.
* 1 if all addresses out of the range are valid.
*/
bool CheckAddressRange(uint16_t startAddress, uint8_t Count);
void init(uint8_t newMfgId, uint8_t newDevId, uint16_t newProductId, uint8_t newSwVersion);
/**
* Check whether a message is an SV programming message. If so, the message
* is processed.
* Call this message in your main loop to implement SV programming.
*
* TODO: This method should be updated to reflect whether the message has
* been consumed.
*
* Note that this method will not send out replies.
*
* Returns:
* SV_OK - the message was or was not an SV programming message.
It may or may not have been consumed.
* SV_DEFERRED_PROCESSING_NEEDED - the message was an SV programming
message and has been consumed. doDeferredProcessing() must be
called to actually process the message.
* SV_ERROR - the message was an SV programming message and carried
an unsupported OPCODE.
*
*/
SV_STATUS processMessage(lnMsg* LnPacket);
/** Read a value from the given EEPROM offset.
*
* There are two special values for the Offset parameter:
* SV_ADDR_EEPROM_SIZE - Return the size of the EEPROM
* SV_ADDR_SW_VERSION - Return the value of swVersion
* 3 and on - Return the byte stored in the EEPROM at location (Offset - 2)
*
* Parameters:
* Offset: The offset into the EEPROM. Despite the value being passed as 2 Bytes, only the lower byte is respected.
*
* Fires notifySVRead(Offset, Value), to enable overriding SV read process.
*
* Returns:
* A Byte containing the EEPROM size, the software version or contents of the EEPROM.
*
*/
uint8_t readSVStorage(uint16_t Offset);
/** Write the given value to the given Offset in EEPROM.
*
* TODO: Writes to Offset 0 and 1 will cause data corruption.
*
* Fires notifySVWrite(Offset, Value), to enable overriding SV write process.
* Fires notifySVChanged(Offset), if the value actually changed.
*
* Returns:
* A Byte containing the new EEPROM value (even if unchanged).
*/
uint8_t writeSVStorage(uint16_t Offset, uint8_t Value);
/**
* Attempts to send a reply to an SV programming message.
* This method will repeatedly try to send the message, until it succeeds.
*
* Returns:
* SV_OK - Reply was successfully sent.
* SV_DEFERRED_PROCESSING_NEEDED - Reply was not sent, a later retry is needed.
*/
SV_STATUS doDeferredProcessing(void);
};
#endif // STM32F1
class LocoNetCVClass
{
private:
void makeLNCVresponse(UhlenbrockMsg& ub, uint8_t originalSource, uint16_t first, uint16_t second, uint16_t third, uint8_t last);
// Computes the PXCT byte from the data bytes in the given UhlenbrockMsg.
void computePXCTFromBytes(UhlenbrockMsg& ub);
// Computes the correct data bytes using the containes PXCT byte
void computeBytesFromPXCT(UhlenbrockMsg& ub);
// Computes an address from a low- and a high-byte
uint16_t getAddress(uint8_t lower, uint8_t higher);
public:
//Call this method when you want to implement a module that can be configured via Uhlenbrock LNVC messages
uint8_t processLNCVMessage(lnMsg* LnPacket);
};
/************************************************************************************
Call-back functions
************************************************************************************/
#if defined (__cplusplus)
extern "C" {
#endif
// Notify *from the ISR context* that a byte was received.
// This is useful to, e.g., wake a task that will checks for new
// LocoNet messages.
extern void notifyLnByteReceived() __attribute__((weak));
extern void notifySensor(uint16_t Address, uint8_t State) __attribute__((weak));
// Address: Switch Address.
// Output: Value 0 for Coil Off, anything else for Coil On
// Direction: Value 0 for Closed/GREEN, anything else for Thrown/RED
// state: Value 0 for no input, anything else for activated
// Sensor: Value 0 for 'Aux'/'thrown' anything else for 'switch'/'closed'
extern void notifySwitchRequest(uint16_t Address, uint8_t Output, uint8_t Direction) __attribute__((weak));
extern void notifySwitchReport(uint16_t Address, uint8_t Output, uint8_t Direction) __attribute__((weak));
extern void notifySwitchOutputsReport(uint16_t Address, uint8_t ClosedOutput, uint8_t ThrownOutput) __attribute__((weak));
extern void notifySwitchState(uint16_t Address, uint8_t Output, uint8_t Direction) __attribute__((weak));
extern void notifyPower(uint8_t State) __attribute__((weak));
extern void notifyLongAck(uint8_t d1, uint8_t d2) __attribute__((weak));
// Power management, Transponding and Multi-Sense Device info Call-back functions
extern void notifyMultiSenseTransponder(uint16_t Address, uint8_t Zone, uint16_t LocoAddress, uint8_t Present) __attribute__((weak));
extern void notifyMultiSensePower(uint8_t BoardID, uint8_t Subdistrict, uint8_t Mode, uint8_t Direction) __attribute__((weak));
// Throttle notify Call-back functions
extern void notifyThrottleAddress(uint8_t UserData, TH_STATE State, uint16_t Address, uint8_t Slot) __attribute__((weak));
extern void notifyThrottleSpeed(uint8_t UserData, TH_STATE State, uint8_t Speed) __attribute__((weak));
extern void notifyThrottleDirection(uint8_t UserData, TH_STATE State, uint8_t Direction) __attribute__((weak));
extern void notifyThrottleFunction(uint8_t UserData, uint8_t Function, uint8_t Value) __attribute__((weak));
extern void notifyThrottleSlotStatus(uint8_t UserData, uint8_t Status) __attribute__((weak));
extern void notifyThrottleSpeedSteps(uint8_t UserData, TH_SPEED_STEPS SpeedSteps) __attribute__((weak));
extern void notifyThrottleError(uint8_t UserData, TH_ERROR Error) __attribute__((weak));
extern void notifyThrottleState(uint8_t UserData, TH_STATE PrevState, TH_STATE State) __attribute__((weak));
// FastClock notify Call-back functions
extern void notifyFastClock(uint8_t Rate, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Sync) __attribute__((weak));
extern void notifyFastClockFracMins(uint16_t FracMins) __attribute__((weak));
// System Variable notify Call-back functions
extern void notifySVChanged(uint16_t Offset) __attribute__((weak));
extern bool notifySVWrite(uint16_t Offset, uint8_t& Value) __attribute__((weak)); // Allow SV write override, return true if override was performed. false to handle to SV write in the LocoNetSystemVariableClass
extern bool notifySVRead(uint16_t Offset, uint8_t& Value) __attribute__((weak)); // Allow SV read override, return true if override was performed. false to handle to SV read in the LocoNetSystemVariableClass
extern void notifySVReconfigure() __attribute__((weak)); // Notify of reconfiguration
extern bool notifyCheckAddressRange(uint16_t startAddress, bool& AddressAccepted) __attribute__((weak)); // Allow SV Address range override, return true if override was performed. false to handle to SV Address range in the LocoNetSystemVariableClass
// LNCV notify Call-back functions
// Negative return codes will result in no message being sent.
// Where a value response is appropriate, a return value of LNCV_LACK_OK will trigger the
// response being sent.
// Other values greater than 0 will result in a LACK message being sent.
// When no value result is appropriate, LNCV_LACK_OK will be sent as a LACK.
/**
* TODO: General LNCV documentation
* Pick an ArtNr
* Implement your code to the following behaviour...
*/
/**
* Notification that an LNCVDiscover message was sent. If a module wants to react to this,
* It should return LNCV_LACK_OK and set ArtNr and ModuleAddress accordingly.
* A response just as in the case of notifyLNCVProgrammingStart will be generated.
* If a module responds to a LNCVDiscover, it should apparently enter programming mode immediately.
*/
extern int8_t notifyLNCVdiscover(uint16_t& ArtNr, uint16_t& ModuleAddress) __attribute__((weak));
/**
* Notification that a LNCVProgrammingStart message was received. Application code should process this message and
* set the return code to LNCV_LACK_OK in case this message was intended for this module (i.e., the addresses match).
* In case ArtNr and/or ModuleAddress were Broadcast addresses, the Application Code should replace them by their
* real values.
* The calling code will then generate an appropriate ACK message.
* A return code different than LACK_LNCV_OK will result in no response being sent.
*/
extern int8_t notifyLNCVprogrammingStart(uint16_t& ArtNr, uint16_t& ModuleAddress) __attribute__((weak));
/**
* Notification that a LNCV read request message was received. Application code should process this message,
* set the lncvValue to its respective value and set an appropriate return code.
* return LNCV_LACK_OK leads the calling code to create a response containing lncvValue.
* return code >= 0 leads to a NACK being sent.
* return code < 0 will result in no reaction.
*/
extern int8_t notifyLNCVread(uint16_t ArtNr, uint16_t lncvAddress, uint16_t& lncvValue) __attribute__((weak));
/**
* Notification that a LNCV value should be written. Application code should process this message and
* set an appropriate return code.
* Note 1: LNCV 0 is spec'd to be the ModuleAddress.
* Note 2: Changes to LNCV 0 must be reflected IMMEDIATELY! E.g. the programmingStop command will
* be sent using the new address.
*
* return codes >= 0 will result in a LACK containing the return code being sent.
* return codes < 0 will result in no reaction.
*/
extern int8_t notifyLNCVwrite(uint16_t ArtNr, uint16_t lncvAddress, uint16_t lncvValue) __attribute__((weak));
/**
* Notification that an LNCV Programming Stop message was received.
* This message is noch ACKed, thus does not require a result to be returned from the application.
*/
extern void notifyLNCVprogrammingStop(uint16_t ArtNr, uint16_t ModuleAddress) __attribute__((weak));
#if defined (__cplusplus)
}
#endif
#endif