├── .clang-format ├── .gitattributes ├── .github ├── actions │ └── godot-cache │ │ └── action.yml └── workflows │ └── deploy.yml ├── .gitignore ├── .gitmodules ├── README.md ├── SCsub ├── config.py ├── doc_classes └── SerialPort.xml ├── gdextension_build └── SConstruct ├── register_types.cpp ├── register_types.h ├── serial_port.cpp └── serial_port.h /.clang-format: -------------------------------------------------------------------------------- 1 | # Commented out parameters are those with the same value as base LLVM style. 2 | # We can uncomment them if we want to change their value, or enforce the 3 | # chosen value in case the base style changes (last sync: Clang 14.0). 4 | --- 5 | ### General config, applies to all languages ### 6 | BasedOnStyle: LLVM 7 | AccessModifierOffset: -4 8 | AlignAfterOpenBracket: DontAlign 9 | # AlignArrayOfStructures: None 10 | # AlignConsecutiveMacros: None 11 | # AlignConsecutiveAssignments: None 12 | # AlignConsecutiveBitFields: None 13 | # AlignConsecutiveDeclarations: None 14 | # AlignEscapedNewlines: Right 15 | AlignOperands: DontAlign 16 | AlignTrailingComments: false 17 | # AllowAllArgumentsOnNextLine: true 18 | AllowAllParametersOfDeclarationOnNextLine: false 19 | # AllowShortEnumsOnASingleLine: true 20 | # AllowShortBlocksOnASingleLine: Never 21 | # AllowShortCaseLabelsOnASingleLine: false 22 | # AllowShortFunctionsOnASingleLine: All 23 | # AllowShortLambdasOnASingleLine: All 24 | # AllowShortIfStatementsOnASingleLine: Never 25 | # AllowShortLoopsOnASingleLine: false 26 | # AlwaysBreakAfterDefinitionReturnType: None 27 | # AlwaysBreakAfterReturnType: None 28 | # AlwaysBreakBeforeMultilineStrings: false 29 | # AlwaysBreakTemplateDeclarations: MultiLine 30 | # AttributeMacros: 31 | # - __capability 32 | # BinPackArguments: true 33 | # BinPackParameters: true 34 | # BraceWrapping: 35 | # AfterCaseLabel: false 36 | # AfterClass: false 37 | # AfterControlStatement: Never 38 | # AfterEnum: false 39 | # AfterFunction: false 40 | # AfterNamespace: false 41 | # AfterObjCDeclaration: false 42 | # AfterStruct: false 43 | # AfterUnion: false 44 | # AfterExternBlock: false 45 | # BeforeCatch: false 46 | # BeforeElse: false 47 | # BeforeLambdaBody: false 48 | # BeforeWhile: false 49 | # IndentBraces: false 50 | # SplitEmptyFunction: true 51 | # SplitEmptyRecord: true 52 | # SplitEmptyNamespace: true 53 | # BreakBeforeBinaryOperators: None 54 | # BreakBeforeConceptDeclarations: true 55 | # BreakBeforeBraces: Attach 56 | # BreakBeforeInheritanceComma: false 57 | # BreakInheritanceList: BeforeColon 58 | # BreakBeforeTernaryOperators: true 59 | # BreakConstructorInitializersBeforeComma: false 60 | BreakConstructorInitializers: AfterColon 61 | # BreakStringLiterals: true 62 | ColumnLimit: 0 63 | # CommentPragmas: '^ IWYU pragma:' 64 | # QualifierAlignment: Leave 65 | # CompactNamespaces: false 66 | ConstructorInitializerIndentWidth: 8 67 | ContinuationIndentWidth: 8 68 | Cpp11BracedListStyle: false 69 | # DeriveLineEnding: true 70 | # DerivePointerAlignment: false 71 | # DisableFormat: false 72 | # EmptyLineAfterAccessModifier: Never 73 | # EmptyLineBeforeAccessModifier: LogicalBlock 74 | # ExperimentalAutoDetectBinPacking: false 75 | # PackConstructorInitializers: BinPack 76 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 77 | # AllowAllConstructorInitializersOnNextLine: true 78 | # FixNamespaceComments: true 79 | # ForEachMacros: 80 | # - foreach 81 | # - Q_FOREACH 82 | # - BOOST_FOREACH 83 | # IfMacros: 84 | # - KJ_IF_MAYBE 85 | # IncludeBlocks: Preserve 86 | IncludeCategories: 87 | - Regex: '".*"' 88 | Priority: 1 89 | - Regex: '^<.*\.h>' 90 | Priority: 2 91 | - Regex: '^<.*' 92 | Priority: 3 93 | # IncludeIsMainRegex: '(Test)?$' 94 | # IncludeIsMainSourceRegex: '' 95 | # IndentAccessModifiers: false 96 | IndentCaseLabels: true 97 | # IndentCaseBlocks: false 98 | # IndentGotoLabels: true 99 | # IndentPPDirectives: None 100 | # IndentExternBlock: AfterExternBlock 101 | # IndentRequires: false 102 | IndentWidth: 4 103 | # IndentWrappedFunctionNames: false 104 | # InsertTrailingCommas: None 105 | # JavaScriptQuotes: Leave 106 | # JavaScriptWrapImports: true 107 | KeepEmptyLinesAtTheStartOfBlocks: false 108 | # LambdaBodyIndentation: Signature 109 | # MacroBlockBegin: '' 110 | # MacroBlockEnd: '' 111 | # MaxEmptyLinesToKeep: 1 112 | # NamespaceIndentation: None 113 | # PenaltyBreakAssignment: 2 114 | # PenaltyBreakBeforeFirstCallParameter: 19 115 | # PenaltyBreakComment: 300 116 | # PenaltyBreakFirstLessLess: 120 117 | # PenaltyBreakOpenParenthesis: 0 118 | # PenaltyBreakString: 1000 119 | # PenaltyBreakTemplateDeclaration: 10 120 | # PenaltyExcessCharacter: 1000000 121 | # PenaltyReturnTypeOnItsOwnLine: 60 122 | # PenaltyIndentedWhitespace: 0 123 | # PointerAlignment: Right 124 | # PPIndentWidth: -1 125 | # ReferenceAlignment: Pointer 126 | # ReflowComments: true 127 | # RemoveBracesLLVM: false 128 | # SeparateDefinitionBlocks: Leave 129 | # ShortNamespaceLines: 1 130 | # SortIncludes: CaseSensitive 131 | # SortJavaStaticImport: Before 132 | # SortUsingDeclarations: true 133 | # SpaceAfterCStyleCast: false 134 | # SpaceAfterLogicalNot: false 135 | # SpaceAfterTemplateKeyword: true 136 | # SpaceBeforeAssignmentOperators: true 137 | # SpaceBeforeCaseColon: false 138 | # SpaceBeforeCpp11BracedList: false 139 | # SpaceBeforeCtorInitializerColon: true 140 | # SpaceBeforeInheritanceColon: true 141 | # SpaceBeforeParens: ControlStatements 142 | # SpaceBeforeParensOptions: 143 | # AfterControlStatements: true 144 | # AfterForeachMacros: true 145 | # AfterFunctionDefinitionName: false 146 | # AfterFunctionDeclarationName: false 147 | # AfterIfMacros: true 148 | # AfterOverloadedOperator: false 149 | # BeforeNonEmptyParentheses: false 150 | # SpaceAroundPointerQualifiers: Default 151 | # SpaceBeforeRangeBasedForLoopColon: true 152 | # SpaceInEmptyBlock: false 153 | # SpaceInEmptyParentheses: false 154 | # SpacesBeforeTrailingComments: 1 155 | # SpacesInAngles: Never 156 | # SpacesInConditionalStatement: false 157 | # SpacesInContainerLiterals: true 158 | # SpacesInCStyleCastParentheses: false 159 | ## Godot TODO: We'll want to use a min of 1, but we need to see how to fix 160 | ## our comment capitalization at the same time. 161 | SpacesInLineCommentPrefix: 162 | Minimum: 0 163 | Maximum: -1 164 | # SpacesInParentheses: false 165 | # SpacesInSquareBrackets: false 166 | # SpaceBeforeSquareBrackets: false 167 | # BitFieldColonSpacing: Both 168 | # StatementAttributeLikeMacros: 169 | # - Q_EMIT 170 | # StatementMacros: 171 | # - Q_UNUSED 172 | # - QT_REQUIRE_VERSION 173 | TabWidth: 4 174 | # UseCRLF: false 175 | UseTab: Always 176 | # WhitespaceSensitiveMacros: 177 | # - STRINGIZE 178 | # - PP_STRINGIZE 179 | # - BOOST_PP_STRINGIZE 180 | # - NS_SWIFT_NAME 181 | # - CF_SWIFT_NAME 182 | --- 183 | ### C++ specific config ### 184 | Language: Cpp 185 | Standard: c++17 186 | --- 187 | ### ObjC specific config ### 188 | Language: ObjC 189 | # ObjCBinPackProtocolList: Auto 190 | ObjCBlockIndentWidth: 4 191 | # ObjCBreakBeforeNestedBlockParam: true 192 | # ObjCSpaceAfterProperty: false 193 | # ObjCSpaceBeforeProtocolList: true 194 | --- 195 | ### Java specific config ### 196 | Language: Java 197 | # BreakAfterJavaFieldAnnotations: false 198 | JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax'] 199 | ... 200 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Properly detect languages on Github 2 | *.h linguist-language=cpp 3 | *.inc linguist-language=cpp 4 | thirdparty/* linguist-vendored 5 | 6 | # Normalize EOL for all files that Git considers text files 7 | * text=auto eol=lf 8 | # Except for bat files, which are Windows only files 9 | *.bat eol=crlf 10 | # And some test files where the EOL matters 11 | *.test.txt -text 12 | 13 | # The above only works properly for Git 2.10+, so for older versions 14 | # we need to manually list the binary files we don't want modified. 15 | *.icns binary 16 | *.ico binary 17 | *.jar binary 18 | *.png binary 19 | *.ttf binary 20 | *.tza binary 21 | -------------------------------------------------------------------------------- /.github/actions/godot-cache/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Godot build cache 2 | description: Setup Godot build cache. 3 | inputs: 4 | cache-name: 5 | description: The cache base name (job name by default). 6 | default: "${{github.job}}" 7 | scons-cache: 8 | description: The scons cache path. 9 | default: "${{github.workspace}}/.scons-cache/" 10 | runs: 11 | using: "composite" 12 | steps: 13 | # Upload cache on completion and check it out now 14 | - name: Load .scons_cache directory 15 | uses: actions/cache@v3 16 | with: 17 | path: ${{inputs.scons-cache}} 18 | key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} 19 | restore-keys: | 20 | ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} 21 | ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}} 22 | ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}} 23 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # Action name 2 | name: Deploy 3 | 4 | # 触发条件,这里是新的 tag 被 push 时触发 5 | on: 6 | push: 7 | tags: 8 | # 正则匹配 tag 格式,如 v0.1.0 9 | - "v[0-9]+.[0-9]+.[0-9]+" 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: write 14 | 15 | # 实际工作 16 | jobs: 17 | build-and-upload: 18 | name: Build and Upload 19 | runs-on: ${{ matrix.os }} 20 | 21 | strategy: 22 | fail-fast: false 23 | # 配置编译目标平台,这里是在 Ubuntu, MacOS, Windows 上分别编译 24 | matrix: 25 | include: 26 | - name: Linux (GCC) 27 | platform: linux 28 | arch: x86_64 29 | os: ubuntu-latest 30 | cache-name: linux-x86_64 31 | 32 | - name: Windows (MSVC) 33 | platform: windows 34 | arch: x86_64 35 | os: windows-latest 36 | cache-name: windows-x86_64-msvc 37 | # 执行流程 38 | steps: 39 | # 克隆仓库代码 40 | - name: Clone repository 41 | uses: actions/checkout@v3 42 | with: 43 | submodules: recursive 44 | 45 | # 获取发布版本号 46 | - name: Get the release version from the tag 47 | shell: bash 48 | run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV 49 | 50 | # 设置 Godot 缓存 51 | - name: Setup Godot build cache 52 | uses: ./.github/actions/godot-cache 53 | with: 54 | cache-name: ${{ matrix.cache-name }} 55 | continue-on-error: true 56 | 57 | # 安装依赖库 58 | - name: Install dependencies 59 | if: ${{ matrix.platform == 'linux' }} 60 | run: | 61 | sudo apt-get update -qq 62 | sudo apt-get install -qqq build-essential pkg-config tree 63 | 64 | # 安装 SCons 65 | - name: Set up Python (for SCons) 66 | uses: actions/setup-python@v4 67 | with: 68 | python-version: '3.x' 69 | 70 | - name: Install scons 71 | run: | 72 | python -m pip install scons 73 | 74 | # 构建二进制文件 75 | - name: Build binary files (template_debug) 76 | run: | 77 | scons target=template_debug --sconstruct=gdextension_build/SConstruct 78 | 79 | - name: Build binary files (template_release) 80 | run: | 81 | scons target=template_release --sconstruct=gdextension_build/SConstruct 82 | 83 | # 打包上传二进制文件 84 | - name: Archive files 85 | shell: bash 86 | run: | 87 | addon_name="serialport" 88 | addon_path="gdextension_build/example/addons/$addon_name" 89 | archive_bin_dir="addons/serialport/bin" 90 | 91 | mkdir -p "$addon_path/bin" 92 | mkdir -p "$archive_bin_dir" 93 | 94 | if [ "${{ matrix.platform }}" = "linux" ]; then 95 | addon_file_debug="lib$addon_name.${{ matrix.platform }}.template_debug.${{ matrix.arch }}.so" 96 | addon_file_release="lib$addon_name.${{ matrix.platform }}.template_release.${{ matrix.arch }}.so" 97 | elif [ "${{ matrix.platform }}" = "windows" ]; then 98 | addon_file_debug="$addon_name.${{ matrix.platform }}.template_debug.${{ matrix.arch }}.dll" 99 | addon_file_release="$addon_name.${{ matrix.platform }}.template_release.${{ matrix.arch }}.dll" 100 | fi 101 | 102 | mv $addon_path/bin/$addon_file_debug $archive_bin_dir/$addon_file_debug 103 | mv $addon_path/bin/$addon_file_release $archive_bin_dir/$addon_file_release 104 | 105 | cat>$archive_bin_dir/../$addon_name.gdextension<> $GITHUB_ENV 121 | elif [ "${{ matrix.platform }}" = "windows" ]; then 122 | 7z a "$archive_name.zip" "addons" 123 | echo "ASSET=$archive_name.zip" >> $GITHUB_ENV 124 | fi 125 | 126 | - name: Release files 127 | uses: softprops/action-gh-release@v1 128 | with: 129 | files: | 130 | ${{ env.ASSET }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot .gitignore config 2 | # 3 | # Aims to encompass the most commonly found files that we don't want committed 4 | # to Git, such as compilation output, IDE specific files, etc. 5 | # 6 | # It doesn't cover *all* thirdparty IDE extensions under the sun so if you have 7 | # specific needs covered here, you can add them to: 8 | # .git/info/exclude 9 | # 10 | # Or contribute them to this file if they're common enough that a good number of 11 | # users would benefit from the shared rules. 12 | # 13 | # This file is organized by sections, with subsections ordered alphabetically. 14 | # - Build configuration 15 | # - Godot generated files 16 | # - General build output 17 | # - IDE and tool specific 18 | # - Visual Studio specific 19 | # - OS specific 20 | 21 | ########################### 22 | ### Build configuration ### 23 | ########################### 24 | 25 | /custom.py 26 | misc/hooks/pre-commit-custom-* 27 | 28 | ############################# 29 | ### Godot generated files ### 30 | ############################# 31 | 32 | # Buildsystem 33 | bin/ 34 | *.gen.* 35 | compile_commands.json 36 | platform/windows/godot_res.res 37 | 38 | # Generated by Godot binary 39 | .import/ 40 | /gdextension_interface.h 41 | extension_api.json 42 | logs/ 43 | 44 | # Generated by unit tests 45 | tests/data/*.translation 46 | 47 | ############################ 48 | ### General build output ### 49 | ############################ 50 | 51 | # C/C++ generated 52 | *.a 53 | *.ax 54 | *.d 55 | *.dll 56 | *.lib 57 | *.lo 58 | *.o 59 | *.os 60 | *.ox 61 | *.Plo 62 | *.so 63 | # Binutils tmp linker output of the form "stXXXXXX" where "X" is alphanumeric 64 | st[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9] 65 | 66 | # Python generated 67 | __pycache__/ 68 | *.pyc 69 | 70 | # Documentation 71 | doc/_build/ 72 | 73 | # Android 74 | .gradle/ 75 | local.properties 76 | *.iml 77 | .gradletasknamecache 78 | project.properties 79 | platform/android/java/*/.cxx/ 80 | platform/android/java/*/build/ 81 | platform/android/java/*/libs/ 82 | 83 | # iOS 84 | *.dSYM 85 | 86 | # Web platform 87 | *.bc 88 | platform/web/node_modules/ 89 | 90 | # Misc 91 | *.debug 92 | 93 | ############################# 94 | ### IDE and tool specific ### 95 | ############################# 96 | 97 | # Automake 98 | .deps/* 99 | .dirstamp 100 | 101 | # ccls 102 | .ccls-cache/ 103 | 104 | # clangd 105 | .clangd/ 106 | .cache/ 107 | 108 | # CLion 109 | cmake-build-debug 110 | 111 | # Code::Blocks 112 | *.cbp 113 | *.layout 114 | *.depend 115 | 116 | # CodeLite 117 | *.project 118 | *.workspace 119 | .codelite/ 120 | 121 | # Cppcheck 122 | *.cppcheck 123 | cppcheck-cppcheck-build-dir/ 124 | 125 | # Eclipse CDT 126 | .cproject 127 | .settings/ 128 | *.pydevproject 129 | *.launch 130 | 131 | # Gcov and Lcov code coverage 132 | *.gcno 133 | *.gcda 134 | *.gcov.html 135 | *.func.html 136 | *.func-sort-c.html 137 | *index-sort-f.html 138 | *index-sort-l.html 139 | *index.html 140 | godot.info 141 | amber.png 142 | emerald.png 143 | glass.png 144 | ruby.png 145 | snow.png 146 | updown.png 147 | gcov.css 148 | 149 | # Geany 150 | *.geany 151 | .geanyprj 152 | 153 | # Gprof 154 | gmon.out 155 | 156 | # Jetbrains IDEs 157 | .idea/ 158 | .fleet/ 159 | 160 | # Kate 161 | *.kate-swp 162 | 163 | # Kdevelop 164 | *.kdev4 165 | 166 | # Qt Creator 167 | *.config 168 | *.creator 169 | *.creator.* 170 | *.files 171 | *.includes 172 | *.cflags 173 | *.cxxflags 174 | 175 | # SCons 176 | .sconf_temp 177 | .sconsign*.dblite 178 | .scons_env.json 179 | .scons_node_count 180 | 181 | # Sourcetrail 182 | *.srctrl* 183 | 184 | # Tags 185 | # https://github.com/github/gitignore/blob/master/Global/Tags.gitignore 186 | # Ignore tags created by etags, ctags, gtags (GNU global) and cscope 187 | TAGS 188 | !TAGS/ 189 | tags 190 | *.tags 191 | !tags/ 192 | gtags.files 193 | GTAGS 194 | GRTAGS 195 | GPATH 196 | cscope.files 197 | cscope.out 198 | cscope.in.out 199 | cscope.po.out 200 | 201 | # Vim 202 | *.swo 203 | *.swp 204 | 205 | # Visual Studio Code 206 | .vscode/ 207 | *.code-workspace 208 | .history/ 209 | 210 | # Xcode 211 | xcuserdata/ 212 | *.xcscmblueprint 213 | *.xccheckout 214 | *.xcodeproj/* 215 | 216 | ############################## 217 | ### Visual Studio specific ### 218 | ############################## 219 | 220 | # https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 221 | # Ignore Visual Studio temporary files, build results, and 222 | # files generated by popular Visual Studio add-ons. 223 | 224 | # Actual VS project files we don't use 225 | *.sln 226 | *.vcxproj* 227 | 228 | # User-specific files 229 | *.rsuser 230 | *.suo 231 | *.user 232 | *.userosscache 233 | *.sln.docstates 234 | 235 | # User-specific files (MonoDevelop/Xamarin Studio) 236 | *.userprefs 237 | 238 | # Build results 239 | [Dd]ebug/ 240 | [Dd]ebugPublic/ 241 | [Rr]elease/ 242 | [Rr]eleases/ 243 | x64/ 244 | x86/ 245 | 246 | # Do not ignore x86 folders anywhere under thirdparty libraries 247 | !thirdparty/**/x86/ 248 | 249 | [Ww][Ii][Nn]32/ 250 | [Aa][Rr][Mm]/ 251 | [Aa][Rr][Mm]64/ 252 | bld/ 253 | [Bb]in/ 254 | [Oo]bj/ 255 | [Ll]og/ 256 | [Ll]ogs/ 257 | 258 | # Visual Studio 2015/2017 cache/options directory 259 | .vs/ 260 | 261 | # Visual Studio 2017 auto generated files 262 | Generated\ Files/ 263 | 264 | # Files built by Visual Studio 265 | *_i.c 266 | *_p.c 267 | *_h.h 268 | *.ilk 269 | *.meta 270 | *.obj 271 | *.iobj 272 | *.pch 273 | *.pdb 274 | *.ipdb 275 | *.pgc 276 | *.pgd 277 | *.rsp 278 | *.sbr 279 | *.tlb 280 | *.tli 281 | *.tlh 282 | *.tmp 283 | *.tmp_proj 284 | *_wpftmp.csproj 285 | *.log 286 | *.tlog 287 | *.vspscc 288 | *.vssscc 289 | .builds 290 | *.pidb 291 | *.svclog 292 | *.scc 293 | 294 | # Visual C++ cache files 295 | ipch/ 296 | *.aps 297 | *.ncb 298 | *.opendb 299 | *.opensdf 300 | *.sdf 301 | *.cachefile 302 | *.VC.db 303 | *.VC.VC.opendb 304 | 305 | # Visual Studio profiler 306 | *.psess 307 | *.vsp 308 | *.vspx 309 | *.sap 310 | 311 | # Visual Studio Trace Files 312 | *.e2e 313 | 314 | # ReSharper is a .NET coding add-in 315 | _ReSharper*/ 316 | *.[Rr]e[Ss]harper 317 | *.DotSettings.user 318 | 319 | # Visual Studio cache files 320 | # files ending in .cache can be ignored 321 | *.[Cc]ache 322 | 323 | # Others 324 | ClientBin/ 325 | enc_temp_folder/ 326 | ~$* 327 | *.dbmdl 328 | *.dbproj.schemaview 329 | *.jfm 330 | *.pfx 331 | *.publishsettings 332 | orleans.codegen.cs 333 | 334 | # Backup & report files from converting an old project file 335 | # to a newer Visual Studio version. Backup files are not needed, 336 | # because we have git ;-) 337 | _UpgradeReport_Files/ 338 | Backup*/ 339 | UpgradeLog*.XML 340 | UpgradeLog*.htm 341 | ServiceFabricBackup/ 342 | *.rptproj.bak 343 | 344 | # Hint file for IntelliSense 345 | cpp.hint 346 | 347 | ################### 348 | ### OS specific ### 349 | ################### 350 | 351 | # Linux 352 | *~ 353 | .directory 354 | 355 | # macOS 356 | .DS_Store 357 | __MACOSX 358 | 359 | # Windows 360 | # https://github.com/github/gitignore/blob/main/Global/Windows.gitignore 361 | [Tt]humbs.db 362 | [Tt]humbs.db:encryptable 363 | ehthumbs.db 364 | ehthumbs_vista.db 365 | *.stackdump 366 | [Dd]esktop.ini 367 | $RECYCLE.BIN/ 368 | *.cab 369 | *.msi 370 | *.msix 371 | *.msm 372 | *.msp 373 | *.lnk 374 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "serial"] 2 | path = serial 3 | url = https://github.com/wjwwood/serial.git 4 | [submodule "godot-cpp"] 5 | path = godot-cpp 6 | url = https://github.com/godotengine/godot-cpp.git 7 | [submodule "gdextension_build/example"] 8 | path = gdextension_build/example 9 | url = https://github.com/matrixant/serial_port_example.git 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serial_port 2 | A godot extension support serial port communication. 3 | 4 | > If want build it as a `module`, switch to the `module` branch. 5 | 6 | ## Usage: 7 | 8 | 1. Clone and build the plugin. 9 | 10 | ```bash 11 | git clone -b plugin https://github.com/matrixant/serial_port.git --recursive 12 | cd serial_port 13 | scons --sconstruct=gdextension_build/SConstruct target=template_debug 14 | ``` 15 | > The plugin things will be build to `gdextension_build/example/addons/serialport` directory. 16 | 17 | 2. The `SerialPort` class will add to godot. You can new a SerialPort object and set it's 'port', 'baudrate', 'bytesize' and so on. Then open it and communicate with your serial device. 18 | 19 | 3. There is an example in [serial_port_example](https://github.com/matrixant/serial_port_example/tree/plugin) repo. 20 | 21 | ![example](https://raw.githubusercontent.com/matrixant/serial_port_example/main/screen_shot_0.png) 22 | -------------------------------------------------------------------------------- /SCsub: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | Import("env") 4 | Import("env_modules") 5 | 6 | env_serial = env_modules.Clone() 7 | 8 | # Thirdparty source files 9 | 10 | serial_obj = [] 11 | 12 | serial_dir = "serial/" 13 | serial_sources = ["src/serial.cc"] 14 | 15 | if env["platform"] == "windows": 16 | serial_sources.append("src/impl/win.cc") 17 | serial_sources.append("src/impl/list_ports/list_ports_win.cc") 18 | if env.get("is_msvc", True): 19 | env.Append(LINKFLAGS=["setupapi.lib", "Advapi32.lib"]) 20 | else: 21 | env.Append(LIBS=["setupapi", "Advapi32"]) 22 | elif env["platform"].startswith("linux"): 23 | serial_sources.append("src/impl/unix.cc") 24 | serial_sources.append("src/impl/list_ports/list_ports_linux.cc") 25 | env.Append(LIBS=["rt", "pthread"]) 26 | elif env["platform"] == "osx": 27 | serial_sources.append("src/impl/unix.cc") 28 | serial_sources.append("src/impl/list_ports/list_ports_osx.cc") 29 | print("Build for {0}.".format(env["platform"])) 30 | 31 | serial_sources = [serial_dir + file for file in serial_sources] 32 | 33 | env_serial.Prepend(CPPPATH=[serial_dir + "include"]) 34 | 35 | env_serial = env_serial.Clone() 36 | env_serial.disable_warnings() 37 | env_serial.add_source_files(serial_obj, serial_sources) 38 | env.modules_sources += serial_obj 39 | 40 | # Godot source files 41 | 42 | module_obj = [] 43 | 44 | env_serial.add_source_files(module_obj, "*.cpp") 45 | env.modules_sources += module_obj 46 | 47 | # Needed to force rebuilding the module files when the serial library is updated. 48 | env.Depends(module_obj, serial_obj) 49 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | def can_build(env, platform): 2 | return True 3 | 4 | 5 | def configure(env): 6 | pass 7 | 8 | 9 | def get_doc_classes(): 10 | return [ 11 | "SerialPort", 12 | ] 13 | 14 | 15 | def get_doc_path(): 16 | return "doc_classes" 17 | -------------------------------------------------------------------------------- /doc_classes/SerialPort.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Support serial port communication. 5 | 6 | 7 | The [SerialPort] enables the serial port communication with your serial devices. 8 | [b]Note:[/b] When using on linux, insure you have the promission to operate the serial port. 9 | 10 | 11 | https://github.com/matrixant/serial_port_example 12 | 13 | 14 | 15 | Set serial port name. 16 | 17 | 18 | Set serial baudrate. 19 | 20 | 21 | Set serial byte size. 22 | 23 | 24 | Set serial parity check type. 25 | 26 | 27 | Set serial stop bits. 28 | 29 | 30 | Set serial flow control. 31 | 32 | 33 | 34 | 35 | 36 | Emitted when there is an error. 37 | 38 | 39 | 40 | 41 | Emitted when the serial port opened. 42 | 43 | 44 | 45 | 46 | Emitted when the serial receive any data. 47 | 48 | 49 | 50 | 51 | Emitted when the serial port closed. 52 | 53 | 54 | 55 | 56 | 57 | One byte has 5 bits. 58 | 59 | 60 | One byte has 6 bits. 61 | 62 | 63 | One byte has 7 bits. 64 | 65 | 66 | One byte has 8 bits. 67 | 68 | 69 | No parity check bit. 70 | 71 | 72 | Odd check. 73 | 74 | 75 | Even check. 76 | 77 | 78 | Mark check. 79 | 80 | 81 | Space check. 82 | 83 | 84 | 1 stop bit. 85 | 86 | 87 | 2 stop bits. 88 | 89 | 90 | 1.5 stop bits. 91 | 92 | 93 | No flow control. 94 | 95 | 96 | Software flow control. 97 | 98 | 99 | Hardware flow control. 100 | 101 | 102 | 103 | 104 | 105 | 106 | Returns a [Dictionary] contains the serial ports information, the information key with [code]port[/code] name. 107 | 108 | 109 | 110 | 111 | 112 | 113 | Returns [enum Error] when the monitoring already started, the [code]interval_in_usec[/code] set the monitoring interval. 114 | When start monitoring, a data receive thread will be started. Any data received will emit the [signal data_received] signal. 115 | [b]Example:[/b] 116 | [codeblock] 117 | var serial = SerialPort.new() 118 | 119 | func _ready(): 120 | serial.port = "COM2" 121 | serial.baudrate = 115200 122 | serial.data_received.connect(_on_data_received) 123 | serial.start_monitoring(20000) 124 | serial.open() 125 | 126 | func _on_data_received(data): 127 | print("Receive %d bytes: %s" % [data.size(), data]) 128 | [/codeblock] 129 | 130 | 131 | 132 | 133 | Stop the data monitoring. 134 | 135 | 136 | 137 | 138 | 139 | Whether the serial port is in error, use [method get_last_error] to find the latest error message. 140 | 141 | 142 | 143 | 144 | 145 | 146 | Open the serial port. 147 | 148 | 149 | 150 | 151 | 152 | Whether the port is open. 153 | 154 | 155 | 156 | 157 | Close the serial port. 158 | 159 | 160 | 161 | 162 | 163 | Return the received byte num. 164 | [b]Note:[/b] If you are using [method start_monitoring] to get data, needless to use this method. 165 | 166 | 167 | 168 | 169 | 170 | Block until there is serial data to read or [code]timeout[/code](Set by [method set_timeout]) number of milliseconds have elapsed. 171 | The return value is true when the function exits with the port in a readable state, false otherwise (due to timeout or select interruption). 172 | [b]Note:[/b] Not implemented on Windows. 173 | 174 | 175 | 176 | 177 | 178 | 179 | Block for a period of time corresponding to the transmission time of count characters at present serial settings. 180 | This may be used in conjunction with [method wait_readable] to read larger blocks of data from the port. 181 | [b]Note:[/b] Not implemented on Windows. 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | Read string from the serial port. When expect an utf-8 string, let the [code]utf8_encoding[/code] be [code]true[/code]. The size is the maximum length of the string. 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | Write string to the serial port. When expect an utf-8 string, let the [code]utf8_encoding[/code] be [code]true[/code]. 198 | 199 | 200 | 201 | 202 | 203 | 204 | Read raw byte data from the serial port. 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | Write raw byte data to the serial port. 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | Read a string line end with the [code]eol[/code] from the serial port. When expect an utf-8 string, let the [code]utf8_encoding[/code] be [code]true[/code]. The max_len is the maximum length of the string. 222 | [b]Note:[/b] The default [code]eol[/code] is [code]\n[/code]. 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | Read multi lines of string which end with the [code]eol[/code] from the serial port. When expect an utf-8 string, let the [code]utf8_encoding[/code] be [code]true[/code]. The max_len is the maximum length of the total lines of string. 232 | [b]Note:[/b] The default [code]eol[/code] is [code]\n[/code]. 233 | 234 | 235 | 236 | 237 | 238 | 239 | Set the serial port name. 240 | 241 | 242 | 243 | 244 | 245 | Get the serial port name. 246 | 247 | 248 | 249 | 250 | -------------------------------------------------------------------------------- /gdextension_build/SConstruct: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | env = SConscript("godot-cpp/SConstruct") 6 | 7 | env.Append(CPPDEFINES=["GDEXTENSION"]) 8 | env.Append(CPPPATH=[".", "serial/include"]) 9 | 10 | addon_sources = [ 11 | "register_types.cpp", 12 | "serial_port.cpp" 13 | ] 14 | 15 | serial_dir = "serial/" 16 | serial_sources = ["src/serial.cc"] 17 | 18 | if env["platform"] == "windows": 19 | serial_sources.append("src/impl/win.cc") 20 | serial_sources.append("src/impl/list_ports/list_ports_win.cc") 21 | if env.get("is_msvc", True): 22 | env.Append(LINKFLAGS=["setupapi.lib", "Advapi32.lib"]) 23 | else: 24 | env.Append(LIBS=["setupapi", "Advapi32"]) 25 | elif env["platform"].startswith("linux"): 26 | serial_sources.append("src/impl/unix.cc") 27 | serial_sources.append("src/impl/list_ports/list_ports_linux.cc") 28 | env.Append(LIBS=["rt", "pthread"]) 29 | elif env["platform"] == "macos": 30 | serial_sources.append("src/impl/unix.cc") 31 | serial_sources.append("src/impl/list_ports/list_ports_osx.cc") 32 | print("Build for {0}.".format(env["platform"])) 33 | 34 | serial_sources = [serial_dir + file for file in serial_sources] 35 | 36 | addon_sources.append(serial_sources) 37 | addon_name = "serialport" 38 | addon_path = "gdextension_build/example/addons/{}".format(addon_name) 39 | if env["platform"] == "macos": 40 | library = env.SharedLibrary( 41 | "{0}/bin/lib{1}.{2}.{3}.dylib".format( 42 | addon_path, 43 | addon_name, 44 | env["platform"], 45 | env["target"] 46 | ), 47 | source=addon_sources, 48 | ) 49 | elif env["platform"] == "windows": 50 | library = env.SharedLibrary( 51 | "{0}/bin/{1}{2}{3}".format( 52 | addon_path, 53 | addon_name, 54 | env["suffix"], 55 | env["SHLIBSUFFIX"] 56 | ), 57 | source=addon_sources, 58 | ) 59 | else: 60 | library = env.SharedLibrary( 61 | "{0}/bin/lib{1}{2}{3}".format( 62 | addon_path, 63 | addon_name, 64 | env["suffix"], 65 | env["SHLIBSUFFIX"] 66 | ), 67 | source=addon_sources, 68 | ) 69 | 70 | Default(library) 71 | -------------------------------------------------------------------------------- /register_types.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* register_types.cpp */ 3 | /*************************************************************************/ 4 | /* This file is part of: */ 5 | /* GODOT ENGINE */ 6 | /* https://godotengine.org */ 7 | /*************************************************************************/ 8 | /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ 9 | /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ 10 | /* */ 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ 12 | /* a copy of this software and associated documentation files (the */ 13 | /* "Software"), to deal in the Software without restriction, including */ 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ 16 | /* permit persons to whom the Software is furnished to do so, subject to */ 17 | /* the following conditions: */ 18 | /* */ 19 | /* The above copyright notice and this permission notice shall be */ 20 | /* included in all copies or substantial portions of the Software. */ 21 | /* */ 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 29 | /*************************************************************************/ 30 | 31 | #include "register_types.h" 32 | 33 | #include "serial_port.h" 34 | 35 | void initialize_serial_port_module(ModuleInitializationLevel p_level) { 36 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 37 | return; 38 | } 39 | 40 | GDREGISTER_CLASS(SerialPort); 41 | } 42 | 43 | void uninitialize_serial_port_module(ModuleInitializationLevel p_level) { 44 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 45 | return; 46 | } 47 | } 48 | 49 | #ifdef GDEXTENSION 50 | 51 | #include 52 | #include 53 | 54 | extern "C" { 55 | // Initialization. 56 | GDExtensionBool GDE_EXPORT serialport_lib_init(const GDExtensionInterfaceGetProcAddress p_get_proc_addr, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { 57 | godot::GDExtensionBinding::InitObject init_obj(p_get_proc_addr, p_library, r_initialization); 58 | 59 | init_obj.register_initializer(initialize_serial_port_module); 60 | init_obj.register_terminator(uninitialize_serial_port_module); 61 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); 62 | 63 | return init_obj.init(); 64 | } 65 | } // extern "C" 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /register_types.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* register_types.h */ 3 | /*************************************************************************/ 4 | /* This file is part of: */ 5 | /* GODOT ENGINE */ 6 | /* https://godotengine.org */ 7 | /*************************************************************************/ 8 | /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ 9 | /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ 10 | /* */ 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ 12 | /* a copy of this software and associated documentation files (the */ 13 | /* "Software"), to deal in the Software without restriction, including */ 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ 16 | /* permit persons to whom the Software is furnished to do so, subject to */ 17 | /* the following conditions: */ 18 | /* */ 19 | /* The above copyright notice and this permission notice shall be */ 20 | /* included in all copies or substantial portions of the Software. */ 21 | /* */ 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 29 | /*************************************************************************/ 30 | 31 | #ifndef SERIAL_PORT_REGISTER_TYPES_H 32 | #define SERIAL_PORT_REGISTER_TYPES_H 33 | 34 | #ifdef GDEXTENSION 35 | #include 36 | using namespace godot; 37 | #else 38 | #include "core/object/class_db.h" 39 | #include "modules/register_module_types.h" 40 | #endif 41 | 42 | void initialize_serial_port_module(ModuleInitializationLevel p_level); 43 | void uninitialize_serial_port_module(ModuleInitializationLevel p_level); 44 | 45 | #endif // SERIAL_PORT_REGISTER_TYPES_H 46 | -------------------------------------------------------------------------------- /serial_port.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* serial_port.cpp */ 3 | /*************************************************************************/ 4 | /* This file is part of: */ 5 | /* GODOT ENGINE */ 6 | /* https://godotengine.org */ 7 | /*************************************************************************/ 8 | /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ 9 | /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ 10 | /* */ 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ 12 | /* a copy of this software and associated documentation files (the */ 13 | /* "Software"), to deal in the Software without restriction, including */ 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ 16 | /* permit persons to whom the Software is furnished to do so, subject to */ 17 | /* the following conditions: */ 18 | /* */ 19 | /* The above copyright notice and this permission notice shall be */ 20 | /* included in all copies or substantial portions of the Software. */ 21 | /* */ 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 29 | /*************************************************************************/ 30 | 31 | #include "serial_port.h" 32 | 33 | #ifdef GDEXTENSION 34 | #include 35 | #include 36 | 37 | using namespace godot; 38 | #else 39 | #include "core/object/class_db.h" 40 | #include "core/os/memory.h" 41 | #include "core/os/os.h" 42 | #endif 43 | #include 44 | 45 | using namespace std::chrono; 46 | 47 | void SerialPort::_data_received(const PackedByteArray &buf) { 48 | emit_signal("data_received", buf); 49 | } 50 | 51 | SerialPort::SerialPort(const String &port, uint32_t baudrate, uint32_t timeout, ByteSize bytesize, Parity parity, StopBits stopbits, FlowControl flowcontrol) { 52 | serial = new Serial(port.ascii().get_data(), 53 | baudrate, Timeout::simpleTimeout(timeout), bytesize_t(bytesize), parity_t(parity), stopbits_t(stopbits), flowcontrol_t(flowcontrol)); 54 | } 55 | 56 | SerialPort::~SerialPort() { 57 | close(); 58 | stop_monitoring(); 59 | delete serial; 60 | } 61 | 62 | Dictionary SerialPort::list_ports() { 63 | std::vector ports_info = serial::list_ports(); 64 | 65 | Dictionary info_dict; 66 | for (PortInfo port : ports_info) { 67 | Dictionary info; 68 | info["desc"] = port.description.c_str(); 69 | info["hw_id"] = port.hardware_id.c_str(); 70 | info_dict[port.port.c_str()] = Variant(info); 71 | } 72 | 73 | return info_dict; 74 | } 75 | 76 | void SerialPort::_on_error(const String &where, const String &what) { 77 | fine_working = false; 78 | error_message = "[" + get_port() + "] Error at " + where + ": " + what; 79 | // ERR_FAIL_MSG(error_message); 80 | emit_signal("got_error", where, what); 81 | } 82 | 83 | Error SerialPort::start_monitoring(uint64_t interval_in_usec) { 84 | ERR_FAIL_COND_V_MSG(!monitoring_should_exit, ERR_ALREADY_IN_USE, "Monitor already started."); 85 | stop_monitoring(); 86 | monitoring_should_exit = false; 87 | monitoring_interval = interval_in_usec; 88 | thread = std::thread(_thread_func, this); 89 | if (is_open()) { 90 | fine_working = true; 91 | } else { 92 | fine_working = false; 93 | } 94 | 95 | return OK; 96 | } 97 | 98 | void SerialPort::stop_monitoring() { 99 | monitoring_should_exit = true; 100 | if (thread.joinable()) { 101 | thread.join(); 102 | } 103 | } 104 | 105 | void SerialPort::_thread_func(void *p_user_data) { 106 | SerialPort *serial_port = static_cast(p_user_data); 107 | while (!serial_port->monitoring_should_exit) { 108 | time_point time_start = system_clock::now(); 109 | 110 | if (serial_port->fine_working) { 111 | if (serial_port->is_open() && serial_port->available() > 0) { 112 | serial_port->call_deferred("_data_received", serial_port->read_raw(serial_port->available())); 113 | } 114 | } 115 | time_t time_elapsed = duration_cast(system_clock::now() - time_start).count(); 116 | if (time_elapsed < serial_port->monitoring_interval) { 117 | std::this_thread::sleep_for(microseconds(serial_port->monitoring_interval - time_elapsed)); 118 | } 119 | } 120 | } 121 | 122 | Error SerialPort::open(String port) { 123 | error_message = ""; 124 | try { 125 | if (serial->isOpen()) { 126 | close(); 127 | } 128 | if (!port.is_empty()) { 129 | set_port(port); 130 | } 131 | serial->open(); 132 | } catch (IOException &e) { 133 | _on_error(__FUNCTION__, e.what()); 134 | return ERR_CANT_OPEN; 135 | } catch (SerialException &e) { 136 | _on_error(__FUNCTION__, e.what()); 137 | return ERR_ALREADY_IN_USE; 138 | } catch (std::invalid_argument &e) { 139 | _on_error(__FUNCTION__, e.what()); 140 | return ERR_INVALID_PARAMETER; 141 | } catch (...) { 142 | _on_error(__FUNCTION__, "Unknown error"); 143 | return FAILED; 144 | } 145 | 146 | fine_working = true; 147 | emit_signal("opened", port); 148 | return OK; 149 | } 150 | 151 | bool SerialPort::is_open() const { 152 | return serial->isOpen(); 153 | } 154 | 155 | void SerialPort::close() { 156 | try { 157 | serial->close(); 158 | } catch (IOException &e) { 159 | _on_error(__FUNCTION__, e.what()); 160 | } catch (SerialException &e) { 161 | _on_error(__FUNCTION__, e.what()); 162 | } catch (...) { 163 | _on_error(__FUNCTION__, "Unknown error"); 164 | } 165 | 166 | fine_working = false; 167 | emit_signal("closed", serial->getPort().c_str()); 168 | } 169 | 170 | size_t SerialPort::available() { 171 | try { 172 | return serial->available(); 173 | } catch (IOException &e) { 174 | _on_error(__FUNCTION__, e.what()); 175 | } catch (SerialException &e) { 176 | _on_error(__FUNCTION__, e.what()); 177 | } catch (...) { 178 | _on_error(__FUNCTION__, "Unknown error"); 179 | } 180 | 181 | return 0; 182 | } 183 | 184 | bool SerialPort::wait_readable() { 185 | try { 186 | return serial->waitReadable(); 187 | } catch (IOException &e) { 188 | _on_error(__FUNCTION__, e.what()); 189 | } catch (SerialException &e) { 190 | _on_error(__FUNCTION__, e.what()); 191 | } catch (...) { 192 | _on_error(__FUNCTION__, "Unknown error"); 193 | } 194 | 195 | return false; 196 | } 197 | 198 | void SerialPort::wait_byte_times(size_t count) { 199 | try { 200 | serial->waitByteTimes(count); 201 | } catch (IOException &e) { 202 | _on_error(__FUNCTION__, e.what()); 203 | } catch (SerialException &e) { 204 | _on_error(__FUNCTION__, e.what()); 205 | } catch (...) { 206 | _on_error(__FUNCTION__, "Unknown error"); 207 | } 208 | } 209 | 210 | PackedByteArray SerialPort::read_raw(size_t size) { 211 | PackedByteArray raw; 212 | std::vector buf_temp; 213 | try { 214 | size_t bytes_read = serial->read(buf_temp, size); 215 | if (bytes_read > 0 && raw.resize(bytes_read) == OK) { 216 | memcpy(raw.ptrw(), (const char *)buf_temp.data(), bytes_read); 217 | } 218 | } catch (PortNotOpenedException &e) { 219 | _on_error(__FUNCTION__, e.what()); 220 | } catch (IOException &e) { 221 | _on_error(__FUNCTION__, e.what()); 222 | } catch (SerialException &e) { 223 | _on_error(__FUNCTION__, e.what()); 224 | } catch (...) { 225 | _on_error(__FUNCTION__, "Unknown error"); 226 | } 227 | 228 | return raw; 229 | } 230 | 231 | String SerialPort::read_str(size_t size, bool utf8_encoding) { 232 | try { 233 | String str; 234 | std::vector buf_temp; 235 | size_t bytes_read = serial->read(buf_temp, size); 236 | buf_temp.insert(buf_temp.end(), '\0'); 237 | if (bytes_read > 0) { 238 | if (utf8_encoding) { 239 | str.parse_utf8((const char *)buf_temp.data(), bytes_read); 240 | } else { 241 | str = (const char *)buf_temp.data(); 242 | } 243 | } 244 | return str; 245 | } catch (PortNotOpenedException &e) { 246 | _on_error(__FUNCTION__, e.what()); 247 | } catch (IOException &e) { 248 | _on_error(__FUNCTION__, e.what()); 249 | } catch (SerialException &e) { 250 | _on_error(__FUNCTION__, e.what()); 251 | } catch (...) { 252 | _on_error(__FUNCTION__, "Unknown error"); 253 | } 254 | 255 | return ""; 256 | } 257 | 258 | size_t SerialPort::write_raw(const PackedByteArray &data) { 259 | try { 260 | return serial->write(data.ptr(), data.size()); 261 | } catch (PortNotOpenedException &e) { 262 | _on_error(__FUNCTION__, e.what()); 263 | } catch (IOException &e) { 264 | _on_error(__FUNCTION__, e.what()); 265 | } catch (SerialException &e) { 266 | _on_error(__FUNCTION__, e.what()); 267 | } catch (...) { 268 | _on_error(__FUNCTION__, "Unknown error"); 269 | } 270 | 271 | return 0; 272 | } 273 | 274 | size_t SerialPort::write_str(const String &data, bool utf8_encoding) { 275 | try { 276 | if (utf8_encoding) { 277 | CharString str = data.utf8(); 278 | return serial->write((const uint8_t *)(str.get_data()), str.length()); 279 | } else { 280 | CharString str = data.ascii(); 281 | return serial->write((const uint8_t *)(str.get_data()), str.length()); 282 | } 283 | } catch (PortNotOpenedException &e) { 284 | _on_error(__FUNCTION__, e.what()); 285 | } catch (IOException &e) { 286 | _on_error(__FUNCTION__, e.what()); 287 | } catch (SerialException &e) { 288 | _on_error(__FUNCTION__, e.what()); 289 | } catch (...) { 290 | _on_error(__FUNCTION__, "Unknown error"); 291 | } 292 | 293 | return 0; 294 | } 295 | 296 | String SerialPort::read_line(size_t max_length, String eol, bool utf8_encoding) { 297 | try { 298 | if (utf8_encoding) { 299 | String str; 300 | str.parse_utf8(serial->readline(max_length, eol.utf8().get_data()).c_str()); 301 | return str; 302 | } else { 303 | return serial->readline(max_length, eol.ascii().get_data()).c_str(); 304 | } 305 | } catch (PortNotOpenedException &e) { 306 | _on_error(__FUNCTION__, e.what()); 307 | } catch (IOException &e) { 308 | _on_error(__FUNCTION__, e.what()); 309 | } catch (SerialException &e) { 310 | _on_error(__FUNCTION__, e.what()); 311 | } catch (...) { 312 | _on_error(__FUNCTION__, "Unknown error"); 313 | } 314 | 315 | return ""; 316 | } 317 | 318 | PackedStringArray SerialPort::read_lines(size_t max_length, String eol, bool utf8_encoding) { 319 | try { 320 | PackedStringArray lines; 321 | if (utf8_encoding) { 322 | for (std::string line : serial->readlines(max_length, eol.utf8().get_data())) { 323 | String str; 324 | str.parse_utf8(line.c_str()); 325 | lines.append(str); 326 | } 327 | return lines; 328 | } else { 329 | for (std::string line : serial->readlines(max_length, eol.utf8().get_data())) { 330 | lines.append(line.c_str()); 331 | } 332 | return lines; 333 | } 334 | } catch (PortNotOpenedException &e) { 335 | _on_error(__FUNCTION__, e.what()); 336 | } catch (IOException &e) { 337 | _on_error(__FUNCTION__, e.what()); 338 | } catch (SerialException &e) { 339 | _on_error(__FUNCTION__, e.what()); 340 | } catch (...) { 341 | _on_error(__FUNCTION__, "Unknown error"); 342 | } 343 | 344 | return PackedStringArray(); 345 | } 346 | 347 | Error SerialPort::set_port(const String &port) { 348 | try { 349 | serial->setPort(port.ascii().get_data()); 350 | return OK; 351 | } catch (IOException &e) { 352 | _on_error(__FUNCTION__, e.what()); 353 | return ERR_CANT_OPEN; 354 | } catch (SerialException &e) { 355 | _on_error(__FUNCTION__, e.what()); 356 | return ERR_ALREADY_IN_USE; 357 | } catch (std::invalid_argument &e) { 358 | _on_error(__FUNCTION__, e.what()); 359 | return ERR_INVALID_PARAMETER; 360 | } catch (...) { 361 | _on_error(__FUNCTION__, "Unknown error"); 362 | return FAILED; 363 | } 364 | 365 | return OK; 366 | } 367 | 368 | String SerialPort::get_port() const { 369 | return serial->getPort().c_str(); 370 | } 371 | 372 | Error SerialPort::set_timeout(uint32_t timeout) { 373 | serial->setTimeout(Timeout::max(), timeout, 0, timeout, 0); 374 | return OK; 375 | } 376 | 377 | uint32_t SerialPort::get_timeout() const { 378 | return serial->getTimeout().read_timeout_constant; 379 | } 380 | 381 | Error SerialPort::set_baudrate(uint32_t baudrate) { 382 | try { 383 | serial->setBaudrate(baudrate); 384 | return OK; 385 | } catch (IOException &e) { 386 | _on_error(__FUNCTION__, e.what()); 387 | } catch (std::invalid_argument &e) { 388 | _on_error(__FUNCTION__, e.what()); 389 | } catch (...) { 390 | _on_error(__FUNCTION__, "Unknown error"); 391 | } 392 | 393 | return FAILED; 394 | } 395 | 396 | uint32_t SerialPort::get_baudrate() const { 397 | return serial->getBaudrate(); 398 | } 399 | 400 | Error SerialPort::set_bytesize(ByteSize bytesize) { 401 | try { 402 | serial->setBytesize(bytesize_t(bytesize)); 403 | return OK; 404 | } catch (IOException &e) { 405 | _on_error(__FUNCTION__, e.what()); 406 | } catch (std::invalid_argument &e) { 407 | _on_error(__FUNCTION__, e.what()); 408 | } catch (...) { 409 | _on_error(__FUNCTION__, "Unknown error"); 410 | } 411 | 412 | return FAILED; 413 | } 414 | 415 | SerialPort::ByteSize SerialPort::get_bytesize() const { 416 | return ByteSize(serial->getBytesize()); 417 | } 418 | 419 | Error SerialPort::set_parity(Parity parity) { 420 | try { 421 | serial->setParity(parity_t(parity)); 422 | return OK; 423 | } catch (IOException &e) { 424 | _on_error(__FUNCTION__, e.what()); 425 | } catch (std::invalid_argument &e) { 426 | _on_error(__FUNCTION__, e.what()); 427 | } catch (...) { 428 | _on_error(__FUNCTION__, "Unknown error"); 429 | } 430 | 431 | return FAILED; 432 | } 433 | 434 | SerialPort::Parity SerialPort::get_parity() const { 435 | return Parity(serial->getParity()); 436 | } 437 | 438 | Error SerialPort::set_stopbits(StopBits stopbits) { 439 | try { 440 | serial->setStopbits(stopbits_t(stopbits)); 441 | return OK; 442 | } catch (IOException &e) { 443 | _on_error(__FUNCTION__, e.what()); 444 | } catch (std::invalid_argument &e) { 445 | _on_error(__FUNCTION__, e.what()); 446 | } catch (...) { 447 | _on_error(__FUNCTION__, "Unknown error"); 448 | } 449 | 450 | return FAILED; 451 | } 452 | 453 | SerialPort::StopBits SerialPort::get_stopbits() const { 454 | return StopBits(serial->getStopbits()); 455 | } 456 | 457 | Error SerialPort::set_flowcontrol(FlowControl flowcontrol) { 458 | try { 459 | serial->setFlowcontrol(flowcontrol_t(flowcontrol)); 460 | return OK; 461 | } catch (IOException &e) { 462 | _on_error(__FUNCTION__, e.what()); 463 | } catch (std::invalid_argument &e) { 464 | _on_error(__FUNCTION__, e.what()); 465 | } catch (...) { 466 | _on_error(__FUNCTION__, "Unknown error"); 467 | } 468 | 469 | return FAILED; 470 | } 471 | 472 | SerialPort::FlowControl SerialPort::get_flowcontrol() const { 473 | return FlowControl(serial->getFlowcontrol()); 474 | } 475 | 476 | Error SerialPort::flush() { 477 | try { 478 | serial->flush(); 479 | return OK; 480 | } catch (PortNotOpenedException &e) { 481 | _on_error(__FUNCTION__, e.what()); 482 | } catch (...) { 483 | _on_error(__FUNCTION__, "Unknown error"); 484 | } 485 | 486 | return FAILED; 487 | } 488 | 489 | Error SerialPort::flush_input() { 490 | try { 491 | serial->flushInput(); 492 | return OK; 493 | } catch (PortNotOpenedException &e) { 494 | _on_error(__FUNCTION__, e.what()); 495 | } catch (...) { 496 | _on_error(__FUNCTION__, "Unknown error"); 497 | } 498 | 499 | return FAILED; 500 | } 501 | 502 | Error SerialPort::flush_output() { 503 | try { 504 | serial->flushOutput(); 505 | return OK; 506 | } catch (PortNotOpenedException &e) { 507 | _on_error(__FUNCTION__, e.what()); 508 | } catch (...) { 509 | _on_error(__FUNCTION__, "Unknown error"); 510 | } 511 | 512 | return FAILED; 513 | } 514 | 515 | Error SerialPort::send_break(int duration) { 516 | try { 517 | serial->sendBreak(duration); 518 | return OK; 519 | } catch (IOException &e) { 520 | _on_error(__FUNCTION__, e.what()); 521 | } catch (PortNotOpenedException &e) { 522 | _on_error(__FUNCTION__, e.what()); 523 | } catch (...) { 524 | _on_error(__FUNCTION__, "Unknown error"); 525 | } 526 | 527 | return FAILED; 528 | } 529 | 530 | Error SerialPort::set_break(bool level) { 531 | try { 532 | serial->setBreak(level); 533 | return OK; 534 | } catch (SerialException &e) { 535 | _on_error(__FUNCTION__, e.what()); 536 | } catch (PortNotOpenedException &e) { 537 | _on_error(__FUNCTION__, e.what()); 538 | } catch (...) { 539 | _on_error(__FUNCTION__, "Unknown error"); 540 | } 541 | 542 | return FAILED; 543 | } 544 | 545 | Error SerialPort::set_rts(bool level) { 546 | try { 547 | serial->setRTS(level); 548 | return OK; 549 | } catch (SerialException &e) { 550 | _on_error(__FUNCTION__, e.what()); 551 | } catch (PortNotOpenedException &e) { 552 | _on_error(__FUNCTION__, e.what()); 553 | } catch (...) { 554 | _on_error(__FUNCTION__, "Unknown error"); 555 | } 556 | 557 | return FAILED; 558 | } 559 | 560 | Error SerialPort::set_dtr(bool level) { 561 | try { 562 | serial->setDTR(level); 563 | return OK; 564 | } catch (SerialException &e) { 565 | _on_error(__FUNCTION__, e.what()); 566 | } catch (PortNotOpenedException &e) { 567 | _on_error(__FUNCTION__, e.what()); 568 | } catch (...) { 569 | _on_error(__FUNCTION__, "Unknown error"); 570 | } 571 | return FAILED; 572 | } 573 | 574 | bool SerialPort::wait_for_change() { 575 | try { 576 | return serial->waitForChange(); 577 | } catch (SerialException &e) { 578 | _on_error(__FUNCTION__, e.what()); 579 | } catch (PortNotOpenedException &e) { 580 | _on_error(__FUNCTION__, e.what()); 581 | } catch (...) { 582 | _on_error(__FUNCTION__, "Unknown error"); 583 | } 584 | 585 | return false; 586 | } 587 | 588 | bool SerialPort::get_cts() { 589 | try { 590 | return serial->getCTS(); 591 | } catch (IOException &e) { 592 | _on_error(__FUNCTION__, e.what()); 593 | } catch (SerialException &e) { 594 | _on_error(__FUNCTION__, e.what()); 595 | } catch (PortNotOpenedException &e) { 596 | _on_error(__FUNCTION__, e.what()); 597 | } catch (...) { 598 | _on_error(__FUNCTION__, "Unknown error"); 599 | } 600 | 601 | return false; 602 | } 603 | 604 | bool SerialPort::get_dsr() { 605 | try { 606 | return serial->getDSR(); 607 | } catch (IOException &e) { 608 | _on_error(__FUNCTION__, e.what()); 609 | } catch (SerialException &e) { 610 | _on_error(__FUNCTION__, e.what()); 611 | } catch (PortNotOpenedException &e) { 612 | _on_error(__FUNCTION__, e.what()); 613 | } catch (...) { 614 | _on_error(__FUNCTION__, "Unknown error"); 615 | } 616 | 617 | return false; 618 | } 619 | 620 | bool SerialPort::get_ri() { 621 | try { 622 | return serial->getRI(); 623 | } catch (IOException &e) { 624 | _on_error(__FUNCTION__, e.what()); 625 | } catch (SerialException &e) { 626 | _on_error(__FUNCTION__, e.what()); 627 | } catch (PortNotOpenedException &e) { 628 | _on_error(__FUNCTION__, e.what()); 629 | } catch (...) { 630 | _on_error(__FUNCTION__, "Unknown error"); 631 | } 632 | 633 | return false; 634 | } 635 | 636 | bool SerialPort::get_cd() { 637 | try { 638 | return serial->getCD(); 639 | } catch (IOException &e) { 640 | _on_error(__FUNCTION__, e.what()); 641 | } catch (SerialException &e) { 642 | _on_error(__FUNCTION__, e.what()); 643 | } catch (PortNotOpenedException &e) { 644 | _on_error(__FUNCTION__, e.what()); 645 | } catch (...) { 646 | _on_error(__FUNCTION__, "Unknown error"); 647 | } 648 | 649 | return false; 650 | } 651 | 652 | String SerialPort::_to_string() const { 653 | Dictionary ser_info; 654 | ser_info["port"] = get_port(); 655 | ser_info["baudrate"] = get_baudrate(); 656 | ser_info["byte_size"] = get_bytesize(); 657 | ser_info["parity"] = get_parity(); 658 | ser_info["stop_bits"] = get_stopbits(); 659 | 660 | return String("[SerialPort: {_}]").format(ser_info); 661 | } 662 | 663 | void SerialPort::_bind_methods() { 664 | ClassDB::bind_static_method("SerialPort", D_METHOD("list_ports"), &SerialPort::list_ports); 665 | 666 | ClassDB::bind_method(D_METHOD("_data_received", "data"), &SerialPort::_data_received); 667 | ClassDB::bind_method(D_METHOD("is_in_error"), &SerialPort::is_in_error); 668 | ClassDB::bind_method(D_METHOD("get_last_error"), &SerialPort::get_last_error); 669 | 670 | ClassDB::bind_method(D_METHOD("start_monitoring", "interval_in_usec"), &SerialPort::start_monitoring, DEFVAL(10000)); 671 | ClassDB::bind_method(D_METHOD("stop_monitoring"), &SerialPort::stop_monitoring); 672 | 673 | ClassDB::bind_method(D_METHOD("open", "port"), &SerialPort::open, DEFVAL("")); 674 | ClassDB::bind_method(D_METHOD("is_open"), &SerialPort::is_open); 675 | ClassDB::bind_method(D_METHOD("close"), &SerialPort::close); 676 | 677 | ClassDB::bind_method(D_METHOD("available"), &SerialPort::available); 678 | ClassDB::bind_method(D_METHOD("wait_readable"), &SerialPort::wait_readable); 679 | ClassDB::bind_method(D_METHOD("wait_byte_times", "count"), &SerialPort::wait_byte_times); 680 | ClassDB::bind_method(D_METHOD("read_str", "size", "utf8_encoding"), &SerialPort::read_str, DEFVAL(1), DEFVAL(false)); 681 | ClassDB::bind_method(D_METHOD("write_str", "content", "utf8_encoding"), &SerialPort::write_str, DEFVAL(false)); 682 | ClassDB::bind_method(D_METHOD("read_raw", "size"), &SerialPort::read_raw, DEFVAL(1)); 683 | ClassDB::bind_method(D_METHOD("write_raw", "data"), &SerialPort::write_raw); 684 | ClassDB::bind_method(D_METHOD("read_line", "max_len", "eol", "utf8_encoding"), &SerialPort::read_line, DEFVAL(65535), DEFVAL("\n"), DEFVAL(false)); 685 | ClassDB::bind_method(D_METHOD("read_lines", "max_len", "eol", "utf8_encoding"), &SerialPort::read_lines, DEFVAL(65535), DEFVAL("\n"), DEFVAL(false)); 686 | 687 | ClassDB::bind_method(D_METHOD("set_port", "port"), &SerialPort::set_port); 688 | ClassDB::bind_method(D_METHOD("get_port"), &SerialPort::get_port); 689 | ClassDB::bind_method(D_METHOD("set_baudrate", "baudrate"), &SerialPort::set_baudrate); 690 | ClassDB::bind_method(D_METHOD("get_baudrate"), &SerialPort::get_baudrate); 691 | ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &SerialPort::set_timeout); 692 | ClassDB::bind_method(D_METHOD("get_timeout"), &SerialPort::get_timeout); 693 | ClassDB::bind_method(D_METHOD("set_bytesize", "bytesize"), &SerialPort::set_bytesize); 694 | ClassDB::bind_method(D_METHOD("get_bytesize"), &SerialPort::get_bytesize); 695 | ClassDB::bind_method(D_METHOD("set_parity", "parity"), &SerialPort::set_parity); 696 | ClassDB::bind_method(D_METHOD("get_parity"), &SerialPort::get_parity); 697 | ClassDB::bind_method(D_METHOD("set_stopbits", "stopbits"), &SerialPort::set_stopbits); 698 | ClassDB::bind_method(D_METHOD("get_stopbits"), &SerialPort::get_stopbits); 699 | ClassDB::bind_method(D_METHOD("set_flowcontrol", "flowcontrol"), &SerialPort::set_flowcontrol); 700 | ClassDB::bind_method(D_METHOD("get_flowcontrol"), &SerialPort::get_flowcontrol); 701 | 702 | ClassDB::bind_method(D_METHOD("flush"), &SerialPort::flush); 703 | ClassDB::bind_method(D_METHOD("flush_input"), &SerialPort::flush_input); 704 | ClassDB::bind_method(D_METHOD("flush_output"), &SerialPort::flush_output); 705 | ClassDB::bind_method(D_METHOD("send_break", "duration"), &SerialPort::send_break); 706 | ClassDB::bind_method(D_METHOD("set_break", "level"), &SerialPort::set_break, DEFVAL(true)); 707 | ClassDB::bind_method(D_METHOD("set_rts", "level"), &SerialPort::set_rts, DEFVAL(true)); 708 | ClassDB::bind_method(D_METHOD("set_dtr", "level"), &SerialPort::set_dtr, DEFVAL(true)); 709 | ClassDB::bind_method(D_METHOD("wait_for_change"), &SerialPort::wait_for_change); 710 | ClassDB::bind_method(D_METHOD("get_cts"), &SerialPort::get_cts); 711 | ClassDB::bind_method(D_METHOD("get_dsr"), &SerialPort::get_dsr); 712 | ClassDB::bind_method(D_METHOD("get_ri"), &SerialPort::get_ri); 713 | ClassDB::bind_method(D_METHOD("get_cd"), &SerialPort::get_cd); 714 | 715 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "port"), "set_port", "get_port"); 716 | ADD_PROPERTY(PropertyInfo(Variant::INT, "baudrate"), "set_baudrate", "get_baudrate"); 717 | ADD_PROPERTY(PropertyInfo(Variant::INT, "timeout"), "set_timeout", "get_timeout"); 718 | ADD_PROPERTY(PropertyInfo(Variant::INT, "bytesize", PROPERTY_HINT_ENUM, "5, 6, 7, 8"), "set_bytesize", "get_bytesize"); 719 | ADD_PROPERTY(PropertyInfo(Variant::INT, "parity", PROPERTY_HINT_ENUM, "None, Odd, Even, Mark, Space"), "set_parity", "get_parity"); 720 | ADD_PROPERTY(PropertyInfo(Variant::INT, "stopbits", PROPERTY_HINT_ENUM, "1, 2, 1.5"), "set_stopbits", "get_stopbits"); 721 | ADD_PROPERTY(PropertyInfo(Variant::INT, "flowcontrol", PROPERTY_HINT_ENUM, "None, Software, Hardware"), "set_flowcontrol", "get_flowcontrol"); 722 | 723 | #ifndef GDEXTENSION 724 | ADD_PROPERTY_DEFAULT("port", ""); 725 | ADD_PROPERTY_DEFAULT("baudrate", 9600); 726 | ADD_PROPERTY_DEFAULT("timeout", 0); 727 | ADD_PROPERTY_DEFAULT("bytesize", BYTESIZE_8); 728 | ADD_PROPERTY_DEFAULT("parity", PARITY_NONE); 729 | ADD_PROPERTY_DEFAULT("stopbits", STOPBITS_1); 730 | ADD_PROPERTY_DEFAULT("flowcontrol", FLOWCONTROL_NONE); 731 | #endif 732 | 733 | ADD_SIGNAL(MethodInfo("got_error", PropertyInfo(Variant::STRING, "where"), PropertyInfo(Variant::STRING, "what"))); 734 | ADD_SIGNAL(MethodInfo("opened", PropertyInfo(Variant::STRING, "port"))); 735 | ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); 736 | ADD_SIGNAL(MethodInfo("closed", PropertyInfo(Variant::STRING, "port"))); 737 | 738 | BIND_ENUM_CONSTANT(BYTESIZE_5); 739 | BIND_ENUM_CONSTANT(BYTESIZE_6); 740 | BIND_ENUM_CONSTANT(BYTESIZE_7); 741 | BIND_ENUM_CONSTANT(BYTESIZE_8); 742 | 743 | BIND_ENUM_CONSTANT(PARITY_NONE); 744 | BIND_ENUM_CONSTANT(PARITY_ODD); 745 | BIND_ENUM_CONSTANT(PARITY_EVEN); 746 | BIND_ENUM_CONSTANT(PARITY_MARK); 747 | BIND_ENUM_CONSTANT(PARITY_SPACE); 748 | 749 | BIND_ENUM_CONSTANT(STOPBITS_1); 750 | BIND_ENUM_CONSTANT(STOPBITS_2); 751 | BIND_ENUM_CONSTANT(STOPBITS_1P5); 752 | 753 | BIND_ENUM_CONSTANT(FLOWCONTROL_NONE); 754 | BIND_ENUM_CONSTANT(FLOWCONTROL_SOFTWARE); 755 | BIND_ENUM_CONSTANT(FLOWCONTROL_HARDWARE); 756 | } 757 | -------------------------------------------------------------------------------- /serial_port.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************/ 2 | /* serial_port.h */ 3 | /*************************************************************************/ 4 | /* This file is part of: */ 5 | /* GODOT ENGINE */ 6 | /* https://godotengine.org */ 7 | /*************************************************************************/ 8 | /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ 9 | /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ 10 | /* */ 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ 12 | /* a copy of this software and associated documentation files (the */ 13 | /* "Software"), to deal in the Software without restriction, including */ 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ 16 | /* permit persons to whom the Software is furnished to do so, subject to */ 17 | /* the following conditions: */ 18 | /* */ 19 | /* The above copyright notice and this permission notice shall be */ 20 | /* included in all copies or substantial portions of the Software. */ 21 | /* */ 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 29 | /*************************************************************************/ 30 | 31 | #ifndef SERIAL_PORT_H 32 | #define SERIAL_PORT_H 33 | 34 | #ifdef GDEXTENSION 35 | #include 36 | #include 37 | 38 | using namespace godot; 39 | #else 40 | #include "core/string/ustring.h" 41 | #include "core/templates/vector.h" 42 | #include "core/variant/array.h" 43 | #include "core/variant/dictionary.h" 44 | #endif 45 | 46 | #include "serial/serial.h" 47 | 48 | #include 49 | #include 50 | 51 | using namespace serial; 52 | 53 | class SerialPort : public Object { 54 | GDCLASS(SerialPort, Object); 55 | 56 | static void _thread_func(void *p_user_data); 57 | 58 | Serial *serial; 59 | int monitoring_interval = 10000; 60 | std::atomic fine_working = false; 61 | std::atomic monitoring_should_exit = true; 62 | std::thread thread; 63 | 64 | String error_message = ""; 65 | 66 | void _data_received(const PackedByteArray &buf); 67 | 68 | public: 69 | enum ByteSize { 70 | BYTESIZE_5 = fivebits, 71 | BYTESIZE_6 = sixbits, 72 | BYTESIZE_7 = sevenbits, 73 | BYTESIZE_8 = eightbits, 74 | }; 75 | enum Parity { 76 | PARITY_NONE = parity_none, 77 | PARITY_ODD = parity_odd, 78 | PARITY_EVEN = parity_even, 79 | PARITY_MARK = parity_mark, 80 | PARITY_SPACE = parity_space, 81 | }; 82 | enum StopBits { 83 | STOPBITS_1 = stopbits_one, 84 | STOPBITS_2 = stopbits_two, 85 | STOPBITS_1P5 = stopbits_one_point_five, 86 | }; 87 | enum FlowControl { 88 | FLOWCONTROL_NONE = flowcontrol_none, 89 | FLOWCONTROL_SOFTWARE = flowcontrol_software, 90 | FLOWCONTROL_HARDWARE = flowcontrol_hardware, 91 | }; 92 | 93 | SerialPort(const String &port = "", 94 | uint32_t baudrate = 9600, 95 | uint32_t timeout = 0, 96 | ByteSize bytesize = BYTESIZE_8, 97 | Parity parity = PARITY_NONE, 98 | StopBits stopbits = STOPBITS_1, 99 | FlowControl flowcontrol = FLOWCONTROL_NONE); 100 | 101 | ~SerialPort(); 102 | 103 | static Dictionary list_ports(); 104 | 105 | bool is_in_error() { return is_open() && !fine_working; } 106 | inline String get_last_error() { return error_message; } 107 | void _on_error(const String &where, const String &what); 108 | 109 | Error start_monitoring(uint64_t interval_in_usec = 10000); 110 | void stop_monitoring(); 111 | 112 | Error open(String port = ""); 113 | 114 | bool is_open() const; 115 | 116 | void close(); 117 | 118 | size_t available(); 119 | 120 | bool wait_readable(); 121 | 122 | void wait_byte_times(size_t count); 123 | 124 | PackedByteArray read_raw(size_t size = 1); 125 | 126 | String read_str(size_t size = 1, bool utf8_encoding = false); 127 | 128 | size_t write_raw(const PackedByteArray &data); 129 | 130 | size_t write_str(const String &data, bool utf8_encoding = false); 131 | 132 | String read_line(size_t size = 65535, String eol = "\n", bool utf8_encoding = false); 133 | PackedStringArray read_lines(size_t size = 65535, String eol = "\n", bool utf8_encoding = false); 134 | 135 | Error set_port(const String &port); 136 | 137 | String get_port() const; 138 | 139 | Error set_timeout(uint32_t timeout); 140 | 141 | uint32_t get_timeout() const; 142 | 143 | Error set_baudrate(uint32_t baudrate); 144 | 145 | uint32_t get_baudrate() const; 146 | 147 | Error set_bytesize(ByteSize bytesize); 148 | 149 | ByteSize get_bytesize() const; 150 | 151 | Error set_parity(Parity parity); 152 | 153 | Parity get_parity() const; 154 | 155 | Error set_stopbits(StopBits stopbits); 156 | 157 | StopBits get_stopbits() const; 158 | 159 | Error set_flowcontrol(FlowControl flowcontrol); 160 | 161 | FlowControl get_flowcontrol() const; 162 | 163 | Error flush(); 164 | 165 | Error flush_input(); 166 | 167 | Error flush_output(); 168 | 169 | Error send_break(int duration); 170 | 171 | Error set_break(bool level = true); 172 | 173 | Error set_rts(bool level = true); 174 | 175 | Error set_dtr(bool level = true); 176 | 177 | bool wait_for_change(); 178 | 179 | bool get_cts(); 180 | 181 | bool get_dsr(); 182 | 183 | bool get_ri(); 184 | 185 | bool get_cd(); 186 | 187 | protected: 188 | String _to_string() const; 189 | 190 | static void _bind_methods(); 191 | }; 192 | 193 | VARIANT_ENUM_CAST(SerialPort::ByteSize); 194 | VARIANT_ENUM_CAST(SerialPort::Parity); 195 | VARIANT_ENUM_CAST(SerialPort::StopBits); 196 | VARIANT_ENUM_CAST(SerialPort::FlowControl); 197 | 198 | #endif // SERIAL_PORT_H 199 | --------------------------------------------------------------------------------