Questions with regards to ESP8266 Basic and hardware interfacing and control via Basic commands

Moderator: Mmiscool

User avatar
By Electroguard
#61431 Sorry about the delay in posting, the reason will become obvious as you progress...

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.

Code: Select allmemclear
'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
       
'-------------

 
User avatar
By Boxey
#61478 Wow Electroguard .. I can see that a lot of thought and time has gone into that.

I've not had a chance to try it out yet ..but I have had a quick look through it, and I'm surprised you were able to squeeze it on to a 1M ESP ;)

I've noticed some parts that I might dissect too ;) ... although my biggest concern is getting a reliable data flow, as I've been playing with UDP, and making every message get to where it should, perfectly, is a bit problematic. A system to resend failed messages seems the only way ..but that gets a bit space (and memory) consuming.

I will try to get it loaded onto a couple of ESP's and have a play with it when I can.

It looks like it is a labour of love for sure ..and I won't ask how many times it crashed during testing or how many times it failed to "save" while writing it ;)
User avatar
By Electroguard
#61484 Yeah it has been a labour of love ever since I fell in love with Esp_Basic a year ago, That's how long I've been trying to give it the unique simple interactive functionality that the EasyNet concept could have offered... it seemed to be a match made in heaven, able to greatly increase Esp_Basic's potential use for real-world interactive applications.
Cicciocb was interested enough to create the UDP functionality for me about a year ago, and I had every confidence he could have included the EasyNet core functionality into Esp_Basic once I'd done it. But circumstances made it a lonely uphill struggle since V2, with too many technical obstacles blocking the way for a pensioner whose only computer programming was done on an ancient Beeb half a century ago.
It's a shame to be giving up now when so tantalisingly close, but I've already got too many scars from continuously bashing my head against brick walls, so I'm long overdue for giving up this pointless struggle... plus I've got bigger problems at the moment, ones that are causing me to sleep with a taser at hand. Perhaps I'll get round to eventually completing Easynet if nobody else does, but I don't think it'll be any time soon. Hopefully you will be able to get a few useful bits out of it.
User avatar
By Electroguard
#61610 Here's something you might find useful Boxey.

It's the same EasyNet script as posted previously, but configured to have an "Announce" user command added for speaking different pre-recorded audio messages using a JQ6500 audio module connected by serial2.

So any triggered sensor node could send an "Announce" + number message to your house Audio node for it to speak the corresponding pre-recorded message (or startrek klaxon sounds or whatever).

These cheap audio modules have stripped down filing systems that usually work by file numbers instead of file names. So this configuration is expecting files called 1.mp3, 2.mp3, 3.mp3 etc (up to 99.mp3) to exist inside a folder called 01.
If you check out what's happening in the additional user command VOL settings branches and [VOICEINIT] branch you'll see it's quite easy for you to similarly change folders, thereby allowing announcement of different pre-recorded audio 'sets' if you wish. For instance: file 1.mp3 in folder 01 might say "One", but 1.mp3 in folder 02 might say "Gate beam", and 1mp3 in folder 03 might say it in French.

Announce messages could be targeted to a specific audio node, or aimed at multiple audio announcers using an appropriate group name.

Using the JQ6500 is really easy-peazy. It draws a bit of current so needs it's own adequate 3.3v power supply, but only connects to the Esp using 1 serial wire and a common 0v. If you use its onboard amp to drive a speaker then it has a tendency to break into motor-boat oscillation when the volume is more than a third, but if it drives an external amp then that isn't such a problem.

I have included a CYCLE2ON command in the [ANNOUNCE] branch, so before it speaks an audio alert it could perhaps precede the announcement by triggering a ding-dong (or just light Relay2 LED). It lets you see that the new command is being recognised just by clicking to Send the default outgoing message even without a serial2 voice module connected, but just delete the CYCLE2ON entry if you don't need it.

Code: Select allmemclear
'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
voice = "y"     'Un-comment to enable JQ6500 Serial Audio module support
if voice = "y" then gosub [VOICEINIT]
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 - with added user commands for JQ6500 serlal audio module support"
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 = 9000
on2delay = 0000
on2durat = 3000
off2delay = 0
off2durat = 3000
usercmds = "Announce Setvol Vol> Vol<"  'corresponding uppercase branches have been added at end of script
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 = "Announce 7"
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
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
       
[VOICEINIT]
'udpwrite netIP & "255", udpport, "Voice Announce initialised"
serial2begin 9600,4,5 '2wire only - Connect common 0v, Esp pin 4 to JQ RX, and ensure JQ6500 has it's own 3.3v supply.
delay 500
let vstart = 126 'Serial command start byte
let vol = 15
let vup = 4
let vdown = 5
let vend = 239 'Serial command end byte
let vplay = 13
let vreset = 12
let vpause = 14
let vfiledir = hextoint("12")
let vfile = 1  'Select file 001
let vdir = 1  'Select folder 01
let vdchange = hextoint("0F")
serial2println chr(vstart) & chr(2) & chr(vreset) & chr(vend)
delay 500
serial2println chr(vstart) & chr(3) & chr(vset) & chr(vol) & chr(vend)
return

[ANNOUNCE]
gosub [CYCLE2ON]
if val(word(payload,2)) > 0 and val(word(payload,2)) < 256 then vfile = val(word(payload,2)) else vfile = val("1")
serial2println chr(vstart) & chr(4) & chr(vfiledir) & chr(vdir) & chr(vfile) & chr(vend)
return

[SETVOL]
if val(word(payload,2)) > 0 and val(word(payload,2)) < 30 then vfile = val(word(payload,2)) else vfile = 14
serial2println chr(vstart) & chr(3) & chr(vset) & chr(vol) & chr(vend)
return

[VOL>]
serial2println chr(vstart) & chr(2) & chr(volup) & chr(vend)
return

[VOL<]
serial2println chr(vstart) & chr(2) & chr(voldown) & chr(vend)
return

'-------- End Of Script --------