Go Down

Topic: Radio code appears to be interfering with other functions. (Read 3661 times) previous topic - next topic

moose4621

I'll look to help resolve the rest here tomorrow Long day at work
I am so thankful for what you have done already. I feel I have well and truly worn out my welcome.

I have trouble understanding why something so relatively simple can be so hard to achieve.

The above code did not make any difference and the Nano is still freezing at seemingly random times.

zhomeslice

I am so thankful for what you have done already. I feel I have well and truly worn out my welcome.

I have trouble understanding why something so relatively simple can be so hard to achieve.

The above code did not make any difference and the Nano is still freezing at seemingly random times.
My friend, You are welcome and no you haven't worn your welcome out!  :)
Your project is sweet and I plan on helping you to the end. I want to hear you say it works!!!
My day job is't as rewarding as my hobby and we are working on my hobby which is electronics. Just three years ago I was introduced to the Arduino and it gave me a huge jump back into what I love. Before then I would build an analog circuit here and there to do something cool, or program web pages for friends. Now I can combine the two and it is fun. 
Helping you has also cleaned up my encoder code, further improved my PID routine to be more accurate among other things. Now we are tackling what everyone faces when pushing the atmega328p chip to its limits. I know with your project we can make it fit. I am here and will be happy to continue to help.  And don't give up you are close, so close!  
Don't worry about the minimal changes I've been sending this is because I've had to work long hours  the last couple days. I have had little time to review where we are at.  I've sent small changes in hopes not to make bigger messes, and I don't want you to think I've vanishes either.  :)


I know that the reason for the forum is to post for others to be helped but if you would like please feel free to private message me. You avatar photo shows we are of similar age. I wouldn't mind sharing other information with you if you would like.
HC

moose4621

Thank you for your encouragement and your commitment. I can see you get a real kick out of this sort of thing as I do. I am a very recent student of the arduino as you can tell although I did get my first UNO several years ago and made a SD card recording anemometer for a wind generator project, but I didn't do anything more with arduino until a couple of months back.

I have been playing with the code for the past couple of hours and have discovered this.
The "void getSetpoint()" was resetting the setpoint to 0 at isStarted == false. This meant that on restart, Setpoint would remain at 0 until the next radio message. Fixed.
I then discovered through my "backup" sketch that it wasn't exiting "void switchOnOff()" as expected so I made some changes to that.

Again, through my backup code I fount that the "void readRpm()" was slowing down my "void computeOutput()" which then got me thinking about the PID control. I then shifted the myPID.Compute() to outside the "void readRpm()" and voila, press start and it goes straight into the PID loop.

Unfortunately, all is not good though. I have buggered up the tacho :-(
It works OK above about 35 rpm but below that it jumps to a reading well above actual rpm and drives the output down to minimum to try to achieve setpoint based on the erroneous Input.

Roughly tuned PID's at (.5, 1.4, 0). Setpoint 40. Actual rpm is close to In.
Code: [Select]

n  39.64 Setpt  40.00 /\T 1408.00 Kp   0.18 Ki  90.42 Kd         0 Out 90
In  39.64 Setpt  40.00 /\T 1408.00 Kp   0.18 Ki  90.47 Kd         0 Out 90
In  39.64 Setpt  40.00 /\T 1408.00 Kp   0.18 Ki  90.53 Kd         0 Out 90
In  41.81 Setpt  40.00 /\T 1000.00 Kp  -0.91 Ki  90.47 Kd         0 Out 89
In  41.81 Setpt  40.00 /\T 1000.00 Kp  -0.91 Ki  90.21 Kd         0 Out 89
In  41.81 Setpt  40.00 /\T 1000.00 Kp  -0.91 Ki  89.95 Kd         0 Out 89
In  41.81 Setpt  40.00 /\T 1000.00 Kp  -0.91 Ki  89.69 Kd         0 Out 88
In  41.81 Setpt  40.00 /\T 1432.00 Kp  -0.91 Ki  89.43 Kd         0 Out 88



Same PID's but with Setpoint at 10 rpm.
Actual rpm in this test is close to 1rpm which is also correct for PWM of 33. So tacho is erroneously reading 32.66 rpm.
Code: [Select]

In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1112.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1048.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33
In  32.66 Setpt  10.00 /\T 1040.00 Kp -11.33 Ki  32.98 Kd         0 Out 33


Here's my latest code.
PID_Controlled_seeder_master_R6_with_413182_47d.

Pt 1
Code: [Select]



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


//*********Height Sensor Stuff*********
#define TriggerPin  4
// echo pin will be interrupt 1 on pin 3
#define DelayBetweenPings 50 // it works to about 5 milliseconds between pings

volatile  unsigned long PingTime;
volatile  unsigned long edgeTime;
volatile  uint8_t PCintLast;
int PinMask = B1000; // pin 3
float Measurements = 0;

void PintTimer( )
{
  static uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PIND; //  get the state of all pins 0-8  quick snap shot
  sei();                    // re enable other interrupts
  uint16_t dTime;
  PCintLast = pin;          // we memorize the current state of all PINs
  int CheckPin = 3;
  if ((pin >> CheckPin & 1))edgeTime = cTime; //Pulse went HIGH store the start time
  else { // Pulse Went low calculate the duratoin
    dTime = cTime - edgeTime; // Calculate the change in time
    PingTime = dTime; // Lets Store any duration up to 65535 micro seconds
  }
}

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, .5, 1.4, 0, DIRECT);

#define PWMpin  6//esc
int currentSensor = A0;
int amps;

volatile unsigned long timeX = 1;
int PulsesPerRevolution = 36;
int MaxRPM = 400;
volatile int Counts = 1;
double PulsesPerMinute;
volatile unsigned long LastTime;
volatile int PulseCtrX;
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
int SampleDuration = 50; // in Milliseconds I used 10 but I was testing for overall workload
// 20 I thing will work well for you bur 100 could also be good
float AvgArray[100];//tach per post 413182 #47
int Readings = 0; //tach per post 413182 #47

//********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 = 600;
int txVal = 0;
int radioRxArray[6];
//0 = motor on/off, 1 = setPoint, 2 = , 3 = Height, 4 = Cal, 5 = Dist
byte radioRxArrayLen = 12;


bool isStarted = false;
bool isCal = false;
bool isDist = false;
bool go = false;
bool Skip = 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);
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("PID controlled Seeder Master R6 with 413182_47");
  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(33, (int) 255);
  PulseCtr = 0;
  //myPID.SetMode(AUTOMATIC);
  //analogWrite(PWMpin, 60);
  //myPID.Compute();
  //delay(11);
  //myPID.Compute();

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

  pinMode(3, INPUT);
  pinMode(4, OUTPUT);
  attachInterrupt(1, PintTimer, CHANGE );

}




moose4621

Pt 2.
Code: [Select]


void loop() {

  // put your main code here, to run repeatedly:
  exchangeData();
  getSetPoint();
  getStartStop();
  PingIt(); // Manage ping data
  readMotorCurrent();
  switchOnOff();
  readRpm();
  debug();
  static unsigned long SpamTimer;

}

void PingTrigger(int Pin) {
  digitalWrite(Pin, LOW);
  delayMicroseconds(1);
  digitalWrite(Pin, HIGH); // Trigger another pulse
  delayMicroseconds(10);
  digitalWrite(Pin, LOW);
}

bool AllClear() {
  return (!(PinMask & PIND)); //  all the input pins are LOW
}

void exchangeData()
{
  radioTxArray[0] = AverageValue();//average sent to hand controller

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

    radio.openWritingPipe(slaveID[0]);
    bool rslt;
    rslt = radio.write( radioTxArray, sizeof(radioTxArray) );

    if ( radio.isAckPayloadAvailable() ) {
      radio.read(radioRxArray, radioRxArrayLen);
      Skip = true;
    }
    prevMillis = millis();
  }

}

void getSetPoint()
{

  Setpoint = radioRxArray[1];

}

void getStartStop()
{
  if (radioRxArray[0] == 1) //start/stop button on.
  {
    if (radioRxArray[3] == 0) //height switch off.
    {
      isStarted = true;
    }
    if (Measurements <= radioRxArray[5] && radioRxArray[3] == 1) //if ping dist is less than dist set point and height switch is on.
    {
      isStarted = true;
    }
    else if (Measurements >= radioRxArray[5] && radioRxArray[3] == 1)
    {
      isStarted = false;
    }
  }

  else
  {
    isStarted = false;
  }

}

void PingIt() {
  unsigned long PT;
  static unsigned long PingTimer;
  if ( AllClear()) { // Wait
    if ((unsigned long)(millis() - PingTimer) >= DelayBetweenPings) {
      PingTimer = millis();
      cli ();         // clear interrupts flag
      PT = PingTime;
      sei ();         // set interrupts flag
      Measurements = (float) (microsecondsToCentimeters(PT));
      PingTrigger(TriggerPin); // Send another ping
    }
  }

}

float microsecondsToCentimeters(long microseconds) {
  return (float)microseconds / 29 / 2;
}


void readMotorCurrent()// shut the motor down on over current.
// send message to hand held.
{

  amps = analogRead(currentSensor);
  if (amps >= 780)//Amps to switch off at. ((0.066*amps)+2.5)*204.6
    //66mv per amp, 2.5 = neutral, 1023/5=204.6 per volt.
  {
    isStarted = false;
    radioTxArray[1] = 1;

  }
  else radioTxArray[1] = 0;

}

void switchOnOff()
{

  if (isStarted == false)
  {
    myPID.SetMode(MANUAL);
    Output = 0;
    analogWrite(PWMpin, Output);// Stop NOW
    AverageReset(0);
    go = false;
  }
  else if (isStarted == true && go == false)
    //else if (myPID.GetMode() == MANUAL)
  {
    myPID.SetMode(AUTOMATIC);
    Output = map(Setpoint, 2, 45, 32, 255);
    analogWrite(PWMpin, Output);
    myPID.Compute();
    delay(11);
    myPID.Compute();
    readRpm();
    go = true;
  }
  else if (isStarted == true && go == true)
  {
    myPID.Compute();
    //if (!myPID.Compute()) Serial.println();
    analogWrite(PWMpin, Output);
  }


}

void AverageCapture(float in) {
  static int Position = 0;
  if (Readings == 1) Position = 1; // << Syncronize position with readings upon reset
  // The Readings variable is the Startup counter to allow us to be accurate while we count up to 100 readings
  // In order for readings to work we must have Position counter match .
  // So if we change the readings to Zero we must change the Position counter also.
  AvgArray[Position] = in;
  Position++;
  Readings++;
  Readings = min (100, Readings); // 100 readings 0-99;
  if (Position >= 100)Position = 0;//99 spots
}

void AverageReset(int Value) { // Value to be displayed as the average usually Zero upon reset
  Readings = 1;
  AvgArray[0] = Value; //
}
float AverageValue() {
  float Total = 0;
  float Average;
  if (!Readings)return (0.0);
  for (int Position = 0; Position < Readings; Position++) {
    Total += AvgArray[Position];
  }
  Average = Total / Readings;
  return (Average);
}


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;
    PulseCtrX ++;  // << changed
    Ctr = 0;
  }
}


void readRpm()
{
  if (!PulseCtrX) return; // << Added lets not stop interrupts unless we know we are ready (keep other code happy).
  cli ();         // clear interrupts flag
  Time = timeX;
  timeX = 0;
  PulseCtr = PulseCtrX; // << Added
  PulseCtrX = 0; // << Added
  sei ();
  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 (!Skip) {
    // if (!myPID.Compute()) Serial.println();
    //analogWrite(PWMpin, Output); // <<<<<<<<<<< use this one instead of the one below
    //}



    AverageCapture(Input); // <<< This is a good spot for to capture the value it is after any time critical code

  }

  else Skip = false;
  //    analogWrite(PWMpin, Output); // <<<<<<<<<<<<<<<<<<<<< This does nothing without myPID.Compute();
  if (Time > ((SampleDuration + 1) * 1000))Counts--;
  if (Time < ((SampleDuration - 1) * 1000))Counts++;
  Counts = constrain(Counts, PulsesPerRevolution * .1, PulsesPerRevolution * 4);

  Time = 0; // set time to zero to wait for the next rpm trigger.
  Counter += PulseCtr;
  PulseCtr = 0; // set pulse Ctr to zero

  PulsesPerMinute = (60.0 * 1000000.0) / (double)((double)PulsesPerRevolution / (double)Counts);

}

void debug()

{
  /*
    char S[20];
    static unsigned long PingTimer;
    if ((unsigned long)(millis() - PingTimer) >= 100) {
    PingTimer = millis();
    //Serial.print(amps);
    Serial.print(dtostrf(Measurements, 6, 1, S));
    Serial.print(" Average: "); Serial.print(dtostrf(AverageValue(), 6, 1, S));
    Serial.println();

    }
  */

}


I need to put notes in there to tell you where I have been.

It's after 1am :-(  Bed time.

I will PM you with my ramblings which are of no interest to anyone else from now on.

moose4621

A brief explanation of what I have done.
I started with a sketch pre post #47 mods since it was most likely to enter the PID loop.

Code: [Select]

void getSetPoint()
{
 
  Setpoint = radioRxArray[1];// removed if isStarted = false, Setpoint = 0 statements.

}


Code: [Select]

void switchOnOff()
{

  if (isStarted == false)
  {
    myPID.SetMode(MANUAL);
    Output = 0;
    analogWrite(PWMpin, Output);// Stop NOW
    AverageReset(0);
    go = false;
  }
  else if (isStarted == true && go == false)//<<used "bool go" instead to detect state change.
    //else if (myPID.GetMode() == MANUAL)//<<< commented this out
  {
    myPID.SetMode(AUTOMATIC);
    Output = map(Setpoint, 2, 45, 32, 255); //<<Set start up Output to somewhere near Setpoint.
    analogWrite(PWMpin, Output);
    myPID.Compute();
    delay(11);
    myPID.Compute();
    readRpm();
    go = true;//<< reset state change.
  }
  else if (isStarted == true && go == true)//<< New run routine. Uses code from void readRpm.
  {
    myPID.Compute();//<< found skip routine was not required.
    //if (!myPID.Compute()) Serial.println();
    analogWrite(PWMpin, Output);
  }


void readRpm;
Code: [Select]

 if (PulseCtr > 0) {
    Input =  (double) (PulsesPerMinute /  (double)(( (unsigned long)Time ) *  (unsigned long)PulseCtr)); // double has more percision
    //   PulseCtr = 0; // set pulse Ctr to zero//<<<Commented all of this out.
    // debug();
    // if (!Skip) {
    // if (!myPID.Compute()) Serial.println();
    //analogWrite(PWMpin, Output);
    //}



    AverageCapture(Input); // <<< This is a good spot for to capture the value it is after any time critical code

  }

  else Skip = false;


I hope this helps make clear what I have done.

zhomeslice

My day was long again but I am working on splitting the code up into small libraries where we can easily test and troubleshoot them and then insert them back into you program without struggling to make sure we don't miss something.
I'll post more tomorrow I hope it isn't as long of a day :)
HC

moose4621

I have a working sketch which I can use with the exception of the ping sensor which seems to play havoc with the rpm sensor or code. I have tried extending "DelayBetweenPings" out as far as 3 seconds and while it improves the rpm sensor, it play havoc with the PID loop and slows it down dramatically.
Almost everything else seems to be working ok. The only other issue is each time I press start, the start up power seems to be cumulative and the motor jumps to a higher power setting each time before settling down to a smooth run at setpoint.

Here's the latest code.
PID_controlled_seeder_master_R7, Pt 1
Code: [Select]



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


//*********Height Sensor Stuff*********
#define TriggerPin  4
// echo pin will be interrupt 1 on pin 3
#define DelayBetweenPings 50 // it works to about 5 milliseconds between pings

volatile  unsigned long PingTime;
volatile  unsigned long edgeTime;
volatile  uint8_t PCintLast;
int PinMask = B1000; // pin 3
float Measurements = 0;

void PintTimer( )
{
  static uint8_t pin;
  static unsigned long cTime;
  cTime = micros();         // micros() return a uint32_t
  pin = PIND; //  get the state of all pins 0-8  quick snap shot
  sei();                    // re enable other interrupts
  uint16_t dTime;
  PCintLast = pin;          // we memorize the current state of all PINs
  int CheckPin = 3;
  if ((pin >> CheckPin & 1))edgeTime = cTime; //Pulse went HIGH store the start time
  else { // Pulse Went low calculate the duratoin
    dTime = cTime - edgeTime; // Calculate the change in time
    PingTime = dTime; // Lets Store any duration up to 65535 micro seconds
  }
}

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, 1.9, 1.5, 0, DIRECT);

#define PWMpin  6//esc
int currentSensor = A0;
int amps;

volatile unsigned long timeX = 1;
int PulsesPerRevolution = 36;
int MaxRPM = 400;
volatile int Counts = 3;
double PulsesPerMinute;
volatile unsigned long LastTime;
volatile int PulseCtrX;
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
int SampleDuration = 50; // in Milliseconds I used 10 but I was testing for overall workload
// 20 I thing will work well for you bur 100 could also be good
float AvgArray[2];//tach per post 413182 #47
int Readings = 0; //tach per post 413182 #47

//********Radio Stuff*******
#include <nRF24L01.h>
#include <RF24.h>
#include <SPI.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 = 200;
int txVal = 0;
int radioRxArray[6];
//0 = motor on/off, 1 = setPoint, 2 = , 3 = Height, 4 = Cal, 5 = Dist
byte radioRxArrayLen = 12;


bool isStarted = false;
bool isCal = false;
bool isDist = false;
bool go = false;
bool Skip = 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);
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("PID controlled Seeder Master R6 with 413182_47");
  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(34, (int) 255);
  PulseCtr = 0;
  //myPID.SetMode(AUTOMATIC);
  //analogWrite(PWMpin, 60);
  //myPID.Compute();
  //delay(11);
  //myPID.Compute();

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

  pinMode(3, INPUT);
  pinMode(4, OUTPUT);
  attachInterrupt(1, PintTimer, CHANGE );

}

moose4621

PID_controlled_seeder_master_R7, Pt 2

Code: [Select]


void loop() {

  // put your main code here, to run repeatedly:
  exchangeData();
  getSetPoint();
  getStartStop();
  PingIt(); // Manage ping data
  readMotorCurrent();
  switchOnOff();
  readRpm();
  debug();
  static unsigned long SpamTimer;

}

void PingTrigger(int Pin) {
  digitalWrite(Pin, LOW);
  delayMicroseconds(1);
  digitalWrite(Pin, HIGH); // Trigger another pulse
  delayMicroseconds(10);
  digitalWrite(Pin, LOW);
}

bool AllClear() {
  return (!(PinMask & PIND)); //  all the input pins are LOW
}

void exchangeData()
{
  radioTxArray[0] = AverageValue();//average sent to hand controller

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

    radio.openWritingPipe(slaveID[0]);
    bool rslt;
    rslt = radio.write( radioTxArray, sizeof(radioTxArray) );

    if ( radio.isAckPayloadAvailable() ) {
      radio.read(radioRxArray, radioRxArrayLen);
      Skip = true;
    }
    prevMillis = millis();
  }

}

void getSetPoint()
{

  Setpoint = radioRxArray[1];

}

void getStartStop()
{
  if (radioRxArray[0] == 1) //start/stop button on.
  {
    if (radioRxArray[3] == 0) //height switch off.
    {
      isStarted = true;
    }
    if (Measurements <= radioRxArray[5] && radioRxArray[3] == 1) //if ping dist is less than dist set point and height switch is on.
    {
      isStarted = true;
    }
    else if (Measurements >= radioRxArray[5] && radioRxArray[3] == 1)
    {
      isStarted = false;
    }
  }

  else
  {
    isStarted = false;
  }

}

void PingIt() {
  /*
    unsigned long PT;
    static unsigned long PingTimer;
    if ( AllClear()) { // Wait
      if ((unsigned long)(millis() - PingTimer) >= DelayBetweenPings) {
        PingTimer = millis();
        cli ();         // clear interrupts flag
        PT = PingTime;
        sei ();         // set interrupts flag
        Measurements = (float) (microsecondsToCentimeters(PT));
        PingTrigger(TriggerPin); // Send another ping
      }
    }
  */
}

float microsecondsToCentimeters(long microseconds) {
  return (float)microseconds / 29 / 2;
}


void readMotorCurrent()// shut the motor down on over current.
// send message to hand held.
{

  amps = analogRead(currentSensor);
  if (amps >= 780)//Amps to switch off at. ((0.066*amps)+2.5)*204.6
    //66mv per amp, 2.5 = neutral, 1023/5=204.6 per volt.
  {
    isStarted = false;
    radioTxArray[1] = 1;

  }
  else radioTxArray[1] = 0;

}

void switchOnOff()
{

  if (isStarted == false)
  {
    myPID.SetMode(MANUAL);
    Output = 0;
    analogWrite(PWMpin, Output);// Stop NOW
    AverageReset(0);
    go = false;
  }
  else if (isStarted == true && go == false)
    //else if (myPID.GetMode() == MANUAL)
  {
    myPID.SetMode(AUTOMATIC);
    //Output = map(Setpoint, 4, 45, 34, 255);
    analogWrite(PWMpin, 50);
    myPID.Compute();
    delay(11);
    myPID.Compute();
    readRpm();
    go = true;
  }
  else if (isStarted == true && go == true)
  {
    myPID.Compute();
    //if (!myPID.Compute()) Serial.println();
    analogWrite(PWMpin, Output);
  }


}

void AverageCapture(float in) {
  static int Position = 0;
  if (Readings == 1) Position = 1; // << Syncronize position with readings upon reset
  // The Readings variable is the Startup counter to allow us to be accurate while we count up to 100 readings
  // In order for readings to work we must have Position counter match .
  // So if we change the readings to Zero we must change the Position counter also.
  AvgArray[Position] = in;
  Position++;
  Readings++;
  Readings = min (2, Readings); // 100 readings 0-99;
  if (Position >= 2)Position = 0;//99 spots
}

void AverageReset(int Value) { // Value to be displayed as the average usually Zero upon reset
  Readings = 1;
  AvgArray[0] = Value; //
}
float AverageValue() {
  float Total = 0;
  float Average;
  if (!Readings)return (0.0);
  for (int Position = 0; Position < Readings; Position++) {
    Total += AvgArray[Position];
  }
  Average = Total / Readings;
  return (Average);
}


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;
    PulseCtrX ++;  // << changed
    Ctr = 0;
  }
}


void readRpm()
{
  if (!PulseCtrX) return; // << Added lets not stop interrupts unless we know we are ready (keep other code happy).
  cli ();         // clear interrupts flag
  Time = timeX;
  timeX = 0;
  PulseCtr = PulseCtrX; // << Added
  PulseCtrX = 0; // << Added
  sei ();
  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 (!Skip) {
    // if (!myPID.Compute()) Serial.println();
    //analogWrite(PWMpin, Output); // <<<<<<<<<<< use this one instead of the one below
    //}



    AverageCapture(Input); // <<< This is a good spot for to capture the value it is after any time critical code

  }

  else Skip = false;
  //    analogWrite(PWMpin, Output); // <<<<<<<<<<<<<<<<<<<<< This does nothing without myPID.Compute();
  //if (Time > ((SampleDuration + 1) * 1000))Counts--;
  //if (Time < ((SampleDuration - 1) * 1000))Counts++;
  //Counts = constrain(Counts, PulsesPerRevolution * .1, PulsesPerRevolution * 4);

  Time = 0; // set time to zero to wait for the next rpm trigger.
  Counter += PulseCtr;
  PulseCtr = 0; // set pulse Ctr to zero

  PulsesPerMinute = (60.0 * 1000000.0) / (double)((double)PulsesPerRevolution / (double)Counts);

}

void debug()

{
  /*
    char S[20];
    static unsigned long PingTimer;
    if ((unsigned long)(millis() - PingTimer) >= 100) {
    PingTimer = millis();
    //Serial.print(amps);
    Serial.print(dtostrf(Measurements, 6, 1, S));
    Serial.print(" Average: "); Serial.print(dtostrf(AverageValue(), 6, 1, S));
    Serial.println();

    }
  */

}


As you can see, I have "void pingit" commented out and everything runs fine.

moose4621

We have been playing around with the seeder today with it now mounted to the soil conditioner and tractor. I fitted the ping sensor which is used to switch the motor on/off with the 3 point linkage, and we are going to try it but at the end of the day the consensus was to drop the ping sensor and use an induction sensor instead which can be triggered off one of the top linkage arms. This will make the height switch more reliable and probably more durable and is a hell of lot a easier from a software point of view.
It means that any adjustment will mean getting out of the tractor and bending the steel bracket that holds the sensor. But farmers are used to that ;-)

After trying the setup with the ping sensor, I will probably fit the induction sensor and update the software then. This, I hope will cure a few problems so from now on, lets assume there is no ping code.

Everything is still on the tractor tonight so I cannot do any more testing and probably won't for a few days.

Maybe we should back pedal a bit since we now know the ping sensor was causing some issues for a while.

moose4621

Update:

The field tests did not go to plan. Not surprising really when it was a rush to get something together in time for a deadline set by the weather. It just wasn't ready, and neither was I.
Original manufacturers controller was hastily reassembled and installed.

I did manage to get some footage of the machine in operation which demonstrates the shortcomings of the original system.
https://youtu.be/QQDtyttMXQc

In the mean time I have removed the ping sensor code and replaced it with an induction sensor which will take a bit more installation but should be more durable and certainly easier to code.

I have also coded the GPS receiver into the master so variable rate application according to ground speed is now possible. Since the calibration rate is at 10kph, the code simply divides the setpoint by 10 and then multiplies it by the ground speed.

snippet.
Code: [Select]

void getSetPoint()
{
  if (radioRxArray[2] == true)//if variable rate option is selected.
  {
    if (gpsStatus == false)//gps data not valid.
    {
      Setpoint = radioRxArray[1];//setpoint set by operator @ 10kph
    }
    else Setpoint = round(((float)radioRxArray[1] / 10) * groundSpeed);// setpoint adjusted according to ground speed.
  }

  else  Setpoint = radioRxArray[1];//if variable rate not selected.
}


I found the gps integration a lot easier than I thought it would be. Very happy with the results so far.

Still need to sought out the PID motor control issues, and set up a single cell lipo charger in the hand controller to charge while connected to 12v.

An issue I found when testing is: I used a voltage divider on the inductance sensor which is 12v. I set resistor values according to 14.4v input but when the tractor is not running and the battery is a bit low, the voltage after the divider drops below 2.6v when high and gives erroneous readings. Is it ok to use a 78L05 (TO92) in this case on the rpm sensor (PNP NO LJ12A3-4-Z/BY). Can they switch fast enough?

Go Up