diff --git a/vendor/build_sample.py b/vendor/MYF_F63/build_sample.py similarity index 97% rename from vendor/build_sample.py rename to vendor/MYF_F63/build_sample.py index 247c708c4cb192d4ab963396525b7cde90c33a11..3f87736a4fe789044473d7709289ea6930245c38 100644 --- a/vendor/build_sample.py +++ b/vendor/MYF_F63/build_sample.py @@ -1,446 +1,446 @@ -import subprocess -import json -import os -import sys -import time -import stat -import shutil -import hashlib -from typing import List, Dict, Union, Set, Optional - -#ws63编译配置 -BUILD_INFO_FILENAME = 'build_config.json' -ws63_cmakelist_search_string = 'build_component()' -ws63_compile_cmakelistfile = 'src/application/samples/CMakeLists.txt' -ws63_menuconfig_file = 'src/build/config/target_config/ws63/menuconfig/acore/ws63_liteos_app.config' -# 编译sample的路径 -ws63_compile_sample_directory = 'src/application/samples' -# 定义要执行的脚本文件和目录 -build_directory_path = 'src' -script_to_execute = 'build.py' -# log复制 -ws63_output_source_directory = 'output/ws63/fwpkg/ws63-liteos-app/ws63-liteos-app_all.fwpkg' -ws63_log_image_target_directory = 'archives' -global_combined = '' -ws63_error_h = 'src/include/errcode.h' -ws63_error_search_string = '#define ERRCODE_SUCC 0UL' -DEFAULT_BUILD_TIMEOUT = 60 * 5 - -def get_local_branches() -> List[str]: - """获取所有本地分支列表""" - result = subprocess.run( - ["git", "branch", "--format=%(refname:short)"], - capture_output=True, - text=True - ) - return result.stdout.strip().split("\n") if result.stdout else [] - -def check_changes_and_get_folders(changed_files: List[str]) -> Optional[Set[str]]: - """ - 检查变更文件并返回受影响的文件夹集合 - 返回None表示不需要构建或存在非法修改 - """ - # 检查是否有C/H文件修改 - has_c_or_h_files = any(f.endswith(('.c', '.h')) for f in changed_files) - - if not has_c_or_h_files: - print("Not need build, only non-source files modified") - return None - - # 检查是否修改了src目录或构建脚本 - for file_path in changed_files: - if '/' in file_path: - src_folder_name = file_path.split('/')[0] - if '"src' in src_folder_name or 'src' in src_folder_name: - print(f"invalid modify, not allow modify src dir and build script") - sys.exit(0) - # Check if it's modifying 'build_sample.py' file - if 'build_sample.py' in file_path: - print(f"invalid modify, not allow modify build_sample.py") - sys.exit(0) - # 提取三级文件夹结构 - changed_folders = set() - for file_path in changed_files: - if file_path.endswith(('.c', '.h')): - parts = file_path.split('/') - if len(parts) >= 4: # 确保路径深度足够 - folder_name = '+'.join(parts[1:4]) # 取第2-4级目录 - changed_folders.add(folder_name) - - print(f"[Changed folders]: {changed_folders}") - return changed_folders - -def compare_with_remote_master() -> Dict[str, Union[List[str], str, Set[str]]]: - """ - 比较所有本地分支与远程master分支的差异 - 返回字典格式: { - 分支名: { - 'files': 差异文件列表, - 'folders': 受影响的文件夹集合(仅C/H文件), - 'error': 错误信息(如果有) - } - } - """ - local_branches = get_local_branches() - remote_master = "origin/master" - branch_diff = {} - - print(f"Comparing {len(local_branches)} local branches with {remote_master}\n") - - for branch in local_branches: - print(f"🔍 Branch [{branch}] vs {remote_master}:") - branch_info = {} - - # 获取差异文件列表 - diff_result = subprocess.run( - ["git", "diff", "--name-only", remote_master, branch], - capture_output=True, - text=True - ) - - if diff_result.returncode != 0: - error_msg = f"Error: {diff_result.stderr.strip()}" - print(f"{error_msg}") - branch_info['error'] = error_msg - branch_diff[branch] = branch_info - continue - - changed_files = diff_result.stdout.strip().split("\n") if diff_result.stdout else [] - branch_info['files'] = changed_files - - if not changed_files: - print(" Identical to remote master") - else: - print(f"{len(changed_files)} changed files:") - for file in changed_files: - print(f"- {file}") - - # 检查变更并获取文件夹信息 - changed_folders = check_changes_and_get_folders(changed_files) - return changed_folders - - -# 获取代码仓所有build_info.json文件内容,并拼接在一起 -def process_build_info_files(): - print(f"start process_build_info_files") - result_list = [] - # 遍历指定目录及其子目录下的所有文件和文件夹 - for root, dirs, files in os.walk("./"): - for file in files: - if file == BUILD_INFO_FILENAME: - file_path = os.path.join(root, file) - print(file_path) - # 读取JSON文件内容 - with open(file_path, 'r') as f: - try: - data = json.load(f) - for item in data: - # 提取需要的字段值 - build_target = item.get('buildTarget', '') - relative_path = item.get('relativePath', '').replace('/','-') - chip_name = item.get('chip', '') - # 组合成一个字符串并添加到结果列表 - if item.get('buildDef', ''): - build_def = item.get('buildDef', '') - combined_value = f"{file_path}+{build_target}+{relative_path}+{chip_name}+{build_def}" - else: - combined_value = f"{file_path}+{build_target}+{relative_path}+{chip_name}" - result_list.append(combined_value) - except json.JSONDecodeError: - print(f"Error decoding JSON in file: {file_path}") - sys.exit(-1) - return result_list - -# 对比git和json内容,一致则代表提交,不一样代表之前提交 -def extract_exact_match(input_list, match_list): - print(f"start extract_exact_match") - print(f"[extract_exact_match] input_list: {input_list}") - print(f"[extract_exact_match] match_list: {match_list}") - exact_matches = [] - try: - for string in input_list: - # 使用 split 方法获取第二个 + 和第三个 + 之间的内容 - parts = string.split('+') - if len(parts) >= 4: - sample_company_name = parts[0].split('/')[2] - sample_name_field = parts[2].replace('-','+') - combined_string = sample_company_name + "+" + sample_name_field - if combined_string in match_list: - exact_matches.append(string) - print(f"[extract_exact_match] exact_matches: {exact_matches}") - if not exact_matches: - print(f"build-config has not been synchronously updated") - exit(0) # 退出并返回非零状 - else: - print("exact_matche 列表不为空") - return exact_matches - except TypeError as e: - print(f"Error: {e}") - # 在捕获到异常后,可以选择退出程序 - exit(0) # 退出并返回非零状 - -# 编译完成后复制log,镜像等到指定目录 -def move_file(source_file, new_filename): - print(f"start move_file") - target_file = os.path.join(f'../{ws63_log_image_target_directory}', f'{new_filename}.fwpkg') - os.chmod(source_file, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) - try: - # 移动并重命名文件 - shutil.move(source_file, target_file) - os.chmod(target_file, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) - print(f"文件 {source_file} 移动并重命名为 {target_file} 成功。") - except FileNotFoundError: - print(f"找不到文件 {source_file}") - except PermissionError: - print(f"权限错误,无法移动文件。") - except Exception as e: - print(f"发生错误:{str(e)}") - -# 编译案例 -def compile_sdk_and_save_log(build_target_name): - print(f"start compile_sdk_and_save_log") - current_dir = os.getcwd() - print(f"Current working directory: {current_dir}") - # 创建目录archives - if not os.path.exists("./archives"): - os.mkdir("./archives") - # 切换到目标目录src - os.chdir(build_directory_path) - log_path = os.path.join('..', 'archives', f'build-{global_combined}.log') - writer = os.fdopen(os.open( - log_path, - os.O_WRONLY | os.O_CREAT | os.O_TRUNC, - stat.S_IWUSR | stat.S_IRUSR, - ), 'wb') - reader = os.fdopen(os.open( - log_path, - os.O_RDONLY, - stat.S_IWUSR | stat.S_IRUSR, - ), 'rb') - os.chmod(log_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) - args = ['-c', build_target_name] - try: - # 打开日志文件准备写入 - # 使用 subprocess.Popen() 执行命令,并将标准输出和标准错误重定向到日志文件 - process = subprocess.Popen(['python', script_to_execute] + args, text=False, stdout=writer, stderr=writer) - # 等待进程结束 - start = time.time() - while True: - timeout = (time.time() - start) > DEFAULT_BUILD_TIMEOUT - - line = reader.readline() - - if line == b'': - if process.poll() is not None: - break - - time.sleep(2) - if not timeout: - continue - else: - process.kill() - raise Exception(f"build exit cause: timeout") - - try: - outs = line.decode('utf-8', errors='strict').rstrip() - except UnicodeDecodeError: - outs = line.decode('gbk', errors='replace').rstrip() - if not outs: - if not timeout: - continue - else: - process.kill() - raise Exception(f"build timeout") - print(outs) - # 检查进程的返回码 - if process.returncode == 0: - writer.write(b"Finished: SUCCESS") - print(f"SDK compilation succeeded. Log saved to '{log_path}'.") - else: - print(f"SDK compilation failed. Check '{log_path}' for details.") - writer.write(b"Finished: FAILURE") - # 输出标准错误信息到控制台 - print(process.stderr.read().decode('utf-8')) - - except Exception as e: - print(f"An error occurred: {str(e)}") - if writer: - writer.close() - if reader: - reader.close() - move_file(ws63_output_source_directory, global_combined) - - -# 删除指定cmakelist里面内容 -def delete_specific_content(file_path, content_to_delete): - print(f"start delete_specific_content") - # 读取文件内容 - with open(file_path, 'r') as file: - lines = file.readlines() - - # 处理文件内容,删除指定内容 - modified_lines = [line for line in lines if content_to_delete not in line] - - # 将修改后的内容写回文件 - with open(file_path, 'w') as file: - file.writelines(modified_lines) - - -# 如果代码编译cmakelist文件中通过宏控制编译哪些文件,则需要用宏重新组合编译配置信息 -def sample_meunconfig_modify(input_list): - print(f"start sample_meunconfig_modify{input_list}") - # 初始化结果列表 - result_list = [] - combined_string = "" - for string in input_list: - parts = string.split('+') - sample_file_path = parts[0] - build_target = parts[1] - sample_name = parts[2] - platform = parts[3] - if len(parts) == 5: - def_name = parts[4] - # 根据逗号分割字符串 - def_parts = def_name.split(',') - # 遍历分割后的每一个部分,添加到结果列表中,宏的个数,决定编译的次数 - if len(def_parts) == 1 and def_parts[0] == def_name: - result_list.append(sample_file_path+"+"+build_target+"+"+sample_name+"+"+platform+"+"+def_name) - else: - for idx, part in enumerate(def_parts): - if idx == 0: - combined_string = part - else: - combined_string += "+" + part # 在每个部分之前添加加号 - result_list.append(sample_file_path+"+"+build_target+"+"+sample_name+"+"+platform+"+"+combined_string) - # 如果没有逗号,则整个字符串保存到结果列表 - elif len(parts) == 4: - result_list.append(sample_file_path+"+"+build_target+"+"+sample_name+"+"+platform) - return result_list - -def insert_content_before_line(file_path, target_line, content_to_insert): - print(f"start insert_content_before_line") - found_target_line = False - try: - # 以读写模式打开文件 - with open(file_path, 'r') as file: - lines = file.readlines() - # 查找目标字符串并在其上一行添加列表中第一个内容 - for i in range(len(lines)): - if target_line in lines[i]: - lines.insert(i, content_to_insert) - found_target_line = True - print(f"成功在文件 {file_path} 中的目标行 {target_line} 的上一行插入内容{content_to_insert}。") - break # 找到目标字符串后添加内容并退出循环 - - # 如果没找到目标字符串则直接在该文件最后一行加入内容 - if not found_target_line: - lines.append(content_to_insert) - print(f"成功在文件 {file_path} 中的最后一行插入内容{content_to_insert}。") - - with open(file_path, 'w') as file: - file.writelines(lines) - except FileNotFoundError: - print(f"文件 {file_path} 未找到。") - - -# 定义一个自定义的复制函数 -def custom_copytree(src, dst): - # 拼接目标路径 - dst_path = os.path.join(dst, os.path.basename(src)) - - # 如果目标路径存在,则先删除目标路径 - if os.path.exists(dst_path): - shutil.rmtree(dst_path) - - # 执行复制操作 - shutil.copytree(src, dst_path) - -# sample编译前需要添加到编译工具链 -def sample_build_prepare(input_list): - print(f"start sample_build_prepare") - print(f"[sample_build_prepare] input_list: {input_list}") - global global_combined - for string in input_list: - parts = string.split('+') - sample_file_path = parts[0] - build_target = parts[1] - sample_name = parts[2] - platfor_name = parts[3] - if len(parts) == 5: - def_name = parts[4] - if '=y' in def_name or '= y' in def_name: - insert_content_before_line(ws63_menuconfig_file, ws63_cmakelist_search_string, f'{def_name}\n') - else: - def_name_cleaned = def_name.replace('=', ' ') - insert_content_before_line(ws63_error_h, ws63_error_search_string, f'#define {def_name_cleaned}\n') - elif len(parts) > 5: - for i in range(len(parts) - 4): - def_name = parts[4 + i] - if '=y' in def_name or '= y' in def_name: - insert_content_before_line(ws63_menuconfig_file, ws63_cmakelist_search_string, f'{def_name}\n') - else: - def_name_cleaned = def_name.replace('=', ' ') - insert_content_before_line(ws63_error_h, ws63_error_search_string, f'#define {def_name_cleaned}\n') - remaining_parts = parts[1:4] - global_combined = '_'.join(remaining_parts) - if len(parts) >= 5: - global_combined += '_' - build_def_sha = hashlib.sha256('-'.join(parts[4:]).encode('utf-8',errors='ignore')) - global_combined += build_def_sha.hexdigest()[:32] - # 获取sample目录路径,并复制到指定目录 - # 截取目标内容 - target_string = sample_file_path.split('/build_config.json')[0] + '/' - samples = sample_name.replace('-','/') - source_directory = target_string + samples - print(source_directory) - print(f"[sample_build_prepare] source_directory: {source_directory}") - try: - if os.path.exists(ws63_compile_sample_directory + '/' + sample_name.split("-")[1]): - shutil.rmtree(ws63_compile_sample_directory + '/' + sample_name.split("-")[1]) - # 使用shutil.copytree()函数复制整个目录树 - shutil.copytree(source_directory, os.path.join(ws63_compile_sample_directory, os.path.basename(source_directory))) - print(f"Directory '{source_directory}' successfully copied to '{ws63_compile_sample_directory}'") - except shutil.Error as e: - print(f"Error: {e}") - except OSError as e: - print(f"Error: {e.strerror}") - delete_specific_content(ws63_compile_cmakelistfile, f'add_subdirectory_if_exist({sample_name.split("-")[1]})') - insert_content_before_line(ws63_compile_cmakelistfile, ws63_cmakelist_search_string, f'add_subdirectory_if_exist({sample_name.split("-")[1]})\n') - compile_sdk_and_save_log(build_target) - current_dir = os.getcwd() - # 切换到上一层目录 - parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) - os.chdir(parent_dir) - try: - shutil.rmtree(ws63_compile_sample_directory + '/' + sample_name.split("-")[1]) - print(f"Directory {ws63_compile_sample_directory + '/' + sample_name} successfully removed recursively.") - except OSError as e: - print(f"Error: {ws63_compile_sample_directory} : {e.strerror}") - delete_specific_content(ws63_compile_cmakelistfile, f'add_subdirectory_if_exist({sample_name.split("-")[1]})') - if len(parts) == 5: - def_name = parts[4] - if '=y' in def_name or '= y' in def_name: - delete_specific_content(ws63_menuconfig_file, f'{def_name}') - else: - def_name_cleaned = def_name.replace('=', ' ') - delete_specific_content(ws63_error_h, f'#define {def_name_cleaned}') - elif len(parts) > 5: - for i in range(len(parts) - 4): - def_name = parts[4 + i] - if '=y' in def_name or '= y' in def_name: - delete_specific_content(ws63_menuconfig_file, f'{def_name}') - else: - def_name_cleaned = def_name.replace('=', ' ') - delete_specific_content(ws63_error_h, f'#define {def_name_cleaned}') - -def main(): - print(f"start main") - check_list = compare_with_remote_master() - input_list = process_build_info_files() - sample_name = extract_exact_match(input_list, check_list) - result_list = sample_meunconfig_modify(sample_name) - sample_build_prepare(result_list) - print(f"all build step execute end") - -if __name__ == '__main__': +import subprocess +import json +import os +import sys +import time +import stat +import shutil +import hashlib +from typing import List, Dict, Union, Set, Optional + +#ws63编译配置 +BUILD_INFO_FILENAME = 'build_config.json' +ws63_cmakelist_search_string = 'build_component()' +ws63_compile_cmakelistfile = 'src/application/samples/CMakeLists.txt' +ws63_menuconfig_file = 'src/build/config/target_config/ws63/menuconfig/acore/ws63_liteos_app.config' +# 编译sample的路径 +ws63_compile_sample_directory = 'src/application/samples' +# 定义要执行的脚本文件和目录 +build_directory_path = 'src' +script_to_execute = 'build.py' +# log复制 +ws63_output_source_directory = 'output/ws63/fwpkg/ws63-liteos-app/ws63-liteos-app_all.fwpkg' +ws63_log_image_target_directory = 'archives' +global_combined = '' +ws63_error_h = 'src/include/errcode.h' +ws63_error_search_string = '#define ERRCODE_SUCC 0UL' +DEFAULT_BUILD_TIMEOUT = 60 * 5 + +def get_local_branches() -> List[str]: + """获取所有本地分支列表""" + result = subprocess.run( + ["git", "branch", "--format=%(refname:short)"], + capture_output=True, + text=True + ) + return result.stdout.strip().split("\n") if result.stdout else [] + +def check_changes_and_get_folders(changed_files: List[str]) -> Optional[Set[str]]: + """ + 检查变更文件并返回受影响的文件夹集合 + 返回None表示不需要构建或存在非法修改 + """ + # 检查是否有C/H文件修改 + has_c_or_h_files = any(f.endswith(('.c', '.h')) for f in changed_files) + + if not has_c_or_h_files: + print("Not need build, only non-source files modified") + return None + + # 检查是否修改了src目录或构建脚本 + for file_path in changed_files: + if '/' in file_path: + src_folder_name = file_path.split('/')[0] + if '"src' in src_folder_name or 'src' in src_folder_name: + print(f"invalid modify, not allow modify src dir and build script") + sys.exit(0) + # Check if it's modifying 'build_sample.py' file + if 'build_sample.py' in file_path: + print(f"invalid modify, not allow modify build_sample.py") + sys.exit(0) + # 提取三级文件夹结构 + changed_folders = set() + for file_path in changed_files: + if file_path.endswith(('.c', '.h')): + parts = file_path.split('/') + if len(parts) >= 4: # 确保路径深度足够 + folder_name = '+'.join(parts[1:4]) # 取第2-4级目录 + changed_folders.add(folder_name) + + print(f"[Changed folders]: {changed_folders}") + return changed_folders + +def compare_with_remote_master() -> Dict[str, Union[List[str], str, Set[str]]]: + """ + 比较所有本地分支与远程master分支的差异 + 返回字典格式: { + 分支名: { + 'files': 差异文件列表, + 'folders': 受影响的文件夹集合(仅C/H文件), + 'error': 错误信息(如果有) + } + } + """ + local_branches = get_local_branches() + remote_master = "origin/master" + branch_diff = {} + + print(f"Comparing {len(local_branches)} local branches with {remote_master}\n") + + for branch in local_branches: + print(f"🔍 Branch [{branch}] vs {remote_master}:") + branch_info = {} + + # 获取差异文件列表 + diff_result = subprocess.run( + ["git", "diff", "--name-only", remote_master, branch], + capture_output=True, + text=True + ) + + if diff_result.returncode != 0: + error_msg = f"Error: {diff_result.stderr.strip()}" + print(f"{error_msg}") + branch_info['error'] = error_msg + branch_diff[branch] = branch_info + continue + + changed_files = diff_result.stdout.strip().split("\n") if diff_result.stdout else [] + branch_info['files'] = changed_files + + if not changed_files: + print(" Identical to remote master") + else: + print(f"{len(changed_files)} changed files:") + for file in changed_files: + print(f"- {file}") + + # 检查变更并获取文件夹信息 + changed_folders = check_changes_and_get_folders(changed_files) + return changed_folders + + +# 获取代码仓所有build_info.json文件内容,并拼接在一起 +def process_build_info_files(): + print(f"start process_build_info_files") + result_list = [] + # 遍历指定目录及其子目录下的所有文件和文件夹 + for root, dirs, files in os.walk("./"): + for file in files: + if file == BUILD_INFO_FILENAME: + file_path = os.path.join(root, file) + print(file_path) + # 读取JSON文件内容 + with open(file_path, 'r') as f: + try: + data = json.load(f) + for item in data: + # 提取需要的字段值 + build_target = item.get('buildTarget', '') + relative_path = item.get('relativePath', '').replace('/','-') + chip_name = item.get('chip', '') + # 组合成一个字符串并添加到结果列表 + if item.get('buildDef', ''): + build_def = item.get('buildDef', '') + combined_value = f"{file_path}+{build_target}+{relative_path}+{chip_name}+{build_def}" + else: + combined_value = f"{file_path}+{build_target}+{relative_path}+{chip_name}" + result_list.append(combined_value) + except json.JSONDecodeError: + print(f"Error decoding JSON in file: {file_path}") + sys.exit(-1) + return result_list + +# 对比git和json内容,一致则代表提交,不一样代表之前提交 +def extract_exact_match(input_list, match_list): + print(f"start extract_exact_match") + print(f"[extract_exact_match] input_list: {input_list}") + print(f"[extract_exact_match] match_list: {match_list}") + exact_matches = [] + try: + for string in input_list: + # 使用 split 方法获取第二个 + 和第三个 + 之间的内容 + parts = string.split('+') + if len(parts) >= 4: + sample_company_name = parts[0].split('/')[2] + sample_name_field = parts[2].replace('-','+') + combined_string = sample_company_name + "+" + sample_name_field + if combined_string in match_list: + exact_matches.append(string) + print(f"[extract_exact_match] exact_matches: {exact_matches}") + if not exact_matches: + print(f"build-config has not been synchronously updated") + exit(0) # 退出并返回非零状 + else: + print("exact_matche 列表不为空") + return exact_matches + except TypeError as e: + print(f"Error: {e}") + # 在捕获到异常后,可以选择退出程序 + exit(0) # 退出并返回非零状 + +# 编译完成后复制log,镜像等到指定目录 +def move_file(source_file, new_filename): + print(f"start move_file") + target_file = os.path.join(f'../{ws63_log_image_target_directory}', f'{new_filename}.fwpkg') + os.chmod(source_file, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) + try: + # 移动并重命名文件 + shutil.move(source_file, target_file) + os.chmod(target_file, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) + print(f"文件 {source_file} 移动并重命名为 {target_file} 成功。") + except FileNotFoundError: + print(f"找不到文件 {source_file}") + except PermissionError: + print(f"权限错误,无法移动文件。") + except Exception as e: + print(f"发生错误:{str(e)}") + +# 编译案例 +def compile_sdk_and_save_log(build_target_name): + print(f"start compile_sdk_and_save_log") + current_dir = os.getcwd() + print(f"Current working directory: {current_dir}") + # 创建目录archives + if not os.path.exists("./archives"): + os.mkdir("./archives") + # 切换到目标目录src + os.chdir(build_directory_path) + log_path = os.path.join('..', 'archives', f'build-{global_combined}.log') + writer = os.fdopen(os.open( + log_path, + os.O_WRONLY | os.O_CREAT | os.O_TRUNC, + stat.S_IWUSR | stat.S_IRUSR, + ), 'wb') + reader = os.fdopen(os.open( + log_path, + os.O_RDONLY, + stat.S_IWUSR | stat.S_IRUSR, + ), 'rb') + os.chmod(log_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) + args = ['-c', build_target_name] + try: + # 打开日志文件准备写入 + # 使用 subprocess.Popen() 执行命令,并将标准输出和标准错误重定向到日志文件 + process = subprocess.Popen(['python', script_to_execute] + args, text=False, stdout=writer, stderr=writer) + # 等待进程结束 + start = time.time() + while True: + timeout = (time.time() - start) > DEFAULT_BUILD_TIMEOUT + + line = reader.readline() + + if line == b'': + if process.poll() is not None: + break + + time.sleep(2) + if not timeout: + continue + else: + process.kill() + raise Exception(f"build exit cause: timeout") + + try: + outs = line.decode('utf-8', errors='strict').rstrip() + except UnicodeDecodeError: + outs = line.decode('gbk', errors='replace').rstrip() + if not outs: + if not timeout: + continue + else: + process.kill() + raise Exception(f"build timeout") + print(outs) + # 检查进程的返回码 + if process.returncode == 0: + writer.write(b"Finished: SUCCESS") + print(f"SDK compilation succeeded. Log saved to '{log_path}'.") + else: + print(f"SDK compilation failed. Check '{log_path}' for details.") + writer.write(b"Finished: FAILURE") + # 输出标准错误信息到控制台 + print(process.stderr.read().decode('utf-8')) + + except Exception as e: + print(f"An error occurred: {str(e)}") + if writer: + writer.close() + if reader: + reader.close() + move_file(ws63_output_source_directory, global_combined) + + +# 删除指定cmakelist里面内容 +def delete_specific_content(file_path, content_to_delete): + print(f"start delete_specific_content") + # 读取文件内容 + with open(file_path, 'r') as file: + lines = file.readlines() + + # 处理文件内容,删除指定内容 + modified_lines = [line for line in lines if content_to_delete not in line] + + # 将修改后的内容写回文件 + with open(file_path, 'w') as file: + file.writelines(modified_lines) + + +# 如果代码编译cmakelist文件中通过宏控制编译哪些文件,则需要用宏重新组合编译配置信息 +def sample_meunconfig_modify(input_list): + print(f"start sample_meunconfig_modify{input_list}") + # 初始化结果列表 + result_list = [] + combined_string = "" + for string in input_list: + parts = string.split('+') + sample_file_path = parts[0] + build_target = parts[1] + sample_name = parts[2] + platform = parts[3] + if len(parts) == 5: + def_name = parts[4] + # 根据逗号分割字符串 + def_parts = def_name.split(',') + # 遍历分割后的每一个部分,添加到结果列表中,宏的个数,决定编译的次数 + if len(def_parts) == 1 and def_parts[0] == def_name: + result_list.append(sample_file_path+"+"+build_target+"+"+sample_name+"+"+platform+"+"+def_name) + else: + for idx, part in enumerate(def_parts): + if idx == 0: + combined_string = part + else: + combined_string += "+" + part # 在每个部分之前添加加号 + result_list.append(sample_file_path+"+"+build_target+"+"+sample_name+"+"+platform+"+"+combined_string) + # 如果没有逗号,则整个字符串保存到结果列表 + elif len(parts) == 4: + result_list.append(sample_file_path+"+"+build_target+"+"+sample_name+"+"+platform) + return result_list + +def insert_content_before_line(file_path, target_line, content_to_insert): + print(f"start insert_content_before_line") + found_target_line = False + try: + # 以读写模式打开文件 + with open(file_path, 'r') as file: + lines = file.readlines() + # 查找目标字符串并在其上一行添加列表中第一个内容 + for i in range(len(lines)): + if target_line in lines[i]: + lines.insert(i, content_to_insert) + found_target_line = True + print(f"成功在文件 {file_path} 中的目标行 {target_line} 的上一行插入内容{content_to_insert}。") + break # 找到目标字符串后添加内容并退出循环 + + # 如果没找到目标字符串则直接在该文件最后一行加入内容 + if not found_target_line: + lines.append(content_to_insert) + print(f"成功在文件 {file_path} 中的最后一行插入内容{content_to_insert}。") + + with open(file_path, 'w') as file: + file.writelines(lines) + except FileNotFoundError: + print(f"文件 {file_path} 未找到。") + + +# 定义一个自定义的复制函数 +def custom_copytree(src, dst): + # 拼接目标路径 + dst_path = os.path.join(dst, os.path.basename(src)) + + # 如果目标路径存在,则先删除目标路径 + if os.path.exists(dst_path): + shutil.rmtree(dst_path) + + # 执行复制操作 + shutil.copytree(src, dst_path) + +# sample编译前需要添加到编译工具链 +def sample_build_prepare(input_list): + print(f"start sample_build_prepare") + print(f"[sample_build_prepare] input_list: {input_list}") + global global_combined + for string in input_list: + parts = string.split('+') + sample_file_path = parts[0] + build_target = parts[1] + sample_name = parts[2] + platfor_name = parts[3] + if len(parts) == 5: + def_name = parts[4] + if '=y' in def_name or '= y' in def_name: + insert_content_before_line(ws63_menuconfig_file, ws63_cmakelist_search_string, f'{def_name}\n') + else: + def_name_cleaned = def_name.replace('=', ' ') + insert_content_before_line(ws63_error_h, ws63_error_search_string, f'#define {def_name_cleaned}\n') + elif len(parts) > 5: + for i in range(len(parts) - 4): + def_name = parts[4 + i] + if '=y' in def_name or '= y' in def_name: + insert_content_before_line(ws63_menuconfig_file, ws63_cmakelist_search_string, f'{def_name}\n') + else: + def_name_cleaned = def_name.replace('=', ' ') + insert_content_before_line(ws63_error_h, ws63_error_search_string, f'#define {def_name_cleaned}\n') + remaining_parts = parts[1:4] + global_combined = '_'.join(remaining_parts) + if len(parts) >= 5: + global_combined += '_' + build_def_sha = hashlib.sha256('-'.join(parts[4:]).encode('utf-8',errors='ignore')) + global_combined += build_def_sha.hexdigest()[:32] + # 获取sample目录路径,并复制到指定目录 + # 截取目标内容 + target_string = sample_file_path.split('/build_config.json')[0] + '/' + samples = sample_name.replace('-','/') + source_directory = target_string + samples + print(source_directory) + print(f"[sample_build_prepare] source_directory: {source_directory}") + try: + if os.path.exists(ws63_compile_sample_directory + '/' + sample_name.split("-")[1]): + shutil.rmtree(ws63_compile_sample_directory + '/' + sample_name.split("-")[1]) + # 使用shutil.copytree()函数复制整个目录树 + shutil.copytree(source_directory, os.path.join(ws63_compile_sample_directory, os.path.basename(source_directory))) + print(f"Directory '{source_directory}' successfully copied to '{ws63_compile_sample_directory}'") + except shutil.Error as e: + print(f"Error: {e}") + except OSError as e: + print(f"Error: {e.strerror}") + delete_specific_content(ws63_compile_cmakelistfile, f'add_subdirectory_if_exist({sample_name.split("-")[1]})') + insert_content_before_line(ws63_compile_cmakelistfile, ws63_cmakelist_search_string, f'add_subdirectory_if_exist({sample_name.split("-")[1]})\n') + compile_sdk_and_save_log(build_target) + current_dir = os.getcwd() + # 切换到上一层目录 + parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) + os.chdir(parent_dir) + try: + shutil.rmtree(ws63_compile_sample_directory + '/' + sample_name.split("-")[1]) + print(f"Directory {ws63_compile_sample_directory + '/' + sample_name} successfully removed recursively.") + except OSError as e: + print(f"Error: {ws63_compile_sample_directory} : {e.strerror}") + delete_specific_content(ws63_compile_cmakelistfile, f'add_subdirectory_if_exist({sample_name.split("-")[1]})') + if len(parts) == 5: + def_name = parts[4] + if '=y' in def_name or '= y' in def_name: + delete_specific_content(ws63_menuconfig_file, f'{def_name}') + else: + def_name_cleaned = def_name.replace('=', ' ') + delete_specific_content(ws63_error_h, f'#define {def_name_cleaned}') + elif len(parts) > 5: + for i in range(len(parts) - 4): + def_name = parts[4 + i] + if '=y' in def_name or '= y' in def_name: + delete_specific_content(ws63_menuconfig_file, f'{def_name}') + else: + def_name_cleaned = def_name.replace('=', ' ') + delete_specific_content(ws63_error_h, f'#define {def_name_cleaned}') + +def main(): + print(f"start main") + check_list = compare_with_remote_master() + input_list = process_build_info_files() + sample_name = extract_exact_match(input_list, check_list) + result_list = sample_meunconfig_modify(sample_name) + sample_build_prepare(result_list) + print(f"all build step execute end") + +if __name__ == '__main__': sys.exit(main()) \ No newline at end of file diff --git a/vendor/developers/build_config.json b/vendor/developers/build_config.json index e8cdd56b3614eb7d656c4ff34a82e9f1094bb6a5..9debfec29d2f83f3b8d7c4f04bbb1f96deb6eceb 100644 --- a/vendor/developers/build_config.json +++ b/vendor/developers/build_config.json @@ -69,4 +69,24 @@ "name": "SLEController", "description": "星闪控制器" } + { + "buildTarget": "ws63-liteos-app", + "relativePath": "demo/STEPMOTOR", + "chip": "WS63", + "buildDef": "", + "needSmoke": "false", + "key": "6", + "name": "STEPMOTOR", + "description": "GPIO模拟pwm" + }, + { + "buildTarget": "ws63-liteos-app", + "relativePath": "demo/SG90", + "chip": "WS63", + "buildDef": "", + "needSmoke": "false", + "key": "6", + "name": "SG90", + "description": "SG90舵机驱动" + } ] diff --git a/vendor/developers/demo/SG90/CMakeLists.txt b/vendor/developers/demo/SG90/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8732e737240284f3d8766ca6172cf681f16345da --- /dev/null +++ b/vendor/developers/demo/SG90/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SOURCES_LIST +${CMAKE_CURRENT_SOURCE_DIR}/SG90.c +# ${CMAKE_CURRENT_SOURCE_DIR}/servo_control.c +${CMAKE_CURRENT_SOURCE_DIR}/sle_uart_server/sle_uart_server_adv.c +${CMAKE_CURRENT_SOURCE_DIR}/sle_uart_server/sle_uart_server.c +) + +set(PUBLIC_HEADER_LIST +${CMAKE_CURRENT_SOURCE_DIR}/sle_uart_server +) + +set(SOURCES "${SOURCES_LIST}" PARENT_SCOPE) +set(PUBLIC_HEADER "${PUBLIC_HEADER_LIST}" PARENT_SCOPE) diff --git a/vendor/developers/demo/SG90/SG90.c b/vendor/developers/demo/SG90/SG90.c new file mode 100644 index 0000000000000000000000000000000000000000..e7411821bb36348d2082f0ecb2d11885cac959bd --- /dev/null +++ b/vendor/developers/demo/SG90/SG90.c @@ -0,0 +1,228 @@ +#include "common_def.h" +#include "securec.h" +#include "soc_osal.h" +#include "sle_errcode.h" +#include "sle_connection_manager.h" +#include "sle_device_discovery.h" +#include "sle_uart_server_adv.h" +#include "sle_uart_server.h" +#include "boards.h" +#include "pinctrl.h" +#include "gpio.h" +#include "timer.h" +#include "osal_debug.h" +#include "cmsis_os2.h" +#include "app_init.h" +#include "tcxo.h" +#include "chip_core_irq.h" +#include "hal_gpio.h" + +/* 定义舵机控制相关的宏 */ +#define RELAY_PIN 7 +#define SERVO_PIN 13 +#define SERVO_TASK_STACK_SIZE 0x1000 +#define SERVO_PULSE_MIN 500 // 最小脉冲宽度(us) +#define SERVO_PULSE_MAX 1500 // 最大脉冲宽度(us) +#define SERVO_PERIOD 20000 // 周期 20ms(us) + +/* 定义星闪相关宏 */ +#define SLE_UART_SERVER_LOG "[SLE Servo Server]" + +/* 定义舵机状态 */ +volatile int is_command = 0; // 0表示没有控制命令,1表示有控制命令 +volatile int servo_command = 0; // 0表示OFF(关门),1表示ON(开门) +volatile int servo_status = 0; // 0表示第一次启动定时器,1表示后续 +extern volatile bool g_reconnect_needed; + +/* 舵机控制结构体 */ +typedef struct servo_info { + uint32_t start_time; + uint32_t end_time; + uint32_t pulse_width; +} servo_info_t; + +static servo_info_t g_servo_info = {0, 0, SERVO_PULSE_MIN}; +static timer_handle_t servo_timer = 0; // 定时器句柄定义为全局变量 +static volatile int servo_timer_running = 0; // 标记定时器是否正在运行 + +/* 定时器回调函数 */ +static void servo_timer_callback(uintptr_t data) +{ + UNUSED(data); + // 生成 PWM 信号 + uapi_gpio_set_val(SERVO_PIN, GPIO_LEVEL_HIGH); + uapi_tcxo_delay_us(g_servo_info.pulse_width); + uapi_gpio_set_val(SERVO_PIN, GPIO_LEVEL_LOW); +} + +/* 舵机控制任务 */ +static void *servo_task(const char *arg) { + unused(arg); + if(servo_status==0) + { + // 初始化 GPIO + uapi_pin_set_mode(SERVO_PIN, HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(SERVO_PIN, GPIO_DIRECTION_OUTPUT); + + uapi_pin_set_mode(RELAY_PIN, HAL_PIO_FUNC_GPIO); + uapi_gpio_set_dir(RELAY_PIN, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_val(RELAY_PIN, GPIO_LEVEL_HIGH); // 初始化继电器为高电平 + + // 初始化定时器 + if (uapi_timer_init() != 0) { + osal_printk("Timer init failed!\r\n"); + return NULL; + } + + if (uapi_timer_adapter(1, TIMER_1_IRQN, 1) != 0) { + osal_printk("Timer adapter failed!\r\n"); + return NULL; + } + + + // 创建定时器 + if (uapi_timer_create(1, &servo_timer) != 0) { + osal_printk("Timer create failed!\r\n"); + return NULL; + } + servo_status=1; + } + + + while (1) { + if (is_command != 0) { // 只有在有命令时才启动定时器 + // 配置PWM信号 + g_servo_info.pulse_width = (servo_command == 1) ? SERVO_PULSE_MAX : SERVO_PULSE_MIN; + + // 启动定时器 + if (uapi_timer_start(servo_timer, SERVO_PERIOD, servo_timer_callback, 0) != 0) { + osal_printk("Timer start failed!\r\n"); + return NULL; + } + servo_timer_running = 1; + is_command=0; + // 打印结果 + osal_printk("Servo Control. Pulse Width: %dus\r\n", g_servo_info.pulse_width); + } else if (servo_timer_running) { + // 停止定时器 + uapi_timer_stop(servo_timer); + servo_timer_running = 0; + } + + // 短暂休眠以避免过度占用CPU + osal_msleep(100); + } + + // 停止并删除定时器 + uapi_timer_stop(servo_timer); + uapi_timer_delete(servo_timer); + + return NULL; +} +static void ssaps_server_read_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_read_cb_t *read_cb_para, + errcode_t status) +{ + osal_printk("%s ssaps read request cbk callback server_id:%x, conn_id:%x, handle:%x, status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, conn_id, read_cb_para->handle, status); +} +/* 星闪服务端接收数据回调 */ +static void ssaps_server_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para, errcode_t status) { + osal_printk("%s ssaps write request callback cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, conn_id, write_cb_para->handle, status); + if ((write_cb_para->length > 0) && write_cb_para->value) { + osal_printk("\n sle uart recived data : %.*s\r\n", write_cb_para->length, write_cb_para->value); + + /* 手动解析JSON格式数据 */ + char *data = (char *)write_cb_para->value; + char *state_start = NULL; + char *state_end = NULL; + + // 查找 "state":" 的位置 + state_start = strstr(data, "\"state\":\""); + if (state_start != NULL) { + state_start += strlen("\"state\":\""); // 跳过 "state":" 前缀 + + // 查找结束引号的位置 + state_end = strchr(state_start, '\"'); + if (state_end != NULL) { + // 计算状态字符串的长度 + size_t state_len = state_end - state_start; + + // 提取状态字符串 + char state[4] = {0}; // 最多3个字符(ON/OFF) + 终止符 + if (state_len <= 3) { + strncpy(state, state_start, state_len); + + osal_printk("Extracted state: %s\r\n", state); + + /* 解析提取到的状态并设置舵机命令 */ + if (strncmp(state, "ON", 2) == 0) { + servo_command = 1; // 开门 + is_command = 1; + } else if (strncmp(state, "OFF", 3) == 0) { + servo_command = 0; // 关门 + is_command = 1; + } else { + osal_printk("Received invalid state value. Use 'ON' or 'OFF'.\r\n"); + // 忽略无效状态值,不改变舵机状态 + } + } else { + osal_printk("State value too long.\r\n"); + } + } else { + osal_printk("Missing closing quote for state value.\r\n"); + } + } else { + osal_printk("Missing \"state\" field in JSON data.\r\n"); + } + } +} + + +/* 星闪服务端任务 */ +static void *sle_uart_server_task(const char *arg) { + unused(arg); + /* 创建舵机控制任务 */ + osal_task *servo_task_handle = NULL; + osal_kthread_lock(); + servo_task_handle = osal_kthread_create((osal_kthread_handler)servo_task, 0, "ServoControlTask", 0x1000); + if (servo_task_handle != NULL) { + osal_kthread_set_priority(servo_task_handle, 24); + } + osal_kthread_unlock(); + + /* 初始化星闪服务端 */ + sle_uart_server_init(NULL, ssaps_server_write_request_cbk); + sle_uart_server_adv_init(); + + while (1) { + // 添加自动重连检测 + if (g_reconnect_needed) { + osal_printk("[SLE] Starting auto-reconnect process...\r\n"); + + /* 初始化星闪服务端 */ + sle_uart_server_init(ssaps_server_read_request_cbk, ssaps_server_write_request_cbk); + sle_uart_server_adv_init(); + + g_reconnect_needed = false; + } + + osal_msleep(1000); // 降低CPU占用 + } + return NULL; +} + +/* 服务端入口 */ +static void sle_uart_server_entry(void) { + osal_task *task_handle = NULL; + osal_kthread_lock(); + task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_server_task, 0, "SLEUartServerTask", 0x1200); + if (task_handle != NULL) { + osal_kthread_set_priority(task_handle, 28); + } + osal_kthread_unlock(); +} + +/* 运行服务端入口 */ +app_run(sle_uart_server_entry); + diff --git a/vendor/developers/demo/SG90/SG90.h b/vendor/developers/demo/SG90/SG90.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server.c b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server.c new file mode 100644 index 0000000000000000000000000000000000000000..4b9bcfd3f08db7e5ba5f6fa9e6806eed1e325b86 --- /dev/null +++ b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server.c @@ -0,0 +1,581 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: SLE UART Server Source. \n + * + * History: \n + * 2023-07-17, Create file. \n + * + * Code Comments: Written by Lamonce. + * Last Modified: April 10, 2025 \n + * + * Introduction: + * This file implements the server-side functionality for SLE UART + * communications. It provides a complete GATT server implementation with service, + * characteristic, and descriptor management. The server broadcasts its availability, + * accepts client connections, handles pairing, and supports bidirectional data transmission + * through both UUID-based and handle-based methods. Key features include connection state + * management, callback registration for various events, and MTU negotiation. + * + * 简介: + * 本文件实现了 SLE UART 通信的服务端功能。它提供了完整的 GATT 服务器 + * 实现,包括服务、特征和描述符管理。服务端广播自身可用性,接受客户端连接请求,处理配对过程, + * 并通过基于 UUID 和基于句柄的方法支持双向数据传输。主要功能包括连接状态管理、各类事件的 + * 回调注册以及 MTU 协商。整个实现遵循星闪低功耗通信协议规范,确保高效、可靠的设备间通信。 + * + * DISCLAIMER: + * This code is provided for reference and learning purposes only. + * No warranty of correctness, completeness, or suitability for any purpose is provided. + * Before using in production environments, please review and test thoroughly. + * + * 免责声明: + * 本文件中的代码及注释仅供学习和参考使用,不保证其在所有环境下的正确性和完整性。 + * 在实际项目中使用前,请根据具体需求进行适当的修改和测试。 + * + */ + +#include "common_def.h" // 常用函数定义 +#include "securec.h" // 安全函数库,用于替代标准 C 库中不安全的函数 +#include "soc_osal.h" // 硬件抽象层 +#include "sle_errcode.h" // 错误码定义 +#include "sle_connection_manager.h" // 连接管理 +#include "sle_device_discovery.h" // 设备发现相关 +#include "sle_uart_server_adv.h" // 广播相关头文件 +#include "sle_uart_server.h" // Server 端头文件 + +#define OCTET_BIT_LEN 8 +#define UUID_LEN_2 2 // 16 位 UUID 长度 +#define UUID_INDEX 14 // UUID 最后两字节索引 +#define BT_INDEX_4 4 +#define BT_INDEX_0 0 +#define UART_BUFF_LENGTH 0x100 // UART 缓冲区长度 + +/* 广播ID */ +#define SLE_ADV_HANDLE_DEFAULT 1 // 设备公开 ID +/* sle server app uuid for test */ +static char g_sle_uuid_app_uuid[UUID_LEN_2] = {0x12, 0x34}; // 服务端应用 UUID +/* server notify property uuid for test */ +static char g_sle_property_value[OCTET_BIT_LEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; // 特征值 +/* sle connect acb handle */ +static uint16_t g_sle_conn_hdl = 0; // 连接句柄 +/* sle server handle */ +static uint8_t g_server_id = 0; // 服务端 ID +/* sle service handle */ +static uint16_t g_service_handle = 0; // 服务句柄 +/* sle ntf property handle */ +static uint16_t g_property_handle = 0; // 特征句柄 +/* sle pair acb handle */ +uint16_t g_sle_pair_hdl; // 配对句柄 + +#define UUID_16BIT_LEN 2 // 16 位 UUID 长度 +#define UUID_128BIT_LEN 16 // 128 位 UUID 长度 +#define sample_at_log_print(fmt, args...) osal_printk(fmt, ##args) +#define SLE_UART_SERVER_LOG "[sle uart server]" // 日志前缀 +#define SLE_SERVER_INIT_DELAY_MS 1000 // 延时 1 秒 +static sle_uart_server_msg_queue g_sle_uart_server_msg_queue = NULL; // 消息队列 + +// 星闪标准服务标识 基础标识(Base UUID):37BEA880-FC70-11EA-B720-000000000000 +// 带上这个基础标识表示这个星闪服务 +// Base UUID 后面6字节是媒体接入层标识(在某个网段内,分配给网络设备的用于网络通信寻址的唯一标识) +// 在用于产品开发时,厂商需要向 SparkLink 组织申请 +static uint8_t g_sle_uart_base[] = {0x37, 0xBE, 0xA8, 0x80, 0xFC, 0x70, 0x11, 0xEA, + 0xB7, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// 获取连接句柄 +uint16_t get_connect_id(void) +{ + return g_sle_conn_hdl; +} + +/** + * @brief 将16位整数(uint16_t)以小端字节序的方式存储到内存中 + * + * @param _ptr 低地址指针 + * @param data 数据 + * + * @attention 将16位整数 data 的低8位(最低有效字节)存储到 _ptr 指向的地址 + * @attention 将16位整数 data 的高8位(最高有效字节)存储到 _ptr+1 指向的地址 + */ +static void encode2byte_little(uint8_t *_ptr, uint16_t data) +{ + *(uint8_t *)((_ptr) + 1) = (uint8_t)((data) >> 0x8); + *(uint8_t *)(_ptr) = (uint8_t)(data); +} + +// 设置服务 UUID 的基础值 +static void sle_uuid_set_base(sle_uuid_t *out) +{ + errcode_t ret; + // 复制 UUID 的基础值 + ret = memcpy_s(out->uuid, SLE_UUID_LEN, g_sle_uart_base, SLE_UUID_LEN); + if (ret != EOK) + { + sample_at_log_print("%s sle_uuid_set_base memcpy fail\n", SLE_UART_SERVER_LOG); + out->len = 0; + return; + } + out->len = UUID_LEN_2; // 设置 UUID 的长度为 2 +} + +// 设置长度为 2 的服务 UUID 的值 +static void sle_uuid_setu2(uint16_t u2, sle_uuid_t *out) +{ + sle_uuid_set_base(out); // 设置 UUID 的基础值 + out->len = UUID_LEN_2; // 设置 UUID 的长度为 2 + encode2byte_little(&out->uuid[UUID_INDEX], u2); // 将 16 位整数以小端字节序存储到 UUID 末尾 +} + +// 输出 UUID 的值 +static void sle_uart_uuid_print(sle_uuid_t *uuid) +{ + if (uuid == NULL) + { + sample_at_log_print("%s uuid_print,uuid is null\r\n", SLE_UART_SERVER_LOG); + return; + } + + // 检查 UUID 长度 + if (uuid->len == UUID_16BIT_LEN) + { + sample_at_log_print("%s uuid: %02x %02x.\n", SLE_UART_SERVER_LOG, + uuid->uuid[14], uuid->uuid[15]); /* 14 15: uuid index */ + } + else if (uuid->len == UUID_128BIT_LEN) + { + sample_at_log_print("%s uuid: \n", SLE_UART_SERVER_LOG); /* 14 15: uuid index */ + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[0], uuid->uuid[1], + uuid->uuid[2], uuid->uuid[3]); + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[4], uuid->uuid[5], + uuid->uuid[6], uuid->uuid[7]); + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[8], uuid->uuid[9], + uuid->uuid[10], uuid->uuid[11]); + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[12], uuid->uuid[13], + uuid->uuid[14], uuid->uuid[15]); + } +} + +// -------- ssapc 注册回调函数 -------- + +/** + * @brief MTU 改变回调函数 + * + * @param server_id 服务 ID + * @param conn_id 连接 ID + * @param mtu_size MTU 大小 + * @param status 状态码 + */ +static void ssaps_mtu_changed_cbk(uint8_t server_id, uint16_t conn_id, ssap_exchange_info_t *mtu_size, + errcode_t status) +{ + sample_at_log_print("%s ssaps ssaps_mtu_changed_cbk callback server_id:%x, conn_id:%x, mtu_size:%x, status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, conn_id, mtu_size->mtu_size, status); + if (g_sle_pair_hdl == 0) + { + g_sle_pair_hdl = conn_id + 1; + } +} + +/** + * @brief 服务启动回调函数 + * + * @param server_id 服务 ID + * @param handle 服务句柄 + * @param status 状态码 + */ +static void ssaps_start_service_cbk(uint8_t server_id, uint16_t handle, errcode_t status) +{ + sample_at_log_print("%s start service cbk callback server_id:%d, handle:%x, status:%x\r\n", SLE_UART_SERVER_LOG, + server_id, handle, status); +} + +/** + * @brief ssaps 添加服务回调函数 + * + * @param server_id 服务 ID + * @param uuid 服务 UUID + * @param handle 服务句柄 + * @param status 状态码 + */ +static void ssaps_add_service_cbk(uint8_t server_id, sle_uuid_t *uuid, uint16_t handle, errcode_t status) +{ + sample_at_log_print("%s add service cbk callback server_id:%x, handle:%x, status:%x\r\n", SLE_UART_SERVER_LOG, + server_id, handle, status); + sle_uart_uuid_print(uuid); +} + +/** + * @brief 服务特征添加回调函数 + * + * @param server_id 服务 ID + * @param uuid 服务 UUID + * @param service_handle 服务句柄 + * @param handle 特征句柄 + * @param status 状态码 + */ +static void ssaps_add_property_cbk(uint8_t server_id, sle_uuid_t *uuid, uint16_t service_handle, + uint16_t handle, errcode_t status) +{ + sample_at_log_print("%s add property cbk callback server_id:%x, service_handle:%x,handle:%x, status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, service_handle, handle, status); + sle_uart_uuid_print(uuid); +} + +/** + * @brief 服务描述符添加回调函数 + * + * @param server_id 服务 ID + * @param uuid 服务 UUID + * @param service_handle 服务句柄 + * @param property_handle 特征句柄 + * @param status 状态码 + */ +static void ssaps_add_descriptor_cbk(uint8_t server_id, sle_uuid_t *uuid, uint16_t service_handle, + uint16_t property_handle, errcode_t status) +{ + sample_at_log_print("%s add descriptor cbk callback server_id:%x, service_handle:%x, property_handle:%x, \ + status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, service_handle, property_handle, status); + sle_uart_uuid_print(uuid); +} + +/** + * @brief 删除所有服务回调函数 + * + * @param server_id 服务 ID + * @param status 状态码 + */ +static void ssaps_delete_all_service_cbk(uint8_t server_id, errcode_t status) +{ + sample_at_log_print("%s delete all service callback server_id:%x, status:%x\r\n", SLE_UART_SERVER_LOG, + server_id, status); +} + +// ssaps 注册回调函数 +static errcode_t sle_ssaps_register_cbks(ssaps_read_request_callback ssaps_read_callback, ssaps_write_request_callback + ssaps_write_callback) +{ + errcode_t ret; + ssaps_callbacks_t ssaps_cbk = {0}; // 回调函数结构体 + ssaps_cbk.add_service_cb = ssaps_add_service_cbk; // 添加服务回调函数 + ssaps_cbk.add_property_cb = ssaps_add_property_cbk; // 添加特征回调函数 + ssaps_cbk.add_descriptor_cb = ssaps_add_descriptor_cbk; // 添加描述符回调函数 + ssaps_cbk.start_service_cb = ssaps_start_service_cbk; // 服务启动回调函数 + ssaps_cbk.delete_all_service_cb = ssaps_delete_all_service_cbk; // 删除所有服务回调函数 + ssaps_cbk.mtu_changed_cb = ssaps_mtu_changed_cbk; // MTU 改变回调函数 + ssaps_cbk.read_request_cb = ssaps_read_callback; // 读请求回调函数 + ssaps_cbk.write_request_cb = ssaps_write_callback; // 写请求回调函数 + ret = ssaps_register_callbacks(&ssaps_cbk); // 注册回调函数 + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_ssaps_register_cbks,ssaps_register_callbacks fail :%x\r\n", SLE_UART_SERVER_LOG, + ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} + +// -------- ssapc 注册回调函数结束 ---- + +// 服务添加 +static errcode_t sle_uuid_server_service_add(void) +{ + errcode_t ret; + sle_uuid_t service_uuid = {0}; // 创建服务 UUID 结构体 + sle_uuid_setu2(SLE_UUID_SERVER_SERVICE, &service_uuid); // 设置服务 UUID + ret = ssaps_add_service_sync(g_server_id, &service_uuid, 1, &g_service_handle); // 添加一个ssap服务 + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uuid add service fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + return ERRCODE_SLE_FAIL; + } + return ERRCODE_SLE_SUCCESS; +} + +// 添加特征 +static errcode_t sle_uuid_server_property_add(void) +{ + errcode_t ret; + ssaps_property_info_t property = {0}; // 创建特征信息结构体 + ssaps_desc_info_t descriptor = {0}; // 创建描述符信息结构体 + uint8_t ntf_value[] = {0x01, 0x0}; // 描述符数据 + + property.permissions = SLE_UUID_TEST_PROPERTIES; // 特征权限,此 demo 设置为可读可写 + property.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_NOTIFY; // 操作指示,数据值可被读取,通过通知方式传递给客户端 + sle_uuid_setu2(SLE_UUID_SERVER_NTF_REPORT, &property.uuid); // 设置特征 UUID + + // 分配内存给特征值(value 为指向特征值的指针) + property.value = (uint8_t *)osal_vmalloc(sizeof(g_sle_property_value)); + if (property.value == NULL) // 检查内存分配是否成功 + { + return ERRCODE_SLE_FAIL; + } + if (memcpy_s(property.value, sizeof(g_sle_property_value), g_sle_property_value, + sizeof(g_sle_property_value)) != EOK) // 复制特征值 + { + osal_vfree(property.value); // 当复制失败时,释放内存 + return ERRCODE_SLE_FAIL; + } + ret = ssaps_add_property_sync(g_server_id, g_service_handle, &property, &g_property_handle); // 添加特征,并获取特征句柄 + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uart add property fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + osal_vfree(property.value); // 当添加特征失败时,释放内存 + return ERRCODE_SLE_FAIL; + } + descriptor.permissions = SLE_UUID_TEST_DESCRIPTOR; // 特征权限,此 demo 设置为可读可写 + descriptor.type = SSAP_DESCRIPTOR_USER_DESCRIPTION; // 描述符类型,属性说明描述符 + descriptor.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_WRITE; // 操作指示,数据值可被读取和写入,写入后产生反馈给客户端 + descriptor.value = ntf_value; // 描述符数据 + descriptor.value_len = sizeof(ntf_value); // 描述符数据长度 + + // 添加描述符 + ret = ssaps_add_descriptor_sync(g_server_id, g_service_handle, g_property_handle, &descriptor); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uart add descriptor fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + osal_vfree(property.value); // 若添加描述符失败,释放特征值内存 + osal_vfree(descriptor.value); // 释放描述符值内存 + return ERRCODE_SLE_FAIL; + } + // 添加特征成功后,释放特征值内存 + osal_vfree(property.value); + return ERRCODE_SLE_SUCCESS; +} + +// 添加服务 +static errcode_t sle_uart_server_add(void) +{ + errcode_t ret; + sle_uuid_t app_uuid = {0}; // 创建应用 UUID 结构体 + + sample_at_log_print("%s sle uart add service in\r\n", SLE_UART_SERVER_LOG); + app_uuid.len = sizeof(g_sle_uuid_app_uuid); // 设置应用 UUID 长度 + // 复制应用 UUID + if (memcpy_s(app_uuid.uuid, app_uuid.len, g_sle_uuid_app_uuid, sizeof(g_sle_uuid_app_uuid)) != EOK) + { + return ERRCODE_SLE_FAIL; + } + ssaps_register_server(&app_uuid, &g_server_id); // 注册 ssap 服务端,参数:app_uuid:上层应用uuid,g_server_id:服务端ID + + // 添加服务 + if (sle_uuid_server_service_add() != ERRCODE_SLE_SUCCESS) + { + ssaps_unregister_server(g_server_id); // 如果添加服务失败,注销服务端 + return ERRCODE_SLE_FAIL; + } + + // 添加特征 + if (sle_uuid_server_property_add() != ERRCODE_SLE_SUCCESS) + { + ssaps_unregister_server(g_server_id); // 如果添加特征失败,注销服务端 + return ERRCODE_SLE_FAIL; + } + sample_at_log_print("%s sle uart add service, server_id:%x, service_handle:%x, property_handle:%x\r\n", + SLE_UART_SERVER_LOG, g_server_id, g_service_handle, g_property_handle); + + // 启动服务 + ret = ssaps_start_service(g_server_id, g_service_handle); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uart add service fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + return ERRCODE_SLE_FAIL; + } + sample_at_log_print("%s sle uart add service out\r\n", SLE_UART_SERVER_LOG); + return ERRCODE_SLE_SUCCESS; +} + +/* device通过uuid向host发送数据:report */ +/** + * @brief Server 端通过 UUID 向 Host(Client) 发送数据 + * + * @param data 发送的数据 + * @param len 数据长度 + * @return errcode_t + */ +errcode_t sle_uart_server_send_report_by_uuid(const uint8_t *data, uint8_t len) +{ + errcode_t ret; + ssaps_ntf_ind_by_uuid_t param = {0}; // 创建通知/指示参数结构体 + param.type = SSAP_PROPERTY_TYPE_VALUE; // 属性类型,特征值 + param.start_handle = g_service_handle; // 起始句柄 + param.end_handle = g_property_handle; // 结束句柄 + param.value_len = len; // 数据长度 + param.value = (uint8_t *)osal_vmalloc(len); // 动态分配内存给数据 + if (param.value == NULL) // 检查内存分配是否成功 + { + sample_at_log_print("%s send report new fail\r\n", SLE_UART_SERVER_LOG); + return ERRCODE_SLE_FAIL; + } + if (memcpy_s(param.value, param.value_len, data, len) != EOK) // 复制数据到参数 + { + sample_at_log_print("%s send input report memcpy fail\r\n", SLE_UART_SERVER_LOG); + osal_vfree(param.value); // 当复制失败时,释放内存 + return ERRCODE_SLE_FAIL; + } + sle_uuid_setu2(SLE_UUID_SERVER_NTF_REPORT, ¶m.uuid); // 设置 UUID + ret = ssaps_notify_indicate_by_uuid(g_server_id, g_sle_conn_hdl, ¶m); // 发送通知/指示,具体发送状态取决于客户端特征配置描述符值 + if (ret != ERRCODE_SLE_SUCCESS) // 检查发送是否成功 + { + sample_at_log_print("%s sle_uart_server_send_report_by_uuid,ssaps_notify_indicate_by_uuid fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + osal_vfree(param.value); + return ret; + } + osal_vfree(param.value); // 释放内存 + return ERRCODE_SLE_SUCCESS; +} + +/* device通过handle向host发送数据:report */ +/** + * @brief Server 端通过句柄向 Host(Client) 发送数据 + * + * @param data 数据 + * @param len 数据长度 + * @return errcode_t + */ +errcode_t sle_uart_server_send_report_by_handle(const uint8_t *data, uint16_t len) +{ + ssaps_ntf_ind_t param = {0}; + uint8_t receive_buf[UART_BUFF_LENGTH] = {0}; /* max receive length. */ + param.handle = g_property_handle; + param.type = SSAP_PROPERTY_TYPE_VALUE; + param.value = receive_buf; + param.value_len = len; + if (memcpy_s(param.value, param.value_len, data, len) != EOK) + { + return ERRCODE_SLE_FAIL; + } + return ssaps_notify_indicate(g_server_id, g_sle_conn_hdl, ¶m); +} + +/** + * @brief 连接状态改变回调函数 + * + * @param conn_id 连接 ID + * @param addr 设备地址 + * @param conn_state 连接状态 + * @param pair_state 配对状态 + * @param disc_reason 断开连接的原因 + */ +static void sle_connect_state_changed_cbk(uint16_t conn_id, const sle_addr_t *addr, + sle_acb_state_t conn_state, sle_pair_state_t pair_state, sle_disc_reason_t disc_reason) +{ + uint8_t sle_connect_state[] = "sle_dis_connect"; // 创建连接状态字符串,并初始化为 "sle_dis_connect" + sample_at_log_print("%s connect state changed callback conn_id:0x%02x, conn_state:0x%x, pair_state:0x%x, \ + disc_reason:0x%x\r\n", + SLE_UART_SERVER_LOG, conn_id, conn_state, pair_state, disc_reason); + sample_at_log_print("%s connect state changed callback addr:%02x:**:**:**:%02x:%02x\r\n", SLE_UART_SERVER_LOG, + addr->addr[BT_INDEX_0], addr->addr[BT_INDEX_4]); + if (conn_state == SLE_ACB_STATE_CONNECTED) // 已连接 + { + g_sle_conn_hdl = conn_id; // 更新连接句柄 + } + else if (conn_state == SLE_ACB_STATE_DISCONNECTED) // 未连接 + { + g_sle_conn_hdl = 0; + g_sle_pair_hdl = 0; + if (g_sle_uart_server_msg_queue != NULL) + { + g_sle_uart_server_msg_queue(sle_connect_state, sizeof(sle_connect_state)); + } + } +} + +// 配对完成回调函数 +static void sle_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status) +{ + sample_at_log_print("%s pair complete conn_id:%02x, status:%x\r\n", SLE_UART_SERVER_LOG, + conn_id, status); + sample_at_log_print("%s pair complete addr:%02x:**:**:**:%02x:%02x\r\n", SLE_UART_SERVER_LOG, + addr->addr[BT_INDEX_0], addr->addr[BT_INDEX_4]); + g_sle_pair_hdl = conn_id + 1; + ssap_exchange_info_t parameter = {0}; + parameter.mtu_size = 520; // 设置 MTU 大小为 520 字节 + parameter.version = 1; + ssaps_set_info(g_server_id, ¶meter); +} + +// 注册连接回调函数 +static errcode_t sle_conn_register_cbks(void) +{ + errcode_t ret; + sle_connection_callbacks_t conn_cbks = {0}; + conn_cbks.connect_state_changed_cb = sle_connect_state_changed_cbk; // 连接状态改变回调 + conn_cbks.pair_complete_cb = sle_pair_complete_cbk; // 配对完成回调 + ret = sle_connection_register_callbacks(&conn_cbks); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_conn_register_cbks,sle_connection_register_callbacks fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} + +// 获取连接句柄 +uint16_t sle_uart_client_is_connected(void) +{ + return g_sle_pair_hdl; +} + +/* 初始化uuid server */ +errcode_t sle_uart_server_init(ssaps_read_request_callback ssaps_read_callback, ssaps_write_request_callback + ssaps_write_callback) +{ + errcode_t ret; + + /* 使能SLE */ + if (enable_sle() != ERRCODE_SUCC) + { + sample_at_log_print("[SLE Server] sle enbale fail !\r\n"); + return -1; + } + + // 注册广播回调函数 + ret = sle_uart_announce_register_cbks(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_uart_announce_register_cbks fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 注册连接回调函数 + ret = sle_conn_register_cbks(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_conn_register_cbks fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 注册 ssaps 回调函数 + ret = sle_ssaps_register_cbks(ssaps_read_callback, ssaps_write_callback); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_ssaps_register_cbks fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 添加服务 + ret = sle_uart_server_add(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_uart_server_add fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 初始化广播 + ret = sle_uart_server_adv_init(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_uart_server_adv_init fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + sample_at_log_print("%s init ok\r\n", SLE_UART_SERVER_LOG); + return ERRCODE_SLE_SUCCESS; +} + +void sle_uart_server_register_msg(sle_uart_server_msg_queue sle_uart_server_msg) +{ + g_sle_uart_server_msg_queue = sle_uart_server_msg; +} \ No newline at end of file diff --git a/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server.h b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server.h new file mode 100644 index 0000000000000000000000000000000000000000..e72a3fea4586146d13003db86c024360982287c5 --- /dev/null +++ b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: SLE uart server Config. \n + * + * History: \n + * 2023-07-17, Create file. \n + */ + +#ifndef SLE_UART_SERVER_H +#define SLE_UART_SERVER_H + +#include +#include "sle_ssap_server.h" +#include "errcode.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +/* Service UUID */ +#define SLE_UUID_SERVER_SERVICE 0x2222 + +/* Property UUID */ +#define SLE_UUID_SERVER_NTF_REPORT 0x2323 + +/* Property Property */ +#define SLE_UUID_TEST_PROPERTIES (SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE) + +/* Operation indication */ +#define SLE_UUID_TEST_OPERATION_INDICATION (SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_WRITE) + +/* Descriptor Property */ +#define SLE_UUID_TEST_DESCRIPTOR (SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE) + +errcode_t sle_uart_server_init(ssaps_read_request_callback ssaps_read_callback, ssaps_write_request_callback + ssaps_write_callback); + +errcode_t sle_uart_server_send_report_by_uuid(const uint8_t *data, uint8_t len); + +errcode_t sle_uart_server_send_report_by_handle(const uint8_t *data, uint16_t len); + +uint16_t sle_uart_client_is_connected(void); + +typedef void (*sle_uart_server_msg_queue)(uint8_t *buffer_addr, uint16_t buffer_size); + +void sle_uart_server_register_msg(sle_uart_server_msg_queue sle_uart_server_msg); + + +uint16_t get_connect_id(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server_adv.c b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server_adv.c new file mode 100644 index 0000000000000000000000000000000000000000..1dd52a6f690dcb08216478872f480aab1dc7016f --- /dev/null +++ b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server_adv.c @@ -0,0 +1,316 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: sle adv config for sle uart server. \n + * + * History: \n + * 2023-07-17, Create file. \n + * + * Code Comments: Written by Lamonce. + * Last Modified: June 09, 2025 \n + * + * Introduction: + * This file implements the advertising functionality for SLE UART server communications. + * It provides complete advertising configuration and management, including advertisement + * parameters setup, advertisement data formatting, and scan response data configuration. + * The module supports standard SLE advertising modes, handles advertisement state callbacks, + * and manages the advertisement lifecycle. Key features include customizable advertisement + * intervals, transmit power control, device name broadcasting, and service data advertising. + * + * 简介: + * 本文件实现了 SLE UART 服务端通信的广播功能。它提供了完整的广播配置和管理, + * 包括广播参数设置、广播数据格式化和扫描响应数据配置。该模块支持标准 SLE 广播模式, + * 处理广播状态回调,并管理广播生命周期。主要功能包括可自定义的广播间隔、发射功率控制、 + * 设备名称广播和服务数据广播。整个模块设计符合星闪低功耗通信协议规范, + * 确保设备能被客户端高效发现和连接。 + * + * DISCLAIMER: + * This code is provided for reference and learning purposes only. + * No warranty of correctness, completeness, or suitability for any purpose is provided. + * Before using in production environments, please review and test thoroughly. + * + * 免责声明: + * 本文件中的代码及注释仅供学习和参考使用,不保证其在所有环境下的正确性和完整性。 + * 在实际项目中使用前,请根据具体需求进行适当的修改和测试。 + * + */ +#include "securec.h" // 安全函数库,用于替代标准 C 库中不安全的函数 +#include "errcode.h" // 错误码定义 +#include "osal_addr.h" // 地址相关函数 +#include "product.h" // 产品相关函数 +#include "sle_common.h" // 公共函数定义 +#include "sle_uart_server.h" // Server 端头文件 +#include "sle_device_discovery.h" // 设备发现相关头文件 +#include "sle_errcode.h" // 错误码定义 +#include "osal_debug.h" // 调试相关函数 +#include "osal_task.h" // 任务相关函数 +#include "string.h" // 字符串相关函数 +#include "sle_uart_server_adv.h" // Server 端广播相关头文件 + +/* sle device name */ +#define NAME_MAX_LENGTH 16 +/* 连接调度间隔12.5ms,单位125us */ +#define SLE_CONN_INTV_MIN_DEFAULT 0x64 +/* 连接调度间隔12.5ms,单位125us */ +#define SLE_CONN_INTV_MAX_DEFAULT 0x64 +/* 连接调度间隔25ms,单位125us */ +#define SLE_ADV_INTERVAL_MIN_DEFAULT 0xC8 +/* 连接调度间隔25ms,单位125us */ +#define SLE_ADV_INTERVAL_MAX_DEFAULT 0xC8 +/* 超时时间5000ms,单位10ms */ +#define SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT 0x1F4 +/* 超时时间4990ms,单位10ms */ +#define SLE_CONN_MAX_LATENCY 0x1F3 +/* 广播发送功率 */ +#define SLE_ADV_TX_POWER 10 +/* 广播ID */ +#define SLE_ADV_HANDLE_DEFAULT 1 +/* 最大广播数据长度 */ +#define SLE_ADV_DATA_LEN_MAX 251 +/* 广播名称 */ + +// static uint8_t sle_local_name[NAME_MAX_LENGTH] = "NearLink"; // 广播名称 +#define SLE_SERVER_INIT_DELAY_MS 1000 // 广播初始化延时 +#define sample_at_log_print(fmt, args...) osal_printk(fmt, ##args) // 日志打印宏 +#define SLE_UART_SERVER_LOG "[sle uart server]" // 日志前缀 + +static uint8_t sle_local_name[NAME_MAX_LENGTH] = {0}; + +int set_SLE_local_name(const uint8_t *name) +{ + if (name == NULL || strlen((char*)name) >= NAME_MAX_LENGTH) + { + osal_printk("%s set_SLE_local_name failed, name is null or too long\n", SLE_UART_SERVER_LOG); + return -1; // 错误处理 + } + // 复制本地名称到全局变量 + strncpy((char *)sle_local_name, (char *)name, NAME_MAX_LENGTH - 1); + sle_local_name[NAME_MAX_LENGTH - 1] = '\0'; // 确保字符串以 null 结尾 + return 0; // 成功 +} + +// 设置广播设备名称,也是本地名称 +static uint16_t sle_set_adv_local_name(uint8_t *adv_data, uint16_t max_len) +{ + errno_t ret; + uint8_t index = 0; + + uint8_t *local_name = sle_local_name; // 赋值本地名称 + uint8_t local_name_len = sizeof(sle_local_name) - 1; // 不包括结束符 + sample_at_log_print("%s local_name_len = %d\r\n", SLE_UART_SERVER_LOG, local_name_len); // 日志 + sample_at_log_print("%s local_name: ", SLE_UART_SERVER_LOG); + for (uint8_t i = 0; i < local_name_len; i++) + { + sample_at_log_print("0x%02x ", local_name[i]); + } + sample_at_log_print("\r\n"); + adv_data[index++] = local_name_len + 1; // 长度+1 + adv_data[index++] = SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME; // 数据类型 + ret = memcpy_s(&adv_data[index], max_len - index, local_name, local_name_len); // 拷贝本地名称 + if (ret != EOK) + { + sample_at_log_print("%s memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + return (uint16_t)index + local_name_len; +} + +static uint16_t sle_set_adv_data(uint8_t *adv_data) +{ + size_t len = 0; + uint16_t idx = 0; + errno_t ret = 0; + + len = sizeof(struct sle_adv_common_value); + struct sle_adv_common_value adv_disc_level = { + .length = len - 1, + .type = SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL, + .value = SLE_ANNOUNCE_LEVEL_NORMAL, + }; + ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_disc_level, len); + if (ret != EOK) + { + sample_at_log_print("%s adv_disc_level memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + idx += len; + + len = sizeof(struct sle_adv_common_value); + struct sle_adv_common_value adv_access_mode = { + .length = len - 1, + .type = SLE_ADV_DATA_TYPE_ACCESS_MODE, + .value = 0, + }; + ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_access_mode, len); + if (ret != EOK) + { + sample_at_log_print("%s adv_access_mode memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + idx += len; + + return idx; +} + +// 设置扫描响应数据 +static uint16_t sle_set_scan_response_data(uint8_t *scan_rsp_data) +{ + uint16_t idx = 0; + errno_t ret; + size_t scan_rsp_data_len = sizeof(struct sle_adv_common_value); + + struct sle_adv_common_value tx_power_level = { + .length = scan_rsp_data_len - 1, + .type = SLE_ADV_DATA_TYPE_TX_POWER_LEVEL, + .value = SLE_ADV_TX_POWER, + }; + ret = memcpy_s(scan_rsp_data, SLE_ADV_DATA_LEN_MAX, &tx_power_level, scan_rsp_data_len); + if (ret != EOK) + { + sample_at_log_print("%s sle scan response data memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + idx += scan_rsp_data_len; + + /* set local name */ + idx += sle_set_adv_local_name(&scan_rsp_data[idx], SLE_ADV_DATA_LEN_MAX - idx); + return idx; +} + +// 设置广播参数 +static int sle_set_default_announce_param(void) +{ + errno_t ret; + sle_announce_param_t param = {0}; + uint8_t index; + unsigned char local_addr[SLE_ADDR_LEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + param.announce_mode = SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE; + param.announce_handle = SLE_ADV_HANDLE_DEFAULT; + param.announce_gt_role = SLE_ANNOUNCE_ROLE_T_CAN_NEGO; + param.announce_level = SLE_ANNOUNCE_LEVEL_NORMAL; + param.announce_channel_map = SLE_ADV_CHANNEL_MAP_DEFAULT; + param.announce_interval_min = SLE_ADV_INTERVAL_MIN_DEFAULT; + param.announce_interval_max = SLE_ADV_INTERVAL_MAX_DEFAULT; + param.conn_interval_min = SLE_CONN_INTV_MIN_DEFAULT; + param.conn_interval_max = SLE_CONN_INTV_MAX_DEFAULT; + param.conn_max_latency = SLE_CONN_MAX_LATENCY; + param.conn_supervision_timeout = SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT; + param.announce_tx_power = 18; // 设置为 18 dBm + param.own_addr.type = 0; + ret = memcpy_s(param.own_addr.addr, SLE_ADDR_LEN, local_addr, SLE_ADDR_LEN); + if (ret != EOK) + { + sample_at_log_print("%s sle_set_default_announce_param data memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + sample_at_log_print("%s sle_uart_local addr: ", SLE_UART_SERVER_LOG); + for (index = 0; index < SLE_ADDR_LEN; index++) + { + sample_at_log_print("0x%02x ", param.own_addr.addr[index]); + } + sample_at_log_print("\r\n"); + return sle_set_announce_param(param.announce_handle, ¶m); +} + +// 设置默认广播数据 +static int sle_set_default_announce_data(void) +{ + errcode_t ret; + uint8_t announce_data_len = 0; + uint8_t seek_data_len = 0; + sle_announce_data_t data = {0}; + uint8_t adv_handle = SLE_ADV_HANDLE_DEFAULT; + uint8_t announce_data[SLE_ADV_DATA_LEN_MAX] = {0}; + uint8_t seek_rsp_data[SLE_ADV_DATA_LEN_MAX] = {0}; + uint8_t data_index = 0; + + announce_data_len = sle_set_adv_data(announce_data); + data.announce_data = announce_data; + data.announce_data_len = announce_data_len; + + sample_at_log_print("%s data.announce_data_len = %d\r\n", SLE_UART_SERVER_LOG, data.announce_data_len); + sample_at_log_print("%s data.announce_data: ", SLE_UART_SERVER_LOG); + for (data_index = 0; data_index < data.announce_data_len; data_index++) + { + sample_at_log_print("0x%02x ", data.announce_data[data_index]); + } + sample_at_log_print("\r\n"); + + seek_data_len = sle_set_scan_response_data(seek_rsp_data); + data.seek_rsp_data = seek_rsp_data; + data.seek_rsp_data_len = seek_data_len; + + sample_at_log_print("%s data.seek_rsp_data_len = %d\r\n", SLE_UART_SERVER_LOG, data.seek_rsp_data_len); + sample_at_log_print("%s data.seek_rsp_data: ", SLE_UART_SERVER_LOG); + for (data_index = 0; data_index < data.seek_rsp_data_len; data_index++) + { + sample_at_log_print("0x%02x ", data.seek_rsp_data[data_index]); + } + sample_at_log_print("\r\n"); + + ret = sle_set_announce_data(adv_handle, &data); + if (ret == ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s set announce data success.\r\n", SLE_UART_SERVER_LOG); + } + else + { + sample_at_log_print("%s set adv param fail.\r\n", SLE_UART_SERVER_LOG); + } + return ERRCODE_SLE_SUCCESS; +} + +// 广播启动回调 +static void sle_announce_enable_cbk(uint32_t announce_id, errcode_t status) +{ + sample_at_log_print("%s sle announce enable callback id:%02x, state:%x\r\n", SLE_UART_SERVER_LOG, announce_id, + status); +} + +// 广播停止回调 +static void sle_announce_disable_cbk(uint32_t announce_id, errcode_t status) +{ + sample_at_log_print("%s sle announce disable callback id:%02x, state:%x\r\n", SLE_UART_SERVER_LOG, announce_id, + status); +} + +// 广播终止回调 +static void sle_announce_terminal_cbk(uint32_t announce_id) +{ + sample_at_log_print("%s sle announce terminal callback id:%02x\r\n", SLE_UART_SERVER_LOG, announce_id); +} + +// 注册广播回调函数 +errcode_t sle_uart_announce_register_cbks(void) +{ + errcode_t ret = 0; + sle_announce_seek_callbacks_t seek_cbks = {0}; + seek_cbks.announce_enable_cb = sle_announce_enable_cbk; + seek_cbks.announce_disable_cb = sle_announce_disable_cbk; + seek_cbks.announce_terminal_cb = sle_announce_terminal_cbk; + ret = sle_announce_seek_register_callbacks(&seek_cbks); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_announce_register_cbks,register_callbacks fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} + +// 初始化广播 +errcode_t sle_uart_server_adv_init(void) +{ + errcode_t ret; + sle_set_default_announce_param(); + sle_set_default_announce_data(); + ret = sle_start_announce(SLE_ADV_HANDLE_DEFAULT); + sample_at_log_print("%s sle_uart_server_adv_init,sle_start_announce devise name :%s\r\n", + SLE_UART_SERVER_LOG, sle_local_name); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_adv_init,sle_start_announce fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} diff --git a/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server_adv.h b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server_adv.h new file mode 100644 index 0000000000000000000000000000000000000000..36376465deafcd1a5949585a3982493727c6ab5b --- /dev/null +++ b/vendor/developers/demo/SG90/sle_uart_server/sle_uart_server_adv.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: SLE ADV Config. \n + * + * History: \n + * 2023-07-17, Create file. \n + */ + +#ifndef SLE_SERVER_ADV_H +#define SLE_SERVER_ADV_H + +typedef struct sle_adv_common_value +{ + uint8_t type; + uint8_t length; + uint8_t value; +} le_adv_common_t; + +typedef enum sle_adv_channel +{ + SLE_ADV_CHANNEL_MAP_77 = 0x01, + SLE_ADV_CHANNEL_MAP_78 = 0x02, + SLE_ADV_CHANNEL_MAP_79 = 0x04, + SLE_ADV_CHANNEL_MAP_DEFAULT = 0x07 +} sle_adv_channel_map_t; + +typedef enum sle_adv_data +{ + SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL = 0x01, /* 发现等级 */ + SLE_ADV_DATA_TYPE_ACCESS_MODE = 0x02, /* 接入层能力 */ + SLE_ADV_DATA_TYPE_SERVICE_DATA_16BIT_UUID = 0x03, /* 标准服务数据信息 */ + SLE_ADV_DATA_TYPE_SERVICE_DATA_128BIT_UUID = 0x04, /* 自定义服务数据信息 */ + SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x05, /* 完整标准服务标识列表 */ + SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x06, /* 完整自定义服务标识列表 */ + SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x07, /* 部分标准服务标识列表 */ + SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x08, /* 部分自定义服务标识列表 */ + SLE_ADV_DATA_TYPE_SERVICE_STRUCTURE_HASH_VALUE = 0x09, /* 服务结构散列值 */ + SLE_ADV_DATA_TYPE_SHORTENED_LOCAL_NAME = 0x0A, /* 设备缩写本地名称 */ + SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME = 0x0B, /* 设备完整本地名称 */ + SLE_ADV_DATA_TYPE_TX_POWER_LEVEL = 0x0C, /* 广播发送功率 */ + SLE_ADV_DATA_TYPE_SLB_COMMUNICATION_DOMAIN = 0x0D, /* SLB通信域域名 */ + SLE_ADV_DATA_TYPE_SLB_MEDIA_ACCESS_LAYER_ID = 0x0E, /* SLB媒体接入层标识 */ + SLE_ADV_DATA_TYPE_EXTENDED = 0xFE, /* 数据类型扩展 */ + SLE_ADV_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF /* 厂商自定义信息 */ +} sle_adv_data_type; + +errcode_t sle_dev_register_cbks(void); +errcode_t sle_uart_server_adv_init(void); + +errcode_t sle_uart_announce_register_cbks(void); +int set_SLE_local_name(const uint8_t *name); // 设置 SLE 本地名称 + +#endif \ No newline at end of file diff --git a/vendor/developers/demo/STEPMOTOR/CMakeLists.txt b/vendor/developers/demo/STEPMOTOR/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..95fc5913171af2421103a481b3a124b07c29391f --- /dev/null +++ b/vendor/developers/demo/STEPMOTOR/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SOURCES_LIST +${CMAKE_CURRENT_SOURCE_DIR}/STEPMOTOR.c +# ${CMAKE_CURRENT_SOURCE_DIR}/servo_control.c +${CMAKE_CURRENT_SOURCE_DIR}/sle_uart_server/sle_uart_server_adv.c +${CMAKE_CURRENT_SOURCE_DIR}/sle_uart_server/sle_uart_server.c +) + +set(PUBLIC_HEADER_LIST +${CMAKE_CURRENT_SOURCE_DIR}/sle_uart_server +) + +set(SOURCES "${SOURCES_LIST}" PARENT_SCOPE) +set(PUBLIC_HEADER "${PUBLIC_HEADER_LIST}" PARENT_SCOPE) diff --git a/vendor/developers/demo/STEPMOTOR/STEPMOTOR.c b/vendor/developers/demo/STEPMOTOR/STEPMOTOR.c new file mode 100644 index 0000000000000000000000000000000000000000..0b24532cfc5f2f2415ec2211195b24d700b9a334 --- /dev/null +++ b/vendor/developers/demo/STEPMOTOR/STEPMOTOR.c @@ -0,0 +1,1101 @@ +// #include "common_def.h" +// #include "securec.h" +// #include "soc_osal.h" +// #include "sle_errcode.h" +// #include "sle_connection_manager.h" +// #include "sle_device_discovery.h" +// #include "sle_uart_server_adv.h" +// #include "sle_uart_server.h" +// #include "boards.h" +// #include "pinctrl.h" +// #include "gpio.h" +// #include "timer.h" +// #include "osal_debug.h" +// #include "cmsis_os2.h" +// #include "app_init.h" +// #include "tcxo.h" +// #include "chip_core_irq.h" +// #include "hal_gpio.h" + +// #define GPIO_Pin_0 10 +// #define GPIO_Pin_1 8 +// #define GPIO_Pin_2 7 +// #define GPIO_Pin_3 13 + +// /* 定义星闪相关宏 */ +// #define SLE_UART_SERVER_LOG "[SLE Stepper Server]" +// extern volatile bool g_reconnect_needed; + +// /* 定义步进电机控制接口 */ +// typedef enum { +// MOTOR_DIR_FORWARD = 1, // 正转(开窗帘) +// MOTOR_DIR_BACKWARD = 0 // 反转(关窗帘) +// } motor_dir_t; + +// /* 电机命令状态 */ +// typedef enum { +// CMD_IDLE = 0, // 空闲状态 +// CMD_RUNNING, // 执行中 +// CMD_PENDING // 有新命令待处理 +// } motor_cmd_state_t; + +// volatile int is_command = 0; // 0 表示没有控制命令,1 表示有控制命令 +// volatile int motor_command = 0; // 0 表示关窗帘,1 表示开窗帘 +// volatile motor_cmd_state_t cmd_state = CMD_IDLE; // 当前命令状态 +// volatile uint16_t current_step = 0; // 当前执行的步数 +// volatile uint16_t pending_steps = 0; // 待执行的步数 +// volatile motor_dir_t pending_dir = MOTOR_DIR_FORWARD; // 待执行的方向 + +// /* 双四拍驱动时序 - 速度更快 */ +// static const uint8_t motor_sequence[4] = { +// 0b1100, // A+B- +// 0b0110, // B+C- +// 0b0011, // C+D- +// 0b1001 // D+A- +// }; + +// /* 设置GPIO输出 */ +// static void set_gpio_output(uint8_t pattern) { +// uapi_gpio_set_val(GPIO_Pin_0, (pattern & 0x01) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_1, (pattern & 0x02) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_2, (pattern & 0x04) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_3, (pattern & 0x08) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// } + +// static void motor_control(motor_dir_t dir, uint16_t steps, uint32_t speed_ms) { +// const uint16_t steps_per_revolution = 2048; // 双四拍驱动方式步数 + +// static bool is_init = false; +// if (!is_init) { +// uapi_gpio_set_dir(GPIO_Pin_0, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_1, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_2, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_3, GPIO_DIRECTION_OUTPUT); +// is_init = true; +// } + +// osal_printk("MOTOR: Starting control - Direction=%s, Steps=%u\r\n", +// dir == MOTOR_DIR_FORWARD ? "FORWARD" : "BACKWARD", steps); + +// cmd_state = CMD_RUNNING; +// current_step = 0; + +// // 临时变量保存当前命令参数 +// motor_dir_t current_dir = dir; +// uint16_t total_steps = steps; + +// while (current_step < total_steps) { +// // 检查是否有新命令 +// if (is_command && cmd_state == CMD_PENDING) { +// osal_printk("MOTOR: Interrupted at step %u/%u\r\n", current_step, total_steps); +// // 保存当前状态,用于恢复执行 +// pending_dir = current_dir; +// pending_steps = total_steps - current_step; +// break; +// } + +// // 执行步进 +// if (current_dir == MOTOR_DIR_FORWARD) { +// set_gpio_output(motor_sequence[current_step % 4]); +// } else { +// set_gpio_output(motor_sequence[3 - (current_step % 4)]); +// } + +// current_step++; +// osal_msleep(speed_ms); +// } + +// // 记录实际执行的步数 +// osal_printk("MOTOR: Completed %u steps\r\n", current_step); + +// // 电机停止 +// set_gpio_output(0x00); + +// // 如果是被中断的,恢复待处理状态 +// if (is_command && cmd_state == CMD_PENDING) { +// cmd_state = CMD_PENDING; +// } else { +// cmd_state = CMD_IDLE; +// } +// } + +// static void motor_task(const char *arg) { +// unused(arg); + +// while (1) { +// if (is_command) { +// motor_dir_t dir = (motor_command == 1) ? MOTOR_DIR_FORWARD : MOTOR_DIR_BACKWARD; + +// // 检查是否需要执行反向相同步数 +// if (cmd_state == CMD_RUNNING) { +// // 如果当前有正在执行的命令,并且新命令方向相反 +// motor_dir_t current_dir = (motor_command == 1) ? MOTOR_DIR_BACKWARD : MOTOR_DIR_FORWARD; +// osal_printk("MOTOR: Reverse command received - Steps=%u\r\n", current_step); + +// // 设置为待处理状态 +// cmd_state = CMD_PENDING; + +// // 等待当前命令完成中断 +// while (cmd_state != CMD_IDLE && cmd_state != CMD_PENDING) { +// osal_msleep(10); +// } + +// // 如果是被中断的,恢复执行剩余步数 +// if (cmd_state == CMD_PENDING) { +// osal_printk("MOTOR: Resuming interrupted command - Steps=%u\r\n", pending_steps); +// motor_control(pending_dir, pending_steps, 1); +// continue; // 继续处理新命令 +// } + +// // 执行反向相同步数 +// motor_control(dir, current_step, 1); +// osal_printk("Curtain reversed to original position.\r\n"); +// } else { +// // 正常执行完整角度 +// const uint16_t angle = 90; +// const uint16_t steps_per_revolution = 2048; +// uint16_t steps = (angle * steps_per_revolution) / 360; + +// motor_control(dir, steps, 1); +// osal_printk(dir == MOTOR_DIR_FORWARD ? "Curtain opened.\r\n" : "Curtain closed.\r\n"); +// } + +// // 重置命令标志 +// is_command = 0; +// } else { +// osal_msleep(100); +// } +// } +// } + +// static void ssaps_server_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para, errcode_t status) { +// osal_printk("%s ssaps write request callback cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n", +// SLE_UART_SERVER_LOG, server_id, conn_id, write_cb_para->handle, status); +// if ((write_cb_para->length > 0) && write_cb_para->value) { +// osal_printk("\n sle uart received data : %.*s\r\n", write_cb_para->length, write_cb_para->value); + +// /* 手动解析JSON格式数据 */ +// char *data = (char *)write_cb_para->value; +// char *state_start = strstr(data, "\"state\":"); // 匹配 "state":"ON" 或 "state":"OFF" +// if (state_start) { +// state_start += 8; // 跳过 "state": 前缀 +// // 跳过可能存在的空格 +// while (*state_start == ' ') state_start++; + +// // 跳过引号 +// if (*state_start == '"') { +// state_start++; +// } else { +// osal_printk("Missing opening quote for state value.\r\n"); +// return; +// } + +// // 检查状态值是否为 "ON" 或 "OFF" +// if (strncmp(state_start, "ON", 2) == 0) { +// motor_command = 1; +// is_command = 1; +// cmd_state = CMD_PENDING; // 设置为待处理状态 +// osal_printk("Curtain opening command received.\r\n"); +// } else if (strncmp(state_start, "OFF", 3) == 0) { +// motor_command = 0; +// is_command = 1; +// cmd_state = CMD_PENDING; // 设置为待处理状态 +// osal_printk("Curtain closing command received.\r\n"); +// } else { +// osal_printk("Invalid state value: %s\r\n", state_start); +// } +// } else { +// osal_printk("Missing \"state\" field in JSON data.\r\n"); +// } +// } +// } + +// /* 星闪服务端任务 */ +// static void *sle_uart_server_task(const char *arg) { +// unused(arg); +// /* 创建电机控制任务 */ +// osal_task *motor_task_handle = NULL; +// osal_kthread_lock(); +// motor_task_handle = osal_kthread_create((osal_kthread_handler)motor_task, 0, "MotorControlTask", 0x1000); +// if (motor_task_handle != NULL) { +// osal_kthread_set_priority(motor_task_handle, 24); +// } +// osal_kthread_unlock(); + +// /* 初始化星闪服务端 */ +// sle_uart_server_init(NULL, ssaps_server_write_request_cbk); +// sle_uart_server_adv_init(); + +// while (1) { +// if (g_reconnect_needed) { +// osal_printk("[SLE] Starting auto-reconnect process...\r\n"); +// sle_uart_server_init(NULL, ssaps_server_write_request_cbk); +// sle_uart_server_adv_init(); +// g_reconnect_needed = false; +// } + +// osal_msleep(1000); +// } +// return NULL; +// } + +// /* 服务端入口 */ +// static void sle_uart_server_entry(void) { +// osal_task *task_handle = NULL; +// osal_kthread_lock(); +// task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_server_task, 0, "SLEUartServerTask", 0x1200); +// if (task_handle != NULL) { +// osal_kthread_set_priority(task_handle, 28); +// } +// osal_kthread_unlock(); +// } + +// /* 应用入口 */ +// app_run(sle_uart_server_entry); + +// #include "common_def.h" +// #include "soc_osal.h" +// #include "gpio.h" +// #include "timer.h" +// #include "osal_debug.h" +// #include "cmsis_os2.h" +// #include "app_init.h" + +// /* GPIO引脚定义 */ +// #define GPIO_Pin_0 10 // A相 +// #define GPIO_Pin_1 8 // B相 +// #define GPIO_Pin_2 7 // C相 +// #define GPIO_Pin_3 13 // D相 + +// /* 电机状态定义 */ +// typedef enum { +// MOTOR_STATE_STOP = 0, // 停止状态 +// MOTOR_STATE_FORWARD, // 正转状态(开窗帘) +// MOTOR_STATE_BACKWARD // 反转状态(关窗帘) +// } motor_state_t; + +// /* 电机控制命令定义 */ +// typedef enum { +// CMD_NONE = 0, // 无命令 +// CMD_OPEN, // 开窗帘命令 +// CMD_CLOSE, // 关窗帘命令 +// CMD_STOP // 停止命令 +// } motor_cmd_t; + +// /* 全局变量 */ +// volatile motor_state_t g_motor_state = MOTOR_STATE_STOP; // 电机当前状态 +// volatile motor_cmd_t g_motor_command = CMD_NONE; // 电机控制命令 +// volatile bool g_command_updated = false; // 命令更新标志 +// osThreadId_t g_motor_task_handle = NULL; // 电机任务句柄 +// osMutexId_t g_motor_mutex = NULL; // 电机控制互斥锁 + +// /* 双四拍驱动时序 - 速度更快 */ +// static const uint8_t motor_sequence[4] = { +// 0b1100, // A+B- +// 0b0110, // B+C- +// 0b0011, // C+D- +// 0b1001 // D+A- +// }; + +// /* GPIO初始化 */ +// static void gpio_init(void) { +// uapi_gpio_set_dir(GPIO_Pin_0, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_1, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_2, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_3, GPIO_DIRECTION_OUTPUT); + +// // 初始化为低电平 +// uapi_gpio_set_val(GPIO_Pin_0, GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_1, GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_2, GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_3, GPIO_LEVEL_LOW); +// } + +// /* 设置GPIO输出 */ +// static void set_gpio_output(uint8_t pattern) { +// uapi_gpio_set_val(GPIO_Pin_0, (pattern & 0x01) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_1, (pattern & 0x02) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_2, (pattern & 0x04) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_3, (pattern & 0x08) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// } + +// /* 电机控制核心函数 */ +// static void motor_control(motor_state_t state, uint16_t angle, uint32_t speed_ms) { +// const uint16_t steps_per_revolution = 2048; // 双四拍驱动方式步数 +// uint16_t steps = (angle * steps_per_revolution) / 360; +// uint8_t step = 0; + +// // 锁定电机控制,防止中断时冲突 +// osMutexAcquire(g_motor_mutex, osWaitForever); + +// // 更新电机状态 +// g_motor_state = state; + +// // 确定转动方向 +// bool is_forward = (state == MOTOR_STATE_FORWARD); + +// for (uint16_t i = 0; i < steps; i++) { +// // 检查是否有新命令 +// if (g_command_updated) { +// osMutexRelease(g_motor_mutex); +// return; +// } + +// // 双四拍驱动 +// if (is_forward) { +// step = i % 4; +// } +// else { +// step = (3 - (i % 4)) % 4; +// } + +// set_gpio_output(motor_sequence[step]); +// osal_msleep(speed_ms); +// } + +// // 电机停止 +// set_gpio_output(0x00); +// g_motor_state = MOTOR_STATE_STOP; +// osMutexRelease(g_motor_mutex); +// } + +// /* 处理电机命令 */ +// static void process_motor_command(void) { +// if (g_motor_command == CMD_NONE) { +// return; +// } + +// osMutexAcquire(g_motor_mutex, osWaitForever); + +// // 如果当前状态与命令冲突,中断当前操作 +// if ((g_motor_state == MOTOR_STATE_FORWARD && g_motor_command == CMD_CLOSE) || +// (g_motor_state == MOTOR_STATE_BACKWARD && g_motor_command == CMD_OPEN)) { +// // 停止当前操作 +// set_gpio_output(0x00); +// g_motor_state = MOTOR_STATE_STOP; +// osal_printk("Interrupting current operation for new command\r\n"); +// } + +// // 执行新命令 +// if (g_motor_command == CMD_OPEN) { +// osal_printk("Executing open command\r\n"); +// motor_control(MOTOR_STATE_FORWARD, 180, 1); // 正转180度打开窗帘 +// } +// else if (g_motor_command == CMD_CLOSE) { +// osal_printk("Executing close command\r\n"); +// motor_control(MOTOR_STATE_BACKWARD, 180, 1); // 反转180度关闭窗帘 +// } +// else if (g_motor_command == CMD_STOP) { +// osal_printk("Executing stop command\r\n"); +// set_gpio_output(0x00); +// g_motor_state = MOTOR_STATE_STOP; +// } + +// g_motor_command = CMD_NONE; +// g_command_updated = false; +// osMutexRelease(g_motor_mutex); +// } + +// /* 设置电机命令(可从其他任务或中断调用) */ +// void set_motor_command(motor_cmd_t cmd) { +// osMutexAcquire(g_motor_mutex, osWaitForever); +// g_motor_command = cmd; +// g_command_updated = true; +// osMutexRelease(g_motor_mutex); +// } + +// /* 电机控制任务 */ +// static void motor_task(const char *arg) { +// unused(arg); + +// // 初始化GPIO +// gpio_init(); +// osal_printk("GPIO initialized\r\n"); // 新增打印确认 + +// // 创建互斥锁 +// g_motor_mutex = osMutexNew(NULL); +// if (g_motor_mutex == NULL) { +// osal_printk("Failed to create motor mutex\r\n"); +// while (1); +// } +// osal_printk("Mutex created successfully\r\n"); // 新增打印确认 + +// osal_printk("Motor control task started\r\n"); + +// // 调试:直接控制电机 +// osal_printk("Testing motor forward...\r\n"); +// motor_control(MOTOR_STATE_FORWARD, 720, 1); // 正向旋转90度,速度5ms/步 + +// osal_msleep(1000); // 等待1秒 + +// osal_printk("Testing motor backward...\r\n"); +// motor_control(MOTOR_STATE_BACKWARD, 90, 1); // 反向旋转90度,速度5ms/步 + +// while (1) { +// // 检查是否有新命令 +// if (g_command_updated) { +// process_motor_command(); +// } +// else { +// osal_msleep(10); // 短暂延时,减少CPU占用 +// } +// } +// } + +// /* 电机控制任务创建入口 */ +// static void motor_control_entry(void) { +// osal_task *task_handle = NULL; +// osal_kthread_lock(); +// task_handle = osal_kthread_create((osal_kthread_handler)motor_task, 0, "MotorTask", 0x1200); // 0x1000 等栈大小可根据实际需求调整,这里先示例改小 +// if (task_handle != NULL) { +// osal_kthread_set_priority(task_handle, 28); // 优先级也可按需调整 +// } +// osal_kthread_unlock(); +// } + + +// app_run(motor_control_entry); + + +// #include "common_def.h" +// #include "securec.h" +// #include "soc_osal.h" +// #include "sle_errcode.h" +// #include "sle_connection_manager.h" +// #include "sle_device_discovery.h" +// #include "sle_uart_server_adv.h" +// #include "sle_uart_server.h" +// #include "boards.h" +// #include "pinctrl.h" +// #include "gpio.h" +// #include "timer.h" +// #include "osal_debug.h" +// #include "cmsis_os2.h" +// #include "app_init.h" +// #include "tcxo.h" +// #include "chip_core_irq.h" +// #include "hal_gpio.h" + +// #define GPIO_Pin_0 10 +// #define GPIO_Pin_1 8 +// #define GPIO_Pin_2 7 +// #define GPIO_Pin_3 13 + +// /* 定义星闪相关宏 */ +// #define SLE_UART_SERVER_LOG "[SLE Stepper Server]" +// extern volatile bool g_reconnect_needed; + +// /* 定义步进电机控制接口 */ +// typedef enum { +// MOTOR_DIR_FORWARD = 1, // 正转(开窗帘) +// MOTOR_DIR_BACKWARD = 0 // 反转(关窗帘) +// } motor_dir_t; + +// volatile int is_command = 0; // 0 表示没有控制命令,1 表示有控制命令 +// volatile int motor_command = 0; // 0 表示关窗帘,1 表示开窗帘 + +// /* 双四拍驱动时序 - 速度更快 */ +// static const uint8_t motor_sequence[4] = { +// 0b1100, // A+B- +// 0b0110, // B+C- +// 0b0011, // C+D- +// 0b1001 // D+A- +// }; + +// /* 设置GPIO输出 */ +// static void set_gpio_output(uint8_t pattern) { +// uapi_gpio_set_val(GPIO_Pin_0, (pattern & 0x01) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_1, (pattern & 0x02) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_2, (pattern & 0x04) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// uapi_gpio_set_val(GPIO_Pin_3, (pattern & 0x08) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); +// } + +// /* 步进电机方向控制函数 */ +// static void motor_control(motor_dir_t dir, uint16_t angle, uint32_t speed_ms) { +// const uint16_t steps_per_revolution = 2048; // 双四拍驱动方式步数 +// uint16_t steps = (angle * steps_per_revolution) / 360; + +// static bool is_init = false; +// if (!is_init) { +// uapi_gpio_set_dir(GPIO_Pin_0, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_1, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_2, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_3, GPIO_DIRECTION_OUTPUT); +// is_init = true; +// } + +// if (dir == MOTOR_DIR_FORWARD) { +// for (uint16_t i = 0; i < steps; i++) { +// set_gpio_output(motor_sequence[i % 4]); +// osal_msleep(speed_ms); +// } +// } else { +// for (uint16_t i = 0; i < steps; i++) { +// set_gpio_output(motor_sequence[3 - (i % 4)]); +// osal_msleep(speed_ms); +// } +// } + +// // 电机停止 +// set_gpio_output(0x00); +// } + +// /* 电机控制任务 */ +// static void motor_task(const char *arg) { +// unused(arg); + +// motor_control(MOTOR_DIR_FORWARD, 90, 1); + +// while (1) { +// if (is_command != 0) { +// if (motor_command == 1) { +// motor_control(MOTOR_DIR_FORWARD, 90, 1); // 正转90度打开窗帘 +// osal_printk("Curtain opened.\r\n"); +// } else if (motor_command == 0) { +// motor_control(MOTOR_DIR_BACKWARD, 90, 1); // 反转90度关闭窗帘 +// osal_printk("Curtain closed.\r\n"); +// } +// is_command = 0; +// } else { +// osal_msleep(100); +// } +// } +// } + +// static void ssaps_server_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para, errcode_t status) { +// osal_printk("%s ssaps write request callback cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n", +// SLE_UART_SERVER_LOG, server_id, conn_id, write_cb_para->handle, status); +// if ((write_cb_para->length > 0) && write_cb_para->value) { +// osal_printk("\n sle uart received data : %.*s\r\n", write_cb_para->length, write_cb_para->value); + +// /* 手动解析JSON格式数据 */ +// char *data = (char *)write_cb_para->value; +// char *state_start = strstr(data, "\"state\":"); // 匹配 "state":"ON" 或 "state":"OFF" +// if (state_start) { +// state_start += 8; // 跳过 "state": 前缀 +// // 跳过可能存在的空格 +// while (*state_start == ' ') state_start++; + +// // 跳过引号 +// if (*state_start == '"') { +// state_start++; +// } else { +// osal_printk("Missing opening quote for state value.\r\n"); +// return; +// } + +// // 检查状态值是否为 "ON" 或 "OFF" +// if (strncmp(state_start, "ON", 2) == 0) { +// motor_command = 1; +// is_command = 1; +// osal_printk("Curtain opening command received.\r\n"); +// } else if (strncmp(state_start, "OFF", 3) == 0) { +// motor_command = 0; +// is_command = 1; +// osal_printk("Curtain closing command received.\r\n"); +// } else { +// osal_printk("Invalid state value: %s\r\n", state_start); +// } +// } else { +// osal_printk("Missing \"state\" field in JSON data.\r\n"); +// } +// } +// } + +// /* 星闪服务端任务 */ +// static void *sle_uart_server_task(const char *arg) { +// unused(arg); +// /* 创建电机控制任务 */ +// osal_task *motor_task_handle = NULL; +// osal_kthread_lock(); +// motor_task_handle = osal_kthread_create((osal_kthread_handler)motor_task, 0, "MotorControlTask", 0x1000); +// if (motor_task_handle != NULL) { +// osal_kthread_set_priority(motor_task_handle, 24); +// } +// osal_kthread_unlock(); + +// /* 初始化星闪服务端 */ +// sle_uart_server_init(NULL, ssaps_server_write_request_cbk); +// sle_uart_server_adv_init(); + +// while (1) { +// if (g_reconnect_needed) { +// osal_printk("[SLE] Starting auto-reconnect process...\r\n"); +// sle_uart_server_init(NULL, ssaps_server_write_request_cbk); +// sle_uart_server_adv_init(); +// g_reconnect_needed = false; +// } + +// osal_msleep(1000); +// } +// return NULL; +// } + +// /* 服务端入口 */ +// static void sle_uart_server_entry(void) { +// osal_task *task_handle = NULL; +// osal_kthread_lock(); +// task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_server_task, 0, "SLEUartServerTask", 0x1200); +// if (task_handle != NULL) { +// osal_kthread_set_priority(task_handle, 28); +// } +// osal_kthread_unlock(); +// } + +// /* 应用入口 */ +// app_run(sle_uart_server_entry); + + +// #include "common_def.h" +// #include "securec.h" +// #include "soc_osal.h" +// #include "sle_errcode.h" +// #include "sle_connection_manager.h" +// #include "sle_device_discovery.h" +// #include "sle_uart_server_adv.h" +// #include "sle_uart_server.h" +// #include "boards.h" +// #include "pinctrl.h" +// #include "gpio.h" +// #include "timer.h" +// #include "osal_debug.h" +// #include "cmsis_os2.h" +// #include "app_init.h" +// #include "tcxo.h" +// #include "chip_core_irq.h" +// #include "hal_gpio.h" + +// #define GPIO_Pin_0 10 +// #define GPIO_Pin_1 8 +// #define GPIO_Pin_2 7 +// #define GPIO_Pin_3 13 + +// /* 定义星闪相关宏 */ +// #define SLE_UART_SERVER_LOG "[SLE Stepper Server]" +// extern volatile bool g_reconnect_needed; + +// /* 步进模式选择 */ +// typedef enum { +// STEP_MODE_FULL, // 全步模式(四拍) +// STEP_MODE_HALF // 半步模式(八拍) +// } StepMode; + +// /* 旋转方向 */ +// typedef enum { +// DIRECTION_CW, // 顺时针 +// DIRECTION_CCW // 逆时针 +// } RotationDirection; + +// // 全步模式相序(双相四拍) +// static const uint8_t full_step_sequence[4][4] = { +// {1, 1, 0, 0}, +// {0, 1, 1, 0}, +// {0, 0, 1, 1}, +// {1, 0, 0, 1} +// }; +// // 半步模式相序(八拍) +// static const uint8_t half_step_sequence[8][4] = { +// {1, 0, 0, 0}, +// {1, 1, 0, 0}, +// {0, 1, 0, 0}, +// {0, 1, 1, 0}, +// {0, 0, 1, 0}, +// {0, 0, 1, 1}, +// {0, 0, 0, 1}, +// {1, 0, 0, 1} +// }; + +// static void GPIO_Init(void) +// { +// // 初始化前打印提示 +// osal_printk("Initializing GPIO pins...\r\n"); + +// // 配置引脚为GPIO模式(模式0通常为通用GPIO) +// uapi_pin_set_mode(GPIO_Pin_0, 0); +// uapi_pin_set_mode(GPIO_Pin_1, 0); +// uapi_pin_set_mode(GPIO_Pin_2, 0); +// uapi_pin_set_mode(GPIO_Pin_3, 0); +// osal_printk("GPIO pins set to mode 0 (GPIO function)\r\n"); + +// // 配置为输出方向 +// uapi_gpio_set_dir(GPIO_Pin_0, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_1, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_2, GPIO_DIRECTION_OUTPUT); +// uapi_gpio_set_dir(GPIO_Pin_3, GPIO_DIRECTION_OUTPUT); +// osal_printk("GPIO pins configured as output\r\n"); +// } + +// static void SetCoilState(const uint8_t coil_state[4]) { +// // 可选:注释掉详细的线圈状态日志(仅保留关键信息) +// // osal_printk("Setting coil state: IN1=%d, IN2=%d, IN3=%d, IN4=%d\r\n", +// // coil_state[0], coil_state[1], coil_state[2], coil_state[3]); + +// // 设置引脚电平 +// uapi_gpio_set_val(GPIO_Pin_0, coil_state[0] ? 1 : 0); +// uapi_gpio_set_val(GPIO_Pin_1, coil_state[1] ? 1 : 0); +// uapi_gpio_set_val(GPIO_Pin_2, coil_state[2] ? 1 : 0); +// uapi_gpio_set_val(GPIO_Pin_3, coil_state[3] ? 1 : 0); +// } + +// /* 步进电机驱动函数 */ +// void Stepper_Step(uint16_t steps, RotationDirection dir, StepMode mode, uint16_t step_delay_ms) { +// const uint8_t (*sequence)[4] = NULL; +// uint8_t sequence_length = 0; + +// // 选择步进模式并打印 +// if(mode == STEP_MODE_FULL) { +// sequence = full_step_sequence; +// sequence_length = 4; +// osal_printk("Using full-step mode (4 steps per cycle)\r\n"); +// } else { +// sequence = half_step_sequence; +// sequence_length = 8; +// osal_printk("Using half-step mode (8 steps per cycle)\r\n"); +// } + +// // 打印本次运动参数 +// osal_printk("Starting %s rotation: %d steps, delay %d ms per step\r\n", +// (dir == DIRECTION_CW) ? "clockwise" : "counter-clockwise", +// steps, step_delay_ms); + +// static int16_t current_step = 0; + +// for(uint16_t i = 0; i < steps; i++) { +// // 减少日志频率(每500步打印一次,避免刷屏) +// if(i % 500 == 0) { +// osal_printk("Step %d/%d\r\n", i, steps); +// } + +// // 更新当前步骤索引(修复格式问题) +// if(dir == DIRECTION_CW) { +// current_step++; +// if(current_step >= sequence_length) { +// current_step = 0; +// } +// } else { +// if(current_step <= 0) { +// current_step = sequence_length - 1; +// } else { +// current_step--; +// } +// } + +// // 设置线圈状态 +// SetCoilState(sequence[current_step]); + +// // 延时 +// osal_mdelay(step_delay_ms); +// } + +// osal_printk("Rotation completed\r\n"); +// } + +// /* 电机控制任务 */ +// static void motor_task(const char *arg) { +// unused(arg); + +// // 任务启动时初始化GPIO +// GPIO_Init(); +// osal_printk("Motor task started, entering main loop\r\n"); + +// while (1) { +// // 执行顺时针全步旋转 +// Stepper_Step(2048, DIRECTION_CW, STEP_MODE_FULL, 2); +// osal_printk("Waiting 1 second...\r\n"); +// osal_mdelay(1000); + +// // 执行逆时针半步旋转 +// Stepper_Step(4096, DIRECTION_CCW, STEP_MODE_FULL, 2); +// osal_printk("Waiting 1 second...\r\n"); +// osal_mdelay(1000); +// } +// } + +// // 星闪服务端入口 +// static void motor_task_entry(void) { +// osal_task *task_handle = NULL; +// osal_printk("Creating motor task...\r\n"); + +// osal_kthread_lock(); +// task_handle = osal_kthread_create((osal_kthread_handler)motor_task, 0, "motor_task", 0x1200); +// if (task_handle != NULL) { +// osal_kthread_set_priority(task_handle, 28); +// osal_printk("Motor task created with priority 28\r\n"); +// } else { +// osal_printk("ERROR: Failed to create motor task!\r\n"); // 关键错误提示 +// } +// osal_kthread_unlock(); +// } + +// // 主入口 +// app_run(motor_task_entry); + +#include "common_def.h" +#include "securec.h" +#include "soc_osal.h" +#include "sle_errcode.h" +#include "sle_connection_manager.h" +#include "sle_device_discovery.h" +#include "sle_uart_server_adv.h" +#include "sle_uart_server.h" +#include "boards.h" +#include "pinctrl.h" +#include "gpio.h" +#include "timer.h" +#include "osal_debug.h" +#include "cmsis_os2.h" +#include "app_init.h" +#include "tcxo.h" +#include "chip_core_irq.h" +#include "hal_gpio.h" + +#define GPIO_Pin_0 10 +#define GPIO_Pin_1 8 +#define GPIO_Pin_2 7 +#define GPIO_Pin_3 13 + +/* 定义星闪相关宏 */ +#define SLE_UART_SERVER_LOG "[SLE Stepper Server]" +extern volatile bool g_reconnect_needed; + +volatile int is_command = 0; // 0 表示没有控制命令,1 表示有控制命令 +volatile int motor_command = 0; // 0 表示关窗帘,1 表示开窗帘 +volatile int is_moving = 0;//-1表示正在关,1表示正在开,0表示空闲 +volatile uint32_t g_stepper_steps = 0; + +/* 步进模式选择 */ +typedef enum { + STEP_MODE_FULL, // 全步模式(四拍) + STEP_MODE_HALF // 半步模式(八拍) +} StepMode; + +/* 旋转方向 */ +typedef enum { + DIRECTION_CW, // 顺时针 + DIRECTION_CCW // 逆时针 +} RotationDirection; + +// 全步模式相序(双相四拍) +static const uint8_t full_step_sequence[4][4] = { + {1, 1, 0, 0}, + {0, 1, 1, 0}, + {0, 0, 1, 1}, + {1, 0, 0, 1} +}; +// 半步模式相序(八拍) +static const uint8_t half_step_sequence[8][4] = { + {1, 0, 0, 0}, + {1, 1, 0, 0}, + {0, 1, 0, 0}, + {0, 1, 1, 0}, + {0, 0, 1, 0}, + {0, 0, 1, 1}, + {0, 0, 0, 1}, + {1, 0, 0, 1} +}; + +static void GPIO_Init(void) +{ + // 初始化前打印提示 + osal_printk("Initializing GPIO pins...\r\n"); + + // 配置引脚为GPIO模式(模式0通常为通用GPIO) + uapi_pin_set_mode(GPIO_Pin_0, 0); + uapi_pin_set_mode(GPIO_Pin_1, 0); + uapi_pin_set_mode(GPIO_Pin_2, 0); + uapi_pin_set_mode(GPIO_Pin_3, 0); + osal_printk("GPIO pins set to mode 0 (GPIO function)\r\n"); + + // 配置为输出方向 + uapi_gpio_set_dir(GPIO_Pin_0, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_dir(GPIO_Pin_1, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_dir(GPIO_Pin_2, GPIO_DIRECTION_OUTPUT); + uapi_gpio_set_dir(GPIO_Pin_3, GPIO_DIRECTION_OUTPUT); + osal_printk("GPIO pins configured as output\r\n"); +} + +static void SetCoilState(const uint8_t coil_state[4]) { + // 可选:注释掉详细的线圈状态日志(仅保留关键信息) + // osal_printk("Setting coil state: IN1=%d, IN2=%d, IN3=%d, IN4=%d\r\n", + // coil_state[0], coil_state[1], coil_state[2], coil_state[3]); + + // 设置引脚电平 + uapi_gpio_set_val(GPIO_Pin_0, coil_state[0] ? 1 : 0); + uapi_gpio_set_val(GPIO_Pin_1, coil_state[1] ? 1 : 0); + uapi_gpio_set_val(GPIO_Pin_2, coil_state[2] ? 1 : 0); + uapi_gpio_set_val(GPIO_Pin_3, coil_state[3] ? 1 : 0); +} + +/* 步进电机驱动函数 */ +void Stepper_Step(uint16_t steps, RotationDirection dir, StepMode mode, uint16_t step_delay_ms) { + const uint8_t (*sequence)[4] = NULL; + uint8_t sequence_length = 0; + + // 选择步进模式并打印 + if(mode == STEP_MODE_FULL) { + sequence = full_step_sequence; + sequence_length = 4; + osal_printk("Using full-step mode (4 steps per cycle)\r\n"); + } else { + sequence = half_step_sequence; + sequence_length = 8; + osal_printk("Using half-step mode (8 steps per cycle)\r\n"); + } + + // 打印本次运动参数 + osal_printk("Starting %s rotation: %d steps, delay %d ms per step\r\n", + (dir == DIRECTION_CW) ? "clockwise" : "counter-clockwise", + steps, step_delay_ms); + + static int16_t current_step = 0; + + for(uint16_t i = 0; i < steps; i++) { + // 减少日志频率(每500步打印一次,避免刷屏) + if(motor_command == 1 && is_moving==-1) break; + if(motor_command == 0 && is_moving==1) break; + if(i % 500 == 0) { + osal_printk("Step %d/%d\r\n", i, steps); + } + + // 更新当前步骤索引(修复格式问题) + if(dir == DIRECTION_CW) { + current_step++; + if(current_step >= sequence_length) { + current_step = 0; + } + } else { + if(current_step <= 0) { + current_step = sequence_length - 1; + } else { + current_step--; + } + } + + // 设置线圈状态 + SetCoilState(sequence[current_step]); + // 更新总步数 + g_stepper_steps++; + + // 延时 + osal_mdelay(step_delay_ms); + } + + osal_printk("Rotation completed\r\n"); +} + +/* 电机控制任务 */ +static void motor_task(const char *arg) { + unused(arg); + + GPIO_Init(); + + while (1) { + if (is_command != 0) { + if (motor_command == 1 && is_moving==-1) { + Stepper_Step(4096-g_stepper_steps, DIRECTION_CW, STEP_MODE_FULL, 2); + osal_printk("Inrrupt Curtain opened.\r\n"); + is_moving=0; + } else if (motor_command == 0 && is_moving==1) { + Stepper_Step(4096-g_stepper_steps, DIRECTION_CCW, STEP_MODE_FULL, 2); + osal_printk("Inrrupt Curtain closed.\r\n"); + is_moving=0; + } else if (motor_command == 1 && is_moving==0) { + is_moving=1; + Stepper_Step(4096, DIRECTION_CW, STEP_MODE_FULL, 2); + osal_printk("Curtain opened.\r\n"); + is_moving=0; + } else if (motor_command == 0 && is_moving==0) { + is_moving=-1; + Stepper_Step(4096, DIRECTION_CCW, STEP_MODE_FULL, 2); + osal_printk("Curtain closed.\r\n"); + is_moving=0; + } + is_command = 0; + } else { + osal_msleep(100); + } + } +} + +static void ssaps_server_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para, errcode_t status) { + osal_printk("%s ssaps write request callback cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, conn_id, write_cb_para->handle, status); + if ((write_cb_para->length > 0) && write_cb_para->value) { + osal_printk("\n sle uart received data : %.*s\r\n", write_cb_para->length, write_cb_para->value); + + /* 手动解析JSON格式数据 */ + char *data = (char *)write_cb_para->value; + char *state_start = strstr(data, "\"state\":"); // 匹配 "state":"ON" 或 "state":"OFF" + if (state_start) { + state_start += 8; // 跳过 "state": 前缀 + // 跳过可能存在的空格 + while (*state_start == ' ') state_start++; + + // 跳过引号 + if (*state_start == '"') { + state_start++; + } else { + osal_printk("Missing opening quote for state value.\r\n"); + return; + } + + // 检查状态值是否为 "ON" 或 "OFF" + if (strncmp(state_start, "ON", 2) == 0) { + motor_command = 1; + is_command = 1; + osal_printk("Curtain opening command received.\r\n"); + } else if (strncmp(state_start, "OFF", 3) == 0) { + motor_command = 0; + is_command = 1; + osal_printk("Curtain closing command received.\r\n"); + } else { + osal_printk("Invalid state value: %s\r\n", state_start); + } + } else { + osal_printk("Missing \"state\" field in JSON data.\r\n"); + } + } +} + +/* 星闪服务端任务 */ +static void *sle_uart_server_task(const char *arg) { + unused(arg); + /* 创建电机控制任务 */ + osal_task *motor_task_handle = NULL; + osal_kthread_lock(); + motor_task_handle = osal_kthread_create((osal_kthread_handler)motor_task, 0, "MotorControlTask", 0x1000); + if (motor_task_handle != NULL) { + osal_kthread_set_priority(motor_task_handle, 24); + } + osal_kthread_unlock(); + + /* 初始化星闪服务端 */ + sle_uart_server_init(NULL, ssaps_server_write_request_cbk); + sle_uart_server_adv_init(); + + while (1) { + if (g_reconnect_needed) { + osal_printk("[SLE] Starting auto-reconnect process...\r\n"); + sle_uart_server_init(NULL, ssaps_server_write_request_cbk); + sle_uart_server_adv_init(); + g_reconnect_needed = false; + } + + osal_msleep(1000); + } + return NULL; +} + +/* 服务端入口 */ +static void sle_uart_server_entry(void) { + osal_task *task_handle = NULL; + osal_kthread_lock(); + task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_server_task, 0, "SLEUartServerTask", 0x1200); + if (task_handle != NULL) { + osal_kthread_set_priority(task_handle, 28); + } + osal_kthread_unlock(); +} + +/* 应用入口 */ +app_run(sle_uart_server_entry); \ No newline at end of file diff --git a/vendor/developers/demo/STEPMOTOR/STEPMOTOR.h b/vendor/developers/demo/STEPMOTOR/STEPMOTOR.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server.c b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server.c new file mode 100644 index 0000000000000000000000000000000000000000..4b9bcfd3f08db7e5ba5f6fa9e6806eed1e325b86 --- /dev/null +++ b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server.c @@ -0,0 +1,581 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: SLE UART Server Source. \n + * + * History: \n + * 2023-07-17, Create file. \n + * + * Code Comments: Written by Lamonce. + * Last Modified: April 10, 2025 \n + * + * Introduction: + * This file implements the server-side functionality for SLE UART + * communications. It provides a complete GATT server implementation with service, + * characteristic, and descriptor management. The server broadcasts its availability, + * accepts client connections, handles pairing, and supports bidirectional data transmission + * through both UUID-based and handle-based methods. Key features include connection state + * management, callback registration for various events, and MTU negotiation. + * + * 简介: + * 本文件实现了 SLE UART 通信的服务端功能。它提供了完整的 GATT 服务器 + * 实现,包括服务、特征和描述符管理。服务端广播自身可用性,接受客户端连接请求,处理配对过程, + * 并通过基于 UUID 和基于句柄的方法支持双向数据传输。主要功能包括连接状态管理、各类事件的 + * 回调注册以及 MTU 协商。整个实现遵循星闪低功耗通信协议规范,确保高效、可靠的设备间通信。 + * + * DISCLAIMER: + * This code is provided for reference and learning purposes only. + * No warranty of correctness, completeness, or suitability for any purpose is provided. + * Before using in production environments, please review and test thoroughly. + * + * 免责声明: + * 本文件中的代码及注释仅供学习和参考使用,不保证其在所有环境下的正确性和完整性。 + * 在实际项目中使用前,请根据具体需求进行适当的修改和测试。 + * + */ + +#include "common_def.h" // 常用函数定义 +#include "securec.h" // 安全函数库,用于替代标准 C 库中不安全的函数 +#include "soc_osal.h" // 硬件抽象层 +#include "sle_errcode.h" // 错误码定义 +#include "sle_connection_manager.h" // 连接管理 +#include "sle_device_discovery.h" // 设备发现相关 +#include "sle_uart_server_adv.h" // 广播相关头文件 +#include "sle_uart_server.h" // Server 端头文件 + +#define OCTET_BIT_LEN 8 +#define UUID_LEN_2 2 // 16 位 UUID 长度 +#define UUID_INDEX 14 // UUID 最后两字节索引 +#define BT_INDEX_4 4 +#define BT_INDEX_0 0 +#define UART_BUFF_LENGTH 0x100 // UART 缓冲区长度 + +/* 广播ID */ +#define SLE_ADV_HANDLE_DEFAULT 1 // 设备公开 ID +/* sle server app uuid for test */ +static char g_sle_uuid_app_uuid[UUID_LEN_2] = {0x12, 0x34}; // 服务端应用 UUID +/* server notify property uuid for test */ +static char g_sle_property_value[OCTET_BIT_LEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; // 特征值 +/* sle connect acb handle */ +static uint16_t g_sle_conn_hdl = 0; // 连接句柄 +/* sle server handle */ +static uint8_t g_server_id = 0; // 服务端 ID +/* sle service handle */ +static uint16_t g_service_handle = 0; // 服务句柄 +/* sle ntf property handle */ +static uint16_t g_property_handle = 0; // 特征句柄 +/* sle pair acb handle */ +uint16_t g_sle_pair_hdl; // 配对句柄 + +#define UUID_16BIT_LEN 2 // 16 位 UUID 长度 +#define UUID_128BIT_LEN 16 // 128 位 UUID 长度 +#define sample_at_log_print(fmt, args...) osal_printk(fmt, ##args) +#define SLE_UART_SERVER_LOG "[sle uart server]" // 日志前缀 +#define SLE_SERVER_INIT_DELAY_MS 1000 // 延时 1 秒 +static sle_uart_server_msg_queue g_sle_uart_server_msg_queue = NULL; // 消息队列 + +// 星闪标准服务标识 基础标识(Base UUID):37BEA880-FC70-11EA-B720-000000000000 +// 带上这个基础标识表示这个星闪服务 +// Base UUID 后面6字节是媒体接入层标识(在某个网段内,分配给网络设备的用于网络通信寻址的唯一标识) +// 在用于产品开发时,厂商需要向 SparkLink 组织申请 +static uint8_t g_sle_uart_base[] = {0x37, 0xBE, 0xA8, 0x80, 0xFC, 0x70, 0x11, 0xEA, + 0xB7, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// 获取连接句柄 +uint16_t get_connect_id(void) +{ + return g_sle_conn_hdl; +} + +/** + * @brief 将16位整数(uint16_t)以小端字节序的方式存储到内存中 + * + * @param _ptr 低地址指针 + * @param data 数据 + * + * @attention 将16位整数 data 的低8位(最低有效字节)存储到 _ptr 指向的地址 + * @attention 将16位整数 data 的高8位(最高有效字节)存储到 _ptr+1 指向的地址 + */ +static void encode2byte_little(uint8_t *_ptr, uint16_t data) +{ + *(uint8_t *)((_ptr) + 1) = (uint8_t)((data) >> 0x8); + *(uint8_t *)(_ptr) = (uint8_t)(data); +} + +// 设置服务 UUID 的基础值 +static void sle_uuid_set_base(sle_uuid_t *out) +{ + errcode_t ret; + // 复制 UUID 的基础值 + ret = memcpy_s(out->uuid, SLE_UUID_LEN, g_sle_uart_base, SLE_UUID_LEN); + if (ret != EOK) + { + sample_at_log_print("%s sle_uuid_set_base memcpy fail\n", SLE_UART_SERVER_LOG); + out->len = 0; + return; + } + out->len = UUID_LEN_2; // 设置 UUID 的长度为 2 +} + +// 设置长度为 2 的服务 UUID 的值 +static void sle_uuid_setu2(uint16_t u2, sle_uuid_t *out) +{ + sle_uuid_set_base(out); // 设置 UUID 的基础值 + out->len = UUID_LEN_2; // 设置 UUID 的长度为 2 + encode2byte_little(&out->uuid[UUID_INDEX], u2); // 将 16 位整数以小端字节序存储到 UUID 末尾 +} + +// 输出 UUID 的值 +static void sle_uart_uuid_print(sle_uuid_t *uuid) +{ + if (uuid == NULL) + { + sample_at_log_print("%s uuid_print,uuid is null\r\n", SLE_UART_SERVER_LOG); + return; + } + + // 检查 UUID 长度 + if (uuid->len == UUID_16BIT_LEN) + { + sample_at_log_print("%s uuid: %02x %02x.\n", SLE_UART_SERVER_LOG, + uuid->uuid[14], uuid->uuid[15]); /* 14 15: uuid index */ + } + else if (uuid->len == UUID_128BIT_LEN) + { + sample_at_log_print("%s uuid: \n", SLE_UART_SERVER_LOG); /* 14 15: uuid index */ + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[0], uuid->uuid[1], + uuid->uuid[2], uuid->uuid[3]); + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[4], uuid->uuid[5], + uuid->uuid[6], uuid->uuid[7]); + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[8], uuid->uuid[9], + uuid->uuid[10], uuid->uuid[11]); + sample_at_log_print("%s 0x%02x 0x%02x 0x%02x \n", SLE_UART_SERVER_LOG, uuid->uuid[12], uuid->uuid[13], + uuid->uuid[14], uuid->uuid[15]); + } +} + +// -------- ssapc 注册回调函数 -------- + +/** + * @brief MTU 改变回调函数 + * + * @param server_id 服务 ID + * @param conn_id 连接 ID + * @param mtu_size MTU 大小 + * @param status 状态码 + */ +static void ssaps_mtu_changed_cbk(uint8_t server_id, uint16_t conn_id, ssap_exchange_info_t *mtu_size, + errcode_t status) +{ + sample_at_log_print("%s ssaps ssaps_mtu_changed_cbk callback server_id:%x, conn_id:%x, mtu_size:%x, status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, conn_id, mtu_size->mtu_size, status); + if (g_sle_pair_hdl == 0) + { + g_sle_pair_hdl = conn_id + 1; + } +} + +/** + * @brief 服务启动回调函数 + * + * @param server_id 服务 ID + * @param handle 服务句柄 + * @param status 状态码 + */ +static void ssaps_start_service_cbk(uint8_t server_id, uint16_t handle, errcode_t status) +{ + sample_at_log_print("%s start service cbk callback server_id:%d, handle:%x, status:%x\r\n", SLE_UART_SERVER_LOG, + server_id, handle, status); +} + +/** + * @brief ssaps 添加服务回调函数 + * + * @param server_id 服务 ID + * @param uuid 服务 UUID + * @param handle 服务句柄 + * @param status 状态码 + */ +static void ssaps_add_service_cbk(uint8_t server_id, sle_uuid_t *uuid, uint16_t handle, errcode_t status) +{ + sample_at_log_print("%s add service cbk callback server_id:%x, handle:%x, status:%x\r\n", SLE_UART_SERVER_LOG, + server_id, handle, status); + sle_uart_uuid_print(uuid); +} + +/** + * @brief 服务特征添加回调函数 + * + * @param server_id 服务 ID + * @param uuid 服务 UUID + * @param service_handle 服务句柄 + * @param handle 特征句柄 + * @param status 状态码 + */ +static void ssaps_add_property_cbk(uint8_t server_id, sle_uuid_t *uuid, uint16_t service_handle, + uint16_t handle, errcode_t status) +{ + sample_at_log_print("%s add property cbk callback server_id:%x, service_handle:%x,handle:%x, status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, service_handle, handle, status); + sle_uart_uuid_print(uuid); +} + +/** + * @brief 服务描述符添加回调函数 + * + * @param server_id 服务 ID + * @param uuid 服务 UUID + * @param service_handle 服务句柄 + * @param property_handle 特征句柄 + * @param status 状态码 + */ +static void ssaps_add_descriptor_cbk(uint8_t server_id, sle_uuid_t *uuid, uint16_t service_handle, + uint16_t property_handle, errcode_t status) +{ + sample_at_log_print("%s add descriptor cbk callback server_id:%x, service_handle:%x, property_handle:%x, \ + status:%x\r\n", + SLE_UART_SERVER_LOG, server_id, service_handle, property_handle, status); + sle_uart_uuid_print(uuid); +} + +/** + * @brief 删除所有服务回调函数 + * + * @param server_id 服务 ID + * @param status 状态码 + */ +static void ssaps_delete_all_service_cbk(uint8_t server_id, errcode_t status) +{ + sample_at_log_print("%s delete all service callback server_id:%x, status:%x\r\n", SLE_UART_SERVER_LOG, + server_id, status); +} + +// ssaps 注册回调函数 +static errcode_t sle_ssaps_register_cbks(ssaps_read_request_callback ssaps_read_callback, ssaps_write_request_callback + ssaps_write_callback) +{ + errcode_t ret; + ssaps_callbacks_t ssaps_cbk = {0}; // 回调函数结构体 + ssaps_cbk.add_service_cb = ssaps_add_service_cbk; // 添加服务回调函数 + ssaps_cbk.add_property_cb = ssaps_add_property_cbk; // 添加特征回调函数 + ssaps_cbk.add_descriptor_cb = ssaps_add_descriptor_cbk; // 添加描述符回调函数 + ssaps_cbk.start_service_cb = ssaps_start_service_cbk; // 服务启动回调函数 + ssaps_cbk.delete_all_service_cb = ssaps_delete_all_service_cbk; // 删除所有服务回调函数 + ssaps_cbk.mtu_changed_cb = ssaps_mtu_changed_cbk; // MTU 改变回调函数 + ssaps_cbk.read_request_cb = ssaps_read_callback; // 读请求回调函数 + ssaps_cbk.write_request_cb = ssaps_write_callback; // 写请求回调函数 + ret = ssaps_register_callbacks(&ssaps_cbk); // 注册回调函数 + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_ssaps_register_cbks,ssaps_register_callbacks fail :%x\r\n", SLE_UART_SERVER_LOG, + ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} + +// -------- ssapc 注册回调函数结束 ---- + +// 服务添加 +static errcode_t sle_uuid_server_service_add(void) +{ + errcode_t ret; + sle_uuid_t service_uuid = {0}; // 创建服务 UUID 结构体 + sle_uuid_setu2(SLE_UUID_SERVER_SERVICE, &service_uuid); // 设置服务 UUID + ret = ssaps_add_service_sync(g_server_id, &service_uuid, 1, &g_service_handle); // 添加一个ssap服务 + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uuid add service fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + return ERRCODE_SLE_FAIL; + } + return ERRCODE_SLE_SUCCESS; +} + +// 添加特征 +static errcode_t sle_uuid_server_property_add(void) +{ + errcode_t ret; + ssaps_property_info_t property = {0}; // 创建特征信息结构体 + ssaps_desc_info_t descriptor = {0}; // 创建描述符信息结构体 + uint8_t ntf_value[] = {0x01, 0x0}; // 描述符数据 + + property.permissions = SLE_UUID_TEST_PROPERTIES; // 特征权限,此 demo 设置为可读可写 + property.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_NOTIFY; // 操作指示,数据值可被读取,通过通知方式传递给客户端 + sle_uuid_setu2(SLE_UUID_SERVER_NTF_REPORT, &property.uuid); // 设置特征 UUID + + // 分配内存给特征值(value 为指向特征值的指针) + property.value = (uint8_t *)osal_vmalloc(sizeof(g_sle_property_value)); + if (property.value == NULL) // 检查内存分配是否成功 + { + return ERRCODE_SLE_FAIL; + } + if (memcpy_s(property.value, sizeof(g_sle_property_value), g_sle_property_value, + sizeof(g_sle_property_value)) != EOK) // 复制特征值 + { + osal_vfree(property.value); // 当复制失败时,释放内存 + return ERRCODE_SLE_FAIL; + } + ret = ssaps_add_property_sync(g_server_id, g_service_handle, &property, &g_property_handle); // 添加特征,并获取特征句柄 + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uart add property fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + osal_vfree(property.value); // 当添加特征失败时,释放内存 + return ERRCODE_SLE_FAIL; + } + descriptor.permissions = SLE_UUID_TEST_DESCRIPTOR; // 特征权限,此 demo 设置为可读可写 + descriptor.type = SSAP_DESCRIPTOR_USER_DESCRIPTION; // 描述符类型,属性说明描述符 + descriptor.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_WRITE; // 操作指示,数据值可被读取和写入,写入后产生反馈给客户端 + descriptor.value = ntf_value; // 描述符数据 + descriptor.value_len = sizeof(ntf_value); // 描述符数据长度 + + // 添加描述符 + ret = ssaps_add_descriptor_sync(g_server_id, g_service_handle, g_property_handle, &descriptor); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uart add descriptor fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + osal_vfree(property.value); // 若添加描述符失败,释放特征值内存 + osal_vfree(descriptor.value); // 释放描述符值内存 + return ERRCODE_SLE_FAIL; + } + // 添加特征成功后,释放特征值内存 + osal_vfree(property.value); + return ERRCODE_SLE_SUCCESS; +} + +// 添加服务 +static errcode_t sle_uart_server_add(void) +{ + errcode_t ret; + sle_uuid_t app_uuid = {0}; // 创建应用 UUID 结构体 + + sample_at_log_print("%s sle uart add service in\r\n", SLE_UART_SERVER_LOG); + app_uuid.len = sizeof(g_sle_uuid_app_uuid); // 设置应用 UUID 长度 + // 复制应用 UUID + if (memcpy_s(app_uuid.uuid, app_uuid.len, g_sle_uuid_app_uuid, sizeof(g_sle_uuid_app_uuid)) != EOK) + { + return ERRCODE_SLE_FAIL; + } + ssaps_register_server(&app_uuid, &g_server_id); // 注册 ssap 服务端,参数:app_uuid:上层应用uuid,g_server_id:服务端ID + + // 添加服务 + if (sle_uuid_server_service_add() != ERRCODE_SLE_SUCCESS) + { + ssaps_unregister_server(g_server_id); // 如果添加服务失败,注销服务端 + return ERRCODE_SLE_FAIL; + } + + // 添加特征 + if (sle_uuid_server_property_add() != ERRCODE_SLE_SUCCESS) + { + ssaps_unregister_server(g_server_id); // 如果添加特征失败,注销服务端 + return ERRCODE_SLE_FAIL; + } + sample_at_log_print("%s sle uart add service, server_id:%x, service_handle:%x, property_handle:%x\r\n", + SLE_UART_SERVER_LOG, g_server_id, g_service_handle, g_property_handle); + + // 启动服务 + ret = ssaps_start_service(g_server_id, g_service_handle); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle uart add service fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret); + return ERRCODE_SLE_FAIL; + } + sample_at_log_print("%s sle uart add service out\r\n", SLE_UART_SERVER_LOG); + return ERRCODE_SLE_SUCCESS; +} + +/* device通过uuid向host发送数据:report */ +/** + * @brief Server 端通过 UUID 向 Host(Client) 发送数据 + * + * @param data 发送的数据 + * @param len 数据长度 + * @return errcode_t + */ +errcode_t sle_uart_server_send_report_by_uuid(const uint8_t *data, uint8_t len) +{ + errcode_t ret; + ssaps_ntf_ind_by_uuid_t param = {0}; // 创建通知/指示参数结构体 + param.type = SSAP_PROPERTY_TYPE_VALUE; // 属性类型,特征值 + param.start_handle = g_service_handle; // 起始句柄 + param.end_handle = g_property_handle; // 结束句柄 + param.value_len = len; // 数据长度 + param.value = (uint8_t *)osal_vmalloc(len); // 动态分配内存给数据 + if (param.value == NULL) // 检查内存分配是否成功 + { + sample_at_log_print("%s send report new fail\r\n", SLE_UART_SERVER_LOG); + return ERRCODE_SLE_FAIL; + } + if (memcpy_s(param.value, param.value_len, data, len) != EOK) // 复制数据到参数 + { + sample_at_log_print("%s send input report memcpy fail\r\n", SLE_UART_SERVER_LOG); + osal_vfree(param.value); // 当复制失败时,释放内存 + return ERRCODE_SLE_FAIL; + } + sle_uuid_setu2(SLE_UUID_SERVER_NTF_REPORT, ¶m.uuid); // 设置 UUID + ret = ssaps_notify_indicate_by_uuid(g_server_id, g_sle_conn_hdl, ¶m); // 发送通知/指示,具体发送状态取决于客户端特征配置描述符值 + if (ret != ERRCODE_SLE_SUCCESS) // 检查发送是否成功 + { + sample_at_log_print("%s sle_uart_server_send_report_by_uuid,ssaps_notify_indicate_by_uuid fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + osal_vfree(param.value); + return ret; + } + osal_vfree(param.value); // 释放内存 + return ERRCODE_SLE_SUCCESS; +} + +/* device通过handle向host发送数据:report */ +/** + * @brief Server 端通过句柄向 Host(Client) 发送数据 + * + * @param data 数据 + * @param len 数据长度 + * @return errcode_t + */ +errcode_t sle_uart_server_send_report_by_handle(const uint8_t *data, uint16_t len) +{ + ssaps_ntf_ind_t param = {0}; + uint8_t receive_buf[UART_BUFF_LENGTH] = {0}; /* max receive length. */ + param.handle = g_property_handle; + param.type = SSAP_PROPERTY_TYPE_VALUE; + param.value = receive_buf; + param.value_len = len; + if (memcpy_s(param.value, param.value_len, data, len) != EOK) + { + return ERRCODE_SLE_FAIL; + } + return ssaps_notify_indicate(g_server_id, g_sle_conn_hdl, ¶m); +} + +/** + * @brief 连接状态改变回调函数 + * + * @param conn_id 连接 ID + * @param addr 设备地址 + * @param conn_state 连接状态 + * @param pair_state 配对状态 + * @param disc_reason 断开连接的原因 + */ +static void sle_connect_state_changed_cbk(uint16_t conn_id, const sle_addr_t *addr, + sle_acb_state_t conn_state, sle_pair_state_t pair_state, sle_disc_reason_t disc_reason) +{ + uint8_t sle_connect_state[] = "sle_dis_connect"; // 创建连接状态字符串,并初始化为 "sle_dis_connect" + sample_at_log_print("%s connect state changed callback conn_id:0x%02x, conn_state:0x%x, pair_state:0x%x, \ + disc_reason:0x%x\r\n", + SLE_UART_SERVER_LOG, conn_id, conn_state, pair_state, disc_reason); + sample_at_log_print("%s connect state changed callback addr:%02x:**:**:**:%02x:%02x\r\n", SLE_UART_SERVER_LOG, + addr->addr[BT_INDEX_0], addr->addr[BT_INDEX_4]); + if (conn_state == SLE_ACB_STATE_CONNECTED) // 已连接 + { + g_sle_conn_hdl = conn_id; // 更新连接句柄 + } + else if (conn_state == SLE_ACB_STATE_DISCONNECTED) // 未连接 + { + g_sle_conn_hdl = 0; + g_sle_pair_hdl = 0; + if (g_sle_uart_server_msg_queue != NULL) + { + g_sle_uart_server_msg_queue(sle_connect_state, sizeof(sle_connect_state)); + } + } +} + +// 配对完成回调函数 +static void sle_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status) +{ + sample_at_log_print("%s pair complete conn_id:%02x, status:%x\r\n", SLE_UART_SERVER_LOG, + conn_id, status); + sample_at_log_print("%s pair complete addr:%02x:**:**:**:%02x:%02x\r\n", SLE_UART_SERVER_LOG, + addr->addr[BT_INDEX_0], addr->addr[BT_INDEX_4]); + g_sle_pair_hdl = conn_id + 1; + ssap_exchange_info_t parameter = {0}; + parameter.mtu_size = 520; // 设置 MTU 大小为 520 字节 + parameter.version = 1; + ssaps_set_info(g_server_id, ¶meter); +} + +// 注册连接回调函数 +static errcode_t sle_conn_register_cbks(void) +{ + errcode_t ret; + sle_connection_callbacks_t conn_cbks = {0}; + conn_cbks.connect_state_changed_cb = sle_connect_state_changed_cbk; // 连接状态改变回调 + conn_cbks.pair_complete_cb = sle_pair_complete_cbk; // 配对完成回调 + ret = sle_connection_register_callbacks(&conn_cbks); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_conn_register_cbks,sle_connection_register_callbacks fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} + +// 获取连接句柄 +uint16_t sle_uart_client_is_connected(void) +{ + return g_sle_pair_hdl; +} + +/* 初始化uuid server */ +errcode_t sle_uart_server_init(ssaps_read_request_callback ssaps_read_callback, ssaps_write_request_callback + ssaps_write_callback) +{ + errcode_t ret; + + /* 使能SLE */ + if (enable_sle() != ERRCODE_SUCC) + { + sample_at_log_print("[SLE Server] sle enbale fail !\r\n"); + return -1; + } + + // 注册广播回调函数 + ret = sle_uart_announce_register_cbks(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_uart_announce_register_cbks fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 注册连接回调函数 + ret = sle_conn_register_cbks(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_conn_register_cbks fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 注册 ssaps 回调函数 + ret = sle_ssaps_register_cbks(ssaps_read_callback, ssaps_write_callback); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_ssaps_register_cbks fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 添加服务 + ret = sle_uart_server_add(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_uart_server_add fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + + // 初始化广播 + ret = sle_uart_server_adv_init(); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_init,sle_uart_server_adv_init fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + sample_at_log_print("%s init ok\r\n", SLE_UART_SERVER_LOG); + return ERRCODE_SLE_SUCCESS; +} + +void sle_uart_server_register_msg(sle_uart_server_msg_queue sle_uart_server_msg) +{ + g_sle_uart_server_msg_queue = sle_uart_server_msg; +} \ No newline at end of file diff --git a/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server.h b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server.h new file mode 100644 index 0000000000000000000000000000000000000000..e72a3fea4586146d13003db86c024360982287c5 --- /dev/null +++ b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: SLE uart server Config. \n + * + * History: \n + * 2023-07-17, Create file. \n + */ + +#ifndef SLE_UART_SERVER_H +#define SLE_UART_SERVER_H + +#include +#include "sle_ssap_server.h" +#include "errcode.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +/* Service UUID */ +#define SLE_UUID_SERVER_SERVICE 0x2222 + +/* Property UUID */ +#define SLE_UUID_SERVER_NTF_REPORT 0x2323 + +/* Property Property */ +#define SLE_UUID_TEST_PROPERTIES (SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE) + +/* Operation indication */ +#define SLE_UUID_TEST_OPERATION_INDICATION (SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_WRITE) + +/* Descriptor Property */ +#define SLE_UUID_TEST_DESCRIPTOR (SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE) + +errcode_t sle_uart_server_init(ssaps_read_request_callback ssaps_read_callback, ssaps_write_request_callback + ssaps_write_callback); + +errcode_t sle_uart_server_send_report_by_uuid(const uint8_t *data, uint8_t len); + +errcode_t sle_uart_server_send_report_by_handle(const uint8_t *data, uint16_t len); + +uint16_t sle_uart_client_is_connected(void); + +typedef void (*sle_uart_server_msg_queue)(uint8_t *buffer_addr, uint16_t buffer_size); + +void sle_uart_server_register_msg(sle_uart_server_msg_queue sle_uart_server_msg); + + +uint16_t get_connect_id(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server_adv.c b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server_adv.c new file mode 100644 index 0000000000000000000000000000000000000000..1dd52a6f690dcb08216478872f480aab1dc7016f --- /dev/null +++ b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server_adv.c @@ -0,0 +1,316 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: sle adv config for sle uart server. \n + * + * History: \n + * 2023-07-17, Create file. \n + * + * Code Comments: Written by Lamonce. + * Last Modified: June 09, 2025 \n + * + * Introduction: + * This file implements the advertising functionality for SLE UART server communications. + * It provides complete advertising configuration and management, including advertisement + * parameters setup, advertisement data formatting, and scan response data configuration. + * The module supports standard SLE advertising modes, handles advertisement state callbacks, + * and manages the advertisement lifecycle. Key features include customizable advertisement + * intervals, transmit power control, device name broadcasting, and service data advertising. + * + * 简介: + * 本文件实现了 SLE UART 服务端通信的广播功能。它提供了完整的广播配置和管理, + * 包括广播参数设置、广播数据格式化和扫描响应数据配置。该模块支持标准 SLE 广播模式, + * 处理广播状态回调,并管理广播生命周期。主要功能包括可自定义的广播间隔、发射功率控制、 + * 设备名称广播和服务数据广播。整个模块设计符合星闪低功耗通信协议规范, + * 确保设备能被客户端高效发现和连接。 + * + * DISCLAIMER: + * This code is provided for reference and learning purposes only. + * No warranty of correctness, completeness, or suitability for any purpose is provided. + * Before using in production environments, please review and test thoroughly. + * + * 免责声明: + * 本文件中的代码及注释仅供学习和参考使用,不保证其在所有环境下的正确性和完整性。 + * 在实际项目中使用前,请根据具体需求进行适当的修改和测试。 + * + */ +#include "securec.h" // 安全函数库,用于替代标准 C 库中不安全的函数 +#include "errcode.h" // 错误码定义 +#include "osal_addr.h" // 地址相关函数 +#include "product.h" // 产品相关函数 +#include "sle_common.h" // 公共函数定义 +#include "sle_uart_server.h" // Server 端头文件 +#include "sle_device_discovery.h" // 设备发现相关头文件 +#include "sle_errcode.h" // 错误码定义 +#include "osal_debug.h" // 调试相关函数 +#include "osal_task.h" // 任务相关函数 +#include "string.h" // 字符串相关函数 +#include "sle_uart_server_adv.h" // Server 端广播相关头文件 + +/* sle device name */ +#define NAME_MAX_LENGTH 16 +/* 连接调度间隔12.5ms,单位125us */ +#define SLE_CONN_INTV_MIN_DEFAULT 0x64 +/* 连接调度间隔12.5ms,单位125us */ +#define SLE_CONN_INTV_MAX_DEFAULT 0x64 +/* 连接调度间隔25ms,单位125us */ +#define SLE_ADV_INTERVAL_MIN_DEFAULT 0xC8 +/* 连接调度间隔25ms,单位125us */ +#define SLE_ADV_INTERVAL_MAX_DEFAULT 0xC8 +/* 超时时间5000ms,单位10ms */ +#define SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT 0x1F4 +/* 超时时间4990ms,单位10ms */ +#define SLE_CONN_MAX_LATENCY 0x1F3 +/* 广播发送功率 */ +#define SLE_ADV_TX_POWER 10 +/* 广播ID */ +#define SLE_ADV_HANDLE_DEFAULT 1 +/* 最大广播数据长度 */ +#define SLE_ADV_DATA_LEN_MAX 251 +/* 广播名称 */ + +// static uint8_t sle_local_name[NAME_MAX_LENGTH] = "NearLink"; // 广播名称 +#define SLE_SERVER_INIT_DELAY_MS 1000 // 广播初始化延时 +#define sample_at_log_print(fmt, args...) osal_printk(fmt, ##args) // 日志打印宏 +#define SLE_UART_SERVER_LOG "[sle uart server]" // 日志前缀 + +static uint8_t sle_local_name[NAME_MAX_LENGTH] = {0}; + +int set_SLE_local_name(const uint8_t *name) +{ + if (name == NULL || strlen((char*)name) >= NAME_MAX_LENGTH) + { + osal_printk("%s set_SLE_local_name failed, name is null or too long\n", SLE_UART_SERVER_LOG); + return -1; // 错误处理 + } + // 复制本地名称到全局变量 + strncpy((char *)sle_local_name, (char *)name, NAME_MAX_LENGTH - 1); + sle_local_name[NAME_MAX_LENGTH - 1] = '\0'; // 确保字符串以 null 结尾 + return 0; // 成功 +} + +// 设置广播设备名称,也是本地名称 +static uint16_t sle_set_adv_local_name(uint8_t *adv_data, uint16_t max_len) +{ + errno_t ret; + uint8_t index = 0; + + uint8_t *local_name = sle_local_name; // 赋值本地名称 + uint8_t local_name_len = sizeof(sle_local_name) - 1; // 不包括结束符 + sample_at_log_print("%s local_name_len = %d\r\n", SLE_UART_SERVER_LOG, local_name_len); // 日志 + sample_at_log_print("%s local_name: ", SLE_UART_SERVER_LOG); + for (uint8_t i = 0; i < local_name_len; i++) + { + sample_at_log_print("0x%02x ", local_name[i]); + } + sample_at_log_print("\r\n"); + adv_data[index++] = local_name_len + 1; // 长度+1 + adv_data[index++] = SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME; // 数据类型 + ret = memcpy_s(&adv_data[index], max_len - index, local_name, local_name_len); // 拷贝本地名称 + if (ret != EOK) + { + sample_at_log_print("%s memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + return (uint16_t)index + local_name_len; +} + +static uint16_t sle_set_adv_data(uint8_t *adv_data) +{ + size_t len = 0; + uint16_t idx = 0; + errno_t ret = 0; + + len = sizeof(struct sle_adv_common_value); + struct sle_adv_common_value adv_disc_level = { + .length = len - 1, + .type = SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL, + .value = SLE_ANNOUNCE_LEVEL_NORMAL, + }; + ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_disc_level, len); + if (ret != EOK) + { + sample_at_log_print("%s adv_disc_level memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + idx += len; + + len = sizeof(struct sle_adv_common_value); + struct sle_adv_common_value adv_access_mode = { + .length = len - 1, + .type = SLE_ADV_DATA_TYPE_ACCESS_MODE, + .value = 0, + }; + ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_access_mode, len); + if (ret != EOK) + { + sample_at_log_print("%s adv_access_mode memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + idx += len; + + return idx; +} + +// 设置扫描响应数据 +static uint16_t sle_set_scan_response_data(uint8_t *scan_rsp_data) +{ + uint16_t idx = 0; + errno_t ret; + size_t scan_rsp_data_len = sizeof(struct sle_adv_common_value); + + struct sle_adv_common_value tx_power_level = { + .length = scan_rsp_data_len - 1, + .type = SLE_ADV_DATA_TYPE_TX_POWER_LEVEL, + .value = SLE_ADV_TX_POWER, + }; + ret = memcpy_s(scan_rsp_data, SLE_ADV_DATA_LEN_MAX, &tx_power_level, scan_rsp_data_len); + if (ret != EOK) + { + sample_at_log_print("%s sle scan response data memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + idx += scan_rsp_data_len; + + /* set local name */ + idx += sle_set_adv_local_name(&scan_rsp_data[idx], SLE_ADV_DATA_LEN_MAX - idx); + return idx; +} + +// 设置广播参数 +static int sle_set_default_announce_param(void) +{ + errno_t ret; + sle_announce_param_t param = {0}; + uint8_t index; + unsigned char local_addr[SLE_ADDR_LEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + param.announce_mode = SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE; + param.announce_handle = SLE_ADV_HANDLE_DEFAULT; + param.announce_gt_role = SLE_ANNOUNCE_ROLE_T_CAN_NEGO; + param.announce_level = SLE_ANNOUNCE_LEVEL_NORMAL; + param.announce_channel_map = SLE_ADV_CHANNEL_MAP_DEFAULT; + param.announce_interval_min = SLE_ADV_INTERVAL_MIN_DEFAULT; + param.announce_interval_max = SLE_ADV_INTERVAL_MAX_DEFAULT; + param.conn_interval_min = SLE_CONN_INTV_MIN_DEFAULT; + param.conn_interval_max = SLE_CONN_INTV_MAX_DEFAULT; + param.conn_max_latency = SLE_CONN_MAX_LATENCY; + param.conn_supervision_timeout = SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT; + param.announce_tx_power = 18; // 设置为 18 dBm + param.own_addr.type = 0; + ret = memcpy_s(param.own_addr.addr, SLE_ADDR_LEN, local_addr, SLE_ADDR_LEN); + if (ret != EOK) + { + sample_at_log_print("%s sle_set_default_announce_param data memcpy fail\r\n", SLE_UART_SERVER_LOG); + return 0; + } + sample_at_log_print("%s sle_uart_local addr: ", SLE_UART_SERVER_LOG); + for (index = 0; index < SLE_ADDR_LEN; index++) + { + sample_at_log_print("0x%02x ", param.own_addr.addr[index]); + } + sample_at_log_print("\r\n"); + return sle_set_announce_param(param.announce_handle, ¶m); +} + +// 设置默认广播数据 +static int sle_set_default_announce_data(void) +{ + errcode_t ret; + uint8_t announce_data_len = 0; + uint8_t seek_data_len = 0; + sle_announce_data_t data = {0}; + uint8_t adv_handle = SLE_ADV_HANDLE_DEFAULT; + uint8_t announce_data[SLE_ADV_DATA_LEN_MAX] = {0}; + uint8_t seek_rsp_data[SLE_ADV_DATA_LEN_MAX] = {0}; + uint8_t data_index = 0; + + announce_data_len = sle_set_adv_data(announce_data); + data.announce_data = announce_data; + data.announce_data_len = announce_data_len; + + sample_at_log_print("%s data.announce_data_len = %d\r\n", SLE_UART_SERVER_LOG, data.announce_data_len); + sample_at_log_print("%s data.announce_data: ", SLE_UART_SERVER_LOG); + for (data_index = 0; data_index < data.announce_data_len; data_index++) + { + sample_at_log_print("0x%02x ", data.announce_data[data_index]); + } + sample_at_log_print("\r\n"); + + seek_data_len = sle_set_scan_response_data(seek_rsp_data); + data.seek_rsp_data = seek_rsp_data; + data.seek_rsp_data_len = seek_data_len; + + sample_at_log_print("%s data.seek_rsp_data_len = %d\r\n", SLE_UART_SERVER_LOG, data.seek_rsp_data_len); + sample_at_log_print("%s data.seek_rsp_data: ", SLE_UART_SERVER_LOG); + for (data_index = 0; data_index < data.seek_rsp_data_len; data_index++) + { + sample_at_log_print("0x%02x ", data.seek_rsp_data[data_index]); + } + sample_at_log_print("\r\n"); + + ret = sle_set_announce_data(adv_handle, &data); + if (ret == ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s set announce data success.\r\n", SLE_UART_SERVER_LOG); + } + else + { + sample_at_log_print("%s set adv param fail.\r\n", SLE_UART_SERVER_LOG); + } + return ERRCODE_SLE_SUCCESS; +} + +// 广播启动回调 +static void sle_announce_enable_cbk(uint32_t announce_id, errcode_t status) +{ + sample_at_log_print("%s sle announce enable callback id:%02x, state:%x\r\n", SLE_UART_SERVER_LOG, announce_id, + status); +} + +// 广播停止回调 +static void sle_announce_disable_cbk(uint32_t announce_id, errcode_t status) +{ + sample_at_log_print("%s sle announce disable callback id:%02x, state:%x\r\n", SLE_UART_SERVER_LOG, announce_id, + status); +} + +// 广播终止回调 +static void sle_announce_terminal_cbk(uint32_t announce_id) +{ + sample_at_log_print("%s sle announce terminal callback id:%02x\r\n", SLE_UART_SERVER_LOG, announce_id); +} + +// 注册广播回调函数 +errcode_t sle_uart_announce_register_cbks(void) +{ + errcode_t ret = 0; + sle_announce_seek_callbacks_t seek_cbks = {0}; + seek_cbks.announce_enable_cb = sle_announce_enable_cbk; + seek_cbks.announce_disable_cb = sle_announce_disable_cbk; + seek_cbks.announce_terminal_cb = sle_announce_terminal_cbk; + ret = sle_announce_seek_register_callbacks(&seek_cbks); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_announce_register_cbks,register_callbacks fail :%x\r\n", + SLE_UART_SERVER_LOG, ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} + +// 初始化广播 +errcode_t sle_uart_server_adv_init(void) +{ + errcode_t ret; + sle_set_default_announce_param(); + sle_set_default_announce_data(); + ret = sle_start_announce(SLE_ADV_HANDLE_DEFAULT); + sample_at_log_print("%s sle_uart_server_adv_init,sle_start_announce devise name :%s\r\n", + SLE_UART_SERVER_LOG, sle_local_name); + if (ret != ERRCODE_SLE_SUCCESS) + { + sample_at_log_print("%s sle_uart_server_adv_init,sle_start_announce fail :%x\r\n", SLE_UART_SERVER_LOG, ret); + return ret; + } + return ERRCODE_SLE_SUCCESS; +} diff --git a/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server_adv.h b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server_adv.h new file mode 100644 index 0000000000000000000000000000000000000000..36376465deafcd1a5949585a3982493727c6ab5b --- /dev/null +++ b/vendor/developers/demo/STEPMOTOR/sle_uart_server/sle_uart_server_adv.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved. + * + * Description: SLE ADV Config. \n + * + * History: \n + * 2023-07-17, Create file. \n + */ + +#ifndef SLE_SERVER_ADV_H +#define SLE_SERVER_ADV_H + +typedef struct sle_adv_common_value +{ + uint8_t type; + uint8_t length; + uint8_t value; +} le_adv_common_t; + +typedef enum sle_adv_channel +{ + SLE_ADV_CHANNEL_MAP_77 = 0x01, + SLE_ADV_CHANNEL_MAP_78 = 0x02, + SLE_ADV_CHANNEL_MAP_79 = 0x04, + SLE_ADV_CHANNEL_MAP_DEFAULT = 0x07 +} sle_adv_channel_map_t; + +typedef enum sle_adv_data +{ + SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL = 0x01, /* 发现等级 */ + SLE_ADV_DATA_TYPE_ACCESS_MODE = 0x02, /* 接入层能力 */ + SLE_ADV_DATA_TYPE_SERVICE_DATA_16BIT_UUID = 0x03, /* 标准服务数据信息 */ + SLE_ADV_DATA_TYPE_SERVICE_DATA_128BIT_UUID = 0x04, /* 自定义服务数据信息 */ + SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x05, /* 完整标准服务标识列表 */ + SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x06, /* 完整自定义服务标识列表 */ + SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x07, /* 部分标准服务标识列表 */ + SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x08, /* 部分自定义服务标识列表 */ + SLE_ADV_DATA_TYPE_SERVICE_STRUCTURE_HASH_VALUE = 0x09, /* 服务结构散列值 */ + SLE_ADV_DATA_TYPE_SHORTENED_LOCAL_NAME = 0x0A, /* 设备缩写本地名称 */ + SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME = 0x0B, /* 设备完整本地名称 */ + SLE_ADV_DATA_TYPE_TX_POWER_LEVEL = 0x0C, /* 广播发送功率 */ + SLE_ADV_DATA_TYPE_SLB_COMMUNICATION_DOMAIN = 0x0D, /* SLB通信域域名 */ + SLE_ADV_DATA_TYPE_SLB_MEDIA_ACCESS_LAYER_ID = 0x0E, /* SLB媒体接入层标识 */ + SLE_ADV_DATA_TYPE_EXTENDED = 0xFE, /* 数据类型扩展 */ + SLE_ADV_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF /* 厂商自定义信息 */ +} sle_adv_data_type; + +errcode_t sle_dev_register_cbks(void); +errcode_t sle_uart_server_adv_init(void); + +errcode_t sle_uart_announce_register_cbks(void); +int set_SLE_local_name(const uint8_t *name); // 设置 SLE 本地名称 + +#endif \ No newline at end of file