getting error: cannot call member function 'void RC::Timers(uint8_t, uint8_t, uint8_t, uint8_t)' without object
I want to package a working chunk of code into a class to simplify adding it to projects.
RC::RC() {
}
// port change Interrupt
ISR(PCINT2_vect) { //this ISR pins 0-7
sei(); // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
RC::Timers(PIND, 0, 0, 7);
}
void RC::Timers(uint8_t pin, uint8_t group, uint8_t StartPin, uint8_t EndPin) {
//process the interrupt
}
So Here is the Full Code I would like to insert into a Class. Maybe this will spark some posts
Any and all help would be great!
I personally created the code from scratch and suggested licencing for others to use would be appreciated also I've never went this far to provide open source code.
Sketch compiles and uses only 3,448 bytes as shown below.
Code functions perfectly!
No delays to slow down your other code!!!
produces values from about 1000~2000 representing width of the pulse coming from each receiver source
And it should be able to use ALL pins on the Arduino UNO at once! 19 inputs! If you could find something that you could use that for.
Take any RC remote and Connect it to any Arduino Uno Pin including Analog!!!
Would like to make this into a nice packaged class and share it!
Any Help
Thanks
byte RCinCtr = 0;
volatile unsigned long edgeTime[20];
volatile uint16_t rcValue[20] = { 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502, 1502}; // interval [1000;2000] RC Timing window
volatile uint16_t rcValueX[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // interval [0;65535] // Allows for alternate usages shorter or longer times
volatile uint8_t PinArray[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // List of pins that could be used as ping inputs:
volatile uint8_t mask[3] = {0, 0, 0};
volatile uint8_t PinMask[3] = {0, 0, 0};
volatile uint8_t PCintLast[3] = {0, 0, 0};
// port change Interrupt
ISR(PCINT2_vect) { //this ISR pins 0~7
sei(); // re enable other interrupts at this point.
Timers(PIND, 0,0,7);
}
ISR(PCINT0_vect) { //this ISR pins 8~13
sei(); // re enable other interrupts at this point
Timers(PINB, 1,8,13);
}
ISR(PCINT1_vect) { //this ISR s A0~A5
sei(); // re enable other interrupts at this point
Timers(PINC, 2,14,19);
}
void Timers(uint8_t pin, byte group,uint8_t StartPin, uint8_t EndPin) {
uint16_t cTime, dTime;
cTime = micros(); // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
mask[group] = pin ^ PCintLast[group]; // doing a ^ between the current interruption and the last one indicates wich pin changed
PCintLast[group] = pin; // we memorize the current state of all PINs in group
for (byte P = 0; P < (EndPin - StartPin); P++) { // Cycle throug all the pins
if (PinMask[group] >> P & 1) { // See if we activated the pin
if (mask[group] >> P & 1) { // See if the pin has changed
if ((pin >> P & 1)) edgeTime[P + StartPin] = cTime; //Pulse went HIGH store the start time
else { // Pulse Went low calculate the duratoin
dTime = cTime - edgeTime[P + StartPin]; // Calculate the change in time
if (900 < dTime && dTime < 2200) rcValue[PinArray[P + StartPin]] = dTime; // only save proper PWM Pulse
rcValueX[PinArray[P + StartPin]] = dTime; // Lets Store any duration up to 65535 micro seconds
}
}
}
}
}
// Install Pin change interrupt for a pin, can be called multiple times
void pciSetupRC(byte pin)
{
if (pin <= 7)PinMask[0] = bitWrite(PinMask[0], pin, 1); // PIND for pins 0~7
if ((pin > 7) && (pin <= 13)) PinMask[1] = bitWrite(PinMask[1] , pin - 8, 1); // PINB for pins 8~13
if (pin > 13) PinMask[2] = bitWrite(PinMask[2] , pin - 14, 1); // PINC for A0~A5 Starts on Pin 14
pinMode(pin, INPUT);// enable interrupt for pin...
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin
PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
PinArray[pin] = RCinCtr;
RCinCtr++;
}
void setup() {
Serial.begin(115200);
while (!Serial) {
}
// enable interrupt for pin... un-comment line to use that pin for input
// Select the pins you want to use for input (Notes for alternate uses)
// pciSetupRC(0); // Serial Communication
// pciSetupRC(1); // Serial Communication
//pciSetupRC(2); // Interrupt 2
//pciSetupRC(3); // Interrupt 1
//pciSetupRC(4);
//pciSetupRC(5);
//pciSetupRC(6);
//pciSetupRC(7);
//pciSetupRC(8);
pciSetupRC(9);
pciSetupRC(10); // SPI SS Slave Select
pciSetupRC(11); // SPI MOSI
//pciSetupRC(12); // SPI MISO
//pciSetupRC(13); // SPI SCK
//pciSetupRC(14); // A0
//pciSetupRC(15); // A1
//pciSetupRC(16); // A2
//pciSetupRC(17); // A3
// pciSetupRC(18); // A4 // i2c
// pciSetupRC(19); // A5 // i2c
}
void loop() {
for (static long QTimer = millis(); (long)( millis() - QTimer ) >= 100; QTimer = millis() ) { // Quick one line no delay spam timer 10 times a second
for (int c = 0; c < RCinCtr; c++) {
Serial.print(rcValueX[c]);
Serial.print("\t");
}
Serial.println();
}
}
Suppose you have a class called Dog, with a method called Bark().. Suppose you have 12 instances of the class, called Spot, Fido, CrapsLots, EatsFurniture, ShedsEverywhere, etc.
Now suppose you have an event which triggers an interrupt, and you want, when the interrupt happens, for a dog to Bark().
Thank you PaulS I've seen this as a difficulty and like the way you explained it. And so I need a shock collar that recognizes the dogs bark lol
So if i were to package the somewhat short chunk of code I submitted below in a class I would need to have some type of pointer to my class function to hand to the ISR(PCINT2_vect){} or break out the interrupts altogether.
I am not sure what the best practices are and where I should go with Plan B. Can someone provide me a Best way scenario I tried to figure out how pinchangeint library or the servo Library do it but I'm not there yet. The code became overwhelming and my attempts failed.
But which dog should bark? If it's Fifi then just call fifi.bark() inside the ISR. If it's Bella just call bella.bark(). If you want all to bark just call .bark() on all. But this may take time and a ISR should be short, you might want to set a flag and check that flag in loop() and call .bark() of all there.
The way that you get it to work is to declare the method to be called, Bark(), as static. That makes is a class method, rather than an instance method.
Then, the class method determines which dog is to bark.
Of course, this means that each dog must tell the class that it is ready, willing, and able to bark, and the class needs to keep track of all the dogs, so that it can decide which one should bark() this time (or maybe all of them should be told to bark()) (and notice that bark() and Bark() are two different methods - one belongs to the class, the other belongs to the instances).
What this means is that Spot->bark() would make one dog bark, while Dog::Bark() might make a real racket, with all the mutts bark()ing their fool heads off.
// port change Interrupt
ISR(PCINT2_vect) { //this ISR pins 0~7
sei(); // re enable other interrupts at this point.
Timers(PIND, 0,0,7);
}
ISR(PCINT0_vect) { //this ISR pins 8~13
sei(); // re enable other interrupts at this point
Timers(PINB, 1,8,13);
}
ISR(PCINT1_vect) { //this ISR s A0~A5
sei(); // re enable other interrupts at this point
Timers(PINC, 2,14,19);
}
void Timers(uint8_t pin, byte group,uint8_t StartPin, uint8_t EndPin) {
uint16_t cTime, dTime;
cTime = micros(); // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
mask[group] = pin ^ PCintLast[group]; // doing a ^ between the current interruption and the last one indicates wich pin changed
PCintLast[group] = pin; // we memorize the current state of all PINs in group
for (byte P = 0; P < (EndPin - StartPin); P++) { // Cycle throug all the pins
if (PinMask[group] >> P & 1) { // See if we activated the pin <<< Check to see if this dog should bark
DogBark();
}
}
}
How would I convert that to a Class and trigger the interrupt... Mabe a Class isn't the correct description of how to do it. A simpler request is I would like to have it contained in a file RC_V1.cpp and RC_V1.h and use included: #include<RC_V1.h> to attach it to my code to make it simple for everyone to use. I can already paste the chunk of code into my robot to control it. I'm hoping to take it to the next level.
Dog::Bark() might make a real racket, with all the mutts bark()ing their fool heads off.
I only need one instance of my class but if there were more than on my code checks by default which pin tripped it and discards the rest. I also free up for other interrupts instantly because I'm not that worried about being a couple microseconds off.
If you have a RC remote connect it up and try my code out it works! This is a discussion on how to share it correctly to the world(making my life easier in the process), Please provide a short example that I can try I an struggling on the conversion of the above section. Everything else is easy.
Then you probably don't need a class. Just because code is declared in a header file and implemented in a source file does not mean that a class is needed. And, if you don't have a class, you don't have a problem.
So is there a way to properly contain it like a class does to have private variables and prevent some other code from creating havoc if a variable is named the same?
Jeez, we are only talking about a few thousand lines at most. Just keep track of things. If necessary use prefixes like: mutt_numDogs or poodle_numDogs.
ETA:
Mainly, just minimize the use of global variables. Functions can use pre-fixes.
The usual way to ensure that there is only one instance of the class is to have a private constructor. That prevents anyone from trying to create another instance.
You provide a public method to get THE instance, and have that method return the instance, if it exists, and create one and return it, if it does not already exist.
But it's not forbidden. Just create 1 instance of that class in your header and use that in the ISR
So in short :
This has to be part of the end users code so cryptic for them...
ISR(PCINT2_vect){
myDog.bark();
}
And can't be hidden away... Disappointing I would love to have a cleaner solution.
JUST IN:
PaulS Thanks this is probably the best post of the bunch!!!
The usual way to ensure that there is only one instance of the class is to have a private constructor. That prevents anyone from trying to create another instance.
You provide a public method to get THE instance, and have that method return the instance, if it exists, and create one and return it, if it does not already exist.
Google "singleton".
I will google it when I get back from work!
Thanks everyone
Please post any examples or other suggestions and I'll look at them. when I get the code done I WILL post it for everyone
I need a brief example on how to put the ISR inside the library.
The ISR is a function, like any other function. Put the function in the sketch, if that is appropriate. Put the function in the source for the library, if that is a appropriate.
And you might also have a look at the Servo library.
I'll look at it. Lots of code to review but I am certainly up for the challenge and they have decent comments. I'll post what I come up with either way. Thanks, wish me luck
I Did It!!!! and here is the code promised RC Remote to ANY Arduino UNO pin
RC.ino:
#include <RC_V1.h>
RC RCRadio();
void setup() {
Serial.begin(115200);
while (!Serial) {
}
Serial.println("Alive");
RCRadio.pciSetupRC(3); // add a pin
RCRadio.pciSetupRC(10); // add a pin
RCRadio.pciSetupRC(11); // add a pin
RCRadio.pciSetupRC(A3); //add a pin
}
void loop() {
int SpamDelay = 100;
RCRadio.Print(SpamDelay);
}
RC_V1.cpp:
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "RC_V1.h"
volatile uint8_t mask[4] = {0, 0, 0};
volatile uint8_t PCintLast[3] = {0, 0, 0}; //looking fo pin changes using bianary
volatile uint8_t PinMask[3] = {0, 0, 0}; // Bionary pin list 3 groups
volatile uint32_t edgeTime[20]; //Pulse HIGH start time
volatile uint16_t rcValue[20] = { 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500}; // interval [1000;2000] Channels 9,10,11,A0,A1
volatile uint16_t rcValueX[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // interval [0;65535] Channels 9,10,11,A0,A1
volatile uint8_t PinArray[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Key = Pin Val = Position List of pins that could be used as ping inputs:
volatile uint16_t rcValueCenter[20] = { 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500}; // interval [1000;2000] Channels 9,10,11,A0,A1
volatile uint32_t AliveBits = 0; // a reading Since Last Test,
volatile int8_t IsAlive = 0;
volatile bool FailSafe = true;
volatile uint8_t RCPinCtr = 0; // Number of Pins Activated
/*** static functions common to all instances ***/
static inline void Timers(uint16_t cTime, uint8_t pin, uint8_t group,uint8_t StartPin, uint8_t EndPin) {
uint16_t dTime;
// cTime = micros(); // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
mask[group] = pin ^ PCintLast[group]; // doing a ^ between the current interruption and the last one indicates wich pin changed Serial.print(mask,BIN);
PCintLast[group] = pin; // we memorize the current state of all PINs in group
for (byte P = 0; P < (EndPin - StartPin); P++) { // Cycle throug all the pins in Group
if (PinMask[group] >> P & 1) { // See if we activated the pin
if (mask[group] >> P & 1) { // See if the pin has changed
uint8_t PinNumber = P + StartPin;
uint8_t Position = PinArray[PinNumber];
if ((pin >> P & 1)) edgeTime[PinNumber] = cTime; //Pulse went HIGH store the start time
else { // Pulse Went low calculate the duratoin
dTime = cTime - edgeTime[PinNumber]; // Calculate the change in time
if (900 < dTime && dTime < 2200)rcValue[Position] = dTime; // only save proper PWM Pulse
rcValueX[Position] = dTime; // Lets Store any duration up to 65535 micro seconds
AliveBits = AliveBits | 1 << Position;
//Serial.print(PinNumber);Serial.print(" - ");Serial.print(Position);Serial.print(" = ");Serial.println(dTime);
}
}
}
}
}
// port change Interrupt
ISR(PCINT2_vect) { //this ISR pins 0-7
sei(); // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
Timers(micros(),PIND, 0,0,7);
}
ISR(PCINT0_vect) { //this ISR is common to every receiver channel, it is call everytime a change state occurs on a pins 8-13 only need 9-11
sei(); // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
Timers(micros(),PINB, 1,8,13);
}
ISR(PCINT1_vect) { //this ISR s A0~A5
sei(); // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
Timers(micros(),PINC, 2,14,19);
}
SIGNAL(TIMER0_COMPA_vect){ // Timer to check for signal loss
sei();
static int MSec=0;
if(MSec++ < 10)return; // Check every 10 Miliseconds for change
MSec = 0;
IsAlive = (AliveBits > 0) ? IsAlive+1 : IsAlive-1 ;
IsAlive = constrain(IsAlive,-20,+20);// Must have 20 tests good or 20 tests bad for IsAlive to change states
if ((IsAlive <= 0) && FailSafe){
// for (uint8_t c = 0;c < RCPinCtr ;c++) rcValue[c] = rcValueCenter[c];
}
AliveBits = 0;
}
/*** end of static functions ***/
RC::RC(){
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
}
// Install Pin change interrupt for a pin, can be called multiple times
void RC::pciSetupRC(uint8_t pin){
if (pin <= 7)PinMask[0] = bitWrite(PinMask[0], pin, 1); // PIND for pins 0~7
if ((pin > 7) && (pin <= 13)) PinMask[1] = bitWrite(PinMask[1] , pin - 8, 1); // PINB for pins 8~13
if (pin > 13) PinMask[2] = bitWrite(PinMask[2] , pin - 14, 1); // PINC for A0~A5 Starts on Pin 14
pinMode(pin, INPUT);// enable interrupt for pin...
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin
PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
PinArray[pin] = RCPinCtr;
PinNumber[RCPinCtr] = pin;
RCPinCtr++;
}
void RC::Print(uint16_t SpamDelay) {
runAfter(SpamDelay){ // print after SpamDelay miliseconds (No Windup)
if(!Alive()) {
Serial.print("No Signal ");
} else {
Serial.print("#");
for (int Ctr = 0; Ctr <= RCPinCtr; Ctr++) {
Serial.print(Ctr);
Serial.print(" Pin ");
Serial.print(PinNumber[Ctr]);
Serial.print(" Value ");
Serial.print(rcValue[Ctr]);
Serial.print("\t");
}
}
Serial.println();
}
}
uint8_t RC::Alive() {
return(IsAlive > 0);
}
int16_t RC::PinTime(uint8_t pin, uint16_t offset) {
return(PosTime(PinArray[pin], offset ));
}
int16_t RC::PinTimeX(uint8_t pin) {
return(PosTimeX(PinArray[pin]));
}
int16_t RC::PosTime(uint8_t PinCount, uint16_t offset ) {
return(rcValue[PinCount] - offset);
}
int16_t RC::PosTimeX(uint8_t PinCount) {
return(rcValueX[PinCount]);
}