Joel Murphy / Mbed 2 deprecated ADS_StreamRawData

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
biomurph
Date:
Mon Mar 23 19:22:04 2015 +0000
Commit message:
Publishing this old ADS1299 code for the first time!

Changed in this revision

ADS1299.cpp Show annotated file Show diff for this revision Revisions of this file
ADS1299.h Show annotated file Show diff for this revision Revisions of this file
ADS1299Manager.cpp Show annotated file Show diff for this revision Revisions of this file
ADS1299Manager.h Show annotated file Show diff for this revision Revisions of this file
Biquad.cpp Show annotated file Show diff for this revision Revisions of this file
Biquad.h Show annotated file Show diff for this revision Revisions of this file
Biquad_multiChan.cpp Show annotated file Show diff for this revision Revisions of this file
Biquad_multiChan.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADS1299.cpp	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,330 @@
+//
+//  ADS1299.cpp   mbed LIBRARY FOR COMMUNICATING WITH ADS1299
+//  
+//  Created by Joel Murphy. Fall, 2013 Ported from Arduino library by Conor, Joel, and Luke, Summer 2013
+//
+
+
+#include "ADS1299.h"
+
+void ADS1299::initialize(){
+    DRDY = _DRDY;
+    CS = _CS;
+    int FREQ = _FREQ;
+    int RST = _RST;
+    DigitalOut CS (PTD0);       // arduino pin 10 - chip select
+    DigitalOut RST(PTD5);       // arduino pin 9 - reset pin
+    DigitalIn DRDY(PTA13);      // arduino pin 8 - data ready pin
+    
+        delay(50);              // recommended power up sequence requiers Tpor (~32mS)  
+        pinMode(RST,OUTPUT);
+        wait_ms(50);                // wait Tpor
+        RST = 0;                    // put the ADS into reset mode
+        wait_us(8);                 // wait Trst
+        RST = 1;                    // take ADS out of reset mode
+        wait_us(20);                // advertised to wait 18 Tclk before operation
+    
+
+ 
+}
+
+//System Commands
+void ADS1299::WAKEUP() {
+    digitalWrite(CS, LOW); 
+    transfer(_WAKEUP);
+    digitalWrite(CS, HIGH); 
+    delayMicroseconds(3);       //must wait 4 tCLK cycles before sending another command (Datasheet, pg. 35)
+}
+
+void ADS1299::STANDBY() {       // only allowed to send WAKEUP after sending STANDBY
+    digitalWrite(CS, LOW);
+    transfer(_STANDBY);
+    digitalWrite(CS, HIGH);
+}
+
+void ADS1299::RESET() {         // reset all the registers to default settings
+    digitalWrite(CS, LOW);
+    transfer(_RESET);
+    delayMicroseconds(12);      //must wait 18 tCLK cycles to execute this command (Datasheet, pg. 35)
+    digitalWrite(CS, HIGH);
+}
+
+void ADS1299::START() {         //start data conversion 
+    digitalWrite(CS, LOW);
+    transfer(_START);
+    digitalWrite(CS, HIGH);
+}
+
+void ADS1299::STOP() {          //stop data conversion
+    digitalWrite(CS, LOW);
+    transfer(_STOP);
+    digitalWrite(CS, HIGH);
+}
+
+void ADS1299::RDATAC() {
+    digitalWrite(CS, LOW);
+    transfer(_RDATAC);
+    digitalWrite(CS, HIGH);
+    delayMicroseconds(3);   
+}
+void ADS1299::SDATAC() {
+    digitalWrite(CS, LOW);
+    transfer(_SDATAC);
+    digitalWrite(CS, HIGH);
+    delayMicroseconds(3);   //must wait 4 tCLK cycles after executing this command (Datasheet, pg. 37)
+}
+
+
+// Register Read/Write Commands
+byte ADS1299::getDeviceID() {           // simple hello world com check
+    byte data = RREG(0x00);
+    if(verbose){                        // verbose otuput
+        Serial.print(F("Device ID "));
+        printHex(data); 
+    }
+    return data;
+}
+
+byte ADS1299::RREG(byte _address) {     //  reads ONE register at _address
+    byte opcode1 = _address + 0x20;     //  RREG expects 001rrrrr where rrrrr = _address
+    digitalWrite(CS, LOW);              //  open SPI
+    transfer(opcode1);                  //  opcode1
+    transfer(0x00);                     //  opcode2
+    regData[_address] = transfer(0x00);//  update mirror location with returned byte
+    digitalWrite(CS, HIGH);             //  close SPI   
+    if (verbose){                       //  verbose output
+        printRegisterName(_address);
+        printHex(_address);
+        Serial.print(", ");
+        printHex(regData[_address]);
+        Serial.print(", ");
+        for(byte j = 0; j<8; j++){
+            Serial.print(bitRead(regData[_address], 7-j));
+            if(j!=7) Serial.print(", ");
+        }
+        
+        Serial.println();
+    }
+    return regData[_address];           // return requested register value
+}
+
+// Read more than one register starting at _address
+void ADS1299::RREGS(byte _address, byte _numRegistersMinusOne) {
+//  for(byte i = 0; i < 0x17; i++){
+//      regData[i] = 0;                 //  reset the regData array
+//  }
+    byte opcode1 = _address + 0x20;     //  RREG expects 001rrrrr where rrrrr = _address
+    digitalWrite(CS, LOW);              //  open SPI
+    transfer(opcode1);                  //  opcode1
+    transfer(_numRegistersMinusOne);    //  opcode2
+    for(int i = 0; i <= _numRegistersMinusOne; i++){
+        regData[_address + i] = transfer(0x00);     //  add register byte to mirror array
+        }
+    digitalWrite(CS, HIGH);             //  close SPI
+    if(verbose){                        //  verbose output
+        for(int i = 0; i<= _numRegistersMinusOne; i++){
+            printRegisterName(_address + i);
+            printHex(_address + i);
+            Serial.print(", ");
+            printHex(regData[_address + i]);
+            Serial.print(", ");
+            for(int j = 0; j<8; j++){
+                Serial.print(bitRead(regData[_address + i], 7-j));
+                if(j!=7) Serial.print(", ");
+            }
+            Serial.println();
+        }
+    }
+    
+}
+
+void ADS1299::WREG(byte _address, byte _value) {    //  Write ONE register at _address
+    byte opcode1 = _address + 0x40;     //  WREG expects 010rrrrr where rrrrr = _address
+    digitalWrite(CS, LOW);              //  open SPI
+    transfer(opcode1);                  //  Send WREG command & address
+    transfer(0x00);                     //  Send number of registers to read -1
+    transfer(_value);                   //  Write the value to the register
+    digitalWrite(CS, HIGH);             //  close SPI
+    regData[_address] = _value;         //  update the mirror array
+    if(verbose){                        //  verbose output
+        Serial.print(F("Register "));
+        printHex(_address);
+        Serial.println(F(" modified."));
+    }
+}
+
+void ADS1299::WREGS(byte _address, byte _numRegistersMinusOne) {
+    byte opcode1 = _address + 0x40;     //  WREG expects 010rrrrr where rrrrr = _address
+    digitalWrite(CS, LOW);              //  open SPI
+    transfer(opcode1);                  //  Send WREG command & address
+    transfer(_numRegistersMinusOne);    //  Send number of registers to read -1 
+    for (int i=_address; i <=(_address + _numRegistersMinusOne); i++){
+        transfer(regData[i]);           //  Write to the registers
+    }   
+    digitalWrite(CS,HIGH);              //  close SPI
+    if(verbose){
+        Serial.print(F("Registers "));
+        printHex(_address); Serial.print(F(" to "));
+        printHex(_address + _numRegistersMinusOne);
+        Serial.println(F(" modified"));
+    }
+}
+
+
+void ADS1299::updateChannelData(){
+    byte inByte;
+    digitalWrite(CS, LOW);              //  open SPI
+    stat = transfer(0x00);              //  read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+    stat = transfer(0x00);              //  read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+    stat = transfer(0x00);              //  read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+    for(int i = 0; i<8; i++){
+        for(int j=0; j<3; j++){     //  read 24 bits of channel data in 8 3 byte chunks
+            inByte = transfer(0x00);
+            channelData[i] = (channelData[i]<<8) | inByte;
+        }
+    }
+    digitalWrite(CS, HIGH);             //  close SPI
+    
+    for(int i=0; i<8; i++){         // convert 3 byte 2's compliment to 4 byte 2's compliment   
+        if(bitRead(channelData[i],23) == 1){    
+            channelData[i] |= 0xFF000000;
+        }else{
+            channelData[i] &= 0x00FFFFFF;
+        }
+    }
+//  if(verbose){
+//      Serial.print(stat); Serial.print(", ");
+//      for(int i=0; i<8; i++){
+//          Serial.print(channelData[i]);
+//          if(i<7){Serial.print(", ");}
+//      }
+//      Serial.println();
+//  }
+}
+    
+
+
+
+void ADS1299::RDATA() {                 //  use in Stop Read Continuous mode when DRDY goes low
+    byte inByte;                        //  to read in one sample of the channels
+    digitalWrite(CS, LOW);              //  open SPI
+    transfer(_RDATA);                   //  send the RDATA command
+    stat = transfer(0x00);              //  read status register (1100 + LOFF_STATP + LOFF_STATN + GPIO[7:4])
+    for(int i = 0; i<8; i++){
+        for(int j=0; j<3; j++){     //  read in the status register and new channel data
+            inByte = transfer(0x00);
+            channelData[i] = (channelData[i]<<8) | inByte;
+        }
+    }
+    digitalWrite(CS, HIGH);             //  close SPI
+    
+    for(int i=0; i<8; i++){
+        if(bitRead(channelData[i],23) == 1){    // convert 3 byte 2's compliment to 4 byte 2's compliment
+            channelData[i] |= 0xFF000000;
+        }else{
+            channelData[i] &= 0x00FFFFFF;
+        }
+    }
+    
+}
+
+
+// String-Byte converters for RREG and WREG
+void ADS1299::printRegisterName(byte _address) {
+    if(_address == ID){
+        Serial.print(F("ID, ")); //the "F" macro loads the string directly from Flash memory, thereby saving RAM
+    }
+    else if(_address == CONFIG1){
+        Serial.print(F("CONFIG1, "));
+    }
+    else if(_address == CONFIG2){
+        Serial.print(F("CONFIG2, "));
+    }
+    else if(_address == CONFIG3){
+        Serial.print(F("CONFIG3, "));
+    }
+    else if(_address == LOFF){
+        Serial.print(F("LOFF, "));
+    }
+    else if(_address == CH1SET){
+        Serial.print(F("CH1SET, "));
+    }
+    else if(_address == CH2SET){
+        Serial.print(F("CH2SET, "));
+    }
+    else if(_address == CH3SET){
+        Serial.print(F("CH3SET, "));
+    }
+    else if(_address == CH4SET){
+        Serial.print(F("CH4SET, "));
+    }
+    else if(_address == CH5SET){
+        Serial.print(F("CH5SET, "));
+    }
+    else if(_address == CH6SET){
+        Serial.print(F("CH6SET, "));
+    }
+    else if(_address == CH7SET){
+        Serial.print(F("CH7SET, "));
+    }
+    else if(_address == CH8SET){
+        Serial.print(F("CH8SET, "));
+    }
+    else if(_address == BIAS_SENSP){
+        Serial.print(F("BIAS_SENSP, "));
+    }
+    else if(_address == BIAS_SENSN){
+        Serial.print(F("BIAS_SENSN, "));
+    }
+    else if(_address == LOFF_SENSP){
+        Serial.print(F("LOFF_SENSP, "));
+    }
+    else if(_address == LOFF_SENSN){
+        Serial.print(F("LOFF_SENSN, "));
+    }
+    else if(_address == LOFF_FLIP){
+        Serial.print(F("LOFF_FLIP, "));
+    }
+    else if(_address == LOFF_STATP){
+        Serial.print(F("LOFF_STATP, "));
+    }
+    else if(_address == LOFF_STATN){
+        Serial.print(F("LOFF_STATN, "));
+    }
+    else if(_address == GPIO){
+        Serial.print(F("GPIO, "));
+    }
+    else if(_address == MISC1){
+        Serial.print(F("MISC1, "));
+    }
+    else if(_address == MISC2){
+        Serial.print(F("MISC2, "));
+    }
+    else if(_address == CONFIG4){
+        Serial.print(F("CONFIG4, "));
+    }
+}
+
+//SPI communication methods
+byte ADS1299::transfer(byte _data) {
+    cli();
+    SPDR = _data;
+    while (!(SPSR & _BV(SPIF)))
+        ;
+    sei();
+    return SPDR;
+}
+
+// Used for printing HEX in verbose feedback mode
+void ADS1299::printHex(byte _data){
+    Serial.print("0x");
+    if(_data < 0x10) Serial.print("0");
+    Serial.print(_data, HEX);
+}
+
+//-------------------------------------------------------------------//
+//-------------------------------------------------------------------//
+//-------------------------------------------------------------------//
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADS1299.h	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,74 @@
+//
+//  ADS1299.h
+//  Created by Joel Murphy, Summer 2K13  
+//  Ported from Conor Russomanno's Arduino library
+//
+//
+
+#ifndef _ADS1299_h
+#define _ADS1299_h
+#include "mbed.h"
+
+//SPI Command Definition Byte Assignments (Datasheet, p35)
+#define _WAKEUP 0x02 // Wake-up from standby mode
+#define _STANDBY 0x04 // Enter Standby mode
+#define _RESET 0x06 // Reset the device
+#define _START 0x08 // Start and restart (synchronize) conversions
+#define _STOP 0x0A // Stop conversion
+#define _RDATAC 0x10 // Enable Read Data Continuous mode (default mode at power-up)
+#define _SDATAC 0x11 // Stop Read Data Continuous mode
+#define _RDATA 0x12 // Read data by command; supports multiple read back
+//#define _RREG 0x20 // (00100000) is the first opcode that the address must be added to for RREG communication
+//#define _WREG 0x40 // 01000000 in binary (Datasheet, p35)
+
+//Register Addresses
+#define ID 0x00
+#define CONFIG1 0x01
+#define CONFIG2 0x02
+#define CONFIG3 0x03
+#define LOFF 0x04
+#define CH1SET 0x05
+#define CH2SET 0x06
+#define CH3SET 0x07
+#define CH4SET 0x08
+#define CH5SET 0x09
+#define CH6SET 0x0A
+#define CH7SET 0x0B
+#define CH8SET 0x0C
+#define BIAS_SENSP 0x0D
+#define BIAS_SENSN 0x0E
+#define LOFF_SENSP 0x0F
+#define LOFF_SENSN 0x10
+#define LOFF_FLIP 0x11
+#define LOFF_STATP 0x12
+#define LOFF_STATN 0x13
+#define GPIO 0x14
+#define MISC1 0x15
+#define MISC2 0x16
+#define CONFIG4 0x17
+
+    void WAKEUP();
+    void STANDBY();
+    void RESET();
+    void START();
+    void STOP();    
+    //Data Read Commands
+    void RDATAC();
+    void SDATAC();
+    void RDATA();   
+    //Register Read/Write Commands
+    char getDeviceID();
+    char RREG(char _address);
+    void RREGS(char _address, char _numRegistersMinusOne);     
+    void printRegisterName(char _address);
+    void WREG(char _address, char _value); 
+    void WREGS(char _address, char _numRegistersMinusOne); 
+    void printHex(char _data);
+    void updateChannelData();
+    
+    int stat;         // used to hold the status register
+    char regData [24];  // array used when reading register data
+    long channelData [8];   // array used when reading channel data
+    bool verbose;        // turn on/off Serial feedback
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADS1299Manager.cpp	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,323 @@
+
+
+
+#include <ADS1299Manager.h>
+
+typedef long int32;
+//typedef byte uint8_t;
+
+
+void ADS1299Manager::initialize(void) {
+    initialize(OPENBCI_V2);
+}
+
+//Initilize the ADS1299 controller...call this once
+void ADS1299Manager::initialize(const int version) 
+{
+  setVersionOpenBCI(version);
+  ADS1299::initialize(PIN_DRDY,PIN_RST,PIN_CS,SCK_MHZ); // (DRDY pin, RST pin, CS pin, SCK frequency in MHz);
+  delay(100);
+  
+  verbose = false;      // when verbose is true, there will be Serial feedback 
+  reset();
+  
+  //set default state for internal test signal
+  //ADS1299::WREG(CONFIG2,0b11010000);delay(1);   //set internal test signal, default amplitude, default speed, datasheet PDF Page 41
+  //ADS1299::WREG(CONFIG2,0b11010001);delay(1);   //set internal test signal, default amplitude, 2x speed, datasheet PDF Page 41
+  configureInternalTestSignal(ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_FAST); //set internal test signal, default amplitude, 2x speed, datasheet PDF Page 41
+
+};
+
+void ADS1299Manager::setVersionOpenBCI(const int version)
+{
+  if (version == OPENBCI_V1) {
+      use_neg_inputs = false;
+      for (int i=0; i < OPENBCI_NCHAN; i++) {
+          use_SRB2[i] = false;
+      }
+  } else {
+      use_neg_inputs = true;
+      for (int i=0; i < OPENBCI_NCHAN; i++) {
+          use_SRB2[i] = true;
+      }
+  }
+  //setSRB1(use_SRB1());  //set whether SRB1 is active or not
+}
+
+//reset all the ADS1299's settings.  Call however you'd like.  Stops all data acquisition
+void ADS1299Manager::reset(void)
+{
+  ADS1299::RESET();             // send RESET command to default all registers
+  ADS1299::SDATAC();            // exit Read Data Continuous mode to communicate with ADS
+  
+  delay(100);
+    
+  // turn off all channels
+  for (int chan=1; chan <= 8; chan++) {
+    deactivateChannel(chan);
+  }
+  
+  setSRB1(use_SRB1());  //set whether SRB1 is active or not
+  
+};
+
+
+//deactivate the given channel...note: stops data colleciton to issue its commands
+//  N is the channel number: 1-8
+// 
+void ADS1299Manager::deactivateChannel(int N)
+{
+  byte reg, config;
+    
+  //check the inputs
+  if ((N < 1) || (N > 8)) return;
+  
+  //proceed...first, disable any data collection
+  ADS1299::SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS
+
+  //shut down the channel
+  N = constrain(N-1,0,7);  //subtracts 1 so that we're counting from 0, not 1
+  reg = CH1SET+(byte)N;
+  config = ADS1299::RREG(reg); delay(1);
+  bitSet(config,7);  //left-most bit (bit 7) = 1, so this shuts down the channel
+  if (use_neg_inputs) bitClear(config,3);  //bit 3 = 0 disconnects SRB2
+  ADS1299::WREG(reg,config); delay(1);
+  
+  //remove the channel from the bias generation...
+  reg = BIAS_SENSP; if (use_neg_inputs) reg = BIAS_SENSN;  //are we using the P inptus or the N inputs?
+  config = ADS1299::RREG(reg); delay(1);//get the current bias settings
+  bitClear(config,N);          //clear this channel's bit to remove from bias generation
+  ADS1299::WREG(reg,config); delay(1);  //send the modified byte back to the ADS
+  
+}; 
+    
+        
+//Active a channel in single-ended mode  
+//  N is 1 through 8
+//  gainCode is defined in the macros in the header file
+//  inputCode is defined in the macros in the header file
+void ADS1299Manager::activateChannel(int N,byte gainCode,byte inputCode) 
+{
+   //check the inputs
+  if ((N < 1) || (N > 8)) return;
+  
+  //proceed...first, disable any data collection
+  ADS1299::SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS
+
+  //active the channel using the given gain.  Set MUX for normal operation
+  //see ADS1299 datasheet, PDF p44
+  N = constrain(N-1,0,7);  //shift down by one
+  byte configByte = 0b00000000;  //left-most zero (bit 7) is to activate the channel
+  gainCode = gainCode & 0b01110000;  //bitwise AND to get just the bits we want and set the rest to zero
+  configByte = configByte | gainCode; //bitwise OR to set just the gain bits high or low and leave the rest alone
+  inputCode = inputCode & 0b00000111;  //bitwise AND to get just the bits we want and set the rest to zero
+  configByte = configByte | inputCode; //bitwise OR to set just the gain bits high or low and leave the rest alone
+  if (use_SRB2[N]) configByte |= 0b00001000;  //set the SRB2 flag...p44 in the data sheet
+  ADS1299::WREG(CH1SET+(byte)N,configByte); delay(1);
+
+  //add this channel to the bias generation
+  //see ADS1299 datasheet, PDF p44
+  byte reg = BIAS_SENSP; if (use_neg_inputs) reg = BIAS_SENSN;  //are we using the P inptus or the N inputs?
+  byte biasSettings = ADS1299::RREG(reg); //get the current bias settings
+  bitSet(biasSettings,N);                   //set this channel's bit
+  ADS1299::WREG(reg,biasSettings); delay(1);  //send the modified byte back to the ADS
+  
+  // // Now, these actions are necessary whenever there is at least one active channel
+  // // though they don't strictly need to be done EVERY time we activate a channel.
+  // // just once after the reset.
+  
+  //activate SRB1 as the Negative input for all channels, if needed
+  setSRB1(use_SRB1());
+
+  //Finalize the bias setup...activate buffer and use internal reference for center of bias creation, datasheet PDF p42
+  ADS1299::WREG(CONFIG3,0b11101100); delay(1); 
+};
+
+void ADS1299Manager::setSRB1(boolean desired_state) {
+    if (desired_state) {
+        ADS1299::WREG(MISC1,0b00100000); delay(1);  //ADS1299 datasheet, PDF p46
+    } else {
+        ADS1299::WREG(MISC1,0b00000000); delay(1);  //ADS1299 datasheet, PDF p46
+    }
+}
+
+
+//Configure the test signals that can be inernally generated by the ADS1299
+void ADS1299Manager::configureInternalTestSignal(byte amplitudeCode, byte freqCode)
+{
+    if (amplitudeCode == ADSTESTSIG_NOCHANGE) amplitudeCode = (ADS1299::RREG(CONFIG2) & (0b00000100));
+    if (freqCode == ADSTESTSIG_NOCHANGE) freqCode = (ADS1299::RREG(CONFIG2) & (0b00000011));
+    freqCode &= 0b00000011;  //only the last two bits should be used
+    amplitudeCode &= 0b00000100;  //only this bit should be used
+    byte message = 0b11010000 | freqCode | amplitudeCode;  //compose the code
+    
+    ADS1299::WREG(CONFIG2,message); delay(1);
+    
+       //ADS1299::WREG(CONFIG2,0b11010000);delay(1);   //set internal test signal, default amplitude, default speed, datasheet PDF Page 41
+      //ADS1299::WREG(CONFIG2,0b11010001);delay(1);   //set internal test signal, default amplitude, 2x speed, datasheet PDF Page 41
+      //ADS1299::WREG(CONFIG2,0b11010101);delay(1);   //set internal test signal, 2x amplitude, 2x speed, datasheet PDF Page 41
+      //ADS1299::WREG(CONFIG2,0b11010011); delay(1);  //set internal test signal, default amplitude, at DC, datasheet PDF Page 41
+      //ADS1299::WREG(CONFIG3,0b01101100); delay(1);  //use internal reference for center of bias creation, datasheet PDF p42 
+}
+ 
+//Start continuous data acquisition
+void ADS1299Manager::start(void)
+{
+    ADS1299::RDATAC(); delay(1);           // enter Read Data Continuous mode
+    ADS1299::START();    //start the data acquisition
+}
+  
+//Query to see if data is available from the ADS1299...return TRUE is data is available
+int ADS1299Manager::isDataAvailable(void)
+{
+  return (!(digitalRead(PIN_DRDY)));
+}
+  
+//Stop the continuous data acquisition
+void ADS1299Manager::stop(void)
+{
+    ADS1299::STOP(); delay(1);   //start the data acquisition
+    ADS1299::SDATAC(); delay(1);      // exit Read Data Continuous mode to communicate with ADS
+}
+  
+//print as text each channel's data
+//   print channels 1-N (where N is 1-8...anything else will return with no action)
+//   sampleNumber is a number that, if greater than zero, will be printed at the start of the line
+void ADS1299Manager::printChannelDataAsText(int N, long int sampleNumber)
+{
+    //check the inputs
+    if ((N < 1) || (N > 8)) return;
+    
+    //print the sample number, if not disabled
+    if (sampleNumber > 0) {
+        pc.printf(sampleNumber);
+        pc.printf(", ");
+    }
+
+    //print each channel
+    for (int chan = 0; chan < N; chan++ )
+    {
+        pc.printf(channelData[chan]);
+        pc.printf(", ");
+    }
+    
+    //print end of line
+    pc.printf("\n");
+    
+};
+
+
+//write as binary each channel's data
+//   print channels 1-N (where N is 1-8...anything else will return with no action)
+//   sampleNumber is a number that, if greater than zero, will be printed at the start of the line
+int32 val;
+byte *val_ptr = (byte *)(&val);
+void ADS1299Manager::writeChannelDataAsBinary(int N, long sampleNumber)
+{
+    //check the inputs
+    if ((N < 1) || (N > 8)) return;
+    
+    // Write header
+    pc.printf( (byte) PCKT_START);
+    //byte byte_val = (1+8)*4;
+    pc.printf((1+N)*4);  //length of data payload, bytes
+    
+    
+        
+    //print the sample number, if not disabled
+    //val = sampleNumber;
+    val = 1L;   
+    //ptr = (uint8_t *)(&val);  //pretend that it is a string buffer so that Serial.write works easier
+    //if (sampleNumber >= 0) {
+        pc.printf(val_ptr,4); //4 bytes long
+    //}
+
+    //print each channel
+    for (int chan = 0; chan < N; chan++ )
+    {
+        val = (long)(channelData[chan]);
+        pc.printf(val_ptr,4); //4 bytes long
+    }
+    
+    // Write footer
+    pc.printf((byte)PCKT_END);
+    
+    // force everything out
+    //Serial.flush();   
+};
+
+//write channel data using binary format of ModularEEG so that it can be used by BrainBay (P2 protocol)
+//this only sends 6 channels of data, per the P2 protocol
+//http://www.shifz.org/brainbay/manuals/brainbay_developer_manual.pdf
+#define max_int16 (32767)
+#define min_int16 (-32767)
+void ADS1299Manager::writeChannelDataAsOpenEEG_P2(long sampleNumber) {
+    ADS1299Manager::writeChannelDataAsOpenEEG_P2(sampleNumber,false);
+}
+#define synthetic_amplitude_counts (8950)   //counts peak-to-peak...should be 100 uV pk-pk  (100e-6 / (4.5 / 24 / 2^24))
+void ADS1299Manager::writeChannelDataAsOpenEEG_P2(long sampleNumber,boolean useSyntheticData) {
+    byte sync0 = 0xA5;
+    byte sync1 = 0x5A;
+    byte version = 2;
+    
+    pc.printf(sync0);
+    pc.printf(sync1);
+    pc.printf(version);
+    pc.printf((byte) sampleNumber);
+    
+    long val32; //32-bit
+    int val_i16;  //16-bit
+    unsigned int val_u16;  //16-bit
+    byte *val16_ptr = (byte *)(&val_u16);  //points to the memory for the variable above
+    for (int chan = 0; chan < 6; chan++ )
+    {
+        //get this channel's data
+        if (useSyntheticData) {
+            //generate 10 uV pk-pk signal
+            long time_samp_255 = (long)((sampleNumber) & (0x000000FF));  //make an 8-bit ramp waveform
+            time_samp_255 = (long)((time_samp_255*(long)(chan+1)) & (0x000000FF)); //each channel is faster than the previous
+            time_samp_255 += 256L*2L;  //make zero mean...empirically tuned via BrainBay visualization
+            val32 = (synthetic_amplitude_counts * time_samp_255) / 255L; //scaled zero-mean ramp 
+        } else {
+            //get the real EEG data for this channel
+            val32 = channelData[chan];
+        }           
+                        
+        //prepare the value for transmission
+        val32 = val32 / (32);  //shrink to fit within a 16-bit number
+        val32 = constrain(val32,min_int16,max_int16);  //constrain to fit in 16 bits
+        val_u16 = (unsigned int) (val32 & (0x0000FFFF));  //truncate and cast
+    
+        //pc.printf(val16_ptr,2); //low byte than high byte on Arduino
+        pc.printf((byte)((val_u16 >> 8) & 0x00FF)); //high byte
+        pc.printf((byte)(val_u16 & 0x00FF)); //low byte
+        
+    }
+    byte switches = 0b00000000;  //the last thing required by the P2 data protocol
+    pc.printf(switches);
+}
+    
+
+
+//print out the state of all the control registers
+void ADS1299Manager::printAllRegisters(void)   
+{
+    boolean prevVerboseState = verbose;
+    
+        verbose = true;
+        ADS1299::RREGS(0x00,0x10);     // write the first registers
+        delay(100);  //stall to let all that data get read by the PC
+        ADS1299::RREGS(0x11,0x17-0x11);     // write the rest
+        verbose = prevVerboseState;
+}
+
+//only use SRB1 if all use_SRB2 are set to false
+boolean ADS1299Manager::use_SRB1(void) {
+    for (int Ichan=0; Ichan < OPENBCI_NCHAN; Ichan++) {
+        if (use_SRB2[Ichan]) {
+            return false;
+        }
+    }
+    return true;
+}
+            
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADS1299Manager.h	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,83 @@
+
+//
+//  ADS1299Manager.h
+//  Part of the Arduino Library for the ADS1299 Shield
+//  Created by Chip Audette, Fall 2013
+//
+
+
+#ifndef ____ADS1299Manager__
+#define ____ADS1299Manager__
+
+#include <ADS1299.h>
+
+//Pick which version of OpenBCI you have
+#define OPENBCI_V1 (1)    //Sept 2013
+#define OPENBCI_V2 (2)    //Oct 24, 2013
+#define OPENBCI_NCHAN (8)  // number of EEG channels
+
+/*   Arduino Uno - Pin Assignments
+  SCK = 13
+  MISO [DOUT] = 12
+  MOSI [DIN] = 11
+  CS = 10; 
+  RESET = 9;
+  DRDY = 8;
+*/
+#define PIN_DRDY (8)
+#define PIN_RST (9)
+#define PIN_CS (10)
+#define SCK_MHZ (4)
+
+//gainCode choices
+#define ADS_GAIN01 (0b00000000)
+#define ADS_GAIN02 (0b00010000)
+#define ADS_GAIN04 (0b00100000)
+#define ADS_GAIN06 (0b00110000)
+#define ADS_GAIN08 (0b01000000)
+#define ADS_GAIN12 (0b01010000)
+#define ADS_GAIN24 (0b01100000)
+
+//inputCode choices
+#define ADSINPUT_NORMAL (0b00000000)
+#define ADSINPUT_SHORTED (0b00000001)
+#define ADSINPUT_TESTSIG (0b00000101)
+
+//test signal choices...ADS1299 datasheet page 41
+#define ADSTESTSIG_AMP_1X (0b00000000)
+#define ADSTESTSIG_AMP_2X (0b00000100)
+#define ADSTESTSIG_PULSE_SLOW (0b00000000)
+#define ADSTESTSIG_PULSE_FAST (0b00000001)
+#define ADSTESTSIG_DCSIG (0b00000011)
+#define ADSTESTSIG_NOCHANGE (0b11111111)
+
+//binary communication codes for each packet
+#define PCKT_START 0xA0
+#define PCKT_END 0xC0
+
+class ADS1299Manager : public ADS1299 {
+  public:
+    void initialize(void);                                     //initialize the ADS1299 controller.  Call once.  Assumes OpenBCI_V2
+    void initialize(int version);                              //initialize the ADS1299 controller.  Call once.  Set which version of OpenBCI you're using.
+    void setVersionOpenBCI(int version);            //Set which version of OpenBCI you're using.
+    void reset(void);                                          //reset all the ADS1299's settings.  Call however you'd like
+    void activateChannel(int N, byte gainCode,byte inputCode); //setup the channel 1-8
+    void deactivateChannel(int N);                            //disable given channel 1-8
+    void configureInternalTestSignal(byte amplitudeCode, byte freqCode);  
+    void start(void);
+    void stop(void);
+    int isDataAvailable(void);
+    void printChannelDataAsText(int N, long int sampleNumber);
+    void writeChannelDataAsBinary(int N, long int sampleNumber);
+    void writeChannelDataAsOpenEEG_P2(long int sampleNumber);
+    void writeChannelDataAsOpenEEG_P2(long int sampleNumber, boolean useSyntheticData);
+    void printAllRegisters(void);
+    void setSRB1(boolean desired_state);
+    
+  private:
+    boolean use_neg_inputs;
+    boolean use_SRB2[OPENBCI_NCHAN];
+    boolean use_SRB1(void);
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad.cpp	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,165 @@
+//
+//  Biquad.cpp
+//
+//  Created by Nigel Redmon on 11/24/12
+//  EarLevel Engineering: earlevel.com
+//  Copyright 2012 Nigel Redmon
+//
+//  For a complete explanation of the Biquad code:
+//  http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
+//
+//  License:
+//
+//  This source code is provided as is, without warranty.
+//  You may copy and distribute verbatim copies of this document.
+//  You may modify and use this source code to create binary code
+//  for your own purposes, free or commercial.
+//
+
+#include <math.h>
+#include "Biquad.h"
+
+Biquad::Biquad() {
+    type = bq_type_lowpass;
+    a0 = 1.0;
+    a1 = a2 = b1 = b2 = 0.0;
+    Fc = 0.50;
+    Q = 0.707;
+    peakGain = 0.0;
+    z1 = z2 = 0.0;
+}
+
+Biquad::Biquad(int type, double Fc, double Q, double peakGainDB) {
+    setBiquad(type, Fc, Q, peakGainDB);
+    z1 = z2 = 0.0;
+}
+
+Biquad::~Biquad() {
+}
+
+void Biquad::setType(int type) {
+    this->type = type;
+    calcBiquad();
+}
+
+void Biquad::setQ(double Q) {
+    this->Q = Q;
+    calcBiquad();
+}
+
+void Biquad::setFc(double Fc) {
+    this->Fc = Fc;
+    calcBiquad();
+}
+
+void Biquad::setPeakGain(double peakGainDB) {
+    this->peakGain = peakGainDB;
+    calcBiquad();
+}
+    
+void Biquad::setBiquad(int type, double Fc, double Q, double peakGainDB) {
+    this->type = type;
+    this->Q = Q;
+    this->Fc = Fc;
+    setPeakGain(peakGainDB);
+}
+
+void Biquad::calcBiquad(void) {
+    double norm;
+    double V = pow(10, fabs(peakGain) / 20.0);
+    double K = tan(M_PI * Fc);
+    switch (this->type) {
+        case bq_type_lowpass:
+            norm = 1 / (1 + K / Q + K * K);
+            a0 = K * K * norm;
+            a1 = 2 * a0;
+            a2 = a0;
+            b1 = 2 * (K * K - 1) * norm;
+            b2 = (1 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_highpass:
+            norm = 1 / (1 + K / Q + K * K);
+            a0 = 1 * norm;
+            a1 = -2 * a0;
+            a2 = a0;
+            b1 = 2 * (K * K - 1) * norm;
+            b2 = (1 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_bandpass:
+            norm = 1 / (1 + K / Q + K * K);
+            a0 = K / Q * norm;
+            a1 = 0;
+            a2 = -a0;
+            b1 = 2 * (K * K - 1) * norm;
+            b2 = (1 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_notch:
+            norm = 1 / (1 + K / Q + K * K);
+            a0 = (1 + K * K) * norm;
+            a1 = 2 * (K * K - 1) * norm;
+            a2 = a0;
+            b1 = a1;
+            b2 = (1 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_peak:
+            if (peakGain >= 0) {    // boost
+                norm = 1 / (1 + 1/Q * K + K * K);
+                a0 = (1 + V/Q * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - V/Q * K + K * K) * norm;
+                b1 = a1;
+                b2 = (1 - 1/Q * K + K * K) * norm;
+            }
+            else {    // cut
+                norm = 1 / (1 + V/Q * K + K * K);
+                a0 = (1 + 1/Q * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - 1/Q * K + K * K) * norm;
+                b1 = a1;
+                b2 = (1 - V/Q * K + K * K) * norm;
+            }
+            break;
+        case bq_type_lowshelf:
+            if (peakGain >= 0) {    // boost
+                norm = 1 / (1 + sqrt(2) * K + K * K);
+                a0 = (1 + sqrt(2*V) * K + V * K * K) * norm;
+                a1 = 2 * (V * K * K - 1) * norm;
+                a2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+                b1 = 2 * (K * K - 1) * norm;
+                b2 = (1 - sqrt(2) * K + K * K) * norm;
+            }
+            else {    // cut
+                norm = 1 / (1 + sqrt(2*V) * K + V * K * K);
+                a0 = (1 + sqrt(2) * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - sqrt(2) * K + K * K) * norm;
+                b1 = 2 * (V * K * K - 1) * norm;
+                b2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+            }
+            break;
+        case bq_type_highshelf:
+            if (peakGain >= 0) {    // boost
+                norm = 1 / (1 + sqrt(2) * K + K * K);
+                a0 = (V + sqrt(2*V) * K + K * K) * norm;
+                a1 = 2 * (K * K - V) * norm;
+                a2 = (V - sqrt(2*V) * K + K * K) * norm;
+                b1 = 2 * (K * K - 1) * norm;
+                b2 = (1 - sqrt(2) * K + K * K) * norm;
+            }
+            else {    // cut
+                norm = 1 / (V + sqrt(2*V) * K + K * K);
+                a0 = (1 + sqrt(2) * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - sqrt(2) * K + K * K) * norm;
+                b1 = 2 * (K * K - V) * norm;
+                b2 = (V - sqrt(2*V) * K + K * K) * norm;
+            }
+            break;
+    }
+    
+    return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad.h	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,60 @@
+//
+//  Biquad.h
+//
+//  Created by Nigel Redmon on 11/24/12
+//  EarLevel Engineering: earlevel.com
+//  Copyright 2012 Nigel Redmon
+//
+//  For a complete explanation of the Biquad code:
+//  http://www.earlevel.com/main/2012/11/25/biquad-c-source-code/
+//
+//  License:
+//
+//  This source code is provided as is, without warranty.
+//  You may copy and distribute verbatim copies of this document.
+//  You may modify and use this source code to create binary code
+//  for your own purposes, free or commercial.
+//
+
+#ifndef Biquad_h
+#define Biquad_h
+
+enum {
+    bq_type_lowpass = 0,
+    bq_type_highpass,
+    bq_type_bandpass,
+    bq_type_notch,
+    bq_type_peak,
+    bq_type_lowshelf,
+    bq_type_highshelf
+};
+
+class Biquad {
+public:
+    Biquad();
+    Biquad(int type, double Fc, double Q, double peakGainDB);
+    ~Biquad();
+    void setType(int type);
+    void setQ(double Q);
+    void setFc(double Fc);
+    void setPeakGain(double peakGainDB);
+    void setBiquad(int type, double Fc, double Q, double peakGain);
+    float process(float in);
+    
+protected:
+    void calcBiquad(void);
+
+    int type;
+    double a0, a1, a2, b1, b2;
+    double Fc, Q, peakGain;
+    double z1, z2;
+};
+
+inline float Biquad::process(float in) {
+    double out = in * a0 + z1;
+    z1 = in * a1 + z2 - b1 * out;
+    z2 = in * a2 - b2 * out;
+    return out;
+}
+
+#endif // Biquad_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad_multiChan.cpp	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,180 @@
+//
+//  Biquad.cpp
+//
+//  Created by Nigel Redmon on 11/24/12
+//  EarLevel Engineering: earlevel.com
+//  Copyright 2012 Nigel Redmon
+//
+//  For a complete explanation of the Biquad code:
+//  http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
+//
+//  License:
+//
+//  This source code is provided as is, without warranty.
+//  You may copy and distribute verbatim copies of this document.
+//  You may modify and use this source code to create binary code
+//  for your own purposes, free or commercial.
+//
+//  Extended by Chip Audette to handle multiple channels of data
+//  that are being filtered by the same coefficients
+//
+
+#include <math.h>
+#include "Biquad_multiChan.h"
+
+
+/*
+Biquad_multiChan::Biquad_multiChan() {
+    type = bq_type_lowpass;
+    a0 = 1.0;
+    a1 = a2 = b1 = b2 = 0.0;
+    Fc = 0.50;
+    Q = 0.707;
+    peakGain = 0.0;
+    z1 = z2 = 0.0;
+}
+*/
+
+Biquad_multiChan::Biquad_multiChan(int N,int type, double Fc, double Q, double peakGainDB) {
+    setBiquad(type, Fc, Q, peakGainDB);
+
+    Nchan = N;
+    z1 = new double[Nchan];
+    z2 = new double[Nchan];
+    for (int Ichan=0;Ichan<Nchan;Ichan++) {
+    z1[Ichan]=0.0;
+    z2[Ichan]=0.0;
+    }
+}
+
+Biquad_multiChan::~Biquad_multiChan() {
+    delete z2;
+    delete z1;
+}
+
+void Biquad_multiChan::setType(int type) {
+    this->type = type;
+    calcBiquad();
+}
+
+void Biquad_multiChan::setQ(double Q) {
+    this->Q = Q;
+    calcBiquad();
+}
+
+void Biquad_multiChan::setFc(double Fc) {
+    this->Fc = Fc;
+    calcBiquad();
+}
+
+void Biquad_multiChan::setPeakGain(double peakGainDB) {
+    this->peakGain = peakGainDB;
+    calcBiquad();
+}
+    
+void Biquad_multiChan::setBiquad(int type, double Fc, double Q, double peakGainDB) {
+    this->type = type;
+    this->Q = Q;
+    this->Fc = Fc;
+    setPeakGain(peakGainDB);
+}
+
+void Biquad_multiChan::calcBiquad(void) {
+    double norm;
+    double V = pow(10, fabs(peakGain) / 20.0);
+    double K = tan(M_PI * Fc);
+    switch (this->type) {
+        case bq_type_lowpass:
+            norm = 1 / (1 + K / Q + K * K);
+            a0 = K * K * norm;
+            a1 = 2 * a0;
+            a2 = a0;
+            b1 = 2 * (K * K - 1) * norm;
+            b2 = (1 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_highpass:
+            norm = 1 / (1 + K / Q + K * K);
+            a0 = 1 * norm;
+            a1 = -2 * a0;
+            a2 = a0;
+            b1 = 2 * (K * K - 1) * norm;
+            b2 = (1 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_bandpass:
+            norm = 1.0 / (1.0 + K / Q + K * K);
+            a0 = K / Q * norm;
+            a1 = 0.0;
+            a2 = -a0;
+            b1 = 2.0 * (K * K - 1.0) * norm;
+            b2 = (1.0 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_notch:
+            norm = 1.0 / (1.0 + K / Q + K * K);
+            a0 = (1 + K * K) * norm;
+            a1 = 2.0 * (K * K - 1) * norm;
+            a2 = a0;
+            b1 = a1;
+            b2 = (1.0 - K / Q + K * K) * norm;
+            break;
+            
+        case bq_type_peak:
+            if (peakGain >= 0) {    // boost
+                norm = 1 / (1 + 1/Q * K + K * K);
+                a0 = (1 + V/Q * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - V/Q * K + K * K) * norm;
+                b1 = a1;
+                b2 = (1 - 1/Q * K + K * K) * norm;
+            }
+            else {    // cut
+                norm = 1 / (1 + V/Q * K + K * K);
+                a0 = (1 + 1/Q * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - 1/Q * K + K * K) * norm;
+                b1 = a1;
+                b2 = (1 - V/Q * K + K * K) * norm;
+            }
+            break;
+        case bq_type_lowshelf:
+            if (peakGain >= 0) {    // boost
+                norm = 1 / (1 + sqrt(2) * K + K * K);
+                a0 = (1 + sqrt(2*V) * K + V * K * K) * norm;
+                a1 = 2 * (V * K * K - 1) * norm;
+                a2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+                b1 = 2 * (K * K - 1) * norm;
+                b2 = (1 - sqrt(2) * K + K * K) * norm;
+            }
+            else {    // cut
+                norm = 1 / (1 + sqrt(2*V) * K + V * K * K);
+                a0 = (1 + sqrt(2) * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - sqrt(2) * K + K * K) * norm;
+                b1 = 2 * (V * K * K - 1) * norm;
+                b2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+            }
+            break;
+        case bq_type_highshelf:
+            if (peakGain >= 0) {    // boost
+                norm = 1 / (1 + sqrt(2) * K + K * K);
+                a0 = (V + sqrt(2*V) * K + K * K) * norm;
+                a1 = 2 * (K * K - V) * norm;
+                a2 = (V - sqrt(2*V) * K + K * K) * norm;
+                b1 = 2 * (K * K - 1) * norm;
+                b2 = (1 - sqrt(2) * K + K * K) * norm;
+            }
+            else {    // cut
+                norm = 1 / (V + sqrt(2*V) * K + K * K);
+                a0 = (1 + sqrt(2) * K + K * K) * norm;
+                a1 = 2 * (K * K - 1) * norm;
+                a2 = (1 - sqrt(2) * K + K * K) * norm;
+                b1 = 2 * (K * K - V) * norm;
+                b2 = (V - sqrt(2*V) * K + K * K) * norm;
+            }
+            break;
+    }
+    
+    return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Biquad_multiChan.h	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,64 @@
+//
+//  Biquad_multiChan.h
+//
+//  Created by Nigel Redmon on 11/24/12
+//  EarLevel Engineering: earlevel.com
+//  Copyright 2012 Nigel Redmon
+//
+//  For a complete explanation of the Biquad code:
+//  http://www.earlevel.com/main/2012/11/25/biquad-c-source-code/
+//
+//  License:
+//
+//  This source code is provided as is, without warranty.
+//  You may copy and distribute verbatim copies of this document.
+//  You may modify and use this source code to create binary code
+//  for your own purposes, free or commercial.
+//
+//  Extended by Chip Audette (Nov 2013) to handle multiple channels of data
+//  that are being filtered by the same coefficients
+//
+
+#ifndef Biquad_multiChan_h
+#define Biquad_multiChan_h
+
+enum {
+    bq_type_lowpass = 0,
+    bq_type_highpass,
+    bq_type_bandpass,
+    bq_type_notch,
+    bq_type_peak,
+    bq_type_lowshelf,
+    bq_type_highshelf
+};
+
+class Biquad_multiChan {
+public:
+    //Biquad_multiChan();
+    Biquad_multiChan(int Nchan, int type, double Fc, double Q, double peakGainDB);
+    ~Biquad_multiChan();
+    void setType(int type);
+    void setQ(double Q);
+    void setFc(double Fc);
+    void setPeakGain(double peakGainDB);
+    void setBiquad(int type, double Fc, double Q, double peakGain);
+    float process(float in,int Ichan);
+    
+protected:
+    void calcBiquad(void);
+
+    int Nchan;
+    int type;
+    double a0, a1, a2, b1, b2;
+    double Fc, Q, peakGain;
+    double *z1, *z2;
+};
+
+inline float Biquad_multiChan::process(float in,int Ichan) {
+    double out = in * a0 + z1[Ichan];
+    z1[Ichan] = in * a1 + z2[Ichan] - b1 * out;
+    z2[Ichan] = in * a2 - b2 * out;
+    return out;
+}
+
+#endif // Biquad_multiChan_h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,351 @@
+
+#include "mbed.h"
+#include "ADS1299.h"
+
+Serial pc(USBTX, USBRX);    // set up the COM
+SPI spi (PTD2,PTD3,PTD1);   // mosi, miso, sck
+
+
+//define how I'd like my channels setup
+#define MAX_N_CHANNELS (8)  //must be less than or equal to length of channelData in ADS1299 object!!
+int nActiveChannels = 8;   //how many active channels would I like?
+byte gainCode = ADS_GAIN24;   //how much gain do I want
+byte inputType = ADSINPUT_NORMAL;   //here's the normal way to setup the channels
+//byte inputType = ADSINPUT_SHORTED;  //here's another way to setup the channels
+//byte inputType = ADSINPUT_TESTSIG;  //here's a third way to setup the channels
+
+//other variables
+long sampleCounter = 0;      // used to time the tesing loop
+boolean is_running = false;    // this flag is set in serialEvent on reciept of prompt
+#define PIN_STARTBINARY (7)  //pull this pin to ground to start binary transfer
+//define PIN_STARTBINARY_OPENEEG (6)
+boolean startBecauseOfPin = false;
+boolean startBecauseOfSerial = false;
+
+#define OUTPUT_NOTHING (0)
+#define OUTPUT_TEXT (1)
+#define OUTPUT_BINARY (2)
+#define OUTPUT_BINARY_4CHAN (4)
+#define OUTPUT_BINARY_OPENEEG (6)
+#define OUTPUT_BINARY_OPENEEG_SYNTHETIC (7)
+int outputType;
+
+//Design filters  (This BIQUAD class requires ~6K of program space!  Ouch.)
+//For frequency response of these filters: http://www.earlevel.com/main/2010/12/20/biquad-calculator/
+#include <Biquad_multiChan.h>   //modified from this source code:  http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
+#define SAMPLE_RATE_HZ (250.0)  //default setting for OpenBCI
+#define FILTER_Q (0.5)        //critically damped is 0.707 (Butterworth)
+#define FILTER_PEAK_GAIN_DB (0.0) //we don't want any gain in the passband
+#define HP_CUTOFF_HZ (0.5)  //set the desired cutoff for the highpass filter
+Biquad_multiChan stopDC_filter(MAX_N_CHANNELS,bq_type_highpass,HP_CUTOFF_HZ / SAMPLE_RATE_HZ, FILTER_Q, FILTER_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+//Biquad_multiChan stopDC_filter(MAX_N_CHANNELS,bq_type_bandpass,10.0 / SAMPLE_RATE_HZ, 6.0, FILTER_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+#define NOTCH_FREQ_HZ (60.0)
+#define NOTCH_Q (4.0)              //pretty shap notch
+#define NOTCH_PEAK_GAIN_DB (0.0)  //doesn't matter for this filter type
+Biquad_multiChan notch_filter1(MAX_N_CHANNELS,bq_type_notch,NOTCH_FREQ_HZ / SAMPLE_RATE_HZ, NOTCH_Q, NOTCH_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+Biquad_multiChan notch_filter2(MAX_N_CHANNELS,bq_type_notch,NOTCH_FREQ_HZ / SAMPLE_RATE_HZ, NOTCH_Q, NOTCH_PEAK_GAIN_DB); //one for each channel because the object maintains the filter states
+boolean useFilters = false;
+
+
+
+DigitalOut CS (PTD0);       // arduino pin 10 - chip select
+DigitalOut RST(PTD5);       // arduino pin 9 - reset pin
+DigitalIn DRDY(PTA13);      // arduino pin 8 - data ready pin
+DigitalOut myled(LED3);     // debugging LED CONFLICT WITH SPI & LED1
+
+
+
+int main() {
+
+    ADS1299Manager ADSManager;
+    int OpenBCI_version = OPENBCI_V2;  //assume V2
+    ADSManager.initialize(OpenBCI_version);  //must do this VERY early in the setup...preferably first
+
+    spi.format(8,1);            // spi: 8 bit, mode 1
+    spi.frequency(1000000);     // spi sck frequency
+    
+    pc.baud(115200);
+    pc.printf("\nADS1299 Stream Raw Data\n");
+    wait(2.0);
+
+  pc.printf("ADS1299-Arduino UNO - Stream Raw Data"); //read the string from Flash to save RAM
+  pc.printf("Configured as OpenBCI_Version code = ");pc.printf(OpenBCI_version);
+  Serial.flush();
+  
+  // setup the channels as desired on the ADS1299..set gain, input type, referece (SRB1), and patient bias signal
+  for (int chan=1; chan <= nActiveChannels; chan++) {
+    ADSManager.activateChannel(chan, gainCode, inputType);
+  }
+
+  //print state of all registers
+  ADSManager.printAllRegisters();Serial.flush();
+
+  // setup hardware to allow a jumper or button to start the digitaltransfer
+  pinMode(PIN_STARTBINARY,INPUT); digitalWrite(PIN_STARTBINARY,HIGH); //activate pullup
+  //pinMode(PIN_STARTBINARY_OPENEEG,INPUT); digitalWrite(PIN_STARTBINARY_OPENEEG,HIGH);  //activate pullup
+  
+  // tell the controlling program that we're ready to start!
+  pc.printf("Press '?' to query and print ADS1299 register settings again"); //read it straight from flash
+  pc.printf("Press 1-8 to disable EEG Channels, q-i to enable (all enabled by default)");
+  pc.printf("Press 'F' to enable filters.  'f' to disable filters (disabled by default)");
+  pc.printf("Press 'x' (text) or 'b' (binary) to begin streaming data...");    
+ 
+boolean firstReport = true;
+unsigned long totalMicrosBusy = 0;  //use this to count time
+
+    while(1) {
+    
+        if (is_running) {
+ 
+        //is data ready?      
+        while(!(ADSManager.isDataAvailable())){            // watch the DRDY pin
+            delayMicroseconds(100);
+        }
+        unsigned long start_micros = micros();
+  
+        //get the data
+        ADSManager.updateChannelData();          // update the channelData array 
+        sampleCounter++;                        // increment my sample counter
+    
+        //Apply  filers to the data
+        if (useFilters) applyFilters();
+
+        //print the data
+        //if ((sampleCounter % 1) == 0) {
+        switch (outputType) {
+            case OUTPUT_NOTHING:
+                //don't output anything...the Arduino is still collecting data from the OpenBCI board...just nothing is being done with it
+            break;
+            case OUTPUT_BINARY:
+                ADSManager.writeChannelDataAsBinary(8,sampleCounter);  //print all channels, whether active or not
+            break;
+            case OUTPUT_BINARY_4CHAN:
+                ADSManager.writeChannelDataAsBinary(4,sampleCounter);  //print all channels, whether active or not
+              break; 
+            case OUTPUT_BINARY_OPENEEG:
+                ADSManager.writeChannelDataAsOpenEEG_P2(sampleCounter);  //print all channels, whether active or not
+            break; 
+            case OUTPUT_BINARY_OPENEEG_SYNTHETIC:
+                ADSManager.writeChannelDataAsOpenEEG_P2(sampleCounter,true);  //print all channels, whether active or not
+            break;           
+            default:
+                ADSManager.printChannelDataAsText(8,sampleCounter);  //print all channels, whether active or not
+            }// end of if(is_running)
+        }// end of while(1)
+
+        if(pc.readable()) {
+            serialEvent();
+        }
+    
+}// end of main()
+
+
+#define ACTIVATE_SHORTED (2)
+#define ACTIVATE (1)
+#define DEACTIVATE (0)
+void serialEvent(){            // 
+    char inChar = pc.getc();
+    switch (inChar)
+    {
+      case '1':
+        changeChannelState_maintainRunningState(1,DEACTIVATE); break;
+      case '2':
+        changeChannelState_maintainRunningState(2,DEACTIVATE); break;
+      case '3':
+        changeChannelState_maintainRunningState(3,DEACTIVATE); break;
+      case '4':
+        changeChannelState_maintainRunningState(4,DEACTIVATE); break;
+      case '5':
+        changeChannelState_maintainRunningState(5,DEACTIVATE); break;
+      case '6':
+        changeChannelState_maintainRunningState(6,DEACTIVATE); break;
+      case '7':
+        changeChannelState_maintainRunningState(7,DEACTIVATE); break;
+      case '8':
+        changeChannelState_maintainRunningState(8,DEACTIVATE); break;
+      case 'q':
+        changeChannelState_maintainRunningState(1,ACTIVATE); break;
+      case 'w':
+        changeChannelState_maintainRunningState(2,ACTIVATE); break;
+      case 'e':
+        changeChannelState_maintainRunningState(3,ACTIVATE); break;
+      case 'r':
+        changeChannelState_maintainRunningState(4,ACTIVATE); break;
+      case 't':
+        changeChannelState_maintainRunningState(5,ACTIVATE); break;
+      case 'y':
+        changeChannelState_maintainRunningState(6,ACTIVATE); break;
+      case 'u':
+        changeChannelState_maintainRunningState(7,ACTIVATE); break;
+      case 'i':
+        changeChannelState_maintainRunningState(8,ACTIVATE); break;
+      case '0':
+        activateAllChannelsToTestCondition(ADSINPUT_SHORTED,ADSTESTSIG_NOCHANGE,ADSTESTSIG_NOCHANGE); break;
+      case '-':
+        activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_SLOW); break;
+      case '+':
+        activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_FAST); break;
+      case '=':
+        //repeat the line above...just for human convenience
+        activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_1X,ADSTESTSIG_PULSE_FAST); break;
+      case 'p':
+        activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_2X,ADSTESTSIG_DCSIG); break;
+      case '[':
+        activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_2X,ADSTESTSIG_PULSE_SLOW); break;
+      case ']':
+        activateAllChannelsToTestCondition(ADSINPUT_TESTSIG,ADSTESTSIG_AMP_2X,ADSTESTSIG_PULSE_FAST); break;
+      case 'n':
+        toggleRunState(OUTPUT_NOTHING);
+        startBecauseOfSerial = is_running;
+        if (is_running) Serial.println(F("FRDM: Starting, but not outputing to PC..."));
+        break;
+      case 'b':
+        toggleRunState(OUTPUT_BINARY);
+        startBecauseOfSerial = is_running;
+        if (is_running) Serial.println(F("FRDM: Starting binary..."));
+        break;
+      case 'v':
+        toggleRunState(OUTPUT_BINARY_4CHAN);
+        startBecauseOfSerial = is_running;
+        if (is_running) Serial.println(F("FRDM: Starting binary 4-chan..."));
+        break;
+     case 's':
+        stopRunning();
+        startBecauseOfSerial = is_running;
+        break;
+     case 'x':
+        toggleRunState(OUTPUT_TEXT);
+        startBecauseOfSerial = is_running;
+        if (is_running) Serial.println(F("FRDM: Starting text..."));
+        break;
+     case 'f':
+        useFilters = false;
+        Serial.println(F("FRDM: disabling filters"));
+        break;
+     case 'F':
+        useFilters = true;
+        Serial.println(F("FRDM: enabaling filters"));
+        break;
+     case '?':
+        //print state of all registers
+        ADSManager.printAllRegisters();
+        break;
+      default:
+        break;
+    }
+  }
+}
+
+boolean toggleRunState(int OUT_TYPE)
+{
+  if (is_running) {
+    return stopRunning();
+  } else {
+    return startRunning(OUT_TYPE);
+  }
+}
+
+boolean stopRunning(void) {
+  ADSManager.stop();                    // stop the data acquisition
+  is_running = false;
+  return is_running;
+}
+
+boolean startRunning(int OUT_TYPE) {
+    outputType = OUT_TYPE;
+    ADSManager.start();    //start the data acquisition
+    is_running = true;
+    return is_running;
+}
+
+int changeChannelState_maintainRunningState(int chan, int start)
+{
+  boolean is_running_when_called = is_running;
+  int cur_outputType = outputType;
+  
+  //must stop running to change channel settings
+  stopRunning();
+  if (start == true) {
+    Serial.print("Activating channel ");
+    Serial.println(chan);
+    ADSManager.activateChannel(chan,gainCode,inputType);
+  } else {
+    Serial.print("Deactivating channel ");
+    Serial.println(chan);
+    ADSManager.deactivateChannel(chan);
+  }
+  
+  //restart, if it was running before
+  if (is_running_when_called == true) {
+    startRunning(cur_outputType);
+  }
+}
+
+int activateAllChannelsToTestCondition(int testInputCode, byte amplitudeCode, byte freqCode)
+{
+  boolean is_running_when_called = is_running;
+  int cur_outputType = outputType;
+  
+  //set the test signal to the desired state
+  ADSManager.configureInternalTestSignal(amplitudeCode,freqCode);
+  
+  //must stop running to change channel settings
+  stopRunning();
+    
+  //loop over all channels to change their state
+  for (int Ichan=1; Ichan <= 8; Ichan++) {
+    ADSManager.activateChannel(Ichan,gainCode,testInputCode);  //Ichan must be [1 8]...it does not start counting from zero
+  }
+      
+  //restart, if it was running before
+  if (is_running_when_called == true) {
+    startRunning(cur_outputType);
+  }
+}
+
+long int runningAve[MAX_N_CHANNELS];
+int applyFilters(void) {
+  //scale factor for these coefficients was 32768 = 2^15
+  const static long int a0 = 32360L; //16 bit shift?
+  const static long int a1 = -2L*a0;
+  const static long int a2 = a0;
+  const static long int b1 = -64718L; //this is a shift of 17 bits!
+  const static long int b2 = 31955L;
+  static long int z1[MAX_N_CHANNELS], z2[MAX_N_CHANNELS];
+  long int val_int, val_in_down9, val_out, val_out_down9;
+  float val;
+  for (int Ichan=0; Ichan < MAX_N_CHANNELS; Ichan++) {
+    switch (1) {
+      case 1:
+        //use BiQuad
+        val = (float) ADSManager.channelData[Ichan]; //get the stored value for this sample
+        val = stopDC_filter.process(val,Ichan);    //apply DC-blocking filter
+        break;
+      case 2:
+        //do fixed point, 1st order running ave
+        val_int = ADSManager.channelData[Ichan]; //get the stored value for this sample
+        //runningAve[Ichan]=( ((512-1)*(runningAve[Ichan]>>2)) + (val_int>>2) )>>7;  // fs/0.5Hz = ~512 points..9 bits
+        //runningAve[Ichan]=( ((256-1)*(runningAve[Ichan]>>2)) + (val_int>>2) )>>6;  // fs/1.0Hz = ~256 points...8 bits
+        runningAve[Ichan]=( ((128-1)*(runningAve[Ichan]>>1)) + (val_int>>1) )>>6;  // fs/2.0Hz = ~128 points...7 bits
+        val = (float)(val_int - runningAve[Ichan]);  //remove the DC
+        break;
+//      case 3:
+//        val_in_down9 = ADSManager.channelData[Ichan] >> 9; //get the stored value for this sample...bring 24-bit value down to 16-bit
+//        val_out = (val_in_down9 * a0  + (z1[Ichan]>>9)) >> (16-9);  //8bits were already removed...results in 24-bit value
+//        val_out_down9 = val_out >> 9;  //remove eight bits to go from 24-bit down to 16 bit
+//        z1[Ichan] = (val_in_down9 * a1 + (z2[Ichan] >> 9) - b1 * val_out_down9  ) >> (16-9);  //8-bits were pre-removed..end in 24 bit number
+//        z2[Ichan] = (val_in_down9 * a2  - b2 * val_out_down9) >> (16-9); //8-bits were pre-removed...end in 24-bit number
+//        val = (float)val_out;
+//        break;
+    }
+    val = notch_filter1.process(val,Ichan);     //apply 60Hz notch filter
+    val = notch_filter2.process(val,Ichan);     //apply it again
+    ADSManager.channelData[Ichan] = (long) val;  //save the value back into the main data-holding object
+  }
+  return 0;
+}
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Mon Mar 23 19:22:04 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/a9913a65894f
\ No newline at end of file