Show Posts
Pages: [1] 2 3 4
1  Using Arduino / Installation & Troubleshooting / Re: Mounting Arduino Ethernet Shield on: May 05, 2012, 12:02:21 am
That appears to be the Arduino Ethernet (sort of like an Uno and Ethernet Shield combined) and not just the shield...
2  Using Arduino / Project Guidance / Re: Using a DAC (MCP4911) instead of a low-pass filter - hardware or software SPI? on: April 30, 2012, 01:21:13 pm
Epilogue: On Friday I ran my controllers properly, the DAC worked absolutely beautifully. I am still serving a page stored on the microSD card, but thanks to JavaScript I don't need to refresh, so there does not appear to be any conflict with the DAC.

Thanks!  smiley-grin
3  Using Arduino / Storage / Re: Yet another data logging problem... on: April 17, 2012, 11:37:43 am
Epilogue: A couple of nights ago while I was reading about HighCharts, I discovered that it could access data in other several formats, including JSON. I then discovered that I could use the TinyWebServer library to send data in this format, obviating (love that word) the need to write to SD rapidly and having the problem of erasing old irrelevant data.

So, I am using the JSON method instead; tests have shown that it works just as I want it to!  smiley-razz

But still, thank you for your help!
+-
4  Using Arduino / Storage / Re: Yet another data logging problem... on: April 15, 2012, 12:31:46 pm
ADC_DELAY is for high impedance sensors.  If you have low impedance sources set ADC_DELAy to zero.

If ADC_DELAY is nonzero,  analogRead is called and the value is discarded to switch the ADC MUX to the channel then delay(ADC_DELAY) is called and finally analogRead() is called again to get the value.

This allows the allows the ADC sample and hold to stabilize with high impedance sensors.

Thanks for the explanation  smiley-grin
5  Using Arduino / Storage / Re: Yet another data logging problem... on: April 13, 2012, 02:13:09 pm
Quote from: fat16lib
Hello!  smiley-grin

I sort of gave up on this for a few days, but then I went through fat16lib's examples and it looks like the AnalogLogger is pretty much exactly what I'm looking for! I will try to fuse it with my code tonight. Thank you fat16lib, your library and examples are just fantastic.

Could you tell me a little more about the ADC delay in the AnalogLogger sketch, please?
6  Using Arduino / Project Guidance / Re: Using a DAC (MCP4911) instead of a low-pass filter - hardware or software SPI? on: April 10, 2012, 07:08:39 am
Hi, my chips arrived today!

I tested it on its own, and it works fine (it seems to be using 12-bit values and it doesn't quite go up to 5V, but I think that's because I'm running of USB right now).

I tried to fuse the example with my webserver code, and it sort of works... I put this in loop():
Code:
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis == 500) {
    previousMillis = currentMillis;
    analogOut.setValue_A(cnt);
    cnt += 20;
    if (cnt > 4095) {
      cnt = 0;
    }
  }

The voltage increases as expected... until I try to load my page! The voltage stays constant after that.

I'm really hoping that my final implementation works properly. The voltage will not be increasing at a constant rate, but will be decided by a controller several times a second. Hopefully loading a page will not affect this!

But if it does... is there a way to specify a different MOSI pin for my DAC? I'm guessing that's where the conflict is coming from...

Edit: I added a println() to see what was going on. When I load a page it stops changing cnt. I won't be using cnt in my final sketch so I don't think it will be a problem!  smiley-sad-blue

Edit2: It seems the webserver stops whatever I have in the if statement, I'll have to fix this...

Edit3: Everything works fine if I use >= instead of ==, which I suppose is okay, the page doesn't refresh anyway. Crisis averted! But my controllers do use a constant sampling interval...
7  Using Arduino / Storage / Re: Yet another data logging problem... on: April 09, 2012, 11:45:28 am
(back to the goal)
Can you explain why you need 1000 samples per second?
What is changing so fast you want to monitor?

maybe tell something more about the project?
Good afternoon! Firstly I'd like to correct a mistake in the original post, I meant 100 times a second, not 1000! How embarrassing...

I am controlling a motor with an Arduino, which is serving a webpage where you can choose a target speed or angle, as well as gains for a PID controller (for example). I want to display a graph on the webpage which shows the reference value and the motor output against time. I planned to use a JavaScript graph library, which can read data from CSV or .txt files.

I plan to have my controller calculate a new control signal 100 times a second (though I may have to change this if I find that the Arduino can't keep up), and originally I wanted to save the reference value and the motor output at the same time. Now that I have thought about it, I don't think the graph needs to have data points so close together, so I could try saving data (millis, reference and output) to file 10 times a second. But the Arduino can't seem to write to file this quickly, so I thought a buffer and using SDFat would help. But I am still having trouble trying to use buffers.

Thanks and sorry for the mistake!
+-
8  Using Arduino / Programming Questions / Re: SD Card logging on: April 08, 2012, 05:39:42 pm
Could you also use
Code:
if(Serial.available() >0) { Serial.println(dataString); }
?
I thought the same thing at first but I figured since Serial.available() only checks for incoming data what happens if he never sends any data from his pc even if the Arduino serial port is open? That would mean he would have to constantly send requests from the pc before the Arduino would send any logging info.

Unless I'm completely missing how Serial.available() works which is a distinct possibility.
Ah, I see.

Don't use that then, Z!  smiley-razz
9  Using Arduino / Storage / Re: Yet another data logging problem... on: April 08, 2012, 05:19:26 pm

Best performance is indeed made with buffers equal to the sector size of the SD card (512 bytes).

You say one record consists of 14 bytes, [millis, f1,f2] comma separated fields. But how do you separate records?

Consider writing pure binary: (the commas do not add anything except 2/14 slack ~14%.
4 bytes long millis
4 bytes float f1
4 bytes float f2

So every 12 bytes is one record. 512 bytes can hold 512/12 = 42 records, leaving 8 bytes .

So in your main loop() you should have a counter that writes the 512 bytes buffer to SD after 42 records.
while writing this buffer you should fill buffer 2. etc

If you think reading 12 bytes records is difficult you could consider writing 16 bytes records of which exactly 32 fit in 512 bytes.
Room for an additional sensor smiley-wink
Hello, thanks for your reply!

I should explain what the data is for. My Arduino is serving a web page, and I'd like to have a live graph that plots the two values (the two floats) with time. I intend to use a JavaScript library called DyGraphs, which can use arrays or comma separated values to display multiple series, which is why the commas are in there for now.

So will each entry to the buffer contain the string with all 3 values at that time? Or just one long array? I tried to code it, but I didn't make much progress, I can't think straight right now...

P.S. This isn't strictly relevant, but... I was concerned that the script would not be able to read as the file is being written to, but I think with the right update rate that should not be a problem. I have a feeling I will need to overwrite existing data after a soft reset (e.g. when the page is reloaded) so that the graph doesn't contain old, irrelevant data, but I will sort that out later.

To log 1000 samples per second requires a totally different technique than your sketch.

I suggest you look at example sketches in fastLoggerBeta20110802.zip here http://code.google.com/p/beta-lib/downloads/list.

Forget advice like 512 byte buffers unless you are doing raw writes to the SD.

The big problem is that a write to a file on an SD can take 200 milliseconds.  You must capture data while the SD write is in progress.  This means interrupts, probably using timers, and cleaver buffering schemes.
Thanks for your reply!

Will I be able to use interrupts if I have other things going on in my sketch? Could I remove the code related to the ADC chip?

As I will be saving the data for a graph on a webpage, I can't save the data in binary format, and I will have to store it somewhere; I assumed a file on the SD card would be the 'best' place. I'm considering increasing my interval too, 1000 times a second for a graph seems unnecessary. I tried writing some code based on the SPI EEPROM example, but it went horribly wrong!

Code:
#include <SD.h>

char buffer[512]; // For storing data
int i = 0; // count
int r = 1234; // "reference"

int delta = 10; // sampling interval, ms
long previousMillis = 0;

const int chipSelect = 4;
const int yPin = A5; // motor output, after scaling

void setup() {
  Serial.begin(115200);
  Serial.print("Initialising SD card...");
  pinMode(10, OUTPUT); // CS pin
  pinMode(yPin, INPUT);

  // see if the card is present and can be initialised:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialised.");
}

void loop() {
  File dataFile = SD.open("graphlog.txt", FILE_WRITE);
  
  unsigned long currentMillis = millis();
  
  int y = analogRead(yPin);
  
  String graphString = String(millis());
  graphString += ",";
  graphString += String(r);
  graphString += ",";
  graphString += String(y);
  
  if(currentMillis - previousMillis == delta) {
    previousMillis = currentMillis;
    for (i=0; i < 510; i++) { // 512?
      buffer[i] = graphString;
    }
  }
}

void fill_buffer() {
  for (int i=0; i < 512; i++) {
    buffer[i] = i;
  }
}

Edit: I started typing the above when there were only 2 replies!

If you do a 1000 samples per second, why add the millis() timestamp?
If evenly divided you just get consecutive numbers, if not you get multiple samples with the same timestamp (implying a zero duration between). using the micros as timestamp gives at least an - sub milli -  interval between two samples.
I haven't found a way to specify the time between data points using DyGraphs, unfortunately, so I need to add the time so the script knows what to plot! I'm starting to think a much much lower sample rate would be more reasonable... this is all much more complicated than I anticipated!  smiley-razz

For raw writes you must write 512 bytes.

For file writes there is some gain if all writes are 512 bytes and aligned on 512 byte boundaries.

For most apps this gain is not worth the effort.  My benchmark example in SdFat uses a 100 byte buffer. 

With a SanDisk 1 GB Ultra II card formatted FAT16 with the SdFormatter example I get this result:

This is a very good card for data logging.  Not because of the average write rate of 198 KB/sec but the max latency of 81396 usec.  Some cards have a max write latency of 200000 usec.

The 198 KB/sec is way faster than needed to log 1000 records per second with 14 byte records.

Once again the design problem is to overcome the occasional long write latency that is inherent in SD cards.

Even class 10 cards have this problem.  The assumption is that devices like video cameras have lots of buffer so they achieve high average write rates for very large files.  This allows occasional pauses by the card to erase large areas of flash.  You can only write to erased flash.
I just ran bench with my little 256 MB card, formatted using SDFormatter. Here are the results:
Code:
Type any character to start
Free RAM: 1043
Type is FAT16
File size 5MB
Starting write test.  Please wait up to a minute
Write 165.30 KB/sec
Maximum latency: 107708 usec, Avg Latency: 600 usec

Starting read test.  Please wait up to a minute
Read 325.54 KB/sec
Maximum latency: 2336 usec, Avg Latency: 302 usec

By file write do you mean writing to a file (such as a CSV file)? If I don't need to have a 512 byte buffer, then would it just need to be long enough to store data points for more than the length of the maximum latency?
10  Using Arduino / Programming Questions / Re: SD Card logging on: April 08, 2012, 11:30:06 am
Could you also use
Code:
if(Serial.available() >0) { Serial.println(dataString); }
?
11  Using Arduino / Storage / [Solved] Yet another data logging problem... on: April 07, 2012, 06:02:05 pm
Good evening.

I would like some guidance on logging data (writing it to a microSD card in the Ethernet Shield). I want to write data 100 times a second, so quite a lot faster than I could manage with a sketch I wrote today (attached, for reference). Each data point will be 14 bytes long, consisting of one long number (millis()), and 2 floats, separated by commas.

I have read several topics about writing to SD faster than 5 times a second. I read that the SDFat library was faster than the standard SD library, so I have downloaded the December version of SDFat. I am considering using a buffer after reading thread, but I don't know how I will write to buffer and then to the card. And I have read things about using 2 or even 3 buffers, and I am not sure if I will need that.

I will be using a Mega, so I should have enough RAM for a large array, but how big would it need to be? I read here that it was best to write in 512 bytes, so would this mean a buffer of exactly 512 bytes?

Thanks,
+-
12  Using Arduino / Project Guidance / Re: Using a DAC (MCP4911) instead of a low-pass filter - hardware or software SPI? on: April 07, 2012, 03:43:21 pm
Wow, that is a lot of code to send two bytes to a DAC.

This part
Code:
#include <SPI.h>
#include "MCP4911.h"

#define AOUT_CS 5
#define AOUT_LDAC 6
#define AOUT_CLK 7
#define AOUT_SDI 8
The SPI pins are specific hardware pins. On a '328, D13-12-11 and typically 10 for CS.
On a '2560, something like D53-52-51 and 50 somewhere in that range).
On a 1284, D13-12-11-10 for Bobuino, and D7-6-5-4 for other versions.
Not sure where on a 32U4 (Leonardo).

How will those "# define"s work when SPI.transfers are actually being used, as in below?
Code:
    // Enable SPI communications
    digitalWrite(cs,LOW);
    // Send command word
    byte0 = cmdWrd >> 8;
    byte1 = cmdWrd & 0b0000000011111111;
    SPI.transfer(byte0);
    SPI.transfer(byte1);
    // Disable SPI communications
    digitalWrite(cs,HIGH);

Could be I am totally misunderstanding this as well, really the first time I looked at what goes into a library/class whatever it is called that you crafted.
The CLK and SDI defines were ones I added. But since SPI.transfer uses the hardware pins (something I completely overlooked!), I will remove all references to the pins I tried to define! I hope that when my chip gets here it will work well with the library, if not... well I'll have to take another look at software SPI, I think.

So I connect:
SDI (MOSI) - 51
SCK - 52
And I get to choose CS (SS), and LDAC. I think I understand.

I have never delved into libraries either, everything is so new and complicated to me! Thank you for your help  smiley
13  Using Arduino / Programming Questions / Re: Converting Analogue input to real value on: April 07, 2012, 09:28:40 am
You could try using the float version of map (paste the function inbetween setup() and loop()):
Code:
void loop()
{
 
  int raw = analogRead(analoginput);
  float val = fmap(raw, 0, 1023, 0.0, 5.0);
  Serial.println(val); //voltage
  delay(1000);
}

float fmap(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
14  Using Arduino / Project Guidance / Re: Using a DAC (MCP4911) instead of a low-pass filter - hardware or software SPI? on: April 07, 2012, 09:20:26 am
You are right CS as CS, vs LDAC.

Yes, these 2 lines use the SPI hardware to shift out two bytes:

SPI.transfer ( highByte( your_data ) );
SPI.transfer ( lowByte( your_data ) );
So in total I have 4 pins - CS, LDAC, SDI (MOSI) and SCK. I found and edited a library for a similar DAC to mine a couple of days ago, which uses SPI.transfer, so I think it could work. Originally you only had to specify the CS and LDAC pins, but because I was unsure about the remaining pins, I edited it so I could choose the MOSI and SCK pins.

I will be using a Mega with my Ethernet shield, so there will be free connections for hardware SPI. Given the relatively low rate that I will be sending data to the DAC at, would it be okay to just use the same MOSI/SCK pins?

Thanks  smiley-grin

.cpp:
Code:
// ----------------------------------------------------------------------------
// MCP4911.cpp
//
// Provides an SPI based interface to the MCP4911 single voltage output digital
// to analog converter.
//
// Author: Will Dickson, IO Rodeo Inc., edited by plusminus_
// ----------------------------------------------------------------------------

#include "Arduino.h"
#include "SPI.h"
#include "MCP4911.h"

#define ACTIVE      0b0001000000000000
#define SHUTDOWN    0b0000000000000000
#define GAIN_1X     0b0010000000000000
#define DAC_A_WRITE 0b0000000000000000

MCP4911::MCP4911() {
}

// ----------------------------------------------------------------------------
// MCP4911::MCP4911
//
// Constructor
// ----------------------------------------------------------------------------
MCP4911::MCP4911(int csPin, int ldacPin, int clkPin, int sdiPin) {
    // Configure chip select and latch pins
// I think MISO can be ignored
    cs = csPin;
    ldac = ldacPin;
clk = clkPin;
sdi = sdiPin;
}

// ----------------------------------------------------------------------------
// MCP4911::MCP4911
//
// initialization function
// ----------------------------------------------------------------------------
void MCP4911::begin() {
pinMode(cs,OUTPUT);
    pinMode(ldac,OUTPUT);
pinMode(clk,OUTPUT);
pinMode(sdi,OUTPUT);

    digitalWrite(cs,HIGH);
    digitalWrite(ldac,HIGH);
digitalWrite(clk,LOW);
digitalWrite(sdi,LOW);

    // Set to default configuration
    setGain1X_A();
}

// ---------------------------------------------------------------------------
// MCP4911::getCmdWrd
//
// Gets command word for writing value to given channel
// ---------------------------------------------------------------------------
int MCP4911::getCmdWrd(int dac, int value) {
    int cmdWrd = 0;
    switch (dac) {
        case MCP4911_DAC_A:
            cmdWrd = DAC_A_WRITE;
            break;
        default:
            return 0;
    }
    cmdWrd |= gain[dac];
    cmdWrd |= ACTIVE;
    cmdWrd |= (0b0000111111111111 & value);
    return cmdWrd;
}

// ----------------------------------------------------------------------------
// MCP4911::setValue
//
// Set the output value of the given dac channel
// ----------------------------------------------------------------------------
void MCP4911::setValue(int dac, int value) {
    int cmdWrd;
    uint8_t byte0;
    uint8_t byte1;
   
    // Enable SPI communications
    digitalWrite(cs,LOW);
    // Send command word
    cmdWrd = getCmdWrd(dac,value);
    byte0 = cmdWrd >> 8;
    byte1 = cmdWrd & 0b0000000011111111;
    SPI.transfer(byte0);
    SPI.transfer(byte1);
    // Disable SPI communications
    digitalWrite(cs,HIGH);
    // Latch value
    digitalWrite(ldac,LOW);
    digitalWrite(ldac,HIGH);
}

// ---------------------------------------------------------------------------
// MCP4911::setValue_A
//
// Set the output value of dac A to the given value
// ---------------------------------------------------------------------------
void MCP4911::setValue_A(int value) {
    setValue(MCP4911_DAC_A, value);
}

// ---------------------------------------------------------------------------
// MCP4911::setGain1X
//
// Set the gain of the given channel to 1X
// ----------------------------------------------------------------------------
void MCP4911::setGain1X(int dac) {
    if (dac == MCP4911_DAC_A) {
        gain[dac] = GAIN_1X;
    }
}

// ----------------------------------------------------------------------------
// MCP4911::setGain1X_A
//
// Set the gain of dac A to 1X
// ----------------------------------------------------------------------------
void MCP4911::setGain1X_A() {
    setGain1X(MCP4911_DAC_A);
}

// ----------------------------------------------------------------------------
// MCP4911::off
//
// Turn off the given dac
// ----------------------------------------------------------------------------
void MCP4911::off(int dac) {
    int cmdWrd = 0;
    uint8_t byte0;
    uint8_t byte1;
   
    // Create shutdown cmd word.
    switch (dac) {
        case MCP4911_DAC_A:
            cmdWrd = DAC_A_WRITE;
            break;
        default:
            return;
    }
    cmdWrd |= SHUTDOWN;
    // Enable SPI communications
    digitalWrite(cs,LOW);
    // Send command word
    byte0 = cmdWrd >> 8;
    byte1 = cmdWrd & 0b0000000011111111;
    SPI.transfer(byte0);
    SPI.transfer(byte1);
    // Disable SPI communications
    digitalWrite(cs,HIGH);
}

// ----------------------------------------------------------------------------
// MCP4911::off_A
//
// Turn off dac A
// ----------------------------------------------------------------------------
void MCP4911::off_A() {
    off(MCP4911_DAC_A);
}

.h:
Code:
// ----------------------------------------------------------------------------
// mcp4911.h
//
// Provides an SPI based interface to the MCP4911 single voltage output digital
// to analog to converter.
//
// Author: Will Dickson, IO Rodeo Inc., edited by plusminus_
// ----------------------------------------------------------------------------
#ifndef _MCP4911_H_
#define _MCP4911_H_

#define MCP4911_NUMCHAN 1

enum MCP4911_DAC_CHAN {MCP4911_DAC_A};

class MCP4911 {
private:
    int cs;
    int ldac;
int clkPin;
int sdiPin;
    int gain[MCP4911_NUMCHAN];
    int getCmdWrd(int dac, int value);
public:
    MCP4911();
    MCP4911(int csPin, int ldacPin, int clkPin, int sdiPin);
    void begin();
    void setValue(int dac, int value);
    void setValue_A(int value);
    void setGain1X(int dac);
    void setGain1X_A();
    void off_A();
    void off(int dac);
};
#endif

example sketch:
Code:
#include <SPI.h>
#include "MCP4911.h"

#define AOUT_CS 5
#define AOUT_LDAC 6
#define AOUT_CLK 7
#define AOUT_SDI 8
// For example, these will change

MCP4911 analogOut = MCP4911(AOUT_CS, AOUT_LDAC, AOUT_CLK, AOUT_SDI);

void setup() {
    // Setup serial and SPI communications
    Serial.begin(115200);
    analogOut.begin();
    // SPI.setDataMode(SPI_MODE0); - default
    // SPI.setBitOrder(MSBFIRST); - default
    // SPI.setClockDivider(SPI_CLOCK_DIV8); - half default speed
    SPI.begin();

    // Configure analog outputs
    analogOut.setGain1X_A();
}


void loop() {
    static int cnt=0;
    analogOut.setValue_A(cnt);
    cnt += 20;
    if (cnt > 1023) {
        cnt = 0;
    }
}
15  Using Arduino / Project Guidance / Re: Using a DAC (MCP4911) instead of a low-pass filter - hardware or software SPI? on: April 06, 2012, 01:17:26 pm
There's no reason the two shouldn't both work on the hardware SPI as long as you deal with the SS issue. OTOH your code may be a bit simpler if you use shiftOut() because your won't have to deal with SS.

As long as you don't need speed I see no real advantage either way.

At what rate do you need to write to the DAC?

______
Rob
Well I want to put my controller in a loop that lasts 10 ms (at least, I haven't determined my sampling interval yet), so at most 100 times a second. Does the DAC hold the last value received until another one is received? I think I read that it does, somewhere...

I'm a bit worried about the Ethernet Shield having one of its SPI pins low for long durations of time, I think it's the SS pin, which would mean that I can't use the DAC while the E. Shield is doing that.

SPI, fast & simple
http://ww1.microchip.com/downloads/en/DeviceDoc/22248a.pdf

digitalWrite ( DAC_SS, LOW ); // connects to device's LDAC pin
SPI.transfer ( command_data );
SPI.transfer ( low_data );
digitalWrite ( DAC_SS, LOW );

where the upper 4 bits of command_data are 0111 00xx typical per datasheet page 24, with xx being the upper 2 bits of data
and low_data is the remaining 8 bits of data.
Sorry, why is the LDAC pin being used as the SS, rather than the CS pin? I don't follow  smiley-red

If your analog data is stored as an int, then the data to be transferred out could be set up as follows:
your_data = your_data | B0111 0000 0000 0000;  // sets the needed command bits high, leaves your 10 bit data alone
your_data = your_data & B0111 0011 1111 1111; // clears the needed command bits low, leaves your 10 bit data alone
(spaces shown for clarity only)

digitalWrite ( DAC_SS, LOW ); // connects to device's LDAC pin
SPI.transfer ( highByte( your_data ) );
SPI.transfer ( lowByte( your_data ) );
digitalWrite ( DAC_SS, HIGH );

I was using 10-bit PWM, so my analogWrite() values would be an int from 0 to 1023. Do the 2 your_data lines convert the integer to binary?

I have a feeling this is something I will need to test with a breadboard when my chips get here...

Thanks for the help so far!  smiley
Pages: [1] 2 3 4