B4J Question [Solved] MQTT payload is garbled?

Mark Read

Well-Known Member
Licensed User
I have the following setup:

NodeMCU Ver. 2: Running Wifi Soft AP and MQTT Broker, programmed with Arduino.
Wemos D1 R2: Connected with a motor and running Arduino code as detailed below.

PC with win 7: B4J app.

The Wemos connects to the Node via Wifi, as does the PC. I can see the connection and the IP.

broker.jpg

The B4J app, on pressing a button should send a single letter "f", "s" or "b" accordingly. I cannot get the payload on the Wemos to convert back to the letter sent.

I have tried using a tablet with a simple MQTT app to send the letters and it works fine. I think I have done something wrong in the B4J code but I am not sure what.

Thanks for any help.

Output from Wemos serial monitor after pressing "forwards" button - letter "f":

Wemos-Motor.jpg

The Wemos code:

B4X:
/*
  Wemos d1 r2

  Connect to Wifi AP and to MQTT broker

  Subscribe to topic "Motor1"

  Start/Stop motor based on "f", "r" and "s"
 
  Status: Working
 
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>


const char* ssid = "Rover AP";
const char* password = "";
const char* mqtt_server = "192.168.4.1";

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

const char* outTopic = "Motor1";    // MQTT Topic for control

// Setup motor
#define IN1  D1
#define IN2  D2
#define IN3  D6
#define IN4  D7
int Steps = 0;
boolean Direction = true;       // cw
boolean MotorStatus = false;    // motor is off

// Setup Wifi connection
void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);            // we need this or we cannot connect!
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

// Called when a message is received
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("]; ");

  payload[length] = '\0';
 
  String value = String((char*)payload);
  
//  for (int i = 0; i < length; i++) {
//    Serial.print((char)payload[i]);
//  }
  Serial.println(value);
  Serial.println();
  if(value.startsWith("s")){ResetMotor(); MotorStatus=false;}
  if(value.startsWith("f")){ResetMotor(); MotorStatus=true;}
  if(value.startsWith("r")){ResetMotor(); Direction=not(Direction); MotorStatus=true;}
  Serial.print("Motor status: ");
  Serial.println(MotorStatus);
}

// Reset motor
void ResetMotor() {
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);

}


// Connect to MQTT Broker
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish(outTopic, "Motor Wemos booted");
      // ... and resubscribe
      client.subscribe(outTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
      }
    }
 
}


void setup() {
  Serial.begin(115200);
  setup_wifi();                   // Connect to wifi
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  ResetMotor();
}


void loop() {

  if (!client.connected()) {
    reconnect();
  }

  if (MotorStatus){
    stepper(1);
    delay(25);        // This delay is required for the motor to respond!!
  }
 
  client.loop();
}


void stepper(int xw){
  for (int x=0;x<xw;x++){
switch(Steps){
   case 0:
     digitalWrite(IN1, HIGH);
     digitalWrite(IN2, HIGH);
     digitalWrite(IN3, LOW);
     digitalWrite(IN4, LOW);
   break;
   case 1:
     digitalWrite(IN1, LOW);
     digitalWrite(IN2, HIGH);
     digitalWrite(IN3, HIGH);
     digitalWrite(IN4, LOW);
   break;
   case 2:
     digitalWrite(IN1, LOW);
     digitalWrite(IN2, LOW);
     digitalWrite(IN3, HIGH);
     digitalWrite(IN4, HIGH);
   break;
   case 3:
     digitalWrite(IN1, HIGH);
     digitalWrite(IN2, LOW);
     digitalWrite(IN3, LOW);
     digitalWrite(IN4, HIGH);
   break;
  
   default:
     digitalWrite(IN1, LOW);
     digitalWrite(IN2, LOW);
     digitalWrite(IN3, LOW);
     digitalWrite(IN4, LOW);
   break;
}
SetDirection();
}
}

void SetDirection(){
if(Direction==1){ Steps++;}
if(Direction==0){ Steps--; }
if(Steps>3){Steps=0;}
if(Steps<0){Steps=3; }
}

and lastly the B4J code:

B4X:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 600
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private btnBackwards As Button
    Private btnConnect As Button
    Private btnForward As Button
    Private btnStop As Button
    Private lbl1 As Label
    Private lblConnectionText As Label
  
    Private mqtt As MqttClient
    Private serializator As B4XSerializator
    Private user As String = ""
    Private password As String = ""
    Private outTopic As String="Motor1"
    Private const port As Int = 1883  
    Private const Host As String="192.168.4.1"
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.SetFormStyle("UNIFIED")
    MainForm.RootPane.LoadLayout("1") 'Load the layout file.
    MainForm.Show
End Sub

Sub mqtt_Connected (Success As Boolean)
    If Success = False Then
        Log(LastException)
        lblConnectionText.Text = "Not Connected"
    Else
        lblConnectionText.Text = "Connected"
        mqtt.Subscribe(outTopic, 0)
        Log("Connected")
        Log(" ")
        Log("Subscribed to " & outTopic)
    End If
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub btnStop_MouseClicked (EventData As MouseEvent)
    If mqtt.Connected = False Then Return
    mqtt.Publish(outTopic, serializator.ConvertObjectToBytes("s"))
End Sub

Sub btnForward_MouseClicked (EventData As MouseEvent)
    If mqtt.Connected = False Then Return
    mqtt.Publish(outTopic, serializator.ConvertObjectToBytes("f"))
    'mqtt.Publish2(outTopic, serializator.ConvertObjectToBytes("f"),0,False)
  
End Sub

Sub btnConnect_MouseClicked (EventData As MouseEvent)
    Dim clientId As String = Rnd(0, 999999999) & DateTime.Now 'create a unique id
    mqtt.Initialize("mqtt", $"tcp://${Host}:${port}"$, clientId)
    Dim mo As MqttConnectOptions
    mo.Initialize(user, password)
    mqtt.Connect2(mo)
End Sub

Sub btnBackwards_MouseClicked (EventData As MouseEvent)
    If mqtt.Connected = False Then Return
    mqtt.Publish(outTopic, serializator.ConvertObjectToBytes("r"))
End Sub

Sub MainForm_Closed
    mqtt.Close
    Log("Connection closed")
End Sub
 

Mark Read

Well-Known Member
Licensed User
You cannot use B4XSerializator unless all sides of the communication use B4XSerializator.

Aha - that I didn't realise but somehow logical.

The NodeMCU is using the µMQTT library which I could not get to work with B4R. I can however try and port the arduino code for the Wemos to B4R. This should work then as the Node only sends the message it gets. Correct?
 
Upvote 0

Mark Read

Well-Known Member
Licensed User
So I am a little further. I have re-written the Wemos code above in B4R. But it now gives an error:

Connected to wireless network.
My ip: 192.168.4.3
Trying to connect to MQTT Broker
connected
1
Message arrived: Motor1 x�cdd``H
Out of bounds error. Array length = 10, Index = 65535

in the sub below. I see in the forum others have had this error but I see no solution.

B4X:
Sub mqtt_MessageArrived (Topic As String, Payload() As Byte)
    Log("Message arrived: ", Topic, " ", Payload)
    Dim be(10) As Object
    Dim data() As Object = ser.ConvertBytesToArray(Payload, be)
    Log("Received:")
    For Each o As Object In data
        Log(o)
    Next
End Sub

New code in B4R for Wemos:

B4X:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 300
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'Public variables can be accessed from all modules.
  
    'Wemos D1 R2
    ' Pin    GPIO    Function
    '____    ______    ___________
    ' D0    GPIO16
    ' D1    GPIO5    SCL
    ' D2    GPIO4    SDA
    ' D3    GPIO0
    ' D4    GPIO2    BUILTIN_LED
    ' D5    GPIO14    SCK
    ' D6    GPIO12    MISO
    ' D7    GPIO13    MOSI
    ' D8    GPIO15
    ' A0    A0
  
    Public Serial1 As Serial
  
    'Motor variables
    Dim IN1 As Pin
    Dim IN2 As Pin
    Dim IN3 As Pin
    Dim IN4 As Pin
    Dim Steps As Int=0
    Dim Direction As Boolean=True
    Dim timer1 As Timer
  
    Private wifi As ESP8266WiFi
    Private ssid As String="Rover AP"
    Private password As String=""
    Private ser As B4RSerializator
    Private mqtt As MqttClient
    Private socket As WiFiSocket
    Private outTopic As String="Motor1"
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    RunNative("SetSTA", Null)
  
    IN1.Initialize(5,IN1.MODE_OUTPUT)
    IN2.Initialize(4,IN2.MODE_OUTPUT)
    IN3.Initialize(12,IN3.MODE_OUTPUT)
    IN4.Initialize(13,IN4.MODE_OUTPUT)
    ResetMotor
  
    If wifi.Connect2(ssid, password) Then 'change to your network SSID (use Connect2 if a password is required).
        Log("Connected to wireless network.")
        Log("My ip: ", wifi.LocalIp)
    Else
        Log("Failed to connect.")
        Return
    End If
  
    mqtt.Initialize( socket.Stream, Array As Byte(192, 168, 4, 1), 1883, _
       "esp2", "mqtt_MessageArrived", "mqtt_Disconnected")
    Connect(0)
  
    timer1.Initialize("timer1_Tick",25)        'Delay between switching coils on motor
  
  
End Sub

Sub Connect(u As Byte)
    Log("Trying to connect to MQTT Broker")
    If mqtt.Connect Then
        Log("connected")
        Log(mqtt.Subscribe(outTopic, 0))
    Else
        Log("No connection - retry in 1 second")
        CallSubPlus("Connect", 1000, 0)
    End If
End Sub

Sub mqtt_Disconnected
    Connect(0)
End Sub

Sub mqtt_MessageArrived (Topic As String, Payload() As Byte)
    Log("Message arrived: ", Topic, " ", Payload)
    Dim be(10) As Object
    Dim data() As Object = ser.ConvertBytesToArray(Payload, be)
    Log("Received:")
    For Each o As Object In data
        Log(o)
    Next
End Sub




#if C
void SetSTA(B4R::Object* o) {
   WiFi.mode(WIFI_STA);
}
#end if

Sub ResetMotor
    IN1.DigitalWrite(False)                        'reset all outputs
    IN2.DigitalWrite(False)
    IN3.DigitalWrite(False)
    IN4.DigitalWrite(False)
End Sub

Sub timer1_Tick
    stepper
End Sub

Sub stepper
    Select Steps
        Case 0
            IN1.DigitalWrite(True)
            IN2.DigitalWrite(True)
            IN3.DigitalWrite(False)
            IN4.DigitalWrite(False)
        Case 1
            IN1.DigitalWrite(False)
            IN2.DigitalWrite(True)
            IN3.DigitalWrite(True)
            IN4.DigitalWrite(False)
              
        Case 2
            IN1.DigitalWrite(False)
            IN2.DigitalWrite(False)
            IN3.DigitalWrite(True)
            IN4.DigitalWrite(True)
        Case 3
            IN1.DigitalWrite(True)
            IN2.DigitalWrite(False)
            IN3.DigitalWrite(False)
            IN4.DigitalWrite(True)
    End Select
    IncStep
End Sub

Sub IncStep
    If Direction=True Then
        Steps=Steps+1
    Else
        Steps=Steps-1
    End If
  
    If Steps>3 Then Steps=0
    If Steps<0 Then Steps=3
End Sub
 
Last edited:
Upvote 0

Mark Read

Well-Known Member
Licensed User
OMG, I am an idiot! :mad:

The correct solution is to switch to B4R and use B4RSerializator in all platforms.

Erel even gave me the answer but I did not see the mistake. I was using B4XSerializator in my B4J code and B4RSerializator in my B4R code. They are not compatible! Added the B4RSerializator.bas module to my B4J code and bang! It works. I send a letter and it arrives. :D

Marking this thread as solved.
 
Upvote 0
Top