The idea behind this script was to provide a simple method for nodes to interact with each other.
Nodes are assigned unique descriptive names rather than using obscure and elusive IP addresses.
Local node events such as button presses and sensor triggers can interact with remote nodes by broadcasting specified udp command messages which include the names of any intended target recipients.
All nodes on the subnet receive all message broadcasts, and the onus is on each node to check each incoming message to see if it recognises itself as a named recipient.
Message recipients can be targeted by unique node name, or unique IP address, or a few collectively by group name, or many collectively by system name.
Named recipients compare the incoming command against their local vocabulary of commands, and action any recognised commands as appropriate.
For flexibility, each nodes vocabulary is comprised of a standard set of common system commands, plus some optional default local commands, plus an empty list of user-defined commands which can easily be added to when needed. The different commands listed are simply text strings of words to be searched through, and which have a corresponding named gosub [branch] for actioning any recognised command as appropriate.
For instance: adding a RELAY3 command to a local node is as simple as adding "RELAY3" to the user commands list then adding a corresponding [RELAY3] branch to action the command as wished.
The action response could include sending a udpreply "acknowledgement" back to the sender if wished, which of course is only useful if the sender is looking to receive an acknowledgement.
In the same way that all nodes can be receivers, all nodes can also be transmitters by broadcasting appropriate event-triggered command messages. For instance: various sensor nodes might broadcast RELAY3 target=alarmname when triggered, causing the targeted alarmname node to trigger an alert by switching its local RELAY3.
There are no masters or slaves, because all are originally equal, the only difference is their unique name, and any specialised local user command functionality they've been assigned to respond to.
The optional default local commands are RELAY and RELAY2 plus associated ON, OFF, CYCLEON, CYCLEOFF, TOGGLE parameters. CYCLEON switches the relay ON (after an optional initial delay) then OFF after a pre-determined duration (useful for triggering lights that automatically turn back off). CYCLEOFF turns the relay OFF after an optional delay, then back ON again after a predetermined duration (useful for rebooting routers and computers). By default RELAY(1) uses gpio02 for relay and gpio01 blue onboard led so it can be used on ESP-01's etc. Using gpio01 messes up the serial port though, so I've been using RELAY2 for most of the dev testing... my serial dev board contains an RGB led, so I have assigned RELAY2 to use gpio12 (red) and it's led2 to be gpio15 (green) which very conveniently results in the RGB led changing between Red and Green whenever RELAY2 is toggled. In addition to the RELAY and RELAY2 commands, I've included TOGGLE and TOGGLE2 as convenient dev shorcut versions, and similarly the target=name option can be reduced to t=name (the long version is clearer, but the short version is easier to enter when testing).
The system commands are:
Blinks [optional number of blinks, default is 5] - causes node to blink its ledpin for location purposes.
BlinkIP - blinks out it's the nodes IP address on ledpin for identification (0 is denoted by 10 blinks).
Help or ? - quick syntax help via udp or serial - originally full on-screen help was available, but this was jettisoned to reduce memory madness.
Ping or ! - returns an acknowledgement response. I've only used it for pinging specific nodes using node names or IP addresses, so if you want to target multiple nodes to respond you will probably need to build in a variable delay mechanism to stop them shouting over each other.
Exit - causes the script to end.
Reboot - cause the ESP to restart (needs script autorun enabled to be useful).
By default, the hardware interrupt is used to read the gpio00 flashing button for user input, a short press toggles Relay2, a long press blinks out the local IP address on the local led. All pins are assigned at the top of the script by a single reference, so whenever I say 'by default' I'm also saying that the pin assignments can easily be changed by a single edit.
If you wish to use a local hardware button, or any other 'dry' contact, it will need a pullup resistor, but it may not be required for active sensors such as PIRs which may already be biased high or low.
If you are using a PIR you will probably want to include an initial stabilisation power-up delay of a several seconds before it starts shouting alerts, and you will probably want to include a 'no-repeat' trigger of perhaps a couple of minutes to prevent constant repeated retriggering. Ditto with the receiving node - ie: if you've been alerted but not responded to a mailbox delivery, you probably don't want to be annoyed by it again for another hour.
Which leads on to system automation... because basically every sensor needs an associated non-retrigger delay, but while Esp_Basic is brilliant for some things, multiple timers isn't one of them - so sometimes you may need to consider employing the aid of other platforms such as Arduino or PIC or Pi.
So I have included a facility for softserial2 communications to access the same node resources that are available to udp.
The easy way of visualising the benefit of that is to consider two Esp_Basic nodes on different wifi networks connected by serial cable, acting as a serial bridge. This could give an isolated wifi intranet limited access to the www Internet. or more importantly could give limited controlled remote access to an isolated intranet from the Internet. Only commands that are transmitted and recognised by those 2 nodes will be passed between them via serial. Now consider that instead of the second Esp it might be a supercomputer, because the only requirement for anything connected via softserial2 is that it can understand and respond to any text commands targeted for it, and likewise issue targeted text commands via the connected node if required.
So using softserial2 allows any non Esp_Basic functionality to be easily incorporated into the EasyNet Esp_Basic system... including an Arduino wired ethernet bridge, or RF bridge, or Bluetooth bridge, etc.
***EDIT***
This was also intended to offer a serially connected watchdog facility, whereby a watchdog or standby-mirror can monitor an important node ready to give it a kick up the bum if it falls asleep (hardwired big-boot from watchdog gpio to sleeping-beauty reset), or a secondary mirror could step in to take over using some new user commands such as PROMOTE DEMOTE.
The same access to all available udp functionality is also available via hardware Serial (1), but better just to use hardware Serial purely as a serial port monitor interface, because hardware serial has some limitations, and serial comms is broken by anything else that uses gpio01.
Those comms alternatives are tied together so that any incoming message on udp or hardware Serial or sofserial2 all end up at the {PARSER] for identical processing, the only difference being an additional ims=UDP or Serial1 or Serial2 option which gets automatically added to the message. This ims (input message stream) info can be used to automatically steer outgoing messages via a similar oms (output message stream) option if required.
This is best demonstrated by the Send message window on the browser page, whereby you can enter a message to send, then select how you want to send it from the dropdown. Everything but Local will send out the message by the selected outgoing message stream, whereas 'Local' adds the local node name as the target and treats it as an incoming message to be actioned locally.
Note that the incoming message window above it will display all incoming messages from any of the input message streams irrespective of whether targeted and actioned by that node. The browser incoming (and outgoing) windows were intended for dev and diagnostic purposes, so if not needed for a specific implementation they can be hacked out to free up loads of extra memory if needed.
Likewise with the elapsed timer, the memory guage, and the debug messages window, which are currently all disabled at the top of the script to make it easier for you to save and run.
Big as this post is, there may still be some surprises, cos much has been skipped over or not mentioned.
Developing the script has been like building a pyramid from playing cards... things appear to be progressing ok, then all sorts of quantum weirdness progressively creeps in and makes everything unstable, as you keep prodding and poking trying to pinpoint the ghosts, then eventually everything comes crashing down and you 'twig' that Esp_Basic interpreter has been corrupted again, causing days of wasted effort. That explains all the commented skeletons still laying around, most of which have been ripped out of useful active service during previous bouts of memory madness.
So if you are going to try to do anything with the script I recommend you frequently copy and paste your edits to text file, cos if you lose the contents of your browser EDIT page, you'll be much better off pasting from a clean text file rather than possibly loading corruption from the Esp using the EDIT button.
But before you even start, you should make sure your ESP is formatted and flashed as a 1Mb build even if it has more memory, and because of the many reboots and disconnections, I found that Firefox was much less frustrating than Chrome.
I suspect that most problems creep in during the failed SAVE which causes the Esp to reboot before returning with a misleading SAVED OK confirmation. So I've learnt not to bother trying to RUN after a rebooted SAVE, but to immediately do another SAVE instead, which most of the time will complete ok without rebooting. The more facilities are enabled (memguage, debugmsgs, elapsed time) the more you can expect to experience quantum weirdness, ie: you might do a SAVE then RUN which results in some of the browser displaying incorrectly (eg: message windows or memguage too short), but if you browser Back then RUN again it might appear ok, or perhaps the elapsed timer hasn't started, and if you browser back and RUN again it might run perfectly, or crash and reboot the Esp... there doesn't seem to be any rhyme or reason to it. I'm sure I could include the required fault tolerance if I trimmed things to a minimum, but it wasn't just my own purposes I've been aiming for, and I've lost patience with it for now.
Anyway, I expect a stripped out version should be able to suit your needs, so good luck.
***EDIT***
Quick Testing
Keep in mind that Relay and Relay2 control different gpio pins, so don't be looking for a relay response on your ESP-01 gpio's if you've been sending it RELAY2 by mistake.
Because of the convenient Relay2 Red/Green RGB switching I previously mentioned, I preferred local quick button presses to Toggle Relay2 - so you might want to change this to Relay (1) in the {PRESSED] branch if you prefer.
Lets assume you've got a suitable led indicator on your assigned Relay pin.
By default, the script monitors udp, serial and softserial2 for incoming messages, so you can send commands to it via any of those means, or from it's browser Send window - so there are many ways to check if a node is responding to a recognised command.
For dev purposes the default System name is ALL.
So without even specifying the nodes unique name you can send RELAY TOGGLE target=ALL (its System name) from serial port monitor or cicciocb's udp debugger utility or from the browser Send window or even by quickly pressing the local button (if you've changed the entry in [PRESSED]).
If you want to confirm that correct messages are being issued by a node (eventually event-driven, but perhaps initially manually from the browsers Send window) - you can send messages via the appropriate oms=(output message stream) and monitor what is received by serial monitor or udp debugger as appropriate.
A satisfying test is to send BLINKIP to a specified node, ie: BLINKIP target=Node1.
Another satisfying test is to send it PING t=192.168.4.1 (or whatever its IP address is) from the udb debugger and check that it receives back a udp acknowledgement from the node.
If you wanted to check where all your driveway PIR nodes were located you could send BLINKS 20 target=DRIVEPIRS (their assigned Group name) then spot the twinklers.
Once you've confirmed you can correctly address and command a node to action, and confirmed any required outgoing messaging, it is ready to join a wifi network and be put to use.
memclear
'showtime = "y" 'Un-comment to enable elapsed timer
'debugmode = "y" 'Un-comment to enable debug messages
'memguage = "y" 'Un-comment to enable memory usage guage
if memguage = "y" then
max = ramfree()
min = 1000
memfree = ramfree()
endif
localIP = ip()
pos = instrrev(localIP,".")
netIP = left(localIP,pos)
nodeIP = mid(localIP,pos+1)
title = "EasyNet 3c"
localname = "" 'Populate for convenience, else will be uniquely named "NODE" + last IP address byte
if localname = "" then localname = "Node" & nodeIP
localname = upper(localname) 'Names are deliberately case-insensitive to offer simplest ease of use
groupname = upper("some") 'Optional way of being addressed by sub groups (ie: PIRs, LIGHTs, etc)
systemname = upper("all") 'Optional way of being addressed by large groups or systems (ie: CCTV, HouseAlarm, etc)
udpport = 5001 'Dev default, so change to suit, different ports could be used for different separated systems
udp = "y"
serial1 = "y"
serial2 = "y"
buttonmode = "MultiMode" 'Quick press (<2s) to toggle led, long press (>2s)to blink ip
buttonpin = 0 'Uses gpio00 flashing button by default, change to suit (needs pullup resistor).
buttonoff = 1 'Default button OFF state
ledpin = 1 'Uses onboard gpio01 blue led by default, but be aware that using gpio01 mucks up the serial port
ledoff = 1 'Default led pin off state (allows configuring led pin for active high or active low operation)
pulsepin = 16 'Optional Pulse timer led showing device is alive
relaypin = 2 'Set to the required Relay control pin
relayoff = 0 'Default relay OFF state - Allows for use of active high or active low relay (or alternative output)
led2pin = 15 'Led2 gpio01
led2off = 1 'Default led2 pin off state (allows configuring led pin for active high or active low operation)
relay2pin = 12 'Set to the required Relay2 control pin
relay2off = 0 'Default relay OFF state - Allows for use of active high or active low relay (or alternative output)
if ledoff = 1 then io(po,ledpin,1) else io(po,ledpin,0) 'initialise led to its off state
if relayoff = 1 then io(po,relaypin,1) else io(po,relaypin,0) 'initialise relay to its off state
if led2off = 1 then io(po,led2pin,1) else io(po,led2pin,0) 'initialise led2 to its off state
if relay2off = 1 then io(po,relay2pin,1) else io(po,relay2pin,0) 'initialise relay2 to its off state
ondelay = 0
ondurat = 3000
offdelay = 1000
offdurat = 4000
on2delay = 2000
on2durat = 6000
off2delay = 0
off2durat = 5000
usercmds = "MyCommand " 'Add your own user commands here, remember to add a corresponding uppercase named branch
commoncmds = " Toggle Toggle2 Relay Relay2 " 'Common commands
systemcmds = " Blinks BlinkIP ? Help ! Ping Exit Reboot " 'System commands
vocabulary = usercmds & " " & commoncmds & " " & systemcmds
payload = ""
sentq = ""
qos = 0 'quality of service, number of unacknowledged transmit retries before giving up
qid = "" 'unique id of sentq msgs
ims = "" 'input message stream - local, serial, serial2, udp
oms = "local" 'output message stream - local, serial, serial2, udp
source = "" 'optional source name or address
target = "" 'list of intended subcribers names, group names, and/or IP addresses
words = 0
start = 0 'used by MultiMode button-pressed timer
stop = 0 'used by MultiMode button-pressed timer
numblinks = 5
etime = ""
days = 0
hours = 0
mins = 0
secs = 0
if serial1 = "y" then serialbranch [SERIAL1MSG]
if serial2 = "y" then serial2branch [SERIAL2MSG]
if udp = "y" then
udpbegin udpport
udpbranch [UDPMSG]
endif
interrupt buttonpin, [PRESSED]
[HOME]
cls
'html |<!DOCTYPE html>|
'html |<h2 style="width:100%;color: blue;text-align:center;margin:auto;display:block;">| & title & |</h2>|
html |<h2 style="text-align:center;top-padding:0px;bottom-padding:0px;">| & title & |</h2>|
'html "EasyNet"
html |<hr>|
if memguage = "y" then
html |<table style="width:100%;margin:auto;text-align:center;">|
html | <tr style="top-padding:0px;bottom-padding:0px;">|
html | <td style="text-align:left;color: red;">1000 min </td>|
html | <td style="text-align:center;color:darkblue;">avail |
memfree = ramfree()
textbox memfree
cssid htmlid(), "color:darkmagenta;width:60px;text-align:center;font-size:18px;"
html |</td>|
html | <td style="text-align:right;color:green;">max | & max & |</td>|
html | </tr>|
html |</table>|
meter memfree, min, max ' *************** MEMORY METER *******************
cssid htmlid(), "width:100%; height:10px; color:red; background-color:blue;display:block;"
endif
html "<BR>"
'html |<table style="font-size: 20px; color:rgb(128,128,128);">|
'html | <tr>|
'html | <td>Localname: </td>|
'html | <td style="color: darkblue;"><b>| & localname & |</b></td>|
'html | </tr>|
'html | <tr>|
'html | <td>Groupname: </td>|
'html | <td style="color: darkviolet;">| & groupname & |</td>|
'html | </tr>|
'html | <tr>|
'html | <td>Systemname: </td>|
'html | <td style="color: orange;">| & systemname & |</td>|
'html | </tr>|
'html | <tr>|
'html | <td>IP address: </td>|
'html | <td style="color: darkgreen;">| & localIP & |</td>|
'html | </tr>|
'html | <tr>|
'html | <td>UDP port: </td>|
'html | <td style="color: darkred;">| & udpport & |</td>|
'html | </tr>|
html | Localname: | & localname & |<br>|
html | Groupname: | & groupname & |<br>|
html | Systemname: | & systemname & |<br>|
html | LocalIP: | & localIP & |<br>|
html | UDPport: | & udpport & |<br>|
if showtime = "y" then
html | <tr>|
html | <td>Elapsed time: </td>|
html | <td style="color: grey;">|
tmp = "width:20px;padding:0px;text-align:right;border:0;font-size:18px;color: grey;"
textbox days
cssid htmlid(), tmp
html |d: |
textbox hours
cssid htmlid(), tmp
html |h: |
textbox mins
cssid htmlid(), tmp
html |m: |
textbox secs
cssid htmlid(), tmp
html |s|
html | </td>|
html | </tr>|
html |</table>|
endif
html |<br><br>|
wprint "Local button mode "
dropdown buttonmode, "MultiMode,BlinkFullIP,BlinkShortIP,ToggleButton,FlipSwitch,Sensor,Ack"
wprint " "
wprint " selects different behaviour for local button. MultiMode is button-speed dependent."
html "<BR><BR>"
'html |<table style="width:100%;font-size:20px;color:rgb(128,128,128);">|
'html | <tr style="top-padding:0px;bottom-padding:0px;">|
'html | <td style="width:200px;text-align:left;">Received Message: </td>|
'html | <td style="background-color: GhostWhite;color:gray;;width:50%;">|
html |Incoming message: |
payload = ""
textbox payload
'tmp = "font-size:20px;background-color:cyan;color:gray;width:100%;text-align:center;padding:6px;border-radius:13px;"
tmp = "width:80%;"
cssid htmlid(), tmp
'html | </td>|
'html | <td style="padding-right:20px;width:200px;text-align:right;cursor:pointer;"> </td>|
'html | </tr>|
'html | <tr style="top-padding:0px;bottom-padding:0px;">|
'html | <td style="width:200px;text-align:left;"> </td>|
'html | <td style=""> </td>|
'html | <td style="width:200px;text-align:left;height:40px;"> </td>|
'html | </tr>|
'html | <tr style="top-padding:0px;bottom-padding:0px;">|
'html | <td style="width:200px;text-align:left;">Transmit Message: </td>|
'html | <td style="background-color: white;color:blue;">|
html |<br><br>|
'html |Outgoing message: |
messageout = "Relay Toggle"
button "Send", [SEND]
cssid htmlid(), "padding-left:3px;height:27px;"
oms="Local"
dropdown oms, "Local,Serial1,Serial2,UDP"
cssid htmlid(), "background-color:yellow;padding:2px;width:64px;"
html | |
textbox messageout
'tmp = "font-size:20px;color:darkblue;width:100%;margin:auto;padding:6px;text-align:center;background-color:GhostWhite;"
cssid htmlid(), tmp
'html | </td>|
'html | <td style="width:200px;padding:6px;text-align:left"> |
'html | </td>|
'html | </tr>|
'html |</table>|
html |<br><br>|
if debugmode == "y" then
debugmsg = ""
'html |<table style="width:100%;font-size:20px;color:rgb(128,128,128);position: fixed; bottom: 20;">|
'html | <tr style="top-padding:0px;bottom-padding:0px;">|
'html | <td style="width:200px;text-align:left;">Debug Message: </td>|
'html | <td style="">|
html | Debug messages : |
textbox debugmsg
cssid htmlid(), tmp
'cssid htmlid(),"font-size:20px;background-color:whitesmoke;color:red;width:100%;height:40px;margin:auto;padding:6px;"
html | </td>|
if memguage = "y" then memfree = ramfree()
'html | <td style="padding-right:20px;width:200px;text-align:right;">|
'tmp = "font-size:16px;color:yellow;margin:auto;padding:2px;text-align:center;background-color:red;"
'textbox memfree
'cssid htmlid(), tmp
'html | </td>|
'html | </tr>|
'html |</table>|
endif
'returngui
'wait
html "<BR><BR>"
udpwrite netIP & "255", udpport, "Node " & Localname & " started."
timer 1000, [PULSE]
if memguage = "y" then memfree = ramfree()
wait
[PULSE]
secs=secs + 1
counter1 = counter1 + 1
if io(laststat,pulsepin) = 1 then io(po,pulsepin,0) else io(po,pulsepin,1)
if secs > 59 then
secs = 0
mins = mins + 1
endif
if mins > 59 then
mins = 0
hours = hours + 1
endif
if hours > 23 then
hours = 0
days = days + 1
endif
if secs > 2 then
'retransmit q
endif
'etime = days & "days : " & hours & "hours : " & mins & "mins :" & secs & "secs"
'if secs = 10 then debugmsg = etime
if memguage = "y" then memfree = ramfree()
wait
[SEND]
target = ""
qos = 0
payload = messageout
if oms="Local" then
if instr(payload,"target=") = 0 then payload = trim(payload) & " target=" & localname
gosub [PARSER]
else
gosub [PUBLISH]
endif
if memguage = "y" then memfree = ramfree()
wait
[PUBLISH]
'debugmsg = payload & " oms=" & oms
if instr(payload,"ACK") = 0 and instr(payload,"qid=") = 0 then
'and qos>0 then
' retries = qos
qid = mins & secs
payload = payload & " qid=" & qid'
sentq = sentq & "*" & payload
endif
debugmsg = "[PUBLISH] sentq =" & sentq
'if ledpin = 1 then gpio1reset()
'serialprintln "PUBLISH sentq=" & sentq
'endif
if oms="Local" then
if instr(payload,"target=") = 0 and instr(payload,"t=") = 0 then
target = localname
endif
gosub [PARSER]
else
if oms="Serial1" then
serialprintln payload
else
if oms="Serial2" then
serial2println payload
else
if oms="UDP" then
udpwrite netIP & "255", udpport, payload
endif
endif
endif
endif
if memguage = "y" then memfree = ramfree()
wait
[SERIAL1MSG]
if ledpin = 1 then gpio1reset()
serialinput payload
if asc(right(payload,1)) <33 then payload = left(payload,len(payload)-1)
ims="serial1"
'debugmsg = "[SERIAL1MSG] payload length=" & len(payload)
gosub [PARSER]
if memguage = "y" then memfree = ramfree()
return
[SERIAL2MSG]
serial2input payload
if asc(right(payload,1)) <33 then payload = left(payload,len(payload)-1)
ims="serial2"
gosub [PARSER]
if memguage = "y" then memfree = ramfree()
return
[UDPMSG]
payload = udpread()
ims="udp"
gosub [PARSER]
if memguage = "y" then memfree = ramfree()
return
[PARSER]
if memguage = "y" then memfree = ramfree()
if payload <> "" and asc(right(payload,1)) < 33 then payload = left(payload,len(payload) - 1
payload = trim(payload)
if payload = "" then return
'debugmsg = "[PARSER] payload='"&payload&"', length="&len(payload)&", target="&target&", ims="&ims&", qos="&qos
qos = 0
qid = ""
ack = ""
source = ""
target = ""
words = 0
'serialprintln "Before Payload="&payload
do
words = words + 1
tmp = word(payload,words)
'serialprintln "Words count="&words &", Tmp="&tmp
'serialprintln "Target="&target&", QOS="&qos&", ims="&ims&", oms="&oms&", source="&source
if tmp <> "" then
pos = instr(tmp,"target=")
if pos > 0 then
target = word(tmp,2,"=")
'payload = replace(payload," " & tmp,"")
endif
pos = instr(tmp,"t=")
if pos > 0 then
target = word(tmp,2,"=")
'payload = replace(payload," " & tmp,"")
endif
pos = instr(tmp,"qos=")
if pos > 0 then
qos = word(tmp,2,"=")
'payload = replace(payload," " & tmp,"")
endif
pos = instr(tmp,"qid=")
if pos > 0 then
qid = word(tmp,2,"=")
'payload = replace(payload," " & tmp,"")
endif
pos = instr(tmp,"ims=")
if pos > 0 then
ims = word(tmp,2,"=")
payload = replace(payload," " & tmp,"")
endif
pos = instr(tmp,"source=")
if pos > 0 then
source = word(tmp,2,"=")
'payload = replace(payload," " & tmp,"")
endif
pos = instr(tmp,"ack")
if pos > 0 then
ack = "y"
payload = replace(payload," ack" & tmp,"")
endif
pos = instr(tmp,"ACK")
if pos > 0 then
ack = "y"
payload = replace(payload," ACK" & tmp,"")
endif
pos = instr(tmp,"Ack")
if pos > 0 then
ack = "y"
payload = replace(payload," Ack" & tmp,"")
endif
else
'serialprintln "Blank tmp. Words count="&words &", Tmp="&tmp
words = words - 1
endif
loop while tmp <> ""
'serialprintln "After Payload="&payload
'serialprintln "Words count="&words &", Tmp="&tmp
'serialprintln "Target="&target&", QOS="&qos&", ims="&ims&", oms="&oms&", source="&source
if upper(target)=upper("Local") then target = localname
target = upper(target)
'debugmsg = "[PARSER] payload='"&payload&"', length="&len(payload)&", target="&target&", ims="&ims&", qos="&qos
'debugmsg = "[Parser] payload='"&payload&"', Target='"&target&"', Qos='"&qos&"', ims='"&ims&"', Source='"&source
if instr(target,localname)>0 or instr(target,groupname)>0 or instr(target,systemname)>0 or target=localIP then
if ack = "y" then gosub [ACKIN] else gosub [SUBSCRIBE]
else
debugmsg = "'"&payload & "': Not a target for '" & target &"', Qos='"&qos
endif
'wait
if memguage = "y" then memfree = ramfree()
return
[SUBSCRIBE]
debugmsg = "[SUBSCRIBE] payload='"&payload&"', (target="&target&", ims="&ims&", qos="&qos&", qid="&qid&")"
tmp = upper(word(payload,1))
if instr(upper(vocabulary),tmp) > 0 then
if qos > 0 then gosub [ACKOUT]
gosub "[" & tmp & "]"
else
tmp = "ERROR: Command (" & tmp & ") not recognised in Payload (" & payload & ")"
if debugmode == "x" then html tmp & "<BR>"
if ims=="udp" then udpreply tmp
if ims=="serial1" then serialprintln tmp
if ims=="serial2" then serial2println tmp
endif
if memguage = "y" then memfree = ramfree()
return
[ACKOUT]
if ims=="udp" then udpreply payload & " ACK"
if ims=="serial1" then serialprintln payload & " ACK"
if ims=="serial2" then serial2println payload & " ACK"
if memguage = "y" then memfree = ramfree()
return
[ACKIN]
if ledpin = 1 then gpio1reset()
words = 0
do
words = words + 1
tmp = word(sentq,words,"*")
if instr(tmp, "qid="&qid) <> 0 then
serialprintln "GOTCHA word=" & words & ", tmp="&tmp
debugmsg = "[ACKIN] gotcha " & sentq
end
sentq = replace(sentq, "*"&tmp,"")
endif
loop while tmp <> ""
'serialprintln "ACKOUT sentq=" & sentq
'Target="&target&", QOS="&qos&", ims="&ims&", oms="&oms&", source="&source'clear all vars
debugmsg = "[ACKIN] sentq=" & sentq
if memguage = "y" then memfree = ramfree()
return
[PRESSED]
if io(laststat,buttonpin) = 0 then start = millis() else stop = millis()
if stop > start then
if stop-start < 2000 then gosub [TOGGLE2] else gosub [BLINKIP]
endif
if memguage = "y" then memfree = ramfree()
wait
[PING]
[!]
'if debugmode = "y" then udpreply localname & " PING Declare IP acknowledged "
udpreply localname & " " & localIP & " PING acknowledged"
serialprintln localname & " " & localIP & " PING acknowledged"
html localname & " " & localIP & " acknowledged<BR>"
if memguage = "y" then memfree = ramfree()
return
[BLINKS]
let oldstate = io(laststat,ledpin) 'Remember LED state before blinking
io(po,ledpin,ledoff)
if val(word(payload,2)) > 0 and val(word(payload,2)) < 99 then numblinks = val(word(payload,2))
data = ""
delay 200
for count = 1 to numblinks
if ledoff = 0 then io(po,ledpin,1) else io(po,ledpin,0)
delay 600
if ledoff = 0 then io(po,ledpin,0) else io(po,ledpin,1)
delay 400
next count
delay 2000
io(po,ledpin,oldstate) 'Restore LED state to its prior value
if memguage = "y" then memfree = ramfree()
return
[BLINKIP]
oldstate = io(laststat,ledpin) 'remember original state
blinkon = 200
blinkoff = 400
blinkpause = 1000
blinkgap = 1400
if ledoff = 0 then io(po,ledpin,0) else io(po,ledpin,1) ' turn led off
delay blinkpause
for pos = 1 to len(localIP)
digitchr = mid(localIP,pos,1)
if digitchr = "." then
delay blinkgap
else
if digitchr = "0" then digit = val("10") else digit = val(digitchr)
for count = 1 to digit
if ledoff = 0 then io(po,ledpin,1) else io(po,ledpin,0)
delay blinkon
if ledoff = 0 then io(po,ledpin,0) else io(po,ledpin,1)
delay blinkoff
next count
delay blinkpause
end if
next pos
delay blinkgap
io(po,ledpin,oldstate) 'restore to original state
if memguage = "y" then memfree = ramfree()
return
[EXIT]
end
[REBOOT]
reboot
[HELP]
[?]
'debugmsg = "[HELP] payload='"&payload&"', length="&len(payload)&", target="&target&", ims="&ims&", qos="&qos
if ledpin = 1 then gpio1reset()
blinkpause = 6000
tmp = "Syntax: "
tmp = tmp & "COMMAND [PARAMETERS] [OPTIONS]"
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
endif
tmp = "User commands = "
if usercmds = "" then tmp = tmp & " <i>(no user commands defined)</i>" else tmp = tmp & usercmds
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
endif
tmp = "Common commands = " & commoncmds
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
endif
tmp = "System commands = " & systemcmds
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
endif
tmp = "Parameters are command dependent, eg: Relay On or CycleOn or Toggle etc"
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
endif
tmp = "User Option1: "
tmp = tmp & " t(arget)=(Nodename or IPaddress or Groupname or Systemname)"
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
endif
tmp = "User Option2: "
tmp = tmp & " qos=(number of unacknowledged re-transmits before giving up)"
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
endif
tmp = "Automatic System Options: "
tmp = tmp & " ims=(input msg stream), ACK (received msg acknowledgement)"
if ims="serial1" then serialprintln tmp
if ims="serial2" then serial2println tmp
if ims="udp" then udpreply tmp
if ims="" then
debugmsg = tmp
memfree = ramfree()
delay blinkpause
tmp = "Send ? or HELP via UDP or Serial console to see all Help lines displayed together"
debugmsg = tmp
endif
if memguage = "y" then memfree = ramfree()
return
[TOGGLE]
if io(laststat,ledpin) = 1 then io(po,ledpin,0) else io(po,ledpin,1)
if io(laststat,relaypin) = 1 then io(po,relaypin,0) else io(po,relaypin,1)
return
[RELAY]
'if debugmode <> "silent" then udpreply localname & " Relay acknowledged"
if upper(word(payload,2)) = "ON" then gosub [RELAYON]
if upper(word(payload,2)) = "OFF" then gosub [RELAYOFF]
if upper(word(payload,2)) = "TOGGLE" then gosub [TOGGLE]
if upper(word(payload,2)) = "CYCLEON" then gosub [CYCLEON]
if upper(word(payload,2)) = "CYCLEOFF" then gosub [CYCLEOFF]
if memguage = "y" then memfree = ramfree()
return
[RELAYON]
'if debugmode <> "silent" then udpreply localname & " On acknowledged"
if io(laststat,relaypin) <> relayoff then return
if relayoff = 0 then io(po,relaypin,1) else io(po,relaypin,0)
if ledoff = 0 then io(po,ledpin,1) else io(po,ledpin,0)
if memguage = "y" then memfree = ramfree()
return
[RELAYOFF]
'if debugmode <> "silent" then udpreply localname & " Off acknowledged"
if io(laststat,relaypin) = relayoff then return
if relayoff = 0 then io(po,relaypin,0) else io(po,relaypin,1)
if ledoff = 0 then io(po,ledpin,0) else io(po,ledpin,1)
if memguage = "y" then memfree = ramfree()
return
[CYCLEON]
'if len(params) > 0 then gosub [GETCYCLEPARS]
if ondelay <> 0 then delay ondelay
if relayoff = 0 then io(po,relaypin,1) else io(po,relaypin,0)
if ledoff = 0 then io(po,ledpin,1) else io(po,ledpin,0)
if ondurat = 0 then return
delay ondurat
gosub [RELAYOFF]
if memguage = "y" then memfree = ramfree()
return
[CYCLEOFF]
if offdelay <> 0 then delay offdelay
if relayoff = 0 then io(po,relaypin,0) else io(po,relaypin,1)
if ledoff = 0 then io(po,ledpin,0) else io(po,ledpin,1)
if offdurat = 0 then goto return
delay offdurat
goto [RELAYON]
if memguage = "y" then memfree = ramfree()
return
[RELAY2]
'if debugmode <> "silent" then udpreply localname & " Relay acknowledged"
if upper(word(payload,2)) = "ON" then gosub [RELAY2ON]
if upper(word(payload,2)) = "OFF" then gosub [RELAY2OFF]
if upper(word(payload,2)) = "TOGGLE" then gosub [TOGGLE2]
if upper(word(payload,2)) = "CYCLEON" then gosub [CYCLE2ON]
if upper(word(payload,2)) = "CYCLEOFF" then gosub [CYCLE2OFF]
if memguage = "y" then memfree = ramfree()
return
[RELAY2ON]
'if debugmode <> "silent" then udpreply localname & " On acknowledged"
if io(laststat,relay2pin) <> relay2off then return
if relay2off = 0 then io(po,relay2pin,1) else io(po,relay2pin,0)
if led2off = 0 then io(po,led2pin,1) else io(po,led2pin,0)
if memguage = "y" then memfree = ramfree()
return
[RELAY2OFF]
'if debugmode <> "silent" then udpreply localname & " Off acknowledged"
if io(laststat,relay2pin) = relay2off then return
if relay2off = 0 then io(po,relay2pin,0) else io(po,relay2pin,1)
if led2off = 0 then io(po,led2pin,0) else io(po,led2pin,1)
if memguage = "y" then memfree = ramfree()
return
[TOGGLE2]
'html ramfree() & "<BR>"
if io(laststat,led2pin) = 1 then io(po,led2pin,0) else io(po,led2pin,1)
if io(laststat,relay2pin) = 1 then io(po,relay2pin,0) else io(po,relay2pin,1)
if memguage = "y" then memfree = ramfree()
return
[CYCLE2ON]
'if len(params) > 0 then gosub [GETCYCLEPARS]
if on2delay <> 0 then delay on2delay
if relay2off = 0 then io(po,relay2pin,1) else io(po,relay2pin,0)
if led2off = 0 then io(po,led2pin,1) else io(po,led2pin,0)
if on2durat = 0 then return
delay on2durat
gosub [RELAY2OFF]
if memguage = "y" then memfree = ramfree()
return
[CYCLE2OFF]
if off2delay <> 0 then delay off2delay
if relay2off = 0 then io(po,relay2pin,0) else io(po,relay2pin,1)
if led2off = 0 then io(po,led2pin,0) else io(po,led2pin,1)
if off2durat = 0 then goto return
delay off2durat
gosub [RELAY2ON]
if memguage = "y" then memfree = ramfree()
return
'-------------