Radio code appears to be interfering with other functions.

The overview.

The plan is to have a hand held controller (client) with a few buttons to change settings like setpoint, on/off, height, etc, and store them in "radioTxArray". It then transmits this array via nrf24L01 radio to the server which the replies with the actual rpm of the motor. This is then displayed on the lcd.

At the other end, the server receives the array and stores it as "radioRxArray" and the sets the setpoint for the motor and controls its rpm via a PID algorithm. (Big thankyou to zhomeslice for his invaluable input in to the motor control). It then sends the actual rpm to the client in reply. The server will also have a ping sensor, current sensor and relay attached.

I have two nrf24L01 radios that talk to each other and send the arrays as I expect using a modified version of the RadioHead nrf24 reliable datagram example. I replaced the delay with a timer and inserted an array with fixed data.

These two sketches seem to work well.

Client modified example.

#include <RHReliableDatagram.h>
#include <RH_NRF24.h>
#include <SPI.h>

#define CLIENT_ADDRESS 1001
#define SERVER_ADDRESS 1002

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display


// Singleton instance of the radio driver
RH_NRF24 driver(9, 10);
// RH_NRF24 driver(8, 7);   // For RFM73 on Anarduino Mini

// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, CLIENT_ADDRESS);
uint8_t radioTxArray[6];//radioTxArray[6];
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];

int rpm;

void setup()
{
  lcd.init();                      // initialize the lcd
  lcd.backlight();
  Serial.begin(9600);
  if (!manager.init())
    Serial.println("init failed");
  // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm




  radioTxArray[0] = 0;
  radioTxArray[1] = 30;
  radioTxArray[2] = 1;
  radioTxArray[3] = 0;
  radioTxArray[4] = 137;
  radioTxArray[5] = 0;
}

void loop()
{
  static unsigned long RadioTimer;
  if ((unsigned long)(millis() - RadioTimer) >= 500) {
    RadioTimer = millis();

    Serial.println("Sending to nrf24_reliable_datagram_server");

    // Send a message to manager_server
    if (manager.sendtoWait(radioTxArray, sizeof(radioTxArray), SERVER_ADDRESS))
    {
      // Now wait for a reply from the server
      uint8_t len = sizeof(buf);
      uint8_t from;
      if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
      {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Reply from: 0x");
        lcd.print(from, HEX);
        //Serial.print("rpm= ");
        //Serial.print(rpm);
        //Serial.print(" 1= ");
        //Serial.print(buf[1]);
        rpm = (buf[0]);
        lcd.setCursor(0, 1);
        lcd.print("    RPM = ");
        lcd.print(rpm);

      }
      else
      {
        lcd.clear();
        lcd.print("No reply");
        Serial.println("No reply, is nrf24_reliable_datagram_server running?");
      }
    }
    else
      Serial.println("sendtoWait failed");
    //delay(500);
  }
}

Server modified example.

// nrf24_reliable_datagram_server.pde
// -*- mode: C++ -*-
// Example sketch showing how to create a simple addressed, reliable messaging server
// with the RHReliableDatagram class, using the RH_NRF24 driver to control a NRF24 radio.
// It is designed to work with the other example nrf24_reliable_datagram_client
// Tested on Uno with Sparkfun WRL-00691 NRF24L01 module
// Tested on Teensy with Sparkfun WRL-00691 NRF24L01 module
// Tested on Anarduino Mini (http://www.anarduino.com/mini/) with RFM73 module
// Tested on Arduino Mega with Sparkfun WRL-00691 NRF25L01 module

#include <RHReliableDatagram.h>
#include <RH_NRF24.h>
#include <SPI.h>

#define CLIENT_ADDRESS 1001
#define SERVER_ADDRESS 1002

// Singleton instance of the radio driver
RH_NRF24 driver(7, 8);


// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, SERVER_ADDRESS);

int rpm = 35;
int radioRxArray[6];
//int radioTxArray[2];
uint8_t radioTxArray[2];
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];


void setup()
{
  Serial.begin(9600);
  if (!manager.init())
    Serial.println("init failed");
  // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
  radioTxArray[0] = rpm;
  radioTxArray[1] = 1;

}
uint8_t data = rpm;
// Dont put this on the stack:
//uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];

void loop()
{

  static unsigned long RadioTimer;
  if ((unsigned long)(millis() - RadioTimer) >= 500) {
    RadioTimer = millis();
    if (manager.available())
    {
      // Wait for a message addressed to us from the client
      uint8_t len = sizeof(buf);
      uint8_t from;
      if (manager.recvfromAck(buf, &len, &from))
      {
        radioRxArray[0] = buf[0];
        radioRxArray[1] = buf[1];
        radioRxArray[2] = buf[2];
        radioRxArray[3] = buf[3];
        radioRxArray[4] = buf[4];
        radioRxArray[5] = buf[5];

        Serial.print("got request from : 0x");
        Serial.print(from, HEX);
        Serial.print(": 0= ");
        Serial.print(radioRxArray[0]);
        Serial.print(": 1= ");
        Serial.print(radioRxArray[1]);
        Serial.print(": 2= ");
        Serial.print(radioRxArray[2]);
        Serial.print(": 3= ");
        Serial.print(radioRxArray[3]);
        Serial.print(": 4= ");
        Serial.print(radioRxArray[4]);
        Serial.print(": 5= ");
        Serial.print(radioRxArray[5]);
        Serial.print(" millis ");
        Serial.println(millis());


        // Send a reply back to the originator client
        if (!manager.sendtoWait(radioTxArray, sizeof(radioTxArray), from))
          Serial.println("sendtoWait failed");
      }
    }
    //  }
  }

Next post |
/

Presumably its the 2s timeout in recvFromAckTimeout() that's the issue - you are busy-waiting,
not polling using recvFromAck()

Now the problem. :confused:

On the client end, I can run my sketch reading buttons and printing to lcd and saving to radioTxArray and it all works as expected as long as I have the radio section, "exchageData" commented out. When I uncomment the "exchangeData", it transmits the array ok, but the buttons become almost unresponsive. ie, they will work if you persist for long enough.

Client controller.

#include <Streaming.h>

#include <EEPROM.h>

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

#include <RHReliableDatagram.h>
#include <RH_NRF24.h>
#include <SPI.h>
#include <Bounce2.h>

//=======For the radio======

#define CLIENT_ADDRESS 1001
#define SERVER_ADDRESS 1002

// Singleton instance of the radio driver
RH_NRF24 driver(9, 10);


// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, CLIENT_ADDRESS);
uint8_t radioTxArray[6];//Array with 6 positions.
//0 = On/Off, 1 = SetPoint, 2 = , 3 = Height, 4 = Cal, 5 = Dist
// Dont put this on the stack:
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
const int radioDelay = 500;
unsigned long previousRadioMillis = 0;


//====For the buttons====
const byte ButtonPins[] = {4, 5, 6, 7, 3};
Bounce buttons[] =
{Bounce(), Bounce(), Bounce(), Bounce(), Bounce()};

//this order should match the order of the buttons. I had no idea...
enum {B_UP, B_DOWN, B_CAL, B_HEIGHT, B_START};

int runTimer = 1;//For Cal timer
int runFor = 60; //Cal run interval
int seconds;
int setpointVariable;
int rpm;
int dist;
int address = 0;
bool isStarted = false;
bool isCal = false;
bool isHeight = false;
bool isDist = false;
unsigned long currentMillis = 0;


void setup() {

  radioTxArray[0] = 0;// sets motor to off at start up.

  for (byte b = 0; b < sizeof(ButtonPins); b++) {
    pinMode (ButtonPins[b], INPUT_PULLUP);
  }
  for (byte b = 0; b < sizeof(ButtonPins); b++) {
    buttons[b].attach(ButtonPins[b]);
  }
  for (byte b = 0; b < sizeof(ButtonPins); b++) {
    buttons[b].interval(15); //15ms debounce interval
  }

  lcd.init();                      // initialize the lcd
  lcd.backlight();
  lcd.setCursor(3, 0);
  lcd.print(" Seabrook");
  lcd.setCursor(5, 1);
  lcd.print("Seeders");
  delay(2000);
  lcd.clear();
  lcd.print("Seedr Controller");
  lcd.setCursor(1, 1);
  lcd.print("by Chris Dalby");
  delay(2000);
  lcd.clear();
  lcd.print("Set RPM  ");
  lcd.print(setpointVariable);
  Serial.begin(9600);
  Serial.println("powered up");
  delay(2000);
  Serial.println("wifi Seeder Controller client r6");
  if (!manager.init())
    Serial.println("init failed");
  // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
  setpointVariable = EEPROM.read(0);
  dist = EEPROM.read(1);


}

void loop()
{

  readButtons();
  checkStartStop();
  adjRequiredRpm();
  calibration();
  heightSensor();
  adjHeight();
  menuSelect();
  displayRpm();
  exchangeData();
  writeToEeprom();

  currentMillis = millis();

  //  sendDatatoServer();//Send RequiredRpm & Start/Stop to Server.

}

void readButtons() {
  for (byte i = 0; i < sizeof(ButtonPins); i++) {
    buttons[i].update();
  }
}

void checkStartStop()
{
  if (buttons[B_START].fell()) {
    if (isStarted == false) {
      isStarted = true;
      radioTxArray[0] = 1;
    }
    else {
      isStarted = false;
      radioTxArray[0] = 0;
    }

  }

}

void adjRequiredRpm() {
  if (buttons[B_UP].fell() && isCal == false && isHeight == false) {
    setpointVariable++;
    if (setpointVariable > 60) {
      setpointVariable = 60;
    }
  }
  if (buttons[B_DOWN].fell()) {
    setpointVariable = setpointVariable - 1;
    if (setpointVariable < 10) {
      setpointVariable = 10;
    }
  }
  radioTxArray[1] = setpointVariable;

}

void calibration() {
  if (buttons[B_CAL].fell() && isStarted == false) {
    if (isCal == false) {
      isCal = true;
      radioTxArray[4] = 1;

    }
    else {
      isCal = false;
      radioTxArray[4] = 0;
    }


  }


}

void heightSensor() {
  if (buttons[B_HEIGHT].fell()) {
    if (isHeight == false) {
      isHeight = true;
      //isCal = false;
      radioTxArray[3] = 1;
    }
    else {
      isHeight = false;
      radioTxArray[3] = 0;
    }
  }

}

void adjHeight() {

  if (buttons[B_UP].fell() && isDist == true) {
    dist++;
    if (dist > 150) {
      dist = 150;
    }
  }
  if (buttons[B_DOWN].fell() && isDist == true) {
    dist = dist - 1;
    if (dist < 10) {
      dist = 10;
    }
  }
  radioTxArray[5] = dist;

}

void menuSelect() {

  if (buttons[B_CAL].fell() && isStarted == false && isCal == true) {
    if (isDist == false) {
      isDist = true;
    }

    else {
      isDist = false;
    }
  }
}

void displayRpm() {


  if (isCal == true && isStarted == false) {
    lcd.setCursor(0, 0);
    lcd.print("Calibration Mode ");
    lcd.setCursor(0, 1);
    lcd.print("Press start     ");
  }
  else if (isCal == true && isStarted == true) {

    lcd.setCursor(0, 0);
    lcd.print("Calibration Mode ");
    lcd.setCursor(0, 1);
    lcd.print("Motor ");
    lcd.print(F("On   "));
    lcd.print("60");
    lcd.print("sec");


  }
  else if (isDist == true) {
    lcd.setCursor(0, 0);
    lcd.print("Adjust height to");
    lcd.setCursor(0, 1);
    lcd.print("switch at ");
    lcd.print(dist);
    lcd.print("cm");
  }

  else {

    lcd.setCursor(0, 0);
    lcd.print("Set RPM  ");
    lcd.print(setpointVariable);
    if (isHeight == true) {
      lcd.print("    H");
    }
    else {
      lcd.print("     ");
    }
    lcd.setCursor(0, 1);
    lcd.print("Motor ");
    if (isStarted) {
      lcd.print(F("On "));
    }
    else {
      lcd.print(F("Off"));
    }
    lcd.print(" RPM ");
    lcd.print(rpm);
  }


  Serial.print(radioTxArray[0]);
  Serial.print(radioTxArray[1]);
  Serial.print(radioTxArray[2]);
  Serial.print(radioTxArray[3]);
  Serial.print(radioTxArray[4]);
  Serial.print(radioTxArray[5]);
  Serial.print("  pin 6 = ");
  Serial.println(digitalRead(6));




}


void exchangeData()  {
  /*
    static unsigned long RadioTimer;
    if ((unsigned long)(millis() - RadioTimer) >= 500) {
    RadioTimer = millis();
  */
  //   if (currentMillis - previousRadioMillis >= radioDelay){
  Serial.println("Sending to nrf24_reliable_datagram_server");
  //previousRadioMillis += radioDelay;

  // Send a message to manager_server
  if (manager.sendtoWait(radioTxArray, sizeof(radioTxArray), SERVER_ADDRESS))
  {
    // Now wait for a reply from the server
    uint8_t len = sizeof(buf);
    uint8_t from;
    if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
    {
      Serial.print("got reply from : 0x");
      Serial.print(from, HEX);
      Serial.print("0= ");
      Serial.print(buf[0]);
      Serial.print(" 1= ");
      Serial.print(buf[1]);
      rpm = (buf[0]);
    }
    else
    {
      Serial.println("No reply, is nrf24_reliable_datagram_server running?");
    }
  }
  else
    Serial.println("sendtoWait failed");
  //  }

}

void writeToEeprom()
{
  EEPROM.update(0, setpointVariable);
  EEPROM.update(1, dist);
}

Now, I know I am a bit simple, but I really would like to get this going so if anyone can point me in the right direction, I would really appreciate it.

I know Robin2 has suggested to me on more than one occasion to use the tmrh libraries instead of radiohead and I accept your advise. If that may be the cause of the problem I would need a hand to get that running since the example didn't run on first attempt.

Thanks

MarkT:
Presumably its the 2s timeout in recvFromAckTimeout() that's the issue - you are busy-waiting,
not polling using recvFromAck()

Wow, that was quick. Thanks MarkT. I did assume that if a response was received, there would be no delay? I will read up on this, thanks.

moose4621:
It then transmits this array via nrf24L01 radio to the server which the replies with the actual rpm of the motor.

That sounds like a situation in which using the ackPayload facility would be very appropriate. It means that the response is received without any need for either the Handheld or the "server" to swap states. It is one of those magic things that "just works"

Have a look at the pair of programs in this link

...R

Robin2:
That sounds like a situation in which using the ackPayload facility would be very appropriate. It means that the response is received without any need for either the Handheld or the "server" to swap states. It is one of those magic things that "just works"

Have a look at the pair of programs in this link

...R

Thanks Robin2, You have finally convinced me to persevere with the tmrh version using your examples.

I have modified the client and it seems to be working well. I will change the master after work and let you know.

Thanks again.

#include <Streaming.h>

#include <EEPROM.h>

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

#include <nRF24L01.h>
#include <RF24.h>
#include <SPI.h>
#include <Bounce2.h>

//=======For the radio======

#define CE_PIN 9
#define CSN_PIN 10

// NOTE: the "LL" at the end of the constant is "LongLong" type

const uint64_t   deviceID = 0xE8E8F0F0E1LL; // Define the ID for this slave

RF24 radio(CE_PIN, CSN_PIN);

int radioTxArray[6];//Array with 6 positions.
//0 = On/Off, 1 = SetPoint, 2 = , 3 = Height, 4 = Cal, 5 = Dist
// Dont put this on the stack:
int radioRxArray[2];


//====For the buttons====
const byte ButtonPins[] = {4, 5, 6, 7, 3};
Bounce buttons[] =
{Bounce(), Bounce(), Bounce(), Bounce(), Bounce()};

//this order should match the order of the buttons. I had no idea...
enum {B_UP, B_DOWN, B_CAL, B_HEIGHT, B_START};

int runTimer = 1;//For Cal timer
int runFor = 60; //Cal run interval
int seconds;
int setpointVariable;
int rpm;
int dist;
int address = 0;
bool isStarted = false;
bool isCal = false;
bool isHeight = false;
bool isDist = false;
unsigned long currentMillis = 0;


void setup() {

  radioTxArray[0] = 0;// sets motor to off at start up.

  for (byte b = 0; b < sizeof(ButtonPins); b++) {
    pinMode (ButtonPins[b], INPUT_PULLUP);
  }
  for (byte b = 0; b < sizeof(ButtonPins); b++) {
    buttons[b].attach(ButtonPins[b]);
  }
  for (byte b = 0; b < sizeof(ButtonPins); b++) {
    buttons[b].interval(15); //15ms debounce interval
  }

  lcd.init();                      // initialize the lcd
  lcd.backlight();
  lcd.setCursor(3, 0);
  lcd.print(" Seabrook");
  lcd.setCursor(5, 1);
  lcd.print("Seeders");
  delay(2000);
  lcd.clear();
  lcd.print("Seedr Controller");
  lcd.setCursor(1, 1);
  lcd.print("by Chris Dalby");
  delay(2000);
  lcd.clear();
  lcd.print("Set RPM  ");
  lcd.print(setpointVariable);
  Serial.begin(9600);
  Serial.println("powered up");
  delay(2000);
  Serial.println("wifi Seeder Controller client r6");

  radio.begin();
  radio.setDataRate( RF24_250KBPS );
  radio.openReadingPipe(1, deviceID);
  radio.enableAckPayload();
  radio.writeAckPayload(1, radioTxArray, sizeof(radioTxArray));
  radio.startListening();

  setpointVariable = EEPROM.read(0);
  dist = EEPROM.read(1);


}

void loop()
{

  readButtons();
  checkStartStop();
  adjRequiredRpm();
  calibration();
  heightSensor();
  adjHeight();
  menuSelect();
  displayRpm();
  exchangeData();
  writeToEeprom();

  currentMillis = millis();

  //  sendDatatoServer();//Send RequiredRpm & Start/Stop to Server.

}

void readButtons() {
  for (byte i = 0; i < sizeof(ButtonPins); i++) {
    buttons[i].update();
  }
}

void checkStartStop()
{
  if (buttons[B_START].fell()) {
    if (isStarted == false) {
      isStarted = true;
      radioTxArray[0] = 1;
    }
    else {
      isStarted = false;
      radioTxArray[0] = 0;
    }

  }

}

void adjRequiredRpm() {
  if (buttons[B_UP].fell() && isCal == false && isHeight == false) {
    setpointVariable++;
    if (setpointVariable > 60) {
      setpointVariable = 60;
    }
  }
  if (buttons[B_DOWN].fell()) {
    setpointVariable = setpointVariable - 1;
    if (setpointVariable < 10) {
      setpointVariable = 10;
    }
  }
  radioTxArray[1] = setpointVariable;

}

void calibration() {
  if (buttons[B_CAL].fell() && isStarted == false) {
    if (isCal == false) {
      isCal = true;
      radioTxArray[4] = 1;

    }
    else {
      isCal = false;
      radioTxArray[4] = 0;
    }


  }


}

void heightSensor() {
  if (buttons[B_HEIGHT].fell()) {
    if (isHeight == false) {
      isHeight = true;
      //isCal = false;
      radioTxArray[3] = 1;
    }
    else {
      isHeight = false;
      radioTxArray[3] = 0;
    }
  }

}

void adjHeight() {

  if (buttons[B_UP].fell() && isDist == true) {
    dist++;
    if (dist > 150) {
      dist = 150;
    }
  }
  if (buttons[B_DOWN].fell() && isDist == true) {
    dist = dist - 1;
    if (dist < 10) {
      dist = 10;
    }
  }
  radioTxArray[5] = dist;

}

void menuSelect() {

  if (buttons[B_CAL].fell() && isStarted == false && isCal == true) {
    if (isDist == false) {
      isDist = true;
    }

    else {
      isDist = false;
    }
  }
}

void displayRpm() {


  if (isCal == true && isStarted == false) {
    lcd.setCursor(0, 0);
    lcd.print("Calibration Mode ");
    lcd.setCursor(0, 1);
    lcd.print("Press start     ");
  }
  else if (isCal == true && isStarted == true) {

    lcd.setCursor(0, 0);
    lcd.print("Calibration Mode ");
    lcd.setCursor(0, 1);
    lcd.print("Motor ");
    lcd.print(F("On   "));
    lcd.print("60");
    lcd.print("sec");


  }
  else if (isDist == true) {
    lcd.setCursor(0, 0);
    lcd.print("Adjust height to");
    lcd.setCursor(0, 1);
    lcd.print("switch at ");
    lcd.print(dist);
    lcd.print("cm");
  }

  else {

    lcd.setCursor(0, 0);
    lcd.print("Set RPM  ");
    lcd.print(setpointVariable);
    if (isHeight == true) {
      lcd.print("    H");
    }
    else {
      lcd.print("     ");
    }
    lcd.setCursor(0, 1);
    lcd.print("Motor ");
    if (isStarted) {
      lcd.print(F("On "));
    }
    else {
      lcd.print(F("Off"));
    }
    lcd.print(" RPM ");
    lcd.print(rpm);
  }


  Serial.print(radioTxArray[0]);
  Serial.print(radioTxArray[1]);
  Serial.print(radioTxArray[2]);
  Serial.print(radioTxArray[3]);
  Serial.print(radioTxArray[4]);
  Serial.print(radioTxArray[5]);
  Serial.print("  pin 6 = ");
  Serial.println(digitalRead(6));




}


void exchangeData()  {

  if ( radio.available() ) {
    radio.read( radioRxArray, sizeof(radioRxArray) );
    Serial.print("Data received Number0 ");
    Serial.print(radioRxArray[0]);
    Serial.print(" Number1 ");
    Serial.println(radioRxArray[1]);
    radio.writeAckPayload(1, radioTxArray, sizeof(radioTxArray));

  }
}

void writeToEeprom()
{
  EEPROM.update(0, setpointVariable);
  EEPROM.update(1, dist);
}

I did manage to get a test with the master done before work and the two are communicating but the data from the client is not being updated. It keeps resending the data it had at the start. I'll be able to supply more info when I get home but if you can see anything that may cause this in the meantime???

Master sketch

//*********Height Sensor Stuff*********
#include <NewPing.h>
#define TRIGGER_PIN  3  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     4  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 400 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

int dist;

//*****Motor Controller Stuff******
#include <PID_v2.h>

//Define Variables we'll be connecting to
double Setpoint, Input, Output;
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, 0.8, 5.2, .01, DIRECT);

#define PWMpin  6

volatile unsigned long timeX = 1;
int PulsesPerRevolution = 36;
int MaxRPM = 400;
volatile int Counts = 1;
double PulsesPerMinute;
volatile unsigned long LastTime;
volatile int PulseCtr;
unsigned long Counter;
int startRamp;
unsigned long Time;
int rpm;

//********Radio Stuff*******
#include <nRF24L01.h>
#include <RF24.h>
#include <SPI.h>

#include <nRF24L01.h>
#include <RF24.h>

#define CE_PIN   7
#define CSN_PIN 8

// NOTE: the "LL" at the end of the constant is "LongLong" type
// These are the IDs of each of the slaves
const uint64_t slaveID[2] = {0xE8E8F0F0E1LL, 0xE8E8F0F0E2LL} ;

RF24 radio(CE_PIN, CSN_PIN); // Create a Radio

int radioTxArray[2];

unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 1000;
int txVal = 0;
int radioRxArray[6];
//0 = motor on/off, 1 = setPoint, 2 = , 3 = Height, 4 = Cal, 5 = Dist
byte radioRxArrayLen = 10; // NB this 4 is the number of bytes in the 2 ints that will be recieved


bool isStarted = false;
bool isCal = false;
bool isDist = false;
bool go = false;

void setup() {
   // note that 1666666.67 = (60 seonds * 1000000 microseconds)microseconds in a minute / (36 / 9) pulses in 1 revolution
  PulsesPerMinute = (60 * 1000000) / (PulsesPerRevolution / Counts);

  pinMode(2, INPUT_PULLUP);
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("PID controlled Seeder Master R0");
  delay(1000);
  //Digital Pin 2 Set As An Interrupt for tacho.
  attachInterrupt(0, sensorInterrupt, FALLING);

   startRamp = 10;//map(PulsesPerRevolution , 1, MaxRPM, MaxRPM, 2);
  myPID.SetSampleTime(1);
  myPID.SetOutputLimits(40, (int) 255);
  PulseCtr = 0;
  myPID.SetMode(AUTOMATIC);
  analogWrite(PWMpin, 40);
  myPID.Compute();
  delay(11);
  myPID.Compute();

  radio.begin();
    radio.setDataRate( RF24_250KBPS );
    radio.enableAckPayload();
    radio.setRetries(3,5); // delay, count

}

void loop() {
  // put your main code here, to run repeatedly:
  exchangeData();
  getSetPoint();
  getStartStop();
  getHeight();
  readMotorCurrent();
  switchOnOff();
  readRpm();
  debug();
static unsigned long SpamTimer;
  
}

void exchangeData()
{
  currentMillis = millis();
    if (currentMillis - prevMillis >= txIntervalMillis) {

    radio.openWritingPipe(slaveID[0]); // calls the first slave
                                        // there could be a FOR loop to call several slaves in turn
    bool rslt;
    rslt = radio.write( radioTxArray, sizeof(radioTxArray) );
    Serial.print("\nRSLT (1 = success) ");
    Serial.println(rslt);
    Serial.print("Data Sent ");
    Serial.print(radioTxArray[0]);
    Serial.print("  ");
    Serial.println(radioTxArray[1]);
    if ( radio.isAckPayloadAvailable() ) {
        radio.read(radioRxArray,radioRxArrayLen);
        Serial.print("Acknowledge received: ");
        Serial.print(radioRxArray[0]);Serial.print("  ");
        Serial.print(radioRxArray[1]);Serial.print("  ");
        Serial.print(radioRxArray[2]);Serial.print("  ");
        Serial.print(radioRxArray[3]);Serial.print("  ");
        Serial.print(radioRxArray[4]);Serial.print("  ");
        Serial.println(radioRxArray[5]);
    }
    prevMillis = millis();
 }
}

void getSetPoint()
{
    Setpoint = 45;
  /*
}
  Setpoint = radioRxArray[1];
  */
}

void getStartStop()
{
  
  if(radioRxArray[0]==1)
  {
    isStarted = true;
  }
  else
  {
    isStarted = false;
  }
  
}

void getHeight()
{
  
  unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  dist = (uS / US_ROUNDTRIP_CM); //raw distance in cm.
  
  if (dist >=50)
  {
    digitalWrite(1, HIGH);
  }
  else
  {
    digitalWrite(1, LOW);
  }
  
}

void readMotorCurrent()
{
  
}

void switchOnOff()
{
  
}

void sensorInterrupt()
{
   static int Ctr;
  unsigned long Time;
  Ctr++;
  if (Ctr >= Counts) { // 36 / 4 = 9 so we are taking an average of 9 readings to use in our calculations
    Time = micros();
    timeX += (Time - LastTime); // this time is accumulative ovrer those 9 readings
    LastTime = Time;
    PulseCtr ++;
    Ctr = 0;
  }
}

void readRpm()
{
  radioTxArray[0] = Input;
  
  cli ();         // clear interrupts flag
  Time = timeX;   // Make a copy so if an interrupt occurs timeX can be altered and not affect the results.
  timeX = 0;
  sei ();         // set interrupts flag
  if (PulseCtr > 0) {
    Input =  (double) (PulsesPerMinute /  (double)(( (unsigned long)Time ) *  (unsigned long)PulseCtr)); // double has more percision
    //   PulseCtr = 0; // set pulse Ctr to zero
    debug();
    if(!myPID.Compute()) Serial.println();
    
    analogWrite(PWMpin, Output);
    //
    Time = 0; // set time to zero to wait for the next rpm trigger.
    Counter += PulseCtr;
    PulseCtr = 0; // set pulse Ctr to zero
    // we are automatically adjusting the diviser to preserve processor time after calculating last rpm
    // starting at 0 RPM ~ 400+RPM we adjust the division of the pulses per revolution

   // Counts = 100; constrain(map((int)Input, startRamp, MaxRPM, 1, PulsesPerRevolution), 1, PulsesPerRevolution);
    PulsesPerMinute = (60.0 * 1000000.0) / (double)((double)PulsesPerRevolution / (double)Counts);
  }
}

void debug()

{
  /*
  char S[20];
  static unsigned long SpamTimer;
  if ((unsigned long)(millis() - SpamTimer) >= 100) {
    SpamTimer = millis();
    Serial.print(" rpm: "); Serial.print(Input );
    Serial.print(" motor: "); Serial.print(radioRxArray[0]);
    Serial.print(" setpoint: "); Serial.print(radioRxArray[1]);
    Serial.print(" height "); Serial.print(radioRxArray[3]);
    Serial.print(" cal: "); Serial.print(radioRxArray[4]);
    Serial.print(" dist: "); Serial.print(radioRxArray[5]);
    Serial.print(" ping sensor "); Serial.println(dist);
  }
  */
}

I think what you have should work - unless there is a small glitch somewhere. I am not good at spotting them.

You might move the line

radio.writeAckPayload(1, radioTxArray, sizeof(radioTxArray));

above the IF in the function.

Note that the ackPayload that will be sent is the value that was in place prior to the Tx from the Master - in other words it is always one step behind - but that should not matter.

Did you try my code unchanged and did that work for you?

...R

Robin2:
I think what you have should work - unless there is a small glitch somewhere. I am not good at spotting them.

You might move the line

radio.writeAckPayload(1, radioTxArray, sizeof(radioTxArray));

above the IF in the function.

Note that the ackPayload that will be sent is the value that was in place prior to the Tx from the Master - in other words it is always one step behind - but that should not matter.

Thanks Robin2. I tried moving that line in the client to above the if function but made no difference. The client, or hand controller is receiving valid data from the master but the master appears to not be receiving updated data from the client. What ever the client sends on the first transmission after rebooting the master is what the master continues to report receiving via radioRxArray. I still don't know if the master is not updating radioRxArray, or the slave is not sending the newest version of radioTxArray.

I am a bit unsure about what to do with the

byte radioRxArrayLen = 9; // NB this 4 is the number of bytes in the 2 ints that will be recieved

since the length will change will it not as some arrays go over 100?
I tried

byte radioRxArrayLen = radioRxArray;

but got invalid conversion from int to byte.

Did you try my code unchanged and did that work for you?

...R

I did, thank you. Your a genius!
They worked as expected.
I must admit I only tried your code AFTER I tried my cobbled together merged mess first.

My esc on the master end just stopped functioning altogether so I will have to order a replacement but the should not stop me getting these radio's and a few other things sorted out.

Thank you for your great help Robin2, I really appreciate it.

moose4621:
since the length will change will it not as some arrays go over 100?

I'm not sure I understand. AFAIK an ackPayload cannot be more than 32 bytes.

Why not use a fixed size for all transmissions?

...R

Robin2:
I'm not sure I understand. AFAIK an ackPayload cannot be more than 32 bytes.

Why not use a fixed size for all transmissions?

...R

It is more than likely ME who does not understand :frowning:

So a byte is a numerical value between 0 - 255, each index in the array radioTxArray will not exceed these values.
Therefore:

int radioRxArray[6];
//0 = motor on/off, 1 = setPoint, 2 = , 3 = Height, 4 = Cal, 5 = Dist
radioRxArrayLen = 6;//number of index's in the array?

If this is the case, why did you have

int ackMessg[6];
byte ackMessgLen = 4; // NB this 4 is the number of bytes in the 2 ints that will be recieved

in your example?

moose4621:
If this is the case, why did you have

int ackMessg[6];

byte ackMessgLen = 4; // NB this 4 is the number of bytes in the 2 ints that will be recieved



in your example?

The first line creates space for an array with 6 ints - that requires 12 bytes.
The second line define how many bytes are being received (and sent) - 4 bytes is required for 2 ints and that will easily fit in the space for 12 bytes.

I have created a bigger array than is needed - I like to be sure that there is plenty of space for the data to avoid memory corruption. It would probably be sufficient to define int ackMessg[2]

...R

Robin2:
The first line creates space for an array with 6 ints - that requires 12 bytes.
The second line define how many bytes are being received (and sent) - 4 bytes is required for 2 ints and that will easily fit in the space for 12 bytes.

I have created a bigger array than is needed - I like to be sure that there is plenty of space for the data to avoid memory corruption. It would probably be sufficient to define int ackMessg[2]

...R

This simple explanation got the radio issues sorted thanks Paul.
I had too few bytes for radioRxArrayLen.
Both radios now updating data and Tx/Rx as expected.
If only I had listened to you weeks ago :wink:

Ummmm, I do have a couple of threads going at the moment, regarding different and unrelated, (I hope), problems. Since my other problem with a countdown timer did not really relate to this thread, I started another. Did I break protocol here?

Many thanks Robin2

Unfortunately, while I have great radio comms now, it has interferred with the PID control on the master.

The txIntervalMillis creates a pulse in the motor control circuit by interrupting the PID updates.

I tried different txIntervalMillis, including 0 but no success.

You have not really provided any information about the txIntervalMillis interrupting the PID updates.

Obviously when it is time to do the wireless stuff the Arduino will take a little longer to complete a loop().

If that causes a problem for the PID system either the PID system is poorly designed or the Arduino does not have the capacity for the job.

Due to lack of information I have no idea if there might be other considerations.

...R

The server end consists of a geared motor connected to an esc which is in turn controlled by the pwm output from pin 6 on a Nano.
The output shaft on the motor has a disc with 36 slots and an LM393 slot type optocoupler connected to pin 2 of the nano.
A ping sensor is connected to pins 3 & 4.
There will be a relay and current sensor connected at some point but not yet.

Latest code.
Master:

/*
  Arduino Nano
  Pin allocation:
  D1
  D2 = speed sensor (interrupt 0)
  D3 = Ping trigger
  D4 = Ping Echo
  D5 = ESC relay
  D6 = ESC PWM out
  D7 = Radio CE
  D8 = Radio CSN
  D9 =
  D10 =
  D11 = Radio MOSI
  D12 = Radio MISO
  D13 = Radio SCK
  A0 = Current sensor
  A1 =
  A2 =
  A3 =
  A4 =
  A5 =
  A6 =
  A7 =
*/


//*********Height Sensor Stuff*********
#include <NewPing.h>
#define TRIGGER_PIN  3  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     4  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 400 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

int dist;

//*****Motor Controller Stuff******
#include <PID_v2.h>

//Define Variables we'll be connecting to
double Setpoint, Input, Output;
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, 0.4, 5.6, 0, DIRECT);

#define PWMpin  6

volatile unsigned long timeX = 1;
int PulsesPerRevolution = 36;
int MaxRPM = 400;
volatile int Counts = 1;
double PulsesPerMinute;
volatile unsigned long LastTime;
volatile int PulseCtr;
unsigned long Counter;
int startRamp;
unsigned long Time;
int rpm;
int printRpm; // Averaged over several readings to smooth it out.
volatile int rpmArray[5] = {0, 0, 0, 0, 0}; // For printRpm

//********Radio Stuff*******
#include <nRF24L01.h>
#include <RF24.h>
#include <SPI.h>

#include <nRF24L01.h>
#include <RF24.h>

#define CE_PIN   7
#define CSN_PIN 8

// NOTE: the "LL" at the end of the constant is "LongLong" type
// These are the IDs of each of the slaves
const uint64_t slaveID[2] = {0xE8E8F0F0E1LL, 0xE8E8F0F0E2LL} ;

RF24 radio(CE_PIN, CSN_PIN); // Create a Radio

int radioTxArray[2];

unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 1000;
int txVal = 0;
int radioRxArray[6];
//0 = motor on/off, 1 = setPoint, 2 = , 3 = Height, 4 = Cal, 5 = Dist
byte radioRxArrayLen = 12; // NB this 4 is the number of bytes in the 2 ints that will be recieved


bool isStarted = false;
bool isCal = false;
bool isDist = false;
bool go = false;

void setup() {
  // note that 1666666.67 = (60 seonds * 1000000 microseconds)microseconds in a minute / (36 / 9) pulses in 1 revolution
  PulsesPerMinute = (60 * 1000000) / (PulsesPerRevolution / Counts);

  pinMode(2, INPUT_PULLUP);
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("PID controlled Seeder Master R0");
  delay(1000);
  //Digital Pin 2 Set As An Interrupt for tacho.
  attachInterrupt(0, sensorInterrupt, FALLING);

  //Motor PID control stuff.
  startRamp = 10;//map(PulsesPerRevolution , 1, MaxRPM, MaxRPM, 2);
  myPID.SetSampleTime(1);
  myPID.SetOutputLimits(40, (int) 255);
  PulseCtr = 0;
  myPID.SetMode(AUTOMATIC);
  analogWrite(PWMpin, 60);
  myPID.Compute();
  delay(11);
  myPID.Compute();

  //Radio stuff.
  radio.begin();
  radio.setDataRate( RF24_250KBPS );
  radio.enableAckPayload();
  radio.setRetries(3, 5); // delay, count

}

void loop() {
  // put your main code here, to run repeatedly:
  exchangeData();
  getSetPoint();
  getStartStop();
  getHeight();
  readMotorCurrent();
  switchOnOff();
  readRpm();
  debug();
  static unsigned long SpamTimer;

}

void exchangeData()//Send and receice radioTxArray and radioRxArray.
{

  currentMillis = millis();
  if (currentMillis - prevMillis >= txIntervalMillis) {

    radio.openWritingPipe(slaveID[0]); // calls the first slave
    // there could be a FOR loop to call several slaves in turn
    bool rslt;
    rslt = radio.write( radioTxArray, sizeof(radioTxArray) );
    Serial.print("\nRSLT (1 = success) ");
    Serial.println(rslt);
    Serial.print("Data Sent ");
    Serial.print(radioTxArray[0]);
    Serial.print("  ");
    Serial.println(radioTxArray[1]);
    if ( radio.isAckPayloadAvailable() ) {
      radio.read(radioRxArray, radioRxArrayLen);
      Serial.print("Acknowledge received: ");
      Serial.print(radioRxArray[0]); Serial.print("  ");
      Serial.print(radioRxArray[1]); Serial.print("  ");
      Serial.print(radioRxArray[2]); Serial.print("  ");
      Serial.print(radioRxArray[3]); Serial.print("  ");
      Serial.print(radioRxArray[4]); Serial.print("  ");
      Serial.print(radioRxArray[5]); Serial.print("  isStarted = ");
      Serial.println(isStarted);

    }
    prevMillis = millis();
  }

}

void getSetPoint()//Required rpm from hand controller
{
  if (isStarted == true) {
    Setpoint = radioRxArray[1];
    // Setpoint = 30;
  }
  else
  {
    Setpoint = 0;
    radioTxArray[0] = 0;
  }
}

void getStartStop()// Determine if motor should start on not using on/off and/or height switching from hand controller.
{
  if (radioRxArray[0] == 1) //On/off = on.
  {
    if (radioRxArray[3] == 0)//Height switching = off.
    {
      isStarted = true;
    }
    if (dist <= radioRxArray[5] == 1 && radioRxArray[3] == 1)// dist is less than switch point & Height switching = on.
    {
      isStarted = true;
    }
  }

  else
  {
    isStarted = false;
  }

}

void getHeight() //read ping sensor.
{

  unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  dist = (uS / US_ROUNDTRIP_CM); //raw distance in cm.



}

void readMotorCurrent()
{

}

void switchOnOff() //activate relay on pin 5 to turn esc on and off.
{
  if (isStarted == true)
  {
    digitalWrite(5, HIGH);

  }
}

void sensorInterrupt() // for tacho.
{
  static int Ctr;
  unsigned long Time;
  Ctr++;
  if (Ctr >= Counts) { // 36 / 4 = 9 so we are taking an average of 9 readings to use in our calculations
    Time = micros();
    timeX += (Time - LastTime); // this time is accumulative ovrer those 9 readings
    LastTime = Time;
    PulseCtr ++;
    Ctr = 0;
  }
}

void readRpm()
{

  cli ();         // clear interrupts flag
  Time = timeX;   // Make a copy so if an interrupt occurs timeX can be altered and not affect the results.
  timeX = 0;
  sei ();         // set interrupts flag
  if (PulseCtr > 0) {
    Input =  (double) (PulsesPerMinute /  (double)(( (unsigned long)Time ) *  (unsigned long)PulseCtr)); // double has more percision
    //   PulseCtr = 0; // set pulse Ctr to zero
    // debug();
    if (!myPID.Compute()); //Serial.println();

    analogWrite(PWMpin, Output);
    //
    Time = 0; // set time to zero to wait for the next rpm trigger.
    Counter += PulseCtr;
    PulseCtr = 0; // set pulse Ctr to zero
    // we are automatically adjusting the diviser to preserve processor time after calculating last rpm
    // starting at 0 RPM ~ 400+RPM we adjust the division of the pulses per revolution

    // Counts = 100; constrain(map((int)Input, startRamp, MaxRPM, 1, PulsesPerRevolution), 1, PulsesPerRevolution);
    PulsesPerMinute = (60.0 * 1000000.0) / (double)((double)PulsesPerRevolution / (double)Counts);
    //Fill rpm array with rpm readings and average them for display.
    rpmArray[0] = rpmArray[1];
    rpmArray[1] = rpmArray[2];
    rpmArray[2] = rpmArray[3];
    rpmArray[3] = rpmArray[4];
    rpmArray[4] = Input;
    //Last 5 Average RPM Counts Eqauls....
    printRpm = (rpmArray[0] + rpmArray[1] + rpmArray[2] + rpmArray[3] + rpmArray[4]) / 5;

    radioTxArray[0] = printRpm;//average sent to hand controller

  }
}

void debug()

{
  
}

Next post.

When the motor is turned on, I get this on serial.

RSLT (1 = success) 1
Data Sent 46  0
Acknowledge received: 1  34  0  0  0  80  isStarted = 1
 In   8.09 Setpt  34.00 /\T 129796.00 Kp  10.37 Ki  98.58 Kd         0 Out 108
 In  44.82 Setpt  34.00 /\T 38156.00 Kp  -4.33 Ki 101.83 Kd         0 Out 97
 In  43.30 Setpt  34.00 /\T 38100.00 Kp  -3.72 Ki  95.60 Kd         0 Out 91
 In  41.21 Setpt  34.00 /\T 39688.00 Kp  -2.89 Ki  90.89 Kd         0 Out 88
 In  36.71 Setpt  34.00 /\T 45440.00 Kp  -1.08 Ki  87.06 Kd         0 Out 85
 In  36.61 Setpt  34.00 /\T 45908.00 Kp  -1.04 Ki  84.69 Kd         0 Out 83
 In  61.19 Setpt  34.00 /\T 30780.00 Kp -10.88 Ki  69.90 Kd         0 Out 59
 In  24.81 Setpt  34.00 /\T 68108.00 Kp   3.67 Ki  72.44 Kd         0 Out 76

RSLT (1 = success) 1
Data Sent 48  0
Acknowledge received: 1  34  0  0  0  80  isStarted = 1
 In   7.08 Setpt  34.00 /\T 123784.00 Kp  10.77 Ki  91.10 Kd         0 Out 101
 In  38.23 Setpt  34.00 /\T 45604.00 Kp  -1.69 Ki  90.23 Kd         0 Out 88
 In  33.23 Setpt  34.00 /\T 46076.00 Kp   0.31 Ki  77.87 Kd         0 Out 78
 In  30.62 Setpt  34.00 /\T 61244.00 Kp   1.35 Ki  80.72 Kd         0 Out 82
 In  34.09 Setpt  34.00 /\T 54600.00 Kp  -0.04 Ki  81.29 Kd         0 Out 81
 In  33.83 Setpt  34.00 /\T 55080.00 Kp   0.07 Ki  81.20 Kd         0 Out 81
 In  33.26 Setpt  34.00 /\T 46016.00 Kp   0.29 Ki  81.26 Kd         0 Out 81

In = rpm, Out = pwm out to esc.

You can see there is a clear effect on the PID loop from the radio.

The code you posted does not seem to print the lines of data that you refer to. The problem is often where you don't expect it (otherwise you find it easily :slight_smile: )

All those print statements in exchangeData() will slow things down.

Could you ignore the first computation after reading wireless data?

...R

Robin2:
The code you posted does not seem to print the lines of data that you refer to. The problem is often where you don't expect it (otherwise you find it easily :slight_smile: )

All those print statements in exchangeData() will slow things down.

Could you ignore the first computation after reading wireless data?

...R

Thank you for your time Robin2, I appreciate it a lot.

The data is generated by the PID library and only comes through when it gets input from the rpm sensor.

You were right on the money with the print statements in exchangeData(). Got rid of them and the whole thing started to work again. PID's are way off but at least no delays. This, again, was so obvious when pointed out. I really must start thinking in simpler terms instead of assuming the problems are complex and beyond me.

Not sure which computation you mean when you refer to the one after wireless data.

moose4621:
Not sure which computation you mean when you refer to the one after wireless data.

From the examples you posted it seems that only one PID calculation is affected by reading the wireless. I just wondered if you could ignore that particular PID calcluation.

...R