Pneumatic Cylinder control and data logging

Hi all,

I’m working on a project where I am using an Arduino Mega 2560 R3 to control a pneumatic cylinder and log cylinder travel data. Basically the Arduino reads in inputs from magnetic reed switches placed on the body of the cylinder which tell it to turn on or switch off the a solenoid which controls a slide valve that diverts air into the cylinder. I have been able to get this function to sucessfully work with my code.

Unfortunately I have not been sucessful in getting data logging to work with the above program. If I make a separate sketch solely reading the linear potentiometer the data logger works fine. Is it possible to run data logging while doing another main function? All the example codes I have found online only show sketches for just data logging. My combined pneumatic cylinder and datalogging code is below. The code is based off the adafruit data logger sketch. Sorry if its confusing, this is all relatively new to me.

Thanks!

#include <SD.h>
#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#include "RTClib.h"

#define LOG_INTERVAL 1 // milsec betweens entries
#define SYNC_INTERVAL 100
uint32_t syncTime =0;

RTC_DS1307 RTC; // Real Time Clock
 
// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 10;


//switch inputs and variables
const int kPinReedSwitch1 = 22;
const int kPinReedSwitch2 = 24;
const int relayPin = 26;

int strokedown = 0;
int inc = 0;
int precountA =0;
int precountB = 0;
int count = 0;
int runcycles = 20;
int reset = 0;
int initial = 1;
int start =1;
int LinearPot1Pin = A0;    // select the input pin for the potentiometer
float sensorValue = 0.0;  // variable to store the value coming from the sensor
int MC_travel = 0; 
int on =0;

//logging file
File logfile;

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);


void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  while(1);
}

 
void setup()
 {
  // Open serial communications and wait for port to open:
   Serial.begin(9600);
    while (!Serial) {
     ; // wait for serial port to connect. Needed for Leonardo only
   }
  // Input pins for Arduino
   pinMode(kPinReedSwitch1, INPUT_PULLUP);
   pinMode(kPinReedSwitch2, INPUT_PULLUP);
   pinMode(relayPin, OUTPUT);
 
  Serial.print("Initializing SD card...");
   // make sure that the default chip select pin is set to
   // output, even if you don't use it:
   pinMode(53, OUTPUT);
   
  // see if the card is present and can be initialized:
   if (!SD.begin(10,11,12,13)) {
     Serial.println("Card failed, or not present");
     // don't do anything more:
     return;
   }
   Serial.println("card initialized.");
   
   
   
   // creating a new file for data logger
   
   char filename[] = "LOGGER00.CSV";
   for (uint8_t i = 0; i < 100; i++){
     filename[6] = i/10 + '0';
     filename[7] = i%10 + '0';
     if(! SD.exists(filename)){
       logfile = SD.open(filename, FILE_WRITE);
       break;
     }
   }
   if (!logfile) { 
     error("couldnt create file");
   }
   
   Serial.print("Logging to:");
   Serial.println(filename);
   
   logfile.println("MC Travel");
     
  // LCD initialization  
   lcd.begin(20,4);
   lcd.clear(); 
   lcd.setCursor(0,0);
   lcd.print("Count:              ");
   lcd.setCursor(0,1);
   lcd.print("MC Travel:    in");
 }
 
void loop()
 {      
    for(int i = 1; i < 2; i++ )
    //an initialization routine just to get cylinder to "home"
    //although should never really be away from home
    {
       digitalWrite(relayPin, HIGH);
       delay(200); 
       digitalWrite(relayPin, LOW);
       delay (2000);
    }
   
    
    while( count <= runcycles){
      
     
     if( start ==1 && digitalRead(kPinReedSwitch1)==HIGH){
       digitalWrite(relayPin, HIGH);
     }
  
     else if (digitalRead(kPinReedSwitch1) == LOW)
    // turns on relay when reed switch A is triggered
    // Logic reversed because of pullup resistors
    {
      strokedown = 1;
      precountA =1;
      start=0;// turn off start sequence
     digitalWrite(relayPin, HIGH);
    }
     else if (digitalRead(kPinReedSwitch2) == LOW ) 
    // turns off relay when reed switch B is triggered and ups count by 1
    //logic reversed because of pullup resistors
    {
      strokedown =0;
      precountB = 1;
      start=0;
     digitalWrite(relayPin, LOW);
    }
    else if ( strokedown == 1 )
    {
      digitalWrite(relayPin, HIGH);
      precountA =1;
      start=0;
    }
    else
    {
     strokedown = 0;
     digitalWrite(relayPin, LOW);
     start=0;
    }
    
    if (precountA + precountB ==2)
    {
      inc++;
      precountA =0;
      precountB= 0;  
    }
    
    else
    {
      inc = inc;
    }
    
    count = inc/2;
   
    lcd.setCursor (7,0);
    lcd.print(count);
    
    float sensorValue = analogRead(LinearPot1Pin);
    float voltage = sensorValue*(5.0/1023.3);
    float MC_travel = 0.4117*voltage;
    Serial.print("Sensor 1 Travel is:");
    Serial.println(MC_travel);
    lcd.setCursor (10,1);
    lcd.print(MC_travel);
    
    
   // logging MC stroke value
    logfile.print(MC_travel);
    logfile.flush();
 
    }
    
     while( count > runcycles){
     lcd.clear();
     digitalWrite(relayPin, LOW);
     lcd.print("Program is finished");
     Serial.println("Program is finished");
     delay(1000);
   } 
  }

While I'm not going to promise this will work, I would look at the timing on things, as the LCD print code block at the end looks like it might hold everything up. I'd try looking at Blink without delay and figure out some timing blocks.

You don’t say what the problem is, but I don’t see anything controlling the execution frequency of your main loop so I guess it will run as fast as it can, and you’re doing output and logging every time round the loop. That probably means you’re going to generate a lot of output, so the quantity of output is probably your main limiting factor for performance. Since you just log a series of float values with no separators and no way of knowing what value relates to what time, I don’t suppose the output will be particularly useful.

What output do you actually want to get from the logging code?

Ideally I'd like the logging code to give me travel values for two potentiometers and a pressure readout with a time stamp. In the code I wrote above, I was just trying to see if I could get the Arduino to log one potentiometer readout to an SD card shield and then hopefully expand upon that. What I found when I ran the code was that overall the code would run much slower, and no data was stored onto the SD card, although a log file was made.

Writing to SD is relatively slow so you will need to control how frequently you do it, but the code you posted should have written something to the SD card. Perhaps you need to close the file at the end of the logging sequence.

I suggest you add some code to execute the code in that 'runcycles' loop at a fixed frequency rather than as fast as possible. I'd expect the current code to run very quickly and end in a fraction of a second. Is that what happens? Is that what you want?

Current code (minus datalogging stuff) works as desired. Basically the pressure of the input air into the cylinder and the distance between the reed sensors control the speed of the cylinder. The arduino does a good job of picking up the reed sensor signals and switching the slide valve to change piston direction as desired.

To further help with the debugging of the code I modified the SD card datalogger example program to read my linear potentiometer and write values to the SD card and LCD screen. I found that when I had it write to both, no information would be written to the SD card. If I comment out the part of the code that writes to the LCD screen, data is successfully written to the SD card. I further tested this on the first code I posted by commenting out code that writes to the LCD. On this code, no data was written to the SD card as well. Thus I assume that the code printing to the lcd screen and the main pneumatic cylinder code is interfering with the code that is writing to the SD card.

Does this sound like a reasonable assumption? What would cause this? Here’s the modified SD datalogger example code:

#include <SPI.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#include <SD.h>
#include "RTClib.h"

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 10;

int linearpotA =A0;
int MasterCylinderTravel = 0;

File logfile;

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);


void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  
      
  // LCD initialization  
   lcd.begin(20,4);
   lcd.clear(); 
   lcd.setCursor(0,0);
   lcd.print("Count:              ");
   lcd.setCursor(0,1);
   lcd.print("MC Travel:    in");
   
   //SD card stuff

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(53, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(10,11,12,13)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    while (1) ;
  }
  Serial.println("card initialized.");
  
  
  // create a new file
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }
  
}

void loop()
{
  // make a string for assembling the data to log:


  // read three sensors and append to the string:
// 
  int sensorValue = analogRead(linearpotA);
  float voltage = sensorValue* (5.0 / 1023.3); 
  float MasterCylinderTravel = 0.4117*voltage; 
  
  delay(500);
  
  lcd.setCursor (10,1);
  lcd.print(MasterCylinderTravel);

  logfile.println(MasterCylinderTravel);

  // print to the serial port too:
  Serial.println(MasterCylinderTravel);
  
  // The following line will 'save' the file to the SD card after every
  // line of data - this will use more power and slow down how much data
  // you can read but it's safer! 
  // If you want to speed up the system, remove the call to flush() and it
  // will save the file only every 512 bytes - every time a sector on the 
  // SD card is filled with data.
  logfile.flush();
  
  
  
  // Take 1 measurement every 500 milliseconds
//  delay(10);
}

Ps. Thanks for the help so far

I ended up figuring out the problem. On my data logging shield chip select is defined as pin 10. Consequently I was also using pin 10 for the LCD. I ended up moving all the LCD pins away from the data logging shield to pins 32,34,36,38,40, and 42 ( I have a Mega 2560) and this solved the issue. Data logging now works great!

replace the delays used with millis() as the program does nothing in that time.

Well, now I have a new problem. I ended up writing my code so that the solenoid was driven off interrupts and the data logging to the sd card was done in the main loop. Unfortunately now I can’t get the program to count correctly while the solenoid is turned on. If I set a maximum amount of run cycles at 10, the program will run maybe 4 to 7 times before turning off. If the solenoid is turned off the program will run for 10 complete cycles. Any ideas on what could be causing this? I think it has to be something to do with the solenoid. What I can’t understand is why I didn’t have this problem with my original code which did the counting in the main loop instead of the interrupt.

Here is a test code I made with interrupts controlling the count and switching the solenoid:

#include <SD.h>
#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal.h>
#include "RTClib.h"


// Calling out stuff for onboard clock
uint32_t syncTime =0;

RTC_DS1307 RTC;

// Program variable name initialization

const int chipSelect =10;

int ReedSwitchA = 2;
int ReedSwitchB = 3;
int Relay = 26;
int cycles = 10;
int MC_travel =0;
int MCPot = A0;
int timer = 0;
long previousMillis =0;
long interval = 1000;

// Values that change in Interrupt

volatile int state = 0;
volatile int count = 0;
volatile int A = 0;
volatile int SA =0;
volatile int SB =0;
void setup() {
  
  Serial.begin(9600);
  //Sensor inputs
  pinMode(ReedSwitchA, INPUT_PULLUP);
  pinMode(ReedSwitchB, INPUT_PULLUP);
  pinMode(Relay, OUTPUT);
  
  // Reed sensor (set to interrupt)
  
  attachInterrupt (0, SensorA, LOW);
  attachInterrupt (1, SensorB, LOW);
  
  // Initial relay start
  
  digitalWrite( Relay, HIGH); 
}

void loop() {
  
  while(count < cycles){
    
  Serial.println(count);
  if (SA == 1){
    Serial.println( "Sensor A is on");}
    
  else if( SB ==1){
    Serial.println( "Sensor B is on");}
  
 
  

  }
  
  while (count = cycles){
   
   digitalWrite( Relay, LOW); 
  } 
}

void SensorA()
{
  // Essentially this is the first half of the original cylinder control program. Whenever Reed A is read,
  // an interrupt is called and the relay is turned 'on'
  if (count < cycles){
  state = 1;
  timer = 0;
  A = 1;
  SA =1;
  SB =0;
  digitalWrite (Relay, state);
  }
  
  else{
    state =0;
    digitalWrite(Relay, state);
  }
}

void SensorB()
{
  // Essentially this is the second half of the original cylinder control program whenever Reed B is read
  // an interrupt is called and the relay is turned off. Also a count is incremented here ONCE so that the 
  // count remains accurate and isn't effected by the data logger in the loop
  state = 0;
  digitalWrite( Relay, state); 
 
  if (state == 0 && A==1){
    while (timer==0){
    count ++;
    timer = 1;
    A = 0;
    SB = 1;
    SA = 0;
    }
  }
  
  else{
    count = count;
  }
 
 

}

Found out that the problem was coming from the AC solenoid. The noise that was generated as the relay was turned off was affecting the interrupt counts and the screen readout. We ended up switching to a solid state relay with Zero crossing switching. This fixed both problems and now the program works well :) Thanks for everyone who helped out!