software:firmware
MakAir Firmware
mass_flow_meter.h File Reference

Mass flow meter management. More...

#include <Arduino.h>

Go to the source code of this file.

Macros

#define MFM_CALIBRATION_OK   0
 
#define MFM_CALIBRATION_IMPOSSIBLE   1
 
#define MFM_CALIBRATION_OUT_OF_RANGE   2
 
#define MFM_SFM_3300D_I2C_ADDRESS   0x40
 
#define MFM_HONEYWELL_HAF_I2C_ADDRESS   0x49
 
#define MFM_SDP703_02_I2C_ADDRESS   0x40
 
#define MFM_SFM3019_I2C_ADDRESS   0x2E
 
#define MFM_FORCE_RELEASE_I2C_TRUE   0xb00b
 
#define MFM_FORCE_RELEASE_I2C_FALSE   0
 

Functions

bool MFM_init (void)
 Initialize Mass Flow Meter. More...
 
int32_t MFM_read_milliliters (bool reset_after_read)
 Get the number of milliliters since last reset. More...
 
int32_t MFM_expi_read_milliliters (bool reset_after_read)
 Get the number of milliliters since last reset for expiratory sensor. More...
 
void MFM_reset (void)
 Reset the volume counter. More...
 
int8_t MFM_calibrateZero (void)
 Calibrate the zero of the sensor. More...
 
int32_t MFM_getOffset (void)
 Get massflow meter offset. More...
 
int32_t MFM_read_airflow (void)
 Read instant air flow. More...
 
int32_t MFM_expi_read_airflow (void)
 Read instant air flow. More...
 
uint32_t MFM_read_serial_number (void)
 Get the serial number of the inspiratory flow meter. More...
 
uint32_t MFM_expi_read_serial_number (void)
 Get the serial number of the expiratory flow meter. More...
 

Variables

volatile uint16_t MFM_force_release_I2C
 

Detailed Description

Mass flow meter management.

Author
Makers For Life

Definition in file mass_flow_meter.h.

Macro Definition Documentation

◆ MFM_CALIBRATION_IMPOSSIBLE

#define MFM_CALIBRATION_IMPOSSIBLE   1

Definition at line 51 of file mass_flow_meter.h.

◆ MFM_CALIBRATION_OK

#define MFM_CALIBRATION_OK   0

Definition at line 50 of file mass_flow_meter.h.

◆ MFM_CALIBRATION_OUT_OF_RANGE

#define MFM_CALIBRATION_OUT_OF_RANGE   2

Definition at line 52 of file mass_flow_meter.h.

◆ MFM_FORCE_RELEASE_I2C_FALSE

#define MFM_FORCE_RELEASE_I2C_FALSE   0

Definition at line 91 of file mass_flow_meter.h.

◆ MFM_FORCE_RELEASE_I2C_TRUE

#define MFM_FORCE_RELEASE_I2C_TRUE   0xb00b

Definition at line 90 of file mass_flow_meter.h.

◆ MFM_HONEYWELL_HAF_I2C_ADDRESS

#define MFM_HONEYWELL_HAF_I2C_ADDRESS   0x49

Definition at line 84 of file mass_flow_meter.h.

◆ MFM_SDP703_02_I2C_ADDRESS

#define MFM_SDP703_02_I2C_ADDRESS   0x40

Definition at line 85 of file mass_flow_meter.h.

◆ MFM_SFM3019_I2C_ADDRESS

#define MFM_SFM3019_I2C_ADDRESS   0x2E

Definition at line 86 of file mass_flow_meter.h.

◆ MFM_SFM_3300D_I2C_ADDRESS

#define MFM_SFM_3300D_I2C_ADDRESS   0x40

Definition at line 83 of file mass_flow_meter.h.

Function Documentation

◆ MFM_calibrateZero()

int8_t MFM_calibrateZero ( void  )

Calibrate the zero of the sensor.

Returns
One of:
  • MFM_CALIBRATION_OK: all right
  • MFM_CALIBRATION_IMPOSSIBLE: communication problem
  • MFM_CALIBRATION_OUT_OF_RANGE: unbelievable 10SLPM sensor drift; time to change it?
Note
This uses the mean of 10 samples

Calibrate the zero of the sensor.

Definition at line 604 of file mass_flow_meter.cpp.

604  {
605  int8_t ret = MFM_CALIBRATION_OK;
606  // activate table fill with last valid value
609  // wait for the table to fill in
610  delay(2 + (MFM_MEAN_SAMPLES * (MASS_FLOW_PERIOD / 10)));
611  // Check that table is full (record must be false)
612  // If it is not, there is a sensor problem
613  // In case of problem, do not update mfmInspiratoryCalibrationOffset
615  int32_t zeroFlow = 0;
616  for (int16_t i = 0; i < MFM_MEAN_SAMPLES; i++) {
618  }
619  zeroFlow /= MFM_MEAN_SAMPLES;
620  // check that value is credible: [-10 10] SLPM
621  if ((zeroFlow < 10000) && (zeroFlow > -10000)) {
623  } else {
625  }
626  } else {
628  }
629  return ret;
630 }
volatile int32_t mfmInspiratoryInstantAirFlowLastValues[MFM_MEAN_SAMPLES]
volatile int32_t mfmInspiratoryCalibrationOffset
uint16_t i
#define MASS_FLOW_PERIOD
volatile int16_t mfmInspiratoryInstantAirFlowLastValuesIndex
#define MFM_MEAN_SAMPLES
volatile bool mfmInspiratoryInstantAirFlowRecord
#define MFM_CALIBRATION_IMPOSSIBLE
#define MFM_CALIBRATION_OUT_OF_RANGE
#define MFM_CALIBRATION_OK

◆ MFM_expi_read_airflow()

int32_t MFM_expi_read_airflow ( void  )

Read instant air flow.

Definition at line 566 of file mass_flow_meter.cpp.

566  {
567  int32_t r;
568  if (mfmFaultCondition) {
570  } else {
572  }
573  return r;
574 }
volatile int32_t mfmExpiratoryInstantAirFlow
volatile int32_t mfmExpiratoryCalibrationOffset
volatile bool mfmFaultCondition
#define MASS_FLOW_ERROR_VALUE
Definition: parameters.h:302

◆ MFM_expi_read_milliliters()

int32_t MFM_expi_read_milliliters ( bool  reset_after_read)

Get the number of milliliters since last reset for expiratory sensor.

Parameters
reset_after_readIf true, performs the volume reset in the same atomic operation
Returns
Volume of air that went through sensor since last reset in mL

Definition at line 654 of file mass_flow_meter.cpp.

654  {
655  int32_t result = MASS_FLOW_ERROR_VALUE;
656 
657 #if MASS_FLOW_METER_SENSOR_EXPI == MFM_SFM_3300D
658  result = mfmFaultCondition
661 #endif
662  if (reset_after_read) {
663  MFM_expi_reset();
664  }
665 
666  return result;
667 }
volatile int32_t mfmExpiratoryAirVolumeSumMilliliters
void MFM_expi_reset(void)

◆ MFM_expi_read_serial_number()

uint32_t MFM_expi_read_serial_number ( void  )

Get the serial number of the expiratory flow meter.

Returns
The serial number, or 0 if before init or if init failed

Get the serial number of the expiratory flow meter.

Note
returns 0 before init, or if init failed.

Definition at line 597 of file mass_flow_meter.cpp.

597 { return mfmSfm3300SerialNumberExpi; }
uint32_t mfmSfm3300SerialNumberExpi

◆ MFM_getOffset()

int32_t MFM_getOffset ( void  )

Get massflow meter offset.

Definition at line 635 of file mass_flow_meter.cpp.

◆ MFM_init()

bool MFM_init ( void  )

Initialize Mass Flow Meter.

Returns
True if there is a Mass Flow Meter connected
Warning
If no Mass Flow Meter is detected, you will always read volume = 0 mL

Definition at line 321 of file mass_flow_meter.cpp.

321  {
323  // cppcheck-suppress unreadVariable
324  uint32_t errorCount = 0;
325  // Set power on (hardware v3)
326  pinMode(MFM_POWER_CONTROL, OUTPUT);
327  digitalWrite(MFM_POWER_CONTROL, MFM_POWER_ON);
328  delay(100); // sfm3300 worst case boot time.
329 
330  // Set the timer
331  massFlowTimer = new HardwareTimer(MASS_FLOW_TIMER);
332 
333  // Prescaler; stm32f411 clock is 100 mHz
334  massFlowTimer->setPrescaleFactor((massFlowTimer->getTimerClkFreq() / MASS_FLOW_TIMER_FREQ) - 1);
335 
336  // Set the period
337  massFlowTimer->setOverflow(MASS_FLOW_PERIOD);
338  massFlowTimer->setMode(MASS_FLOW_CHANNEL, TIMER_OUTPUT_COMPARE, NC);
339  massFlowTimer->attachInterrupt(MFM_Timer_Callback);
340 
341  // Interrupt priority is documented here:
342  // https://stm32f4-discovery.net/2014/05/stm32f4-stm32f429-nvic-or-nested-vector-interrupt-controller/
343  // WARNING : since 1.9.0 lib, I2C is on level 2. must be under...
344  massFlowTimer->setInterruptPriority(3, 0);
345 
346  // default Wire instance is on PB8 BP9, anyway
347  Wire.setSDA(PIN_I2C_SDA);
348  Wire.setSCL(PIN_I2C_SCL);
349  // Wire.setClock(400000); // honeywell do support, but no information about sfm3300d
350 
351 #if MASS_FLOW_METER_SENSOR == MFM_SFM3019
352  Wire.begin();
353  Wire.beginTransmission(0x00);
354  Wire.write(0x06);
355  Wire.endTransmission();
356  Wire.end();
357  delay(4);
358 
359  // // start air continuous measurement
360  // Wire.begin();
361  // Wire.beginTransmission(MFM_SFM3019_I2C_ADDRESS);
362  // Wire.write(0x36);
363  // Wire.write(0x08);
364  // errorCount += Wire.endTransmission();
365  // Wire.end();
366 
367  // delay(2);
368  // //Stop continuous measurement 0x3FF9
369  // Wire.begin();
370  // Wire.beginTransmission(0x2E);
371  // Wire.write(0x3F);
372  // Wire.write(0xF9);
373  // Wire.endTransmission();
374  // Wire.end();
375 
376  // delay(2);
377  // //Read Scale Factor, Offset, and Flow Unit 0x3661
378  // Wire.begin();
379  // Wire.beginTransmission(0x2E);
380  // Wire.write(0x36);
381  // Wire.write(0x61);
382  // Wire.endTransmission();
383  // Wire.end();
384  // delay(2);
385  // Wire.begin();
386  // int ccc = Wire.requestFrom(0x2E, 8);
387  // Serial.print(ccc);
388  // Serial.print(" ");
389  // Serial.print(Wire.read()); Serial.print(",");
390  // Serial.print(Wire.read()); Serial.print(",");
391  // Serial.print(Wire.read()); Serial.print(",");
392  // Serial.print(Wire.read()); Serial.print(",");
393  // Serial.print(Wire.read()); Serial.print(",");
394  // Serial.print(Wire.read()); Serial.print(",");
395  // Serial.print(Wire.read()); Serial.print(",");
396  // Serial.print(Wire.read()); Serial.print(",");
397 
398  // Wire.end();
399 
400  delay(5);
401  // Read serial number
402  Wire.begin();
403  Wire.beginTransmission(MFM_SFM3019_I2C_ADDRESS);
404  Wire.write(0xE1);
405  Wire.write(0x02);
406  errorCount = Wire.endTransmission();
407 
408  delay(1);
409  errorCount += ((18u == Wire.requestFrom(MFM_SFM3019_I2C_ADDRESS, 18)) ? 0u : 1u);
410  if (errorCount == 0u) {
411  // the serial number is 64 bits wide, but it will never be used until year 2042.
412  // The serial number can be converted from binary into decimal,
413  // whereby in decimal it has the following format::
414  // yywwxxxxxx, where: yy: last to digits of calibration year, ww:
415  // calibration week, xxxxxx: unique 6-digit sequential number
416  // within the calibration week.
417  uint32_t sn_inspi = 0;
418  Wire.read();
419  Wire.read(); // product number part 1
420  Wire.read(); // crc
421  Wire.read();
422  Wire.read(); // product number part 2
423  Wire.read(); // crc
424 
425  Wire.read();
426  Wire.read(); // ignore this part of serial
427  Wire.read(); // ignore inlined crc
428  Wire.read();
429  Wire.read(); // ignore this part of serial
430  Wire.read(); // ignore inlined crc
431  sn_inspi |= Wire.read();
432  sn_inspi <<= 8;
433  sn_inspi |= Wire.read();
434  sn_inspi <<= 8;
435  Wire.read(); // ignore inlined crc
436  sn_inspi |= Wire.read();
437  sn_inspi <<= 8;
438  sn_inspi |= Wire.read();
439  Wire.read(); // ignore inlined crc
440  mfmSfm3019SerialNumber = sn_inspi;
441  }
442  delay(1);
443 
444  // start air continuous measurement
445  Wire.begin();
446  Wire.beginTransmission(MFM_SFM3019_I2C_ADDRESS);
447  Wire.write(0x36);
448  Wire.write(0x08);
449  errorCount += Wire.endTransmission();
450  Wire.end();
451 
452  delay(40); // the first measurement result will be available after 12ms
453  // small accuracy deviations (few % of reading) can occur during the first 30ms
454 
455  // delay(10000);
456  if (errorCount != 0u) {
457  mfmFaultCondition = true;
459  }
460 #endif
461 
462 #if MASS_FLOW_METER_SENSOR_EXPI == MFM_SFM_3300D
463  Wire.begin(); // Join I2C bus (address is optional for master)
464  Wire.beginTransmission(MFM_SFM_3300D_I2C_ADDRESS);
465  Wire.write(0x20); // 0x2000 soft reset
466  Wire.write(0x00);
467  errorCount = Wire.endTransmission();
468  delay(5); // end of reset
469 
470  Wire.beginTransmission(MFM_SFM_3300D_I2C_ADDRESS);
471  Wire.write(0x31); // 0x31AE read serial
472  Wire.write(0xAE);
473  errorCount += Wire.endTransmission();
474 
475  errorCount += ((6u == Wire.requestFrom(MFM_SFM_3300D_I2C_ADDRESS, 6)) ? 0u : 1u);
476  if (errorCount == 0u) {
477  u_int32_t sn_expi = 0;
478  sn_expi = Wire.read();
479  sn_expi <<= 8;
480  sn_expi |= Wire.read();
481  sn_expi <<= 8;
482  Wire.read(); // ignore inlined crc
483  sn_expi |= Wire.read();
484  sn_expi <<= 8;
485  sn_expi |= Wire.read();
486  Wire.read(); // ignore inlined crc
487  mfmSfm3300SerialNumberExpi = sn_expi;
488  }
489  delay(10);
490  Wire.beginTransmission(MFM_SFM_3300D_I2C_ADDRESS);
491  Wire.write(0x10); // 0x1000 start measurement
492  Wire.write(0x00);
493  errorCount += Wire.endTransmission();
494  Wire.end();
495  delay(100); // wait 100ms before having available data.
496 
497  if (errorCount != 0u) {
498  mfmFaultCondition = true;
500  }
501 #endif
502 
503 #if MASS_FLOW_METER_SENSOR == MFM_HONEYWELL_HAF
504 
505  /*
506  Init sequence for Honeywell Zephyr mass flow sensor:
507  1st read operation: the sensor will send 0x0000
508  2nd read operation: the sensor will send the first part of the serial number
509  3rd read operation: the sensor will send the second part of the serial number
510  Subsequent read operations: the sensor will send calibrated mass air flow values with two
511  leading 0
512  */
513  Wire.begin();
514  Wire.beginTransmission(MFM_HONEYWELL_HAF_I2C_ADDRESS);
515  Wire.write(0x02); // Force reset
516  uint8_t txOk = Wire.endTransmission();
517  Wire.end();
518  delay(30);
519 
520  u_int32_t sn = 0;
521  Wire.begin();
522  Wire.beginTransmission(MFM_HONEYWELL_HAF_I2C_ADDRESS);
523  uint8_t rxcount = Wire.requestFrom(MFM_HONEYWELL_HAF_I2C_ADDRESS, 2);
524  sn = Wire.read();
525  sn <<= 8;
526  sn |= Wire.read(); // first transmission is serial number register 0
527  sn <<= 8;
528  delay(2); // if you do not wait, sensor will send again register 0
529  rxcount += Wire.requestFrom(MFM_HONEYWELL_HAF_I2C_ADDRESS, 2);
530  sn |= Wire.read();
531  sn <<= 8;
532  sn |= Wire.read(); // second transmission is serial number register 1
533 
534  if ((txOk != 0u) || (rxcount != 4u)) { // If transmission failed
535  mfmFaultCondition = true;
537  } else {
539  }
540  Wire.end();
541 
542 #if MODE == MODE_MFM_TESTS
543  Serial.println("Read 1");
544  Serial.println(mfmLastData.i);
545  Serial.println("fault condition:");
546  Serial.println(mfmFaultCondition ? "failure" : "no failure");
547 #endif
548  delay(100);
549 #endif
550 
551  massFlowTimer->resume();
552  return !mfmFaultCondition;
553 }
uint32_t mfmSfm3019SerialNumber
volatile int32_t mfmInspiratoryAirVolumeSumMilliliters
union @0 mfmLastData
HardwareTimer * massFlowTimer
int32_t mfmResetStateMachine
#define MASS_FLOW_TIMER_FREQ
void MFM_Timer_Callback(HardwareTimer *)
#define MFM_WAIT_RESET_PERIODS
uint32_t mfmHoneywellHafSerialNumber
#define MFM_SFM_3300D_I2C_ADDRESS
#define MFM_SFM3019_I2C_ADDRESS
#define MFM_HONEYWELL_HAF_I2C_ADDRESS
#define MASS_FLOW_CHANNEL
Definition: parameters.h:294
#define MASS_FLOW_TIMER
Definition: parameters.h:293
#define PIN_I2C_SCL
Definition: parameters.h:296
#define MFM_POWER_ON
Definition: parameters.h:300
#define MFM_POWER_CONTROL
Definition: parameters.h:298
#define PIN_I2C_SDA
Definition: parameters.h:295

◆ MFM_read_airflow()

int32_t MFM_read_airflow ( void  )

Read instant air flow.

Definition at line 555 of file mass_flow_meter.cpp.

555  {
556  int32_t r;
557  if (mfmFaultCondition) {
559  } else {
561  }
562  return r;
563 }
volatile int32_t mfmInspiratoryInstantAirFlow

◆ MFM_read_milliliters()

int32_t MFM_read_milliliters ( bool  reset_after_read)

Get the number of milliliters since last reset.

Parameters
reset_after_readIf true, performs the volume reset in the same atomic operation
Returns
Volume of air that went through sensor since last reset in mL

Definition at line 637 of file mass_flow_meter.cpp.

637  {
638  int32_t result = MASS_FLOW_ERROR_VALUE;
639 
640 #if MASS_FLOW_METER_SENSOR == MFM_HONEYWELL_HAF || MASS_FLOW_METER_SENSOR == MFM_SFM3019
641  // period is MASS_FLOW_PERIOD / 10000 (100 µs prescaler)
642  result = mfmFaultCondition
645 #endif
646 
647  if (reset_after_read) {
648  MFM_reset();
649  }
650 
651  return result;
652 }
void MFM_reset(void)
Reset the volume counter.

◆ MFM_read_serial_number()

uint32_t MFM_read_serial_number ( void  )

Get the serial number of the inspiratory flow meter.

Returns
The serial number, or 0 if before init or if init failed

Definition at line 581 of file mass_flow_meter.cpp.

581  {
582 #if MASS_FLOW_METER_SENSOR == MFM_HONEYWELL_HAF
584 #elif MASS_FLOW_METER_SENSOR == MFM_SFM3019
585  return mfmSfm3019SerialNumber;
586 #endif
587  return 0;
588 }

◆ MFM_reset()

void MFM_reset ( void  )

Reset the volume counter.

Definition at line 576 of file mass_flow_meter.cpp.

Variable Documentation

◆ MFM_force_release_I2C

volatile uint16_t MFM_force_release_I2C
extern

Definition at line 32 of file mass_flow_meter.cpp.