Telemetry: Bluetooth Testing Code

The following code is what I used to test the Bluetooth communications. You can modify this code to send only a few commands and see the response back, but otherwise this code will continually send the AT command for the vehicle speed and the Hex returned value will be output on the computer terminal program.

#include <stm32f4xx.h>
#include <misc.h>
#include <stm32f4xx_usart.h>
#include <stm32f4xx_gpio.h>
#include <stm32f4xx_rcc.h>


#define BAUD_RATE 38400
#define PROMPT '>'			//End of ELM response character
#define DONE 1				//Tells status of ELM output
#define WAITING 0			//Tells status of ELM output
#define MAX_STRLEN 50 		// this is the maximum string length of our string in characters
volatile char received_string1[MAX_STRLEN+1]; // this will hold the recieved string from USART1
volatile char received_string2[MAX_STRLEN+1]; // this will hold the recieved string from USART2
volatile uint32_t msTicks;  //counts 1ms timeTicks
int ELM_reading = WAITING;	//Elm Starts out in waiting mode, not finished sending data or waiting

/***ELM AT COMMANDS***/
#define ELM_RESET 	"ATZ\r"
#define PROTOCOL_SELECT "ATSP0\r"
#define ADD_LINES 	"ATL1\r"
#define ENGINE_RPM 	"010C\r"
#define VEHICLE_SPEED 	"010D\r"
#define MAF_RATE 	"0110\r"


/*----------------------------------------------------------------------------
  delays number of tick Systicks (happens every 1 ms)
 *----------------------------------------------------------------------------*/
void Delay (uint32_t dlyTicks) {
  uint32_t curTicks;

  curTicks = msTicks;
  while ((msTicks - curTicks) < dlyTicks);
}
/*----------------------------------------------------------------------------
  SysTick_Handler
 *----------------------------------------------------------------------------*/
void SysTick_Handler(void) {
	msTicks++;
}

void init_USART1(uint32_t baudrate){

	GPIO_InitTypeDef GPIO_InitStruct; 	 // this is for the GPIO pins used as TX and RX
	USART_InitTypeDef USART_InitStruct;  // this is for the USART1 initialization
	NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)

	/* enable APB2 peripheral clock for USART1
	 * note that only USART1 and USART6 are connected to APB2
	 * the other USARTs are connected to APB1
	 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

	/* enable the peripheral clock for the pins used by
	 * USART1, PB6 for TX and PB7 for RX
	 */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

	/* This sequence sets up the TX and RX pins
	 * so they work correctly with the USART1 peripheral
	 */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // Pins 6 (TX) and 7 (RX) are used
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 			// the pins are configured as alternate function so the USART peripheral has access to them
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;		// this defines the IO speed and has nothing to do with the baudrate!
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			// this defines the output type as push pull mode (as opposed to open drain)
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;			// this activates the pullup resistors on the IO pins
	GPIO_Init(GPIOB, &GPIO_InitStruct);					// now all the values are passed to the GPIO_Init() function which sets the GPIO registers

	/* The RX and TX pins are now connected to their AF
	 * so that the USART1 can take over control of the
	 * pins
	 */
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); //
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

	/* Now the USART_InitStruct is used to define the
	 * properties of USART1
	 */
	USART_InitStruct.USART_BaudRate = baudrate;				// the baudrate is set to the value we passed into this init function
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;// we want the data frame size to be 8 bits (standard)
	USART_InitStruct.USART_StopBits = USART_StopBits_1;		// we want 1 stop bit (standard)
	USART_InitStruct.USART_Parity = USART_Parity_No;		// we don't want a parity bit (standard)
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don't want flow control (standard)
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver
	USART_Init(USART1, &USART_InitStruct);					// again all the properties are passed to the USART_Init function which takes care of all the bit setting


	/* Here the USART1 receive interrupt is enabled
	 * and the interrupt controller is configured
	 * to jump to the USART1_IRQHandler() function
	 * if the USART1 receive interrupt occurs
	 */
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // enable the USART1 receive interrupt

	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		 // we want to configure the USART1 interrupts
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// this sets the priority group of the USART1 interrupts
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		 // this sets the subpriority inside the group
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			 // the USART1 interrupts are globally enabled
	NVIC_Init(&NVIC_InitStructure);							 // the properties are passed to the NVIC_Init function which takes care of the low level stuff

	// finally this enables the complete USART1 peripheral
	USART_Cmd(USART1, ENABLE);
}

void init_USART2(uint32_t baudrate){
	GPIO_InitTypeDef GPIO_InitStruct; 	 // this is for the GPIO pins used as TX and RX
	USART_InitTypeDef USART_InitStruct;  // this is for the USART2 initialization
	NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)

	// enable Clocks for APB1 and GPIOA
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; // Pins 2 (TX) and 3 (RX) are used
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 			
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;		
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;			
	GPIO_Init(GPIOA, &GPIO_InitStruct);					

	//Connect to AF
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); //
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);

	USART_InitStruct.USART_BaudRate = baudrate;				
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;		
	USART_InitStruct.USART_Parity = USART_Parity_No;		
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; 
	USART_Init(USART2, &USART_InitStruct);					

	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // enable the USART2 receive interrupt

	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;		 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		 
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			 
	NVIC_Init(&NVIC_InitStructure);							 

	USART_Cmd(USART2, ENABLE);	//Enable USART2
}

void USART_puts(USART_TypeDef* USARTx, volatile char *s){

	while(*s){
		// wait until data register is empty
		while( !(USARTx->SR & 0x00000040) );
		USART_SendData(USARTx, *s);
		*s++;
	}
}

void send_command(volatile char *command){
	while(ELM_reading==0){			//Stay here if ELM is still responding (i.e. ELM_reading==0)
		if(ELM_reading==1)
			break;
	}
	ELM_reading=WAITING;			//Set value back to waiting
	USART_puts(USART1, command);	//Send command passed in to the OBDII
	return;
}


int main(void){	
	volatile char RPM_value[5], MAF[5], Vss[3];
	int i=0;

	  SystemCoreClockUpdate();                      /* Get Core Clock Frequency   */
	  if (SysTick_Config(SystemCoreClock / 1000)) { /* SysTick 1 msec interrupts  */
	    while (1);                                  /* Capture error              */
	  }


	  init_USART1(BAUD_RATE); 	//init BT_USART to 38400 baud
	  init_USART2(BAUD_RATE); 	//init COMP_USART to 38400 baud


	  Delay(5000);		//!!Delay 5 sec for testing, let BT connect to each other
	  send_command(ELM_RESET);	//Reset ELM
	  send_command(ADD_LINES);	
	  Delay(5000);		//!!Delay 5 sec
	  send_command(PROTOCOL_SELECT);
	  send_command(ENGINE_RPM);	//Get rid of buffer from start "SEARCHING..."
	  Delay(2000);
	  
	  //Infinite loop sending/receiving vehicle speed
	  while(1){
		  send_command(VEHICLE_SPEED);	//Then get real RPM values
		  Delay(2000);
		  for(i=0;received_string1[i]!='\0';i++){
			  if(received_string1[i]=='4' && received_string1[i+1]=='1'){
				  Vss[0]=received_string1[i+6];
				  Vss[1]=received_string1[i+7];
				  Vss[2]='\0';
				  /*Output Vehicle speed in Hex*/
				  USART_puts(USART2, "\r\n\nVehicle Speed:");	//in km/hr
				  USART_puts(USART2, Vss);
				  USART_puts(USART2, "\r\n\n");	
				  break;
			  }
		  }		 
	  }
	  while (1);	//Stay in program
}


// this is the interrupt request handler (IRQ) for ALL USART1 interrupts
void USART1_IRQHandler(void){
	// check if the USART1 receive interrupt flag was set
	if( USART_GetITStatus(USART1, USART_IT_RXNE) ){

		static uint8_t cnt = 0; // this counter is used to determine the string length
		char t = USART1->DR; // the character from the USART1 data register is saved in t

		//Check for PROMPT, which signifies the end of ELM response
		if((t!=PROMPT) && (cnt < MAX_STRLEN) ){		//PROMPT == '>'
			received_string1[cnt] = t;
			cnt++;
		}
		else{		//We reached the end of prompt OR too many characters
			received_string1[cnt] = t;
			received_string1[cnt+1]='\0';
			cnt = 0;
			USART_puts(USART2, received_string1);	//!!Send response to USART2
			ELM_reading = DONE;		//Done == 1, set back to zero right away (Poll this in code until == 1)
		}
	}
}

// this is the interrupt request handler (IRQ) for ALL USART2 interrupts
void USART2_IRQHandler(void){

	// check if the USART2 receive interrupt flag was set
	if( USART_GetITStatus(USART2, USART_IT_RXNE) ){

		static uint8_t cnt = 0; // this counter is used to determine the string length
		char t = USART2->DR; // the character from the USART2 data register is saved in t

		/* check if the received character is not the LF character (used to determine end of string)
		 * or the if the maximum string length has been been reached
		 */
		if( (t!=0xA && t!=0xD) && (cnt < MAX_STRLEN) ){	//!!0xA == '\n' and 0xD == \r Carriage Return
			received_string2[cnt] = t;
			cnt++;
		}
		else{ // otherwise reset the character counter and print the received string
			received_string2[cnt]=0xD;
			received_string2[cnt+1]='\0';
			cnt = 0;
			USART_puts(USART1, received_string2);	//!!Send response to USART1

		}
	}
}

6 thoughts on “Telemetry: Bluetooth Testing Code

  1. Scooby says:

    Hi Tony,
    I am using the above code with the STM32F4Discovery expansion board (with Baseboard and LCD) and the OBD-II UART Adapter Model A with UART ( http://arduinodev.com/hardware/obd-kit/ , it’s for arduino but I think it will work with the STM32F4). I am just using the USART1 from the code and trying to display on the LCD. I haven’t changed anything else except for the delays. But I am not able to display anything. I don’t think it’s sending anything to the OBD on the car. I am relatively new to the area. Can you please help me with this? Thanks.

    • That adapter should work fine as long as it has the Tx and Rx lines for USART. What I would do first is try testing your code in the terminal on your computer and eliminate the LCD for now. Just make sure that you can talk to the car first, then you can output to the LCD later. The code I wrote above takes the input from the car on USART1 and the output is sent to USART2 (Terminal). Adding the terminal in will also be helpful because you could use it as a printf() debugger by placing USART_puts() around to see where your code is hanging.

      Make sure you are connecting the adapter correctly as well,
      USART1: PB6 (Tx) -> Adapter (Rx)
      PB7 (RX) -> Adapter (Tx)

      Also, how are you compiling your code? You can email me your package and I can take a look at it if you can’t get it working.
      Another suggestion would be to simplify the code to the basics of just sending or receiving and test it this way. You can then start to narrow down which parts are working correctly.

  2. Scooby says:

    I will try to get the output on the terminal, hope it work. I will use the same code as above without any changes this time.
    I had connected the adapter correctly, PB6(Tx)->Apapter(Rx) and vice versa.
    I am using Coocox as IDE and Arm Gcc for compiling. I will send you the code, shall I reply it the email I received regarding confirmation?

  3. That email is good to send it to.

    I highly recommend getting a USART to USB converter, like the CP2102, that way you can debug easily. I would connect the OBDII connector to this and just use the terminal to send and receive commands. This will tell you a lot about how to get communication going through the STM32F4.

  4. Scooby says:

    Hi Tony,
    I got a CP2102 USB to UART converter and tried to send the AT commands but it just doesn’t allow me to type the commands. when I press a letter for example when try to send “ATZ” it doesn’t take “A” but some other character like “{” and for “T” it takes “w” and so on.
    Then I tried to send it from the STM32F4 controller using the code above, and it was working, there was some bad connections in the CP2102 I ordered, it was displaying the data only when I hold the CP2102 in one particular direction. Then I decided to add the LCD to the code as I was getting the data. But again! the OBD adapter starts working only when I send the AT commands from the terminal. When I remove the USART2 and keep only the LCD, without switching off the OBD Adapter from the previous connection( when it was working with USART2) it works, the data is displayed on the LCD. But when I switch off the ignition of the car and try again only with the LCD it doesn’t work. The OBD adapter doesn’t transmit anything. For the OBD to transmit again I have to put th USART2 again and send the AT command through terminal again.
    And the email address to send displays donotreply, can you please send an email to my account so I can send you the package. Thanks.

  5. Scooby says:

    Hi Tony, It’s all working now, it was getting stuck on the while condition for ELM reading, I removed it and it works fine. But odd though it didn’t work with the Terminal when I sent the commands. Anyways thanks for the advice and great work with your projetcs.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s