Control of the mass-flow controller for the micro-fluid pumps

Hi there,
I have been presented with problem of controlling mass-flow controller for the 4 micro-fluid pumps that guys from lab got. The mass-flow controller is connected to the Arduino nano, which is then trough USB connected to the computer. The people who sold them this machine produce as well a demo code for arduino. Sadly there is no comment to speak of at all.

#include <Wire.h>

#define Addr (0xF6>>1)
#define TASTER          3
#define LED             13
#define I2C_DEVICEID	0x00
#define I2C_POWERMODE	0x01
#define I2C_FREQUENCY	0x02
#define I2C_SHAPE		0x03
#define I2C_BOOST		0x04
#define I2C_PVOLTAGE	0x06
#define I2C_P3VOLTAGE	0x06
#define I2C_P2VOLTAGE	0x07
#define I2C_P1VOLTAGE	0x08
#define I2C_P4VOLTAGE	0x09
#define I2C_UPDATEVOLTAGE	0x0A
#define I2C_AUDIO       0x05

int debounce = 0 ; 

int mode = 0;
#define MODE_OFF  0
#define MODE_50   1
#define MODE_100  2
#define MODE_200  3
#define MODE_400  4
#define MODE_800  5
#define MODE_MAX  6

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  Wire.begin();
  Serial.begin(9600);
  Serial.println();
  pinMode(LED, OUTPUT);
  pinMode (TASTER,INPUT_PULLUP);
  Wire.beginTransmission(Addr);
  Wire.write(I2C_POWERMODE);             			// write address = 0x01
  Wire.write(0x00);                       			// Adress 0x01 = 0x01 (enable)
  Wire.write(0x40);            			            // Adress 0x02 = 0x40 (100Hz)
  Wire.write(0x00);                       			// Adress 0x03 = 0x00 (sine wave)
  Wire.write(0x00);                       			// Adress 0x04 = 0x00 (800KHz)
  Wire.write(0x00);                       			// Adress 0x05 = 0x00 (audio off)
  Wire.write(0x1F);                       			// Adress 0x06 = 0x00 (EL1)
  Wire.write(0x1F);                       			// Adress 0x07 = 0x00 (EL2)
  Wire.write(0x1F);                       			// Adress 0x08 = 0x00 (EL3)
  Wire.write(0x1F);                       			// Adress 0x09 = 0x00 (EL4)
  Wire.write(0x01);                       			// Adress 0x0A = 0x00 (update)
  Wire.endTransmission();
}

// the loop routine runs over and over again forever:
void loop() {
  if (digitalRead(TASTER)==LOW) {
    if (!(debounce&0x01)) {
      mode=(mode+1)%MODE_MAX;
      switch (mode) {
        case MODE_OFF:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_POWERMODE);   // start adress
          Wire.write(0x00);            // disable
          Wire.endTransmission();
          digitalWrite(LED,LOW);
          break;
        case MODE_50:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_POWERMODE);   // start adress
          Wire.write(0x01);            // enable
          Wire.write(0x00);            // 50 Hz
          Wire.endTransmission();
          digitalWrite(LED,HIGH);
          break;
        case MODE_100:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0x40);            // 100 Hz
          Wire.endTransmission();
          break;
        case MODE_200:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0x80);            // 200 Hz
          Wire.endTransmission();
          break;
        case MODE_400:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0xC0);            // 400 Hz
          Wire.endTransmission();
          break;
        case MODE_800:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0xFF);            // 800 Hz
          Wire.endTransmission();
          break;
      }
      debounce|=0x01;
    }
  } else {
    if (debounce&0x01) {
      debounce&=~0x01;
    }
  }
  delay(100);
}

This is the demo code. As I understand it it should run trough the switch statements based on input, but I never manage to figure out how to produce the right input to make it work.
I know that this code work at least partially because I manage to make it work in a way.

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  Wire.begin();
  Serial.begin(9600);
  Serial.println();
  pinMode(LED, OUTPUT);
  pinMode (TASTER,INPUT_PULLUP);
  Wire.beginTransmission(Addr);
  Wire.write(I2C_POWERMODE);             			// write address = 0x01
  Wire.write(0x01);                       			// Adress 0x01 = 0x01 (enable)
  Wire.write(0x40);            			            // Adress 0x02 = 0x40 (100Hz)
  Wire.write(0x00);                       			// Adress 0x03 = 0x00 (sine wave)
  Wire.write(0x00);                       			// Adress 0x04 = 0x00 (800KHz)
  Wire.write(0x00);                       			// Adress 0x05 = 0x00 (audio off)
  Wire.write(0x1F);                       			// Adress 0x06 = 0x00 (EL1)
  Wire.write(0x1F);                       			// Adress 0x07 = 0x00 (EL2)
  Wire.write(0x1F);                       			// Adress 0x08 = 0x00 (EL3)
  Wire.write(0x1F);                       			// Adress 0x09 = 0x00 (EL4)
  Wire.write(0x01);                       			// Adress 0x0A = 0x00 (update)
  Wire.endTransmission();
  
  delay(1000);
  Wire.begin();
  Serial.begin(9600);
  Serial.println();
  pinMode(LED, OUTPUT);
  pinMode (TASTER,INPUT_PULLUP);
  Wire.beginTransmission(Addr);
  Wire.write(I2C_POWERMODE);             			// write address = 0x01
  Wire.write(0x00);                       			// Adress 0x01 = 0x01 (enable)
  Wire.write(0x40);            			            // Adress 0x02 = 0x40 (100Hz)
  Wire.write(0x00);                       			// Adress 0x03 = 0x00 (sine wave)
  Wire.write(0x00);                       			// Adress 0x04 = 0x00 (800KHz)
  Wire.write(0x00);                       			// Adress 0x05 = 0x00 (audio off)
  Wire.write(0x1F);                       			// Adress 0x06 = 0x00 (EL1)
  Wire.write(0x1F);                       			// Adress 0x07 = 0x00 (EL2)
  Wire.write(0x1F);                       			// Adress 0x08 = 0x00 (EL3)
  Wire.write(0x1F);                       			// Adress 0x09 = 0x00 (EL4)
  Wire.write(0x01);                       			// Adress 0x0A = 0x00 (update)
  Wire.endTransmission();
}

If I write it like this I can create sort of script that board is reacting on correctly.
The problem is that while I could be able to work with it like this others might not. To make it easier to my colleges I decided to create a windows based application in C# that could send inputs to the arduino code and initialize it with simple "click" of the button.
For that purpose i try to change the original code slightly:

void loop() {

     if (Serial.available() > 0) { // Check to see if there is a new message
      mode = Serial.read(); // Put the serial input into the message
      switch (mode) 
          {
        case MODE_OFF:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_POWERMODE);   // start adress
          Wire.write(0x00);            // disable
          Wire.endTransmission();
          digitalWrite(LED,LOW);
          break;
        case MODE_50:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_POWERMODE);   // start adress
          Wire.write(0x01);            // enable
          Wire.write(0x00);            // 50 Hz
          Wire.endTransmission();
          digitalWrite(LED,HIGH);
          break;
        case MODE_100:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0x40);            // 100 Hz
          Wire.endTransmission();
          break;
        case MODE_200:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0x80);            // 200 Hz
          Wire.endTransmission();
          break;
        case MODE_400:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0xC0);            // 400 Hz
          Wire.endTransmission();
          break;
        case MODE_800:
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0xFF);            // 800 Hz
          Wire.endTransmission();
          break;
      }
      debounce|=0x01;
    }
  } else {
    if (debounce&0x01) {
      debounce&=~0x01;
    }
  }
  delay(100);
}

Sorry I posting it like this but my original post was too big :slight_smile:
here is the c# code I using to send inputs:

namespace Arduino_try
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            
            InitializeComponent();
            serialPort1.PortName = "COM6";
            serialPort1.BaudRate = 9600;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("MODE_OFF");
                
            }
            serialPort1.Close();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            if (serialPort1.IsOpen)
            {
                serialPort1.WriteLine("MODE_50");
            }
            serialPort1.Close();
           
        }
    }
}

Now I know that the board getting some signals from the app thanks to the led lights flashing.
Now my question to people on this forum is. Does anyone see some way to modify the original arduino code from up so it could receive the input from serial port. And if anyone can make some sense from the original code from the top of this post and explain it (the loop() part) to me, that would be most helpful as well

You need a datasheet of the device. Which sensor is it ? Or is it not a sensor but a microcontroller ? Then at least you need a description of all the registers. Just a few registers that are called P1VOLTAGE and so on, that is not enough. It is not a sensor, but a controller ? And data is only written to it ? Can you make a photo of the controller that shows the chips inside ?

Some devices require a special sequence to wake up. Perhaps it is possible to read from it to check the hardware.

I believe that it is microcontroller. Mainly there is no sensor only 4 plug-ins for the miro pumps. I think there is no need for it to give any data back, except for ON/OFF on the four pumps connected. Also do I really need more registers, even tho it kind of worked, if I initialize it in the setup function as "ON" (0x01)?

Sadly I am unable to go to lab until thursday so I need to wait for my colleague to get me all the data that came with the board (and the picture).

Can you describe what is not working and what do you want it to do ?

You define commands with binary numbers, but perpaps you want to type ascii numbers ? and the C# uses text strings. You should use the same thing for everything.

What do you want for a command ? Command "A" is easy. Command "MODE_OFF" is not so easy, because you have to determine where the command begins and where it ends.
Do you want to be able to type the commands, to test the communication with a terminal ?

If you use a terminal, a line could be terminated with '\r' or with '\n' or with both or with none.

I prefer a start byte and stop byte. That could be binary codes of stx and etx, but also readable bytes, for example "#A150!" to transmit command that 'A' must be set to 150.
There is also code to read serial commands, I can't find it right now, but they are mentioned now and then on this forum.

As I understand it it should run trough the switch statements based on input, but I never manage to figure out how to produce the right input to make it work.

#define TASTER          3

pinMode (TASTER,INPUT_PULLUP);

int mode = 0;
#define MODE_OFF  0
#define MODE_50   1
#define MODE_100  2
#define MODE_200  3
#define MODE_400  4
#define MODE_800  5
#define MODE_MAX  6


 if (digitalRead(TASTER)==LOW) {
    if (!(debounce&0x01)) {
      mode=(mode+1)%MODE_MAX;
      switch (mode) {

There should be a push button (connected between pin3 and ground), and each time it is pushed it will change the mode. Do you have the switch installed, and does it change the frequencies?

And if anyone can make some sense from the original code from the top of this post and explain it (the loop() part) to me, that would be most helpful as well

The loop just looks for input from the push button, switches case based on the number of pushes, and sets the registers for on/off and frequency(pump soeed?) using 12c commands. What specifically do you not understand?

Does anyone see some way to modify the original arduino code from up so it could receive the input from serial port.

What happened with your attempt

if (Serial.available() > 0) { // Check to see if there is a new message
      mode = Serial.read(); // Put the serial input into the message
      switch (mode) 
          {

Try change the code to input 0-10 instead of ascii and don't switch if there is not a new mode

byte mode;
byte lastMode;

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0)
    mode = Serial.read() - '0';
  if (mode != lastMode)
  {
    switch (mode)
    {
      case 1:
        Serial.println("mode 1");
        lastMode = 1;
        break;
      case 2:
        Serial.println("mode 2");
        lastMode = 2;
        break;
      case 3:
        Serial.println("mode 3");
        lastMode = 3;
        break;
      default:
        // if nothing else matches, do the default
        // default is optional
        break;
    }
  }
}

The user input is pretty poor in the code you inherited. You may want to work with a labeled rotary switch, or Serial input with prompts. I'd separate the user interface development from getting the device to work with basic Serial input from the monitor. You may want to review Robin2's excellent tutorial on Serial input.

Koepel: What I trying to do is send a simple command that would say. Start the pump with this frequency (Hz) so that it is possible to control the speed of the fluid and also to be able to switch it off and on. (So i dont need to upload entire code to the board again ).

I dont really need to be "comand_off" it it can be called comand "A", but what I need to be able is to send and input lets say commmand "A", which would start the mode with frequency 50Hz etc. Best if I can recive this inpute from serial port so I can create window-form aplication which would make it morea eassly to controle for people who dont know a think abbout code. Yes I would like to be able to tape that comand or send it from the C#.

cattledog: yes there is Push button on the side of the massflow controler, but it is very inconvenient way of controling it becouse each push go from one mode to the other (from off to max), which is not good in terms of precision. (And with microfluid there is apparently great need for precision).
Since I never used Arduino before I didn't realize that loop looks for input from button. Pardon my
mistake.
My attempt to modify it as I posted didn't do anything. Perhaps because I tried to send "MODE OFF" etc. which was not taken as full string?
But knowing that this loop was asking for the input from button I might try to modify it and send only case 1 case 2 etc. as per your suggestion. Sadly I going to get my hand on that board in Thursday not sooner, so I dont really see anz changes before that. But thank you very much for your advices :).

The loop() runs over and over again, run Serial.available() in the loop().
If something is available, perhaps check for isalpha().
After that it is possible to read the number with a parse function : Serial.parseInt() - Arduino Reference
Those parse functions have a timeout. If you don't use them, you have make some kind of timing to allow that the whole command is received.

I prefer that you add a newline to every line. It can be useful when debugging.

For example "A\n" for stop and "F500\n" for frequency set to 500.

void loop()
{
  if( Serial.available() > 0)
  {
    int c = Serial.read();
    if( isalpha(c))
    {
      switch( c)
      {
      case 'S':
        // Stop
        ...
        break;
      case 'F':
        // The number of HZ followes the 'F'
        int frequency = Serial.parseInt();
        ...
        break;
      }
    }
  }
}

Would this do the trick? (As I said i have no way to test it on the actual hardware until thursday)

#include <Wire.h>

#define Addr (0xF6>>1)
#define TASTER          3
#define LED             13
#define I2C_DEVICEID  0x00
#define I2C_POWERMODE 0x01
#define I2C_FREQUENCY 0x02
#define I2C_SHAPE 0x03
#define I2C_BOOST 0x04
#define I2C_PVOLTAGE  0x06
#define I2C_P3VOLTAGE 0x06
#define I2C_P2VOLTAGE 0x07
#define I2C_P1VOLTAGE 0x08
#define I2C_P4VOLTAGE 0x09
#define I2C_UPDATEVOLTAGE 0x0A
#define I2C_AUDIO       0x05

int debounce = 0 ; 

#define MODE_OFF  0
#define MODE_50   1
#define MODE_100  2
#define MODE_200  3
#define MODE_400  4
#define MODE_800  5
#define MODE_MAX  6

// the setup routine runs once when you press reset:
void setup() {

                     
  // initialize the digital pin as an output.  
  Wire.begin();
  Serial.begin(9600);
  Serial.println();
  pinMode(LED, OUTPUT);
  pinMode (TASTER,INPUT_PULLUP);
  Wire.beginTransmission(Addr);
  Wire.write(I2C_POWERMODE);                  // write address = 0x01
  Wire.write(0x00);                             // Adress 0x01 = 0x01 (enable)
  Wire.write(0x40);                             // Adress 0x02 = 0x40 (100Hz)
  Wire.write(0x00);                             // Adress 0x03 = 0x00 (sine wave)
  Wire.write(0x00);                             // Adress 0x04 = 0x00 (800KHz)
  Wire.write(0x00);                             // Adress 0x05 = 0x00 (audio off)
  Wire.write(0x1F);                             // Adress 0x06 = 0x00 (EL1)
  Wire.write(0x1F);                             // Adress 0x07 = 0x00 (EL2)
  Wire.write(0x1F);                             // Adress 0x08 = 0x00 (EL3)
  Wire.write(0x1F);                             // Adress 0x09 = 0x00 (EL4)
  Wire.write(0x01);                             // Adress 0x0A = 0x00 (update)
  Wire.endTransmission();
}


// the loop routine runs over and over again forever:
void loop() 
{
 if( Serial.available() > 0)
  {
    int c = Serial.read();
    if( isalpha(c))
    {
      switch(c)
      {
        case 'S':
         Wire.beginTransmission(Addr);
         Wire.write(I2C_POWERMODE);   // start adress
         Wire.write(0x00);            // disable
         Wire.endTransmission();
         digitalWrite(LED,LOW);
         break;  
      

      case 'A': // 50Hz
            Wire.beginTransmission(Addr);
          Wire.write(I2C_POWERMODE);   // start adress
          Wire.write(0x01);            // enable
          Wire.write(0x00);            // 50 Hz
          Wire.endTransmission();
          digitalWrite(LED,HIGH);
          break;

      case 'B': // 100Hz
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0x40);            // 100 Hz
          Wire.endTransmission();
          break;

      case 'C': // 200Hz
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0x80);            // 200 Hz
          Wire.endTransmission();
          break;

      case 'D': // 400 Hz
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0xC0);            // 400 Hz
          Wire.endTransmission();
          break;    

      case 'M': // maximum 800Hz
          Wire.beginTransmission(Addr);
          Wire.write(I2C_FREQUENCY);   // start adress
          Wire.write(0xFF);            // 800 Hz
          Wire.endTransmission();
          break;     
        }  
    }
}
delay(100);
}

Because never worked with code that would have to be send to external hardware so I have no idea what would happen if I try to give the frequency actual number as value instead of using 0x01, 0xC0 etc.

Perhaps this is more like what you had in mind?

void loop() {
  
   if( Serial.available() > 0)
  {
    int c = Serial.read();
    if( isalpha(c))
    {
      switch( c)
    {

    case 'S':
  
     Wire.beginTransmission(Addr);
     Wire.write(I2C_POWERMODE);   // start adress
     Wire.write(0x00);            // disable
     Wire.endTransmission();
      digitalWrite(LED,LOW);
      break;
  

  case 'F':    
    int frequency = Serial.parseInt();
    String frequencyHex =  String(frequency, HEX); // convert input into hexadecimal
     frequency = frequencyHex.toInt(); // conver string with hexadecimal into integeer
    
        Wire.beginTransmission(Addr);
          Wire.write(I2C_POWERMODE);   // start adress
          Wire.write(0x01);            // enable
          Wire.write(frequency);    // set the frequency with hexadecimal value    
          Wire.endTransmission();
          digitalWrite(LED,HIGH);
          break;
  }
 delay(2000); // dealy for 2 seconds

}
  }
}

I converting the in for frequency into hexadecimal (trough string) then back from string into integger so it can be used in Wire.write();

if I try to give the frequency actual number as value instead of using 0x01, 0xC0 etc.

Wire.write() expects a byte, and it does not matter if the number is represented in binary, decimal or hex. Wire.write(128), wire.write(B10000000) and wire.write(0x80) are all the same if you wanted to set the frequency to 200Hz.

String frequencyHex =  String(frequency, HEX); // convert input into hexadecimal
     frequency = frequencyHex.toInt(); // conver string with hexadecimal into integeer

Forget about this stuff. First, you do not need the String objects to achieve what you need. You do not need the hex representation of the integer.

Did you study Robin2"s tutorial on Serial input? Follow his methods. You are designing the interface, so decide if you want the user to enter letters, numbers, word commands, whatever. Prompt the user, or provide a menu. Once you design in detail how you want the user input to be, the code will be straightforward.

It is best to have a start and end marker, and to wait for the end marker before you act on the command

To split an integer (from parseInt) into two bytes, you can use lowByte and highByte.
https://www.arduino.cc/en/Reference/LowByte
https://www.arduino.cc/en/Reference/HighByte

Don't use a delay in the loop(). Call Wire.available() as many times as possible.

This is Robin2's tutorial on Serial (which I could not find in my Reply #4) : https://forum.arduino.cc/index.php?topic=288234.0

The other tutorial is (of course) from Nick Gammon : Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking

Have a look at both tutorials.
The 'parse' functions were added later on. I don't use them often, but when I do, I set the timeout to 20ms or 100ms or so. The 'parse' functions are blocking, that means the Arduino can not run other code while waiting for the end of the serial input. I think they will be okay for your project.
The 'SerialEvent()' was also added, but we don't like it, since it is not an interrupt but only a polling function during every run of loop().