Max485 Arduino Code. Please help me to make it run smoother. Master slave

Sorry error with posting:

this was the start --

Hi there guys.

I am building a smart home system and currently working on the master slave rs485 part of it. I would love some tips on my code to try and make it run smoother. At the moment it is a bit frankenstein code. But it seems like good stuff.

So the Master Arduino mega with esp8266 connected receives GET request and then sends an rs485 message to the slave, which then checks its hall current sensor to see if the switch is open, if it is it will either reset or set a dual latch relay.

This is my code:

SLAVE

/*
  Measuring AC Current Using ACS712
*/
const int sensorIn = A0;
int mVperAmp = 66; // use 100 for 20A Module and 66 for 30A Module


double Voltage = 0;
double VRMS = 0;
double AmpsRMS = 0;
/*
  Measuring AC Current Using ACS712
*/


//RS485 Intros
/*-----( Import needed libraries )-----*/
#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX        10  //Serial Receive pin
#define SSerialTX        11  //Serial Transmit pin

#define SSerialTxControl 3   //RS485 Direction control
#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define Pin13LED         13

/*-----( Declare objects )-----*/
SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX

/*-----( Declare Variables )-----*/
int byteReceived;
int byteSend;
//RS485 Intros



const byte setPin =  6;            // connect to relay coil "S" pin
const byte resetPin = 7;           // connect to relay coil "R" pin

int ReplyCommand = 0;



void setup() {
  Serial.begin(9600);
  Serial.println("SerialRemote");  // Can be ignored

  pinMode(Pin13LED, OUTPUT);
  pinMode(SSerialTxControl, OUTPUT);
  pinMode(setPin, OUTPUT);
  pinMode(resetPin, OUTPUT);

  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver

  // Start the software serial port, to another device
  RS485Serial.begin(9600);   // set the data rate
}

void loop() {

  Voltage = getVPP();
  VRMS = (Voltage / 2.0) * 0.707;
  AmpsRMS = ((VRMS * 1000) / mVperAmp) - 0.08;
  Serial.print(AmpsRMS);
  Serial.println(" Amps RMS");

  //Copy input data to output
  if (RS485Serial.available())
  {
    byteSend = RS485Serial.read();   // Read the byte

    digitalWrite(Pin13LED, HIGH);  // Show activity
    delay(10);
    digitalWrite(Pin13LED, LOW);

    if (byteSend == '4')
    {

      digitalWrite (setPin, HIGH);
      delay (40);
      digitalWrite (setPin, LOW);
      ReplyCommand = 1;

    }

    if (byteSend == '3')
    {

      digitalWrite (resetPin, HIGH);
      delay (40);
      digitalWrite (resetPin, LOW);
      ReplyCommand = 1;

    }

    if (byteSend == '5')
    {


      if (AmpsRMS > 0.09)
      {
        digitalWrite (setPin, HIGH);
        delay (40);
        digitalWrite (setPin, LOW);
        ReplyCommand = 1;

      }
      else
      {
        digitalWrite (resetPin, HIGH);
        delay (40);
        digitalWrite (resetPin, LOW);
        ReplyCommand = 1;

      }
    }

    //    delay(100);
  }// End If RS485SerialAvailable

  if (ReplyCommand == 1) {
    digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit
    RS485Serial.write(byteSend); // Send the byte back
    RS485Serial.write(AmpsRMS); // Send the byte back
    delay(10);
    digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit
    ReplyCommand = 0;

  }

}

float getVPP()
{
  float result;

  int readValue;             //value read from the sensor
  int maxValue = 0;          // store max value here
  int minValue = 1024;          // store min value here

  uint32_t start_time = millis();
  while ((millis() - start_time) < 1000) //sample for 1 Sec
  {
    readValue = analogRead(sensorIn);
    // see if you have a new maxValue
    if (readValue > maxValue)
    {
      /*record the maximum sensor value*/
      maxValue = readValue;
    }
    if (readValue < minValue)
    {
      /*record the maximum sensor value*/
      minValue = readValue;
    }
  }

  // Subtract min from max
  result = ((maxValue - minValue) * 5.0) / 1024.0;

  return result;
}

Thank you in advance

MASTER

#define TIMEOUT     5000 // mS
#define CONTINUE    false
#define HALT        true
#define ECHO_COMMANDS // Un-comment to echo AT+ commands to serial monitor

#define SSID        "Jakael"
#define PASS        "PWORD" // My luggage has the same combination!

#define DEBUG true

#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX        26  //Serial Receive pin
#define SSerialTX        27  //Serial Transmit pin

#define SSerialTxControl 24   //RS485 Direction control

#define RS485Transmit    HIGH
#define RS485Receive     LOW

SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX

/*-----( Declare Variables )-----*/
int byteReceived;
int byteSend;

byte byteSendClose;

byte byteSendOpen;


// Echo module output until 3 newlines encountered.
// (Used when we're indifferent to "OK" vs. "no change" responses.)
void echoSkip()
{
  echoFind("\n");        // Search for nl at end of command echo
  echoFind("\n");        // Search for 2nd nl at end of response.
  echoFind("\n");        // Search for 3rd nl at end of blank line.
}

// Connect to the specified wireless network.
boolean connectWiFi()
{
  String cmd = "AT+CWJAP=\""; cmd += SSID; cmd += "\",\""; cmd += PASS; cmd += "\"";
  if (echoCommand(cmd, "OK", CONTINUE)) // Join Access Point
  {
    Serial.println("Connected to WiFi.");
    return true;
  }
  else
  {
    Serial.println("Connection to WiFi failed.");
    return false;
  }
}

// Read characters from WiFi module and echo to serial until keyword occurs or timeout.
boolean echoFind(String keyword)
{
  byte current_char   = 0;
  byte keyword_length = keyword.length();

  // Fail if the target string has not been sent by deadline.
  long deadline = millis() + TIMEOUT;
  while (millis() < deadline)
  {
    if (Serial1.available())
    {
      char ch = Serial1.read();
      Serial.write(ch);
      if (ch == keyword[current_char])
        if (++current_char == keyword_length)
        {
          Serial.println();
          return true;
        }
    }
  }
  return false;  // Timed out
}

// Print error message and loop stop.
void errorHalt(String msg)
{
  Serial.println(msg);
  Serial.println("HALT");
  while (true) {};
}

// Send a command to the module and wait for acknowledgement string
// (or flush module output if no ack specified).
// Echoes all data received to the serial monitor.
boolean echoCommand(String cmd, String ack, boolean halt_on_fail)
{
  Serial1.println(cmd);
#ifdef ECHO_COMMANDS
  Serial.print("--"); Serial.println(cmd);
#endif

  // If no ack response specified, skip all available module output.
  if (ack == "")
    echoSkip();
  else
    // Otherwise wait for ack.
    if (!echoFind(ack))          // timed out waiting for ack string
      if (halt_on_fail)
        errorHalt(cmd + " failed"); // Critical failure halt.
      else
        return false;            // Let the caller handle it.
  return true;                   // ack blank or ack found
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);         // Communication with PC monitor via USB
  Serial1.begin(115200);        // Communication with ESP8266 via 5V/3.3V level shifter
  pinMode(SSerialTxControl, OUTPUT);

  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver

  // Start the software serial port, to another device
  RS485Serial.begin(9600);   // set the data rate

  Serial1.setTimeout(TIMEOUT);
  Serial.println("ESP8266 Demo");

  delay(2000);

  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);

  pinMode(3, OUTPUT);
  digitalWrite(3, LOW);

  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);

  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);

  echoCommand("AT+RST", "ready", HALT);    // Reset & test if the module is ready
  Serial.println("Module is ready.");
  echoCommand("AT+GMR", "OK", CONTINUE);   // Retrieves the firmware ID (version number) of the module.
  echoCommand("AT+CWMODE?", "OK", CONTINUE); // Get module access mode.

  echoCommand("AT+CWLAP", "OK", CONTINUE); // List available access points - DOESN't WORK FOR ME

  //connect to the wifi
  boolean connection_established = false;
  for (int i = 0; i < 5; i++)
  {
    if (connectWiFi())
    {
      connection_established = true;
      break;
    }
  }
  if (!connection_established) errorHalt("Connection failed");

  delay(5000);

  echoCommand("AT+CWSAP=?", "OK", CONTINUE); // Test connection
  echoCommand("AT+CIPSTA=\"192.168.20.88\"", "OK", CONTINUE);
  echoCommand("AT+CIFSR", "", HALT);         // Echo IP address. (Firmware bug - should return "OK".)
  echoCommand("AT+CIPMUX=1", "", CONTINUE);
  echoCommand("AT+CIPSERVER=1,80", "", HALT);
}


void loop()
{

  if (Serial1.available()) // check if the esp is sending a message
  {


    if (Serial1.find("+IPD,"))
    {
      Serial.println("listening");
      delay(1000); // wait for the serial buffer to fill up (read all the serial data)
      // get the connection id so that we can then disconnect
      int connectionId = Serial1.read() - 48; // subtract 48 because the read() function returns
      // the ASCII decimal value and 0 (the first decimal number) starts at 48

      Serial1.find("pin="); // advance cursor to "pin="

      int pinNumber = (Serial1.read() - 48) * 10; // get first number i.e. if the pin 13 then the 1st number is 1, then multiply to get 10
      pinNumber += (Serial1.read() - 48); // get second number, i.e. if the pin number is 13 then the 2nd number is 3, then add to the first number

      digitalWrite(pinNumber, !digitalRead(pinNumber)); // toggle pin

      String webpage = "LED: ";

      if (pinNumber == 4) {
        digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit
        RS485Serial.write('4');          // Send byte to Remote Arduino
        delay(10);
        digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit
        webpage = webpage + "Light turned on";

      }

      if (pinNumber == 3) {
        digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit
        RS485Serial.write('3');          // Send byte to Remote Arduino
        delay(10);
        digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit
        webpage = webpage + "Blue and Light turned off";

      }

      if (pinNumber == 5) {
        digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit
        RS485Serial.write('5');          // Send byte to Remote Arduino
        delay(10);
        digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit
        webpage = webpage + "Green Light toggle";
      }

      //WebPage attempt
      

      String cipSend = "AT+CIPSEND=";
      cipSend += connectionId;
      cipSend += ",";
      cipSend += webpage.length();
      cipSend += "\r\n";

      sendData(cipSend, 1000, DEBUG);
      sendData(webpage, 1000, DEBUG);

      webpage = "<button>LED2</button>";

      cipSend = "AT+CIPSEND=";
      cipSend += connectionId;
      cipSend += ",";
      cipSend += webpage.length();
      cipSend += "\r\n";

      sendData(cipSend, 1000, DEBUG);
      sendData(webpage, 1000, DEBUG);

      // make close command
      String closeCommand = "AT+CIPCLOSE=";
      closeCommand += connectionId; // append connection id
      closeCommand += "\r\n";

      sendData(closeCommand, 3000, DEBUG); // close connection
    }
  }
}

/*
  Name: sendData
  Description: Function used to send data to ESP8266.
  Params: command - the data/command to send; timeout - the time to wait for a response; debug - print to Serial window?(true = yes, false = no)
  Returns: The response from the esp8266 (if there is a reponse)
*/
String sendData(String command, const int timeout, boolean debug)
{
  String response = "";

  Serial1.print(command); // send the read character to the esp8266

  long int time = millis();

  while ( (time + timeout) > millis())
  {
    while (Serial1.available())
    {

      // The esp has data so display its output to the serial window
      char c = Serial1.read(); // read the next character.
      response += c;
    }
  }

  if (debug)
  {
    Serial.print(response);
  }

  return response;
}

I was thinking something like..as the bytes only can be sent one at a time, maybe I should make it into an array which i loop through and send, then set something like 0 as the start transmission and 9 as the end and anything in between is slave addresses.

#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX        26  //Serial Receive pin
#define SSerialTX        27  //Serial Transmit pin

Which Arduino are you using?

I am using a mega and slave nanos.

Would you recommend using hardware serial isntead? I was saving hardware serial for communiation with esp8266. not sure how the mega will do with multiple hardware serials open...

What are your thoughts?

Thank you for your time.

Would you recommend using hardware serial isntead?

On the Mega? Absolutely. If you insist on using SoftwareSerial, RTFM. Use pins that can actually support SoftwareSerial.

not sure how the mega will do with multiple hardware serials open...

Perfectly fine.

Thank you for the reply. Can you suggest any better ways to use the reading of the current sensor and using it in the function to toggle the light.

I was thinking of trading the hall current sensors in for a little mains detection circuit.

I have heard that with a opto-isolator (i have ps2502-1), 200k resistor, Diode to ensure the mains goes in one direction and then the other side of the isolator set up like an arduino button, is it true I can safely detect mains?

The data sheet says that it is rated for 5000 volts. I was told that was what you should look out for in a mains detection opto-isolator. Some of the other statistics go over my head a bit.

Thank you for your time.

I don't really understand what your problem is. Your thread title talks about the need to make "it run smoother", with no explanation of what "it" is that needs to "run smoother", or what "run smoother" means.

The code you posted can't possibly function to send data to the SoftwareSerial instance, because it uses pins that do not support pin change interrupts.

You apparently know what is being sent (if anything actually is) between the two Arduinos, and what that data means, but it is hard for us to jump back and forth between the master and slave code, to see what a 5 means. Some comments in the master describing what the value being sent is supposed to cause to happen would be useful.

Calling the received value byteSend does not make sense to me. byteSent, maybe, but receivedValue would certainly be better. I just cringe when I see a type (byte) used in the name of the variable that is not of that type. I've just about given up on even reading the code, or trying to help.

Some comments in the slave code that describe what it expects to receive, and what to do when a particular value is received, would certainly be useful.

I can't see how the value read from a current sensor connected to the slave would be useful for toggling the state of a light, or how the master is supposed to know that that happened.

Hi there paul,

Thank you for the continued correspondence. I am sorry for the frustration. I shall re write the code and re post it to make it easier to understannd.

To reference a few of your statements.

  1. The 26 and 27 for software serial seems to work. Maybe it is not working as effectively as a result of those pins being unable to handle pin change interupts. I figure this is events that occur that trigger functions based on pin readings?

  2. The hall current sensor isnt used by the master, but by the slave in the exact instance it receives a 5 over the rs485 aerial communication. If it is on and a 5 is received it will switch relay to opposite state. 3 and 4 were used to set or reset the relay respectively.

  3. 3,4 and 5 are just simple messages which i have hardcoded for testing.

  4. And of course sorry. By smoother i mean...the sometimes delay betweem GET request sent, then rs485 message sent, then received and light changed.

Once again thank you for your help. I will re write it to make it easier to read. Sorry about the frankenstein code, i am new to c++ and the code is still very much in development.