Showing posts with label arduino code. Show all posts
Showing posts with label arduino code. Show all posts

Thursday, January 2, 2014

Getting and sending analog data over bluetooth with Arduino Pro at 8 MHz

I am thinking of using the ATmega328 running at 8MHz for the microcontroller on my wireless module.  To gather neural data, I am contemplating using an Intan chip, possibly the RHD2216 which can communicate with the ATmega328 using SPI:


I will not be able to send all 16 channels (at 16 bits) at a reasonable sample rate unless I do some compression, however, I think I can send 8 channels reasonably.  As proof of principle, I set up an Arduino Pro (ATmega328, 8MHz) getting data via SPI from an MCP3208 and sending via bluetooth.

Aside: I could compress sent data by sending only changes in ADC values, which are likely far smaller than the full 16 bits.  If I could get away with sending only 8 bits, then I could send all 16 channels at a reasonable sampling rate.

In any case, I found that on the 8MHz chip, just performing SPI communication was a little slow when using digitalWrite/digitalRead.  I started out using code from http://playground.arduino.cc/Code/MCP3208, but it ran too slow.  I had make things faster.  Here is what I had to do:

  1. Use port manipulation where possible (this make the code less portable)
  2. Remove any loops (e.g. for loops for sending bits)
  3. Use digitalWriteFast and digitalReadFast available at: https://code.google.com/p/digitalwritefast/
  4. Cycle the SPI clock with a minimal delay supported by MCP3208 - in case of 8 MHz, a single asm("nop\n") call is enough
So the result is that I can read and send 8 16 bit values from the ADC at 500 Hz.  The rate-limiting step here is reading the data, but the only way to go faster is to run faster chips.  The Intan chip is capable of running at 24MHz and has 16, 16Bit differential inputs plus amplifiers.  This may or may not be overkill for our application.

Monday, December 23, 2013

Good data rate from Arduino over Bluetooth Serial

I am designing a better wireless data transmitter.  To do this, I have decided to switch from XBee to a bluetooth module.  I purchased the following:


Bluetooth Module Breakout - Roving Networks (RN-41)
https://www.sparkfun.com/products/12579

The Roving Networks module is easy to use once you figure out how to pair it with your PC.  Here is how I did it:

2.  Pair the module with your PC using the default pairing code on the Roving module "1234"
3.  Find out the mapped serial port.  On my computer this produced two ports, only one of which worked for connecting to the module.
4.  Set the appropriate baud rate.  The module comes pre-configured for 115200 baud, however, I found it helpful to set the baud to 9600 to get started by putting pin GPIO7 to VDD.
5.  Program the module (including name) using PuTTY as follows:
     a.  Ready the PuTTY connection 
     b.  Power on the module
     c.  Connect via PuTTY within the first minute and enter programming mode by sending $$$ to the module
     d.  Program the module using instructions in:  https://www.sparkfun.com/datasheets/Wireless/Bluetooth/rn-bluetooth-um.pdf

I hooked it up to my Arduino Mega powered from the 3.3V output and with the transmit pin of Serial1 connected to the receive pin of the RN-41 through a voltage divider: 

TX1 (pin 18) via 10K/20K voltage divider to TX pin on the RN-41

I also added some LEDs for status output on pins PIO5 and PIO2 which together help me understand the status of the module (PIO5 blinks 1Hz when waiting for connection and 10Hz 10Hz when in command mode and PIO2 is on solid when connection is made)

Here is what it looks like:



I then wrote a little Arduino sketch for testing how fast I could get data to the PC via bluetooth.  I am interested in sending analog values to the module, so I simulated that by reading analog channels on the Arduino (for now).  I intend to send that data as unsigned integers with 16 bit precision so what I am doing is sending over 16 ASCII characters ("ABCDEFGHIJKLMNO_").

Here is my code:

unsigned int val;
unsigned int fakeVal[8];
char *p_val;
int count;
int reportCount;
int nChans;
unsigned long t0;
char str[17] = "ABCDEFGHIJKLMNO_";

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
  count = 0;
  reportCount = 2000;
  nChans = 8;
  
  p_val = &str[0];
  //p_val = (char*)&val;
  t0 = millis();
}

void loop() {

  int i;
  for (i=0;i<nChans;i++)
  {
    val = analogRead(i); // 100 microseconds
    Serial1.write(p_val[i*2]);
    Serial1.write(p_val[i*2+1]);
    delayMicroseconds(97);
  }
  
  if (++count==reportCount) {
    Serial.print("Simulating ");
    Serial.print(nChans);
    Serial.println(" 16 bit channels");
    Serial.print("Data rate: ");
    Serial.print(1000.0*((double)reportCount)/((double)(millis()-t0)));
    Serial.println(" values per channel per sec");
    count = 0; 
    t0 = millis();
  }
}

I am using PuTTY to monitor the arriving data and things look pretty good sending 16 bytes at 500 Hz.  


The theoretical limit of an 115200 bps connection is 115200/16/8 = 900 Hz, however, I find that the connection is unstable (i.e. module spontaneously disconnects) at anything much over 600 Hz.  This may be a PuTTY problem so we'll see if I can get up to the max.

As for power requirements.  Bluetooth pulls about 50 mA of current in default power mode.
Here are my power readings:

Setting  Output (dBM)  Current Draw (mA)
                         Approximate via FLUKE Multimeter
SY,0004       4              50
SY,0001       0              43
SY,FFFC      -4              40
SY,FFF8      -8              40
SY,FFF4      -12             50-80 (choppy connection?)

This range of current draw is similar to what is described for the XBee so this thing may still play nice with the battery though I do not know if the battery will survive this thing PLUS an arduino... only one way to know :)

Sunday, February 24, 2013

Arduino I2C DAC Array Using MCP4725

The XBee in line passing mode would output signals via PWM.  However, this method is not very good because of the low PWM frequency of the XBee and the need to filter signals introducing additional phase delays on top of transmission times.

To remove this problem, I use an Arduino to receive signals from XBee and send appropriate values to an array of MCP4725 DAC chips.

The MCP4725 chips come with a set address on I2C with just one address bit selectable by the user.  To implement the DAC array, connected the selectable address bits of the DACs on the I2C to digital outputs of the Arduino.  To select one DAC for writing, the Arduino sets its address bit high.  The Arduino is programmed to write to that specific address.

Here is the datasheet for the MCP4725:
http://www.sparkfun.com/datasheets/BreakoutBoards/MCP4725.pdf

All schematic etc can be found here: https://docs.google.com/folder/d/0Byu6zHhzDPqVQlEtMFBaeHRZUlE/edit?usp=sharing

Here is my breakout board for an array of four MCP4725s and a picture of the array on top of the XBee shield from sparkfun:
The green colored board is the DAC breakout.  Blue is the XBee and red is the SparkFun XBee Shield.

The PCB Layout.

The circuit schematic.
The Arduino code is as follows:


#include <Wire.h>

#define BUFFER_LEN 64

// constants
#define START_DELIMITER 0x7E
#define API_ID          0x83

#define PACKET_LEN      0x10
#define N_SAMPLES       0x01

#define MCP4725_ID      0b01100000  

int DAC_BUS_PINS[] = {9,11,12,13};
int N_DAC = 4;

int i, j;

byte buffer[BUFFER_LEN];
byte L12[2];
byte dac_bits[2];

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 115200 bits per second:
  Serial.begin(115200);
  Wire.begin();
  
  
  for (i=0;i<N_DAC;i++)
  {
    pinMode(DAC_BUS_PINS[i],OUTPUT);
    digitalWrite(DAC_BUS_PINS[i],LOW);
  }
  
  dac_bits[0] = 0;
  dac_bits[1] = 0;
}

// the loop routine runs over and over again forever:
void loop() {
  
  int len;
  unsigned char firstByte;
    
  int value, X, Y, Z;
  word adc_val;

  if (Serial.available()) {
  
    firstByte = Serial.read();

    if (firstByte==START_DELIMITER) // start of a packet
    {
      Serial.readBytes((char*)L12,2);
      len = word(L12[0],L12[1]);

      if (len==PACKET_LEN) { // check for API packet of right length
        
        Serial.readBytes((char*)buffer,len);  
       
        //      [ sniff |   X   |   Y   |   Z   ]
        //buffer[  8, 9, 10, 11,  12,13,  14,15 ]
       
        // DAC Communication
        for (j=0;j<N_DAC;j++)
        {
          // adc value
          value=word(buffer[(j*2)+8],buffer[(j*2)+9]);
          //value=word(buffer[8],buffer[9]);
          adc_val = value*4;
   
          digitalWrite(DAC_BUS_PINS[j],HIGH); // address a DAC chip [0b1100011]
          Wire.beginTransmission(99); //99
          Wire.write(0b01000000); // write dac register command [c2 c1 c0 x x pd1 pd0 x]  for write dac c2 = 0, c1 = 1, c0 = 0
          for (i=0;i<8;i++) bitWrite(dac_bits[0],i,bitRead(adc_val,i+4)); // shift bits around for making i2c compatible
          for (i=0;i<4;i++) bitWrite(dac_bits[1],i+4,bitRead(adc_val,i));
          Wire.write(dac_bits[0]);
          Wire.write(dac_bits[1]);
          Wire.endTransmission();
          digitalWrite(DAC_BUS_PINS[j],LOW); // null DAC address to [0b1100010]
        } 
        // END DAC Communication  
      }          
    }
  }
  
}

Sunday, June 17, 2012

Fake Neuron in Arduino

I am getting ready to go ahead with electrophysiology experiments recording from the olfactory bulb.  To test my acquisition equipment and software, I created a little Arduino program that simulates a simple neuron with firing rate driven by "sniffing".  The neuron generates Poisson action potentials with the rate parameter governed by a sinusoid (sniffing).

The shield has an LED that indicates the sniffing and a speaker that indicates the spiking.  Here is the shield and program in action (turn volume to max to hear the neuron):


And here is the sketch:


/*
  Fake Neuron
  Yevgeniy Sirotin, 6/16/12
 */


#include <math.h>


unsigned long t0;
unsigned long t;
unsigned long ti;


int ts;
float r;


void setup() {
    pinMode(9, OUTPUT); 
    pinMode(7, OUTPUT);
    t0 = millis();
    
    TCCR1B = 0x01;
    
    ti = 0;
    ts = 0;
}


void loop() {
    t = millis();
    
    r = 0.5 * (1.0 + sin( (float) ((t-t0) % 2000) * 2.0 * 3.14 * 1.0 / 1000));
    analogWrite(9, (int) (255.0 * r ));
       
    if (t>=ti)
    { 
      digitalWrite(7,HIGH);
      delay(1);
      digitalWrite(7,LOW);
      
      if (r<0.1) r = 0.1;
      ti = t + (unsigned long) (-1000.0 / (100 * r) * log(random(10000) / 10000.0 ));
      
   }      
}

Saturday, May 26, 2012

8Kb is not a lot

...but it just has to do.

The Arduino with the Atmega chip has only 8Kb bytes of RAM.  In writing my Arduino-MATLAB interface I came up hard against that limit.  My interface works by allowing a user to upload a small program to the Arduino that uses a locally defined function library.  I realized something was funny when programs will too many instructions failed.  This was because when program upload size exceeded a certain value, the Arduino would crash because it could no longer allocate memory for the incoming data.  By being more careful with memory, I was able to extend the amount I could send.

Also I found some nice ways to check the amount of memory available to malloc.  In the following code, x is there just to test that this function is working properly (i.e. detecting a prior malloc usage):


void funcFreeMem(IN_FREE_MEM_FUNC *in,OUT_FREE_MEM_FUNC *out)
{
  int sz = 0; 
  
  byte* x = (byte*) malloc(in->in * sizeof(byte));
  
  byte *buf;
  while ( (buf = (byte*) malloc (sz * sizeof(byte))) != NULL ) {
    sz++; // if allocation was successful, then up the count for the next try
    free(buf); // free memory after allocating it
  }
  
  free(buf);
  free(x);
  out->mem = sz;
}

Monday, March 19, 2012

MFC Control Complete

Today I finished implementing control over my mass flow controllers from Alicat.  I now have the capacity to drive two MFCs via analog voltage or serial from our custom Arduino shield.  Everything works directly from MATLAB.

This is our Arduino-based olfactometer control module connected to two Alicat MFCs.
The output works at high frequencies with the MFCs being able to follow up to 3 Hz well.  I doubt that this performance will be maintained with long downstream tubing, but I hope to be able to do at least 1 Hz at output.

I had to work out a kink to get switching between serial and analog control to work.  The Alicat documentation says that A$$W20=16384 should enable analog set point. However, I found that this does not work.  Reading register 20 after setting analog set point from the front panel, however, showed that the correct value is actually 17408.

The Arduino will start by default in analog control mode. To switch modes:

[mode] = {0: SERIAL_OUT, 1: ANALOG_OUT}
olf.funcALIMode([mode]);

I tested the whole setup using my handy dandy flowmeter and things work very well so far.  Here is the code I used for my current test setup:


olf = OlfactometerDriver_SerialInterface('COM10'); pause(12);


% proportional control var
olf.funcALIMessage('A$$W21=500')
% differential control var
olf.funcALIMessage('A$$W22=10')


olf.funcALISin(1,1,0.5);
olf.funcALISin(0,1,0.5);


Next step: connect the MFCs to the olfactometer, determine the right parameters for sinusoids, and verify output via PID.


Saturday, March 17, 2012

Arduino Timer Configuration for 10bit Analog Output

Two things matter for PWM analog output:

1. PWM Frequency
2. PWM Resolution

The Atmega1280 comes with a couple of timers that control PWM on specific pins.  These timers are, by default, set for 8bit operation and a prescale value of 64.  That means that the timer resets every 256 ticks and each tick is 64 clock cycles.  The Atmega1280 runs at 16 MHz so, by default the timer frequency is 1/64th of that, or 250 KHz and the timer cycle is 1/256th of that, or ~977 Hz.

This is fine so long as you want fairly crude signals, but I wanted signals good to the millisecond and better bit depth.

The manual for the Atmega1280 is fairly clear:  http://www.atmel.com/Images/doc2549.pdf

PWM frequency can be increased by setting the prescale value to 1 (i.e. the 16 MHz clock).  PWM resolution can be increased by increasing the number of timer ticks in the cycle from 256 (8 bit) to 1024 (10 bit).  This produces a PWM of 16 MHz / 1 / 1024 = 15.6 KHz.  Much better!

Since I am interested in signals << 1KHz, I can simply run the PWM output through an RC circuit with a time constant of 1 msec  (e.g. 1 uF capacitor and 1K resistor).  This produces a reasonable sine function for frequencies up to ~50 Hz.

This code, which runs in setup(), applies the aforementioned settings for Timer 1, which controls pins 11 to 13 on the Arduino Mega.  To understand it, one has to dig through the manual for the descriptions of registers TCCR1A and TCCR1B.


  // set Timer1 (pins 13 and 12 and 11)
  TCCR1B = 0x09;  // set Control Register to no prescaling 
                  // WGM02 = 1


  TCCR1A = 0x03;  // set WGM01 and WGM00 to 1 (10 bit resolution)

Friday, March 16, 2012

Arduino as Analog Function Generator

I realized that outputting functions over the serial port is a little too much overhead.  However, my MFCs can be controlled by analog signals on input pin 4 in the miniDIN.  So I decided to see if the Arduino can output reasonable signals by PWM.

I generated a sin using PWM and filtered the signal using a simple RC circuit (tau = 1 msec).  It took a little fiddling with the Control Register of the appropriate timer (I had to remove prescaling) to get good output PWM frequency, but it looks pretty good.

This is my test code:


unsigned long t0;


void setup() {
    pinMode(9, OUTPUT); 
    t0 = millis();
    
    TCCR1B = 0x01;
}


void loop() {
    analogWrite(9, (int) (255.0 * 0.5 *(1.0 + sin( (float) ((millis()-t0) % 1000) * 2.0 * 3.14 * 5.0 / 1000))));    
}

UPDATE: click here for explanation of my choice of RC filter

Along the way, I found this useful blog with info about the Arduino:
http://softsolder.com
http://softsolder.com/2010/09/05/arduino-mega-1280-pwm-to-timer-assignment/
http://softsolder.com/2009/02/21/changing-the-arduino-pwm-frequency/ 

Thursday, March 15, 2012

Outputting Functions to MFC

One of the thing I want to be able to do is to output arbitrary odor waveforms with my MFCs.  As a start, I implemented a Sin function to be output to the MFC (i.e. changing the flow rate at some set frequency).  I am hoping to put my MFCs into the setup to verify that they are working properly.

What I did was put a function that gets called on each loop of the Arduino which updates the flow rate at 100 msec intervals.

void loop()                     
{
  ...
  ALI.makeSin(); // ALI is my AlicatMFC object
}


void AlicatMFC::makeSin()

{
  float flow;
  unsigned int uiflow, flow_out;
  
  if (!_on) return;
  if ((millis()-_tL)<100) return;
  
  _tL = millis();
  
  flow = _mean + _ampl / 2.0 * sin( (float) (_tL - _t0) * 2.0 * 3.1415926536 * _freq / 1000.0 );


  if (flow > 1.0) flow = 1.0;
  if (flow < 0.0) flow = 0.0;
  
  uiflow = (unsigned int) ((float) ALI_MULTIPLIER * flow);
  
  setFlow('A',uiflow,&flow_out);  
}



Tuesday, March 13, 2012

Setting Up Serial Comm with MFC via Arduino

I am now working on linking our Arduino to the Alicat MFCs.  I am connecting Serial port 3 on the arduino to the Alicat via a DB9 on our custom made Arduino shield.  As far as the software goes, I am first just forwarding strings sent to the Arduino from MATLAB on the PC to the Alicat and strings sent from the Alicat back to MATLAB.

One of the things I want to be able to do is to produce different odor concentration waveforms by manipulating the MFCs.  For example sine curves.

To do this more interesting and faster control, I will implement direct functions in the Arduino that will manipulate the MFC flow rate in real time.

Monday, March 12, 2012

Serial Port Support

Our Arduino powered valve driver makes control of solenoid valves from the PC easy.  We gave one of our valve drivers to a neighboring lab that is setting up some basic olfactometry.  They are trying to integrate it with existing software.  However, I realized that I wrote the whole library for our Arduino to specifically communicate with MATLAB through a custom object.  To make the system compatible with their software, I added simple serial port text parsing so now the Arduino can be controlled by several methods!

It should now be possible to turn on valve 0 by typing:

S valve 0 1