In this article, I will demonstrate the Smart Helmet v0.1 I am always on the move so I struggle to find time but slowly going further. Let's summarise what have done up to now. I am using TI-RTOS. I stack the Wi-Fi booster pack to MSP432. Connect ADXL345 accelerometer and TMP102 using I2C interface. Detect free-fall, inactivity, and send all the data to Ground Operations Center over Wi-Fi using TCP/IP protocol. I coded Ground Operations Center using C# and it still needs some modifications. DLP-7970ABP boosterpack is connected to Ground Operations Center via UART interface. It controls the gate and doesn't allow unauthorised access and access without the helmet.

 

In this week, I added a buzzer and pressure sensor. The buzzer is driven by PWM and pressure sensor is read using the analogue input. For pressure, I have used FSR 402 Interlink Electronics. Actually, it is force sensing resistor. It is resistance changes based on the applied force and it is not sensitive but it shows the idea. The code for the ADC? I follow the MSP432 and TI-RTOS: Getting Started Pt. 2 - Add an ADC Sample Task. I create task by code instead of the GUI so the task initialization is as follow

 

    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &taskADCStack;
    Task_construct(&taskADCStruct, (Task_FuncPtr) taskAdcSample, &taskParams, NULL);

 

I only use the adc0 so I removed the adc1 measurements from the taskADCSample task. pressureAdcValue is where the analogue value is saved. It is then transmitted via Wi-Fi to the Ground Operations Centre with other data. 

 

/*
 *  ======== taskAdcSample ========
 *  Open an ADC instance and get a sampling result from a one-shot conversion.
 */


Void taskAdcSample(UArg arg0, UArg arg1)
{
    ADC_Handle   adc0;
    ADC_Params   params;
    ADC_Params_init(&params);
    adc0 = ADC_open(Board_ADC0, &params);


    while (1) {
        Task_sleep(10);
        /* Blocking mode conversion */
        ADC_convert(adc0, &pressureAdcValue);
    }
    // theoretically close the ADC driver. This code is never reached
//    ADC_close(adc0);
//    ADC_close(adc1);
}

 

The next step is adding PWM for the buzzer. Hence, the buzzer sounds pretty awful, I recognise that I need to add some button control to stop it. It is not for the terrible sound of course. There may be cases where the alarm triggered falsely or the miner inform that he got the warning or some other reasons. So how we will do this? There should be a PWM task but duty cycle will change. At first duty cycle is 0 and if the alarm is triggered it will be something valuable like 50.  If the miner press the button, it should be zero again. It looks like there will be message transfer between the task so the answer is Mailboxes. This project is the first time I use RTOS, I don't know there is a better way but  Mailboxes works pretty well for what I want to do. I get great help from jancumps's article MSP432 and TI-RTOS: PID Library Part 2 - Real World Example.

The PWM task is as shown below. It setup PWM and sets the duty cycle 0. Then, it waits for the message to read from Mailboxes. When a message arrives it change the duty cycle.

 

Void pwmFxn(UArg arg0, UArg arg1)
{
    PWM_Handle pwm1;
    PWM_Params params;
    uint16_t   pwmPeriod = 2000;      // Period and duty in microseconds


    MsgPWM msg;


    PWM_Params_init(&params);
    params.dutyUnits = PWM_DUTY_US;
    params.dutyValue = 0;
    params.periodUnits = PWM_PERIOD_US;
    params.periodValue = pwmPeriod;
    pwm1 = PWM_open(Board_PWM0, &params);
    if (pwm1 == NULL) {
        System_abort("Board_PWM0 did not open");
    }
    PWM_start(pwm1);




    /* Loop forever incrementing the PWM duty */
    while (1) {
        //Task_sleep(10);
        /* wait for mailbox to be posted by writer() */
        if (Mailbox_pend(mbPWM, &msg, BIOS_WAIT_FOREVER)) {
            PWM_setDuty(pwm1, msg.pwm);
        }
    }
}

 

 

Where I change the duty cycle is one in ISR of ADX345 and another one is in ISR of the button press.

 

/******** activity, inactivity from ADXL345 int source1. Activated via Hwi***/
void ADXL345_int1(unsigned int index)
{
    MsgPWM pMsg;
    /* Clear the GPIO interrupt and toggle an LED */
    GPIO_toggle(Board_LED0);
    pMsg.pwm = 50;
    Mailbox_post(mbPWM, &pMsg, 10);
}

 

 

/*
 *  ======== gpioButtonFxn ========
 *  Callback function for the GPIO interrupt on Board_BUTTON1.
 */
void gpioButtonFxn(unsigned int index)
{


    MsgPWM pMsg;
    pMsg.pwm = 0;
    Mailbox_post(mbPWM, &pMsg, 10);


}

 

 

I was planning to make a video to demonstrate the design but it is so messy now. I will prepare a circuit board then put things inside the helmet. Hopefully, I will publish video next week. What I will do next is modify the program and finish the PC side.

WhatsApp Image 2017-06-11 at 18.29.29.jpeg

 

You can see all the links related to this project in the first blog: Safe & Sound Wearables - Trackable Safety Helmet for Miners #1: Introduction to Project

 

This is the full code of the program up to now.

/*
 * Copyright (c) 2015-2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/*
 *  ======== tcpEchoCC3X00.c ========
 */


#include <driverlib.h>
#include <string.h>
#include <stdbool.h>


/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/cfg/global.h>


/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>


/* TI-RTOS Header files */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/UART.h>
#include <ti/drivers/I2C.h>
#include <ti/drivers/ADC.h>
#include <ti/drivers/PWM.h>


/* SimpleLink Wi-Fi Host Driver Header files */
#include <simplelink.h>


/* Example/Board Header file */
#include "Board.h"


/* Local Platform Specific Header file */
#include "sockets.h"


#include <stdint.h>


#include "ADXL345.h"






/* Port number for listening for TCP packets */
#define TCPPORT         1000


#define TASKSTACKSIZE   1024


/* IP addressed of server side socket. Should be in long format,
 * E.g: 0xC0A8000A == 192.168.0.10 */
#define IP_ADDR_Server 0xC0A8000A


extern bool smartConfigFlag;
Task_Struct taskWiFiStruct;
Char taskWiFiStack[TASKSTACKSIZE];


Task_Struct taskUARTStruct;
Char taskUARTStack[TASKSTACKSIZE];


Task_Struct taskI2CStruct;
Char taskI2CStack[TASKSTACKSIZE];


Task_Struct taskADXL345Struct;
Char taskADXL345Stack[TASKSTACKSIZE];


Task_Struct taskADCStruct;
Char taskADCStack[TASKSTACKSIZE];


Task_Struct taskPWMStruct;
Char taskPWMStack[512];










/* Globals */


void *netIF;


char connected = 0; // re-connect the AP


volatile char *Mymessage = "elemet14";


uint16_t pressureAdcValue;








// mailbox


typedef struct MsgPWM {
    uint32_t pwm;
} MsgPWM;


/*
 *  ======== echoFxn ========
 *  Task for this function is created statically. See the project's .cfg file.
 */
Void echoFxn(UArg arg0, UArg arg1)
{
    char input;
    UART_Handle uart;
    UART_Params uartParams;
    const char echoPrompt[] = "\fEchoing characters:\r\n";


    /* Create a UART with data processing off. */
    UART_Params_init(&uartParams);
    uartParams.writeDataMode = UART_DATA_BINARY;
    uartParams.readDataMode = UART_DATA_BINARY;
    uartParams.readReturnMode = UART_RETURN_FULL;
    uartParams.readEcho = UART_ECHO_OFF;
    uartParams.baudRate = 9600;
    uart = UART_open(Board_UART0, &uartParams);


    if (uart == NULL)
    {
        System_abort("Error opening the UART");
    }


    UART_write(uart, echoPrompt, sizeof(echoPrompt));


    /* Loop forever echoing */
    while (1)
    {
        UART_read(uart, &input, 10);
        Mymessage = &input;
        //UART_write(uart, &input, 10);
        //UART_write(uart, "\n\r", 10);
        Task_sleep(10);
    }
}


/*
 *  ======== I2C readPos Fxn ========
 *  Task for this function is created statically. See the project's .cfg file.
 */
#define TMP102_I2C_ADDR    0x48
#define ADXL345_I2C_ADDR   0x53
#define adxl345_I2C   MSP_EXP432P401R_I2CB0


#define ADXL345_POWER_CTL       0x2D        // Power-Saving Features Control


int temperature;
int interruptData;
char accelerometerData[6];


/*    Reads Num Bytes. Starts from Address Reg to _buff Array        */
//void ADXL345::readFrom(byte address, int num, byte _buff[])
//void ADXL345::writeTo(byte address, byte val)
Void readPos(UArg arg0, UArg arg1)
{
    uint8_t txBuffer[2];
    uint8_t rxBuffer[6];
    I2C_Handle i2c;
    I2C_Params i2cParams;
    I2C_Transaction i2cTransaction;
    char I2CErrorN = 0;
    char i;


    /* Create I2C for usage */
    I2C_Params_init(&i2cParams);
    i2cParams.bitRate = I2C_100kHz;
    i2c = I2C_open(adxl345_I2C, &i2cParams);
    if (i2c == NULL)
    {
        System_abort("Error Initializing I2C\n");
    }
    else
    {
        System_printf("I2C Initialized!\n");
    }


    txBuffer[0] = 0;
    i2cTransaction.slaveAddress = TMP102_I2C_ADDR;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rxBuffer;
    i2cTransaction.readCount = 2;


    I2C_transfer(i2c, &i2cTransaction);


    txBuffer[0] = 0x32;
    txBuffer[1] = 0;
    i2cTransaction.slaveAddress = ADXL345_I2C_ADDR;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 1;
    i2cTransaction.readBuf = rxBuffer;
    i2cTransaction.readCount = 2;


    while (1)
    {


        //Read interrupt bits on ADXL345
        i2cTransaction.readCount = 1;
        txBuffer[0] = ADXL345_INT_SOURCE;
        i2cTransaction.slaveAddress = ADXL345_I2C_ADDR;
        I2CErrorN = I2C_transfer(i2c, &i2cTransaction);
        interruptData = rxBuffer[0];


        //Read accelerometer data
        i2cTransaction.readCount = 6;
        txBuffer[0] = 0x32;
        i2cTransaction.slaveAddress = ADXL345_I2C_ADDR;
        I2CErrorN = I2C_transfer(i2c, &i2cTransaction);
        for(i=0;i<6;i++)
            accelerometerData[i] = rxBuffer[i];


        //Read temperature
        i2cTransaction.readCount = 1;
        txBuffer[0] = 0;
        i2cTransaction.slaveAddress = TMP102_I2C_ADDR;
        I2CErrorN = I2C_transfer(i2c, &i2cTransaction);
        temperature = rxBuffer[0];






        if (I2CErrorN != 1)
        {
            System_printf("I2C Bus fault\n");
            System_flush();
        }


        Task_sleep(10);
    }


    /* Deinitialized I2C */
    I2C_close(i2c);
    System_printf("I2C closed!\n");
    System_flush();


}


/******** activity, inactivity from ADXL345 int source1. Activated via Hwi***/
void ADXL345_int1(unsigned int index)
{
    MsgPWM pMsg;
    /* Clear the GPIO interrupt and toggle an LED */
    GPIO_toggle(Board_LED0);
    pMsg.pwm = 50;
    Mailbox_post(mbPWM, &pMsg, 10);
}


void ADXL345_init(UArg arg0, UArg arg1)
{
    uint8_t txBuffer[2];
    uint8_t rxBuffer[2];
    I2C_Handle i2c;
    I2C_Params i2cParams;
    I2C_Transaction i2cTransaction;


    /* Create I2C for usage */
    I2C_Params_init(&i2cParams);
    i2cParams.bitRate = I2C_100kHz;
    i2c = I2C_open(adxl345_I2C, &i2cParams);
    if (i2c == NULL)
    {
        System_abort("Error Initializing I2C\n");
    }
    else
    {
        System_printf("I2C Initialized!\n");
    }


    /**** ADXL345 TURN ON ***/


    txBuffer[0] = ADXL345_POWER_CTL;  //Wakeup
    txBuffer[1] = 0;
    i2cTransaction.slaveAddress = ADXL345_I2C_ADDR;
    i2cTransaction.writeBuf = txBuffer;
    i2cTransaction.writeCount = 2;
    i2cTransaction.readBuf = rxBuffer;
    i2cTransaction.readCount = 1;


    I2C_transfer(i2c, &i2cTransaction);


    txBuffer[1] = 16;               // Auto_sleep
    I2C_transfer(i2c, &i2cTransaction);


    txBuffer[1] = 8;               // Measure
    I2C_transfer(i2c, &i2cTransaction);


    /***** Give the range settings *****/
    // Accepted values are 2g, 4g, 8g or 16g - ADXL345_DATA_FORMAT
    // Higher Values = Wider Measurement Range
    // Lower Values = Greater Sensitivity
    /**** Activity Inactivity setting  ***/


    txBuffer[0] = ADXL345_ACT_INACT_CTL;
    txBuffer[1] = 0b01110111;
    I2C_transfer(i2c, &i2cTransaction);


    txBuffer[0] = ADXL345_TIME_INACT; //
    txBuffer[1] = 10;
    I2C_transfer(i2c, &i2cTransaction);


    /**** Activity Threshold  ***/


    txBuffer[0] = ADXL345_THRESH_ACT;
    txBuffer[1] = 75; // 62.5mg per increment    // Activity thresholds (0-255)
    I2C_transfer(i2c, &i2cTransaction);


    /**** Inactivity Threshold  ***/


    txBuffer[0] = ADXL345_THRESH_INACT;
    txBuffer[1] = 20; // 62.5mg per increment    // Inactivity thresholds (0-255)
    I2C_transfer(i2c, &i2cTransaction);


    /**** Free-Fall Threshold and Time ***/


    txBuffer[0] = ADXL345_THRESH_FF;
    txBuffer[1] = 7;  // (5 - 9) recommended - 62.5mg per increment
    I2C_transfer(i2c, &i2cTransaction);


    txBuffer[0] = ADXL345_TIME_FF;
    txBuffer[1] = 30; // (20 - 70) recommended - 5ms per increment
    I2C_transfer(i2c, &i2cTransaction);


    /**** Interrupt setup  ***/


    //Interrupt mapping
    txBuffer[0] = ADXL345_INT_MAP;
    txBuffer[1] = 0; // 0b11110011;  //inactivity and free-fall is int1, activity is int2 ( others are int 2 but disabled)
    I2C_transfer(i2c, &i2cTransaction);


    //Interrupt enable


    txBuffer[0] = ADXL345_INT_ENABLE;
    txBuffer[1] = 0b00011100; //activity, inactivity, and free-fall interrupts are enabled
    I2C_transfer(i2c, &i2cTransaction);


    /* Deinitialized I2C */
    I2C_close(i2c);
    System_printf("I2C closed!\n");
    System_flush();


    /* Construct BIOS objects */
    Task_Params taskParams;


    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &taskI2CStack;
    Task_construct(&taskI2CStruct, (Task_FuncPtr) readPos, &taskParams, NULL);


}


/*
 *  ======== gpioButtonFxn ========
 *  Callback function for the GPIO interrupt on Board_BUTTON1.
 */
void gpioButtonFxn(unsigned int index)
{
    /* Begin smart config process */
    //smartConfigFlag = true;


    MsgPWM pMsg;
    pMsg.pwm = 0;
    Mailbox_post(mbPWM, &pMsg, 10);


}


int socketHandler = -1;
int status;
char recievedBuff[4];


/*
 *  ======== TCPsend Function ========
 *  It creates socket, send message, receive it
 *  then close the socket
 */
char* str;
Void TCPSend(char *message)
{
    char i;
    while (1)
    {
        if (connected != 1)
        {
            // Open WiFi and await a connection
            netIF = socketsStartUp();


            socketHandler = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


            /*
             * SL_AF_INET indicates using IPv4
             * SL_SOCK_STREAM indicates using TCP
             * IPPROTO_TCP
             */


            connected = 1;


            if (socketHandler == -1)
            {
                System_printf("Error: socket not created.\n");
                connected = 0;
            }


            SlSockAddrIn_t Addr;  // Socket settings
            Addr.sin_family = SL_AF_INET;
            Addr.sin_port = sl_Htons(TCPPORT);
            Addr.sin_addr.s_addr = sl_Htonl(IP_ADDR_Server);


            status = sl_Connect(socketHandler, (SlSockAddr_t *) &Addr,
                                sizeof(SlSockAddrIn_t));
        }


        status = sl_Send(socketHandler, "##*", 3, 0);


        Mymessage = char2str(temperature) ;
        status = sl_Send(socketHandler, Mymessage, 3, 0);
        status = sl_Send(socketHandler, "*", 1, 0);
        Mymessage = char2str(interruptData) ;
        status = sl_Send(socketHandler, Mymessage, 3, 0);
        status = sl_Send(socketHandler, "*", 1, 0);


        for(i =0 ; i<6 ; i++)
        {
            Mymessage = char2str(accelerometerData[i]) ;
            status = sl_Send(socketHandler, Mymessage, 3, 0);


        }
        status = sl_Send(socketHandler, "*", 1, 0);
        Mymessage = int2str(pressureAdcValue) ;
        status = sl_Send(socketHandler, Mymessage, 5, 0);
        status = sl_Send(socketHandler, "*--", 3, 0);


        // sl_Close(socketHandler);


        Task_sleep(10);


    }
    // Close the network - don't do this if other tasks are using it
    //socketsShutDown(netIF);
}




/*
 *  ======== taskAdcSample ========
 *  Open an ADC instance and get a sampling result from a one-shot conversion.
 */


Void taskAdcSample(UArg arg0, UArg arg1)
{
    ADC_Handle   adc0;
    ADC_Params   params;
    ADC_Params_init(&params);
    adc0 = ADC_open(Board_ADC0, &params);


    while (1) {
        Task_sleep(10);
        /* Blocking mode conversion */
        ADC_convert(adc0, &pressureAdcValue);
    }
    // theoretically close the ADC driver. This code is never reached
//    ADC_close(adc0);
//    ADC_close(adc1);
}










/*
 *  ======== pwmFxn ========
 */


Void pwmFxn(UArg arg0, UArg arg1)
{
    PWM_Handle pwm1;
    PWM_Params params;
    uint16_t   pwmPeriod = 2000;      // Period and duty in microseconds


    MsgPWM msg;


    PWM_Params_init(&params);
    params.dutyUnits = PWM_DUTY_US;
    params.dutyValue = 0;
    params.periodUnits = PWM_PERIOD_US;
    params.periodValue = pwmPeriod;
    pwm1 = PWM_open(Board_PWM0, &params);
    if (pwm1 == NULL) {
        System_abort("Board_PWM0 did not open");
    }
    PWM_start(pwm1);




    /* Loop forever incrementing the PWM duty */
    while (1) {
        //Task_sleep(10);
        /* wait for mailbox to be posted by writer() */
        if (Mailbox_pend(mbPWM, &msg, BIOS_WAIT_FOREVER)) {
            PWM_setDuty(pwm1, msg.pwm);
        }
    }
}






/*
 *  ======== main ========
 */
int main(void)
{
    /* Construct BIOS objects */
    Task_Params taskParams;


    /* Call board init functions. */
    Board_initGeneral();
    Board_initGPIO();
    Board_initWiFi();
    Board_initUART();
    Board_initI2C();
    Board_initADC();
    Board_initPWM();






    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &taskWiFiStack;
    taskParams.priority = 1;
    Task_construct(&taskWiFiStruct, (Task_FuncPtr) TCPSend, &taskParams, NULL);


    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &taskUARTStack;
    taskParams.instance->name = "echo";
    Task_construct(&taskUARTStruct, (Task_FuncPtr) echoFxn, &taskParams, NULL);




    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &taskADCStack;
    taskParams.priority = 1;
    Task_construct(&taskADCStruct, (Task_FuncPtr) taskAdcSample, &taskParams, NULL);




    Task_Params_init(&taskParams);
    taskParams.stackSize = TASKSTACKSIZE;
    taskParams.stack = &taskADXL345Stack;
    Task_construct(&taskADXL345Struct, (Task_FuncPtr) ADXL345_init, &taskParams,
    NULL);




    /* Construct LED Task thread */
    Task_Params_init(&taskParams);
    taskParams.stackSize = 512;
    taskParams.stack = &taskPWMStack;
    taskParams.arg0 = 50;
    Task_construct(&taskPWMStruct, (Task_FuncPtr)pwmFxn, &taskParams, NULL);




    /* Install Button callback */
    GPIO_setCallback(Board_BUTTON1, gpioButtonFxn);


    /* Enable interrupts */
    GPIO_enableInt(Board_BUTTON1);


    /* Turn on user LED */
    GPIO_write(Board_LED0, Board_LED_ON);


    System_printf("Starting the TCP Echo example for the CC3X00 \n"
                  "System provider is set to SysMin. Halt the target to view"
                  " any SysMin content in ROV.\n");


    /* SysMin will only print to the console when you call flush or exit */
    System_flush();


    /* install Button callback */
    GPIO_setCallback(MSP_EXP432P401R_INT1, ADXL345_int1);


    /* Enable interrupts */
    GPIO_enableInt(MSP_EXP432P401R_INT1);


    /* Start BIOS */
    BIOS_start();


    return (0);
}