Post topics, source code that relate to the Arduino Platform

User avatar
By NickNinjaneer
#67943 I'm in a world of hurt and don't know how to get out of it.

I've built a set for a theatre production of Newsies, which consists of three two-story towers. Each of six total openings in the towers have servo driven, Feather Huzzah controlled projection screens that move up and down independently throughout the show. They are also battery powered, since the towers have to move around the stage.

Our show opens tonight, and nothing is working reliably. Yesterday morning, I got all six screens to work, but then at last nights tech rehearsal, I had connection dropouts and occasional repetitive blasts of Serial transmissions from what appears to be one of the chips stuck in the message received callback, which fires off the Serial.print call "Start Here: Message received from..." over and over. I'm using the "Painless Mesh" library from BlackEdder: https://gitlab.com/BlackEdder/painlessMesh/tree/master

I've tried everything mentioned all over the interwebs that I can find - yield();, setting the watchdog to 8 seconds, adding a 1000uf cap on the battery input. The boards were REALLY wonky before adding the yield(); I instead had ESP.wdtfeed - when I added yield(); the boards flipped out, but when I removed the wdtfeed and kept yield, they all worked fairly well. But there are still too many hiccups to feel comfortable running them for the show with an audience.

I am controlling the AP with Touchdesigner, a video production software that has integrated serial out communication. The Touchdesigner side of things works just fine. The code below is what's on the AP, and then one of the six that's on the nodes. The only difference in the code on the nodes is the value used to stop the continuous servo andwhich char to read from the packet sent to all Nodes from the AP. For example, 111111 means all screens up, 000000, all down, 111000, the first three up and last three down, etc. 333333 and 444444 is for my IR tracking LEDs relay triggering. I haven't got to that part yet.

I know this is long, but I'm begging for any help at this point - like I said, show opens tonight, and I'm at work all day =(


AP Code:
Code: Select all#include <painlessMesh.h>
#include <painlessMeshSTA.h>
#include <painlessMeshSync.h>
#include <painlessScheduler.h>
#include <SimpleList.h>

painlessMesh mesh;
bool calc_delay = false;
SimpleList<uint32_t> nodes;

//Servo Identifiers
#define SERVO_TOP_LEFT      15774220      //350 STOP    NEEDS REVISITED
#define SERVO_TOP_CENTER    15711956      //343 STOP
#define SERVO_TOP_RIGHT     15772684      //353 STOP
#define SERVO_BOTTOM_LEFT   15710311      //346 STOP
#define SERVO_BOTTOM_CENTER 15712451      //353 STOP
#define SERVO_BOTTOM_RIGHT  15773974      //360 STOP

int incomingByte = 0;   // for incoming serial data
String incomingPackage = "";
String package = "";
String msg = "";
uint32_t dest;

//#include <easyMesh.h>
#include <string>

// some gpio pin that is connected to an LED...
// on my rig, this is 5, change to the right number of your LED.
#define   LED             0       // GPIO number of connected LED

#define   BLINK_PERIOD    1000000 // microseconds until cycle repeat
#define   BLINK_DURATION  100000  // microseconds LED is on for

#define   MESH_SSID       "newsies"
#define   MESH_PASSWORD   "dreamers"
#define   MESH_PORT       5555

//easyMesh  mesh;


uint32_t sendMessageTime = 0;


void setup() {
  Serial.begin(115200);
   
  pinMode( LED, OUTPUT );

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  mesh.init(MESH_SSID, MESH_PASSWORD, MESH_PORT);
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  mesh.onNodeDelayReceived(&delayReceivedCallback);

  //mesh.scheduler.addTask( taskSendMessage );
  //taskSendMessage.enable() ;

  randomSeed(analogRead(A0));
}


void loop() {
  mesh.update();
 
  if (Serial.available() > 0) {
      // read the incoming byte:
      incomingPackage = Serial.readStringUntil('\n');

      Serial.print(incomingPackage);

      yield();

      mesh.sendBroadcast(incomingPackage);
  }
}



void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("startHere: Received from %d msg=%s\n", from, msg.c_str());
}

void newConnectionCallback( uint32_t nodeId ) {
  Serial.printf("startHere: New Connection, nodeId = %u\n", nodeId);
}


void changedConnectionCallback() {
  Serial.printf("Changed connections %s\n", mesh.subConnectionJson().c_str());

  nodes = mesh.getNodeList();

  Serial.printf("Num nodes: %d\n", nodes.size());
  Serial.printf("Connection list:");

  SimpleList<uint32_t>::iterator node = nodes.begin();
  while (node != nodes.end()) {
    Serial.printf(" %u", *node);
    node++;
  }
  Serial.println();
  calc_delay = true;
}

void nodeTimeAdjustedCallback(int32_t offset) {
  Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
}

void delayReceivedCallback(uint32_t from, int32_t delay) {
  Serial.printf("Delay to node %u is %d us\n", from, delay);
}



Node 1 Code:
Code: Select all#include <painlessMesh.h>
#include <painlessMeshSTA.h>
#include <painlessMeshSync.h>
#include <painlessScheduler.h>
#include <SimpleList.h>

painlessMesh mesh;
bool calc_delay = false;
SimpleList<uint32_t> nodes;

#include <Adafruit_PWMServoDriver.h>
//#include <easyMesh.h>

Adafruit_PWMServoDriver servo = Adafruit_PWMServoDriver();

// some gpio pin that is connected to an LED...
// on my rig, this is 5, change to the right number of your LED.
#define   LED             0       // GPIO number of connected LED
#define   TOPIR           13      // GPIO Pin for Top IR Interrupt Receiver
#define   BOTTOMIR        12      // GPIO Pin for Bottom IR Interrupt Receiver
#define   RELAY           16      //Solid State Relay to Control the IR Emitters

//Servo Directional Speeds
//Down is lower than 350 @60Hz
//Up is Higher than 350 @60Hz
#define   UP              500
#define   DOWN            200
#define   STOP            350

#define   MESH_SSID       "newsies"
#define   MESH_PASSWORD   "dreamers"
#define   MESH_PORT       5555

//easyMesh  mesh;

String package = "";

uint32_t sendMessageTime = 0;






void setup() {
  Serial.begin(115200);

  servo.begin();
  delay(10);
  servo.setPWMFreq(60);  // Analog servos run at ~60 Hz updates
   
  pinMode( LED, OUTPUT );
  pinMode( TOPIR, INPUT_PULLUP );
  pinMode( BOTTOMIR, INPUT_PULLUP );
  pinMode( RELAY, OUTPUT );

  ESP.wdtDisable();
  ESP.wdtEnable(WDTO_8S);

  mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  mesh.init(MESH_SSID, MESH_PASSWORD, MESH_PORT);
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  mesh.onNodeDelayReceived(&delayReceivedCallback);

  Serial.print("This Node is: ");
  Serial.println(ESP.getChipId());
}




//...........Periods are items to change............



void loop() {
  mesh.update();
 
  servo.setPWM(0, 0, 352);                //This cannot be removed.....................
  //Serial.println(package);

  if (package != "") {
   
    if (package.charAt(0) == '1')         
    {
      yield();
      while (digitalRead(TOPIR) == LOW)
      {
        yield();
        servo.setPWM(0, 0, UP);
        //ESP.wdtFeed();
      }
      delay(15);
      package = "";
    }
    else if (package.charAt(0) == '0')     
    {
      yield();
      while (digitalRead(BOTTOMIR) == HIGH)
      {
        yield();
        servo.setPWM(0, 0, DOWN);
        //ESP.wdtFeed();
      }
      delay(15);
      package = "";
    }
    else if (package.charAt(0) == '3')
    {
      digitalWrite(RELAY, LOW);
    }
    else if (package.charAt(0) == '4')
    {
      digitalWrite(RELAY, HIGH);
    }
   
    ESP.wdtFeed();
  }

  package == "";      //Resets Package Variable to Empty to Avoid Loop Trigger
}






void receivedCallback( uint32_t from, String &msg ) {
  //Serial.printf("startHere: Received from %d msg=%s\n", from, msg.c_str());
  package = msg.c_str();
}

void newConnectionCallback( uint32_t nodeId ) {
  Serial.printf("startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
  Serial.printf("Changed connections %s\n", mesh.subConnectionJson().c_str());

  nodes = mesh.getNodeList();

  Serial.printf("Num nodes: %d\n", nodes.size());
  Serial.printf("Connection list:");

  SimpleList<uint32_t>::iterator node = nodes.begin();
  while (node != nodes.end()) {
    Serial.printf(" %u", *node);
    node++;
  }
  Serial.println();
  calc_delay = true;
}

void nodeTimeAdjustedCallback(int32_t offset) {
  Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
}

void delayReceivedCallback(uint32_t from, int32_t delay) {
  Serial.printf("Delay to node %u is %d us\n", from, delay);
}



ANY Advice is appreciated. Let me know if any of the code (which I never commented) doesn't make sense. Thanks!
User avatar
By NickNinjaneer
#68161 Since I never had anyone respond, I kept digging and digging. I've solved my issue, and I'm hoping this helps another user.

The key to the solution is to GET RID OF WHILE LOOPS. The unreliable ESP8266 chips simply do not like them, regardless of any yields, wdtfeeds, etc.

In order to bypass the maxx number of connections to the AP (if you want direct connections, instead of hops), since the max s 4 or 5, depending on the SDK, for the Main AP, add this to the top section...
Code: Select allextern "C" {
  #include "espconn.h"
}


and in the setup function, add this...
Code: Select allespconn_tcp_set_max_con(6);




Here is the updated code for each of the Nodes...

Code: Select all#include <painlessMesh.h>
#include <painlessMeshSTA.h>
#include <painlessMeshSync.h>
#include <painlessScheduler.h>
#include <SimpleList.h>

painlessMesh mesh;
bool calc_delay = false;
SimpleList<uint32_t> nodes;

#include <Adafruit_PWMServoDriver.h>
//#include <easyMesh.h>

Adafruit_PWMServoDriver servo = Adafruit_PWMServoDriver();

// some gpio pin that is connected to an LED...
// on my rig, this is 5, change to the right number of your LED.
#define   LED             0       // GPIO number of connected LED
#define   TOPIR           12      // GPIO Pin for Top IR Interrupt Receiver
#define   BOTTOMIR        13      // GPIO Pin for Bottom IR Interrupt Receiver
#define   RELAY           16      //Solid State Relay to Control the IR Emitters

//Servo Directional Speeds
//Down is lower than 350 @60Hz
//Up is Higher than 350 @60Hz
#define   UP              500
#define   DOWN            200
#define   STOP            350

#define   MESH_SSID       "newsies"
#define   MESH_PASSWORD   "dreamers"
#define   MESH_PORT       5555
#define   CONNECT_MODE    STA_ONLY
#define   AUTH_MODE       AUTH_WPA2_PSK
#define   CHANNEL         1
#define   PHY_MODE        PHY_MODE_11G
#define   MAX_POWER       0
#define   HIDDEN          0
#define   MAXCONN         1


//easyMesh  mesh;

String package = "";

uint32_t sendMessageTime = 0;

void setup() {
  Serial.begin(115200);

  servo.begin();
  delay(10);
  servo.setPWMFreq(60);  // Analog servos run at ~60 Hz updates
   
  pinMode( LED, OUTPUT );
  pinMode( TOPIR, INPUT_PULLUP );
  pinMode( BOTTOMIR, INPUT_PULLUP );
  pinMode( RELAY, OUTPUT );

  ESP.wdtDisable();
  ESP.wdtEnable(WDTO_8S);
 
  mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  mesh.init(MESH_SSID, MESH_PASSWORD, MESH_PORT, CONNECT_MODE, AUTH_MODE, CHANNEL, PHY_MODE, MAX_POWER, HIDDEN, MAXCONN);
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  mesh.onNodeDelayReceived(&delayReceivedCallback);

  Serial.print("This Node is: ");
  Serial.println(ESP.getChipId());
}

void loop() {
  mesh.update();
 
    if (package == "") {servo.setPWM(0, 0, 372);}           
    if (package.charAt(4) == '1')         //................................
    {
      servo.setPWM(0, 0, UP);
      //delay(30);
      if (digitalRead(TOPIR) == HIGH)
      {
        package = "";
      }
    }
    else if (package.charAt(4) == '0')      //...............................
    {
      servo.setPWM(0, 0, DOWN);
      //delay(30);
      if (digitalRead(BOTTOMIR) == LOW)
      {
        package = "";
      }
    }
    else if (package.charAt(4) == '3')
    {
      digitalWrite(RELAY, LOW);
      package = "";
    }
    else if (package.charAt(4) == '4')
    {
      digitalWrite(RELAY, HIGH);
      package = "";
    }
}

void receivedCallback( uint32_t from, String &msg ) {
  //Serial.printf("startHere: Received from %d msg=%s\n", from, msg.c_str());
  package = msg.c_str();
}

void newConnectionCallback( uint32_t nodeId ) {
  Serial.printf("startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
  Serial.printf("Changed connections %s\n", mesh.subConnectionJson().c_str());

  nodes = mesh.getNodeList();

  Serial.printf("Num nodes: %d\n", nodes.size());
  Serial.printf("Connection list:");

  SimpleList<uint32_t>::iterator node = nodes.begin();
  while (node != nodes.end()) {
    Serial.printf(" %u", *node);
    node++;
  }
  Serial.println();
  calc_delay = true;
}

void nodeTimeAdjustedCallback(int32_t offset) {
  Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
}

void delayReceivedCallback(uint32_t from, int32_t delay) {
  Serial.printf("Delay to node %u is %d us\n", from, delay);
}

User avatar
By NickNinjaneer
#68599 That's an excellent point.

Yes, they are completely reliable now. Maybe it has something to do with setting pwm frequency, but before changing them from while loops to if statements, as soon as the servos reached end position, a MINIMUM of two of the six boards would reset, sometimes never reconnecting.

Now, the most I've ever lost is one board, while system is idle, but it reconnects to another node within five seconds.