Multi-function shield for Arduino

Arduino Multi-function Shield Part 2

Reading Sensors

This is Part 2 of the Applied Hackatronics Series for the Arduino Multi-function shield (following on from Part 1), and shows how the multi-function shield library can be used to read values from external sensors, such as temperature, sonar and motion sensors, and how to process electronic pulses from an external source. If you haven’t already done so, you’ll need to download the source code and install the libraries using the links in the introduction.

By following the Hackatronics series, you agree to do so at your own risk, and agree to take full responsibility for any loss or damages you may incur upon yourself or others.

Counting pulses

The multi-function shield library has support for counting pulses (up to 500Hz) applied to an input pin of the Arduino. The counting of pulses is managed in the background using interrupts, which allows your application to focus on performing its main task. After uploading this sketch, repeatedly press button 1 to generate the pulses and see a reading of the press rate on the digit display.

#include <TimerOne.h>
#include <Wire.h>
#include <MultiFuncShield.h>

void setup() {
  // put your setup code here, to run once:
  Timer1.initialize();
  MFS.initialize(&Timer1);  // initialize multi-function shield library
  
  MFS.initPulseInCounter(
      BUTTON_1_PIN,      // use button 1 as means of generating pulses.
      1500,              // the number of milliseconds to wait for a pulse, before resetting pulse in period to 0.
      LOW                // trigger pulse on LOW input.
  );
}

void loop() {
  // put your main code here, to run repeatedly:

  // Get the period of the most recent pulse (in milliseconds).
  // NOTE: pulse measurements are actually performed using interrupts.
  unsigned int pulsePeriodMs = MFS.getPulseInPeriod();
  
  if (pulsePeriodMs == 0)
  {
    MFS.write(0.0, 1);
  }
  else
  {
    MFS.write(1000.0 / pulsePeriodMs, 1);  // calculate pulses per second. Display to 1 decimal place.
  }
}

Reading the temperature using an LM35 sensor

Arduino and LM35 temperature sensor.

Arduino and LM35 temperature sensor.

The multi-function shield has a socket for accepting an LM35 temperature sensor, which must be inserted correctly, otherwise you may irreparably damage your Arduino, or computer connected to it. You will know when the sensor is incorrectly connected, because it will become very hot. The multi-function shield library provides three levels of filtering for smoothing the readings from this sensor.

#include <TimerOne.h>
#include <Wire.h>
#include <MultiFuncShield.h>

// NOTE: make sure jumper J1 is removed from shield, and that LM35 is inserted correctly.

void setup() {
  // put your setup code here, to run once:
  Timer1.initialize();
  MFS.initialize(&Timer1);  // initialize multi-function shield library
  
  // Initialize using a low pass filter.
  // Choose either: SMOOTHING_NONE, SMOOTHING_MODERATE or SMOOTHING_STRONG
  MFS.initLM35(SMOOTHING_MODERATE);
}

void loop() {
  // put your main code here, to run repeatedly:

  int tempCentigrade = MFS.getLM35Data(); // get centigrade in 1/10 of degree.
  MFS.write((float)tempCentigrade / 10, 1);  // display temp to 1 decimal place.
  delay(100);
}

Using an HC SR04 sonar module

Multi-function shield and HC-SR04.

Multi-function shield and HC-SR04.

When using an HC SR04 sonar module, it is advisable to use the multi-function shield library to read and calculate distance values if interrupts are turned on. The reason for this is that the library’s interrupt service routine affects the timing requirements of this sonar module, and as such the library compensates for it. The library also offers three levels of filtering for smoothing the readings from the sonar module. The trigger and echo pins of the sonar module are connected to Arduino pins 5 and 6 respectively.

#include <TimerOne.h>
#include <Wire.h>
#include <MultiFuncShield.h>

const int TrigPin = 5;
const int EchoPin = 6;

void setup() {
  // put your setup code here, to run once:

  pinMode(TrigPin, OUTPUT);
  pinMode(EchoPin, INPUT);
  
  Timer1.initialize();
  MFS.initialize(&Timer1);  // initialize multi-function shield library
  
  // initialize with low pass filter: SMOOTHING_NONE, SMOOTHING_MODERATE or SMOOTHING_STRONG
  MFS.initSonar(SMOOTHING_MODERATE);
}

void loop() {
  // put your main code here, to run repeatedly:
  MFS.write((int)MFS.getSonarDataCm(TrigPin, EchoPin));
  delay(100);
}

Getting data from an MPU6050 motion sensor

Multi-function shield and MPU6050.

Multi-function shield and MPU6050.

The multi-function shield does not expose the hardware I2C pins of some Arduino boards, such as the Leonardo. The following sketch uses the shield library to read raw values from the MPU6050 sensor, using either the Wire library or a software implementation of I2C. Download the full source code to upload the sketch to an Arduino, after which press button 1 on the shield to toggle reporting of sensor data, and press button 2 to cycle through acceleration, gyro and temperature values.

#define _SOFTI2C_H
#include <SoftI2CMaster.h>
#include <Wire.h>
#include <TimerOne.h>
#include <MultiFuncShield.h>

#include "SoftwareI2C.h"
#include "I2C.h"
#include "MPU6050.h"

#define SOFTWARE_I2C

MPU6050 MPU;
void displayHeading(byte mode);

void setup() {
  Serial.begin(9600);
  
  // put your setup code here, to run once:
  Timer1.initialize();

#if defined (SOFTWARE_I2C)
  // Use software I2C
  // Uno and Leonardo, use pin 5 for SCL and 6 for SDA. Mega2560, use pin A5 for SDA.
  SoftI2C1.initialize();
  MPU.initialize(&SoftI2C1, MPU_DEFAULT_ADDRESS << 1);
#else
  // Use hardware I2C
  Wire.begin();
  I2C1.initialize(&Wire);
  MPU.initialize(&I2C1, MPU_DEFAULT_ADDRESS);
#endif

  MFS.initialize(&Timer1);
  MFS.write("Acc");
}

byte displayValues = true;
byte displayMode = 0;


void loop() {
  // put your main code here, to run repeatedly:

  byte btn = MFS.getButton();

  // Use button 1 to toggle reporting of sensor values.
  if (btn == BUTTON_1_PRESSED)
  {
    displayValues = !displayValues;
    
    if (displayValues)
    {
      displayHeading(displayMode);
    }
    else
    {
      MFS.write("Off");
    }
  }
  
  if (displayValues)
  {
    // Use button 2 to cycle though the display modes.
    if (btn == BUTTON_2_PRESSED)
    {
      displayMode++;
      
      if (displayMode == 3)
      {
        displayMode = 0;
      }
      
      displayHeading(displayMode);
    }
    
    if (displayMode == 0)
    {
      // display raw acceleration values.
      MPU.getAccelRaw();
      Serial.print((float)MPU.accel_X_Raw / MPU.accelScaleValue);
      Serial.print("\t");
      Serial.print((float)MPU.accel_Y_Raw / MPU.accelScaleValue);
      Serial.print("\t");
      Serial.print((float)MPU.accel_Z_Raw / MPU.accelScaleValue);
      Serial.print("\t\n");
    }
    else if (displayMode == 1)
    {
      // display raw gyrovalues
      MPU.getGyroRaw();
      Serial.print((float)MPU.gyro_X_Raw / MPU.gyroScaleValue);
      Serial.print("\t");
      Serial.print((float)MPU.gyro_Y_Raw / MPU.gyroScaleValue);
      Serial.print("\t");
      Serial.print((float)MPU.gyro_Z_Raw / MPU.gyroScaleValue);
      Serial.print("\t\n");
    }
    else if (displayMode == 2)
    {
      // display temperature value.
      Serial.println((float)MPU.getTemp10th() / 10);
    }
  }
  
  delay(50);
}

void displayHeading(byte mode)
{
  if (mode == 0)
  {
    Serial.println("Acceleration in g (1g = 9.8 m/s/s)");
    Serial.println("X\tY\tZ");
    MFS.write("Acc");
  }
  else if (mode == 1)
  {
    Serial.println("Gyro angular velocity in degrees / second");
    Serial.println("X\tY\tZ");
    MFS.write("Gyro");
  }
  else if (mode == 2)
  {
    Serial.println("Temperature in degrees celsius.");
    MFS.write("Te");
  }
}

All the code samples and applications have been tested and work. If you experience any difficulties, please leave a comment, and we’ll get back to you as soon as we can.

Share on LinkedInShare on Google+Tweet about this on TwitterShare on Facebook

11 thoughts on “Arduino Multi-function Shield Part 2

  1. Claudio

    Hello, I would like to know if you can bring up the point of separation between minutes and seconds. Can you show me how?

    Reply
    1. Kashif Baig Post author

      Hi Claudio, you could try replacing the following code in the countdown timer sketch:

      MFS.write(minutes*100 + seconds);

      with this:

      MFS.write((float)(minutes*100 + seconds)/100,2);

      A similar approach could be used with the 24 hour clock.

      Reply
  2. Claudio

    Thanks for the reply, but I have another request. For my timer I would need to bring up the “0” character when the tens of minutes end. I edited this line library
    displayText char [5] = {”, ”, ”, ”, 0};
    replacing the blanks with the character “0” and it worked, but now I’ve made the changes to the recommended code in the previous post when the timer reaches 9 minutes, the left display appears to be off.
    How can I do?

    Reply
    1. Kashif Baig Post author

      Claudio, you can try the following. Instead of modifying the library, add this function to your count down sketch:

      void displayCountdown(char mins, char sec)
      {
      char outstr[7];
      dtostrf((float)(mins*100 + sec)/100, 4, 2, outstr);

      if (mins < 10) { for (int i = strlen(outstr); i > 0; i--) outstr[i] = outstr[i-1];
      outstr[0] = '0';
      }
      MFS.write(outstr);
      }

      Replace all instances of the following:

      MFS.write(minutes*100 + seconds);

      With

      displayCountdown(minutes, seconds);

      See how that works.

      Reply
  3. Claudio

    Perfect!!! Thank you very much.
    As soon as I finish my little project will bring you the video to show you what I did. Thanks again, unfortunately, as you may have figured out yet is not much of a programmer.

    Reply
  4. Carlos Alberto Pinto

    Not work, the sketch of ‘Getting data from an MPU6050 motion sensor’, in de code, library ‘#include “SoftwareI2C.h”‘ not include in zip of librarys, in web no serch compatible, (not using this), this code in I2C incllude name of obj is I2C1 not this SoftI2C1 iis name obj of include “SoftwereI2C.h’ not present, please publish this lib for this sketch.

    My Board is Uno (Clone) whit chip (not smd)

    Reply
    1. Kashif Baig Post author

      Hi Carlos

      The file “SoftwareI2C.h” is included in the source code zip (download from here). I have just noticed that in recent Arduino IDEs the software I2C library has a problem compiling, so it may not work with your UNO. If you have Leonardo or MEGA2560, you can use hardware I2C by commenting the line #define SOFTWARE_I2C in file MPU6050.ino

      I hope that helps.

      Reply
  5. holger

    Hello Kashif,
    very nice demos and projcts on MFS.
    I like to installe a DS3231 RTC in connection with the 24 hour alarm clock.
    But im very new in programming …how can i get the time from the RTC and show it on the 7 segmant display?
    thank you
    Holger

    Reply
    1. Kashif Baig Post author

      Hi. Unfortunately, I haven’t used DS3231 RTC before, but I believe it uses I2C, which could be a problem if your Arduino board’s hardware I2C pins are already in use by the multi-function shield. I remember trying to use software I2C for a different RTC but couldn’t get it to work.

      Reply
      1. holger

        Hi Kashif…
        no it works with the Board…the A4 and A5 are free and i use the RTC with another skeetch…but i will get it work with the 24 hour alarm clock.
        I have connected the RTC and i can read out the time, but i have no idea how i can show the time on the 7 segment Led´s?

        Reply
  6. holger

    this is your code including my rtc code for serial output. both running togehter without problems, but i like to remove some lines and put the rtc time on the segments!?
    #include
    #include
    #include
    #define RTC_I2C_ADDRESS 0x68 // I2C Adresse des RTC DS3231

    //Membervariablen
    int jahr,monat,tag,stunde,minute,sekunde, wochentag;
    int daysInMonth[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    String daysInWeek[7] = {“Sonntag”, “Montag”, “Dienstag”, “Mittwoch”, “Donnerstag”, “Freitag”, “Samstag”};
    String monthInYear[12] = {“Januar”,”Februar”, “März”, “April”, “Mai”, “Juni”, “Juli”, “August”, “September”, “Oktober”, “November”, “Dezember”};
    String outputFormat = “%s, %02d.%s %04d %02d:%02d:%02d Uhr”;
    //Ließt den aktuellen Zeitstempel aus dem RTC Modul.
    void rtcReadTime(){
    Wire.beginTransmission(RTC_I2C_ADDRESS); //Aufbau der Verbindung zur Adresse 0x68
    Wire.write(0);
    Wire.endTransmission();
    Wire.requestFrom(RTC_I2C_ADDRESS, 7);
    sekunde = bcdToDec(Wire.read() & 0x7f);
    minute = bcdToDec(Wire.read());
    stunde = bcdToDec(Wire.read() & 0x3f);
    //Der Wochentag wird hier nicht ausgelesen da dieses mit
    //dem Modul RTC DS3231 nicht über die Wire.h zuverlässig funktioniert.
    /* wochentag =*/ bcdToDec(Wire.read());
    tag = bcdToDec(Wire.read());
    monat = bcdToDec(Wire.read());
    jahr = bcdToDec(Wire.read())+2000;
    }

    //Funktion zum schreiben / setzen der Uhrzeit.
    void rtcWriteTime(int jahr, int monat, int tag, int stunde, int minute, int sekunde){
    Wire.beginTransmission(RTC_I2C_ADDRESS);
    Wire.write(0); // Der Wert 0 aktiviert das RTC Modul.
    Wire.write(decToBcd(sekunde));
    Wire.write(decToBcd(minute));
    Wire.write(decToBcd(stunde));
    Wire.write(decToBcd(0)); // Wochentag unberücksichtigt
    Wire.write(decToBcd(tag));
    Wire.write(decToBcd(monat));
    Wire.write(decToBcd(jahr-2000));
    Wire.endTransmission();
    }

    //Berechnet den Tag der Woche aus dem übergebenen Datumswerten.
    byte calcDayOfWeek(int jahr, byte monat, byte tag) {
    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    jahr -= monat 0){
    num–;
    //Bei Kommanseparierten Listen werden die Kommata
    //übersprungen und geben damit die Unterteilung des Strings an.
    while ((!isdigit (*stringWithInt))&&(*stringWithInt!=0)){
    stringWithInt++;
    }
    tail=stringWithInt;
    //Schleife solange ausführen bis eine Zahl gefunden wurde
    //welche größer 0 ist.
    while ((isdigit(*tail))&&(*tail!=0)){
    tail++;
    }

    if (num>0){
    stringWithInt=tail;
    }
    }
    return(strtol(stringWithInt, &tail, 10));
    }

    /*
    button 1 : hold to set time or alarm
    button 2 : press to view alarm time or cancel alarm if in progress
    button 3 : increment hour / minute when setting (alarm) time. Hold to toggle alarm setting.

    LED1 : on = alarm enabled
    */

    volatile unsigned int clockMilliSeconds = 0;
    volatile byte clockSeconds = 0;
    volatile byte clockMinutes = 0;
    volatile byte clockHours = 12;
    volatile byte clockEnabled = 1;

    byte alarmMinutes = 30;
    byte alarmHours = 6;
    volatile byte alarmEnabled = false;

    byte alarmTogglePressed = false;

    enum displayModeValues
    {
    MODE_CLOCK_TIME,
    MODE_CLOCK_TIME_SET_HOUR,
    MODE_CLOCK_TIME_SET_MINUTE,
    MODE_ALARM_TIME,
    MODE_ALARM_TIME_SET_HOUR,
    MODE_ALARM_TIME_SET_MINUTE
    };

    byte displayMode = MODE_CLOCK_TIME;

    //————————————————————————————————–
    void setup()
    {
    Timer1.initialize();
    MFS.userInterrupt = clockISR;
    MFS.initialize(&Timer1);

    MFS.blinkDisplay(DIGIT_ALL);
    //MFS.beep(500);

    //Setup Methode
    Wire.begin(); //Kommunikation über die Wire.h bibliothek beginnen.
    Serial.begin(57600); //Übertragungsgeschwindigkeit 9600 Baud
    Serial.println(“Mit dem Befehl kann das Datum und die Uhrzeit gesetzt oder veraendert werden.”);
    Serial.println(“Beispiel: set 28.08.2013 10:54”);
    }

    //Gibt einen Zeitstempel auf der Seriellen Schnittstelle aus.
    void printRTCDateTime(){
    char linebuf[60];
    int dOW = calcDayOfWeek(jahr, monat, tag);
    String wochentagC = daysInWeek[dOW];
    String monatC = monthInYear[monat];

    String result = “”;
    result += wochentagC;
    result += “, “;
    result += tag;
    result += “.”;
    result += monatC;
    result += ” “;
    result += jahr;
    result += ” “;
    if(stunde<10) { result += "0"; }
    result += stunde;
    result += ":";
    if(minute<10) { result += "0"; }
    result += minute;
    result += ":";
    if(sekunde<10) { result += "0"; }
    result += sekunde;
    Serial.println(result);
    }

    //Manuelles setzen der Uhrzeit über den Seriellen Monitor der IDE.
    void setRTCTime(){
    char linebuf[30];
    byte counter;
    if (Serial.available()){
    delay(100); // Warte auf das Eintreffen aller Zeichen vom seriellen Monitor
    memset(linebuf,0,sizeof(linebuf)); // Zeilenpuffer löschen
    counter=0; // Zähler auf Null
    while (Serial.available()){
    linebuf[counter]=Serial.read(); // Zeichen in den Zeilenpuffer einfügen
    if (counter2000){
    result = true;
    } else {
    return false;
    }
    // Schaltjahr prüfen
    if(jahr % 400 == 0 || (jahr % 100 != 0 && jahr % 4 == 0)){
    //Wenn es ein Schaltjahr ist dann den Wert 29 in das Array an der Stelle 1 für den Monat Februar schreiben.
    daysInMonth[1]=29;
    }

    //Monat muss kleiner 13 sein.
    if (monat<13){
    if( tag <= daysInMonth[monat-1] ){
    result = true;
    }
    } else {
    return false;
    }

    //Wert für Stunde muss zwischen 0 und 24 liegen,
    //Wert für Minute und sekunde muss zwischen 0 und 59 liegen
    if(stunde <24 && minute <60 && sekunde = 0 && minute >=0 && sekunde >=0){
    result = true;
    } else {
    return false;
    }

    return result;
    }
    void loop()
    {
    // put your main code here, to run repeatedly:

    byte btn = MFS.getButton();

    switch (displayMode)
    {
    case MODE_CLOCK_TIME:
    displayTime(clockHours, clockMinutes);

    if (btn == BUTTON_2_PRESSED)
    {
    MFS.beep(0); // cancel the alarm.
    displayMode = MODE_ALARM_TIME;
    }
    else if (btn == BUTTON_1_LONG_PRESSED)
    {
    MFS.blinkDisplay(DIGIT_ALL, OFF);
    MFS.blinkDisplay(DIGIT_1 | DIGIT_2);
    displayMode = MODE_CLOCK_TIME_SET_HOUR;
    clockEnabled = false;
    clockMilliSeconds = 0;
    clockSeconds = 0;
    }
    else if (btn == BUTTON_3_LONG_PRESSED && !alarmTogglePressed)
    {
    alarmTogglePressed = true;
    alarmEnabled = !alarmEnabled;
    MFS.writeLeds(LED_1, alarmEnabled);
    }
    else if (btn == BUTTON_3_LONG_RELEASE)
    {
    alarmTogglePressed = false;
    }
    break;

    case MODE_CLOCK_TIME_SET_HOUR:
    if (btn == BUTTON_1_PRESSED)
    {
    MFS.blinkDisplay(DIGIT_1 | DIGIT_2, OFF);
    MFS.blinkDisplay(DIGIT_3 | DIGIT_4);
    displayMode = MODE_CLOCK_TIME_SET_MINUTE;
    }
    else if (btn == BUTTON_3_PRESSED || btn == BUTTON_3_LONG_PRESSED)
    {
    clockHours++;
    if (clockHours >= 24)
    {
    clockHours = 0;
    }
    displayTime(clockHours, clockMinutes);
    }
    break;

    case MODE_CLOCK_TIME_SET_MINUTE:
    if (btn == BUTTON_1_PRESSED)
    {
    MFS.blinkDisplay(DIGIT_3 | DIGIT_4, OFF);
    displayMode = MODE_CLOCK_TIME;
    clockEnabled = true;
    }
    else if (btn == BUTTON_3_PRESSED || btn == BUTTON_3_LONG_PRESSED)
    {
    clockMinutes++;
    if (clockMinutes >= 60)
    {
    clockMinutes = 0;
    }
    displayTime(clockHours, clockMinutes);
    }
    break;

    case MODE_ALARM_TIME:
    displayTime(alarmHours, alarmMinutes);

    if (btn == BUTTON_2_SHORT_RELEASE || btn == BUTTON_2_LONG_RELEASE)
    {
    displayMode = MODE_CLOCK_TIME;
    }
    else if (btn == BUTTON_1_LONG_PRESSED)
    {
    MFS.blinkDisplay(DIGIT_ALL, OFF);
    MFS.blinkDisplay(DIGIT_1 | DIGIT_2);
    displayMode = MODE_ALARM_TIME_SET_HOUR;
    alarmEnabled = false;
    }
    break;

    case MODE_ALARM_TIME_SET_HOUR:
    if (btn == BUTTON_1_PRESSED)
    {
    MFS.blinkDisplay(DIGIT_1 | DIGIT_2, OFF);
    MFS.blinkDisplay(DIGIT_3 | DIGIT_4);
    displayMode = MODE_ALARM_TIME_SET_MINUTE;
    }
    else if (btn == BUTTON_3_PRESSED || btn == BUTTON_3_LONG_PRESSED)
    {
    alarmHours++;
    if (alarmHours >= 24)
    {
    alarmHours = 0;
    }
    displayTime(alarmHours, alarmMinutes);
    }
    break;

    case MODE_ALARM_TIME_SET_MINUTE:
    if (btn == BUTTON_1_PRESSED)
    {
    MFS.blinkDisplay(DIGIT_3 | DIGIT_4, OFF);
    displayMode = MODE_CLOCK_TIME;
    alarmEnabled = true;
    MFS.writeLeds(LED_1, ON);
    }
    else if (btn == BUTTON_3_PRESSED || btn == BUTTON_3_LONG_PRESSED)
    {
    alarmMinutes++;
    if (alarmMinutes >= 60)
    {
    alarmMinutes = 0;
    }
    displayTime(alarmHours, alarmMinutes);
    }
    break;
    }
    }

    void displayTime (byte hours, byte minutes)
    {
    char time[5];

    sprintf(time, “%03d”, (hours * 100) + minutes);
    //MFS.write(time, 1);
    MFS.write((float)(hours *100 + minutes)/100,2);

    rtcReadTime();
    printRTCDateTime();
    setRTCTime();
    delay(1000);
    }

    //————————————————————————————————–
    void clockISR ()
    {
    // Perform ripple count for all time components.
    if (clockEnabled)
    {
    clockMilliSeconds++;
    if (clockMilliSeconds >= 1000)
    {
    clockMilliSeconds = 0;

    clockSeconds++;
    if (clockSeconds >= 60)
    {
    clockSeconds = 0;

    clockMinutes++;
    if (clockMinutes >= 60)
    {
    clockMinutes = 0;

    clockHours++;
    if (clockHours >= 24)
    {
    clockHours = 0;
    }
    }

    // If current time coincides with alarm time, and alarm is enabled, engage the alarm.
    if (alarmEnabled && (clockMinutes == alarmMinutes) && (clockHours == alarmHours))
    {
    MFS.beep(
    10, // on period
    5, // off period
    4, // number of cycles
    100, // number of loop cycles
    50 // delay between loop cycles
    );
    }
    }
    }
    }
    }

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *