Arduino receiving data through HC12 but the code gets stuck on one value only

Hi,

I am currently working on a project in which I wish to control a DC motor with the tempo from an audio recording software (ableton). Through Max for Live I send Serial data (the tempo) as an integer value from 0 to 999 and above.

The idea is to have this unit in the studio and control it wirelessly from one room to another. The code works fine when not using wireless communication. But I decided to use the HC12 modules to get data from one side and send it all to the other room. Here is my code on the receiver side which controls the motor:

#include <SoftwareSerial.h>

SoftwareSerial HC12 (10, 11);

int motor1pin1 = 2;
int motor1pin2 = 3;
int LEDred = 5;
int LEDgreen = 6;
int LEDblue = 7;

void setup() {

  Serial.begin(9600); 
  HC12.begin(9600);
  pinMode(motor1pin1, OUTPUT);
  pinMode(motor1pin2, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(LEDred, OUTPUT);
  pinMode(LEDgreen, OUTPUT);
  pinMode(LEDblue, OUTPUT);
  

}

void loop() {

  while (HC12.available()){
  
    int value1 = HC12.read();
    Serial.write(value1);
  
     if (value1 == 0) {
       analogWrite(9, 0);
       digitalWrite(motor1pin1, HIGH);
       digitalWrite(motor1pin2, LOW);
       digitalWrite(LEDred, LOW);
       digitalWrite (LEDgreen, LOW);
       digitalWrite(LEDblue, HIGH);
    
     }
     else if ((value1 > 0) && (value1 < 55)) {
       analogWrite(9, 55);
        digitalWrite(motor1pin1, HIGH);
        digitalWrite(motor1pin2, LOW);
        digitalWrite (LEDgreen, LOW);
        digitalWrite(LEDred, HIGH);
        digitalWrite(LEDblue, LOW);
      }
      else if ((value1 >= 55) && (value1 <= 254)) {
        analogWrite(9, value1);
        digitalWrite(motor1pin1, HIGH);
        digitalWrite(motor1pin2, LOW);
        digitalWrite(LEDred, LOW);
        digitalWrite(LEDgreen, HIGH);
        digitalWrite(LEDblue, LOW);
  
      }
      else if (value1 >= 255) {
        analogWrite(9, 255);
        digitalWrite(motor1pin1, HIGH);
        digitalWrite(motor1pin2, LOW);
        digitalWrite(LEDred, HIGH);
        digitalWrite (LEDgreen, LOW);
        digitalWrite(LEDblue, LOW);
     }
}
}

Basically the motor should receive values only between "55" and "255" since below it does not turn and above it is no longer relevant. "0" stands for "stop", "0" to "54" should be "55", "55" to "255" should use the tempo value from the software and above "255" should remain "255" being the maximum speed.

The LED gives a visual understanding of the current situation to be able to quickly solve it from the audio software ( increasing the tempo value or decreasing it).

On the other side I just use a code I found on the internet ,which seems to work seemlessly, to send values through the Serial Monitor:

#include <SoftwareSerial.h>

SoftwareSerial HC12(10, 11); // HC-12 TX Pin, HC-12 RX Pin

void setup() {
  Serial.begin(9600);             // Serial port to computer
  HC12.begin(9600);               // Serial port to HC12

}

void loop() {
  while (HC12.available()) {        // If HC-12 has data
    Serial.write(HC12.read());      // Send the data to Serial monitor
  }
  while (Serial.available()) {      // If Serial monitor has data
    HC12.write(Serial.read());      // Send that data to HC-12
  }
}

The issue I am facing at the moment is that regardless of the value being sent wirelessly only the part

     else if ((value1 > 0) && (value1 < 55)) {
       analogWrite(9, 55);
        digitalWrite(motor1pin1, HIGH);
        digitalWrite(motor1pin2, LOW);
        digitalWrite (LEDgreen, LOW);
        digitalWrite(LEDred, HIGH);
        digitalWrite(LEDblue, LOW);

becomes true and therefore the LED stays red and the motor runs slowly. BUT the Serial monitor displays the correct value (with Serial.write(value1)) when being inserted directly as follow:

void loop() {
  // put your main code here, to run repeatedly:
  while (HC12.available()){
  
    int value1 = HC12.read();
    Serial.write(value1);
  
     if (value1 == 0) {

However when inserted in "else if" loops: "70" "80" and "90" only appear as "0" (also "170", "180" and "190").

Is there anything I am missing the code? is there any additional information regarding the HC12 or similar issues known?

I would be very grateful if anyone could have a look into it and eventually help me figuring it out :slight_smile:

All the best!

ps: if you wonder what this is all for, this whole system aims at emulating the Leslie Cabinet effect on a microphone (recording phase instead of reproduction phase of a project).

how do you send that ? in ASCII ?

this will just read 1 byte

I doubt this - or it's not the same code


I would suggest to study Serial Input Basics to handle this

Hey Jackson,

thank you for your answer. I have been checking in to understand what is ASCII at the very beginning and I could not quite figure it out. As far as I can see I an sendign integer values from Max for live to the Serial port and receive the exact same one in Arduino (when not using wireless connection).

Here is the code without wireless:

int motor1pin1 = 2;
int motor1pin2 = 3;
int LEDred = 11;
int LEDgreen = 12;
int LEDblue = 13;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); 
  pinMode(motor1pin1, OUTPUT);
  pinMode(motor1pin2, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
  int value1 = Serial.read();
  
  if (value1 == 0) {
    analogWrite(9, 0);
    digitalWrite(motor1pin1, HIGH);
    digitalWrite(motor1pin2, LOW);
    digitalWrite(LEDred, LOW);
    digitalWrite (LEDgreen, LOW);
    digitalWrite(LEDblue, HIGH);
  }
  if ((value1 > 0) && (value1 < 55)) {
    analogWrite(9, 55);
    digitalWrite(motor1pin1, HIGH);
    digitalWrite(motor1pin2, LOW);
    digitalWrite (LEDgreen, LOW);
    digitalWrite(LEDred, HIGH);
    digitalWrite(LEDblue, LOW);
  }
  if ((value1 >= 55) && (value1 <= 254)) {
  analogWrite(9, value1);
  digitalWrite(motor1pin1, HIGH);
  digitalWrite(motor1pin2, LOW);
  digitalWrite(LEDred, LOW);
  digitalWrite(LEDgreen, HIGH);
  digitalWrite(LEDblue, LOW);
  }
  if (value1 >= 255) {
  analogWrite(9, 255);
  digitalWrite(motor1pin1, HIGH);
  digitalWrite(motor1pin2, LOW);
  digitalWrite(LEDred, HIGH);
  digitalWrite (LEDgreen, LOW);
  digitalWrite(LEDblue, LOW);
  }
}

And this works fine. Whenever I send a new value from the audio software the motor's speed changes.

I don't think that I need more than one byte at the time. At the moment it looks like it is reading this one piece of information and stays stuck with it.

Thank you, I will have a look at it. Coding is very new to me and is quite confusing...

All the best!

a byte can only code 256 values, typically 0 to 255 if it's unsigned or -128 to 127 if it's signed.
if you want to send values from 0 to 999 and above it can't fit in one byte.

Could you upload this code

#include <SoftwareSerial.h>
SoftwareSerial HC12(10, 11); // HC-12 TX Pin, HC-12 RX Pin

void setup() {
  Serial.begin(115200);       // Serial port to computer
  HC12.begin(9600);           // Serial port to HC12
}

void loop() {
  if (HC12.available()) {
    int c = HC12.read();
    Serial.print("Got: 0x"); Serial.print(c, HEX);
    if (isprint(c)) {
      Serial.print("\t->["); Serial.write(c); Serial.write(']');
    }
    Serial.println();
  }
}

and open the Serial monitor at 115200 bauds and play a bit with the emitter side and copy and paste here what you got in the Serial monitor (using code tags)

Sure, I will try it out and let you know.

Got: 0x39	->[9]
Got: 0x39	->[9]
Got: 0xA
Got: 0x31	->[1]
Got: 0x32	->[2]
Got: 0x30	->[0]
Got: 0xA
Got: 0x39	->[9]
Got: 0x39	->[9]
Got: 0x39	->[9]
Got: 0xA
Got: 0x35	->[5]
Got: 0x36	->[6]
Got: 0xA
Got: 0x31	->[1]
Got: 0x33	->[3]
Got: 0x30	->[0]
Got: 0xA

It seems that it is spliting the values in digits from 0 to 9.

You are sending ascii followed by a new line.

As mentioned I would suggest to study Serial Input Basics to handle this

Hey Jackson,

I am going through it at the moment. Hope to figure out sometimes :slight_smile:

thank you!

There is code to receive text with an end marker (here you have one, 0xA, so it fits your needs exactly ) and transform it into a number (example 4)

It uses the atoi() function on the received text buffer to transform the string into a number

Edit
When you see in the example

char endMarker = '\n';

That’s the same as 0xA which is the ascii code for line feed

Does it mean that I should replace '\n' with '0xA' in this case?

Thank you so much! it works now! I could not really explain why yet but here is what I did on the receiver side:

// Example 4 - Receive a number as text and convert it to an int

#include <SoftwareSerial.h>

SoftwareSerial HC12(10, 11); // HC-12 TX Pin, HC-12 RX Pin

int motor1pin1 = 2;
int motor1pin2 = 3;
int LEDred = 5;
int LEDgreen = 6;
int LEDblue = 7;

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

int dataNumber = 0;             // new for this version

void setup() {
    Serial.begin(9600);
    HC12.begin(9600);
    Serial.println("<Arduino is ready>");
}

void loop() {
    recvWithEndMarker();
    showNewNumber();
    Applycode();
}

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = 0xA;
    char rc;
    
    if (HC12.available() > 0) {
        rc = HC12.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = 0xA; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

void showNewNumber() {
    if (newData == true) {
        dataNumber = 0;             // new for this version
        dataNumber = atoi(receivedChars);   // new for this version
        Serial.print("This just in ... ");
        Serial.println(receivedChars);
        Serial.print("Data as Number ... ");    // new for this version
        Serial.print(dataNumber);     // new for this version
        newData = false;
        }

}

void Applycode() {


    Serial.write(dataNumber);
  
     if (dataNumber == 0) {
       analogWrite(9, 0);
       digitalWrite(motor1pin1, HIGH);
       digitalWrite(motor1pin2, LOW);
       digitalWrite(LEDred, LOW);
       digitalWrite (LEDgreen, LOW);
       digitalWrite(LEDblue, HIGH);
    
     }
     else if ((dataNumber > 0) && (dataNumber < 55)) {
       analogWrite(9, 55);
        digitalWrite(motor1pin1, HIGH);
        digitalWrite(motor1pin2, LOW);
        digitalWrite (LEDgreen, LOW);
        digitalWrite(LEDred, HIGH);
        digitalWrite(LEDblue, LOW);
      }
      else if ((dataNumber <= 254) && (dataNumber >= 55)) {
        analogWrite(9, dataNumber);
        digitalWrite(motor1pin1, HIGH);
        digitalWrite(motor1pin2, LOW);
        digitalWrite(LEDred, LOW);
        digitalWrite(LEDgreen, HIGH);
        digitalWrite(LEDblue, LOW);
  
      }
      else if (dataNumber >= 255) {
        analogWrite(9, 255);
        digitalWrite(motor1pin1, HIGH);
        digitalWrite(motor1pin2, LOW);
        digitalWrite(LEDred, HIGH);
        digitalWrite (LEDgreen, LOW);
        digitalWrite(LEDblue, LOW);
     }
}

Seems you got it :wink: well done

It’s almost right, applying the code should only be done when you get the value, not all the time, so you should call it within showNewNumber() within the if

'\n' is the same value as as 0xA, so you could have kept it. It’s called an escape sequence in C++. See Escape sequences - cppreference.com

Alright, so instead of creating a new void I should just keep the same one and have all the "Applycode" as else if :slight_smile:

Thanks for the documentation, I will take a closer look at it.

best

It’s good to keep the function

Just insert it in the if

void showNewNumber() {
    if (newData == true) {
        dataNumber = 0;             // new for this version
        dataNumber = atoi(receivedChars);   // new for this version
        Serial.print("This just in ... ");
        Serial.println(receivedChars);
        Serial.print("Data as Number ... ");    // new for this version
        Serial.print(dataNumber);     // new for this version
        Applycode();  // <=== here
        newData = false;
     }
}

Basically it reads like a description in English

If you received a new code then print it and apply that change.

PS: we use camelCase within arduino world so we would write applyCode() instead of Applycode()