Using an array in a Library,

Hello everyone, I've been following the playground guide for creating your own Library. I've successfully written one that takes care of controlling a L298N DC motor driver. Now i'm in the process of writing one that takes care of reading a PPM stream from a RC receiver.

I first began by writing it as a function as part of my normal program, Here it is;

/*
 * The PPM signal from RC RX will be fed into pin 2, interrupt 0, on a rising edge the intefrrupt is triggered. 
 * From here each pulseWidth is calculated. The channels are then stored for access later.
 * 
 */

//PPM_INPUT
#define SYNC_PULSE_WIDTH 3000 //Length of the PPM sync pulse
int ppmPin = 2;
unsigned long oldRisingMicros;
unsigned long newRisingMicros;
//channelArray 0 = PPM Sync Pulse, channelArray 1 = channel 1, channelArray 2 = channel 2, etc... 
int channelArray[9] = {1000, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500};
int channelCounter = 0;


void setup() {
  
  pinMode(ppmPin, INPUT_PULLUP);
  attachInterrupt(0, ppmServiceRoutine, RISING);  // attach interrupt handler rising edge

}



void ppmServiceRoutine(){
  
  oldRisingMicros = newRisingMicros;
  newRisingMicros = micros();
  int pulseWidth = newRisingMicros - oldRisingMicros;
  
  if (pulseWidth >= SYNC_PULSE_WIDTH){
    channelCounter = 0;
  }
  channelArray[channelCounter] = pulseWidth;
  channelCounter++; 
  
  if (channelCounter >= 9){
    Serial.println("channelCounter OUT OF RANGE!!!!!");
    channelCounter = 0;
  }
  
}

int GetChannelPosition(int channel){
  
  if (channelArray[0] >= SYNC_PULSE_WIDTH && channelArray[channel] > 800 && channelArray[channel] < 2200){
    int pulseWidth = constrain(channelArray[channel], 1000, 2000);
    return pulseWidth;
  }
  else{
    Serial.print("channel "); Serial.print(channel); Serial.println(" OUT OF RANGE or NO VALID SYNC PULSE");
    return 1500;
  }
  
}

This code works just fine, all i do is call GetChannelPosition(channel) whenever i need channel info.

So next i pushed it out to a Library

RC_PPM.h

/*
  
  Use at your peril!!
  Jan 16
*/
#ifndef RC_PPM_h
#define RC_PPM_h

#include "Arduino.h"

class RC_PPM
{
  public:
    RC_PPM(int PPMpin);
	int GetChannelPosition(int channel);
	
  private:
	void ppmServiceRoutine();
	int _ppmPin;
	unsigned long _oldRisingMicros;
	unsigned long _newRisingMicros;
	int _pulseWidth;
	int _position;
	
	//int _channelArray;
	int _channelCounter;
};

#endif

RC_PPM.cpp

*
  
  Use at your peril!!
  Use to grab channels from the PPM stream of an RC receiver
  Jan 16
*/

#include "Arduino.h"
#include "RC_PPM.h"

int _channelArray[9] = {1000, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500};

RC_PPM::RC_PPM(int ppmPin)
{ 
  pinMode(ppmPin, INPUT_PULLUP);
  _ppmPin = digitalPinToInterrupt(ppmPin);
  attachInterrupt(_ppmPin, ppmServiceRoutine, RISING);  // attach interrupt handler rising edge
  
  _channelCounter = 0;
  
}

void RC_PPM::ppmServiceRoutine()
{
  _oldRisingMicros = _newRisingMicros;
  _newRisingMicros = micros();
  int _pulseWidth = _newRisingMicros - _oldRisingMicros;
  
  if (_pulseWidth >= 3000){
    _channelCounter = 0;
  }
  _channelArray[_channelCounter] = _pulseWidth;
  _channelCounter++; 
  
  if (_channelCounter >= 9){
    Serial.println("channelCounter OUT OF RANGE!!!!!");
    _channelCounter = 0;
  }
}

int RC_PPM::GetChannelPosition(int channel)
{
  _channel = channel;
  if (_channelArray[0] >= 3000 && _channelArray[_channel] > 800 && channelArray[_channel] < 2200){
    _position = constrain(_channelArray[_channel], 1000, 2000);
    return _position;
  }
  else{
    Serial.print("channel "); Serial.print(_channel); Serial.println(" OUT OF RANGE or NO VALID SYNC PULSE");
    return 1500;
  }
}

when i try to use the Library i get errors which i think are related to the attempt to declare and initialise the channelArray. I've tried many combinations but none seem to have the desired effect. can anyone enlighten me as to how this should be done?

Im also curious as to wether i can attach an interrupt inside of my library? any thoughts??

Thanks in advance,
Ben.

Have a look at my suggestion:

hpp

#ifndef RC_PPM_h
#define RC_PPM_h

class RC_PPM
{
  public:
    RC_PPM(byte PPMpin);
    int GetChannelPosition(int channel);

  private:
    static RC_PPM* anchor;
    static void ppmISR();
    void ppmServiceRoutine();
    byte _ppmPin;
    unsigned long _oldRisingMicros;
    unsigned long _newRisingMicros;
    int _pulseWidth;
    int _position;
    int _channel;

    static int _channelArray[];
    int _channelCounter;
};

#endif

cpp

#include "Arduino.h"
#include "RC_PPM.h"

RC_PPM* RC_PPM::anchor = NULL;

void RC_PPM::ppmISR()
{
  anchor->ppmServiceRoutine();
}

int RC_PPM::_channelArray[9] = {1000, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500};

RC_PPM::RC_PPM(byte ppmPin)
{
  anchor = this;
  pinMode(ppmPin, INPUT_PULLUP);
  _ppmPin = digitalPinToInterrupt(ppmPin);
  attachInterrupt(_ppmPin, ppmISR, RISING);  // attach interrupt handler rising edge
  _channelCounter = 0;
}

void RC_PPM::ppmServiceRoutine()
{
  _oldRisingMicros = _newRisingMicros;
  _newRisingMicros = micros();
  int _pulseWidth = _newRisingMicros - _oldRisingMicros;

  if (_pulseWidth >= 3000) {
    _channelCounter = 0;
  }
  _channelArray[_channelCounter] = _pulseWidth;
  _channelCounter++;

  if (_channelCounter >= 9) {
    Serial.println(F("channelCounter OUT OF RANGE"));
    _channelCounter = 0;
  }
}

int RC_PPM::GetChannelPosition(int channel)
{
  _channel = channel;
  if (_channelArray[0] >= 3000 && _channelArray[_channel] > 800 && _channelArray[_channel] < 2200) {
    _position = constrain(_channelArray[_channel], 1000, 2000);
    return _position;
  }
  else {
    Serial.print(F("channel ")); Serial.print(_channel); Serial.println(F(" OUT OF RANGE or NO VALID SYNC PULSE"));
    return 1500;
  }
}

Couple of questions:

Whats the "anchor" business all about? what does it do?
What does the "this" command do?

my programming experience is nearly solely related to arduino so not come across these before.

When an interrupt occures, there is no object context.

So you need a pointer to the object that should handle it.

I used a static member to hold that information.
In the constructor the pointer gets initialized to the class (this points to the current instance).

The interrupt is attached to a static member function, that combines pointer and routine.

I've been going over your examples and am curious as to why the extra F in the print statements at the bottom of your .cpp sugestion. Ive looked it up and i see it prints Flash memory. What is the advantage of this over a normal println?

What is the advantage of this over a normal println?

The F() macro keeps the string literal from being copied to SRAM at run time. Using less SRAM is always a good thing.

I can confirm that your modified code works correctly. :slight_smile: thanks for your help!!