Efficiently Send Data Across

Hi there,

So my goal is very straight forward, I want to send the reading of all 12 pressure sensors as quickly as possible to my processing visualisation. My target sample rate is 40ms.

Each sensor is connected to a MCP3008 so I can access each reading like so:

#include <MCP3008.h>

int val = chipOne.readADC(0); // read Chanel 0 from MCP3008 chipOne

int val2 = chipTwo.readADC(0); // read Chanel 0 from MCP3008 chipTwo

The connection is made via a HC-05 bluetooth connection.

So my question is, what is the most efficient way to parse this data (perhaps binary?) for sending and how should it be read?

Many thanks,
Charles

This thread is serial input basics, but the concepts work both ways in terms of making up data packets and parsing the received packets on the Processing side.

Serial Input Basics

An easy way is to use sprintf() to build a comma separated value character array with start and end markers (data packet) to send and strtok() on the processing side to separate out the data values.

If you using digitalWrite for ADC CS pin then you will not getting the Sampling Speed that you want

read this

/*
  
  A sketch to control the 10-Bit, 8-channel ADC MCP3008 on the Rheingold Heavy
  I2C and SPI Education Shield at speeds necessary to sample an audio frequency signal.
  This code specifically uses PORT commands to toggle the ADC chip select pin, instead
  of using digitalWrite();
  
  The code supposes the use of the Education Shield, but if you're using a breakout
  board, connect the CS pin to Digital 4, and the SPI pins in their usual locations.
  
  Website:   http://www.rheingoldheavy.com/mcp3008-tutorial-05-sampling-audio-frequency-signals-02
  Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf

*/


#include <SPI.h>                         // Include the SPI library

SPISettings MCP3008(2000000, MSBFIRST, SPI_MODE0);

const int  CS_MCP3008       = 4;          // ADC Chip Select
const byte adc_single_ch0   = (0x08);     // ADC Channel 0
const byte adc_single_ch1   = (0x09);     // ADC Channel 1
const byte adc_single_ch2   = (0x0A);     // ADC Channel 2
const byte adc_single_ch3   = (0x0B);     // ADC Channel 3
const byte adc_single_ch4   = (0x0C);     // ADC Channel 4
const byte adc_single_ch5   = (0x0D);     // ADC Channel 5
const byte adc_single_ch6   = (0x0E);     // ADC Channel 6
const byte adc_single_ch7   = (0x0F);     // ADC Channel 7

void setup()
{

  SPI.begin     ();
  Serial.begin  (9600);
  pinMode       (CS_MCP3008, OUTPUT);
  digitalWrite  (CS_MCP3008, LOW);        // Cycle the ADC CS pin as per datasheet
  digitalWrite  (CS_MCP3008, HIGH);

  delay(100);

  int adc_reading[500];

  SPI.beginTransaction (MCP3008);
  for (int i = 0; i < 500; i++) {
    adc_reading [i] = adc_single_channel_read (adc_single_ch7);
  }
  SPI.endTransaction   ();
  
  for (int i = 0; i < 500; i++) {
    Serial.println (adc_reading[i]);
  }

}



void loop()
{

}


int adc_single_channel_read(byte readAddress)
{

  byte dataMSB =    0;
  byte dataLSB =    0;
  byte JUNK    = 0x00;

  //digitalWrite         (CS_MCP3008, LOW);
  PORTD = PORTD & 0xEF;
  SPI.transfer         (0x01);                                 // Start Bit
  dataMSB =            SPI.transfer(readAddress << 4) & 0x03;  // Send readAddress and receive MSB data, masked to two bits
  dataLSB =            SPI.transfer(JUNK);                     // Push junk data and get LSB byte return
  PORTD = PORTD | 0x10;
  //digitalWrite         (CS_MCP3008, HIGH);

  return               dataMSB << 8 | dataLSB;

}

I'm using HC-05 and HC-06 modules for a remote control application and I couldn't get the latency (one way) below 30ms stable.

Sometimes it's as low as 3ms for a couple of 100 packets, then again 30ms most of the time.

I couldn't figure out what's wrong so far, so please keep us (me) updated :D

lg, couka

@groundfungus Very nice post, unfortunately I’m trying to do the opposite and it’s not so obvious how it would translate

@BillHo Thanks for sharing, fortunately I’m just reading values from the MCP3008 so I won’t be using digitalWrite

@couka 30ms is better than my goal :slight_smile: Would you mind sharing how you transferred the data?

If you are using MCP3008 Library like this one then you are using digitalWrite

/*
  MCP3008.cpp - Library for communicating with MCP3008 Analog to digital converter.
  Created by Uros Petrevski, Nodesign.net 2013
  Released into the public domain.
  
  ported from Python code originaly written by Adafruit learning system for rPI :
  http://learn.adafruit.com/send-raspberry-pi-data-to-cosm/python-script
*/

#include "Arduino.h"
#include "MCP3008.h"

MCP3008::MCP3008(int clockpin, int mosipin, int misopin, int cspin) {
    
    // define SPI outputs and inputs for bitbanging
    
    _cspin = cspin;
    _clockpin = clockpin;
    _mosipin = mosipin;
    _misopin = misopin;
    
    pinMode(_cspin, OUTPUT);
    pinMode(_clockpin, OUTPUT);
    pinMode(_mosipin, OUTPUT);
    pinMode(_misopin, INPUT);
    
}

// read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
int MCP3008::readADC(int adcnum) {

  if ((adcnum > 7) || (adcnum < 0)) return -1; // Wrong adc address return -1

  // algo
  digitalWrite(_cspin, HIGH);

  digitalWrite(_clockpin, LOW); //  # start clock low
  digitalWrite(_cspin, LOW); //     # bring CS low

  int commandout = adcnum;
  commandout |= 0x18; //  # start bit + single-ended bit
  commandout <<= 3; //    # we only need to send 5 bits here
 
  for (int i=0; i<5; i++) {
    if (commandout & 0x80) 
      digitalWrite(_mosipin, HIGH);
    else   
      digitalWrite(_mosipin, LOW);
      
    commandout <<= 1;
    digitalWrite(_clockpin, HIGH);
    digitalWrite(_clockpin, LOW);

  }

  int adcout = 0;
  // read in one empty bit, one null bit and 10 ADC bits
  for (int i=0; i<12; i++) {
    digitalWrite(_clockpin, HIGH);
    digitalWrite(_clockpin, LOW);
    adcout <<= 1;
    if (digitalRead(_misopin))
      adcout |= 0x1;
  } 
  digitalWrite(_cspin, HIGH);

  adcout >>= 1; //      # first bit is 'null' so drop it
  return adcout;
}

In addition to Serial Input Basics I also wrote a short Binary Data demo.

Sending data in human readable form will make debugging much easier. I would only use binary data if I had no other choice.

An Arduino can send at 500,000 baud and if you are only sending 12 x 4 digit numbers (0-1023) that should only take less that 2 millisecs (if my maths is correct).

Edit to add ... The previous paragraph may be very optimistic. I had forgotten there is a HC05 and that it may be connected to SoftwareSerial which probably only works well at 9600 or 19200 baud. I don't know what is the max baud rate for a HC05.

Apologies for any confusion.

...R

Robin2: An Arduino can send at 500,000 baud and if you are only sending 12 x 4 digit numbers (0-1023) that should only take less that 2 millisecs (if my maths is correct).

That requires to use the hardware serial port, so it would be a good idea to use an arduino which has more than one of them. Debugging is a pain in the ass when you can't use the Serial Monitor.

lg, couka

couka: That requires to use the hardware serial port,

Thanks for pointing that out. I have amended my earlier Post.

...R

@BillHo Yes, i completely overlooked that you're right, it's not good news. I'm wondering whether it's worth trying to avoid it at the expense of complicating everything.

@Robin2 Very nice post btw thanks again. I guess I'll take the easier route as you suggested and send everything from Arduino as integers. However I still have a few questions, if on the receiving side I want my data to look like this:

123 23 234 255 123 45 78 23 67 89 23 45 0 255

On the sending side (Arduino) how should the data be separated? Do I need to indicate a start and end value?

I'm trying to avoid converting everything to a string as one in five readings in processing gives me a null.

@couka I'm not actually using the tx,rx pins but rather 10 & 11 perhaps I can use them for debugging?

CharlesDesign:
if on the receiving side I want my data to look like this:

123 23 234 255 123 45 78 23 67 89 23 45 0 255

On the sending side (Arduino) how should the data be separated?
Do I need to indicate a start and end value?

I would use the system in the 3rd example in Serial Input Basics. It is as valid for receiving data on a PC as on an Arduino. The code to do that is simple

Serial.print('<');
Serial.print(val1);
Serial.print(',');
Serial.print(val2);
//etc
Serial.println('>');

If the data values are in an array it could be even simpler using a FOR loop to iterate through the array.

as one in five readings in processing gives me a null

If you really mean “null” (rather than a zero value) then your code will need to check for that and send a suitable character - maybe ‘0’ is adequate, or perhaps ‘N’ if you want the PC to know there was a null value rather than a reading of 0.

I guess at the back of all this is the need to develop the PC program and the Arduino program in harmony with each other. And I always try to do all the heavy lifting in my PC programs and keep the Arduino stuff as simple as possible.

…R

I’m trying to translate example three from your guide to processing, there’s just one part I don’t understand.

void recvWithStartEndMarkers() {

    char rc;
 
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();
              if (recvInProgress == true) {
                    if (rc != endMarker) {
                    }
              }
         }
   }

Isn’t the output of Serial.read() the entire package that we’re sending, in your example “qwertyzxcvb”?
If so how can it be compared against the endMarker?

Serial.read() just takes a single byte from the Serial input buffer.

...R