4 x Half duplex HC-12 wireless boards on two Arduino's (Bidirectional)

Hi,

Ok so this is my setup. I have 2 Arduino Mega boards, each with one half duplex HC-12 wireless RF module attached. Using software serial board one transmits analogue values from a pot to board two which receives the value and subsequently outputs a PWM signal to control revs on an engine. So one board is ALWAYS transmitting and the other ALWAYS receiving. This works well.

Now, here is my issue....

I am wanting to take this further and attach an analogue water temp sensor to board 2. This value is then sent back to board 1 where it is displayed on an LCD. All simple enough right! But I need these engine rev control and water temp values sent between the two simultaneously with minimal delay, so basically bidirectional. I know with the current setup I could technically send values back and forth between the two, but while one is transmitting the other is receiving, they cannot both be receiving and transmitting at the same time. This won't do as the application requires minimal latency for controlling engine revs and the same for water temp feedback.

Instead of having to get my head around full duplex RF wireless boards and then having to alter my code massively I have another idea.

Could I attach a second set of HC-12 boards to each Mega and use a second software serial? So per Mega, while one HC-12 has a soul job of transmitting, the other has the job of receiving. The second Mega would have the same configuration. Each pair of HC-12' would then be setup to use different channels.

From this URL under limitations:

If using multiple software serial ports, only one can receive data at a time

Technically, even though each Mega would be running 2 software serials, only one software serial on each Mega would be receiving data (the other transmitting).

The long and short of it is..... Would this work?

Many thanks in advance.
Jim

Anyone any advice on this?

Its really depends on how well a HC12 receiver can reject an adjacent HC12 transmitter in close proximity, and this info isnt available from the data sheets.
You would have to try it , but its unlikley to work due to receiver overload.
Essentially you really need a full duplex radio with an antenna diplexer, and these arnt cheap.

Isn't a HC-12 a bi-directional wireless device?

...R

No, the Hc12 is a half duplex radio, ie it can send or receive but not both at the same time.
The serial port looks like its bidirectional as the RF switching is done by the on board controller.
Most cheap bi directional radios work like this, but incur a latency delay when data has to be sent in both directions simultaeneously .

mauried:
No, the Hc12 is a half duplex radio, ie it can send or receive but not both at the same time.

That sounds like hair-splitting. The same is true of an nRF24L01+ but nobody treats them as half-duplex.

...R

A full duplex radio can transmit and receive at the same time , as they use differant frequencies for both paths and an antenna diplexer to isolate the radios transmitter and receiver from each other.
This means you can have simultaenous transmission in both directions continuously.
If you try doing this with a nRF24L01+ it wont work, you will lose data.

Hi all,

I have small skid steer machine which I have modified to take control from two Arduino's using HC-12 wifi communication with software serial. So I have a controller with Arduino Mega, HC-12, LCD, pot and two single axis joysticks which transmits analog signals to the Arduino Mega mounted on the machine. PWM outputs then appropriately drive each hydraulic solenoid to control direction & speed. The pot value controls engine revs. This works fine.

The HC-12' are half duplex and so simultaneous data transmission is not possible. The controller transmits, the machine receives.... simple as that.

I want to be able to send engine fuel level, water temp and oil pressure back to the LCD on the controller without causing any latency / lag or delay to the skid steer, thus maintaining control functionality and safety.

I know the HC-12 are transceivers and so can send and receive, but the delay or induced stop in machine control while engine values are sent back to the controller is unacceptable.

Has anyone any ideas on how I can achieve my goal?

Thanks

Hi,
What you are looking for is called FULL DUPLEX operation, that is both units transmit continuously.

This is done with TX/Rx units that each have their Tx frequencies different to each other.

The problem is that these frequencies have to be significantly spaced to prevent interference with each other.
There are filter/cavity resonators to help with this job, but that increases size and cost.

There may be solutions that use different frequency bands to minimize the interference, called desensing of the RX.

So you need to search for a FULL DUPLEX system to get full two way information.

Tom... :slight_smile:
PS. Your machine sounds interesting, with hydraulics etc, can you post a picture?

Excuse me, I just Googled the "HC-12 wifi " and find they are not wifi. They operate on 433MHz. They also rate the devices to 5000BPS. There may be several problems that are slowing the data transmission, including interference from some other devices operating on the same or adjacent frequencies.

Please show the software you are using on both ends. There must be some problems. What baud rate are you using? There is no physical reason for not being able to send and receive much more data than you are currently doing.

Paul

How is this different from your other Thread where you have already been getting advice?

I am suggesting to the Moderator to merge them.

...R

mauried:
If you try doing this with a nRF24L01+ it wont work, you will lose data.

I am well aware of that. But in practice you can use nRF24s (and HC12s, I suspect) in such a way that you get useful 2-way communication. A round-trip message just takes a few millisecs.

I have seen nothing in the OP's comments that suggests he requires the data to move in both directions at the exact same millisecond.

...R

@Jim_cliff, please do not cross-post. Threads merged.

Sorry for the cross post.
This is really frustrating me lol!

Ok, thanks for the information guys. I'll do some research into full duplex, but I'll also try the method suggested by Robin and measure the delay.

Will keep you posted.

Jim_cliff:
but I'll also try the method suggested by Robin and measure the delay.

I didn't actually suggest measuring the delay - though I am not against it.

Let's go back to basics. How often (how many messages per second) are you now sending data from Mega A to Mega B?

And why are you wasting your time with SoftwareSerial on a Mega when it has 3 spare HardwareSerial port?

...R

I'm sending as many messages as possible. But in total there are 4 separate values sent before being repeated.

I've had to use a 25ms delay before repeating the values as I found I was getting erratic readings on the receiver end. Just adding 25ms between sending the next set of values has helped stabilise it massively. Obviously if I could run the code without the delay this would massively speed things up. When I tried this however; every half second one or two of my received values would be 0.

I will post the code tomorrow.

Tbh I didn't realise I could use an additional hardware serial for this. What would be the advantages of of the hardware over software serial?

Jim_cliff:
I've had to use a 25ms delay before repeating the values as I found I was getting erratic readings on the receiver end.

In that 25ms interval there should be more than enough time to receive a message from the the other Mega without needing a second pair of HC-12s. Of course to do that you must not use the delay() function. Have a look at how millis() is used to manage timing without blocking in Several things at a time

HardwareSerial is far superior to SoftwareSerial. It can operate at higher baud rates, it can send and receive at the same time and it does not require the main processor when it is receiving or sending a byte.

...R

That is very true Robin,

I didn't think of that. I'll add that to the list of modifications I need to make to my code along with hardware serial. The code below is what I'm now using. I've replaced the rev pot with a SPDT rocker. So revs are either at idle or flat out.

This code was taken from the link below and modified to suit (Thanks Robin2 - this was put together by you. Your hard work is much appreciated).
http://forum.arduino.cc/index.php?topic=288234.0

Controller - Transmit

Few points to mention:

I found I couldn't transmit the full analogue 1023 bits (4 digit value) using this code below, so thats why I've mapped the input from 0 - 998. This works, so I wasn't to fussed about resolving it.

#include <SoftwareSerial.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C  lcd(0x3F,2,1,0,4,5,6,7);

SoftwareSerial mySerial(10, 11); //RX, TX

const int an2 = A14;
const int an3 = A15;
const int dRhigh = 36;

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  
  pinMode(dRhigh, INPUT_PULLUP);
  
}

void loop() {
  
  int in2(analogRead(an2));
  int intTL = map(in2, 0, 1023, 0, 998);

  int in3(analogRead(an3));
  int intTR = map(in3, 0, 1023, 0, 998);

  int Rhigh(digitalRead(dRhigh));

  mySerial.print("<");
  mySerial.print(intTL);
  mySerial.print(", ");
  mySerial.print(intTR);
  mySerial.print(", ");
  mySerial.print(Rhigh);
  mySerial.println(">");
  
  delay(25);

}

Machine - Receive

#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); //RX, TX

const byte buffSize = 50;
char inputBuffer[buffSize];
const char startMarker = '<';
const char endMarker = '>';
byte bytesRecvd = 0;
boolean readInProgress = false;
boolean newDataFromPC = false;

char messageFromPC[buffSize] = {0};

int intlt = 0;
int intrt = 0;
int Rhigh = 1;

int LTF = 0;
int LTR = 0;
int RTF = 0;
int RTR = 0;
int LTCali = 0;
int RTCali = 0;

unsigned long curMillis;

unsigned long prevReplyToPCmillis = 0;
unsigned long replyToPCinterval = 1000;


void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);

  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(2, OUTPUT);
  analogWrite(5, 0);
  analogWrite(4, 0);
  analogWrite(3, 0);
  analogWrite(2, 0);

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

}

void loop() {
  
  curMillis = millis();
  
  // Recieve data from controller
  recData();

  // Output to machine
  output();   

  // Serial output for debugging
  serialOutput();

}

void output() {

  // Joystick mid count
  LTCali = 491;
  RTCali = 520;
  int LTFthres = LTCali - 2;
  int LTRthres = LTCali + 2;
  int RTFthres = RTCali - 2;
  int RTRthres = RTCali + 2;
  
  // Left side Control (499 middle)
  if (intlt <= LTFthres) {
    LTF = map(intlt, LTFthres, 0, 0, 255);
    analogWrite(5, LTF); }
  else { analogWrite(5, 0); }

  if (intlt >= LTRthres) {
    LTR = map(intlt, LTRthres, 998, 0, 255);
    analogWrite(4, LTR); }
  else { analogWrite(4, 0); }  

  // Right side Control (499 middle)
  if (intrt <= RTFthres) {
    RTF = map(intrt, RTFthres, 0, 0, 255);
    analogWrite(3, RTF); }
  else { analogWrite(3, 0); }

  if (intrt >= RTRthres) {
    RTR = map(intrt, RTRthres, 998, 0, 255);
    analogWrite(2, RTR); }
  else { analogWrite(2, 0); }  

  // Aux functions

  if (Rhigh == 0) { digitalWrite(14, HIGH); } else {digitalWrite(14, LOW); } 

}


void recData() {

    // receive data from PC and save it into inputBuffer
    
  if(mySerial.available() > 0) {

    char x = mySerial.read();

      // the order of these IF clauses is significant
      
    if (x == endMarker) {
      readInProgress = false;
      newDataFromPC = true;
      inputBuffer[bytesRecvd] = 0;
      parseData();
    }
    
    if(readInProgress) {
      inputBuffer[bytesRecvd] = x;
      bytesRecvd ++;
      if (bytesRecvd == buffSize) {
        bytesRecvd = buffSize - 1;
      }
    }

    if (x == startMarker) { 
      bytesRecvd = 0; 
      readInProgress = true;
    }
  }
}
 
void parseData() {

    // split the data into its parts
    
  char * strtokIndx; // this is used by strtok() as an index
  
  strtokIndx = strtok(inputBuffer,",");    
  intlt = atoi(strtokIndx);
  
  strtokIndx = strtok(NULL, ","); 
  intrt = atoi(strtokIndx);    
  
  strtokIndx = strtok(NULL, ","); 
  Rhigh = atoi(strtokIndx);    
  
}

void serialOutput() {

  if (newDataFromPC) {
    newDataFromPC = false;
    Serial.print(" Tleft: ");
    Serial.print(intlt);
    Serial.print(" Tright: ");
    Serial.print(intrt);
    Serial.print(" Hrevs: ");
    Serial.print(Rhigh);
    Serial.println(">");
  }
}

It might not be the 'right' way of doing it, but it works so far. Over the next few weeks I will develop the code further to include using the millis() instead of delay(), hardware serial, an emergency stop, wireless failure shutdown and a few more services.

My main priority at the minute is sending back oil pressure (digital), water temp (analogue) and fuel level (analogue) from the machine arduino to the controller arduino without causing interruption of machine control.

I understand how to implement the millis() function now, but how would I go about sending data back to the controller during this time?

ps. Tom, I will post pictures when the wheels are on and the machine is outside. Currently its on axle stands lol.

Thanks

Just an update:

I've adjusted the code and pins for working on hardware serial 1 @ 9600 baud rate (both Mega's).

However my serial output on the receiving board (machine) is showing random outputs every 4 or 5 lines and subsequently adjusting the revs, wheel direction / speed on its own. The only way I can stop this is to increase the transmission delay to 75ms on the controller (transmitting) board.

As I see it at the minute the wireless comms functioned quicker and more stable using software serial.

Do I need to adjust baud rates or is there something else causing this?

Actually it's time to take a really hard look at what you are doing. First, you really, really need to study the way to do many things at a time for both your control unit and your receiving board on the machine.

Then you need to design a communications protocol that will require an acknowledgement from the machine board before you send another control message. You will discover the acknowledgement message can also carry the information about the machine that you are needing.

In addition, you can make the protocol be a "dead man switch", so if you have a medical emergency and cannot send a control message, the machine will time out and shut down.

Paul