Search code examples
arduinors485arduino-mkr1000

Using a MKR1400GSM and setting pin High or LOW from library keeps it LOW


I have a system using a LOLIN mini d1 that reads a battery BMS via RS485. The library works fine after a few tweeks. Now I am trying to use it with a MKR1400 to send the MQTT data via GSM.

I have encountered an issue that really puzzled me. I am using a MAX485 and I need to turn DE and RE high for sending and low for receiving. I do that in the library. Below are very simplified library to debug. If I mark out delay then the pin goes high and sends but I can not receive, with the delay set the pin stays low. I could understand that it disturbs the serial but not setting the pin high or up.

h file

#ifndef SERIALTEST_H 
#define SERIALTEST_H

#include "Arduino.h"

class ETDBms {
public:
    ETDBms();  // Constructor

    void begin(Uart &port); 
    void SerialTxControl(uint8_t pinTx);
    void SerialRxControl(uint8_t pinRx);
    void main_task(); 
    void readSerial(); // New member function to read from serial port and write to serial monitor
    
private:
    bool is_initialized;
    Uart* serial; 
    uint8_t _pinTx;
    uint8_t _pinRx;
};

#endif  // SERIALTEST_H 

cpp file

#include "SerialTest.h"

// Constructor
ETDBms::ETDBms() {
    is_initialized = false;
}

// Start processing
void ETDBms::begin(Uart &port) {
    serial = &port;
    is_initialized = true;
}

// Set the Rx and Tx pins
void ETDBms::SerialTxControl(uint8_t pinTx) {
    _pinTx = pinTx;
}

void ETDBms::SerialRxControl(uint8_t pinRx) {
    _pinRx = pinRx;
}

// Call this as fast as possible within the sketch's loop() function
void ETDBms::main_task() {
    if (is_initialized) {
        // SET the RS485 shield to transmit mode
        digitalWrite(_pinTx, HIGH);
        digitalWrite(_pinRx, HIGH);

        
        serial->print("test"); // Sending "test" over the serial port
        
        // SET the RS485 shield to receive mode
        digitalWrite(_pinTx, LOW);
        digitalWrite(_pinRx, LOW);

        
        delay(2000); // Delay for readability
    }
}

// Read from Serial1 port and write to serial monitor (Serial)
void ETDBms::readSerial() {
    if (serial->available()) {
        Serial.print("Received from Serial1: ");
        while (serial->available()) {
            char c = serial->read();
            Serial.print(c);
        }
        Serial.println();
    }
}

and the sketch

#include "SerialTest.h"

ETDBms bms;

int pinRX = 2;
int pinTX = 3;

void setup() {
  pinMode(pinRX, OUTPUT);
  pinMode(pinTX,OUTPUT);

  Serial.begin(115200);
  Serial1.begin(9600);

  bms.begin(Serial1); //baud rate 9600
  bms.SerialRxControl(pinRX);
  bms.SerialTxControl(pinTX);
}

void loop() {
  bms.main_task();
  //bms.readSerial();

}

Am I missing something specific to the MKR1400?

I tried using the code in the sketch directly but I still encounter weird Low/high behaviour. I removed all the delays in my library but it did not help.

My MAX485 will not send/recieve properly with this issue as it is not set properly. I am quite positive that the library will work once this issue is solved.


Solution

  • I did not test your code so technically this is not a definitive answer, but I see a few issues regarding your library and it is also too long as a comment, so I provide my suggestions and comments here.

    int pinRX = 2;
    int pinTX = 3;
    

    First, I assumed those are DE pin and RE pin respectively, personally I preferred to name them that way instead of to be confused as TX and RX pins. I will use pinRE for pinRX and pinDE for pinTX for the rest of the text below.

    serial->print("test") does not send out data immediately, it adds the data to the transmission queue and send in a rather slow baud rate (in your case, 9600 baud), so your code prematurely terminate the DE pin. In order to make sure that the data has been sent out, it should call serial->flush() prior deactivate the DE pin.

    The delay(2000) is not necessary, however, toggle the DE pin take time, especially when you have a long RS485 cable, it is advisable to have a delay of 50us (or longer, read the datasheet and RS485 spec) prior or after the actual data transmission.

    So here is the modified code for data transmission

        digitalWrite(_pinDE, HIGH);
        delayMicroseconds(50);  // this delay may or may not need longer
    
        serial->print("test");  // Sending "test" over the serial port
        serial->flush();        // make sure all data are sent
    
        delayMicroseconds(50);
        digitalWrite(_pinDE, LOW);
    

    For receiving, you will just need to put the MAX485 in receive mode (i.e. RE pin at LOW) by default, and only toggle the DE pin during transmission.

    void setup() {
      // your original setup code here
    
      digitalWrite(pinRE, LOW);  // in receive mode by default
    }