一、项目目标
构建一个基于 STM32 + TKB-623 LoRa 模组的无线测距系统,实现从传感器采集 → 本地处理 → AT 指令无线透传 → 远程接收的完整闭环,并验证其可靠性与实用性。
项目重点验证以下能力:
- STM32 与 TKB-623 模组的串口通信与 AT 指令控制;
- 基于异步终端工作模式的点对点无线数据透传;
- 端到端的数据完整性与通信稳定性;
- 轻量级上位机(Python)对无线传感数据的接收与处理。
二、硬件与软件准备
硬件清单
-
TKB-623 开发板 × 2(板 A:数据转发端,板 B:数据接收端)
-
HC-SR04 超声波传感器 × 1(测距,本次项目的数据来源)
-
USB-TTL 模块 × 2、若干杜邦线、天线 × 2、5V 电源
-
STM32F103C8T6(用于采集数据)
发送端(STM32F103连HC-SR04,STM32通过串口连TKB623,TKB623连电脑(通过电脑显示要发送的数据))

接收端

软件工具
- 串口助手(逐飞助手 / SSCOM)
- Python 3.8+(pyserial库:pip install pyserial)
三、编写程序
步骤 1:STM32实现超声波测距 + 配置板 A透传模式
板A的配置:
AT+FREQ=473200000
AT+RATE=9
AT+ADDR=00:00:00:01
AT+DEST=00:00:00:02
之后输入 ```
AT+WORKMODE=81,3,100
进入透传模式(将板A收到的数据转发给B板)

STM32F103C8T6的关键代码如下。
main.c
#include "stm32f10x.h"
#include "usart.h"
#include "HCSR04.h"
#include "delay.h"
#include "timer.h"
#include <stdio.h>
#include <string.h>
char buf[128] = {0};
void Send_To_TKB623(int data1, int data2) {
char data_str[32];
sprintf(data_str, "distances=%d.%d", data1, data2);
char hex_str[64] = {0};
for (int i = 0; i < strlen(data_str); i++) {
sprintf(&hex_str[i*2], "%02X", (unsigned char)data_str[i]);
}
char at_cmd[80];
sprintf(at_cmd, "AT+SENDB=%s\\r\\n", hex_str);
USART1_SendString(at_cmd);
}
int main(void)
{
USART1_Config();
USART2_Config();
Timer_Config();
HC_SR04_Config();
sprintf(buf,"AT\\r\\n");
USART1_SendString(buf);
while(1)
{
int Distance_mm=sonar_mm();
int Distance_m=Distance_mm/1000;
int Distance_m_p=Distance_mm%1000;
Send_To_TKB623(Distance_m,Distance_m_p);
delayms(1000);
}
}
usart.c
#include "usart.h"
void USART1_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_InitStructure.USART_BaudRate=115200;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
USART_Cmd(USART1,ENABLE);
}
void USART1_SendString(const char* str)
{
while(*str)
{
USART_SendData(USART1,*str++);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}
}
void USART1_IRQHandler()
{
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
{
USART_SendData(USART2,USART_ReceiveData(USART1));
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
delayms.c
void delayms(uint32_t t)
{
for (int i=0;i<1000;i++){
SysTick->CTRL = 0;
SysTick->LOAD=9*t-1;
SysTick->VAL=0;
SysTick->CTRL=1;
while((SysTick->CTRL & 0x00010000)==0);
SysTick->CTRL=0;
}
}
HCSR04.c
#include "HCSR04.h"
#include "delay.h"
uint64_t time=0;
uint64_t time_end=0;
void HC_SR04_Config(void)
{
RCC_APB2PeriphClockCmd(HCSR04_CLK,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = HCSR04_TRIG_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(HCSR04_PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO_PIN;
GPIO_Init(HCSR04_PORT,&GPIO_InitStructure);
GPIO_WriteBit(HCSR04_PORT,HCSR04_TRIG_PIN,Bit_RESET);
delayus(15);
}
uint16_t sonar_mm(void)
{
uint32_t Distance,Distance_mm = 0;
GPIO_WriteBit(HCSR04_PORT,HCSR04_TRIG_PIN,Bit_SET);
delayus(15);
GPIO_WriteBit(HCSR04_PORT,HCSR04_TRIG_PIN,Bit_RESET);
while(GPIO_ReadInputDataBit(HCSR04_PORT,HCSR04_ECHO_PIN) == 0);
time = 0;
while(GPIO_ReadInputDataBit(HCSR04_PORT,HCSR04_ECHO_PIN)==1);
time_end = time;
if(time_end/100 < 38)
{
Distance=(time_end*343) / 2;
Distance_mm=Distance/100;
}
if(Distance_mm < 20 || Distance_mm > 4000)
return 0;
delayms(50);
return Distance_mm;
}
float sonar(void)
{
float result = sonar_mm();
return result / 1000.0f;
}
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
time++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
省略Tim.c和其他头文件等文件。
步骤3:编写python脚本,通过PySerial将STM32的数据转发给A板(A板接收到数据后转发)
import serial
import time
STM32_COM = 'COM10'
TKB_COM = 'COM8'
BAUDRATE = 115200
def main():
stm32_ser = None
tkb_ser = None
try:
stm32_ser = serial.Serial(STM32_COM, BAUDRATE, timeout=1)
print(f"[✓] 已连接 STM32: {STM32_COM}")
tkb_ser = serial.Serial(TKB_COM, BAUDRATE, timeout=1)
print(f"[✓] 已连接 TKB623: {TKB_COM}")
print("[INFO] 开始转发:STM32 → TKB623(透传)")
print("按 Ctrl+C 退出...")
while True:
if stm32_ser.in_waiting > 0:
data = stm32_ser.read(stm32_ser.in_waiting)
try:
text = data.decode('utf-8', errors='replace')
print(f"[RX] 原始: {data.hex()} | 文本: {repr(text)}")
except:
print(f"[RX] 原始: {data.hex()}")
tkb_ser.write(data)
tkb_ser.flush()
time.sleep(0.01)
except KeyboardInterrupt:
print("\n[!] 用户中断退出")
except Exception as e:
print(f"[ERROR] {e}")
finally:
if stm32_ser and stm32_ser.is_open:
stm32_ser.close()
if tkb_ser and tkb_ser.is_open:
tkb_ser.close()
print("[✓] 串口已关闭")
if __name__ == '__main__':
main()

步骤3:配置板B,显示板B接收板A传来的数据
AT+FREQ=473200000
AT+RATE=9
AT+ADDR=00:00:00:02
再通过串口助手显示。

表示远程接收端板B已接收到数据。