├── LICENSE ├── README.md ├── binaries ├── kernel-soldering.img └── kernel-syringe.img ├── include ├── btbl.h ├── cfa.h ├── cfa_common.h ├── cfa_private.h ├── cfa_stub.h ├── list.h └── ltbl.h ├── instrumentation ├── hookit ├── hookit.cfg.sample └── hooks.ld ├── library └── cfa_stub.c ├── measurement-engine ├── cfa.c └── cfa_asm.S ├── runtime-tracer └── hooks_a32.S └── samples ├── soldering ├── Button.c ├── Button.h ├── Buzzer.c ├── Buzzer.h ├── Config.c ├── Config.h ├── Display.c ├── Display.h ├── Encoder.c ├── Encoder.h ├── History.c ├── History.h ├── Iron.c ├── Iron.h ├── LedControl.c ├── LedControl.h ├── README.md ├── Screen.c ├── Screen.h ├── ScreenConfig.c ├── ScreenConfig.h ├── ScreenError.c ├── ScreenError.h ├── ScreenMain.c ├── ScreenMain.h ├── ScreenPower.c ├── ScreenPower.h ├── ScreenTune.c ├── ScreenTune.h ├── ScreenWork.c ├── ScreenWork.h ├── arduino.c ├── arduino.h ├── constants.h ├── eeprom.c ├── eeprom.h ├── hooks.ld ├── oo.h ├── serial.c ├── serial.h ├── solderingIron.c ├── types.h ├── util.c └── util.h └── syringe ├── LiquidCrystal.h ├── README.md ├── attack.txt ├── syringe-auth.txt ├── syringePump.c └── util.h /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2016 Aalto University 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Accompanying Material for C-FLAT: Control Flow Attestation for Embedded Systems Software 2 | 3 | This archive contains the accompanying material for [C-FLAT: Control Flow 4 | Attestation for Embedded Systems Software][1]. 5 | 6 | This repository contains the source code for the C-FLAT Runtime Tracer, 7 | Measurement engine, C-FLAT Library and the sample applications used for 8 | evaluation. It also contains our binary instrumentation tool for ARM 9 | binaries and pre-build kernel images of the sample applications 10 | instrumented with C-FLAT runnable on Raspberry Pi 2. 11 | 12 | Note: Due to confidentiality reasons, we are unable to make the complete 13 | platform source code available in this release. It may, however, be open 14 | sourced in the future, in which case this README will be updated with 15 | information on how to access the complete source code. 16 | 17 | Those wishing to reproduce C-FLAT on a Raspberry Pi 2 or other ARM-based 18 | board will need to provide the implementation for the bootloader and 19 | secure world kernel. In particular the kernel needs to provide: 20 | 21 | - Basic UART functionality and printf-style formatted printing (used 22 | through `info()` / `debug()` macros throughout the source code). 23 | 24 | - Dynamic memory allocation (through `malloc()` / `free()`). For the 25 | evaluation, we ported v3.0 of the [Two-Level Segregated Fit memory 26 | allocator implementation by Matthew Conte][2] to the Raspberry Pi 2 27 | 28 | - [BLAKE2][3] cryptographic hash function. For the evaluation, we ported 29 | the [reference implementation of BLAKE2s][4] to the Raspberry Pi 2 30 | 31 | - `memcpy()` and `memset()`. For the evaluation, we used the `memcpy` 32 | and `memset` optimized for the Raspberry Pi from [Simon J. Halls 33 | copies-and-fills library][5]. 34 | 35 | ## Contents 36 | 37 | - `binaries`: Runnable pre-build kernel images for Raspberry Pi 2 38 | - `instrumentation`: instrumentation tool 39 | - `library`: C-FLAT Library code 40 | - `runtime-tracer`: Runtime Tracer Trampolines 41 | - `measurement-engine`: C-FLAT Measurement Engine 42 | - `samples/syringe`: Sample program and output discussed in paper 43 | - `samples/soldering`: Sample program and output discussed in technical report 44 | 45 | ## Requirements 46 | 47 | - [Raspberry Pi 2 Model B][6] with Broadcom BCM2836 SoC and ARM Cortex-A7 CPU 48 | - USB TO UART interface, such as [Waveshare DVK512][7] 49 | 50 | ## Running the pre-built kernel images 51 | 52 | The assume that the Raspberry Pi 2 SD card has been prepared with [pre-compiled 53 | binaries of the current bootloader/GPU firmware][8] available from the Raspberry 54 | Pi Foundation. The easiest way to obtain these is to install a [prebuilt Raspbian 55 | image][9] on the SD card using the [offical instructions][9]. 56 | 57 | NOTE: WE DO NOT USE RASPBIAN LINUX IN THIS PROTOTYPE. THE ONLY THING NEEDED ARE 58 | THE FIRMWARE IMAGES NEEDED TO BOOT UP THE BOARD AND THE BARE-METAL KERNEL IMAGE 59 | REFERRED TO BELOW. 60 | 61 | In order to seize control of the TrustZone secure world, the bootloader needs to 62 | obtain control of the board while it’s still in secure mode. This can be 63 | achieved by setting the `kernel_old=1` option in `config.txt` on the Raspberry 2 64 | SD card `boot` partition: 65 | 66 | kernel_old=1 67 | disable_commandline_tags=0 68 | 69 | With this done, the bare-metal `kernel.img` can be loaded onto the Raspberry Pi 70 | 2 SD card: 71 | 72 | $ cp binaries/kernel-.img /boot/kernel.img 73 | 74 | where `/boot` is the boot partition of the Raspberry Pi 2 SD card. 75 | 76 | Please make sure that `kernel.img` is the only `.img` file present. In 77 | particular that `kernel7.img` is NOT present on the SD card. 78 | 79 | ## Disclaimer 80 | 81 | All implementations are only research prototypes! 82 | 83 | Our code is NOT safe for production use! Please use it only for tests. 84 | 85 | ## License 86 | 87 | Copyright (c) 2016 Aalto University 88 | 89 | Licensed under the Apache License, Version 2.0 (the "License"); 90 | you may not use this file except in compliance with the License. 91 | You may obtain a copy of the License at 92 | 93 | [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 94 | 95 | Unless required by applicable law or agreed to in writing, software 96 | distributed under the License is distributed on an "AS IS" BASIS, 97 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 98 | See the License for the specific language governing permissions and 99 | limitations under the License. 100 | 101 | 102 | [1]: https://arxiv.org/abs/1605.07763 "C-FLAT: Control-FLow ATtestation for Embedded Systems Software" 103 | [2]: https://github.com/mattconte/tlsf "mattconte/tlsf: Two-Level Segregated Fit memory allocator implementation" 104 | [3]: https://blake2.net/ "BLAKE2 — fast secure hashing" 105 | [4]: https://github.com/BLAKE2/BLAKE2 "BLAKE2/BLAKE2: BLAKE2 official implementations" 106 | [5]: https://github.com/simonjhall/copies-and-fills "simonjhall/copies-and-fills" 107 | [6]: https://www.raspberrypi.org/products/raspberry-pi-2-model-b/ "Raspberry Pi 2 Model B" 108 | [7]: http://www.waveshare.com/dvk512.htm "DVK512, Raspberry Pi Expansion Board" 109 | [8]: https://github.com/raspberrypi/firmware "pre-compiled binaries of the current bootloader/GPU firmware" 110 | [9]: https://www.raspberrypi.org/documentation/installation/installing-images/README.md "Installing Operating System Images" 111 | -------------------------------------------------------------------------------- /binaries/kernel-soldering.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/control-flow-attestation/c-flat/83bb9f14bf65db9accef957fa538a276662fd017/binaries/kernel-soldering.img -------------------------------------------------------------------------------- /binaries/kernel-syringe.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/control-flow-attestation/c-flat/83bb9f14bf65db9accef957fa538a276662fd017/binaries/kernel-syringe.img -------------------------------------------------------------------------------- /include/btbl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Branch table entry structure 3 | */ 4 | #ifndef BTBL_H 5 | #define BTBL_H 6 | 7 | #include 8 | 9 | typedef struct btbl_entry { 10 | uint32_t src; 11 | uint32_t dst; 12 | } btbl_entry_t; 13 | 14 | #endif /* BTBL_H */ 15 | 16 | -------------------------------------------------------------------------------- /include/cfa.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C-FLAT Secure World API 3 | */ 4 | #ifndef CFA_H 5 | #define CFA_H 6 | 7 | #ifdef CFA_STUB_H 8 | #error "CFA_STUB_H already defined, did you mean to call the normal world API?" 9 | #endif 10 | 11 | #include 12 | #include 13 | 14 | #include "list.h" 15 | 16 | #include "btbl.h" 17 | #include "ltbl.h" 18 | 19 | #include "cfa_common.h" 20 | #include "cfa_private.h" 21 | 22 | typedef struct cfa_event { 23 | union { 24 | uint32_t type; 25 | uint32_t err; 26 | }; 27 | union { 28 | cfa_addr_t src_addr; 29 | cfa_init_params_t* iparams; 30 | cfa_quote_params_t* qparams; 31 | }; 32 | cfa_addr_t dst_addr; 33 | cfa_addr_t lr_value; 34 | } cfa_event_t; 35 | 36 | /* Node in the control flow graph */ 37 | typedef struct cfa_node { 38 | cfa_addr_t start; 39 | cfa_addr_t end; 40 | } cfa_node_t; 41 | 42 | /* Path descriptor */ 43 | typedef struct cfa_path_desc { 44 | uint32_t ctr; 45 | uint8_t digest[DIGEST_SIZE_BYTES]; 46 | } cfa_path_desc_t; 47 | 48 | /* Currently executed path context */ 49 | typedef struct cfa_path_ctx { 50 | cfa_hash_state_t state; 51 | } cfa_path_ctx_t; 52 | 53 | /* Loop descriptor */ 54 | typedef struct cfa_loop_desc { 55 | uint8_t digest[DIGEST_SIZE_BYTES]; 56 | list_t path_list; 57 | } cfa_loop_desc_t; 58 | 59 | /* Currently executed loop context */ 60 | typedef struct cfa_loop_ctx { 61 | cfa_node_t entry; 62 | cfa_addr_t exit; 63 | cfa_addr_t ret; 64 | cfa_path_ctx_t *path; 65 | cfa_loop_desc_t *desc; 66 | } cfa_loop_ctx_t; 67 | 68 | /* Context for CFA operations */ 69 | typedef struct cfa_ctx { 70 | cfa_hash_state_t state; 71 | cfa_node_t cur_node; 72 | cfa_addr_t main_start; 73 | cfa_addr_t main_end; 74 | const btbl_entry_t *btbl_start; 75 | const btbl_entry_t *btbl_end; 76 | const ltbl_entry_t *ltbl_start; 77 | const ltbl_entry_t *ltbl_end; 78 | list_t loop_stack; 79 | list_t loop_list; 80 | bool initialized; 81 | } cfa_ctx_t; 82 | 83 | /* Secure world API */ 84 | 85 | /*! 86 | * \brief cfa_get_ctx 87 | * Returns the current CFA context. 88 | */ 89 | cfa_ctx_t* cfa_get_ctx(); 90 | 91 | /*! 92 | * \brief cfa_init 93 | * Initialize the CFA subsystem. 94 | * \param main_start Start address of main function 95 | * \param main_end End address of main function 96 | * \param btbl_start Start address of branch table 97 | * \param btbl_end End address of branch table 98 | */ 99 | uint32_t cfa_init(cfa_ctx_t *ctx, 100 | const cfa_addr_t main_start, const cfa_addr_t main_end, 101 | const btbl_entry_t *btbl_start, const btbl_entry_t *btbl_end, 102 | const ltbl_entry_t *ltbl_start, const ltbl_entry_t *ltbl_end); 103 | 104 | /*! 105 | * \brief cfa_event 106 | * Extend the digest. 107 | * \param type Type of control flow event (e.g. CFA_EVENT_XX) 108 | * \param src_addr Source address of the control flow event 109 | * \param dst_addr Destination address of the control flow event 110 | */ 111 | uint32_t cfa_event(cfa_ctx_t *ctx, const cfa_event_t *event); 112 | 113 | /*! 114 | * \brief cfa_quote 115 | * Quote the current digest value. 116 | * \param user_data Unpredictable user-supplied data to be included in quote 117 | * \param user_data_len Length of the user-supplied data 118 | * \param out Output buffer for storing the digest quote 119 | * \param outlen Length of the output buffer for storing the digest quote 120 | */ 121 | uint32_t cfa_quote(cfa_ctx_t *ctx, 122 | const uint8_t *user_data, const uint32_t user_data_len, 123 | uint8_t *out, uint32_t *out_len); 124 | 125 | #endif /* CFA_H */ 126 | -------------------------------------------------------------------------------- /include/cfa_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Aalto University 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef CFA_COMMON_H 17 | #define CFA_COMMON_H 18 | 19 | /* Length of the computed digests */ 20 | #define DIGEST_SIZE_BYTES 16 21 | 22 | /* Length of instruction in bytes */ 23 | #define INSTRUCTION_LEN 4 24 | 25 | /* CFA event types */ 26 | #define CFA_EVENT_INIT 0x00000000 27 | #define CFA_EVENT_B 0x00000001 28 | #define CFA_EVENT_BL 0x00000002 29 | #define CFA_EVENT_BX_LR 0x00000004 30 | #define CFA_EVENT_POP_FP_PC 0x00000008 31 | #define CFA_EVENT_POP_FP_LR 0x00000010 32 | #define CFA_EVENT_BLX_R3 0x00000020 33 | #define CFA_EVENT_QUOTE 0x80000000 34 | #define CFA_EVENT_ERROR 0x000000FF 35 | 36 | /* Internal CFA return values */ 37 | #define CFA_SUCCESS 0x00000000 38 | #define CFA_ERROR_GENERIC 0xFFFF0000 39 | #define CFA_ERROR_BAD_PARAMETERS 0xFFFF0006 40 | #define CFA_ERROR_OUT_OF_MEMORY 0xFFFF000C 41 | 42 | #ifndef ASSEMBLY 43 | 44 | #include 45 | 46 | /* Type alias for memory address */ 47 | typedef uint32_t cfa_addr_t; 48 | 49 | /* Structure for transferring init parameters */ 50 | typedef struct cfa_init_params { 51 | cfa_addr_t main_start; 52 | cfa_addr_t main_end; 53 | const struct btbl_entry *btbl_start; 54 | const struct btbl_entry *btbl_end; 55 | const struct ltbl_entry *ltbl_start; 56 | const struct ltbl_entry *ltbl_end; 57 | } cfa_init_params_t; 58 | 59 | /* Structure for transferring quote parameters */ 60 | typedef struct cfa_quote_params { 61 | const uint8_t *user_data; 62 | uint32_t user_data_len; 63 | uint8_t *out; 64 | uint32_t *out_len; 65 | } cfa_quote_params_t; 66 | 67 | #endif 68 | 69 | #endif /* CFA_COMMON_H */ 70 | 71 | -------------------------------------------------------------------------------- /include/cfa_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C-FLAT Private Declarations 3 | */ 4 | #ifndef CFA_PRIVATE_H 5 | #define CFA_PRIVATE_H 6 | 7 | #include "lib/blake2.h" 8 | 9 | typedef blake2s_state cfa_hash_state_t; 10 | 11 | #ifdef CFA_LTBL_BSEARCH 12 | extern const ltbl_entry_t *ltbl_bsearch(const ltbl_entry_t *start, const ltbl_entry_t *end, cfa_addr_t addr); 13 | #endif 14 | 15 | #endif /* CFA_PRIVATE_H */ 16 | -------------------------------------------------------------------------------- /include/cfa_stub.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Aalto University 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef CFA_STUB_H 17 | #define CFA_STUB_H 18 | 19 | #ifdef CFA_H 20 | #error "CFA_H already defined, did you mean to call the secure world API?" 21 | #endif 22 | 23 | #include 24 | #include 25 | 26 | #include "btbl.h" 27 | #include "ltbl.h" 28 | 29 | #include "cfa_common.h" 30 | 31 | /* Normal world API */ 32 | 33 | /*! 34 | * \brief cfa_init 35 | * Initialize the CFA subsystem. 36 | * \param main_start Start address of main function 37 | * \param main_end End address of main function 38 | * \param u_start Start address of user code to be attested 39 | * \param u_end End address of user code to be attested 40 | */ 41 | uint32_t cfa_init(const cfa_addr_t main_start, const cfa_addr_t main_end, 42 | const btbl_entry_t *btbl_start, const btbl_entry_t *btbl_end, 43 | const ltbl_entry_t *ltbl_start, const ltbl_entry_t *ltbl_end); 44 | 45 | /*! 46 | * \brief cfa_quote 47 | * Quote the current digest value. 48 | * \param out Output buffer for storing the digest quote 49 | * \param outlen Length of the output buffer for storing the digest quote 50 | */ 51 | uint32_t cfa_quote(const uint8_t *user_data, const uint32_t user_data_len, 52 | uint8_t *out, uint32_t *out_len); 53 | 54 | #endif /* CFA_STUB_H */ 55 | 56 | -------------------------------------------------------------------------------- /include/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Singly-Linked List API 3 | */ 4 | #ifndef UTIL_LIST_H 5 | #define UTIL_LIST_H 6 | 7 | #include 8 | 9 | typedef struct elem elem_t; 10 | typedef struct list list_t; 11 | typedef struct iter iter_t; 12 | 13 | typedef struct elem { 14 | elem_t *next; 15 | void *data; 16 | } elem_t; 17 | 18 | typedef struct list { 19 | elem_t *head; 20 | elem_t *last; 21 | } list_t; 22 | 23 | typedef struct iter { 24 | elem_t *curr; 25 | } iter_t; 26 | 27 | void list_init(list_t *list); 28 | void list_finalize(list_t *list); 29 | 30 | void *list_head(const list_t *list); 31 | void *list_last(const list_t *list); 32 | bool list_is_empty(const list_t *list); 33 | 34 | void list_append(list_t *list, void* data); 35 | void *list_remove(list_t *list); 36 | 37 | void list_push(list_t *list, void* data); 38 | void *list_pop(list_t *list); 39 | 40 | iter_t list_iter(list_t *list); 41 | void *list_next(iter_t *iter); 42 | 43 | #endif /* UTIL_LIST_H */ 44 | 45 | -------------------------------------------------------------------------------- /include/ltbl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Loop table entry structure 3 | */ 4 | #ifndef LTBL_H 5 | #define LTBL_H 6 | 7 | #include "lib/cfa_common.h" 8 | 9 | typedef struct ltbl_entry { 10 | cfa_addr_t entry; 11 | cfa_addr_t exit; 12 | } ltbl_entry_t; 13 | 14 | #endif /* LTBL_H */ 15 | 16 | -------------------------------------------------------------------------------- /instrumentation/hookit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # ARMv7 bare metal binary instrumentation tool 4 | # 5 | # Copyright (c) 2016 Aalto University 6 | # 7 | import argparse 8 | import binascii 9 | import ConfigParser 10 | import logging 11 | import math 12 | import mmap 13 | import os.path 14 | import struct 15 | import sys 16 | from argparse import Namespace 17 | from bitarray import bitarray 18 | from capstone.arm import * 19 | from capstone import * 20 | from enum import Enum 21 | from datetime import datetime 22 | 23 | CONFIG_DEFAULT_PATHNAME = './hookit.cfg' 24 | CONFIG_SECTION_CODE_ADDRESSES = 'code-addresses' 25 | CONFIG_SECTION_HOOK_ADDRESSES = 'hook-addresses' 26 | 27 | DEFAULT_BTBL_PATHNAME = 'btbl.c' 28 | 29 | CONFIG_DEFAULTS = { 30 | 'load_address' : '0x0000', 31 | 'text_start' : None, 32 | 'text_end' : None, 33 | 'omit_addresses' : None, 34 | 'hook_b' : None, 35 | 'hook_bl' : None, 36 | 'hook_bx_lr' : None, 37 | 'hook_pop_r3_r4_fp_pc' : None, 38 | 'hook_pop_r4_fp_pc' : None, 39 | 'hook_pop_fp_pc' : None, 40 | 'hook_pop_fp_lr' : None, 41 | 'hook_blx_r3' : None, 42 | } 43 | 44 | class CFS(Enum): 45 | b = 1 46 | bl = 2 47 | bx_lr = 3 48 | pop_fp_pc = 4 49 | pop_fp_lr = 5 50 | blx_r3 = 6 51 | 52 | def new_cfs(cfs_type, cfs_instr, cfs_src, cfs_dst): 53 | return Namespace( 54 | type = cfs_type, 55 | instr = cfs_instr, 56 | src = cfs_src, 57 | dst = cfs_dst, 58 | ) 59 | 60 | 61 | def read_config(pathname): 62 | parser = ConfigParser.SafeConfigParser(CONFIG_DEFAULTS) 63 | parser.read(pathname) 64 | 65 | return Namespace( 66 | load_address = parser.get(CONFIG_SECTION_CODE_ADDRESSES, 'load_address'), 67 | text_start = parser.get(CONFIG_SECTION_CODE_ADDRESSES, 'text_start'), 68 | text_end = parser.get(CONFIG_SECTION_CODE_ADDRESSES, 'text_end'), 69 | omit_addresses = parser.get(CONFIG_SECTION_CODE_ADDRESSES, 'omit_addresses'), 70 | hook_b = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_b'), 71 | hook_bl = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_bl'), 72 | hook_bx_lr = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_bx_lr'), 73 | hook_pop_r3_r4_fp_pc = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_pop_r3_r4_fp_pc'), 74 | hook_pop_r4_fp_pc = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_pop_r4_fp_pc'), 75 | hook_pop_fp_pc = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_pop_fp_pc'), 76 | hook_pop_fp_lr = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_pop_fp_lr'), 77 | hook_blx_r3 = parser.get(CONFIG_SECTION_HOOK_ADDRESSES, 'hook_blx_r3'), 78 | ) 79 | 80 | 81 | def main(): 82 | parser = argparse.ArgumentParser(description='ARMv7 Branch Target Rewriting Tool') 83 | parser.add_argument('file', nargs='?', metavar='FILE', 84 | help='binary file to instrument') 85 | parser.add_argument('-L', '--load-address', dest='load_address', default=None, 86 | help='load address of binary image') 87 | parser.add_argument('--text-start', dest='text_start', default=None, 88 | help='start address of section to instrument') 89 | parser.add_argument('--text-end', dest='text_end', default=None, 90 | help='end address of section to instrument') 91 | parser.add_argument('--omit-addresses', dest='omit_addresses', default=None, 92 | help='comma separated list of addresses of instructions to omit from instrumentation') 93 | parser.add_argument('-l', '--little-endian', dest='flags', default=[], 94 | action='append_const', const=CS_MODE_LITTLE_ENDIAN, 95 | help='disassemble in little endian mode') 96 | parser.add_argument('-b', '--big-endian', dest='flags', default=[], 97 | action='append_const', const=CS_MODE_BIG_ENDIAN, 98 | help='disassemble in big endian mode') 99 | parser.add_argument('-o', '--outfile', dest='outfile', default=None, 100 | help='outfile for branch table') 101 | parser.add_argument('-c', '--config', dest='config', default=None, 102 | help='pathname of configuration file') 103 | parser.add_argument('--dry-run', '-n', dest='dry_run', action='store_true', 104 | help='perform a dry run (do not modify binary)') 105 | parser.add_argument('--verbose', '-v', action='count', 106 | help='verbose output (repeat up to three times for additional information)') 107 | parser.add_argument('--print-cfs-table', dest='print_cfs_table', action='store_true', 108 | help='print control flow statement table') 109 | parser.add_argument('--print-branch-table', dest='print_branch_table', action='store_true', 110 | help='') 111 | parser.add_argument('--print-loop-table', dest='print_loop_table', action='store_true', 112 | help='') 113 | parser.add_argument('--create-branch-table', dest='gen_branch_table', action='store_true', 114 | help='') 115 | parser.add_argument('--create-loop-table', dest='gen_loop_table', action='store_true', 116 | help='') 117 | 118 | args = parser.parse_args() 119 | 120 | if args.verbose is None: 121 | logging.basicConfig(format='%(message)s',level=logging.ERROR) 122 | if args.verbose is 1: 123 | logging.basicConfig(format='%(message)s',level=logging.WARNING) 124 | if args.verbose is 2: 125 | logging.basicConfig(format='%(message)s',level=logging.INFO) 126 | if args.verbose >= 3: 127 | logging.basicConfig(format='%(message)s',level=logging.DEBUG) 128 | 129 | try: 130 | config = read_config(args.config if args.config is not None 131 | else CONFIG_DEFAULT_PATHNAME) 132 | except ConfigParser.MissingSectionHeaderError as error: 133 | logging.error(error) 134 | sys.exit(1) 135 | 136 | def get_req_opt(opt): 137 | args_value = getattr(args, opt) if hasattr(args, opt) else None 138 | config_value = getattr(config, opt) if hasattr(config, opt) else None 139 | 140 | if args_value is not None: 141 | return args_value 142 | elif config_value is not None: 143 | return config_value 144 | else: 145 | exit("%s: required option '%s' not defined" % (sys.argv[0], opt)); 146 | 147 | def get_csv_opt(opt): 148 | args_value = getattr(args, opt) if hasattr(args, opt) else None 149 | config_value = getattr(config, opt) if hasattr(config, opt) else None 150 | 151 | if args_value is not None: 152 | return args_value.split(',') 153 | elif config_value is not None: 154 | return config_value.split(',') 155 | else: 156 | return [] 157 | 158 | opts = Namespace( 159 | binfile = args.file, 160 | outfile = args.outfile, 161 | dry_run = args.dry_run, 162 | cs_mode_flags = args.flags, 163 | load_address = int(get_req_opt('load_address'), 16), 164 | text_start = int(get_req_opt('text_start'), 16), 165 | text_end = int(get_req_opt('text_end'), 16), 166 | omit_addresses = [int(i,16) for i in get_csv_opt('omit_addresses')], 167 | hook_b = int(get_req_opt('hook_b'), 16), 168 | hook_bl = int(get_req_opt('hook_bl'), 16), 169 | hook_bx_lr = int(get_req_opt('hook_bx_lr'), 16), 170 | hook_pop_r3_r4_fp_pc = int(get_req_opt('hook_pop_r3_r4_fp_pc'), 16), 171 | hook_pop_r4_fp_pc = int(get_req_opt('hook_pop_r4_fp_pc'), 16), 172 | hook_pop_fp_pc = int(get_req_opt('hook_pop_fp_pc'), 16), 173 | hook_pop_fp_lr = int(get_req_opt('hook_pop_fp_lr'), 16), 174 | hook_blx_r3 = int(get_req_opt('hook_blx_r3'), 16), 175 | print_cfs_table = args.print_cfs_table, 176 | print_branch_table = args.print_branch_table, 177 | print_loop_table = args.print_loop_table, 178 | gen_branch_table = args.gen_branch_table, 179 | gen_loop_table = args.gen_loop_table, 180 | ) 181 | 182 | logging.debug("load_address = 0x%08x" % opts.load_address) 183 | logging.debug("text_start = 0x%08x" % opts.text_start) 184 | logging.debug("text_end = 0x%08x" % opts.text_end) 185 | logging.debug("omit_addresses = %s" % ['0x%08x' % i for i in opts.omit_addresses]) 186 | logging.debug("hook_b = 0x%08x" % opts.hook_b) 187 | logging.debug("hook_bl = 0x%08x" % opts.hook_bl) 188 | logging.debug("hook_bx_lr = 0x%08x" % opts.hook_bx_lr) 189 | logging.debug("hook_pop_r3_r4_fp_pc = 0x%08x" % opts.hook_pop_fp_pc) 190 | logging.debug("hook_pop_r4_fp_pc = 0x%08x" % opts.hook_pop_fp_pc) 191 | logging.debug("hook_pop_fp_lr = 0x%08x" % opts.hook_pop_fp_pc) 192 | logging.debug("hook_blx_r3 = 0x%08x" % opts.hook_blx_r3) 193 | 194 | if not os.path.isfile(args.file): 195 | exit("%s: file '%s' not found" % (sys.argv[0], args.file)); 196 | 197 | control_flow_statements = hookit(opts) 198 | 199 | if opts.print_cfs_table: 200 | for cfs in control_flow_statements: 201 | print "%s,0x%08x,0x%08x" % (hexbytes(cfs.instr), cfs.src, cfs.dst) 202 | 203 | if opts.print_branch_table: 204 | for cfs in get_branches(control_flow_statements): 205 | print "0x%08x,0x%08x" % (cfs.src, cfs.dst) 206 | 207 | if opts.print_loop_table: 208 | for (entry, exit) in get_loops(control_flow_statements): 209 | print "0x%08x,0x%08x" % (entry, exit) 210 | 211 | if opts.gen_branch_table: 212 | write_branch_table(opts.outfile if opts.outfile != None else DEFAULT_BTBL_PATHNAME, 213 | get_branches(control_flow_statements)) 214 | 215 | if opts.gen_loop_table: 216 | write_loop_table(opts.outfile if opts.outfile != None else DEFAULT_LTBL_PATHNAME, 217 | get_loops(control_flow_statements)) 218 | 219 | def get_branches(control_flow_statements): 220 | return [c for c in control_flow_statements if c.type == CFS.b or c.type == CFS.bl] 221 | 222 | def get_loops(control_flow_statements): 223 | branches = [c for c in control_flow_statements if (c.type == CFS.b and c.dst < c.src)] 224 | loop_entries = set([c.dst for c in branches]) 225 | loops = [(entry, 4 + max([b.src for b in branches if b.dst == entry])) for entry in loop_entries] 226 | return sorted(loops, key=lambda x: x[0]) 227 | 228 | def hookit(opts): 229 | control_flow_statements = [] 230 | 231 | md = Cs(CS_ARCH_ARM, CS_MODE_ARM + sum(opts.cs_mode_flags)) 232 | md.detail = True; 233 | 234 | with open(opts.binfile, "rw+b") as f: 235 | mm = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ|mmap.PROT_WRITE) 236 | 237 | offset = opts.text_start - opts.load_address 238 | logging.debug("hooking %s from 0x%08x to 0x%08x" % (opts.binfile, offset, opts.text_end - opts.load_address)) 239 | mm.seek(offset) 240 | code = mm.read(mm.size() - mm.tell()) 241 | 242 | current_address = opts.load_address + (offset) 243 | 244 | prev_address = -0x0001 245 | 246 | while True: 247 | for i in md.disasm(code, current_address): 248 | 249 | # Workaround for md.disasm returning dublicate instructions 250 | if i.address == prev_address: 251 | continue 252 | else: 253 | prev_address = i.address 254 | 255 | if (i.address in opts.omit_addresses): 256 | logging.info("omit at 0x%08x: %-10s\t%s\t%s" % 257 | (i.address, hexbytes(i.bytes), i.mnemonic, i.op_str)) 258 | 259 | if i.address >= opts.text_end: 260 | break 261 | 262 | continue 263 | 264 | # branch instruction; b 265 | if (i.id == ARM_INS_B): 266 | rewrite_branch(mm, control_flow_statements, opts.load_address, i, opts.hook_b, opts.dry_run) 267 | 268 | # branch w/ link instruction; bl 269 | elif (i.id == ARM_INS_BL): 270 | rewrite_branch_with_link(mm, control_flow_statements, opts.load_address, i, opts.hook_bl, opts.dry_run) 271 | 272 | # branch and exchange where operand is link register; bx lr 273 | elif (i.id == ARM_INS_BX and len(i.operands) == 1 and i.operands[0].reg == ARM_REG_LR): 274 | rewrite_branch_and_exchange_lr(mm, control_flow_statements, opts.load_address, i, opts.hook_bx_lr, opts.dry_run) 275 | 276 | # pop regs 3, 4, frame pointer and return address to program counter off stack; pop {r3, r4, fp, pc} 277 | elif (i.id == ARM_INS_POP and len(i.operands) == 4 278 | and i.operands[0].reg == ARM_REG_R3 279 | and i.operands[1].reg == ARM_REG_R4 280 | and i.operands[2].reg == ARM_REG_FP 281 | and i.operands[3].reg == ARM_REG_PC): 282 | rewrite_pop_fp_pc(mm, control_flow_statements, opts.load_address, i, opts.hook_pop_r3_r4_fp_pc, opts.dry_run) 283 | 284 | # pop reg 4, frame pointer and return address to program counter off stack; pop {r4, fp, pc} 285 | elif (i.id == ARM_INS_POP and len(i.operands) == 3 286 | and i.operands[0].reg == ARM_REG_R4 287 | and i.operands[1].reg == ARM_REG_FP 288 | and i.operands[2].reg == ARM_REG_PC): 289 | rewrite_pop_fp_pc(mm, control_flow_statements, opts.load_address, i, opts.hook_pop_r4_fp_pc, opts.dry_run) 290 | 291 | # pop frame pointer and return address to program counter off stack; pop {fp, pc} 292 | elif (i.id == ARM_INS_POP and len(i.operands) == 2 293 | and i.operands[0].reg == ARM_REG_FP 294 | and i.operands[1].reg == ARM_REG_PC): 295 | rewrite_pop_fp_pc(mm, control_flow_statements, opts.load_address, i, opts.hook_pop_fp_pc, opts.dry_run) 296 | 297 | # pop frame pointer and return address to link register off stack; pop {fp, lr} 298 | elif (i.id == ARM_INS_POP and len(i.operands) == 2 299 | and i.operands[0].reg == ARM_REG_FP 300 | and i.operands[1].reg == ARM_REG_LR): 301 | rewrite_pop_fp_lr(mm, control_flow_statements, opts.load_address, i, opts.hook_pop_fp_lr, opts.dry_run) 302 | 303 | elif (i.id == ARM_INS_BLX and len(i.operands) == 1 and i.operands[0].reg == ARM_REG_R3): 304 | rewrite_blx_rx(mm, control_flow_statements, opts.load_address, i, opts.hook_blx_r3, opts.dry_run) 305 | 306 | # check for currently unhandled instructions 307 | elif (i.id == ARM_INS_BX): 308 | logging.warn("bx at 0x%08x: %-10s\t%s\t%s" % 309 | (i.address, hexbytes(i.bytes), i.mnemonic, i.op_str)) 310 | 311 | elif (i.id == ARM_INS_BLX): 312 | logging.warn("blx at 0x%08x: %-10s\t%s\t%s" % 313 | (i.address, hexbytes(i.bytes), i.mnemonic, i.op_str)) 314 | 315 | else: 316 | logging.debug(" 0x%08x: %-10s\t%s\t%s" % 317 | (i.address, hexbytes(i.bytes), i.mnemonic, i.op_str)) 318 | 319 | if i.address >= opts.text_end: 320 | break 321 | 322 | current_address = (i.address if i.address > current_address 323 | else current_address + 4) 324 | 325 | if (current_address >= opts.text_end or 326 | current_address >= opts.load_address + mm.size()): 327 | break 328 | 329 | mm.seek(current_address - opts.load_address) 330 | code = mm.read(mm.size() - mm.tell()) 331 | 332 | return control_flow_statements 333 | 334 | def rewrite_branch(mm, cfs_table, load_address, i, target, dry_run=False): 335 | word = new_branch_with_link_instruction(i, target) 336 | 337 | j = disasm_single(word, i.address) 338 | 339 | logging.info("b at 0x%08x: hooking %-10s\t%s\t%s -> %-10s\t%s\t%s" % 340 | (i.address, 341 | hexbytes(i.bytes), i.mnemonic, i.op_str, 342 | hexbytes(j.bytes), j.mnemonic, j.op_str)) 343 | 344 | cfs_table.append(new_cfs(CFS.b, i.bytes, i.address, get_branch_target(i))) 345 | 346 | if dry_run: return 347 | write_back(mm, i.address - load_address, word) 348 | 349 | def rewrite_branch_with_link(mm, cfs_table, load_address, i, target, dry_run=False): 350 | word = new_branch_with_link_instruction(i, target) 351 | 352 | j = disasm_single(word, i.address) 353 | 354 | logging.info("bl at 0x%08x: hooking %-10s\t%s\t%s -> %-10s\t%s\t%s" % 355 | (i.address, 356 | hexbytes(i.bytes), i.mnemonic, i.op_str, 357 | hexbytes(j.bytes), j.mnemonic, j.op_str)) 358 | 359 | cfs_table.append(new_cfs(CFS.bl, i.bytes, i.address, get_branch_target(i))) 360 | 361 | if dry_run: return 362 | write_back(mm, i.address - load_address, word) 363 | 364 | def rewrite_branch_and_exchange_lr(mm, cfs_table, load_address, i, target, dry_run=False): 365 | word = new_branch_with_link_instruction(i, target) 366 | 367 | j = disasm_single(word, i.address) 368 | 369 | logging.info("bx lr at 0x%08x: hooking %-10s\t%s\t%s -> %-10s\t%s\t%s" % 370 | (i.address, 371 | hexbytes(i.bytes), i.mnemonic, i.op_str, 372 | hexbytes(j.bytes), j.mnemonic, j.op_str)) 373 | 374 | cfs_table.append(new_cfs(CFS.bx_lr, i.bytes, 0, 0)) 375 | 376 | if dry_run: return 377 | write_back(mm, i.address - load_address, word) 378 | 379 | def rewrite_pop_fp_pc(mm, cfs_table, load_address, i, target, dry_run=False): 380 | word = new_branch_with_link_instruction(i, target) 381 | 382 | j = disasm_single(word, i.address) 383 | 384 | logging.info("pop at 0x%08x: hooking %-10s\t%s\t%s -> %-10s\t%s\t%s" % 385 | (i.address, 386 | hexbytes(i.bytes), i.mnemonic, i.op_str, 387 | hexbytes(j.bytes), j.mnemonic, j.op_str)) 388 | 389 | cfs_table.append(new_cfs(CFS.pop_fp_pc, i.bytes, 0, 0)) 390 | 391 | if dry_run: return 392 | write_back(mm, i.address - load_address, word) 393 | 394 | def rewrite_pop_fp_lr(mm, cfs_table, load_address, i, target, dry_run=False): 395 | word = new_branch_with_link_instruction(i, target) 396 | 397 | j = disasm_single(word, i.address) 398 | 399 | logging.info("pop at 0x%08x: hooking %-10s\t%s\t%s -> %-10s\t%s\t%s" % 400 | (i.address, 401 | hexbytes(i.bytes), i.mnemonic, i.op_str, 402 | hexbytes(j.bytes), j.mnemonic, j.op_str)) 403 | 404 | cfs_table.append(new_cfs(CFS.pop_fp_lr, i.bytes, 0, 0)) 405 | 406 | if dry_run: return 407 | write_back(mm, i.address - load_address, word) 408 | 409 | def rewrite_blx_rx(mm, cfs_table, load_address, i, target, dry_run=False): 410 | word = new_branch_with_link_instruction(i, target) 411 | 412 | j = disasm_single(word, i.address) 413 | 414 | logging.info("blx rx at 0x%08x: hooking %-10s\t%s\t%s -> %-10s\t%s\t%s" % 415 | (i.address, 416 | hexbytes(i.bytes), i.mnemonic, i.op_str, 417 | hexbytes(j.bytes), j.mnemonic, j.op_str)) 418 | 419 | cfs_table.append(new_cfs(CFS.blx_r3, i.bytes, 0, 0)) 420 | 421 | if dry_run: return 422 | write_back(mm, i.address - load_address, word) 423 | 424 | 425 | def hexbytes(insn): 426 | width = int(pow(2, math.ceil(math.log(len(insn))/math.log(2)))) 427 | return "0x" + binascii.hexlify(bytearray(insn)).zfill(width) 428 | 429 | def get_current_pc(i): 430 | return i.address + 4 431 | 432 | def get_target_offset(current_pc, target): 433 | return (target - current_pc) / 4 - 1 # pc relative offset of target 434 | 435 | def get_target_address(current_pc, offset): 436 | return (offset * 4) + current_pc + 4 # absolute address of pc relative offset 437 | 438 | def long_to_bytes(value, width=8, endian='big'): 439 | s = binascii.unhexlify(('%%0%dx' % (width)) % ((value + (1 << width*4)) % (1 << width*4))) 440 | return s[::-1] if endian == 'little' else s 441 | 442 | def bytes_to_long(data, endian='big'): 443 | data = data[::-1] if endian == 'little' else data 444 | 445 | if data[0] & 0x80 > 0: 446 | return -bytes_to_long(bytearray(~d % 256 for d in data)) - 1 447 | 448 | return int(str(data).encode('hex'), 16) 449 | 450 | def disasm_single(word, address): 451 | md = Cs(CS_ARCH_ARM, CS_MODE_ARM) 452 | 453 | for i in md.disasm(str(word), address): 454 | return i 455 | 456 | def write_back(mm, addr, word): 457 | mm.seek(addr) 458 | mm.write(str(word)) 459 | 460 | def get_branch_target(i): 461 | b = bitarray(endian="big") 462 | b.frombytes(str(i.bytes)) 463 | 464 | return get_target_address(get_current_pc(i), bytes_to_long(bytearray(b[0:24].tobytes()), endian='little')) 465 | 466 | def new_branch_with_link_instruction(i, target): 467 | bits = bitarray('0'*32, endian='big') 468 | 469 | bits[28:32] = bitarray('1011') # opcode for BL 470 | bits[24:28] = cond_bits(i) # condition bits from original instruction 471 | bits[00:24] = bytes_to_bits(long_to_bytes( 472 | get_target_offset(get_current_pc(i), target), 473 | width=6, endian='little')) 474 | 475 | return bytearray(bits.tobytes()) 476 | 477 | def cond_bits(i): 478 | bits = bitarray(endian='big') 479 | bits.frombytes(str(i.bytes)) 480 | return bits[24:28] 481 | 482 | def bytes_to_bits(data_bytes): 483 | bits = bitarray(endian='big') 484 | bits.frombytes(str(data_bytes)) 485 | return bits 486 | 487 | def write_branch_table(pathname, branches): 488 | with open(pathname, "w") as f: 489 | f.write("/* Automatically generated by %s on %s, do not edit! */\n\n" % (sys.argv[0], datetime.today())) 490 | f.write("#include \"lib/btbl.h\"\n\n") 491 | f.write("static __attribute__((section(\".btbl\"),unused)) struct btbl_entry btbl[] = {\n") 492 | 493 | for b in branches: 494 | f.write("\t{0x%08x,0x%08x},\n" % (b.src, b.dst)) 495 | 496 | f.write("};\n") 497 | 498 | def write_loop_table(pathname, loops): 499 | with open(pathname, "w") as f: 500 | f.write("/* Automatically generated by %s on %s, do not edit! */\n\n" % (sys.argv[0], datetime.today())) 501 | f.write("#include \"lib/ltbl.h\"\n\n") 502 | f.write("static __attribute__((section(\".ltbl\"),unused)) struct ltbl_entry ltbl[] = {\n") 503 | 504 | for (entry, exit) in loops: 505 | f.write("\t{0x%08x,0x%08x},\n" % (entry, exit)) 506 | 507 | f.write("};\n") 508 | 509 | if __name__ == "__main__": 510 | main() 511 | 512 | -------------------------------------------------------------------------------- /instrumentation/hookit.cfg.sample: -------------------------------------------------------------------------------- 1 | [code-addresses] 2 | load_address = 0x8000 3 | text_start = 0x8168 4 | text_end = 0x9910 5 | omit_addresses = 0x8b40,0x98ac,0x98ec 6 | 7 | [hook-addresses] 8 | hook_b = 0xa0e4 9 | hook_bl = 0xa134 10 | hook_bx_lr = 0xa184 11 | hook_pop_r3_r4_fp_pc = 0xa1ac 12 | hook_pop_r5_fp_pc = 0xa1ac 13 | hook_pop_fp_pc = 0xa1ac 14 | hook_pop_fp_lr = 0xa1d8 15 | hook_blx_r3 = 0xa200 16 | -------------------------------------------------------------------------------- /instrumentation/hooks.ld: -------------------------------------------------------------------------------- 1 | /* Hook trampolines */ 2 | . = ALIGN(4); 3 | __hook_start = .; 4 | .hook : { *(.hook) } 5 | __hook_end = .; 6 | 7 | /* Hook branch table */ 8 | . = ALIGN(8); 9 | __btbl_start = .; 10 | .btbl : { *(.btbl) } 11 | __btbl_end = .; 12 | 13 | /* Loop entr/exit table */ 14 | . = ALIGN(8); 15 | __ltbl_start = .; 16 | .ltbl : { *(.ltbl) } 17 | __ltbl_end = .; 18 | -------------------------------------------------------------------------------- /library/cfa_stub.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Aalto University 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "sm/smcall.h" 17 | 18 | #include "lib/cfa_stub.h" 19 | 20 | /* Normal world API */ 21 | 22 | uint32_t cfa_init(const cfa_addr_t main_start, const cfa_addr_t main_end, 23 | const btbl_entry_t *btbl_start, const btbl_entry_t *btbl_end, 24 | const ltbl_entry_t *ltbl_start, const ltbl_entry_t *ltbl_end) 25 | { 26 | cfa_init_params_t init_params; 27 | init_params.main_start = main_start; 28 | init_params.main_end = main_end; 29 | init_params.btbl_start = btbl_start; 30 | init_params.btbl_end = btbl_end; 31 | init_params.ltbl_start = ltbl_start; 32 | init_params.ltbl_end = ltbl_end; 33 | 34 | return smcall(CFA_EVENT_INIT, (uint32_t)&init_params, 0, 0); 35 | } 36 | 37 | uint32_t cfa_quote(const uint8_t *user_data, const uint32_t user_data_len, 38 | uint8_t *out, uint32_t *out_len) 39 | { 40 | cfa_quote_params_t quote_params; 41 | quote_params.user_data = user_data; 42 | quote_params.user_data_len = user_data_len; 43 | quote_params.out = out; 44 | quote_params.out_len = out_len; 45 | 46 | return smcall(CFA_EVENT_QUOTE, (uint32_t)"e_params, 0, 0); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /measurement-engine/cfa.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Aalto University 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "alloc.h" 17 | #include "string.h" 18 | #include "assert.h" 19 | #include "assertf.h" 20 | 21 | #include "kernel/log.h" 22 | 23 | #include "lib/cfa.h" 24 | #include "lib/cfa_private.h" 25 | #include "lib/blake2.h" 26 | 27 | extern int memcmp(const void *s1, const void *s2, size_t n); 28 | 29 | static inline void hash_init(cfa_hash_state_t *state) 30 | { 31 | blake2s_init(state, DIGEST_SIZE_BYTES); 32 | } 33 | 34 | static inline void hash_update(cfa_hash_state_t *state, const cfa_node_t *node) 35 | { 36 | blake2s_update(state, (uint8_t *)node, sizeof(cfa_node_t)); 37 | } 38 | 39 | static inline void hash_digest(cfa_hash_state_t *state, uint8_t *digest) 40 | { 41 | blake2s_state s; 42 | memcpy(&s, state, sizeof(blake2s_state)); 43 | blake2s_final(&s, digest, DIGEST_SIZE_BYTES); 44 | } 45 | 46 | static inline void path_extend(cfa_path_ctx_t *curr_path, const cfa_node_t *node) 47 | { 48 | debug("CFA EXTEND path: stx = 0x%08x, end = 0x%08x", node->start, node->end); 49 | hash_update(&(curr_path->state), node); 50 | } 51 | 52 | static inline void node_extend(cfa_ctx_t *ctx, const cfa_node_t *node) 53 | { 54 | debug("CFA EXTEND node: stx = 0x%08x, end = 0x%08x", node->start, node->end); 55 | hash_update(&(ctx->state), node); 56 | } 57 | 58 | static cfa_path_desc_t *new_cfa_path_desc() 59 | { 60 | cfa_path_desc_t *pd; 61 | 62 | if (!(pd = malloc(sizeof(cfa_path_desc_t)))) { 63 | assert(false && "cfa_path_desc_t allocation failed"); 64 | return NULL; 65 | } 66 | 67 | pd->ctr = 1; 68 | 69 | return pd; 70 | } 71 | 72 | static cfa_loop_desc_t *new_cfa_loop_desc() 73 | { 74 | cfa_loop_desc_t *ld; 75 | 76 | if (!(ld = malloc(sizeof(cfa_loop_desc_t)))) { 77 | assert(false && "cfa_loop_desc_t allocation failed"); 78 | return NULL; 79 | } 80 | 81 | list_init(&(ld->path_list)); 82 | 83 | return ld; 84 | } 85 | 86 | static cfa_path_ctx_t *new_cfa_path_ctx() 87 | { 88 | cfa_path_ctx_t *pctx; 89 | 90 | if (!(pctx = malloc(sizeof(cfa_path_ctx_t)))) { 91 | assert(false && "cfa_path_ctx_t allocation failed"); 92 | return NULL; 93 | } 94 | 95 | hash_init(&(pctx->state)); 96 | 97 | return pctx; 98 | } 99 | 100 | static cfa_loop_ctx_t *new_cfa_loop_ctx(cfa_addr_t loop_entry, cfa_addr_t loop_exit) 101 | { 102 | cfa_loop_ctx_t * lctx; 103 | 104 | if (!(lctx = malloc(sizeof(cfa_loop_ctx_t)))) { 105 | assert(false && "cfa_loop_ctx_t allocation failed"); 106 | return NULL; 107 | } 108 | 109 | lctx->entry.start = loop_entry; 110 | lctx->exit = loop_exit; 111 | lctx->ret = 0; 112 | lctx->desc = new_cfa_loop_desc(); 113 | 114 | return lctx; 115 | } 116 | 117 | static inline cfa_addr_t next_instr_addr(cfa_addr_t addr) 118 | { 119 | return addr + INSTRUCTION_LEN; 120 | } 121 | 122 | static inline cfa_loop_ctx_t *get_current_loop(const cfa_ctx_t *ctx) 123 | { 124 | return list_head(&(ctx->loop_stack)); 125 | } 126 | 127 | static inline cfa_path_ctx_t *get_current_path(const cfa_loop_ctx_t *curr_loop) 128 | { 129 | return curr_loop == NULL ? NULL : curr_loop->path; 130 | } 131 | 132 | static inline bool is_event_loop_exit(const cfa_ctx_t *ctx, const cfa_event_t *event) 133 | { 134 | return event->dst_addr >= (get_current_loop(ctx))->exit; 135 | } 136 | 137 | static inline bool is_event_in_loop(const cfa_ctx_t *ctx, const cfa_event_t *event) 138 | { 139 | (void) event; 140 | return !list_is_empty(&(ctx->loop_stack)); 141 | } 142 | 143 | static bool is_event_loop_entry(cfa_ctx_t *ctx, const cfa_event_t *event, 144 | const ltbl_entry_t **ltbl_ptr) 145 | { 146 | *ltbl_ptr = ctx->ltbl_start; 147 | 148 | if (event->type == CFA_EVENT_B && event->src_addr > event->dst_addr) { 149 | 150 | debug("ltbl_bsearch: start: 0x%08x, end: 0x%08x, addr = 0x%08x", 151 | ctx->ltbl_start, ctx->ltbl_end, event->dst_addr); 152 | 153 | #ifdef CFA_LTBL_BSEARCH 154 | *ltbl_ptr = ltbl_bsearch(ctx->ltbl_start, ctx->ltbl_end, event->dst_addr); 155 | #else /* CFA_LTBL_BSEARCH */ 156 | while((*ltbl_ptr) < ctx->ltbl_end && (*ltbl_ptr)->entry < event->dst_addr) { 157 | (*ltbl_ptr)++; 158 | } 159 | #endif /* CFA_LTBL_BSEARCH */ 160 | 161 | assertf((*ltbl_ptr)->entry == event->dst_addr, 162 | "unexpected loop starting at 0x%08x", event->dst_addr); 163 | 164 | return true; 165 | } else { 166 | (*ltbl_ptr) = NULL; 167 | return false; 168 | } 169 | } 170 | 171 | static cfa_path_ctx_t *path_entry(cfa_loop_ctx_t *curr_loop) 172 | { 173 | debug("CFA PATH entry: ent = 0x%08x", curr_loop->entry); 174 | curr_loop->path = new_cfa_path_ctx(); 175 | return curr_loop->path; 176 | } 177 | 178 | static void path_exit(cfa_loop_ctx_t *curr_loop, cfa_path_ctx_t *curr_path) 179 | { 180 | cfa_path_desc_t *curr_pd, *pd; 181 | iter_t i; 182 | 183 | debug("CFA PATH exit: end = 0x%08x", curr_loop->entry.start); 184 | 185 | curr_pd = new_cfa_path_desc(); 186 | hash_digest(&(curr_path->state), curr_pd->digest); 187 | 188 | free(curr_path); 189 | 190 | debug("curr_path : %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x, ctr = %d", 191 | curr_pd->digest[0], curr_pd->digest[1], 192 | curr_pd->digest[2], curr_pd->digest[3], 193 | curr_pd->digest[4], curr_pd->digest[5], 194 | curr_pd->digest[6], curr_pd->digest[7], 195 | curr_pd->digest[8], curr_pd->digest[9], 196 | curr_pd->digest[10], curr_pd->digest[11], 197 | curr_pd->digest[12], curr_pd->digest[13], 198 | curr_pd->digest[14], curr_pd->digest[15], 199 | curr_pd->ctr); 200 | 201 | i = list_iter(&(curr_loop->desc->path_list)); 202 | 203 | while ((pd = list_next(&i)) != NULL) { 204 | if (!memcmp(pd->digest, curr_pd->digest, DIGEST_SIZE_BYTES)) { 205 | pd->ctr = pd->ctr + 1; 206 | free(curr_pd); 207 | return; 208 | } 209 | } 210 | 211 | list_append(&(curr_loop->desc->path_list), curr_pd); 212 | } 213 | 214 | static cfa_loop_ctx_t *loop_entry(cfa_ctx_t *ctx, const ltbl_entry_t *ltbl_ptr) 215 | { 216 | cfa_loop_ctx_t *l; 217 | 218 | debug("CFA LOOP entry: ent = 0x%08x, ext = 0x%08x", ltbl_ptr->entry, ltbl_ptr->exit); 219 | 220 | l = new_cfa_loop_ctx(ltbl_ptr->entry, ltbl_ptr->exit); 221 | 222 | hash_digest(&(ctx->state), l->desc->digest); 223 | 224 | list_push(&(ctx->loop_stack), l); 225 | 226 | return l; 227 | } 228 | 229 | static cfa_loop_ctx_t *loop_entry_from_path(cfa_ctx_t *ctx, const ltbl_entry_t *ltbl_ptr, cfa_path_ctx_t *curr_path) 230 | { 231 | cfa_loop_ctx_t *l; 232 | 233 | debug("CFA LOOP entry: ent = 0x%08x, ext = 0x%08x", ltbl_ptr->entry, ltbl_ptr->exit); 234 | 235 | l = new_cfa_loop_ctx(ltbl_ptr->entry, ltbl_ptr->exit); 236 | 237 | hash_digest(&(curr_path->state), l->desc->digest); 238 | 239 | list_push(&(ctx->loop_stack), l); 240 | 241 | return l; 242 | } 243 | 244 | static void loop_exit(cfa_ctx_t *ctx) 245 | { 246 | cfa_loop_ctx_t *curr_loop; 247 | 248 | curr_loop = (cfa_loop_ctx_t *)list_pop(&(ctx->loop_stack)); 249 | 250 | debug("CFA LOOP exit: ent = 0x%08x, ext = 0x%08x", curr_loop->entry.start, curr_loop->exit); 251 | 252 | list_append(&(ctx->loop_list), curr_loop->desc); 253 | 254 | free(curr_loop); 255 | } 256 | 257 | /* Secure world API */ 258 | 259 | /* Initialize the CFA algorithm and context */ 260 | uint32_t cfa_init(cfa_ctx_t *ctx, 261 | const cfa_addr_t main_start, const cfa_addr_t main_end, 262 | const btbl_entry_t* btbl_start, const btbl_entry_t* btbl_end, 263 | const ltbl_entry_t* ltbl_start, const ltbl_entry_t* ltbl_end) 264 | { 265 | iter_t i,j; 266 | cfa_loop_desc_t *ld; 267 | cfa_path_desc_t *pd; 268 | 269 | debug("Initializing CFA"); 270 | debug(" main_start = 0x%.8x", main_start); 271 | debug(" main_end = 0x%.8x", main_end); 272 | debug(" btbl_start = 0x%.8x", (uint32_t)btbl_start); 273 | debug(" btbl_end = 0x%.8x", (uint32_t)btbl_end); 274 | 275 | /* Clean up accumulated loop and path descriptor lists */ 276 | if (ctx->initialized) { 277 | i = list_iter(&(ctx->loop_list)); 278 | while ((ld = list_next(&i)) != NULL) { 279 | j = list_iter(&(ld->path_list)); 280 | while ((pd = list_next(&j)) != NULL) { 281 | free(pd); 282 | } 283 | list_finalize(&(ld->path_list)); 284 | free(ld); 285 | } 286 | list_finalize(&(ctx->loop_list)); 287 | } 288 | 289 | ctx->cur_node.start = main_start; 290 | 291 | ctx->main_start = main_start; 292 | ctx->main_end = main_end; 293 | 294 | ctx->btbl_start = btbl_start; 295 | ctx->btbl_end = btbl_end; 296 | 297 | ctx->ltbl_start = ltbl_start; 298 | ctx->ltbl_end = ltbl_end; 299 | 300 | hash_init(&(ctx->state)); 301 | 302 | list_init(&(ctx->loop_stack)); 303 | list_init(&(ctx->loop_list)); 304 | 305 | ctx->initialized = true; 306 | 307 | return 0; 308 | } 309 | 310 | static const btbl_entry_t *btbl_bsearch(const btbl_entry_t *start, const btbl_entry_t *end, cfa_addr_t addr) 311 | { 312 | size_t low, high, mid; 313 | const btbl_entry_t *e; 314 | 315 | high = ((uint32_t)end - (uint32_t)start) / sizeof(btbl_entry_t); 316 | low = 0; 317 | mid = (high + low) / 2; 318 | 319 | e = start; 320 | 321 | while (high != low+1) { 322 | e = start + mid; 323 | 324 | if (e->src == addr) { 325 | return e; 326 | } else if (e->src < addr){ 327 | low = mid; 328 | } else{ 329 | high = mid; 330 | } 331 | 332 | mid = (high + low) / 2; 333 | } 334 | 335 | return e; 336 | } 337 | 338 | static uint32_t infer_non_taken_conditionals(cfa_ctx_t *ctx, const cfa_event_t *event) 339 | { 340 | /* Check for non-taken conditional branches */ 341 | const btbl_entry_t *btbl_ptr = ctx->btbl_start; 342 | const btbl_entry_t *btbl_end = ctx->btbl_end; 343 | cfa_loop_ctx_t *curr_loop; 344 | cfa_path_ctx_t *curr_path; 345 | uint8_t err = 0; 346 | 347 | btbl_ptr = btbl_bsearch(ctx->btbl_start, ctx->btbl_end, ctx->cur_node.start); 348 | 349 | // Return early to avoid while loop below from reading from loop table 350 | if (btbl_ptr >= btbl_end) { 351 | return err; 352 | } 353 | 354 | curr_loop = get_current_loop(ctx); 355 | curr_path = get_current_path(curr_loop); 356 | 357 | while(btbl_ptr->src < event->src_addr) { 358 | ctx->cur_node.end = btbl_ptr->src; 359 | 360 | if (curr_path != NULL) { 361 | path_extend(curr_path, &(ctx->cur_node)); 362 | } else { 363 | node_extend(ctx, &(ctx->cur_node)); 364 | } 365 | 366 | ctx->cur_node.start = next_instr_addr(btbl_ptr->src); 367 | 368 | if (curr_loop != NULL && !(curr_loop->ret) && 369 | ctx->cur_node.start >= curr_loop->exit) { 370 | 371 | path_exit(curr_loop, curr_path); 372 | loop_exit(ctx); 373 | 374 | curr_loop = get_current_loop(ctx); 375 | curr_path = get_current_path(curr_loop); 376 | } 377 | 378 | btbl_ptr++; 379 | } 380 | 381 | return err; 382 | } 383 | 384 | /* Process an incoming CFA event */ 385 | uint32_t cfa_event(cfa_ctx_t *ctx, const cfa_event_t *event) 386 | { 387 | int ret = 0; 388 | const ltbl_entry_t *ltbl_ptr; 389 | cfa_loop_ctx_t *curr_loop; 390 | cfa_path_ctx_t *curr_path; 391 | 392 | if(ctx->initialized != true) { 393 | return CFA_ERROR_BAD_PARAMETERS; 394 | } 395 | 396 | ret = infer_non_taken_conditionals(ctx, event); 397 | 398 | /* Update block end */ 399 | ctx->cur_node.end = event->src_addr; 400 | 401 | if (is_event_in_loop(ctx, event)) { 402 | curr_loop = get_current_loop(ctx); 403 | curr_path = get_current_path(curr_loop); 404 | 405 | if (!(curr_loop->ret) && event->type == CFA_EVENT_BL) { 406 | curr_loop->ret = next_instr_addr(event->src_addr); 407 | debug("CFA LOOP call: ent = 0x%08x, ext = 0x%08x, ret = 0x%08x", curr_loop->entry.start, curr_loop->exit, curr_loop->ret); 408 | } 409 | 410 | if (curr_loop->ret == event->dst_addr) { 411 | debug("CFA LOOP ret: ent = 0x%08x, ext = 0x%08x, ret = 0x%08x", curr_loop->entry.start, curr_loop->exit); 412 | curr_loop->ret = 0; 413 | } 414 | 415 | assert(curr_path != NULL && "current path is null"); 416 | path_extend(curr_path, &(ctx->cur_node)); 417 | 418 | if (event->dst_addr == curr_loop->entry.start) { // Start of new iteration, new path 419 | path_exit(curr_loop, curr_path); 420 | path_entry(curr_loop); 421 | } else if (is_event_loop_entry(ctx, event, <bl_ptr)) { // Nested loop entry 422 | curr_loop = loop_entry_from_path(ctx, ltbl_ptr, curr_path); 423 | curr_path = path_entry(curr_loop); 424 | } else if (!(curr_loop->ret) && is_event_loop_exit(ctx, event)) { // End of loop reached 425 | path_exit(curr_loop, curr_path); 426 | loop_exit(ctx); 427 | } 428 | 429 | } else { // Not currently in loop 430 | 431 | if (is_event_loop_entry(ctx, event, <bl_ptr)) { // New loop entry 432 | curr_loop = loop_entry(ctx, ltbl_ptr); 433 | path_entry(curr_loop); 434 | } 435 | 436 | /* Extend a basic block */ 437 | node_extend(ctx, &(ctx->cur_node)); 438 | } 439 | 440 | /* Update block start for next invocation */ 441 | ctx->cur_node.start = event->dst_addr; 442 | 443 | return ret; 444 | } 445 | 446 | /* Produce a CFA quote of the current hash value and reset hash */ 447 | uint32_t cfa_quote(cfa_ctx_t *ctx, 448 | const uint8_t *user_data, const uint32_t user_data_len, 449 | uint8_t *out, uint32_t *out_len) 450 | { 451 | (void)ctx; 452 | (void)user_data; 453 | (void)user_data_len; 454 | (void)out; 455 | (void)out_len; 456 | 457 | //TODO cfa_quote 458 | 459 | cfa_loop_desc_t *ld; 460 | cfa_path_desc_t *pd; 461 | iter_t i, j; 462 | uint32_t lnum, pnum; 463 | uint8_t digest[DIGEST_SIZE_BYTES]; 464 | 465 | hash_digest(&(ctx->state), digest); 466 | 467 | info("cfa_quote: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", 468 | digest[0], digest[1], 469 | digest[2], digest[3], 470 | digest[4], digest[5], 471 | digest[6], digest[7], 472 | digest[8], digest[9], 473 | digest[10], digest[11], 474 | digest[12], digest[13], 475 | digest[14], digest[15]); 476 | 477 | i = list_iter(&(ctx->loop_list)); 478 | lnum = 0; 479 | 480 | while ((ld = list_next(&i)) != NULL) { 481 | info("loop[%03d]: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", 482 | lnum++, 483 | ld->digest[0], ld->digest[1], 484 | ld->digest[2], ld->digest[3], 485 | ld->digest[4], ld->digest[5], 486 | ld->digest[6], ld->digest[7], 487 | ld->digest[8], ld->digest[9], 488 | ld->digest[10], ld->digest[11], 489 | ld->digest[12], ld->digest[13], 490 | ld->digest[14], ld->digest[15]); 491 | 492 | j = list_iter(&(ld->path_list)); 493 | pnum = 0; 494 | 495 | while ((pd = list_next(&j)) != NULL) { 496 | info("path[%03d]: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x (%d)", 497 | pnum++, 498 | pd->digest[0], pd->digest[1], 499 | pd->digest[2], pd->digest[3], 500 | pd->digest[4], pd->digest[5], 501 | pd->digest[6], pd->digest[7], 502 | pd->digest[8], pd->digest[9], 503 | pd->digest[10], pd->digest[11], 504 | pd->digest[12], pd->digest[13], 505 | pd->digest[14], pd->digest[15], 506 | pd->ctr); 507 | } 508 | } 509 | 510 | ctx->initialized = false; 511 | 512 | return 0; 513 | } 514 | 515 | -------------------------------------------------------------------------------- /measurement-engine/cfa_asm.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Aalto University 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* ltbl_entry_t *ltbl_bsearch(start, end, addr) */ 18 | .global ltbl_bsearch 19 | .type ltbl_bsearch, %function 20 | ltbl_bsearch: 21 | push {r1-r4} 22 | _ltbl_bsearch_start: 23 | cmp r0, r1 /* has end of auxiliary data been reached? */ 24 | bgt _ltbl_bsearch_error 25 | 26 | add r3, r0, r1 27 | mov r3, r3, lsr #1 28 | bic r3, r3, #7 /* entry dest addr at 8-byte */ 29 | 30 | ldr r4, [r3] 31 | cmp r4, r2 /* has matching source address been found? */ 32 | 33 | bgt _ltbl_bsearch_right /* no match, search lower addrs */ 34 | blt _ltbl_bsearch_left /* no match, search higher addrs */ 35 | _ltbl_bsearch_return: 36 | mov r0, r3 /* match! branch to handler */ 37 | pop {r1-r4} 38 | bx lr 39 | _ltbl_bsearch_right: 40 | mov r1, r3 41 | b _ltbl_bsearch_start 42 | _ltbl_bsearch_left: 43 | mov r0, r3 44 | b _ltbl_bsearch_start 45 | _ltbl_bsearch_error: 46 | mov r0, #0 47 | pop {r1-r4} 48 | bx lr 49 | .size ltbl_bsearch, .-ltbl_bsearch 50 | 51 | -------------------------------------------------------------------------------- /runtime-tracer/hooks_a32.S: -------------------------------------------------------------------------------- 1 | /* 2 | * ARMv7 bare metal binary instrumentation trampolines 3 | * 4 | * Copyright (c) 2016 Aalto University 5 | */ 6 | #include "lib/cfa_common.h" 7 | 8 | .arch_extension sec 9 | 10 | .section .hook 11 | 12 | /*---------------------------------------------------------------------------*/ 13 | /* hooking trampoline for f(void) { return 0; } hook for debugging purposes */ 14 | /*---------------------------------------------------------------------------*/ 15 | 16 | __hook_ret0: 17 | mov r0, #0 18 | bx lr 19 | 20 | /*---------------------------------------------------------------------------*/ 21 | /* hooking trampoline for branch instruction */ 22 | /*---------------------------------------------------------------------------*/ 23 | 24 | __hook_b: 25 | push {r0-r4} /* store scratch registers */ 26 | 27 | ldr r0, =__btbl_start 28 | ldr r1, =__btbl_end 29 | mov r4, lr 30 | bl _btbl_bsearch 31 | 32 | _hook_b_ret: /* cleanup and return (branch) */ 33 | mov lr, r4 34 | add r0, #4 /* advance r0 to entry dest addr */ 35 | ldr r12, [r0] /* retrieve branch target addr */ 36 | 37 | mov r0, #CFA_EVENT_B 38 | 39 | sub r1, lr, #4 40 | mov r2, r12 41 | 42 | ldr r3, __shdw_lr 43 | ldr lr, [r3] /* restore stored lr value */ 44 | 45 | mov r3, lr 46 | 47 | smc #0 48 | 49 | pop {r0-r4} /* restore scratch registers */ 50 | bx r12 /* return */ 51 | 52 | /*---------------------------------------------------------------------------*/ 53 | /* hooking trampoline for branch w/ link instruction */ 54 | /*---------------------------------------------------------------------------*/ 55 | 56 | __hook_bl: 57 | push {r0-r4} /* store scratch registers */ 58 | 59 | ldr r0, =__btbl_start 60 | ldr r1, =__btbl_end 61 | mov r4, lr 62 | bl _btbl_bsearch 63 | 64 | _hook_bl_ret: /* cleanup and return (branch w/ link) */ 65 | mov lr, r4 66 | add r0, #4 /* advance r0 to entry dest addr */ 67 | ldr r12, [r0] /* retrieve branch target addr */ 68 | 69 | mov r0, #CFA_EVENT_BL 70 | sub r1, lr, #4 71 | mov r2, r12 72 | mov r3, lr 73 | 74 | smc #0 75 | 76 | ldr r0, __shdw_lr 77 | str lr, [r0] /* store lr value */ 78 | 79 | pop {r0-r4} /* restore scratch registers */ 80 | bx r12 /* return */ 81 | 82 | /*---------------------------------------------------------------------------*/ 83 | /* branch table binary search routine */ 84 | /*---------------------------------------------------------------------------*/ 85 | 86 | _btbl_bsearch: 87 | cmp r0, r1 /* has end of auxiliary data been reached? */ 88 | bgt _btbl_bsearch_error 89 | 90 | add r2, r0, r1 91 | mov r2, r2, lsr #1 92 | bic r2, r2, #7 /* entry dest addr at 8-byte */ 93 | 94 | ldr r3, [r2] 95 | add r3, #4 /* adjust source addr by 4 to match lr */ 96 | cmp r3, r4 /* has matching source address been found? */ 97 | 98 | bgt _btbl_bsearch_right /* no match, search lower addrs */ 99 | blt _btbl_bsearch_left /* no match, search higher addrs */ 100 | 101 | mov r0, r2 /* match! branch to handler */ 102 | bx lr 103 | _btbl_bsearch_right: 104 | mov r1, r2 105 | b _btbl_bsearch 106 | _btbl_bsearch_left: 107 | mov r0, r2 108 | b _btbl_bsearch 109 | _btbl_bsearch_error: 110 | mov r0, #CFA_EVENT_ERROR 111 | sub r1, r4, #4 112 | smc #0 113 | 114 | /*---------------------------------------------------------------------------*/ 115 | /* hooking trampoline for branch and exchange where operand is link register */ 116 | /*---------------------------------------------------------------------------*/ 117 | 118 | __hook_bx_lr: 119 | push {r0-r3} /* store scratch registers */ 120 | 121 | mov r0, #CFA_EVENT_BX_LR 122 | mov r1, lr /* keep hook trigger addr in r1 */ 123 | sub r1, #4 124 | 125 | ldr r3, __shdw_lr 126 | ldr r2, [r3] /* restore stored lr value */ 127 | mov lr, r2 128 | mov r3, lr 129 | 130 | smc #0 131 | 132 | pop {r0-r3} /* restore scratch registers */ 133 | bx lr 134 | 135 | /*---------------------------------------------------------------------------*/ 136 | /* hooking trampoline for pop of frame pointer and return address off stack */ 137 | /*---------------------------------------------------------------------------*/ 138 | 139 | __hook_pop_r3_r4_fp_pc: 140 | pop {r3} 141 | __hook_pop_r4_fp_pc: 142 | pop {r4} 143 | __hook_pop_fp_pc: 144 | push {r0-r3} /* store scratch registers */ 145 | 146 | mov r0, #CFA_EVENT_POP_FP_PC 147 | 148 | mov r1, lr /* keep hook trigger addr in r1 */ 149 | sub r1, #4 150 | 151 | add r2, sp, #20 /* addr of stored lr in stack */ 152 | ldr r2, [r2] /* load stored lr into r2 */ 153 | 154 | ldr r3, __shdw_lr 155 | ldr lr, [r3] /* restore stored lr value */ 156 | mov r3, lr 157 | 158 | smc #0 159 | 160 | pop {r0-r3} /* restore scratch registers */ 161 | pop {fp, pc} 162 | 163 | /*---------------------------------------------------------------------------*/ 164 | /* hooking trampoline for pop of frame pointer and link register off stack */ 165 | /*---------------------------------------------------------------------------*/ 166 | 167 | __hook_pop_fp_lr: 168 | pop {fp, r12} 169 | push {r0-r3} /* store scratch registers */ 170 | 171 | mov r0, r12 172 | mov r1, lr 173 | mov lr, r0 174 | mov r12, r1 /* keep hook trigger addr in r0 */ 175 | 176 | ldr r0, __shdw_lr 177 | str lr, [r0] /* store lr value */ 178 | 179 | #ifdef INSTR_AUX_HOOKS_TRAP_INTO_SW 180 | mov r0, #CFA_EVENT_POP_FP_LR 181 | sub r1, r12, #4 182 | mov r2, r12 183 | mov r3, lr 184 | 185 | smc #0 186 | #endif 187 | 188 | pop {r0-r3} /* restore scratch registers */ 189 | bx r12 190 | 191 | /*---------------------------------------------------------------------------*/ 192 | /* hooking trampoline for branch w/ link and exchange from r3 */ 193 | /*---------------------------------------------------------------------------*/ 194 | __hook_blx_r3: 195 | mov r12, r3 /* save destination addr in r12 */ 196 | push {r0-r3} /* store scratch registers */ 197 | 198 | mov r0, #CFA_EVENT_BLX_R3 199 | 200 | mov r1, lr /* keep hook trigger addr in r1 */ 201 | sub r1, #4 202 | 203 | mov r2, r12 /* destination addr to r2*/ 204 | 205 | ldr r3, __shdw_lr 206 | str lr, [r3] /* store lr value */ 207 | mov r3, lr 208 | 209 | smc #0 210 | 211 | pop {r0-r3} /* restore scratch registers */ 212 | bx r12 213 | 214 | /*---------------------------------------------------------------------------*/ 215 | /* hook data */ 216 | /*---------------------------------------------------------------------------*/ 217 | __shdw_lr: 218 | .word 0 219 | 220 | -------------------------------------------------------------------------------- /samples/soldering/Button.c: -------------------------------------------------------------------------------- 1 | #include "Button.h" 2 | #include "arduino.h" 3 | #include "alloc.h" 4 | 5 | const uint16_t tickTimeout = 200; // Period of button tick, while tha button is pressed 6 | const uint16_t shortPress = 900; // If the button was pressed less that this timeout, we assume the short button press 7 | 8 | void createButtonD(Button *const self, byte buttonPIN) { 9 | createButton(self, buttonPIN, 3000); 10 | } 11 | 12 | void createButton(Button *const self, byte buttonPIN, unsigned int timeout_ms) { 13 | self->pt = 0; 14 | self->tickTime = 0; 15 | self->buttonPIN = buttonPIN; 16 | self->overPress = timeout_ms; 17 | } 18 | 19 | void initButton(Button *const self) { 20 | pinMode(self->buttonPIN, INPUT_PULLUP); 21 | } 22 | 23 | void setTimeoutD(Button *const self) { 24 | setTimeout(self, 3000); 25 | } 26 | 27 | void setTimeout(Button *const self, uint16_t timeout_ms) { 28 | self->overPress = timeout_ms; 29 | } 30 | 31 | byte intButtonStatus(Button *const self) { 32 | byte m = self->mode; 33 | self->mode = 0; 34 | return m; 35 | } 36 | 37 | void changeButtonINTR(Button *const self) { 38 | // Interrupt function, called when the button status changed 39 | 40 | boolean keyUp = digitalRead(self->buttonPIN); 41 | unsigned long now_t = millis(); 42 | if (!keyUp) { // The button has been pressed 43 | if ((self->pt == 0) || (now_t - self->pt > self->overPress)) { 44 | self->pt = now_t; 45 | } 46 | } else { 47 | if (self->pt > 0) { 48 | if ((now_t - self->pt) < shortPress) { 49 | self->mode = 1; // short press 50 | } else { 51 | self->mode = 2; // long press 52 | } 53 | self->pt = 0; 54 | } 55 | } 56 | } 57 | 58 | 59 | byte buttonCheck(Button *const self) { 60 | // Check the button state, called each time in the main loop 61 | 62 | byte mode = 0; 63 | boolean keyUp = digitalRead(self->buttonPIN); // Read the current state of the button 64 | uint32_t now_t = millis(); 65 | if (!keyUp) { // The button is pressed 66 | if ((self->pt == 0) || (now_t - self->pt > self->overPress)) { 67 | self->pt = now_t; 68 | } 69 | } else { 70 | if (self->pt == 0) { 71 | return 0; 72 | } 73 | 74 | if ((now_t - self->pt) > shortPress) { 75 | // Long press 76 | self->mode = 2; 77 | } else { 78 | self->mode = 1; 79 | } 80 | self->pt = 0; 81 | } 82 | return mode; 83 | } 84 | 85 | boolean buttonTick(Button *const self) { 86 | // When the button pressed for a while, generate periodical ticks 87 | 88 | // Read the current state of the button 89 | boolean keyUp = digitalRead(self->buttonPIN); 90 | uint32_t now_t = millis(); 91 | 92 | if (!keyUp && (now_t - self->pt > shortPress)) { // The button have been pressed for a while 93 | if (now_t - self->tickTime > tickTimeout) { 94 | self->tickTime = now_t; 95 | return (self->pt != 0); 96 | } 97 | } else { 98 | if (self->pt == 0) { 99 | return FALSE; 100 | } 101 | self->tickTime = 0; 102 | } 103 | return FALSE; 104 | } 105 | -------------------------------------------------------------------------------- /samples/soldering/Button.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTON_H 2 | #define BUTTON_H 3 | 4 | #include "types.h" 5 | 6 | typedef struct ButtonStruct { 7 | volatile byte mode; // The button mode: 0 - not pressed, 1 - pressed, 2 - long pressed 8 | uint16_t overPress; // Maxumum time in ms the button can be pressed 9 | volatile uint32_t pt; // Time in ms when the button was pressed (press time) 10 | uint32_t tickTime; // The time in ms when the button Tick was set 11 | byte buttonPIN; // The pin number connected to the button 12 | } Button; 13 | 14 | void createButtonD(Button *const self, byte buttonPIN); //with default: 3000 15 | void createButton(Button *const self, byte buttonPIN, unsigned int timeout_ms); 16 | 17 | void initButton(Button *const self); 18 | 19 | void setTimeoutD(Button *const self); // uint16_t timeout_ms = 3000 20 | void setTimeout(Button *const self, uint16_t timeout_ms); 21 | 22 | byte intButtonStatus(Button *const self); 23 | 24 | void changeButtonINTR(Button *const self); 25 | byte buttonCheck(Button *const self); 26 | boolean buttonTick(Button *const self); 27 | 28 | #endif /* BUTTON_H */ 29 | 30 | -------------------------------------------------------------------------------- /samples/soldering/Buzzer.c: -------------------------------------------------------------------------------- 1 | #include "Buzzer.h" 2 | #include "arduino.h" 3 | #include "alloc.h" 4 | 5 | void createBuzzer(Buzzer *const self, byte pin) { 6 | self->buzzerPIN = pin; 7 | } 8 | 9 | void shortBeep(Buzzer *const self) { 10 | tone(self->buzzerPIN , 3520, 80); 11 | } 12 | -------------------------------------------------------------------------------- /samples/soldering/Buzzer.h: -------------------------------------------------------------------------------- 1 | #ifndef BUZZER_H 2 | #define BUZZER_H 3 | 4 | #include "types.h" 5 | 6 | typedef struct BuzzerStruct { 7 | byte buzzerPIN; 8 | } Buzzer; 9 | 10 | void createBuzzer(Buzzer *const self, byte pin); 11 | void shortBeep(Buzzer *const self); 12 | 13 | #endif /* BUZZER_H */ 14 | -------------------------------------------------------------------------------- /samples/soldering/Config.c: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | #include "alloc.h" 3 | #include "eeprom.h" 4 | 5 | #include "string.h" 6 | 7 | #define SELF Config *const self 8 | 9 | void createConfig(Config *const self) { 10 | self->can_write = FALSE; 11 | self->buffRecords = 0; 12 | 13 | self->rAddr = 0; 14 | self->wAddr = 0; 15 | self->eLength = 0; 16 | self->nextRecID = 0; 17 | self->Config.temp = 470; 18 | self->Config.temp_min = 417; 19 | self->Config.temp_max = 700; 20 | self->save_calibration = FALSE; 21 | 22 | self->record_size = 16; 23 | } 24 | 25 | void initConfig(Config *const self) { 26 | self->eLength = EEPROM_length(); 27 | //byte t, p ,h; // changed: was unused!?! 28 | uint32_t recID; 29 | uint32_t minRecID = 0xffffffff; 30 | uint16_t minRecAddr = 0; 31 | uint32_t maxRecID = 0; 32 | uint16_t maxRecAddr = 0; 33 | byte records = 0; 34 | 35 | self->nextRecID = 0; 36 | 37 | // read all the records in the EEPROM find min and max record ID 38 | for (uint16_t addr = 0; addr < self->eLength; addr += self->record_size) { 39 | if (readRecord(self, addr, &recID)) { 40 | ++records; 41 | if (minRecID > recID) { 42 | minRecID = recID; 43 | minRecAddr = addr; 44 | } 45 | if (maxRecID < recID) { 46 | maxRecID = recID; 47 | maxRecAddr = addr; 48 | } 49 | } else { 50 | break; 51 | } 52 | } 53 | 54 | if (records == 0) { 55 | self->wAddr = self->rAddr = 0; 56 | self->can_write = true; 57 | return; 58 | } 59 | 60 | self->rAddr = maxRecAddr; 61 | if (records < (self->eLength / self->record_size)) { // The EEPROM is not full 62 | self->wAddr = self->rAddr + self->record_size; 63 | if (self->wAddr > self->eLength) { 64 | self->wAddr = 0; 65 | } 66 | } else { 67 | self->wAddr = minRecAddr; 68 | } 69 | self->can_write = true; 70 | } 71 | 72 | bool isValid(const Config *self) { 73 | return self->is_valid; 74 | } 75 | 76 | uint16_t Config_temp(const Config *self) { 77 | return self->Config.temp; 78 | } 79 | 80 | byte getBrightness(const Config *self) { 81 | return self->Config.brightness; 82 | } 83 | 84 | bool Config_getTempUnits(const Config *self) { 85 | return self->Config.celsius; 86 | } 87 | 88 | bool saveTemp(Config *const self, uint16_t t) { 89 | if (!self->save_calibration && (t == self->Config.temp)) { 90 | return TRUE; 91 | } 92 | 93 | self->Config.temp = t; 94 | self->save_calibration = FALSE; 95 | return save(self); 96 | } 97 | 98 | void saveConfig(Config *const self, byte bright, bool cels) { 99 | //if ((bright >= 0) && (bright <= 15)) { // Always true 100 | if (bright <= 15) { 101 | self->Config.brightness = bright; 102 | } 103 | self->Config.celsius = cels; 104 | save(self); 105 | } 106 | 107 | void saveCalibrationData(Config *const self, uint16_t t_max, uint16_t t_min) { 108 | self->Config.temp_max = t_max; 109 | self->Config.temp_min = t_min; 110 | self->save_calibration = TRUE; 111 | } 112 | 113 | void getCalibrationData(Config *const self, uint16_t *t_max, uint16_t *t_min) { 114 | *t_max = self->Config.temp_max; 115 | *t_min = self->Config.temp_min; 116 | } 117 | 118 | bool save(Config *const self) { 119 | if (!self->can_write) { 120 | return 0; 121 | } 122 | 123 | if (self->nextRecID == 0) { 124 | self->nextRecID = 1; 125 | } 126 | 127 | uint16_t startWrite = self->wAddr; 128 | uint32_t nxt = self->nextRecID; 129 | byte summ = 0; 130 | 131 | for (byte i = 0; i < 4; ++i) { 132 | EEPROM_write(startWrite++, nxt & 0xff); 133 | summ <<=2; summ += nxt; 134 | nxt >>= 8; 135 | } 136 | 137 | byte* p = (byte *)&self->Config; 138 | for (byte i = 0; i < sizeof(struct cfg); ++i) { 139 | summ <<= 2; summ += p[i]; 140 | EEPROM_write(startWrite++, p[i]); 141 | } 142 | summ ++; // To avoid empty records 143 | EEPROM_write(self->wAddr + self->record_size - 1, summ); 144 | 145 | self->rAddr = self->wAddr; 146 | self->wAddr += self->record_size; 147 | 148 | if (self->wAddr > EEPROM_length()) { 149 | self->wAddr = 0; 150 | } 151 | 152 | return true; 153 | } 154 | 155 | bool load(SELF) { 156 | self->is_valid = readRecord(self, self->rAddr, &(self->nextRecID)); 157 | self->nextRecID++; 158 | 159 | if (self->is_valid) { 160 | if (self->Config.temp_min >= self->Config.temp_max) { 161 | self->Config.temp_min = 417; 162 | self->Config.temp_max = 700; 163 | } 164 | if ( (self->Config.temp > self->Config.temp_max) 165 | || (self->Config.temp < self->Config.temp_min)) { 166 | self->Config.temp = 470; 167 | } 168 | // if ((self->Config.brightness < 0) || (self->Config.brightness > 15)) { Always false 169 | if (self->Config.brightness > 15) { 170 | self->Config.brightness = M_INTENSITY; 171 | } 172 | } 173 | return self->is_valid; 174 | } 175 | 176 | bool readRecord(SELF, uint16_t addr, uint32_t *recID) { 177 | byte Buff[16]; 178 | 179 | for (byte i = 0; i < 16; ++i) { 180 | Buff[i] = EEPROM_read(addr+i); 181 | } 182 | 183 | byte summ = 0; 184 | for (byte i = 0; i < sizeof(struct cfg) + 4; ++i) { 185 | summ <<= 2; summ += Buff[i]; 186 | } 187 | summ ++; // To avoid empty fields 188 | if (summ == Buff[15]) { // Checksumm is correct 189 | uint32_t ts = 0; 190 | for (int i = 3; i >= 0; --i) { // changed char i -> int i 191 | ts <<= 8; 192 | ts |= Buff[i]; 193 | } 194 | *recID = ts; 195 | // byte i = 4; // changed: was unused!?! 196 | memcpy(&self->Config, &Buff[4], sizeof(struct cfg)); 197 | return true; 198 | } 199 | return false; 200 | } 201 | -------------------------------------------------------------------------------- /samples/soldering/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include "types.h" 5 | #include "constants.h" 6 | #include "arduino.h" 7 | 8 | #define SELF Config *const self 9 | 10 | struct cfg { 11 | uint16_t temp_min; // The minimum temperature (180 centegrees) 12 | uint16_t temp_max; // The temperature for 400 centegrees 13 | uint16_t temp; // The temperature of the iron to be start 14 | byte brightness; // The display brightness [0-15] 15 | bool celsius; // Temperature units: true - celsius, false - farenheit 16 | }; 17 | 18 | typedef struct ConfigStruct { 19 | struct cfg Config; 20 | bool can_write; 21 | bool is_valid; 22 | bool save_calibration; 23 | byte buffRecords; 24 | uint16_t rAddr; 25 | uint16_t wAddr; 26 | uint16_t eLength; 27 | uint32_t nextRecID; 28 | byte record_size; // was const 29 | } Config; 30 | 31 | void createConfig(Config *const self); 32 | 33 | void initConfig(Config *const self); 34 | bool load(SELF); 35 | bool isValid(const Config *self); 36 | uint16_t Config_temp(const Config *self); 37 | byte getBrightness(const Config *self); 38 | bool Config_getTempUnits(const Config *self); 39 | bool saveTemp(Config *const self, uint16_t t); 40 | void saveConfig(Config *const self, byte bright, bool cels); 41 | void saveCalibrationData(Config *const self, uint16_t t_max, uint16_t t_min); 42 | void getCalibrationData(Config *const self, uint16_t *t_max, uint16_t *t_min); 43 | 44 | // Private function declarations 45 | bool readRecord(SELF, uint16_t addr, uint32_t *recID); 46 | bool save(SELF); 47 | 48 | #undef SELF 49 | #endif /* CONFIG_H */ 50 | -------------------------------------------------------------------------------- /samples/soldering/Display.c: -------------------------------------------------------------------------------- 1 | #include "Display.h" 2 | #include "alloc.h" 3 | #include "string.h" 4 | #include "arduino.h" 5 | 6 | const int HEATING_SIZE = 5; 7 | const byte HEATING[5] = { 8 | '^', '^', '^', '^', '^' 9 | //0b00001000, 0b00010100, 0b00000001, 0b00100010, 0b01000000 10 | }; 11 | 12 | const int COOLING_SIZE = 4; 13 | const byte COOLING[4] = { 14 | '_', '_', '_', '_' 15 | //0b01000000, 0b00000001, 0b00001000, 0 16 | }; 17 | 18 | const int SETUP_MODE_SIZE = 12; 19 | const byte SETUP_MODE[3][4] = { 20 | {'b','r','g','t'}, 21 | {' ','C','-','F'}, 22 | {'t','u','n','e'}, 23 | //{0b00011111, 0b00000101, 0b01111011, 0b00001111}, // 'brgt' 24 | //{0b00000000, 0b01001110, 0b00000001, 0b01000111}, // 'C-F' 25 | //{0b00001111, 0b00011100, 0b00010101, 0b01001111} // 'tune' 26 | }; 27 | 28 | void createDisplay(Display *const self, byte DIN, byte CLK, byte CS, byte BRIGHT) { 29 | createLedControl(&self->parent, (int)DIN, (int)CLK, (int)CS); 30 | self->intensity = BRIGHT; 31 | 32 | self->animate_speed = 100; 33 | 34 | memcpy(self->a_heating, HEATING, HEATING_SIZE); 35 | memcpy(self->a_cooling, COOLING, COOLING_SIZE); 36 | memcpy(self->setup_mode, SETUP_MODE, SETUP_MODE_SIZE); 37 | } 38 | 39 | void initDisplay(Display *const self) { 40 | shutdown(&self->parent, 0, FALSE); 41 | setIntensity(&self->parent, 0, self->intensity); 42 | clear(self); 43 | 44 | delay(500); 45 | 46 | self->dot_mask = 0; 47 | self->big_number = FALSE; 48 | noAnimation(self); 49 | } 50 | 51 | void clear(Display *const self) { 52 | clearDisplay(&self->parent); 53 | } 54 | 55 | void brightness(Display *const self, byte BRIGHT) { 56 | if (BRIGHT > 15) { 57 | BRIGHT = 15; 58 | } 59 | self->intensity = BRIGHT; 60 | setIntensity(&self->parent, 0, self->intensity); 61 | } 62 | 63 | void noAnimation(Display *const self) {; 64 | // Switch off the animation in the highest digit 65 | self->animate_type = 0; 66 | self->animate_count = 0; 67 | self->animate_index = 0; 68 | self->animate_ms = 0; 69 | } 70 | 71 | void number(Display *const self, int data, byte a_dot_mask) { 72 | self->big_number = (data >= 1000); 73 | byte i; 74 | byte m = 1; 75 | 76 | for (i = 0; i < 4; ++i) { 77 | if (data == 0 && i != 0) { break; } 78 | 79 | byte s = data % 10; 80 | data /= 10; 81 | 82 | setDigit(&self->parent, 0, 3-i, s, (m & a_dot_mask)); 83 | m <<= 1; 84 | } 85 | 86 | for (; i < 4; ++i) { 87 | setChar(&self->parent, 0, 3-i, ' ', (m & a_dot_mask)); 88 | m <<= 1; 89 | } 90 | } 91 | 92 | void message(Display *const self, char msg[4]) { 93 | for (byte i = 0; i < 4; ++i) { 94 | setChar(&self->parent, 0, i, msg[i], FALSE); 95 | } 96 | self->animate_type = 0; 97 | } 98 | 99 | void setupMode(Display *const self, byte mode) { 100 | // Show the onfigureation mode [0 - 2] 101 | if (mode <= 2) { // mode >= 0 always true 102 | for (byte i = 0; i < 4; ++i) { 103 | setRow(&self->parent, 0, i, self->setup_mode[mode][i]); 104 | } 105 | } 106 | } 107 | 108 | void heating(Display *const self) { 109 | if (self->big_number) { 110 | return; 111 | } 112 | self->animate_type = 1; 113 | self->animate_count = 5; 114 | self->animate_index = 0; 115 | self->animate_ms = 0; 116 | self->animate_ms = millis() + self->animate_speed; 117 | setRow(&self->parent, 0, 0, self->a_heating[0]); 118 | } 119 | 120 | void cooling(Display *const self) { 121 | if (self->big_number) { 122 | return; 123 | } 124 | self->animate_type = 2; 125 | self->animate_count = 4; 126 | self->animate_index = 0; 127 | self->animate_ms = 0; // pointless?!? 128 | self->animate_ms = millis() + self->animate_speed * 5; 129 | setRow(&self->parent, 0, 0, self->a_cooling[0]); 130 | } 131 | 132 | void P(Display *const self) { 133 | // The highest digit show 'setting power' process 134 | if (self->big_number) { 135 | return; 136 | } 137 | self->animate_type = 0; 138 | setChar(&self->parent, 0, 0, 'P', TRUE); 139 | } 140 | 141 | void upper(Display *const self) { 142 | // The highest digit show 'setting upper temperature' process 143 | if (self->big_number) { 144 | return; 145 | } 146 | self->animate_type = 0; 147 | //setRow(&self->parent, 0, 0, 0b01100011); 148 | setRow(&self->parent, 0, 0, 'u'); 149 | } 150 | 151 | void lower(Display *const self) { 152 | // The highest digit show 'setting lower temperature' process 153 | if (self->big_number) { 154 | return; 155 | } 156 | self->animate_type = 0; 157 | //setRow(&self->parent, 0, 0, 0b00011101); 158 | setRow(&self->parent, 0, 0, 'l'); 159 | } 160 | 161 | void tSet(Display *const self) { 162 | // THe highest digit show 'temperature set' information (idle state) 163 | if (self->big_number) { 164 | return; 165 | } 166 | self->animate_type = 0; 167 | //setRow(&self->parent, 0, 0, 0b10001111); 168 | setRow(&self->parent, 0, 0, 's'); 169 | } 170 | 171 | void Display_show(Display *const self) { 172 | // Show the data on the 4-digit indicator 173 | if (!self->big_number && self->animate_type) { 174 | if ((self->animate_count > 1) && (self->animate_ms < millis())) { 175 | if(++(self->animate_index) >= self->animate_count) { 176 | self->animate_index = 0; 177 | } 178 | 179 | switch (self->animate_type) { 180 | case 1: 181 | self->animate_ms = millis() + self->animate_speed; 182 | setRow(&self->parent, 0, 0, self->a_heating[self->animate_index]); 183 | break; 184 | case 2: 185 | self->animate_ms = millis() + self->animate_speed * 5; 186 | setRow(&self->parent, 0, 0, self->a_cooling[self->animate_index]); 187 | break; 188 | default: 189 | break; 190 | } 191 | } 192 | } 193 | } 194 | 195 | void percent(Display *const self, byte Power) { 196 | // Show the percentage on the led bar (for example power supplied) 197 | if (Power > 10) { 198 | Power = 10; 199 | } 200 | uint16_t mask = 0; 201 | 202 | for (byte i = 0; i < Power; ++i) { 203 | mask >>= 1; 204 | mask |= 0x8000; // 15-th bit 205 | } 206 | 207 | byte m1 = mask >> 8; 208 | byte m2 = mask & 0xff; 209 | 210 | setRow(&self->parent, 0, 4, m1); 211 | setRow(&self->parent, 0, 5, m2); 212 | } 213 | -------------------------------------------------------------------------------- /samples/soldering/Display.h: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_H 2 | #define DISPLAY_H 3 | 4 | #include "types.h" 5 | #include "LedControl.h" 6 | 7 | typedef struct DisplayStruct { 8 | LedControl parent; 9 | byte intensity; // The display brightness 10 | byte dot_mask; // the decimal dot mask 11 | boolean big_number; // If the number is big, we cannot use highest digit for animation 12 | byte animate_type; // animation type: 0 - off, 1 - heating, 2 - cooling 13 | byte animate_count; // The number of the bytes in the animation 14 | byte animate_index; // Current byte in the animation 15 | uint32_t animate_ms; // The time in ms when animation should change to the symbol 16 | // the following we're const: 17 | uint16_t animate_speed; // milliseconds to switch next byte in the animation string (100) 18 | byte a_heating[5]; 19 | byte a_cooling[4]; 20 | byte setup_mode[3][4]; 21 | } Display; 22 | 23 | 24 | void createDisplay(Display *const self, byte DIN, byte CLK, byte CS, byte BRIGHT); 25 | void initDisplay(Display *const self); 26 | void clear(Display *const self); 27 | 28 | /** 29 | * Set the display brightness 30 | */ 31 | void brightness(Display *const self, byte BRIGHT); 32 | 33 | /** 34 | * Set the digital number to be displayed 35 | */ 36 | void number(Display *const self, int data, byte dot_mask); 37 | 38 | /** 39 | * Set the 4-shar word to be displayed 40 | */ 41 | void message(Display *const self, char msg[4]); 42 | 43 | /** 44 | * The highest digit animate heating process 45 | */ 46 | void heating(Display *const self); 47 | 48 | // The highest digit animate cooling process 49 | void cooling(Display *const self); 50 | 51 | void P(Display *const self); // The highest digit show 'setting power' process 52 | void tSet(Display *const self); // THe highest digit show 'temperature set' information (idle state) 53 | void upper(Display *const self); // The highest digit show 'setting upper temperature' process 54 | void lower(Display *const self); // The highest digit show 'setting lower temperature' process 55 | void setupMode(Display *const self, byte mode); // Show the onfigureation mode [0 - 2] 56 | void noAnimation(Display *const self); // Switch off the animation in the highest digit 57 | void Display_show(Display *const self); // Show the data on the 4-digit indicator 58 | void percent(Display *const self, byte Power); // Show the percentage on the led bar (for example power supplied) 59 | 60 | #endif /* DISPLAY_H */ 61 | -------------------------------------------------------------------------------- /samples/soldering/Encoder.c: -------------------------------------------------------------------------------- 1 | #include "Encoder.h" 2 | 3 | #include "arduino.h" 4 | 5 | const uint16_t fast_timeout = 300; // Time in ms to change encodeq quickly 6 | const uint16_t overPress = 1000; 7 | 8 | void createEncoderD(Encoder *const self, byte aPIN, byte bPIN) { 9 | createEncoder(self, aPIN, bPIN, 0); 10 | } 11 | 12 | void createEncoder(Encoder *const self, byte aPIN, byte bPIN, int16_t initPos) { 13 | self->pt = 0; 14 | self->mPIN = aPIN; 15 | self->sPIN = bPIN; 16 | self->pos = initPos; 17 | 18 | self->min_pos = -32767; 19 | self->max_pos = 32766; 20 | self->channelB = FALSE; 21 | self->increment = 1; 22 | self->changed = 0; 23 | self->is_looped = FALSE; 24 | 25 | self->fast_timeout = 300; // Time in ms to change encodeq quickly 26 | self->overPress = 1000; 27 | } 28 | 29 | void initEncoderD(Encoder *const self, int16_t initPos, int16_t low, int16_t upp) { 30 | initEncoder(self, initPos, low, upp, 1, 0, false); 31 | } 32 | 33 | void initEncoder(Encoder *const self, int16_t initPos, int16_t low, int16_t upp, byte inc, byte fast_inc, boolean looped) { 34 | self->min_pos = low; 35 | self->max_pos = upp; 36 | 37 | if (!write(self, initPos)) { 38 | initPos = self->min_pos; 39 | } 40 | 41 | self->fast_increment = inc; 42 | self->increment = inc; 43 | 44 | if (fast_inc > self->increment) { 45 | self->fast_increment = fast_inc; 46 | } 47 | 48 | self->is_looped = looped; 49 | pinMode(self->mPIN, INPUT_PULLUP); 50 | pinMode(self->sPIN, INPUT_PULLUP); 51 | } 52 | 53 | void reset(Encoder *const self, int16_t initPos, int16_t low, int16_t upp, byte inc) { 54 | self->min_pos = low; 55 | self->max_pos = upp; 56 | 57 | if (!write(self, initPos)) { 58 | // this does seem to do anything but change an unused local variable?!? 59 | initPos = self->min_pos; 60 | } 61 | 62 | self->increment = inc; 63 | } 64 | 65 | void set_increment(Encoder *const self, byte inc) { 66 | self->increment = inc; 67 | } 68 | 69 | byte get_increment(Encoder *const self) { 70 | return self->increment; 71 | } 72 | 73 | boolean write(Encoder *const self, int16_t initPos) { 74 | if ((initPos >= self->min_pos) && (initPos <= self->max_pos)) { 75 | self->pos = initPos; 76 | return TRUE; 77 | } 78 | return FALSE; 79 | } 80 | 81 | int16_t read(Encoder *const self) { 82 | return self->pos; 83 | } 84 | 85 | void changeEncoderINTR(Encoder *const self) { 86 | // Interrupt function, called when the channel A of encoder changed 87 | 88 | boolean rUp = digitalRead(self->mPIN); 89 | unsigned long now_t = millis(); 90 | 91 | if (!rUp) { // The channel A has been "pressed" 92 | if ((self->pt == 0) || (now_t - self->pt > self->overPress)) { 93 | self->pt = now_t; 94 | self->channelB = digitalRead(self->sPIN); 95 | } 96 | } else { 97 | if (self->pt > 0) { 98 | byte inc = self->increment; 99 | 100 | if ((now_t - self->pt) < self->overPress) { 101 | if ((now_t - self->changed) < self->fast_timeout) { 102 | inc = self->fast_increment; 103 | } 104 | 105 | self->changed = now_t; 106 | if (self->channelB) { 107 | self->pos -= inc; 108 | } else { 109 | self->pos += inc; 110 | } 111 | 112 | if (self->pos > self->max_pos) { 113 | if (self->is_looped) { 114 | self->pos = self->min_pos; 115 | } else { 116 | self->pos = self->max_pos; 117 | } 118 | } 119 | if (self->pos < self->min_pos) { 120 | if (self->is_looped) { 121 | self->pos = self->max_pos; 122 | } else { 123 | self->pos = self->min_pos; 124 | } 125 | } 126 | } 127 | self->pt = 0; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /samples/soldering/Encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCODER_H 2 | #define ENCODER_H 3 | 4 | #include "types.h" 5 | 6 | typedef struct EncoderStruct { 7 | int32_t min_pos; 8 | int32_t max_pos; 9 | volatile uint32_t pt; // Time in ms when the encoder was rotaded 10 | volatile uint32_t changed; // Time in ms when the value was changed 11 | volatile boolean channelB; 12 | volatile int32_t pos; // Encoder current position 13 | byte mPIN; 14 | byte sPIN; // The pin numbers connected to the main channel and to the socondary channel 15 | boolean is_looped; // Weither the encoder is looped 16 | byte increment; // The value to add or substract for each encoder tick 17 | byte fast_increment; // The value to change encoder when in runs quickly 18 | /*const*/ uint16_t fast_timeout; // Time in ms to change encodeq quickly 19 | /*const*/ uint16_t overPress; 20 | } Encoder; 21 | 22 | void createEncoderD(Encoder *const self, byte aPIN, byte bPIN); // default initPos = 0 23 | void createEncoder(Encoder *const self, byte aPIN, byte bPIN, int16_t initPos); 24 | 25 | void initEncoderD(Encoder *const self, int16_t initPos, int16_t low, int16_t upp); // defalt values: byte inc = 1, byte fast_inc = 0, bool looped = false 26 | void initEncoder(Encoder *const self, int16_t initPos, int16_t low, int16_t upp, byte inc, byte fast_inc, boolean looped); 27 | 28 | void reset(Encoder *const self, int16_t initPos, int16_t low, int16_t upp, byte inc); 29 | 30 | void set_increment(Encoder *const self, byte inc); 31 | byte get_increment(Encoder *const self); 32 | boolean write(Encoder *const self, int16_t initPos); 33 | int16_t read(Encoder *const self); 34 | void changeEncoderINTR(Encoder *const self); 35 | 36 | #endif /* ENCODER_H */ 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /samples/soldering/History.c: -------------------------------------------------------------------------------- 1 | #include "History.h" 2 | 3 | void createHistory(History *const self) { 4 | // Not sure why there was a separate init, but keeping this as the "constructor" 5 | // to maintain similarity with originals use of init functions. 6 | self->len = 0; 7 | } 8 | 9 | void initHistory(History *const self) { 10 | self->len = 0; 11 | } 12 | 13 | void put(History *const self, int item) { 14 | if (self->len < H_LENGTH) { 15 | self->queue[self->len++] = item; 16 | } else { 17 | for (byte i = 0; i < self->len - 1; ++i) { 18 | self->queue[i] = self->queue[i+1]; 19 | } 20 | self->queue[H_LENGTH - 1] = item; 21 | } 22 | } 23 | 24 | bool isFull(History *const self) { 25 | return (self->len == H_LENGTH); 26 | } 27 | 28 | int top(History *const self) { 29 | return self->queue[0]; 30 | } 31 | 32 | int average(History *const self) { 33 | long sum = 0; 34 | if (self->len == 0) { 35 | return 0; 36 | } 37 | 38 | if (self->len == 1) { 39 | return self->queue[0]; 40 | } 41 | 42 | for (byte i = 0; i < self->len; ++i) { 43 | sum += self->queue[i]; 44 | } 45 | sum += self->len >> 1; // round the average 46 | sum /= self->len; 47 | 48 | if (sum < 0) serialWriteString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGH"); 49 | return (int)sum; 50 | } 51 | 52 | float dispersion(History *const self) { 53 | if (self->len < 3) { 54 | return 1000; 55 | } 56 | 57 | long sum = 0; 58 | long avg = average(self); 59 | 60 | for (byte i = 0; i < self->len; ++i) { 61 | long q = self->queue[i]; 62 | q -= avg; 63 | q *= q; 64 | sum += q; 65 | } 66 | sum += self->len << 1; 67 | float d = (float)sum / (float)self->len; 68 | return d; 69 | } 70 | 71 | float gradient(History *const self) { 72 | // approfimating the history with the line (y = ax+b) using method of minimum square. Gradient is parameter a 73 | if (self->len < 2) return 0; 74 | long sx, sx_sq, sxy, sy; 75 | sx = sx_sq = sxy = sy = 0; 76 | 77 | for (byte i = 1; i <= self->len; ++i) { 78 | sx += i; 79 | sx_sq += i*i; 80 | sxy += i*self->queue[i-1]; 81 | sy += self->queue[i-1]; 82 | } 83 | long numerator = self->len * sxy - sx * sy; 84 | long denominator = self->len * sx_sq - sx * sx; 85 | float a = (float)numerator / (float)denominator; 86 | return a; 87 | } 88 | 89 | -------------------------------------------------------------------------------- /samples/soldering/History.h: -------------------------------------------------------------------------------- 1 | #ifndef HISTORY_H 2 | #define HISTORY_H 3 | 4 | #include "types.h" 5 | 6 | #define H_LENGTH 8 7 | 8 | typedef struct HistoryStruct { 9 | byte len; 10 | int queue[H_LENGTH]; 11 | } History; 12 | 13 | void createHistory(History *const self); 14 | 15 | void initHistory(History *const self); 16 | void put(History *const self, int item); 17 | boolean isfull(History *const self); 18 | int top(History *const self); 19 | int average(History *const self); 20 | float dispersion(History *const self); 21 | float gradient(History *const self); 22 | 23 | #endif /* HISTORY_H */ 24 | -------------------------------------------------------------------------------- /samples/soldering/Iron.c: -------------------------------------------------------------------------------- 1 | #include "Iron.h" 2 | #include "arduino.h" 3 | #include "constants.h" 4 | 5 | #define SELF Iron *const self 6 | 7 | // private: 8 | void applyPower(SELF); // Check the the power limits and apply power to the heater 9 | void resetPID(SELF); // Reset PID algorythm parameters 10 | 11 | void createIron(SELF, byte heater_pin, byte sensor_pin) { 12 | // create* child objects 13 | createHistory(&self->h_power); 14 | 15 | // "const" values 16 | self->temp_cold = 255; // The cold temperature to touch the iron safely 17 | self->max_power = 220; // maximum power to the iron (180) 18 | self->min_power = 25; // minimum power to the iron 19 | self->delta_t = 2; // measurement error of the temperature 20 | self->period = 500; // The period to check the soldering iron temperature, ms 21 | self->check_time = 10; // Time in seconds to check weither the solder is heating 22 | self->heat_expected = 10; // The iron should change the temperature at check_time 23 | self->denominator_p = 10; // The common coefficeient denominator power of 2 (10 means 1024) 24 | self->Kp = 5120; // Kp multiplied by denominator 25 | self->Ki = 512; // Ki multiplied by denominator 26 | self->Kd = 768; // Kd multiplied by denominator 27 | 28 | // Constructor 29 | self->hPIN = heater_pin; 30 | self->sPIN = sensor_pin; 31 | self->on = false; 32 | self->unit_celsius = true; 33 | self->fix_power = false; 34 | self->unit_celsius = true; 35 | } 36 | 37 | void initIron(SELF, uint16_t t_max, uint16_t t_min) { 38 | pinMode(self->sPIN, INPUT); 39 | pinMode(self->hPIN, OUTPUT); 40 | digitalWrite(self->hPIN, LOW); 41 | self->on = false; 42 | self->fix_power = false; 43 | self->power = 0; 44 | self->checkMS = 0; 45 | 46 | self->elapsed_time = 0; 47 | self->temp_start = analogRead(self->sPIN); 48 | self->iron_checked = false; 49 | self->temp_max = t_max; 50 | self->temp_min = t_min; 51 | 52 | resetPID(self); 53 | initHistory(&self->h_power); 54 | } 55 | 56 | bool isOn(SELF) { 57 | return self->on; 58 | } 59 | 60 | void switchPower(SELF, bool On) { 61 | self->on = On; 62 | if (!self->on) { 63 | digitalWrite(self->hPIN, LOW); 64 | self->fix_power = false; 65 | return; 66 | } 67 | 68 | resetPID(self); 69 | self->temp_hist[1] = analogRead(self->sPIN); 70 | initHistory(&self->h_power); 71 | self->checkMS = millis(); 72 | } 73 | 74 | bool isCold(SELF) { 75 | return (Iron_temp(self) < self->temp_cold); 76 | } 77 | 78 | void setTempUnits(SELF, bool celsius) { 79 | self->unit_celsius = celsius; 80 | } 81 | 82 | bool getTempUnits(SELF) { 83 | return self->unit_celsius; 84 | } 85 | 86 | uint16_t getTemp(SELF) { 87 | return self->temp_set; 88 | } 89 | 90 | uint16_t lastTemp(SELF) { 91 | return self->temp_hist[1]; 92 | } 93 | 94 | /** 95 | * Set the temperature to be keeped 96 | */ 97 | void setTemp(SELF, int t) { 98 | if (self->on) { 99 | resetPID(self); 100 | } 101 | self->temp_set = t; 102 | } 103 | 104 | /** 105 | * Set the temperature to be keeped in human readable units (celsius or farenheit) 106 | */ 107 | void setTempHumanUnits(SELF, int t) { 108 | int temp; 109 | if (self->unit_celsius) { 110 | if (t < temp_minC) { 111 | t = temp_minC; 112 | } 113 | if (t > temp_maxC) { 114 | t = temp_maxC; 115 | } 116 | temp = map(t+1, temp_minC, temp_maxC, self->temp_min, self->temp_max); 117 | } else { 118 | if (t < temp_minF) { 119 | t = temp_minF; 120 | } 121 | if (t > temp_maxF) { 122 | t = temp_maxF; 123 | } 124 | temp = map(t+2, temp_minF, temp_maxF, self->temp_min, self->temp_max); 125 | } 126 | for (byte i = 0; i < 10; ++i) { 127 | int tH = temp2humanUnits(self, temp); 128 | if (tH <= t) { 129 | break; 130 | } 131 | --temp; 132 | } 133 | setTemp(self, temp); 134 | } 135 | 136 | /** 137 | * Translate internal temperature to the celsius or farenheit 138 | */ 139 | uint16_t temp2humanUnits(SELF, uint16_t temp) { 140 | if (!self->unit_celsius) { 141 | return map(temp, self->temp_min, self->temp_max, temp_minF, temp_maxF); 142 | } 143 | return map(temp, self->temp_min, self->temp_max, temp_minC, temp_maxC); 144 | } 145 | 146 | void resetPID(SELF) { 147 | self->pid_iterate = false; 148 | self->temp_hist[0] = self->temp_hist[1] = 0; 149 | } 150 | 151 | uint16_t Iron_temp(SELF) { 152 | uint16_t t1 = analogRead(self->sPIN); 153 | delay(50); 154 | uint16_t t2 = analogRead(self->sPIN); 155 | 156 | if (abs(t1 - t2) < 50) { 157 | t1 += t2 + 1; 158 | // average of two measurements 159 | t1 >>= 1; 160 | return t1; 161 | } 162 | if (abs(t1 - self->temp_hist[1]) < abs(t2 - self->temp_hist[1])) { 163 | return t1; 164 | } else { 165 | return t2; 166 | } 167 | } 168 | 169 | uint16_t lastTemp(SELF); 170 | 171 | byte getPower(SELF) { 172 | int p = self->power; 173 | if (p < 0) p = 0; 174 | if (p > self->max_power) { 175 | p = self->max_power; 176 | } 177 | return p & 0xff; 178 | } 179 | 180 | byte getAvgPower(SELF) { 181 | int p = average(&self->h_power); 182 | return p & 0xff; 183 | } 184 | 185 | byte appliedPower(SELF) { 186 | byte p = getPower(self); 187 | return map(p, 0, self->max_power, 0, 10); 188 | } 189 | 190 | byte hotPercent(SELF) { 191 | uint16_t t = Iron_temp(self); 192 | byte r = map(t, self->temp_cold, self->temp_set, 0, 10); 193 | // if (r < 0) r = 0; // always false 194 | return r; 195 | } 196 | 197 | void keepTemp(SELF) { 198 | if (self->checkMS > millis()) { 199 | return; 200 | } 201 | 202 | self->checkMS = millis() + self->period; 203 | 204 | if (!self->on) { 205 | // If the soldering iron is set to be switched off 206 | if (!self->fix_power) { 207 | // Surely power off the iron 208 | digitalWrite(self->hPIN, LOW); 209 | } 210 | return; 211 | } 212 | 213 | int temp_curr = Iron_temp(self); 214 | 215 | // Check weither the iron can be heated 216 | if (!self->iron_checked) { 217 | self->elapsed_time += self->period / 1000; 218 | 219 | if (self->elapsed_time >= self->check_time) { 220 | if ((abs(self->temp_set - temp_curr) < 100) 221 | || ((temp_curr - self->temp_start) > self->heat_expected)) { 222 | self->iron_checked = true; 223 | } else { 224 | // Prevent the iron damage 225 | switchPower(self, false); 226 | self->elapsed_time = 0; 227 | self->temp_start = analogRead(self->sPIN); 228 | self->iron_checked = false; 229 | } 230 | } 231 | } 232 | 233 | // first, use the direct formulae, not the iterate process 234 | if (self->temp_hist[0] == 0) { 235 | long p = (long) self->Kp * (self->temp_set - temp_curr) + 236 | (long) self->Ki * (self->temp_set - temp_curr); 237 | 238 | p += (1 << (self->denominator_p - 1)); 239 | p >>= self->denominator_p; 240 | 241 | self->temp_hist[1] = temp_curr; 242 | 243 | // If the temperature is near, prepare the PID iteration process 244 | if ((self->temp_set - temp_curr) < 30) { 245 | // The second loop 246 | if (self->pid_iterate) { 247 | // Now we are redy to use iterate algorythm 248 | self->temp_hist[0] = self->temp_hist[1]; 249 | } else { 250 | // The first loop 251 | self->pid_iterate = true; 252 | } 253 | } 254 | self->power = p; 255 | } else { 256 | long delta_p = (long) self->Kp * (self->temp_hist[1] - temp_curr); 257 | delta_p += (long)self->Ki * (self->temp_set - temp_curr); 258 | delta_p += (long)self->Kd * (2*self->temp_hist[1] - self->temp_hist[0] - temp_curr); 259 | delta_p += (1 << (self->denominator_p-1)); 260 | delta_p >>= self->denominator_p; 261 | self->power += delta_p; 262 | self->temp_hist[0] = self->temp_hist[1]; 263 | self->temp_hist[1] = temp_curr; 264 | } 265 | applyPower(self); 266 | } 267 | 268 | void applyPower(SELF) { 269 | byte p = getPower(self); 270 | if (self->temp_hist[1] > (self->temp_set + 1)) { 271 | p = 0; 272 | } 273 | if (p == 0) { 274 | digitalWrite(self->hPIN, LOW); 275 | } 276 | if (self->on) { 277 | analogWrite(self->hPIN, p & 0xff); 278 | } 279 | put(&self->h_power, p); 280 | } 281 | 282 | bool fixPower(SELF, byte Power) { 283 | if (Power == 0) { // To switch off the iron, set the power to 0 284 | self->fix_power = false; 285 | digitalWrite(self->hPIN, LOW); 286 | return true; 287 | } 288 | 289 | if (Power > 80) { 290 | return false; 291 | } 292 | 293 | if (!self->fix_power) { 294 | self->fix_power = true; 295 | self->power = Power; 296 | analogWrite(self->hPIN, self->power & 0xff); 297 | } else { 298 | if (self->power != Power) { 299 | self->power = Power; 300 | analogWrite(self->hPIN, self->power & 0xff); 301 | } 302 | } 303 | return true; 304 | } 305 | -------------------------------------------------------------------------------- /samples/soldering/Iron.h: -------------------------------------------------------------------------------- 1 | #ifndef IRON_H 2 | #define IRON_H 3 | 4 | #include "types.h" 5 | #include "History.h" 6 | 7 | #define SELF Iron *const self 8 | typedef struct IronStruct { 9 | History h_power; 10 | uint32_t checkMS; // Milliseconds to measure the temperature next time 11 | byte hPIN, sPIN; // the heater PIN and the sensor PIN 12 | int power; // The soldering station power 13 | bool on; // Weither the soldering iron is on 14 | bool fix_power; // Weither the soldering iron is set the fix power 15 | bool unit_celsius; // Human readable units for the temparature (celsius or farenheit) 16 | int temp_set; // The temperature that should be established 17 | int temp_hist[2]; // previously measured temperature 18 | bool iron_checked; // Weither the iron works 19 | int temp_start; // The temperature when the solder was switched on 20 | int elapsed_time; // The time elipsed from the start (in seconds) 21 | uint16_t temp_min; // The minimum temperature (180 centegrees) 22 | uint16_t temp_max; // The maximum temperature (400 centegrees) 23 | bool pid_iterate; // Weither the inerative PID formulae can be used 24 | uint16_t temp_cold; // The cold temperature to touch the iron safely 25 | byte max_power; // maximum power to the iron (180) 26 | byte min_power; // minimum power to the iron 27 | byte delta_t; // measurement error of the temperature 28 | uint16_t period; // The period to check the soldering iron temperature, ms 29 | int check_time; // Time in seconds to check weither the solder is heating 30 | int heat_expected; // The iron should change the temperature at check_time 31 | byte denominator_p; // The common coefficeient denominator power of 2 (10 means 1024) 32 | int Kp; // Kp multiplied by denominator 33 | int Ki; // Ki multiplied by denominator 34 | int Kd; // Kd multiplied by denominator 35 | } Iron; 36 | 37 | void createIron(SELF, byte heater_pin, byte sensor_pin); 38 | 39 | void initIron(SELF, uint16_t t_max, uint16_t t_min); 40 | 41 | bool isOn(SELF); 42 | void switchPower(SELF, bool On); 43 | bool isCold(SELF); 44 | void setTempUnits(SELF, bool celsius); 45 | bool getTempUnits(SELF); 46 | uint16_t getTemp(SELF); 47 | 48 | /** 49 | * Set the temperature to be keeped 50 | */ 51 | void setTemp(SELF, int t); 52 | 53 | /** 54 | * Set the temperature to be keeped in human readable units (celsius or farenheit) 55 | */ 56 | void setTempHumanUnits(SELF, int t); 57 | 58 | /** 59 | * Translate internal temperature to the celsius or farenheit 60 | */ 61 | uint16_t temp2humanUnits(SELF, uint16_t temp); 62 | 63 | /** 64 | * The actual temperature of the soldering iron 65 | */ 66 | uint16_t Iron_temp(SELF); 67 | 68 | uint16_t lastTemp(SELF); 69 | 70 | byte getPower(SELF); // power that is applied to the soldering iron [0 - 255] 71 | byte getAvgPower(SELF); // average applied power 72 | byte appliedPower(SELF); // Power applied to the solder [0-10] 73 | byte hotPercent(SELF); // How hot is the iron (used in the idle state) 74 | void keepTemp(SELF); // Main solder iron loop 75 | bool fixPower(SELF, byte Power); // Set the specified power to the the soldering iron 76 | 77 | // private: -> Iron.c 78 | //void applyPower(SELF); // Check the the power limits and apply power to the heater 79 | //void resetPID(SELF); // Reset PID algorythm parameters 80 | 81 | #undef SELF 82 | #endif /* IRON_H */ 83 | -------------------------------------------------------------------------------- /samples/soldering/LedControl.c: -------------------------------------------------------------------------------- 1 | #include "LedControl.h" 2 | #include "serial.h" 3 | #include "util.h" 4 | 5 | #define SELF LedContorl *const self 6 | 7 | void createLedControl(LedControl *const self, int DIN, int CLK, int CS) { 8 | (void) DIN; 9 | (void) CLK; 10 | (void) CS; 11 | clearDisplay(self); 12 | } 13 | 14 | void shutdown(LedControl *const self, int a, boolean b) { 15 | UNIMPLEMENTED("LedControl : shutdown"); 16 | (void) self; 17 | (void) a; 18 | (void) b; 19 | } 20 | 21 | void setIntensity(LedControl *const self, int a, int b) { 22 | UNIMPLEMENTED("LedControl : setIntentisty"); 23 | (void) self; 24 | (void) a; 25 | (void) b; 26 | } 27 | 28 | /* Switch all Leds on the display off. 29 | * Params: 30 | * addr The address of the display to control 31 | * 32 | * void clearDisplay(int *addr); 33 | */ 34 | void clearDisplay(LedControl *const self) { 35 | for (int i = 0; i < LED_CHARS_LENGTH; i++) { 36 | self->chars[i] = ' '; 37 | } 38 | } 39 | 40 | /* 41 | * 42 | * Display a (hexadecimal) digit on a 7-Segment Display 43 | * Params: 44 | * addr address of the display 45 | * digit the position of the digit on the display (0..7) 46 | * value the value to be displayed. (0x00..0x0F) 47 | * dp sets the decimal point. 48 | * 49 | * void setDigit(int addr, int digit, byte value, boolean dp) 50 | */ 51 | void setDigit(LedControl *const self, int addr, int digit, byte value, boolean dp) { 52 | (void) addr; 53 | (void) dp; 54 | 55 | self->chars[digit] = hexDigitToChar(value); 56 | } 57 | 58 | /* Display a character on a 7-Segment display. 59 | * Params: 60 | * addr address of the display 61 | * digit the position of the character on the display (0..7) 62 | * value the character to be displayed. 63 | * dp sets the decimal point. 64 | */ 65 | void setChar(LedControl *const self, int addr, int digit, char value, boolean dp) { 66 | (void) addr; 67 | (void) digit; 68 | (void) dp; 69 | 70 | self->chars[digit] = value; 71 | } 72 | 73 | /* Set all 8 Led's in a row to a new state 74 | * Params: 75 | * addr ddress of the display 76 | * row row which is to be set (0..7) 77 | * value each bit set to 1 will light up the corresponding Led. 78 | */ 79 | void setRow(LedControl *const self, int addr, int row, byte value) { 80 | (void) addr; 81 | self->chars[row] = (char) value; 82 | } 83 | 84 | 85 | void emulate_showLed(LedControl *const self) { 86 | serialWriteString("D>>> "); 87 | serialWrite(self->chars, 4); 88 | serialWriteString(" <<= 0) 9 | 10 | #include "types.h" 11 | 12 | typedef struct LecControlStruct { 13 | char chars[LED_CHARS_LENGTH]; 14 | } LedControl; 15 | 16 | void createLedControl(LedControl *const self, int DIN, int CLK, int CS); 17 | 18 | /** 19 | * Send updated view view serial 20 | */ 21 | void emulate_showLed(LedControl *const self); 22 | 23 | void shutdown(LedControl *const self, int a, boolean b); 24 | void setIntensity(LedControl *const self, int a, int b); 25 | 26 | /* Switch all Leds on the display off. 27 | * Params: 28 | * addr The address of the display to control 29 | * 30 | * void clearDisplay(int *addr); 31 | */ 32 | void clearDisplay(LedControl *const self); 33 | 34 | /* 35 | * 36 | * Display a (hexadecimal) digit on a 7-Segment Display 37 | * Params: 38 | * addr address of the display 39 | * digit the position of the digit on the display (0..7) 40 | * value the value to be displayed. (0x00..0x0F) 41 | * dp sets the decimal point. 42 | * 43 | * void setDigit(int addr, int digit, byte value, boolean dp) 44 | */ 45 | void setDigit(LedControl *const self, int addr, int digit, byte value, boolean dp); 46 | 47 | /* Display a character on a 7-Segment display. 48 | * Params: 49 | * addr address of the display 50 | * digit the position of the character on the display (0..7) 51 | * value the character to be displayed. 52 | * dp sets the decimal point. 53 | */ 54 | void setChar(LedControl *const self, int addr, int digit, char value, boolean dp); 55 | 56 | /* Set all 8 Led's in a row to a new state 57 | * Params: 58 | * addr ddress of the display 59 | * row row which is to be set (0..7) 60 | * value each bit set to 1 will light up the corresponding Led. 61 | */ 62 | void setRow(LedControl *const self, int addr, int row, byte value); 63 | 64 | #endif /* LED_CONTROL_H */ 65 | -------------------------------------------------------------------------------- /samples/soldering/README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi 2 port of Soldering Iron Controller for Hakko 907 2 | 3 | Adapted from the original project [1][2] 4 | 5 | ## Functionality 6 | 7 | This application controls the power applied to a connected soldering iron in an effor to keep the 8 | temperature stable. 9 | 10 | ### Input 11 | 12 | Input is entered through a serial interface connection in a way that emulates the input mechanisms 13 | on the physical device. 14 | 15 | There is a button that recognizes either short or long presses. 16 | - 'k' presses the button down 17 | - 'j' releases the button 18 | 19 | There is also a rotary controller (turn knob) which can be turned by repeatedly tapping either 'h' or 20 | 'l'. 21 | 22 | ### Output 23 | 24 | The physical devices has a four digit 7-led display, which is emulated with serial console outputs 25 | (a new line is shown when any changes are made on the display). 26 | 27 | D>>> _0FF <<-> 474) (cur scr: ScreenMain) 28 | 29 | The first section between the < & > brackets and Ds is the device led display, the rest is internal 30 | states mainly for debugging. 31 | 32 | #### Display output 33 | 34 | The display is mostly self-explanatory, in regular work mode simply showing the current iron 35 | temperature. When turning the knob it shows a number prefix with an 's' to indicate that the value 36 | shown is the targeted value. NOTE: The default temperature unit is Fahrenheit. 37 | 38 | The original implementation had heating cooling animations, which are here exchanged to simple 39 | prefixing '\_' (cooling) or '^' (heating). The tune mode (used to calibrate the heat sensors) also 40 | uses prefixes 'u' and 'l' to indicate upper and lower bounds. 41 | 42 | #### Debug ouput 43 | - btn mode: the most recent button state, where 0 is none, 1 is short press, 2 is long press 44 | - rot: shows the current internal value and available range on the internal rotary controller 45 | - iron: shows the current internal value and target-value for the soldering iron temperature. 46 | - cur scr: show the currently active Screen (state) 47 | 48 | ## Similarity to original 49 | 50 | The program structure and operation is essentially identical to original, although the main loop 51 | includes some additional calls to handle input and output. 52 | 53 | One big exception is the manual implementation of object creation and polymorphism. The explicit 54 | object creation funcitons are executed only once before entering the main loop. Method calls and 55 | object member variable acces also necessitated addition of explicit object pointers. 56 | 57 | On very few occasions (which are documented in the code) unused/redundant statements we're removed 58 | to get rid of compiler warnings. Also, binary values used for led animations have also been replaced 59 | with simple character values for easier printing. 60 | 61 | And finally, all hardware interfaces that weren't part of the original project have also been 62 | replaced by dummies. These include: 63 | 64 | arduino.*, eeprom.*, LedControl.*, oo.h, serial.*, util.* 65 | 66 | [1]: https://create.arduino.cc/projecthub/sfrwmaker/soldering-iron-controller-for-hakko-907-8c5866A "Soldering Iron Controller for Hakko 907" 67 | [2]: https://github.com/sfrwmaker/soldering_controller "sfrwmaker/soldering_controller" 68 | -------------------------------------------------------------------------------- /samples/soldering/Screen.c: -------------------------------------------------------------------------------- 1 | #include "Screen.h" 2 | 3 | #include "arduino.h" 4 | #include "serial.h" 5 | 6 | #define SELF Screen *const self 7 | 8 | void initScreen(SELF) { 9 | if (self->init != 0) { 10 | self->init(self); 11 | } 12 | } 13 | 14 | void showScreen(SELF) { 15 | if (self->showScreen != 0) { 16 | self->showScreen(self); 17 | } 18 | } 19 | 20 | void* menu(SELF) { 21 | if (self->menu != 0) { 22 | return self->menu(self); 23 | } 24 | 25 | if (self->next != 0) { 26 | return self->next; 27 | } else { 28 | return self; 29 | } 30 | } 31 | void* menu_long(SELF) { 32 | if (self->menu_long != 0) { 33 | return self->menu_long(self); 34 | } 35 | 36 | if (self->nextL != 0) { 37 | return self->nextL; 38 | } else { 39 | return self; 40 | } 41 | } 42 | 43 | void rotaryValue(SELF, int16_t value) { 44 | if (self->rotaryValue != 0) { 45 | self->rotaryValue(self, value); 46 | return; 47 | } 48 | } 49 | 50 | bool isSetup(SELF) { 51 | return (self->scr_timeout != 0); 52 | } 53 | 54 | void forceRedraw(SELF) { 55 | self->force_redraw = true; 56 | } 57 | 58 | void *returnToMain(SELF) { 59 | if (self->main && (self->scr_timeout != 0) && (millis() >= self->time_to_return)) { 60 | self->scr_timeout = 0; 61 | return self->main; 62 | } 63 | return self; 64 | } 65 | 66 | void resetTimeout(SELF) { 67 | if (self->scr_timeout > 0) { 68 | self->time_to_return = millis() + (self->scr_timeout)*1000; 69 | } 70 | } 71 | 72 | void setSCRtimeout(SELF, uint16_t t) { 73 | self->scr_timeout = t; 74 | resetTimeout(self); 75 | } 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /samples/soldering/Screen.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_H 2 | #define SCREEN_H 3 | 4 | #include "types.h" 5 | 6 | #define SELF Screen *const self 7 | typedef struct ScreenStruct { 8 | void *next; // Pointer to the next screen 9 | void *nextL; // Pointer to the next Level screen, usually, setup 10 | void *main; // Pointer to the main screen 11 | bool force_redraw; 12 | uint16_t scr_timeout; // Timeout is sec. to return to the main screen, canceling all changes 13 | uint32_t time_to_return; // Time in ms to return to main screen 14 | void (*init)(void *const ptr); 15 | void (*showScreen)(void *const ptr); 16 | void *(*menu)(void *const ptr); 17 | void *(*menu_long)(void *const ptr); 18 | void (*rotaryValue)(void *const ptr, int16_t value); 19 | bool (*isSetup)(void *const ptr); 20 | void (*forceRedraw)(void *const ptr); 21 | void (*retrunToMain)(void *const ptr); 22 | void (*resetTimeout)(void *const ptr); 23 | void (*setSCRtimeout)(void *const ptr, uint16_t t); 24 | } Screen; 25 | 26 | void initScreen(SELF); 27 | void showScreen(SELF); 28 | void *menu(SELF); 29 | void *menu_long(SELF); 30 | void rotaryValue(SELF, int16_t value); 31 | bool isSetup(SELF); 32 | void forceRedraw(SELF); 33 | void *returnToMain(SELF); 34 | void resetTimeout(SELF); 35 | void setSCRtimeout(SELF, uint16_t t); 36 | 37 | #undef SELF 38 | #endif /* SCREEN_H */ 39 | 40 | -------------------------------------------------------------------------------- /samples/soldering/ScreenConfig.c: -------------------------------------------------------------------------------- 1 | #include "ScreenConfig.h" 2 | #include "string.h" 3 | 4 | const char MSG_CELSIUS[4] = {' ', ' ', ' ', 'c' }; 5 | const char MSG_FARENHEIT[4] = {' ', ' ', ' ', 'F' }; 6 | 7 | void createScreenConfig(ScreenConfig *const self, Iron* iron, Display* display, Encoder* enc, Config* cfg) { 8 | self->pIron = iron; 9 | self->pD = display; 10 | self->pEnc = enc; 11 | self->pCfg = cfg; 12 | 13 | memcpy(self->msg_celsius, MSG_CELSIUS, 4); 14 | memcpy(self->msg_farenheit, MSG_FARENHEIT, 4); 15 | 16 | // overrides 17 | self->parent.init = initScreenConfig; 18 | self->parent.showScreen = showScreenConfig; 19 | self->parent.rotaryValue = rotaryValueScreenConfig; 20 | self->parent.menu = menuScreenConfig; 21 | self->parent.menu_long = menu_longScreenConfig; 22 | } 23 | 24 | void initScreenConfig(VOID_SELF) { 25 | CAST_SELF(ScreenConfig); 26 | 27 | self->mode = 0; 28 | initEncoder(self->pEnc, self->mode, 0, 2, 1, 0, true); 29 | self->tune = false; 30 | self->changed = false; 31 | self->brigh = getBrightness(self->pCfg); 32 | self->cels = Config_getTempUnits(self->pCfg); 33 | clear(self->pD); 34 | setSCRtimeout(&self->parent, 30); 35 | } 36 | 37 | void showScreenConfig(VOID_SELF) { 38 | CAST_SELF(ScreenConfig); 39 | Display *pD = self->pD; 40 | 41 | if ((!self->parent.force_redraw) && (millis() < self->update_screen)) { 42 | return; 43 | } 44 | 45 | self->parent.force_redraw = false; 46 | self->update_screen = millis() + 10000; 47 | 48 | if ((self->mode == 1) && self->tune) { 49 | if (self->cels) { 50 | message(pD, self->msg_celsius); 51 | } else { 52 | message(pD, self->msg_farenheit); 53 | } 54 | return; 55 | } 56 | setupMode(pD, self->mode); 57 | } 58 | 59 | void rotaryValueScreenConfig(VOID_SELF, int16_t value) { 60 | CAST_SELF(ScreenConfig); 61 | Display *pD = self->pD; 62 | 63 | self->update_screen = millis() + 10000; 64 | if (self->tune) { 65 | self->changed = true; 66 | if (self->mode == 0) { 67 | self->brigh = value; 68 | brightness(pD, self->brigh); 69 | } else { // mode == 1, C/F 70 | self->cels = !value; 71 | } 72 | } else { 73 | self->mode = value; 74 | } 75 | self->parent.force_redraw = true; 76 | } 77 | 78 | void* menuScreenConfig(VOID_SELF) { 79 | CAST_SELF(ScreenConfig); 80 | Encoder *pEnc = self->pEnc; 81 | 82 | if (self->tune) { 83 | self->tune = false; 84 | initEncoder(pEnc, self->mode, 0, 2, 1, 0, true); 85 | } else { 86 | switch (self->mode) { 87 | case 0: // brightness 88 | initEncoder(pEnc, self->brigh, 0, 15, 1, 0, false); 89 | break; 90 | case 1: // C/F 91 | initEncoder(pEnc, self->cels, 0, 1, 1, 0, true ); 92 | break; 93 | case 2: // Calibration 94 | if (self->parent.next) { 95 | return self->parent.next; 96 | } 97 | break; 98 | } 99 | self->tune = true; 100 | } 101 | self->parent.force_redraw = true; 102 | return self; 103 | } 104 | 105 | void* menu_longScreenConfig(VOID_SELF) { 106 | CAST_SELF(ScreenConfig); 107 | 108 | if (self->parent.nextL) { 109 | if (self->changed) { 110 | saveConfig(self->pCfg, self->brigh, self->cels); 111 | setTempUnits(self->pIron, self->cels); 112 | } 113 | return self->parent.nextL; 114 | } 115 | return (Screen*)self; 116 | } 117 | 118 | -------------------------------------------------------------------------------- /samples/soldering/ScreenConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_CONFIG_H 2 | #define SCREEN_CONFIG_H 3 | 4 | #include "Screen.h" 5 | #include "Display.h" 6 | #include "Iron.h" 7 | #include "Encoder.h" 8 | #include "Config.h" 9 | #include "oo.h" 10 | 11 | typedef struct ScreenConfigStruct { 12 | Screen parent; 13 | Iron* pIron; // Pointer to the iron instance 14 | Display* pD; // Pointer to the display instance 15 | Encoder* pEnc; // Pointer to the rotary encoder instance 16 | Config* pCfg; // Pointer to the config instance 17 | uint32_t update_screen; // Time in ms to update the screen 18 | byte mode; // Which parameter to change: 0 - brightness, 1 - C/F, 2 - tuneSCREEN 19 | bool tune; // Weither the parameter is modified 20 | bool changed; // Weither some configuration parameter has been changed 21 | byte brigh; // Current screen brightness 22 | bool cels; // Current celsius/farenheit; 23 | char msg_celsius[4]; 24 | char msg_farenheit[4]; 25 | } ScreenConfig; 26 | 27 | void createScreenConfig(ScreenConfig *const self, Iron* iron, Display* display, Encoder* enc, Config* cfg); 28 | void initScreenConfig(VOID_SELF); 29 | void showScreenConfig(VOID_SELF); 30 | void rotaryValueScreenConfig(VOID_SELF, int16_t value); 31 | void* menuScreenConfig(VOID_SELF); 32 | void* menu_longScreenConfig(VOID_SELF); 33 | 34 | #endif /* SCREEN_CONFIG_H */ 35 | -------------------------------------------------------------------------------- /samples/soldering/ScreenError.c: -------------------------------------------------------------------------------- 1 | #include "ScreenError.h" 2 | 3 | #include "string.h" // memcpy 4 | #include "oo.h" 5 | 6 | #define CAST_SELF(type) type *const self = (type *const) void_self 7 | 8 | const char MSG_FAIL[4] = {'F', 'A', '1', 'L'}; 9 | 10 | void createScreenError(ScreenError *const self, Display *display) { 11 | self->pD = display; 12 | memcpy(self->msg_fail, MSG_FAIL, 4); 13 | 14 | // Override methods 15 | self->parent.init = initScreenError; 16 | } 17 | 18 | void initScreenError(void *const void_self) { 19 | CAST_SELF(ScreenError); 20 | 21 | clear(self->pD); 22 | message(self->pD, self->msg_fail); 23 | } 24 | -------------------------------------------------------------------------------- /samples/soldering/ScreenError.h: -------------------------------------------------------------------------------- 1 | #ifndef SCEEN_ERROR_H 2 | #define SCEEN_ERROR_H 3 | 4 | #include "Display.h" 5 | #include "Screen.h" 6 | 7 | typedef struct ScreenErrorStruct { 8 | Screen parent; 9 | Display *pD; 10 | char msg_fail[4]; 11 | } ScreenError; 12 | 13 | void createScreenError(ScreenError *const self, Display *display); 14 | void initScreenError(void *const void_self); 15 | 16 | #endif /* SCEEN_ERROR_H */ 17 | 18 | -------------------------------------------------------------------------------- /samples/soldering/ScreenMain.c: -------------------------------------------------------------------------------- 1 | #include "ScreenMain.h" 2 | #include "string.h" 3 | 4 | #define SELF ScreenMain *const self 5 | #define PTR void *const void_self 6 | #define CAST_SELF ScreenMain *const self = (ScreenMain *const) void_self; 7 | 8 | const char MSG_OFF[4] = {' ', '0', 'F', 'F'}; 9 | const char MSG_COOL[4] = {'c', '0', 'L', 'd'}; 10 | const char MSG_IDLE[4] = {'1', 'd', 'L', 'E'}; 11 | 12 | void createScreenMain(SELF, 13 | Iron *iron, Display *display, Encoder *encoder, Buzzer *buzzer, Config *config) { 14 | self->update_screen = 0; 15 | self->pIron = iron; 16 | self->pD = display; 17 | self->pEnc = encoder; 18 | self->pBz = buzzer; 19 | self->pCfg = config; 20 | self->is_celsius = true; 21 | 22 | memcpy(self->msg_off, MSG_OFF, 4); 23 | memcpy(self->msg_cool, MSG_COOL, 4); 24 | memcpy(self->msg_idle, MSG_IDLE, 4); 25 | 26 | // Override parent functions 27 | self->parent.init = initScreenMain; 28 | self->parent.showScreen = showScreenMain; 29 | self->parent.rotaryValue = rotaryValueScreenMain; 30 | } 31 | 32 | 33 | void initScreenMain(PTR) { 34 | CAST_SELF; 35 | 36 | Display *pD = self->pD; 37 | Iron *pIron = self->pIron; 38 | 39 | switchPower(pIron, false); 40 | uint16_t temp_set = getTemp(pIron); 41 | self->is_celsius = Config_getTempUnits(self->pCfg); 42 | 43 | setTempUnits(pIron, self->is_celsius); 44 | 45 | uint16_t tempH = temp2humanUnits(pIron, temp_set); 46 | if (self->is_celsius) { 47 | initEncoder(self->pEnc, tempH, temp_minC, temp_maxC, 1, 5, false); // added default arg false 48 | } else { 49 | initEncoder(self->pEnc, tempH, temp_minF, temp_maxF, 1, 5, false); // added default arg false 50 | } 51 | 52 | self->show_set_temp = false; 53 | self->update_screen = millis(); 54 | 55 | clear(pD); 56 | forceRedraw(void_self); 57 | 58 | uint16_t temp = Iron_temp(pIron); 59 | self->used = ((temp > 400) && (temp < 740)); 60 | self->cool_notified = !self->used; 61 | if (self->used) { // the iron was used, we should save new data in EEPROM 62 | saveTemp(self->pCfg, temp_set); 63 | } 64 | } 65 | 66 | void rotaryValueScreenMain(PTR, int16_t value) { 67 | CAST_SELF; 68 | 69 | Display *pD = self->pD; 70 | Iron *pIron = self->pIron; 71 | 72 | self->show_set_temp = true; 73 | self->update_screen = millis() + 1000; 74 | setTempHumanUnits(pIron, value); 75 | number(pD, value, 0); 76 | tSet(pD); 77 | } 78 | 79 | void showScreenMain(PTR) { 80 | CAST_SELF; 81 | 82 | Display *pD = self->pD; 83 | Iron *pIron = self->pIron; 84 | 85 | if ((!self->parent.force_redraw) && (millis() < self->update_screen)) { 86 | return; 87 | } 88 | 89 | self->parent.force_redraw = false; 90 | self->update_screen = millis(); 91 | 92 | uint16_t temp = Iron_temp(pIron); 93 | if (temp > 720) { // No iron connected 94 | message(pD, self->msg_idle); 95 | noAnimation(pD); 96 | return; 97 | } 98 | 99 | if (self->show_set_temp) { 100 | self->update_screen += 7000; 101 | uint16_t temp = getTemp(pIron); 102 | temp = temp2humanUnits(pIron, temp); 103 | number(pD, temp, 0); 104 | tSet(pD); 105 | } else { 106 | self->update_screen += 10000; 107 | if (self->used && isCold(pIron)) { 108 | message(pD, self->msg_cool); 109 | if (!self->cool_notified) { 110 | shortBeep(self->pBz); 111 | self->cool_notified = true; 112 | } 113 | } else { 114 | message(pD, self->msg_off); 115 | if (self->used && !self->cool_notified) { 116 | cooling(pD); 117 | } 118 | } 119 | } 120 | byte hot = hotPercent(pIron); 121 | percent(pD, hot); 122 | self->show_set_temp = !self->show_set_temp; 123 | } 124 | -------------------------------------------------------------------------------- /samples/soldering/ScreenMain.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_MAIN_H 2 | #define SCREEN_MAIN_H 3 | 4 | #include "Screen.h" 5 | #include "Iron.h" 6 | #include "Display.h" 7 | #include "Encoder.h" 8 | #include "Buzzer.h" 9 | #include "Config.h" 10 | 11 | #define SELF ScreenMain *const self 12 | typedef struct ScreenMainStruct { 13 | Screen parent; 14 | 15 | Iron *pIron; // Pointer to the iron instance 16 | Display *pD; // Pointer to the display instance 17 | Encoder *pEnc; // Pointer to the rotary encoder instance 18 | Buzzer *pBz; // Pointer to the simple buzzer instance 19 | Config *pCfg; // Pointer to the configuration instance 20 | 21 | bool show_set_temp; // Weither show the temperature was set or 'OFF' string 22 | uint32_t update_screen; // Time in ms to switch information on the display 23 | bool used; // Weither the iron was used (was hot) 24 | bool cool_notified; // Weither there was cold notification played 25 | bool is_celsius; // The temperature units (Celsius or farenheit) 26 | char msg_off[4]; 27 | char msg_cool[4]; 28 | char msg_idle[4]; 29 | } ScreenMain; 30 | 31 | void createScreenMain(SELF, 32 | Iron *iron, Display *display, Encoder *encoder, Buzzer *buzzer, Config *config); 33 | 34 | void initScreenMain(void *const ptr); 35 | void showScreenMain(void *const ptr); 36 | void rotaryValueScreenMain(void *const ptr, int16_t value); 37 | 38 | 39 | #undef SELF 40 | #endif /* SCREEN_MAIN_H */ 41 | -------------------------------------------------------------------------------- /samples/soldering/ScreenPower.c: -------------------------------------------------------------------------------- 1 | #include "ScreenPower.h" 2 | #include "arduino.h" 3 | 4 | #define SELF ScreenPower *const self 5 | 6 | void createScreenPower(SELF, Iron* iron, Display* display, Encoder *encoder) { 7 | self->pIron = iron; 8 | self->pD = display; 9 | self->pEnc = encoder; 10 | self->on = false; 11 | 12 | self->max_power = 100; 13 | 14 | // overrides 15 | self->parent.init = initScreenPower; 16 | self->parent.showScreen = showScreenPower; 17 | self->parent.rotaryValue = rotaryValueScreenPower; 18 | //FIXME: fails for some reason 19 | //self->parent.menu = menuScreenPower; 20 | //self->parent.menu_long = menu_longScreenPower; 21 | } 22 | 23 | void initScreenPower(VOID_SELF) { 24 | CAST_SELF(ScreenPower); 25 | Iron *pIron = self->pIron; 26 | 27 | byte p = getAvgPower(pIron); 28 | initEncoderD(self->pEnc, p, 0, self->max_power); // defaults: false 29 | // Do start heating immediately 30 | self->on = true; 31 | switchPower(pIron, false); 32 | fixPower(pIron, p); 33 | initHistory(&self->hTemp); 34 | clear(self->pD); 35 | } 36 | 37 | void showScreenPower(VOID_SELF) { 38 | CAST_SELF(ScreenPower); 39 | Iron *pIron = self->pIron; 40 | Display *pD = self->pD; 41 | 42 | if ((!self->parent.force_redraw) && (millis() < self->update_screen)) return; 43 | 44 | self->parent.force_redraw = false; 45 | 46 | uint16_t temp = Iron_temp(pIron); 47 | put(&self->hTemp, temp); 48 | int avg = average(&self->hTemp); 49 | temp = temp2humanUnits(pIron, avg); 50 | number(pD, temp, 0); 51 | noAnimation(pD); 52 | byte p = appliedPower(pIron); 53 | percent(pD, p); 54 | self->update_screen = millis() + 500; 55 | 56 | } 57 | 58 | void rotaryValueScreenPower(VOID_SELF, int16_t value) { 59 | CAST_SELF(ScreenPower); 60 | Display *pD = self->pD; 61 | 62 | number(pD, value, 0); 63 | P(pD); 64 | byte p = appliedPower(self->pIron); 65 | percent(pD, p); 66 | if (self->on) { 67 | fixPower(self->pIron, value); 68 | } 69 | self->update_screen = millis() + 1000; 70 | } 71 | 72 | void* menuScreenPower(VOID_SELF) { 73 | CAST_SELF(ScreenPower); 74 | 75 | self->on = !self->on; 76 | 77 | if (self->on) { 78 | uint16_t pos = read(self->pEnc); 79 | self->on = fixPower(self->pIron, pos); 80 | } else { 81 | fixPower(self->pIron, 0); 82 | } 83 | return self; 84 | } 85 | 86 | void* menu_longScreenPower(VOID_SELF) { 87 | CAST_SELF(ScreenPower); 88 | 89 | self->on = !self->on; 90 | 91 | if (self->on) { 92 | uint16_t pos = read(self->pEnc); 93 | self->on = fixPower(self->pIron, pos); 94 | } else { 95 | fixPower(self->pIron, 0); 96 | } 97 | return self; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /samples/soldering/ScreenPower.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_POWER_H 2 | #define SCREEN_POWER_H 3 | 4 | #include "Screen.h" 5 | #include "Iron.h" 6 | #include "Encoder.h" 7 | #include "Display.h" 8 | #include "oo.h" 9 | 10 | #define SELF ScreenPower *const self 11 | typedef struct ScreenPowerStruct { 12 | Screen parent; 13 | Iron* pIron; // Pointer to the iron instance 14 | Display* pD; // Pointer to the display instance 15 | Encoder* pEnc; // Pointer to the rotary encoder instance 16 | uint32_t update_screen; // Time in ms to update the screen 17 | bool on; // Weither the power of soldering iron is on 18 | History hTemp; 19 | byte max_power; 20 | } ScreenPower; 21 | 22 | void createScreenPower(SELF, Iron* iron, Display* display, Encoder *encoder); 23 | void initScreenPower(VOID_SELF); 24 | void showScreenPower(VOID_SELF); 25 | void rotaryValueScreenPower(VOID_SELF, int16_t value); 26 | void *menuScreenPower(VOID_SELF); 27 | void *menu_longScreenPower(VOID_SELF); 28 | 29 | #undef SELF 30 | #endif /* SCREEN_POWER_H */ 31 | -------------------------------------------------------------------------------- /samples/soldering/ScreenTune.c: -------------------------------------------------------------------------------- 1 | #include "ScreenTune.h" 2 | #include "oo.h" 3 | 4 | // "private functions" 5 | void saveTempParam(ScreenTune *const self); 6 | 7 | void createScreenTune(ScreenTune *const self, 8 | Iron* iron, Display* display, Encoder* enc, Buzzer* buzz, Config* cfg) { 9 | self->update_screen = 0; 10 | self->pIron = iron; 11 | self->pD = display; 12 | self->pEnc = enc; 13 | self->pBz = buzz; 14 | self->pCfg = cfg; 15 | self->upper = true; 16 | 17 | self->max_power = 100; 18 | 19 | // overrides 20 | self->parent.init = initScreenTune; 21 | self->parent.showScreen = showScreenTune; 22 | self->parent.rotaryValue = rotaryValueScreenTune; 23 | self->parent.menu = menuScreenTune; 24 | self->parent.menu_long = menu_longScreenTune; 25 | } 26 | 27 | void saveTempParam(ScreenTune *const self) { 28 | uint16_t pos = read(self->pEnc); 29 | (void) pos; // unused (also in original)? 30 | uint16_t temp = Iron_temp(self->pIron); 31 | put(&self->hTemp, temp); 32 | temp = average(&self->hTemp); 33 | byte indx = 1; 34 | if (self->upper) { 35 | indx = 0; 36 | } 37 | self->tul[indx] = temp; 38 | } 39 | 40 | void initScreenTune(VOID_SELF) { 41 | CAST_SELF(ScreenTune); 42 | 43 | byte p = 75; 44 | initEncoder(self->pEnc, p, 0, self->max_power, 1, 0, false); //defaults 0, false 45 | fixPower(self->pIron, p); 46 | self->update_screen = millis(); 47 | self->upper = true; 48 | self->arm_beep = true; 49 | self->tul[0] = self->tul[1] = 0; 50 | initHistory(&self->hTemp); 51 | forceRedraw((Screen*) self); 52 | } 53 | 54 | void* menuScreenTune(VOID_SELF) { 55 | CAST_SELF(ScreenTune); 56 | Display *pD = self->pD; 57 | 58 | saveTempParam(self); 59 | 60 | uint16_t temp = average(&self->hTemp); 61 | number(pD, temp, 0); 62 | if (self->upper) { 63 | upper(pD); 64 | } else { 65 | lower(pD); 66 | } 67 | self->update_screen = millis() + 1000; 68 | self->upper = !self->upper; 69 | initHistory(&self->hTemp); 70 | self->arm_beep = true; 71 | self->parent.force_redraw = true; 72 | return self; 73 | } 74 | 75 | void* menu_longScreenTune(VOID_SELF) { 76 | CAST_SELF(ScreenTune); 77 | Iron *pIron = self->pIron; 78 | 79 | saveTempParam(self); 80 | fixPower(pIron, 0); 81 | bool all_data = true; 82 | 83 | for (byte i = 0; i < 2; ++i) { 84 | if (!self->tul[i]) { 85 | all_data = false; 86 | } 87 | } 88 | if (all_data) { // save calibration data 89 | saveCalibrationData(self->pCfg, self->tul[0], self->tul[1]); 90 | } 91 | 92 | if (self->parent.nextL) { 93 | return self->parent.nextL; 94 | } 95 | return self; 96 | } 97 | 98 | void rotaryValueScreenTune(VOID_SELF, int16_t value) { 99 | CAST_SELF(ScreenTune); 100 | Display *pD = self->pD; 101 | Iron *pIron = self->pIron; 102 | 103 | number(pD, value, 0); 104 | if (self->upper) { 105 | upper(pD); 106 | } else { 107 | lower(pD); 108 | } 109 | fixPower(pIron, value); 110 | self->update_screen = millis() + 1000; 111 | } 112 | 113 | void showScreenTune(VOID_SELF) { 114 | CAST_SELF(ScreenTune); 115 | Display *pD = self->pD; 116 | Iron *pIron = self->pIron; 117 | 118 | if ((!self->parent.force_redraw) && (millis() < self->update_screen)) { 119 | return; 120 | } 121 | 122 | self->parent.force_redraw = false; 123 | self->update_screen = millis() + 1000; 124 | 125 | int16_t temp = Iron_temp(pIron); 126 | put(&self->hTemp, temp); 127 | temp = average(&self->hTemp); 128 | number(pD, temp, 0); 129 | if (self->upper) { 130 | upper(pD); 131 | } else { 132 | lower(pD); 133 | } 134 | byte p = appliedPower(pIron); 135 | percent(pD, p); 136 | if (self->arm_beep && (dispersion(&self->hTemp) < 15)) { 137 | shortBeep(self->pBz); 138 | self->arm_beep = false; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /samples/soldering/ScreenTune.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_TUNE_H 2 | #define SCREEN_TUNE_H 3 | 4 | #include "Screen.h" 5 | #include "Iron.h" 6 | #include "Display.h" 7 | #include "Encoder.h" 8 | #include "Buzzer.h" 9 | #include "Config.h" 10 | #include "History.h" 11 | #include "oo.h" 12 | 13 | typedef struct ScreenTuneStruct { 14 | Screen parent; 15 | Iron* pIron; // Pointer to the iron instance 16 | Display* pD; // Pointer to the display instance 17 | Encoder* pEnc; // Pointer to the rotary encoder instance 18 | Buzzer* pBz; // Pointer to the simple Buzzer instance 19 | Config* pCfg; // Pointer to the configuration class 20 | bool upper; // Weither tune upper or lower temperature 21 | bool arm_beep; // Weither beep is armed 22 | uint32_t update_screen; // Time in ms to switch information on the display 23 | uint16_t tul[2]; // upper & lower temp 24 | History hTemp; 25 | byte max_power; // Maximum possible power to be applied 26 | } ScreenTune; 27 | 28 | void createScreenTune(ScreenTune *const self, 29 | Iron* iron, Display* display, Encoder* enc, Buzzer* buzz, Config* cfg); 30 | 31 | void initScreenTune(VOID_SELF); 32 | void* menuScreenTune(VOID_SELF); 33 | void* menu_longScreenTune(VOID_SELF); 34 | void showScreenTune(VOID_SELF); 35 | void rotaryValueScreenTune(VOID_SELF, int16_t value); 36 | 37 | #endif /* SCREEN_TUNE_H */ 38 | -------------------------------------------------------------------------------- /samples/soldering/ScreenWork.c: -------------------------------------------------------------------------------- 1 | #include "ScreenWork.h" 2 | #include "constants.h" 3 | #include "Screen.h" 4 | 5 | #include "serial.h" 6 | 7 | #define SELF ScreenWork *const self 8 | #define CAST_SELF ScreenWork *const self = (ScreenWork *const) void_self 9 | 10 | void createScreenWork(SELF, Iron *iron, Display *display, Encoder *encoder, Buzzer *buzzer) { 11 | self->update_screen = 0; 12 | self->pIron = iron; 13 | self->pD = display; 14 | self->pBz = buzzer; 15 | self->pEnc = encoder; 16 | self->heating_animation = false; 17 | self->ready = false; 18 | 19 | // Overrides 20 | self->parent.rotaryValue = rotaryValueScreenWork; 21 | self->parent.showScreen = showScreenWork; 22 | self->parent.init = initScreenWork; 23 | } 24 | 25 | 26 | void initScreenWork(VOID_SELF) { 27 | CAST_SELF; 28 | 29 | Iron *pIron = self->pIron; 30 | Encoder *pEnc = self->pEnc; 31 | 32 | uint16_t temp_set = getTemp(pIron); 33 | bool is_celsius = getTempUnits(pIron); 34 | uint16_t tempH = temp2humanUnits(pIron, temp_set); 35 | 36 | if (is_celsius) { 37 | initEncoder(pEnc, tempH, temp_minC, temp_maxC, 1, 5, false); // Added default false 38 | } 39 | else { 40 | initEncoder(pEnc, tempH, temp_minF, temp_maxF, 1, 5, false); // Added default false 41 | } 42 | switchPower(pIron, true); 43 | self->heating_animation = false; 44 | self->ready = false; 45 | 46 | initHistory(&self->hTemp); 47 | clear(self->pD); 48 | forceRedraw(void_self); 49 | } 50 | 51 | void rotaryValueScreenWork(VOID_SELF, int16_t value) { 52 | CAST_SELF; 53 | 54 | self->heating_animation = false; 55 | self->ready = false; 56 | self->update_screen = millis() + 2000; 57 | setTempHumanUnits(self->pIron, value); 58 | number(self->pD, value, 0); 59 | tSet(self->pD); 60 | } 61 | 62 | void showScreenWork(VOID_SELF) { 63 | CAST_SELF; 64 | Iron *pIron = self->pIron; 65 | Display *pD = self->pD; 66 | 67 | if ((!self->parent.force_redraw) && (millis() < self->update_screen)) { 68 | return; 69 | } 70 | 71 | self->parent.force_redraw = false; 72 | self->update_screen = millis() + 1000; 73 | 74 | uint16_t temp = lastTemp(pIron); 75 | uint16_t temp_set = getTemp(pIron); 76 | put(&self->hTemp, temp); 77 | int avg = average(&self->hTemp); 78 | 79 | temp = temp2humanUnits(pIron, avg); 80 | number(pD, temp, 0); 81 | byte p = appliedPower(pIron); 82 | percent(pD, p); 83 | 84 | if ((abs(temp_set - avg) < 4) && (dispersion(&self->hTemp) < 15)) { 85 | noAnimation(pD); 86 | self->heating_animation = false; 87 | if (!self->ready) { 88 | shortBeep(self->pBz); 89 | self->ready = true; 90 | } 91 | return; 92 | } 93 | if (!self->ready && temp < temp_set) { 94 | if (!self->heating_animation) { 95 | self->heating_animation = true; 96 | heating(pD); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /samples/soldering/ScreenWork.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_WORK_H 2 | #define SCREEN_WORK_H 3 | 4 | #include "types.h" 5 | #include "Screen.h" 6 | #include "Iron.h" 7 | #include "Display.h" 8 | #include "Buzzer.h" 9 | #include "Encoder.h" 10 | #include "History.h" 11 | 12 | #define VOID_SELF void *const void_self 13 | #define SELF ScreenWork *const self 14 | typedef struct ScreenWorkStruct { 15 | Screen parent; 16 | Iron* pIron; // Pointer to the iron instance 17 | Display* pD; // Pointer to the display instance 18 | Buzzer* pBz; // Pointer to the simple Buzzer instance 19 | Encoder* pEnc; // Pointer to the rotary encoder instance 20 | History hTemp; 21 | bool heating_animation; // Weither the heating animation is ON 22 | uint32_t update_screen; // Time in ms to update the screen 23 | bool ready; // Weither the iron is ready 24 | } ScreenWork; 25 | 26 | void createScreenWork(SELF, Iron *iron, Display *display, Encoder *encoder, Buzzer *buzzer); 27 | 28 | void initScreenWork(VOID_SELF); 29 | void showScreenWork(VOID_SELF); 30 | void rotaryValueScreenWork(VOID_SELF, int16_t value); 31 | 32 | #undef SELF 33 | #endif /* SCREEN_WORK_H */ 34 | -------------------------------------------------------------------------------- /samples/soldering/arduino.c: -------------------------------------------------------------------------------- 1 | #include "arduino.h" 2 | #include "util.h" 3 | #include "serial.h" 4 | #include "constants.h" 5 | 6 | #include 7 | 8 | // This is just a guess, and is probably highly inaccurate for timing (used at least in delay) 9 | // 10 | // The soldering samples sources indicate RPi timing functions would help, if these become available 11 | // one coudle use those for uptime and delay and remove updateTime and related globals. 12 | // 13 | #define LOOPS_PER_MS 150 14 | #define MS_PER_TICK 1 15 | 16 | // NORMAL_TEMPERATURE should probably be kept above Iron.temp_min (which seems to be 417?) 17 | #define NORMAL_TEMPERATURE 256 18 | #define MAX_TEMP 700 19 | #define ANALOG_MAX 1023 20 | 21 | long uptime = 0; 22 | 23 | byte R_BUTN_PIN_value = LOW; 24 | byte R_MAIN_PIN_value = LOW; 25 | byte R_SECD_PIN_value = LOW; 26 | 27 | void (*R_BUTN_PIN_callback)(void); 28 | void (*R_MAIN_PIN_callback)(void); 29 | 30 | int PROBEPIN_value = NORMAL_TEMPERATURE; 31 | int PROBEPIN_old = NORMAL_TEMPERATURE; 32 | int HEATERPIN_value = LOW; 33 | 34 | int tempCounter = 0; 35 | 36 | int16_t oldRotVal = 0; 37 | 38 | char stateDisplay[4] = { ' ', ' ', ' ', ' ' }; 39 | byte stateButtonMode = 0; 40 | 41 | void handleInput(void); 42 | void updateTime(void); 43 | void updateTemperature(void); 44 | 45 | byte R_MAIN_PIN_old = LOW; 46 | byte R_SECD_PIN_old = LOW; 47 | 48 | bool emulate_State(LedControl *const disp, byte buttonMode, Encoder *const enc, Iron *iron) { 49 | bool changed = false; 50 | 51 | for (int i = 0; i < 4; i++) { 52 | char d = disp->chars[i]; 53 | if (stateDisplay[i] != d) { 54 | stateDisplay[i] = d; 55 | changed = true; 56 | } 57 | } 58 | 59 | if (stateButtonMode != buttonMode) { 60 | stateButtonMode = buttonMode; 61 | changed = true; 62 | } 63 | 64 | if (R_MAIN_PIN_value != R_MAIN_PIN_old) { 65 | changed = true; 66 | R_MAIN_PIN_old = R_MAIN_PIN_value; 67 | } 68 | 69 | if (R_SECD_PIN_value != R_SECD_PIN_old) { 70 | changed = true; 71 | R_SECD_PIN_old = R_SECD_PIN_value; 72 | } 73 | 74 | if (oldRotVal != read(enc)) { 75 | oldRotVal = read(enc); 76 | changed = true; 77 | } 78 | 79 | if (PROBEPIN_old != PROBEPIN_value) { 80 | PROBEPIN_old = PROBEPIN_value; 81 | changed = true; 82 | } 83 | 84 | 85 | if (changed) { 86 | emulate_showLed(disp); 87 | 88 | serialWriteString(" ("); 89 | 90 | serialWriteString("btn mode: "); 91 | char c = '0' + buttonMode; 92 | serialWrite(&c, 1); 93 | 94 | serialWriteString(", rot: "); 95 | serialWriteInt(enc->pos); 96 | serialWriteString(" ["); 97 | serialWriteInt(enc->min_pos); 98 | serialWriteString(", "); 99 | serialWriteInt(enc->max_pos); 100 | serialWriteString("]"); 101 | 102 | serialWriteString(", iron: "); 103 | serialWriteInt(PROBEPIN_value); 104 | serialWriteString(" ->-> "); 105 | serialWriteInt(iron->temp_set); 106 | 107 | serialWriteString(")"); 108 | } 109 | 110 | return changed; 111 | } 112 | 113 | 114 | 115 | void emulate_tick() { 116 | updateTime(); 117 | updateTemperature(); 118 | handleInput(); 119 | } 120 | 121 | void updateTemperature() { 122 | if (tempCounter >= 400) { 123 | tempCounter = 0; 124 | if (HEATERPIN_value && PROBEPIN_value < ANALOG_MAX) { 125 | int target = map(HEATERPIN_value, 0, 255, NORMAL_TEMPERATURE, MAX_TEMP); 126 | int diff = target - PROBEPIN_value; 127 | PROBEPIN_value += (diff < 0 ? -1 : (diff < 10 ? 1 : diff / 10)); 128 | 129 | /* 130 | serialWriteString("HEATERPIN_value is "); 131 | serialWriteInt(HEATERPIN_value); 132 | serialWriteNewLine(); 133 | serialWriteString("Target is "); 134 | serialWriteInt(target); 135 | serialWriteNewLine(); 136 | serialWriteString("PROBEPIN_value is "); 137 | serialWriteInt(PROBEPIN_value); 138 | serialWriteNewLine(); 139 | */ 140 | } else if (PROBEPIN_value > NORMAL_TEMPERATURE) { 141 | PROBEPIN_value -= 2; 142 | if (PROBEPIN_value < NORMAL_TEMPERATURE) PROBEPIN_value = NORMAL_TEMPERATURE; 143 | } 144 | } 145 | tempCounter++; 146 | } 147 | 148 | long previousTimeSend = 0; 149 | void updateTime(void) { 150 | uptime += MS_PER_TICK; 151 | /* 152 | if (uptime - previousTimeSend > 1000) { 153 | serialWriteString("one second\r\n"); 154 | previousTimeSend = uptime; 155 | } 156 | */ 157 | } 158 | 159 | 160 | void handleInput(void) { 161 | while (serialHasData()) { 162 | int data = serialRead(); 163 | char c = (char) data; 164 | switch(c) { 165 | case('j'): 166 | case('k'): 167 | { 168 | byte newValue = (c == 'j' ? LOW : HIGH); 169 | if (R_BUTN_PIN_value != newValue) { 170 | // notify callback if changed 171 | R_BUTN_PIN_callback(); 172 | } 173 | R_BUTN_PIN_value = newValue; // button pressed => LOW 174 | break; 175 | } 176 | case('h'): 177 | case('l'): 178 | { 179 | if (R_MAIN_PIN_value == HIGH) { 180 | byte value = (c == 'h' ? LOW : HIGH); 181 | R_MAIN_PIN_value = LOW; 182 | R_SECD_PIN_value = value; 183 | } else { 184 | R_MAIN_PIN_value = HIGH; 185 | } 186 | R_MAIN_PIN_callback(); 187 | } 188 | break; 189 | default: 190 | serialWriteString("Got unrecognized key: "); 191 | serialWrite(&c, 1); 192 | serialWriteNewLine(); 193 | } 194 | } 195 | } 196 | 197 | 198 | unsigned long millis(void) { 199 | return uptime; 200 | } 201 | 202 | void delay(int ms) { 203 | while(1) { 204 | for(int i = 0; i < LOOPS_PER_MS; i++) { /* Busy wait */ } 205 | ms--; 206 | if (ms <= 0) { 207 | return; 208 | } 209 | } 210 | uptime += ms; 211 | } 212 | 213 | int abs(int value) { 214 | if (value >= 0) { 215 | return value; 216 | } 217 | return -1 * value; 218 | } 219 | 220 | void tone(byte buzzerPIN, int frequency, int duraion) { 221 | UNIMPLEMENTED("arduino : tone"); 222 | // TODO 223 | (void) buzzerPIN; 224 | (void) frequency; 225 | (void) duraion; 226 | } 227 | 228 | 229 | void pinMode(byte pin, byte mode) { 230 | //UNIMPLEMENTED("arduino : pinMode"); 231 | (void) pin; 232 | (void) mode; 233 | } 234 | 235 | byte digitalRead(byte pin) { 236 | switch(pin) { 237 | case R_BUTN_PIN: 238 | return R_BUTN_PIN_value; 239 | case R_MAIN_PIN: 240 | return R_MAIN_PIN_value; 241 | case R_SECD_PIN: 242 | return R_SECD_PIN_value; 243 | default: 244 | serialWriteString("digitalRead, unimplemented pin: "); 245 | char c = '0' + pin; 246 | serialWrite(&c, 1); 247 | serialWriteNewLine(); 248 | return LOW; 249 | } 250 | } 251 | 252 | void digitalWrite(byte pin, byte value) { 253 | switch(pin) { 254 | case HEATERPIN: 255 | HEATERPIN_value = value; 256 | break; 257 | default: 258 | serialWriteString("digitalWrite, unimplemented pin: "); 259 | char c = '0' + pin; 260 | serialWrite(&c, 1); 261 | serialWriteNewLine(); 262 | } 263 | } 264 | 265 | int analogRead(byte pin) { 266 | switch(pin) { 267 | case HEATERPIN: 268 | return HEATERPIN_value; 269 | break; 270 | case PROBEPIN: 271 | return PROBEPIN_value; 272 | break; 273 | default: 274 | serialWriteString("analogRead, unimplemented pin: "); 275 | char c = '0' + pin; 276 | serialWrite(&c, 1); 277 | serialWriteNewLine(); 278 | return 0; 279 | } 280 | } 281 | 282 | void analogWrite(byte pin, byte value) { 283 | switch(pin) { 284 | case HEATERPIN: 285 | HEATERPIN_value = value; 286 | break; 287 | default: 288 | serialWriteString("analogWrite, unimplemented pin: "); 289 | char c = '0' + pin; 290 | serialWrite(&c, 1); 291 | serialWriteNewLine(); 292 | } 293 | } 294 | 295 | int map(int value, int fromLow, int fromHigh, int toLow, int toHigh) { 296 | /* 297 | if (value < fromLow || value > fromHigh) { 298 | // DEBUG 299 | serialWriteString("Mapping value not within range"); 300 | serialWriteInt(value); 301 | serialWriteString(" -> ["); 302 | serialWriteInt(fromLow); 303 | serialWriteString(", "); 304 | serialWriteInt(fromHigh); 305 | serialWriteString("]\r\n"); 306 | } 307 | */ 308 | int fromRange = fromHigh - fromLow; 309 | int fromDiff = fromHigh - value; 310 | float diff = (float)fromRange / (float)fromDiff; 311 | int toRange = toHigh - toLow; 312 | return toHigh - (toRange / diff); 313 | } 314 | 315 | void attachInterrupt(byte pin, void (*ISR)(void), byte mode) { 316 | (void) mode; 317 | switch(pin) { 318 | case R_BUTN_PIN: 319 | R_BUTN_PIN_callback = ISR; 320 | break; 321 | case R_MAIN_PIN: 322 | R_MAIN_PIN_callback = ISR; 323 | break; 324 | default: 325 | serialWriteString("digitalRead, unimplemented pin: "); 326 | char c = '0' + pin; 327 | serialWrite(&c, 1); 328 | serialWriteNewLine(); 329 | } 330 | } 331 | 332 | byte digitalPinToInterrupt(byte pin) { 333 | // This is used only twice in solderingIron.c 334 | //attachInterrupt(digitalPinToInterrupt(R_MAIN_PIN), rotEncChange, CHANGE); 335 | //attachInterrupt(digitalPinToInterrupt(R_BUTN_PIN), rotPushChange, CHANGE); 336 | return pin; 337 | } 338 | -------------------------------------------------------------------------------- /samples/soldering/arduino.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Some, what seems to me to be, Arduino specifics API functions 3 | * 4 | * Constnats as needed from here (filled with bogus values): 5 | * https://www.arduino.cc/en/Reference/Constants 6 | * 7 | */ 8 | #ifndef ARDUINO_H 9 | #define ARDUINO_H 10 | 11 | #include "types.h" 12 | #include "LedControl.h" 13 | #include "Encoder.h" 14 | #include "Iron.h" 15 | 16 | // Digital pin modes 17 | #define INPUT_PULLUP (byte)1 18 | #define INPUT (byte)2 19 | #define OUTPUT (byte)3 20 | 21 | // Interrupt modes, see https://www.arduino.cc/en/Reference/AttachInterrupt 22 | #define CHANGE 1 23 | 24 | // Pin Levels (at least soldering implementation relies on LOW == 0 25 | #define LOW (byte)0 26 | #define HIGH (byte)1 27 | 28 | // Pin Mappings 29 | // https://www.arduino.cc/en/Tutorial/AnalogInputPins 30 | #define A0 (byte)67 31 | 32 | 33 | /** 34 | * This needs to be run once per progrma loop to advance time 35 | */ 36 | void emulate_tick(void); 37 | bool emulate_State(LedControl *const disp, byte buttonMode, Encoder *const enc, Iron *iron); 38 | 39 | unsigned long millis(void); 40 | void delay(int ms); 41 | 42 | int abs(int value); 43 | 44 | void tone(byte buzzerPIN, int frequency, int duraion); 45 | void pinMode(byte pin, byte mode); 46 | byte digitalRead(byte pin); 47 | 48 | 49 | /** https://www.arduino.cc/en/Reference/DigitalWrite 50 | * 51 | * Parameters: 52 | * 53 | * pin: the pin number 54 | * 55 | * value: HIGH or LOW 56 | * 57 | * Returns: void 58 | */ 59 | void digitalWrite(byte pin, byte value); 60 | 61 | /** https://www.arduino.cc/en/Reference/AnalogRead 62 | * 63 | * Parameters: 64 | * 65 | * pin: the number of the analog input pin to read from (0 to 5 on most boards, 0 to 7 on the Mini 66 | * and Nano, 0 to 15 on the Mega) 67 | * 68 | * Returns: int (0 to 1023) 69 | */ 70 | int analogRead(byte pin); 71 | 72 | 73 | /** https://www.arduino.cc/en/Reference/AnalogWrite 74 | * 75 | * Parameters: 76 | * 77 | * pin: the pin to write to. 78 | * value: the duty cycle: between 0 (always off) and 255 (always on). 79 | * 80 | * Returns: nothing 81 | */ 82 | void analogWrite(byte pin, byte value); 83 | 84 | /** https://www.arduino.cc/en/Reference/Map 85 | * 86 | * Re-maps a number from one range to another. That is, a value of fromLow would get mapped to 87 | * toLow, a value of fromHigh to toHigh, values in-between to values in-between, etc. 88 | */ 89 | int map(int value, int fromLow, int fromHigh, int toLow, int toHigh); 90 | 91 | /** https://www.arduino.cc/en/Reference/AttachInterrupt 92 | * 93 | * attachInterrupt(digitalPinToInterrupt(pin), ISR, mode); 94 | */ 95 | void attachInterrupt(byte pin, void (*ISR)(void), byte mode); 96 | 97 | byte digitalPinToInterrupt(byte pin); 98 | 99 | 100 | #endif /* ARDUINO_H */ 101 | -------------------------------------------------------------------------------- /samples/soldering/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #include "types.h" 5 | #include "arduino.h" 6 | 7 | // max7219 interface 8 | #define M_DIN 12 9 | #define M_CLK 10 10 | #define M_CS 11 11 | // display intensity (overwitten by brhghtness configuration parameter) 12 | #define M_INTENSITY 3 13 | 14 | // Rotary Encoder main pin (right) 15 | #define R_MAIN_PIN 2 16 | // Rotary Encoder second pin (left) 17 | #define R_SECD_PIN 4 18 | #define R_BUTN_PIN 3 19 | // Rotary Encoder push button pin 20 | 21 | // Thermometer pin from soldering iron 22 | #define PROBEPIN A0 23 | // soldering iron heater pin 24 | #define HEATERPIN 5 25 | // simple buzzer to make a noise 26 | #define BUZZERPIN 6 27 | 28 | #define TEMP_MIN 180 29 | #define TEMP_MAX 400 30 | 31 | // Minimum temperature in degrees of celsius 32 | #define temp_minC TEMP_MIN 33 | // Maximum temperature in degrees of celsius 34 | #define temp_maxC TEMP_MAX 35 | #define temp_minF (TEMP_MIN * 9 + 32*5 + 2)/5 36 | #define temp_maxF (TEMP_MAX * 9 + 32*5 + 2)/5 37 | 38 | #endif /* CONSTANTS_H */ 39 | -------------------------------------------------------------------------------- /samples/soldering/eeprom.c: -------------------------------------------------------------------------------- 1 | #include "eeprom.h" 2 | 3 | // This proably wouldn't need to be as much? 4 | #define MEM_SIZE 100 5 | byte EEPROM_memory[MEM_SIZE]; 6 | 7 | uint16_t EEPROM_length(void) { 8 | return MEM_SIZE; 9 | } 10 | 11 | void EEPROM_write(const byte addr, const byte data) { 12 | EEPROM_memory[addr] = data; 13 | } 14 | 15 | byte EEPROM_read(const byte addr) { 16 | return EEPROM_memory[addr]; 17 | } 18 | -------------------------------------------------------------------------------- /samples/soldering/eeprom.h: -------------------------------------------------------------------------------- 1 | #ifndef EEPROM_H 2 | #define EEPROM_H 3 | 4 | #include "types.h" 5 | 6 | uint16_t EEPROM_length(void); 7 | void EEPROM_write(const byte addr, const byte data); 8 | byte EEPROM_read(const byte addr); 9 | 10 | #endif /* EEPROM_H */ 11 | -------------------------------------------------------------------------------- /samples/soldering/hooks.ld: -------------------------------------------------------------------------------- 1 | /* Hook trampolines */ 2 | . = ALIGN(4); 3 | __hook_start = .; 4 | .hook : { *(.hook) } 5 | __hook_end = .; 6 | 7 | /* Hook branch table */ 8 | . = ALIGN(4); 9 | __btbl_start = .; 10 | .btbl : { *(.btbl) } 11 | __btbl_end = .; 12 | 13 | /* Loop entr/exit table */ 14 | . = ALIGN(4); 15 | __ltbl_start = .; 16 | .ltbl : { *(.ltbl) } 17 | __ltbl_end = .; 18 | -------------------------------------------------------------------------------- /samples/soldering/oo.h: -------------------------------------------------------------------------------- 1 | #define VOID_SELF void *const void_self 2 | #define CAST_SELF(type) type *const self = (type *const) void_self 3 | -------------------------------------------------------------------------------- /samples/soldering/serial.c: -------------------------------------------------------------------------------- 1 | #include "serial.h" 2 | #include "rpi/aux.h" 3 | 4 | void serialWrite(char *const output, const int len) { 5 | for (int i = 0; i < len; i++) { 6 | rpi_aux_mini_uart_write(output[i]); 7 | } 8 | } 9 | 10 | void serialWriteString(char *const output) { 11 | int i = 0; 12 | while(1) { 13 | if (output[i] == '\0') { 14 | return; 15 | } 16 | rpi_aux_mini_uart_write(output[i++]); 17 | 18 | if (i > 100) { 19 | serialWriteString("String too long, stopping output"); 20 | return; 21 | } 22 | } 23 | } 24 | 25 | void serialWriteInt(int value) { 26 | // FIXME: make sure this actually works 27 | int i = 0; 28 | char str[20]; 29 | 30 | if (value == 0) { 31 | char c = '0'; 32 | serialWrite(&c, 1); 33 | return; 34 | } 35 | 36 | for (; value > 0; i++) { 37 | str[i] = '0' + (value%10); 38 | value = value / 10; 39 | } 40 | // serialWriteString(""); 41 | i--; 42 | for (; i >= 0; i--) { 43 | serialWrite(&str[i], 1); 44 | } 45 | // serialWriteString(""); 46 | } 47 | 48 | void serialWriteNewLine(void) { 49 | rpi_aux_mini_uart_write('\n'); 50 | rpi_aux_mini_uart_write('\r'); 51 | } 52 | 53 | int serialRead(void) { 54 | return rpi_aux_mini_uart_read(); 55 | } 56 | 57 | bool serialHasData(void) { 58 | return rpi_aux_mini_uart_data_available(); 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /samples/soldering/serial.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_H 2 | #define SERIAL_H 3 | 4 | #include "types.h" 5 | 6 | void serialWrite(char *const output, const int len); 7 | /** 8 | * NOTE: must be '\0' terminated! 9 | */ 10 | void serialWriteString(char *const output); 11 | void serialWriteNewLine(void); 12 | void serialWriteInt(int value); 13 | bool serialHasData(void); 14 | int serialRead(void); 15 | 16 | 17 | #endif /* SERIAL_H */ 18 | 19 | -------------------------------------------------------------------------------- /samples/soldering/solderingIron.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Aalto University 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "kernel/init.h" 17 | #include "kernel/led.h" 18 | #include "kernel/log.h" 19 | #include "kernel/misc.h" 20 | #include "kernel/perf.h" 21 | #include "sm/smcall.h" 22 | #include "sm/sm.h" 23 | #include "arm.h" 24 | 25 | #include "constants.h" 26 | #include "serial.h" 27 | #include "arduino.h" 28 | 29 | #include "Iron.h" 30 | #include "Display.h" 31 | #include "Button.h" 32 | #include "Buzzer.h" 33 | #include "Config.h" 34 | #include "Encoder.h" 35 | #include "Screen.h" 36 | #include "ScreenMain.h" 37 | #include "ScreenWork.h" 38 | #include "ScreenError.h" 39 | #include "ScreenPower.h" 40 | #include "ScreenConfig.h" 41 | #include "ScreenTune.h" 42 | 43 | #include "lib/cfa_stub.h" 44 | 45 | void createGlobals(void); 46 | void setup(void); 47 | void loop(void); 48 | void rotEncChange(void); 49 | void rotPushChange(void); 50 | 51 | // These are additions to debug the Pi stuff 52 | void debug_printScreenType(void *const p); 53 | void debug_printScreenSwitch(char *const string, void *const from, void *const to); 54 | 55 | static uint8_t user_data[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; 56 | static uint8_t quote_out[128]; 57 | static uint32_t quote_len; 58 | 59 | extern btbl_entry_t __btbl_start; 60 | extern btbl_entry_t __btbl_end; 61 | extern ltbl_entry_t __ltbl_start; 62 | extern ltbl_entry_t __ltbl_end; 63 | 64 | void main(unsigned int r0, unsigned int r1, unsigned int atags) 65 | { 66 | (void)r0; 67 | (void)r1; 68 | (void)atags; 69 | 70 | // run global "constructors" 71 | createGlobals(); 72 | 73 | setup(); 74 | while(1) { 75 | loop(); 76 | } 77 | } 78 | 79 | //=================================== End of class declarations ================================================ 80 | 81 | Display disp; 82 | Encoder rotEncoder; 83 | Button rotButton; 84 | Iron iron; 85 | 86 | Config ironCfg; 87 | Buzzer simpleBuzzer; 88 | 89 | ScreenMain offScr; 90 | ScreenWork wrkScr; 91 | ScreenError errScr; 92 | ScreenPower powerScr; 93 | ScreenConfig cfgScr; 94 | ScreenTune tuneScr; 95 | 96 | void *pCurrentScreen = &offScr; 97 | 98 | void createGlobals(void) { 99 | createDisplay(&disp, M_DIN, M_CLK, M_CS, M_INTENSITY); 100 | createEncoderD(&rotEncoder, R_MAIN_PIN, R_SECD_PIN); 101 | createButtonD(&rotButton, R_BUTN_PIN); 102 | createIron(&iron, HEATERPIN, PROBEPIN); 103 | 104 | createConfig(&ironCfg); 105 | createBuzzer(&simpleBuzzer, BUZZERPIN); 106 | 107 | createScreenMain(&offScr, &iron, &disp, &rotEncoder, &simpleBuzzer, &ironCfg); 108 | createScreenWork(&wrkScr, &iron, &disp, &rotEncoder, &simpleBuzzer); 109 | createScreenError(&errScr, &disp); 110 | createScreenPower(&powerScr, &iron, &disp, &rotEncoder); 111 | createScreenConfig(&cfgScr, &iron, &disp, &rotEncoder, &ironCfg); 112 | createScreenTune(&tuneScr, &iron, &disp, &rotEncoder, &simpleBuzzer, &ironCfg); 113 | } 114 | 115 | // the setup routine runs once when you press reset: 116 | void setup() { 117 | initDisplay(&disp); 118 | clear(&disp); 119 | 120 | // Load configuration parameters 121 | initConfig(&ironCfg); 122 | bool is_cfg_valid = load(&ironCfg); 123 | 124 | uint16_t temp_min, temp_max; 125 | getCalibrationData(&ironCfg, &temp_max, &temp_min); 126 | 127 | if (is_cfg_valid) { 128 | byte dBright = getBrightness(&ironCfg); 129 | brightness(&disp, dBright); 130 | } 131 | 132 | initIron(&iron, temp_max, temp_min); 133 | uint16_t temp = Config_temp(&ironCfg); 134 | setTemp(&iron, temp); 135 | 136 | // Initialize rotary encoder 137 | initButton(&rotButton); 138 | delay(500); 139 | attachInterrupt(digitalPinToInterrupt(R_MAIN_PIN), rotEncChange, CHANGE); 140 | attachInterrupt(digitalPinToInterrupt(R_BUTN_PIN), rotPushChange, CHANGE); 141 | 142 | // Initialize SCREEN hierarchy 143 | offScr.parent.next = (void*) &wrkScr; 144 | offScr.parent.nextL = (void*) &cfgScr; 145 | wrkScr.parent.next = (void*) &offScr; 146 | wrkScr.parent.nextL = (void*) &powerScr; 147 | errScr.parent.next = (void*) &offScr; 148 | errScr.parent.nextL = (void*) &offScr; 149 | powerScr.parent.nextL = (void*) &wrkScr; 150 | cfgScr.parent.next = (void*) &tuneScr; 151 | cfgScr.parent.nextL = (void*) &offScr; 152 | cfgScr.parent.main = (void*) &offScr; 153 | tuneScr.parent.nextL = (void*) &cfgScr; 154 | 155 | initScreen(pCurrentScreen); 156 | } 157 | 158 | void rotEncChange(void) { 159 | //serialWriteString("rotEncChange triggered\n\r"); 160 | changeEncoderINTR(&rotEncoder); 161 | } 162 | 163 | void rotPushChange(void) { 164 | //serialWriteString("rotPushChange triggered\n\r"); 165 | changeButtonINTR(&rotButton); 166 | } 167 | 168 | // The main loop 169 | void loop() { 170 | int16_t old_pos = read(&rotEncoder); 171 | bool iron_on = isOn(&iron); 172 | 173 | if ((pCurrentScreen == &wrkScr) && !iron_on) { // the soldering iron failed 174 | serialWriteString("failure\r\n"); 175 | debug_printScreenSwitch("failure", pCurrentScreen, (void*)&errScr); 176 | pCurrentScreen = &errScr; 177 | initScreen(pCurrentScreen); 178 | } 179 | 180 | void* nxt = returnToMain(pCurrentScreen); 181 | if (nxt != pCurrentScreen) { // return to the main screen by timeout 182 | serialWriteString("timeout return\r\n"); 183 | pCurrentScreen = nxt; 184 | initScreen(pCurrentScreen); 185 | } 186 | 187 | byte bStatus = intButtonStatus(&rotButton); 188 | 189 | switch (bStatus) { 190 | case 2: // long press; 191 | nxt = menu_long(pCurrentScreen); 192 | 193 | if (nxt != pCurrentScreen) { 194 | debug_printScreenSwitch("long press", pCurrentScreen, nxt); 195 | pCurrentScreen = nxt; 196 | initScreen(pCurrentScreen); 197 | } else { 198 | if (isSetup(pCurrentScreen)) { 199 | resetTimeout(pCurrentScreen); 200 | } 201 | } 202 | break; 203 | case 1: // short press 204 | nxt = menu(pCurrentScreen); 205 | if (nxt != pCurrentScreen) { 206 | debug_printScreenSwitch("short press", pCurrentScreen, nxt); 207 | pCurrentScreen = nxt; 208 | initScreen(pCurrentScreen); 209 | } else { 210 | if (isSetup(pCurrentScreen)) { 211 | resetTimeout(pCurrentScreen); 212 | } 213 | } 214 | break; 215 | case 0: // Not pressed 216 | default: 217 | break; 218 | } 219 | 220 | // PORT: Emulation tick must be done here to ensure rotEncoder changes between pos and oldPos 221 | // reads! 222 | emulate_tick(); 223 | 224 | int16_t pos = read(&rotEncoder); 225 | if (old_pos != pos) { 226 | rotaryValue(pCurrentScreen, pos); 227 | old_pos = pos; 228 | if (isSetup(pCurrentScreen)) { 229 | resetTimeout(pCurrentScreen); 230 | } 231 | } 232 | 233 | showScreen(pCurrentScreen); 234 | 235 | 236 | cfa_init((cfa_addr_t)&loop, (cfa_addr_t)0, 237 | (btbl_entry_t*)&__btbl_start, (btbl_entry_t*)&__btbl_end, 238 | (ltbl_entry_t*)&__ltbl_start, (ltbl_entry_t*)&__ltbl_end); 239 | 240 | perf_cc_reset(); 241 | perf_cc_enable(); 242 | 243 | keepTemp(&iron); 244 | 245 | perf_cc_disable(); 246 | 247 | if (perf_cc_overflow()) { 248 | info("invalid measurement, cycle counter overflowed"); 249 | } else { 250 | info("cycle count: %u", perf_cc_read()); 251 | } 252 | 253 | quote_len = sizeof(quote_out); 254 | cfa_quote(user_data, sizeof(user_data), quote_out, "e_len); 255 | 256 | Display_show(&disp); 257 | delay(10); 258 | 259 | // PORT: Advance and display all the emulated stuff here 260 | if (emulate_State(&disp.parent, bStatus, &rotEncoder, &iron)) { 261 | serialWriteString(" (cur scr: "); 262 | debug_printScreenType(pCurrentScreen); 263 | serialWriteString(")"); 264 | serialWriteNewLine(); 265 | } 266 | } 267 | 268 | void debug_printScreenSwitch(char *const string, void *const from, void *const to) { 269 | serialWriteString("Switching: "); 270 | debug_printScreenType(from); 271 | serialWriteString("-->"); 272 | serialWriteString(string); 273 | serialWriteString("-->"); 274 | debug_printScreenType(to); 275 | serialWriteNewLine(); 276 | } 277 | 278 | void debug_printScreenType(void *const p) { 279 | if (p == (void*)&offScr) { 280 | serialWriteString("ScreenMain"); 281 | } else if (p == (void*)&wrkScr) { 282 | serialWriteString("ScreenWork"); 283 | } else if (p == (void*)&errScr) { 284 | serialWriteString("ScreenErr"); 285 | } else if (p == (void*)&powerScr) { 286 | serialWriteString("ScreenPower"); 287 | } else if (p == (void*)&cfgScr) { 288 | serialWriteString("ScreenConfig"); 289 | } else if (p == (void*)&tuneScr) { 290 | serialWriteString("ScreenTune"); 291 | } else { 292 | serialWriteString("Unknown"); 293 | } 294 | } 295 | 296 | -------------------------------------------------------------------------------- /samples/soldering/types.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_H 2 | #define TYPES_H 3 | 4 | #include 5 | #include 6 | 7 | #define INT_MAX 2147483647 8 | #define BYTE_MAX 255 9 | 10 | typedef unsigned char byte; 11 | typedef int boolean; 12 | 13 | /* 14 | typedef unsigned short uint16_t; 15 | typedef unsigned int uint32_t; 16 | 17 | typedef short int16_t; 18 | typedef int int32_t; 19 | */ 20 | 21 | #define FALSE 0 22 | #define TRUE 1 23 | 24 | #endif /* TYPES_H */ 25 | -------------------------------------------------------------------------------- /samples/soldering/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | char hexDigitToChar(const byte input) { 4 | return input + '0'; 5 | } 6 | 7 | /** 8 | * "Stolen" from the syringe sample. 9 | */ 10 | int toUInt(char* input, int len) { 11 | unsigned int output = 0; 12 | unsigned int overflow = 0; 13 | int i; 14 | 15 | for(i=0; i < len; i++) 16 | { 17 | if(0x30 <= input[i] && input[i] <= 0x39) 18 | break; 19 | } 20 | 21 | for( ; i < len; i++){ 22 | if(input[i] < 0x30 || 0x39 < input[i]) 23 | break; 24 | overflow = output; 25 | output = (output * 10) + (int)(input[i]) - 0x30; 26 | if(output < overflow){ /* report overflow */ 27 | return INT_MAX; 28 | } 29 | } 30 | 31 | return output; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /samples/soldering/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include "serial.h" 5 | #include "types.h" 6 | 7 | #define UNIMPLEMENTED(x) {\ 8 | serialWriteString("Unimplmemented: "); \ 9 | serialWriteString(x); \ 10 | serialWriteNewLine(); \ 11 | } 12 | 13 | char hexDigitToChar(const byte input); 14 | int toUInt(char* input, int len); 15 | 16 | #endif /* UTIL_H */ 17 | -------------------------------------------------------------------------------- /samples/syringe/LiquidCrystal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LCD Display API 3 | */ 4 | 5 | #ifndef LIQUID_CRYSTAL_H 6 | #define LIQUID_CRYSTAL_H 7 | 8 | typedef struct LiquidCrystalStruct{ 9 | unsigned int id; 10 | } LiquidCrystal; 11 | 12 | void lcd_begin(LiquidCrystal* lcd, unsigned int cols, unsigned int rows); 13 | void lcd_clear(LiquidCrystal* lcd); //http://stackoverflow.com/questions/10105666/clearing-the-terminal-screen 14 | void lcd_print(LiquidCrystal* lcd, char* output, int len); 15 | void lcd_setCursor(LiquidCrystal* lcd, int x, int y); //http://stackoverflow.com/questions/10105666/clearing-the-terminal-screen 16 | 17 | #endif /* LIQUID_CRYSTAL_H */ 18 | -------------------------------------------------------------------------------- /samples/syringe/README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi 2 port of Open Syringe Pump 2 | 3 | Adapted from the original Open Syringe Pump project [1][2] 4 | 5 | ## Functionality 6 | 7 | This application controls a stepper motor, which is connected to a syringe pump in order to deliver precise quantities of liquid from the syringe. In medical terminology, a "bolus" is a single dose of a drug or other medicinal preparation given all at once [6]. 8 | 9 | ### Input 10 | 11 | Input is provided through the serial interface using the following commands: 12 | - an integer number sets the bolus size in micro-litres (uL) 13 | - "+" pushes a bolus of the specified size 14 | - "-" pulls a bolus of the specified size 15 | 16 | All commands must be terminated by a newline character. 17 | 18 | Input can also be provided through a set of trigger pins or an attached keypad. This functionality is not implemented for the Raspberry Pi in this version. 19 | 20 | ### Output 21 | 22 | Output is provided through an attached LCD display. In this version, the information that would be displayed on the LCD is output via the serial port. 23 | 24 | 25 | ## Similarity to original 26 | 27 | Apart from the unimplemented features, this port is functionally equivalent to the original application. 28 | 29 | The original code in the syringePump.ino file has been retained unmodified as far as possible in syringePump.c. The ported version contains 85 new or modified lines of code not present in the original*. This equates to less than 25% of the original code. 30 | 31 | *calculated using: 32 | comm -23 <(sort syringePump.c) <(sort syringePump.c.orig) | wc -l 33 | 34 | The external functions used by this code (e.g. serial port control and GPIO operations etc.) naturally differ between the Arduino and Raspberry Pi platforms, and are excluded from the above metric. 35 | 36 | 37 | ## Critical functions 38 | 39 | - void bolus(int direction) { ... } calculates the duration for which the motor step pin should be held at the HIGH level to push or pull a bolus of the specified size. It also sets the respective direction pin level and sets the motor step pin to HIGH for the calculated duration using a for loop. 40 | 41 | - void readSerial() { ... } reads values from the serial input. This function may introduce a buffer overflow vulnerability due to the fixed length of the serialStr variable. 42 | 43 | 44 | [1]: https://hackaday.io/project/1838-open-syringe-pump "Open Syringe Pump (project page)" 45 | [2]: https://github.com/naroom/OpenSyringePump/blob/master/syringePump/syringePump.ino "Open Syringe Pump (GitHub)" 46 | [3]: https://www.raspberrypi.org/products/raspberry-pi-2-model-b/ "Raspberry Pi 2 Model B" 47 | [4]: http://www.waveshare.com/dvk512.htm "DVK512, Raspberry Pi Expansion Board" 48 | [5]: https://launchpad.net/gcc-arm-embedded "GCC ARM Embedded on Launchpad" 49 | [6]: https://en.wikipedia.org/wiki/Bolus_(medicine) "Wikipedia: Bolus (medicine)" 50 | -------------------------------------------------------------------------------- /samples/syringe/attack.txt: -------------------------------------------------------------------------------- 1 | Attack goal: hijack control flow to trigger the movement of the syringe 2 | pump. 3 | 4 | What we need to accomplish with a gadget chain: 5 | - write an argument of syringe-move function on r0 6 | - branch to syringe-move function 7 | - write argumenets of cfa_quote on r0, r1, r2, r3 8 | - branch to cfa_quote 9 | 10 | Required gadgets: 11 | Gadget1: load r3 from the stack and branches to Gadget2 12 | 00008208 LDMFD SP!, {R3,R4,R11,PC} 13 | 14 | Gadget2: Move value in register r3 to r0 and branches to move-syringe 15 | 00009C40 MOV R0, R3 16 | 00009C44 SUB SP, R11, #4 17 | 00009C48 LDMFD SP!, {R11,LR} 18 | 00009C4C ADD SP, SP, #0x10 19 | 00009C50 BX LR 20 | 21 | Gadget3: load r0, r1, r2, r3 and calls cfa_quote 22 | 000082A0 LDR R3, =(user_data - 0x82AC) 23 | 000082A4 ADD R3, PC, R3 ; user_data 24 | 000082A8 MOV R0, R3 25 | 000082AC MOV R1, #8 26 | 000082B0 LDR R3, =(quote_out - 0x82BC) 27 | 000082B4 ADD R3, PC, R3 ; quote_out 28 | 000082B8 MOV R2, R3 29 | 000082BC LDR R3, =(quote_len - 0x82C8) 30 | 000082C0 ADD R3, PC, R3 ; quote_len 31 | 000082C4 BL cfa_quote 32 | 000082C8 SUB SP, R11, #8 33 | 000082CC LDMFD SP!, {R4,R 34 | 35 | The steps of the attack are as following: 36 | 1. The program reads serial data and we provide the chain of gadgets as 37 | input 2. The return address of the vulnerable function is overwritten by the 38 | address of Gadget1. When the function returns, control flow is redirected to 39 | Gadget1 3. Gadget1 loads registers r3 and pc with values from the stack and 40 | control flows to Gadget2. 41 | move r3 <-- [argument value] 42 | move pc <-- [address of Gadget2] 43 | branch to pc 44 | 45 | 3. Gadget 2 moves the value in r3 to r0, loads lr from stack and branches to 46 | move-syringe 47 | move r0 <-- r3 48 | move lr <-- [address of move syringe function + 4], avoid the first 49 | instruction that stores lr 50 | branch to lr 51 | 52 | 4. The function move-syringe ends with instruction: LDMFD SP!, 53 | {R4,R11,PC}. Control flows to Gadget3 54 | move pc <-- [address of Gadget3] 55 | 56 | 5. Gadget3 loads the arguments of cfa_quote on r0, r1, r2, and r3 and calls 57 | cfa_quote The attack ends by outputting the hash value of the control flow. 58 | 59 | 6. Execution may continue depending on the values on the stack. 60 | -------------------------------------------------------------------------------- /samples/syringe/syringe-auth.txt: -------------------------------------------------------------------------------- 1 | SyringePump v2.0 2 | 3 | INPUT: 10 4 | 5 | Used 0.0 mL 6 | Bolus 0.10 mL 7 | [INFO] cfa_quote: 7c 16 d6 51 20 a2 a0 c7 90 f5 ef 04 0c 2e ba bc 8 | 9 | [INFO] loop[000]: 78 22 5b 62 92 41 ca 02 7b ff 29 57 c6 6f 9b a2 10 | [INFO] path[000]: 2f a5 8c dc 1b 35 41 29 ab dd 35 5c f2 69 08 37 (1) 11 | [INFO] loop[001]: d6 90 9e a0 8c ae 90 84 9e 66 09 f8 a6 7b 52 04 12 | [INFO] path[000]: 92 fb d1 e8 90 cb 02 e5 6c f2 65 8c 86 72 0e d3 (2) 13 | [INFO] loop[002]: ff e1 39 f8 17 97 b3 12 1f 30 28 74 e7 b7 d3 92 14 | [INFO] path[000]: 2f a5 8c dc 1b 35 41 29 ab dd 35 5c f2 69 08 37 (1) 15 | [INFO] loop[003]: 46 9c 4b 68 53 d4 18 7d 7d 8f 0b c3 48 5b 33 a0 16 | [INFO] path[000]: 92 fb d1 e8 90 cb 02 e5 6c f2 65 8c 86 72 0e d3 (2) 17 | [INFO] loop[004]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 18 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 19 | [INFO] loop[005]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 20 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 21 | [INFO] loop[006]: 05 e3 92 40 95 ef 7b 46 13 7d 6e 8b 05 be bf 41 22 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (9) 23 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 24 | [INFO] loop[007]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 25 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 26 | [INFO] loop[008]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 27 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 28 | [INFO] loop[009]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 29 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 30 | [INFO] loop[010]: 47 d0 ce 3d 23 6e bc f1 7f 1f 4f 61 bf e9 a9 60 31 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (10) 32 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 33 | [INFO] loop[011]: b6 54 50 30 ef 16 73 61 21 12 d0 a8 2e 4d 3e 6c 34 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (11) 35 | [INFO] loop[012]: a5 04 21 91 ac 9b 70 9a 49 1e 61 dd e4 8b 91 db 36 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 37 | [INFO] loop[013]: 74 58 fb 20 2c 63 54 7e e4 ae a5 98 75 ef 5f 6c 38 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (13) 39 | [INFO] loop[014]: 7b 91 ba 41 5d 76 05 1e 8f ef 75 c5 b3 ad 44 9c 40 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 41 | 42 | INPUT: + 43 | 44 | Used 0.10 mL 45 | Bolus 0.10 mL 46 | [INFO] cfa_quote: 57 92 0f 9e 98 47 30 bb a5 f7 5d 2a dc 8a 7b 5f 47 | 48 | [INFO] loop[000]: b3 c5 ca c4 6f dc 6a d0 4a 80 10 09 af a3 59 70 49 | [INFO] path[000]: 97 78 fb fc 93 09 4e d7 ac 32 5d 65 eb 29 08 0c (68) 50 | [INFO] loop[001]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 51 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 52 | [INFO] loop[002]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 53 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 54 | [INFO] loop[003]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 55 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 56 | [INFO] loop[004]: f5 77 b7 94 bd 6c 81 e2 2f 36 da ad cd df 56 6e 57 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (9) 58 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 59 | [INFO] loop[005]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 60 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 61 | [INFO] loop[006]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 62 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 63 | [INFO] loop[007]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 64 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 65 | [INFO] loop[008]: ca 34 cb 8a 0b 8a 76 41 0f 59 e9 b2 8d 76 91 30 66 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (10) 67 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 68 | [INFO] loop[009]: 2d 80 99 2c f1 61 b1 19 53 4d 0a 96 be be a8 1f 69 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (12) 70 | [INFO] loop[010]: d2 32 da 39 c8 7f 0d bb 13 c0 a7 12 7d 4b 0c ce 71 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 72 | [INFO] loop[011]: 73 e3 be b7 33 30 58 5a 59 1b 2b c0 60 50 c6 36 73 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (13) 74 | [INFO] loop[012]: c2 88 83 00 68 dd 41 32 2c 0d 37 f6 d3 be fd 09 75 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 76 | 77 | INPUT: 20 78 | 79 | Used 0.10 mL 80 | Bolus 0.20 mL 81 | [INFO] cfa_quote: 7c 16 d6 51 20 a2 a0 c7 90 f5 ef 04 0c 2e ba bc 82 | 83 | [INFO] loop[000]: 78 22 5b 62 92 41 ca 02 7b ff 29 57 c6 6f 9b a2 84 | [INFO] path[000]: 2f a5 8c dc 1b 35 41 29 ab dd 35 5c f2 69 08 37 (1) 85 | [INFO] loop[001]: d6 90 9e a0 8c ae 90 84 9e 66 09 f8 a6 7b 52 04 86 | [INFO] path[000]: 92 fb d1 e8 90 cb 02 e5 6c f2 65 8c 86 72 0e d3 (2) 87 | [INFO] loop[002]: ff e1 39 f8 17 97 b3 12 1f 30 28 74 e7 b7 d3 92 88 | [INFO] path[000]: 2f a5 8c dc 1b 35 41 29 ab dd 35 5c f2 69 08 37 (1) 89 | [INFO] loop[003]: 46 9c 4b 68 53 d4 18 7d 7d 8f 0b c3 48 5b 33 a0 90 | [INFO] path[000]: 92 fb d1 e8 90 cb 02 e5 6c f2 65 8c 86 72 0e d3 (2) 91 | [INFO] loop[004]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 92 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 93 | [INFO] loop[005]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 94 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 95 | [INFO] loop[006]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 96 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 97 | [INFO] loop[007]: 05 e3 92 40 95 ef 7b 46 13 7d 6e 8b 05 be bf 41 98 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (9) 99 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 100 | [INFO] loop[008]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 101 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 102 | [INFO] loop[009]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 103 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 104 | [INFO] loop[010]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 105 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 106 | [INFO] loop[011]: 47 d0 ce 3d 23 6e bc f1 7f 1f 4f 61 bf e9 a9 60 107 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (10) 108 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 109 | [INFO] loop[012]: b6 54 50 30 ef 16 73 61 21 12 d0 a8 2e 4d 3e 6c 110 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (12) 111 | [INFO] loop[013]: a5 04 21 91 ac 9b 70 9a 49 1e 61 dd e4 8b 91 db 112 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 113 | [INFO] loop[014]: 74 58 fb 20 2c 63 54 7e e4 ae a5 98 75 ef 5f 6c 114 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (13) 115 | [INFO] loop[015]: 7b 91 ba 41 5d 76 05 1e 8f ef 75 c5 b3 ad 44 9c 116 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 117 | 118 | INPUT: + 119 | 120 | Used 0.30 mL 121 | Bolus 0.20 mL 122 | [INFO] cfa_quote: 57 92 0f 9e 98 47 30 bb a5 f7 5d 2a dc 8a 7b 5f 123 | 124 | [INFO] loop[000]: b3 c5 ca c4 6f dc 6a d0 4a 80 10 09 af a3 59 70 125 | [INFO] path[000]: 97 78 fb fc 93 09 4e d7 ac 32 5d 65 eb 29 08 0c (136) 126 | [INFO] loop[001]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 127 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 128 | [INFO] loop[002]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 129 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 130 | [INFO] loop[003]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 131 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 132 | [INFO] loop[004]: f5 77 b7 94 bd 6c 81 e2 2f 36 da ad cd df 56 6e 133 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (9) 134 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 135 | [INFO] loop[005]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 136 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 137 | [INFO] loop[006]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 138 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 139 | [INFO] loop[007]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 140 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 141 | [INFO] loop[008]: ca 34 cb 8a 0b 8a 76 41 0f 59 e9 b2 8d 76 91 30 142 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (10) 143 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 144 | [INFO] loop[009]: 2d 80 99 2c f1 61 b1 19 53 4d 0a 96 be be a8 1f 145 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (12) 146 | [INFO] loop[010]: d2 32 da 39 c8 7f 0d bb 13 c0 a7 12 7d 4b 0c ce 147 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 148 | [INFO] loop[011]: 73 e3 be b7 33 30 58 5a 59 1b 2b c0 60 50 c6 36 149 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (13) 150 | [INFO] loop[012]: c2 88 83 00 68 dd 41 32 2c 0d 37 f6 d3 be fd 09 151 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 152 | 153 | INPUT: 100 154 | 155 | Used 0.30 mL 156 | Bolus 0.100 mL 157 | [INFO] cfa_quote: 7c 16 d6 51 20 a2 a0 c7 90 f5 ef 04 0c 2e ba bc 158 | 159 | [INFO] loop[000]: 78 22 5b 62 92 41 ca 02 7b ff 29 57 c6 6f 9b a2 160 | [INFO] path[000]: 2f a5 8c dc 1b 35 41 29 ab dd 35 5c f2 69 08 37 (1) 161 | [INFO] loop[001]: d6 90 9e a0 8c ae 90 84 9e 66 09 f8 a6 7b 52 04 162 | [INFO] path[000]: 92 fb d1 e8 90 cb 02 e5 6c f2 65 8c 86 72 0e d3 (3) 163 | [INFO] loop[002]: ff e1 39 f8 17 97 b3 12 1f 30 28 74 e7 b7 d3 92 164 | [INFO] path[000]: 2f a5 8c dc 1b 35 41 29 ab dd 35 5c f2 69 08 37 (1) 165 | [INFO] loop[003]: 46 9c 4b 68 53 d4 18 7d 7d 8f 0b c3 48 5b 33 a0 166 | [INFO] path[000]: 92 fb d1 e8 90 cb 02 e5 6c f2 65 8c 86 72 0e d3 (3) 167 | [INFO] loop[004]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 168 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 169 | [INFO] loop[005]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 170 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (1) 171 | [INFO] loop[006]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 172 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (2) 173 | [INFO] loop[007]: 05 e3 92 40 95 ef 7b 46 13 7d 6e 8b 05 be bf 41 174 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (9) 175 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 176 | [INFO] loop[008]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 177 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 178 | [INFO] loop[009]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 179 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (2) 180 | [INFO] loop[010]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 181 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (3) 182 | [INFO] loop[011]: 47 d0 ce 3d 23 6e bc f1 7f 1f 4f 61 bf e9 a9 60 183 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (10) 184 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 185 | [INFO] loop[012]: b6 54 50 30 ef 16 73 61 21 12 d0 a8 2e 4d 3e 6c 186 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (12) 187 | [INFO] loop[013]: a5 04 21 91 ac 9b 70 9a 49 1e 61 dd e4 8b 91 db 188 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 189 | [INFO] loop[014]: 74 58 fb 20 2c 63 54 7e e4 ae a5 98 75 ef 5f 6c 190 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (14) 191 | [INFO] loop[015]: 7b 91 ba 41 5d 76 05 1e 8f ef 75 c5 b3 ad 44 9c 192 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 193 | 194 | INPUT: + 195 | 196 | Used 0.130 mL 197 | Bolus 0.100 mL 198 | [INFO] cfa_quote: 57 92 0f 9e 98 47 30 bb a5 f7 5d 2a dc 8a 7b 5f 199 | 200 | [INFO] loop[000]: b3 c5 ca c4 6f dc 6a d0 4a 80 10 09 af a3 59 70 201 | [INFO] path[000]: 97 78 fb fc 93 09 4e d7 ac 32 5d 65 eb 29 08 0c (682) 202 | [INFO] loop[001]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 203 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 204 | [INFO] loop[002]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 205 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (2) 206 | [INFO] loop[003]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 207 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (3) 208 | [INFO] loop[004]: f5 77 b7 94 bd 6c 81 e2 2f 36 da ad cd df 56 6e 209 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (9) 210 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 211 | [INFO] loop[005]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 212 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (1) 213 | [INFO] loop[006]: 6d 05 6e b2 3a 27 1e 2b 78 3e f9 4c e3 a7 cb f8 214 | [INFO] path[000]: 62 f7 b8 0b 65 4b de 35 c7 05 bc 28 06 43 11 6e (2) 215 | [INFO] loop[007]: eb 16 88 8a d2 3b c6 19 f9 01 94 5d ee cb 1c 13 216 | [INFO] path[000]: b9 7d cf 8d 00 b6 5f 63 b3 7c 60 e4 e3 be 56 17 (3) 217 | [INFO] loop[008]: ca 34 cb 8a 0b 8a 76 41 0f 59 e9 b2 8d 76 91 30 218 | [INFO] path[000]: 67 c6 5e d4 18 13 02 bc 4a 5d 60 a0 16 85 f4 ed (10) 219 | [INFO] path[001]: 78 19 af 09 0f d5 64 f4 39 b4 7a 0d 97 57 77 8c (2) 220 | [INFO] loop[009]: 2d 80 99 2c f1 61 b1 19 53 4d 0a 96 be be a8 1f 221 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (13) 222 | [INFO] loop[010]: d2 32 da 39 c8 7f 0d bb 13 c0 a7 12 7d 4b 0c ce 223 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 224 | [INFO] loop[011]: 73 e3 be b7 33 30 58 5a 59 1b 2b c0 60 50 c6 36 225 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (14) 226 | [INFO] loop[012]: c2 88 83 00 68 dd 41 32 2c 0d 37 f6 d3 be fd 09 227 | [INFO] path[000]: 74 af 0f dc 3b 17 ff 67 03 72 d0 db fe b5 94 78 (2) 228 | -------------------------------------------------------------------------------- /samples/syringe/syringePump.c: -------------------------------------------------------------------------------- 1 | //Open Syringe Pump 2 | //https://github.com/naroom/OpenSyringePump/blob/master/syringePump/syringePump.ino 3 | //https://hackaday.io/project/1838-open-syringe-pump 4 | 5 | // Controls a stepper motor via an LCD keypad shield. 6 | // Accepts triggers and serial commands. 7 | // To run, you will need the LCDKeypad library installed - see libraries dir. 8 | 9 | // Serial commands: 10 | // Set serial baud rate to 57600 and terminate commands with newlines. 11 | // Send a number, e.g. "100", to set bolus size. 12 | // Send a "+" to push that size bolus. 13 | // Send a "-" to pull that size bolus. 14 | 15 | #include "LiquidCrystal.h" 16 | #include "util.h" 17 | #include "kernel/kprintf.h" 18 | #include "kernel/log.h" 19 | #include "kernel/led.h" 20 | 21 | #include "lib/cfa_stub.h" 22 | 23 | 24 | /* -- Constants -- */ 25 | #define SYRINGE_VOLUME_ML 30.0 26 | #define SYRINGE_BARREL_LENGTH_MM 80.0 27 | 28 | #define THREADED_ROD_PITCH 1.25 29 | #define STEPS_PER_REVOLUTION 200.0 30 | #define MICROSTEPS_PER_STEP 16.0 31 | 32 | #define SPEED_MICROSECONDS_DELAY 100 //longer delay = lower speed 33 | 34 | #define false 0 35 | #define true 1 36 | 37 | #define boolean _Bool 38 | #define three_dec_places( x ) ( (int)( (x*1e3)+0.5 - (((int)x)*1e3) ) ) 39 | 40 | long ustepsPerMM = MICROSTEPS_PER_STEP * STEPS_PER_REVOLUTION / THREADED_ROD_PITCH; 41 | long ustepsPerML = (MICROSTEPS_PER_STEP * STEPS_PER_REVOLUTION * SYRINGE_BARREL_LENGTH_MM) / (SYRINGE_VOLUME_ML * THREADED_ROD_PITCH ); 42 | 43 | /* -- Pin definitions -- */ 44 | int motorDirPin = 2; 45 | int motorStepPin = 3; 46 | 47 | int triggerPin = 0; //TODO check RPi pin-out before implementing 48 | int bigTriggerPin = 0; //TODO check RPi pin-out before implementing 49 | 50 | /* -- Keypad states -- */ 51 | int adc_key_val[5] ={30, 150, 360, 535, 760 }; 52 | 53 | enum{ KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_SELECT, KEY_NONE}; 54 | int NUM_KEYS = 5; 55 | int adc_key_in; 56 | int key = KEY_NONE; 57 | 58 | /* -- Enums and constants -- */ 59 | enum{PUSH,PULL}; //syringe movement direction 60 | enum{MAIN, BOLUS_MENU}; //UI states 61 | 62 | enum{INPUT, OUTPUT}; //GPIO directions 63 | enum{HIGH, LOW}; //GPIO states 64 | 65 | const int mLBolusStepsLength = 9; 66 | float mLBolusSteps[9] = {0.001, 0.005, 0.010, 0.050, 0.100, 0.500, 1.000, 5.000, 10.000}; 67 | 68 | /* -- Default Parameters -- */ 69 | float mLBolus = 0.500; //default bolus size 70 | float mLBigBolus = 1.000; //default large bolus size 71 | float mLUsed = 0.0; 72 | int mLBolusStepIdx = 3; //0.05 mL increments at first 73 | //float mLBolusStep = mLBolusSteps[mLBolusStepIdx]; 74 | float mLBolusStep = 0.050; 75 | 76 | long stepperPos = 0; //in microsteps 77 | char charBuf[16]; 78 | 79 | //debounce params 80 | long lastKeyRepeatAt = 0; 81 | long keyRepeatDelay = 400; 82 | long keyDebounce = 125; 83 | int prevKey = KEY_NONE; 84 | 85 | //menu stuff 86 | int uiState = MAIN; 87 | 88 | //triggering 89 | int prevBigTrigger = HIGH; 90 | int prevTrigger = HIGH; 91 | 92 | //serial 93 | char serialStr[80] = ""; 94 | boolean serialStrReady = false; 95 | int serialStrLen = 0; 96 | 97 | /* -- Initialize libraries -- */ 98 | LiquidCrystal lcd; 99 | 100 | void setup(){ 101 | /* LCD setup */ 102 | lcd_begin(&lcd, 16, 2); 103 | lcd_clear(&lcd); 104 | 105 | lcd_print(&lcd, "SyringePump v2.0", 16); 106 | 107 | /* Triggering setup */ 108 | pinMode(triggerPin, INPUT); 109 | pinMode(bigTriggerPin, INPUT); 110 | digitalWrite(triggerPin, HIGH); //enable pullup resistor 111 | digitalWrite(bigTriggerPin, HIGH); //enable pullup resistor 112 | 113 | /* Motor Setup */ 114 | pinMode(motorDirPin, OUTPUT); 115 | pinMode(motorStepPin, OUTPUT); 116 | 117 | /* Serial setup */ 118 | //Note that serial commands must be terminated with a newline 119 | //to be processed. Check this setting in your serial monitor if 120 | //serial commands aren't doing anything. 121 | Serial_begin(57600); //Note that your serial connection must be set to 57600 to work! 122 | } 123 | 124 | void checkTriggers(); 125 | void readSerial(); 126 | void processSerial(); 127 | void bolus(int direction); 128 | void readKey(); 129 | void doKeyAction(unsigned int key); 130 | void updateScreen(); 131 | int get_key(unsigned int input); 132 | unsigned int tim; 133 | 134 | 135 | static uint8_t user_data[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; 136 | static uint8_t quote_out[128]; 137 | static uint32_t quote_len; 138 | 139 | extern btbl_entry_t __btbl_start; 140 | extern btbl_entry_t __btbl_end; 141 | extern ltbl_entry_t __ltbl_start; 142 | extern ltbl_entry_t __ltbl_end; 143 | 144 | void loop(){ 145 | 146 | //check for LCD updates 147 | readKey(); 148 | 149 | //look for triggers on trigger lines 150 | checkTriggers(); 151 | 152 | //check serial port for new commands 153 | readSerial(); 154 | if(serialStrReady){ 155 | 156 | cfa_init((cfa_addr_t)&loop, (cfa_addr_t)0, 157 | (btbl_entry_t*)&__btbl_start, (btbl_entry_t*)&__btbl_end, 158 | (ltbl_entry_t*)&__ltbl_start, (ltbl_entry_t*)&__ltbl_end); 159 | 160 | processSerial(); 161 | 162 | quote_len = sizeof(quote_out); 163 | cfa_quote(user_data, sizeof(user_data), quote_out, "e_len); 164 | } 165 | } 166 | 167 | void checkTriggers(){ 168 | //check low-reward trigger line 169 | int pushTriggerValue = digitalRead(triggerPin); 170 | if(pushTriggerValue == HIGH && prevTrigger == LOW){ 171 | bolus(PUSH); 172 | updateScreen(); 173 | } 174 | prevTrigger = pushTriggerValue; 175 | 176 | //check high-reward trigger line 177 | int bigTriggerValue = digitalRead(bigTriggerPin); 178 | if(bigTriggerValue == HIGH && prevBigTrigger == LOW){ 179 | //push big reward amount 180 | float mLBolusTemp = mLBolus; 181 | mLBolus = mLBigBolus; 182 | bolus(PUSH); 183 | mLBolus = mLBolusTemp; 184 | 185 | updateScreen(); 186 | } 187 | prevBigTrigger = bigTriggerValue; 188 | } 189 | 190 | void readSerial(){ 191 | //pulls in characters from serial port as they arrive 192 | //builds serialStr and sets ready flag when newline is found 193 | while (Serial_available()) { 194 | char inChar = (char)Serial_read(); 195 | if (inChar < 0x20) { 196 | serialStrReady = true; 197 | } 198 | else{ 199 | serialStr[serialStrLen] = inChar; 200 | serialStrLen++; 201 | } 202 | } 203 | } 204 | 205 | void processSerial(){ 206 | //process serial commands as they are read in 207 | if(serialStr[0] == '+'){ 208 | bolus(PUSH); 209 | updateScreen(); 210 | } 211 | else if(serialStr[0] == '-'){ 212 | bolus(PULL); 213 | updateScreen(); 214 | } 215 | else if(toUInt(serialStr, serialStrLen) != 0){ 216 | int uLbolus = toUInt(serialStr, serialStrLen); 217 | mLBolus = (float)uLbolus / 1000.0; 218 | updateScreen(); 219 | } 220 | else{ 221 | Serial_write("Invalid command: [", 18); 222 | Serial_write(serialStr, serialStrLen); 223 | Serial_write("]\n", 2); 224 | } 225 | serialStrReady = false; 226 | serialStrLen = 0; 227 | } 228 | 229 | void bolus(int direction){ 230 | //Move stepper. Will not return until stepper is done moving. 231 | 232 | //change units to steps 233 | long steps = (mLBolus * ustepsPerML); 234 | if(direction == PUSH){ 235 | led_on(); 236 | digitalWrite(motorDirPin, HIGH); 237 | steps = mLBolus * ustepsPerML; 238 | mLUsed += mLBolus; 239 | } 240 | else if(direction == PULL){ 241 | led_off(); 242 | digitalWrite(motorDirPin, LOW); 243 | if((mLUsed-mLBolus) > 0){ 244 | mLUsed -= mLBolus; 245 | } 246 | else{ 247 | mLUsed = 0; 248 | } 249 | } 250 | 251 | float usDelay = SPEED_MICROSECONDS_DELAY; //can go down to 20 or 30 252 | 253 | for(long i=0; i < steps; i++){ 254 | digitalWrite(motorStepPin, HIGH); 255 | delayMicroseconds(usDelay); 256 | 257 | digitalWrite(motorStepPin, LOW); 258 | delayMicroseconds(usDelay); 259 | } 260 | 261 | } 262 | 263 | void readKey(){ 264 | //Some UI niceness here. 265 | //When user holds down a key, it will repeat every so often (keyRepeatDelay). 266 | //But when user presses and releases a key, 267 | //the key becomes responsive again after the shorter debounce period (keyDebounce). 268 | 269 | adc_key_in = analogRead(0); 270 | key = get_key(adc_key_in); // convert into key press 271 | 272 | long currentTime = millis(); 273 | long timeSinceLastPress = (currentTime-lastKeyRepeatAt); 274 | 275 | boolean processThisKey = false; 276 | if (prevKey == key && timeSinceLastPress > keyRepeatDelay){ 277 | processThisKey = true; 278 | } 279 | if(prevKey == KEY_NONE && timeSinceLastPress > keyDebounce){ 280 | processThisKey = true; 281 | } 282 | if(key == KEY_NONE){ 283 | processThisKey = false; 284 | } 285 | 286 | prevKey = key; 287 | 288 | if(processThisKey){ 289 | doKeyAction(key); 290 | lastKeyRepeatAt = currentTime; 291 | } 292 | } 293 | 294 | void doKeyAction(unsigned int key){ 295 | if(key == KEY_NONE){ 296 | return; 297 | } 298 | 299 | if(key == KEY_SELECT){ 300 | if(uiState == MAIN){ 301 | uiState = BOLUS_MENU; 302 | } 303 | else if(BOLUS_MENU){ 304 | uiState = MAIN; 305 | } 306 | } 307 | 308 | if(uiState == MAIN){ 309 | if(key == KEY_LEFT){ 310 | bolus(PULL); 311 | } 312 | if(key == KEY_RIGHT){ 313 | bolus(PUSH); 314 | } 315 | if(key == KEY_UP){ 316 | mLBolus += mLBolusStep; 317 | } 318 | if(key == KEY_DOWN){ 319 | if((mLBolus - mLBolusStep) > 0){ 320 | mLBolus -= mLBolusStep; 321 | } 322 | else{ 323 | mLBolus = 0; 324 | } 325 | } 326 | } 327 | else if(uiState == BOLUS_MENU){ 328 | if(key == KEY_LEFT){ 329 | //nothin' 330 | } 331 | if(key == KEY_RIGHT){ 332 | //nothin' 333 | } 334 | if(key == KEY_UP){ 335 | if(mLBolusStepIdx < mLBolusStepsLength-1){ 336 | mLBolusStepIdx++; 337 | mLBolusStep = mLBolusSteps[mLBolusStepIdx]; 338 | } 339 | } 340 | if(key == KEY_DOWN){ 341 | if(mLBolusStepIdx > 0){ 342 | mLBolusStepIdx -= 1; 343 | mLBolusStep = mLBolusSteps[mLBolusStepIdx]; 344 | } 345 | } 346 | } 347 | 348 | updateScreen(); 349 | } 350 | 351 | void updateScreen(){ 352 | //build strings for upper and lower lines of screen 353 | char s1[80]; //upper line 354 | char s2[80]; //lower line 355 | int s1Len = 0; 356 | int s2Len = 0; 357 | 358 | if(uiState == MAIN){ 359 | s1Len = sprintf(s1, "Used %d.%d mL", (int)mLUsed, three_dec_places(mLUsed)); 360 | s2Len = sprintf(s2, "Bolus %d.%d mL", (int)mLBolus, three_dec_places(mLBolus)); 361 | } 362 | else if(uiState == BOLUS_MENU){ 363 | s1Len = sprintf(s1, "Menu> BolusStep"); 364 | s2Len = sprintf(s2, "%d.%d", (int)mLBolusStep, three_dec_places(mLBolusStep)); 365 | } 366 | 367 | //do actual screen update 368 | lcd_clear(&lcd); 369 | 370 | lcd_setCursor(&lcd, 0, 0); //line=1, x=0 371 | lcd_print(&lcd, s1, s1Len); 372 | 373 | lcd_setCursor(&lcd, 0, 1); //line=2, x=0 374 | lcd_print(&lcd, s2, s2Len); 375 | } 376 | 377 | 378 | // Convert ADC value to key number 379 | int get_key(unsigned int input){ 380 | int k; 381 | for (k = 0; k < NUM_KEYS; k++){ 382 | if (input < (unsigned int)adc_key_val[k]){ 383 | return k; 384 | } 385 | } 386 | if (k >= NUM_KEYS){ 387 | k = KEY_NONE; // No valid key pressed 388 | } 389 | return k; 390 | } 391 | 392 | /* 393 | String decToString(float decNumber){ 394 | //not a general use converter! Just good for the numbers we're working with here. 395 | int wholePart = decNumber; //truncate 396 | int decPart = round(abs(decNumber*1000)-abs(wholePart*1000)); //3 decimal places 397 | String strZeros = String(""); 398 | if(decPart < 10){ 399 | strZeros = String("00"); 400 | } 401 | else if(decPart < 100){ 402 | strZeros = String("0"); 403 | } 404 | return String(wholePart) + String('.') + strZeros + String(decPart); 405 | } 406 | */ 407 | 408 | //C-FLAT new code 409 | void main(unsigned int r0, unsigned int r1, unsigned int atags) { 410 | (void)r0; 411 | (void)r1; 412 | (void)atags; 413 | info("Starting syringe pump"); 414 | setup(); 415 | while(1) { 416 | loop(); 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /samples/syringe/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Utility API 3 | */ 4 | 5 | #define INT_MAX 2147483647 6 | 7 | void pinMode(int pin, int mode); 8 | void digitalWrite(int pin, int value); 9 | int digitalRead(int pin); 10 | int analogRead(int pin); 11 | 12 | void Serial_begin(int baud); 13 | int Serial_available(); 14 | int Serial_read(); 15 | int Serial_write(char* output, int len); 16 | 17 | unsigned long millis(); 18 | void delayMicroseconds(float usecs); 19 | 20 | int toUInt(char* input, int len); 21 | --------------------------------------------------------------------------------