Loconet
Eigenbau | Allgemein | LocoNet Buffer | LocoNet Kreuzung | LocoNet Throttle | LocoNet Light Control | LocoNet Spezialdekoder
Protokoll
Das LocoNet-Protokoll ist offiziell nicht offengelegt. Unter diesen Links sind die vorhanden Dokumentationen zu dem Protokoll zu finden:
- LocoNet Personal Edition - offielle Protokollbeschreibung von Digitrax.
- Programming Messages - Aufbau von LNCV Programmiernachrichten.
- Rocrail LocoNet Protokoll - Beschreibung mit Erklärung auf der Rocrail Webseite
Rocrail GCA-Module
Rocrail bietet auch einige Module für LocoNet zum Eigenbau auf seiner Webseite an:
Loconet Patch Panel
An diesem Verteiler finden mehrere Loconet Geräte wie der FredI Anschluss. Dazu sind vier RJ12 Buchsen je zwei auf der einen und zwei auf der anderen Seite installiert. Die Eingangsbuchse ist dabei frei wählbar.
Der Verteiler ist im wesentlichen eine Kopie des MVG91 von Rocrail.
Jedoch wurde auf die GCA-LocoNet SUB-D Buchsen und die Jumper für die zusätzliche Stromversorgung verzichtet.
Als Statusanzeige ist eine LED zur Aktivitätsanzeige von LocoNet und eine Power ON LED vorhanden.
Schaltplan:
Arduino und LocoNet
Hardware
serielle Datenübertragung
- TxPin wird bei der Initialisierung des Interface gesetzt. Der Standard ist in der Library auf Pin 6 festgelegt:
LocoNet.init(TxPin);
- RxPin ist an ICP1 und beim Arduino Mega an ICP5:
Arduino Uno (ATmega168, ATmega328, etc.) PB0, Digital Pin 8 Arduino Leonardo (ATmega32U4) PD4, Digital Pin 4 Arduino Mega (ATmega1280, ATmega2560) PL1, Digital Pin 48 Arduino Sanguino (ATmega644p) PD6, Digital Pin 6
Arduino Interface
Je nach Arduino wird ein anderer fester Pin für das Rx-Signal(ICP) verwendet. Das LocoNet Interface nutzt zur Kommunikation den Timer1 und im Arduino Mega den Timer 5
Das Railsync Signal ist ein DCC-Signal, welches auch zum ansteuern weiterer Booster (Loconet-B) genutzt werden kann. Da meist der Treiber für das Railsync nur 500 mA Leistung liefern, gibt es oft auch eine weitere Loconet-Buchse (LocoNet-T), welche ohne dieses Signal ist und stattdessen eine Gleichspannung von +12 Volt dort abgenommen werden kann.
Stromquelle
Ein Pull-Up (15 mA) für die Datenleitung ist dann notwendig, wenn sich zum Beispiel kein Master am LocoNet befindet. Um Daten senden zu können muss die Datenleitung mit einer 15 mA Stromquelle ausgestattet sein.
Software
Arduino Bibliothek
Die Software ist mit der Arduino IDE geschrieben. Diese Bibliothek von MRRWA erzeugt einen LocoNet Client. Um die Library auch mit dem Arduino MEGA (wie oben unter Hardware beschrieben) nutzbar zu machen, wurde die "ln_config.h" Datei angepasst.
Download
Beispiele
LocoNet Monitor
#include <LocoNet.h> lnMsg *LnPacket; #define LOCONET_TX_PIN 7 void setup() { LocoNet.init(LOCONET_TX_PIN); //First initialize the LocoNet interface // Configure the serial port for 57600 baud Serial.begin(57600); Serial.println("LocoNet Monitor"); } void loop() { //Check for any received LocoNet packets LnPacket = LocoNet.receive() ; if( LnPacket ) { //First print out the packet in HEX Serial.print("RX: "); uint8_t msgLen = getLnMsgSize(LnPacket); for (uint8_t x = 0; x < msgLen; x++) { uint8_t val = LnPacket->data[x]; //Print a leading 0 if less than 16 to make 2 HEX digits if(val < 16) Serial.print('0'); Serial.print(val, HEX); Serial.print(' '); } //If this packet was not a Switch or Sensor Message then print a new line if(!LocoNet.processSwitchSensorMessage(LnPacket)) { Serial.println(); } } } // This call-back function is called from LocoNet.processSwitchSensorMessage // for all Sensor messages void notifySensor( uint16_t Address, uint8_t State ) { Serial.print("Sensor: "); Serial.print(Address, DEC); Serial.print(" - "); Serial.println( State ? "Active" : "Inactive" ); } // This call-back function is called from LocoNet.processSwitchSensorMessage // for all Switch Request messages void notifySwitchRequest( uint16_t Address, uint8_t Output, uint8_t Direction ) { Serial.print("Switch Request: "); Serial.print(Address, DEC); Serial.print(':'); Serial.print(Direction ? "Closed" : "Thrown"); Serial.print(" - "); Serial.println(Output ? "On" : "Off"); } // This call-back function is called from LocoNet.processSwitchSensorMessage // for all Switch Report messages void notifySwitchReport( uint16_t Address, uint8_t Output, uint8_t Direction ) { Serial.print("Switch Report: "); Serial.print(Address, DEC); Serial.print(':'); Serial.print(Direction ? "Closed" : "Thrown"); Serial.print(" - "); Serial.println(Output ? "On" : "Off"); } // This call-back function is called from LocoNet.processSwitchSensorMessage // for all Switch State messages void notifySwitchState( uint16_t Address, uint8_t Output, uint8_t Direction ) { Serial.print("Switch State: "); Serial.print(Address, DEC); Serial.print(':'); Serial.print(Direction ? "Closed" : "Thrown"); Serial.print(" - "); Serial.println(Output ? "On" : "Off"); }
LocoNet Throttle
#include <LocoNet.h> #include <BasicTerm.h> #include <ctype.h> #define LOCONET_TX_PIN 7 // Address Recall Stack Length #define RECALL_STACK_LEN 6 #define AJS_RECALL_STACK { 38, 44, 53, 99, 191, 192 } typedef struct { word RecallStack[ RECALL_STACK_LEN ] ; word LastAddress ; byte LastSlot ; } TH_EE_CONFIG ; TH_EE_CONFIG eeConfig __attribute__((section(".eeprom"))) = { AJS_RECALL_STACK, 0, 0xFF }; LocoNetThrottleClass Throttle ; word LocoAddr ; byte RecallIndex ; LnBuf LnRxBuffer ; lnMsg *RxPacket ; byte i ; byte tByte ; uint32_t LastThrottleTimerTick; BasicTerm Term(&Serial); void DrawStaticText(void) { Term.show_cursor(0); Term.cls(); Term.position(0,0); Term.println(F("LocoNet Throttle Library Demonstration Version: 1")); Term.println(); Term.println(F("Address :")); Term.println(); Term.println(F("Speed :")); Term.println(); Term.println(F("Direction :")); Term.println(); Term.println(F("Functions : 0 1 2 3 4 5 6 7 8")); // 1 2 3 // 0123456789012345678901234567890 Term.println(); Term.println("Status :"); Term.println(); Term.println("Error :"); Term.println(); Term.println("Last Key :"); Term.println(); Term.println(F("Keys: A - Enter Loco Address, <BS> to delete, <ENTER> to select")); Term.println(F("Keys: Q - Release Loco Address")); Term.println(F("Keys: [ - Reduce Speed")); Term.println(F("Keys: ] - Increase Speed")); Term.println(F("Keys: F - Forward")); Term.println(F("Keys: R - Reverse")); Term.println(F("Keys: T - Toggle Direction")); Term.println(F("Keys: <SP> - Stop")); Term.println(F("Keys: 0..8 - Toggle Functions 0..8")); } void notifyThrottleAddress( uint8_t UserData, TH_STATE State, uint16_t Address, uint8_t Slot ) { Term.position(2,13); Term.print(Address); Term.print(" "); // Erase any extra chars }; void notifyThrottleSpeed( uint8_t UserData, TH_STATE State, uint8_t Speed ) { Term.position(4,13); Term.print(Speed); Term.print(" "); // Erase any extra chars }; void notifyThrottleDirection( uint8_t UserData, TH_STATE State, uint8_t Direction ) { Term.position(6,13); Term.print(Direction ? "Reverse" : "Forward"); }; void notifyThrottleFunction( uint8_t UserData, uint8_t Function, uint8_t Value ) { Term.position(8,13 + (Function * 2) ); if(Value) Term.set_attribute(BT_BOLD); Term.print(Function, DEC); if(Value) Term.set_attribute(BT_NORMAL); }; void notifyThrottleSlotStatus( uint8_t UserData, uint8_t Status ) {}; void notifyThrottleState( uint8_t UserData, TH_STATE PrevState, TH_STATE State ) { Term.position(10, 13); Term.print(State, DEC); Term.print(' '); Term.print(Throttle.getStateStr(State)); Term.print(" "); } void notifyThrottleError( uint8_t UserData, TH_ERROR Error ) { Term.position(12, 13); Term.print(Error, DEC); Term.print(' '); Term.print(Throttle.getErrorStr(Error)); Term.print(" "); } void setup() { LocoNet.init(LOCONET_TX_PIN); // First initialize the LocoNet interface // Configure the serial port for 57600 baud Serial.begin(115200); Term.init(); DrawStaticText(); Throttle.init(0, 0, 9999); } boolean isTime(unsigned long *timeMark, unsigned long timeInterval) { unsigned long timeNow = millis(); if ( timeNow - *timeMark >= timeInterval) { *timeMark = timeNow; return true; } return false; } void loop() { // Check for any received LocoNet packets RxPacket = LocoNet.receive() ; if( RxPacket ) { digitalWrite(13, LOW); if( !LocoNet.processSwitchSensorMessage(RxPacket) ) Throttle.processMessage(RxPacket) ; } if( Serial.available()) { int16_t inChar = toupper(Serial.read()); Term.position(14,13); Term.print(inChar); switch(inChar){ case 'A': Throttle.setAddress(1234); break; case 'X': DrawStaticText(); Throttle.freeAddress(1234); break; case 'Q': DrawStaticText(); Throttle.releaseAddress(); break; case 'F': Throttle.setDirection(0); break; case 'R': Throttle.setDirection(1); break; case 'T': Throttle.setDirection(!Throttle.getDirection()); break; case '[': if(Throttle.getSpeed() > 0 ) Throttle.setSpeed(Throttle.getSpeed() - 1); break; case ']': if(Throttle.getSpeed() < 127 ) Throttle.setSpeed(Throttle.getSpeed() + 1); break; case ' ': Throttle.setSpeed(0); break; default: if( (inChar >= '0') && (inChar <= '8')) Throttle.setFunction( inChar - '0', !Throttle.getFunction(inChar - '0')); break; } } if(isTime(&LastThrottleTimerTick, 100)) { Throttle.process100msActions() ; digitalWrite(13, HIGH); } }