• Home
  • Exercises 07
  • System Integration
  • System Integration

    The questions below are due on Thursday April 13, 2023; 10:00:00 AM.
     
    You are not logged in.

    Please Log In for full access to the web site.
    Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

    System Integration

    Over the past few weeks we've worked on developing the individual components of our MILO system:

    • Sensor board
    • MCU board
    • Power board
    • Server interface

    The next step will be sticking these individual components together into a fully working system. This process is called system integration and usually comes with a host of new bugs and challenges! Part of good system integration is figuring out how to incrementally test out system's functionality, verify our assumptions about what the system is doing, and efficiently track down bugs.

    Prior to system integration, we'll typically want to ensure each component works individually by running unit tests (we've done this through our previous exercises). This is a really good sanity check and helps to reduce the number of things that can be wrong. This semester we've encountered a few different tools for unit testing our components: status LEDs, multimeters, the serial monitor, Postman, etc.

    Once we're confident each piece works on its own, we use integration tests to verify that the components interact together in the way that we expect. In this exercise, our approach to integrating the parts of our system will be to systematically add components one at a time and verify they're working at each stage. This strategy (as opposed to putting everything together at once) makes it easier to narrow down which component or interface between components is malfunctioning.

    Work your way through the following steps to slowly add components to your system until everything is working flawlessly in perfect harmony.

    Powering the MCU

    Start by ensuring that you can power your MCU board from your power board:

    • Connect your battery to your power board and verify with a multimeter that it's correctly outputting 3.3V.
    • Next, check the orientation of the pins on your power board power connector and your MCU board power connect.

    Make sure the pins on your two board connectors correspond: aka if pin 1 on your power board power connector is 3.3V, make sure pin 1 on the MCU board power connector is also 3.3V! If it isn't you'll need to make a JST cable with the wires reversed.

    • Now, get some JST cables from the lab. We'll use those to connect your MCU board to your power board.

    • Plug your MCU board into your power board and verify that the LED lights up and your ESP is responsive. Connect the MCU board to your laptop, and try uploading some code to it to verify that it's alive

    Reading the Sensors

    Next, power your sensor board and take sensor readings with your MCU:

    Make sure the pins on your two board connectors correspond, just like for MCU and power board. In addition, make sure the I2C connector pin orientation is the same between the two board. If it isn't you'll need to make a JST cable with the wires reversed.

    • Power your sensor board from your power board and verify that the LED turns on
    • Upload the code below to your MCU and verify that it prints sensor readings from both sensors to the Serial Monitor
    #include <Arduino.h>
    #include <SensirionI2CSgp41.h>    // link to the library: https://github.com/sensirion/arduino-i2c-sgp41
    #include <SparkFun_SHTC3.h>       // link to the library: https://github.com/sparkfun/SparkFun_SHTC3_Arduino_Library
    #include <Wire.h>
    
    SHTC3 shtc3;              // Declare an instance of the SHTC3 class
    SensirionI2CSgp41 sgp41;
    
    #define I2C_SDA 4
    #define I2C_SCL 5
    
    // time in seconds needed for NOx conditioning
    uint16_t conditioning_s = 10;
    
    void setup() {
    
        Serial.begin(115200);
        while (!Serial) {
            delay(100);
        }
    
        Wire.begin(I2C_SDA, I2C_SCL);
    
        uint16_t error;
        char errorMessage[256];
    
        errorDecoder(shtc3.begin());
    
        sgp41.begin(Wire);
    
        uint16_t serialNumber[3];
        uint8_t serialNumberSize = 3;
    
        error = sgp41.getSerialNumber(serialNumber, serialNumberSize);
    
        if (error) {
            Serial.print("Error trying to execute getSerialNumber(): ");
            errorToString(error, errorMessage, 256);
            Serial.println(errorMessage);
        } else {
            Serial.print("SerialNumber:");
            Serial.print("0x");
            for (size_t i = 0; i < serialNumberSize; i++) {
                uint16_t value = serialNumber[i];
                Serial.print(value < 4096 ? "0" : "");
                Serial.print(value < 256 ? "0" : "");
                Serial.print(value < 16 ? "0" : "");
                Serial.print(value, HEX);
            }
            Serial.println();
        }
    
        uint16_t testResult;
        error = sgp41.executeSelfTest(testResult);
        if (error) {
            Serial.print("Error trying to execute executeSelfTest(): ");
            errorToString(error, errorMessage, 256);
            Serial.println(errorMessage);
        } else if (testResult != 0xD400) {
            Serial.print("executeSelfTest failed with error: ");
            Serial.println(testResult);
        }
    }
    
    void loop() {
        uint16_t error;
        char errorMessage[256];
        uint16_t defaultRh = 0x8000;
        uint16_t defaultT = 0x6666;
        uint16_t srawVoc = 0;
        uint16_t srawNox = 0;
    
        delay(1000);
    
        SHTC3_Status_TypeDef result = shtc3.update();             // Call "update()" to command a measurement, wait for measurement to complete, and update the RH and T members of the object
        printInfo();                                                // This function is used to print a nice little line of info to the serial port
    
        if (conditioning_s > 0) {
            // During NOx conditioning (10s) SRAW NOx will remain 0
            error = sgp41.executeConditioning(defaultRh, defaultT, srawVoc);
            conditioning_s--;
        } else {
            // Read Measurement
            error = sgp41.measureRawSignals(defaultRh, defaultT, srawVoc, srawNox);
        }
    
        if (error) {
            Serial.print("Error trying to execute measureRawSignals(): ");
            errorToString(error, errorMessage, 256);
            Serial.println(errorMessage);
        } else {
            Serial.print("SRAW_VOC:");
            Serial.print(srawVoc);
            Serial.print("\t");
            Serial.print("SRAW_NOx:");
            Serial.println(srawNox);
        }
    }
    
    
    
    /////////////////////////////
    // SHTC3 Utility Functions //
    /////////////////////////////
    void printInfo()
    {
      if(shtc3.lastStatus == SHTC3_Status_Nominal)              // You can also assess the status of the last command by checking the ".lastStatus" member of the object
      {
        Serial.print("RH = "); 
        Serial.print(shtc3.toPercent());                        // "toPercent" returns the percent humidity as a floating point number
        Serial.print("%, T = "); 
        Serial.print(shtc3.toDegF());                           // "toDegF" and "toDegC" return the temperature as a flaoting point number in deg F and deg C respectively 
        Serial.println(" deg F"); 
      }
      else
      {
        Serial.print("Update failed, error: "); 
        errorDecoder(shtc3.lastStatus);
        Serial.println();
      }
    }
    
    void errorDecoder(SHTC3_Status_TypeDef message)                             // The errorDecoder function prints "SHTC3_Status_TypeDef" resultsin a human-friendly way
    {
      switch(message)
      {
        case SHTC3_Status_Nominal : Serial.print("Nominal"); break;
        case SHTC3_Status_Error : Serial.print("Error"); break;
        case SHTC3_Status_CRC_Fail : Serial.print("CRC Fail"); break;
        default : Serial.print("Unknown return code"); break;
      }
    }
    

    Posting to the Server

    Finally, send the sensor readings up to your server

    • Make sure your server is set up to recieve and display sensor readings. You may use/modify your code from previous exercises or set up a new endpoint if you'd like. (Note: you may want to use Postman to verify that your serevr is working independently before trying to integrate it with the rest of your system)
    • Modify your MCU code to post the sensor readings to your server and verify that they're displayed correctly on a webpage

    Success!

    Once everything is working, take a quick video of your system working and drop us a URL below!

    Congratulations, you've designed a working system completely from scratch! You've designed, assembled, and tested several (nontrivial) PCBs, written firmware for your MCU, and setup a webserver to receive and display data. This is super cool and you should feel proud of all the work you've done and empowered to be able to tackle any future projects that come your way.

    Insert the URL below.

    System Integration Video URL: