Hardware wise, RX is connected to RX and TX is connected to TX. I am seeing data transmission both ways on the bus. If i terminate with the recommended 120 Ohm resistor, i think the M4 runs out of poop and data stops.
During operation, i get the "Error trying to execute readAveragedMeasuredValue(): Not enough space in buffer" error generated in the code. I do not see how i can increase the buffer size and i've never used an M4 or a SFC6000 before.
Here's the code:
```cpp
// Sensirion sourced
// Generator: sensirion-driver-generator 0.23.0
// Product: sfx6xxx
// Model-Version: 2.0.0
// FROM https://forum.arduino.cc/t/samd51-rs485-hardware-driven-te-pin/687055
// NOTES FROM AUTHOR
// The following code activates the TE (Tranmit Enable) on D10 (PA20)
// using an Adafruit Feather M4, with TX and RX on D1 and D0 respectively:
#include <Arduino.h>
#include <SensirionUartSfx6xxx.h>
// Adjust as needed for you Arduino board.
// [Serial, Serial1, Serial2, etc.]
#define SENSOR_SERIAL_INTERFACE Serial1
SensirionUartSfx6xxx sensor;
//static char errorMessage[64];
static char errorMessage[256];
static int16_t error;
// Convert Serial1 for RS485 operation on the Adafruit Feather M4
// this is per the second link
void setup() {
Serial1.begin(115200); // Initialise Serial1 (on SERCOM5)
while (!Serial1); // Wait for the console to open
SERCOM5->USART.CTRLA.bit.ENABLE = 0x0; // Disable SERCOM5
while (SERCOM5->USART.SYNCBUSY.bit.ENABLE); // Wait for synchronization
PORT->Group[g_APinDescription[10].ulPort].PINCFG[g_APinDescription[10].ulPin].bit.PMUXEN = 1; // Enable the TE output on D10
PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg |= PORT_PMUX_PMUXE(2);
SERCOM5->USART.CTRLA.bit.TXPO = 0x3; // Set TXPO to 3 to activate the TE output
SERCOM5->USART.CTRLC.bit.GTIME = 0x2; // Set the RS485 guard time
SERCOM5->USART.CTRLA.bit.ENABLE = 0x1; // Enable SERCOM5
while (SERCOM5->USART.SYNCBUSY.bit.ENABLE); // Wait for synchronization
// initialize serial interface for logging-- THIS IS THE CONSOLE
Serial.begin(115200);
while (!Serial) {
delay(100);
}
// initialize serial interface for sensor communication
SENSOR_SERIAL_INTERFACE.begin(115200);
while (!SENSOR_SERIAL_INTERFACE) {
delay(100);
}
sensor.begin(SENSOR_SERIAL_INTERFACE);
error = sensor.deviceReset();
if (error != NO_ERROR) {
Serial.print("Error trying to execute deviceReset(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
delay(2000);
int8_t serialNumber[64] = {0};
error = sensor.getSerialNumber(serialNumber, 64);
if (error != NO_ERROR) {
Serial.print("Error trying to execute getSerialNumber(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
Serial.print("serialNumber: ");
Serial.print((const char*)serialNumber);
Serial.println();
error = sensor.setSetpoint(2);
if (error != NO_ERROR) {
Serial.print("Error trying to execute setSetpoint(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
}
void loop() {
float averagedMeasuredValue = 0.0;
error = sensor.readAveragedMeasuredValue(50, averagedMeasuredValue);
if (error != NO_ERROR) {
Serial.print("Error trying to execute readAveragedMeasuredValue(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
if (error == STATUS_CODE_SENSOR_MEASURE_LOOP_NOT_RUNNING_ERROR) {
Serial.println(
"Most likely the valve was closed due to overheating "
"protection.\nMake sure a flow is applied and restart the "
"script.");
}
delay( 2500 );
return;
}
Serial.print("averagedMeasuredValue: ");
Serial.print(averagedMeasuredValue);
Serial.println();
delay( 2500 );
}
I went into HardwareSerial.cpp per another forum question, but was unsuccessful figuring out what i could change and even if it is the right place.
I think I2C would be easier, but it doesn't fit in well with our environment.
RS485 is a multidrop bus, unlike RS232 which is point-to-point and requires a crossover cable. There isn't really an "RX" or "TX", so i used incorrect nomenclature. I used names to match the naming of RX and TX (D1 and D0) of the M4 Express. I purchased the M4 because it was supposed to have RS485 capability, but i am concerned I'll need to add a 485 transceiver since the Arduino isn't true differential.
My question was regarding what to do about the serial buffer overflow (but i do appreciate other comments). I'm clueless since i discovered there isn't a native driver.
While the M4's USARTs support RS-485, this does not include the differential transceiver.
Instead the M4 provides a Transmit Enable (TE) pin that is driven high to activate the transceiver during data transmission. It also has the ability to set the guard time (transmit delay extention) after each byte has been tranmitted. Furthermore, the M4 facilitates collision detection for bidirectional transfers as well. These features require the configuration of the USART's registers that are detailed in the SAMD51 datasheet.
I may have to. They are used to control MFC's for individual equipment in a large NOAA measurement site and the risk of I2C cable damage as the massive amount of cables and equipment is worked on/moved is high. I have also found quick disconnects that are impossible to connect wrong are highly preferred.
THANK YOU MartinL!
I didn't read enough in advance... the Adafruit documentation is not very detailed. All the feather M0 work has required reading the Cortex M0 specs, and i guess i should have done this first. We are in a massive hurry with a deadline and limited funds... what else is new in engineering?
I will see if i can find a PTH transceiver in my stock. I know i have SMT.
Do you think the M4 buffer size problem will be fixed once i get the right transceivers? The line is very short and though the signals look poor, i have been getting some data transfers as i occasionally see the CRC post errors.
I now have good looking RS485 transmit data after adding a driver/receiver.
However, i can't get a TXEN signal to release the bus. Pin 10 seems to be in the undriven floating state which results in TXN always being on (hence the ability to transmit).
Per MartinL's posting (referenced above), SAMD51 RS485 Hardware driven TE pin, his TXEN is on pin 10. I've looked at all pins with a scope and none appear to be changing in sync with the TX data stream.
When i run his code alone, it works. Mine doesn't. Does anyone see what might be wrong? I'm going to start digging into the Sensirion Uart library next.
TXEN stops happening when this is called
// initialize serial interface for sensor communication
// THIS IS WHAT CLOBBERS THE PIN 10 TXEN
SENSOR_SERIAL_INTERFACE.begin(115200);
while (!SENSOR_SERIAL_INTERFACE) {
delay(100);
}
```cpp
// Sensirion sourced
// Generator: sensirion-driver-generator 0.23.0
// Product: sfx6xxx
// Model-Version: 2.0.0
// FROM https://forum.arduino.cc/t/samd51-rs485-hardware-driven-te-pin/687055
// NOTES FROM AUTHOR
// The following code activates the TE (Tranmit Enable) on ** D6 per reasearch (author says D10 (PA20) )
// using an Adafruit Feather M4, with TX and RX on D1 and D0 respectively:
// my notes
// Try MC34050 RS422 (not supposed to work, but this has an enable pin.
// Ard TX to pin 15 (DR1 in)
// Ard RX to pin 3 (REC1 out)
// cable white (D+) to chip pin 14 (sig+)
// cable black (D-) to chip pin 13 (sig-)
// Ard D10 goes to both pin 4 (DR1 EN) to pin 12 !(REC EN)
// Pin 1 (REC1 IN-) tied to Pin 13 (!DR1 OUT)
// pin 2 (REC1 IN+) tied to pin 14 (DR1 OUT)
// Pin 8 = GND
// Pin 16 = Vcc (+5V)
// tie pins 6 and 7 together to avoid RCV2 oscillation
// 120 Ohms across pins 13 and 14 if this works (termination)
#include <Arduino.h>
#include <SensirionUartSfx6xxx.h>
// // Adjust as needed for you Arduino board.
// // [Serial, Serial1, Serial2, etc.]
// #define SENSOR_SERIAL_INTERFACE Serial1
SensirionUartSfx6xxx sensor;
//static char errorMessage[64];
static char errorMessage[256];
static int16_t error;
// Convert Serial1 for RS485 operation on the Adafruit Feather M4
// this is per the second link
void setup() {
// try configuring pin 10 as an output
// pinMode (6, OUTPUT); // didn't work- always low
Serial1.begin(115200); // Initialise Serial1 (on SERCOM5)
while (!Serial1); // Wait for the console to open
SERCOM5->USART.CTRLA.bit.ENABLE = 0x0; // Disable SERCOM5
while (SERCOM5->USART.SYNCBUSY.bit.ENABLE); // Wait for synchronization
PORT->Group[g_APinDescription[10].ulPort].PINCFG[g_APinDescription[10].ulPin].bit.PMUXEN = 1; // Enable the TE output on D10
PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg |= PORT_PMUX_PMUXE(2);
// below from data sheet Table 6-1
// pad[0] TxD; PAD[1] = RxD, Trans enable should be PAD[2]
// SERCOM PADS for M4 Express CHIP pin/Feather pins for SERCOM5: PAD[0] = 39 / TX_D1 JP1-2; PAD[1] = 40 / RX_D0 JP1-3; PAD[2] = 37 / ; PAD[3] = 38
SERCOM5->USART.CTRLA.bit.TXPO = 0x3; // Set TXPO to 3 to activate the TE output and TxD: definition page 870 section 34.7
SERCOM5->USART.CTRLC.bit.GTIME = 0x2; // Set the RS485 guard time
SERCOM5->USART.CTRLA.bit.ENABLE = 0x1; // Enable SERCOM5
while (SERCOM5->USART.SYNCBUSY.bit.ENABLE); // Wait for synchronization
// initialize serial interface for logging-- THIS IS THE CONSOLE
Serial.begin(115200);
while (!Serial) {
delay(100);
}
// Adjust as needed for you Arduino board.
// [Serial, Serial1, Serial2, etc.]
#define SENSOR_SERIAL_INTERFACE Serial1
// initialize serial interface for sensor communication
SENSOR_SERIAL_INTERFACE.begin(115200);
while (!SENSOR_SERIAL_INTERFACE) {
delay(100);
}
sensor.begin(SENSOR_SERIAL_INTERFACE);
error = sensor.deviceReset();
if (error != NO_ERROR) {
Serial.print("Error trying to execute deviceReset(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
delay(2000);
int8_t serialNumber[64] = {0};
error = sensor.getSerialNumber(serialNumber, 64);
if (error != NO_ERROR) {
Serial.print("Error trying to execute getSerialNumber(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
Serial.print("serialNumber: ");
Serial.print((const char*)serialNumber);
Serial.println();
error = sensor.setSetpoint(2);
if (error != NO_ERROR) {
Serial.print("Error trying to execute setSetpoint(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
}
void loop() {
float averagedMeasuredValue = 0.0;
error = sensor.readAveragedMeasuredValue(50, averagedMeasuredValue);
if (error != NO_ERROR) {
Serial.print("Error trying to execute readAveragedMeasuredValue(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
if (error == STATUS_CODE_SENSOR_MEASURE_LOOP_NOT_RUNNING_ERROR) {
Serial.println(
"Most likely the valve was closed due to overheating "
"protection.\nMake sure a flow is applied and restart the "
"script.");
}
delay( 2500 );
return;
}
Serial.print("averagedMeasuredValue: ");
Serial.print(averagedMeasuredValue);
Serial.println();
delay( 2500 );
}
Looking at your code, it appears as though you're setting up Serial1 two times. Calling Serial1/SENSOR_SERIAL_INTERFACE begin() function a second time will disable the TE settings that were set previously.
You should be able to just configure the Serial1 (SERCOM5) registers to enable the TE pin, then pass this (C++) object as a reference to the SensirionUartSfx6xxx sensor.begin() function.
In this way you'll get the TE pin to automatically operate on D10, but as this change has been made at the register level, Sensirion's sensor object should remain unaffected:
// Adjust as needed for you Arduino board.
// [Serial, Serial1, Serial2, etc.]
#define SENSOR_SERIAL_INTERFACE Serial1
// initialize serial interface for sensor communication
SENSOR_SERIAL_INTERFACE.begin(115200);
while (!SENSOR_SERIAL_INTERFACE) {
delay(100);
}
SERCOM5->USART.CTRLA.bit.ENABLE = 0x0; // Disable SERCOM5
while (SERCOM5->USART.SYNCBUSY.bit.ENABLE); // Wait for synchronization
PORT->Group[g_APinDescription[10].ulPort].PINCFG[g_APinDescription[10].ulPin].bit.PMUXEN = 1; // Enable the TE output on D10
PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg |= PORT_PMUX_PMUXE(2);
// below from data sheet Table 6-1
// pad[0] TxD; PAD[1] = RxD, Trans enable should be PAD[2]
// SERCOM PADS for M4 Express CHIP pin/Feather pins for SERCOM5: PAD[0] = 39 / TX_D1 JP1-2; PAD[1] = 40 / RX_D0 JP1-3; PAD[2] = 37 / ; PAD[3] = 38
SERCOM5->USART.CTRLA.bit.TXPO = 0x3; // Set TXPO to 3 to activate the TE output and TxD: definition page 870 section 34.7
SERCOM5->USART.CTRLC.bit.GTIME = 0x2; // Set the RS485 guard time
SERCOM5->USART.CTRLA.bit.ENABLE = 0x1; // Enable SERCOM5
while (SERCOM5->USART.SYNCBUSY.bit.ENABLE); // Wait for synchronization
sensor.begin(SENSOR_SERIAL_INTERFACE);