Tuesday, February 26, 2013

Made a slot for NRF module directly onto Arduino digital pins


Once I ran out of jumper cables, I've made a slot to hold the nRF module that sits directly onto Arduino UNO digital pins.

I've change the CSN pin from pin7 to pin9 and swap the LCD pin9 to pin7. This is reflected in my latest codes below.


// Set up nRF24L01 radio on SPI pin for CE, CSN ( *** older codes uses Pin 7 instead )
RF24 radio(8,9);


LiquidCrystal lcd(10, 7, 3, 4, 5, 6);
// Make way for the SPI pins
// 10 -> LCD 4
// 7  -> LCD 6
// 3  -> LCD 11
// 4  -> LCD 12
// 5  -> LCD 13
// 6  -> LCD 14



Arduino with nRF slot directly to digital pins


I've re-wrote both the program on the Raspberry Pi and Arduino to make it more practical use. I've made the Raspberry Pi as the central hub to receive sensor data from Arduino nodes and returned back the same data to the sender for calculating the round-trip time (rtt).

You can download the updated codes at the summary links below.

I'm using all the pipes for each of the nodes, so if you want to use this codes, uncomment the pipes for different nodes to send payload to the Raspberry Pi using different node address.


// Radio pipe addresses for the 2 nodes to communicate. Uncomment for the active node
 const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };
// const uint64_t pipes[2] = { 0xF0F0F0F0E2LL, 0xF0F0F0F0D2LL };
// const uint64_t pipes[2] = { 0xF0F0F0F0E3LL, 0xF0F0F0F0D2LL };
// const uint64_t pipes[2] = { 0xF0F0F0F0F1LL, 0xF0F0F0F0D2LL };
// const uint64_t pipes[2] = { 0xF0F0F0F0F2LL, 0xF0F0F0F0D2LL };
// Pipe0 is F0F0F0F0D2 ( same as reading pipe )


*** You can either connect a small buzzer or LED to pin2 for audio/visual acknowledgement the payload once it is returned and matched the outBuffer payload sent to the RPi.


Summary Links :-

- RF24 libs/codes on github:-  https://github.com/stanleyseow/RF24 ( all Arduino & Raspberry Pi codes are on github )




Tuesday, February 19, 2013

Setup Nordic nRF24L01 RF modules to both Arduino UNO and Raspberry Pi


In my process of building a Wireless Sensor Network, I obtained  a few low cost RF module from Inhaos that are compatible with Nordic Semiconductor nRF24L01. My plans are to have these low cost RF modules replaces the wires for my sensor network on Arduino and connect them to Raspberry Pi as the IP gateway to the rest of the Internet.

Raspberry Pi with nRF24L01-PA-LNA with BT module and USB Wifi


This entry will show you on how to setup both Arduino UNO & Raspberry Pi to connect to these nRF24L01 modules.

Some basic information on these low cost RF modules, they communicate using SPI to the microcontroller and works on raw speed of 250Kbps, 1Mbps and 2Mbps.

I'm using the following hardware & software:-


Raspberry Pi Model B running Adafruit's Occidentalis V0.2 ( http://learn.adafruit.com/adafruit-raspberry-pi-educational-linux-distro/occidentalis-v0-dot-2 ) that support SPI.

Nordic Semiconductor Datasheet / Product Info ( http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01P )

RF modules used here :-
------------------

nRF24L01+ PA+LNA+2dBi ant

nRF24L01+ with PA & LNA and 2 dBi antenna ( http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_nRF24L01p_with_PA_and_LNA ), these are long range RF modules acting as hub on the Raspberry Pi.

Inhaos RF2400P


Inhaos RF2400P (with 5dBm RF power output) on BEKEN Chipset ( http://www.inhaos.com/product_info.php?products_id=35 ). They are compatible with the nRF24L01 but with higher output power of 5dBm.

nRF24L01+


Too many to choose from, just google for the above keyword on eBay or choose the cheapest as they sells in bulk of 10 pieces for a very low cost...

*** The main differences between the regular nRF24L01+ modules and the Inhaos RF2400P modules are that the RF2400P have a higher output power of 5dBm but could not do 250Kbps speed.

I'm running my setup using 1Mbps speed.

Setting up Arduino 

Connect using jumper wires from the above pin to the nRF24L01+ modules
Upload the sketches/program to the Arduino, download it here from Dropbox. 


Arduino UNO with nRF24L01+/PA/LNA/2dBi ant


Arduino connections to the nRF24L01 modules :-

Arduino Pin 11 to RF Module Pin 6 ( MOSI)
Arduino Pin 12 to RF Module Pin 7 ( MISO )
Arduino Pin 13 to RF Module Pin 5 ( SCK )
Arduino Pin 7 to RF Module Pin 4 ( CSN )
Arduino Pin 8 to RF Module Pin 3 ( CE )
Arduino 3.3V to RF Module Pin 2 ( VCC / 3.3V ) *** RF Module can only take 3.3V 
Arduino GND to RF Module Pin 1 ( GND )
RF Module Pin 8 (IRQ) is not connected

*** All the other pins are 5V tolerant

Optional LCD Module to display the received payload & payload length :-

I'm using Arduino Pin 7 & 8 for the SPI because my LCD module is using the following pins :-

Arduino Pin 9 - LCD Pin 4
Arduino Pin 10 - LCD Pin 6
Arduino Pin 3 - LCD Pin 11
Arduino Pin 4 - LCD Pin 12
Arduino Pin 5 - LCD Pin 13
Arduino Pin 6 - LCD Pin 14

Arduino Pin 2 - connect to buzzer ( for sound feedback when it received a valid packet )


Setting up Raspberry Pi

Upgrade your Raspberry Pi firmware to Adafruit Occidentalis V0.2 that support hardware SPI.

Download the above firmware and flash your SD card according to the instruction on Adafruit/Raspberry Pi website.

Refer to the two Raspberry Pi GPIO pinout and the physical pinout.

Raspberry Pi GPIO
GPIO with pin functions


RPi GPIO9    (Pin 21)  to RF Module Pin 7 ( MISO )
RPi GPIO10  (Pin 19)  to RF Module Pin 6 ( MOSI )
RPi GPIO 11 (Pin 23)  to RF Module Pin 5 ( SCK )
RPi GPIO8    (Pin 24)  to RF Module Pin 3 ( CE )
RPi GPIO 25 (Pin 22)  to RF Module Pin 4 ( CSN )
RPI 3.3V       (Pin 17)  to RF Module Pin 2 ( VCC/3.3V )
RPi Gnd        (Pin 25)  to RF Module Pin 1 (GND)



Instructions on using github version for RPi :-

Using git, issue the following instructions on Raspberry Pi command prompt :-

$ git clone https://github.com/stanleyseow/RF24.git
$ cd RF24
$ cd librf24-rpi/librf24
$ make                                <--- Compile the files
$ sudo make install              <--- This will install the librf24.so.1.0 into the shared libraries
$ sudo ldconfig -v | grep librf
     librf24.so.1 -> librf24.so.1.0

$ cd ../examples/
$ make

To run the programs, type 

$ sudo ./rpi-hub 
or 
$ sudo ./scanner

*** You need sudo/root to access the spidev0.0 device

Instruction on Arduino :-

Got examples/RF24/rpi_hub_arduino ( make sure the pins matches the above Arduino pinout )

Compile and upload to the Arduino

Optional :- Connect a small buzzer to digital Pin2 to hear a beep when the packet is returned to sender


If everything is working, you should see a screenshot below :-

rpi-hub screenshot


Summary Links :-

- Raspberry Pi firmware :- Adafruit Occidentalis-v0.2

- RF24 library (Arduino & RPi libs) on Github https://github.com/stanleyseow/RF24

Datasheets :-
- Nordic nRF24L01+ Datasheet :- nRF24L01+ DS

Forum :-
- Raspberry Pi Forum on nRF24L01 Click here
- Arduino Forum on maniacbug nRf24L01 driver Click here


Tuesday, February 12, 2013

Arduino Notes, Frequencies and Scales plus a Bonus Filter.

A quick post with a file I use a lot to experiment with Audio projects.

The file contains the frequencies used to generate the midi notes 0 to 127 in your Arduino projects. The file doesn't quite provide the frequency, instead it provides the phase increment required to generate the frequency. If you are familiar with Direct Digital Synthesis you will know that this is what we use in our Auduino projects to generate sine and other interesting waveforms at a given frequency.

The Illutron B Uses Direct Digital Synthesis, there is an introduction to the technique here -
http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html

Saving Memory
The note tables file uses PROGMEM to efficiently store the frequency table out of the way in program memory -

http://arduino.cc/en/Reference/PROGMEM

In addition to the 128 midi notes, two scales are provided, the pentatonic blues scale that will be familiar to anyone who has built and Auduino and the equally widely used C Minor blues scale. To preserve memory, these scales are also stored in progmem. You can add your own scales by following the same pattern.

Accessing The Notes

To access a note you can use -

uint16_t unPhaseIncrement = getMidiNotePhaseIncrement(sNote);

where sNote is between 0 and 127

To access a note from one of the scales based on an analogInput you can use -

uint16_t unPhaseIncrement = getCMBluesPhaseIncrement(map(aInput,0,1024,0,CMBLUES_NOTES));

In the sample code above, we are using the map function to map an analog input value held in aInput the range of notes available in the C Minor Blues scale.

A Simple Test Synthesiser
At the end of this post is a very simple synth which uses the note tables and is based on the popular Auduino with one bonus feature. I have included the low pass filter from the Mozzi Arduino audio libraries. The Mozzi filter is better than the simple hardware filters I have tried and is very easy to add to a project.

In the test sketch the filter can be driven in two ways based on the position of an analog input. At the bottom end of the input range, the filter is controlled directly by the input, at the top end of the range, the filter is controlled by an oscillator, the frequency of the oscillator is  adjustable within this range.

To keep things simple, the test sketch uses the same hardware as an Auduino so if you want to have a play around with the mozzi filter or creating your own scales - load up and play away.

Mozzi

The Mozzi filter can be downloaded from http://sensorium.github.com/Mozzi/

The Mozzi sound samples are very good quality, the project implements a range of synthesizer building blocks and is something to take a closer look at.

Arduino Note Tables -

// Arduino Note Tables rcarduino.blogspot.com

#ifndef _NOTETABLES_
#define _NOTETABLES_

#include "avr/pgmspace.h"

#define MIDI_NOTES 128
// used to convert midi note numbers into the increments required to generate the note in the ISR
PROGMEM unsigned int midiNoteToWavePhaseIncrement[MIDI_NOTES] =
{
 66 // 0,8.18,66.98,66                C
,70 // 1,8.66,70.96,70
,75 // 2,9.18,75.18,75
,79 // 3,9.72,79.65,79
,84 // 4,10.30,84.38,84
,89 // 5,10.91,89.40,89
,94 // 6,11.56,94.72,94
,100 // 7,12.25,100.35,100
,106 // 8,12.98,106.32,106
,112 // 9,13.75,112.64,112
,119 // 10,14.57,119.34,119
,126 // 11,15.43,126.43,126
,133 // 12,16.35,133.95,133            C
,141 // 13,17.32,141.92,141
,150 // 14,18.35,150.35,150
,159 // 15,19.45,159.29,159
,168 // 16,20.60,168.77,168
,178 // 17,21.83,178.80,178
,189 // 18,23.12,189.43,189
,200 // 19,24.50,200.70,200
,212 // 20,25.96,212.63,212
,225 // 21,27.50,225.28,225
,238 // 22,29.14,238.67,238
,252 // 23,30.87,252.86,252
,267 // 24,32.70,267.90,267           C - lowest note used on rcarduino ribbon synth
,283 // 25,34.65,283.83,283
,300 // 26,36.71,300.71,300
,318 // 27,38.89,318.59,318
,337 // 28,41.20,337.53,337
,357 // 29,43.65,357.60,357
,378 // 30,46.25,378.87,378
,401 // 31,49.00,401.40,401
,425 // 32,51.91,425.27,425
,450 // 33,55.00,450.55,450
,477 // 34,58.27,477.34,477
,505 // 35,61.74,505.73,505
,535 // 36,65.41,535.80,535           C
,567 // 37,69.30,567.66,567
,601 // 38,73.42,601.42,601
,637 // 39,77.78,637.18,637
,675 // 40,82.41,675.07,675
,715 // 41,87.31,715.21,715
,757 // 42,92.50,757.74,757
,802 // 43,98.00,802.79,802
,850 // 44,103.83,850.53,850
,901 // 45,110.00,901.11,901
,954 // 46,116.54,954.69,954
,1011 // 47,123.47,1011.46,1011       C
,1071 // 48,130.81,1071.60,1071
,1135 // 49,138.59,1135.32,1135
,1202 // 50,146.83,1202.83,1202
,1274 // 51,155.56,1274.36,1274
,1350 // 52,164.81,1350.13,1350
,1430 // 53,174.61,1430.42,1430
,1515 // 54,185.00,1515.47,1515
,1605 // 55,196.00,1605.59,1605
,1701 // 56,207.65,1701.06,1701
,1802 // 57,220.00,1802.21,1802
,1909 // 58,233.08,1909.38,1909
,2022 // 59,246.94,2022.92,2022
,2143 // 60,261.63,2143.20,2143       C
,2270 // 61,277.18,2270.64,2270
,2405 // 62,293.66,2405.66,2405
,2548 // 63,311.13,2548.71,2548
,2700 // 64,329.63,2700.27,2700
,2860 // 65,349.23,2860.83,2860
,3030 // 66,369.99,3030.95,3030
,3211 // 67,392.00,3211.18,3211
,3402 // 68,415.30,3402.12,3402
,3604 // 69,440.00,3604.42,3604
,3818 // 70,466.16,3818.75,3818
,4045 // 71,493.88,4045.83,4045
,4286 // 72,523.25,4286.41,4286      C
,4541 // 73,554.37,4541.29,4541
,4811 // 74,587.33,4811.33,4811
,5097 // 75,622.25,5097.42,5097
,5400 // 76,659.26,5400.53,5400
,5721 // 77,698.46,5721.67,5721
,6061 // 78,739.99,6061.89,6061
,6422 // 79,783.99,6422.36,6422
,6804 // 80,830.61,6804.25,6804
,7208 // 81,880.00,7208.85,7208
,7637 // 82,932.33,7637.51,7637
,8091 // 83,987.77,8091.66,8091
,8572 // 84,1046.50,8572.82,8572     C
,9082 // 85,1108.73,9082.58,9082
,9622 // 86,1174.66,9622.66,9622
,10194 // 87,1244.51,10194.85,10194
,10801 // 88,1318.51,10801.07,10801
,11443 // 89,1396.91,11443.33,11443
,12123 // 90,1479.98,12123.79,12123
,12844 // 91,1567.98,12844.71,12844
,13608 // 92,1661.22,13608.50,13608
,14417 // 93,1760.00,14417.70,14417
,15275 // 94,1864.65,15275.02,15275
,16183 // 95,1975.53,16183.31,16183
,17145 // 96,2093.00,17145.63,17145     C
,18165 // 97,2217.46,18165.16,18165
,19245 // 98,2349.32,19245.31,19245
,20389 // 99,2489.01,20389.70,20389
,21602 // 100,2637.02,21602.14,21602
,22886 // 101,2793.83,22886.67,22886
,24247 // 102,2959.95,24247.58,24247
,25689 // 103,3135.96,25689.42,25689
,27216 // 104,3322.44,27216.99,27216
,28835 // 105,3520.00,28835.39,28835
,30550 // 106,3729.31,30550.04,30550
,32366 // 107,3951.06,32366.63,32366
,34291 // 108,4186.01,34291.26,34291    C
,36330 // 109,4434.92,36330.32,36330
,38490 // 110,4698.64,38490.65,38490
,40779 // 111,4978.03,40779.41,40779
,43204 // 112,5274.04,43204.25,43204
,45773 // 113,5587.65,45773.32,45773
,48495 // 114,5919.91,48495.14,48495
,51378 // 115,6271.92,51378.79,51378
,54433 // 116,6644.87,54433.96,54433
,57670 // 117,7040.00,57670.76,57670
,61100 // 118,7458.62,61100.07,61100
,64733 // 119,7902.13,64733.26,64733
,3046 // 120,8372.02,68582.53,3046      C
,7124 // 121,8869.84,72660.64,7124
,11445 // 122,9397.27,76981.30,11445
,16022 // 123,9956.06,81558.77,16022
,20872 // 124,10548.07,86408.50,20872
,26010 // 125,11175.30,91546.65,26010
,31454 // 126,11839.81,96990.28,31454
,31454 // 127,11839.81,96990.28,31454 // this is wrong, need to calculate correct value, even though at 8Khz its wrapping around on every tick
};

// Pentatonic scale
// C D E G A C
// to map to midi note

#define PENTATONIC_NOTES 54

// provides an index of pentatonic notes in the midi note table

PROGMEM unsigned char sPentatonicNotes[PENTATONIC_NOTES] =
{
  0,   2,  4,  7,  9,
  12, 14, 16, 19, 21,
  24, 26, 28, 31, 33,
  36, 38, 40, 43, 45,
  48, 50, 52, 55, 57,
  60, 62, 64, 67, 69,
  72, 74, 76, 79, 81,
  84, 86, 88, 91, 93,
  96, 98,100,103,105,
 108,110,112,115,117,
 120,122,124,127
};

// C Minor Blues scale
// C D# F F# G A# C
// to map to midi note

#define CMBLUES_NOTES 65

// provides an index of pentatonic notes in the midi note table

PROGMEM unsigned char sCMBluesNotes[CMBLUES_NOTES] =
{
  0,   3,  5,  6,  7, 10,  // 6
  12, 15, 17, 18, 19, 22,  // 12
  24, 27, 29, 30, 31, 34,  // 18
  36, 39, 41, 42, 43, 46,  // 24
  48, 51, 53, 54, 55, 58,  // 30
  60, 63, 65, 66, 67, 70,  // 36  
  72, 75, 77, 78, 79, 82,  // 42
  84, 87, 89, 90, 91, 94,  // 48
  96, 99,101,102,103,106,  // 54
 108,111,113,114,115,118,  // 60
 120,123,125,126,127       // 65
};


unsigned int getMidiNotePhaseIncrement(unsigned char sNote)
{
 if(sNote >= MIDI_NOTES)
 {
   sNote = (MIDI_NOTES - 1);
 }

 return pgm_read_word(midiNoteToWavePhaseIncrement + (sNote));
}
unsigned int getPentatonicPhaseIncrement(unsigned char sPentatonicNote)
{
 if(sPentatonicNote >= PENTATONIC_NOTES)
  sPentatonicNote = (PENTATONIC_NOTES - 1);

 uint8_t sMidiIndex = pgm_read_byte(sPentatonicNotes + sPentatonicNote);

 return pgm_read_word(midiNoteToWavePhaseIncrement + sMidiIndex);
}

unsigned int getCMBluesPhaseIncrement(unsigned char sCMBluesNote)
{
 if(sCMBluesNote >= CMBLUES_NOTES)
  sCMBluesNote = (CMBLUES_NOTES - 1);
 
 uint8_t sMidiIndex = pgm_read_byte(sCMBluesNotes + sCMBluesNote);
 return pgm_read_word(midiNoteToWavePhaseIncrement + sMidiIndex);
}

#endif

The very simple, very lo-fi synth

// Very Lo Fi synth with Mozzi filter rcarduino.blogspot.com

#include "NoteTables.h"
#include "lowpass.h"

#define PWM_OUT_REG OCR2B

#define KEY_COUNT 20
#define KEY_WIDTH (1024/KEY_COUNT)

class CAudio
{
  public:
   CAudio(){}
   static void begin(uint8_t bStopTimer0Interrupts)
   {
     // Setup timer 1
     TCCR1A=0x0;          // set the timer prescaler to 8 = 16/8 = 2MHz
     TCCR1B=0x02;          // set the timer prescaler to 8 = 16/8 = 2MHz
     TIMSK1 |= (1<<OCIE1A);   // Enable output compare match interrupt on OCR1A
 
     TCCR2A=0B10110011;                                    //-8 bit audio PWM
     //TCCR0A=0x83;          // Set timer waveform generation mode to FAST PWM, clear OC0A On match, set at bottom - OC0A = digital pin 6.
     TCCR2B=0x01;          // Set to clock frequency, no prescaler

     pinMode(3,OUTPUT);
    
     if(bStopTimer0Interrupts)
     {
      // stops timer0 which triggers interrupts for the millis and micros functions
      // if we stop the timer, we get better audio, but loose millis and micros.
      TIMSK0 &= (~((1 << OCIE0A)| (1 << OCIE0B)));
     }
   }
};


#define SAW 0
#define SQUARE 1
#define RAMP 2


class COscilator
{
public:
  void setWaveform(uint8_t sWaveform)
  {
    m_sWaveform = sWaveform;
  }
  void setPhaseIncrement(uint16_t unPhaseIncrement)
  {
    m_unPhaseIncrement = unPhaseIncrement;
  }
  uint8_t hasTriggered()
  {
    return m_unPhaseAccumulator < m_unPhaseIncrement;
  }
  void trigger()
  {
    m_unPhaseAccumulator = 0;
  }
  void updatePhase()
  {
    m_unPhaseAccumulator += m_unPhaseIncrement;
  }
  uint8_t getSample()
  {
    uint8_t sSample = m_unPhaseAccumulator>>8;
    switch(m_sWaveform)
    {
      case SQUARE:
       (sSample >= 127) ? sSample = 255 : sSample = 0;
      break;
      case RAMP:
       sSample = ~sSample;
      break;
    }
    return sSample;
  }
protected:
  uint16_t m_unPhaseIncrement;
  uint16_t m_unPhaseAccumulator;
  uint8_t m_sWaveform;
};

COscilator Osc1,Osc2,LFO,LFOFilter;

uint16_t getPhaseIncrement(uint16_t aInput,uint8_t sOffset)
{
  uint8_t sKey = aInput/KEY_WIDTH;

  // find where on the ribbon the key starts 
  uint16_t unKeyStart = sKey * KEY_WIDTH;
 
  // find where on the ribbon the key ends
  uint16_t unKeyEnd = (sKey+1) * KEY_WIDTH;
 
  // map where the current input sits between the key start and key end to a frequency that sits at the same point between
  // the frequency of the key and the frequency of the next key
 
  return map(aInput,unKeyStart,unKeyEnd,getMidiNotePhaseIncrement(sKey+sOffset),getMidiNotePhaseIncrement(sKey+1+sOffset));
}

LowPassFilter lpFilter;
uint16_t gFilter;

void setup()
{
  Serial.begin(9600);
  CAudio::begin(true); 

  Osc1.setWaveform(RAMP);
  Osc2.setWaveform(RAMP);
  LFO.setWaveform(SAW);
  LFOFilter.setWaveform(RAMP);
}

void loop()
{
  uint16_t aInput = analogRead(1);
  if(aInput >= 1021)
  {
    Osc1.setPhaseIncrement(0);
    Osc2.setPhaseIncrement(0);
    LFO.setPhaseIncrement(0);
    LFOFilter.trigger();
  }
  else
  {
    uint8_t sOffset = 20;//((analogRead(1) >> 3)/12)*12;
    uint16_t unPhaseIncrement = getCMBluesPhaseIncrement(map(aInput,0,1024,0,CMBLUES_NOTES));
    Osc1.setPhaseIncrement(analogRead(2)<<2);
    Osc2.setPhaseIncrement(analogRead(3)<<5);

    //lpFilter.setCutoffFreq(analogRead(4)>>2);
    lpFilter.setResonance(analogRead(5)>>2);
   
    LFO.setPhaseIncrement(unPhaseIncrement);   
   
    uint16_t unFilter = analogRead(4);
    if(unFilter < 512)
    {
     LFOFilter.setPhaseIncrement(0);
     unFilter>>=1;
    
     uint8_t sreg = SREG;
     cli();
     gFilter = unFilter;
     SREG = sreg;
    }
    else
    {
     uint8_t sreg = SREG;
     cli();
     gFilter = unFilter;
     SREG = sreg;
    
     LFOFilter.setPhaseIncrement((gFilter-512));
    }
  }
}



#define DELAY_LENGTH 1024

// iterate the grains and LFO
SIGNAL (TIMER1_COMPA_vect)
{
  static uint8_t sDelayBuffer[DELAY_LENGTH];
  static uint16_t sDelayIndex;

  sDelayIndex++;
  if(sDelayIndex >= DELAY_LENGTH)
  {
    sDelayIndex = 0;
  }
 
  OCR1A += 125;

  Osc1.updatePhase();
  Osc2.updatePhase();
  LFO.updatePhase();
  LFOFilter.updatePhase();
 
  if(LFO.hasTriggered())
  {
    Osc1.trigger();
    Osc2.trigger();
  }
   
  if(gFilter < 512)
  {
    lpFilter.setCutoffFreq(gFilter>>1);
  }
  else
  {
   lpFilter.setCutoffFreq(LFOFilter.getSample());
  }
 
  uint16_t nOutput = lpFilter.next((Osc1.getSample()>>1)+(Osc2.getSample()>>1));
  if(nOutput > 255)
   nOutput = 255;
  PWM_OUT_REG = nOutput;

  // not being mixed in to output but easy to add, see rcarduino.blogspot.com
  sDelayBuffer[sDelayIndex] = PWM_OUT_REG;
}




And don't forget the Mozzi filter 'lowpass.h' from https://github.com/sensorium/Mozzi

Duane B

Tuesday, February 5, 2013

RCArduino Libraries FAQ

A central point for questions and help on using the RCArduino Libraries.

The RCArduino Libraries provides servo output, PPM input and individual RC Channel input.

Its been created to reduce servo jitter in projects that need to both read and output RC Channels. If you want smooth focus for camera work, animatronics that don't twitch around or fast smooth control of your RC Vehicle, read on.

1) Why a new servo and RC library ?

The standard Arduino Servo library is very good, its well documented and well supported, it also has some challenges when used in projects that read incoming RC Signals. The RCArduino Servo library addresses these challenges to provide smoother output. This is achieved through faster interrupt service routines which reduce clashes and glitches.

A major improvement is also acheived by not resetting timer1 as the standard servo library can. This allows us to use timer1 for timing incoming RC Signals as well as generating the servo outputs.



An detailed explanation can be found here -

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

2) Can I use the library to read PPM signals and output RC Channels ?

Yes, an example is provided here, but come back here for more documentation if you need it.

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html

3) Can I use the library to read individual RC Channels and output RC Channels ? 

Yes, an example is provided here, but come back here for more documentation and if your planning to use a mega look out for an update with an example using the six Arduino Mega interrupts instead of the pinchangeint library which is used in the UNO version to provide more than the standard two interrupts.

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

Some background on the pinchangeint library if you need more than 2 interrupts on an UNO or more than 6 on a Mega -

http://rcarduino.blogspot.com/2012/03/need-more-interrupts-to-read-more.html
How do we use the library ?

The RCArduino library uses arrays internally to record the channel input and output values. This is slightly more efficient than the linked lists used by the servo library however its also a little less user friendly. Unlike the Servo library which allows us to attach servos at runtime, the RCArduino library requires that we set the number of servos at compile time. Similarly if you plan to use the PPM Input capability you will need to specify the number of input channels at compile time.

To make this as simple as possible, two #define's are used  in the RCArduinoFastLib.h file.

To set the number of input channels if your going to read a PPM Stream change the number here -
// Change to set the number of channels in PPM Input stream
#define RC_CHANNEL_IN_COUNT 3

To set the number of servos in your project change the number here -

// Change to set the number of servos/ESCs
#define RC_CHANNEL_OUT_COUNT 4

Set the RC_CHANNEL_OUT_COUNT to one more servo than you need, the library is able to generate higher refresh rate signals than the standard Servo library which forces a 50Hz refresh rate. To set the refresh rate we create an additional entry in the servo array. We use the 'setFrameSpace' function on this last entry to set the frame space which sets the refresh rate. While this is initially an awkward additional step, it also gives us a lot more flexibility, for example we can use the library to drive high refresh rates upto 500Hz.

To generate a 50Hz refresh rate which is equivalent to the standard servo library we can use the following calculation -

1/50 = 20,000 microseconds
frame space = 20,000 - (2,000 * number of servos)

An Example -
To drive 8 servos we would set RC_CHANNEL_OUT_COUNT to (8 + 1)= 9. Remember the additional entry is for our framespace.

#define RC_CHANNEL_OUT_COUNT 9

we can then calculate

frame space = 20,000 - (2,000 * (RC_CHANNEL_OUT_COUNT - 1) )

Note we take away 1 so that we do not include the framespace in the framespace calculation

frame space = 20,000 - (2,000 * 8)
frame space = 4,000

So to set the frame space to give a refresh rate of 50 Hz for 8 servos we call

CRCArduinoFastServos::setFrameSpaceA(SERVO_FRAME_SPACE,4000);

Why the maths ?
 
While this is more complex than the standard servo library it is also more flexible and provides a quick and easy way to drive the high frequency ESCs and Servos that are becoming more popular.

If your unsure about the frame space calculation or would like to try the RCArduino libraries with 500Hz ESCs and Servos use the comments or contact me Duane B through the Arduino forum.

I am also considering automating the calculation through a convenience function in a future release, stay tuned.

Now that we have that out of the way, its all very easy.

Attaching A Servo
To attach a servo we call the attach function supply the index of the servo we want to attach (they start at 0, remember they are held in an array) and the Arduino pin we want to attach it to.

CRCArduinoFastServos::attach(SERVO_THROTTLE/* Servo index */,THROTTLE_OUT_PIN /* the pin we want it attached to */);

The final thing we need to do inside out set up function is to call the begin function, this initialises timer1 for use by the library and attaches the interrupt routines.

Understanding The Library and Reading and Writing Channels

The key to understanding and using the library is to be aware of the RC_CHANNEL_IN_COUNT and RC_CHANNEL_OUT_COUNT settings and also to understand that the channel values are stored in arrays which are accessed by the index you supply when calling the read and write functions -

CRCArduinoFastServos::writeMicroseconds(SERVO_STEERING /* 0 based index into servo array */,unSteeringIn);
CRCArduinoPPMChannels::getChannel(SERVO_STEERING /* 0 based index into PPM input array */);

You can create your own indexes into the arrays by defining them in your sketch, the number of input channels does not need to match the number of output channels. You can also have a different index for an input channel that you use for the output channel, in fact you will sometimes find that channels within the PPM stream are in a different order than the printed channel numbers on your receiver.

To get started try the examples provided -

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-channels-rcarduinofastlib.html

http://rcarduino.blogspot.com/2012/11/how-to-read-rc-receiver-ppm-stream.html

For any help, use the comments section below

Duane B