REM ===================================================================== REM SerialPower I V4.0 (M2 Part) SLAVE NODE REM ===================================================================== REM *************************************** REM * Application T2: Temperature sensors with network-configurable threshold* REM *************************************** REM REM - Extension of example T1 REM - addition of possibility to define thresholds: REM - below a common threshold the temperature is not reported (timeslot not used by process) REM - This example shows: REM - process "readTemperatures" can be addressed by other processes, REM allowing a common temperature threshold to be specified REM NOTE: This application functions only properly for positive REM temperatures, some code needs to be added for negative temperature applications REM concerning threshold checking due to the DS18B20 temperature format REM Dataframe format used here: 4 bytes: REM [IDcalledProcess, IDcallerInfo, dataByteH, dataByteLow] 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 ************************* REM This application example was tailored to the PICAXE-08M2 #picaxe 08m2 #no_data 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 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 ******************************* REM END of User Programmable Area 1 REM (see also Area 2 further below) REM ********************************************************************* REM Registers used as resources by the Network Stack SYMBOL commandIndex = b9 SYMBOL IDsendingProcess = b9 SYMBOL IDdestinationProcess = b10 SYMBOL IDcalledProcess = b10 SYMBOL IDcallerInfo = b11 SYMBOL dataByteH = b12 SYMBOL dataByteL = b13 SYMBOL NetworkFrequency = b13 SYMBOL rises = b13 SYMBOL IDcandidateSendingProcess=b8 REM Definition of default index for process selection SYMBOL noProcessIndex = $FF REM Boolean constants definition: SYMBOL FALSE = 0 SYMBOL TRUE = 1 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 RAMIDdestinationProcess = $50 'dst process ID SYMBOL RAMIDsendingProcessInfo = RAMIDdestinationProcess + 1 'src process ID SYMBOL RAMdataByteLocationH = RAMIDsendingProcessInfo + 1 'dataByteH location SYMBOL RAMdataByteLocationL = RAMdataByteLocationH + 1 'dataByteL location REM RAM location for storing network configuration info as sent by the master node at start-up SYMBOL RAMnetworkConfigurationByte = RAMdataByteLocationL + 1 'storage of Network Configuration information REM (Optional use) RAM locations that contain the most recent network message received REM These locations are updated automatically after each network message received (i.e. after each interrupt) REM and can be used to synchronize the process(es) in the body program of this slave REM with processes running elsewhere SYMBOL RAMMessage_DestinationProcess = RAMnetworkConfigurationByte + 1 SYMBOL RAMMessage_SendingProcess = RAMMessage_DestinationProcess + 1 SYMBOL RAMMessage_dataByteH = RAMMessage_SendingProcess + 1 SYMBOL RAMMessage_dataByteL = RAMMessage_dataByteH + 1 REM Time intervals for network synchronization REM - in milliseconds REM - assuming 2400 baud data rate: REM - 1 bit takes 1/2400 = 0.42 ms REM - 1 byte takes 8/2400 = 1/300 sec = 3.3 ms = approx. 4 ms REM - 1 message takes 4 bytes -> 4 * 4 ms = 16 ms REM Note that these defs should be the same as those for the master node! SYMBOL t_startUp = 1000 SYMBOL t_byte = 4 SYMBOL t_INTmaster = 2*t_byte SYMBOL t_INTslave = t_INTmaster SYMBOL t_dataFrame = 16 SYMBOL t_respondDelay = 3*t_byte SYMBOL t_testTimeSlot = 3*t_byte SYMBOL t_calc1 = t_respondDelay + t_INTslave SYMBOL t_slaveTimeSlot = t_calc1 + t_dataFrame SYMBOL t_masterTimeSlot = t_INTmaster + t_dataFrame SYMBOL t_comms = t_masterTimeSlot + t_slaveTimeSlot REM Network Status definitions SYMBOL NETWORK_NOT_READY = %00000000 REM Network Frequency definitions SYMBOL FREQ_BASE = %00000001 REM Corressponds to 4MHz (M2) or 8 MHz (X2) but same base timing SYMBOL FREQ_BASEx2 = %00000010 SYMBOL FREQ_BASEx4 = %00000011 SYMBOL FREQ_BASEx8 = %00000100 REM Freqs lower than Base (d# = divide Base by #): SYMBOL FREQ_BASEd2 = %00001000 SYMBOL FREQ_BASEd4 = %00001001 SYMBOL FREQ_BASEd8 = %00001010 SYMBOL FREQ_BASEd16 = %00001011 SYMBOL FREQ_BASEd32 = %00001100 REM not supported on M2 series since 128 KHz not available REM Process Identifiers of network stack related processes: SYMBOL IDallProcesses = 255 SYMBOL IDnoProcess = 0 SYMBOL IDroamSendingProcess = 1 'Slave process SYMBOL IDregisterSendingProcess = 2 SYMBOL IDunRegisterSendingProcess = 3 SYMBOL IDavailableTimeSlot = 4 'Slave process SYMBOL IDroamPlugNplayNode = 5 'Slave process (plugNplay node) SYMBOL IDnetworkManager = 6 'Slave process (special external Network Manager Node; not currently implemented) SYMBOL IDflashLEDmasterNode = 7 SYMBOL IDnetworkConfigProcess = 8 'Allows to configure slaves at start-up (clock speed etc.) SYMBOL IDspecialProcess9 = 9 'Reserved ID for special system purposes (do not use for user processes) REM Data on allowed user-defined process IDs REM (used for roaming sending slave processes) REM Note that ID=255 is reserved (IDallProcesses) and should NOT be used as a user process! SYMBOL firstUserProcessID = 10 'First user-available process ID (max 254) SYMBOL lastUserProcessID = 254 'Max 254, ID=255 is reserved REM ********************************************************************* REM User Programmable Area 2: REM - Info on registers available to users REM - Network behaviour REM ************************* REM IMPORTANT: REM - registers b0 - b7 and b14 - b27 freely available REM - registers b8 - b13 in use by network stack: do not use in your program! REM - Allowable IDs for user processes: 10 - 254 REM Specify here if a simple diode-mixing network is used (separate power and serial comms lines) REM IMPORTANT !!!: It is very important to choose the right option here, otherwise the fet REM at the output gets destroyed! REM - - - - - - - - - - - REM #define simpleDiodeMixing REM - - - - - - - - - - - REM Specify here if all slave network messages have to be catched and stored in the assigned RAM locations. REM This feature allows synchronization with other processes. REM - - - - - - - - - - - - - #define storeAllSlaveMessages REM - - - - - - - - - - - - - REM ******************************* REM END of User Programmable Area 2 REM ********************************************************************* REM =============================== REM START of Main Program execution REM =============================== 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 (equal for X2 and M2) 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 operation 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) REM Note that currently this part can be removed if the network is always run at a fixed frequency LET dataByteL = NETWORK_NOT_READY DO LOOP WHILE dataByteL = NETWORK_NOT_READY REM --------------------------- REM End of REM Network initialization code REM --------------------------- REM ======================================================= REM USER PROGRAMMABLE AREA 3: 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 RAM message transfer locations, REM the network stack will inspect this information during REM every interrupt to see if action is needed. REM ======================================================= REM Definitions particular to this temperature reading application REM Define pin connections for DS18B20 temperature sensors SYMBOL TempSensor1 = C.1 SYMBOL TempSensor2 = C.2 REM Register to store network status SYMBOL networkStatus = b0 REM Registers to store temperature values SYMBOL Temperature1 = b1 SYMBOL Temperature2 = b2 SYMBOL commonThresholdTemperature = b3 SYMBOL thresholdDefined = b4 REM Processes that are related with temperature application REM - On this node SYMBOL IDreadTemperatures = 10 REM - On some other node SYMBOL IDdisplayTemperatures = 11 REM Obligatory administrative definitions (See User Programming Guide): REM Highest user ID available on this slave SYMBOL highestSlaveID = 11 REM ---MAIN USER CODE----------- REM Start of main user-defined process: "readTemperatures", with ID = 10 readTemperatures: REM First wait and do nothing until a certain threshold temperature is defined by an external process LET thresholdDefined = FALSE DO LOOP WHILE thresholdDefined = FALSE REM Threshold temperatures have been defined, so now we can start measuring and reporting if needed. DO REM Monitor temperature at two locations using two DS18B20 temperature sensors REM When threshold is reached for any of the two temperatures, report them REM to the network. GOSUB deSynch REM The READTEMP command takes a lot of time to execute, therefore the following commands are inside REM the de-synchronized part of the code READTEMP TempSensor1, Temperature1 READTEMP TempSensor2, Temperature2 REM Now store the measurements such that they can be assembled automatically into a network message REM by the SerialPower network stack. This operation needs to be undisturbed, therfore is de-synched as well IF Temperature1 >= commonThresholdTemperature OR Temperature2 >= commonThresholdTemperature THEN POKE RAMIDdestinationProcess, IDdisplayTemperatures POKE RAMIDsendingProcessInfo, IDreadTemperatures POKE RAMdataByteLocationH, Temperature2 POKE RAMdataByteLocationL, Temperature1 ELSE POKE RAMIDdestinationProcess, IDnoProcess POKE RAMIDsendingProcessInfo, IDreadTemperatures ENDIF GOSUB reSynch REM Wait until previously stored temperatures have been transmitted to the network REM before storing/obtaining the new ones. REM While waiting the node may receive/respond to other network messages GOSUB checkReadyToPrepareNewMessage REM Now wait a full pass by preparing a dummy message (with the latest determined temperatures) REM in order to allow any other process GUARANTEED access to the ReadTemperatures process REM for setting new threshold values. This is an example of how such a guarantee could be met. POKE RAMIDdestinationProcess, IDnoProcess POKE RAMIDsendingProcessInfo, IDreadTemperatures GOSUB checkReadyToPrepareNewMessage LOOP REM ======================== REM END OF USER PROGRAM BODY REM ======================== REM -------------------------------- REM Slave Network Stack subroutines: REM -------------------------------- Interrupt: REM Network message frame reception and handling routine REM ==================================================== REM Line has been brought low by master or slave, REM Read the command frame (4 bytes) and determine if REM the process addressed is actually present on this slave LET commandIndex = noProcessIndex SERIN netSerialIN, N2400_4, IDcalledProcess, IDcallerInfo, dataByteH, dataByteL LOOKDOWN IDcalledProcess, (IDroamSendingProcess, IDavailableTimeSlot, IDnetworkConfigProcess, IDreadTemperatures), commandIndex REM Jump to proper command handling routine, and then REM return to normal execution BRANCH commandIndex, (roamSendingProcess, availableTimeSlot, networkConfigProcess, readTemperatures_Pars) REM If command is not recognised then return to normal execution: REM however, optionally the message just received may be stored so that REM processes in the body may use this information for synchronization with other processes. #ifdef storeAllSlaveMessages then POKE RAMMessage_DestinationProcess, IDcalledProcess POKE RAMMessage_SendingProcess, IDcallerInfo POKE RAMMessage_dataByteH, dataByteH POKE RAMMessage_dataByteL, dataByteL #endif exitFromSession: REM Network issues dealt with, REM prepare to return to normal background execution mode REM with Interrupts ENABLED SETINT INTinputs, INTmaskENABLED RETURN REM ------------------- availableTimeSlot: REM Here is a new message created and put on the network REM if requested by some process in the body: PEEK RAMIDsendingProcessInfo, IDsendingProcess IF IDcallerInfo = IDsendingProcess THEN sendSlaveData: REM first get the proper data bytes PEEK RAMdataByteLocationH, dataByteH PEEK RAMdataByteLocationL, dataByteL PEEK RAMIDdestinationProcess, IDdestinationProcess REM Use the granted timeslot to send the message GOSUB takeNetworkSlot ENDIF REM then prepare to normal execution mode GOTO exitFromSession REM ------------------- takeNetworkSlot: REM Helper subroutine to send a message; may be used by user processes as well REM See the SEROUT command below to see which variables are used for contructing the message #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 ... and send out the message! SEROUT netSerialOUT, N2400_4, (IDdestinationProcess, IDsendingProcess, dataByteH, dataByteL) REM make the network line high again HIGH netSerialOUT #else REM bring down 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 ... and send out the message! SEROUT netSerialOUT, T2400_4, (IDdestinationProcess, IDsendingProcess, dataByteH, dataByteL) REM make the network line high again LOW netSerialOUT #endif REM All has been sent, so allow other processes to send as well POKE RAMIDsendingProcessInfo, IDnoProcess 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 - IDdestinationProcess is the ID of the registering process at the master node REM - IDsendingProcess is the ID of the current process (IDroamSendingProces) REM that requests for timeslots REM - dataByteH indicates the largest ID of all IDs implemented at the node REM (sending as well as listening-only processes) REM - dataByteL indicates the IDs of the processes 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 = IDreadTemperatures THEN IDdestinationProcess = IDregisterSendingProcess IDsendingProcess = IDroamSendingProcess REM In databyteH the user should indicate the maximum ID of all processes REM (sending as well as non-sending) on this slave databyteH = highestSlaveID REM Do not change this line databyteL = IDcallerInfo REM Now use the same code as in availableTimeSlot to send the message frame GOSUB takeNetworkSlot ENDIF GOTO exitFromSession REM ------------------- networkConfigProcess: REM Set the slave node clock frequency as indicated by the master node REM at network start-up. REM Firstly, store the Network Configuration byte POKE RAMnetworkConfigurationByte, dataByteL LET NetworkFrequency = dataByteL CALL applyNetworkFrequency GOTO exitFromSession 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_byte , rises IF rises > 0 THEN waitAWhile GOTO exitFromSession waitAWhile: PAUSE t_byte 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 RAMIDsendingProcessInfo, IDcandidateSendingProcess LOOP UNTIL IDcandidateSendingProcess = IDnoProcess RETURN 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 information on netwrok frequency is assumed to be stored in RAMnetworkFrequency REM based on the base frequency definition, select the correct CPU frequency SELECT NetworkFrequency 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 REM --------------------------------------------- REM END of Slave Network Stack subroutines. REM User subroutines/processes may be added REM here below: REM IMPORTANT: Do not conclude with "RETURN" but REM with "GOTO exitFromSession" instead, as interrupts have to be enabled again first REM --------------------------------------------- readTemperatures_Pars: REM This is the interface (parameters) part of the readTemperatures process REM The common threshold temperature is passed to the body of the readTemperatures process REM It is assumed that a call to the readTemperatures process looks REM as follows (message structure as sent by another process): REM REM [ IDreadTemperatures | -- sending process ID -- | d.c. (dont care) | common Threshold temperature ] REM LET commonThresholdTemperature = dataByteL LET thresholdDefined = TRUE GOTO exitFromSession REM ================================= REM END of SLAVE NODE implementation REM =================================