[Reconstruction of old electric meter IoT based on STM32F1 and RT-Thread - lower computer terminal]

This method is based on the transformation project of the old electric meter IOT in a park of the unit. The author is mainly responsible for the selection and production of the lower computer and the writing of codes, as well as the docking with the server-side engineers, and self-study of server programming.
  
Development environment
  Select RT-Thread official idea: RT-Thread Studio, and use STM32 official development environment STM32CUBEMX to configure pins. Beginners can configure RT-Thread Studio and STM32CubeMX through this link
to develop
  
the system process
insert image description here
  . In this project, the lower computer Use the data in json format to regularly send the data of multiple electric meters connected by 485 daisy chain to the server. STM32 mainly uses serial port 1 and serial port 3 for communication. Serial port 1 converts data into 485 signals, sends instructions and reads data with the meter; serial port 3 packs the meter data received by serial port 1 in json format. The 4G module is sent to the server cloud.

Software process
insert image description here
test code
Definition of related variables

unsigned char usart_tx_data[89];	 //4G模块发送数组
unsigned char usart_485_send[8];     //485发送数组
uint8_t power_sum[20]={
    
    0};			 //电量存储数组	
uint8_t test_data[100]={
    
    0};

rt_mutex_t dynamic_mutex = RT_NULL;

char not_connect_4G=0;          //连接4G未成功次数
unsigned char receive_ok;		//统计电表接受次数

Serial port 1 receives data callback function

static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    
    
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem1);

    return RT_EOK;
}

Related threads for serial port 1

static void serial_thread_entry(void *parameter)
{
    
    
    char ch;
    char Con485_i = 0;
    while (1)
    {
    
    
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
        while (rt_device_read(serial1, -1, &ch, 1) != 1)
        {
    
    
            /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem1, RT_WAITING_FOREVER);
        }

        rt_device_write(serial1, 0, &ch, 1);
        //接收到了电表发来的数据

        receive_ok++;
        if(receive_ok == 4 )//接收到了四个电表的数据
        {
    
    
            Connect_4G();
            //清空、置FF操作
            receive_ok = 0;
            power_sum[4*Con485_i-1]=0xff;
            power_sum[4*Con485_i-4]=0xff;
            power_sum[4*Con485_i-2]=0xff;
            power_sum[4*Con485_i-3]=0xff;
            Con485_i = 0;
        }
        else
        {
    
    
            power_sum[4*Con485_i++]=ch;
        }
    }
}

The initialization function of serial port 1 and exported to the msh command list (started from the command line)

int uart_sample(void)
{
    
    
    rt_err_t ret = RT_EOK;
    char str1[] = "Uart1:hello RT-Thread!\r\n";


    /* 查找系统中的串口设备 */
    serial1 = rt_device_find(SAMPLE_UART_NAME1);
    if (!serial1)
    {
    
    
        rt_kprintf("find %s failed!\n", SAMPLE_UART_NAME1);
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem1, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial1, RT_DEVICE_FLAG_INT_RX);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial1, uart_input);
    /* 发送字符串 */
    rt_device_write(serial1, 0, str1, (sizeof(str1) - 1));

    /* 创建 serial 线程 */
    rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
    /* 创建成功则启动线程 */
    if (thread != RT_NULL)
    {
    
    
        rt_thread_startup(thread);
    }
    else
    {
    
    
        ret = RT_ERROR;
    }

    return ret;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_sample, uart device sample);

Serial port 3 receives data callback function

static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
{
    
    
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    if (size > 0)
    {
    
    
        rt_sem_release(&rx_sem);
    }
    return RT_EOK;
}

The data parsing thread of serial port 3 and the single character processing function (the data in json format sent by the server is more, so it is accepted in this way)

static char uart_sample_get_char(void)
{
    
    
    char ch;

    while (rt_device_read(serial, 0, &ch, 1) == 0)
    {
    
    
        rt_sem_control(&rx_sem, RT_IPC_CMD_RESET, RT_NULL);
        rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
    }
    return ch;
}

/* 数据解析线程 */
static void data_parsing(void)
{
    
    
    char ch;
    char data[ONE_DATA_MAXLEN];
    static char i = 0;

    while (1)
    {
    
    
        ch = uart_sample_get_char();
        rt_device_write(serial, 0, &ch, 1);
        if(ch == DATA_CMD_END)
        {
    
    
            test_data[i++] = '\0';
            rt_kprintf("data=%s\r\n",test_data);

            //遍历判断新接受到的200个字节内是否有0x6E和0x63的发送成功
            for(int sent_j=0;sent_j<i;sent_j++)
            {
    
    
                rt_kprintf("%c",test_data[sent_j]);
            }
            rt_kprintf("\r\n");
            for(int sent_j=0;sent_j<98;sent_j++)
            {
    
    
                if(test_data[sent_j]==0x6E&&test_data[sent_j+1]==0x63)
                {
    
    
                    rt_kprintf("####receive_ok=1####\r\n  okkosent_j:%d\r\n",sent_j);
                    /* 线程 获取到互斥量后,将4G未连接的次数清零 */
                    rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
                    not_connect_4G = 0;
                    rt_mutex_release(dynamic_mutex);
                }
            }

            //处理完上次接受的一组数据,继续while循环接受
            continue;
        }
        i = (i >= ONE_DATA_MAXLEN-1) ? ONE_DATA_MAXLEN-1 : i;
        test_data[i++] = ch;
    }
}

The initialization function of serial port 3 and export to the msh command list

int uart_data_sample(void)
{
    
    
    rt_err_t ret = RT_EOK;
    char uart_name[] = SAMPLE_UART_NAME;
    char str3[] = "Uart3:hello RT-Thread!\r\n";

    /* 查找系统中的串口设备 */
    serial = rt_device_find(uart_name);
    if (!serial)
    {
    
    
        rt_kprintf("find %s failed!\n", uart_name);
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial, uart_rx_ind);
    /* 发送字符串 */
    rt_device_write(serial, 0, str3, (sizeof(str3) - 1));

    /* 创建 serial 线程 */
    rt_thread_t thread = rt_thread_create("serial", (void (*)(void *parameter))data_parsing, RT_NULL, 1024, 25, 10);
    /* 创建成功则启动线程 */
    if (thread != RT_NULL)
    {
    
    
        rt_thread_startup(thread);
    }
    else
    {
    
    
        ret = RT_ERROR;
    }

    return ret;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_data_sample, uart device sample);

Serial port 1 sends instructions to the meter through 485, and sends instructions to the 4G module through serial port 3 (conversion between character strings and ASCII codes is required)

//读取一个485电表设备的函数
//参数Con485_i,表示读取第几个电表
//返回值表示是否读取成功,1表示成功,0表示读取失败
char Connect_485(char Con485_i)
{
    
    

    usart_485_send[0]=Con485_i+1;   //电表ID
    switch(Con485_i)
    {
    
    
        case 0:usart_485_send[6]=0x54;
                    usart_485_send[7]=0x0d;
                    break;
        case 1:usart_485_send[6]=0x54;
                    usart_485_send[7]=0x3e;
                    break;
        case 2:usart_485_send[6]=0x55;
                    usart_485_send[7]=0xef;
                    break;
        case 3:usart_485_send[6]=0x54;
                    usart_485_send[7]=0x58;
                    break;
        case 4:usart_485_send[6]=0x55;
                    usart_485_send[7]=0x89;
                    break;
    }

    /* 发送给485端 */
    int send_len = rt_device_write(serial1, 0, usart_485_send, (sizeof(usart_485_send)));
    if (send_len != sizeof(usart_485_send))
    {
    
    
        rt_kprintf("send data failed\r\n");
        return -RT_ERROR;
    }

}

char Connect_4G(void)
{
    
    
    //字符串与ASCII码的数据转换,结果存在usart_tx_data中,这里不再书写
    
    /* 发送给服务器端 */
    send_len = rt_device_write(serial, 0, usart_tx_data, (sizeof(usart_tx_data)));
    if (send_len != sizeof(usart_tx_data))
    {
    
    
        rt_kprintf("send data failed\r\n");
        return -RT_ERROR;
    }
    return 0;
}

The main function is responsible for sending the meter command in a 5s cycle and restarting the error judgment

int main(void)
{
    
    
    char not_connect_485=0;         //连接485未成功次数
    char uart_i=0;                  //所读电表的ID序列
    char not_con485_i[5]={
    
    0};
    int i = 0;                      //用于循环计数
    unsigned char test_data[8] = {
    
    0x02,0x03,0x00,0x1d,0x00,0x02,0x54,0x3e};

    LOG_D("Hello RT-Thread!");
    rt_pin_mode(15, PIN_MODE_OUTPUT);
    /* 创建一个动态互斥量 */
    dynamic_mutex = rt_mutex_create("dmutex", RT_IPC_FLAG_PRIO);
    if (dynamic_mutex == RT_NULL)
    {
    
    
        rt_kprintf("create dynamic mutex failed.\n");
        return -1;
    }
    //不用命令行,以直接初始化的方式初始化串口1和3
    //uart_sample();
    //uart_data_sample();
    while (1)
    {
    
    
        //延时5s
        rt_thread_mdelay(4000);
        //小于等于3时读第一个电表数据,等于4时发送给4G
        if(uart_i<=3)
        {
    
    
            not_con485_i[uart_i] = Connect_485(uart_i);
        }
        
        //所读电表id++,共0-4号五个电表
        uart_i=(uart_i+1)%5;
        
        rt_thread_mdelay(1000);
        rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);
        not_connect_4G++;
        rt_mutex_release(dynamic_mutex);
        if(not_connect_485==3||not_connect_4G>=3)
        {
    
    
            rt_kprintf("$$$$$$restart$$$$$$\r\n");
            //4G模块重启,加在一个cube里面定义引脚
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
            rt_thread_mdelay(800);
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
            //stm32模块重启
            __set_FAULTMASK(1);
            NVIC_SystemReset();
        }

    }

    return RT_EOK;
}

Guess you like

Origin blog.csdn.net/Sky777wdnmd/article/details/123555368