Notes on STM32F103 and M5311NBiot module connecting to onenet platform based on LWM2M protocol

Debugging notes of STM32F103 and M5311NBiot modules

preface:

I don't know why. Now the Internet of things is becoming more and more popular. GPRS and other wireless communication modules seem to be slowly replaced by NBiot. With my dear roommate (son) encountering the problem of NBiot. I was trying to debug this so-called "nice to tune" module. Only taking this article as a study note, I would like to express my high respect to the people who have learned from it. At the same time, there are still many shortcomings and imperfections. Just be a joke.

1. Hardware selection

1.1 NBiot module selection:

It can be seen from the title that the NBiot module selected is a chip mainly controlled by M5311. Now there are various NBiot modules on the market. Choosing this is nothing more than cheap. In addition, I have seen the simple debugging process of a big guy in station B before, so I chose M5311. The purchase link of TB is as follows:
Link: M5311

1.2 selection of MCU:

MCU with serial communication is OK. Here, stm32F103C8T6 is selected because it is not only cheap, but also in the process of processing this AT command communication, it can automatically handle data by using serial idle interrupt + DMA when returning various variable length data. It really doesn't smell too good!

2. Communication protocol

The LWM2M protocol used by the M5311 to connect to the onenet platform does not seem to need to understand the meaning of the specific protocol, because all we use in the process of communicating with the module is the AT instruction. We just need to know which AT instructions to use and what these AT instructions mean.
A simple example:

The software reset terminal can see that its fixed return values are OK and ERROR. Therefore, after sending the command, we need to check whether the return value of the module is ERROR or OK.
The same can be obtained
AT+CGPADDR=1
AT+MIPLCREATE
AT+MIPLADDOBJ
AT+MIPLDISCOVERRSP
AT+MIPLOPEN
AT+MIPLNOTIFY and other AT instructions.









3. Communication mode between STM32 and NBiot module

First, let's talk about why we should use the serial port idle interrupt instead of the field standard serial port to accept the non empty interrupt. The reason is very simple: when the processed data is a fixed length, it's really easy to determine the data acceptance, just count in the serial port interrupt, but when the data sent back to MCU is an indefinite length, and the frame header and the frame tail are all irregular data, we should determine what the data is When the acceptance is over, it will become very difficult. I only recently know that there is a serial port idle interrupt. The simple point is that when the serial port accepts a frame of data, and no data is sent later, the MCU will automatically place the serial port idle interrupt flag bit (instead of the word meaning that the serial port will enter the interrupt when it is idle)
So why DMA?
In fact, the reason is very simple. We can understand DMA as a "licking dog". When you specify the direction of DMA transportation and enable it, once the serial port accepts the data, DMA will run to help the serial port to move things to the place she wants, without the participation of CPU. Therefore, we can define an array receive [buffer [], and let DMA carry the data in usartx - > Dr register of serial port to the array. This is very useful when debugging.
The specific serial port idle interrupt combined with DMA handling can be seen in this article, which is very good
Link: Serial port idle interrupt and DMA handling.



4 hardware connection

M5311 STM32
PWR -------- PB1 (general push-pull output)
RX------------PB10(USART_3_TX)
TX-------------PB10(USART_3_RX)
VCC -------- you understand
GND -------- you know
This is to explain why serial port 3 is selected. On my smallest system board, only USART1 is connected to ch340 to communicate with the computer. Therefore, serial port 1 is used as a tool to print the communication records between serial port 3 and M5311 (the old tool is old). You can also choose not to connect PWR, and use the M5311 core board to press and hold the key to make it work.





5 program ideas and code implementation

Serial port 1 configuration: normal configuration can be performed without any interruption
Serial port 3, DMA configuration, serial port 3 interrupt configuration:
Receive [buffer []: DMA handling data terminal, debugging information summary

static void DMA_Config(void)
{
	DMA_InitTypeDef    DMA_Initstructure;
   /*Turn on DMA clock*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	DMA_DeInit(DMA1_Channel3);
   /*DMA To configure*/
	DMA_Initstructure.DMA_PeripheralBaseAddr =  (uint32_t)(&USART3->DR);
	DMA_Initstructure.DMA_MemoryBaseAddr     = (uint32_t)Receive_Buffer;
	DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_Initstructure.DMA_BufferSize =DATA_LENGTH;
	DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_Initstructure.DMA_MemoryInc =DMA_MemoryInc_Enable;
	DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;
	DMA_Initstructure.DMA_Priority = DMA_Priority_High;
	DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel3,&DMA_Initstructure);
	DMA_Cmd(DMA1_Channel3,ENABLE);	
}

void MY_USART3_CONFIG(uint32_t baud)//Serial initialization function
{
	DMA_Config();
	//Initialize clock 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	//Serial port reset
	USART_DeInit(USART3);
	//Using serial port 3 for communication     
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	//TXD PB10 configuration
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	//RXD PB11 configuration
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	//Communication protocol setting of serial port
	USART_InitStructure.USART_BaudRate=baud;//Baud rate
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//Whether there is hardware flow
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//Transceiver mode fully open
	USART_InitStructure.USART_Parity=USART_Parity_No;//No verification
	USART_InitStructure.USART_StopBits=USART_StopBits_1;//One stop bit
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;//Send byte length
	USART_Init(USART3,&USART_InitStructure);
	//Open serial port idle receive interrupt
	USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);
	//Enable serial DMA reception
	USART_DMACmd(USART3,USART_DMAReq_Rx,ENABLE);
	//Turn on the serial clock
	USART_Cmd(USART3,ENABLE);
}
void USART3_IRQHandler(void)
{
	uint8_t length=0;
	if(USART_GetITStatus(USART3,USART_IT_IDLE)==SET)
	{
		//Clear idle interrupt flag (software sequence clear)
		length=USART3->SR;
		length=USART3->DR;
		//When DMA channel is configured as acyclic mode, in order to start a new DMA transmission command after transmission, DMA must be disabled and the received data length must be reset
		DMA_Cmd(DMA1_Channel3,DISABLE);
		length=DATA_LENGTH-DMA_GetCurrDataCounter(DMA1_Channel3);
		Receive_Buffer[length]='\0';
		DMA1_Channel3->CNDTR=DATA_LENGTH;
		DMA_Cmd(DMA1_Channel3,ENABLE);
		//Print M5311 to send instruction through serial port 1
		if(observe_flag)
			printf("%s\r\n",Receive_Buffer);
		receive_flag=1;
	}
}

The comparison function between the sending character and the required return character refers to the code of brother a Zheng in station B. the specific idea is to compare the returned string after sending the string with the expected return string (the function of strstr str), and add a delay judgment to prevent leaving too early.
Return 1 if the received data is correct
Otherwise, 0 is returned, and when (true) can be used in the necessary AT instruction setting process;
Initialize it until it is implemented.


//Receive [buffer []; DMA transfer terminal
uint8_t NBiot_SendCmd(char *cmd,char *reply,int wait)
{
	uint32_t i;
	//Memset (receive buffer, 0, sizeof (receive buffer)); / / clear the receive buffer first
	printf("[NBiot_SendCmd] %s", cmd);//Serial port Print send command
	delay_ms(100);//Appropriate delay
	COM_Sendstr(USART3,cmd,strlen(cmd));
	//Delay waiting function
	if (wait >= 1000)
  {
    for (i = 0; i < (wait / 1000); i++)
      delay_ms(1000);
  } 
	else 
		delay_ms(wait);
	if (strcmp(reply, "") == 0) //Return value is empty
    return 0;
	else 
  {
    if (strstr((char*)Receive_Buffer, reply))   //Include data or not
    {
      printf("respond Successfully\r\n");
			receive_flag=0;
      return 1;
    }
    else if (strstr((char*)Receive_Buffer, "ERROR"))
    {
      printf("ERROR\r\n");
			receive_flag=0;
      return 0;
    }
    else
    {
      printf("Other Error:%s\r\n",Receive_Buffer);
			receive_flag=0;
      return 0;
    }
  }
}

AT instructions used for module initialization and code implementation:
AT+CMRB: reset command

while(!NBiot_SendCmd("AT+CMRB\r\n", "ATREADY", 2000)); //Soft reset

AT+CGPADDR=1: it is said to be networking test

while(NBiot_SendCmd("AT+CGPADDR=1\r\n","+CGPADDR: 1",1000));

AT+MIPLCREATE = registration code: the registration code here can be reused

	const char *Onenet_Key="AT+MIPLCREATE=56,130038F10003F2002A04001100000000000010196E62696F7462742E6865636C6F7564732E636F6D3A35363833000131F300080000000000,0,56,0\r\n";
	while(NBiot_SendCmd((char *)Onenet_Key,"OK",20000)==0);//Longer time delay for platform connection

At + mipladdobj = 03300,1, "1", 0,1 create instance format parameter see note

	//Create object
	/*<ref>,<objid>,<inscount>,<bitmap>,<atts>,<acts>*/
	char *Create_OBJ="AT+MIPLADDOBJ=0,3300,1,\"1\",0,1\r\n"; 
	while(NBiot_SendCmd((char *)Create_OBJ,"OK",1000)==0);

At + mipldiscoverrsp = 03300,1,4, "5750" subscription resource format is shown in the note

	//Subscription resources
	/*<ref>,<objid>,<result>,<length>,<data>*/
	char *Create_Resource="AT+MIPLDISCOVERRSP=0,3300,1,4,\"5750\"\r\n";
	while(NBiot_SendCmd((char *)Create_Resource,"OK",1000)==0);

At + miplopen = 03000,30 connect to onenet

	//Try to connect to Onenet
	/*<ref>,<lifetime>[,<timeout>]*/
	char *Onenet_Connect="AT+MIPLOPEN=0,3000,30\r\n"; 
	while(NBiot_SendCmd((char *)Onenet_Connect,"OK",1000)==0);

At + miplnotify = 0,03300,05750,1,18, "reporting data"
You can replace the data you want to send with the location of 18 representing the string length

	//Reporting data
	/*<ref>,<mid>,<objid>,<insid>,<resid>,<type>,<len>,<value>,<index>,<flag>[,<ackid>]*/
	char *Notify_Data="AT+MIPLNOTIFY=0,0,3300,0,5750,1,18,\"songjunchuanshizhu\",0,0\r\n";
	while(NBiot_SendCmd((char *)Notify_Data,"OK",1000)==0);

Operation results

After the connection is powered on and reset, wait for M5311 initialization. In the configured onenet platform, you can see the reported data. You can change the data in the notify instruction to the data you want to report, such as temperature and humidity. You don't know how to create OBJ and subscribe resources. Just take a note.

summary

The full text is rather rough. Use it as a note. I hope that the big guy who would have done it would not spray.

Posted on Sun, 03 May 2020 06:46:29 -0400 by auteejay