├── .clang-format ├── .clang-tidy ├── .gitignore ├── .vscode └── settings.json ├── CMakeLists.txt ├── JURA WiFi ├── 3D model │ ├── Bottom.stl │ ├── JURA Smart Connect WiFi v21.f3d │ ├── Top.stl │ └── img │ │ ├── 3D Printed housing ABS │ │ ├── bottom1.jpg │ │ ├── compare1.jpg │ │ ├── compare2.jpg │ │ ├── side1.jpg │ │ ├── side2.jpg │ │ ├── side3.jpg │ │ └── top1.jpg │ │ ├── JURA Smart Connect WiFi bottom.png │ │ └── JURA Smart Connect WiFi top.png ├── JURA WiFi Dongle │ ├── JURA WiFi Dongle.kicad_pcb │ ├── JURA WiFi Dongle.kicad_prl │ ├── JURA WiFi Dongle.kicad_pro │ ├── JURA WiFi Dongle.kicad_sch │ └── fp-info-cache └── img │ ├── 7edfa89acfea145ea8f41ab4ab15ee2e153c43e4.jpeg │ ├── back.jpg │ ├── back2.jpg │ ├── front.jpg │ └── side.jpg ├── LICENSE ├── README.md ├── cmake ├── clang-tidy.cmake ├── gcc_analyze.cmake └── sanitizer.cmake ├── protocol_snoops ├── keyexchange.md ├── snoop_bluetooth_1.md ├── snoop_hello_1.md ├── snoop_hello_2.md ├── snoop_hot_water.md └── snoop_keep_alive.txt ├── src ├── CMakeLists.txt ├── include │ ├── CMakeLists.txt │ ├── jutta_proto │ │ ├── CoffeeMaker.hpp │ │ ├── JuttaCommands.hpp │ │ └── JuttaConnection.hpp │ ├── logger │ │ └── Logger.hpp │ └── serial │ │ └── SerialConnection.hpp ├── jutta_proto │ ├── CMakeLists.txt │ ├── CoffeeMaker.cpp │ └── JuttaConnection.cpp ├── logger │ ├── CMakeLists.txt │ └── Logger.cpp ├── serial │ ├── CMakeLists.txt │ └── SerialConnection.cpp └── test_exec │ ├── CMakeLists.txt │ └── handshake_test.cpp └── tests ├── CMakeLists.txt └── Tests.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -3 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Left 8 | AlignOperands: false 9 | AlignTrailingComments: false 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: true 12 | AllowShortCaseLabelsOnASingleLine: true 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: MultiLine 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | AfterExternBlock: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Attach 40 | BreakBeforeInheritanceComma: false 41 | BreakInheritanceList: BeforeColon 42 | BreakBeforeTernaryOperators: false 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 0 48 | CommentPragmas: '(LCOV|unreachable)' 49 | CompactNamespaces: true 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: true 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"' 65 | Priority: 1 66 | - Regex: '^' 67 | Priority: 2 68 | - Regex: '^<.*\.h>' 69 | Priority: 4 70 | - Regex: '^<' 71 | Priority: 3 72 | - Regex: '.\*' 73 | Priority: 5 74 | IncludeIsMainRegex: '(Test)?$' 75 | IndentCaseLabels: true 76 | IndentPPDirectives: None 77 | IndentWidth: 4 78 | IndentWrappedFunctionNames: false 79 | JavaScriptQuotes: Leave 80 | JavaScriptWrapImports: true 81 | KeepEmptyLinesAtTheStartOfBlocks: false 82 | MacroBlockBegin: PROXY_BEGIN 83 | MacroBlockEnd: PROXY_END 84 | MaxEmptyLinesToKeep: 1 85 | NamespaceIndentation: None 86 | ObjCBinPackProtocolList: Auto 87 | ObjCBlockIndentWidth: 2 88 | ObjCSpaceAfterProperty: false 89 | ObjCSpaceBeforeProtocolList: true 90 | PenaltyBreakAssignment: 2 91 | PenaltyBreakBeforeFirstCallParameter: 50 92 | PenaltyBreakComment: 50 93 | PenaltyBreakFirstLessLess: 50 94 | PenaltyBreakString: 50 95 | PenaltyBreakTemplateDeclaration: 10 96 | PenaltyExcessCharacter: 1000000 97 | PenaltyReturnTypeOnItsOwnLine: 50 98 | PointerAlignment: Left 99 | ReflowComments: false 100 | SortIncludes: true 101 | SortUsingDeclarations: true 102 | SpaceAfterCStyleCast: true 103 | SpaceAfterTemplateKeyword: true 104 | SpaceBeforeAssignmentOperators: true 105 | SpaceBeforeCpp11BracedList: false 106 | SpaceBeforeCtorInitializerColon: true 107 | SpaceBeforeInheritanceColon: true 108 | SpaceBeforeParens: ControlStatements 109 | SpaceBeforeRangeBasedForLoopColon: true 110 | SpaceInEmptyParentheses: false 111 | SpacesBeforeTrailingComments: 2 112 | SpacesInAngles: false 113 | SpacesInContainerLiterals: false 114 | SpacesInCStyleCastParentheses: false 115 | SpacesInParentheses: false 116 | SpacesInSquareBrackets: false 117 | Standard: Cpp11 118 | TabWidth: 4 119 | UseTab: Never 120 | ... 121 | 122 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*, 3 | -cppcoreguidelines-pro-type-static-cast-downcast, 4 | -fuchsia-default-arguments-calls, 5 | -fuchsia-default-arguments, 6 | -fuchsia-default-arguments-declarations, 7 | -fuchsia-overloaded-operator, 8 | -fuchsia-statically-constructed-objects, 9 | -hicpp-use-auto, 10 | -modernize-use-auto, 11 | -modernize-use-trailing-return-type, 12 | -readability-implicit-bool-conversion, 13 | -readability-const-return-type, 14 | -google-runtime-references, 15 | -misc-non-private-member-variables-in-classes, 16 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 17 | -hicpp-no-array-decay, 18 | -llvm-include-order, 19 | -cppcoreguidelines-non-private-member-variables-in-classes, 20 | -cppcoreguidelines-pro-type-vararg, 21 | -hicpp-vararg, 22 | -cppcoreguidelines-avoid-magic-numbers, 23 | -readability-magic-numbers, 24 | -cppcoreguidelines-owning-memory, 25 | -llvmlibc-implementation-in-namespace, 26 | -llvmlibc-callee-namespace, 27 | -llvmlibc-restrict-system-libc-headers, 28 | -fuchsia-multiple-inheritance, 29 | -hicpp-signed-bitwise, 30 | -misc-no-recursion, 31 | -altera-unroll-loops, 32 | -altera-id-dependent-backward-branch, 33 | -bugprone-easily-swappable-parameters, 34 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 35 | -cppcoreguidelines-pro-bounds-constant-array-index 36 | ' 37 | WarningsAsErrors: '*' 38 | HeaderFilterRegex: 'src/*.hpp' 39 | FormatStyle: file 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Misc 35 | logs/ 36 | build/ 37 | 38 | # VScode clangd stuff 39 | .clangd/ 40 | compile_commands.json 41 | .cache/ 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.configureArgs": [ 3 | "-DJUTTA_PROTO_BUILD_TEST_EXEC=ON" 4 | ] 5 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | set(PROJECT_DESCRIPTION "An unofficial serial (UART) protocol library implementation to control JURA coffee makers.") 4 | 5 | project("Jutta Protocol Library" 6 | VERSION 0.0.0 7 | DESCRIPTION "${PROJECT_DESCRIPTION}" 8 | HOMEPAGE_URL "https://github.com/Jutta-Proto/protocol-cpp") 9 | set(VERSION_NAME "dev") 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | 14 | macro(jutta_proto_option OPTION_NAME OPTION_TEXT OPTION_DEFAULT) 15 | option(${OPTION_NAME} ${OPTION_TEXT} ${OPTION_DEFAULT}) 16 | if(DEFINED ENV{${OPTION_NAME}}) 17 | # Allow setting the option through an environment variable 18 | set(${OPTION_NAME} $ENV{${OPTION_NAME}}) 19 | endif() 20 | if(${OPTION_NAME}) 21 | add_definitions(-D${OPTION_NAME}) 22 | endif() 23 | message(STATUS " ${OPTION_NAME}: ${${OPTION_NAME}}") 24 | endmacro() 25 | 26 | message(STATUS "C++ Jutta Protocol Library Options") 27 | message(STATUS "=======================================================") 28 | jutta_proto_option(JUTTA_PROTO_BUILD_TESTS "Set to ON to build tests." ON) 29 | jutta_proto_option(JUTTA_PROTO_STATIC_ANALYZE "Set to ON to enable the GCC 10 static analysis." OFF) 30 | jutta_proto_option(JUTTA_PROTO_ENABLE_LINTING "Set to ON to enable clang linting." OFF) 31 | jutta_proto_option(JUTTA_PROTO_BUILD_TEST_EXEC "Build test executables." OFF) 32 | message(STATUS "=======================================================") 33 | 34 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}) 35 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) 36 | list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}) 37 | 38 | include(GNUInstallDirs) 39 | include(sanitizer) 40 | include(gcc_analyze) 41 | 42 | if (${JUTTA_PROTO_ENABLE_LINTING}) 43 | message(STATUS "Enabling linting") 44 | include(clang-tidy) 45 | else() 46 | message(STATUS "Linting is disabled") 47 | endif() 48 | 49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") 50 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") 51 | 52 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") 53 | message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") 54 | file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/0.18.1/conan.cmake" 55 | "${CMAKE_BINARY_DIR}/conan.cmake" 56 | TLS_VERIFY ON) 57 | endif() 58 | 59 | include(${CMAKE_BINARY_DIR}/conan.cmake) 60 | 61 | set(CONAN_CONFIGS"Release;Debug;RelWithDebInfo") 62 | if(NOT CMAKE_BUILD_TYPE IN_LIST CONAN_CONFIGS) 63 | set(CONAN_BUILD_TYPE "Debug") 64 | else() 65 | set(CONAN_BUILD_TYPE ${CMAKE_BUILD_TYPE}) 66 | endif() 67 | 68 | conan_cmake_configure(REQUIRES catch2/2.13.8 69 | spdlog/1.10.0 70 | GENERATORS cmake_find_package 71 | BUILD missing) 72 | conan_cmake_autodetect(settings) 73 | conan_cmake_install(PATH_OR_REFERENCE . 74 | BUILD missing 75 | REMOTE conancenter 76 | SETTINGS ${settings}) 77 | 78 | find_package(spdlog REQUIRED) 79 | 80 | include_directories(${CMAKE_SOURCE_DIR}/src) 81 | 82 | add_subdirectory(src) 83 | 84 | # Testing 85 | if(${JUTTA_PROTO_BUILD_TESTS}) 86 | message(STATUS "Testing is enabled") 87 | enable_testing() 88 | add_subdirectory(tests) 89 | else() 90 | message(STATUS "Testing is disabled") 91 | endif() 92 | 93 | -------------------------------------------------------------------------------- /JURA WiFi/3D model/Bottom.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/Bottom.stl -------------------------------------------------------------------------------- /JURA WiFi/3D model/JURA Smart Connect WiFi v21.f3d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/JURA Smart Connect WiFi v21.f3d -------------------------------------------------------------------------------- /JURA WiFi/3D model/Top.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/Top.stl -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/3D Printed housing ABS/bottom1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/3D Printed housing ABS/bottom1.jpg -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/3D Printed housing ABS/compare1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/3D Printed housing ABS/compare1.jpg -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/3D Printed housing ABS/compare2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/3D Printed housing ABS/compare2.jpg -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/3D Printed housing ABS/side1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/3D Printed housing ABS/side1.jpg -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/3D Printed housing ABS/side2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/3D Printed housing ABS/side2.jpg -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/3D Printed housing ABS/side3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/3D Printed housing ABS/side3.jpg -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/3D Printed housing ABS/top1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/3D Printed housing ABS/top1.jpg -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/JURA Smart Connect WiFi bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/JURA Smart Connect WiFi bottom.png -------------------------------------------------------------------------------- /JURA WiFi/3D model/img/JURA Smart Connect WiFi top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/3D model/img/JURA Smart Connect WiFi top.png -------------------------------------------------------------------------------- /JURA WiFi/JURA WiFi Dongle/JURA WiFi Dongle.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 0, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 1, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 1.0, 12 | "pads": 1.0, 13 | "tracks": 1.0, 14 | "vias": 1.0, 15 | "zones": 0.75 16 | }, 17 | "ratsnest_display_mode": 0, 18 | "selection_filter": { 19 | "dimensions": true, 20 | "footprints": true, 21 | "graphics": true, 22 | "keepouts": true, 23 | "lockedItems": true, 24 | "otherItems": true, 25 | "pads": true, 26 | "text": true, 27 | "tracks": true, 28 | "vias": true, 29 | "zones": true 30 | }, 31 | "visible_items": [ 32 | 0, 33 | 1, 34 | 2, 35 | 3, 36 | 4, 37 | 5, 38 | 8, 39 | 9, 40 | 10, 41 | 11, 42 | 12, 43 | 13, 44 | 14, 45 | 15, 46 | 16, 47 | 17, 48 | 18, 49 | 19, 50 | 20, 51 | 21, 52 | 22, 53 | 23, 54 | 24, 55 | 25, 56 | 26, 57 | 27, 58 | 28, 59 | 29, 60 | 30, 61 | 32, 62 | 33, 63 | 34, 64 | 35, 65 | 36 66 | ], 67 | "visible_layers": "fffffff_ffffffff", 68 | "zone_display_mode": 0 69 | }, 70 | "meta": { 71 | "filename": "JURA WiFi Dongle.kicad_prl", 72 | "version": 3 73 | }, 74 | "project": { 75 | "files": [] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /JURA WiFi/JURA WiFi Dongle/JURA WiFi Dongle.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "board_outline_line_width": 0.09999999999999999, 7 | "copper_line_width": 0.19999999999999998, 8 | "copper_text_italic": false, 9 | "copper_text_size_h": 1.5, 10 | "copper_text_size_v": 1.5, 11 | "copper_text_thickness": 0.3, 12 | "copper_text_upright": false, 13 | "courtyard_line_width": 0.049999999999999996, 14 | "dimension_precision": 4, 15 | "dimension_units": 3, 16 | "dimensions": { 17 | "arrow_length": 1270000, 18 | "extension_offset": 500000, 19 | "keep_text_aligned": true, 20 | "suppress_zeroes": false, 21 | "text_position": 0, 22 | "units_format": 1 23 | }, 24 | "fab_line_width": 0.09999999999999999, 25 | "fab_text_italic": false, 26 | "fab_text_size_h": 1.0, 27 | "fab_text_size_v": 1.0, 28 | "fab_text_thickness": 0.15, 29 | "fab_text_upright": false, 30 | "other_line_width": 0.15, 31 | "other_text_italic": false, 32 | "other_text_size_h": 1.0, 33 | "other_text_size_v": 1.0, 34 | "other_text_thickness": 0.15, 35 | "other_text_upright": false, 36 | "pads": { 37 | "drill": 0.0, 38 | "height": 0.4, 39 | "width": 0.46 40 | }, 41 | "silk_line_width": 0.15, 42 | "silk_text_italic": false, 43 | "silk_text_size_h": 1.0, 44 | "silk_text_size_v": 1.0, 45 | "silk_text_thickness": 0.15, 46 | "silk_text_upright": false, 47 | "zones": { 48 | "45_degree_only": false, 49 | "min_clearance": 0.508 50 | } 51 | }, 52 | "diff_pair_dimensions": [ 53 | { 54 | "gap": 0.0, 55 | "via_gap": 0.0, 56 | "width": 0.0 57 | } 58 | ], 59 | "drc_exclusions": [], 60 | "meta": { 61 | "version": 2 62 | }, 63 | "rule_severities": { 64 | "annular_width": "error", 65 | "clearance": "error", 66 | "copper_edge_clearance": "error", 67 | "courtyards_overlap": "error", 68 | "diff_pair_gap_out_of_range": "error", 69 | "diff_pair_uncoupled_length_too_long": "error", 70 | "drill_out_of_range": "error", 71 | "duplicate_footprints": "warning", 72 | "extra_footprint": "warning", 73 | "footprint_type_mismatch": "error", 74 | "hole_clearance": "error", 75 | "hole_near_hole": "error", 76 | "invalid_outline": "error", 77 | "item_on_disabled_layer": "error", 78 | "items_not_allowed": "error", 79 | "length_out_of_range": "error", 80 | "malformed_courtyard": "error", 81 | "microvia_drill_out_of_range": "error", 82 | "missing_courtyard": "ignore", 83 | "missing_footprint": "warning", 84 | "net_conflict": "warning", 85 | "npth_inside_courtyard": "ignore", 86 | "padstack": "error", 87 | "pth_inside_courtyard": "ignore", 88 | "shorting_items": "error", 89 | "silk_over_copper": "warning", 90 | "silk_overlap": "warning", 91 | "skew_out_of_range": "error", 92 | "through_hole_pad_without_hole": "error", 93 | "too_many_vias": "error", 94 | "track_dangling": "warning", 95 | "track_width": "error", 96 | "tracks_crossing": "error", 97 | "unconnected_items": "error", 98 | "unresolved_variable": "error", 99 | "via_dangling": "warning", 100 | "zone_has_empty_net": "error", 101 | "zones_intersect": "error" 102 | }, 103 | "rules": { 104 | "allow_blind_buried_vias": false, 105 | "allow_microvias": false, 106 | "max_error": 0.005, 107 | "min_clearance": 0.0, 108 | "min_connection": 0.0, 109 | "min_copper_edge_clearance": 0.0, 110 | "min_hole_clearance": 0.25, 111 | "min_hole_to_hole": 0.25, 112 | "min_microvia_diameter": 0.19999999999999998, 113 | "min_microvia_drill": 0.09999999999999999, 114 | "min_resolved_spokes": 2, 115 | "min_silk_clearance": 0.0, 116 | "min_text_height": 0.7999999999999999, 117 | "min_text_thickness": 0.08, 118 | "min_through_hole_diameter": 0.3, 119 | "min_track_width": 0.19999999999999998, 120 | "min_via_annular_width": 0.049999999999999996, 121 | "min_via_diameter": 0.39999999999999997, 122 | "solder_mask_clearance": 0.0, 123 | "solder_mask_min_width": 0.0, 124 | "solder_mask_to_copper_clearance": 0.0, 125 | "use_height_for_length_calcs": true 126 | }, 127 | "teardrop_options": [ 128 | { 129 | "td_allow_use_two_tracks": true, 130 | "td_curve_segcount": 5, 131 | "td_on_pad_in_zone": false, 132 | "td_onpadsmd": true, 133 | "td_onroundshapesonly": false, 134 | "td_ontrackend": false, 135 | "td_onviapad": true 136 | } 137 | ], 138 | "teardrop_parameters": [ 139 | { 140 | "td_curve_segcount": 0, 141 | "td_height_ratio": 1.0, 142 | "td_length_ratio": 0.5, 143 | "td_maxheight": 2.0, 144 | "td_maxlen": 1.0, 145 | "td_target_name": "td_round_shape", 146 | "td_width_to_size_filter_ratio": 0.9 147 | }, 148 | { 149 | "td_curve_segcount": 0, 150 | "td_height_ratio": 1.0, 151 | "td_length_ratio": 0.5, 152 | "td_maxheight": 2.0, 153 | "td_maxlen": 1.0, 154 | "td_target_name": "td_rect_shape", 155 | "td_width_to_size_filter_ratio": 0.9 156 | }, 157 | { 158 | "td_curve_segcount": 0, 159 | "td_height_ratio": 1.0, 160 | "td_length_ratio": 0.5, 161 | "td_maxheight": 2.0, 162 | "td_maxlen": 1.0, 163 | "td_target_name": "td_track_end", 164 | "td_width_to_size_filter_ratio": 0.9 165 | } 166 | ], 167 | "track_widths": [ 168 | 0.0, 169 | 0.254, 170 | 0.512 171 | ], 172 | "via_dimensions": [ 173 | { 174 | "diameter": 0.0, 175 | "drill": 0.0 176 | } 177 | ], 178 | "zones_allow_external_fillets": false, 179 | "zones_use_no_outline": true 180 | }, 181 | "layer_presets": [], 182 | "viewports": [] 183 | }, 184 | "boards": [], 185 | "cvpcb": { 186 | "equivalence_files": [] 187 | }, 188 | "erc": { 189 | "erc_exclusions": [], 190 | "meta": { 191 | "version": 0 192 | }, 193 | "pin_map": [ 194 | [ 195 | 0, 196 | 0, 197 | 0, 198 | 0, 199 | 0, 200 | 0, 201 | 1, 202 | 0, 203 | 0, 204 | 0, 205 | 0, 206 | 2 207 | ], 208 | [ 209 | 0, 210 | 2, 211 | 0, 212 | 1, 213 | 0, 214 | 0, 215 | 1, 216 | 0, 217 | 2, 218 | 2, 219 | 2, 220 | 2 221 | ], 222 | [ 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | 0, 228 | 0, 229 | 1, 230 | 0, 231 | 1, 232 | 0, 233 | 1, 234 | 2 235 | ], 236 | [ 237 | 0, 238 | 1, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 1, 244 | 1, 245 | 2, 246 | 1, 247 | 1, 248 | 2 249 | ], 250 | [ 251 | 0, 252 | 0, 253 | 0, 254 | 0, 255 | 0, 256 | 0, 257 | 1, 258 | 0, 259 | 0, 260 | 0, 261 | 0, 262 | 2 263 | ], 264 | [ 265 | 0, 266 | 0, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 0, 273 | 0, 274 | 0, 275 | 0, 276 | 2 277 | ], 278 | [ 279 | 1, 280 | 1, 281 | 1, 282 | 1, 283 | 1, 284 | 0, 285 | 1, 286 | 1, 287 | 1, 288 | 1, 289 | 1, 290 | 2 291 | ], 292 | [ 293 | 0, 294 | 0, 295 | 0, 296 | 1, 297 | 0, 298 | 0, 299 | 1, 300 | 0, 301 | 0, 302 | 0, 303 | 0, 304 | 2 305 | ], 306 | [ 307 | 0, 308 | 2, 309 | 1, 310 | 2, 311 | 0, 312 | 0, 313 | 1, 314 | 0, 315 | 2, 316 | 2, 317 | 2, 318 | 2 319 | ], 320 | [ 321 | 0, 322 | 2, 323 | 0, 324 | 1, 325 | 0, 326 | 0, 327 | 1, 328 | 0, 329 | 2, 330 | 0, 331 | 0, 332 | 2 333 | ], 334 | [ 335 | 0, 336 | 2, 337 | 1, 338 | 1, 339 | 0, 340 | 0, 341 | 1, 342 | 0, 343 | 2, 344 | 0, 345 | 0, 346 | 2 347 | ], 348 | [ 349 | 2, 350 | 2, 351 | 2, 352 | 2, 353 | 2, 354 | 2, 355 | 2, 356 | 2, 357 | 2, 358 | 2, 359 | 2, 360 | 2 361 | ] 362 | ], 363 | "rule_severities": { 364 | "bus_definition_conflict": "error", 365 | "bus_entry_needed": "error", 366 | "bus_label_syntax": "error", 367 | "bus_to_bus_conflict": "error", 368 | "bus_to_net_conflict": "error", 369 | "different_unit_footprint": "error", 370 | "different_unit_net": "error", 371 | "duplicate_reference": "error", 372 | "duplicate_sheet_names": "error", 373 | "extra_units": "error", 374 | "global_label_dangling": "warning", 375 | "hier_label_mismatch": "error", 376 | "label_dangling": "error", 377 | "lib_symbol_issues": "warning", 378 | "multiple_net_names": "warning", 379 | "net_not_bus_member": "warning", 380 | "no_connect_connected": "warning", 381 | "no_connect_dangling": "warning", 382 | "pin_not_connected": "error", 383 | "pin_not_driven": "error", 384 | "pin_to_pin": "warning", 385 | "power_pin_not_driven": "error", 386 | "similar_labels": "warning", 387 | "unannotated": "error", 388 | "unit_value_mismatch": "error", 389 | "unresolved_variable": "error", 390 | "wire_dangling": "error" 391 | } 392 | }, 393 | "libraries": { 394 | "pinned_footprint_libs": [], 395 | "pinned_symbol_libs": [] 396 | }, 397 | "meta": { 398 | "filename": "JURA WiFi Dongle.kicad_pro", 399 | "version": 1 400 | }, 401 | "net_settings": { 402 | "classes": [ 403 | { 404 | "bus_width": 12.0, 405 | "clearance": 0.2, 406 | "diff_pair_gap": 0.25, 407 | "diff_pair_via_gap": 0.25, 408 | "diff_pair_width": 0.2, 409 | "line_style": 0, 410 | "microvia_diameter": 0.3, 411 | "microvia_drill": 0.1, 412 | "name": "Default", 413 | "pcb_color": "rgba(0, 0, 0, 0.000)", 414 | "schematic_color": "rgba(0, 0, 0, 0.000)", 415 | "track_width": 0.25, 416 | "via_diameter": 0.8, 417 | "via_drill": 0.4, 418 | "wire_width": 6.0 419 | } 420 | ], 421 | "meta": { 422 | "version": 2 423 | }, 424 | "net_colors": null, 425 | "netclass_assignments": null, 426 | "netclass_patterns": [] 427 | }, 428 | "pcbnew": { 429 | "last_paths": { 430 | "gencad": "", 431 | "idf": "", 432 | "netlist": "", 433 | "specctra_dsn": "", 434 | "step": "JURA WiFi Dongle.step", 435 | "vrml": "" 436 | }, 437 | "page_layout_descr_file": "" 438 | }, 439 | "schematic": { 440 | "annotate_start_num": 0, 441 | "drawing": { 442 | "default_line_thickness": 6.0, 443 | "default_text_size": 50.0, 444 | "field_names": [], 445 | "intersheets_ref_own_page": false, 446 | "intersheets_ref_prefix": "", 447 | "intersheets_ref_short": false, 448 | "intersheets_ref_show": false, 449 | "intersheets_ref_suffix": "", 450 | "junction_size_choice": 3, 451 | "label_size_ratio": 0.375, 452 | "pin_symbol_size": 25.0, 453 | "text_offset_ratio": 0.15 454 | }, 455 | "legacy_lib_dir": "", 456 | "legacy_lib_list": [], 457 | "meta": { 458 | "version": 1 459 | }, 460 | "net_format_name": "", 461 | "ngspice": { 462 | "fix_include_paths": true, 463 | "fix_passive_vals": false, 464 | "meta": { 465 | "version": 0 466 | }, 467 | "model_mode": 0, 468 | "workbook_filename": "" 469 | }, 470 | "page_layout_descr_file": "", 471 | "plot_directory": "", 472 | "spice_adjust_passive_values": false, 473 | "spice_external_command": "spice \"%I\"", 474 | "subpart_first_id": 65, 475 | "subpart_id_separator": 0 476 | }, 477 | "sheets": [ 478 | [ 479 | "27147150-5427-4c9a-9fbd-286065d958e2", 480 | "" 481 | ] 482 | ], 483 | "text_variables": {} 484 | } 485 | -------------------------------------------------------------------------------- /JURA WiFi/img/7edfa89acfea145ea8f41ab4ab15ee2e153c43e4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/img/7edfa89acfea145ea8f41ab4ab15ee2e153c43e4.jpeg -------------------------------------------------------------------------------- /JURA WiFi/img/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/img/back.jpg -------------------------------------------------------------------------------- /JURA WiFi/img/back2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/img/back2.jpg -------------------------------------------------------------------------------- /JURA WiFi/img/front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/img/front.jpg -------------------------------------------------------------------------------- /JURA WiFi/img/side.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jutta-Proto/protocol-cpp/4e8f1930b08ce1beb57fe99de40ce8c2b5abb3b4/JURA WiFi/img/side.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JURA Protocol 2 | `C++` JURA protocol implementation for controlling a Jura coffee maker over a serial (UART) connection. 3 | 4 | This work is based on the excellent work done by the people over at [Protocol JURA](http://protocoljura.wiki-site.com/index.php/Hauptseite). 5 | They were able to figure out the basic protocol used for communication with older JURA coffee makers. 6 | 7 | Since newer models **do not use** this old V1-Protocol any more I started this project to understand the new one and create a reference implementation for it. 8 | 9 | ## Table of Contents 10 | 0. [Example](#example) 11 | 1. [Protocol](#protocol) 12 | 2. [JURA Commands](#jura-commands) 13 | 3. [Requirements](#requirements) 14 | 4. [Building](#building) 15 | 16 | ## Example 17 | The following example shows the interaction with a JURA coffee maker over [XMPP](https://xmpp.org/). 18 | The complete implementation for this demo can be found [here](https://github.com/COM8/esp32-jura). 19 | [![Watch the video](https://user-images.githubusercontent.com/11741404/89994342-489af800-dc88-11ea-9a4e-c4407ce79a8d.png)](https://twitter.com/UWPX_APP/status/1293461429677436931) 20 | 21 | ## Protocol 22 | 23 | ### General 24 | There are several steps of obfuscation being done by the JURA coffee maker to prevent others from reading the bare protocol. 25 | 26 | #### Connecting to an JURA coffee maker 27 | To connect to an JURA coffee maker we are using a 5V UART signal with the following configuration: 28 | * **Baud Rate:** 9600 29 | * **Data Bits:** 8 30 | * **Parity:** Disabled 31 | * **Stop Bits:** 1 32 | * **Flow Control:** Hardware flow control disabled 33 | * **RX Flow Control Threshold:** 0 34 | 35 | #### Deobfuscating 36 | Once a connection has been established we can start sending and receiving data. 37 | **But** all data send and received is obfuscated. 38 | The following description shows how to **deobfuscate** data received from the coffee maker. 39 | To obfuscate data just follow the steps in reverse. 40 | 41 | **Step 0** 42 | The coffee maker always sends 4 "raw" byte per one byte of data with a break of 8ms in between each "raw" byte. 43 | This looks something like this: 44 | ``` 45 | 01011011 <8ms break> 01011111 <8ms break> 01011111 <8ms break> 01011111 <8ms break> 46 | 01011111 <8ms break> 01111011 <8ms break> 01011111 <8ms break> 01011111 <8ms break> 47 | 01111011 <8ms break> 01111011 <8ms break> 01111111 <8ms break> 01011011 <8ms break> 48 | 01011111 <8ms break> 01111111 <8ms break> 01011011 <8ms break> 01011011 <8ms break> 49 | 01111011 <8ms break> 01111011 <8ms break> 01011011 <8ms break> 01011011 <8ms break> 50 | ``` 51 | Each line corresponds to one actual data byte. 52 | 53 | **Step 1** 54 | Each of our 4 "raw" bytes (each line) contains only 2 bits of our resulting data bit. 55 | Bit 2 and 5. 56 | All other bits (except 0) are set to 1. 57 | 58 | ``` 59 | 0b01011011 60 | ^ ^ 61 | DB1 DB2 62 | ``` 63 | `DB1` and `DB2` are our actual data bits here. 64 | We have to combine alle 8 (of our 4 "raw" bytes) into a single data byte. 65 | 66 | Examples: 67 | ```C++ 68 | const std::array encData{0b01011011, 0b01011111, 0b01011111, 0b01011111}; 69 | 70 | // Bit mask for the 2. bit from the left: 71 | constexpr uint8_t B2_MASK = (0b10000000 >> 2); 72 | 73 | // Bit mask for the 5. bit from the left: 74 | constexpr uint8_t B5_MASK = (0b10000000 >> 5); 75 | 76 | uint8_t decData = 0; 77 | decData |= (encData[0] & B2_MASK) << 2; 78 | decData |= (encData[0] & B5_MASK) << 4; 79 | decData |= (encData[1] & B2_MASK); 80 | decData |= (encData[1] & B5_MASK) << 2; 81 | decData |= (encData[2] & B2_MASK) >> 2; 82 | decData |= (encData[2] & B5_MASK); 83 | decData |= (encData[3] & B2_MASK) >> 4; 84 | decData |= (encData[3] & B5_MASK) >> 2; // 0b00010101 85 | ``` 86 | ``` 87 | Input -> Output 88 | 0 0 0 1 0 1 0 1 -> 0 1 0 1 0 0 0 1 89 | 0 1 1 0 0 1 0 1 -> 0 1 0 1 0 1 1 0 90 | 1 0 1 0 1 1 0 0 -> 1 1 0 0 1 0 1 0 91 | 0 1 1 1 0 0 0 0 -> 0 0 0 0 0 1 1 1 92 | 1 0 1 0 0 0 0 0 -> 0 0 0 0 1 0 1 0 93 | ``` 94 | 95 | **Step 2** 96 | In this step we switch both nibbles (4 bit) of each byte. 97 | Examples: 98 | `1100 1100 -> 0011 0011` 99 | ```C++ 100 | uint8_t in = 0b00010101; 101 | uint8_t out = ((in & 0xF0) >> 4) | ((in & 0x0F) << 4); // 0b01010001 102 | ``` 103 | ``` 104 | Input -> Output 105 | 01011011 <8ms break> 01011111 <8ms break> 01011111 <8ms break> 01011111 <8ms break> -> 0 1 0 1 0 0 0 1 106 | 01011111 <8ms break> 01111011 <8ms break> 01011111 <8ms break> 01011111 <8ms break> -> 0 1 0 1 0 1 1 0 107 | 01111011 <8ms break> 01111011 <8ms break> 01111111 <8ms break> 01011011 <8ms break> -> 1 1 0 0 1 0 1 0 108 | 01011111 <8ms break> 01111111 <8ms break> 01011011 <8ms break> 01011011 <8ms break> -> 0 0 0 0 0 1 1 1 109 | 01111011 <8ms break> 01111011 <8ms break> 01011011 <8ms break> 01011011 <8ms break> -> 0 0 0 0 1 0 1 0 110 | ``` 111 | 112 | **Step 3** 113 | A last time we have to shift all bits in our byte around. 114 | Here we have to split up our byte into two nibbles (4 bit) and switch two bits each. 115 | Examples: 116 | `1100 1100 -> 0011 0011` 117 | ```C++ 118 | uint8_t in = 0b01010001; 119 | uint8_t out = ((in & 0xC0) >> 2) | ((in & 0x30) << 2) | ((in & 0x0C) >> 2) | ((in & 0x03) << 2); // 0b01010100 120 | ``` 121 | ``` 122 | Input -> Output -> Output_Hex Output_Dec Output_Char 123 | 0 1 0 1 0 0 0 1 -> 0 1 0 1 0 1 0 0 -> 54 84 T 124 | 0 1 0 1 0 1 1 0 -> 0 1 0 1 1 0 0 1 -> 59 89 Y 125 | 1 1 0 0 1 0 1 0 -> 0 0 1 1 1 0 1 0 -> 3A 58 : 126 | 0 0 0 0 0 1 1 1 -> 0 0 0 0 1 1 0 1 -> 0d 13 '\r' 127 | 0 0 0 0 1 0 1 0 -> 0 0 0 0 1 0 1 0 -> 0a 10 '\n' 128 | ``` 129 | Which results in the message `TY:\r\n`. 130 | 131 | ## JURA Commands 132 | Every message/command send from or to the coffee maker has to end with `\r\n` to be valid. 133 | For simplicity reasons we omit the `\r\n` from all of the following messages and examples. 134 | 135 | ### Command Structure 136 | In general for every **valid** command a response will be send from the coffee maker. 137 | The actual command is always uppercase (e.g. `TY:`) and the response send back is lowercase (`ty:EF532M V02.03`). 138 | 139 | ### Available Commands 140 | The following list of commands has been tested on an `Jura E6 2019 platin (15326)`. 141 | 142 | | Name | Command | Response | Description | 143 | |----|----|----|----| 144 | | UNKNOWN | `AN:01` | `ok:` | - | 145 | | Turn off | `AN:02` | `ok:` | Turns off the coffee maker. | 146 | | Erase EPROM | `AN:0A` | UNKNOWN | **Untested!** Erases the EPROM. Do not use. | 147 | | Test UCHI | `AN:0C` | `ok:` | Test the UCHI steam plate. | 148 | | Test Mode on | `AN:20` | `ok:` | Turns on the test mode. | 149 | | Test Mode off | `AN:21` | `ok:` | Turns off the test mode. | 150 | | UNKNOWN | `AN:40` | `an:40` | - | 151 | | UNKNOWN | `AN:AA` | `ok:` | - | 152 | | Get Type of Machine | `TY:` | `ty:` (e.g. `ty:EF532M V02.03`) | Returns the type of the machine. | 153 | | UNKNOWN | `FA:01` | `ok:` | - | 154 | | (Button 1) | `FA:04` | `ok:` | Simulates the button 1 press (left top). | 155 | | (Button 2) | `FA:05` | `ok:` | Simulates the button 2 press (left center). | 156 | | (Button 3) | `FA:06` | `ok:` | Simulates the button 3 press (left bottom). | 157 | | (Button 4) | `FA:07` | `ok:` | Simulates the button 4 press (right top). | 158 | | (Button 5) | `FA:08` | `ok:` | Simulates the button 5 press (right center). | 159 | | (Button 6) | `FA:09` | `ok:` | Simulates the button 6 press (right bottom). | 160 | | Coffee Pump on | `FN:01` | `ok:` | Turns on the coffee pump. | 161 | | Coffee Pump off | `FN:02` | `ok:` | Turns off the coffee pump. | 162 | | Coffee Heater on | `FN:03` | `ok:` | Turns on the coffee heater. | 163 | | Coffee Heater off | `FN:04` | `ok:` | Turns off the coffee heater. | 164 | | Grinder on | `FN:07` | `ok:` | Turns on the coffee grinder. | 165 | | Grinder off | `FN:08` | `ok:` | Turns off the coffee grinder. | 166 | | Brew Group **Something** on | `FN:09` | `ok:` | Turns **something** in relation to the brew group on. | 167 | | Brew Group **Something** off | `FN:0A` | `ok:` | Turns **something** in relation to the brew group off. | 168 | | Coffee press on | `FN:0B` | `ok:` | Turns on the coffee press. | 169 | | Coffee press off | `FN:0C` | `ok:` | Turns off the coffee press. | 170 | | Init Brew Group | `FN:0D` | `ok:` | Initializes the brew group. | 171 | | Brew Group to **open** Postion | `FN:0E` | `ok:` | Moves the brew group into the "open" position. | 172 | | Brew Group to **grinding** Postion | `FN:0F` | `ok:` | Moves the brew group into the grinding position. | 173 | | Brew Group to **unknown** Postion XYZ | `FN:13` | `ok:` | Moves the brew group into an currently unknown position. | 174 | | Brew Group to **unknown** Postion XYZ | `FN:1B` | `ok:` | Moves the brew group into an currently unknown position. | 175 | | Brew Group to **throw out position?!** Postion XYZ | `FN:1C` | `ok:` | Moves the brew group into the throw out position. | 176 | | Brew Group to **brewing** Position | `FN:22` | `ok:` | Moves the brew group into the brewing position. | 177 | | UNKNOWN | `FN:24` | `ok:` | - | 178 | | UNKNOWN | `FN:25` | `ok:` | - | 179 | | UNKNOWN | `FN:26` | `ok:` | - | 180 | | UNKNOWN | `FN:27` | `ok:` | - | 181 | | UNKNOWN | `FN:44` | `ok:` | - | 182 | | UNKNOWN | `FN:45` | `ok:` | - | 183 | | UNKNOWN | `FN:50` | `ok:` | - | 184 | | Turn off | `FN:51` | `ok:` | Turns off the coffee maker. | 185 | | UNKNOWN | `FN:54` | `ok:` | - | 186 | | UNKNOWN | `FN:55` | `ok:` | - | 187 | | UNKNOWN | `FN:60` | `ok:` | - | 188 | | UNKNOWN | `FN:61` | `ok:` | - | 189 | | UNKNOWN | `FN:62` | `ok:` | - | 190 | | UNKNOWN | `FN:63` | `ok:` | - | 191 | | UNKNOWN | `FN:64` | `ok:` | - | 192 | | UNKNOWN | `FN:65` | `ok:` | - | 193 | | UNKNOWN | `FN:66` | `ok:` | - | 194 | | UNKNOWN | `FN:67` | `ok:` | - | 195 | | UNKNOWN | `FN:70` | `ok:` | - | 196 | | UNKNOWN | `FN:71` | `ok:` | - | 197 | | UNKNOWN | `FN:72` | `ok:` | - | 198 | | UNKNOWN | `FN:73` | `ok:` | - | 199 | | UNKNOWN | `FN:80` | `ok:` | - | 200 | | UNKNOWN | `FN:81` | `ok:` | - | 201 | | UNKNOWN | `FN:88` | `ok:` | - | 202 | | UNKNOWN | `FN:89` | `ok:` | - | 203 | | Debug mode on | `FN:89` | `ku:`, `Ku:` pause `ku:`, `Ku:`, ... | Enables the debug mode. Sends continuously `ku:`, `Ku:`, ... Once an action like opening the hot water valve accrues, outputs information like percentage done. To disable it again disconnect the coffee maker from power. | 204 | | UNKNOWN | `FN:90` | `ok:` | - | 205 | | UNKNOWN | `FN:99` | `ok:` | - | 206 | 207 | ### Coffee Brewing Sequence 208 | * `FN:07` # Grind on 209 | * Sleep 3 seconds # Determines how strong the coffee will be 210 | * `FN:08` # Grind off 211 | * `FN:22` # Brew group to brewing position 212 | * `FN:0B` # Coffee press on 213 | * Sleep 0.5 seconds # Compress the coffee 214 | * `FN:0C` # Coffee press off 215 | * `FN:03` # Turn on the coffee water heater 216 | * `FN:01` # Coffee water pump on 217 | * Sleep 2 seconds # Initial amount of water 218 | * `FN:02` # Coffee water pump off 219 | * `FN:04` # Turn off the coffee water heater 220 | * Sleep 2 seconds # Allow the water to run everywhere 221 | * `FN:03` # Turn on the coffee water heater 222 | * `FN:01` # Coffee water pump on 223 | * Sleep 40 seconds # 40 seconds of water result in 200 ml of coffee 224 | * `FN:02` # Coffee water pump off 225 | * `FN:04` # Turn off the coffee water heater 226 | * `FN:0D` # Reset the brew group and throw out the old coffee grain 227 | 228 | ## Requirements 229 | The following requirements are required to build this project. 230 | * A C++20 compatible compiler like [gcc](https://gcc.gnu.org/) or [clang](https://clang.llvm.org/) 231 | * The build system is written using [CMake](https://cmake.org/) 232 | * For managing dependencies in CMake, we are using [conan](https://conan.io/) 233 | 234 | ### Fedora 235 | To install those dependencies on Fedora, run the following commands: 236 | ```bash 237 | sudo dnf install -y gcc clang cmake python3 python3-pip 238 | pip install --user conan==1.59.0 # conan 2.x.x is not supported right now 239 | ``` 240 | 241 | ### Raspberry Pi 242 | To install those dependencies on a Raspberry Pi, running the [Raspberry Pi OS](https://www.raspberrypi.org/software/), run the following commands: 243 | ```bash 244 | sudo apt install -y cmake python3 python3-pip 245 | pip3 install --user conan==1.59.0 # conan 2.x.x is not supported right now 246 | ``` 247 | For all the other requirements, head over here: https://github.com/Jutta-Proto/hardware-pi#raspberry-pi-os 248 | 249 | ## Building 250 | Run the following commands to build this project: 251 | ```bash 252 | # Clone the repository: 253 | git clone https://github.com/Jutta-Proto/protocol-cpp.git 254 | # Switch into the newly cloned repository: 255 | cd protocol-cpp 256 | # Build the project: 257 | mkdir build 258 | cd build 259 | cmake .. 260 | cmake --build . 261 | ``` 262 | 263 | `[1]`: https://uk.jura.com/en/homeproducts/accessories/SmartConnect-Main-72167 264 | -------------------------------------------------------------------------------- /cmake/clang-tidy.cmake: -------------------------------------------------------------------------------- 1 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") 2 | find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy-7 clang-tidy) 3 | mark_as_advanced(CLANG_TIDY_EXECUTABLE) 4 | 5 | if (${CLANG_TIDY_EXECUTABLE}) 6 | message(WARNING "Clang-tidy not found") 7 | else() 8 | message(STATUS "Enabling clang-tidy") 9 | set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE};-warnings-as-errors=*") 10 | endif() 11 | else() 12 | message(STATUS "Clang-tidy is not supporten when building for windows") 13 | endif() 14 | -------------------------------------------------------------------------------- /cmake/gcc_analyze.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | if(JUTTA_PROTO_STATIC_ANALYZE) 4 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 5 | check_cxx_compiler_flag("-fanalyzer" HAS_GCC_STATIC_ANALYZER) 6 | if(HAS_GCC_STATIC_ANALYZER) 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fanalyzer") 8 | set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -fanalyzer") 9 | message(STATUS "GCC static analysis successfully enabled.") 10 | else() 11 | message(FATAL_ERROR "Failed to enable GCC static analysis since the compiler does not support it.") 12 | endif() 13 | else() 14 | message(FATAL_ERROR "Failed to enable GCC static analysis since the compiler does not seam to be GCC (GNU).") 15 | endif() 16 | endif() 17 | -------------------------------------------------------------------------------- /cmake/sanitizer.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | include(CheckCXXSourceRuns) 3 | 4 | set(ALLOWED_BUILD_TYPES Debug Release RelWithDebInfo MinSizeRel) 5 | set(ALLSAN_FLAGS "") 6 | 7 | # ThreadSanitizer 8 | set(THREAD_SAN_FLAGS "-fsanitize=thread") 9 | set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS}) 10 | set(CMAKE_REQUIRED_FLAGS "${THREAD_SAN_FLAGS}") 11 | check_cxx_source_runs("int main() { return 0; }" THREAD_SANITIZER_AVAILABLE) 12 | set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG}) 13 | if(THREAD_SANITIZER_AVAILABLE) 14 | list(APPEND ALLOWED_BUILD_TYPES ThreadSan) 15 | # Do not add Thread Sanitizer to all Sanitizers because it is incompatible with other Sanitizers 16 | endif() 17 | set(CMAKE_C_FLAGS_THREADSAN "${CMAKE_C_FLAGS_DEBUG} ${THREAD_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C compiler during Thread Sanitizer builds." FORCE) 18 | set(CMAKE_CXX_FLAGS_THREADSAN "${CMAKE_CXX_FLAGS_DEBUG} ${THREAD_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C++ compiler during Thread Sanitizer builds." FORCE) 19 | 20 | # AddressSanitizer 21 | set(ADDR_SAN_FLAGS "-fsanitize=address") 22 | set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS}) 23 | set(CMAKE_REQUIRED_FLAGS "${ADDR_SAN_FLAGS}") 24 | check_cxx_source_runs("int main() { return 0; }" ADDRESS_SANITIZER_AVAILABLE) 25 | set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG}) 26 | if(ADDRESS_SANITIZER_AVAILABLE) 27 | list(APPEND ALLOWED_BUILD_TYPES AddrSan) 28 | set(ALLSAN_FLAGS "${ALLSAN_FLAGS} ${ADDR_SAN_FLAGS}") 29 | endif() 30 | set(CMAKE_C_FLAGS_ADDRSAN "${CMAKE_C_FLAGS_DEBUG} ${ADDR_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C compiler during AddressSanitizer builds." FORCE) 31 | set(CMAKE_CXX_FLAGS_ADDRSAN "${CMAKE_CXX_FLAGS_DEBUG} ${ADDR_SAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C++ compiler during AddressSanitizer builds." FORCE) 32 | 33 | # LeakSanitizer 34 | set(LEAK_SAN_FLAGS "-fsanitize=leak") 35 | check_cxx_compiler_flag(${LEAK_SAN_FLAGS} LEAK_SANITIZER_AVAILABLE) 36 | if(LEAK_SANITIZER_AVAILABLE) 37 | list(APPEND ALLOWED_BUILD_TYPES LeakSan) 38 | set(ALLSAN_FLAGS "${ALLSAN_FLAGS} ${LEAK_SAN_FLAGS}") 39 | endif() 40 | set(CMAKE_C_FLAGS_LEAKSAN "${CMAKE_C_FLAGS_DEBUG} ${LEAK_SAN_FLAGS} -fno-omit-frame-pointer" CACHE INTERNAL "Flags used by the C compiler during LeakSanitizer builds." FORCE) 41 | set(CMAKE_CXX_FLAGS_LEAKSAN "${CMAKE_CXX_FLAGS_DEBUG} ${LEAK_SAN_FLAGS} -fno-omit-frame-pointer" CACHE INTERNAL "Flags used by the C++ compiler during LeakSanitizer builds." FORCE) 42 | 43 | # UndefinedBehaviour 44 | set(UDEF_SAN_FLAGS "-fsanitize=undefined") 45 | check_cxx_compiler_flag(${UDEF_SAN_FLAGS} UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE) 46 | if(UNDEFINED_BEHAVIOUR_SANITIZER_AVAILABLE) 47 | list(APPEND ALLOWED_BUILD_TYPES UdefSan) 48 | set(ALLSAN_FLAGS "${ALLSAN_FLAGS} ${UDEF_SAN_FLAGS}") 49 | endif() 50 | set(CMAKE_C_FLAGS_UDEFSAN "${CMAKE_C_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C compiler during Undefined_BehaviourSanitizer builds." FORCE) 51 | set(CMAKE_CXX_FLAGS_UDEFSAN "${CMAKE_CXX_FLAGS_DEBUG} ${UDEF_SAN_FLAGS}" CACHE INTERNAL "Flags used by the C++ compiler during Undefined_BehaviourSanitizer builds." FORCE) 52 | 53 | # AllSanetizer 54 | if(NOT ALLSAN_FLAGS STREQUAL "") 55 | set(PREV_FLAG ${CMAKE_REQUIRED_FLAGS}) 56 | set(CMAKE_REQUIRED_FLAGS "${ALLSAN_FLAGS}") 57 | check_cxx_source_runs("int main() { return 0; }" ALL_SANITIZERS_AVAILABLE) 58 | set(CMAKE_REQUIRED_FLAGS ${PREV_FLAG}) 59 | if(ALL_SANITIZERS_AVAILABLE) 60 | list(APPEND ALLOWED_BUILD_TYPES AllSan) 61 | endif() 62 | endif() 63 | 64 | set(CMAKE_C_FLAGS_ALLSAN "${CMAKE_C_FLAGS_DEBUG} ${ALLSAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C compiler during All Possible Sanetizer builds." FORCE) 65 | set(CMAKE_CXX_FLAGS_ALLSAN "${CMAKE_CXX_FLAGS_DEBUG} ${ALLSAN_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE INTERNAL "Flags used by the C++ compiler during All Possible Sanetizer builds." FORCE) 66 | -------------------------------------------------------------------------------- /protocol_snoops/keyexchange.md: -------------------------------------------------------------------------------- 1 | ## Hello 2 | ``` 3 | [D]: TY:\r\n 4 | [C]: ty:EF532M V02.03\r\n 5 | ``` 6 | 7 | ## Hello without \r\n 8 | ``` 9 | [D]: TY: 10 | [C]: ty:EF532M V02.03 11 | ``` 12 | 13 | ## Key exchange 14 | ``` 15 | [D]: @T1\r\n 16 | [C]: @t1\r\n 17 | [C]: @T2:010001B228\r\n 18 | [D]: @t2:8120000000\r\n 19 | [C]: @T3:3BDEEF532M V02.03\r\n 20 | [D]: @t3\r\n 21 | ``` 22 | 23 | ## Key exchange without \r\n 24 | ``` 25 | [D]: @T1 26 | [C]: @t1 27 | [C]: @T2:010001B228 28 | [D]: @t2:8120000000 29 | [C]: @T3:3BDEEF532M V02.03 30 | [D]: @t3 31 | ``` -------------------------------------------------------------------------------- /protocol_snoops/snoop_bluetooth_1.md: -------------------------------------------------------------------------------- 1 | ``` 2 | Checking Python dependencies... 3 | Python requirements from /home/fabian/Documents/ESP32/esp-idf/requirements.txt are satisfied. 4 | Executing action: monitor 5 | Running idf_monitor in directory /home/fabian/Documents/ESP32/esp32-jura/esp32 6 | Executing "/usr/bin/python /home/fabian/Documents/ESP32/esp-idf/tools/idf_monitor.py -p /dev/ttyUSB0 -b 115200 /home/fabian/Documents/ESP32/esp32-jura/esp32/build/esp32Jura.elf -m '/usr/bin/python' '/home/fabian/Documents/ESP32/esp-idf/tools/idf.py' '-p' '/dev/ttyUSB0'"... 7 | --- idf_monitor on /dev/ttyUSB0 115200 --- 8 | --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- 9 | ets Jun 8 2016 00:22:57 10 | 11 | rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) 12 | configsip: 0, SPIWP:0xee 13 | clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 14 | mode:DIO, clock div:2 15 | load:0x3fff0018,len:4 16 | load:0x3fff001c,len:6504 17 | load:0x40078000,len:11368 18 | load:0x40080400,len:6644 19 | entry 0x4008076c 20 | I (28) boot: ESP-IDF v4.1-dev 2nd stage bootloader 21 | I (29) boot: compile time 15:14:25 22 | I (29) boot: Enabling RNG early entropy source... 23 | I (33) boot: SPI Speed : 40MHz 24 | I (37) boot: SPI Mode : DIO 25 | I (41) boot: SPI Flash Size : 2MB 26 | I (45) boot: Partition Table: 27 | I (49) boot: ## Label Usage Type ST Offset Length 28 | I (56) boot: 0 nvs WiFi data 01 02 00009000 00006000 29 | I (64) boot: 1 phy_init RF data 01 01 0000f000 00001000 30 | I (71) boot: 2 factory factory app 00 00 00010000 00100000 31 | I (79) boot: End of partition table 32 | I (83) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x31e6c (204396) map 33 | I (165) esp_image: segment 1: paddr=0x00041e94 vaddr=0x3ffb0000 size=0x033d4 ( 13268) load 34 | I (170) esp_image: segment 2: paddr=0x00045270 vaddr=0x40080000 size=0x00400 ( 1024) load 35 | 0x40080000: _WindowOverflow4 at /home/fabian/Documents/ESP32/esp-idf/components/freertos/xtensa_vectors.S:1778 36 | 37 | I (172) esp_image: segment 3: paddr=0x00045678 vaddr=0x40080400 size=0x0a998 ( 43416) load 38 | I (199) esp_image: segment 4: paddr=0x00050018 vaddr=0x400d0018 size=0xa7cdc (687324) map 39 | 0x400d0018: _stext at ??:? 40 | 41 | I (445) esp_image: segment 5: paddr=0x000f7cfc vaddr=0x4008ad98 size=0x06a7c ( 27260) load 42 | 0x4008ad98: block_data_size at /home/fabian/Documents/ESP32/esp-idf/components/heap/multi_heap.c:153 43 | (inlined by) multi_heap_realloc_impl at /home/fabian/Documents/ESP32/esp-idf/components/heap/multi_heap.c:539 44 | 45 | I (468) boot: Loaded app from partition at offset 0x10000 46 | I (468) boot: Disabling RNG early entropy source... 47 | I (468) cpu_start: Pro cpu up. 48 | I (472) cpu_start: Application information: 49 | I (477) cpu_start: Project name: esp32Jura 50 | I (482) cpu_start: App version: 24fc3fd-dirty 51 | I (487) cpu_start: Compile time: May 13 2020 12:47:35 52 | I (493) cpu_start: ELF file SHA256: 1994ec26dc6041e3... 53 | I (499) cpu_start: ESP-IDF: v4.1-dev 54 | I (504) cpu_start: Starting app cpu, entry point is 0x40081324 55 | 0x40081324: call_start_cpu1 at /home/fabian/Documents/ESP32/esp-idf/components/esp32/cpu_start.c:281 56 | 57 | I (0) cpu_start: App cpu up. 58 | I (515) heap_init: Initializing. RAM available for dynamic allocation: 59 | I (522) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM 60 | I (528) heap_init: At 3FFBAD90 len 00025270 (148 KiB): DRAM 61 | I (534) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM 62 | I (540) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM 63 | I (547) heap_init: At 40091814 len 0000E7EC (57 KiB): IRAM 64 | I (553) cpu_start: Pro cpu start user code 65 | I (575) spi_flash: detected chip: generic 66 | I (575) spi_flash: flash io: dio 67 | W (575) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. 68 | I (586) cpu_start: Chip Revision: 1 69 | I (590) cpu_start: Starting scheduler on PRO CPU. 70 | I (0) cpu_start: Starting scheduler on APP CPU. 71 | I (684) FSLock: Max number of open files set to: 5 72 | ESP32 Jura initializing... 73 | ESP32 Jura initialized. 74 | Initializing JURA snooper... 75 | Initializing JURA connection on port (Port: 1 , TX: 23 , RX: 25)... 76 | JURA connection on port (Port: 1 , TX: 23 , RX: 25) has been initialized successfully. 77 | Initializing JURA connection on port (Port: 2 , TX: 14 , RX: 27)... 78 | JURA connection on port (Port: 2 , TX: 14 , RX: 27) has been initialized successfully. 79 | JURA snooper initialized. 80 | -------------------------Dongle:--------------------------- 81 | -----------------------Coffee-Maker:----------------------- 82 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 83 | 0 0 1 0 1 1 0 0 -> 44 2c , | 52 34 4 | 194 c2 � 84 | 1 0 1 0 1 0 0 1 -> 169 a9 � | 149 95 � | 154 9a � 85 | 0 1 0 1 0 0 1 0 -> 82 52 R | 74 4a J | 37 25 % 86 | 1 1 1 1 0 1 1 0 -> 246 f6 � | 111 6f o | 111 6f o 87 | 1 1 1 0 0 1 1 0 -> 230 e6 � | 103 67 g | 110 6e n 88 | 0 0 1 1 1 0 1 1 -> 59 3b ; | 220 dc � | 179 b3 � 89 | 0 1 1 1 0 0 1 1 -> 115 73 s | 206 ce � | 55 37 7 90 | 1 0 0 0 1 0 0 0 -> 136 88 � | 17 11 | 136 88 � 91 | 1 1 1 1 0 1 0 0 -> 244 f4 � | 47 2f / | 79 4f O 92 | 1 1 1 1 1 0 1 1 -> 251 fb � | 223 df � | 191 bf � 93 | 1 1 0 0 0 1 1 1 -> 199 c7 � | 227 e3 � | 124 7c | 94 | 1 0 1 1 0 0 0 1 -> 177 b1 � | 141 8d � | 27 1b 95 | 0 0 1 0 0 1 1 -> 147 93 � | 201 c9 � | 57 39 9 96 | 0 1 1 1 0 1 0 1 -> 117 75 u | 174 ae � | 87 57 W 97 | 0 0 1 0 1 0 0 1 -> 41 29 ) | 148 94 � | 146 92 � 98 | 0 0 1 1 1 0 1 1 -> 59 3b ; | 220 dc � | 179 b3 � 99 | 1 0 0 0 1 0 1 1 -> 139 8b � | 209 d1 � | 184 b8 � 100 | 1 0 0 0 1 1 0 0 -> 140 8c � | 49 31 1 | 200 c8 � 101 | 0 0 0 0 1 1 1 0 -> 14 0e | 112 70 p | 224 e0 � 102 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 103 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 104 | 105 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 106 | 0 1 1 0 0 1 1 1 -> 103 67 g | 230 e6 � | 118 76 v 107 | 1 0 0 1 1 0 1 1 -> 155 9b � | 217 d9 � | 185 b9 � 108 | 0 1 1 1 0 1 0 0 -> 116 74 t | 46 2e . | 71 47 G 109 | 1 1 1 1 0 0 1 0 -> 242 f2 � | 79 4f O | 47 2f / 110 | 1 0 0 0 1 0 1 1 -> 139 8b � | 209 d1 � | 184 b8 � 111 | 1 1 0 0 0 1 1 1 -> 199 c7 � | 227 e3 � | 124 7c | 112 | 1 0 1 1 0 0 0 1 -> 177 b1 � | 141 8d � | 27 1b 113 | 0 0 1 0 0 1 1 -> 147 93 � | 201 c9 � | 57 39 9 114 | 1 0 1 0 0 1 0 1 -> 165 a5 � | 165 a5 � | 90 5a Z 115 | 0 0 1 1 0 1 1 0 -> 54 36 6 | 108 6c l | 99 63 c 116 | 0 0 0 1 1 1 0 1 -> 29 1d | 184 b8 � | 209 d1 � 117 | 1 0 1 1 0 0 1 1 -> 179 b3 � | 205 cd � | 59 3b ; 118 | 0 0 1 0 0 0 0 0 -> 32 20 | 4 04 | 202 119 | 0 0 1 1 1 1 0 1 -> 61 3d = | 188 bc � | 211 d3 � 120 | 0 1 1 0 1 0 0 1 -> 105 69 i | 150 96 � | 150 96 � 121 | 1 1 1 1 1 0 0 1 -> 249 f9 � | 159 9f � | 159 9f � 122 | 1 0 1 1 1 0 0 0 -> 184 b8 � | 29 1d | 139 8b � 123 | 1 0 0 1 0 1 0 1 -> 149 95 � | 169 a9 � | 89 59 Y 124 | 0 0 1 0 0 0 1 0 -> 34 22 " | 68 44 D | 34 22 " 125 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 126 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 127 | 128 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 129 | 0 1 1 1 1 1 0 1 -> 125 7d } | 190 be � | 215 d7 � 130 | 0 0 0 1 1 1 1 0 -> 30 1e | 120 78 x | 225 e1 � 131 | 1 1 1 0 1 1 1 0 -> 238 ee � | 119 77 w | 238 ee � 132 | 1 0 0 0 1 0 1 1 -> 139 8b � | 209 d1 � | 184 b8 � 133 | 0 1 1 1 1 0 0 0 -> 120 78 x | 30 1e | 135 87 � 134 | 1 0 0 1 1 0 1 1 -> 155 9b � | 217 d9 � | 185 b9 � 135 | 0 0 0 1 0 1 0 1 -> 21 15 | 168 a8 � | 81 51 Q 136 | 0 1 1 0 1 1 0 1 -> 109 6d m | 182 b6 � | 214 d6 � 137 | 0 1 0 0 0 1 1 0 -> 70 46 F | 98 62 b | 100 64 d 138 | 1 1 0 1 0 0 0 0 -> 208 d0 � | 11 0b 139 | | 13 0d 140 | 1 1 1 1 1 1 1 1 -> 255 ff � | 255 ff � | 255 ff � 141 | 0 1 0 1 1 0 1 0 -> 90 5a Z | 90 5a Z | 165 a5 � 142 | 0 1 1 0 1 0 1 1 -> 107 6b k | 214 d6 � | 182 b6 � 143 | 0 1 1 1 0 1 1 1 -> 119 77 w | 238 ee � | 119 77 w 144 | 1 0 0 0 1 0 1 0 -> 138 8a � | 81 51 Q | 168 a8 � 145 | 0 0 1 1 1 1 1 1 -> 63 3f ? | 252 fc � | 243 f3 � 146 | 1 1 0 1 0 0 0 1 -> 209 d1 � | 139 8b � | 29 1d 147 | 1 1 0 1 0 1 0 1 -> 213 d5 � | 171 ab � | 93 5d ] 148 | 0 1 1 0 0 1 0 1 -> 101 65 e | 166 a6 � | 86 56 V 149 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 150 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 151 | 152 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 153 | 0 0 0 0 0 0 0 1 -> 1 01 | 128 80 � | 16 10 154 | 0 0 1 1 0 0 1 0 -> 50 32 2 | 76 4c L | 35 23 # 155 | 0 0 0 0 0 0 1 0 -> 2 02 | 64 40 @ | 32 20 156 | 0 0 1 1 1 1 1 1 -> 63 3f ? | 252 fc � | 243 f3 � 157 | 0 1 0 1 0 1 1 1 -> 87 57 W | 234 ea � | 117 75 u 158 | 0 1 1 1 0 1 0 1 -> 117 75 u | 174 ae � | 87 57 W 159 | 0 1 1 1 1 0 1 1 -> 123 7b { | 222 de � | 183 b7 � 160 | 1 1 0 0 0 0 1 0 -> 194 c2 � | 67 43 C | 44 2c , 161 | 1 1 1 1 1 0 0 0 -> 248 f8 � | 31 1f | 143 8f � 162 | 0 1 1 1 1 0 1 1 -> 123 7b { | 222 de � | 183 b7 � 163 | 1 0 1 1 1 0 1 0 -> 186 ba � | 93 5d ] | 171 ab � 164 | 0 0 1 1 1 1 1 1 -> 63 3f ? | 252 fc � | 243 f3 � 165 | 1 1 0 1 0 1 0 0 -> 212 d4 � | 43 2b + | 77 4d M 166 | 0 0 0 1 1 1 1 1 -> 31 1f | 248 f8 � | 241 f1 � 167 | 1 1 1 0 0 1 0 0 -> 228 e4 � | 39 27 ' | 78 4e N 168 | 1 0 0 1 1 0 1 0 -> 154 9a � | 89 59 Y | 169 a9 � 169 | 0 1 0 0 1 0 1 0 -> 74 4a J | 82 52 R | 164 a4 � 170 | 1 0 0 1 1 1 0 1 -> 157 9d � | 185 b9 � | 217 d9 � 171 | 1 0 0 0 0 1 0 0 -> 132 84 � | 33 21 ! | 72 48 H 172 | 1 0 1 1 1 0 1 1 -> 187 bb � | 221 dd � | 187 bb � 173 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 174 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 175 | 176 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 177 | 0 1 1 0 0 1 0 1 -> 101 65 e | 166 a6 � | 86 56 V 178 | 1 1 0 0 0 1 0 1 -> 197 c5 � | 163 a3 � | 92 5c \ 179 | 1 1 0 1 1 0 1 0 -> 218 da � | 91 5b [ | 173 ad � 180 | 0 0 0 0 1 0 0 0 -> 8 08 | 16 10 | 128 80 � 181 | 0 0 0 1 0 1 0 1 -> 21 15 | 168 a8 � | 81 51 Q 182 | 1 0 0 1 1 0 0 1 -> 153 99 � | 153 99 � | 153 99 � 183 | 0 0 1 1 1 0 1 1 -> 59 3b ; | 220 dc � | 179 b3 � 184 | 1 0 0 0 1 0 1 1 -> 139 8b � | 209 d1 � | 184 b8 � 185 | 0 0 1 0 1 0 1 1 -> 43 2b + | 212 d4 � | 178 b2 � 186 | 1 0 1 0 1 1 0 1 -> 173 ad � | 181 b5 � | 218 da � 187 | 1 1 1 1 1 0 0 1 -> 249 f9 � | 159 9f � | 159 9f � 188 | 1 1 1 1 1 0 0 1 -> 249 f9 � | 159 9f � | 159 9f � 189 | 1 0 1 1 1 0 0 0 -> 184 b8 � | 29 1d | 139 8b � 190 | 0 1 0 1 0 1 1 0 -> 86 56 V | 106 6a j | 101 65 e 191 | 0 0 0 0 | 1 0 1 17613 b0 � | 208 d0 � 192 | 1 0 1 1 0 0 1 1 -> 179 b3 � | 205 cd � | 59 3b ; 193 | 0 0 1 0 0 0 0 0 -> 32 20 | 4 04 | 202 194 | 1 0 1 1 1 0 1 1 -> 187 bb � | 221 dd � | 187 bb � 195 | 0 0 1 1 0 1 0 1 -> 53 35 5 | 172 ac � | 83 53 S 196 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 197 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 198 | 199 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 200 | 0 0 1 1 0 0 0 1 -> 49 31 1 | 140 8c � | 19 13 201 | 1 0 1 0 1 0 1 0 -> 170 aa � | 85 55 U | 170 aa � 202 | 0 0 1 0 0 0 1 1 -> 35 23 # | 196 c4 � | 50 32 2 203 | 1 0 1 1 1 1 1 1 -> 191 bf � | 253 fd � | 251 fb � 204 | 0 0 1 1 1 0 1 0 -> 58 3a : | 92 5c \ | 163 a3 � 205 | 0 1 0 0 0 0 0 1 -> 65 41 A | 130 82 � | 20 14 206 | 0 1 1 1 1 0 1 0 -> 122 7a z | 94 5e ^ | 167 a7 � 207 | 0 0 0 1 0 0 0 0 -> 16 10 | 8 08 | 1 01 208 | 0 0 1 0 0 1 0 1 -> 37 25 % | 164 a4 � | 82 52 R 209 | 0 0 0 1 1 1 0 1 -> 29 1d | 184 b8 � | 209 d1 � 210 | 1 0 0 1 1 0 1 0 -> 154 9a � | 89 59 Y | 169 a9 � 211 | -------------------------Dongle:--------------------------- 212 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 213 | 0 0 1 0 1 1 1 0 -> 46 2e . | 116 74 t | 226 e2 � 214 | 0 0 1 1 0 1 0 0 -> 52 34 4 | 44 2c , | 67 43 C 215 | 1 1 1 1 0 1 1 0 -> 246 f6 � | 111 6f o | 111 6f o 216 | 1 0 0 0 1 0 1 1 -> 139 8b � | 209 d1 � | 184 b8 � 217 | 1 0 0 0 0 1 0 0 -> 132 84 � | 33 21 ! | 72 48 H 218 | 1 0 1 0 1 1 1 0 -> 174 ae � | 117 75 u | 234 ea � 219 | 1 0 1 0 1 0 0 1 -> 169 a9 � | 149 95 � | 154 9a � 220 | 1 1 1 0 0 0 1 1 -> 227 e3 � | 199 c7 � | 62 3e > 221 | 1 0 0 0 0 1 1 0 -> 134 86 � | 97 61 a | 104 68 h 222 | 1 1 0 0 0 1 0 1 -> 197 c5 � | 163 a3 � | 92 5c \ 223 | 1 0 0 1 1 0 0 1 -> 153 99 � | 153 99 � | 153 99 � 224 | 0 0 1 1 1 0 1 1 -> 59 3b ; | 220 dc � | 179 b3 � 225 | 1 0 0 0 1 0 1 1 -> 139 8b � | 209 d1 � | 184 b8 � 226 | 1 1 1 1 1 0 1 1 -> 251 fb � | 223 df � | 191 bf � 227 | 1 1 0 0 0 1 1 1 -> 199 c7 � | 227 e3 � | 124 7c | 228 | 0 0 1 1 0 0 0 1 -> 49 31 1 | 140 8c � | 19 13 229 | 0 0 0 1 0 0 1 1 -> 19 13 | 200 c8 � | 49 31 1 230 | 1 0 1 1 1 0 0 0 -> 184 b8 � | 29 1d | 139 8b � 231 | 0 0 1 1 0 1 1 0 -> 54 36 6 | 108 6c l | 99 63 c 232 | 0 0 0 1 1 1 0 1 -> 29 1d | 184 b8 � | 209 d1 � 233 | 1 0 1 1 0 0 1 1 -> 179 b3 � | 205 cd � | 59 3b ; 234 | 0 0 1 0 0 0 0 0 -> 32 20 | 4 04 | 202 235 | 1 0 0 1 1 1 0 1 -> 157 9d � | 185 b9 � | 217 d9 � 236 | 1 1 1 1 1 0 0 1 -> 249 f9 � | 159 9f � | 159 9f � 237 | 1 1 1 1 1 0 0 1 -> 249 f9 � | 159 9f � | 159 9f � 238 | 1 0 0 1 0 0 1 1 -> 147 93 � | 201 c9 � | 57 39 9 239 | 1 1 1 0 0 1 0 0 -> 228 e4 � | 39 27 ' | 78 4e N 240 | 1 1 1 0 0 1 1 0 -> 230 e6 � | 103 67 g | 110 6e n 241 | 0 1 1 0 1 0 0 1 -> 105 69 i | 150 96 � | 150 96 � 242 | -----------------------Coffee-Maker:----------------------- 243 | 0 1 0 0 1 1 0 1 -> 77 4d M | 178 b2 � | 212 d4 � 244 | 1 1 1 1 0 0 0 0 -> 240 f0 � | 15 0f | 15 0f 245 | 1 1 0 0 0 0 0 0 -> 192 c0 � | 3 03 | 12 0c 246 | 247 | 1 0 1 0 1 0 0 1 -> 169 a9 � | 149 95 � | 154 9a � 248 | 1 0 1 0 0 1 0 0 -> 164 a4 � | 37 25 % | 74 4a J 249 | 0 1 1 0 1 1 1 1 -> 111 6f o | 246 f6 � | 246 f6 � 250 | 0 0 1 1 0 0 1 1 -> 51 33 3 | 204 cc � | 51 33 3 251 | 0 1 0 0 1 1 0 1 -> 77 4d M | 178 b2 � | 212 d4 � 252 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 253 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 254 | 255 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 256 | 0 1 0 1 1 1 0 1 -> 93 5d ] | 186 ba � | 213 d5 � 257 | -------------------------Dongle:--------------------------- 258 | 1 1 1 1 1 1 1 1 -> 255 ff � | 255 ff � | 255 ff � 259 | 1 0 1 1 1 0 1 0 -> 186 ba � | 93 5d ] | 171 ab � 260 | 0 0 1 0 1 0 1 0 -> 42 2a * | 84 54 T | 162 a2 � 261 | 1 1 0 0 1 0 0 0 -> 200 c8 � | 19 13 | 140 8c � 262 | 0 1 1 0 0 0 1 0 -> 98 62 b | 70 46 F | 38 26 & 263 | 1 0 1 1 0 1 0 1 -> 181 b5 � | 173 ad � | 91 5b [ 264 | 1 1 0 0 0 1 0 0 -> 196 c4 � | 35 23 # | 76 4c L 265 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 266 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 267 | 268 | -----------------------Coffee-Maker:----------------------- 269 | 0 1 0 1 0 0 1 0 -> 82 52 R | 74 4a J | 37 25 % 270 | 1 0 1 0 1 0 1 0 -> 170 aa � | 85 55 U | 170 aa � 271 | 0 0 0 1 1 0 0 0 -> 24 18 | 24 18 | 129 81 � 272 | 1 1 1 1 0 1 1 0 -> 246 f6 � | 111 6f o | 111 6f o 273 | 1 0 0 0 0 1 1 0 -> 134 86 � | 97 61 a | 104 68 h 274 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 275 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 276 | 277 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 278 | 1 1 1 1 0 1 0 1 -> 245 f5 � | 175 af � | 95 5f _ 279 | 0 0 1 0 0 0 1 1 -> 35 23 # | 196 c4 � | 50 32 2 280 | 1 0 0 1 0 0 0 0 -> 144 90 � | 9 09 |9 09 281 | 1 0 1 1 1 0 1 1 -> 187 bb � | 221 dd � | 187 bb � 282 | 1 0 1 0 1 0 1 0 -> 170 aa � | 85 55 U | 170 aa � 283 | 0 0 1 0 1 1 0 1 -> 45 2d - | 180 b4 � | 210 d2 � 284 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 285 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 286 | 287 | -------------------------Dongle:--------------------------- 288 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 289 | 1 0 1 1 0 0 1 0 -> 178 b2 � | 77 4d M | 43 2b + 290 | 1 0 1 1 0 0 1 1 -> 179 b3 � | 205 cd � | 59 3b ; 291 | 0 0 1 1 1 1 0 1 -> 61 3d = | 188 bc � | 211 d3 � 292 | 1 0 1 1 | 0 0 0 11 176 0b � | 13 0d 293 | 294 | 1 0 0 1 0 0 0 1 -> 145 91 � | 137 89 � | 25 19 295 | 1 0 0 0 0 1 1 0 -> 134 86 � | 97 61 a | 104 68 h 296 | 0 0 0 1 0 0 0 0 -> 16 10 | 8 08 | 1 01 297 | 0 0 1 1 0 0 1 1 -> 51 33 3 | 204 cc � | 51 33 3 298 | 0 1 0 0 1 1 0 1 -> 77 4d M | 178 b2 � | 212 d4 � 299 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 300 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 301 | 302 | -----------------------Coffee-Maker:----------------------- 303 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 304 | 1 1 1 1 1 0 0 1 -> 249 f9 � | 159 9f � | 159 9f � 305 | 0 1 0 1 1 1 1 1 -> 95 5f _ | 250 fa � | 245 f5 � 306 | 1 0 1 1 0 1 1 1 -> 183 b7 � | 237 ed � | 123 7b { 307 | 0 1 1 0 1 1 0 0 -> 108 6c l | 54 36 6 | 198 c6 � 308 | 1 0 1 1 1 1 0 1 -> 189 bd � | 189 bd � | 219 db � 309 | 1 1 1 0 0 0 0 1 -> 225 e1 � | 135 87 � | 30 1e 310 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 311 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 312 | 313 | 1 0 0 1 1 0 0 0 -> 152 98 � | 25 19 | 137 89 � 314 | 0 0 1 1 0 0 0 1 -> 49 31 1 | 140 8c � | 19 13 315 | 1 0 1 0 1 0 1 0 -> 170 aa � | 85 55 U | 170 aa � 316 | 0 0 1 0 0 0 1 1 -> 35 23 # | 196 c4 � | 50 32 2 317 | 1 0 1 1 1 1 1 0 -> 190 be � | 125 7d } | 235 eb � 318 | 0 0 1 1 1 0 1 0 -> 58 3a : | 92 5c \ | 163 a3 � 319 | 0 1 0 0 0 0 0 1 -> 65 41 A | 130 82 � | 20 14 320 | 1 0 0 0 1 1 1 1 -> 143 8f � | 241 f1 � | 248 f8 � 321 | 0 0 0 1 0 0 0 0 -> 16 10 | 8 08 | 1 01 322 | 0 0 0 1 0 1 0 1 -> 21 15 | 168 a8 � | 81 51 Q 323 | 1 1 1 0 0 1 1 0 -> 230 e6 � | 103 67 g | 110 6e n 324 | 1 1 1 1 0 1 1 1 -> 247 f7 � | 239 ef � | 127 7f 325 | 0 1 1 1 0 0 0 0 -> 112 70 p | 14 0e | 707 326 | 1 0 1 0 0 0 0 0 -> 160 a0 � | 5 05 | 10 0a 327 | ``` -------------------------------------------------------------------------------- /protocol_snoops/snoop_hello_1.md: -------------------------------------------------------------------------------- 1 | ``` 2 | -------------------------Dongle:--------------------------- 3 | 0 1 0 1 0 1 0 0 -> 84 54 T 4 | 0 1 0 1 1 0 0 1 -> 89 59 Y 5 | 0 0 1 1 1 0 1 0 -> 58 3a : 6 | 0 0 0 0 1 1 0 1 -> 13 0d 7 | 0 0 0 0 1 0 1 0 -> 10 0a 8 | 9 | -----------------------Coffee-Maker:----------------------- 10 | 0 1 1 1 0 1 0 0 -> 116 74 t 11 | 0 1 1 1 1 0 0 1 -> 121 79 y 12 | 0 0 1 1 1 0 1 0 -> 58 3a : 13 | 0 1 0 0 0 1 0 1 -> 69 45 E 14 | 0 1 0 0 0 1 1 0 -> 70 46 F 15 | 0 0 1 1 0 1 0 1 -> 53 35 5 16 | 0 0 1 1 0 0 1 1 -> 51 33 3 17 | 0 0 1 1 0 0 1 0 -> 50 32 2 18 | 0 1 0 0 1 1 0 1 -> 77 4d M 19 | 0 0 1 0 0 0 0 0 -> 32 20 20 | 0 1 0 1 0 1 1 0 -> 86 56 V 21 | 0 0 1 1 0 0 0 0 -> 48 30 0 22 | 0 0 1 1 0 0 1 0 -> 50 32 2 23 | 0 0 1 0 1 1 1 0 -> 46 2e . 24 | 0 0 1 1 0 0 0 0 -> 48 30 0 25 | 0 0 1 1 0 0 1 1 -> 51 33 3 26 | 0 0 0 0 1 1 0 1 -> 13 0d 27 | 0 0 0 0 1 0 1 0 -> 10 0a 28 | 29 | -------------------------Dongle:--------------------------- 30 | 0 1 0 0 0 0 0 0 -> 64 40 @ 31 | 0 1 0 1 0 1 0 0 -> 84 54 T 32 | 0 0 1 1 0 0 0 1 -> 49 31 1 33 | 0 0 0 0 1 1 0 1 -> 13 0d 34 | 0 0 0 0 1 0 1 0 -> 10 0a 35 | 36 | -----------------------Coffee-Maker:----------------------- 37 | 0 1 0 0 0 0 0 0 -> 64 40 @ 38 | 0 1 1 1 0 1 0 0 -> 116 74 t 39 | 0 0 1 1 0 0 0 1 -> 49 31 1 40 | 0 0 0 0 1 1 0 1 -> 13 0d 41 | 0 0 0 0 1 0 1 0 -> 10 0a 42 | 43 | 0 1 0 0 0 0 0 0 -> 64 40 @ 44 | 0 1 0 1 0 1 0 0 -> 84 54 T 45 | 0 0 1 1 0 0 1 0 -> 50 32 2 46 | 0 0 1 1 1 0 1 0 -> 58 3a : 47 | 0 0 1 1 0 0 0 0 -> 48 30 0 48 | 0 0 1 1 0 0 0 1 -> 49 31 1 49 | 0 0 1 1 0 0 0 0 -> 48 30 0 50 | 0 0 1 1 0 0 0 0 -> 48 30 0 51 | 0 0 1 1 0 0 0 0 -> 48 30 0 52 | 0 0 1 1 0 0 0 1 -> 49 31 1 53 | 0 1 0 0 0 0 1 0 -> 66 42 B 54 | 0 0 1 1 0 0 1 0 -> 50 32 2 55 | 0 0 1 1 0 0 1 0 -> 50 32 2 56 | 0 0 1 1 1 0 0 0 -> 56 38 8 57 | 0 0 0 0 1 1 0 1 -> 13 0d 58 | 0 0 0 0 1 0 1 0 -> 10 0a 59 | 60 | -------------------------Dongle:--------------------------- 61 | 0 1 0 0 0 0 0 0 -> 64 40 @ 62 | 0 1 1 1 0 1 0 0 -> 116 74 t 63 | 0 0 1 1 0 0 1 0 -> 50 32 2 64 | 0 0 1 1 1 0 1 0 -> 58 3a : 65 | 0 0 1 1 1 0 0 0 -> 56 38 8 66 | 0 0 1 1 0 0 0 1 -> 49 31 1 67 | 0 0 1 1 0 0 1 0 -> 50 32 2 68 | 0 0 1 1 0 0 0 0 -> 48 30 0 69 | 0 0 1 1 0 0 0 0 -> 48 30 0 70 | 0 0 1 1 0 0 0 0 -> 48 30 0 71 | 0 0 1 1 0 0 0 0 -> 48 30 0 72 | 0 0 1 1 0 0 0 0 -> 48 30 0 73 | 0 0 1 1 0 0 0 0 -> 48 30 0 74 | 0 0 1 1 0 0 0 0 -> 48 30 0 75 | 0 0 0 0 1 1 0 1 -> 13 0d 76 | 0 0 0 0 1 0 1 0 -> 10 0a 77 | 78 | -----------------------Coffee-Maker:----------------------- 79 | 0 1 0 0 0 0 0 0 -> 64 40 @ 80 | 0 1 0 1 0 1 0 0 -> 84 54 T 81 | 0 0 1 1 0 0 1 1 -> 51 33 3 82 | 0 0 1 1 1 0 1 0 -> 58 3a : 83 | 0 0 1 1 0 0 1 1 -> 51 33 3 84 | 0 1 0 0 0 0 1 0 -> 66 42 B 85 | 0 1 0 0 0 1 0 0 -> 68 44 D 86 | 0 1 0 0 0 1 0 1 -> 69 45 E 87 | 0 1 0 0 0 1 0 1 -> 69 45 E 88 | 0 1 0 0 0 1 1 0 -> 70 46 F 89 | 0 0 1 1 0 1 0 1 -> 53 35 5 90 | 0 0 1 1 0 0 1 1 -> 51 33 3 91 | 0 0 1 1 0 0 1 0 -> 50 32 2 92 | 0 1 0 0 1 1 0 1 -> 77 4d M 93 | 0 0 1 0 0 0 0 0 -> 32 20 94 | 0 1 0 1 0 1 1 0 -> 86 56 V 95 | 0 0 1 1 0 0 0 0 -> 48 30 0 96 | 0 0 1 1 0 0 1 0 -> 50 32 2 97 | 0 0 1 0 1 1 1 0 -> 46 2e . 98 | 0 0 1 1 0 0 0 0 -> 48 30 0 99 | 0 0 1 1 0 0 1 1 -> 51 33 3 100 | 0 0 0 0 1 1 0 1 -> 13 0d 101 | 0 0 0 0 1 0 1 0 -> 10 0a 102 | 103 | -------------------------Dongle:--------------------------- 104 | 0 1 0 0 0 0 0 0 -> 64 40 @ 105 | 0 1 1 1 0 1 0 0 -> 116 74 t 106 | 0 0 1 1 0 0 1 1 -> 51 33 3 107 | 0 0 0 0 1 1 0 1 -> 13 0d 108 | 0 0 0 0 1 0 1 0 -> 10 0a 109 | 110 | -----------------------Coffee-Maker:----------------------- 111 | 0 0 1 0 0 1 1 0 -> 38 26 & 112 | 1 1 0 0 1 1 1 0 -> 206 ce � 113 | 0 0 1 0 0 1 0 0 -> 36 24 $ 114 | 1 0 1 0 1 0 0 0 -> 168 a8 � 115 | 0 1 0 1 0 0 0 1 -> 81 51 Q 116 | 0 0 0 0 1 1 0 0 -> 12 0c 117 | 118 | 0 0 0 1 0 0 1 1 -> 19 13 119 | 1 1 1 1 0 0 1 1 -> 243 f3 � 120 | 1 0 1 0 1 0 0 1 -> 169 a9 � 121 | 1 0 0 1 1 1 0 1 -> 157 9d � 122 | 1 0 1 1 0 1 1 1 -> 183 b7 � 123 | 1 1 1 1 0 0 0 1 -> 241 f1 � 124 | 0 1 1 1 0 1 1 1 -> 119 77 w 125 | 1 0 0 0 1 1 0 1 -> 141 8d � 126 | 1 1 1 1 0 0 0 0 -> 240 f0 � 127 | 0 0 1 0 1 0 1 0 -> 42 2a * 128 | 0 1 0 1 0 1 0 1 -> 85 55 U 129 | 0 1 1 1 1 0 1 0 -> 122 7a z 130 | 1 0 1 1 0 0 0 0 -> 176 b0 � 131 | 0 0 1 0 0 1 0 1 -> 37 25 % 132 | 0 0 0 0 1 1 0 1 -> 13 0d 133 | 0 0 0 0 1 0 1 0 -> 10 0a 134 | 135 | -------------------------Dongle:--------------------------- 136 | 0 0 1 0 0 1 1 0 -> 38 26 & 137 | 1 1 0 1 1 1 1 1 -> 223 df � 138 | 1 1 1 1 0 1 0 0 -> 244 f4 � 139 | 0 1 1 1 0 0 0 1 -> 113 71 q 140 | 0 1 1 0 1 0 0 1 -> 105 69 i 141 | 0 0 0 1 0 0 0 0 -> 16 10 142 | 1 0 1 0 0 0 1 0 -> 162 a2 � 143 | 1 1 0 0 1 1 0 1 -> 205 cd � 144 | 0 1 0 1 1 1 1 1 -> 95 5f _ 145 | 1 0 1 0 1 1 0 1 -> 173 ad � 146 | 0 0 0 0 1 1 0 1 -> 13 0d 147 | 0 0 0 0 1 0 1 0 -> 10 0a 148 | 149 | -----------------------Coffee-Maker:----------------------- 150 | 0 0 1 0 0 1 1 0 -> 38 26 & 151 | 1 1 1 1 1 0 0 1 -> 249 f9 � 152 | 1 1 0 1 1 1 0 0 -> 220 dc � 153 | 1 1 0 1 0 0 0 1 -> 209 d1 � 154 | 1 1 0 1 1 0 1 0 -> 218 da � 155 | 1 1 0 1 1 0 1 1 -> 219 db � 156 | 0 1 0 0 1 0 0 0 -> 72 48 H 157 | 1 1 0 0 1 0 1 0 -> 202 ca � 158 | 0 1 1 1 0 0 0 1 -> 113 71 q 159 | 1 1 1 0 0 1 1 1 -> 231 e7 � 160 | 1 0 0 0 0 1 1 1 -> 135 87 � 161 | 1 1 0 0 1 0 0 0 -> 200 c8 � 162 | 0 0 0 1 1 0 1 1 -> 27 1b 163 | 0 0 0 1 1 0 1 -> 141 8d � 164 | 0 1 1 1 1 0 1 0 -> 122 7a z 165 | 0 1 1 0 0 0 1 1 -> 99 63 c 166 | 0 1 1 0 1 0 1 1 -> 107 6b k 167 | 0 0 1 0 1 0 0 1 -> 41 29 ) 168 | 1 0 0 1 1 1 0 1 -> 157 9d � 169 | 0 0 0 1 0 1 1 1 -> 23 17 170 | 1 1 1 1 1 0 0 1 -> 249 f9 � 171 | 1 0 1 0 1 1 0 0 -> 172 ac � 172 | 1 0 1 0 0 0 0 1 -> 161 a1 � 173 | 1 1 1 0 0 1 0 1 -> 229 e5 � 174 | 1 0 0 0 0 0 1 0 -> 130 82 � 175 | 1 1 0 0 1 0 1 1 -> 203 cb � 176 | 0 1 0 1 0 1 1 0 -> 86 56 V 177 | 1 1 0 1 1 1 1 0 -> 222 de � 178 | 0 0 0 0 1 1 0 1 -> 13 0d 179 | 0 0 0 0 1 0 1 0 -> 10 0a 180 | 181 | -------------------------Dongle:--------------------------- 182 | 0 0 1 0 0 1 1 0 -> 38 26 & 183 | 0 1 1 0 1 0 0 1 -> 105 69 i 184 | 0 1 1 1 0 0 0 1 -> 113 71 q 185 | 1 0 0 1 0 0 1 0 -> 146 92 � 186 | 0 1 0 0 0 1 0 0 -> 68 44 D 187 | 0 0 1 1 0 0 0 1 -> 49 31 1 188 | 0 1 1 0 0 0 1 0 -> 98 62 b 189 | 0 0 0 0 1 1 0 1 -> 13 0d 190 | 0 0 0 0 1 0 1 0 -> 10 0a 191 | 192 | -----------------------Coffee-Maker:----------------------- 193 | 0 0 1 0 0 1 1 0 -> 38 26 & 194 | 0 1 1 1 1 0 0 0 -> 120 78 x 195 | 1 0 0 1 1 0 1 1 -> 155 9b � 196 | 0 1 0 0 0 1 1 0 -> 70 46 F 197 | 0 0 0 1 1 0 0 1 -> 25 19 198 | 1 1 0 0 0 0 0 1 -> 193 c1 � 199 | 0 0 1 0 1 1 1 1 -> 47 2f / 200 | 0 0 0 1 1 0 0 1 -> 25 19 201 | 1 0 1 1 1 0 1 0 -> 186 ba � 202 | 0 1 1 0 1 1 0 0 -> 108 6c l 203 | 0 1 0 0 1 1 1 0 -> 78 4e N 204 | 1 1 0 0 0 1 1 0 -> 198 c6 � 205 | 0 1 0 1 0 0 1 1 -> 83 53 S 206 | 0 1 1 0 0 1 1 0 -> 102 66 f 207 | 1 1 1 0 0 0 0 1 -> 225 e1 � 208 | 1 1 1 0 0 0 0 1 -> 225 e1 � 209 | 1 1 1 0 1 1 1 1 -> 239 ef � 210 | 1 1 0 1 0 0 1 1 -> 211 d3 � 211 | 1 0 0 0 1 0 1 1 -> 139 8b � 212 | 1 0 1 1 1 1 0 0 -> 188 bc � 213 | 0 0 0 0 1 1 0 1 -> 13 0d 214 | 0 0 0 0 1 0 1 0 -> 10 0a 215 | 216 | 0 0 1 0 0 1 1 0 -> 38 26 & 217 | 1 0 0 1 1 0 0 1 -> 153 99 � 218 | 0 0 0 1 1 0 1 1 -> 27 1b 219 | 0 1 0 0 1 1 0 -> 166 a6 � 220 | 0 0 1 0 1 0 1 0 -> 42 2a * 221 | 0 1 1 0 0 1 1 0 -> 102 66 f 222 | 1 0 0 1 0 1 0 0 -> 148 94 � 223 | 1 0 1 0 1 1 0 0 -> 172 ac � 224 | 0 0 0 0 1 1 0 1 -> 13 0d 225 | 0 0 0 0 1 0 1 0 -> 10 0a 226 | ``` -------------------------------------------------------------------------------- /protocol_snoops/snoop_hello_2.md: -------------------------------------------------------------------------------- 1 | ``` 2 | -------------------------Dongle:--------------------------- 3 | 0 1 0 1 0 1 0 0 -> 84 54 T 4 | 0 1 0 1 1 0 0 1 -> 89 59 Y 5 | 0 0 1 1 1 0 1 0 -> 58 3a : 6 | 0 0 0 0 1 1 0 1 -> 13 0d 7 | 0 0 0 0 1 0 1 0 -> 10 0a 8 | 9 | -----------------------Coffee-Maker:----------------------- 10 | 0 1 1 1 0 1 0 0 -> 116 74 t 11 | 0 1 1 1 1 0 0 1 -> 121 79 y 12 | 0 0 1 1 1 0 1 0 -> 58 3a : 13 | 0 1 0 0 0 1 0 1 -> 69 45 E 14 | 0 1 0 0 0 1 1 0 -> 70 46 F 15 | 0 0 1 1 0 1 0 1 -> 53 35 5 16 | 0 0 1 1 0 0 1 1 -> 51 33 3 17 | 0 0 1 1 0 0 1 0 -> 50 32 2 18 | 0 1 0 0 1 1 0 1 -> 77 4d M 19 | 0 0 1 0 0 0 0 0 -> 32 20 20 | 0 1 0 1 0 1 1 0 -> 86 56 V 21 | 0 0 1 1 0 0 0 0 -> 48 30 0 22 | 0 0 1 1 0 0 1 0 -> 50 32 2 23 | 0 0 1 0 1 1 1 0 -> 46 2e . 24 | 0 0 1 1 0 0 0 0 -> 48 30 0 25 | 0 0 1 1 0 0 1 1 -> 51 33 3 26 | 0 0 0 0 1 1 0 1 -> 13 0d 27 | 0 0 0 0 1 0 1 0 -> 10 0a 28 | 29 | -------------------------Dongle:--------------------------- 30 | 0 1 0 0 0 0 0 0 -> 64 40 @ 31 | 0 1 0 1 0 1 0 0 -> 84 54 T 32 | 0 0 1 1 0 0 0 1 -> 49 31 1 33 | 0 0 0 0 1 1 0 1 -> 13 0d 34 | 0 0 0 0 1 0 1 0 -> 10 0a 35 | 36 | -----------------------Coffee-Maker:----------------------- 37 | 0 1 0 0 0 0 0 0 -> 64 40 @ 38 | 0 1 1 1 0 1 0 0 -> 116 74 t 39 | 0 0 1 1 0 0 0 1 -> 49 31 1 40 | 0 0 0 0 1 1 0 1 -> 13 0d 41 | 0 0 0 0 1 0 1 0 -> 10 0a 42 | 43 | 0 1 0 0 0 0 0 0 -> 64 40 @ 44 | 0 1 0 1 0 1 0 0 -> 84 54 T 45 | 0 0 1 1 0 0 1 0 -> 50 32 2 46 | 0 0 1 1 1 0 1 0 -> 58 3a : 47 | 0 0 1 1 0 0 0 0 -> 48 30 0 48 | 0 0 1 1 0 0 0 1 -> 49 31 1 49 | 0 0 1 1 0 0 0 0 -> 48 30 0 50 | 0 0 1 1 0 0 0 0 -> 48 30 0 51 | 0 0 1 1 0 0 0 0 -> 48 30 0 52 | 0 0 1 1 0 0 0 1 -> 49 31 1 53 | 0 1 0 0 0 0 1 0 -> 66 42 B 54 | 0 0 1 1 0 0 1 0 -> 50 32 2 55 | 0 0 1 1 0 0 1 0 -> 50 32 2 56 | 0 0 1 1 1 0 0 0 -> 56 38 8 57 | 0 0 0 0 1 1 0 1 -> 13 0d 58 | 0 0 0 0 1 0 1 0 -> 10 0a 59 | 60 | -------------------------Dongle:--------------------------- 61 | 0 1 0 0 0 0 0 0 -> 64 40 @ 62 | 0 1 1 1 0 1 0 0 -> 116 74 t 63 | 0 0 1 1 0 0 1 0 -> 50 32 2 64 | 0 0 1 1 1 0 1 0 -> 58 3a : 65 | 0 0 1 1 1 0 0 0 -> 56 38 8 66 | 0 0 1 1 0 0 0 1 -> 49 31 1 67 | 0 0 1 1 0 0 1 0 -> 50 32 2 68 | 0 0 1 1 0 0 0 0 -> 48 30 0 69 | 0 0 1 1 0 0 0 0 -> 48 30 0 70 | 0 0 1 1 0 0 0 0 -> 48 30 0 71 | 0 0 1 1 0 0 0 0 -> 48 30 0 72 | 0 0 1 1 0 0 0 0 -> 48 30 0 73 | 0 0 1 1 0 0 0 0 -> 48 30 0 74 | 0 0 1 1 0 0 0 0 -> 48 30 0 75 | 0 0 0 0 1 1 0 1 -> 13 0d 76 | 0 0 0 0 1 0 1 0 -> 10 0a 77 | 78 | -----------------------Coffee-Maker:----------------------- 79 | 0 1 0 0 0 0 0 0 -> 64 40 @ 80 | 0 1 0 1 0 1 0 0 -> 84 54 T 81 | 0 0 1 1 0 0 1 1 -> 51 33 3 82 | 0 0 1 1 1 0 1 0 -> 58 3a : 83 | 0 0 1 1 0 0 1 1 -> 51 33 3 84 | 0 1 0 0 0 0 1 0 -> 66 42 B 85 | 0 1 0 0 0 1 0 0 -> 68 44 D 86 | 0 1 0 0 0 1 0 1 -> 69 45 E 87 | 0 1 0 0 0 1 0 1 -> 69 45 E 88 | 0 1 0 0 0 1 1 0 -> 70 46 F 89 | 0 0 1 1 0 1 0 1 -> 53 35 5 90 | 0 0 1 1 0 0 1 1 -> 51 33 3 91 | 0 0 1 1 0 0 1 0 -> 50 32 2 92 | 0 1 0 0 1 1 0 1 -> 77 4d M 93 | 0 0 1 0 0 0 0 0 -> 32 20 94 | 0 1 0 1 0 1 1 0 -> 86 56 V 95 | 0 0 1 1 0 0 0 0 -> 48 30 0 96 | 0 0 1 1 0 0 1 0 -> 50 32 2 97 | 0 0 1 0 1 1 1 0 -> 46 2e . 98 | 0 0 1 1 0 0 0 0 -> 48 30 0 99 | 0 0 1 1 0 0 1 1 -> 51 33 3 100 | 0 0 0 0 1 1 0 1 -> 13 0d 101 | 0 0 0 0 1 0 1 0 -> 10 0a 102 | 103 | -------------------------Dongle:--------------------------- 104 | 0 1 0 0 0 0 0 0 -> 64 40 @ 105 | 0 1 1 1 0 1 0 0 -> 116 74 t 106 | 0 0 1 1 0 0 1 1 -> 51 33 3 107 | 0 0 0 0 1 1 0 1 -> 13 0d 108 | 0 0 0 0 1 0 1 0 -> 10 0a 109 | 110 | -----------------------Coffee-Maker:----------------------- 111 | 0 0 1 0 0 1 1 0 -> 38 26 & 112 | 0 1 0 0 1 1 1 0 -> 78 4e N 113 | 1 0 1 1 0 1 0 1 -> 181 b5 � 114 | 1 0 1 1 0 1 1 0 -> 182 b6 � 115 | 1 1 0 0 0 0 1 0 -> 194 c2 � 116 | 1 0 0 1 0 0 1 1 -> 147 93 � 117 | 1 1 0 1 0 0 0 0 -> 208 d0 � 118 | 0 1 0 0 0 1 1 1 -> 71 47 G 119 | 1 0 0 1 0 0 1 0 -> 146 92 � 120 | 0 0 0 0 0 1 0 0 -> 4 04 121 | 1 1 1 1 0 0 1 0 -> 242 f2 � 122 | 0 0 1 0 0 1 0 1 -> 37 25 % 123 | 0 1 0 1 0 1 0 1 -> 85 55 U 124 | 0 1 1 1 1 0 1 0 -> 122 7a z 125 | 1 0 1 1 1 1 0 0 -> 188 bc � 126 | 0 0 0 0 0 0 1 0 -> 2 02 127 | 0 1 1 1 0 1 1 1 -> 119 77 w 128 | 1 0 0 0 1 1 0 1 -> 141 8d � 129 | 0 0 1 0 0 0 0 1 -> 33 21 ! 130 | 0 1 0 0 1 0 1 1 -> 75 4b K 131 | 0 0 0 0 1 1 0 1 -> 13 0d 132 | 0 0 0 0 1 0 1 0 -> 10 0a 133 | 134 | -------------------------Dongle:--------------------------- 135 | 0 0 1 0 0 1 1 0 -> 38 26 & 136 | 1 1 0 1 1 1 1 1 -> 223 df � 137 | 1 1 1 1 0 1 0 0 -> 244 f4 � 138 | 0 1 1 1 0 0 0 1 -> 113 71 q 139 | 0 1 1 0 1 0 0 1 -> 105 69 i 140 | 0 0 0 1 0 0 0 0 -> 16 10 141 | 1 0 1 0 0 0 1 0 -> 162 a2 � 142 | 1 1 0 0 1 1 0 1 -> 205 cd � 143 | 0 1 0 1 1 1 1 1 -> 95 5f _ 144 | 1 0 1 0 1 1 0 1 -> 173 ad � 145 | 0 0 0 0 1 1 0 1 -> 13 0d 146 | 0 0 0 0 1 0 1 0 -> 10 0a 147 | 148 | -----------------------Coffee-Maker:----------------------- 149 | 0 0 1 0 0 1 1 0 -> 38 26 & 150 | 1 0 1 0 0 0 1 1 -> 163 a3 � 151 | 1 1 0 0 0 1 1 0 -> 198 c6 � 152 | 1 0 1 0 1 0 0 1 -> 169 a9 � 153 | 0 0 1 0 0 0 0 0 -> 32 20 154 | 1 1 0 0 1 0 0 1 -> 201 c9 � 155 | 0 1 0 1 0 0 0 1 -> 81 51 Q 156 | 1 1 1 1 0 1 0 1 -> 245 f5 � 157 | 0 1 1 1 1 0 0 1 -> 121 79 y 158 | 0 0 1 1 1 0 1 0 -> 58 3a : 159 | 0 0 0 1 0 0 0 1 -> 17 11 160 | 1 0 0 0 1 1 1 1 -> 143 8f � 161 | 1 0 0 0 1 1 1 0 -> 142 8e � 162 | 1 1 0 1 1 1 1 1 -> 223 df � 163 | 0 0 0 1 0 1 0 0 -> 20 14 164 | 1 0 1 0 1 1 1 0 -> 174 ae � 165 | 1 1 0 1 0 1 0 1 -> 213 d5 � 166 | 1 0 0 1 0 0 1 0 -> 146 92 � 167 | 1 0 1 0 0 0 0 0 -> 160 a0 � 168 | 0 1 1 1 0 0 1 1 -> 115 73 s 169 | 1 0 1 0 1 1 0 1 -> 173 ad � 170 | 0 1 0 0 1 1 0 1 -> 77 4d M 171 | 0 1 1 1 0 0 0 0 -> 112 70 p 172 | 0 0 0 1 0 1 0 0 -> 20 14 173 | 1 1 1 1 1 0 0 1 -> 249 f9 � 174 | 0 1 0 1 1 0 1 1 -> 91 5b [ 175 | 0 1 0 0 0 1 1 0 -> 70 46 F 176 | 0 0 0 0 1 1 0 1 -> 13 0d 177 | 0 0 0 0 1 0 1 0 -> 10 0a 178 | 179 | -------------------------Dongle:--------------------------- 180 | 0 0 1 0 0 1 1 0 -> 38 26 & 181 | 0 1 1 0 1 0 0 1 -> 105 69 i 182 | 0 1 1 1 0 0 0 1 -> 113 71 q 183 | 1 0 0 1 0 0 1 0 -> 146 92 � 184 | 0 1 0 0 0 1 0 0 -> 68 44 D 185 | 0 0 1 1 0 0 0 1 -> 49 31 1 186 | 0 1 1 0 0 0 1 0 -> 98 62 b 187 | 0 0 0 0 1 1 0 1 -> 13 0d 188 | 0 0 0 0 1 0 1 0 -> 10 0a 189 | 190 | -----------------------Coffee-Maker:----------------------- 191 | 0 0 1 0 0 1 1 0 -> 38 26 & 192 | 1 0 1 1 1 1 1 0 -> 190 be � 193 | 1 1 0 0 0 1 0 1 -> 197 c5 � 194 | 1 1 0 0 0 0 1 1 -> 195 c3 � 195 | 1 1 0 1 1 1 1 0 -> 222 de � 196 | 0 0 1 0 0 1 0 1 -> 37 25 % 197 | 0 1 0 0 0 1 1 1 -> 71 47 G 198 | 0 0 0 0 1 1 0 1 -> 13 0d 199 | 0 0 0 0 1 0 1 0 -> 10 0a 200 | ``` -------------------------------------------------------------------------------- /protocol_snoops/snoop_hot_water.md: -------------------------------------------------------------------------------- 1 | ``` 2 | -------------------------Dongle:--------------------------- 3 | 0 1 0 0 1 1 1 1 -> 79 4f O 4 | 0 1 1 0 0 0 1 0 -> 98 62 b 5 | 0 0 0 1 1 0 1 0 -> 26 1a � 6 | 0 0 1 1 1 0 1 1 -> 59 3b ; 7 | 0 1 1 1 0 0 0 0 -> 112 70 p 8 | 1 0 1 1 0 1 1 1 -> 183 b7 � 9 | 1 1 1 1 0 0 0 1 -> 241 f1 � 10 | 1 0 1 0 1 1 1 1 -> 175 af � 11 | 0 1 0 1 0 1 1 0 -> 86 56 V 12 | 13 | -------------------------Dongle:--------------------------- 14 | 0 1 0 1 1 0 0 0 -> 88 58 X 15 | 0 0 0 1 1 1 0 0 -> 28 1c 16 | 1 0 0 0 1 1 0 1 -> 141 8d � 17 | 0 1 0 0 1 0 0 0 -> 72 48 H 18 | 1 1 1 0 1 0 0 0 -> 232 e8 � 19 | 1 1 0 0 1 1 0 1 -> 205 cd � 20 | 0 0 1 0 0 0 1 0 -> 34 22 " 21 | 0 1 1 1 0 1 1 0 -> 118 76 v 22 | 0 0 0 1 0 0 0 0 -> 16 10 23 | 24 | -------------------------Dongle:--------------------------- 25 | 0 1 0 0 1 0 0 1 -> 73 49 I 26 | 1 0 0 0 0 1 1 0 -> 134 86 � 27 | 0 0 1 0 0 0 1 1 -> 35 23 # 28 | 1 0 0 1 1 1 0 0 -> 156 9c � 29 | 0 1 0 0 1 0 0 1 -> 73 49 I 30 | 0 0 0 0 1 1 0 0 -> 12 0c 31 | 0 1 1 1 1 0 0 1 -> 121 79 y 32 | 1 1 0 0 0 1 1 1 -> 199 c7 � 33 | 0 0 1 0 0 1 0 0 -> 36 24 $ 34 | ``` 35 | 36 | ### Line 1, 2 and 3 of each try: 37 | ``` 38 | 0 1 0 0 1 1 1 1 -> 79 4f O 39 | 0 1 0 1 1 0 0 0 -> 88 58 X 40 | 0 1 0 0 1 0 0 1 -> 73 49 I 41 | 42 | 0 1 1 0 0 0 1 0 -> 98 62 b 43 | 0 0 0 1 1 1 0 0 -> 28 1c 44 | 1 0 0 0 0 1 1 0 -> 134 86 � 45 | 46 | 0 0 0 1 1 0 1 0 -> 26 1a � 47 | 1 0 0 0 1 1 0 1 -> 141 8d � 48 | 0 0 1 0 0 0 1 1 -> 35 23 # 49 | ``` -------------------------------------------------------------------------------- /protocol_snoops/snoop_keep_alive.txt: -------------------------------------------------------------------------------- 1 | 2 | 22B 00100110 01010010 10011110 01001011 10111000 00010101 10001010 01011110 10001111 11000001 11001010 01110010 10101011 11010010 11011000 10011011 11001100 00011101 01000011 10111101 00001101 00001010 3 | 22B 00100110 11010010 00000110 00111101 01100001 11001100 10001000 00010111 01111001 00011000 11011000 10010001 11001100 00011101 11001010 01110001 10101011 11010010 11100100 11001001 00001101 00001010 4 | 22B 00100110 00010111 00101000 11110111 10101001 00100100 00011111 11111111 01111000 00011001 00011001 10111010 01101100 11001011 10010010 11101010 11001101 00100010 10100011 00100010 00001101 00001010 5 | 22B 00100110 01111110 01100111 11011010 11001010 11100101 11010101 10100011 11111100 01000111 11001001 10111000 10010111 00010100 00101010 01000011 00011010 10101000 01000001 01110010 00001101 00001010 6 | 22B 00100110 01011000 00011100 10001101 01010100 11101000 11001111 00100010 00011001 10111010 11010011 01001110 11000110 01010011 01100110 11101001 11100010 11101111 10110111 10001110 00001101 00001010 7 | 22B 00100110 00011001 10000010 11001000 01011101 01000110 11000011 01010011 01100110 11101100 11001110 00001000 01111010 01101111 01101111 00101001 10011100 01110100 10000001 01111000 00001101 00001010 8 | & \r \n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 5B 00100110 00000011 00101011 00111011 01111010 20 | 11B 10000100 01001001 10100110 10011010 11110010 11011111 00010111 10100100 11011011 10010111 10000100 21 | 6B 10001110 10001100 01010011 00100001 00001101 00001010 22 | 3B 00100110 10010101 00001110 23 | 11B 10010000 11100100 11000010 11010111 11110101 11101001 01010101 01100100 10010111 11111010 01011010 24 | 9B 00011011 10100110 01101101 00011110 10011001 10101000 01100110 00001101 00001010 25 | 10B 00100110 01110110 00011001 00011011 10000000 00100001 11110000 01011100 00011011 10100110 26 | 10B 01101001 00011110 00100011 00011011 10011011 11111111 01111000 00011001 10110000 11011110 27 | 5B 00100010 11100100 11001011 00001101 00001010 28 | 7B 00100110 10000000 11110111 11100000 10101111 11100101 10001000 29 | 11B 00101110 10111100 10110100 11111100 00010111 11110110 00101010 10100001 01111100 11101101 10101110 30 | 4B 01110101 10010100 00001101 00001010 31 | 6B 00100110 11101000 11110100 10011110 11110100 10000010 32 | 10B 10011001 01011100 10001001 10110010 01111001 11111010 11010010 01010110 00000001 10001001 33 | 6B 01000100 00001100 11001100 01000110 00001101 00001010 34 | 2B 00100110 00100011 35 | 12B 01100101 00011110 11110010 01000011 10101001 10011010 11110010 11001011 00010111 10100100 11011011 10010111 36 | 8B 10001100 10000010 10001100 11011111 00101011 01011011 00001101 00001010 37 | 1B 00100110 38 | 11B 10011111 11101000 10110101 11111011 00100000 01001110 00011010 10101000 11001001 01010110 10111011 39 | 10B 01000010 11111111 00001011 11010100 01010100 11100110 11000000 01110010 00001101 00001010 40 | 9B 00100110 11011000 10001101 11101111 11101101 10110000 01100101 11001011 10010010 41 | 11B 11101100 01100110 11101100 11100010 11101111 11010011 01000011 11000110 01010011 10110011 11100110 42 | 2B 00001101 00001010 43 | 8B 00100110 00101110 00100100 11010011 11010100 10101011 10011100 11010100 44 | 9B 01000111 10010010 10001101 11110010 00100101 01010101 01111010 10110110 11110001 45 | 5B 01110111 10110011 00101000 00001101 00001010 46 | 6B 00100110 00000100 11101111 01100010 11110011 11011100 47 | 11B 10010110 10001100 10001110 10001100 01001110 11000010 00010111 11111111 11000100 01010001 01111001 48 | 5B 10101001 10100101 01100111 00001101 00001010 49 | 5B 00100110 11110001 01101011 00111101 00110001 50 | 11B 01110011 11111000 11101101 11101100 10010111 01011110 10001111 11000001 10001110 00010111 01110110 51 | 6B 00011000 10000110 00101010 10111101 00001101 00001010 52 | 3B 00100110 11000011 11011010 53 | 11B 01101110 00100111 01011000 11111110 10001000 01001000 10100110 10001110 10001100 11011111 00010111 54 | 8B 10100100 11010101 10010111 10001100 01000110 11101111 00001101 00001010 55 | 11B 00100110 10110000 00001011 01111000 10110100 00001000 11011000 01010100 11100110 01010110 11101100 56 | 11B 10010111 01111011 01011010 01000111 01111110 11110001 11101101 00100010 01000100 00001101 00001010 57 | 8B 00100110 00110100 00100010 10110110 11010010 00011011 10100110 10110110 58 | 11B 11001011 10110110 00010100 11110101 11101001 01010101 11111110 00000100 01010101 11000101 11010010 59 | 4B 10011000 00101001 00001101 00001010 60 | 6B 00100110 01111100 10001010 11000110 00010101 11010011 61 | 9B 10000110 11011011 10111011 11001011 10010100 11100011 01010011 00101111 11100110 62 | 7B 01010110 01111001 10011101 00111011 11100100 00001101 00001010 63 | 3B 00100110 10111001 00000011 64 | 11B 10001000 11011110 11101100 11101000 11010011 01001110 11000110 00101110 10011100 01110100 11001110 65 | 8B 00001000 01111110 01101111 01101111 00101001 01011111 00001101 00001010 66 | 1B 00100110 67 | 10B 00111110 00001100 00011011 10001101 01100001 11110100 10100010 11101001 11011110 10100011 68 | 11B 00011010 10101000 11001001 10111000 10010111 00011010 00101010 01000101 11100111 11001110 00001101 69 | 1B 00001010 70 | 9B 00100110 00100100 00000111 01100000 01100001 10011110 10001000 10001110 10001100 71 | 11B 11011111 11000010 00010111 11111111 11000100 01010011 01111100 10101001 01001110 01101110 10010111 72 | 2B 00001101 00001010 73 | 74 | A 00001010 \n 75 | B 00001011 76 | C 00001100 77 | D 00001101 \r -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_subdirectory(serial) 4 | add_subdirectory(jutta_proto) 5 | add_subdirectory(logger) 6 | add_subdirectory(include) 7 | add_subdirectory(test_exec) 8 | -------------------------------------------------------------------------------- /src/include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | target_include_directories(serial PUBLIC 4 | $ 5 | $) 6 | 7 | target_sources(serial PRIVATE 8 | # Header files (useful in IDEs) 9 | serial/SerialConnection.hpp) 10 | 11 | target_include_directories(jutta_proto PUBLIC 12 | $ 13 | $) 14 | 15 | target_sources(jutta_proto PRIVATE 16 | # Header files (useful in IDEs) 17 | jutta_proto/CoffeeMaker.hpp 18 | jutta_proto/JuttaConnection.hpp 19 | jutta_proto/JuttaCommands.hpp) 20 | 21 | target_include_directories(logger PUBLIC 22 | $ 23 | $) 24 | 25 | target_sources(logger PRIVATE 26 | # Header files (useful in IDEs) 27 | logger/Logger.hpp) 28 | 29 | install(DIRECTORY serial DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 30 | install(DIRECTORY jutta_proto DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 31 | install(DIRECTORY logger DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -------------------------------------------------------------------------------- /src/include/jutta_proto/CoffeeMaker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "JuttaConnection.hpp" 10 | 11 | //--------------------------------------------------------------------------- 12 | namespace jutta_proto { 13 | //--------------------------------------------------------------------------- 14 | class CoffeeMaker { 15 | public: 16 | /** 17 | * All available coffee types. 18 | **/ 19 | enum coffee_t { ESPRESSO = 0, 20 | COFFEE = 1, 21 | CAPPUCCINO = 2, 22 | MILK_FOAM = 3, 23 | CAFFE_BARISTA = 4, 24 | LUNGO_BARISTA = 5, 25 | ESPRESSO_DOPPIO = 6, 26 | MACCHIATO = 7 }; 27 | enum jutta_button_t { 28 | BUTTON_1 = 1, 29 | BUTTON_2 = 2, 30 | BUTTON_3 = 3, 31 | BUTTON_4 = 4, 32 | BUTTON_5 = 5, 33 | BUTTON_6 = 6, 34 | }; 35 | 36 | std::unique_ptr connection; 37 | 38 | private: 39 | static constexpr size_t NUM_PAGES = 2; 40 | /** 41 | * Mapping of all coffee types to page. 42 | **/ 43 | std::map coffee_page_map{{coffee_t::ESPRESSO, 0}, {coffee_t::COFFEE, 0}, {coffee_t::CAPPUCCINO, 0}, {coffee_t::MILK_FOAM, 0}, {coffee_t::CAFFE_BARISTA, 1}, {coffee_t::LUNGO_BARISTA, 1}, {coffee_t::ESPRESSO_DOPPIO, 1}, {coffee_t::MACCHIATO, 1}}; 44 | /** 45 | * Mapping of all coffee types to their button. 46 | **/ 47 | std::map coffee_button_map{{coffee_t::ESPRESSO, jutta_button_t::BUTTON_1}, {coffee_t::COFFEE, jutta_button_t::BUTTON_2}, {coffee_t::CAPPUCCINO, jutta_button_t::BUTTON_4}, {coffee_t::MILK_FOAM, jutta_button_t::BUTTON_5}, {coffee_t::CAFFE_BARISTA, jutta_button_t::BUTTON_1}, {coffee_t::LUNGO_BARISTA, jutta_button_t::BUTTON_2}, {coffee_t::ESPRESSO_DOPPIO, jutta_button_t::BUTTON_4}, {coffee_t::MACCHIATO, jutta_button_t::BUTTON_5}}; 48 | 49 | /** 50 | * The current page we are on. 51 | **/ 52 | size_t pageNum{0}; 53 | 54 | /** 55 | * True in case we are currently making a something like a cup of coffee. 56 | **/ 57 | bool locked{false}; 58 | 59 | public: 60 | /** 61 | * Takes an initialized JuttaConnection. 62 | **/ 63 | explicit CoffeeMaker(std::unique_ptr&& connection); 64 | 65 | /** 66 | * Switches to the next page. 67 | * 0 -> 1 68 | * 1 -> 0 69 | **/ 70 | void switch_page(); 71 | /** 72 | * Switches to the given page number. 73 | * Does nothing, in case the page number is the same as the current one. 74 | **/ 75 | void switch_page(size_t pageNum); 76 | /** 77 | * Brews the given coffee and switches to the appropriate page for this. 78 | **/ 79 | void brew_coffee(coffee_t coffee); 80 | /** 81 | * Brews a custom coffee with the given grind and water times. 82 | * A default coffee on a JUTTA E6 (2019) grinds for 3.6 seconds and then lets the water run for 40 seconds (200 ml). 83 | * This corresponds to a water flow rate of 5 ml/s. 84 | * As long as cancel is set to true, the process will continue. 85 | * In case it changes from true to false, the coffee maker will cancel brewing and will reset the coffee maker to it's default state before returning. 86 | **/ 87 | void brew_custom_coffee(const bool* cancel, const std::chrono::milliseconds& grindTime = std::chrono::milliseconds{3600}, const std::chrono::milliseconds& waterTime = std::chrono::milliseconds{40000}); 88 | /** 89 | * Simulates a button press of the given button. 90 | **/ 91 | void press_button(jutta_button_t button) const; 92 | 93 | /** 94 | * Returns true in case the coffee maker is locked due to it currently interacting with the coffee maker e.g. brewing a coffee. 95 | **/ 96 | [[nodiscard]] bool is_locked() const; 97 | 98 | private: 99 | /** 100 | * Returns the page number for the given coffee type. 101 | **/ 102 | [[nodiscard]] size_t get_page_num(coffee_t coffee) const; 103 | 104 | /** 105 | * Returns the button number for the given coffee type. 106 | **/ 107 | [[nodiscard]] jutta_button_t get_button_num(coffee_t coffee) const; 108 | /** 109 | * Writes the given string to the coffee maker and waits for an "ok:\r\n" 110 | **/ 111 | [[nodiscard]] bool write_and_wait(const std::string& s) const; 112 | /** 113 | * Turns on the water pump and heater for the given amount of time. 114 | * As long as cancel is set to true, the process will continue. 115 | * In case it changes from true to false, the coffee maker will cancel pumping returns. 116 | * 117 | * Returns true in case pumping was successfull and has not returned early. 118 | **/ 119 | bool pump_hot_water(const std::chrono::milliseconds& waterTime, const bool* cancel) const; 120 | 121 | /** 122 | * Tries to sleep the given amount of milliseconds before returning. 123 | * In case cancel will be set to true, the sleep will return after roughly 100ms 124 | * Since the check happens every <= 100ms. 125 | * 126 | * Returns true in case the sleep was successfull and has not returned early. 127 | **/ 128 | static bool sleep_cancelable(const std::chrono::milliseconds& time, const bool* cancel); 129 | }; 130 | //--------------------------------------------------------------------------- 131 | } // namespace jutta_proto 132 | //--------------------------------------------------------------------------- 133 | -------------------------------------------------------------------------------- /src/include/jutta_proto/JuttaCommands.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //--------------------------------------------------------------------------- 6 | namespace jutta_proto { 7 | //--------------------------------------------------------------------------- 8 | const std::string JUTTA_POWER_OFF = "AN:01\r\n"; 9 | const std::string JUTTA_TEST_MODE_ON = "AN:20\r\n"; 10 | const std::string JUTTA_TEST_MODE_OFF = "AN:21\r\n"; 11 | 12 | const std::string JUTTA_GET_TYPE = "TY:\r\n"; 13 | 14 | const std::string JUTTA_BUTTON_1 = "FA:04\r\n"; 15 | const std::string JUTTA_BUTTON_2 = "FA:05\r\n"; 16 | const std::string JUTTA_BUTTON_3 = "FA:06\r\n"; 17 | const std::string JUTTA_BUTTON_4 = "FA:07\r\n"; 18 | const std::string JUTTA_BUTTON_5 = "FA:08\r\n"; 19 | const std::string JUTTA_BUTTON_6 = "FA:09\r\n"; 20 | 21 | const std::string JUTTA_BREW_GROUP_TO_BREWING_POSITION = "FN:22\r\n"; 22 | const std::string JUTTA_BREW_GROUP_RESET = "FN:0D\r\n"; 23 | 24 | const std::string JUTTA_GRINDER_ON = "FN:07\r\n"; 25 | const std::string JUTTA_GRINDER_OFF = "FN:08\r\n"; 26 | const std::string JUTTA_COFFEE_PRESS_ON = "FN:0B\r\n"; 27 | const std::string JUTTA_COFFEE_PRESS_OFF = "FN:0C\r\n"; 28 | const std::string JUTTA_COFFEE_WATER_HEATER_ON = "FN:03\r\n"; 29 | const std::string JUTTA_COFFEE_WATER_HEATER_OFF = "FN:04\r\n"; 30 | const std::string JUTTA_COFFEE_WATER_PUMP_ON = "FN:01\r\n"; 31 | const std::string JUTTA_COFFEE_WATER_PUMP_OFF = "FN:02\r\n"; 32 | //--------------------------------------------------------------------------- 33 | } // namespace jutta_proto 34 | //--------------------------------------------------------------------------- 35 | -------------------------------------------------------------------------------- /src/include/jutta_proto/JuttaConnection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "serial/SerialConnection.hpp" 11 | 12 | //--------------------------------------------------------------------------- 13 | namespace jutta_proto { 14 | //--------------------------------------------------------------------------- 15 | class JuttaConnection { 16 | private: 17 | /** 18 | * Mutex that prevents multiple threads from accessing the serial connection at the same time. 19 | * Usefull, when using 'wait_for_ok()' to prevent other threads from manipulating the result. 20 | **/ 21 | std::mutex actionLock{}; 22 | serial::SerialConnection serial; 23 | 24 | public: 25 | /** 26 | * Initializes a new Jutta (UART) connection. 27 | **/ 28 | explicit JuttaConnection(std::string&& device); 29 | 30 | /** 31 | * Tries to initializes the Jutta serial (UART) connection. 32 | * Throws a exception in case something goes wrong. 33 | * [Thread Safe] 34 | **/ 35 | void init(); 36 | 37 | /** 38 | * Tries to read a single decoded byte. 39 | * This requires reading 4 JUTTA bytes and converting them to a single actual data byte. 40 | * The result will be stored in the given "byte" pointer. 41 | * Returns true on success. 42 | * [Thread Safe] 43 | **/ 44 | bool read_decoded(uint8_t* byte); 45 | /** 46 | * Reads as many data bytes, as there are availabel. 47 | * Each data byte consists of 4 JUTTA bytes which will be decoded into a single data byte. 48 | * [Thread Safe] 49 | **/ 50 | bool read_decoded(std::vector& data); 51 | /** 52 | * Waits until the coffee maker responded with a "ok:\r\n". 53 | * The default timeout for this operation is 5 seconds. 54 | * To disable the timeout, set the timeout to 0 seconds. 55 | * Returns true on success. 56 | * Returns false when a timeout occurred. 57 | * [Thread Safe] 58 | **/ 59 | bool wait_for_ok(const std::chrono::milliseconds& timeout = std::chrono::milliseconds{5000}); 60 | /** 61 | * Writes the given data to the coffee maker and then waits for the given response with an optional timeout. 62 | * The response has to include the "\r\n" at the end of a message. 63 | * The default timeout for this operation is 5 seconds. 64 | * To disable the timeout, set the timeout to 0 seconds. 65 | * Returns true on success. 66 | * Returns false when a timeout occurred or writing failed. 67 | * [Thread Safe] 68 | **/ 69 | bool write_decoded_wait_for(const std::vector& data, const std::string& response, const std::chrono::milliseconds& timeout = std::chrono::milliseconds{5000}); 70 | /** 71 | * Writes the given data to the coffee maker and then waits for the given response with an optional timeout. 72 | * The response has to include the "\r\n" at the end of a message. 73 | * The default timeout for this operation is 5 seconds. 74 | * To disable the timeout, set the timeout to 0 seconds. 75 | * Returns true on success. 76 | * Returns false when a timeout occurred or writing failed. 77 | * [Thread Safe] 78 | **/ 79 | bool write_decoded_wait_for(const std::string& data, const std::string& response, const std::chrono::milliseconds& timeout = std::chrono::milliseconds{5000}); 80 | 81 | /** 82 | * Writes the given data to the coffee maker and then waits for any response with an optional timeout. 83 | * The default timeout for this operation is 5 seconds. 84 | * To disable the timeout, set the timeout to 0 seconds. 85 | * Returns true on success. 86 | * Returns false when a timeout occurred or writing failed. 87 | * [Thread Safe] 88 | **/ 89 | std::shared_ptr write_decoded_with_response(const std::vector& data, const std::chrono::milliseconds& timeout = std::chrono::milliseconds{5000}); 90 | /** 91 | * Writes the given data to the coffee maker and then waits for any response with an optional timeout. 92 | * The default timeout for this operation is 5 seconds. 93 | * To disable the timeout, set the timeout to 0 seconds. 94 | * Returns true on success. 95 | * Returns false when a timeout occurred or writing failed. 96 | * [Thread Safe] 97 | **/ 98 | std::shared_ptr write_decoded_with_response(const std::string& data, const std::chrono::milliseconds& timeout = std::chrono::milliseconds{5000}); 99 | 100 | /** 101 | * Encodes the given byte into 4 JUTTA bytes and writes them to the coffee maker. 102 | * [Thread Safe] 103 | **/ 104 | bool write_decoded(const uint8_t& byte); 105 | /** 106 | * Encodes each byte of the given bytes into 4 JUTTA bytes and writes them to the coffee maker. 107 | * [Thread Safe] 108 | **/ 109 | bool write_decoded(const std::vector& data); 110 | /** 111 | * Encodes each character into 4 JUTTA bytes and writes them to the coffee maker. 112 | * 113 | * An example call could look like: write_decoded("TY:\r\n"); 114 | * This would request the device type from the coffee maker. 115 | * [Thread Safe] 116 | **/ 117 | bool write_decoded(const std::string& data); 118 | 119 | /** 120 | * Helper function used for debugging. 121 | * Prints the given byte in binary, hex and as a char. 122 | * Does not append a new line at the end! 123 | * 124 | * Example output: 125 | * 0 1 0 1 0 1 0 0 -> 84 54 T 126 | **/ 127 | static void print_byte(const uint8_t& byte); 128 | /** 129 | * Prints each byte in the given vector in binary, hex and as a char 130 | * 131 | * Example output: 132 | * 0 1 0 1 0 1 0 0 -> 84 54 T 133 | * 0 1 0 1 1 0 0 1 -> 89 59 Y 134 | * 0 0 1 1 1 0 1 0 -> 58 3a : 135 | * 0 0 0 0 1 1 0 1 -> 13 0d 136 | * 0 0 0 0 1 0 1 0 -> 10 0a 137 | **/ 138 | static void print_bytes(const std::vector& data); 139 | 140 | /** 141 | * Runs the encode and decode test. 142 | * Ensures encoding and decoding is reversable. 143 | * Should be run at least once per session to ensure proper functionality. 144 | **/ 145 | static void run_encode_decode_test(); 146 | 147 | /** 148 | * Converts the given binary vector to a string and returns it. 149 | **/ 150 | static std::string vec_to_string(const std::vector& data); 151 | 152 | private: 153 | /** 154 | * Encodes the given byte into four bytes that the coffee maker understands. 155 | * Based on: http://protocoljura.wiki-site.com/index.php/Protocol_to_coffeemaker 156 | * 157 | * A full documentation of the process can be found here: 158 | * https://github.com/Jutta-Proto/protocol-cpp#deobfuscating 159 | **/ 160 | static std::array encode(const uint8_t& decData); 161 | /** 162 | * Decodes the given four bytes read from the coffee maker into on byte. 163 | * Based on: http://protocoljura.wiki-site.com/index.php/Protocol_to_coffeemaker 164 | * 165 | * A full documentation of the process can be found here: 166 | * https://github.com/Jutta-Proto/protocol-cpp#deobfuscating 167 | **/ 168 | static uint8_t decode(const std::array& encData); 169 | /** 170 | * Writes four bytes of encoded data to the coffee maker and then waits 8ms. 171 | **/ 172 | [[nodiscard]] bool write_encoded_unsafe(const std::array& encData) const; 173 | /** 174 | * Reads four bytes of encoded data which represent one byte of actual data. 175 | * Returns true on success. 176 | * Not thread safe! 177 | **/ 178 | [[nodiscard]] bool read_encoded_unsafe(std::array& buffer) const; 179 | /** 180 | * Reads multiples of four bytes. Every four bytes represent one actual byte. 181 | * Returns the number of 4 byte tuples read. 182 | * Not thread safe! 183 | **/ 184 | [[nodiscard]] size_t read_encoded_unsafe(std::vector>& data) const; 185 | /** 186 | * Tries to read a single decoded byte. 187 | * This requires reading 4 JUTTA bytes and converting them to a single actual data byte. 188 | * The result will be stored in the given "byte" pointer. 189 | * Returns true on success. 190 | * Not thread safe! 191 | **/ 192 | [[nodiscard]] bool read_decoded_unsafe(uint8_t* byte) const; 193 | /** 194 | * Reads as many data bytes, as there are availabel. 195 | * Each data byte consists of 4 JUTTA bytes which will be decoded into a single data byte. 196 | * Not thread safe! 197 | **/ 198 | [[nodiscard]] bool read_decoded_unsafe(std::vector& data) const; 199 | 200 | /** 201 | * Encodes the given byte into 4 JUTTA bytes and writes them to the coffee maker. 202 | * Not thread safe! 203 | **/ 204 | [[nodiscard]] bool write_decoded_unsafe(const uint8_t& byte) const; 205 | /** 206 | * Encodes each byte of the given bytes into 4 JUTTA bytes and writes them to the coffee maker. 207 | * Not thread safe! 208 | **/ 209 | [[nodiscard]] bool write_decoded_unsafe(const std::vector& data) const; 210 | /** 211 | * Encodes each character into 4 JUTTA bytes and writes them to the coffee maker. 212 | * 213 | * An example call could look like: write_decoded("TY:\r\n"); 214 | * This would request the device type from the coffee maker. 215 | * Not thread safe! 216 | **/ 217 | [[nodiscard]] bool write_decoded_unsafe(const std::string& data) const; 218 | 219 | /** 220 | * Waits until the coffee maker responded with the given response. 221 | * The response has to include the "\r\n" at the end of a message. 222 | * The default timeout for this operation is 5 seconds. 223 | * To disable the timeout, set the timeout to 0 seconds. 224 | * Returns true on success. 225 | * Returns false when a timeout occurred. 226 | * Not thread safe! 227 | **/ 228 | [[nodiscard]] bool wait_for_response_unsafe(const std::string& response, const std::chrono::milliseconds& timeout = std::chrono::milliseconds{5000}) const; 229 | 230 | /** 231 | * Waits for any response with an optional timeout. 232 | * The default timeout for this operation is 5 seconds. 233 | * To disable the timeout, set the timeout to 0 seconds. 234 | * Returns the string on success. 235 | * Not thread safe! 236 | **/ 237 | [[nodiscard]] std::shared_ptr wait_for_str_unsafe(const std::chrono::milliseconds& timeout = std::chrono::milliseconds{5000}) const; 238 | }; 239 | //--------------------------------------------------------------------------- 240 | } // namespace jutta_proto 241 | //--------------------------------------------------------------------------- 242 | -------------------------------------------------------------------------------- /src/include/logger/Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace logger { 7 | const std::filesystem::path log_folder("logs"); 8 | // Setup the logger, note the loglevel can not be set below the CMAKE log level (To change this use -DLOG_LEVEL=...) 9 | void setup_logger(const spdlog::level::level_enum level); 10 | void set_log_level(const spdlog::level::level_enum level); 11 | void deactivate_logger(); 12 | } // namespace logger 13 | -------------------------------------------------------------------------------- /src/include/serial/SerialConnection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | //--------------------------------------------------------------------------- 8 | namespace serial { 9 | //--------------------------------------------------------------------------- 10 | enum SerialConnectionState { SC_DISABLED = 0, 11 | SC_OPENED = 1, 12 | SC_READY = 2, 13 | SC_ERROR = 3 }; 14 | 15 | /** 16 | * Based on: https://en.wikibooks.org/wiki/Serial_Programming/termios 17 | **/ 18 | class SerialConnection { 19 | private: 20 | const std::string device; 21 | int fd = -1; 22 | SerialConnectionState state{SC_DISABLED}; 23 | 24 | public: 25 | explicit SerialConnection(std::string&& device); 26 | ~SerialConnection(); 27 | 28 | /** 29 | * Tries to initializes the serial (UART) connection. 30 | * Throws a exception in case something goes wrong. 31 | **/ 32 | void init(); 33 | 34 | /** 35 | * Reads at maximum four bytes. 36 | * Returns how many bytes have been actually read. 37 | **/ 38 | [[nodiscard]] size_t read_serial(std::array& buffer) const; 39 | /** 40 | * Writes the given data buffer to the serial connection. 41 | **/ 42 | [[nodiscard]] size_t write_serial(const std::array& data) const; 43 | void flush() const; 44 | 45 | /** 46 | * Returns all available serial port paths for this device. 47 | **/ 48 | static std::vector get_available_ports(); 49 | 50 | private: 51 | /** 52 | * Tries to open the given serial port and stores the resulting file descriptor in fd. 53 | * Throws a exception in case something goes wrong. 54 | **/ 55 | void openTty(const std::string& device); 56 | /** 57 | * Tries to configure the current file descriptor fd as serial (UART) device. 58 | * Throws a exception in case something goes wrong. 59 | **/ 60 | void configureTty(); 61 | void closeTty(); 62 | }; 63 | //--------------------------------------------------------------------------- 64 | } // namespace serial 65 | //--------------------------------------------------------------------------- 66 | -------------------------------------------------------------------------------- /src/jutta_proto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(jutta_proto SHARED CoffeeMaker.cpp 4 | JuttaConnection.cpp) 5 | 6 | target_link_libraries(jutta_proto PUBLIC serial 7 | PRIVATE logger) 8 | 9 | # Set version for shared libraries. 10 | set_target_properties(jutta_proto 11 | PROPERTIES 12 | VERSION ${${PROJECT_NAME}_VERSION} 13 | SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR}) 14 | 15 | install(TARGETS jutta_proto) 16 | -------------------------------------------------------------------------------- /src/jutta_proto/CoffeeMaker.cpp: -------------------------------------------------------------------------------- 1 | #include "jutta_proto/CoffeeMaker.hpp" 2 | 3 | #include "logger/Logger.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "jutta_proto/JuttaCommands.hpp" 14 | 15 | //--------------------------------------------------------------------------- 16 | namespace jutta_proto { 17 | //--------------------------------------------------------------------------- 18 | CoffeeMaker::CoffeeMaker(std::unique_ptr&& connection) : connection(std::move(connection)) {} 19 | 20 | void CoffeeMaker::switch_page() { 21 | press_button(jutta_button_t::BUTTON_6); 22 | if (++pageNum >= NUM_PAGES) { 23 | pageNum = 0; 24 | } 25 | } 26 | 27 | void CoffeeMaker::switch_page(size_t pageNum) { 28 | if (this->pageNum == pageNum) { 29 | return; 30 | } 31 | 32 | press_button(jutta_button_t::BUTTON_6); 33 | if (++pageNum >= NUM_PAGES) { 34 | pageNum = 0; 35 | } 36 | switch_page(pageNum); 37 | } 38 | 39 | void CoffeeMaker::brew_coffee(coffee_t coffee) { 40 | assert(!locked); 41 | locked = true; 42 | 43 | size_t pageNum = get_page_num(coffee); 44 | switch_page(pageNum); 45 | jutta_button_t button = get_button_num(coffee); 46 | press_button(button); 47 | locked = false; 48 | } 49 | 50 | size_t CoffeeMaker::get_page_num(coffee_t coffee) const { 51 | for (const std::pair& c : coffee_page_map) { 52 | if (c.first == coffee) { 53 | return c.second; 54 | } 55 | } 56 | assert(false); // Should not happen 57 | return std::numeric_limits::max(); 58 | } 59 | 60 | CoffeeMaker::jutta_button_t CoffeeMaker::get_button_num(coffee_t coffee) const { 61 | for (const std::pair& c : coffee_button_map) { 62 | if (c.first == coffee) { 63 | return c.second; 64 | } 65 | } 66 | assert(false); // Should not happen 67 | return jutta_button_t::BUTTON_6; 68 | } 69 | 70 | void CoffeeMaker::press_button(jutta_button_t button) const { 71 | switch (button) { 72 | case jutta_button_t::BUTTON_1: 73 | static_cast(write_and_wait(JUTTA_BUTTON_1)); 74 | break; 75 | 76 | case jutta_button_t::BUTTON_2: 77 | static_cast(write_and_wait(JUTTA_BUTTON_2)); 78 | break; 79 | 80 | case jutta_button_t::BUTTON_3: 81 | static_cast(write_and_wait(JUTTA_BUTTON_3)); 82 | break; 83 | 84 | case jutta_button_t::BUTTON_4: 85 | static_cast(write_and_wait(JUTTA_BUTTON_4)); 86 | break; 87 | 88 | case jutta_button_t::BUTTON_5: 89 | static_cast(write_and_wait(JUTTA_BUTTON_5)); 90 | break; 91 | 92 | case jutta_button_t::BUTTON_6: 93 | static_cast(write_and_wait(JUTTA_BUTTON_6)); 94 | break; 95 | 96 | default: 97 | assert(false); // Should not happen 98 | break; 99 | } 100 | 101 | // Give the coffee maker time to react: 102 | std::this_thread::sleep_for(std::chrono::milliseconds{500}); 103 | } 104 | 105 | void CoffeeMaker::brew_custom_coffee(const bool* cancel, const std::chrono::milliseconds& grindTime, const std::chrono::milliseconds& waterTime) { 106 | assert(!locked); 107 | locked = true; 108 | SPDLOG_INFO("Brewing custom coffee with {} ms grind time and {} ms ms water time...", std::to_string(grindTime.count()), std::to_string(waterTime.count())); 109 | 110 | // Grind: 111 | SPDLOG_INFO("Custom coffee grinding..."); 112 | static_cast(write_and_wait(JUTTA_GRINDER_ON)); 113 | if (!sleep_cancelable(grindTime, cancel)) { 114 | static_cast(write_and_wait(JUTTA_BREW_GROUP_RESET)); 115 | locked = false; 116 | return; 117 | } 118 | static_cast(write_and_wait(JUTTA_GRINDER_OFF)); 119 | static_cast(write_and_wait(JUTTA_BREW_GROUP_TO_BREWING_POSITION)); 120 | 121 | // Compress: 122 | SPDLOG_INFO("Custom coffee compressing..."); 123 | static_cast(write_and_wait(JUTTA_COFFEE_PRESS_ON)); 124 | if (!sleep_cancelable(grindTime, cancel)) { 125 | static_cast(write_and_wait(JUTTA_COFFEE_PRESS_OFF)); 126 | static_cast(write_and_wait(JUTTA_BREW_GROUP_RESET)); 127 | locked = false; 128 | return; 129 | } 130 | sleep_cancelable(std::chrono::milliseconds{500}, cancel); 131 | static_cast(write_and_wait(JUTTA_COFFEE_PRESS_OFF)); 132 | 133 | // Brew step 1: 134 | SPDLOG_INFO("Custom coffee brewing..."); 135 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_PUMP_ON)); 136 | if (!sleep_cancelable(std::chrono::milliseconds{2000}, cancel)) { 137 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_PUMP_OFF)); 138 | static_cast(write_and_wait(JUTTA_BREW_GROUP_RESET)); 139 | locked = false; 140 | return; 141 | } 142 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_PUMP_OFF)); 143 | if (!sleep_cancelable(std::chrono::milliseconds{2000}, cancel)) { 144 | static_cast(write_and_wait(JUTTA_BREW_GROUP_RESET)); 145 | locked = false; 146 | return; 147 | } 148 | 149 | // Brew setp 2: 150 | pump_hot_water(waterTime, cancel); 151 | 152 | // Reset: 153 | SPDLOG_INFO("Custom coffee finishing up..."); 154 | static_cast(write_and_wait(JUTTA_BREW_GROUP_RESET)); 155 | SPDLOG_INFO("Custom coffee done."); 156 | 157 | locked = false; 158 | } 159 | 160 | bool CoffeeMaker::write_and_wait(const std::string& s) const { 161 | static_cast(connection->write_decoded(s)); 162 | return connection->wait_for_ok(); 163 | } 164 | 165 | bool CoffeeMaker::pump_hot_water(const std::chrono::milliseconds& waterTime, const bool* cancel) const { 166 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_PUMP_ON)); 167 | std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now() + waterTime; 168 | // NOLINTNEXTLINE (hicpp-use-nullptr, modernize-use-nullptr) 169 | while (std::chrono::steady_clock::now() < end) { 170 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_HEATER_ON)); 171 | SPDLOG_INFO("Heater turned on."); 172 | if (!sleep_cancelable(waterTime / 8, cancel)) { 173 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_HEATER_OFF)); 174 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_PUMP_OFF)); 175 | return false; 176 | } 177 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_HEATER_OFF)); 178 | SPDLOG_INFO("Heater turned off."); 179 | if (!sleep_cancelable(waterTime / 20, cancel)) { 180 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_PUMP_OFF)); 181 | return false; 182 | } 183 | } 184 | static_cast(write_and_wait(JUTTA_COFFEE_WATER_PUMP_OFF)); 185 | return !(*cancel); 186 | } 187 | 188 | bool CoffeeMaker::is_locked() const { return locked; } 189 | 190 | bool CoffeeMaker::sleep_cancelable(const std::chrono::milliseconds& time, const bool* cancel) { 191 | // By default we perform 100ms increment steps: 192 | constexpr size_t TIME_STEPS = 100; 193 | 194 | const size_t duration = time.count(); 195 | for (size_t i = 0; i < duration && !(*cancel); i += TIME_STEPS) { 196 | std::this_thread::sleep_for(std::chrono::milliseconds{TIME_STEPS}); 197 | } 198 | if (!(*cancel) && (duration % TIME_STEPS != 0)) { 199 | std::this_thread::sleep_for(std::chrono::milliseconds{duration % TIME_STEPS}); 200 | } 201 | return !(*cancel); 202 | } 203 | 204 | //--------------------------------------------------------------------------- 205 | } // namespace jutta_proto 206 | //--------------------------------------------------------------------------- 207 | -------------------------------------------------------------------------------- /src/jutta_proto/JuttaConnection.cpp: -------------------------------------------------------------------------------- 1 | #include "jutta_proto/JuttaConnection.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | //--------------------------------------------------------------------------- 14 | namespace jutta_proto { 15 | //--------------------------------------------------------------------------- 16 | JuttaConnection::JuttaConnection(std::string&& device) : serial(std::move(device)) {} 17 | 18 | void JuttaConnection::init() { 19 | actionLock.lock(); 20 | serial.init(); 21 | actionLock.unlock(); 22 | } 23 | 24 | bool JuttaConnection::read_decoded(std::vector& data) { 25 | actionLock.lock(); 26 | bool result = read_decoded_unsafe(data); 27 | actionLock.unlock(); 28 | return result; 29 | } 30 | 31 | bool JuttaConnection::read_decoded(uint8_t* byte) { 32 | actionLock.lock(); 33 | bool result = read_decoded_unsafe(byte); 34 | actionLock.unlock(); 35 | return result; 36 | } 37 | 38 | bool JuttaConnection::read_decoded_unsafe(uint8_t* byte) const { 39 | std::array buffer{}; 40 | if (!read_encoded_unsafe(buffer)) { 41 | return false; 42 | } 43 | *byte = decode(buffer); 44 | return true; 45 | } 46 | 47 | bool JuttaConnection::read_decoded_unsafe(std::vector& data) const { 48 | // Read encoded data: 49 | std::vector> dataBuffer; 50 | if (read_encoded_unsafe(dataBuffer) <= 0) { 51 | return false; 52 | } 53 | 54 | // Decode all: 55 | for (const std::array& buffer : dataBuffer) { 56 | data.push_back(decode(buffer)); 57 | } 58 | SPDLOG_DEBUG("Read: {}", vec_to_string(data)); 59 | return true; 60 | } 61 | 62 | bool JuttaConnection::write_decoded_unsafe(const uint8_t& byte) const { 63 | return write_encoded_unsafe(encode(byte)); 64 | } 65 | 66 | bool JuttaConnection::write_decoded_unsafe(const std::vector& data) const { 67 | // Bad compiler support: 68 | // return std::ranges::all_of(data.begin(), data.end(), [this](uint8_t byte) { return write_decoded_unsafe(byte); }); 69 | // So we use this until it gets better: 70 | bool result = true; 71 | for (uint8_t byte : data) { 72 | if (!write_decoded_unsafe(byte)) { 73 | result = false; 74 | } 75 | } 76 | return result; 77 | } 78 | 79 | bool JuttaConnection::write_decoded_unsafe(const std::string& data) const { 80 | // Bad compiler support: 81 | // return std::ranges::all_of(data.begin(), data.end(), [this](char c) { return write_decoded_unsafe(static_cast(c)); }); 82 | // So we use this until it gets better: 83 | bool result = true; 84 | for (char c : data) { 85 | if (!write_decoded_unsafe(static_cast(c))) { 86 | result = false; 87 | } 88 | } 89 | return result; 90 | } 91 | 92 | bool JuttaConnection::write_decoded(const uint8_t& byte) { 93 | actionLock.lock(); 94 | bool result = write_decoded_unsafe(byte); 95 | actionLock.unlock(); 96 | return result; 97 | } 98 | 99 | bool JuttaConnection::write_decoded(const std::vector& data) { 100 | actionLock.lock(); 101 | bool result = write_decoded_unsafe(data); 102 | actionLock.unlock(); 103 | return result; 104 | } 105 | 106 | bool JuttaConnection::write_decoded(const std::string& data) { 107 | actionLock.lock(); 108 | bool result = write_decoded_unsafe(data); 109 | actionLock.unlock(); 110 | return result; 111 | } 112 | 113 | void JuttaConnection::print_byte(const uint8_t& byte) { 114 | for (size_t i = 0; i < 8; i++) { 115 | SPDLOG_INFO("{} ", ((byte >> (7 - i)) & 0b00000001)); 116 | } 117 | // printf("-> %d\t%02x\t%c", byte, byte, byte); 118 | printf("-> %d\t%02x", byte, byte); 119 | } 120 | 121 | void JuttaConnection::print_bytes(const std::vector& data) { 122 | for (const uint8_t& byte : data) { 123 | print_byte(byte); 124 | } 125 | } 126 | 127 | void JuttaConnection::run_encode_decode_test() { 128 | bool success = true; 129 | 130 | for (uint16_t i = 0b00000000; i <= 0b11111111; i++) { 131 | if (i != decode(encode(i))) { 132 | success = false; 133 | SPDLOG_ERROR("data:"); 134 | print_byte(i); 135 | 136 | std::array dataEnc = encode(i); 137 | for (size_t i = 0; i < 4; i++) { 138 | SPDLOG_ERROR("dataEnc[{}]", i); 139 | print_byte(dataEnc.at(i)); 140 | } 141 | 142 | uint8_t dataDec = decode(dataEnc); 143 | SPDLOG_ERROR("dataDec:"); 144 | print_byte(dataDec); 145 | } 146 | } 147 | // Flush the stdout to ensure the result gets printed when assert(success) fails: 148 | SPDLOG_INFO("Encode decode test: {}", success); 149 | assert(success); 150 | } 151 | 152 | std::array JuttaConnection::encode(const uint8_t& decData) { 153 | // 1111 0000 -> 0000 1111: 154 | uint8_t tmp = ((decData & 0xF0) >> 4) | ((decData & 0x0F) << 4); 155 | 156 | // 1100 1100 -> 0011 0011: 157 | tmp = ((tmp & 0xC0) >> 2) | ((tmp & 0x30) << 2) | ((tmp & 0x0C) >> 2) | ((tmp & 0x03) << 2); 158 | 159 | // The base bit layout for all send bytes: 160 | constexpr uint8_t BASE = 0b01011011; 161 | 162 | std::array encData{}; 163 | encData[0] = BASE | ((tmp & 0b10000000) >> 2); 164 | encData[0] |= ((tmp & 0b01000000) >> 4); 165 | 166 | encData[1] = BASE | (tmp & 0b00100000); 167 | encData[1] |= ((tmp & 0b00010000) >> 2); 168 | 169 | encData[2] = BASE | ((tmp & 0b00001000) << 2); 170 | encData[2] |= (tmp & 0b00000100); 171 | 172 | encData[3] = BASE | ((tmp & 0b00000010) << 4); 173 | encData[3] |= ((tmp & 0b00000001) << 2); 174 | 175 | return encData; 176 | } 177 | 178 | uint8_t JuttaConnection::decode(const std::array& encData) { 179 | // Bit mask for the 2. bit from the left: 180 | constexpr uint8_t B2_MASK = (0b10000000 >> 2); 181 | // Bit mask for the 5. bit from the left: 182 | constexpr uint8_t B5_MASK = (0b10000000 >> 5); 183 | 184 | uint8_t decData = 0; 185 | decData |= (encData[0] & B2_MASK) << 2; 186 | decData |= (encData[0] & B5_MASK) << 4; 187 | 188 | decData |= (encData[1] & B2_MASK); 189 | decData |= (encData[1] & B5_MASK) << 2; 190 | 191 | decData |= (encData[2] & B2_MASK) >> 2; 192 | decData |= (encData[2] & B5_MASK); 193 | 194 | decData |= (encData[3] & B2_MASK) >> 4; 195 | decData |= (encData[3] & B5_MASK) >> 2; 196 | 197 | // 1111 0000 -> 0000 1111: 198 | decData = ((decData & 0xF0) >> 4) | ((decData & 0x0F) << 4); 199 | 200 | // 1100 1100 -> 0011 0011: 201 | decData = ((decData & 0xC0) >> 2) | ((decData & 0x30) << 2) | ((decData & 0x0C) >> 2) | ((decData & 0x03) << 2); 202 | 203 | return decData; 204 | } 205 | 206 | bool JuttaConnection::write_encoded_unsafe(const std::array& encData) const { 207 | bool result = serial.write_serial(encData); 208 | serial.flush(); 209 | std::this_thread::sleep_for(std::chrono::milliseconds{8}); 210 | return result; 211 | } 212 | 213 | bool JuttaConnection::read_encoded_unsafe(std::array& buffer) const { 214 | size_t size = serial.read_serial(buffer); 215 | if (size <= 0 || size > 4) { 216 | SPDLOG_TRACE("No serial data found."); 217 | return false; 218 | } 219 | if (size < 4) { 220 | SPDLOG_WARN("Invalid amount of UART data found ({} byte) - ignoring.", size); 221 | return false; 222 | } 223 | SPDLOG_TRACE("Read 4 encoded bytes."); 224 | return true; 225 | } 226 | 227 | size_t JuttaConnection::read_encoded_unsafe(std::vector>& data) const { 228 | while (true) { 229 | std::array buffer{}; 230 | if (!read_encoded_unsafe(buffer)) { 231 | // Wait 100 ms for the next bunch of data to arrive: 232 | std::this_thread::sleep_for(std::chrono::milliseconds{100}); 233 | if (!read_encoded_unsafe(buffer)) { 234 | break; 235 | } 236 | } 237 | data.push_back(buffer); 238 | } 239 | return data.size(); 240 | } 241 | 242 | bool JuttaConnection::wait_for_ok(const std::chrono::milliseconds& timeout) { 243 | actionLock.lock(); 244 | bool result = wait_for_response_unsafe("ok:\r\n", timeout); 245 | actionLock.unlock(); 246 | return result; 247 | } 248 | 249 | std::shared_ptr JuttaConnection::write_decoded_with_response(const std::vector& data, const std::chrono::milliseconds& timeout) { 250 | std::shared_ptr result{nullptr}; 251 | actionLock.lock(); 252 | if (write_decoded_unsafe(data)) { 253 | result = wait_for_str_unsafe(timeout); 254 | } 255 | actionLock.unlock(); 256 | return result; 257 | } 258 | 259 | std::shared_ptr JuttaConnection::write_decoded_with_response(const std::string& data, const std::chrono::milliseconds& timeout) { 260 | std::shared_ptr result{nullptr}; 261 | actionLock.lock(); 262 | if (write_decoded_unsafe(data)) { 263 | result = wait_for_str_unsafe(timeout); 264 | } 265 | actionLock.unlock(); 266 | return result; 267 | } 268 | 269 | std::shared_ptr JuttaConnection::wait_for_str_unsafe(const std::chrono::milliseconds& timeout) const { 270 | std::shared_ptr result{nullptr}; 271 | std::vector buffer; 272 | std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); 273 | // NOLINTNEXTLINE (hicpp-use-nullptr, modernize-use-nullptr) 274 | while ((timeout.count() <= 0) || ((std::chrono::steady_clock::now() - start) < timeout)) { 275 | if (read_decoded_unsafe(buffer)) { 276 | result = std::make_shared(vec_to_string(buffer)); 277 | break; 278 | } 279 | std::this_thread::sleep_for(std::chrono::milliseconds{250}); 280 | } 281 | return result; 282 | } 283 | 284 | bool JuttaConnection::wait_for_response_unsafe(const std::string& response, const std::chrono::milliseconds& timeout) const { 285 | std::vector buffer; 286 | std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); 287 | // NOLINTNEXTLINE (hicpp-use-nullptr, modernize-use-nullptr) 288 | while ((timeout.count() <= 0) || ((std::chrono::steady_clock::now() - start) < timeout)) { 289 | if (read_decoded_unsafe(buffer)) { 290 | for (size_t i = 0; (buffer.size() >= response.size()) && (i < buffer.size() - (response.size() - 1)); i++) { 291 | bool success = true; 292 | for (size_t e = 0; e < response.size(); e++) { 293 | if (static_cast(buffer[i + e]) != response[e]) { 294 | success = false; 295 | break; 296 | } 297 | } 298 | if (success) { 299 | return true; 300 | } 301 | } 302 | buffer.clear(); 303 | } 304 | std::this_thread::sleep_for(std::chrono::milliseconds{250}); 305 | } 306 | return false; 307 | } 308 | 309 | bool JuttaConnection::write_decoded_wait_for(const std::string& data, const std::string& response, const std::chrono::milliseconds& timeout) { 310 | actionLock.lock(); 311 | bool result = write_decoded_unsafe(data); 312 | if (result) { 313 | result = wait_for_response_unsafe(response, timeout); 314 | } 315 | actionLock.unlock(); 316 | return result; 317 | } 318 | 319 | std::string JuttaConnection::vec_to_string(const std::vector& data) { 320 | if (data.empty()) { 321 | return ""; 322 | } 323 | 324 | std::ostringstream sstream; 325 | for (unsigned char i : data) { 326 | sstream << static_cast(i); 327 | } 328 | return sstream.str(); 329 | } 330 | 331 | //--------------------------------------------------------------------------- 332 | } // namespace jutta_proto 333 | //--------------------------------------------------------------------------- 334 | -------------------------------------------------------------------------------- /src/logger/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(logger SHARED Logger.cpp) 4 | target_link_libraries(logger PUBLIC spdlog::spdlog stdc++fs) 5 | 6 | set(LOG_LEVEL Default CACHE STRING "Sets the build type") 7 | set_property(CACHE LOG_LEVEL PROPERTY STRINGS Default Trace Debug Info Warn Error Critical Off) 8 | 9 | if(${LOG_LEVEL} STREQUAL Trace) 10 | set (LOG_LEVEL TRACE) 11 | message(STATUS "Using log level Trace") 12 | elseif(${LOG_LEVEL} STREQUAL Debug) 13 | set (LOG_LEVEL DEBUG) 14 | message(STATUS "Using log level Debug") 15 | elseif(${LOG_LEVEL} STREQUAL Info) 16 | set (LOG_LEVEL INFO) 17 | message(STATUS "Using log level Info") 18 | elseif(${LOG_LEVEL} STREQUAL Warn) 19 | set (LOG_LEVEL WARN) 20 | message(STATUS "Using log level Warn") 21 | elseif(${LOG_LEVEL} STREQUAL Error) 22 | set (LOG_LEVEL ERROR) 23 | message(STATUS "Using log level Error") 24 | elseif(${LOG_LEVEL} STREQUAL Critical) 25 | set (LOG_LEVEL CRITICAL) 26 | message(STATUS "Using log level Critical") 27 | elseif(${LOG_LEVEL} STREQUAL Off) 28 | set (LOG_LEVEL OFF) 29 | message(STATUS "Using log level Off") 30 | else() 31 | if((NOT ${LOG_LEVEL} STREQUAL Default) AND (NOT LOG_LEVEL STREQUAL "")) 32 | message(WARNING "Log level unknown, use -DLOG_LEVEL=[Trace, Debug, Info, Warn, Error, Critical, Off]") 33 | endif() 34 | set (LOG_LEVEL $,DEBUG,INFO>) 35 | message(STATUS "Setting LOG_LEVEL to according to build type") 36 | endif() 37 | 38 | target_compile_definitions(logger INTERFACE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_${LOG_LEVEL}) 39 | install(TARGETS logger) 40 | -------------------------------------------------------------------------------- /src/logger/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger/Logger.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef _WIN32 12 | #include 13 | #endif // _WIN32 14 | 15 | namespace logger { 16 | constexpr int THREAD_QUEUE_LENGTH = 8192; 17 | constexpr int FILE_ROTATION_TIME = 1048576 * 5; 18 | 19 | void setup_logger(const spdlog::level::level_enum level) { 20 | if (not std::filesystem::exists(logger::log_folder)) { 21 | std::filesystem::create_directory(logger::log_folder); 22 | } 23 | spdlog::init_thread_pool(THREAD_QUEUE_LENGTH, 1); 24 | spdlog::sink_ptr console_sink = std::make_shared(); 25 | console_sink->set_pattern("[%^%=8l%$] [thread %t]\t%v"); 26 | #ifdef _WIN32 27 | std::string s = (logger::log_folder / "jutta.log").string(); 28 | spdlog::sink_ptr file_sink = std::make_shared(s, FILE_ROTATION_TIME, 3); 29 | #else // _WIN32 30 | spdlog::sink_ptr file_sink = std::make_shared(logger::log_folder / "jutta.log", FILE_ROTATION_TIME, 3); 31 | #endif 32 | file_sink->set_pattern("[%H:%M:%S %z] [%=8l] [thread %t] [%@]\t%v"); 33 | std::vector sinks{file_sink, console_sink}; 34 | std::shared_ptr logger = std::make_shared("", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); 35 | logger->set_level(level); 36 | spdlog::set_default_logger(logger); 37 | } 38 | 39 | void set_log_level(const spdlog::level::level_enum level) { 40 | spdlog::default_logger()->set_level(level); 41 | } 42 | 43 | void deactivate_logger() { 44 | logger::set_log_level(spdlog::level::off); 45 | } 46 | } // namespace logger 47 | -------------------------------------------------------------------------------- /src/serial/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(serial SHARED SerialConnection.cpp) 4 | target_link_libraries(serial PRIVATE logger) 5 | 6 | install(TARGETS serial) 7 | -------------------------------------------------------------------------------- /src/serial/SerialConnection.cpp: -------------------------------------------------------------------------------- 1 | #include "serial/SerialConnection.hpp" 2 | #include "logger/Logger.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" { 11 | #include 12 | #include 13 | #include 14 | #include 15 | } 16 | 17 | //--------------------------------------------------------------------------- 18 | namespace serial { 19 | //--------------------------------------------------------------------------- 20 | SerialConnection::SerialConnection(std::string&& device) : device(std::move(device)) {} 21 | 22 | SerialConnection::~SerialConnection() { 23 | closeTty(); 24 | } 25 | 26 | void SerialConnection::init() { 27 | assert(state == SC_DISABLED); 28 | openTty(device); 29 | configureTty(); 30 | assert(state == SC_READY); 31 | } 32 | 33 | void SerialConnection::openTty(const std::string& device) { 34 | assert(state == SC_DISABLED || state == SC_ERROR); 35 | // Open with: 36 | // O_RDWR - Open for read and write. 37 | // O_NOCTTY - The device never becomes the controlling terminal of the process. 38 | // O_NDELAY - Use non-blocking I/O. 39 | // NOLINTNEXTLINE (hicpp-signed-bitwise) 40 | fd = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); 41 | if (fd < 0) { 42 | // NOLINTNEXTLINE (concurrency-mt-unsafe) 43 | throw std::runtime_error("Failed to open '" + device + "' with: " + strerror(errno)); 44 | } 45 | tcflush(fd, TCIOFLUSH); 46 | state = SC_OPENED; 47 | SPDLOG_INFO("Successfully opened serial device: {}", device); 48 | } 49 | 50 | void SerialConnection::configureTty() { 51 | assert(state == SC_OPENED); 52 | assert(fd != -1); // Ensure opening was successfull 53 | // Ensure the device is a TTY: 54 | if (!isatty(fd)) { 55 | throw std::runtime_error("Failed to configure '" + device + "'. The device is no TTY."); 56 | } 57 | 58 | termios config{}; 59 | if (tcgetattr(fd, &config) < 0) { 60 | // NOLINTNEXTLINE (concurrency-mt-unsafe) 61 | throw std::runtime_error("Failed to get configuration for '" + device + "' with: " + strerror(errno)); 62 | } 63 | 64 | config.c_iflag = 0; 65 | config.c_oflag = 0; 66 | config.c_cflag = CS8 | CREAD | CLOCAL; 67 | config.c_lflag = 0; 68 | /** 69 | * Max time in tenth of seconds between characters allowed. 70 | * We abuse this since the coffee maker will make a 8ms break between each byte it sends. 71 | * For fail safe reasons we allow 2 ms between the individual bytes. 72 | * http://unixwiz.net/techtips/termios-vmin-vtime.html 73 | **/ 74 | config.c_cc[VTIME] = 2; 75 | /** 76 | * Number of characters have been received, with no more data available. 77 | * http://unixwiz.net/techtips/termios-vmin-vtime.html 78 | **/ 79 | config.c_cc[VMIN] = 4; 80 | if (cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0) { 81 | // NOLINTNEXTLINE (concurrency-mt-unsafe) 82 | throw std::runtime_error("Failed to set the baud rate for '" + device + "' with: " + strerror(errno)); 83 | } 84 | 85 | if (tcsetattr(fd, TCSANOW, &config) < 0) { 86 | // NOLINTNEXTLINE (concurrency-mt-unsafe) 87 | throw std::runtime_error("Failed to set configuration for '" + device + "' with: " + strerror(errno)); 88 | } 89 | state = SC_READY; 90 | SPDLOG_INFO("Successfully configured serial device."); 91 | } 92 | 93 | void SerialConnection::closeTty() { 94 | if (state != SC_DISABLED) { 95 | close(fd); 96 | fd = -1; 97 | SPDLOG_INFO("Serial device closed."); 98 | state = SC_DISABLED; 99 | } 100 | } 101 | 102 | size_t SerialConnection::read_serial(std::array& buffer) const { 103 | assert(state == SC_READY); 104 | return read(fd, buffer.data(), buffer.size()); 105 | } 106 | 107 | size_t SerialConnection::write_serial(const std::array& data) const { 108 | assert(state == SC_READY); 109 | size_t result = write(fd, data.data(), data.size()); 110 | return result; 111 | } 112 | 113 | void SerialConnection::flush() const { 114 | // Wait until everything has been send: 115 | tcdrain(fd); 116 | } 117 | 118 | std::vector SerialConnection::get_available_ports() { 119 | std::vector ports{}; 120 | return ports; 121 | } 122 | //--------------------------------------------------------------------------- 123 | } // namespace serial 124 | //--------------------------------------------------------------------------- 125 | -------------------------------------------------------------------------------- /src/test_exec/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | if(JUTTA_PROTO_BUILD_TEST_EXEC) 4 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 5 | 6 | # Handshake Test: 7 | set(EXECUTABLE_NAME "handshake_test") 8 | set(EXECUTABLE_MAIN "handshake_test.cpp") 9 | 10 | add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_MAIN}) 11 | target_link_libraries(${EXECUTABLE_NAME} PRIVATE logger jutta_proto) 12 | set_property(SOURCE ${EXECUTABLE_MAIN} PROPERTY COMPILE_DEFINITIONS) 13 | endif() 14 | -------------------------------------------------------------------------------- /src/test_exec/handshake_test.cpp: -------------------------------------------------------------------------------- 1 | #include "jutta_proto/JuttaCommands.hpp" 2 | #include "jutta_proto/JuttaConnection.hpp" 3 | #include "logger/Logger.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | const std::array disc1 = {0x08, 0x0E, 0x0C, 0x04, 0x03, 0x0D, 0x0A, 0x0B, 0x00, 0x0F, 0x06, 0x07, 0x02, 0x05, 0x01, 0x09}; 11 | const std::array disc2 = {0x04, 0x0B, 0x0D, 0x0A, 0x00, 0x07, 0x0F, 0x05, 0x09, 0x08, 0x03, 0x01, 0x0E, 0x02, 0x0C, 0x06}; 12 | 13 | //-------------------------------------------------------------------------------------------------------------------------- 14 | // const std::array numbers1 = {14, 4, 3, 2, 1, 13, 8, 11, 6, 15, 12, 7, 10, 5, 0, 9}; 15 | // const std::array numbers2 = {10, 6, 13, 12, 14, 11, 1, 9, 15, 7, 0, 5, 3, 2, 4, 8}; 16 | 17 | uint8_t mod256(int i) { 18 | while (i > 255) { 19 | i -= 256; 20 | } 21 | while (i < 0) { 22 | i += 256; 23 | } 24 | return static_cast(i); 25 | } 26 | 27 | uint8_t shuffle(int dataNibble, int nibbleCount, int keyLeftNibbel, int keyRightNibbel) { 28 | uint8_t i5 = mod256(nibbleCount >> 4); 29 | uint8_t tmp1 = disc1[mod256(dataNibble + nibbleCount + keyLeftNibbel) % 16]; 30 | uint8_t tmp2 = disc2[mod256(tmp1 + keyRightNibbel + i5 - nibbleCount - keyLeftNibbel) % 16]; 31 | uint8_t tmp3 = disc1[mod256(tmp2 + keyLeftNibbel + nibbleCount - keyRightNibbel - i5) % 16]; 32 | return mod256(tmp3 - nibbleCount - keyLeftNibbel) % 16; 33 | } 34 | 35 | std::vector encDecBytes(const std::vector& data, uint8_t key) { 36 | std::vector result; 37 | result.resize(data.size()); 38 | uint8_t keyLeftNibbel = key >> 4; 39 | uint8_t keyRightNibbel = key & 15; 40 | int nibbelCount = 0; 41 | for (size_t offset = 0; offset < data.size(); offset++) { 42 | uint8_t d = data[offset]; 43 | uint8_t dataLeftNibbel = d >> 4; 44 | uint8_t dataRightNibbel = d & 15; 45 | uint8_t resultLeftNibbel = shuffle(dataLeftNibbel, nibbelCount++, keyLeftNibbel, keyRightNibbel); 46 | uint8_t resultRightNibbel = shuffle(dataRightNibbel, nibbelCount++, keyLeftNibbel, keyRightNibbel); 47 | result[offset] = (resultLeftNibbel << 4) | resultRightNibbel; 48 | } 49 | return result; 50 | } 51 | 52 | void comp(const std::vector& a, const std::vector& b) { 53 | assert(a.size() == b.size()); 54 | for (size_t i = 0; i < a.size(); i++) { 55 | assert(a[i] == b[i]); 56 | } 57 | } 58 | 59 | std::vector encDecBytes(const std::vector& input) { 60 | std::vector data; 61 | uint8_t key = input[1]; 62 | if (key == 0x1b) { 63 | key = input[2] ^ 0x80; 64 | data.insert(data.end(), input.begin() + 2, input.end()); 65 | data[0] = key; 66 | } else { 67 | data.insert(data.end(), input.begin() + 1, input.end()); 68 | } 69 | std::vector result = encDecBytes(data, key); 70 | comp(encDecBytes(result, key), data); 71 | return result; 72 | } 73 | //-------------------------------------------------------------------------------------------------------------------------- 74 | 75 | void discBasedDecryption(uint8_t* dst, uint8_t* src) { 76 | uint8_t curchar = 0; 77 | uint8_t key = 0; 78 | uint8_t* data = nullptr; 79 | uint8_t* pcVar1 = nullptr; 80 | uint8_t nibbelCount = 0; 81 | uint8_t keyLeftNibble = 0; 82 | uint8_t keyRightNibble = 0; 83 | uint8_t bVar1 = 0; 84 | uint8_t cVar1 = 0; 85 | 86 | key = src[1]; 87 | if (key == 0x1b) { 88 | key = static_cast(src[2] ^ 0x80); 89 | data = src + 2; 90 | src[2] = key; 91 | } else { 92 | data = src + 1; 93 | } 94 | keyLeftNibble = key >> 4; 95 | keyRightNibble = key & 0xf; 96 | curchar = data[1]; 97 | nibbelCount = 0; 98 | while (curchar != 0xd) { 99 | pcVar1 = data + 1; 100 | if (curchar == 0x1b) { 101 | curchar = data[2] ^ 0x80; 102 | data[2] = curchar; 103 | pcVar1 = data + 2; 104 | } 105 | bVar1 = nibbelCount + 1; 106 | uint8_t discOff1 = ((curchar >> 4) + nibbelCount + keyLeftNibble) & 0xf; 107 | uint8_t discOff2 = ((disc1[discOff1] + keyRightNibble - nibbelCount - keyLeftNibble + (nibbelCount >> 4)) & 0xf) + 0x10; 108 | uint8_t discOff3 = (((keyLeftNibble + nibbelCount + disc2[discOff2]) - keyRightNibble) - (nibbelCount >> 4)) & 0xf; 109 | cVar1 = disc1[discOff3] - nibbelCount; 110 | nibbelCount = nibbelCount + 2; 111 | 112 | uint8_t discOff4 = ((curchar & 0xf) + bVar1 + keyLeftNibble) & 0xf; 113 | uint8_t discOff5 = ((((disc1[discOff4] + keyRightNibble + (bVar1 >> 4)) - keyLeftNibble) - bVar1) & 0xf) + 0x10; 114 | uint8_t discOff6 = (((keyLeftNibble + bVar1 + disc2[discOff5]) - keyRightNibble) - (bVar1 >> 4)) & 0xf; 115 | *dst = ((cVar1 - keyLeftNibble) << 4) | (((disc1[discOff6] - bVar1) - keyLeftNibble) & 0xf); 116 | dst++; 117 | data = pcVar1; 118 | curchar = pcVar1[1]; 119 | } 120 | *dst = 0; 121 | } 122 | 123 | std::vector decode(const std::vector& data) { 124 | std::vector input; 125 | input.insert(input.end(), data.begin(), data.end()); 126 | discBasedDecryption(input.data(), input.data()); 127 | return input; 128 | } 129 | 130 | void test(uint8_t key) { 131 | uint8_t keyLeftNibble = key >> 4; 132 | uint8_t keyRightNibble = key & 0xf; 133 | 134 | uint8_t output = '@'; 135 | uint8_t outputLeftNibble = output >> 4; 136 | uint8_t outputRightNibble = output & 0xf; 137 | 138 | uint8_t cVar1 = outputLeftNibble + keyLeftNibble; 139 | } 140 | 141 | int main(int /*argc*/, char** /*argv*/) { 142 | logger::setup_logger(spdlog::level::debug); 143 | SPDLOG_INFO("Starting handshake test..."); 144 | 145 | jutta_proto::JuttaConnection connection("/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A5047JSK-if00-port0"); 146 | connection.init(); 147 | while (true) { 148 | std::shared_ptr coffeeMakerType = nullptr; 149 | while (!coffeeMakerType || coffeeMakerType->find("ty:") == std::string::npos) { 150 | coffeeMakerType = connection.write_decoded_with_response(jutta_proto::JUTTA_GET_TYPE, std::chrono::milliseconds{1000}); 151 | if (!coffeeMakerType) { 152 | std::this_thread::sleep_for(std::chrono::milliseconds{500}); 153 | } 154 | } 155 | SPDLOG_INFO("Found coffee maker: {}", *coffeeMakerType); 156 | 157 | // Handshake: 158 | SPDLOG_INFO("Continuing with the handshake..."); 159 | SPDLOG_INFO("Sending '@T1'..."); 160 | if (!connection.write_decoded_wait_for("@T1\r\n", "@t1\r\n")) { 161 | SPDLOG_WARN("Failed to receive '@t1'"); 162 | continue; 163 | } 164 | 165 | SPDLOG_INFO("Waiting for '@T2:...'..."); 166 | std::vector buf; 167 | // NOLINTNEXTLINE (abseil-string-find-str-contains) 168 | while (connection.vec_to_string(buf).find("@T2") == std::string::npos) { 169 | connection.read_decoded(buf); 170 | } 171 | 172 | SPDLOG_INFO("Sending '@t2:...'..."); 173 | buf.clear(); 174 | connection.write_decoded("@t2:8120000000\r\n"); 175 | // NOLINTNEXTLINE (abseil-string-find-str-contains) 176 | while (connection.vec_to_string(buf).find("@T3") == std::string::npos) { 177 | connection.read_decoded(buf); 178 | } 179 | 180 | SPDLOG_INFO("Sending '@t3'..."); 181 | connection.write_decoded("@t3\r\n"); 182 | SPDLOG_INFO("Handshake done!"); 183 | break; 184 | } 185 | 186 | test(); 187 | 188 | std::vector response; 189 | while (true) { 190 | connection.read_decoded(response); 191 | if (!response.empty()) { 192 | if (response[0] == '&') { 193 | std::vector dec1 = decode(response); 194 | std::string s1 = jutta_proto::JuttaConnection::vec_to_string(dec1); 195 | SPDLOG_INFO("Received1: {}", s1); 196 | std::vector dec2 = encDecBytes(response); 197 | std::string s2 = jutta_proto::JuttaConnection::vec_to_string(dec2); 198 | SPDLOG_INFO("Received2: {}", s2); 199 | } 200 | response.clear(); 201 | } 202 | } 203 | 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(Catch2 REQUIRED) 4 | include(Catch) 5 | 6 | add_executable(proto_tests Tests.cpp) 7 | 8 | set_target_properties(proto_tests PROPERTIES UNITY_BUILD OFF) 9 | target_link_libraries(proto_tests PRIVATE Catch2::Catch2) 10 | 11 | catch_discover_tests(proto_tests) 12 | 13 | -------------------------------------------------------------------------------- /tests/Tests.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | 3 | #include 4 | --------------------------------------------------------------------------------