REM Slave Network Stack V.II(a.1) REM ======================== REM "INTELLIGENT" NETWORK-ROAMING SLAVE NODE for M2 & X2 newer parts, REM with following implementation details: REM - PICAXE-08M2 implementation, REM - choice for 2-wire nonpolarized network connection REM - selectable frequency 256 KHz - 32 MHz, including master node freq, REM - Tested & Validated on testplatform 2018 (hardware improved) REM Dataframe format used here: 4+ bytes: REM [MessageDescriptorByte, IDcalledProcess, IDcallerInfo, dataByte_1, ... , dataByte_n] REM Note that two dummy user processes have been implemented as well REM (userDefinedProcess and userDefinedSendingProcess) REM which can be extended with functionality REM -------------------------------------------- REM STATEMENT OF COPYRIGHT REM Copyright (C) 2018, Jurjen Kranenborg REM This work is licensed under the Creative Commons REM Attribution-Noncommercial-Share Alike 4.0. REM To view a copy of this license, visit REM http://creativecommons.org/licenses/by-nc-sa/4.0/deed.se REM To view a translation of this license in English, visit: REM http://creativecommons.org/licenses/by-nc-sa/4.0 REM or send a letter to REM Creative Commons, REM 171 Second Street, Suite 300, REM San Francisco, California, 94105, USA. REM In short form (me, my = holder of copyright): REM * You may use, adapt, and distribute this work. REM * You must give credit to me when using this code in your application. REM * If you use my code commercially, I want my share. REM * You may share your adapted code (now with you as holder of copyright) REM on the condition that the same license conditions are applied. REM In order to acknowledge Jurjen Kranenborg (i.e. not the copyright holder of any derived work) REM as the architect of the original network concepts, REM please include the following statement in all derived works (code, webpages, docs, etc.): REM The network architecture of this work was originally developed by Jurjen Kranenborg. REM -------------------------------------------- REM ********************************************************************* REM User Programmable Area 1: REM - PICAXE chip definition REM - Network type definition REM - I/O pin selection for network connection REM - Interrupt definitions REM - Info on usage of registers and process IDs REM ************************* REM This application example was tailored to the PICAXE-08M2 #picaxe 08m2 REM Definition of network input & output communication pins REM Note: The default configuration here using C.3 and C.4 frees up the flexibele pins C.0, C.1 and C.2 REM for user interfacing to sensors, actuators etc. using I2C, SPI, DAQ, the SR-latch etc. SYMBOL netSerialIN = C.3 SYMBOL netSerialINPUT = pinC.3 SYMBOL netSerialOUT = C.4 REM: User-defined LED (may be removed) SYMBOL signallingLED = C.0 REM interrupt definitions (change INTmask as well if NetSerialIn is changed) SYMBOL INTmaskENABLED = %00001000 'Definition of which pins are to generate an interrupt SYMBOL INTmaskDISABLED = %00000000 SYMBOL INTinputs = %00000000 'Pin Logic levels to generate interrupt REM NOTE: registers b0 - b12 (w0 - w6) freely available on a M2 REM registers b13 - b27 in use by network stack REM Allowable IDs for user processes: 10 - 254 REM ******************************* REM END of User Programmable Area 1 REM (see also Area 2 further below) REM ********************************************************************* REM Include some user-dependent settings that are common to both master and slave nodes #INCLUDE "SP-II_CommonSettings.basinc" REM Registers used as resources by the Network Stack SYMBOL commandIndex = b23 SYMBOL IDsendingProcess = b17 SYMBOL IDdestinationProcess = b24 SYMBOL IDcalledProcess = b13 SYMBOL IDcallerInfo = b25 SYMBOL dataByteH = b26 REM Scratchpad byte 1 SYMBOL dataByteL = b27 REM Scratchpad byte 2 SYMBOL rises = w10 REM (w10=b21,b20) SYMBOL IDcandidateSendingProcess=b22 SYMBOL messageDescriptor = b16 SYMBOL numberOfDataBytes=b18 SYMBOL stringAddress = b19 SYMBOL character = b15 SYMBOL loopcounter = b14 REM Definition of default index for process selection SYMBOL noProcessIndex = $FF REM Boolean constants definition: SYMBOL FALSE = 0 SYMBOL TRUE = 1 REM Scratchpad Area (RAMfile) definitions for input messages REM Currently only using single byte IDs REM The Message Descriptor Byte is currently defined bitwise as follows REM - Bit 7: Message Valid (1) / Invalid (0) REM - Bit 6-5: 0 *** Reserved for future use *** REM - Bit 4-0: Number of databytes in message (min 1 - max 32) SYMBOL MaxMessageDatabytes = 32 SYMBOL MaxMessageSize = MaxMessageDatabytes + 5 SYMBOL RAMfile_InMsg_Start = 28 REM Start above b0-b27 register file SYMBOL RAMfile_InMsg_FrameDescriptor = RAMfile_InMsg_Start SYMBOL RAMfile_InMsg_IDcalledProcess = RAMfile_InMsg_FrameDescriptor + 1 SYMBOL RAMfile_InMsg_IDcallerInfo = RAMfile_InMsg_IDcalledProcess + 1 SYMBOL RAMfile_InMsg_FirstDataByte = RAMfile_InMsg_IDcallerInfo + 1 SYMBOL RAMfile_InMsg_SecondDataByte = RAMfile_InMsg_FirstDataByte + 1 SYMBOL RAMfile_InMsg_LastDataByte = RAMfile_InMsg_Start + MaxMessageSize REM RAM MTLs (Message Transfer Locations) for storing message frame information that needs REM to be sent to the network as a message later on REM Each time the master node sends a timeslot message, the network stack will inspect the contents REM of RAMIDsendingProcessInfo (i.e. the ID of the process that wants this message to be transmitted) REM and if this ID and the timeslot ID match the message will be composed from the RAM locations REM and put on the network. No user intervention is required. After the message has been transmitted REM the network software will write IDnoProcess is written to RAMIDsendingProcessInfo, indicating to REM slave processes on this node that a new message may be composed. SYMBOL RAMfile_OutMsg_Start = RAMfile_InMsg_LastDataByte + 1 SYMBOL RAMfile_OutMsg_FrameDescriptor = RAMfile_OutMsg_Start SYMBOL RAMfile_OutMsg_IDcalledProcess = RAMfile_OutMsg_FrameDescriptor + 1 SYMBOL RAMfile_OutMsg_IDcallerInfo = RAMfile_OutMsg_IDcalledProcess + 1 SYMBOL RAMfile_OutMsg_FirstDataByte = RAMfile_OutMsg_IDcallerInfo + 1 SYMBOL RAMfile_OutMsg_SecondDataByte = RAMfile_OutMsg_FirstDataByte + 1 SYMBOL RAMfile_OutMsg_LastDataByte = RAMfile_OutMsg_Start + MaxMessageSize REM RAM location for storing network configuration info as sent by the master node at start-up SYMBOL RAMnetworkFrequency = RAMfile_OutMsg_LastDataByte + 1 'storage of network frequency SYMBOL RAMnodeFrequencyCalibrFact = RAMnetworkFrequency + 1 'Calibration factor (not yet used) SYMBOL RAMnetworkStatus = RAMnodeFrequencyCalibrFact + 1 'State of network: ready if all sending processes have been registered and thus the network becomes operational REM Macros to overcome limitations of the BINTTOASCII format (use of @bptr type variables) REM See also: https://picaxeforum.co.uk/threads/bintoascii-implementation-change.30159/ #Macro B_BinToAscii(bVar,n1,n2,n3) n1 = bVar / 100 // 10 + "0" n2 = bVar / 10 // 10 + "0" n3 = bVar // 10 + "0" #EndMacro #Macro W_BinToAscii(wVar,n1,n2,n3,n4,n5) n1 = wVar / 10000 // 10 + "0" n2 = wVar / 1000 // 10 + "0" n3 = wVar / 100 // 10 + "0" n4 = wVar / 10 // 10 + "0" n5 = wVar // 10 + "0" #EndMacro REM =============================== REM START of Main Program execution REM =============================== REM network is not ready yet, need to wait for REM: - Network frequncy to be defined REM: - Roaming for sending processes has completed POKE RAMnetworkFrequency, FREQ_UNDEFINED POKE RAMnetworkStatus, NETWORK_NOT_READY REM --------------------------- REM Network initialization code REM Do not change this part! REM --------------------------- #ifdef simpleDiodeMixing REM Put line high as default HIGH netSerialOut #else REM Guarantee that the output mosfet is closed by setting as output + Low! LOW netSerialOut #endif REM Now set default (and accurate) operating frequency SETFREQ m4 REM Calibfreq 0 'See also: http://www.picaxeforum.co.uk/showthread.php?19603-Automatic-frequency-calibration-finetuning-of-internal-PICAXE-resonators for adaption if needed to improve network stability REM Allow local capacitor to charge up for some time REM note that the master node must have a longer startup time Pause t_startup REM Now the node is almost ready for network initialization CALL reSynch REM First await the network information as sent by the master node (clock frequency to be used) REM Since this is the first message received, the network status will be available in dataByteL REM (as well as stored in RAMnetworkConfigurationByte for later use) DO PEEK RAMnetworkFrequency, dataByteH LOOP WHILE dataByteH = FREQ_UNDEFINED REM Now wait until Master roaming for sending processes has completed DO PEEK RAMnetworkStatus, dataByteH LOOP WHILE dataByteH = NETWORK_NOT_READY REM --------------------------- REM End of network initialization code, REM The network is now up and running, and the main program body execution can start REM --------------------------- REM ======================================================= REM USER PROGRAMMABLE AREA 2: PROGRAM BODY REM Main location for processes to do something useful here REM If they have data they whish to send to other processes REM they should set the RAMfile message transfer locations, REM the network stack will inspect this information during REM every interrupt to see if action is needed. REM ======================================================= REM User-defined process IDs: SYMBOL IDuserDefinedSendingProcess = 20 REM This is the ID of our sending process, it gets registered and gets timeslots REM Obligatory administrative definitions (See User Programming Guide): REM Highest user ID available on this slave SYMBOL highestSlaveID = 20 REM ==================== REM User messages to be sent REM Note: string length (in databytes) of first message = 15 (12 text chars + CR + LF + ETX) REM whereof 14 are put in the network message (ETX is never included as terminator) SYMBOL Address_HelloWorld = 0 SYMBOL Address_SecondMessage = 15 SYMBOL sendingProcessIDinfo = b0 EEPROM Address_HelloWorld, ("Hello World!",CR,LF,ETX) EEPROM Address_SecondMessage, ("Process ID ",NUL,NUL,NUL," is calling!",CR,LF,ETX) DO REM "Dummy" message sent for network testing purposes REM Note that this is the main body part of REM userDefinedSendingprocess REM Sit and wait for indication that message has been sent CALL checkReadyToPrepareNewMessage REM Compose a string message that is to be sent to a PC via SERTXD on the Master node for testing LET stringAddress = Address_HelloWorld POKE RAMfile_OutMsg_IDcalledProcess, IDsendStringSERTXD_Master POKE RAMfile_OutMsg_IDcallerInfo ,IDuserDefinedSendingProcess CALL constructStringMessage REM Wait for string message is sent before a new message REM can be constructed CALL checkReadyToPrepareNewMessage REM Create a message that informs on which process created REM the Hello World message (i.e. include ID number in the REM text that is to be sent, for which we use the B_BINTOASCII REM macro as a general solution) REM First define area in output message where ID is to be added REM (depends on the format of the second message, in particular the REM position of the NUL characters) LET bptr = RAMfile_OutMsg_FirstDataByte + 11 REM Use B_BINTOASCII macro command (generic approach), the command requires REM the number information to be in a register LET sendingProcessIDinfo = IDuserDefinedSendingProcess B_BINTOASCII(sendingProcessIDinfo, @bptrinc, @bptrinc, @bptr) REM Now prepare the whole string LET stringAddress = Address_SecondMessage POKE RAMfile_OutMsg_IDcalledProcess, IDsendStringSERTXD_Master POKE RAMfile_OutMsg_IDcallerInfo ,IDuserDefinedSendingProcess CALL constructStringMessage LOOP END REM ======================== REM END OF USER PROGRAM BODY REM ======================== REM -------------------------------- REM Slave Network Stack subroutines: REM -------------------------------- Interrupt: PAUSEUS 1 REM Mitigation for interrupt start issue for 18M2 REM Network message frame reception and handling routine REM ==================================================== REM Line has been brought low by master or slave for interrupt, REM then line brought high again. REM Read the message and determine if REM the process addressed is actually present on this slave LET commandIndex = noProcessIndex LET bptr = RAMfile_InMsg_Start REM First get the message descriptor byte SERIN netSerialIN, T4800_4, messageDescriptor LET @bptrinc = messageDescriptor REM get number of databytes contained in the message LET numberOfDataBytes = messageDescriptor AND %00011111 + 1 REM Get the Dst and Src ID's SERIN netSerialIN, T4800_4, IDcalledProcess SERIN netSerialIN, T4800_4, IDcallerInfo LET @bptrinc = IDcalledProcess LET @bptrinc = IDcallerInfo REM now get the required number of databytes (1-32) DO WHILE numberOfDataBytes > 0 REM get a databyte SERIN netSerialIN, T4800_4, @bptrinc DEC numberOfDataBytes LOOP LOOKDOWN IDcalledProcess, (IDroamSendingProcess, IDavailableTimeSlot, IDnetworkConfigProcess, IDsendStringSERTXD_Slaves), commandIndex REM Jump to proper command handling routine, and then REM return to normal execution BRANCH commandIndex, (roamSendingProcess, availableTimeSlot, networkConfigProcess, sendStringSERTXD_Slaves) exitFromSession: REM Network issues dealt with, REM prepare to return to normal background execution mode REM with Interrupts ENABLED SETINT INTinputs, INTmaskENABLED RETURN REM --------------------------- REM Interrupt handler routines: REM --------------------------- availableTimeSlot: REM Here a new message gets created and put on the network REM if a valid message is present and if it is requested by some process in the body: PEEK RAMfile_OutMsg_FrameDescriptor, messageDescriptor PEEK RAMfile_OutMsg_IDcallerInfo, IDsendingProcess IF messageDescriptor <> 0 AND IDcallerInfo = IDsendingProcess THEN sendSlaveData: REM Use the granted timeslot to send the message GOSUB takeNetworkSlot ENDIF REM then prepare to normal execution mode SETINT INTinputs, INTmaskENABLED RETURN REM ------------------- takeNetworkSlot: REM Helper subroutine to send a message; may be used by user processes as well #ifdef simpleDiodeMixing REM bring down the transmission line to take the network timeslot LOW netSerialOUT REM take a while for other slaves/master to get into receiving mode PAUSE t_INTslave REM now set the network line high and switch to T4800_4 mode HIGH netSerialOut PAUSE 1 LET bptr = RAMfile_OutMsg_Start REM Send out the Message Descriptor Byte in T4800_4 mode first SEROUT netSerialOUT, T4800_4, (@bptrinc) REM First, wait a while since the recceiving nodes now have to REM extract the number of databytes from the frame descriptors PAUSE 3 REM Send the Dst and Src ID pair SEROUT netSerialOUT, T4800_4, (@bptrinc) PAUSE 2 SEROUT netSerialOUT, T4800_4, (@bptrinc) REM wait a while again PAUSE 2 REM Determine how many databytes have to be sent, from message descriptor PEEK RAMfile_OutMsg_FrameDescriptor, messageDescriptor LET numberOfDataBytes = messageDescriptor AND %00011111 + 1 REM now send the required number of databytes (1-32) FOR loopcounter = 1 to numberofDataBytes REM Send a databyte SEROUT netSerialOUT, T4800_4, (@bptrinc) PAUSE 2 NEXT REM make the network line high again (3-stating) INPUT netSerialOUT #else REM Set low the transmission line to take the network timeslot HIGH netSerialOUT REM take a while for other slaves/master to get into receiving mode PAUSE t_INTslave REM now bring the network line high and switch to T4800_4 mode LOW netSerialOut PAUSE 1 LET bptr = RAMfile_OutMsg_Start REM Send out the Frame Descriptor Byte in N4800_4 mode SEROUT netSerialOUT, N4800_4, (@bptrinc) #ifdef APP ActiveHigh_Slave #endif REM First, wait a while since the receiving nodes now have to REM extract the number of databytes from the frame descriptors PAUSE 3 REM Send the Dst and Src ID pair SEROUT netSerialOUT, N4800_4, (@bptrinc) #ifdef APP ActiveHigh_Slave #endif Pause 2 SEROUT netSerialOUT, N4800_4, (@bptrinc) #ifdef APP ActiveHigh_Slave #endif REM wait a while again PAUSE 2 REM Determine how many databytes have to be sent, from message descriptor PEEK RAMfile_OutMsg_FrameDescriptor, messageDescriptor LET numberOfDataBytes = messageDescriptor AND %00011111 + 1 REM now sent the required number of databytes (1-32) FOR loopcounter = 1 to numberofDataBytes REM Send a databyte SEROUT netSerialOUT, N4800_4, (@bptrinc) #ifdef APP ActiveHigh_Slave #endif PAUSE 2 NEXT REM make the network line high again LOW netSerialOUT #endif REM All has been sent, so allow other processes to send as well REM so invalidate the message just sent. POKE RAMfile_OutMsg_FrameDescriptor, %00000000 PULSOUT signallingLED, 100 RETURN REM ------------------- roamSendingProcess: REM This process is called by the master node at power-up to see REM whether this slave node implements one or more sending processes REM REM If so, this subroutine returns the ID of the sending process REM to the registerSendingProcess at the master. REM REM The information in the message frame to be sent (see takeNetworkSlot) is defined as follows: REM - IDcalledProcess is the ID of the registering process at the master node REM - IDcallerInfo is the ID of the current process (IDroamSendingProces) REM that requests for timeslots REM - First databyte indicates the largest ID of all IDs implemented at the node REM (sending as well as listening-only processes) REM - Second databyte indicates the ID of the proces(ses) that request a timeslot REM Note that a slave may implement several sending processes REM so this subroutine should deliver all related IDs REM This can be realized simply by extending the next line through ORing with multiple processes REM (IF IDcallerInfo = IDuserproc1 OR IDcallerInfo = IDuserproc2 ... THEN ) IF IDcallerInfo = IDuserDefinedSendingProcess THEN POKE RAMfile_OutMsg_IDcalledProcess, IDregisterSendingProcess POKE RAMfile_OutMsg_IDcallerInfo, IDroamSendingProcess POKE RAMfile_OutMsg_FirstDataByte, highestSlaveID POKE RAMfile_OutMsg_SecondDataByte, IDcallerInfo POKE RAMfile_OutMsg_FrameDescriptor, %10000001 REM two-byte datapacket REM Now use the same code as in availableTimeSlot to send the message frame GOSUB takeNetworkSlot ENDIF REM Now check if roaming is finished, so that the network status can be set to ready PEEK RAMfile_InMsg_SecondDataByte, dataByteL IF IDcallerInfo = dataByteL THEN POKE RAMnetworkStatus, NETWORK_READY ENDIF SETINT INTinputs, INTmaskENABLED RETURN REM ------------------- networkConfigProcess: REM Set the slave node clock frequency as indicated by the master node REM at network start-up. PEEK RAMfile_InMsg_FirstDataByte, dataByteH REM Firstly, store the Network frequency byte POKE RAMnetworkFrequency, dataByteH REM Now set the node frequency equal to the agreed network frequency CALL ApplyNetworkFrequency SETINT INTinputs, INTmaskENABLED RETURN REM ------------------- REM Synchronization routines for slow instructions / code parts REM which should not be interrupted. REM See documentation for detailed explanation deSynch: SETINT INTinputs, INTmaskDISABLED RETURN reSynch: IF netSerialINPUT = 0 THEN waitAWhile COUNT netSerialIN, t_INTslave , rises IF rises > 0 THEN waitAWhile SETINT INTinputs, INTmaskENABLED RETURN waitAWhile: PAUSE t_INTslave GOTO reSynch REM ------------------------ checkReadyToPrepareNewMessage: REM Monitor the RAM message transfer locations to see whether previous information still waiting to be sent REM has actually been transmitted as a message to the network (the RAM location RAMIDsendingProcessInfo then REM contains IDnoProcess ("empty" process) to indicate that new message information can be stored). REM If not, wait until the previous message has been sent REM REM An important advantage of the application of this routine is that while the slave node is REM waiting for a previous message to be sent, it is waiting in an interruptable loop, REM allowing it to catch any network messages that arrive before its own timeslot arrives! DO PEEK RAMfile_OutMsg_FrameDescriptor, messageDescriptor LOOP UNTIL messageDescriptor = 0 RETURN sendStringSERTXD_Slaves: REM Can be used by any slave process on another node to send a string message to a PC REM via this node's programming cable connector. REM The string is contained as data in the message that addresses this process REM NOTE: Best remove this routine from slaves on which this process is not used REM since it consumes a lot of time (for sending the string via SERTXD)! CALL deSynch REM Get the message descriptor of incoming message to determine string length PEEK RAMfile_InMsg_FrameDescriptor, messageDescriptor LET numberOfDataBytes = messageDescriptor AND %00011111 + 1 REM Now point to first databyte of the string in the input message LET bptr = RAMfile_InMsg_FirstDataByte REM send the string via SERTXD: FOR loopCounter = 1 to numberOfDataBytes SERTXD (@bptrinc) NEXT CALL reSynch RETURN REM -------------------------------------- REM Helper routines for user applications: REM -------------------------------------- applyNetworkFrequency: REM Set the node frequency in accordance with the REM network frequency definitions. This routine is needed REM since the M2 and X2 have different base frequencies REM (M2: 4MHz, X2: 8MHz) for the same (network) timing REM First get the network frequency information PEEK RAMnetworkFrequency, dataByteH REM based on the base frequency definition, select the correct CPU frequency SELECT dataByteH CASE FREQ_BASE SETFREQ m4 CASE FREQ_BASEx2 SETFREQ m8 CASE FREQ_BASEx4 SETFREQ m16 CASE FREQ_BASEx8 SETFREQ m32 CASE FREQ_BASEd2 SETFREQ m2 CASE FREQ_BASEd4 SETFREQ m1 CASE FREQ_BASEd8 SETFREQ k500 CASE FREQ_BASEd16 SETFREQ k250 ELSE SETFREQ m4 ENDSELECT RETURN constructStringMessage: REM Helper suboutine for contructing an ETX-terminated string message to be sent, REM using a string specified at an EEPROM (DATA) address REM REM Input to routine: REM 1. Adress of a ETX-terminated string (1-32 bytes) in EEPROM (DATA) memory. REM Its EEPROM address should be specified in variable "stringAddress" REM 2. Destination process ID put in RAM location at address "RAMfile_OutMsg_IDcalledProcess" REM 3. Source process ID put in RAM location at address "RAMfile_OutMsg_IDcallerInfo" REM (NOTE: this process must have registered for a timeslot!) REM Effect: REM 1. Databytes of message to be sent are filled with string characters REM including control characters (but NUL characters as well as REM the terminating ETX character are not included in the message) REM 2. The message descriptor will reflect length of string and has its REM VALID bit set as well REM Consequently the message will be sent as soon as possible. REM REM Example: Hello World! (in the body of a slave node) REM - - - - - - - - - - - (begin example) REM SYMBOL AddressHello = 0 REM EEPROM AddressHello, ("Hello World!",CR,LF,ETX) REM ... REM LET stringAddress = AddressHello REM POKE RAMfile_OutMsg_IDcalledProcess, IDsendStringSERTXD REM POKE RAMfile_OutMsg_IDcallerInfo ,IDuserDefinedSendingProcess REM CALL constructStringMessage REM - - - - - - - - - - - (end example) CALL deSynch LET NumberOfDataBytes = 0 READ stringAddress, character LET bptr = RAMfile_OutMsg_FirstDataByte DO WHILE character <> ETX AND numberOfDataBytes < 32 IF character <> NUL THEN LET @bptr = character INC bptr ELSE INC bptr ENDIF INC stringAddress INC NumberOfDataBytes READ stringAddress, character LOOP DEC NumberOfDataBytes LET MessageDescriptor = numberOfDataBytes OR %10000000 POKE RAMfile_OutMsg_FrameDescriptor, MessageDescriptor CALL reSynch RETURN REM --------------------------------------------- REM END of Slave Network Stack subroutines. REM User subroutines/processes may be added REM here below: REM IMPORTANT: Do not conclude with just "RETURN" but REM first enable interrupts with "SETINT INTinputs, INTmaskENABLED", REM before returning REM --------------------------------------------- userDefinedProcess: REM Dummy listening-only process (Not currently implemented) REM If this process is not needed, then remove this routine here as well as REM the corresponding ID and address in the interrupt routine to save memory space REM [Body here ...] SETINT INTinputs, INTmaskENABLED RETURN userDefinedSendingprocess: REM Parameter-passing part of sending process (Not currently implemented) REM You may use this section to pass parameters to the main part of REM userDefinedSendingprocess in the body REM If this is not needed, then remove this routine here as well as REM the corresponding ID and address in the interrupt routine to save memory space REM [Body here ...] SETINT INTinputs, INTmaskENABLED RETURN REM ================================= REM END of SLAVE NODE implementation REM =================================