diff --git a/examples/LocoNetMonitor/LocoNetMonitor.ino b/examples/LocoNetMonitor/LocoNetMonitor.ino index 839c0ec..9df8e66 100644 --- a/examples/LocoNetMonitor/LocoNetMonitor.ino +++ b/examples/LocoNetMonitor/LocoNetMonitor.ino @@ -1,98 +1,141 @@ -#include - -// LocoNet Packet Monitor -// Demonstrates the use of the: -// -// LocoNet.processSwitchSensorMessage(LnPacket) -// -// function and examples of each of the notifyXXXXXXX user call-back functions - -lnMsg *LnPacket; - -void setup() { - // First initialize the LocoNet interface - LocoNet.init(); - - // 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 Output Report messages -void notifySwitchOutputsReport( uint16_t Address, uint8_t ClosedOutput, uint8_t ThrownOutput) { - Serial.print("Switch Outputs Report: "); - Serial.print(Address, DEC); - Serial.print(": Closed - "); - Serial.print(ClosedOutput ? "On" : "Off"); - Serial.print(": Thrown - "); - Serial.println(ThrownOutput ? "On" : "Off"); -} - -// This call-back function is called from LocoNet.processSwitchSensorMessage -// for all Switch Sensor Report messages -void notifySwitchReport( uint16_t Address, uint8_t State, uint8_t Sensor ) { - Serial.print("Switch Sensor Report: "); - Serial.print(Address, DEC); - Serial.print(':'); - Serial.print(Sensor ? "Switch" : "Aux"); - Serial.print(" - "); - Serial.println( State ? "Active" : "Inactive" ); -} - -// 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"); -} - +#include + +// LocoNet Packet Monitor +// Demonstrates the use of the: +// +// LocoNet.processSwitchSensorMessage(LnPacket) +// +// function and examples of each of the notifyXXXXXXX user call-back functions + +lnMsg *LnPacket; + +void setup() { + // First initialize the LocoNet interface + LocoNet.init(); + + // 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 Output Report messages +void notifySwitchOutputsReport( uint16_t Address, uint8_t ClosedOutput, uint8_t ThrownOutput) { + Serial.print("Switch Outputs Report: "); + Serial.print(Address, DEC); + Serial.print(": Closed - "); + Serial.print(ClosedOutput ? "On" : "Off"); + Serial.print(": Thrown - "); + Serial.println(ThrownOutput ? "On" : "Off"); +} + +// This call-back function is called from LocoNet.processSwitchSensorMessage +// for all Switch Sensor Report messages +void notifySwitchReport( uint16_t Address, uint8_t State, uint8_t Sensor ) { + Serial.print("Switch Sensor Report: "); + Serial.print(Address, DEC); + Serial.print(':'); + Serial.print(Sensor ? "Switch" : "Aux"); + Serial.print(" - "); + Serial.println( State ? "Active" : "Inactive" ); +} + +// 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"); +} + +// This call-back function is called from LocoNet.processSwitchSensorMessage +// for all Power messages +void notifyPower(uint8_t State) { + Serial.print("Layout Power State: "); + Serial.println(State ? "On" : "Off"); +} + +// This call-back function is called from LocoNet.processSwitchSensorMessage +// for all MultiSensePower messages +void notifyMultiSensePower(uint8_t BoardID, uint8_t Subdistrict, uint8_t Mode, uint8_t Direction) { + Serial.print("MultiSensePower: Board ID: "); + Serial.print(BoardID, DEC); + Serial.print(" Sub District: "); + Serial.print(Subdistrict, DEC); + Serial.print(" Mode: "); + Serial.print(Mode, DEC); + Serial.print(" Direction: "); + Serial.println(Direction, DEC); +} + +// This call-back function is called from LocoNet.processSwitchSensorMessage +// for all notifyMultiSenseTransponder messages +void notifyMultiSenseTransponder(uint16_t Address, uint8_t Zone, uint16_t LocoAddress, uint8_t Present) { + Serial.print("MultiSenseTransponder: Address: "); + Serial.print(Address, DEC); + Serial.print(" Zone: "); + Serial.print(Zone, DEC); + Serial.print(" Loco Address: "); + Serial.print(LocoAddress, DEC); + Serial.print(" Present: "); + Serial.println(Present, DEC); +} + +// This call-back function is called from LocoNet.processSwitchSensorMessage +// for all LongAck messages + +void notifyLongAck(uint8_t d1, uint8_t d2) { + Serial.print("LongACK : Data Byte 1: "); + Serial.print(d1, DEC); + Serial.print(" Data Byte 2: "); + Serial.println(d2, DEC); + +} diff --git a/examples/LoconetLayoutControlPanel_SerialTerminal/LoconetLayoutControlPanel_SerialTerminal.ino b/examples/LoconetLayoutControlPanel_SerialTerminal/LoconetLayoutControlPanel_SerialTerminal.ino new file mode 100644 index 0000000..fd26d19 --- /dev/null +++ b/examples/LoconetLayoutControlPanel_SerialTerminal/LoconetLayoutControlPanel_SerialTerminal.ino @@ -0,0 +1,199 @@ + +/* + LocoNet Arduino Layout Controler using Serial Terminal + LocoNetLayoutControlPanel_SerialTerminal.ino + + A standalone controller which will generate LocoNet commands from a computer when key 'o', 'f', 'c' or 'e' is pressed + Turn on track power with 'o' key + Turn off track power with 'f' key + Clear all decoder slots with 'c' key + Stop all engines with 's' key + + RX and TX Loconet + Hardcoded to use ICP pin 8 (port PINB bit PB0) for LocoNet input and a user define'd pin for output/transmit + + Copyright (c) 2012, 2014 John Plocher, released under the terms of the MIT License (MIT) + + Modified by Alex Shephard at Al Silverstein request + + Change from eStop command to Loco Speed command by ALS on 11/9/21 + +*/ + +#include + + +#define LNtxPort 7 // LocoNet Transmit pin (LocoShield uses pin7) + +int MaxSlot = 120; + +typedef enum +{ + CMD_DONE, + CMD_SPEED_ZERO, + CMD_SLOT_CLEAR +} CMD_STATE; + +CMD_STATE CommandState = CMD_DONE; + +String ValidCommands = "ofcis"; + +void sendOPC_GP(byte on) { + lnMsg SendPacket; + if (on) { + SendPacket.data[ 0 ] = OPC_GPON; + } else { + SendPacket.data[ 0 ] = OPC_GPOFF; + } + LocoNet.send( &SendPacket ) ; +} + + +void sendOPC_IDLE() { + lnMsg SendPacket; + SendPacket.data[ 0 ] = OPC_IDLE; + LocoNet.send( &SendPacket ) ; +} + +void send3bytePacket(int opcode, int slot, int spd) { + lnMsg SendPacket; + SendPacket.data[ 0 ] = opcode; + SendPacket.data[ 1 ] = slot; + SendPacket.data[ 2 ] = spd; + LocoNet.send( &SendPacket ); +} +void sendOPC_LOCO_SPD(int slot, int spd) { + send3bytePacket(OPC_LOCO_SPD, slot, spd); +} + +void sendOPC_LOCO_DIRF(int slot, int dirf) { + send3bytePacket(OPC_LOCO_DIRF, slot, dirf); +} + +void sendOPC_LOCO_SND(int slot, int snd) { + send3bytePacket(OPC_LOCO_SND, slot, snd); +} + +void sendOPC_SLOT_STAT1(int slot, int stat) { + send3bytePacket(OPC_SLOT_STAT1, slot, stat); +} + +void sendOPC_RQ_SL_DATA(int slot) { + send3bytePacket(OPC_RQ_SL_DATA, slot, 0); +} + +void processIncomingLoconetCommand(lnMsg* LnPacket) { + if ( LnPacket ) + { + switch(LnPacket->sz.command) + { + case OPC_GPON: + Serial.println("Power ON"); + break; + + case OPC_GPOFF: + Serial.println("Power OFF"); + break; + + case OPC_IDLE: + Serial.println("EStop!"); + break; + + case OPC_SL_RD_DATA: + if (CommandState == CMD_SLOT_CLEAR) + { + if(LnPacket->sd.stat & LOCO_IN_USE) + { + Serial.print("Clear Slot: "); Serial.print(LnPacket->sd.slot); Serial.print(" Status: "); Serial.println(LnPacket->sd.stat, HEX); + LnPacket->sd.command = OPC_WR_SL_DATA; + LnPacket->sd.stat = 0; + LnPacket->sd.adr = 0; + LnPacket->sd.spd = 0; + LnPacket->sd.dirf = 0; + LnPacket->sd.ss2 = 0; + LnPacket->sd.adr2 = 0; + LnPacket->sd.snd = 0; + LnPacket->sd.id1 = 0; + LnPacket->sd.id2 = 0; + LocoNet.send(LnPacket); + } + } + + else if(CommandState == CMD_SPEED_ZERO) + { + if((LnPacket->sd.stat & LOCO_IN_USE) && (LnPacket->sd.spd)) + sendOPC_LOCO_SPD(LnPacket->sd.slot, 0); + } + + if(LnPacket->sd.slot >= MaxSlot) + CommandState = CMD_DONE; + break; + + case OPC_LOCO_SPD: + Serial.print("Set Slot: "); Serial.print(LnPacket->lsp.slot); + Serial.print(" : Speed: "); Serial.println(LnPacket->lsp.spd); + break; + } + } +} + +void setup() { + Serial.begin(115200); + Serial.println("\nLocoNet Controller"); + + // initialize the LocoNet interface + LocoNet.init(LNtxPort); +} + +char inChar = 0; +lnMsg sendPacket; + +void loop() { + // Check for any received LocoNet packets + while (lnMsg *LnPacket = LocoNet.receive() ) + { + processIncomingLoconetCommand( LnPacket ); + } + + if (Serial.available()) + { + inChar = Serial.read(); + + if (ValidCommands.indexOf(inChar) >= 0) + { + Serial.print("Command: "); Serial.println(inChar); + + if(inChar == 'o') // Track Power On + { + sendOPC_GP(1); + } + + else if(inChar == 'f') // Track Power Off + { + sendOPC_GP(0); + } + + else if(inChar == 'i') // Idle + { + sendOPC_IDLE(); + } + + else + { + if(inChar == 's') + CommandState = CMD_SPEED_ZERO; + + else if(inChar == 'c') + CommandState = CMD_SLOT_CLEAR; + + for (int slot = 0; slot <= MaxSlot; slot++) + { + sendOPC_RQ_SL_DATA(slot); + + while (lnMsg *LnPacket = LocoNet.receive() ) + processIncomingLoconetCommand( LnPacket ); + } + } + } + } +} diff --git a/library.properties b/library.properties index a396707..5b2385f 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=LocoNet -version=1.1.8 +version=1.1.9 author=Alex Shepherd, John Plocher, Damian Philipp, Tom Knox, Hans Tanner, Björn Rennfanz maintainer=Alex Shepherd sentence=Enables Digitrax LocoNet Communication paragraph=This library allows you to interface to a LocoNet network and send/receive LocoNet commands. The library currently supports the AVR ATTiny84 & ATMega88/168/328/32u4 using the 16-Bit Timer1 and ICP1. It also supports the Mega2560 using Timer5 and ICP5. It also supports the STM32F1 using TIM2 and the ESP8266 uses an hardware interrupt driven software uart. category=Communication url=http://mrrwa.org/loconet-interface/ -architectures=avr,esp8266,stm32 \ No newline at end of file +architectures=avr,esp8266,stm32 diff --git a/utility/ln_config.h b/utility/ln_config.h index 07e962d..993ec24 100755 --- a/utility/ln_config.h +++ b/utility/ln_config.h @@ -92,7 +92,7 @@ typedef volatile LnPortRegisterType* LnPortAddrType; #define LN_SW_UART_TX_INVERTED #ifdef ESP8266 -# define LN_BIT_PERIOD ((F_CPU / 16) / 16666) +# define LN_BIT_PERIOD 4720 //((F_CPU / 16) / 16666) //PG: 11.01.2022 correct ESP speed --> CPU Frequenz != Timer Frequenz!!! # define LN_TIMER_TX_RELOAD_ADJUST 60 #else # if defined(STM32F1) @@ -241,4 +241,4 @@ typedef volatile LnPortRegisterType* LnPortAddrType; #endif // board type -#endif // include file \ No newline at end of file +#endif // include file diff --git a/utility/ln_sw_uart.cpp b/utility/ln_sw_uart.cpp index f41a90d..920830f 100755 --- a/utility/ln_sw_uart.cpp +++ b/utility/ln_sw_uart.cpp @@ -73,6 +73,8 @@ volatile uint8_t lnBitCount; volatile uint8_t lnCurrentByte; volatile LnCompareTargetType lnCompareTarget; +bool checkStartBit = false; + LnBuf* lnRxBuffer; volatile lnMsg* volatile lnTxData; volatile uint8_t lnTxIndex; @@ -191,6 +193,10 @@ ISR(LN_SB_SIGNAL) // Reset the bit counter so that on first increment it is on 0 lnBitCount = 0; + + // Next Bit ist Startbit + checkStartBit=true; + #if defined(ESP8266) // Must clear this bit in the interrupt register, @@ -228,6 +234,33 @@ ISR(LN_TMR_SIGNAL) /* signal handler for timer0 overflow */ LN_TMR_OUTP_CAPT_REG = lnCompareTarget; # endif #endif + + // Check if there is really a start bit or just a glitch + if (checkStartBit) { + checkStartBit = false; +#ifdef LN_SW_UART_RX_INVERTED + if (bit_is_clear(LN_RX_PORT, LN_RX_BIT)) { +#else + if (bit_is_set(LN_RX_PORT, LN_RX_BIT)) { +#endif + lnState = LN_ST_CD_BACKOFF; +#if defined(ESP8266) + // Enable the pin interrupt +#ifdef LN_SW_UART_RX_INVERTED + attachInterrupt(digitalPinToInterrupt(LN_RX_PORT), ln_esp8266_pin_isr, RISING); +#else + attachInterrupt(digitalPinToInterrupt(LN_RX_PORT), ln_esp8266_pin_isr, FALLING); +#endif +#else + // Clear the Start Bit Interrupt Status Flag and Enable ready to + // detect the next Start Bit + LN_CLEAR_START_BIT_FLAG(); + LN_ENABLE_START_BIT_INTERRUPT(); +#endif + } + return; + } + lnBitCount++; // Increment bit_counter if (lnState == LN_ST_RX) { // Are we in RX mode @@ -403,7 +436,7 @@ void initLocoNetHardware(LnBuf * RxBuffer) #if defined(ESP8266) timer1_detachInterrupt(); - timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); //remove divider to make timing more accurate #elif defined(STM32F1) // === Setup the timer === diff --git a/utility/ln_sw_uart.h b/utility/ln_sw_uart.h index a07d8ea..abe0f2f 100755 --- a/utility/ln_sw_uart.h +++ b/utility/ln_sw_uart.h @@ -152,7 +152,8 @@ // The Start Bit period is a full bit period + half of the next bit period // so that the bit is sampled in middle of the bit -#define LN_TIMER_RX_START_PERIOD LN_BIT_PERIOD + (LN_BIT_PERIOD / 2) +//#define LN_TIMER_RX_START_PERIOD LN_BIT_PERIOD + (LN_BIT_PERIOD / 2) +#define LN_TIMER_RX_START_PERIOD LN_BIT_PERIOD / 2 // Read Startbit to make sure we are not started by a glitch #define LN_TIMER_RX_RELOAD_PERIOD LN_BIT_PERIOD #define LN_TIMER_TX_RELOAD_PERIOD LN_BIT_PERIOD