|
| 1 | +/* |
| 2 | +|| |
| 3 | +|| @file LocoNetKS.cpp |
| 4 | +|| @version 1.0 |
| 5 | +|| @author Michael Zimmermann |
| 6 | +|| @contact michael.zimmermann.sg@t-online.de |
| 7 | +|| |
| 8 | +|| @description |
| 9 | +|| | additional functions for class 'LocoNet' |
| 10 | +|| # |
| 11 | +|| |
| 12 | +|| @license |
| 13 | +|| | Copyright (c) 2021 Michael Zimmermann <http://www.kruemelsoft.privat.t-online.de> |
| 14 | +|| | All rights reserved. |
| 15 | +|| | |
| 16 | +|| | This program is free software: you can redistribute it and/or modify |
| 17 | +|| | it under the terms of the GNU General Public License as published by |
| 18 | +|| | the Free Software Foundation, either version 3 of the License, or |
| 19 | +|| | (at your option) any later version. |
| 20 | +|| | |
| 21 | +|| | This program is distributed in the hope that it will be useful, |
| 22 | +|| | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 23 | +|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 24 | +|| | GNU General Public License for more details. |
| 25 | +|| | |
| 26 | +|| | You should have received a copy of the GNU General Public License |
| 27 | +|| | along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 28 | +|| # |
| 29 | +|| |
| 30 | +*/ |
| 31 | + |
| 32 | +#include <LocoNet.h> |
| 33 | +#include "LocoNetKS.h" |
| 34 | + |
| 35 | +#if ARDUINO >= 100 |
| 36 | +#include "Arduino.h" |
| 37 | +#else |
| 38 | +#include "WProgram.h" |
| 39 | +#endif |
| 40 | + |
| 41 | +//=== LocoNetClassKS === |
| 42 | +LN_STATUS LocoNetClassKS::send(uint8_t OpCode) |
| 43 | +{ |
| 44 | + lnMsg SendPacket; |
| 45 | + |
| 46 | + SendPacket.data[0] = OpCode; |
| 47 | + |
| 48 | + return LocoNet.send(&SendPacket); |
| 49 | +} |
| 50 | + |
| 51 | +LN_STATUS LocoNetClassKS::sendSwitchState(uint16_t ui16_swAdr, boolean b_swOn, boolean b_swDirection, uint8_t ui8_OPC) |
| 52 | +{ |
| 53 | + if (!ui16_swAdr || (ui16_swAdr > 2048) || (ui8_OPC < OPC_SW_REQ) || (ui8_OPC > OPC_INPUT_REP)) |
| 54 | + return LN_UNKNOWN_ERROR; |
| 55 | + |
| 56 | + switch (ui8_OPC) |
| 57 | + { |
| 58 | + case OPC_SW_REQ: // B0 |
| 59 | + return LocoNet.requestSwitch(ui16_swAdr, b_swOn, b_swDirection); |
| 60 | + case OPC_SW_REP: // B1 |
| 61 | + return sendSwitchReport(ui16_swAdr, b_swOn, b_swDirection); |
| 62 | + case OPC_INPUT_REP: // B2 |
| 63 | + return sendReportSensor(ui16_swAdr, b_swOn); |
| 64 | + } |
| 65 | + return LN_UNKNOWN_ERROR; |
| 66 | +} |
| 67 | + |
| 68 | +LN_STATUS LocoNetClassKS::sendSwitchReport(uint16_t Address, uint8_t Output, uint8_t Direction) |
| 69 | +{ // B1 |
| 70 | + uint8_t AddrH = (--Address >> 7) & 0x0F; |
| 71 | + uint8_t AddrL = Address & 0x7F; |
| 72 | + |
| 73 | + if (Output) |
| 74 | + AddrH |= OPC_SW_REQ_OUT; |
| 75 | + |
| 76 | + if (Direction) |
| 77 | + AddrH |= OPC_SW_REQ_DIR; |
| 78 | + |
| 79 | + return LocoNet.send(OPC_SW_REP, AddrL, AddrH); |
| 80 | +} |
| 81 | + |
| 82 | +LN_STATUS LocoNetClassKS::sendReportSensor(uint16_t Address, uint8_t State) |
| 83 | +{ // B2 |
| 84 | + byte AddrH = ((--Address >> 8) & 0x0F); |
| 85 | + byte AddrL = (Address >> 1) & 0x7F; |
| 86 | + if (Address % 2) |
| 87 | + AddrH |= OPC_INPUT_REP_SW; |
| 88 | + |
| 89 | + if (State) |
| 90 | + AddrH |= OPC_INPUT_REP_HI; |
| 91 | + |
| 92 | + return LocoNet.send(OPC_INPUT_REP, AddrL, AddrH); |
| 93 | +} |
| 94 | +//=== end LocoNetClassKS === |
| 95 | + |
| 96 | +//=== LocoNetFastClockClassKS === |
| 97 | +/* this class is copied from original LocoNetFastClockClass in file loconet.cpp (Version 1.1.13) |
| 98 | +* modification is made for new flag "FC_FLAG_NOTIFY_JMRI" (see also commenst "added") |
| 99 | +*/ |
| 100 | +#define FC_FLAG_DCS100_COMPATIBLE_SPEED 0x01 |
| 101 | +#define FC_FLAG_MINUTE_ROLLOVER_SYNC 0x02 |
| 102 | +#define FC_FLAG_NOTIFY_FRAC_MINS_TICK 0x04 |
| 103 | +#define FC_FLAG_NOTIFY_JMRI 0x08 // new for JMRI |
| 104 | +#define FC_FRAC_MIN_BASE 0x3FFF |
| 105 | +#define FC_FRAC_RESET_HIGH 0x78 |
| 106 | +#define FC_FRAC_RESET_LOW 0x6D |
| 107 | +#define FC_TIMER_TICKS 65 // 65ms ticks |
| 108 | +#define FC_TIMER_TICKS_REQ 250 // 250ms waiting for Response to FC Req |
| 109 | + |
| 110 | +void LocoNetFastClockClassKS::init(uint8_t DCS100CompatibleSpeed, uint8_t CorrectDCS100Clock, uint8_t NotifyFracMin) |
| 111 | +{ |
| 112 | + fcState = FC_ST_IDLE; |
| 113 | + |
| 114 | + fcFlags = 0; |
| 115 | + if (DCS100CompatibleSpeed) |
| 116 | + fcFlags |= FC_FLAG_DCS100_COMPATIBLE_SPEED; |
| 117 | + |
| 118 | + if (CorrectDCS100Clock) |
| 119 | + fcFlags |= FC_FLAG_MINUTE_ROLLOVER_SYNC; |
| 120 | + |
| 121 | + if (NotifyFracMin) |
| 122 | + fcFlags |= FC_FLAG_NOTIFY_FRAC_MINS_TICK; |
| 123 | +} |
| 124 | + |
| 125 | +// new method |
| 126 | +void LocoNetFastClockClassKS::initJMRI(uint8_t IsJMRI) |
| 127 | +{ |
| 128 | + fcLastfrac_minsh = 0; |
| 129 | + if (IsJMRI) |
| 130 | + fcFlags |= FC_FLAG_NOTIFY_JMRI; |
| 131 | +} |
| 132 | + |
| 133 | +void LocoNetFastClockClassKS::poll(void) |
| 134 | +{ |
| 135 | + LocoNet.send(OPC_RQ_SL_DATA, FC_SLOT, 0); |
| 136 | +} |
| 137 | + |
| 138 | +void LocoNetFastClockClassKS::doNotify(uint8_t Sync) |
| 139 | +{ |
| 140 | + if (notifyFastClock) |
| 141 | + notifyFastClock(fcSlotData.clk_rate, fcSlotData.days, |
| 142 | + (fcSlotData.hours_24 >= (128 - 24)) ? fcSlotData.hours_24 - (128 - 24) : fcSlotData.hours_24 % 24, |
| 143 | + fcSlotData.mins_60 - (127 - 60), Sync); |
| 144 | +} |
| 145 | + |
| 146 | +// changed method |
| 147 | +void LocoNetFastClockClassKS::processMessage(lnMsg* LnPacket) |
| 148 | +{ |
| 149 | + if ((LnPacket->fc.slot == FC_SLOT) && ((LnPacket->fc.command == OPC_WR_SL_DATA) || (LnPacket->fc.command == OPC_SL_RD_DATA))) |
| 150 | + { |
| 151 | + if ((LnPacket->fc.clk_cntrl & 0x40) |
| 152 | +// +++ added because JMRI is sending 'fc.clk_cntrl = 0x00' (Bit 6 not set) |
| 153 | + || (fcFlags & FC_FLAG_NOTIFY_JMRI)) |
| 154 | +// added end |
| 155 | + { |
| 156 | + if (fcState >= FC_ST_REQ_TIME) |
| 157 | + { |
| 158 | + memcpy(&fcSlotData, &LnPacket->fc, sizeof(fastClockMsg)); |
| 159 | +// ++ added for JMRI and use in "process66msActions" |
| 160 | + fcLastfrac_minsh = fcSlotData.frac_minsh; |
| 161 | +// added end |
| 162 | + doNotify(1); |
| 163 | + |
| 164 | + if (notifyFastClockFracMins && fcFlags & FC_FLAG_NOTIFY_FRAC_MINS_TICK) |
| 165 | + notifyFastClockFracMins(FC_FRAC_MIN_BASE - ((fcSlotData.frac_minsh << 7) + fcSlotData.frac_minsl)); |
| 166 | + |
| 167 | + fcState = FC_ST_READY; |
| 168 | + } |
| 169 | + } |
| 170 | + else |
| 171 | + fcState = FC_ST_DISABLED; |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +// changed method |
| 176 | +void LocoNetFastClockClassKS::process66msActions(void) |
| 177 | +{ |
| 178 | + // If we are all initialised and ready then increment accumulators |
| 179 | + if (fcState == FC_ST_READY) |
| 180 | + { |
| 181 | + fcSlotData.frac_minsl += fcSlotData.clk_rate; |
| 182 | + if (fcSlotData.frac_minsl & 0x80) |
| 183 | + { |
| 184 | + fcSlotData.frac_minsl &= ~0x80; |
| 185 | + |
| 186 | + fcSlotData.frac_minsh++; |
| 187 | + if ((fcSlotData.frac_minsh & 0x80) |
| 188 | +// +++ added because JMRI starts with 'fcSlotData.frac_minsh = 3' instead of FC_FRAC_RESET_HIGH (0x78) |
| 189 | +// therefore fcSlotData.frac_minsh is always less than 0x80 |
| 190 | +// and fcSlotData.frac_minsh comes in maximum up to 10, independant from fcSlotdata.clk_rate |
| 191 | + || ((fcFlags & FC_FLAG_NOTIFY_JMRI) && |
| 192 | + (fcSlotData.frac_minsh > (fcLastfrac_minsh + 6)) && |
| 193 | + (fcSlotData.frac_minsh < (FC_FRAC_RESET_HIGH + (fcFlags & FC_FLAG_DCS100_COMPATIBLE_SPEED))) |
| 194 | + ) |
| 195 | + ) |
| 196 | +// added end |
| 197 | + { |
| 198 | + // For the next cycle prime the fraction of a minute accumulators |
| 199 | + fcSlotData.frac_minsl = FC_FRAC_RESET_LOW; |
| 200 | + |
| 201 | + // If we are in FC_FLAG_DCS100_COMPATIBLE_SPEED mode we need to run faster |
| 202 | + // by reducong the FRAC_MINS duration count by 128 |
| 203 | + fcSlotData.frac_minsh = FC_FRAC_RESET_HIGH + (fcFlags & FC_FLAG_DCS100_COMPATIBLE_SPEED); |
| 204 | + |
| 205 | + fcSlotData.mins_60++; |
| 206 | + if (fcSlotData.mins_60 >= 0x7F) |
| 207 | + { |
| 208 | + fcSlotData.mins_60 = 127 - 60; |
| 209 | + |
| 210 | + fcSlotData.hours_24++; |
| 211 | + if (fcSlotData.hours_24 & 0x80) |
| 212 | + { |
| 213 | + fcSlotData.hours_24 = 128 - 24; |
| 214 | + |
| 215 | + fcSlotData.days++; |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + // We either send a message out onto the LocoNet to change the time, |
| 220 | + // which we will also see and act on or just notify our user |
| 221 | + // function that our internal time has changed. |
| 222 | + if (fcFlags & FC_FLAG_MINUTE_ROLLOVER_SYNC) |
| 223 | + { |
| 224 | + fcSlotData.command = OPC_WR_SL_DATA; |
| 225 | + LocoNet.send((lnMsg*)&fcSlotData); |
| 226 | + } |
| 227 | + else |
| 228 | + doNotify(0); |
| 229 | + } |
| 230 | + } |
| 231 | + |
| 232 | + if (notifyFastClockFracMins && (fcFlags & FC_FLAG_NOTIFY_FRAC_MINS_TICK)) |
| 233 | + notifyFastClockFracMins(FC_FRAC_MIN_BASE - ((fcSlotData.frac_minsh << 7) + fcSlotData.frac_minsl)); |
| 234 | + } |
| 235 | + |
| 236 | + if (fcState == FC_ST_IDLE) |
| 237 | + { |
| 238 | + LocoNet.send(OPC_RQ_SL_DATA, FC_SLOT, 0); |
| 239 | + fcState = FC_ST_REQ_TIME; |
| 240 | + } |
| 241 | +} |
| 242 | +//=== end LocoNetFastClockClassKS === |
0 commit comments