adding nRF8001 to an existing Arduino UNO project

Dear forum members!

After many hours of suffering I decided to post my problem on this forum.
I'm working on an existing project, that is an Arduino based metal detector. The source code is here:

I tried to connect my Arduino UNO to a phone app through nRF8001 bluetooth module following this tutorial:

The example tutorial app works just fine, I could connect to my phone without problem with it, but with this github code above I couldn't.
In the end of the Arduino code it sends a lot of things on the serial port. I only need the "Ferrous" or "Non Ferrous" part to be sent through bluetooth nothing else.
Can someone help me with merging the bluetooth codes with the metal detector's code? I would highly appreciate any help!

Regards,
TH

Please do not cross-post. Other thread removed.

Post your code here so we can get at it easily.
And please use the code button </>so your code looks like thisand is easy to copy to a text editor

Also post a link to the datasheet for the nRF8001.

...R

The github version is to long to be pasted here, but here is a previous version.

The metal detector's code:

// Induction balance metal detector

// We run the CPU at 16MHz and the ADC clock at 1MHz. ADC resolution is reduced to 8 bits at this speed.

// Timer 1 is used to divide the system clock by 256 to produce a 62.5kHz square wave. This is used to drive timer 0 and also to trigger ADC conversions.
// Timer 0 is used to divide the output of timer 1 by 8, giving a 7812.5Hz signal for driving the transmit coil.
// This gives us 16 ADC clock cycles for each ADC conversion (it actually takes 13.5 cycles), and we take 8 samples per cycle of the coil drive voltage.
// Timer 2 is used to generate a tone for the earpiece or headset.

// Wiring:
// Connect digital pin 4 (alias T0) to digital pin 9
// Connect digital pin 5 through resistor to primary coil and tuning capacitor
// Connect output from receive amplifier to analog pin 0. Output of receive amplifier should be biased to about half of the analog reference.
// When using USB power, change analog reference to the 3.3V pin, because there is too much noise on the +5V rail to get good sensitivity.

#define TIMER1_TOP  (255)

// Digital pin definitions
// Digital pin 0 not used, however if we are using the serial port for debugging then it's serial input
const int debugTxPin = 1;        // transmit pin reserved for debugging
const int encoderButtonPin = 2;  // encoder button, also IN0 for waking up from sleep mode
const int earpiecePin = 3;       // earpiece, aka OCR2B for tone generation
const int T0InputPin = 4;
const int coilDrivePin = 5;
const int LcdRsPin = 6;
const int LcdEnPin = 7;
const int LcdPowerPin = 8;       // LCD power and backlight enable
const int T0OutputPin = 9;
const int lcdD0Pin = 10;
const int lcdD1Pin = 11;         // pins 11-13 also used for ICSP
const int LcdD2Pin = 12;
const int LcdD3Pin = 13;

// Analog pin definitions
const int receiverInputPin = 0;
const int encoderAPin = A1;
const int encoderBpin = A2;
// Analog pins 3-5 not used

// Variables used only by the ISR
int16_t bins[4];                 // bins used to accumulate ADC readings, one for each of the 4 phases
uint16_t numSamples = 0;
const uint16_t numSamplesToAverage = 1024;

// Variables used by the ISR and outside it
volatile int16_t averages[4];    // when we've accumulated enough readings in the bins, the ISR copies them to here and starts again
volatile uint16_t ticks = 0;     // system tick counter for timekeeping
volatile bool sampleReady = false;  // indicates that the averages array has been updated

// Variables used only outside the ISR
int16_t calib[4];                // values (set during calibration) that we subtract from the averages

volatile uint8_t lastctr, lastMiss;
volatile uint16_t misses = 0;

const double halfRoot2 = sqrt(0.5);
const double quarterPi = 3.1415927/4.0;
const double radiansToDegrees = 180.0/3.1415927;

void setup()
{
 pinMode(encoderButtonPin, INPUT_PULLUP);  
 digitalWrite(T0OutputPin, LOW);
 pinMode(T0OutputPin, OUTPUT);       // pulse pin from timer 1 used to feed timer 0
 digitalWrite(coilDrivePin, LOW);
 pinMode(coilDrivePin, OUTPUT);      // timer 0 output, square wave to drive transmit coil
 
 cli();
 // Set up timer 1.
 // Prescaler = 1, phase correct PWM mode, TOP = ICR1A
 TCCR1A = (1 << COM1A1) | (1 << WGM11);
 TCCR1B = (1 << WGM12) | (1 << WGM13) | (1 << CS10);    // CTC mode, prescaler = 1
 TCCR1C = 0;
 OCR1AH = (TIMER1_TOP/2 >> 8);
 OCR1AL = (TIMER1_TOP/2 & 0xFF);
 ICR1H = (TIMER1_TOP >> 8);
 ICR1L = (TIMER1_TOP & 0xFF);
 TCNT1H = 0;
 TCNT1L = 0;
 TIFR1 = 0x07;      // clear any pending interrupt
 TIMSK1 = (1 << TOIE1);

 // Set up timer 0
 // Clock source = T0, fast PWM mode, TOP (OCR0A) = 7, PWM output on OC0B
 TIMSK0 = 0;        // disable interrupt (was enabled by Arduino core)
 TIFR0 = 0x07;      // clear any pending interrupt
 TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);
 TCCR0B = (1 << CS00) | (1 << CS01) | (1 << CS02) | (1 << WGM02);
 OCR0A = 7;
 OCR0B = 3;
 TCNT0 = 0;
 
 // Set up ADC to trigger on timer 1 overflow, read channel 0
 ADMUX = /*(1 << REFS0) | */ (1 << ADLAR);    // Use Avcc as voltage reference, read channel 0, left-adjust result
 ADCSRB = (1 << ADTS2) | (1 << ADTS1);
 ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIE) | (1 << ADPS2);  // enable adc, free running, enable interrupt, prescaler = 16
 DIDR0 = 1;
 sei(); 
 
 Serial.begin(19200);
}

ISR(TIMER1_OVF_vect)
{
 ++ticks;
}

// Interrupt service routine for ADC conversion complete
ISR(ADC_vect) 
{
 int16_t val = (int16_t)(uint16_t)ADCH;    // only need to read most significant 8 bits
 uint8_t ctr = TCNT0;
 if (ctr != ((lastctr + 1) & 7))
 {
   ++misses;
   lastMiss = ctr;
 }
 lastctr = ctr;
 int16_t *p = &bins[ctr & 3];
 if (ctr < 4)
 {
   *p += (val);
   if (*p > 15000) *p = 15000;
 }
 else
 {
   *p -= val;
   if (*p < -15000) *p = -15000;
 } 
 if (ctr == 7)
 {
   ++numSamples;
   if (numSamples == numSamplesToAverage)
   {
     numSamples = 0;
     memcpy((void*)averages, bins, sizeof(averages));
     memset(bins, 0, sizeof(bins));
     sampleReady = true;
   }
 }
}

void loop()
{
 sampleReady = false;
 while (!sampleReady) {}
 uint16_t oldTicks = ticks;
 
 if (digitalRead(encoderButtonPin) == LOW)
 {
   for (int i = 0; i < 4; ++i)
   {
     calib[i] = averages[i];
   }
   Serial.print("Calibrated: ");
   for (int i = 0; i < 4; ++i)
   {
     Serial.write(' ');
     Serial.print(calib[i]);
   }
   Serial.println();
 }
 else
 {  
   for (int i = 0; i < 4; ++i)
   {
     averages[i] -= calib[i];
   }
   const double f = 200.0;
   double bin0 = (averages[0] + halfRoot2 * (averages[1] - averages[3]))/f;
   double bin1 = (averages[1] + halfRoot2 * (averages[0] + averages[2]))/f;
   double bin2 = (averages[2] + halfRoot2 * (averages[1] + averages[3]))/f;
   double bin3 = (averages[3] + halfRoot2 * (averages[2] - averages[0]))/f;
   
   double amp1 = sqrt((bin0 * bin0) + (bin2 * bin2));
   double amp2 = sqrt((bin1 * bin1) + (bin3 * bin3));
   double ampAverage = (amp1 + amp2)/2.0;
   
   double phase1 = atan2(bin2, bin0) * radiansToDegrees - 45;
   double phase2 = atan2(bin3, bin1) * radiansToDegrees;
 
   if (phase1 > phase2)
   {
     double temp = phase1;
     phase1 = phase2;
     phase2 = temp;
   }
   
   double phaseAverage = (phase1 + phase2)/2.0;
   if (phase2 - phase1 > 180.0)
   { 
     if (phaseAverage < 0.0)
     {
       phaseAverage += 180.0;
     }
     else
     {
       phaseAverage -= 180.0;
     }
   }
                                                         
   if (ampAverage >= threshold)
   {
   // When held in line with the centre of the coil:
   // - non-ferrous metals give a negative phase shift, e.g. -90deg for thick copper or aluminium, a copper olive, -30deg for thin alumimium.
   // Ferrous metals give zero phase shift or a small positive phase shift.
   // So we'll say that anything with a phase shift below -20deg is non-ferrous.
       if (phaseAverage < -20.0)
       {
           Serial.write("Non ferrous");  
       }
       else
       {
           Serial.write("Ferrous"); 
       }
   
   Serial.println();
   //misses = 0;
 }
 while (ticks - oldTicks < 16000)
 {
 }
}

The Adafruit Bluetooth module's example code:

// This version uses the internal data queing so you can treat it like Serial (kinda)!
 
#include <SPI.h>
#include "Adafruit_BLE_UART.h"
 
// Connect CLK/MISO/MOSI to hardware SPI
// e.g. On UNO & compatible: CLK = 13, MISO = 12, MOSI = 11
#define ADAFRUITBLE_REQ 10
#define ADAFRUITBLE_RDY 2     // This should be an interrupt pin, on Uno thats #2 or #3
#define ADAFRUITBLE_RST 9
 
Adafruit_BLE_UART BTLEserial = Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST);
/**************************************************************************/
/*!
    Configure the Arduino and start advertising with the radio
*/
/**************************************************************************/
void setup(void)
{ 
  Serial.begin(9600);
  Serial.println(F("Adafruit Bluefruit Low Energy nRF8001 Print echo demo"));
 
  BTLEserial.begin();
}
 
/**************************************************************************/
/*!
    Constantly checks for new events on the nRF8001
*/
/**************************************************************************/
aci_evt_opcode_t laststatus = ACI_EVT_DISCONNECTED;
 
void loop()
{
  // Tell the nRF8001 to do whatever it should be working on.
  BTLEserial.pollACI();
  
  // Ask what is our current status
  aci_evt_opcode_t status = BTLEserial.getState();
  // If the status changed....
  if (status != laststatus) {
    // print it out!
    if (status == ACI_EVT_DEVICE_STARTED) {
        Serial.println(F("* Advertising started"));
    }
    if (status == ACI_EVT_CONNECTED) {
        Serial.println(F("* Connected!"));
    }
    if (status == ACI_EVT_DISCONNECTED) {
        Serial.println(F("* Disconnected or advertising timed out"));
    }
    // OK set the last status change to this one
    laststatus = status;
  }
  
  if (status == ACI_EVT_CONNECTED) {
    // Lets see if there's any data for us!
    if (BTLEserial.available()) {
      Serial.print("* "); Serial.print(BTLEserial.available()); Serial.println(F(" bytes available from BTLE"));
    }
    // OK while we still have something to read, get a character and print it out
    while (BTLEserial.available()) {
      char c = BTLEserial.read();
      Serial.print(c);
    }
    
    // Next up, see if we have any data to get from the Serial console
 
    if (Serial.available()) {
      // Read a line from Serial
      Serial.setTimeout(100); // 100 millisecond timeout
      String s = Serial.readString();
 
      // We need to convert the line to bytes, no more than 20 at this time
      uint8_t sendbuffer[20];
      s.getBytes(sendbuffer, 20);
      char sendbuffersize = min(20, s.length());
      
      Serial.print(F("\n* Sending -> \"")); Serial.print((char *)sendbuffer); Serial.println("\"");
      
      // write the data
      BTLEserial.write(sendbuffer, sendbuffersize);
    }
  }
}

So I need to merge the two code somehow, so when it starts it prints out on the normal serial that the bluetooth is advertising. When I connect by phone to it, it would start the metal detecting stuff. But the metal detector is written by someone else, and its so complicated, I cannot understand it properly to make these changes..
Please help me!

I merged the codes together:

http://pastebin.com/eQAR6h6V

This should work like this:

  • it writes out: Advertising
  • Than I connect to it with my phone, than it should write: connected
  • after connection it should start the metal detecting stuff
  • finally when I find something it should write back to my phone what I found

but this is not working, it doesn't even goes in to the loop(), but stays in that function before loop..

All that stuff with registers is too time-consuming to figure out.

We get 62500 ticks/second

If your ISR is called that often there won't be much time for anything else. Why is that frequency necessary?

Can you describe in simple English (not code) how the program is intended to work?

My guess (and it is no more than that) is that you want to simplify the BLE code and simplify the other code. Don't assume that example code is the best way to do things. Often the authors make no effort to accommodate other activities.

When your program is to long to include in a post just add the .ino file as an attachment.

...R

// Induction balance metal detector

// We run the CPU at 16MHz and the ADC clock at 1MHz. ADC resolution is reduced to 8 bits at this speed.

// Timer 1 is used to divide the system clock by 256 to produce a 62.5kHz square wave. This is used to drive timer 0 and also to trigger ADC conversions.
// Timer 0 is used to divide the output of timer 1 by 8, giving a 7812.5Hz signal for driving the transmit coil.
// This gives us 16 ADC clock cycles for each ADC conversion (it actually takes 13.5 cycles), and we take 8 samples per cycle of the coil drive voltage.

I can't really explain it in detail either, because I can't figure out the register stuff.. The 62,5kHz is for the ADC that samples the signal of the coils.

Yes, to simplify this would be awesome..but I don't know how to do it. I wrote you a private message.