├── .devcontainer └── devcontainer.json ├── .github ├── actions │ └── load-ci-image │ │ └── action.yml └── workflows │ ├── note-arduino-ci.yml │ └── version-check.yml ├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── extensions.json ├── launch.json └── tasks.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── assets └── blues-wireless.png ├── examples ├── Example0_LibrarylessCommunication │ └── Example0_LibrarylessCommunication.ino ├── Example1_NotecardBasics │ └── Example1_NotecardBasics.ino ├── Example2_PeriodicCommunications │ └── Example2_PeriodicCommunications.ino ├── Example3_InboundPolling │ └── Example3_InboundPolling.ino ├── Example4_InboundInterrupts │ └── Example4_InboundInterrupts.ino ├── Example5_UsingTemplates │ └── Example5_UsingTemplates.ino ├── Example6_SensorTutorial │ └── Example6_SensorTutorial.ino ├── Example7_PowerControl │ └── Example7_PowerControl.ino ├── Example8_BinarySendReceive │ └── Example8_BinarySendReceive.ino ├── Example9_BinarySendReceiveChunked │ └── Example9_BinarySendReceiveChunked.ino └── build_example.sh ├── keywords.txt ├── library.properties ├── scripts ├── check_version.sh └── update_note_c.sh ├── src ├── NoteDefines.h ├── NoteI2c.hpp ├── NoteI2c_Arduino.cpp ├── NoteI2c_Arduino.hpp ├── NoteLog.hpp ├── NoteLog_Arduino.cpp ├── NoteLog_Arduino.hpp ├── NoteSerial.hpp ├── NoteSerial_Arduino.cpp ├── NoteSerial_Arduino.hpp ├── NoteTime.h ├── NoteTime_Arduino.cpp ├── NoteTxn.hpp ├── NoteTxn_Arduino.cpp ├── NoteTxn_Arduino.hpp ├── Notecard.cpp ├── Notecard.h ├── Notecarrier.h └── note-c │ ├── .gitignore │ ├── CMakeLists.txt │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── README.md │ ├── n_atof.c │ ├── n_b64.c │ ├── n_cjson.c │ ├── n_cjson.h │ ├── n_cjson_helpers.c │ ├── n_cobs.c │ ├── n_const.c │ ├── n_ftoa.c │ ├── n_helpers.c │ ├── n_hooks.c │ ├── n_i2c.c │ ├── n_lib.h │ ├── n_md5.c │ ├── n_printf.c │ ├── n_request.c │ ├── n_serial.c │ ├── n_str.c │ ├── n_ua.c │ └── note.h └── test ├── NoteI2c_Arduino.test.cpp ├── NoteLog_Arduino.test.cpp ├── NoteSerial_Arduino.test.cpp ├── NoteTxn_Arduino.test.cpp ├── Notecard.test.cpp ├── TestFunction.hpp ├── mock ├── NoteI2c_Mock.cpp ├── NoteI2c_Mock.hpp ├── NoteLog_Mock.cpp ├── NoteLog_Mock.hpp ├── NoteSerial_Mock.cpp ├── NoteSerial_Mock.hpp ├── NoteTime_Mock.cpp ├── NoteTime_Mock.hpp ├── NoteTxn_Mock.cpp ├── NoteTxn_Mock.hpp ├── mock-arduino.cpp ├── mock-arduino.hpp ├── mock-note-c-note.c └── mock-parameters.hpp └── run_all_tests.sh /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/docker-existing-dockerfile 3 | { 4 | "name": "Arduino Development Environment Dockerfile", 5 | 6 | // Sets the run context to one level up instead of the .devcontainer folder. 7 | "context": "..", 8 | 9 | // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. 10 | "image": "ghcr.io/blues/note_arduino_ci:latest", 11 | // "dockerFile": "../Dockerfile", 12 | 13 | // Set *default* container specific settings.json values on container create. 14 | "settings": {}, 15 | 16 | // Add the IDs of extensions you want installed when the container is created. 17 | "extensions": [ 18 | "ms-vscode.cpptools", 19 | "shardulm94.trailing-spaces" 20 | ] 21 | 22 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 23 | // "forwardPorts": [], 24 | 25 | // Uncomment the next line to run commands after the container is created - for example installing curl. 26 | // "postCreateCommand": "apt-get update && apt-get install -y curl", 27 | 28 | // Uncomment when using a ptrace-based debugger like C++, Go, and Rust 29 | // "runArgs": [ 30 | // "--device=/dev/bus/usb/" 31 | // ], 32 | 33 | // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. 34 | // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], 35 | 36 | // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. 37 | // "remoteUser": "blues" 38 | } 39 | -------------------------------------------------------------------------------- /.github/actions/load-ci-image/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Load note-arduino CI Docker image' 2 | runs: 3 | using: 'composite' 4 | steps: 5 | - name: Set up Docker Buildx 6 | uses: docker/setup-buildx-action@v3 7 | 8 | - name: Download image artifact 9 | uses: actions/download-artifact@v4 10 | with: 11 | name: note_arduino_ci_image 12 | path: /tmp 13 | 14 | - name: Load Docker image 15 | shell: bash 16 | run: | 17 | docker load --input /tmp/note_arduino_ci_image.tar 18 | -------------------------------------------------------------------------------- /.github/workflows/note-arduino-ci.yml: -------------------------------------------------------------------------------- 1 | name: Note Arduino CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | check_dockerfile_changed: 11 | runs-on: ubuntu-latest 12 | outputs: 13 | changed: ${{ steps.filter.outputs.changed }} 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | 19 | # TODO: This is a 3rd party GitHub action from some dude. Ideally, we'd 20 | # use something more "official". 21 | - name: Check if Dockerfile changed 22 | uses: dorny/paths-filter@v3 23 | id: filter 24 | with: 25 | base: 'master' 26 | filters: | 27 | changed: 28 | - 'Dockerfile' 29 | 30 | build_ci_docker_image: 31 | runs-on: ubuntu-latest 32 | needs: [check_dockerfile_changed] 33 | if: ${{ needs.check_dockerfile_changed.outputs.changed == 'true' }} 34 | 35 | steps: 36 | - name: Checkout code 37 | uses: actions/checkout@v4 38 | 39 | - name: Login to GitHub Container Registry 40 | uses: docker/login-action@v3 41 | with: 42 | registry: ghcr.io 43 | username: ${{ github.actor }} 44 | password: ${{ secrets.GITHUB_TOKEN }} 45 | 46 | - name: Set up Docker Buildx 47 | uses: docker/setup-buildx-action@v3 48 | 49 | - name: Rebuild image 50 | uses: docker/build-push-action@v6 51 | with: 52 | context: . 53 | load: true 54 | tags: ghcr.io/blues/note_arduino_ci:latest 55 | outputs: type=docker,dest=/tmp/note_arduino_ci_image.tar 56 | 57 | - name: Upload image artifact 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: note_arduino_ci_image 61 | path: /tmp/note_arduino_ci_image.tar 62 | 63 | run_tests: 64 | runs-on: ubuntu-latest 65 | if: ${{ always() }} 66 | needs: [build_ci_docker_image] 67 | 68 | steps: 69 | - name: Checkout code 70 | uses: actions/checkout@v4 71 | 72 | - name: Load CI Docker image 73 | # Only load the Docker image artifact if build_ci_docker_image actually 74 | # ran (e.g. it wasn't skipped and was successful). 75 | if: ${{ needs.build_ci_docker_image.result == 'success' }} 76 | uses: ./.github/actions/load-ci-image 77 | 78 | - name: Run tests 79 | run: | 80 | docker run --rm --volume $(pwd):/note-arduino/ \ 81 | --workdir /note-arduino/ \ 82 | --entrypoint ./test/run_all_tests.sh \ 83 | --user root \ 84 | ghcr.io/blues/note_arduino_ci:latest 85 | 86 | - name: Adjust lcov source file paths for Coveralls 87 | run: sudo sed -i 's/\/note-arduino\///g' ./coverage/lcov.info 88 | 89 | - name: Publish test coverage 90 | uses: coverallsapp/github-action@master 91 | with: 92 | github-token: ${{ secrets.GITHUB_TOKEN }} 93 | path-to-lcov: ./coverage/lcov.info 94 | 95 | build_examples: 96 | runs-on: ubuntu-latest 97 | if: ${{ always() }} 98 | needs: [build_ci_docker_image] 99 | strategy: 100 | fail-fast: false 101 | matrix: 102 | example-sketch: 103 | - ./examples/Example0_LibrarylessCommunication/Example0_LibrarylessCommunication.ino 104 | - ./examples/Example1_NotecardBasics/Example1_NotecardBasics.ino 105 | - ./examples/Example2_PeriodicCommunications/Example2_PeriodicCommunications.ino 106 | - ./examples/Example3_InboundPolling/Example3_InboundPolling.ino 107 | - ./examples/Example4_InboundInterrupts/Example4_InboundInterrupts.ino 108 | - ./examples/Example5_UsingTemplates/Example5_UsingTemplates.ino 109 | - ./examples/Example6_SensorTutorial/Example6_SensorTutorial.ino 110 | - ./examples/Example7_PowerControl/Example7_PowerControl.ino 111 | - ./examples/Example8_BinarySendReceive/Example8_BinarySendReceive.ino 112 | - ./examples/Example9_BinarySendReceiveChunked/Example9_BinarySendReceiveChunked.ino 113 | fully-qualified-board-name: 114 | - adafruit:nrf52:feather52840:softdevice=s140v6 115 | - adafruit:samd:adafruit_feather_m4 116 | # The binary examples don't fit in the Uno's flash. 117 | # - arduino:avr:uno 118 | # The mbed_nano platform generates compiler warning. 119 | # https://github.com/arduino/ArduinoCore-mbed/issues/1024 120 | # - arduino:mbed_nano:nano33ble 121 | - esp32:esp32:featheresp32 122 | - rp2040:rp2040:rpipico 123 | - SparkFun:apollo3:sfe_artemis_thing_plus 124 | - STMicroelectronics:stm32:Blues:pnum=CYGNET 125 | - STMicroelectronics:stm32:Blues:pnum=SWAN_R5 126 | - STMicroelectronics:stm32:GenF4:pnum=FEATHER_F405 127 | - STMicroelectronics:stm32:Nucleo_32:pnum=NUCLEO_L432KC 128 | 129 | steps: 130 | - name: Checkout code 131 | id: checkout 132 | uses: actions/checkout@v4 133 | 134 | - name: Load CI docker image 135 | if: ${{ needs.build_ci_docker_image.result == 'success' }} 136 | uses: ./.github/actions/load-ci-image 137 | 138 | - name: Compile Examples 139 | run: | 140 | docker run --rm --volume $(pwd):/note-arduino/ \ 141 | --workdir /note-arduino/ \ 142 | --entrypoint ./examples/build_example.sh \ 143 | ghcr.io/blues/note_arduino_ci:latest \ 144 | ${{ matrix.fully-qualified-board-name }} \ 145 | ${{ matrix.example-sketch }} 146 | 147 | publish_ci_image: 148 | runs-on: ubuntu-latest 149 | # Make sure tests passed and examples built successfully before publishing. 150 | needs: [build_ci_docker_image, run_tests, build_examples] 151 | # Only publish the image if this is a push event and the Docker image was rebuilt. 152 | if: ${{ github.event_name == 'push' && needs.build_ci_docker_image.result == 'success' }} 153 | 154 | steps: 155 | - name: Login to GitHub Container Registry 156 | uses: docker/login-action@v3 157 | with: 158 | registry: ghcr.io 159 | username: ${{ github.actor }} 160 | password: ${{ secrets.GITHUB_TOKEN }} 161 | 162 | - name: Set up Docker Buildx 163 | uses: docker/setup-buildx-action@v3 164 | 165 | - name: Push image to registry 166 | uses: docker/build-push-action@v6 167 | with: 168 | push: true 169 | tags: ghcr.io/blues/note_arduino_ci:latest 170 | -------------------------------------------------------------------------------- /.github/workflows/version-check.yml: -------------------------------------------------------------------------------- 1 | name: Version Check 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - 'src/NoteDefines.h' 8 | - 'library.properties' 9 | pull_request: 10 | branches: [ master ] 11 | paths: 12 | - 'src/NoteDefines.h' 13 | - 'library.properties' 14 | workflow_dispatch: 15 | 16 | jobs: 17 | check-version: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v3 22 | 23 | - name: Check version consistency 24 | run: | 25 | chmod +x scripts/check_version.sh 26 | ./scripts/check_version.sh 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.gch 3 | *.out 4 | *.gcda 5 | *.gcno 6 | *.gcov 7 | vgcore.* 8 | *.tests 9 | failed_test_run 10 | 11 | .vscode/ 12 | build/ 13 | coverage/ 14 | 15 | Doxyfile* 16 | doxygen_sqlite3.db 17 | html 18 | latex 19 | coverage.json 20 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "NOTE_MOCK" 10 | ], 11 | "compilerPath": "/usr/bin/gcc", 12 | "cStandard": "gnu11", 13 | "cppStandard": "gnu++11", 14 | "intelliSenseMode": "linux-gcc-x86" 15 | } 16 | ], 17 | "version": 4 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [], 5 | "unwantedRecommendations": [ 6 | "ms-vscode.cmake-tools", 7 | "ms-vscode.cpptools-extension-pack", 8 | "ms-azuretools.vscode-docker" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug Failed Unit-tests", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceRoot}/failed_test_run", 12 | "args": [], 13 | "cwd": "${workspaceRoot}", 14 | "MIMode": "gdb", 15 | "miDebuggerPath": "/usr/bin/gdb" 16 | }, 17 | { 18 | "name": "Debug Notecard Unit-tests", 19 | "type": "cppdbg", 20 | "request": "launch", 21 | "program": "${workspaceRoot}/notecard.tests", 22 | "args": [], 23 | "cwd": "${workspaceRoot}", 24 | "preLaunchTask": "Compile Notecard Tests", 25 | "postDebugTask": "Clear Test Binaries", 26 | "MIMode": "gdb", 27 | "miDebuggerPath": "/usr/bin/gdb" 28 | }, 29 | { 30 | "name": "Debug NoteI2c Arduino Unit-tests", 31 | "type": "cppdbg", 32 | "request": "launch", 33 | "program": "${workspaceRoot}/notei2c_arduino.tests", 34 | "args": [], 35 | "cwd": "${workspaceRoot}", 36 | "preLaunchTask": "Compile NoteI2c Arduino Tests", 37 | "postDebugTask": "Clear Test Binaries", 38 | "MIMode": "gdb", 39 | "miDebuggerPath": "/usr/bin/gdb" 40 | }, 41 | { 42 | "name": "Debug NoteI2c Arduino Unit-tests (-DWIRE_HAS_END)", 43 | "type": "cppdbg", 44 | "request": "launch", 45 | "program": "${workspaceRoot}/notei2c_arduino_wire_has_end.tests", 46 | "args": [], 47 | "cwd": "${workspaceRoot}", 48 | "preLaunchTask": "Compile NoteI2c Arduino Tests (-DWIRE_HAS_END)", 49 | "postDebugTask": "Clear Test Binaries", 50 | "MIMode": "gdb", 51 | "miDebuggerPath": "/usr/bin/gdb" 52 | }, 53 | { 54 | "name": "Debug NoteLog Arduino Unit-tests", 55 | "type": "cppdbg", 56 | "request": "launch", 57 | "program": "${workspaceRoot}/notelog_arduino.tests", 58 | "args": [], 59 | "cwd": "${workspaceRoot}", 60 | "preLaunchTask": "Compile NoteLog Arduino Tests", 61 | "postDebugTask": "Clear Test Binaries", 62 | "MIMode": "gdb", 63 | "miDebuggerPath": "/usr/bin/gdb" 64 | }, 65 | { 66 | "name": "Debug NoteSerial Arduino Unit-tests", 67 | "type": "cppdbg", 68 | "request": "launch", 69 | "program": "${workspaceRoot}/noteserial_arduino.tests", 70 | "args": [], 71 | "cwd": "${workspaceRoot}", 72 | "preLaunchTask": "Compile NoteSerial Arduino Tests", 73 | "postDebugTask": "Clear Test Binaries", 74 | "MIMode": "gdb", 75 | "miDebuggerPath": "/usr/bin/gdb" 76 | }, 77 | { 78 | "name": "Debug NoteTxn Arduino Unit-tests", 79 | "type": "cppdbg", 80 | "request": "launch", 81 | "program": "${workspaceRoot}/notetxn_arduino.tests", 82 | "args": [], 83 | "cwd": "${workspaceRoot}", 84 | "preLaunchTask": "Compile NoteTxn Arduino Tests", 85 | "postDebugTask": "Clear Test Binaries", 86 | "MIMode": "gdb", 87 | "miDebuggerPath": "/usr/bin/gdb" 88 | }, 89 | ] 90 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Compile and Run ALL Tests", 6 | "type": "cppbuild", 7 | "command": "./test/run_all_tests.sh", 8 | "args": [], 9 | "options": { 10 | "cwd": "${workspaceFolder}" 11 | }, 12 | "problemMatcher": [ 13 | "$gcc" 14 | ], 15 | "group": { 16 | "kind": "build", 17 | "isDefault": true 18 | } 19 | }, 20 | { 21 | "label": "Compile Notecard Tests", 22 | "type": "cppbuild", 23 | "command": "g++", 24 | "args": [ 25 | "-Wall", 26 | "-Wextra", 27 | "-Wpedantic", 28 | "test/mock/mock-arduino.cpp", 29 | "test/mock/mock-note-c-note.c", 30 | "test/mock/NoteI2c_Mock.cpp", 31 | "test/mock/NoteLog_Mock.cpp", 32 | "test/mock/NoteSerial_Mock.cpp", 33 | "test/mock/NoteTime_Mock.cpp", 34 | "test/mock/NoteTxn_Mock.cpp", 35 | "src/Notecard.cpp", 36 | "test/Notecard.test.cpp", 37 | "-std=c++11", 38 | "-Itest", 39 | "-Isrc", 40 | "-DNOTE_MOCK", 41 | "-o", 42 | "notecard.tests", 43 | "-g", 44 | ], 45 | "options": { 46 | "cwd": "${workspaceFolder}" 47 | }, 48 | "problemMatcher": [ 49 | "$gcc" 50 | ], 51 | "group": { 52 | "kind": "build", 53 | "isDefault": false 54 | } 55 | }, 56 | { 57 | "label": "Compile NoteI2c Arduino Tests", 58 | "type": "cppbuild", 59 | "command": "g++", 60 | "args": [ 61 | "-Wall", 62 | "-Wextra", 63 | "-Wpedantic", 64 | "test/mock/mock-arduino.cpp", 65 | "test/mock/mock-note-c-note.c", 66 | "src/NoteI2c_Arduino.cpp", 67 | "test/NoteI2c_Arduino.test.cpp", 68 | "-std=c++11", 69 | "-Itest", 70 | "-Isrc", 71 | "-DNOTE_MOCK", 72 | "-o", 73 | "notei2c_arduino.tests", 74 | "-g", 75 | ], 76 | "options": { 77 | "cwd": "${workspaceFolder}" 78 | }, 79 | "problemMatcher": [ 80 | "$gcc" 81 | ], 82 | "group": { 83 | "kind": "build", 84 | "isDefault": false 85 | } 86 | }, 87 | { 88 | "label": "Compile NoteI2c Arduino Tests (-DWIRE_HAS_END)", 89 | "type": "cppbuild", 90 | "command": "g++", 91 | "args": [ 92 | "-Wall", 93 | "-Wextra", 94 | "-Wpedantic", 95 | "test/mock/mock-arduino.cpp", 96 | "test/mock/mock-note-c-note.c", 97 | "src/NoteI2c_Arduino.cpp", 98 | "test/NoteI2c_Arduino.test.cpp", 99 | "-std=c++11", 100 | "-Itest", 101 | "-Isrc", 102 | "-DNOTE_MOCK", 103 | "-DWIRE_HAS_END", 104 | "-o", 105 | "notei2c_arduino_wire_has_end.tests", 106 | "-g", 107 | ], 108 | "options": { 109 | "cwd": "${workspaceFolder}" 110 | }, 111 | "problemMatcher": [ 112 | "$gcc" 113 | ], 114 | "group": { 115 | "kind": "build", 116 | "isDefault": false 117 | } 118 | }, 119 | { 120 | "label": "Compile NoteLog Arduino Tests", 121 | "type": "cppbuild", 122 | "command": "g++", 123 | "args": [ 124 | "-Wall", 125 | "-Wextra", 126 | "-Wpedantic", 127 | "test/mock/mock-arduino.cpp", 128 | "test/mock/mock-note-c-note.c", 129 | "src/NoteLog_Arduino.cpp", 130 | "test/NoteLog_Arduino.test.cpp", 131 | "-std=c++11", 132 | "-Itest", 133 | "-Isrc", 134 | "-DNOTE_MOCK", 135 | "-o", 136 | "notelog_arduino.tests", 137 | "-g", 138 | ], 139 | "options": { 140 | "cwd": "${workspaceFolder}" 141 | }, 142 | "problemMatcher": [ 143 | "$gcc" 144 | ], 145 | "group": { 146 | "kind": "build", 147 | "isDefault": false 148 | } 149 | }, 150 | { 151 | "label": "Compile NoteSerial Arduino Tests", 152 | "type": "cppbuild", 153 | "command": "g++", 154 | "args": [ 155 | "-Wall", 156 | "-Wextra", 157 | "-Wpedantic", 158 | "test/mock/mock-arduino.cpp", 159 | "test/mock/mock-note-c-note.c", 160 | "src/NoteSerial_Arduino.cpp", 161 | "test/NoteSerial_Arduino.test.cpp", 162 | "-std=c++11", 163 | "-Itest", 164 | "-Isrc", 165 | "-DNOTE_MOCK", 166 | "-o", 167 | "noteserial_arduino.tests", 168 | "-g", 169 | ], 170 | "options": { 171 | "cwd": "${workspaceFolder}" 172 | }, 173 | "problemMatcher": [ 174 | "$gcc" 175 | ], 176 | "group": { 177 | "kind": "build", 178 | "isDefault": false 179 | } 180 | }, 181 | { 182 | "label": "Compile NoteTxn Arduino Tests", 183 | "type": "cppbuild", 184 | "command": "g++", 185 | "args": [ 186 | "-Wall", 187 | "-Wextra", 188 | "-Wpedantic", 189 | "test/mock/mock-arduino.cpp", 190 | "test/mock/mock-note-c-note.c", 191 | "src/NoteTxn_Arduino.cpp", 192 | "test/NoteTxn_Arduino.test.cpp", 193 | "-std=c++11", 194 | "-Itest", 195 | "-Isrc", 196 | "-DNOTE_MOCK", 197 | "-o", 198 | "notetxn_arduino.tests", 199 | "-g", 200 | ], 201 | "options": { 202 | "cwd": "${workspaceFolder}" 203 | }, 204 | "problemMatcher": [ 205 | "$gcc" 206 | ], 207 | "group": { 208 | "kind": "build", 209 | "isDefault": false 210 | } 211 | }, 212 | { 213 | "label": "Clear Test Binaries", 214 | "type": "shell", 215 | "command": "rm -rf *.tests", 216 | "options": { 217 | "cwd": "${workspaceFolder}" 218 | }, 219 | "problemMatcher": [ 220 | "$gcc" 221 | ], 222 | }, 223 | ] 224 | } 225 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | By participating in this project, you agree to abide by the 4 | [Blues Inc code of conduct][1]. 5 | 6 | [1]: https://blues.github.io/opensource/code-of-conduct 7 | 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to blues/note-arduino 2 | 3 | We love pull requests from everyone. By participating in this project, you 4 | agree to abide by the Blues Inc [code of conduct]. 5 | 6 | [code of conduct]: https://blues.github.io/opensource/code-of-conduct 7 | 8 | Here are some ways *you* can contribute: 9 | 10 | * by using alpha, beta, and prerelease versions 11 | * by reporting bugs 12 | * by suggesting new features 13 | * by writing or editing documentation 14 | * by writing specifications 15 | * by writing code ( **no patch is too small** : fix typos, add comments, 16 | clean up inconsistent whitespace ) 17 | * by refactoring code 18 | * by closing [issues][] 19 | * by reviewing patches 20 | 21 | [issues]: https://github.com/blues/note-arduino/issues 22 | 23 | ## Submitting an Issue 24 | 25 | * We use the [GitHub issue tracker][issues] to track bugs and features. 26 | * Before submitting a bug report or feature request, check to make sure it 27 | hasn't 28 | already been submitted. 29 | * When submitting a bug report, please include a [Gist][] that includes a stack 30 | trace and any details that may be necessary to reproduce the bug, including 31 | your release version, stack, and operating system. Ideally, a bug report 32 | should include a pull request with failing specs. 33 | 34 | [gist]: https://gist.github.com/ 35 | 36 | ## Cleaning up issues 37 | 38 | * Issues that have no response from the submitter will be closed after 30 days. 39 | * Issues will be closed once they're assumed to be fixed or answered. If the 40 | maintainer is wrong, it can be opened again. 41 | * If your issue is closed by mistake, please understand and explain the issue. 42 | We will happily reopen the issue. 43 | 44 | ## Submitting a Pull Request 45 | 46 | 1. [Fork][fork] the [official repository][repo]. 47 | 2. [Create a topic branch.][branch] 48 | 3. Implement your feature or bug fix. 49 | 4. Add, commit, and push your changes. 50 | 5. [Submit a pull request.][pr] 51 | 52 | ## Notes 53 | 54 | * Please add tests if you changed code. Contributions without tests won't be accepted. 55 | * If you don't know how to add tests, please put in a PR and leave a comment asking for help. 56 | We love helping! 57 | 58 | [repo]: https://github.com/blues/note-arduino/tree/master 59 | [fork]: https://help.github.com/articles/fork-a-repo/ 60 | [branch]: 61 | https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/ 62 | [pr]: https://help.github.com/articles/creating-a-pull-request-from-a-fork/ 63 | 64 | Inspired by 65 | https://github.com/thoughtbot/factory_bot/blob/master/CONTRIBUTING.md 66 | 67 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Blues Inc. All rights reserved. 2 | # Use of this source code is governed by licenses granted by the 3 | # copyright holder including that found in the LICENSE file. 4 | 5 | # Build development environment 6 | # docker build --file Dockerfile.arduino-cli --tag arduino-cli . 7 | 8 | # Launch development environment 9 | # docker run --entrypoint bash --interactive --rm --tty --volume "$(pwd)":/host-volume/ --workdir /host-volume/ arduino-cli 10 | 11 | # Define global arguments 12 | ARG ARDUINO_CLI_VERSION=1.1.1 13 | ARG DEBIAN_FRONTEND="noninteractive" 14 | ARG UID=1000 15 | ARG USER="blues" 16 | 17 | # POSIX compatible (Linux/Unix) base image 18 | FROM debian:bookworm-slim 19 | 20 | # Import global arguments 21 | ARG ARDUINO_CLI_VERSION 22 | ARG DEBIAN_FRONTEND 23 | ARG UID 24 | ARG USER 25 | 26 | # Define local arguments 27 | ARG BINDIR=/usr/local/bin 28 | ARG ECHO_BC_FILE='$bcfile' 29 | 30 | # Define environment variables 31 | ENV ARDUINO_UPDATER_ENABLE_NOTIFICATION=false 32 | 33 | # Create Non-Root User 34 | RUN ["dash", "-c", "\ 35 | addgroup \ 36 | --gid ${UID} \ 37 | \"${USER}\" \ 38 | && adduser \ 39 | --disabled-password \ 40 | --gecos \"\" \ 41 | --ingroup \"${USER}\" \ 42 | --uid ${UID} \ 43 | \"${USER}\" \ 44 | && usermod \ 45 | --append \ 46 | --groups \"dialout,plugdev\" \ 47 | \"${USER}\" \ 48 | "] 49 | 50 | # Establish development environment 51 | RUN ["dash", "-c", "\ 52 | apt-get update --quiet \ 53 | && apt-get install --assume-yes --no-install-recommends --quiet \ 54 | bash \ 55 | bash-completion \ 56 | ca-certificates \ 57 | curl \ 58 | g++ \ 59 | gdb \ 60 | git \ 61 | gzip \ 62 | lcov \ 63 | nano \ 64 | python-is-python3 \ 65 | python3 \ 66 | python3-pip \ 67 | ssh \ 68 | tree \ 69 | valgrind \ 70 | && pip install --break-system-packages \ 71 | adafruit-nrfutil \ 72 | pyserial \ 73 | && apt-get clean \ 74 | && apt-get purge \ 75 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ 76 | "] 77 | 78 | # Download/Install Arduino CLI 79 | RUN ["dash", "-c", "\ 80 | curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=${BINDIR} sh -s ${ARDUINO_CLI_VERSION} \ 81 | && mkdir -p /etc/bash_completion.d/ \ 82 | && arduino-cli completion bash > /etc/bash_completion.d/arduino-cli.sh \ 83 | && echo >> /etc/bash.bashrc \ 84 | && echo \"for bcfile in /etc/bash_completion.d/* ; do\" >> /etc/bash.bashrc \ 85 | && echo \" [ -f \\\"${ECHO_BC_FILE}\\\" ] && . \\\"${ECHO_BC_FILE}\\\"\" >> /etc/bash.bashrc \ 86 | && echo \"done\" >> /etc/bash.bashrc \ 87 | && echo \"if [ -f /etc/bash_completion ]; then\" >> /etc/bash.bashrc \ 88 | && echo \" . /etc/bash_completion\" >> /etc/bash.bashrc \ 89 | && echo \"fi\" >> /etc/bash.bashrc \ 90 | "] 91 | 92 | # Configure Arduino CLI 93 | USER ${USER} 94 | RUN ["dash", "-c", "\ 95 | arduino-cli config init \ 96 | && arduino-cli config add board_manager.additional_urls \ 97 | https://raw.githubusercontent.com/stm32duino/BoardManagerFiles/main/package_stmicroelectronics_index.json \ 98 | https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json \ 99 | https://raw.githubusercontent.com/sparkfun/Arduino_Apollo3/main/package_sparkfun_apollo3_index.json \ 100 | https://raw.githubusercontent.com/adafruit/arduino-board-index/gh-pages/package_adafruit_index.json \ 101 | https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json \ 102 | && arduino-cli core update-index \ 103 | && arduino-cli core install adafruit:nrf52 \ 104 | && arduino-cli core install adafruit:samd \ 105 | && arduino-cli core install arduino:avr \ 106 | && arduino-cli core install arduino:mbed_nano \ 107 | && arduino-cli core install arduino:samd \ 108 | && arduino-cli core install esp32:esp32 \ 109 | && arduino-cli core install rp2040:rp2040 \ 110 | && arduino-cli core install SparkFun:apollo3 \ 111 | && arduino-cli core install STMicroelectronics:stm32 \ 112 | && arduino-cli lib install \"Blues Wireless Notecard Pseudo Sensor\" \ 113 | "] 114 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Blues Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /assets/blues-wireless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blues/note-arduino/9d78fc7dfdfbff969a95b671e4ba2d711dcb6891/assets/blues-wireless.png -------------------------------------------------------------------------------- /examples/Example0_LibrarylessCommunication/Example0_LibrarylessCommunication.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This is the simplest example of how a device may send commands to the 7 | // Notecard over a serial port by using nothing but simple "print line" 8 | // functions targeting that Arduino serial port. 9 | // 10 | // If the Notecard is connected to a serial port, define it here. For example, 11 | // if you are using the Adafruit Feather NRF52840 Express, the RX/TX pins (and 12 | // thus the Notecard) are on Serial1. If however you are using an M5Stack Basic 13 | // Core IoT Development Kit, you would connect the R2 pin to the Notecard's TX 14 | // pin, and the M5Stack's T2 pin to the Notecard's RX pin, and then would 15 | // use Serial2. 16 | 17 | #include "Arduino.h" 18 | 19 | #if defined(ARDUINO_ARCH_AVR) && not defined(HAVE_HWSERIAL1) 20 | #define txRxPinsSerial Serial 21 | #elif defined(ARDUINO_ARCH_STM32) && not defined(HAVE_HWSERIAL1) 22 | #define txRxPinsSerial Serial 23 | #else 24 | #define txRxPinsSerial Serial1 25 | #endif 26 | 27 | #ifdef ARDUINO_NRF52840_FEATHER 28 | #include "Adafruit_TinyUSB.h" 29 | #endif 30 | 31 | // This is the unique Product Identifier for your device. This Product ID tells 32 | // the Notecard what type of device has embedded the Notecard, and by extension 33 | // which vendor or customer is in charge of "managing" it. In order to set this 34 | // value, you must first register with notehub.io and "claim" a unique product 35 | // ID for your device. It could be something as simple as as your email address 36 | // in reverse, such as "com.gmail.smith.lisa.test-device" or 37 | // "com.outlook.gates.bill.demo" 38 | 39 | // This is the unique Product Identifier for your device 40 | #ifndef PRODUCT_UID 41 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 42 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 43 | #endif 44 | 45 | #define myProductID PRODUCT_UID 46 | #define myLiveDemo true 47 | 48 | // One-time Arduino initialization 49 | void setup() 50 | { 51 | // Initialize the serial port being used by the Notecard, and send a newline 52 | // to clear out any data that the Arduino software may have pending so that 53 | // we always start sending commands "cleanly". By delaying for 250ms, we 54 | // ensure the any pending commands can be processed or discarded. We use the 55 | // speed of 9600 because the Notecard's RX/TX pins are always configured for 56 | // that speed. 57 | txRxPinsSerial.begin(9600); 58 | txRxPinsSerial.println("\n"); 59 | delay(250); 60 | 61 | // This command (required) causes the data to be delivered to the Project on 62 | // notehub.io that has claimed this Product ID (see above). 63 | if (myProductID[0]) 64 | { 65 | txRxPinsSerial.println("{\"cmd\":\"hub.set\",\"product\":\"" myProductID "\"}"); 66 | } 67 | 68 | // This command determines how often the Notecard connects to the service. 69 | // If "continuous" the Notecard immediately establishes a session with the 70 | // service at notehub.io, and keeps it active continuously. Because of the 71 | // power requirements of a continuous connection, a battery powered device 72 | // would instead only sample its sensors occasionally, and would only upload 73 | // to the service on a periodic basis. 74 | #if myLiveDemo 75 | txRxPinsSerial.println("{\"cmd\":\"hub.set\",\"mode\":\"continuous\"}"); 76 | #else 77 | txRxPinsSerial.println("{\"cmd\":\"hub.set\",\"mode\":\"periodic\",\"outbound\":60}"); 78 | #endif 79 | } 80 | 81 | // In the Arduino main loop which is called repeatedly, add outbound data every 82 | // 15 seconds if `myLiveDemo` is `true`, or 15 minutes if `false`. 83 | void loop() 84 | { 85 | // Count the simulated measurements that we send to the cloud, and stop the 86 | // demo before long. 87 | static unsigned eventCounter = 0; 88 | if (++eventCounter > 25) 89 | { 90 | delay(10000); // 10 seconds 91 | return; 92 | } 93 | 94 | // Simulate a temperature reading, between 5.0 and 35.0 degrees C 95 | double temperature = (double)random(50, 350) / 10.0; 96 | 97 | // Simulate a voltage reading, between 3.1 and 4.2 degrees 98 | double voltage = (double)random(31, 42) / 10.0; 99 | 100 | // Add a "note" to the Notecard, in the default data notefile. The "body" 101 | // of the note is JSON object completely of our own design, and is passed 102 | // straight through as-is to notehub.io. 103 | // Note that we add the "sync" flag for demonstration purposes to upload the 104 | // data instantaneously, so that if you are looking at this on notehub.io 105 | // you will see the data appearing 'live'. 106 | // Note that we use a somewhat convoluted way of displaying a floating point 107 | // number because `%f` isn't supported in many versions of Arduino (newlib). 108 | char message[150]; 109 | snprintf(message, sizeof(message), 110 | "{" 111 | "\"cmd\":\"note.add\"" 112 | "," 113 | "\"sync\":true" 114 | "," 115 | "\"body\":{\"temp\":%d.%02d,\"voltage\":%d.%02d,\"count\":%d}" 116 | "}", 117 | (int)temperature, abs(((int)(temperature * 100.0) % 100)), 118 | (int)voltage, (int)(voltage * 100.0) % 100, 119 | eventCounter); 120 | txRxPinsSerial.println(message); 121 | 122 | // Delay between simulated measurements 123 | #if myLiveDemo 124 | delay(15 * 1000); // 15 seconds 125 | #else 126 | delay(15 * 60 * 1000); // 15 minutes 127 | #endif 128 | } 129 | -------------------------------------------------------------------------------- /examples/Example1_NotecardBasics/Example1_NotecardBasics.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This example does the same function as the "basic" example, but demonstrates 7 | // how easy it is to use the Notecard libraries to construct JSON commands and 8 | // also to extract responses. 9 | // 10 | // Using the Notecard library, you can also easily set up your Arduino 11 | // environment to "watch" JSON request and response traffic going to/from the 12 | // Notecard on your Arduino debug port. 13 | // 14 | // Note that by using the Notecard library, it is also quite easy to connect the 15 | // Notecard to a Microcontroller's I2C ports (SDA and SCL) rather than using 16 | // Serial, in case there is no unused serial port available to use for the 17 | // Notecard. 18 | 19 | // Include the Arduino library for the Notecard 20 | #include 21 | 22 | // If the Notecard is connected to a serial port, define it here. For example, 23 | // if you are using the Adafruit Feather NRF52840 Express, the RX/TX pins (and 24 | // thus the Notecard) are on Serial1. However, if you are using an M5Stack Basic 25 | // Core IoT Development Kit, you would connect the R2 pin to the Notecard's TX 26 | // pin, and the M5Stack's T2 pin to the Notecard's RX pin, and then would use 27 | // Serial2. 28 | // 29 | // Also, you may define a debug output port where you can watch transaction as 30 | // they are sent to and from the Notecard. When using the Arduino IDE this is 31 | // typically "Serial", but you can use any available port. 32 | // 33 | // Note that both of these definitions are optional; just prefix either line 34 | // with `//` to remove it. 35 | // 36 | // - Remove `txRxPinsSerial` if you wired your Notecard using I2C SDA/SCL pins, 37 | // instead of serial RX/TX. 38 | // - Remove `usbSerial` if you don't want the Notecard library to output debug 39 | // information. 40 | 41 | // #define txRxPinsSerial Serial1 42 | #define usbSerial Serial 43 | 44 | // This is the unique Product Identifier for your device. This Product ID tells 45 | // the Notecard what type of device has embedded the Notecard, and by extension 46 | // which vendor or customer is in charge of "managing" it. In order to set this 47 | // value, you must first register with notehub.io and "claim" a unique product 48 | // ID for your device. It could be something as simple as as your email address 49 | // in reverse, such as "com.gmail.smith.lisa:test-device" or 50 | // "com.outlook.gates.bill.demo" 51 | 52 | // This is the unique Product Identifier for your device 53 | #ifndef PRODUCT_UID 54 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 55 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 56 | #endif 57 | 58 | #define myProductID PRODUCT_UID 59 | Notecard notecard; 60 | 61 | // One-time Arduino initialization 62 | void setup() 63 | { 64 | // Set up for debug output (if available). 65 | #ifdef usbSerial 66 | // If you open Arduino's serial terminal window, you'll be able to watch 67 | // JSON objects being transferred to and from the Notecard for each request. 68 | usbSerial.begin(115200); 69 | const size_t usb_timeout_ms = 3000; 70 | for (const size_t start_ms = millis(); !usbSerial && (millis() - start_ms) < usb_timeout_ms;) 71 | ; 72 | 73 | // For low-memory platforms, don't turn on internal Notecard logs. 74 | #ifndef NOTE_C_LOW_MEM 75 | notecard.setDebugOutputStream(usbSerial); 76 | #else 77 | #pragma message("INFO: Notecard debug logs disabled. (non-fatal)") 78 | #endif // !NOTE_C_LOW_MEM 79 | #endif // usbSerial 80 | 81 | // Initialize the physical I/O channel to the Notecard 82 | #ifdef txRxPinsSerial 83 | notecard.begin(txRxPinsSerial, 9600); 84 | #else 85 | notecard.begin(); 86 | #endif 87 | 88 | // "newRequest()" uses the bundled "J" json package to allocate a "req", 89 | // which is a JSON object for the request to which we will then add Request 90 | // arguments. The function allocates a "req" request structure using 91 | // malloc() and initializes its "req" field with the type of request. 92 | J *req = notecard.newRequest("hub.set"); 93 | 94 | // This command (required) causes the data to be delivered to the Project 95 | // on notehub.io that has claimed this Product ID (see above). 96 | if (myProductID[0]) 97 | { 98 | JAddStringToObject(req, "product", myProductID); 99 | } 100 | 101 | // This command determines how often the Notecard connects to the service. 102 | // If "continuous", the Notecard immediately establishes a session with the 103 | // service at notehub.io, and keeps it active continuously. Due to the power 104 | // requirements of a continuous connection, a battery powered device would 105 | // instead only sample its sensors occasionally, and would only upload to 106 | // the service on a "periodic" basis. 107 | JAddStringToObject(req, "mode", "continuous"); 108 | 109 | // Issue the request, telling the Notecard how and how often to access the 110 | // service. 111 | // This results in a JSON message to Notecard formatted like: 112 | // { 113 | // "req" : "service.set", 114 | // "product" : myProductID, 115 | // "mode" : "continuous" 116 | // } 117 | // Note that `notecard.sendRequestWithRetry()` always frees the request data 118 | // structure, and it returns "true" if success or "false" if there is any 119 | // failure. It is important to use `sendRequestWithRetry()` on the first 120 | // message from the MCU to the Notecard, because there will always be a 121 | // hardware race condition on cold boot and the Notecard must be ready to 122 | // receive and process the message. 123 | notecard.sendRequestWithRetry(req, 5); // 5 seconds 124 | } 125 | 126 | // In the Arduino main loop which is called repeatedly, add outbound data every 127 | // 15 seconds 128 | void loop() 129 | { 130 | 131 | // Count the simulated measurements that we send to the cloud, and stop the 132 | // demo before long. 133 | static unsigned eventCounter = 0; 134 | if (++eventCounter > 25) 135 | { 136 | usbSerial.println("[APP] Demo cycle complete. Program stopped. Press RESET to restart."); 137 | delay(10000); // 10 seconds 138 | return; 139 | } 140 | 141 | // Rather than simulating a temperature reading, use a Notecard request to 142 | // read the temp from the Notecard's built-in temperature sensor. We use 143 | // `requestAndResponse()` to indicate that we would like to examine the 144 | // response of the transaction. This method takes a JSON data structure, 145 | // "request" as input, then processes it and returns a JSON data structure, 146 | // "response", with the response. Note that because the Notecard library 147 | // uses malloc(), developers must always check for `NULL` to ensure that 148 | // there was enough memory available on the microcontroller to satisfy the 149 | // allocation request. 150 | double temperature = 0; 151 | J *rsp = notecard.requestAndResponse(notecard.newRequest("card.temp")); 152 | if (rsp != NULL) 153 | { 154 | temperature = JGetNumber(rsp, "value"); 155 | notecard.deleteResponse(rsp); 156 | } 157 | 158 | // Do the same to retrieve the voltage that is detected by the Notecard on 159 | // its `V+` pin. 160 | double voltage = 0; 161 | rsp = notecard.requestAndResponse(notecard.newRequest("card.voltage")); 162 | if (rsp != NULL) 163 | { 164 | voltage = JGetNumber(rsp, "value"); 165 | notecard.deleteResponse(rsp); 166 | } 167 | 168 | // Enqueue the measurement to the Notecard for transmission to the Notehub, 169 | // adding the "sync" flag for demonstration purposes to upload the data 170 | // instantaneously. If you are looking at this on notehub.io you will see 171 | // the data appearing 'live'. 172 | J *req = notecard.newRequest("note.add"); 173 | if (req != NULL) 174 | { 175 | JAddBoolToObject(req, "sync", true); 176 | J *body = JAddObjectToObject(req, "body"); 177 | if (body != NULL) 178 | { 179 | JAddNumberToObject(body, "temp", temperature); 180 | JAddNumberToObject(body, "voltage", voltage); 181 | JAddNumberToObject(body, "count", eventCounter); 182 | } 183 | notecard.sendRequest(req); 184 | } 185 | 186 | // Delay between samples 187 | delay(15 * 1000); // 15 seconds 188 | } 189 | -------------------------------------------------------------------------------- /examples/Example2_PeriodicCommunications/Example2_PeriodicCommunications.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This example does the same function as the "using library" example, but 7 | // rather than keeping the modem turned on constantly this example demonstrates 8 | // how a developer would gather sensor measurements "offline", then perform 9 | // uploads on a periodic basis. 10 | 11 | // Include the Arduino library for the Notecard 12 | #include 13 | #include 14 | 15 | #define ledPin LED_BUILTIN 16 | 17 | // Define the pin number of the pushbutton pin 18 | #define buttonPin B0 19 | #define buttonPressedState LOW 20 | 21 | #if defined(ARDUINO_FEATHER_F405) 22 | #define NON_AF_COMPAT_FEATHER 23 | #elif defined(ARDUINO_ARCH_APOLLO3) 24 | // #undef buttonPin 25 | // #define buttonPin 10 26 | #elif defined(ARDUINO_NUCLEO_L432KC) 27 | #define BREADBOARD_REQUIRED 28 | #elif defined(ARDUINO_ARCH_STM32) 29 | #undef buttonPin 30 | #define buttonPin USER_BTN 31 | #elif defined(ARDUINO_NRF52840_FEATHER) 32 | #undef buttonPin 33 | #define buttonPin 7 34 | #elif defined(ARDUINO_RASPBERRY_PI_PICO) 35 | #define BREADBOARD_REQUIRED 36 | #elif defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MBED) 37 | #define BREADBOARD_REQUIRED 38 | #endif 39 | 40 | #ifdef NON_AF_COMPAT_FEATHER 41 | #pragma message("This feather does not support the Notecarrier-AF button, additional hardware is required.") 42 | #undef buttonPin 43 | #define buttonPin A5 44 | #endif 45 | #ifdef BREADBOARD_REQUIRED 46 | #pragma message("The board selected requires additional hardware.") 47 | #undef buttonPin 48 | #define buttonPin 3 49 | #endif 50 | 51 | // Note that both of these definitions are optional; just prefix either line 52 | // with `//` to remove it. 53 | // - Remove txRxPinsSerial if you wired your Notecard using I2C SDA/SCL pins 54 | // instead of serial RX/TX 55 | // - Remove usbSerial if you don't want the Notecard library to output debug 56 | // information 57 | 58 | // #define txRxPinsSerial Serial1 59 | #define usbSerial Serial 60 | 61 | // This is the unique Product Identifier for your device 62 | #ifndef PRODUCT_UID 63 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 64 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 65 | #endif 66 | 67 | #define myProductID PRODUCT_UID 68 | Notecard notecard; 69 | 70 | // Button handling 71 | #define BUTTON_IDLE 0 72 | #define BUTTON_PRESS 1 73 | #define BUTTON_DOUBLEPRESS 2 74 | int buttonPress(void); 75 | 76 | // One-time Arduino initialization 77 | void setup() 78 | { 79 | // Initialize Arduino GPIO pins 80 | pinMode(ledPin, OUTPUT); 81 | pinMode(buttonPin, buttonPressedState == LOW ? INPUT_PULLUP : INPUT); 82 | 83 | // Set up for debug output (if available). 84 | #ifdef usbSerial 85 | // If you open Arduino's serial terminal window, you'll be able to watch 86 | // JSON objects being transferred to and from the Notecard for each request. 87 | usbSerial.begin(115200); 88 | const size_t usb_timeout_ms = 3000; 89 | for (const size_t start_ms = millis(); !usbSerial && (millis() - start_ms) < usb_timeout_ms;) 90 | ; 91 | 92 | // For low-memory platforms, don't turn on internal Notecard logs. 93 | #ifndef NOTE_C_LOW_MEM 94 | notecard.setDebugOutputStream(usbSerial); 95 | #else 96 | #pragma message("INFO: Notecard debug logs disabled. (non-fatal)") 97 | #endif // !NOTE_C_LOW_MEM 98 | #endif // usbSerial 99 | 100 | // Initialize the physical I/O channel to the Notecard 101 | #ifdef txRxPinsSerial 102 | notecard.begin(txRxPinsSerial, 9600); 103 | #else 104 | notecard.begin(); 105 | #endif 106 | 107 | // Service configuration request 108 | J *req = notecard.newRequest("hub.set"); 109 | 110 | // This command (required) causes the data to be delivered to the Project on 111 | // notehub.io that has claimed this Product ID (see above). 112 | if (myProductID[0]) 113 | { 114 | JAddStringToObject(req, "product", myProductID); 115 | } 116 | 117 | // This sets the notecard's connectivity mode to "periodic", rather than 118 | // being continuously connected. 119 | JAddStringToObject(req, "mode", "periodic"); 120 | 121 | // This parameter establishes how often, in minutes, the Notecard will check 122 | // for data that is waiting to be uploaded to the service. Generally this 123 | // might be something like 60 minutes, or perhaps even 12 hours * 60 min = 124 | // 720 min. For the purpose of this demonstration, however, we'll set the 125 | // period such that it checks for outgoing data at most every 2 minutes. 126 | JAddNumberToObject(req, "outbound", 2); 127 | 128 | // This parameter establishes how often, in minutes, the Notecard will check 129 | // for data that is waiting on the service to be downloaded to the Notecard. 130 | // Generally this might be 12, 24, or even 48 hours, however for the 131 | // purpose of this demonstration we will connect to the service to check for 132 | // incoming data at least once every hour. 133 | JAddNumberToObject(req, "inbound", 60); 134 | 135 | // Issue the request 136 | notecard.sendRequestWithRetry(req, 5); // 5 seconds 137 | } 138 | 139 | // In the Arduino main loop which is called repeatedly 140 | void loop() 141 | { 142 | static unsigned long lastStatusMs = 0; 143 | 144 | // Activity indicator 145 | digitalWrite(ledPin, HIGH); 146 | 147 | // Wait for a button press, or perform idle activities 148 | int buttonState = buttonPress(); 149 | switch (buttonState) 150 | { 151 | 152 | case BUTTON_IDLE: 153 | if (notecard.debugSyncStatus(2500, 0)) 154 | { 155 | lastStatusMs = millis(); 156 | } 157 | if (millis() > lastStatusMs + 10000) 158 | { 159 | lastStatusMs = millis(); 160 | usbSerial.println("[APP] press button to simulate a sensor measurement"); 161 | } 162 | delay(25); 163 | digitalWrite(ledPin, LOW); 164 | delay(100); 165 | return; 166 | 167 | case BUTTON_DOUBLEPRESS: 168 | notecard.requestAndResponse(notecard.newRequest("hub.sync")); 169 | digitalWrite(ledPin, LOW); 170 | return; 171 | } 172 | 173 | // The button was pressed, so we should begin a transaction 174 | usbSerial.println("[APP] performing sensor measurement"); 175 | lastStatusMs = millis(); 176 | 177 | // Read the notecard's current temperature and voltage, as simulated sensor 178 | // measurements 179 | double temperature = 0; 180 | J *rsp = notecard.requestAndResponse(notecard.newRequest("card.temp")); 181 | if (rsp != NULL) 182 | { 183 | temperature = JGetNumber(rsp, "value"); 184 | notecard.deleteResponse(rsp); 185 | } 186 | double voltage = 0; 187 | rsp = notecard.requestAndResponse(notecard.newRequest("card.voltage")); 188 | if (rsp != NULL) 189 | { 190 | voltage = JGetNumber(rsp, "value"); 191 | notecard.deleteResponse(rsp); 192 | } 193 | 194 | // Enqueue the measurement to the Notecard for transmission to the Notehub. 195 | // These measurements will be staged in the Notecard's flash memory until 196 | // it's time to transmit them to the service. 197 | J *req = notecard.newRequest("note.add"); 198 | if (req != NULL) 199 | { 200 | J *body = JAddObjectToObject(req, "body"); 201 | if (body != NULL) 202 | { 203 | JAddNumberToObject(body, "temp", temperature); 204 | JAddNumberToObject(body, "voltage", voltage); 205 | } 206 | notecard.sendRequest(req); 207 | } 208 | 209 | // Done with transaction 210 | digitalWrite(ledPin, LOW); 211 | } 212 | 213 | // Button handling 214 | int buttonPress() 215 | { 216 | // Detect the "press" transition 217 | static bool buttonBeingDebounced = false; 218 | int buttonState = digitalRead(buttonPin); 219 | if (buttonState != buttonPressedState) 220 | { 221 | if (buttonBeingDebounced) 222 | { 223 | buttonBeingDebounced = false; 224 | } 225 | return BUTTON_IDLE; 226 | } 227 | if (buttonBeingDebounced) 228 | { 229 | return BUTTON_IDLE; 230 | } 231 | 232 | // Wait to see if this is a double-press 233 | bool buttonDoublePress = false; 234 | bool buttonReleased = false; 235 | unsigned long buttonPressedMs = millis(); 236 | unsigned long ignoreBounceMs = 100; 237 | unsigned long doublePressMs = 750; 238 | while (millis() < buttonPressedMs + doublePressMs || digitalRead(buttonPin) == buttonPressedState) 239 | { 240 | if (millis() < buttonPressedMs + ignoreBounceMs) 241 | { 242 | continue; 243 | } 244 | if (digitalRead(buttonPin) != buttonPressedState) 245 | { 246 | if (!buttonReleased) 247 | { 248 | buttonReleased = true; 249 | } 250 | continue; 251 | } 252 | if (buttonReleased) 253 | { 254 | buttonDoublePress = true; 255 | if (digitalRead(buttonPin) != buttonPressedState) 256 | { 257 | break; 258 | } 259 | } 260 | } 261 | 262 | return (buttonDoublePress ? BUTTON_DOUBLEPRESS : BUTTON_PRESS); 263 | } -------------------------------------------------------------------------------- /examples/Example3_InboundPolling/Example3_InboundPolling.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This example shows the simplest possible method demonstrating how a device 7 | // might poll a notefile used as an "inbound queue", using it to receive 8 | // messages sent to the device from the service. The message gets into the 9 | // service by use of the Notehub's HTTP/HTTPS inbound request capability. 10 | // 11 | // In order to use this example, 12 | // 1. Get the device up and running the code below, successfully connecting to 13 | // the servie 14 | // 2. Use the "Devices" view on notehub.io to determine the DeviceUID of the 15 | // device, which is a unique string that looks like "dev:000000000000000" 16 | // 3. Use the "Settings / Project" view on notehub.io to determine the App UID 17 | // of your project, a unique string that looks like 18 | // "app:00000000-0000-0000-0000-000000000000" 19 | // 4. At the command line of your PC, send an HTTP message to the service 20 | // such as: 21 | // curl -L 'http://api.notefile.net/req?project="app:00000000-0000-0000-0000-000000000000"&device="dev:000000000000000"' -d '{"req":"note.add","file":"my-inbound.qi","body":{"my-request-type":"my-request"}}' 22 | 23 | #include 24 | 25 | // Parameters for this example 26 | #define INBOUND_QUEUE_POLL_SECS 10 27 | #define INBOUND_QUEUE_NOTEFILE "my-inbound.qi" 28 | #define INBOUND_QUEUE_COMMAND_FIELD "my-request-type" 29 | 30 | // Note that both of these definitions are optional; just prefix either line 31 | // with `//` to remove it. 32 | // - Remove txRxPinsSerial if you wired your Notecard using I2C SDA/SCL pins 33 | // instead of serial RX/TX 34 | // - Remove usbSerial if you don't want the Notecard library to output debug 35 | // information 36 | 37 | // #define txRxPinsSerial Serial1 38 | #define usbSerial Serial 39 | 40 | // This is the unique Product Identifier for your device 41 | #ifndef PRODUCT_UID 42 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 43 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 44 | #endif 45 | 46 | #define myProductID PRODUCT_UID 47 | Notecard notecard; 48 | #define myLiveDemo true 49 | 50 | // One-time Arduino initialization 51 | void setup() 52 | { 53 | // Set up for debug output (if available). 54 | #ifdef usbSerial 55 | // If you open Arduino's serial terminal window, you'll be able to watch 56 | // JSON objects being transferred to and from the Notecard for each request. 57 | usbSerial.begin(115200); 58 | const size_t usb_timeout_ms = 3000; 59 | for (const size_t start_ms = millis(); !usbSerial && (millis() - start_ms) < usb_timeout_ms;) 60 | ; 61 | 62 | // For low-memory platforms, don't turn on internal Notecard logs. 63 | #ifndef NOTE_C_LOW_MEM 64 | notecard.setDebugOutputStream(usbSerial); 65 | #else 66 | #pragma message("INFO: Notecard debug logs disabled. (non-fatal)") 67 | #endif // !NOTE_C_LOW_MEM 68 | #endif // usbSerial 69 | 70 | // Initialize the physical I/O channel to the Notecard 71 | #ifdef txRxPinsSerial 72 | notecard.begin(txRxPinsSerial, 9600); 73 | #else 74 | notecard.begin(); 75 | #endif 76 | 77 | // Configure the productUID, and instruct the Notecard to stay connected to 78 | // the service if `myLiveDemo` is `true`. 79 | J *req = notecard.newRequest("hub.set"); 80 | if (myProductID[0]) 81 | { 82 | JAddStringToObject(req, "product", myProductID); 83 | } 84 | #if myLiveDemo 85 | JAddStringToObject(req, "mode", "continuous"); 86 | JAddBoolToObject(req, "sync", true); // Automatically sync when changes are 87 | // made on notehub 88 | #else 89 | JAddStringToObject(req, "mode", "periodic"); 90 | JAddNumberToObject(req, "inbound", 60); 91 | #endif 92 | notecard.sendRequestWithRetry(req, 5); 93 | } 94 | 95 | // In the Arduino main loop which is called repeatedly, add outbound data every 96 | // 15 seconds 97 | void loop() 98 | { 99 | // On a periodic basis, check the inbound queue for messages. In a 100 | // real-world application, this would be checked using a frequency 101 | // commensurate with the required inbound responsiveness. For the most 102 | // common "periodic" mode applications, this might be daily or weekly. In 103 | // this example, where we are using "continuous" mode, we check quite often 104 | // for demonstratio purposes. 105 | static unsigned long nextPollMs = 0; 106 | if (millis() > nextPollMs) 107 | { 108 | nextPollMs = millis() + (INBOUND_QUEUE_POLL_SECS * 1000); 109 | 110 | // Process all pending inbound requests 111 | while (true) 112 | { 113 | // Get the next available note from our inbound queue notefile, 114 | // deleting it 115 | J *req = notecard.newRequest("note.get"); 116 | JAddStringToObject(req, "file", INBOUND_QUEUE_NOTEFILE); 117 | JAddBoolToObject(req, "delete", true); 118 | J *rsp = notecard.requestAndResponse(req); 119 | if (rsp != NULL) 120 | { 121 | // If an error is returned, this means that no response is 122 | // pending. Note that it's expected that this might return 123 | // either a "note does not exist" error if there are no pending 124 | // inbound notes, or a "file does not exist" error if the 125 | // inbound queue hasn't yet been created on the service. 126 | if (notecard.responseError(rsp)) 127 | { 128 | notecard.deleteResponse(rsp); 129 | break; 130 | } 131 | 132 | // Get the note's body 133 | J *body = JGetObject(rsp, "body"); 134 | if (body != NULL) 135 | { 136 | 137 | // Simulate Processing the response here 138 | usbSerial.print("[APP] INBOUND REQUEST: "); 139 | usbSerial.println(JGetString(body, INBOUND_QUEUE_COMMAND_FIELD)); 140 | usbSerial.println(); 141 | } 142 | } 143 | notecard.deleteResponse(rsp); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /examples/Example4_InboundInterrupts/Example4_InboundInterrupts.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This example shows the simplest possible method demonstrating how a device 7 | // might poll a Notefile used as an "inbound queue", using it to receive 8 | // messages sent to the device from the service. The message gets into the 9 | // service by use of the Notehub's HTTP/HTTPS inbound request capability. 10 | // 11 | // In order to use this example, 12 | // 1. Wire the Notecard's ATTN pin output to a GPIO input on your MCU, and 13 | // change the definition below to reflect the proper GPIO pin. On the 14 | // Notecard Development Kit with an nRF53840 Feather, this would mean placing 15 | // a jumper wire between the pin marked "5" and the pin marked "ATTN". 16 | // 2. Get the device up and running the code below, successfully connecting to 17 | // the servie 18 | // 3. Use the "Devices" view on notehub.io to determine the DeviceUID of the 19 | // device, which is a unique string that looks like "dev:000000000000000" 20 | // 4. Use the "Settings / Project" view on notehub.io to determine the App UID 21 | // of your project, a unique string that looks like 22 | // "app:00000000-0000-0000-0000-000000000000" 23 | // 5. At the command line of your PC, send an HTTP message to the service 24 | // such as: 25 | // curl -L 'http://api.notefile.net/req?project="app:00000000-0000-0000-0000-000000000000"&device="dev:000000000000000"' -d '{"req":"note.add","file":"my-inbound.qi","body":{"my-request-type":"my-request"}}' 26 | 27 | #include 28 | 29 | // GPIO pin definitions 30 | #define ATTN_INPUT_PIN 5 // Any digital GPIO pin on your board 31 | 32 | // Parameters for this example 33 | #define INBOUND_QUEUE_NOTEFILE "my-inbound.qi" 34 | #define INBOUND_QUEUE_COMMAND_FIELD "my-request-type" 35 | 36 | // Note that both of these definitions are optional; just prefix either line 37 | // with `//` to remove it. 38 | // - Remove txRxPinsSerial if you wired your Notecard using I2C SDA/SCL pins 39 | // instead of serial RX/TX 40 | // - Remove usbSerial if you don't want the Notecard library to output debug 41 | // information 42 | 43 | // #define txRxPinsSerial Serial1 44 | #define usbSerial Serial 45 | 46 | // This is the unique Product Identifier for your device 47 | #ifndef PRODUCT_UID 48 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 49 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 50 | #endif 51 | 52 | #define myProductID PRODUCT_UID 53 | Notecard notecard; 54 | #define myLiveDemo true 55 | 56 | // Set to true whenever ATTN interrupt occurs 57 | static bool attnInterruptOccurred; 58 | 59 | // Forwards 60 | void attnISR(void); 61 | void attnArm(); 62 | 63 | // One-time Arduino initialization 64 | void setup() 65 | { 66 | // Set up for debug output (if available). 67 | #ifdef usbSerial 68 | // If you open Arduino's serial terminal window, you'll be able to watch 69 | // JSON objects being transferred to and from the Notecard for each request. 70 | usbSerial.begin(115200); 71 | const size_t usb_timeout_ms = 3000; 72 | for (const size_t start_ms = millis(); !usbSerial && (millis() - start_ms) < usb_timeout_ms;) 73 | ; 74 | 75 | // For low-memory platforms, don't turn on internal Notecard logs. 76 | #ifndef NOTE_C_LOW_MEM 77 | notecard.setDebugOutputStream(usbSerial); 78 | #else 79 | #pragma message("INFO: Notecard debug logs disabled. (non-fatal)") 80 | #endif // !NOTE_C_LOW_MEM 81 | #endif // usbSerial 82 | 83 | // Initialize the physical I/O channel to the Notecard 84 | #ifdef txRxPinsSerial 85 | notecard.begin(txRxPinsSerial, 9600); 86 | #else 87 | notecard.begin(); 88 | #endif 89 | 90 | // Configure the productUID, and instruct the Notecard to stay connected to 91 | // the service 92 | J *req = notecard.newRequest("hub.set"); 93 | if (myProductID[0]) 94 | { 95 | JAddStringToObject(req, "product", myProductID); 96 | } 97 | #if myLiveDemo 98 | JAddStringToObject(req, "mode", "continuous"); 99 | JAddBoolToObject(req, "sync", true); 100 | #else 101 | JAddStringToObject(req, "mode", "periodic"); 102 | JAddNumberToObject(req, "outbound", 60); 103 | #endif 104 | notecard.sendRequest(req); 105 | 106 | // Disarm ATTN To clear any previous state before rearming 107 | req = notecard.newRequest("card.attn"); 108 | JAddStringToObject(req, "mode", "disarm,-files"); 109 | notecard.sendRequest(req); 110 | 111 | // Configure ATTN to wait for a specific list of files 112 | req = notecard.newRequest("card.attn"); 113 | const char *filesToWatch[] = {INBOUND_QUEUE_NOTEFILE}; 114 | int numFilesToWatch = sizeof(filesToWatch) / sizeof(const char *); 115 | J *filesArray = JCreateStringArray(filesToWatch, numFilesToWatch); 116 | JAddItemToObject(req, "files", filesArray); 117 | JAddStringToObject(req, "mode", "files"); 118 | notecard.sendRequest(req); 119 | 120 | // Attach an interrupt pin 121 | pinMode(ATTN_INPUT_PIN, INPUT); 122 | attachInterrupt(digitalPinToInterrupt(ATTN_INPUT_PIN), attnISR, RISING); 123 | 124 | // Arm the interrupt, so that we are notified whenever ATTN rises 125 | attnArm(); 126 | } 127 | 128 | // In the Arduino main loop which is called repeatedly, add outbound data every 129 | // 15 seconds 130 | void loop() 131 | { 132 | // If the interrupt hasn't occurred, exit 133 | if (!attnInterruptOccurred) 134 | { 135 | return; 136 | } 137 | 138 | // Re-arm the interrupt 139 | attnArm(); 140 | 141 | // Process all pending inbound requests 142 | while (true) 143 | { 144 | // Get the next available note from our inbound queue notefile, 145 | // deleting it 146 | J *req = notecard.newRequest("note.get"); 147 | JAddStringToObject(req, "file", INBOUND_QUEUE_NOTEFILE); 148 | JAddBoolToObject(req, "delete", true); 149 | J *rsp = notecard.requestAndResponse(req); 150 | if (rsp != NULL) 151 | { 152 | // If an error is returned, this means that no response is pending. 153 | // Note that it's expected that this might return either a "note 154 | // does not exist" error if there are no pending inbound Notes, or a 155 | // "file does not exist" error if the inbound queue hasn't yet been 156 | // created on the service. 157 | if (notecard.responseError(rsp)) 158 | { 159 | notecard.deleteResponse(rsp); 160 | break; 161 | } 162 | 163 | // Get the note's body 164 | J *body = JGetObject(rsp, "body"); 165 | if (body != NULL) 166 | { 167 | // Simulate Processing the response here 168 | char *myCommandType = JGetString(body, INBOUND_QUEUE_COMMAND_FIELD); 169 | usbSerial.print("[APP] INBOUND REQUEST: "); 170 | usbSerial.println(myCommandType); 171 | usbSerial.println(); 172 | } 173 | } 174 | notecard.deleteResponse(rsp); 175 | } 176 | } 177 | 178 | // Interrupt Service Routine for ATTN_INPUT_PIN transitions rising from LOW 179 | // to HIGH 180 | void attnISR() 181 | { 182 | attnInterruptOccurred = true; 183 | } 184 | 185 | // Re-arm the interrupt 186 | void attnArm() 187 | { 188 | // Make sure that we pick up the next RISING edge of the interrupt 189 | attnInterruptOccurred = false; 190 | 191 | // Set the ATTN pin low, and wait for the earlier of file modification or 192 | // a timeout 193 | J *req = notecard.newRequest("card.attn"); 194 | JAddStringToObject(req, "mode", "reset"); 195 | JAddNumberToObject(req, "seconds", 120); 196 | notecard.sendRequest(req); 197 | } 198 | -------------------------------------------------------------------------------- /examples/Example6_SensorTutorial/Example6_SensorTutorial.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This example contains the complete source for the Sensor Tutorial at 7 | // https//blues.dev 8 | // https://blues.dev/guides-and-tutorials/collecting-sensor-data/notecarrier-f/blues-wireless-swan/c-cpp-arduino-wiring/ 9 | 10 | // Include the Arduino library for the Notecard 11 | #include 12 | #include 13 | 14 | // #define txRxPinsSerial Serial1 15 | #define usbSerial Serial 16 | 17 | // This is the unique Product Identifier for your device 18 | #ifndef PRODUCT_UID 19 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 20 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 21 | #endif 22 | 23 | #define myProductID PRODUCT_UID 24 | 25 | using namespace blues; 26 | 27 | Notecard notecard; 28 | NotecardPseudoSensor sensor(notecard); 29 | 30 | // One-time Arduino initialization 31 | void setup() 32 | { 33 | // Set up for debug output (if available). 34 | #ifdef usbSerial 35 | // If you open Arduino's serial terminal window, you'll be able to watch 36 | // JSON objects being transferred to and from the Notecard for each request. 37 | usbSerial.begin(115200); 38 | const size_t usb_timeout_ms = 3000; 39 | for (const size_t start_ms = millis(); !usbSerial && (millis() - start_ms) < usb_timeout_ms;) 40 | ; 41 | 42 | // For low-memory platforms, don't turn on internal Notecard logs. 43 | #ifndef NOTE_C_LOW_MEM 44 | notecard.setDebugOutputStream(usbSerial); 45 | #else 46 | #pragma message("INFO: Notecard debug logs disabled. (non-fatal)") 47 | #endif // !NOTE_C_LOW_MEM 48 | #endif // usbSerial 49 | 50 | // Initialize the physical I/O channel to the Notecard 51 | #ifdef txRxPinsSerial 52 | notecard.begin(txRxPinsSerial, 9600); 53 | #else 54 | notecard.begin(); 55 | #endif 56 | 57 | J *req = notecard.newRequest("hub.set"); 58 | if (myProductID[0]) 59 | { 60 | JAddStringToObject(req, "product", myProductID); 61 | } 62 | JAddStringToObject(req, "mode", "continuous"); 63 | notecard.sendRequestWithRetry(req, 5); // 5 seconds 64 | } 65 | 66 | // In the Arduino main loop which is called repeatedly, add outbound data every 67 | // 15 seconds 68 | void loop() 69 | { 70 | // Count the simulated measurements that we send to the cloud, and stop the 71 | // demo before long. 72 | static unsigned eventCounter = 0; 73 | if (++eventCounter > 25) 74 | { 75 | usbSerial.println("[APP] Demo cycle complete. Program stopped. Press RESET to restart."); 76 | delay(10000); // 10 seconds 77 | return; 78 | } 79 | 80 | float temperature = sensor.temp(); 81 | float humidity = sensor.humidity(); 82 | 83 | usbSerial.print("[APP] Temperature = "); 84 | usbSerial.print(temperature); 85 | usbSerial.println(" *C"); 86 | usbSerial.print("[APP] Humidity = "); 87 | usbSerial.print(humidity); 88 | usbSerial.println(" %"); 89 | 90 | J *req = notecard.newRequest("note.add"); 91 | if (req != NULL) 92 | { 93 | JAddStringToObject(req, "file", "sensors.qo"); 94 | JAddBoolToObject(req, "sync", true); 95 | J *body = JAddObjectToObject(req, "body"); 96 | if (body) 97 | { 98 | JAddNumberToObject(body, "temp", temperature); 99 | JAddNumberToObject(body, "humidity", humidity); 100 | } 101 | notecard.sendRequest(req); 102 | } 103 | 104 | delay(15000); 105 | } 106 | -------------------------------------------------------------------------------- /examples/Example7_PowerControl/Example7_PowerControl.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This tutorial requires a Notecarrier-F (or equivalently-wired carrier board) 7 | // designed enable the Notecard's ATTN pin to control a host MCU's power supply. 8 | 9 | #include 10 | 11 | // This is the unique Product Identifier for your device 12 | #ifndef PRODUCT_UID 13 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 14 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 15 | #endif 16 | 17 | // Parameters for this example 18 | #define myProductID PRODUCT_UID 19 | #define notehubUploadPeriodMins 10 20 | #define hostSleepSeconds 60 21 | 22 | // Arduino serial debug monitor port definitions. 23 | // Note this is using the TX/RX pins for logging and not the USB connector. An 24 | // FTDI cable is required to read the logs produced by this sketch. Power is 25 | // delivered over USB, which interferes with the way the Notecard puts the 26 | // Feather MCU to sleep. As a result, USB cannot be used to capture logs. 27 | #ifdef ARDUINO_AVR_UNO 28 | #define txRxSerial Serial 29 | #elif ARDUINO_FEATHER_F405 30 | // https://github.com/stm32duino/Arduino_Core_STM32/issues/1990 31 | #define txRxSerial Serial3 32 | #elif ARDUINO_NUCLEO_L432KC 33 | #define txRxSerial Serial2 34 | #else 35 | #define txRxSerial Serial1 36 | #endif 37 | 38 | // Notecard I2C port definitions 39 | Notecard notecard; 40 | 41 | // When the Notecard puts the host MCU to sleep, it enables the host to save 42 | // 'state' inside the Notecard while it's asleep, and to retrieve this state 43 | // when it awakens. These are several 'segments' of state that may individually 44 | // be saved. 45 | struct 46 | { 47 | int cycles; 48 | } globalState; 49 | const char globalSegmentID[] = "GLOB"; 50 | 51 | struct 52 | { 53 | int measurements; 54 | } tempSensorState; 55 | const char tempSensorSegmentID[] = "TEMP"; 56 | 57 | struct 58 | { 59 | int measurements; 60 | } voltSensorState; 61 | const char voltSensorSegmentID[] = "VOLT"; 62 | 63 | // One-time Arduino initialization 64 | void setup() 65 | { 66 | // Set up for debug output (if available). 67 | #ifdef txRxSerial 68 | // If you open Arduino's serial terminal window, you'll be able to watch 69 | // JSON objects being transferred to and from the Notecard for each request. 70 | txRxSerial.begin(115200); 71 | const size_t usb_timeout_ms = 3000; 72 | for (const size_t start_ms = millis(); !txRxSerial && (millis() - start_ms) < usb_timeout_ms;) 73 | ; 74 | 75 | // For low-memory platforms, don't turn on internal Notecard logs. 76 | #ifndef NOTE_C_LOW_MEM 77 | notecard.setDebugOutputStream(txRxSerial); 78 | #else 79 | #pragma message("INFO: Notecard debug logs disabled. (non-fatal)") 80 | #endif // !NOTE_C_LOW_MEM 81 | #endif // txRxSerial 82 | 83 | 84 | // Initialize the physical I2C I/O channel to the Notecard 85 | notecard.begin(); 86 | 87 | // Determine whether or not this is a 'clean boot', or if we're 88 | // restarting after having been put to sleep by the Notecard. 89 | NotePayloadDesc payload; 90 | bool retrieved = NotePayloadRetrieveAfterSleep(&payload); 91 | 92 | // If the payload was successfully retrieved, attempt to restore state from 93 | // the payload 94 | if (retrieved) 95 | { 96 | // Restore the various state data structures 97 | retrieved &= NotePayloadGetSegment(&payload, globalSegmentID, &globalState, sizeof(globalState)); 98 | retrieved &= NotePayloadGetSegment(&payload, tempSensorSegmentID, &tempSensorState, sizeof(tempSensorState)); 99 | retrieved &= NotePayloadGetSegment(&payload, voltSensorSegmentID, &voltSensorState, sizeof(voltSensorState)); 100 | 101 | // We're done with the payload, so we can free it 102 | NotePayloadFree(&payload); 103 | } 104 | 105 | // If this is our first time through, initialize the Notecard and state 106 | if (!retrieved) 107 | { 108 | 109 | // Initialize operating state 110 | memset(&globalState, 0, sizeof(globalState)); 111 | memset(&tempSensorState, 0, sizeof(tempSensorState)); 112 | memset(&voltSensorState, 0, sizeof(voltSensorState)); 113 | 114 | // Initialize the Notecard 115 | J *req = notecard.newRequest("hub.set"); 116 | if (myProductID[0]) 117 | { 118 | JAddStringToObject(req, "product", myProductID); 119 | } 120 | JAddStringToObject(req, "mode", "periodic"); 121 | JAddNumberToObject(req, "outbound", notehubUploadPeriodMins); 122 | notecard.sendRequestWithRetry(req, 5); // 5 seconds 123 | 124 | // Because many devs will be using oscilloscopes or joulescopes to 125 | // closely examine power consumption, it can be helpful during 126 | // development to provide a stable and repeatable power consumption 127 | // environment. In the Notecard's default configuration, the 128 | // accelerometer is 'on'. As such, when debugging, devs may see tiny 129 | // little blips from time to time on the scope. These little blips are 130 | // caused by accelerometer interrupt processing, when developers 131 | // accidentally tap the notecard or carrier. As such, to help during 132 | // development and measurement, this request disables the accelerometer. 133 | req = notecard.newRequest("card.motion.mode"); 134 | JAddBoolToObject(req, "stop", true); 135 | notecard.sendRequest(req); 136 | } 137 | } 138 | 139 | void loop() 140 | { 141 | // Bump the number of cycles 142 | if (++globalState.cycles > 25) 143 | { 144 | txRxSerial.println("[APP] Demo cycle complete. Program stopped. Press RESET to restart."); 145 | delay(10000); // 10 seconds 146 | return; 147 | } 148 | 149 | // Simulation of a device taking a measurement of a temperature sensor. 150 | // Because we don't have an actual external hardware sensor in this example, 151 | // we're just retrieving the internal surface temperature of the Notecard. 152 | double currentTemperature = 0.0; 153 | J *rsp = notecard.requestAndResponse(notecard.newRequest("card.temp")); 154 | if (rsp != NULL) 155 | { 156 | currentTemperature = JGetNumber(rsp, "value"); 157 | notecard.deleteResponse(rsp); 158 | tempSensorState.measurements++; 159 | } 160 | 161 | // Simulation of a device taking a measurement of a voltage sensor. Because 162 | // we don't have an actual external hardware sensor in this example, we're 163 | // just retrieving the battery voltage being supplied to the Notecard. 164 | double currentVoltage = 0.0; 165 | rsp = notecard.requestAndResponse(notecard.newRequest("card.voltage")); 166 | if (rsp != NULL) 167 | { 168 | currentVoltage = JGetNumber(rsp, "value"); 169 | notecard.deleteResponse(rsp); 170 | voltSensorState.measurements++; 171 | } 172 | 173 | // Add a note to the Notecard containing the sensor readings 174 | J *req = notecard.newRequest("note.add"); 175 | if (req != NULL) 176 | { 177 | JAddStringToObject(req, "file", "example.qo"); 178 | J *body = JAddObjectToObject(req, "body"); 179 | if (body != NULL) 180 | { 181 | JAddNumberToObject(body, "cycles", globalState.cycles); 182 | JAddNumberToObject(body, "temperature", currentTemperature); 183 | JAddNumberToObject(body, "temperature_measurements", tempSensorState.measurements); 184 | JAddNumberToObject(body, "voltage", currentVoltage); 185 | JAddNumberToObject(body, "voltage_measurements", voltSensorState.measurements); 186 | } 187 | notecard.sendRequest(req); 188 | } 189 | 190 | // Put ourselves back to sleep for a fixed period of time 191 | NotePayloadDesc payload = {0, 0, 0}; 192 | NotePayloadAddSegment(&payload, globalSegmentID, &globalState, sizeof(globalState)); 193 | NotePayloadAddSegment(&payload, voltSensorSegmentID, &voltSensorState, sizeof(voltSensorState)); 194 | NotePayloadAddSegment(&payload, tempSensorSegmentID, &tempSensorState, sizeof(tempSensorState)); 195 | NotePayloadSaveAndSleep(&payload, hostSleepSeconds, NULL); 196 | 197 | // We should never return here, because the Notecard put us to sleep. If we 198 | // do get here, it's because the Notecarrier was configured to supply power 199 | // to this host MCU without being switched by the ATTN pin. 200 | delay(15000); 201 | } 202 | -------------------------------------------------------------------------------- /examples/Example8_BinarySendReceive/Example8_BinarySendReceive.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Blues Inc. All rights reserved. 2 | // 3 | // Use of this source code is governed by licenses granted by the 4 | // copyright holder including that found in the LICENSE file. 5 | // 6 | // This example exercises the Notecard's ability to send and receive a binary 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | // Note that both of these definitions are optional; just prefix either line 16 | // with `//` to remove it. 17 | // - Remove txRxPinsSerial if you wired your Notecard using I2C SDA/SCL pins 18 | // instead of serial RX/TX 19 | // - Remove usbSerial if you don't want the Notecard library to output debug 20 | // information 21 | 22 | // #define txRxPinsSerial Serial1 23 | #define usbSerial Serial 24 | 25 | // This is the unique Product Identifier for your device 26 | #ifndef PRODUCT_UID 27 | #define PRODUCT_UID "" // "com.my-company.my-name:my-project" 28 | #pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid" 29 | #endif 30 | 31 | #define myProductID PRODUCT_UID 32 | Notecard notecard; 33 | 34 | // One-time Arduino initialization 35 | void setup() 36 | { 37 | // Set up for debug output (if available). 38 | #ifdef usbSerial 39 | // If you open Arduino's serial terminal window, you'll be able to watch 40 | // JSON objects being transferred to and from the Notecard for each request. 41 | usbSerial.begin(115200); 42 | const size_t usb_timeout_ms = 3000; 43 | for (const size_t start_ms = millis(); !usbSerial && (millis() - start_ms) < usb_timeout_ms;) 44 | ; 45 | 46 | // For low-memory platforms, don't turn on internal Notecard logs. 47 | #ifndef NOTE_C_LOW_MEM 48 | notecard.setDebugOutputStream(usbSerial); 49 | #else 50 | #pragma message("INFO: Notecard debug logs disabled. (non-fatal)") 51 | #endif // !NOTE_C_LOW_MEM 52 | #endif // usbSerial 53 | 54 | // Initialize the physical I/O channel to the Notecard 55 | #ifdef txRxPinsSerial 56 | notecard.begin(txRxPinsSerial, 9600); 57 | #else 58 | notecard.begin(); 59 | #endif 60 | 61 | // Configure the productUID, and instruct the Notecard to stay connected to 62 | // the service. 63 | J *req = notecard.newRequest("hub.set"); 64 | if (myProductID[0]) 65 | { 66 | JAddStringToObject(req, "product", myProductID); 67 | } 68 | JAddStringToObject(req, "mode", "continuous"); 69 | notecard.sendRequestWithRetry(req, 5); // 5 seconds 70 | 71 | // Reset the state of the Notecard's binary data store to a known value. 72 | NoteBinaryStoreReset(); 73 | } 74 | 75 | // In the Arduino main loop which is called repeatedly, add outbound data every 76 | // 30 seconds 77 | void loop() 78 | { 79 | // Stop the demo after five iterations to conserve data 80 | static unsigned event_counter = 0; 81 | if (++event_counter > 5) 82 | { 83 | usbSerial.println("[APP] Demo cycle complete. Program stopped. Press RESET to restart."); 84 | delay(10000); // 10 seconds 85 | return; 86 | } 87 | 88 | // Send data to the Notecard storage and pull it back 89 | { 90 | ///////////////////////////////////////////////// 91 | // Transmit that beautiful bean footage 92 | ///////////////////////////////////////////////// 93 | 94 | char data[64] = "https://youtu.be/0epWToAOlFY?t=21"; 95 | uint32_t data_len = strlen(data); 96 | const uint32_t notecard_binary_area_offset = 0; 97 | NoteBinaryStoreTransmit(reinterpret_cast(data), data_len, sizeof(data), notecard_binary_area_offset); 98 | usbSerial.print("\n[APP] Transmitted "); 99 | usbSerial.print(data_len); 100 | usbSerial.println(" bytes."); 101 | 102 | // Log for the sake of curiosity (not necessary for operation) 103 | // NOTE: NoteBinaryMaxEncodedLength() is imprecise. It will most 104 | // commonly return a number greater than the actual bytes encoded. 105 | // However, in this contrived example there is no difference, 106 | // so it works for the purposes of displaying the encoded data -- 107 | // which would never be done in practice. 108 | usbSerial.println("\n[APP] *** Encoded Binary Transmission ***"); 109 | uint32_t tx_len = NoteBinaryCodecMaxEncodedLength(data_len); 110 | for (size_t i = 0 ; i < tx_len ; ++i) { 111 | usbSerial.print(data[i], HEX); 112 | usbSerial.print(" "); 113 | if ((i + 1) % 16 == 0) { 114 | usbSerial.println(); 115 | } 116 | } 117 | usbSerial.println("\n[APP] *** Encoded Binary Transmission ***\n"); 118 | 119 | ///////////////////////////////////////////////// 120 | // Receive data from the Notecard binary data store 121 | ///////////////////////////////////////////////// 122 | 123 | // Calcluate the length of the decoded data 124 | data_len = 0; 125 | NoteBinaryStoreDecodedLength(&data_len); 126 | 127 | // Create a buffer to receive the entire data store. This buffer must be 128 | // large enough to hold the encoded data that will be transferred from 129 | // the Notecard, as well as the terminating newline. 130 | // `NoteBinaryMaxEncodedLength()` will compute the worst-case size of 131 | // the encoded length plus the byte required for the newline terminator. 132 | uint32_t rx_buffer_len = NoteBinaryCodecMaxEncodedLength(data_len); 133 | uint8_t *rx_buffer = (uint8_t *)malloc(rx_buffer_len); 134 | 135 | // Receive the data 136 | NoteBinaryStoreReceive(reinterpret_cast(rx_buffer), rx_buffer_len, 0, data_len); 137 | usbSerial.print("\n[APP] Received "); 138 | usbSerial.print(data_len); 139 | usbSerial.println(" bytes."); 140 | 141 | // Display received buffer 142 | usbSerial.println("\n[APP] *** Decoded Data ***"); 143 | for (size_t i = 0 ; i < data_len ; ++i) { 144 | usbSerial.print(rx_buffer[i]); 145 | } 146 | usbSerial.println("\n[APP] *** Decoded Data ***\n"); 147 | 148 | // Free the receive buffer 149 | free(rx_buffer); 150 | 151 | // NOTE: The binary data store is not cleared on receive, which 152 | // allows us to submit it to Notehub in the next step. 153 | } 154 | 155 | // Send it to Notehub 156 | { 157 | // Submit binary object to the Notehub using `note.add`. This will send 158 | // the binary to Notehub in the payload field of the Note. The payload 159 | // will not be visible in the Notehub UI, but the data will be forwarded 160 | // to any pre-configured routes. 161 | if (J *req = notecard.newRequest("note.add")) 162 | { 163 | JAddStringToObject(req, "file", "cobs.qo"); 164 | JAddBoolToObject(req, "binary", true); 165 | JAddBoolToObject(req, "live", true); 166 | if (!notecard.sendRequest(req)) { 167 | // The binary data store is cleared on successful transmission, 168 | // but we need to reset it manually if the request failed. 169 | NoteBinaryStoreReset(); 170 | } 171 | } 172 | } 173 | 174 | // Delay between sends 175 | delay(30 * 1000); // 30 seconds 176 | } 177 | -------------------------------------------------------------------------------- /examples/build_example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 4 | NOTE_ARDUINO_DIR="$SCRIPT_DIR/.." 5 | 6 | # If this is being run inside a Docker container (i.e. for GitHub actions CI), 7 | # copy the latest note-arduino code into the appropriate place so that it can 8 | # be consumed when building the example. 9 | if [ -f /.dockerenv ]; then 10 | cp -r $NOTE_ARDUINO_DIR $HOME/Arduino/libraries/Blues_Wireless_Notecard 11 | fi 12 | 13 | arduino-cli compile \ 14 | --build-property compiler.cpp.extra_flags='-Wno-unused-parameter -Werror' \ 15 | --fqbn $1 \ 16 | --log-level trace \ 17 | --verbose \ 18 | --warnings all \ 19 | $2 20 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ######################################## 2 | ## CLASS 3 | ######################################## 4 | Notecard KEYWORD1 5 | 6 | ######################################## 7 | ## FUNCTIONS 8 | ######################################## 9 | begin KEYWORD2 10 | clearDebugOutputStream KEYWORD2 11 | clearTransactionPins KEYWORD2 12 | debugSyncStatus KEYWORD2 13 | deleteResponse KEYWORD2 14 | end KEYWORD2 15 | newCommand KEYWORD2 16 | newRequest KEYWORD2 17 | requestAndResponse KEYWORD2 18 | requestAndResponseWithRetry KEYWORD2 19 | responseError KEYWORD2 20 | sendRequest KEYWORD2 21 | sendRequestWithRetry KEYWORD2 22 | setDebugOutputStream KEYWORD2 23 | setFnI2cMutex KEYWORD2 24 | setFnNoteMutex KEYWORD2 25 | setTransactionPins KEYWORD2 26 | 27 | ######################################## 28 | ## CONSTANTS 29 | ######################################## 30 | Notecard_h LITERAL1 31 | Notecarrier_h LITERAL1 32 | NOTE_ARDUINO_VERSION LITERAL1 33 | NOTE_ARDUINO_VERSION_MAJOR LITERAL1 34 | NOTE_ARDUINO_VERSION_MINOR LITERAL1 35 | NOTE_ARDUINO_VERSION_PATCH LITERAL1 36 | 37 | ######################################## 38 | ## DATA TYPES 39 | ######################################## 40 | 41 | ######################################## 42 | # NOTE: Variables and KEYWORDs must be tab delimited. 43 | # https://forum.arduino.cc/t/keywords-txt/145895/15 44 | ######################################## 45 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Blues Wireless Notecard 2 | version=1.7.1 3 | author=Blues 4 | maintainer=Blues 5 | sentence=An easy to use Notecard Library for Arduino. 6 | paragraph=Supports Serial and I2C for communication from a host MCU. 7 | category=Communication 8 | url=https://github.com/blues/note-arduino 9 | architectures=* 10 | includes=Notecard.h 11 | -------------------------------------------------------------------------------- /scripts/check_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Script to check that the version in src/NoteDefines.h matches the version in library.properties 5 | # Usage: ./check_version.sh 6 | 7 | # Get the version from library.properties 8 | LIBRARY_VERSION=$(grep "^version=" library.properties | cut -d= -f2) 9 | 10 | # Get the version components from NoteDefines.h 11 | MAJOR=$(grep "NOTE_ARDUINO_VERSION_MAJOR" src/NoteDefines.h | grep -o "[0-9]\+") 12 | MINOR=$(grep "NOTE_ARDUINO_VERSION_MINOR" src/NoteDefines.h | grep -o "[0-9]\+") 13 | PATCH=$(grep "NOTE_ARDUINO_VERSION_PATCH" src/NoteDefines.h | grep -o "[0-9]\+") 14 | 15 | # Construct the version string from the components 16 | DEFINES_VERSION="$MAJOR.$MINOR.$PATCH" 17 | 18 | echo "Version in library.properties: $LIBRARY_VERSION" 19 | echo "Version in src/NoteDefines.h: $DEFINES_VERSION" 20 | 21 | # Compare the versions 22 | if [ "$LIBRARY_VERSION" = "$DEFINES_VERSION" ]; then 23 | echo "✅ Versions match!" 24 | exit 0 25 | else 26 | echo "❌ Version mismatch!" 27 | echo "library.properties version: $LIBRARY_VERSION" 28 | echo "src/NoteDefines.h version: $DEFINES_VERSION" 29 | exit 1 30 | fi 31 | -------------------------------------------------------------------------------- /scripts/update_note_c.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Update the note-c subtree. 5 | # 6 | # Usage: ./update_note_c.sh 7 | # 8 | # The note-c git ref argument is optional. If not specified, the master branch 9 | # will be used. 10 | # 11 | 12 | # Exit script if any command fails. 13 | set -e 14 | 15 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 16 | ROOT_DIR="$SCRIPT_DIR/.." 17 | 18 | # Optional argument to specify the note-c ref to check out when creating the 19 | # subtree. 20 | if [ $# -eq 0 ]; then 21 | REF="master" 22 | else 23 | REF="$1" 24 | fi 25 | 26 | # Move to the root of note-arduino. 27 | pushd $ROOT_DIR 28 | # Remove the existing note-c directory. 29 | rm -rf src/note-c 30 | # Commit the changes so that the next git subtree command doesn't complain about 31 | # changes in the working tree. 32 | git commit -am 'Remove `note-c` before re-add' 33 | # Check out the note-c subtree using the given ref or master if not specified. 34 | git subtree add --prefix=src/note-c --squash https://github.com/blues/note-c.git $REF 35 | 36 | # Remove all the unneeded directories from the subtree. 37 | NOTE_C_DIR="$ROOT_DIR/src/note-c" 38 | NOTE_C_UNNEEDED_DIRS=( 39 | "$NOTE_C_DIR/.devcontainer" 40 | "$NOTE_C_DIR/.github" 41 | "$NOTE_C_DIR/.vscode" 42 | "$NOTE_C_DIR/assets" 43 | "$NOTE_C_DIR/docs" 44 | "$NOTE_C_DIR/scripts" 45 | "$NOTE_C_DIR/test" 46 | ) 47 | for DIR in "${NOTE_C_UNNEEDED_DIRS[@]}"; do 48 | rm -rf "$DIR" 49 | done 50 | 51 | # Commit the removal of unneeded directories. 52 | git commit -am 'Remove unneeded directories from `note-c`' 53 | 54 | # Return to original directory. 55 | popd 56 | -------------------------------------------------------------------------------- /src/NoteDefines.h: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_DEFINES_H 2 | #define NOTE_DEFINES_H 3 | 4 | // Define the version of the `note-arduino` library 5 | #define NOTE_ARDUINO_VERSION_MAJOR 1 6 | #define NOTE_ARDUINO_VERSION_MINOR 7 7 | #define NOTE_ARDUINO_VERSION_PATCH 1 8 | 9 | #define NOTE_ARDUINO_VERSION NOTE_C_STRINGIZE(NOTE_ARDUINO_VERSION_MAJOR) "." NOTE_C_STRINGIZE(NOTE_ARDUINO_VERSION_MINOR) "." NOTE_C_STRINGIZE(NOTE_ARDUINO_VERSION_PATCH) 10 | 11 | // Unified attribute for marking functions as deprecated 12 | #if defined(__GNUC__) | defined(__clang__) 13 | #define NOTE_ARDUINO_DEPRECATED __attribute__((__deprecated__)) 14 | #elif defined(_MSC_VER) 15 | #define NOTE_ARDUINO_DEPRECATED __declspec(deprecated) 16 | #else 17 | #define NOTE_ARDUINO_DEPRECATED 18 | #define NOTE_ARDUINO_NO_DEPRECATED_ATTR 19 | #endif // __GNUC__ || __clang__ 20 | 21 | // Switches for enabling/disabling features 22 | #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_NRF52) || (defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_ARCH_MBED)) 23 | #define NOTE_ARDUINO_SOFTWARE_SERIAL_SUPPORT 24 | #endif 25 | 26 | #endif // NOTE_DEFINES_H 27 | -------------------------------------------------------------------------------- /src/NoteI2c.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_I2C_HPP 2 | #define NOTE_I2C_HPP 3 | 4 | #include 5 | #include 6 | 7 | class NoteI2c 8 | { 9 | public: 10 | 11 | virtual ~NoteI2c(void) {} 12 | 13 | /**************************************************************************/ 14 | /*! 15 | @brief Receives an amount of data from the Notecard in blocking mode. 16 | @param[in] device_address 17 | The I2C address. 18 | @param[out] buffer 19 | A buffer to hold the data read from the I2C controller. 20 | @param[in] requested_byte_count 21 | The number of bytes requested. 22 | @param[out] available 23 | The number of bytes available for subsequent calls to receive(). 24 | @returns A string with an error, or `nullptr` if the receive was 25 | successful. 26 | */ 27 | /**************************************************************************/ 28 | virtual const char * receive(uint16_t device_address, uint8_t * buffer, uint16_t size, uint32_t * available) = 0; 29 | 30 | /**************************************************************************/ 31 | /*! 32 | @brief Resets the I2C port. Required by note-c. 33 | @return `true`. 34 | */ 35 | /**************************************************************************/ 36 | virtual bool reset(uint16_t device_address) = 0; 37 | 38 | /**************************************************************************/ 39 | /*! 40 | @brief Transmits an amount of data from the host in blocking mode. 41 | @param[in] device_address 42 | The I2C address. 43 | @param[in] buffer 44 | The data to transmit over I2C. The caller should have shifted 45 | it right so that the low bit is NOT the read/write bit. 46 | @param[in] size 47 | The number of bytes to transmit. 48 | @returns A string with an error, or `nullptr` if the transmission was 49 | successful. 50 | */ 51 | /**************************************************************************/ 52 | virtual const char * transmit(uint16_t device_address, uint8_t * buffer, uint16_t size) = 0; 53 | 54 | /**************************************************************************/ 55 | /*! 56 | @brief Size of the header for Serial-Over-I2C requests. 57 | 58 | @details The request made to the low-level I2C controller should be 59 | for REQUEST_HEADER_SIZE + the `size` parameter supplied to the 60 | `receive` method. 61 | 62 | @see NoteI2c::receive 63 | */ 64 | /**************************************************************************/ 65 | static const size_t REQUEST_HEADER_SIZE = 2; 66 | 67 | /**************************************************************************/ 68 | /*! 69 | @brief Maximum size of a Serial-Over-I2C request. 70 | 71 | @details The requests made to and responses received from the low-level 72 | I2C controller can be no larger than (REQUEST_MAX_SIZE 73 | - REQUEST_HEADER_SIZE). 74 | 75 | @see NoteI2c::receive 76 | */ 77 | /**************************************************************************/ 78 | static const size_t REQUEST_MAX_SIZE = 255; 79 | }; 80 | 81 | /******************************************************************************/ 82 | /*! 83 | @brief Helper function to abstract, create and maintain a single instance 84 | of the NoteI2c interface implementation, as required by the underlying 85 | `note-c` library. 86 | @param[in] i2c_parameters 87 | Pointer to the parameters required to instantiate 88 | the platform specific I2C implementation. 89 | */ 90 | /******************************************************************************/ 91 | template NoteI2c * make_note_i2c (T & i2c_parameters); 92 | NoteI2c * make_note_i2c (nullptr_t); 93 | 94 | #endif // NOTE_I2C_HPP 95 | -------------------------------------------------------------------------------- /src/NoteI2c_Arduino.cpp: -------------------------------------------------------------------------------- 1 | #include "NoteI2c_Arduino.hpp" 2 | 3 | #if defined(NOTE_C_LOW_MEM) 4 | static const char *i2cerr = "i2c {io}"; 5 | #endif 6 | 7 | // Singleton instance of the NoteI2c_Arduino class 8 | namespace instance { 9 | inline NoteI2c* & note_i2c (void) { 10 | static NoteI2c* note_i2c = nullptr; 11 | return note_i2c; 12 | } 13 | }; 14 | 15 | NoteI2c * 16 | make_note_i2c ( 17 | nullptr_t 18 | ) { 19 | NoteI2c* & note_i2c = instance::note_i2c(); 20 | if (note_i2c) { 21 | delete note_i2c; 22 | note_i2c = nullptr; 23 | } 24 | return note_i2c; 25 | } 26 | 27 | template 28 | NoteI2c * 29 | make_note_i2c ( 30 | T & i2c_parameters_ 31 | ) 32 | { 33 | NoteI2c* & note_i2c = instance::note_i2c(); 34 | if (!note_i2c) { 35 | note_i2c = new NoteI2c_Arduino(i2c_parameters_); 36 | } 37 | 38 | return note_i2c; 39 | } 40 | 41 | NoteI2c_Arduino::NoteI2c_Arduino 42 | ( 43 | TwoWire & i2c_bus_ 44 | ) : 45 | _i2cPort(i2c_bus_) 46 | { 47 | _i2cPort.begin(); 48 | } 49 | 50 | NoteI2c_Arduino::~NoteI2c_Arduino ( 51 | void 52 | ) 53 | { 54 | #if WIRE_HAS_END 55 | _i2cPort.end(); 56 | #endif 57 | } 58 | 59 | const char * 60 | NoteI2c_Arduino::receive ( 61 | uint16_t device_address_, 62 | uint8_t * buffer_, 63 | uint16_t requested_byte_count_, 64 | uint32_t * available_ 65 | ) 66 | { 67 | const char *result = nullptr; 68 | 69 | const size_t retry_count = 3; 70 | size_t i = 0; 71 | do { 72 | uint8_t transmission_error = 0; 73 | 74 | // Request response data from Notecard 75 | _i2cPort.beginTransmission(static_cast(device_address_)); 76 | _i2cPort.write(static_cast(0)); 77 | _i2cPort.write(static_cast(requested_byte_count_)); 78 | transmission_error = _i2cPort.endTransmission(); 79 | 80 | switch (transmission_error) { 81 | case 0: 82 | // I2C transmission was successful 83 | result = nullptr; 84 | break; 85 | case 1: 86 | result = ERRSTR("i2c: data too long to fit in transmit buffer {io}",i2cerr); 87 | break; 88 | case 2: 89 | result = ERRSTR("i2c: received NACK on transmit of address {io}",i2cerr); 90 | break; 91 | case 3: 92 | result = ERRSTR("i2c: received NACK on transmit of data {io}",i2cerr); 93 | break; 94 | case 4: 95 | result = ERRSTR("i2c: unknown error on TwoWire::endTransmission() {io}",i2cerr); 96 | break; 97 | case 5: 98 | result = ERRSTR("i2c: timeout {io}",i2cerr); 99 | break; 100 | default: 101 | result = ERRSTR("i2c: unknown error encounter during I2C transmission {io}",i2cerr); 102 | } 103 | 104 | // Read and cache response from Notecard 105 | if (!transmission_error) { 106 | // Delay briefly ensuring that the Notecard can 107 | // deliver the data in real-time to the I2C ISR 108 | ::delay(2); 109 | 110 | const int request_length = requested_byte_count_ + NoteI2c::REQUEST_HEADER_SIZE; 111 | const int response_length = _i2cPort.requestFrom((int)device_address_, request_length); 112 | if (!response_length) { 113 | result = ERRSTR("serial-over-i2c: no response to read request {io}",i2cerr); 114 | } else if (response_length != request_length) { 115 | result = ERRSTR("serial-over-i2c: unexpected raw byte count {io}",i2cerr); 116 | } else { 117 | // Ensure available byte count is within expected range 118 | static const size_t AVAILABLE_MAX = (NoteI2c::REQUEST_MAX_SIZE - NoteI2c::REQUEST_HEADER_SIZE); 119 | uint32_t available = _i2cPort.read(); 120 | if (available > AVAILABLE_MAX) { 121 | result = ERRSTR("serial-over-i2c: available byte count greater than max allowed {io}",i2cerr); 122 | } else if (requested_byte_count_ != static_cast(_i2cPort.read())) { 123 | // Ensure protocol response length matches size request 124 | result = ERRSTR("serial-over-i2c: unexpected protocol byte count {io}",i2cerr); 125 | } else { 126 | // Update available with remaining bytes 127 | *available_ = available; 128 | 129 | for (size_t i = 0 ; i < requested_byte_count_ ; ++i) { 130 | //TODO: Perf test against indexed buffer reads 131 | *buffer_++ = _i2cPort.read(); 132 | } 133 | result = nullptr; 134 | break; 135 | } 136 | } 137 | } 138 | 139 | // Flash stalls have been observed on the Notecard ESP. Delaying 140 | // between retries provides time for the Notecard to recover from 141 | // the resource contention. 142 | ::delay(1000); 143 | NOTE_C_LOG_ERROR(result); 144 | NOTE_C_LOG_WARN("serial-over-i2c: reattempting to read Notecard response"); 145 | } while (result && (i++ < retry_count)); 146 | 147 | return result; 148 | } 149 | 150 | bool 151 | NoteI2c_Arduino::reset ( 152 | uint16_t device_address_ 153 | ) 154 | { 155 | (void)device_address_; 156 | #if WIRE_HAS_END 157 | _i2cPort.end(); 158 | #endif 159 | _i2cPort.begin(); 160 | return true; 161 | } 162 | 163 | const char * 164 | NoteI2c_Arduino::transmit ( 165 | uint16_t device_address_, 166 | uint8_t * buffer_, 167 | uint16_t size_ 168 | ) 169 | { 170 | const char * result = nullptr; 171 | uint8_t transmission_error = 0; 172 | 173 | _i2cPort.beginTransmission(static_cast(device_address_)); 174 | _i2cPort.write(static_cast(size_)); 175 | _i2cPort.write(buffer_, size_); 176 | transmission_error = _i2cPort.endTransmission(); 177 | 178 | if (transmission_error) { 179 | switch (transmission_error) { 180 | case 1: 181 | result = ERRSTR("i2c: data too long to fit in transmit buffer {io}",i2cerr); 182 | break; 183 | case 2: 184 | result = ERRSTR("i2c: received NACK on transmit of address {io}",i2cerr); 185 | break; 186 | case 3: 187 | result = ERRSTR("i2c: received NACK on transmit of data {io}",i2cerr); 188 | break; 189 | case 4: 190 | result = ERRSTR("i2c: unknown error on TwoWire::endTransmission() {io}",i2cerr); 191 | break; 192 | case 5: 193 | result = ERRSTR("i2c: timeout {io}",i2cerr); 194 | break; 195 | default: 196 | result = ERRSTR("i2c: unknown error encounter during I2C transmission {io}",i2cerr); 197 | } 198 | } 199 | 200 | return result; 201 | } 202 | 203 | // Explicitly instantiate the template function for the supported types 204 | template NoteI2c * make_note_i2c(TwoWire &); 205 | -------------------------------------------------------------------------------- /src/NoteI2c_Arduino.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_I2C_ARDUINO_HPP 2 | #define NOTE_I2C_ARDUINO_HPP 3 | 4 | #include "NoteI2c.hpp" 5 | 6 | #include "Notecard.h" 7 | 8 | #ifndef NOTE_MOCK 9 | #include 10 | #else 11 | #include "mock/mock-arduino.hpp" 12 | #include "mock/mock-parameters.hpp" 13 | #endif 14 | 15 | class NoteI2c_Arduino final : public NoteI2c 16 | { 17 | public: 18 | NoteI2c_Arduino(TwoWire & i2c_bus); 19 | ~NoteI2c_Arduino(void); 20 | const char * receive(uint16_t device_address, uint8_t * buffer, uint16_t requested_byte_count, uint32_t * available) override; 21 | bool reset(uint16_t device_address) override; 22 | const char * transmit(uint16_t device_address, uint8_t * buffer, uint16_t size) override; 23 | 24 | private: 25 | TwoWire & _i2cPort; 26 | }; 27 | 28 | #endif // NOTE_I2C_ARDUINO_HPP 29 | -------------------------------------------------------------------------------- /src/NoteLog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_LOG_HPP 2 | #define NOTE_LOG_HPP 3 | 4 | #include 5 | 6 | class NoteLog 7 | { 8 | public: 9 | 10 | virtual ~NoteLog(void) {} 11 | 12 | /**************************************************************************/ 13 | /*! 14 | @brief Writes a message to the Notecard debug port. 15 | @param message 16 | A null-terminated, message string. 17 | @return The number of bytes transmitted. 18 | */ 19 | /**************************************************************************/ 20 | virtual size_t print(const char * message) = 0; 21 | }; 22 | 23 | /******************************************************************************/ 24 | /*! 25 | @brief Helper function to abstract, create and maintain a single instance 26 | of the NoteLog interface implementation, as required by the underlying 27 | `note-c` library. 28 | @param[in] log_parameters 29 | Pointer to the parameters required to instantiate 30 | the platform specific log output implementation. 31 | */ 32 | /******************************************************************************/ 33 | template NoteLog * make_note_log (T & log_parameters); 34 | NoteLog * make_note_log (nullptr_t); 35 | 36 | #endif // NOTE_LOG_HPP 37 | -------------------------------------------------------------------------------- /src/NoteLog_Arduino.cpp: -------------------------------------------------------------------------------- 1 | #include "NoteLog_Arduino.hpp" 2 | 3 | // Singleton instance of the NoteLog_Arduino class 4 | namespace instance { 5 | inline NoteLog* & note_log (void) { 6 | static NoteLog* note_log = nullptr; 7 | return note_log; 8 | } 9 | }; 10 | 11 | NoteLog * 12 | make_note_log ( 13 | nullptr_t 14 | ) { 15 | NoteLog* & note_log = instance::note_log(); 16 | if (note_log) { 17 | delete note_log; 18 | note_log = nullptr; 19 | } 20 | return note_log; 21 | } 22 | 23 | template 24 | NoteLog * 25 | make_note_log ( 26 | T & log_parameters_ 27 | ) { 28 | NoteLog* & note_log = instance::note_log(); 29 | if (!note_log) { 30 | note_log = new NoteLog_Arduino(reinterpret_cast(&log_parameters_)); 31 | } 32 | 33 | return note_log; 34 | } 35 | 36 | NoteLog_Arduino::NoteLog_Arduino 37 | ( 38 | Stream * log_stream_ 39 | ) : 40 | _notecardLog(log_stream_) 41 | { } 42 | 43 | size_t 44 | NoteLog_Arduino::print ( 45 | const char * str_ 46 | ) 47 | { 48 | size_t result; 49 | 50 | result = _notecardLog->print(str_); 51 | 52 | return result; 53 | } 54 | 55 | // Explicitly instantiate the template function for the supported types 56 | template NoteLog * make_note_log(Stream &); 57 | -------------------------------------------------------------------------------- /src/NoteLog_Arduino.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_LOG_ARDUINO_HPP 2 | #define NOTE_LOG_ARDUINO_HPP 3 | 4 | #include "NoteLog.hpp" 5 | 6 | #ifndef NOTE_MOCK 7 | #include 8 | #else 9 | #include "mock/mock-arduino.hpp" 10 | #endif 11 | 12 | class NoteLog_Arduino final : public NoteLog 13 | { 14 | public: 15 | NoteLog_Arduino(Stream * log_stream_); 16 | size_t print(const char * message) override; 17 | 18 | private: 19 | Stream * const _notecardLog; 20 | }; 21 | 22 | #endif // NOTE_LOG_ARDUINO_HPP 23 | -------------------------------------------------------------------------------- /src/NoteSerial.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_SERIAL_HPP 2 | #define NOTE_SERIAL_HPP 3 | 4 | #include 5 | #include 6 | 7 | class NoteSerial 8 | { 9 | public: 10 | 11 | virtual ~NoteSerial(void) {} 12 | 13 | /**************************************************************************/ 14 | /*! 15 | @brief Determines if the Notecard Serial port has data available. 16 | @return The number of bytes available to read. 17 | */ 18 | /**************************************************************************/ 19 | virtual size_t available(void) = 0; 20 | 21 | /**************************************************************************/ 22 | /*! 23 | @brief Read a byte from the Notecard Serial port. 24 | @return A single character byte. 25 | */ 26 | /**************************************************************************/ 27 | virtual char receive(void) = 0; 28 | 29 | /**************************************************************************/ 30 | /*! 31 | @brief Resets the serial port. 32 | @return `true` if the Serial port is available. 33 | */ 34 | /**************************************************************************/ 35 | virtual bool reset(void) = 0; 36 | 37 | /**************************************************************************/ 38 | /*! 39 | @brief Writes a message to the Notecard Serial port. 40 | @param buffer 41 | The bytes to write. 42 | @param size 43 | The number of bytes to write. 44 | @param flush 45 | Use `true` to flush to Serial. 46 | @return The number of bytes transmitted. 47 | */ 48 | /**************************************************************************/ 49 | virtual size_t transmit(uint8_t * buffer, size_t size, bool flush) = 0; 50 | }; 51 | 52 | /******************************************************************************/ 53 | /*! 54 | @brief Helper function to abstract, create and maintain a single instance 55 | of the NoteSerial interface implementation, as required by the underlying 56 | `note-c` library. 57 | @param[in] serial_parameters 58 | Pointer to the parameters required to instantiate 59 | the platform specific UART implementation. 60 | */ 61 | /******************************************************************************/ 62 | template NoteSerial * make_note_serial (T & serial_parameters); 63 | NoteSerial * make_note_serial (nullptr_t); 64 | 65 | #endif // NOTE_SERIAL_HPP 66 | -------------------------------------------------------------------------------- /src/NoteSerial_Arduino.cpp: -------------------------------------------------------------------------------- 1 | #include "NoteSerial_Arduino.hpp" 2 | 3 | #include "NoteDefines.h" 4 | 5 | #ifndef NOTE_MOCK 6 | #ifdef NOTE_ARDUINO_SOFTWARE_SERIAL_SUPPORT 7 | #include 8 | #endif 9 | #else 10 | #include "mock/mock-arduino.hpp" 11 | #endif 12 | 13 | #define NOTE_C_SERIAL_TIMEOUT_MS 3500 14 | 15 | // Template Meta-Programming (TMP) to extract the nested template type 16 | template 17 | struct ExtractNestedTemplateType { 18 | // Default case: no extraction 19 | }; 20 | template 21 | struct ExtractNestedTemplateType> { 22 | using type = nested_type; 23 | }; 24 | 25 | // Singleton instance of the NoteSerial_Arduino class 26 | namespace instance { 27 | inline NoteSerial* & note_serial (void) { 28 | static NoteSerial* note_serial = nullptr; 29 | return note_serial; 30 | } 31 | }; 32 | 33 | NoteSerial * 34 | make_note_serial ( 35 | nullptr_t 36 | ) { 37 | NoteSerial* & note_serial = instance::note_serial(); 38 | if (note_serial) { 39 | delete note_serial; 40 | note_serial = nullptr; 41 | } 42 | return note_serial; 43 | } 44 | 45 | template 46 | NoteSerial * 47 | make_note_serial ( 48 | T & serial_parameters_ 49 | ) { 50 | NoteSerial* & note_serial = instance::note_serial(); 51 | if (!note_serial) { 52 | using serial_type = typename ExtractNestedTemplateType::type; 53 | note_serial = new NoteSerial_Arduino(serial_parameters_.serial, serial_parameters_.baud_rate); 54 | } 55 | 56 | return note_serial; 57 | } 58 | 59 | template 60 | NoteSerial_Arduino::NoteSerial_Arduino 61 | ( 62 | T & serial_, 63 | size_t baud_rate_ 64 | ) : 65 | _notecardSerial(serial_), 66 | _notecardSerialSpeed(baud_rate_) 67 | { 68 | _notecardSerial.begin(_notecardSerialSpeed); 69 | 70 | // Wait for the serial port to be ready 71 | for (const size_t startMs = ::millis() ; !_notecardSerial && ((::millis() - startMs) < NOTE_C_SERIAL_TIMEOUT_MS) ;); 72 | } 73 | 74 | template 75 | NoteSerial_Arduino::~NoteSerial_Arduino ( 76 | void 77 | ) 78 | { 79 | _notecardSerial.end(); 80 | } 81 | 82 | template 83 | size_t 84 | NoteSerial_Arduino::available ( 85 | void 86 | ) 87 | { 88 | return _notecardSerial.available(); 89 | } 90 | 91 | template 92 | char 93 | NoteSerial_Arduino::receive ( 94 | void 95 | ) 96 | { 97 | return _notecardSerial.read(); 98 | } 99 | 100 | template 101 | bool 102 | NoteSerial_Arduino::reset ( 103 | void 104 | ) 105 | { 106 | _notecardSerial.end(); 107 | _notecardSerial.begin(_notecardSerialSpeed); 108 | 109 | return true; 110 | } 111 | 112 | template 113 | size_t 114 | NoteSerial_Arduino::transmit ( 115 | uint8_t *buffer, 116 | size_t size, 117 | bool flush 118 | ) 119 | { 120 | size_t result; 121 | result = _notecardSerial.write(buffer, size); 122 | if (flush) { 123 | _notecardSerial.flush(); 124 | } 125 | return result; 126 | } 127 | 128 | // Explicitly instantiate the classes and methods for the supported types 129 | template class NoteSerial_Arduino; 130 | template NoteSerial * make_note_serial>(MakeNoteSerial_ArduinoParameters &); 131 | 132 | #ifdef NOTE_ARDUINO_SOFTWARE_SERIAL_SUPPORT 133 | template class NoteSerial_Arduino; 134 | template NoteSerial * make_note_serial>(MakeNoteSerial_ArduinoParameters &); 135 | #endif 136 | -------------------------------------------------------------------------------- /src/NoteSerial_Arduino.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_SERIAL_ARDUINO_HPP 2 | #define NOTE_SERIAL_ARDUINO_HPP 3 | 4 | #include "NoteSerial.hpp" 5 | 6 | #ifndef NOTE_MOCK 7 | #include 8 | #else 9 | #include "mock/mock-arduino.hpp" 10 | #endif 11 | 12 | template 13 | struct MakeNoteSerial_ArduinoParameters { 14 | MakeNoteSerial_ArduinoParameters ( 15 | T & serial_, 16 | size_t baud_rate_ 17 | ) : 18 | serial(serial_), 19 | baud_rate(baud_rate_) 20 | { } 21 | T & serial; 22 | size_t baud_rate; 23 | }; 24 | 25 | template 26 | class NoteSerial_Arduino final : public NoteSerial 27 | { 28 | public: 29 | NoteSerial_Arduino(T & serial_, size_t baud_rate_); 30 | ~NoteSerial_Arduino(void); 31 | size_t available(void) override; 32 | char receive(void) override; 33 | bool reset(void) override; 34 | size_t transmit(uint8_t * buffer, size_t size, bool flush) override; 35 | 36 | private: 37 | T & _notecardSerial; 38 | const int _notecardSerialSpeed; 39 | }; 40 | 41 | #endif // NOTE_SERIAL_ARDUINO_HPP 42 | -------------------------------------------------------------------------------- /src/NoteTime.h: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_TIME_H 2 | #define NOTE_TIME_H 3 | 4 | #include 5 | 6 | extern "C" { 7 | 8 | /**************************************************************************/ 9 | /*! 10 | @brief Halts the current unit of execution for the specified 11 | period in milliseconds 12 | @param ms 13 | The number of milliseconds to halt 14 | */ 15 | /**************************************************************************/ 16 | void noteDelay(uint32_t ms); 17 | 18 | /**************************************************************************/ 19 | /*! 20 | @brief Reports the count of milliseconds that have transpired since 21 | the application began. 22 | @return The count of milliseconds 23 | @note The counter is not guaranteed to be 32-bit and any algorithm 24 | leveraging this API should take rollover into consideration. 25 | */ 26 | /**************************************************************************/ 27 | uint32_t noteMillis(void); 28 | 29 | } 30 | 31 | #endif // NOTE_TIME_H 32 | -------------------------------------------------------------------------------- /src/NoteTime_Arduino.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "NoteTime.h" 3 | 4 | #include 5 | 6 | #ifndef NOTE_MOCK 7 | #include 8 | #else 9 | #include "mock/mock-arduino.hpp" 10 | #endif 11 | 12 | uint32_t noteMillis(void) 13 | { 14 | return static_cast(::millis()); 15 | } 16 | 17 | void noteDelay(uint32_t ms) 18 | { 19 | ::delay(static_cast(ms)); 20 | } 21 | -------------------------------------------------------------------------------- /src/NoteTxn.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_TXN_HPP 2 | #define NOTE_TXN_HPP 3 | 4 | #include 5 | #include 6 | 7 | class NoteTxn 8 | { 9 | public: 10 | 11 | virtual ~NoteTxn(void) {} 12 | 13 | /**************************************************************************/ 14 | /*! 15 | @brief A blocking call used to initiate a transaction with the Notecard 16 | @param[in] timeout_ms 17 | The number of milliseconds to wait before aborting the request 18 | @returns A boolean indicating success. 19 | */ 20 | /**************************************************************************/ 21 | virtual bool start (uint32_t timeout_ms) = 0; 22 | 23 | /**************************************************************************/ 24 | /*! 25 | @brief Non-blocking call to terminate the transaction 26 | */ 27 | /**************************************************************************/ 28 | virtual void stop (void) = 0; 29 | }; 30 | 31 | /******************************************************************************/ 32 | /*! 33 | @brief Helper function to abstract, create and maintain a single instance 34 | of the NoteTxn interface implementation, as required by the underlying 35 | `note-c` library. 36 | @param[in] txn_parameters 37 | Pointer to the parameters required to instantiate 38 | the platform specific transaction implementation. 39 | */ 40 | /******************************************************************************/ 41 | template NoteTxn * make_note_txn (T & txn_parameters); 42 | NoteTxn * make_note_txn (nullptr_t); 43 | 44 | #endif // NOTE_TXN_HPP 45 | -------------------------------------------------------------------------------- /src/NoteTxn_Arduino.cpp: -------------------------------------------------------------------------------- 1 | #include "NoteTxn_Arduino.hpp" 2 | 3 | #ifndef NOTE_MOCK 4 | #include 5 | #else 6 | #include "mock/mock-arduino.hpp" 7 | #include "mock/mock-parameters.hpp" 8 | #endif 9 | 10 | // Singleton instance of the NoteTxn_Arduino class 11 | namespace instance { 12 | inline NoteTxn* & note_txn (void) { 13 | static NoteTxn* note_txn = nullptr; 14 | return note_txn; 15 | } 16 | }; 17 | 18 | NoteTxn * 19 | make_note_txn ( 20 | nullptr_t 21 | ) { 22 | NoteTxn* & note_txn = instance::note_txn(); 23 | if (note_txn) { 24 | delete note_txn; 25 | note_txn = nullptr; 26 | } 27 | return note_txn; 28 | } 29 | 30 | template 31 | NoteTxn * 32 | make_note_txn ( 33 | T & txn_pins_ 34 | ) { 35 | NoteTxn* & note_txn = instance::note_txn(); 36 | 37 | if (txn_pins_[0] == txn_pins_[1]) { 38 | // Invalid tuple invokes deletion 39 | if (note_txn) { 40 | delete note_txn; 41 | note_txn = nullptr; 42 | } 43 | } else if (!note_txn) { 44 | note_txn = new NoteTxn_Arduino(txn_pins_[0], txn_pins_[1]); 45 | } 46 | 47 | return note_txn; 48 | } 49 | 50 | NoteTxn_Arduino::NoteTxn_Arduino 51 | ( 52 | uint8_t ctx_pin_, 53 | uint8_t rtx_pin_ 54 | ) : 55 | _ctx_pin(ctx_pin_), 56 | _rtx_pin(rtx_pin_) 57 | { 58 | // Float CTX/RTX to conserve energy 59 | ::pinMode(_ctx_pin, INPUT); 60 | ::pinMode(_rtx_pin, INPUT); 61 | } 62 | 63 | bool 64 | NoteTxn_Arduino::start ( 65 | uint32_t timeout_ms_ 66 | ) 67 | { 68 | bool result = false; 69 | 70 | // Signal Request To Transact 71 | ::pinMode(_rtx_pin, OUTPUT); 72 | ::digitalWrite(_rtx_pin, HIGH); 73 | 74 | // Await Clear To Transact Signal 75 | ::pinMode(_ctx_pin, INPUT_PULLUP); 76 | for (uint32_t timeout = (::millis() + timeout_ms_) 77 | ; ::millis() < timeout 78 | ; ::delay(1) 79 | ) { 80 | if (HIGH == ::digitalRead(_ctx_pin)) { 81 | result = true; 82 | break; 83 | } 84 | } 85 | 86 | // Float CTX to conserve energy 87 | ::pinMode(_ctx_pin, INPUT); 88 | 89 | // Abandon request on timeout 90 | if (!result) { 91 | stop(); 92 | } 93 | 94 | return result; 95 | } 96 | 97 | void 98 | NoteTxn_Arduino::stop ( 99 | void 100 | ) 101 | { 102 | // Float RTX pin 103 | ::pinMode(_rtx_pin, INPUT); 104 | } 105 | 106 | // Explicitly instantiate the template function for array types 107 | template NoteTxn * make_note_txn(uint8_t(&)[2]); 108 | template NoteTxn * make_note_txn(const uint8_t(&)[2]); 109 | -------------------------------------------------------------------------------- /src/NoteTxn_Arduino.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_TXN_ARDUINO_HPP 2 | #define NOTE_TXN_ARDUINO_HPP 3 | 4 | #include "NoteTxn.hpp" 5 | 6 | class NoteTxn_Arduino final : public NoteTxn 7 | { 8 | public: 9 | NoteTxn_Arduino (uint8_t ctx_pin, uint8_t rtx_pin); 10 | bool start (uint32_t timeout_ms) override; 11 | void stop (void) override; 12 | 13 | private: 14 | uint8_t _ctx_pin; 15 | uint8_t _rtx_pin; 16 | }; 17 | 18 | #endif // NOTE_TXN_ARDUINO_HPP 19 | -------------------------------------------------------------------------------- /src/Notecard.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file Notecard.h 3 | * 4 | * The note-arduino Arduino library for communicating with the 5 | * Blues 6 | * Notecard via serial or I2C. 7 | * 8 | * This library allows you to control a Notecard by writing an Arduino sketch in 9 | * C or C++. Your sketch may programmatically configure Notecard and send Notes 10 | * to Notehub.io. 11 | * 12 | * This library is a wrapper around the 13 | * note-c library, which it 14 | * includes as a git subtree. 15 | * 16 | * Written by Ray Ozzie and Brandon Satrom for Blues Inc. 17 | * 18 | * MIT License. Use of this source code is governed by licenses granted by the 19 | * copyright holder including that found in the 20 | * LICENSE 21 | * file. 22 | * 23 | */ 24 | #ifndef Notecard_h 25 | #define Notecard_h 26 | 27 | #include 28 | #include 29 | 30 | #include "NoteDefines.h" 31 | #include "NoteI2c.hpp" 32 | #include "NoteLog.hpp" 33 | #include "NoteSerial.hpp" 34 | #include "NoteTxn.hpp" 35 | 36 | #ifdef ARDUINO 37 | #include 38 | #ifdef NOTE_ARDUINO_SOFTWARE_SERIAL_SUPPORT 39 | #include 40 | #endif 41 | #include 42 | #include "NoteSerial_Arduino.hpp" 43 | #endif 44 | 45 | #ifndef NOTE_MOCK 46 | #include 47 | #else 48 | #include "mock/mock-arduino.hpp" 49 | #include "mock/mock-parameters.hpp" 50 | #endif 51 | 52 | /**************************************************************************/ 53 | /*! 54 | @brief Class that stores state and functions for interacting with the 55 | Blues Notecard. 56 | */ 57 | /**************************************************************************/ 58 | class Notecard 59 | { 60 | public: 61 | ~Notecard(void); 62 | #ifdef ARDUINO 63 | inline void begin(uint32_t i2cAddress = NOTE_I2C_ADDR_DEFAULT, 64 | uint32_t i2cMax = NOTE_I2C_MAX_DEFAULT, 65 | TwoWire &wirePort = Wire) { 66 | begin(make_note_i2c(wirePort), i2cAddress, i2cMax); 67 | } 68 | inline void begin(HardwareSerial &serial, uint32_t speed = 9600) { 69 | MakeNoteSerial_ArduinoParameters arduino_parameters(serial, speed); 70 | begin(make_note_serial>(arduino_parameters)); 71 | } 72 | #ifdef NOTE_ARDUINO_SOFTWARE_SERIAL_SUPPORT 73 | inline void begin(SoftwareSerial &serial, uint32_t speed = 9600) { 74 | MakeNoteSerial_ArduinoParameters arduino_parameters(serial, speed); 75 | begin(make_note_serial>(arduino_parameters)); 76 | } 77 | #endif 78 | inline void setDebugOutputStream(Stream &dbgserial) { 79 | setDebugOutputStream(make_note_log(dbgserial)); 80 | } 81 | inline void setTransactionPins(uint8_t ctx_pin, uint8_t rtx_pin) { 82 | uint8_t txn_pins[2] = {ctx_pin, rtx_pin}; 83 | setTransactionPins(make_note_txn(txn_pins)); 84 | } 85 | #endif 86 | void begin(NoteI2c * noteI2c, 87 | uint32_t i2cAddress = NOTE_I2C_ADDR_DEFAULT, 88 | uint32_t i2cMax = NOTE_I2C_MAX_DEFAULT); 89 | void begin(NoteSerial * noteSerial); 90 | inline void clearDebugOutputStream(void) { 91 | setDebugOutputStream(nullptr); 92 | } 93 | inline void clearTransactionPins(void) { 94 | setTransactionPins(nullptr); 95 | } 96 | bool debugSyncStatus (int pollFrequencyMs, int maxLevel); 97 | void deleteResponse(J *rsp) const; 98 | void end(void); 99 | NOTE_ARDUINO_DEPRECATED void logDebug(const char *message) const; 100 | NOTE_ARDUINO_DEPRECATED void logDebugf(const char *format, ...) const; 101 | J *newCommand(const char *request) const; 102 | J *newRequest(const char *request) const; 103 | J *requestAndResponse(J *req) const; 104 | J *requestAndResponseWithRetry(J *req, uint32_t timeoutSeconds) const; 105 | bool responseError(J *rsp) const; 106 | bool sendRequest(J *req) const; 107 | bool sendRequestWithRetry(J *req, uint32_t timeoutSeconds) const; 108 | void setDebugOutputStream(NoteLog * noteLog); 109 | void setFn(mallocFn mallocHook, freeFn freeHook, delayMsFn delayMsHook, getMsFn getMsHook); 110 | void setFnI2cMutex(mutexFn lockI2cFn, mutexFn unlockI2cFn); 111 | void setFnNoteMutex(mutexFn lockNoteFn, mutexFn unlockNoteFn); 112 | void setTransactionPins(NoteTxn * noteTxn); 113 | 114 | private: 115 | void platformInit (bool assignCallbacks); 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /src/Notecarrier.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef NOTECARRIER_SUPPORT_H 3 | #define NOTECARRIER_SUPPORT_H 4 | 5 | /* 6 | * \brief Adafruit Feather-Specific Definitions 7 | * 8 | * The Adafruit feather specification defines standard pinout names for digital 9 | * and analog pins. Unfortunately, the Adafruit Huzzah32 does not comply with 10 | * this specification. As such, we provide mappings for the pins that are 11 | * technically incorrect but which make it easier to write code that works 12 | * across different feathers. 13 | * 14 | * On the ESP32 for instance, the pinout for Digital Pin 14 is located where 15 | * D5 is usually located, so our symbol maps D5 to Digital Pin 14 on that 16 | * board, etc... 17 | * 18 | * \see https://learn.adafruit.com/adafruit-feather/feather-specification 19 | */ 20 | 21 | // Establish portable Notecarrier pin definitions 22 | 23 | #ifndef D5 24 | #define D5 5 25 | #endif 26 | 27 | #ifndef D6 28 | #define D6 6 29 | #endif 30 | 31 | #ifndef D9 32 | #define D9 9 33 | #endif 34 | 35 | #ifndef D10 36 | #define D10 10 37 | #endif 38 | 39 | #ifndef D11 40 | #define D11 11 41 | #endif 42 | 43 | #ifndef D12 44 | #define D12 12 45 | #endif 46 | 47 | #ifndef D13 48 | #define D13 13 49 | #endif 50 | 51 | #if defined(ARDUINO_APOLLO3_SFE_ARTEMIS_THING_PLUS) 52 | 53 | #ifdef B0 54 | #undef B0 55 | #endif 56 | #define B0 2 57 | 58 | #ifdef D5 59 | #undef D5 60 | #endif 61 | #define D5 3 62 | 63 | #ifdef D6 64 | #undef D6 65 | #endif 66 | #define D6 4 67 | 68 | #ifdef D9 69 | #undef D9 70 | #endif 71 | #define D9 5 72 | 73 | #ifdef D10 74 | #undef D10 75 | #endif 76 | #define D10 6 77 | 78 | #ifdef D11 79 | #undef D11 80 | #endif 81 | #define D11 7 82 | 83 | #ifdef D12 84 | #undef D12 85 | #endif 86 | #define D12 8 87 | 88 | #ifdef D13 89 | #undef D13 90 | #endif 91 | #define D13 9 92 | 93 | #elif defined(ARDUINO_FEATHER_ESP32) 94 | 95 | #ifdef B0 96 | #undef B0 97 | #endif 98 | #define B0 21 99 | 100 | #ifdef D5 101 | #undef D5 102 | #endif 103 | #define D5 14 104 | 105 | #ifdef D6 106 | #undef D6 107 | #endif 108 | #define D6 32 109 | 110 | #ifdef D9 111 | #undef D9 112 | #endif 113 | #define D9 15 114 | 115 | #ifdef D10 116 | #undef D10 117 | #endif 118 | #define D10 33 119 | 120 | #ifdef D11 121 | #undef D11 122 | #endif 123 | #define D11 27 124 | 125 | #ifdef D12 126 | #undef D12 127 | #endif 128 | #define D12 12 129 | 130 | #ifdef D13 131 | #undef D13 132 | #endif 133 | #define D13 13 134 | 135 | #elif defined(ARDUINO_FEATHER_F405) 136 | 137 | #ifdef B0 138 | #undef B0 139 | #endif 140 | #define B0 PNUM_NOT_DEFINED 141 | 142 | #elif defined(ARDUINO_FEATHER_M4) 143 | 144 | #ifdef B0 145 | #undef B0 146 | #endif 147 | #define B0 4 148 | 149 | #elif defined(ARDUINO_NRF52840_FEATHER) 150 | 151 | #ifdef B0 152 | #undef B0 153 | #endif 154 | #define B0 0 155 | 156 | #elif defined(ARDUINO_SWAN_R5) 157 | 158 | // Swan v1 (deprecated) 159 | #ifdef CS 160 | #undef CS 161 | #endif 162 | #define CS PD0 163 | 164 | #ifdef B0_V1 165 | #undef B0_V1 166 | #endif 167 | #define B0_V1 CS 168 | 169 | // Swan v3 170 | #ifdef B0 171 | #undef B0 172 | #endif 173 | #define B0 PH3 174 | 175 | #endif 176 | 177 | #endif // NOTECARRIER_SUPPORT_H 178 | -------------------------------------------------------------------------------- /src/note-c/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Doxyfile.bak 3 | latex/ 4 | html/ 5 | __pycache__/ 6 | 7 | # VS Code workspace files 8 | *.code-workspace 9 | *.orig 10 | settings.json 11 | 12 | # Development Artifacts 13 | CMakeFiles/ 14 | CMakeCache.txt 15 | cppcheck_output.txt 16 | -------------------------------------------------------------------------------- /src/note-c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | cmake_policy(SET CMP0095 NEW) 3 | 4 | if ("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") 5 | message(FATAL_ERROR "In-source builds are not allowed. 6 | Please create a build directory and use `cmake ..` inside it. 7 | NOTE: cmake will now create CMakeCache.txt and CMakeFiles/*. 8 | You must delete them, or cmake will refuse to work.") 9 | endif() 10 | 11 | project(note_c) 12 | 13 | # Automatically ignore CMake build directory. 14 | if(NOT EXISTS ${PROJECT_BINARY_DIR}/.gitignore) 15 | file(WRITE ${PROJECT_BINARY_DIR}/.gitignore "*") 16 | endif() 17 | 18 | option(NOTE_C_BUILD_DOCS "Build docs." OFF) 19 | option(NOTE_C_BUILD_TESTS "Build tests." ON) 20 | option(NOTE_C_COVERAGE "Compile for test NOTE_C_COVERAGE reporting." OFF) 21 | option(NOTE_C_LOW_MEM "Build the library tailored for low memory usage." OFF) 22 | option(NOTE_C_MEM_CHECK "Run tests with Valgrind." OFF) 23 | option(NOTE_C_NO_LIBC "Build the library without linking against libc, generating errors for any undefined symbols." OFF) 24 | option(NOTE_C_SHOW_MALLOC "Build the library with flags required to log memory usage." OFF) 25 | option(NOTE_C_SINGLE_PRECISION "Use single precision for JSON floating point numbers." OFF) 26 | 27 | set(NOTE_C_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 28 | add_library(note_c SHARED) 29 | target_sources( 30 | note_c 31 | PRIVATE 32 | ${NOTE_C_SRC_DIR}/n_atof.c 33 | ${NOTE_C_SRC_DIR}/n_b64.c 34 | ${NOTE_C_SRC_DIR}/n_cjson.c 35 | ${NOTE_C_SRC_DIR}/n_cjson_helpers.c 36 | ${NOTE_C_SRC_DIR}/n_cobs.c 37 | ${NOTE_C_SRC_DIR}/n_const.c 38 | ${NOTE_C_SRC_DIR}/n_ftoa.c 39 | ${NOTE_C_SRC_DIR}/n_helpers.c 40 | ${NOTE_C_SRC_DIR}/n_hooks.c 41 | ${NOTE_C_SRC_DIR}/n_i2c.c 42 | ${NOTE_C_SRC_DIR}/n_md5.c 43 | ${NOTE_C_SRC_DIR}/n_printf.c 44 | ${NOTE_C_SRC_DIR}/n_request.c 45 | ${NOTE_C_SRC_DIR}/n_serial.c 46 | ${NOTE_C_SRC_DIR}/n_str.c 47 | ) 48 | target_compile_options( 49 | note_c 50 | PRIVATE 51 | -Wall 52 | -Wextra 53 | -Wpedantic 54 | -Werror 55 | -Og 56 | -ggdb 57 | PUBLIC 58 | -m32 59 | -mfpmath=sse 60 | -msse2 61 | ) 62 | target_include_directories( 63 | note_c 64 | PUBLIC ${NOTE_C_SRC_DIR} 65 | ) 66 | target_link_directories( 67 | note_c 68 | PUBLIC 69 | /lib32 70 | /usr/lib32 71 | /usr/lib/gcc/x86_64-linux-gnu/12/32 72 | ) 73 | target_link_options( 74 | note_c 75 | PUBLIC 76 | -m32 77 | -Wl,-melf_i386 78 | ) 79 | 80 | if(NOTE_C_LOW_MEM) 81 | target_compile_definitions( 82 | note_c 83 | PUBLIC 84 | NOTE_C_LOW_MEM 85 | ) 86 | else() 87 | # This file is empty if NOTE_C_LOW_MEM is defined, which leads to a warning 88 | # about an empty translation unit, so we only add it to the build if 89 | # NOTE_C_LOW_MEM is false. 90 | target_sources( 91 | note_c 92 | PRIVATE 93 | ${NOTE_C_SRC_DIR}/n_ua.c 94 | ) 95 | endif() 96 | 97 | if(NOTE_C_NO_LIBC) 98 | target_link_options( 99 | note_c 100 | PRIVATE 101 | -nostdlib 102 | -nodefaultlibs 103 | LINKER:--no-undefined 104 | ) 105 | endif() 106 | 107 | if(NOTE_C_SHOW_MALLOC) 108 | target_compile_definitions( 109 | note_c 110 | PUBLIC 111 | NOTE_C_SHOW_MALLOC 112 | ) 113 | endif() 114 | 115 | if(NOTE_C_SINGLE_PRECISION) 116 | target_compile_definitions( 117 | note_c 118 | PUBLIC 119 | NOTE_C_SINGLE_PRECISION 120 | ) 121 | endif() 122 | 123 | if(NOTE_C_BUILD_TESTS) 124 | # Including CTest here rather than in test/CMakeLists.txt allows us to run 125 | # ctest from the root build directory (e.g. build/ instead of build/test/). 126 | # We also need to set MEMORYCHECK_COMMAND_OPTIONS before including this. 127 | # See: https://stackoverflow.com/a/60741757 128 | if(NOTE_C_MEM_CHECK) 129 | # Go ahead and make sure we can find valgrind while we're here. 130 | find_program(VALGRIND valgrind REQUIRED) 131 | message(STATUS "Found valgrind: ${VALGRIND}") 132 | set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --error-exitcode=1") 133 | endif(NOTE_C_MEM_CHECK) 134 | include(CTest) 135 | 136 | target_compile_definitions( 137 | note_c 138 | PUBLIC 139 | NOTE_C_TEST 140 | ) 141 | target_include_directories( 142 | note_c 143 | PRIVATE 144 | ${CMAKE_CURRENT_LIST_DIR}/test/include 145 | ) 146 | 147 | add_subdirectory(test) 148 | endif(NOTE_C_BUILD_TESTS) 149 | 150 | if(NOTE_C_BUILD_DOCS) 151 | add_subdirectory(docs) 152 | endif(NOTE_C_BUILD_DOCS) 153 | -------------------------------------------------------------------------------- /src/note-c/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | By participating in this project, you agree to abide by the 4 | [Blues Inc code of conduct][1]. 5 | 6 | [1]: https://blues.github.io/opensource/code-of-conduct 7 | 8 | -------------------------------------------------------------------------------- /src/note-c/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to blues/note-c 2 | 3 | We love pull requests from everyone. By participating in this project, you 4 | agree to abide by the Blues Inc [code of conduct]. 5 | 6 | Here are some ways *you* can contribute: 7 | 8 | * by using alpha, beta, and prerelease versions 9 | * by reporting bugs 10 | * by suggesting new features 11 | * by writing or editing documentation 12 | * by writing specifications 13 | * by writing code ( **no patch is too small** : fix typos, add comments, 14 | clean up inconsistent whitespace ) 15 | * by refactoring code 16 | * by closing [issues][] 17 | * by reviewing patches 18 | 19 | [issues]: https://github.com/blues/note-c/issues 20 | 21 | ## Submitting an Issue 22 | 23 | * We use the [GitHub issue tracker][issues] to track bugs and features. 24 | * Before submitting a bug report or feature request, check to make sure it 25 | hasn't 26 | already been submitted. 27 | * When submitting a bug report, please include a [Gist][] that includes a stack 28 | trace and any details that may be necessary to reproduce the bug, including 29 | your gem version, Ruby version, and operating system. Ideally, a bug report 30 | should include a pull request with failing specs. 31 | 32 | [gist]: https://gist.github.com/ 33 | 34 | ## Library Development 35 | 36 | Review our [contribution guidelines](./CONTRIBUTING.md) and [developer 37 | documentation](./test/README.md) for setting up your environment and 38 | configuring your toolchain. 39 | 40 | ## Cleaning up issues 41 | 42 | * Issues that have no response from the submitter will be closed after 30 days. 43 | * Issues will be closed once they're assumed to be fixed or answered. If the 44 | maintainer is wrong, it can be opened again. 45 | * If your issue is closed by mistake, please understand and explain the issue. 46 | We will happily reopen the issue. 47 | 48 | ## Submitting a Pull Request 49 | 1. [Fork][fork] the [official repository][repo]. 50 | 2. [Create a topic branch.][branch] 51 | 3. Implement your feature or bug fix. 52 | 4. Add, commit, and push your changes. 53 | 5. [Submit a pull request.][pr] 54 | 55 | ## Notes 56 | * Please add tests if you changed code. Contributions without tests won't be 57 | * accepted. If you don't know how to add tests, please put in a PR and leave a 58 | * comment asking for help. We love helping! 59 | 60 | [repo]: https://github.com/blues/note-c/tree/master 61 | [fork]: https://help.github.com/articles/fork-a-repo/ 62 | [branch]: 63 | https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/ 64 | [pr]: https://help.github.com/articles/creating-a-pull-request-from-a-fork/ 65 | 66 | Inspired by: 67 | https://github.com/thoughtbot/factory_bot/blob/master/CONTRIBUTING.md 68 | 69 | [code of conduct]: https://blues.github.io/opensource/code-of-conduct 70 | -------------------------------------------------------------------------------- /src/note-c/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Blues Inc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/note-c/README.md: -------------------------------------------------------------------------------- 1 | [![Coverage Status][coverage badge]][coverage details] 2 | 3 | # note-c 4 | 5 | The note-c C library for communicating with the 6 | [Blues Wireless][blues] Notecard via serial or I²C. 7 | 8 | This library allows you to control a Notecard by writing a C 9 | or C++ program. Your program may programmatically configure Notecard and send 10 | Notes to [Notehub.io][notehub]. 11 | 12 | This library is used by the [note-arduino library][note-arduino], which includes 13 | it as a git subtree. 14 | 15 | ## API Documentation 16 | 17 | The API documentation for this library can be found [here][note-c API docs]. 18 | 19 | ## Logging Control 20 | 21 | `note-c` provides a comprehensive and flexible logging functionality. 22 | 23 | To activate logging, you must provide a callback for logging via 24 | `hookDebugOutput()`. The callback takes the following form: 25 | 26 | ```c 27 | typedef size_t (*debugOutputFn) (const char *text); 28 | ``` 29 | 30 | The callback is responsible for taking a character array (C-style string) and 31 | returning the number of bytes processed (written out) as confirmation. The 32 | exact implementation will be up to the user who provided the function pointer, 33 | but its presence will active logging in the library. 34 | 35 | ### Library Logging 36 | 37 | #### Log Levels 38 | 39 | `note-c` provides for four (4) levels of logging. Here they are listed from 40 | most severe to most verbose: 41 | 42 | 0. `NOTE_C_LOG_LEVEL_ERROR` 43 | 1. `NOTE_C_LOG_LEVEL_WARN` 44 | 2. `NOTE_C_LOG_LEVEL_INFO` 45 | 3. `NOTE_C_LOG_LEVEL_DEBUG` 46 | 47 | By default, `note-c` logs at `NOTE_C_LOG_LEVEL_INFO`. 48 | 49 | #### Default Logging Behavior 50 | 51 | To modify the default behavior, you may specify the desired log level at compile 52 | time, as follows: 53 | 54 | ```sh 55 | -DNOTE_C_LOG_LEVEL=0 56 | ``` 57 | 58 | _**NOTE:** In the example above, you will notice we used zero (`0`), instead of 59 | `NOTE_C_LOG_LEVEL_ERROR`. This is because the warning constants are internal to 60 | the library, and not available in the context of the command line._ 61 | 62 | Here, we have decided to show only the most severe (i.e. `[ERROR]`) logs. 63 | Alternatively, you may set the level to any of the values listed above. 64 | 65 | #### Dynamic Logging Behavior 66 | 67 | In the previous section, we discussed setting the base (or default) logging 68 | behavior for the library. However, you may also set the log level dynamically, 69 | during runtime, by using the `NoteSetLogLevel()` API. 70 | 71 | ```c 72 | NoteSetLogLevel(NOTE_C_LOG_LEVEL_WARN); 73 | ``` 74 | 75 | ### Notecard Sync Logging (`[SYNC]`) 76 | 77 | Tangential to the standard logging behavior, `note-c` also provides a helper 78 | function to invoke/extract synchronization logs from the Notecard. 79 | 80 | - `NoteDebugSyncStatus()` 81 | 82 | Instead of toggling features inside the library, this helper functions sends a 83 | request to the Notecard to inquire about its synchronization status and logs 84 | those details. 85 | 86 | The function is designed to be called in a loop and throttled by a parameter. 87 | See [the documentation page][NoteDebugSyncStatus] for more information. 88 | 89 | ## Versioning 90 | 91 | The `note-c` versioning scheme is a variant of [Semantic 92 | Versioning](https://semver.org/). 93 | 94 | Below is a high-level overview of the major/minor/patch versions: 95 | 96 | - Major Version: Signals incompatible API changes. 97 | - Minor Version: Signals added functionality in a backward compatible manner. 98 | - Patch Version: Signals backward compatible bug fixes. 99 | 100 | Beyond the SemVer foundation, Blues has imposed additional requirements for a 101 | version to be considered valid: 102 | 103 | - Major/minor/patch versions SHALL NOT be zero. 104 | - For anything other than major version, version numbers MUST NOT contain 105 | EITHER leading zeroes OR trailing zeroes (e.g. version `1.10.2` is invalid). 106 | 107 | > Example version progression: 108 | > 109 | > `1.8.1`, `1.9.1`, `1.9.2`, `1.11.1`, `1.11.2`, `1.11.3`, `1.12.1`, `2.1.1` 110 | 111 | These additional constraints have been observed to help disambiguate versions 112 | and reduce support burden. 113 | 114 | ### Version Artifacts 115 | 116 | The version can be referenced/tested programmatically via the following 117 | preprocessor defined integers found in `note.h`: 118 | 119 | - `NOTE_C_VERSION_MAJOR` 120 | - `NOTE_C_VERSION_MINOR` 121 | - `NOTE_C_VERSION_PATCH` 122 | 123 | The version may also be logged via the preprocessor defined string literal, 124 | `NOTE_C_VERSION`. 125 | 126 | ## Contributing 127 | 128 | We love issues, fixes, and pull requests from everyone. By participating in this 129 | project, you agree to abide by the Blues Inc [code of conduct]. 130 | 131 | For details on contributions we accept and the process for contributing, see our 132 | [contribution guide](CONTRIBUTING.md). 133 | 134 | ## More Information 135 | 136 | For additional Notecard SDKs and Libraries, see: 137 | 138 | - [note-arduino][note-arduino] for Arduino support 139 | - [note-python][note-python] for Python 140 | - [note-go][note-go] for Go 141 | 142 | ## To learn more about Blues Wireless, the Notecard and Notehub, see: 143 | 144 | - [blues.com](https://blues.io) 145 | - [notehub.io][notehub] 146 | - [wireless.dev](https://wireless.dev) 147 | 148 | ## License 149 | 150 | Copyright (c) 2019 Blues Inc. Released under the MIT license. See 151 | [LICENSE](LICENSE) for details. 152 | 153 | [blues]: https://blues.com 154 | [code of conduct]: https://blues.github.io/opensource/code-of-conduct 155 | [coverage badge]: https://coveralls.io/repos/github/blues/note-c/badge.svg?branch=master 156 | [coverage details]: https://coveralls.io/github/blues/note-c?branch=master 157 | [NoteDebugSyncStatus]: https://blues.github.io/note-c/api_reference.html#c.NoteDebugSyncStatus 158 | [notehub]: https://notehub.io 159 | [note-arduino]: https://github.com/blues/note-arduino 160 | [note-c API docs]: https://blues.github.io/note-c/index.html 161 | [note-go]: https://github.com/blues/note-go 162 | [note-python]: https://github.com/blues/note-python 163 | -------------------------------------------------------------------------------- /src/note-c/n_atof.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file n_atof.c 3 | * 4 | * Derived from the "strtod" library procedure to be locale-independent 5 | * 6 | * Copyright (c) 1988-1993 The Regents of the University of California. 7 | * Copyright (c) 1994 Sun Microsystems, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this 10 | * software and its documentation for any purpose and without 11 | * fee is hereby granted, provided that the above copyright 12 | * notice appear in all copies. The University of California 13 | * makes no representations about the suitability of this 14 | * software for any purpose. It is provided "as is" without 15 | * express or implied warranty. 16 | * 17 | * RCS: @(#) $Id$ 18 | */ 19 | 20 | #ifdef HAVE_STDLIB_H 21 | # include 22 | #endif 23 | #include 24 | 25 | #include "n_lib.h" 26 | 27 | #ifndef __STDC__ 28 | # ifdef __GNUC__ 29 | # define const __const__ 30 | # else 31 | # define const 32 | # endif 33 | #endif 34 | 35 | #ifndef TRUE 36 | #define TRUE 1 37 | #define FALSE 0 38 | #endif 39 | #ifndef NULL 40 | #define NULL 0 41 | #endif 42 | 43 | #define MAX_EXPONENT 511 /* Largest possible base 10 exponent. Any 44 | * exponent larger than this will already 45 | * produce underflow or overflow, so there's 46 | * no need to worry about additional digits. 47 | */ 48 | 49 | /* 50 | *---------------------------------------------------------------------- 51 | * 52 | * atof -- a LOCALE-INDEPENDENT string to floating point 53 | * 54 | * This procedure converts a floating-point number from an ASCII 55 | * decimal representation to internal double-precision format. 56 | * 57 | * Results: 58 | * The return value is the double-precision floating-point 59 | * representation of the characters in string. If endPtr isn't 60 | * NULL, then *endPtr is filled in with the address of the 61 | * next character after the last one that was part of the 62 | * floating-point number. 63 | * 64 | * Side effects: 65 | * None. 66 | * 67 | *---------------------------------------------------------------------- 68 | */ 69 | 70 | JNUMBER 71 | JAtoN(string, endPtr) 72 | const char *string; /* A decimal ASCII floating-point number, 73 | * optionally preceded by white space. 74 | * Must have form "-I.FE-X", where I is the 75 | * integer part of the mantissa, F is the 76 | * fractional part of the mantissa, and X 77 | * is the exponent. Either of the signs 78 | * may be "+", "-", or omitted. Either I 79 | * or F may be omitted, or both. The decimal 80 | * point isn't necessary unless F is present. 81 | * The "E" may actually be an "e". E and X 82 | * may both be omitted (but not just one). 83 | */ 84 | char **endPtr; /* If non-NULL, store terminating character's 85 | * address here. */ 86 | { 87 | int sign, expSign = FALSE; 88 | JNUMBER fraction; 89 | register const char *p; 90 | register int c; 91 | int exp = 0; /* Exponent read from "EX" field. */ 92 | int fracExp = 0; /* Exponent that derives from the fractional 93 | * part. Under normal circumstatnces, it is 94 | * the negative of the number of digits in F. 95 | * However, if I is very long, the last digits 96 | * of I get dropped (otherwise a long I with a 97 | * large negative exponent could cause an 98 | * unnecessary overflow on I alone). In this 99 | * case, fracExp is incremented one for each 100 | * dropped digit. */ 101 | int mantSize; /* Number of digits in mantissa. */ 102 | int decPt; /* Number of mantissa digits BEFORE decimal 103 | * point. */ 104 | const char *pExp; /* Temporarily holds location of exponent 105 | * in string. */ 106 | 107 | /* 108 | * Strip off leading blanks and check for a sign. 109 | */ 110 | 111 | p = string; 112 | while (*p == ' ') { 113 | p += 1; 114 | } 115 | if (*p == '-') { 116 | sign = TRUE; 117 | p += 1; 118 | } else { 119 | if (*p == '+') { 120 | p += 1; 121 | } 122 | sign = FALSE; 123 | } 124 | 125 | /* 126 | * Count the number of digits in the mantissa (including the decimal 127 | * point), and also locate the decimal point. 128 | */ 129 | 130 | decPt = -1; 131 | for (mantSize = 0; ; mantSize += 1) { 132 | c = *p; 133 | if (c < '0' || c > '9') { 134 | if ((c != '.') || (decPt >= 0)) { 135 | break; 136 | } 137 | decPt = mantSize; 138 | } 139 | p += 1; 140 | } 141 | 142 | /* 143 | * Now suck up the digits in the mantissa. Use two integers to 144 | * collect 9 digits each (this is faster than using floating-point). 145 | * If the mantissa has more than 18 digits, ignore the extras, since 146 | * they can't affect the value anyway. 147 | */ 148 | 149 | pExp = p; 150 | p -= mantSize; 151 | if (decPt < 0) { 152 | decPt = mantSize; 153 | } else { 154 | mantSize -= 1; /* One of the digits was the point. */ 155 | } 156 | if (mantSize > 18) { 157 | fracExp = decPt - 18; 158 | mantSize = 18; 159 | } else { 160 | fracExp = decPt - mantSize; 161 | } 162 | if (mantSize == 0) { 163 | fraction = 0.0; 164 | p = string; 165 | goto done; 166 | } else { 167 | long frac1, frac2; 168 | frac1 = 0L; 169 | for ( ; mantSize > 9; mantSize -= 1) { 170 | c = *p; 171 | p += 1; 172 | if (c == '.') { 173 | c = *p; 174 | p += 1; 175 | } 176 | frac1 = 10*frac1 + (c - '0'); 177 | } 178 | frac2 = 0L; 179 | for (; mantSize > 0; mantSize -= 1) { 180 | c = *p; 181 | p += 1; 182 | if (c == '.') { 183 | c = *p; 184 | p += 1; 185 | } 186 | frac2 = 10*frac2 + (c - '0'); 187 | } 188 | fraction = (1.0e9 * frac1) + frac2; 189 | } 190 | 191 | /* 192 | * Skim off the exponent. 193 | */ 194 | 195 | p = pExp; 196 | if ((*p == 'E') || (*p == 'e')) { 197 | p += 1; 198 | if (*p == '-') { 199 | expSign = TRUE; 200 | p += 1; 201 | } else { 202 | if (*p == '+') { 203 | p += 1; 204 | } 205 | expSign = FALSE; 206 | } 207 | while (*p >= '0' && *p <= '9') { 208 | exp = exp * 10 + (*p - '0'); 209 | p += 1; 210 | } 211 | } 212 | if (expSign) { 213 | exp = fracExp - exp; 214 | } else { 215 | exp = fracExp + exp; 216 | } 217 | 218 | /* 219 | * Generate a floating-point number that represents the exponent. 220 | * Do this by processing the exponent one bit at a time to combine 221 | * many powers of 2 of 10. Then combine the exponent with the 222 | * fraction. 223 | */ 224 | 225 | if (exp < 0) { 226 | expSign = TRUE; 227 | exp = -exp; 228 | } else { 229 | expSign = FALSE; 230 | } 231 | if (exp > MAX_EXPONENT) { 232 | exp = MAX_EXPONENT; 233 | } 234 | int d; 235 | for (d = 0; exp != 0; exp >>= 1, d += 1) { 236 | /* Table giving binary powers of 10. Entry */ 237 | /* is 10^2^i. Used to convert decimal */ 238 | /* exponents into floating-point numbers. */ 239 | JNUMBER p10 = 0.0; 240 | switch (d) { 241 | case 0: 242 | p10 = 10.0; 243 | break; 244 | case 1: 245 | p10 = 100.0; 246 | break; 247 | case 2: 248 | p10 = 1.0e4; 249 | break; 250 | case 3: 251 | p10 = 1.0e8; 252 | break; 253 | case 4: 254 | p10 = 1.0e16; 255 | break; 256 | case 5: 257 | p10 = 1.0e32; 258 | break; 259 | #ifndef NOTE_C_SINGLE_PRECISION 260 | case 6: 261 | p10 = 1.0e64; 262 | break; 263 | case 7: 264 | p10 = 1.0e128; 265 | break; 266 | case 8: 267 | p10 = 1.0e256; 268 | break; 269 | #endif 270 | } 271 | if (p10 == 0.0) { 272 | break; 273 | } 274 | if (exp & 01) { 275 | if (expSign) { 276 | fraction /= p10; 277 | } else { 278 | fraction *= p10; 279 | } 280 | } 281 | } 282 | 283 | done: 284 | if (endPtr != NULL) { 285 | *endPtr = (char *) p; 286 | } 287 | 288 | if (sign) { 289 | return -fraction; 290 | } 291 | return fraction; 292 | } 293 | -------------------------------------------------------------------------------- /src/note-c/n_b64.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file n_b64.c 3 | * 4 | * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. 5 | * * 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. 7 | * 8 | * This file contains Original Code and/or Modifications of Original Code 9 | * as defined in and that are subject to the Apple Public Source License 10 | * Version 2.0 (the 'License'). You may not use this file except in 11 | * compliance with the License. Please obtain a copy of the License at 12 | * http://www.opensource.apple.com/apsl/ and read it before using this 13 | * file. 14 | * 15 | * The Original Code and all software distributed under the License are 16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 20 | * Please see the License for the specific language governing rights and 21 | * limitations under the License. 22 | * 23 | */ 24 | /* ==================================================================== 25 | * Copyright (c) 1995-1999 The Apache Group. All rights reserved. 26 | * 27 | * Redistribution and use in source and binary forms, with or without 28 | * modification, are permitted provided that the following conditions 29 | * are met: 30 | * 31 | * 1. Redistributions of source code must retain the above copyright 32 | * notice, this list of conditions and the following disclaimer. 33 | * 34 | * 2. Redistributions in binary form must reproduce the above copyright 35 | * notice, this list of conditions and the following disclaimer in 36 | * the documentation and/or other materials provided with the 37 | * distribution. 38 | * 39 | * 3. All advertising materials mentioning features or use of this 40 | * software must display the following acknowledgment: 41 | * "This product includes software developed by the Apache Group 42 | * for use in the Apache HTTP server project (http://www.apache.org/)." 43 | * 44 | * 4. The names "Apache Server" and "Apache Group" must not be used to 45 | * endorse or promote products derived from this software without 46 | * prior written permission. For written permission, please contact 47 | * apache@apache.org. 48 | * 49 | * 5. Products derived from this software may not be called "Apache" 50 | * nor may "Apache" appear in their names without prior written 51 | * permission of the Apache Group. 52 | * 53 | * 6. Redistributions of any form whatsoever must retain the following 54 | * acknowledgment: 55 | * "This product includes software developed by the Apache Group 56 | * for use in the Apache HTTP server project (http://www.apache.org/)." 57 | * 58 | * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY 59 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 61 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR 62 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 63 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 64 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 65 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 67 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 68 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 69 | * OF THE POSSIBILITY OF SUCH DAMAGE. 70 | * ==================================================================== 71 | * 72 | * This software consists of voluntary contributions made by many 73 | * individuals on behalf of the Apache Group and was originally based 74 | * on public domain software written at the National Center for 75 | * Supercomputing Applications, University of Illinois, Urbana-Champaign. 76 | * For more information on the Apache Group and the Apache HTTP server 77 | * project, please see . 78 | * 79 | * Base64 encoder/decoder. Originally Apache file ap_base64.c 80 | */ 81 | 82 | #include 83 | #include "n_lib.h" 84 | 85 | /* aaaack but it's fast and const should make it shared text page. */ 86 | static const unsigned char pr2six[256] = { 87 | /* ASCII table */ 88 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 89 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 90 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 91 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 92 | 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 93 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 94 | 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 95 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 96 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 97 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 98 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 99 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 100 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 101 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 102 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 103 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 104 | }; 105 | 106 | int JB64DecodeLen(const char *bufcoded) 107 | { 108 | int nbytesdecoded; 109 | register const unsigned char *bufin; 110 | register int nprbytes; 111 | 112 | bufin = (const unsigned char *) bufcoded; 113 | while (pr2six[*(bufin++)] <= 63); 114 | 115 | nprbytes = (bufin - (const unsigned char *) bufcoded) - 1; 116 | nbytesdecoded = ((nprbytes + 3) / 4) * 3; 117 | 118 | return nbytesdecoded + 1; 119 | } 120 | 121 | int JB64Decode(char *bufplain, const char *bufcoded) 122 | { 123 | int nbytesdecoded; 124 | register const unsigned char *bufin; 125 | register unsigned char *bufout; 126 | register int nprbytes; 127 | 128 | bufin = (const unsigned char *) bufcoded; 129 | while (pr2six[*(bufin++)] <= 63); 130 | nprbytes = (bufin - (const unsigned char *) bufcoded) - 1; 131 | nbytesdecoded = ((nprbytes + 3) / 4) * 3; 132 | 133 | bufout = (unsigned char *) bufplain; 134 | bufin = (const unsigned char *) bufcoded; 135 | 136 | while (nprbytes > 4) { 137 | *(bufout++) = 138 | (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); 139 | *(bufout++) = 140 | (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); 141 | *(bufout++) = 142 | (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); 143 | bufin += 4; 144 | nprbytes -= 4; 145 | } 146 | 147 | /* Note: (nprbytes == 1) would be an error, so just ingore that case */ 148 | if (nprbytes > 1) { 149 | *(bufout++) = 150 | (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); 151 | } 152 | if (nprbytes > 2) { 153 | *(bufout++) = 154 | (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); 155 | } 156 | if (nprbytes > 3) { 157 | *(bufout++) = 158 | (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); 159 | } 160 | 161 | *(bufout++) = '\0'; 162 | nbytesdecoded -= (4 - nprbytes) & 3; 163 | return nbytesdecoded; 164 | } 165 | 166 | static const char basis_64[] = 167 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 168 | 169 | int JB64EncodeLen(int len) 170 | { 171 | return ((len + 2) / 3 * 4) + 1; 172 | } 173 | 174 | int JB64Encode(char *encoded, const char *string, int len) 175 | { 176 | int i; 177 | char *p; 178 | 179 | p = encoded; 180 | for (i = 0; i < len - 2; i += 3) { 181 | *p++ = basis_64[(string[i] >> 2) & 0x3F]; 182 | *p++ = basis_64[((string[i] & 0x3) << 4) | ((int) (string[i + 1] & 0xF0) >> 4)]; 183 | *p++ = basis_64[((string[i + 1] & 0xF) << 2) | ((int) (string[i + 2] & 0xC0) >> 6)]; 184 | *p++ = basis_64[string[i + 2] & 0x3F]; 185 | } 186 | if (i < len) { 187 | *p++ = basis_64[(string[i] >> 2) & 0x3F]; 188 | if (i == (len - 1)) { 189 | *p++ = basis_64[((string[i] & 0x3) << 4)]; 190 | *p++ = '='; 191 | } else { 192 | *p++ = basis_64[((string[i] & 0x3) << 4) | ((int) (string[i + 1] & 0xF0) >> 4)]; 193 | *p++ = basis_64[((string[i + 1] & 0xF) << 2)]; 194 | } 195 | *p++ = '='; 196 | } 197 | 198 | *p++ = '\0'; 199 | return p - encoded; 200 | } 201 | -------------------------------------------------------------------------------- /src/note-c/n_cobs.c: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Blues Inc. All rights reserved. 2 | // Use of this source code is governed by licenses granted by the 3 | // copyright holder including that found in the LICENSE file. 4 | 5 | #include 6 | 7 | #define COBS_EOP_OVERHEAD 1 8 | 9 | //**************************************************************************/ 10 | /*! 11 | @brief Decode a string encoded with COBS encoding 12 | 13 | @details Because the decoded length is guaranteed to be less than or equal to 14 | length, the decode may be done in-place. Default behavior (with eop == 0) is 15 | to restore all '\0' into output data, but if a different value is specified, 16 | the input is XOR'ed such that THAT byte is the one that is assumed can't be 17 | in the input. 18 | 19 | @param ptr Pointer to the data to decode 20 | @param length Length of the data to decode 21 | @param eop Byte to use as the end-of-packet marker 22 | @param dst Pointer to the buffer for the decoded data 23 | 24 | @return the length of the decoded data 25 | */ 26 | /**************************************************************************/ 27 | uint32_t _cobsDecode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst) 28 | { 29 | const uint8_t *start = dst, *end = ptr + length; 30 | uint8_t code = 0xFF, copy = 0; 31 | for (; ptr < end; copy--) { 32 | if (copy != 0) { 33 | *dst++ = (*ptr++) ^ eop; 34 | } else { 35 | if (code != 0xFF) { 36 | *dst++ = 0; 37 | } 38 | copy = code = (*ptr++) ^ eop; 39 | if (code == 0) { 40 | break; 41 | } 42 | } 43 | } 44 | return dst - start; 45 | } 46 | 47 | //**************************************************************************/ 48 | /*! 49 | @brief Encode a string with Consistent Overhead Byte Stuffing (COBS) encoding 50 | 51 | @details behavior (with eop == 0) is to eliminate all '\0' from input data, 52 | but if a different value is specified, the output is XOR'ed such that THAT 53 | byte is the one that won't be contained in output. 54 | https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing 55 | 56 | @param ptr Pointer to the data to encode 57 | @param length Length of the data to encode 58 | @param eop Byte to use as the end-of-packet marker 59 | @param dst Pointer to the buffer for the encoded data 60 | 61 | @return the length of the encoded data 62 | 63 | @note You may use `_cobsEncodedLength()` to calculate the required size for 64 | the buffer pointed to by the `dst` parameter. 65 | 66 | @see _cobsEncodedLength() 67 | */ 68 | /**************************************************************************/ 69 | uint32_t _cobsEncode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst) 70 | { 71 | uint8_t ch; 72 | uint8_t *start = dst; 73 | uint8_t code = 1; 74 | uint8_t *code_ptr = dst++; // Where to insert the leading count 75 | while (length--) { 76 | ch = *ptr++; 77 | if (ch != 0) { // Input byte not zero 78 | *dst++ = ch ^ eop; 79 | code++; 80 | } 81 | if (ch == 0 || code == 0xFF) { // Input is zero or complete block 82 | *code_ptr = code ^ eop; 83 | code = 1; 84 | code_ptr = dst++; 85 | } 86 | } 87 | *code_ptr = code ^ eop; // Final code 88 | return (dst - start); 89 | } 90 | 91 | //**************************************************************************/ 92 | /*! 93 | @brief Compute the encoding length of unencoded data 94 | 95 | @param ptr Pointer to the data to encode 96 | @param length Length of the data to encode 97 | 98 | @return the length required for encoded data 99 | 100 | @note The computed length does not include the EOP (end-of-packet) marker 101 | */ 102 | /**************************************************************************/ 103 | uint32_t _cobsEncodedLength(const uint8_t *ptr, uint32_t length) 104 | { 105 | uint8_t ch; 106 | uint32_t dst = 1; 107 | uint8_t code = 1; 108 | while (length--) { 109 | ch = *ptr++; 110 | if (ch != 0) { // Input byte not zero 111 | dst++; 112 | code++; 113 | } 114 | if (ch == 0 || code == 0xFF) { // Input is zero or complete block 115 | code = 1; 116 | dst++; 117 | } 118 | } 119 | return dst; 120 | } 121 | 122 | //**************************************************************************/ 123 | /*! 124 | @brief Compute the max encoding length for a given length of unencoded data 125 | 126 | @param length Length of the data to encode 127 | 128 | @return The max length required to encode the data 129 | 130 | @note Since the contents of the buffer are unknown, then we must assume 131 | that the entire buffer has no end-of-packet markers. This would 132 | require the injection of overhead bytes (as opposed to the 133 | replacement of end-of-packet markers with overhead bytes) at 134 | intervals of 255, thus producing the worst case scenario. 135 | @note An additional byte is added for the EOP (end-of-packet) marker. 136 | */ 137 | /**************************************************************************/ 138 | uint32_t _cobsEncodedMaxLength(uint32_t length) 139 | { 140 | const uint32_t overheadBytes = ((length / 254) + ((length % 254) > 0)); 141 | return (length + overheadBytes + COBS_EOP_OVERHEAD); 142 | } 143 | 144 | //**************************************************************************/ 145 | /*! 146 | @brief Compute the maximum length of unencoded data that can fit into a 147 | buffer of specified length. 148 | 149 | @param bufLen Length of the buffer in bytes 150 | 151 | @return the length of unencoded data 152 | 153 | @note The computation may leave additional space at the end. 154 | @note An additional byte is added for the EOP (end-of-packet) marker. 155 | */ 156 | /**************************************************************************/ 157 | uint32_t _cobsGuaranteedFit(uint32_t bufLen) 158 | { 159 | uint32_t cobsOverhead = 1 + (bufLen / 254) + COBS_EOP_OVERHEAD; 160 | return (cobsOverhead > bufLen ? 0 : (bufLen - cobsOverhead)); 161 | } 162 | -------------------------------------------------------------------------------- /src/note-c/n_const.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file n_const.c 3 | * 4 | * Written by Ray Ozzie and Blues Inc. team. 5 | * 6 | * Copyright (c) 2019 Blues Inc. MIT License. Use of this source code is 7 | * governed by licenses granted by the copyright holder including that found in 8 | * the 9 | * LICENSE 10 | * file. 11 | * 12 | */ 13 | 14 | #include "n_lib.h" 15 | 16 | const char *c_null = "null"; 17 | const char *c_false = "false"; 18 | const char *c_true = "true"; 19 | const char *c_nullstring = ""; 20 | const char *c_newline = "\r\n"; 21 | const char *c_mem = "mem"; 22 | const char *c_iotimeout = "timeout {io}"; 23 | const char *c_err = "err"; 24 | const char *c_req = "req"; 25 | const char *c_cmd = "cmd"; 26 | const char *c_bad = "bad"; 27 | const char *c_iobad = "bad {io}"; 28 | const char *c_ioerr = "{io}"; 29 | const char *c_unsupported = "{not-supported}"; 30 | const char *c_badbinerr = "{bad-bin}"; 31 | -------------------------------------------------------------------------------- /src/note-c/n_printf.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file n_printf.c 3 | * 4 | * Written by Ray Ozzie and Blues Inc. team. 5 | * 6 | * Copyright (c) 2019 Blues Inc. MIT License. Use of this source code is 7 | * governed by licenses granted by the copyright holder including that found in 8 | * the 9 | * LICENSE 10 | * file. 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "n_lib.h" 19 | 20 | // Externalized Hooks 21 | //**************************************************************************/ 22 | /*! 23 | @brief Hook for the calling platform's debug interface, if any. 24 | */ 25 | /**************************************************************************/ 26 | extern debugOutputFn hookDebugOutput; 27 | 28 | //**************************************************************************/ 29 | /*! 30 | @brief Write a formatted string to the debug output. 31 | @param format A format string for output. 32 | @param ... One or more values to interpolate into the format string. 33 | */ 34 | /**************************************************************************/ 35 | void NoteDebugf(const char *format, ...) 36 | { 37 | #ifndef NOTE_NODEBUG 38 | if (_noteIsDebugOutputActive()) { 39 | char line[256]; 40 | va_list args; 41 | va_start(args, format); 42 | vsnprintf(line, sizeof(line), format, args); 43 | va_end(args); 44 | hookDebugOutput(line); 45 | } 46 | #endif 47 | } 48 | 49 | //**************************************************************************/ 50 | /*! 51 | @brief Write a formatted string to the debug output. 52 | @param format A format string for output. 53 | @param ... One or more values to interpolate into the format string. 54 | @note. Do NOT use this in a memory-constrained environment (vsnprintf is large) 55 | */ 56 | /**************************************************************************/ 57 | #ifndef NOTE_LOMEM 58 | bool NotePrintf(const char *format, ...) 59 | { 60 | char line[256]; 61 | va_list args; 62 | va_start(args, format); 63 | vsnprintf(line, sizeof(line), format, args); 64 | va_end(args); 65 | return NotePrint(line); 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /src/note-c/n_str.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file n_str.c 3 | * 4 | * $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ 5 | * $OpenBSD: strlcat.c,v 1.12 2005/03/30 20:13:52 otto Exp $ 6 | * 7 | * Copyright (c) 1998 Todd C. Miller 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include "note.h" 24 | 25 | /* 26 | * Copy src to string dst of size siz. At most siz-1 characters 27 | * will be copied. Always NUL terminates (unless siz == 0). 28 | * Returns strlen(src); if retval >= siz, truncation occurred. 29 | */ 30 | #if defined(_MSC_VER) 31 | size_t strlcpy(char *dst, const char *src, size_t siz) 32 | #else 33 | __attribute__((weak)) size_t strlcpy(char *dst, const char *src, size_t siz) 34 | #endif 35 | { 36 | char *d = dst; 37 | const char *s = src; 38 | size_t n = siz; 39 | 40 | /* Copy as many bytes as will fit */ 41 | if (n != 0) { 42 | while (--n != 0) { 43 | if ((*d++ = *s++) == '\0') { 44 | break; 45 | } 46 | } 47 | } 48 | 49 | /* Not enough room in dst, add NUL and traverse rest of src */ 50 | if (n == 0) { 51 | if (siz != 0) { 52 | *d = '\0'; /* NUL-terminate dst */ 53 | } 54 | while (*s++) 55 | ; 56 | } 57 | 58 | return(s - src - 1); /* count does not include NUL */ 59 | } 60 | 61 | /* 62 | * Appends src to string dst of size siz (unlike strncat, siz is the 63 | * full size of dst, not space left). At most siz-1 characters 64 | * will be copied. Always NUL terminates (unless siz <= strlen(dst)). 65 | * Returns strlen(src) + MIN(siz, strlen(initial dst)). 66 | * If retval >= siz, truncation occurred. 67 | */ 68 | #if defined(_MSC_VER) 69 | size_t strlcat(char *dst, const char *src, size_t siz) 70 | #else 71 | __attribute__((weak)) size_t strlcat(char *dst, const char *src, size_t siz) 72 | #endif 73 | { 74 | char *d = dst; 75 | const char *s = src; 76 | size_t n = siz; 77 | size_t dlen; 78 | 79 | /* Find the end of dst and adjust bytes left but don't go past end */ 80 | while (n-- != 0 && *d != '\0') { 81 | d++; 82 | } 83 | dlen = d - dst; 84 | n = siz - dlen; 85 | 86 | if (n == 0) { 87 | return(dlen + strlen(s)); 88 | } 89 | while (*s != '\0') { 90 | if (n != 1) { 91 | *d++ = *s; 92 | n--; 93 | } 94 | s++; 95 | } 96 | *d = '\0'; 97 | 98 | return(dlen + (s - src)); /* count does not include NUL */ 99 | } 100 | -------------------------------------------------------------------------------- /src/note-c/n_ua.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file n_ua.c 3 | * 4 | * Written by Ray Ozzie and Blues Inc. team. 5 | * 6 | * Copyright (c) 2019 Blues Inc. MIT License. Use of this source code is 7 | * governed by licenses granted by the copyright holder including that found in 8 | * the 9 | * LICENSE 10 | * file. 11 | * 12 | */ 13 | 14 | #ifndef NOTE_C_LOW_MEM 15 | 16 | #include "n_lib.h" 17 | 18 | // Override-able statics 19 | static char *n_agent = (char *) ("note-c" " " NOTE_C_VERSION); 20 | static int n_cpu_cores = 0; 21 | static int n_cpu_mem = 0; 22 | static int n_cpu_mhz = 0; 23 | static char *n_cpu_vendor = NULL; 24 | static char *n_os_family = NULL; 25 | static char *n_os_name = NULL; 26 | static char *n_os_platform = NULL; 27 | static char *n_os_version = NULL; 28 | 29 | #if defined(ARDUINO_ARCH_ARC32) 30 | static char *n_cpu_name = (char *) "arc32"; 31 | #elif defined(ARDUINO_ARCH_AVR) 32 | static char *n_cpu_name = (char *) "avr"; 33 | #elif defined(ARDUINO_ARCH_ESP32) 34 | static char *n_cpu_name = (char *) "esp32"; 35 | #elif defined(ARDUINO_ARCH_ESP8266) 36 | static char *n_cpu_name = (char *) "esp8266"; 37 | #elif defined(ARDUINO_ARCH_MEGAAVR) 38 | static char *n_cpu_name = (char *) "megaavr"; 39 | #elif defined(ARDUINO_ARCH_NRF52840) 40 | static char *n_cpu_name = (char *) "nrf52840"; 41 | #elif defined(ARDUINO_ARCH_NRF52) 42 | static char *n_cpu_name = (char *) "nrf52"; 43 | #elif defined(ARDUINO_ARCH_NRF51) 44 | static char *n_cpu_name = (char *) "nrf51"; 45 | #elif defined(ARDUINO_ARCH_PIC32) 46 | static char *n_cpu_name = (char *) "pic32"; 47 | #elif defined(ARDUINO_ARCH_SAMD) 48 | static char *n_cpu_name = (char *) "samd"; 49 | #elif defined(ARDUINO_ARCH_SAM) 50 | static char *n_cpu_name = (char *) "sam"; 51 | #elif defined(ARDUINO_ARCH_SPRESENSE) 52 | static char *n_cpu_name = (char *) "spresence"; 53 | #elif defined(ARDUINO_ARCH_STM32F0) 54 | static char *n_cpu_name = (char *) "stm32f0"; 55 | #elif defined(ARDUINO_ARCH_STM32F1) 56 | static char *n_cpu_name = (char *) "stm32f1"; 57 | #elif defined(ARDUINO_ARCH_STM32F4) 58 | static char *n_cpu_name = (char *) "stm32f4"; 59 | #elif defined(ARDUINO_ARCH_STM32G0) 60 | static char *n_cpu_name = (char *) "stm32g0"; 61 | #elif defined(ARDUINO_SWAN_R5) 62 | static char *n_cpu_name = (char *) "swan_r5"; 63 | #elif defined(ARDUINO_ARCH_STM32L4) 64 | static char *n_cpu_name = (char *) "stm32l4"; 65 | #elif defined(ARDUINO_ARCH_STM32U5) 66 | static char *n_cpu_name = (char *) "stm32u5"; 67 | #elif defined(ARDUINO_ARCH_STM32) 68 | static char *n_cpu_name = (char *) "stm32"; 69 | #else 70 | static char *n_cpu_name = NULL; 71 | #endif 72 | 73 | /**************************************************************************/ 74 | /*! 75 | @brief Override-able method to add more data to the user agent object 76 | @returns a `J` cJSON object with the user agent object. 77 | */ 78 | /**************************************************************************/ 79 | #if defined(_MSC_VER) 80 | void NoteUserAgentUpdate(J *ua) 81 | #else 82 | __attribute__((weak)) void NoteUserAgentUpdate(J *ua) 83 | #endif 84 | { 85 | ((void)ua); // avoid compiler warning 86 | } 87 | 88 | /**************************************************************************/ 89 | /*! 90 | @brief Override-able method to return user agent object 91 | @returns a `J` cJSON object with the user agent object. 92 | */ 93 | /**************************************************************************/ 94 | #if defined(_MSC_VER) 95 | J *NoteUserAgent() 96 | #else 97 | __attribute__((weak)) J *NoteUserAgent() 98 | #endif 99 | { 100 | 101 | J *ua = JCreateObject(); 102 | if (ua == NULL) { 103 | return ua; 104 | } 105 | 106 | #if defined(__cplusplus) 107 | #define PLUS " c++" 108 | #else 109 | #define PLUS "" 110 | #endif 111 | 112 | #if defined(__ICCARM__) 113 | char *compiler = (char *) ("iar arm" PLUS " " NOTE_C_STRINGIZE(__VER__)); 114 | #elif defined(__IAR_SYSTEMS_ICC__) 115 | char *compiler = (char *) ("iar" PLUS " " NOTE_C_STRINGIZE(__VER__)); 116 | #elif defined(__clang__) 117 | char *compiler = (char *) ("clang" PLUS " " __VERSION__); 118 | #elif defined(__ATOLLIC__) && defined(__GNUC__) 119 | char *compiler = (char *) ("atollic gcc" PLUS " " __VERSION__); 120 | #elif defined(__GNUC__) 121 | char *compiler = (char *) ("gcc" PLUS " " __VERSION__); 122 | #elif defined(_MSC_FULL_VER) 123 | char *compiler = (char *) ("msc" PLUS " " _MSC_FULL_VER); 124 | #elif defined(__STDC_VERSION___) 125 | char *compiler = (char *) ("STDC" PLUS " " __STDC_VERSION__); 126 | #else 127 | char *compiler = (char *) ("unknown" PLUS " " __VERSION__) 128 | #endif 129 | 130 | JAddStringToObject(ua, "agent", n_agent); 131 | JAddStringToObject(ua, "compiler", compiler); 132 | 133 | switch (NoteGetActiveInterface()) { 134 | case NOTE_C_INTERFACE_NONE: 135 | JAddStringToObject(ua, "req_interface", "none"); 136 | break; 137 | case NOTE_C_INTERFACE_SERIAL: 138 | JAddStringToObject(ua, "req_interface", "serial"); 139 | break; 140 | case NOTE_C_INTERFACE_I2C: 141 | JAddStringToObject(ua, "req_interface", "i2c"); 142 | break; 143 | default: 144 | JAddStringToObject(ua, "req_interface", "unknown"); 145 | break; 146 | } 147 | 148 | // Add CPU Details 149 | if (n_cpu_cores != 0) { 150 | JAddNumberToObject(ua, "cpu_cores", n_cpu_cores); 151 | } 152 | if (n_cpu_mem != 0) { 153 | JAddNumberToObject(ua, "cpu_mem", n_cpu_mem); 154 | } 155 | if (n_cpu_mhz != 0) { 156 | JAddNumberToObject(ua, "cpu_mhz", n_cpu_mhz); 157 | } 158 | if (n_cpu_name != NULL) { 159 | JAddStringToObject(ua, "cpu_name", n_cpu_name); 160 | } 161 | if (n_cpu_vendor != NULL) { 162 | JAddStringToObject(ua, "cpu_vendor", n_cpu_vendor); 163 | } 164 | 165 | // Add Operating System Details 166 | if (n_os_family != NULL) { 167 | JAddStringToObject(ua, "os_family", n_os_family); 168 | } 169 | if (n_os_name != NULL) { 170 | JAddStringToObject(ua, "os_name", n_os_name); 171 | } 172 | if (n_os_platform != NULL) { 173 | JAddStringToObject(ua, "os_platform", n_os_platform); 174 | } 175 | if (n_os_version != NULL) { 176 | JAddStringToObject(ua, "os_version", n_os_version); 177 | } 178 | 179 | // Add more data to the UA from a higher level 180 | NoteUserAgentUpdate(ua); 181 | 182 | return ua; 183 | 184 | } 185 | 186 | /**************************************************************************/ 187 | /*! 188 | @brief Set key UA fields from a higher level library context 189 | */ 190 | /**************************************************************************/ 191 | void NoteSetUserAgent(char *agent) 192 | { 193 | n_agent = agent; 194 | } 195 | 196 | /**************************************************************************/ 197 | /*! 198 | @brief Set key UA fields from a higher level library context 199 | */ 200 | /**************************************************************************/ 201 | void NoteSetUserAgentOS(char *os_name, char *os_platform, char *os_family, char *os_version) 202 | { 203 | n_os_family = os_family; 204 | n_os_name = os_name; 205 | n_os_platform = os_platform; 206 | n_os_version = os_version; 207 | } 208 | 209 | /**************************************************************************/ 210 | /*! 211 | @brief Set key UA fields from a higher level library context 212 | */ 213 | /**************************************************************************/ 214 | void NoteSetUserAgentCPU(int cpu_mem, int cpu_mhz, int cpu_cores, char *cpu_vendor, char *cpu_name) 215 | { 216 | n_cpu_cores = cpu_cores; 217 | n_cpu_mem = cpu_mem; 218 | n_cpu_mhz = cpu_mhz; 219 | n_cpu_name = cpu_name; 220 | n_cpu_vendor = cpu_vendor; 221 | } 222 | 223 | #endif // !NOTE_C_LOW_MEM 224 | -------------------------------------------------------------------------------- /test/NoteLog_Arduino.test.cpp: -------------------------------------------------------------------------------- 1 | #include "NoteLog_Arduino.hpp" 2 | #include "TestFunction.hpp" 3 | #include "mock/mock-arduino.hpp" 4 | #include "mock/mock-parameters.hpp" 5 | 6 | #include 7 | #include 8 | 9 | // Compile command: g++ -Wall -Wextra -Wpedantic mock/mock-arduino.cpp ../src/NoteLog_Arduino.cpp NoteLog_Arduino.test.cpp -std=c++11 -I. -I../src -DNOTE_MOCK -ggdb -O0 -o noteLog_arduino.tests && ./noteLog_arduino.tests || echo "Tests Result: $?" 10 | 11 | int test_make_note_log_instantiates_notelog_object() 12 | { 13 | int result; 14 | 15 | // Arrange 16 | NoteLog * notelog = nullptr; 17 | Stream & serial_stream = Serial; 18 | 19 | // Action 20 | notelog = make_note_log(serial_stream); 21 | 22 | // Assert 23 | if (nullptr != notelog) 24 | { 25 | result = 0; 26 | } 27 | else 28 | { 29 | result = static_cast('d' + 'e' + 'b' + 'u' + 'g'); 30 | std::cout << "\33[31mFAILED\33[0m] " << __FILE__ << ":" << __LINE__ << std::endl; 31 | std::cout << "\tnotelog == " << !!notelog << ", EXPECTED: not nullptr" << std::endl; 32 | std::cout << "["; 33 | } 34 | 35 | // Clean-up 36 | make_note_log(nullptr); 37 | 38 | return result; 39 | } 40 | 41 | int test_make_note_log_enforces_singleton_by_returning_same_notelog_object_for_all_calls() 42 | { 43 | int result; 44 | 45 | // Arrange 46 | Stream & serial_stream = Serial; 47 | NoteLog * const notelog_1 = make_note_log(serial_stream); 48 | 49 | // Action 50 | NoteLog * const notelog_2 = make_note_log(serial_stream); 51 | 52 | // Assert 53 | if (notelog_1 == notelog_2) 54 | { 55 | result = 0; 56 | } 57 | else 58 | { 59 | result = static_cast('d' + 'e' + 'b' + 'u' + 'g'); 60 | std::cout << "\33[31mFAILED\33[0m] " << __FILE__ << ":" << __LINE__ << std::endl; 61 | std::cout << "\tnotelog_2 == " << std::hex << notelog_2 << ", EXPECTED: " << notelog_1 << std::endl; 62 | std::cout << "["; 63 | } 64 | 65 | // Clean-up 66 | make_note_log(nullptr); 67 | 68 | return result; 69 | } 70 | 71 | //int test_make_note_log_returns_nullptr_when_nullptr_is_passed_as_parameter() 72 | int test_make_note_log_deletes_singleton_when_nullptr_is_passed_as_parameter() 73 | { 74 | int result; 75 | 76 | // Arrange 77 | Stream & serial_stream = Serial; 78 | NoteLog * notelog = make_note_log(serial_stream); 79 | assert(notelog); 80 | 81 | // Action 82 | notelog = make_note_log(nullptr); 83 | 84 | // Assert 85 | if (nullptr == notelog) 86 | { 87 | result = 0; 88 | } 89 | else 90 | { 91 | result = static_cast('d' + 'e' + 'b' + 'u' + 'g'); 92 | std::cout << "\33[31mFAILED\33[0m] " << __FILE__ << ":" << __LINE__ << std::endl; 93 | std::cout << "\tnotelog == " << std::hex << notelog << ", EXPECTED: 0 (nullptr)" << std::endl; 94 | std::cout << "["; 95 | } 96 | 97 | return result; 98 | } 99 | 100 | int test_notelog_arduino_print_does_not_modify_str_parameter_value_before_passing_to_stream_print() 101 | { 102 | int result; 103 | 104 | // Arrange 105 | NoteLog_Arduino notelog(&Serial); 106 | const char EXPECTED_RESULT[] = "Hello, Test!"; 107 | 108 | streamPrint_Parameters.reset(); 109 | 110 | // Action 111 | notelog.print(EXPECTED_RESULT); 112 | 113 | // Assert 114 | if (!strcmp(EXPECTED_RESULT, streamPrint_Parameters.str_cache.c_str())) 115 | { 116 | result = 0; 117 | } 118 | else 119 | { 120 | result = static_cast('d' + 'e' + 'b' + 'u' + 'g'); 121 | std::cout << "\33[31mFAILED\33[0m] " << __FILE__ << ":" << __LINE__ << std::endl; 122 | std::cout << "\tstreamPrint_Parameters.str_cache.c_str() == \"" << streamPrint_Parameters.str_cache.c_str() << "\", EXPECTED: \"" << EXPECTED_RESULT << "\"" << std::endl; 123 | std::cout << "["; 124 | } 125 | 126 | return result; 127 | } 128 | 129 | int test_notelog_arduino_print_does_not_modify_stream_print_result_value_before_returning_to_caller() 130 | { 131 | int result; 132 | 133 | // Arrange 134 | NoteLog_Arduino notelog(&Serial); 135 | const size_t EXPECTED_RESULT = 13; 136 | 137 | streamPrint_Parameters.reset(); 138 | streamPrint_Parameters.result = EXPECTED_RESULT; 139 | 140 | // Action 141 | const size_t ACTUAL_RESULT = notelog.print("Hello, Test!"); 142 | 143 | // Assert 144 | if (ACTUAL_RESULT == EXPECTED_RESULT) 145 | { 146 | result = 0; 147 | } 148 | else 149 | { 150 | result = static_cast('d' + 'e' + 'b' + 'u' + 'g'); 151 | std::cout << "\33[31mFAILED\33[0m] " << __FILE__ << ":" << __LINE__ << std::endl; 152 | std::cout << "\tnotelog.print(\"Hello, Test!\") == " << ACTUAL_RESULT << ", EXPECTED: " << EXPECTED_RESULT << std::endl; 153 | std::cout << "["; 154 | } 155 | 156 | return result; 157 | } 158 | 159 | int main(void) 160 | { 161 | TestFunction tests[] = { 162 | {test_make_note_log_instantiates_notelog_object, "test_make_note_log_instantiates_notelog_object"}, 163 | {test_make_note_log_enforces_singleton_by_returning_same_notelog_object_for_all_calls, "test_make_note_log_enforces_singleton_by_returning_same_notelog_object_for_all_calls"}, 164 | {test_make_note_log_deletes_singleton_when_nullptr_is_passed_as_parameter, "test_make_note_log_deletes_singleton_when_nullptr_is_passed_as_parameter"}, 165 | {test_notelog_arduino_print_does_not_modify_str_parameter_value_before_passing_to_stream_print, "test_notelog_arduino_print_does_not_modify_buffer_parameter_value_before_passing_to_stream_print"}, 166 | {test_notelog_arduino_print_does_not_modify_stream_print_result_value_before_returning_to_caller, "test_notelog_arduino_print_does_not_modify_stream_print_result_value_before_returning_to_caller"}, 167 | }; 168 | 169 | return TestFunction::runTests(tests, (sizeof(tests) / sizeof(TestFunction))); 170 | } 171 | -------------------------------------------------------------------------------- /test/TestFunction.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEST_FUNCTION_HPP 2 | #define TEST_FUNCTION_HPP 3 | 4 | #include 5 | #include 6 | 7 | class TestFunction 8 | { 9 | public: 10 | typedef int (*test_fn)(void); 11 | 12 | TestFunction( 13 | test_fn fn_, 14 | const char *name_) : name(name_), 15 | fn(fn_) 16 | { 17 | } 18 | 19 | int operator()() 20 | { 21 | return fn(); 22 | } 23 | 24 | static int runTests(TestFunction *tests_, size_t cnt_) 25 | { 26 | int result = 0; 27 | 28 | std::cout << "Running " << cnt_ << " tests..." << std::endl; 29 | for (size_t i = 0; i < cnt_; ++i) 30 | { 31 | std::cout << "["; 32 | if (0 == (result = tests_[i]())) 33 | { 34 | std::cout << "\33[32mpassed\33[0m"; 35 | } 36 | else 37 | { 38 | std::cout << "\33[31mFAILED\33[0m"; 39 | } 40 | 41 | std::cout << "] " << tests_[i].name << std::endl; 42 | if (result) 43 | { 44 | break; 45 | } 46 | } 47 | 48 | return result; 49 | } 50 | 51 | const char *name; 52 | 53 | private: 54 | const test_fn fn; 55 | }; 56 | 57 | #endif // TEST_FUNCTION_HPP 58 | -------------------------------------------------------------------------------- /test/mock/NoteI2c_Mock.cpp: -------------------------------------------------------------------------------- 1 | #include "mock/NoteI2c_Mock.hpp" 2 | 3 | MakeNoteI2c_Parameters make_note_i2c_Parameters; 4 | NoteI2cReceive_Parameters noteI2cReceive_Parameters; 5 | NoteI2cReset_Parameters noteI2cReset_Parameters; 6 | NoteI2cTransmit_Parameters noteI2cTransmit_Parameters; 7 | 8 | NoteI2c * 9 | make_note_i2c ( 10 | nullptr_t 11 | ) { 12 | // Record invocation(s) 13 | ++make_note_i2c_Parameters.invoked; 14 | 15 | // Stash parameter(s) 16 | make_note_i2c_Parameters.i2c_parameters = nullptr; 17 | 18 | // Return user-supplied result 19 | return make_note_i2c_Parameters.result; 20 | } 21 | 22 | template 23 | NoteI2c * 24 | make_note_i2c ( 25 | T & i2c_parameters_ 26 | ) { 27 | // Record invocation(s) 28 | ++make_note_i2c_Parameters.invoked; 29 | 30 | // Stash parameter(s) 31 | make_note_i2c_Parameters.i2c_parameters = i2c_parameters_; 32 | 33 | // Return user-supplied result 34 | return make_note_i2c_Parameters.result; 35 | } 36 | 37 | const char * 38 | NoteI2c_Mock::receive ( 39 | uint16_t device_address_, 40 | uint8_t * buffer_, 41 | uint16_t requested_byte_count_, 42 | uint32_t * available_ 43 | ) { 44 | // Record invocation(s) 45 | ++noteI2cReceive_Parameters.invoked; 46 | 47 | // Stash parameter(s) 48 | noteI2cReceive_Parameters.device_address = device_address_; 49 | noteI2cReceive_Parameters.buffer = buffer_; 50 | noteI2cReceive_Parameters.requested_byte_count = requested_byte_count_; 51 | noteI2cReceive_Parameters.available = available_; 52 | 53 | // Return user-supplied result 54 | return noteI2cReceive_Parameters.result; 55 | } 56 | 57 | bool 58 | NoteI2c_Mock::reset ( 59 | uint16_t device_address_ 60 | ) { 61 | // Record invocation(s) 62 | ++noteI2cReset_Parameters.invoked; 63 | 64 | // Stash parameter(s) 65 | noteI2cReset_Parameters.device_address = device_address_; 66 | 67 | // Return user-supplied result 68 | return noteI2cReset_Parameters.result; 69 | } 70 | 71 | const char * 72 | NoteI2c_Mock::transmit ( 73 | uint16_t device_address_, 74 | uint8_t * buffer_, 75 | uint16_t size_ 76 | ) { 77 | // Record invocation(s) 78 | ++noteI2cTransmit_Parameters.invoked; 79 | 80 | // Stash parameter(s) 81 | noteI2cTransmit_Parameters.device_address = device_address_; 82 | noteI2cTransmit_Parameters.buffer = buffer_; 83 | if (buffer_) { 84 | noteI2cTransmit_Parameters.buffer_cache = reinterpret_cast(buffer_); 85 | } 86 | noteI2cTransmit_Parameters.size = size_; 87 | 88 | // Return user-supplied result 89 | return noteI2cTransmit_Parameters.result; 90 | } 91 | -------------------------------------------------------------------------------- /test/mock/NoteI2c_Mock.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_NOTE_I2C_HPP 2 | #define MOCK_NOTE_I2C_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "NoteI2c.hpp" 10 | #include "mock/mock-arduino.hpp" 11 | 12 | class NoteI2c_Mock final : public NoteI2c 13 | { 14 | public: 15 | const char * receive(uint16_t device_address, uint8_t * buffer, uint16_t requested_byte_count, uint32_t * available) override; 16 | bool reset(uint16_t device_address) override; 17 | const char * transmit(uint16_t device_address, uint8_t * buffer, uint16_t size) override; 18 | }; 19 | 20 | template 21 | struct MakeNoteI2c_Parameters { 22 | MakeNoteI2c_Parameters( 23 | void 24 | ) : 25 | invoked(0), 26 | i2c_parameters(nullptr), 27 | result(nullptr) 28 | { } 29 | void reset ( 30 | void 31 | ) { 32 | invoked = 0; 33 | i2c_parameters = nullptr; 34 | result = nullptr; 35 | } 36 | size_t invoked; 37 | T * i2c_parameters; 38 | NoteI2c * result; 39 | }; 40 | 41 | struct NoteI2cReceive_Parameters { 42 | NoteI2cReceive_Parameters( 43 | void 44 | ) : 45 | invoked(0), 46 | device_address(0), 47 | buffer(nullptr), 48 | requested_byte_count(0), 49 | available(nullptr), 50 | result(nullptr) 51 | { } 52 | void reset ( 53 | void 54 | ) { 55 | invoked = 0; 56 | device_address = 0; 57 | buffer = nullptr; 58 | buffer_cache.clear(); 59 | requested_byte_count = 0; 60 | available = nullptr; 61 | result = nullptr; 62 | } 63 | size_t invoked; 64 | uint16_t device_address; 65 | uint8_t * buffer; 66 | std::string buffer_cache; 67 | uint16_t requested_byte_count; 68 | uint32_t * available; 69 | const char * result; 70 | }; 71 | 72 | struct NoteI2cReset_Parameters { 73 | NoteI2cReset_Parameters( 74 | void 75 | ) : 76 | invoked(0), 77 | device_address(0), 78 | result(false) 79 | { } 80 | void reset ( 81 | void 82 | ) { 83 | invoked = 0; 84 | device_address = 0; 85 | result = false; 86 | } 87 | size_t invoked; 88 | uint16_t device_address; 89 | bool result; 90 | }; 91 | 92 | struct NoteI2cTransmit_Parameters { 93 | NoteI2cTransmit_Parameters( 94 | void 95 | ) : 96 | invoked(0), 97 | device_address(0), 98 | buffer(nullptr), 99 | size(0), 100 | result(nullptr) 101 | { } 102 | void reset ( 103 | void 104 | ) { 105 | invoked = 0; 106 | device_address = 0; 107 | buffer = nullptr; 108 | buffer_cache.clear(); 109 | size = 0; 110 | result = nullptr; 111 | } 112 | size_t invoked; 113 | uint16_t device_address; 114 | uint8_t * buffer; 115 | std::string buffer_cache; 116 | uint16_t size; 117 | const char * result; 118 | }; 119 | 120 | extern MakeNoteI2c_Parameters make_note_i2c_Parameters; 121 | extern NoteI2cReceive_Parameters noteI2cReceive_Parameters; 122 | extern NoteI2cReset_Parameters noteI2cReset_Parameters; 123 | extern NoteI2cTransmit_Parameters noteI2cTransmit_Parameters; 124 | 125 | #endif // MOCK_NOTE_I2C_HPP 126 | -------------------------------------------------------------------------------- /test/mock/NoteLog_Mock.cpp: -------------------------------------------------------------------------------- 1 | #include "mock/NoteLog_Mock.hpp" 2 | 3 | MakeNoteLog_Parameters make_note_log_Parameters; 4 | NoteLogPrint_Parameters noteLogPrint_Parameters; 5 | 6 | NoteLog * 7 | make_note_log ( 8 | nullptr_t 9 | ) { 10 | // Record invocation(s) 11 | ++make_note_log_Parameters.invoked; 12 | 13 | // Stash parameter(s) 14 | make_note_log_Parameters.log_parameters = nullptr; 15 | 16 | // Return user-supplied result 17 | return make_note_log_Parameters.result; 18 | } 19 | 20 | template 21 | NoteLog * 22 | make_note_log ( 23 | T & log_parameters_ 24 | ) 25 | { 26 | // Record invocation(s) 27 | ++make_note_log_Parameters.invoked; 28 | 29 | // Stash parameter(s) 30 | make_note_log_Parameters.log_parameters = log_parameters_; 31 | 32 | // Return user-supplied result 33 | return make_note_log_Parameters.result; 34 | } 35 | 36 | size_t 37 | NoteLog_Mock::print ( 38 | const char * message_ 39 | ) 40 | { 41 | // Record invocation(s) 42 | ++noteLogPrint_Parameters.invoked; 43 | 44 | // Stash parameter(s) 45 | noteLogPrint_Parameters.message = message_; 46 | 47 | // Return user-supplied result 48 | return noteLogPrint_Parameters.result; 49 | } 50 | -------------------------------------------------------------------------------- /test/mock/NoteLog_Mock.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_NOTE_LOG_HPP 2 | #define MOCK_NOTE_LOG_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "NoteLog.hpp" 8 | #include "mock-arduino.hpp" 9 | 10 | class NoteLog_Mock final : public NoteLog 11 | { 12 | public: 13 | size_t print(const char * message) override; 14 | }; 15 | 16 | template 17 | struct MakeNoteLog_Parameters { 18 | MakeNoteLog_Parameters( 19 | void 20 | ) : 21 | invoked(0), 22 | log_parameters(nullptr), 23 | result(nullptr) 24 | { } 25 | void reset ( 26 | void 27 | ) { 28 | invoked = 0; 29 | log_parameters = nullptr; 30 | result = nullptr; 31 | } 32 | size_t invoked; 33 | T * log_parameters; 34 | NoteLog * result; 35 | }; 36 | 37 | struct NoteLogPrint_Parameters { 38 | NoteLogPrint_Parameters( 39 | void 40 | ) : 41 | invoked(0), 42 | message(nullptr), 43 | result(0) 44 | { } 45 | void reset ( 46 | void 47 | ) { 48 | invoked = 0; 49 | message = nullptr; 50 | result = 0; 51 | } 52 | size_t invoked; 53 | const char * message; 54 | size_t result; 55 | }; 56 | 57 | extern MakeNoteLog_Parameters make_note_log_Parameters; 58 | extern NoteLogPrint_Parameters noteLogPrint_Parameters; 59 | 60 | #endif // MOCK_NOTE_LOG_HPP 61 | -------------------------------------------------------------------------------- /test/mock/NoteSerial_Mock.cpp: -------------------------------------------------------------------------------- 1 | #include "mock/NoteSerial_Mock.hpp" 2 | 3 | MakeNoteSerial_Parameters make_note_serial_Parameters; 4 | NoteSerialAvailable_Parameters noteSerialAvailable_Parameters; 5 | NoteSerialReceive_Parameters noteSerialReceive_Parameters; 6 | NoteSerialReset_Parameters noteSerialReset_Parameters; 7 | NoteSerialTransmit_Parameters noteSerialTransmit_Parameters; 8 | 9 | NoteSerial * 10 | make_note_serial ( 11 | nullptr_t 12 | ) { 13 | // Record invocation(s) 14 | ++make_note_serial_Parameters.invoked; 15 | 16 | // Stash parameter(s) 17 | make_note_serial_Parameters.serial_parameters = nullptr; 18 | 19 | // Return user-supplied result 20 | return make_note_serial_Parameters.result; 21 | } 22 | 23 | template 24 | NoteSerial * 25 | make_note_serial ( 26 | T & serial_parameters_ 27 | ) 28 | { 29 | // Record invocation(s) 30 | ++make_note_serial_Parameters.invoked; 31 | 32 | // Stash parameter(s) 33 | make_note_serial_Parameters.serial_parameters = &serial_parameters_; 34 | 35 | // Return user-supplied result 36 | return make_note_serial_Parameters.result; 37 | } 38 | 39 | size_t 40 | NoteSerial_Mock::available ( 41 | void 42 | ) 43 | { 44 | // Record invocation(s) 45 | ++noteSerialAvailable_Parameters.invoked; 46 | 47 | // Stash parameter(s) 48 | 49 | // Return user-supplied result 50 | return noteSerialAvailable_Parameters.result; 51 | } 52 | 53 | char 54 | NoteSerial_Mock::receive ( 55 | void 56 | ) 57 | { 58 | // Record invocation(s) 59 | ++noteSerialReceive_Parameters.invoked; 60 | 61 | // Stash parameter(s) 62 | 63 | // Return user-supplied result 64 | return noteSerialReceive_Parameters.result; 65 | } 66 | 67 | bool 68 | NoteSerial_Mock::reset ( 69 | void 70 | ) 71 | { 72 | // Record invocation(s) 73 | ++noteSerialReset_Parameters.invoked; 74 | 75 | // Stash parameter(s) 76 | 77 | // Return user-supplied result 78 | return noteSerialReset_Parameters.result; 79 | } 80 | 81 | size_t 82 | NoteSerial_Mock::transmit ( 83 | uint8_t *buffer_, 84 | size_t size_, 85 | bool flush_ 86 | ) 87 | { 88 | // Record invocation(s) 89 | ++noteSerialTransmit_Parameters.invoked; 90 | 91 | // Stash parameter(s) 92 | noteSerialTransmit_Parameters.buffer = buffer_; 93 | noteSerialTransmit_Parameters.size = size_; 94 | noteSerialTransmit_Parameters.flush = flush_; 95 | 96 | // Return user-supplied result 97 | return noteSerialTransmit_Parameters.result; 98 | } 99 | -------------------------------------------------------------------------------- /test/mock/NoteSerial_Mock.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_NOTE_SERIAL_HPP 2 | #define MOCK_NOTE_SERIAL_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "NoteSerial.hpp" 8 | #include "mock/mock-arduino.hpp" 9 | 10 | class NoteSerial_Mock final : public NoteSerial 11 | { 12 | public: 13 | size_t available(void) override; 14 | char receive(void) override; 15 | bool reset(void) override; 16 | size_t transmit(uint8_t * buffer, size_t size, bool flush) override; 17 | }; 18 | 19 | template 20 | struct MakeNoteSerial_Parameters { 21 | MakeNoteSerial_Parameters( 22 | void 23 | ) : 24 | invoked(0), 25 | serial_parameters(nullptr), 26 | result(nullptr) 27 | { } 28 | void reset ( 29 | void 30 | ) { 31 | invoked = 0; 32 | serial_parameters = nullptr; 33 | result = nullptr; 34 | } 35 | size_t invoked; 36 | T * serial_parameters; 37 | NoteSerial * result; 38 | }; 39 | 40 | struct NoteSerialAvailable_Parameters { 41 | NoteSerialAvailable_Parameters( 42 | void 43 | ) : 44 | invoked(0), 45 | result(0) 46 | { } 47 | void reset ( 48 | void 49 | ) { 50 | invoked = 0; 51 | result = 0; 52 | } 53 | size_t invoked; 54 | size_t result; 55 | }; 56 | 57 | struct NoteSerialReceive_Parameters { 58 | NoteSerialReceive_Parameters( 59 | void 60 | ) : 61 | invoked(0), 62 | result('\0') 63 | { } 64 | void reset ( 65 | void 66 | ) { 67 | invoked = 0; 68 | result = '\0'; 69 | } 70 | size_t invoked; 71 | char result; 72 | }; 73 | 74 | struct NoteSerialReset_Parameters { 75 | NoteSerialReset_Parameters( 76 | void 77 | ) : 78 | invoked(0), 79 | result(false) 80 | { } 81 | void reset ( 82 | void 83 | ) { 84 | invoked = 0; 85 | result = false; 86 | } 87 | size_t invoked; 88 | bool result; 89 | }; 90 | 91 | struct NoteSerialTransmit_Parameters { 92 | NoteSerialTransmit_Parameters( 93 | void 94 | ) : 95 | invoked(0), 96 | buffer(nullptr), 97 | size(0), 98 | flush(false), 99 | result(0) 100 | { } 101 | void reset ( 102 | void 103 | ) { 104 | invoked = 0; 105 | buffer = nullptr; 106 | size = 0; 107 | flush = false; 108 | result = 0; 109 | } 110 | size_t invoked; 111 | uint8_t * buffer; 112 | size_t size; 113 | bool flush; 114 | size_t result; 115 | }; 116 | 117 | extern MakeNoteSerial_Parameters make_note_serial_Parameters; 118 | extern NoteSerialAvailable_Parameters noteSerialAvailable_Parameters; 119 | extern NoteSerialReceive_Parameters noteSerialReceive_Parameters; 120 | extern NoteSerialReset_Parameters noteSerialReset_Parameters; 121 | extern NoteSerialTransmit_Parameters noteSerialTransmit_Parameters; 122 | 123 | #endif // MOCK_NOTE_SERIAL_HPP 124 | -------------------------------------------------------------------------------- /test/mock/NoteTime_Mock.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mock/NoteTime_Mock.hpp" 3 | 4 | #include "NoteTime.h" 5 | 6 | NoteDelay_Parameters noteDelay_Parameters; 7 | NoteMillis_Parameters noteMillis_Parameters; 8 | 9 | void noteDelay ( 10 | uint32_t ms_ 11 | ) { 12 | // Record invocation(s) 13 | ++noteDelay_Parameters.invoked; 14 | 15 | // Stash parameter(s) 16 | noteDelay_Parameters.ms = ms_; 17 | } 18 | 19 | uint32_t noteMillis ( 20 | void 21 | ) { 22 | // Record invocation(s) 23 | ++noteMillis_Parameters.invoked; 24 | 25 | // Return user-supplied result 26 | return noteMillis_Parameters.result; 27 | } 28 | -------------------------------------------------------------------------------- /test/mock/NoteTime_Mock.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_NOTE_TIME_HPP 2 | #define MOCK_NOTE_TIME_HPP 3 | 4 | #include 5 | #include 6 | 7 | struct NoteDelay_Parameters { 8 | NoteDelay_Parameters( 9 | void 10 | ) : 11 | invoked(0), 12 | ms(0) 13 | { } 14 | void reset ( 15 | void 16 | ) { 17 | invoked = 0; 18 | ms = 0; 19 | } 20 | size_t invoked; 21 | uint32_t ms; 22 | }; 23 | 24 | struct NoteMillis_Parameters { 25 | NoteMillis_Parameters( 26 | void 27 | ) : 28 | invoked(0), 29 | result(0) 30 | { } 31 | void reset ( 32 | void 33 | ) { 34 | invoked = 0; 35 | result = 0; 36 | } 37 | size_t invoked; 38 | uint32_t result; 39 | }; 40 | 41 | extern NoteDelay_Parameters noteDelay_Parameters; 42 | extern NoteMillis_Parameters noteMillis_Parameters; 43 | 44 | #endif // MOCK_NOTE_TIME_HPP 45 | -------------------------------------------------------------------------------- /test/mock/NoteTxn_Mock.cpp: -------------------------------------------------------------------------------- 1 | #include "mock/NoteTxn_Mock.hpp" 2 | 3 | MakeNoteTxn_Parameters make_note_txn_Parameters; 4 | NoteTxnStart_Parameters noteTxnStart_Parameters; 5 | NoteTxnStop_Parameters noteTxnStop_Parameters; 6 | 7 | NoteTxn * 8 | make_note_txn ( 9 | nullptr_t 10 | ) { 11 | // Record invocation(s) 12 | ++make_note_txn_Parameters.invoked; 13 | 14 | // Stash parameter(s) 15 | 16 | // Return user-supplied result 17 | return make_note_txn_Parameters.result; 18 | } 19 | 20 | template 21 | NoteTxn * 22 | make_note_txn ( 23 | T & txn_parameters_ 24 | ) 25 | { 26 | // Record invocation(s) 27 | ++make_note_txn_Parameters.invoked; 28 | 29 | // Stash parameter(s) 30 | make_note_txn_Parameters.txn_parameters = txn_parameters_; 31 | 32 | // Return user-supplied result 33 | return make_note_txn_Parameters.result; 34 | } 35 | 36 | bool 37 | NoteTxn_Mock::start ( 38 | uint32_t timeout_ms_ 39 | ) { 40 | // Record invocation(s) 41 | ++noteTxnStart_Parameters.invoked; 42 | 43 | // Stash parameter(s) 44 | noteTxnStart_Parameters.timeout_ms = timeout_ms_; 45 | 46 | // Return user-supplied result 47 | return noteTxnStart_Parameters.result; 48 | } 49 | 50 | void 51 | NoteTxn_Mock::stop ( 52 | void 53 | ) { 54 | // Record invocation(s) 55 | ++noteTxnStop_Parameters.invoked; 56 | 57 | // Stash parameter(s) 58 | 59 | // Return user-supplied result 60 | } 61 | -------------------------------------------------------------------------------- /test/mock/NoteTxn_Mock.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_NOTE_TXN_HPP 2 | #define MOCK_NOTE_TXN_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "NoteTxn.hpp" 8 | 9 | class NoteTxn_Mock final : public NoteTxn 10 | { 11 | public: 12 | bool start (uint32_t timeout_ms) override; 13 | void stop (void) override; 14 | }; 15 | 16 | template 17 | struct MakeNoteTxn_Parameters { 18 | MakeNoteTxn_Parameters( 19 | void 20 | ) : 21 | invoked(0), 22 | txn_parameters{0, 0}, 23 | result(nullptr) 24 | { } 25 | void reset ( 26 | void 27 | ) { 28 | invoked = 0; 29 | txn_parameters = nullptr; 30 | result = nullptr; 31 | } 32 | size_t invoked; 33 | T txn_parameters; 34 | NoteTxn * result; 35 | }; 36 | 37 | struct NoteTxnStart_Parameters { 38 | NoteTxnStart_Parameters( 39 | void 40 | ) : 41 | invoked(0), 42 | timeout_ms(0), 43 | result(false) 44 | { } 45 | void reset ( 46 | void 47 | ) { 48 | invoked = 0; 49 | timeout_ms = 0; 50 | result = false; 51 | } 52 | size_t invoked; 53 | uint32_t timeout_ms; 54 | bool result; 55 | }; 56 | 57 | struct NoteTxnStop_Parameters { 58 | NoteTxnStop_Parameters( 59 | void 60 | ) : 61 | invoked(0) 62 | { } 63 | void reset ( 64 | void 65 | ) { 66 | invoked = 0; 67 | } 68 | size_t invoked; 69 | }; 70 | 71 | extern MakeNoteTxn_Parameters make_note_txn_Parameters; 72 | extern NoteTxnStart_Parameters noteTxnStart_Parameters; 73 | extern NoteTxnStop_Parameters noteTxnStop_Parameters; 74 | 75 | #endif // MOCK_NOTE_TXN_HPP 76 | -------------------------------------------------------------------------------- /test/run_all_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GREEN='\x1B[0;32m' 4 | RED='\x1B[0;31m' 5 | YELLOW='\x1B[0;33m' 6 | DEFAULT='\x1B[0;0m' 7 | 8 | all_tests_result=0 9 | 10 | # This fixes a problem when running valgrind in a Docker container when the 11 | # host machine is running Fedora. See https://stackoverflow.com/a/75293014. 12 | ulimit -n 1024 13 | 14 | # Note that we use -Wno-deprecated-declarations in the compilation commands 15 | # below because we have deprecated some note-arduino functions (e.g. logDebug), 16 | # but we still have unit tests for them. 17 | 18 | if [ 0 -eq $all_tests_result ]; then 19 | echo && echo -e "${YELLOW}Compiling and running Notecard Test Suite...${DEFAULT}" 20 | g++ -fprofile-arcs -ftest-coverage -Wall -Wextra -Werror -Wpedantic -Wno-deprecated-declarations -std=c++11 -O0 -g \ 21 | src/Notecard.cpp \ 22 | test/Notecard.test.cpp \ 23 | test/mock/mock-arduino.cpp \ 24 | test/mock/mock-note-c-note.c \ 25 | test/mock/NoteI2c_Mock.cpp \ 26 | test/mock/NoteLog_Mock.cpp \ 27 | test/mock/NoteSerial_Mock.cpp \ 28 | test/mock/NoteTime_Mock.cpp \ 29 | test/mock/NoteTxn_Mock.cpp \ 30 | -Isrc \ 31 | -Itest \ 32 | -DNOTE_MOCK \ 33 | -o failed_test_run 34 | if [ 0 -eq $? ]; then 35 | valgrind --leak-check=full --error-exitcode=66 ./failed_test_run 36 | tests_result=$? 37 | if [ 0 -eq ${tests_result} ]; then 38 | echo -e "${GREEN}Notecard tests passed!${DEFAULT}" 39 | else 40 | echo -e "${RED}Notecard tests failed!${DEFAULT}" 41 | fi 42 | all_tests_result=$((all_tests_result+tests_result)) 43 | else 44 | all_tests_result=999 45 | fi 46 | fi 47 | 48 | if [ 0 -eq $all_tests_result ]; then 49 | echo && echo -e "${YELLOW}Compiling and running NoteI2c_Arduino Test Suite (no flags)...${DEFAULT}" 50 | g++ -fprofile-arcs -ftest-coverage -Wall -Wextra -Werror -Wpedantic -Wno-deprecated-declarations -std=c++11 -O0 -g \ 51 | src/NoteI2c_Arduino.cpp \ 52 | test/NoteI2c_Arduino.test.cpp \ 53 | test/mock/mock-arduino.cpp \ 54 | -Isrc \ 55 | -Itest \ 56 | -DNOTE_MOCK \ 57 | -o failed_test_run 58 | if [ 0 -eq $? ]; then 59 | valgrind --leak-check=full --error-exitcode=66 ./failed_test_run 60 | tests_result=$? 61 | if [ 0 -eq ${tests_result} ]; then 62 | echo -e "${GREEN}NoteI2c_Arduino tests passed!${DEFAULT}" 63 | else 64 | echo -e "${RED}NoteI2c_Arduino tests failed!${DEFAULT}" 65 | fi 66 | all_tests_result=$((all_tests_result+tests_result)) 67 | else 68 | all_tests_result=999 69 | fi 70 | fi 71 | 72 | if [ 0 -eq $all_tests_result ]; then 73 | echo && echo -e "${YELLOW}Compiling and running NoteI2c_Arduino Test Suite (-DWIRE_HAS_END)...${DEFAULT}" 74 | g++ -fprofile-arcs -ftest-coverage -Wall -Wextra -Werror -Wpedantic -Wno-deprecated-declarations -std=c++11 -O0 -g \ 75 | src/NoteI2c_Arduino.cpp \ 76 | test/NoteI2c_Arduino.test.cpp \ 77 | test/mock/mock-arduino.cpp \ 78 | -Isrc \ 79 | -Itest \ 80 | -DNOTE_MOCK \ 81 | -DWIRE_HAS_END \ 82 | -o failed_test_run 83 | if [ 0 -eq $? ]; then 84 | valgrind --leak-check=full --error-exitcode=66 ./failed_test_run 85 | tests_result=$? 86 | if [ 0 -eq ${tests_result} ]; then 87 | echo -e "${GREEN}NoteI2c_Arduino tests passed! (-DWIRE_HAS_END)${DEFAULT}" 88 | else 89 | echo -e "${RED}NoteI2c_Arduino tests failed!${DEFAULT}" 90 | fi 91 | all_tests_result=$((all_tests_result+tests_result)) 92 | else 93 | all_tests_result=999 94 | fi 95 | fi 96 | 97 | if [ 0 -eq $all_tests_result ]; then 98 | echo && echo -e "${YELLOW}Compiling and running NoteLog_Arduino Test Suite...${DEFAULT}" 99 | g++ -fprofile-arcs -ftest-coverage -Wall -Wextra -Werror -Wpedantic -Wno-deprecated-declarations -std=c++11 -O0 -g \ 100 | src/NoteLog_Arduino.cpp \ 101 | test/NoteLog_Arduino.test.cpp \ 102 | test/mock/mock-arduino.cpp \ 103 | -Isrc \ 104 | -Itest \ 105 | -DNOTE_MOCK \ 106 | -o failed_test_run 107 | if [ 0 -eq $? ]; then 108 | valgrind --leak-check=full --error-exitcode=66 ./failed_test_run 109 | tests_result=$? 110 | if [ 0 -eq ${tests_result} ]; then 111 | echo -e "${GREEN}NoteLog_Arduino tests passed!${DEFAULT}" 112 | else 113 | echo -e "${RED}NoteLog_Arduino tests failed!${DEFAULT}" 114 | fi 115 | all_tests_result=$((all_tests_result+tests_result)) 116 | else 117 | all_tests_result=999 118 | fi 119 | fi 120 | 121 | if [ 0 -eq $all_tests_result ]; then 122 | echo && echo -e "${YELLOW}Compiling and running NoteSerial_Arduino Test Suite...${DEFAULT}" 123 | g++ -fprofile-arcs -ftest-coverage -Wall -Wextra -Werror -Wpedantic -Wno-deprecated-declarations -std=c++11 -O0 -g \ 124 | src/NoteSerial_Arduino.cpp \ 125 | test/NoteSerial_Arduino.test.cpp \ 126 | test/mock/mock-arduino.cpp \ 127 | -Isrc \ 128 | -Itest \ 129 | -DNOTE_MOCK \ 130 | -o failed_test_run 131 | if [ 0 -eq $? ]; then 132 | valgrind --leak-check=full --error-exitcode=66 ./failed_test_run 133 | tests_result=$? 134 | if [ 0 -eq ${tests_result} ]; then 135 | echo -e "${GREEN}NoteSerial_Arduino tests passed!${DEFAULT}" 136 | else 137 | echo -e "${RED}NoteSerial_Arduino tests failed!${DEFAULT}" 138 | fi 139 | all_tests_result=$((all_tests_result+tests_result)) 140 | else 141 | all_tests_result=999 142 | fi 143 | fi 144 | 145 | if [ 0 -eq $all_tests_result ]; then 146 | echo && echo -e "${YELLOW}Compiling and running NoteTxn_Arduino Test Suite...${DEFAULT}" 147 | g++ -fprofile-arcs -ftest-coverage -Wall -Wextra -Werror -Wpedantic -Wno-deprecated-declarations -std=c++11 -O0 -g \ 148 | src/NoteTxn_Arduino.cpp \ 149 | test/NoteTxn_Arduino.test.cpp \ 150 | test/mock/mock-arduino.cpp \ 151 | -Isrc \ 152 | -Itest \ 153 | -DNOTE_MOCK \ 154 | -o failed_test_run 155 | if [ 0 -eq $? ]; then 156 | valgrind --leak-check=full --error-exitcode=66 ./failed_test_run 157 | tests_result=$? 158 | if [ 0 -eq ${tests_result} ]; then 159 | echo -e "${GREEN}NoteTxn_Arduino tests passed!${DEFAULT}" 160 | else 161 | echo -e "${RED}NoteTxn_Arduino tests failed!${DEFAULT}" 162 | fi 163 | all_tests_result=$((all_tests_result+tests_result)) 164 | else 165 | all_tests_result=999 166 | fi 167 | fi 168 | 169 | # Print summary statement 170 | if [ 0 -eq ${all_tests_result} ]; then 171 | echo && echo -e "${GREEN}All tests have passed!${DEFAULT}" && echo 172 | 173 | # Run coverage if available 174 | if [ $(which lcov) ]; then 175 | rm -f mock-*.gc?? *_Mock.gc?? *test.gc?? 176 | gcov --version \ 177 | && lcov --version \ 178 | && mkdir -p ./coverage \ 179 | && lcov --capture \ 180 | --directory . \ 181 | --no-external \ 182 | --exclude '/note-arduino/test/*' \ 183 | --output-file ./coverage/lcov.info \ 184 | --rc lcov_branch_coverage=1 185 | if [ ! -f "./coverage/lcov.info" ]; then 186 | echo -e "${YELLOW}COVERAGE REPORT NOT PRODUCED!!!${DEFAULT}"; 187 | all_tests_result=998 188 | else 189 | lcov --summary --rc lcov_branch_coverage=1 ./coverage/lcov.info 190 | fi 191 | fi 192 | rm -f failed_test_run 193 | else 194 | echo && echo -e "${RED}TESTS FAILED!!!${DEFAULT}" 195 | fi 196 | 197 | # Clean testing artifacts 198 | rm -f *.gcda *.gcno 199 | 200 | exit $all_tests_result 201 | --------------------------------------------------------------------------------