-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathLocoLinx32U4.ino
197 lines (153 loc) · 5.45 KB
/
LocoLinx32U4.ino
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
// Arduino - AVR ATmega32U4 based USB LocoNet PC Interface Demo
#if !defined(ARDUINO_ARCH_AVR) and !defined(ARDUINO_MEGA_32U4)
#error "This sketch only supports the AVR ATmega32U4 processor as it relies on specific USB Port functionality"
#endif
#include <LocoNet.h>
// There are a number of ATmega32U4 boards available but each may have different pin numbers for on-baord LEDs.
//
// This example sketch was developed using the Sparkfun Arduino Pro Mico (see: https://www.sparkfun.com/products/12640)
// and a small LocoNet interface shield designed by John Plocher specifically to match up with the Arduino Pro Micro footprint
// that can be found here: http://www.spcoast.com/wiki/index.php/Core-Locobuffer
// The LocoNet Tx/Rx traffic can be indicated using some LEDs. The default values below are for the Sparkfun Arduino Pro Micro board.
// If your board doesn't have LEDs or you don't want to use them, comment out the two #define lines below
#define TX_LED 30
#define RX_LED 17
#if defined(TX_LED) or defined(RX_LED)
// If you're using LEDs to indicate LocoNet traffic then define the HIGH/LOW state to turn the LEDs ON/OFF in the two lines below
#define LED_ON LOW
#define LED_OFF HIGH
// These elapsedMillis timers are used to turn off the Tx/Rx LEDs after a short delay, so they're only needed when the LEDs are used
#include <elapsedMillis.h> // See http://playground.arduino.cc/Code/ElapsedMillis
elapsedMillis txElapsedMillis;
elapsedMillis rxElapsedMillis;
#endif
#define VERSION 2
// The Rx Pin on the ATmega32U4 always uses the ICP input so that is fixed and enabled in the Library.
// However the Tx pin can be any other available pin on the board.
// For the Sparkfun Arduino Pro Micro and the Core-Locobuffer shield it uses Digital pin 7
#define TX_PIN 7
static LnBuf LnTxBuffer ;
static lnMsg *LnTxPacket;
static lnMsg *LnRxPacket;
static lnMsg *LnStatsPacket;
static lnMsg myStats;
// Format a LocoBuffer II Status Response
void updateStats()
{
memset(&myStats, 0, sizeof(peerXferMsg));
myStats.data[ 0] = OPC_PEER_XFER;
myStats.data[ 1] = 0x10;
myStats.data[ 2] = 0x50;
myStats.data[ 3] = 0x50;
myStats.data[ 4] = 0x01;
LnBufStats* pLnStats = LocoNet.getStats();
long Errors = pLnStats->RxErrors + pLnStats->TxErrors;
uint8_t myStatsData[8];
myStatsData[0] = 0x00;
myStatsData[1] = (Errors >> 16) & 0xFF;
myStatsData[2] = (Errors >> 8) & 0xFF;
myStatsData[3] = Errors & 0xFF;
myStatsData[4] = VERSION;
myStatsData[5] = 0x00;
myStatsData[6] = (pLnStats->Collisions >> 8) & 0xFF ;
myStatsData[7] = pLnStats->Collisions & 0xFF;
encodePeerData(&myStats.px, myStatsData);
uint8_t CheckSum = 0xFF ;
for( uint8_t lnTxIndex = 0; lnTxIndex < sizeof(peerXferMsg) - 1; lnTxIndex++ ) {
CheckSum ^= myStats.data[ lnTxIndex ] ;
}
myStats.data[sizeof(peerXferMsg) - 1] = CheckSum ;
LnStatsPacket = &myStats;
}
void setup()
{
// Configure the serial port for 57600 baud, even though the baud rate is ignored for USB interfaces
Serial.begin(57600);
while (!Serial); // Wait for USB Serial port to connect - needed for native USB
// First initialize the LocoNet interface, specifying the TX Pin
LocoNet.init(TX_PIN);
// Initialize a LocoNet packet buffer to buffer bytes from the PC
initLnBuf(&LnTxBuffer) ;
// Set this to NULL so we know we don't have an unsent LocoNet packet
LnRxPacket = NULL;
LnTxPacket = NULL;
LnStatsPacket = NULL;
#ifdef RX_LED
pinMode(RX_LED, OUTPUT);
digitalWrite(RX_LED, LED_OFF);
#endif
#ifdef TX_LED
pinMode(TX_LED, OUTPUT);
digitalWrite(TX_LED, LED_OFF);
#endif
}
void loop()
{
// Before we check for a new LocoNet packet, make sure we haven't already got a previously unset packet
if(LnRxPacket == NULL)
{
if(LnStatsPacket)
{
LnRxPacket = LnStatsPacket;
LnStatsPacket = NULL;
}
else
LnRxPacket = LocoNet.receive() ;
}
if( LnRxPacket )
{
#ifdef RX_LED
digitalWrite(RX_LED, LED_ON);
rxElapsedMillis = 0;
#endif
// Get the length of the received packet
uint8_t Length = getLnMsgSize( LnRxPacket ) ;
uint8_t USBWriteBufferFree = Serial.availableForWrite();
if( USBWriteBufferFree >= Length)
{
Serial.write((uint8_t*)LnRxPacket, Length);
LnRxPacket = NULL;
}
}
// Before we check for a new LocoNet TX packet, make sure we haven't already got a previously unset packet
if(LnTxPacket == NULL)
{
int charWaiting;
// Check to see if there are any bytes from the PC
while( (charWaiting = Serial.available()) && (LnTxPacket == NULL) )
{
// Read the byte
uint8_t inByte = Serial.read() & 0xFF;
// Add it to the buffer
addByteLnBuf( &LnTxBuffer, inByte ) ;
// Check to see if we have received a complete packet yet
LnTxPacket = recvLnMsg( &LnTxBuffer ) ;
}
}
// Send the received packet from the PC to the LocoNet
if(LnTxPacket )
{
// Check for a Request for LocoNet Stats
if(LnTxPacket->data[0] == OPC_BUSY)
{
LnTxPacket = NULL;
updateStats();
}
else if(LocoNet.send( LnTxPacket ) == LN_DONE)
{
LnTxPacket = NULL;
#ifdef TX_LED
digitalWrite(TX_LED, LED_ON);
txElapsedMillis = 0;
#endif
}
}
#ifdef RX_LED
if(rxElapsedMillis > 50)
digitalWrite(RX_LED, LED_OFF);
#endif
#ifdef TX_LED
if(txElapsedMillis > 50)
digitalWrite(TX_LED, LED_OFF);
#endif
}