├── ChangeLog.md ├── max31855.png ├── screenshots ├── 0.png ├── 1.png ├── 2.png └── 3.png ├── application.fam ├── ReadMe.md ├── max31855.h ├── max31855.c ├── .github └── workflows │ └── build.yml ├── max31855_app.c └── .clang-format /ChangeLog.md: -------------------------------------------------------------------------------- 1 | - 0.1 - Initial version -------------------------------------------------------------------------------- /max31855.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skotopes/flipperzero_max31855/HEAD/max31855.png -------------------------------------------------------------------------------- /screenshots/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skotopes/flipperzero_max31855/HEAD/screenshots/0.png -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skotopes/flipperzero_max31855/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skotopes/flipperzero_max31855/HEAD/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skotopes/flipperzero_max31855/HEAD/screenshots/3.png -------------------------------------------------------------------------------- /application.fam: -------------------------------------------------------------------------------- 1 | App( 2 | appid="max31855", 3 | name="MAX31855", 4 | apptype=FlipperAppType.EXTERNAL, 5 | entry_point="max31855_app", 6 | stack_size=2 * 1024, 7 | fap_category="GPIO", 8 | fap_version="0.1", 9 | fap_icon="max31855.png", 10 | fap_description="MAX31855 Thermocouple Sensor Application", 11 | fap_author="Aku", 12 | fap_weburl="https://github.com/skotopes/flipperzero_max31855", 13 | ) 14 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | MAX31855 (K, J, N, T, S, R, and E)-thermocouple sensor app for flipper zero. 4 | 5 | Shows following data: 6 | 7 | - Thermocouple temperature 8 | - Internal temperature 9 | - Fault status and flags 10 | 11 | # Pinout 12 | 13 | This application will work with any chip/thermocouple type, but ensure that you have appropriate combo. 14 | Most common one is MAX31855K and K-type thermocouple. 15 | 16 | Also check if your sensor board got it's own LDO for power supply. If there are no LDO on your board then connect sensor to 3.3V. 17 | Boards with LDO are recommended. 18 | 19 | Flipper | Sensor 20 | --------|------- 21 | 1 | 5V (If your sensor board have LDO) 22 | 3 | DO(MISO) 23 | 4 | CS 24 | 5 | CLK 25 | 8 | GND 26 | 9 | 3.3V (If your sensor board do not have LDO) 27 | -------------------------------------------------------------------------------- /max31855.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | static inline float max31855_unpack_temp(uint32_t data) { 7 | data >>= 18; 8 | return ((data & 0x4000) ? -0.25f : 0.25f) * (data & 0x3FFF); 9 | } 10 | 11 | static inline float max31855_unpack_internal_temp(uint32_t data) { 12 | data >>= 4; 13 | return ((data & 0x800) ? -0.0625f : 0.0625f) * (data & 0x7FF); 14 | } 15 | 16 | static inline bool max31855_unpack_fault(uint32_t data) { 17 | return data & (1u << 16); 18 | } 19 | 20 | static inline bool max31855_unpack_oc(uint32_t data) { 21 | return data & (1u << 0); 22 | } 23 | 24 | static inline bool max31855_unpack_scg(uint32_t data) { 25 | return data & (1u << 1); 26 | } 27 | 28 | static inline bool max31855_unpack_scv(uint32_t data) { 29 | return data & (1u << 2); 30 | } 31 | 32 | void max31855_open(); 33 | 34 | void max31855_close(); 35 | 36 | uint32_t max31855_read(); 37 | -------------------------------------------------------------------------------- /max31855.c: -------------------------------------------------------------------------------- 1 | #include "max31855.h" 2 | 3 | #include 4 | #include 5 | 6 | void max31855_open() { 7 | furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); 8 | furi_hal_power_enable_otg(); 9 | } 10 | 11 | void max31855_close() { 12 | furi_hal_power_disable_otg(); 13 | furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_external); 14 | } 15 | 16 | uint32_t max31855_read() { 17 | uint32_t data = 0; 18 | uint8_t buffer[4] = {0}; 19 | 20 | furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); 21 | furi_hal_spi_bus_rx( 22 | &furi_hal_spi_bus_handle_external, buffer, sizeof(buffer), FuriWaitForever); 23 | furi_hal_spi_release(&furi_hal_spi_bus_handle_external); 24 | 25 | data = buffer[0]; 26 | data <<= 8; 27 | data |= buffer[1]; 28 | data <<= 8; 29 | data |= buffer[2]; 30 | data <<= 8; 31 | data |= buffer[3]; 32 | 33 | return data; 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "FAP: Build for multiple SDK sources" 2 | # This will build your app for dev and release channels on GitHub. 3 | # It will also build your app every day to make sure it's up to date with the latest SDK changes. 4 | # See https://github.com/marketplace/actions/build-flipper-application-package-fap for more information 5 | 6 | on: 7 | push: 8 | ## put your main branch name under "braches" 9 | #branches: 10 | # - master 11 | pull_request: 12 | schedule: 13 | # do a build every day 14 | - cron: "1 1 * * *" 15 | 16 | jobs: 17 | ufbt-build: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | include: 22 | - name: dev channel 23 | sdk-channel: dev 24 | - name: release channel 25 | sdk-channel: release 26 | # You can add unofficial channels here. See ufbt action docs for more info. 27 | name: 'ufbt: Build for ${{ matrix.name }}' 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | - name: Build with ufbt 32 | uses: flipperdevices/flipperzero-ufbt-action@v0.1.1 33 | id: build-app 34 | with: 35 | sdk-channel: ${{ matrix.sdk-channel }} 36 | - name: Upload app artifacts 37 | uses: actions/upload-artifact@v3 38 | with: 39 | # See ufbt action docs for other output variables 40 | name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }} 41 | path: ${{ steps.build-app.outputs.fap-artifacts }} 42 | -------------------------------------------------------------------------------- /max31855_app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "max31855.h" 13 | 14 | typedef struct { 15 | Gui* gui; 16 | NotificationApp* notification; 17 | ViewPort* view_port; 18 | FuriMessageQueue* event_queue; 19 | 20 | FuriMutex* mutex; 21 | uint32_t data; 22 | } Max31855App; 23 | 24 | static void max31855_render_callback(Canvas* canvas, void* ctx) { 25 | Max31855App* state = ctx; 26 | char buffer[64]; 27 | 28 | float tc_temp = max31855_unpack_temp(state->data); 29 | float chip_temp = max31855_unpack_internal_temp(state->data); 30 | 31 | if(locale_get_measurement_unit() == LocaleMeasurementUnitsImperial) { 32 | tc_temp = locale_celsius_to_fahrenheit(tc_temp); 33 | chip_temp = locale_celsius_to_fahrenheit(chip_temp); 34 | } 35 | 36 | furi_mutex_acquire(state->mutex, FuriWaitForever); 37 | 38 | canvas_set_font(canvas, FontPrimary); 39 | canvas_draw_str(canvas, 0, 15, "TC temp:"); 40 | 41 | canvas_set_font(canvas, FontBigNumbers); 42 | snprintf(buffer, sizeof(buffer), "%0.2f", (double)tc_temp); 43 | canvas_draw_str(canvas, 50, 15, buffer); 44 | 45 | canvas_set_font(canvas, FontSecondary); 46 | snprintf(buffer, sizeof(buffer), "Chip temp: %0.2f", (double)chip_temp); 47 | canvas_draw_str(canvas, 0, 28, buffer); 48 | 49 | if(max31855_unpack_fault(state->data)) { 50 | snprintf( 51 | buffer, 52 | sizeof(buffer), 53 | "Fault: %s %s %s", 54 | max31855_unpack_oc(state->data) ? "OC" : "", 55 | max31855_unpack_scg(state->data) ? "SCG" : "", 56 | max31855_unpack_scv(state->data) ? "SCV" : ""); 57 | canvas_draw_str(canvas, 0, 41, buffer); 58 | } 59 | 60 | canvas_set_font(canvas, FontSecondary); 61 | canvas_draw_str(canvas, 0, 63, "[back] - to exit"); 62 | 63 | furi_mutex_release(state->mutex); 64 | } 65 | 66 | static void max31855_input_callback(InputEvent* input_event, void* ctx) { 67 | FuriMessageQueue* event_queue = ctx; 68 | if(input_event->type == InputTypeShort) { 69 | furi_message_queue_put(event_queue, input_event, FuriWaitForever); 70 | } 71 | } 72 | 73 | int32_t max31855_app(void* p) { 74 | UNUSED(p); 75 | 76 | Max31855App state = {0}; 77 | state.gui = furi_record_open(RECORD_GUI); 78 | state.notification = furi_record_open(RECORD_NOTIFICATION); 79 | state.view_port = view_port_alloc(); 80 | state.event_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); 81 | state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); 82 | 83 | view_port_draw_callback_set(state.view_port, max31855_render_callback, &state); 84 | view_port_input_callback_set(state.view_port, max31855_input_callback, state.event_queue); 85 | gui_add_view_port(state.gui, state.view_port, GuiLayerFullscreen); 86 | max31855_open(); 87 | notification_message(state.notification, &sequence_display_backlight_enforce_on); 88 | 89 | InputEvent event; 90 | do { 91 | bool do_refresh = furi_message_queue_get(state.event_queue, &event, 1000) != FuriStatusOk; 92 | 93 | furi_mutex_acquire(state.mutex, FuriWaitForever); 94 | 95 | if(do_refresh) { 96 | state.data = max31855_read(); 97 | } else if(event.key == InputKeyBack) { 98 | furi_mutex_release(state.mutex); 99 | break; 100 | } 101 | 102 | furi_mutex_release(state.mutex); 103 | view_port_update(state.view_port); 104 | } while(true); 105 | 106 | notification_message(state.notification, &sequence_display_backlight_enforce_auto); 107 | max31855_close(); 108 | gui_remove_view_port(state.gui, state.view_port); 109 | 110 | furi_mutex_free(state.mutex); 111 | furi_message_queue_free(state.event_queue); 112 | view_port_free(state.view_port); 113 | state.notification = NULL; 114 | furi_record_close(RECORD_NOTIFICATION); 115 | state.gui = NULL; 116 | furi_record_close(RECORD_GUI); 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignArrayOfStructures: None 6 | AlignConsecutiveMacros: None 7 | AlignConsecutiveAssignments: None 8 | AlignConsecutiveBitFields: None 9 | AlignConsecutiveDeclarations: None 10 | AlignEscapedNewlines: Left 11 | AlignOperands: Align 12 | AlignTrailingComments: false 13 | AllowAllArgumentsOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: false 15 | AllowShortEnumsOnASingleLine: true 16 | AllowShortBlocksOnASingleLine: Never 17 | AllowShortCaseLabelsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: None 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortIfStatementsOnASingleLine: WithoutElse 21 | AllowShortLoopsOnASingleLine: true 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: None 24 | AlwaysBreakBeforeMultilineStrings: false 25 | AlwaysBreakTemplateDeclarations: Yes 26 | AttributeMacros: 27 | - __capability 28 | BinPackArguments: false 29 | BinPackParameters: false 30 | BraceWrapping: 31 | AfterCaseLabel: false 32 | AfterClass: false 33 | AfterControlStatement: Never 34 | AfterEnum: false 35 | AfterFunction: false 36 | AfterNamespace: false 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false 41 | BeforeCatch: false 42 | BeforeElse: false 43 | BeforeLambdaBody: false 44 | BeforeWhile: false 45 | IndentBraces: false 46 | SplitEmptyFunction: true 47 | SplitEmptyRecord: true 48 | SplitEmptyNamespace: true 49 | BreakBeforeBinaryOperators: None 50 | BreakBeforeConceptDeclarations: true 51 | BreakBeforeBraces: Attach 52 | BreakBeforeInheritanceComma: false 53 | BreakInheritanceList: BeforeColon 54 | BreakBeforeTernaryOperators: false 55 | BreakConstructorInitializersBeforeComma: false 56 | BreakConstructorInitializers: BeforeComma 57 | BreakAfterJavaFieldAnnotations: false 58 | BreakStringLiterals: false 59 | ColumnLimit: 99 60 | CommentPragmas: '^ IWYU pragma:' 61 | QualifierAlignment: Leave 62 | CompactNamespaces: false 63 | ConstructorInitializerIndentWidth: 4 64 | ContinuationIndentWidth: 4 65 | Cpp11BracedListStyle: true 66 | DeriveLineEnding: true 67 | DerivePointerAlignment: false 68 | DisableFormat: false 69 | EmptyLineAfterAccessModifier: Never 70 | EmptyLineBeforeAccessModifier: LogicalBlock 71 | ExperimentalAutoDetectBinPacking: false 72 | PackConstructorInitializers: BinPack 73 | BasedOnStyle: '' 74 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 75 | AllowAllConstructorInitializersOnNextLine: true 76 | FixNamespaceComments: false 77 | ForEachMacros: 78 | - foreach 79 | - Q_FOREACH 80 | - BOOST_FOREACH 81 | IfMacros: 82 | - KJ_IF_MAYBE 83 | IncludeBlocks: Preserve 84 | IncludeCategories: 85 | - Regex: '.*' 86 | Priority: 1 87 | SortPriority: 0 88 | CaseSensitive: false 89 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 90 | Priority: 3 91 | SortPriority: 0 92 | CaseSensitive: false 93 | - Regex: '.*' 94 | Priority: 1 95 | SortPriority: 0 96 | CaseSensitive: false 97 | IncludeIsMainRegex: '(Test)?$' 98 | IncludeIsMainSourceRegex: '' 99 | IndentAccessModifiers: false 100 | IndentCaseLabels: false 101 | IndentCaseBlocks: false 102 | IndentGotoLabels: true 103 | IndentPPDirectives: None 104 | IndentExternBlock: AfterExternBlock 105 | IndentRequires: false 106 | IndentWidth: 4 107 | IndentWrappedFunctionNames: true 108 | InsertTrailingCommas: None 109 | JavaScriptQuotes: Leave 110 | JavaScriptWrapImports: true 111 | KeepEmptyLinesAtTheStartOfBlocks: false 112 | LambdaBodyIndentation: Signature 113 | MacroBlockBegin: '' 114 | MacroBlockEnd: '' 115 | MaxEmptyLinesToKeep: 1 116 | NamespaceIndentation: None 117 | ObjCBinPackProtocolList: Auto 118 | ObjCBlockIndentWidth: 4 119 | ObjCBreakBeforeNestedBlockParam: true 120 | ObjCSpaceAfterProperty: true 121 | ObjCSpaceBeforeProtocolList: true 122 | PenaltyBreakAssignment: 10 123 | PenaltyBreakBeforeFirstCallParameter: 30 124 | PenaltyBreakComment: 10 125 | PenaltyBreakFirstLessLess: 0 126 | PenaltyBreakOpenParenthesis: 0 127 | PenaltyBreakString: 10 128 | PenaltyBreakTemplateDeclaration: 10 129 | PenaltyExcessCharacter: 100 130 | PenaltyReturnTypeOnItsOwnLine: 60 131 | PenaltyIndentedWhitespace: 0 132 | PointerAlignment: Left 133 | PPIndentWidth: -1 134 | ReferenceAlignment: Pointer 135 | ReflowComments: false 136 | RemoveBracesLLVM: false 137 | SeparateDefinitionBlocks: Leave 138 | ShortNamespaceLines: 1 139 | SortIncludes: Never 140 | SortJavaStaticImport: Before 141 | SortUsingDeclarations: false 142 | SpaceAfterCStyleCast: false 143 | SpaceAfterLogicalNot: false 144 | SpaceAfterTemplateKeyword: true 145 | SpaceBeforeAssignmentOperators: true 146 | SpaceBeforeCaseColon: false 147 | SpaceBeforeCpp11BracedList: false 148 | SpaceBeforeCtorInitializerColon: true 149 | SpaceBeforeInheritanceColon: true 150 | SpaceBeforeParens: Never 151 | SpaceBeforeParensOptions: 152 | AfterControlStatements: false 153 | AfterForeachMacros: false 154 | AfterFunctionDefinitionName: false 155 | AfterFunctionDeclarationName: false 156 | AfterIfMacros: false 157 | AfterOverloadedOperator: false 158 | BeforeNonEmptyParentheses: false 159 | SpaceAroundPointerQualifiers: Default 160 | SpaceBeforeRangeBasedForLoopColon: true 161 | SpaceInEmptyBlock: false 162 | SpaceInEmptyParentheses: false 163 | SpacesBeforeTrailingComments: 1 164 | SpacesInAngles: Never 165 | SpacesInConditionalStatement: false 166 | SpacesInContainerLiterals: false 167 | SpacesInCStyleCastParentheses: false 168 | SpacesInLineCommentPrefix: 169 | Minimum: 1 170 | Maximum: -1 171 | SpacesInParentheses: false 172 | SpacesInSquareBrackets: false 173 | SpaceBeforeSquareBrackets: false 174 | BitFieldColonSpacing: Both 175 | Standard: c++03 176 | StatementAttributeLikeMacros: 177 | - Q_EMIT 178 | StatementMacros: 179 | - Q_UNUSED 180 | - QT_REQUIRE_VERSION 181 | TabWidth: 4 182 | UseCRLF: false 183 | UseTab: Never 184 | WhitespaceSensitiveMacros: 185 | - STRINGIZE 186 | - PP_STRINGIZE 187 | - BOOST_PP_STRINGIZE 188 | - NS_SWIFT_NAME 189 | - CF_SWIFT_NAME 190 | ... 191 | 192 | --------------------------------------------------------------------------------