20M2 MQTT node with ESP8266 ESP-01 running Annex (and PCB)

lbenson

Senior Member
Here is code to run an MQTT node on a PICAXE 20M2, sending the data using an ESP-01 flashed with Annex RDS (https://sites.google.com/site/annexwifi/home). (See post 11 for 14M2 PCB.)
Code:
'20mqtt
#picaxe 20M2
#no_data
#terminal 4800

symbol baud=t2400_4
symbol pLED=B.0

symbol action=b4
symbol temp=b5
symbol lasttemp=b6

pullup %00001000 ' pullup on b.3
'setfreq M8
pause 2000
readtemp B.7,b1
sertxd(ppp_filename," ", ppp_datetime," starting; temp = ",#b1,cr,lf)

do
    serin [1000,tempread],b.6,baud,("MsgForA"),action
    select action
      case "0": low pLED
      case "1": high pLED
    endselect
  tempread:
    readtemp B.7,temp
    if lasttemp <> temp then
      lasttemp = temp
      sertxd(#temp," ")
      serout C.0,baud,("T ",#temp,13,10) ' temperature changed message
    endif
    if pinB.3 = 0 then
      pause 100 ' debounce
      bit0 = 1-bit0 ' toggle bit
      do while pinB.3=0: loop
      serout C.0,baud,("B 1",13,10) ' button depressed message
    endif
loop

do
  if pinB.3 = 0 then
    pause 100 ' debounce
    bit0 = 1-bit0 ' toggle bit
    do while pinB.3=0: loop
  endif
  if bit0=1 then: toggle B.0: else: low B.0: endif
  pause 1000
  readtemp B.7,temp
  if b1 <> b2 then: b2 = b1: sertxd(#temp," "): endif
loop
The 20M2 has a DS18B20 on pin B.7, an LED On B.0, and a pushbutton on pinB.3. I set up a mosquitto MQTT broker on a Raspberry Pi-ZW following this tutorial:
I set up mosquitto clients on another pi-zw and on a Dockstar running openwrt.
I use a topic of "broadcast", with the intention that each picaxe node will recognize a message for itself by matching a SERIN qualifier, like "MsgForA" (through "MsgForZ").
Sending from the Dockstar MQTT client of a message of "MsgForA1" turns on the LED; "MsgForA0" turns it off, as with this command:
mosquitto_pub -h 192.168.2.40 -t broadcast -m "MsgForA1"
When the button is depressed, the picaxe sends serial to the ESP-01 and that sends a message, e.g., FromA=B (indicating the button was pressed). When the temperature changes, "FromA=T:25" is sent.
Here is the Annex code on the ESP-01:

Code:
' MQTT test program
serial2.mode 2400,1,3 ' set up serial on the Tx & Rx pins
wlog "Connecting to 192.168.2.40"
pause 1000
print "Setup: 2.40",mqtt.setup("192.168.2.40")
print "Connect: ",mqtt.connect("", "") ' No login / pass required
topic$="broadcast"
print "Subscribe: ",mqtt.subscribe(topic$) ' subscribe to the topic /AnnexTest
onmqtt mqtt_msg

onSerial2 ser2in
print "Ram ",ramfree
wlog "Ram ",ramfree
wait

ser2in:
  pause 50 ' accept characters
  in$=serial2.input$
  code$=mid$(in$,1,1)
  if code$="B" then
    ret=mqtt.publish(topic$, "FromA=B")
  endif
  if code$="T" then
    msg$="FromA=T:"+mid$(in$,3)
    ret=mqtt.publish(topic$, msg$)
  endif
  return

' receive messages from the broker

mqtt_msg:
wlog "Topic/Message  : "; mqtt.topic$;" "; mqtt.message$
if instr(mqtt.message$,"MsgForA") <> 0 then
print2 mqtt.message$ ' e.g., "MsgForA1" turns on LED; "MsgForA0" turns off LED
endif
Here's a photo:
PX20M2 Annex mqtt PCB.jpg
Eaglecad files for the PCB for this setup are here--zip file may be sent directly to fab (e.g., JLCPCB.com) or .brd file can be sent to oshpark.com.
 

Attachments

Last edited:

lbenson

Senior Member
I still don't know much about mqtt. I think a common topic like "broadcast" will work fine for all picaxe nodes receiving a 1-byte message and using a qualifier, but I suspect that outgoing logging should have a specific topic for each data element (like temperature at a given location) e.g., "TempFromA", "TempFromB", etc., with perhaps more explanatory topics like "BasementTemp", "OutsideTemp".
 

hippy

Technical Support
Staff member
Looks like a good start. The only thing which probably needs improving is the means of passing messages from the ESP to PICAXE, because Sod's Law will have it that a message arrives just as the SERIN times out and gets missed. Or a second message arrives while handling the first or doing something else. READTEMP can block for quite some time.

That could be solved with a hardware handshaking lines but better by reserving a character which when sent causes the ESP to send its message.

To avoid a lot of polling it would be possible to send a single byte as a 'data available' trigger from the ESP to HSERIN and then poll for on anything received rather than polling the ESP.

Whether to have a single topic with a qualifier in the payload, or multiple topics with just raw data is a tough one, both have their pro's and con's. Ideally the later in the spirit of MQTT but a single topic with qualifiers is more suitable for a PICAXE which is what we are concerned about here, and most things outside the PICAXE universe should still be able to handle that.

The best way to handle multiple topics for a PICAXE would be to tell the ESP which topics it was looking for, ask for specific topic data, but that will complicate the ESP code.
 

lbenson

Senior Member
The only thing which probably needs improving is the means of passing messages from the ESP to PICAXE
Good thought. Here's modified PICAXE code using HSERIN and a character output to the ESP indicating ClearToSend:
Code:
'20mqtt
#picaxe 20M2
#no_data
#terminal 4800

symbol baud=t2400_4
symbol pLED=B.0

symbol action=b4
symbol temp=b5
symbol lasttemp=b6
symbol tempdiff=b7

HSerSetup B2400_4,%01000

pullup %00001000 ' pullup on b.3
'setfreq M8
pause 2000
readtemp B.7,b1
sertxd(ppp_filename," ", ppp_datetime," starting; temp = ",#b1,cr,lf)

do
  b1 = 1
  HSerIn w0
  If b1 = 0 Then ' byte received into b0
    HSerSetup OFF
    sertxd(b0)
    if b0 = ":" then 
      sertxd(">")
      serout C.0,baud,(">",cr,lf) ' esp send message to px

      serin [1000,tempread],b.6,baud,("MsgForA"),action
      select action
        case "0": low pLED
        case "1": high pLED
      endselect
    End If
    HSerSetup B2400_4,%01000
  End If

tempread:
    readtemp B.7,temp
    if lasttemp > temp then
      tempdiff = lasttemp - temp
    else
      tempdiff = temp - lasttemp
    endif
    if tempdiff > 1 then
      lasttemp = temp
      sertxd(#temp," ")
      serout C.0,baud,("T ",#temp,cr,lf) ' temperature changed message
    endif
    if pinB.3 = 0 then
      pause 100 ' debounce
      bit16 = 1-bit16 ' toggle bit
      do while pinB.3=0: loop
      serout C.0,baud,("B 1",cr,lf) ' button depressed message
      sertxd(" btn ")
    endif
loop
And the ESP-01 Annex code:
Code:
' MQTT test program
' Using mosquitto test server
'wlog "Connecting to test.mosquitto.org"
serial2.mode 2400,1,3
wlog "Connecting to 192.168.2.40"
print "Connecting to 192.168.2.40"
pause 1000
'print mqtt.setup("test.mosquitto.org")
print "Setup: 2.40",mqtt.setup("192.168.2.40")
print "Connect: ",mqtt.connect("", "") ' No login / pass required
topic$="broadcast"
print "Subscribe: ",mqtt.subscribe(topic$) ' subscribe to the topic /AnnexTest
onmqtt mqtt_msg

onSerial2 ser2in
print "Ram ",ramfree
wlog "Ram ",ramfree
wait

ser2in:
  pause 50 ' accept characters
  in$=serial2.input$
  wlog "Serial Input: "+in$
  code$=mid$(in$,1,1)
  if code$="B" then
    ret=mqtt.publish(topic$, "FromA=B")
  endif
  if code$="T" then
    msg$="FromA=T:"+mid$(in$,3)
    ret=mqtt.publish(topic$, msg$)
  endif
  if code$=">" then  ' picaxe waiting for message
    pause 100
    print2 msgRcvd$  ' e.g., "MsgForA1" turns on LED; "MsgForA0" turns off LED
  endif
  return

' receive messages from the broker

mqtt_msg:
wlog "Topic/Message  : "; mqtt.topic$;" "; mqtt.message$
if instr(mqtt.message$,"MsgForA") <> 0 then
  msgRcvd$=mqtt.message$
  print2 ":    " ' tell picaxe message is ready
endif
return
Or a second message arrives while handling the first or doing something else. READTEMP can block for quite some time.
That's an issue. I don't want to build queuing into the ESP (though it might not be especially hard), so the practice in the MQTT mesh will have to be such that at least a couple of seconds elapse between messages to PICAXEs. A busy network would probably have to use unique topics for each PICAXE node instead of "broadcast".

I tried to set up node-red on the pi, but I couldn't get the "switch" function to display the options that the tutorials showed--in particular, I couldn't find how to set "On" payload (message) and "Off" payload. So I gave up for now and wrote a bash script which runs on the pi which subscribes to the "broadcast" topic and sends a "turn on the LED" (or off) message when a button press is signaled from the PICAXE.
Code:
#!/bin/bash
mosquitto_pub -h 192.168.2.40 -t broadcast -m "MsgForA0"
switchA="0"
mosquitto_sub -h 192.168.2.40 -t broadcast 2>/dev/null | while read v1; do
  topic=${v1:0:5}
  if [ "$topic" == "FromA" ] ; then
    echo $v1
    action=${v1:6:1}
    if [ "$action" == "B" ] ; then
      if [ "$switchA" == "0" ] ; then
        mosquitto_pub -h 192.168.2.40 -t broadcast -m "MsgForA1"
        switchA="1"
      else
        mosquitto_pub -h 192.168.2.40 -t broadcast -m "MsgForA0"
        switchA="0"
      fi
    fi
  fi
done
(This is not foolproof, since another node could interject a turn on/off message and the bash script would become out of sync.)
 
Last edited:

geoff07

Senior Member
the readtemp delay can be avoided by setting it up with a owout command and then later going to collect the data with owin. It doesn't have to be set up and read in the same command, you can do something else in the meantime.

This is all very promising.
 

lbenson

Senior Member
the readtemp delay can be avoided by setting it up with a owout command
Right, but for the sake of less than a second, that wouldn't seem necessary when the PICAXE and the ESP are able to engage in RTS/CTS handshaking (by means of a character sent back and forth). But if it becomes critical, that option is available.
 

lbenson

Senior Member
Well, I figured out enough about node-red to configure it to turn on and off the LED on my MQTT-enabled PICAXE.

Node-red is installed on the same pi-ZW as the Mosquitto MQTT broker, at 192.168.2.40 on my network. In a PC browser, I went to node-red at 192.168.2.40:1880, dragged two "inject" nodes to the workspace, and two MQTT-out nodes, and linked each "inject" node to one mqtt-out node. Then I double-clicked an inject node to configure it as shown in this pic:
Node-red_Inject.jpg
The Payload field type is changed to text/alpha, and "MsgForA1" is typed; the topic is set to "broadcast" (arbitrary topic name I have used). I gave the node a name of "LampA_On". Then "Done".

Next I double-clicked the linked MQTT-out node and set the values:
Node-red_MQTTout.jpg
Here I set the server to "localhost" (because the mqtt broker is local to the pi which also is running node-red), and provided a name of "TurnOnLampA". Note that I didn't have to set the topic here because I set it in the inject node.

I then did similar for the Lamp-Off inject and mqtt-out pair.

I clicked "Deploy", and then clicking on the image at the left of the LampA_On inject node turns on the LED on the PICAXE; clicking on the LampA_Off node turns it off.

Note that the pushbutton on the PICAXE still works through the bash script to toggle the LED, and independently publishing "MsgForA1" also works, so there are three independent ways to turn the LED on and off. And of course, the node-red page works on any browser on any device connected to my network--on my phone, for instance.

Hmmm ... after posting I realized that the two MQTT-out nodes are identical, so wondered if one could be deleted and the other inject node connected to the one MQTT-out node. That does work:
Node-red_both.jpg
 
Last edited:

lbenson

Senior Member
Here's another node-red wrinkle for controlling the LED using the pushbutton. First add a MQTT-in node:
Node-red_MQTTin.jpg
This listens to topic "broadcast" and passes the message payload to a function node:
Node-red_function.jpg
The function node uses javascript code. In this case, the code checks that we actually have a button press from the PICAXE (which on this MQTT mesh is node A), signalled by "FromA=B". If that's the case, the code retrieves a global variable, LampAstatus into local variable x (which, if undefined, will be set to "0"). x is toggled between "0" and "1", and the turn on/off message is passed on the the MQTT-out node--either "MsgForA1" or "MsgForA0", which turns the LED on or off.
Code:
if (msg.payload == "FromA=B") {
  var x=context.get("LampAstatus");
  if (typeof x=="undefined") x="0";
  if (x == "0") {
    x="1";
  } else {
    x="0";
  }
  context.set("LampAstatus",x);
  msg.payload="MsgForA"+x;
  return msg;
}
Note that the global variable can get out of sync if the LampA_On or LampA_Off buttons have been pressed. There's probably a way around this, but the way to do that that I see is to route the two injection nodes into the function node and have it keep everything straight. Early days for me with this stuff but a lot of power for remotely controlling outputs on a picaxe.
 

geoff07

Senior Member
I'm just getting my hardware sorted for my experiments, but I'm a few days behind you with Node-red. Your guidance is appreciated! This stuff is extremely powerful, but there is a lot to learn. I'm very impressed with AnnexRDS.
 

lbenson

Senior Member
Here's a 14M2 PCB doing mqtt with an ESP-01:
14Esp_mqtt.jpg
Eagle cad schematic and board (sch and brd files attached--remove ".txt" to use files in Eagle CAD--.brd file can be sent directly to Oshpark.com):
View attachment 23900
Here's the picaxe code, set up as node C on the mqtt net (looking for serial in with a qualifier of "MsgForC"):
Code:
'14mqtt
#picaxe 14M2
#no_data
#terminal 4800
#rem
PICAXE 14M2 MQTT with ESP-01 (ESP is 2.117)
             +V 1 |      | 14 0V
         c.5 SI 2 |      | 13 b.0 SO hserout debug
            c.4 3 |      | 12 b.1 hserin
            c.3 4 |      | 11 b.2 pLED -- Yellow LED 
            c.2 5 | 14M2 | 10 b.3 SLC -- pTemp (DS18B20)
       pBTN c.1 6 |      |  9 b.4 SDA
            c.0 7 |      |  8 b.5 

 5-row 3-pin header
  B.2 pLED
  C.4
  C.2
  C.1 pBTN 
  B.5
#endrem
symbol baud=t2400_4
symbol pLED=B.2
symbol pBTN=pinC.1
symbol pSERIN=B.1
symbol pTemp=B.3
symbol pSEROUT=C.0
'symbol pESPReset=C.1

symbol action=b4
symbol temp=b5
symbol lasttemp=b6
symbol tempdiff=b7

HSerSetup B2400_4,%01000

pullup %0000001000000000 ' pullup on pBtn, C.1
'setfreq M8
pause 2000
'high pESPReset
readtemp pTemp,b1
sertxd(ppp_filename," ", ppp_datetime," starting; Node C; temp = ",#b1,cr,lf)

do
  b1 = 1
  HSerIn w0
  If b1 = 0 or time > 3600 Then ' byte received into b0 or more than 1 hour lapsed
    HSerSetup OFF
    sertxd(b0)
    if b0 = ":" or time > 3600 then 
      sertxd(">")
      serout pSEROUT,baud,(">",cr,lf) ' esp send message to px

      serin [1000,noResponse],pSERIN,baud,("MsgForC"),action
      select action
        case "0": low pLED: time=0 ' reset clock
        case "1": high pLED: time=0 ' reset clock
        case "+": time=0 ' received response
      endselect
    End If
    HSerSetup B2400_4,%01000
    goto tempread
  noResponse:
'    low pESPReset
    pause 200
'    high pESPReset
    pause 3000
  End If

tempread:
    readtemp pTemp,temp
    if lasttemp > temp then
      tempdiff = lasttemp - temp
    else
      tempdiff = temp - lasttemp
    endif
    if tempdiff > 1 then
      lasttemp = temp
      sertxd(#temp," ")
      serout pSEROUT,baud,("T ",#temp,cr,lf) ' temperature changed message
    endif
    if pBTN = 0 then
      pause 100 ' debounce
      bit16 = 1-bit16 ' toggle bit
      do while pBTN=0: loop
      serout pSEROUT,baud,("B 1",cr,lf) ' button depressed message
      sertxd(" btn ")
    endif
loop
And the very slightly changed Annex Basic code for the ESP-01:
Code:
' MQTT test program
' Using mosquitto test server
serial2.mode 2400,1,3
wlog "Connecting to 192.168.2.40"
print "Connecting to 192.168.2.40"
pause 1000
print "Setup: 2.40",mqtt.setup("192.168.2.40")
print "Connect: ",mqtt.connect("", "") ' No login / pass required

msg$="nodeA "+str$(BAS.RESETREASON)+" "+mid$(date$,7,2)+mid$(date$,4,2)+mid$(date$,1,2)
msg$=msg$+mid$(time$,1,2)+mid$(time$,4,2)+mid$(time$,7,2)
ret=mqtt.publish("log", msg$)

topic$="broadcast"
print "Subscribe: ",mqtt.subscribe(topic$) ' subscribe to the topic /AnnexTest
msgRcvd$=""
onmqtt mqtt_msg

onSerial2 ser2in
print "Ram ",ramfree
wlog "Ram ",ramfree
wait

ser2in:
  pause 50 ' accept characters
  in$=serial2.input$
  wlog "Serial Input: "+in$
  code$=mid$(in$,1,1)
  if code$="B" then
    ret=mqtt.publish(topic$, "FromC=B")
  endif
  if code$="T" then
    msg$="FromC=T:"+mid$(in$,3)
    ret=mqtt.publish(topic$, msg$)
  endif
  if code$=">" then  ' picaxe waiting for message
    pause 100
    if msgRcvd$ = "" then ' picaxe asking if we're alive
      print2 "MsgForC+"  ' we're OK
    else
      print2 msgRcvd$  ' e.g., "MsgForC1" turns on LED; "MsgForC0" turns off LED
      msgRcvd$=""
    endif
  endif
  return

' receive messages from the broker

mqtt_msg:
wlog "Topic/Message  : "; mqtt.topic$;" "; mqtt.message$
if instr(mqtt.message$,"MsgForC") <> 0 then
  msgRcvd$=mqtt.message$
  print2 ":    " ' tell picaxe message is ready
endif
return
14Esp_mqtt_b eagle.jpg
 

Attachments

Last edited:

lbenson

Senior Member
I must admit to two embarrassing mistakes regarding the 14M2 version--one masking the other. Haste makes waste.

On the hardware side, I swapped Tx and Rx on the ESP-01, and on the software side I was toggling the LED in the picaxe program (as per my initial testing of the board without the esp-01), and not going through the mqtt message exchanges. In post 11 above, I've replaced the faulty programs and the Eagle CAD .sch and .brd files. Now the mqtt test programs (e.g., the Windows program MQTT Explorer) are seeing the messages and publishing messages which the picaxe responds to.

If anyone has used the Eagle files, my apologies.
 
Last edited:

geoff07

Senior Member
Just one question so far: the ESP-01 is limited to 3.6v apparently. I'm using 3.3v. But you are using 4.5v/5v. I don't see a regulator. I am thinking of having some boards made so the issue is relevant. I'm not sure I trust my own eagle skills.

And if anyone else in the UK wants some boards, I'm happy to do a bigger batch (cheaper). I'm being quoted about £80 for 10. Let me know. Having lots of headers as designed would make interfacing to stuff much easier.
 

mushroom

New Member
Geoff07
For boards that size I'd expect to pay USD $19.90 plus delivery for 10 quality boards from itead.cc in China. Never had a problem or long delays.
Note that the currency can be set at the top of the page. I set mine for Aussie $'s.
 

lbenson

Senior Member
I'm not using 4.5/5V. The 20M2 version I was powering from the 3V3 feeds of a 18650 charger/holder like this:

For the 14M2 version I'm using a 2-cell AA battery holder. Started off at 3.15V. I don't know how long it will last, but it's fine so far for testing.

£80 for 10 sounds quite high. I just checked JLCPCB.com--$24.07 USD for 10 of the 14M2s and 5 of the 20M2s (or vice versa, no difference)--$7 for the PCBs and $17.01 for express shipping. Slow boat shipping would be $9.56USD for a total of $16.56.

I've updated the 20M2 board to allow control of the RST button from C.1. 20M2 and 14M2 gerber zip files attached.

(I see that the labels on the PCBs say 5V--that's just a holdover from previous designs. I don't particularly like "VCC" as a label--perhaps I should use "V+".)
(Silkscreen labels changed from "5V" to "3V3", May 29.)
 

Attachments

Last edited:

lbenson

Senior Member
Having lots of headers as designed would make interfacing to stuff much easier.
I find the 3-pin headers (+, signal, -) very useful. For experimenting, many of the A******* sensor kit items can be fitted with dupont cables:

I also like to have plenty of +V and 0V header pins. The boards also have the I2C pins brought out to a header. I use the long pin female headers with the pins bent, so I have a female I2C connection on the bottom and male on the top. (In the photos above I happen to have a DS18B20 plugged into the +V/0V/SDA part of the I2C connector, since it was convenient with the 4K7 pullup already there.)
 

tmfkam

Senior Member
Geoff07
For boards that size I'd expect to pay USD $19.90 plus delivery for 10 quality boards from itead.cc in China. Never had a problem or long delays.
Note that the currency can be set at the top of the page. I set mine for Aussie $'s.
I'd suggest JLCPCB. Five boards, $2 if the board size is under 100mm x 100mm.

Fifty boards of around 60mm x 80mm cost me $30 or so plus another $30 (ish) for express shipping and UK import duties.
 

geoff07

Senior Member
I was expecting to hear about better prices, thanks. I wasnt expecting such a difference though.

Leaving the 5v on the schematic is asking for trouble! Thanks for the work though!

Still working on hardware here. Has anyone found a good android mqtt client? There are many but it isn't obvious which are actually useful.
 

mushroom

New Member
tmfkam,
Thanks for your tip on JLCPCB. I was about to get some boards made and now plan to try JLCPCB.
I have reasons for preferring Hong Kong manufacture to mainland China anyway, however my reason for this may no longer apply, unfortunately.
Pete
 
Top