analogWrite and VirtualWire library problem

I have this weird problem where the RF receiver just does not want to respond any more after a PWM signal is active on ANY of the PWM pins. The receiver does not crash if i use 0 or 255. Everything else seems to crash the VirtualWire library.

The program still runs, i get serial.print commands from the arduino.

I know that pin 9 and 10 cant be used because of VirtualWire taking over Timer1.

After searching for answers on google, it seems that nobody else has had this problem.

#include <VirtualWire.h>

uint8_t bufferMax = VW_MAX_MESSAGE_LEN; // 30 bits

int pspd = 0;
int spd = 0;
int motorBs = 4;
int motorBe = 3;

//sbi(TCCR2A, WGM21);  // Change from Phase-Correct PWM to Fast PWM

void setup() {
  pinMode(motorBs, OUTPUT); //speed
  pinMode(motorBe, OUTPUT); //direction
  //digitalWrite(motorBe, HIGH);
  digitalWrite(motorBe, LOW);
  digitalWrite(motorBs, LOW);
  Serial.begin(9600);
  Serial.println("setup");    // Prints "Setup" to the serial monitor
  vw_set_rx_pin(12);          // Sets pin D12 as the RX Pin
  vw_set_ptt_inverted(true);  // Required for DR3100
  vw_setup(4000);              // Bits per sec
  vw_rx_start();              // Start the receiver PLL running
}

void loop() {
  uint8_t recieveBuffer[bufferMax];
  if (vw_get_message(recieveBuffer, &bufferMax)) { // Non-blocking
    int i;
    digitalWrite(13, true);  // Flash a light to show received good message

    // Message with a good checksum received, dump it.
    Serial.print("Got: ");

    for (i = 0; i < bufferMax - 2; i += 3) {
      char chanID = recieveBuffer[i];               // Extract channel ID
      int c = (recieveBuffer[i + 2] << 8) | recieveBuffer[i + 1]; // Reconstruct 16-bit joystick reading
      spd = map(c, 0, 1023, 0, 255);
      //analogWrite(motorBs, spd);
      Serial.print(chanID);
      Serial.print(" ");
      Serial.print(c);
    }
    Serial.println("");
    digitalWrite(13, false);
  }
  if (spd != pspd) {
    pspd = spd;
    Serial.println(spd);
    delay(15);
    analogWrite(motorBs, spd);
    delay(100);
  }
}

Where are you resetting bufferMax to its default value? Once it has dropped to zero you are toast

Well, i don't know what that means.
The original code is not mine, i just used this guys instructable for the original code, and added some pwm functions from the readings.

It's not an excuse - it's YOUR code too :slight_smile:

Joke aside, When you do if (vw_get_message(recieveBuffer, [color=red]&bufferMax[/color])) you are passing to the function vw_get_message() in the bufferMax variable the max size of the data you can hold into the recieveBuffer. The function upon execution will return the number of bytes read by updating the content of the bufferMax variable (thats why you pass the address of that variable, it will come back modified).

Now imagine there is nothing to read, then the bufferMax variable is set to 0 and the next time you enter the loop you allocate a buffer with zero length and when you call the function you are telling the function that your buffer cannot hold any data, so it can't read anything.

My recommendation thus would be to re-initialize the bufferMax variable to bufferMax = VW_MAX_MESSAGE_LEN; in the loop before calling the function vw_get_message() and of course to declare your local buffer as uint8_t recieveBuffer[VW_MAX_MESSAGE_LEN]; to have the right mount of memory always allocated

Not sure this is your unique bug but that's an issue

Well that might be so, but it has never been a problem before.
i tested the code extensively before messing with it, and it never locked up or anything.
I have tried several different guides for getting this to work.

all of them work well until i try using any form of analogWrite.
normally they just freeze the entire arduino, but this time it just stops receiving messages.

i have tried looking in the library source, but i cant find anything which seem relevant to my problem.
I also tried the radiohead version of that code with no luck. this time it refuses to accept even one message.

do you have enough power for your motor? how is this all powered/wired?

all the components work as they should for themselves.
im using a small dc fan for testing together with a h-bridge.

i could also add that if i set the code to spin the motor on HIGH or 255, then it will work.

It also registers the first message and applies the signal. Sometimes it will work a couple more times before the lockup.

lets say i get 250 from the transmitter. then i do analogWrite(250), and the fan spins at 250 pwm signal.
the next time i try to send a signal, it completely ignores the message.
Nothing shows up in the serial monitor.

if you look at the last bit of the code, you see that i added an if statement to check for changes, and if so, apply it.
before i did that, it applied and printed every cycle.

thats how i know the code still runs. I saw the serial monitor scroll more and more with the pwm signal getting printed over and over.

if you look at the last bit of the code

where is the last bit?

in the main post, but i can type it here

if (spd != pspd) {
    pspd = spd;
    Serial.println(spd);
    delay(15);
    analogWrite(motorBs, spd);
    delay(100);
  }

if you change your first post as you go people coming to read this thread will get lost.

Are you using a UNO? You know that the UNO has PWM only on pins 3, 5, 6, 9, 10, and 11.
so

int motorBs = 4;
...
analogWrite(motorBs, spd);

has limited chance of success outside 0 and 255....

Once you have changed that pin, I would still recommend to have your loop look like this

void loop() {

  uint8_t recieveBuffer[VW_MAX_MESSAGE_LEN];
  uint8_t bufferMax = VW_MAX_MESSAGE_LEN; // no need to make it global

  if (vw_have_message()) {
    if (vw_get_message(recieveBuffer, &bufferMax)) {      // Non-blocking
      int i;
      digitalWrite(13, true);  // Flash a light to show received good message

      // Message with a good checksum received, dump it.
      Serial.print("Got: ");

      for (i = 0; i < bufferMax - 2; i += 3) {
        char chanID = recieveBuffer[i];               // Extract channel ID
        int c = (((int) recieveBuffer[i + 2]) << 8) | recieveBuffer[i + 1]; // Reconstruct 16-bit joystick reading
        spd = map(c, 0, 1023, 0, 255);
        //analogWrite(motorBs, spd);
        Serial.print(chanID);
        Serial.print(" ");
        Serial.print(c);
      }
      Serial.println("");
      digitalWrite(13, false);
      
      if (spd != pspd) {
        pspd = spd;
        Serial.println(spd);
        delay(15);
        analogWrite(motorBs, spd);
        delay(100);
      }
    }
  }
}

J-M-L:
if you change your first post as you go people coming to read this thread will get lost.

Yeah i know this, so thats why i did not do any changes to the original post.

The problem is still there after doing the changes you recommended.

Ok - thought you had been adding there - stood

Which pin did you pick for motorBs? Can you post the new source code? What's the source code of the emitter? Is that a PWM pin now? Do you have a UNO? If you answer by tin you bits it's not easy to help.

Im using an arduino UNO as receiver and a nano as transmitter.
the fan (in the future is going to be 2 motors) is connected to a H-bridge where one pin sets the direction and the other the speed(PWM). What im going to use it for is controlling an RC car. it was supposed to be a simple project to learn more about data transmission between arduinos and such.

the transmitter is wired like the guide i posted earlier. It only has a small change to add another pot reading (which was already handled by the code, just needed to be added as a new line)

Transmitter code:

#include <VirtualWire.h>

#define POT_TOLERANCE 10            // Noise tolerance level for potentiometers
// this is where i changed from 1 to 2 for the extra potentiometer. the author clearly states that the code supports up to 4
#define POT_NUM 2                   // 1 in this case, but a handheld radio has up to 4

// Constants
int potPin[POT_NUM];                // Potentiometer pin numbers
int buttonPin[POT_NUM][2];          // Button pin numbers, each potentiometer has 2 buttons
uint8_t buflen = 3;                 // Buffer length: 1 letter per channel + a 2 byte reading

// Variables will change:
int buttonState[POT_NUM][2];        // The current reading from the button pins
int lastButtonState[POT_NUM][2];    // The previous reading from the push-button pins
int joystick[POT_NUM];              // Raw potentiometer readings from joystick commands
int trimmer[POT_NUM];               // Trimmer settings
int channelPos[POT_NUM];            // Final channel data to transmit wirelessly

// The following variables are longs because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime[POT_NUM][2];  // The last time each output pin was toggled
long debounceDelay = 50;            // The debounce time; increase if the output flickers


void setup() {
  int c, b;
  potPin[0] = A0;                         // Channel 1 potentiometer on analog A0
  potPin[1] = A1;                         // Channel 2 potentiometer on analog A1
  buttonPin[0][0] = 2;                    // Channel 1 push-button 1 on pin 2
  buttonPin[0][1] = 3;                    // Channel 1 push-button 2 on pin 3

  //i don't plan to use the trimmer buttons, so i set them as the same as the other 2, and connect the wires with a 10kohm resisitor to ground
  buttonPin[1][0] = 2;                    // Channel 2 push-button 1 on pin 2
  buttonPin[1][1] = 3;                    // Channel 2 push-button 2 on pin 3

  //Iterate over channels and buttons
  for(c = 0; c < POT_NUM; c++){
    //Set initial states to 0
    trimmer[c] = 0;
    for(b = 0; b < 2; b++){
      lastButtonState[c][b] = LOW;
      lastDebounceTime[c][b] = 0;
      // Set button pins as inputs
      pinMode(buttonPin[c][b], INPUT);
    }
    joystick[c] = analogRead(potPin[c]);  // Get initial joystick reading
  }

  Serial.begin(9600);
  vw_set_tx_pin(4);                      // Sets pin D4 as the TX pin
  vw_set_ptt_inverted(true);              // Required for DR3100
  vw_setup(4000);                         // Bits per sec
}

void loop() {
  int c, b, potReading, buttonReading;
  
  //Iterate over channels and buttons
  for(c = 0; c < POT_NUM; c++){
    // Read the joystick position
    potReading = analogRead(potPin[c]);
    joystick_update(c, &potReading);

    for(b = 0; b < 2; b++){
      // Read button to update trimmer
      buttonReading = digitalRead(buttonPin[c][b]);
      trim_update(c, b, &buttonReading);
      
      // Save the readings
      lastButtonState[c][b] = buttonReading;
    }
  }
}


// Helper Functions
void joystick_update(int c, int* potReading){
  // Only accept reading if the change is outside noise tolerance level
  if(abs(*potReading - joystick[c]) > POT_TOLERANCE){
    joystick[c] = *potReading;
    pos_update(c);
  }
}

void trim_update(int c, int b, int* buttonReading){
  // Check to see if you just pressed the button (i.e.
  // the input went from LOW to HIGH), and you've waited
  // long enough since the last press to ignore any noise:
  
  // If the switch changed, due to noise or pressing:
  if (*buttonReading != lastButtonState[c][b]) {
    // Reset the debouncing timer
    lastDebounceTime[c][b] = millis();
  }
  
  if ((millis() - lastDebounceTime[c][b]) > debounceDelay) {
    // Whatever the reading is at, it's been there for longer than
    // the debounce delay, so take it as the actual current state:

    // If the button state has changed:
    if (*buttonReading != buttonState[c][b]) {
      buttonState[c][b] = *buttonReading;

      // Only update trimmer if the new button state is HIGH
      if (buttonState[c][b] == HIGH) {
        if(b == 0 && channelPos[c] < 1023){
          trimmer[c]++;
        } else if(b == 1 && channelPos[c] > 0) {
          trimmer[c]--;
        }
        // Update channel position
        pos_update(c);
      }
    }
  }
}

void pos_update(int c){
  //Update position, send over channel and print data
  channelPos[c] = joystick[c] + trimmer[c];
  send_message('A'+c,&channelPos[c]);       // 'A'+c == konverter bokstav "A" til binær, 65 og legg til C (potensiometer)

  
  Serial.print("Channel: ");
  Serial.print(char('A'+c));
  Serial.print("  joystick: ");
  Serial.print(joystick[c]);
  Serial.print("  trimmer: ");
  Serial.print(trimmer[c]);
  Serial.print("  position: ");
  Serial.print(channelPos[c]);
  Serial.println("");
}

void send_message(char chanID, int *msg){
  //Since the reading is somewhere between 0 and 1023 (from the analog read) and we can only
  //send 8-bit packets through VirtualWire's vw_send function, each reading must be split as
  //two 8-bit elements and channel ID is sent as a single character, 8-bits large by default.
  
  uint8_t buf[buflen];
  
  buf[0] = chanID;    // Label this channel, to distinguish it from other channels
  buf[1] = *msg;      // Least significant byte (8 bits), rest is truncated from int to uint8_t
  buf[2] = (*msg)>>8; // Most significant byte
  
  digitalWrite(13, true);       // Flash a light to show transmitting
  vw_send(buf, buflen);         // Sending the message
  vw_wait_tx();                 // Wait until the whole message is gone
  digitalWrite(13, false);      // Turn the LED off.
  delay(50);                    // A short gap.
}

Receiver code:

#include <VirtualWire.h>

//uint8_t bufferMax = VW_MAX_MESSAGE_LEN; // 30 bits

int pspd = 0;
int spd = 0;
int motorBs = 3;
int motorBd = 4;

//sbi(TCCR2A, WGM21);  // Change from Phase-Correct PWM to Fast PWM

void setup() {
  pinMode(motorBs, OUTPUT); //speed
  pinMode(motorBd, OUTPUT); //direction
  digitalWrite(motorBd, LOW);
  digitalWrite(motorBs, LOW);
  Serial.begin(9600);
  Serial.println("setup");    // Prints "Setup" to the serial monitor
  vw_set_rx_pin(12);          // Sets pin D12 as the RX Pin
  //vw_set_ptt_inverted(true);  // Required for DR3100
  vw_setup(4000);              // Bits per sec
  vw_rx_start();              // Start the receiver PLL running
}

void loop() {
  uint8_t recieveBuffer[VW_MAX_MESSAGE_LEN];
  uint8_t bufferMax = VW_MAX_MESSAGE_LEN; // no need to make it global

  //uint8_t recieveBuffer[bufferMax];

  if (vw_get_message(recieveBuffer, &bufferMax)) { // Non-blocking
    int i;
    digitalWrite(13, true);  // Flash a light to show received good message

    // Message with a good checksum received, dump it.
    Serial.print("Got: ");

    for (i = 0; i < bufferMax - 2; i += 3) {
      char chanID = recieveBuffer[i];               // Extract channel ID
      int c = (recieveBuffer[i + 2] << 8) | recieveBuffer[i + 1]; // Reconstruct 16-bit joystick reading
      spd = map(c, 0, 1023, 0, 255);
      
      Serial.print(chanID);
      Serial.print(" ");
      Serial.print(c);
    }
    Serial.println("");
    digitalWrite(13, false);
  }
  if (spd != pspd) {
    pspd = spd;
    Serial.println(spd);
    delay(15);
    analogWrite(motorBs, spd);
    delay(100);
  }
}

I have had this problem before using HC-04 ultrasonic range finders and an ir receiver, but if i recall correctly that was a library conflict. (both libraries used the same timer for interrupt)

I feel like i need to specify this more clearly: The code works as long as i dont use a PWM signal. i can use analogWrite 0 and 255 because that is the same as LOW and HIGH.
If set the analogWrite to a pwm signal the code still runs. Its just the RF receiver either not responding or possibly dropping all the messages because of some bug in the library.

i dont know if you are familiar with the VirtualWire library?

I also just want to thank you for letting me steal some time of your life to help me with this
i really appreciate it :slight_smile:

I am familiar with the library (and noticed you did not use my recommended code :slight_smile: ) although long time since I used it as it is deprecated so i moved to the RadioHead library

Have you tried to run it with vw_setup(2000); and boost your Serial link to 115200 (or test without the printing)

Can you show the console output at 115200?

3 pictures attached

Picture 1: the same code as it was, just changed the serial speed to 115200 and vw_setup to 2000

Picture 2: removed the filter for printing the motor speed

Picture 3: added back the filter, but removed the analog write

The received number is a reading from 1 to 1023 from a potentiometer
then i map the value to 0-255 and print it