Main loop stops when I publish a message

So i'm trying to program a DCC sniffer that would come from model railway sensors that takes the DCC values and sends to my MQTT broker on a RPI. This code is being used on Arduino Mega with built in ESP 8266.

I have the sniffer working as expected on its own. Also I have the connection to MQTT working fine on its own. However when i put the two together the main loop stops once I publish a message and I can't for the life of me figure out what is causing the conflict.

#include "WiFiEsp.h"
#include <ArduinoMqttClient.h>
#include "arduino_secrets.h"
#include "Sensor.h"
#include <PubSubClient.h>

char ssid[] = "";            // your network SSID (name)
char pass[] = "";        // your network password
int status = WL_IDLE_STATUS;     // the Wifi radio's status

//========================
IPAddress mqtt_server = {};
const int mqtt_port = 1883;
//const char broker[] = "";
//int        port     = 1883;
const char topic[]  = "track/sensor/1001";
WiFiEspClient espClient;
PubSubClient client(espClient);
//MqttClient mqttClient(espClient);

//========================

byte refreshTime = 4; // Time between DCC packets buffer refreshes in s
byte packetBufferSize = 32; // DCC packets buffer size

#define TIMER_PRESCALER 64
#define DccBitTimerCount (F_CPU * 80L / TIMER_PRESCALER / 1000000L)
// 16000000 * 80 / 64 / 1000000 = 20; 20 x 4usecs = 80us

boolean packetEnd;
boolean preambleFound;

const byte bitBufSize = 50; // number of slots for bits
volatile byte bitBuffer[bitBufSize]; 
volatile byte bitBuffHead = 1;
volatile byte bitBuffTail = 0;

byte pktByteCount=0;
byte packetBytesCount;
byte preambleOneCount;
byte dccPacket[6]; // buffer to hold a packet
byte instrByte1;
byte decoderType; //0=Loc, 1=Acc
byte bufferCounter=0;
byte isDifferentPacket=0;
byte showLoc=1;
byte showAcc=1;


unsigned int decoderAddress;
unsigned int packetBuffer[64];
unsigned int packetNew=0;

unsigned long timeToRefresh = millis() + refreshTime*1000;

long lastReconnectAttempt = 0;

boolean reconnect() {
  if (client.connect("arduinoClient")) {
    // Once connected, publish an announcement...
    client.publish("outTopic","hello world");
    // ... and resubscribe
    client.subscribe("inTopic");
  }
  return client.connected();
}
//========================

void getPacket() {
  preambleFound = false;
  packetEnd = false;
  packetBytesCount = 0;
  preambleOneCount = 0;
  while (! packetEnd) {
    if (preambleFound) getNextByte();
    else checkForPreamble();
  }
}

//========================

void checkForPreamble() {
   byte nextBit = getBit();
   if (nextBit == 1) preambleOneCount++;
   if (preambleOneCount < 10 && nextBit == 0) preambleOneCount = 0;
   if (preambleOneCount >= 10 && nextBit == 0) preambleFound = true;
}

//========================

void getNextByte() {
  byte newByte = 0;
  for (byte n = 0; n < 8; n++) newByte = (newByte << 1) + getBit();
  packetBytesCount ++;  
  dccPacket[packetBytesCount] = newByte;
  dccPacket[0] = packetBytesCount;
  if (getBit() == 1) packetEnd = true;
}

//========================

byte getBit() {
  // gets the next bit from the bitBuffer
  // if the buffer is empty it will wait indefinitely for bits to arrive
  byte nbs = bitBuffHead;
  while (nbs == bitBuffHead) byte nbs = nextBitSlot(bitBuffTail); //Buffer empty
  bitBuffTail = nbs;
  return (bitBuffer[bitBuffTail]);
}

//========================

void beginBitDetection() {
  TCCR0A &= B11111100;
  attachInterrupt(0, startTimer, RISING);
}

//========================

void startTimer() {
  OCR0B = TCNT0 + DccBitTimerCount;
  TIMSK0 |= B00000100;
  TIFR0  |= B00000100;
}

//========================

ISR(TIMER0_COMPB_vect) {
  byte bitFound = ! ((PINE & B00010000) >> 4);
  TIMSK0 &= B11111011;
  byte nbs = nextBitSlot(bitBuffHead);
  if (nbs == bitBuffTail) return;
  else {
    bitBuffHead = nbs;
    bitBuffer[bitBuffHead] = bitFound;
  }
}

//========================

byte nextBitSlot(byte slot) {
  slot ++;
  if (slot >= bitBufSize) slot = 0;
  return(slot);
}

//========================

void printPacket() {
  Serial.print("PP ");
  for (byte n=1; n<pktByteCount; n++) {
    Serial.print(" ");
    Serial.print(dccPacket[n],BIN);
  }
  Serial.println(" ");
}

//========================

void refreshBuffer() {
  timeToRefresh = millis() + refreshTime*1000;
  for (byte n=0; n<packetBufferSize; n++) packetBuffer[n]=0;
  bufferCounter=0;
  Serial.println("-");
  /*
  Serial.print("Loc ");
  Serial.print(showLoc);
  Serial.print(" / Acc ");
  Serial.print(showAcc);
  Serial.print(" / Time ");
  Serial.print(refreshTime);
  Serial.print(" / Buff ");
  Serial.println(packetBufferSize);
  Serial.println(" ");
  */

}

/***** Call back Method for Receiving MQTT messages****/

void callback(char* topic, byte* payload, unsigned int length) {
  String incommingMessage = "";
  for (int i = 0; i < length; i++) incommingMessage+=(char)payload[i];

  Serial.println("Message arrived ["+String(topic)+"]"+incommingMessage);

}

/**** Method for Publishing MQTT Messages **********/
void publishMessage(const char* topic, String payload , boolean retained){
  if (client.publish(topic, payload.c_str(), true))
      Serial.println("Message publised ["+String(topic)+"]: "+payload);
}
//========================

void setup() {
  // initialize serial for debugging
  Serial.begin(38400);
  // initialize serial for ESP module
  Serial3.begin(115200);
  // initialize ESP module
  WiFi.init(&Serial3);
  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
  }
  Serial.println("You're connected to the network");
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
  client.setKeepAlive(5000);
  delay(1500);
  lastReconnectAttempt = 0;
  if (!client.connected()) {
    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  } else {
    // Client connected

    client.loop();
  }

  // Setup DCC Sniff
  Serial.println("---");
  Serial.println("DCC Packet Analyze started");
  Serial.print("Updates every ");
  Serial.print(refreshTime);
  Serial.println(" seconds");
  Serial.println("---");
  beginBitDetection();
} 

//====================

void loop() {
  getPacket(); 
  byte speed;
  byte checksum = 0;
  if (millis() > timeToRefresh) refreshBuffer();
  
  pktByteCount = dccPacket[0];
  if (!pktByteCount) return; // No new packet available

  for (byte n = 1; n <= pktByteCount; n++) checksum ^= dccPacket[n];
  //checksum=0; //Comment this line when on DCC !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  if (checksum) return; // Invalid Checksum
  
  else { // There is a new packet with a correct checksum
    isDifferentPacket=1;
    for (byte n=0; n<packetBufferSize ; n++) {// Check if packet is not already in the buffer. 
    // The checksum byte is used for the test, not ideal, some new packets may not show (only 256 different checksums)
      if (dccPacket[pktByteCount]==packetBuffer[n]) isDifferentPacket=0; 
    }

    if (isDifferentPacket) {  // packet does not yet exist in the packet buffer
      packetBuffer[bufferCounter] = dccPacket[pktByteCount]; // add new packet to the buffer
      bufferCounter = (++bufferCounter)&(packetBufferSize-1);
      
      if (dccPacket[1]==B11111111) { //Idle packet
        Serial.println("Idle ");
        return;
      }
    
      if (!bitRead(dccPacket[1],7)) { //bit7=0 -> Loc Decoder Short Address
        decoderAddress = dccPacket[1];
        instrByte1 = dccPacket[2];
        decoderType = 0;
      }
      else {
        if (bitRead(dccPacket[1],6)) { //bit7=1 AND bit6=1 -> Loc Decoder Long Address
          decoderAddress = 256 * (dccPacket[1] & B00000111) + dccPacket[2];
          instrByte1 = dccPacket[3];
          decoderType = 0;
        }
        else { //bit7=1 AND bit6=0 -> Accessory Decoder
          decoderAddress = dccPacket[1]&B00111111;
          instrByte1 = dccPacket[2];
          decoderType = 1;
        }
      }
      if (decoderType) { // Accessory Basic
        if (showAcc) {
          if (instrByte1&B10000000) { // Basic Accessory
            decoderAddress = (((~instrByte1)&B01110000)<<2) + decoderAddress;
            byte port = (instrByte1&B00000110)>>1;
            Serial.print("Acc ");
            Serial.print((decoderAddress-1)*4 + port + 5);
            int accAdd = (decoderAddress-1)*4 + port + 5;
            Serial.print(" ");
            Serial.print(decoderAddress);
            Serial.print(":");
            Serial.print(port);
            Serial.print(" ");
            Serial.print(bitRead(instrByte1,3));
            if (bitRead(instrByte1,0)) Serial.print(" On");
            else Serial.print(" Off");
            Serial.println("");
            String publishMessage = "INACTIVE";
            if (bitRead(instrByte1,0)) publishMessage = "ACTIVE";
            else publishMessage = "INACTIVE";
            String topic = "track/sensor/"+ String(decoderAddress);
            Serial.println(topic);
            Serial.println(publishMessage);
            //client.publish(topic.c_str(), publishMessage.c_str());
          }
          else { // Accessory Extended NMRA spec is not clear about address and instruction format !!!
            Serial.print("Acc Ext ");
            decoderAddress = (decoderAddress<<5) + ((instrByte1&B01110000)>>2) + ((instrByte1&B00000110)>>1);
            Serial.print(decoderAddress);
            Serial.print(" Asp ");
            Serial.print(dccPacket[3],BIN);
          }
          printPacket();
        }
      }
    }
  }
}

//=====================

It seems to start the next loop but doesn't get past getPacket(). If I comment out the publish message it works fine again. If I keep it commented out and add client.loop() again it will stop at the second getPacket(). So seems like any interaction with client in the main loop causes it to stop working.

This is very odd. You declare a variable nbs but then inside your while loop, you declare a new, different variable that just happens to have the same name. The compiler knows it is a different variable, do you?
Then, when the while() loop terminates, you are back to accessing the outer variable. It is basically like this:

byte getBit() {
  // gets the next bit from the bitBuffer
  // if the buffer is empty it will wait indefinitely for bits to arrive
  byte nbs1 = bitBuffHead;
  while (nbs1 == bitBuffHead)
    byte nbs2 = nextBitSlot(bitBuffTail); //Buffer empty
  bitBuffTail = nbs1;
  return (bitBuffer[bitBuffTail]);
}

and since you are never updating nbs1 that could be an infinite loop.

You should also avoid the String class as it can lead to memory fragmentation on AVR processesors. That function easily be done without Strings:

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("[");
  for (int i = 0; i < length; i++) Serial.print(payload[i]);
  Serial.println();
}

Same for your publishMessage() function

1 Like

Some of those bits are copied from guides on web etc regarding using MQTT. The bit DCC collection stuff isn't written by me. My question was about the main loop stopping as I assume there is a conflict with the collection code and PubSubClient.

I tried with another example on web of collection DCC packets and it works now with MQTT so this can be closed. Thanks

That a poor problem description. The main loop doesn't "stop". More likely one of the functions you call hangs or your loop() code is simply wrong. For one thing, I see your code only calls
client.loop(); once, in the setup() function. It needs to be called continously (say in the loop() function).

Yeah that caused it to "stop" too. I think it interfered with some of the code that fetches the DCC packets so it just got into a never ending loop looking for some. Thats my guess. But anyway as I said above I have fixed it by using a different DCC packet analyser.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.