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 :)

Tuesday, October 15, 2013

Messing around with my new (free) Surface RT

So I recently received a Surface RT tablet, which was free courtesy of Microsof's slow service.  I have to say that at first I liked the device, then I hated the device, and then I loved the device.  So my experience is a roller coaster.  However, one great thing about this tablet is that it runs a full blown version of Windows.  Before you roll your eyes, it means that I can run a command prompt and write shell scripts on this little gadget.  That is pretty cool for a person like me.

So here are some things that I have done so far:

My Nokia 928 has an app for tethering (TetherX).

Tether-X for WP8 download: http://bit.ly/tether

It works by connecting to a hosted network created on the target pc and then starting a proxy server.  The target pc then uses this proxy server to connect to the internet via the phone.  Clever.

Quick setup video: http://bit.ly/tetheringvideo

However, this requires some rather cumbersome command line foo and internet explorer settings changes to work.  So what I did was to write a couple of batch files on my surface for starting the hosted network, then waiting for the phone to connect (I know my phone's MAC address) and setting IE's proxy settings to the phone's IP automatically.  This way all I have to do is connect my phone, start proxy server on it, and launch IE on my Surface RT.

My second project was to be able to share files via WiFi from the Surface RT to my laptop.  To do this, I again created a hosted network on my RT (even though you cannot do it from the network settings page, netsh works just fine via command line).  I then enabled sharing on my surface and voila!  My laptop can now connect to the network and share files via the shared directories on the Surface RT.

After this process, I am totally sure why Surface RT is not doing well with consumers.  Configuring it is hard and its potential is really only apparent to folks that can handle the command line.  However, for folks like me, this thing is FUN.

Naturally, here are my scripts:

tether.bat
@echo off
netsh wlan set hosted mode=allow ssid=tetherx key=mypasswordgoeshere

netsh wlan start hosted
set IP=null
set MAC=re-mo-ve-d1-23-45
set PORT=8080

REM for /f "tokens=3" %%a in ('reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters /v ScopeAddress') do set host=%%a
echo | set /p dummy=Waiting for connection
:while
for /f %%a in ('arp -a ^| find "%MAC%"') do set IP=%%a
if %IP%==null (
  echo | set /p dummy=.
  ping 127.0.0.1 > NUL
  goto :while
)
echo !
echo Connected %IP%
ping 127.0.0.1 > NUL

reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyServer /t REG_SZ /d %IP%:%PORT%
reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v MigrateProxy /t REG_DWORD /d 0x1
reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyHttp1.1 /t REG_DWORD /d 0x0
reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyEnable /t REG_DWORD /d 0x1
reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyOverride /t REG_SZ /d "<local>"


tetherx_off.bat
@echo off
netsh wlan stop hosted
reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /f /v ProxyEnable /t REG_DWORD /d 0x0


hosted.bat
@echo off
netsh wlan set hosted mode=allow ssid=MYSSID key=mypasswordgoeshere
netsh wlan start hosted


hostedstop.bat
@echo off
netsh wlan stop hosted

Thursday, March 7, 2013

Two XBee Modules Communicate at High Data Rates with a Single Receiver

I now made two head mounted XBee modules with sniff and accelerometer monitoring for a total of 4 ADC channels each.  Each module alone sends a sample from each channel at 1000 / 4 = 250 Hz.  Each 4 sample packet is 16 bytes in length so the XBee is sending 16*250 = 4000 bytes per second or 32 Kbps.

With two modules, I was not sure if the receiving XBee was going to be able to handle twice the data (though the stated RF data rate is 250 Kbps).  A further concern was that my receiving XBee is communicating with an Arduino at 115.2 Kbps so with data from a single remote XBee, we are already one third of the way there.

Surprisingly, everything worked together immediately after a slight tweak of the Arduino code (below).

I can now have two animals with the magnetically connected sensors running around while I monitor sniffing and movement.  The total data rate into the receiving XBee is now 64 Kbps.  In principle I should only be limited by the serial port speed.

Each XBee has a further 2 on-board ADCs which I am planning to use soon.

Next: Adding an on-board amplifier for recording bio-potentials (EMG, LFP, etc).

Two XBee sensor modules and one receiver module hooked up to an Arduino which outputs a total of 8 DAC channels.
Here is the Arduino code:


#include <Wire.h>

#define BUFFER_LEN 64

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

// source addresses of two XBee modules
#define SRC1            0x4321
#define SRC2            0x4322

#define PACKET_LEN      0x10
#define N_SAMPLES       0x01

#define MCP4725_ID      0b01100000  

int DAC_BUS_PINS[] = {13,12,11,9,8,7,6,5};
int N_DAC = 4;

int i, j;

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

int dac_off = 0;

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(115200);
  Wire.begin();
  
  
  for (i=0;i<(N_DAC*2);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, addr;
  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);  
               
        addr = (int)word(buffer[1],buffer[2]);
        (addr==SRC2) ? dac_off = 4 : dac_off = 0;
        
       
        //      [ 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+dac_off],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+dac_off],LOW); // null DAC address to [0b1100010]
        } 
        // END DAC Communication  
      }          
    }
  }
  
}

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  
      }          
    }
  }
  
}

Wireless Movement & Sniffing Monitor

Using an XBee, Arduino, and some sensors (accelerometer, pressure sensor), we put together this wireless movement/sniffing monitor.  The device is powered by a 3.7V rechargeable Lithium Ion coin cell.  The small (4cm x 4cm) board connects via a magnet so it is easy to put on and off.  Optional LEDs can be used for tracking.  I am using the native ADCs on the XBee for passing signals.

In the next post I will put the Arduino code and circuit for receiving the signal from the sensor board.

The accelerometer is Analog Devices ADXL335: http://www.analog.com/static/imported-files/data_sheets/ADXL335.pdf
The amplifier for pressure sensor is Analog Devices AD627: http://www.analog.com/static/imported-files/data_sheets/AD627.pdf
The pressure sensor is Honeywell 24PCAFA6G: http://sccatalog.honeywell.com/pdbdownload/images/24pc.series.chart.5.pdf

The magnetic clip is made so as to connect the pressure sensor to a cannula on the other side of the connector and to prevent arbitrary rotation.

Here is a video of the magnetic clip:


Here are pictures of the device:
Front of board with XBee removed.  Pressure sensor and magnetic attachment on bottom.

Back of board has battery clip.
Front of board with XBee.


Magnetic attachment around pressure sensor and mating piece (right).

Here is the link to the project files and some additional information below:
https://docs.google.com/folder/d/0Byu6zHhzDPqVQlEtMFBaeHRZUlE/edit?usp=sharing
The PCB Layout that I used.

The circuit schematic.


Monday, January 21, 2013

Sending wireless signals with XBee & Arduino

We recently needed to make a system by which we can send sensor data from a freely moving animal wirelessly.  One simple way to do this is by using XBee modules from Digi.  These modules are fairly small (about the size of a quarter) and are incredibly easy to use in a new project.  We received a pair from SparkFun to play with.

Here is the information on XBee:
Product: https://www.sparkfun.com/products/11215 ($22.95)
Datasheet: http://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-Datasheet.pdf

We also got accessories:
XBee Shield: https://www.sparkfun.com/products/10854 ($24.95)
XBee Explorer Dongle: https://www.sparkfun.com/products/9819 ($24.95)

Also, I downloaded the X-CTU software from Digi: http://ftp1.digi.com/support/documentation/90001003_A.pdf

My plan was to use the on-board AD on the XBee to sample an analog signal (max sampling rate 1 kHz) and to send it to a receiving XBee and then to Arduino (an old Demilanove) for processing and output using an I2C DAC.

For DAC, I got MCP4725 breakout from Sparkfun:
https://www.sparkfun.com/products/8736 ($4.95)

Overall, this project cost: $100.75 not counting the Arduino, which I already had on hand.

First, I configured the two XBee modules to talk to each other using X-CTU and XBee Explorer Dongle.

Transmit Module Settings: 
DL = 1234  //Destination Address Low
MY = 4321 //Source Address
BD = 7 //Interface Data Rate: 115200
D0 = 2 //Digital input 0 is ADC
IT = 4 //Samples before transmitting = 4
IR = 1 //Sampling rate = 1 (1kHz)

Receive Module Settings:
DL = 4321 //Destination Address Low
MY = 1234 //Source Address
BD = 7 //Interface Data Rate: 115200
AP = 1 // API Enable
IA = 4321 // I/O Input Address

Next, I put the Transmit module on the dongle on a breadboard hooked up to a signal source (in this case a pressure sensor).  I put the Receive module on the Arduino Shield and also hooked up the DAC on a separate breadboard.  The setup looks like so:

Transmit Module with pressure sensor input.
Receive Module with Arduino and DAC.

To communicate with I2C, I am using the Wire library for Arduino.  To read packets from the XBee, I wrote a really simple little script:



#include <Wire.h>

#define BUFFER_LEN 64

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

#define PACKET_LEN      0xA //0x10
#define N_SAMPLES       0x01

#define MCP4725_ID      0b01100000  

int i;

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 9600 bits per second:
  Serial.begin(115200);
  Wire.begin();
  
  for (i=0;i<10;i++)
    pinMode(i+2,OUTPUT);
  
  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;
  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);  
        
        // add 4 samples together
        value = 0;
        for (i=0;i<N_SAMPLES;i++)
          value+=word(buffer[(i*2)+8],buffer[(i*2)+9]);

        adc_val = value * (4 / N_SAMPLES);
        
        // DAC Communication
        Wire.beginTransmission(96);
        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();
        // END DAC Communication  
      }          
    }
  }
  
}
The Arduino is plenty fast to handle all the data from the XBee.  However, I was not pleased to see that the real-world transmission rate for the XBee capped out at 1 packet every 5 msec or 200 Hz.  This means that if you are sampling and transmitting as fast as possible, you take only one sample every 5 msec in the end.  This leads to a minimum 5msec lag in the system.  If you acquire more samples as in my case (4).  You get a 10 msec lag and a sampling rate of 100 Hz.

Averaging 4 samples let's me effectively improve the ADC resolution to 12 bits fro the 10 bit native on the XBee.

It works overall.  Here is a 10 Hz sine curve passed at 4 samples per packet compared to the original:


If I do not do any averaging and just send one sample at maximum settings data looks like:


Feeding this signal into a proper ADC like one from National Instruments sampling at 1 kHz, it should be fine to resolve and time-stamp individual samples.

NOTE: Reading the data using Arduino and then passing it to DAC is better than filtering the native PWM of the XBee.  This is because filters that can cut out the PWM frequency of the XBee (which runs at 15.6 kHz) will yield significant amount of distortion for signals near the cutoff.  For example, Digi suggests using the following RC combination to filter: 850 Ohm, 1 uF.  Fc = 187 Hz.  However, using this filter with signals at 50 Hz, you will get a lot of phase distortion.  This does not happen with a DAC.  Further, the XBee actually receives the data as digital values.  It makes no sense to convert those to PWM unless you absolutely can't afford to have the Arduino/DAC.

In conclusion, however, for faster signals, we will need a different radio.  Perhaps a bluetooth one.