Help with data acquisition of capacitance measurement data from multimeter

Hi, so I have designed a system where:

  1. The teensy 3.5 triggers the multimeter to make a capacitance measurement
  2. The teensy acquires signal that measurement has been made (VM COMP signal) from multimeter using interrupt
  3. Read data from multimeter (connection via rs232 connected to TTL converter)
  4. Record / output the data
  5. Trigger the multimeter again and repeat step 1-4

The code looks like this:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#define multimeter Serial1
//#include <EEPROM.h>
int dec_input;
char incomingByte;
String input;
int current;
bool initial=true;
volatile bool input_available;
#include <SPI.h>
#include <SD.h>

double measurement_time=20.0;
int S0 = 17;
int S1 = 18; //switch bit two on pin
int S2 = 19; //switch bit three on pin
int S3 = 20; //switch bit four on pin  NEEDS SECOND MULTIPLEXER
int T0 = 9; //trigger measurement on pin  sends pulse to multimeter
int I0 = 8; //receive interrupt(finish flag)       receives pulse from multimeter
int I1 = 7; //receives data 
int led=13; //LED assignment
int x=0;
File myFile;
int count=0;

void setup() {
  
  Serial.begin(9600);  //9600 baud rate required by usb
  multimeter.begin(38400);
  delay(3000);

  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(T0, OUTPUT);
  pinMode(I0, INPUT);
  pinMode(led, OUTPUT);
  pinMode(I1, INPUT);


  attachInterrupt(digitalPinToInterrupt(I0), rx, FALLING); //The multimeter will send a 2us high pulse on measurement, this statement tells pin I0 to watch for it
  digitalWrite(T0,LOW);
  input_available=false;
  current=0;
  digitalWrite(S2,LOW);
  digitalWrite(S1,LOW);
  digitalWrite(S0,LOW);
  delay(2000);
}

void loop() {
  if(x==0)                            //first iteration
  {
    Serial.print("initial trigger!\n\n\n");
    Serial.println("time(s)\t\tS0(F)\t\ttime(s)\t\tS1(F)");
    x=x+1;
    delay(5);
    digitalWrite(T0,HIGH);
  }
  else if (input_available==true&&count>=5)
  {
      dec_input=0;
      detachInterrupt(I0);
      while (!multimeter.available()){;}
      if (multimeter.available())
      { 
          while (!(dec_input==10))                //wait until newline
              {
                     multimeter.flush();
                //    Serial.println("Waiting for newline");
                    incomingByte = multimeter.read();
                    dec_input=(int) incomingByte;
                    input+=incomingByte;
                    Serial.write(incomingByte);
               }
      }
        Serial.print((millis()/1000.0));Serial.print("\t\t");
      input.remove(12);
      Serial.print(input);
      Serial.print(" time: ");
      Serial.print(millis()/1000.0);
      Serial.println("s");
      input=String("");
      attachInterrupt(digitalPinToInterrupt(I0), rx, FALLING);
  }
  else if (input_available==true)
  {
    delay(100);
  }
  
  if (input_available==true)                   //
  {
      input_available=false;
  }
  
  if ((millis()/1000)>measurement_time)        //Finish
  {
    //myFile.close();
    Serial.print("Done");
    delay(10000);
  }
  else
  {
    delay(100);                          //DELAY REFERENCE 
   // multimeter.clear();
    digitalWrite(T0, HIGH);   
    digitalWrite(led, HIGH); 
    while (input_available==false)
    {
    }
    //delay until interrupt received
    
  }  
  count++;
}




void rx()
  {

      
      Serial.print("  Interrupt!  ");
      input_available=true;
       digitalWrite(T0,LOW);
  }

Then the output for the code is the following:

initial trigger!

time(s) S0(F) time(s) S1(F)
Interrupt! Interrupt! Interrupt! Interrupt! Interrupt! Interrupt! 6.57 1.038526E-05 time: 6.57s
Interrupt! 6.73 1.032304E-05 time: 6.73s
Interrupt! 6.90 1.032676E-05 time: 6.90s
Interrupt! 7.06 1.032264E-05 time: 7.06s
Interrupt! 7.23 1.03⸮⸮⸮⸮⸮⸮⸮⸮ time: 7.23s
Interrupt!

I am using RIGOL DM3068, with the following external trigger and VMC output data:

External Trigger Input
Level: 5 V TTL compatible
Impedance: >30 kΩ in parallel with 500 pF
Delay: < 50 μs
Jitter: < 50 μs (ACV, ACI, FREQ and PREIOD <2 ms)
Polarity: rising edge, falling edge available
Maximum Rate: 300/s
Minimum Pulse Width: 2 μs

VMC Output
Level: 5 V TTL compatible
Output Impedance: 100 Ω, typical
Output Polarity: Negative
Pulse Width: about 2 μs

From the output screen, I have no idea where those squares are coming from. If I increase the length of delay commented as DELAY REFERENCE, the squares are gone or they show up after longer time of proper data measurement. How should I modify the code to perfectly measure the data at maximum frequency?

Thank you so much. This is stressing me out so much, any help would be very much appreciated.

I tried changing the baud rate of both the microprocessor and the multimeter, but no improvements.

Do not attempt to do Serial printing inside an interrupt.

While that output from the multimeter may be labelled "interrupt" that doesn't mean you must use an interrupt in the Teensy to read the signal. Just read it like a regular pin with digitalRead().

Do not rely on the built-in Serial1 buffer to hold an entire message. It is very small. I think it defaults to 30 characters on the Teensy. (Silly, with so much memory available, but that's what it is.) You should be checking multimeter.available() thousands of times per second and not just when the interrupt signal is active.

MorganS:
While that output from the multimeter may be labelled "interrupt" that doesn't mean you must use an interrupt in the Teensy to read the signal. Just read it like a regular pin with digitalRead().

But digitalRead() only returns if it is high or low? I want to read the ASCII byte transferred from multimeter.

MorganS:
Do not rely on the built-in Serial1 buffer to hold an entire message. It is very small. I think it defaults to 30 characters on the Teensy. (Silly, with so much memory available, but that's what it is.) You should be checking multimeter.available() thousands of times per second and not just when the interrupt signal is active.

Then should I clear the buffer by Serial.clear() after every iteration? Also, by checking thousands of times per seconds, you mean read each digit whenever available by the multimeter.available() function?

Thanks so much!

Serial is

S

L

O

W

The Arduino can do thousands of things between each letter arriving. So you just store them away somewhere until you see the last letter has arrived. Usually something like a linefeed character. Interrupts are not appropriate for things this slow.

Ah. I see.

So your suggestion is not using interrupt, but instead checking the serial.available() thousand of times every second and store each letter that arrives using serial.read() until the newline feed?

One question: in my code, all the interrupt does is it changes the state of the global variable. In the main loop function, with the que from the interrupt (change in state of global variable), it waits for the letters to arrive. Do you mean that this segment of code of waiting for the letters to arrive is not good since the serial is slow?

if (multimeter.available())
{
while (!(dec_input==10)) //wait until newline
{
multimeter.flush();
// Serial.println("Waiting for newline");
incomingByte = multimeter.read();
dec_input=(int) incomingByte;
input+=incomingByte;
Serial.write(incomingByte);
}
}

Thanks,
Jaewoo

That fails badly. You check if there is a character available to read and then you read thousands of characters, almost all of which will be -1 because there aren't any more available. Then memory fills up (assuming input is a String) and it crashes.

If there is a character available, you should store it and then go do something else productive for a millisecond or two. Don't wait for that elusive newline.

I see the problem. I know that each data set will be 14 bytes, with the last one being newline character. That was the reason I was waiting for the newline character. Would it solve the problem if I disregard the -1 response while waiting for the newline?

I don't have anything else to do productive, I want to make sure I receive the data correctly and then move on.

Thanks alot,
Jaewoo

Hi, so before I start my problem description, I would like to thank you very much for your intention in helping me.

So, I have a system where it is designed to do the following:

  1. The teensy 3.5 triggers the multimeter to make a capacitance measurement (by digitalWrite(LOW), and multimeter is triggered by external input node voltage slope of falling)
  2. With the trigger from step1, the multimeter will make a measurement and send output the data via rs232. The teensy loops checking serial.available() if it is receiving any data from the multimeter
  3. Read and store each data (each byte) from multimeter (connection via rs232 connected to TTL converter)
  4. Repeat steps 2-3 until newline character is read
  5. Trigger the multimeter to make another measurement and repeat step 1-4

The code looks like the following:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <SoftwareSerial.h>
//#define multimeter Serial1
SoftwareSerial multimeter = SoftwareSerial(0,1);
int dec_input;
char incomingByte;
String input;
int multi_count=0;
volatile bool input_available;
#include <SPI.h>
#include <SD.h>

int TriggerPin= 9; //trigger measurement on pin  sends pulse to multimeter
int x=0;
int count=0;

void setup() {
  
  
  Serial.begin(19200);
  multimeter.begin(19200);
  delay(3000);

  pinMode(TriggerPin, OUTPUT);
  pinMode(ReceiveData, INPUT);


  digitalWrite(TriggerPin,HIGH);
  delay(2000);
}

void loop(){  
  if(x==0)                            //first iteration
  {
    Serial.print("initial trigger!\n\n\n");
    x=x+1;
    delay(5);
    digitalWrite(TriggerPin,LOW);
  }
  else if (multimeter.available())
  {
                      incomingByte = multimeter.read();
                      dec_input=(int) incomingByte;
                      input+=incomingByte;              
  }

  if (dec_input==13)
  {
    dec_input=0;
    input.remove(13);
    Serial.print(input);
    Serial.print(" time: ");
    Serial.print(millis()/1000.0);
    Serial.println("s");
    input=String("");
    digitalWrite(TriggerPin,HIGH);
    delay(150);                                            //DELAY REFERENCE
    digitalWrite(TriggerPin,LOW);
  }

}

In terms of reading the ASCII from the multimeter, I have tried both initializing by softwareserial and Serial1, but both gives me the following output:

initial trigger!


1.020473E-08time: 5.41s

1.003963E-08 time: 5.55s

1.008742E-08 time: 5.77s

So, what seems like happening is after triggering the multimeter after 5.77s, the microprocessor does not successfully read the incoming bytes? At least that is what I think.

If I increase the length of delay commented as DELAY REFERENCE in the code, the data acquisition would be more successful. For example, by increasing it to 300, the following output is obtained:

initial trigger!


1.010234E-08 time: 5.41s

9.940274E-09 time: 5.70s

9.944433E-09 time: 6.01s

9.948112E-09 time: 6.30s

9.944829E-09 time: 6.67s

1.025796E-08 time: 7.03s

The data acquisition loop still stops for some reason. I have no idea why. The multimeter specs tells me that the measurement rate is 25 readings/s 50Hz (I don’t know what the 50Hz means but it is literally written like this in the spec sheet), so the measurement rate should not be a problem I think.

So, here is the question: How should I modify my code so that the Teensy 3.5 will receive data from the multimeter fastly and while not losing any data?

Thank you so much. I have been stressing out so much since last week. Any help will be very much appreciated.

For your information, below is the data specs for external triggering the multimeter.

External Trigger Input
Level: 5 V TTL compatible
Impedance: >30 kΩ in parallel with 500 pF
Delay: < 50 μs
Jitter: < 50 μs (ACV, ACI, FREQ and PREIOD <2 ms)
Polarity: rising edge, falling edge available
Maximum Rate: 300/s
Minimum Pulse Width: 2 μs

(deleted)

int T0 = 9; //trigger measurement on pin  sends pulse to multimeter

Wouldn’t it be far more straightforward and readable to call T0 something like “triggerPin”? Also assign meaningful names to all the other mnemonic names?

Now is a good time to start changing things like that because the code you posted is a real work in progress with commented out lines and post-it notes throughout. It’s hard to comment on such a piece because it’s confusing and hard to work out what you intend and what you don’t.

spycatcher2k:
Did you know URGENT is forum speak for 'Ignore this post because I'm so disrespectful I want to jump the queue'

I sincerely apologize, I did not mean to be disrespectful. Please disregard the "urgent" note.

Thanks,
Jaewoo

aarg:

int T0 = 9; //trigger measurement on pin  sends pulse to multimeter

Wouldn't it be far more straightforward and readable to call T0 something like "triggerPin"? Also assign meaningful names to all the other mnemonic names?

Now is a good time to start changing things like that because the code you posted is a real work in progress with commented out lines and post-it notes throughout. It's hard to comment on such a piece because it's confusing and hard to work out what you intend and what you don't.

Hi, thanks for your support.

I have cleaned up the code as you have said. Sorry for the confusion.

Thanks,
Jaewoo

Since the problem seems to be the meter not reacting to a pulse there are some things you can do:

If you are waiting for input characters for more than a second, send another pulse.

Add a slight delay between receiving the 'newline' and sending the 'start again' pulse. Perhaps the meter is not recovering in time to process the pulse.

Check the waveform of the pulse. Maybe the drive is weak and the rising edge is not noticed.

Try some experiments to see if the meter triggers on a rising edge, falling edge, or both.

No idea about teensy. Your code uses pins 0 and 1 for software serial. On most arduinos, that's hardware serial (e.g. Uno) or hardware serial 1 (e.g. Leonardo); is it different for the teensy?

po03087:
2. With the trigger from step1, the multimeter will make a measurement and send output the data via rs232. The teensy loops checking serial.available() if it is receiving any data from the multimeter
3. Read and store each data (each byte) from multimeter (connection via rs232 connected to TTL converter)
4. Repeat steps 2-3 until newline character is read

Read Serial Input Basics - updated to get ideas how to properly read serial data. That way you can also get rid of the String (capital S). You might want to add a timeout.

If you check for CR and or NL, use the character representations '\r' and '\n' and not the ascii values; it makes you code more understandable as you did not comment in the code why you're using the line [if (dec_input == 13). What is the meter returning? CR or LF or both? 13 means CR, maybe you need to test for LF (10).

Have you analysed the data by simply printing the ascii values of the received characters?

Hi, so before I start my problem description, I would like to thank you very much for your intention in helping me.

Well, you're off to a bad start there. Your other thread about this exact same project was going along quite well. Why did you start another thread? Then you made a post on the previous thread after starting this one?

I've asked a moderator to merge the threads, so that all of the answers on this one project are kept together.

@po03087, do not cross-post. Threads merged.

MorganS:
Well, you're off to a bad start there. Your other thread about this exact same project was going along quite well. Why did you start another thread? Then you made a post on the previous thread after starting this one?

I've asked a moderator to merge the threads, so that all of the answers on this one project are kept together.

Sorry for the confusion. I have re-written the code and was having a different problem, so I thought a new post would be less confusing to understand the situation.

I understand the rules for the forum and will make sure to follow it.

Thanks,
Jaewoo

sterretje:
No idea about teensy. Your code uses pins 0 and 1 for software serial. On most arduinos, that's hardware serial (e.g. Uno) or hardware serial 1 (e.g. Leonardo); is it different for the teensy?
Read Serial Input Basics - updated to get ideas how to properly read serial data. That way you can also get rid of the String (capital S). You might want to add a timeout.

It actually was hardware serial, so I changed it to #define multimeter Serial1 for the setting the connection.

sterretje:
If you check for CR and or NL, use the character representations '\r' and '\n' and not the ascii values; it makes you code more understandable as you did not comment in the code why you're using the line [if (dec_input == 13). What is the meter returning? CR or LF or both? 13 means CR, maybe you need to test for LF (10).

Have you analysed the data by simply printing the ascii values of the received characters?

I have tried checking CR and LF, and both options yield the same output. I have also tried simply printing the ascii values, but that did not help in increasing the frequency of the data acquisition.

Would you say this is a problem with the multimeter?

Thanks,
Jaewoo

johnwasser:
Since the problem seems to be the meter not reacting to a pulse there are some things you can do:

If you are waiting for input characters for more than a second, send another pulse.

Add a slight delay between receiving the ‘newline’ and sending the ‘start again’ pulse. Perhaps the meter is not recovering in time to process the pulse.

I have tried adding delays, tried switching multimeter’s trigger options of rising edge and falling edge, amplified the 3.3V teensy output voltage to 5V amplitude, but nothing solved the problem of increasing the data acquisition frequency.

Here is my new code:

--------------------New code ----------------

#include <avr/io.h>
#include <avr/interrupt.h>
#include <SoftwareSerial.h>
#define multimeter Serial1
int dec_input;
char incomingByte;
String input;
int multi_count=0;
volatile bool input_available;
#include <SPI.h>
#include <SD.h>

int TriggerPin= 9; //trigger measurement on pin sends pulse to multimeter
int x=0;
int count=0;

void setup() {

Serial.begin(19200);
multimeter.begin(19200);
delay(3000);

pinMode(TriggerPin, OUTPUT);
pinMode(ReceiveData, INPUT);

digitalWrite(TriggerPin,HIGH);
delay(2000);
}

void loop(){
if(x==0) //first iteration
{
Serial.print(“initial trigger!\n\n\n”);
x=x+1;
delay(5);
digitalWrite(TriggerPin,LOW);
}
else if (multimeter.available())
{
incomingByte = multimeter.read();
dec_input=(int) incomingByte;
input+=incomingByte;
}

if (dec_input==13)
{
dec_input=0;
input.remove(13);
Serial.print(input);
Serial.print(" time: “);
Serial.print(millis()/1000.0);
Serial.println(“s”);
input=String(”");
digitalWrite(TriggerPin,HIGH);
delay(150); //DELAY REFERENCE
digitalWrite(TriggerPin,LOW);
}

if (iteration_counta>30&&(!data_receiving))
{
digitalWrite(T0,HIGH);
delay(1);
digitalWrite(T0,LOW);
multi_count=0;
}
iteration_count++;
}


I also felt like I had to modify the multimeter’s setting of trigger delay, so I tried setting it to auto, zero, and manual, but none solved the problem as well.

Would there be any other way? I will try adding the timeout and double check the waveform of trigger.

Thanks so much,
Jaewoo