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(¶ms); adc0 = ADC_open(Board_ADC0, ¶ms); 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 Jan Cumps'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(¶ms); params.dutyUnits = PWM_DUTY_US; params.dutyValue = 0; params.periodUnits = PWM_PERIOD_US; params.periodValue = pwmPeriod; pwm1 = PWM_open(Board_PWM0, ¶ms); 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.
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(¶ms); adc0 = ADC_open(Board_ADC0, ¶ms); 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(¶ms); params.dutyUnits = PWM_DUTY_US; params.dutyValue = 0; params.periodUnits = PWM_PERIOD_US; params.periodValue = pwmPeriod; pwm1 = PWM_open(Board_PWM0, ¶ms); 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); }
Top Comments