├── .github └── workflows │ ├── release.json │ ├── release.yaml │ ├── test.json │ ├── test.yaml │ └── to_yaml.sh ├── LICENSE.md ├── README.md ├── examples ├── decode │ └── decode.ino └── encode │ └── encode.ino ├── library.properties └── src ├── arduino_base64.hpp └── base64.cpp /.github/workflows/release.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "release", 3 | "on": { 4 | "push": { 5 | "tags": "v[0-9]+.[0-9]+.[0-9]+" 6 | } 7 | }, 8 | "jobs": { 9 | "release": { 10 | "name": "release: ${{github.ref_name}}", 11 | "runs-on": "ubuntu-latest", 12 | "steps": [{ 13 | "name": "clone repository", 14 | "uses": "actions/checkout@v4" 15 | }, { 16 | "name": "dispatch release", 17 | "uses": "softprops/action-gh-release@v2", 18 | "with": { 19 | "generate_release_notes": true 20 | } 21 | }] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: v[0-9]+.[0-9]+.[0-9]+ 5 | jobs: 6 | release: 7 | name: 'release: ${{github.ref_name}}' 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: clone repository 11 | uses: actions/checkout@v4 12 | - name: dispatch release 13 | uses: softprops/action-gh-release@v2 14 | with: 15 | generate_release_notes: true -------------------------------------------------------------------------------- /.github/workflows/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "on": { 4 | "push": { 5 | "branches-ignore": [ 6 | "master" 7 | ], 8 | "paths-ignore": [ 9 | ".git*", 10 | "**.md", 11 | "*.properties" 12 | ] 13 | }, 14 | "pull_request": { 15 | "branches": [ 16 | "master" 17 | ], 18 | "paths-ignore": [ 19 | ".git*", 20 | "**.md", 21 | "*.properties" 22 | ] 23 | } 24 | }, 25 | "jobs": { 26 | "test": { 27 | "name": "test: ${{matrix.board.name}}", 28 | "runs-on": "ubuntu-latest", 29 | "strategy": { 30 | "fail-fast": true, 31 | "matrix": { 32 | "board": [{ 33 | "vendor": "arduino", 34 | "arch": "avr", 35 | "name": "uno" 36 | }, { 37 | "vendor": "arduino", 38 | "arch": "samd", 39 | "name": "arduino_zero_native" 40 | }, { 41 | "vendor": "arduino", 42 | "arch": "renesas_uno", 43 | "name": "minima" 44 | }], 45 | "include": [{ 46 | "index": "https://downloads.arduino.cc/packages/package_index.json", 47 | "board": { 48 | "vendor": "arduino" 49 | } 50 | }] 51 | } 52 | }, 53 | "steps": [{ 54 | "name": "clone repository", 55 | "uses": "actions/checkout@v4" 56 | }, { 57 | "name": "install arduino and run test", 58 | "uses": "arduino/compile-sketches@v1", 59 | "with": { 60 | "fqbn": "${{matrix.board.vendor}}:${{matrix.board.arch}}:${{matrix.board.name}}", 61 | "platforms": "- name: ${{matrix.board.vendor}}:${{matrix.board.arch}}\n source-url: ${{matrix.index}}" 62 | } 63 | }] 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches-ignore: 5 | - master 6 | paths-ignore: 7 | - .git* 8 | - '**.md' 9 | - '*.properties' 10 | pull_request: 11 | branches: 12 | - master 13 | paths-ignore: 14 | - .git* 15 | - '**.md' 16 | - '*.properties' 17 | jobs: 18 | test: 19 | name: 'test: ${{matrix.board.name}}' 20 | runs-on: ubuntu-latest 21 | strategy: 22 | fail-fast: true 23 | matrix: 24 | board: 25 | - vendor: arduino 26 | arch: avr 27 | name: uno 28 | - vendor: arduino 29 | arch: samd 30 | name: arduino_zero_native 31 | - vendor: arduino 32 | arch: renesas_uno 33 | name: minima 34 | include: 35 | - index: https://downloads.arduino.cc/packages/package_index.json 36 | board: 37 | vendor: arduino 38 | steps: 39 | - name: clone repository 40 | uses: actions/checkout@v4 41 | - name: install arduino and run test 42 | uses: arduino/compile-sketches@v1 43 | with: 44 | fqbn: ${{matrix.board.vendor}}:${{matrix.board.arch}}:${{matrix.board.name}} 45 | platforms: |- 46 | - name: ${{matrix.board.vendor}}:${{matrix.board.arch}} 47 | source-url: ${{matrix.index}} -------------------------------------------------------------------------------- /.github/workflows/to_yaml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | 4 | cd ${0%/*} 5 | 6 | for _v in $(find ./ -maxdepth 1 -type f -name '*.json'); do 7 | yq -I 4 -o y ${_v} | head -c -1 > ${_v%.*}.yaml 8 | done; unset _v -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Kazuki Ota 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **Arduino Base64** 2 | ![actions:test](https://github.com/dojyorin/arduino_base64/actions/workflows/test.yaml/badge.svg) 3 | ![actions:release](https://github.com/dojyorin/arduino_base64/actions/workflows/release.yaml/badge.svg) 4 | ![shields:license](https://img.shields.io/github/license/dojyorin/arduino_base64) 5 | ![shields:release](https://img.shields.io/github/release/dojyorin/arduino_base64) 6 | 7 | Convert between binary and base64 encoded string. 8 | Easily convert sensor raw values, structures, etc... 9 | 10 | # Details 11 | This library made to convert binary data (e.g. raw sensor values) to base64 encoded string. 12 | String can be convert by cast them to `uint8_t*`, but that not what this library is for, nor do we plan to provide means. 13 | If you want to convert string, use this library and implement wrapper functions yourself. 14 | 15 | # API 16 | See [`arduino_base64.hpp`](./src/arduino_base64.hpp) for details. -------------------------------------------------------------------------------- /examples/decode/decode.ino: -------------------------------------------------------------------------------- 1 | #include "arduino_base64.hpp" 2 | 3 | void setup() { 4 | Serial.begin(115200); 5 | while(!Serial); 6 | 7 | const char input[] = "F3c7EYKkxMgnvO0nB8FWVw=="; 8 | uint8_t output[base64::decodeLength(input)]; 9 | base64::decode(input, output); 10 | 11 | Serial.println((const char*)output); 12 | } 13 | 14 | void loop() { 15 | // nop 16 | } -------------------------------------------------------------------------------- /examples/encode/encode.ino: -------------------------------------------------------------------------------- 1 | #include "arduino_base64.hpp" 2 | 3 | void setup() { 4 | Serial.begin(115200); 5 | while(!Serial); 6 | 7 | const uint8_t input[] = {0x17, 0x77, 0x3B, 0x11, 0x82, 0xA4, 0xC4, 0xC8}; 8 | auto inputLength = sizeof(input); 9 | char output[base64::encodeLength(inputLength)]; 10 | base64::encode(input, inputLength, output); 11 | 12 | Serial.println(output); 13 | } 14 | 15 | void loop() { 16 | // nop 17 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=base64_encode 2 | author=dojyorin 3 | version=2.0.4 4 | architectures=* 5 | includes=arduino_base64.hpp 6 | sentence=Convert between binary and base64 encoded string. 7 | paragraph=Easily convert sensor raw values, structures, etc... 8 | category=Other 9 | maintainer=https://github.com/dojyorin 10 | url=https://github.com/dojyorin/arduino_base64.git -------------------------------------------------------------------------------- /src/arduino_base64.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.h" 4 | #include "string.h" 5 | 6 | /** 7 | * Convert between binary and base64 encoded string. 8 | * @see https://github.com/dojyorin/arduino_base64 9 | */ 10 | namespace base64 { 11 | /** 12 | * Convert binary to base64 encoded string. 13 | * If input is string, cast to `uint8_t*`. 14 | * @example 15 | * ```c++ 16 | * const uint8_t input[] = {0x17, 0x77, 0x3B, 0x11, 0x82, 0xA4, 0xC4, 0xC8}; 17 | * auto inputLength = sizeof(input); 18 | * char output[base64::encodeLength(inputLength)]; 19 | * base64::encode(input, inputLength, output); 20 | * ``` 21 | */ 22 | void encode(const uint8_t* input, size_t inputLength, char* output); 23 | 24 | /** 25 | * Calculate number of output characters. 26 | * @example 27 | * ```c++ 28 | * const uint8_t input[] = {0x17, 0x77, 0x3B, 0x11, 0x82, 0xA4, 0xC4, 0xC8}; 29 | * auto inputLength = sizeof(input); 30 | * char output[base64::encodeLength(inputLength)]; 31 | * base64::encode(input, inputLength, output); 32 | * ``` 33 | */ 34 | size_t encodeLength(size_t inputLength); 35 | 36 | /** 37 | * Convert base64 encoded string to binary. 38 | * If output is string, cast to `char*`. 39 | * @example 40 | * ```c++ 41 | * const char input[] = "F3c7EYKkxMgnvO0nB8FWVw=="; 42 | * uint8_t output[base64::decodeLength(input)]; 43 | * base64::decode(input, output); 44 | * ``` 45 | */ 46 | void decode(const char* input, uint8_t* output); 47 | 48 | /** 49 | * Calculate number of output bytes. 50 | * @example 51 | * ```c++ 52 | * const char input[] = "F3c7EYKkxMgnvO0nB8FWVw=="; 53 | * uint8_t output[base64::decodeLength(input)]; 54 | * base64::decode(input, output); 55 | * ``` 56 | */ 57 | size_t decodeLength(const char* input); 58 | } -------------------------------------------------------------------------------- /src/base64.cpp: -------------------------------------------------------------------------------- 1 | #include "./arduino_base64.hpp" 2 | 3 | namespace { 4 | constexpr char CODE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 5 | 6 | uint8_t indexOf(char search) { 7 | for(uint8_t i = 0; i < 64; i++) { 8 | if(::CODE[i] == search) { 9 | return i; 10 | } 11 | } 12 | 13 | return 0xFF; 14 | } 15 | 16 | void to6x4(uint8_t* input, uint8_t* output) { 17 | output[0] = (input[0] & 0xFC) >> 2; 18 | output[1] = ((input[0] & 0x03) << 4) + ((input[1] & 0xF0) >> 4); 19 | output[2] = ((input[1] & 0x0F) << 2) + ((input[2] & 0xC0) >> 6); 20 | output[3] = input[2] & 0x3F; 21 | } 22 | 23 | void to8x3(uint8_t* input, uint8_t* output) { 24 | output[0] = (input[0] << 2) + ((input[1] & 0x30) >> 4); 25 | output[1] = ((input[1] & 0x0F) << 4) + ((input[2] & 0x3C) >> 2); 26 | output[2] = ((input[2] & 0x03) << 6) + input[3]; 27 | } 28 | } 29 | 30 | void base64::encode(const uint8_t* input, size_t inputLength, char* output) { 31 | uint8_t position = 0; 32 | uint8_t bit8x3[3] = {}; 33 | uint8_t bit6x4[4] = {}; 34 | 35 | while(inputLength--) { 36 | bit8x3[position++] = *input++; 37 | 38 | if(position == 3) { 39 | ::to6x4(bit8x3, bit6x4); 40 | 41 | for(const auto &v: bit6x4) { 42 | *output++ = ::CODE[v]; 43 | } 44 | 45 | position = 0; 46 | } 47 | } 48 | 49 | if(position) { 50 | for(uint8_t i = position; i < 3; i++) { 51 | bit8x3[i] = 0x00; 52 | } 53 | 54 | ::to6x4(bit8x3, bit6x4); 55 | 56 | for(uint8_t i = 0; i < position + 1; i++) { 57 | *output++ = ::CODE[bit6x4[i]]; 58 | } 59 | 60 | while(position++ < 3) { 61 | *output++ = '='; 62 | } 63 | } 64 | 65 | *output = '\0'; 66 | } 67 | 68 | size_t base64::encodeLength(size_t inputLength) { 69 | return (inputLength + 2 - ((inputLength + 2) % 3)) / 3 * 4 + 1; 70 | } 71 | 72 | void base64::decode(const char* input, uint8_t* output) { 73 | auto inputLength = strlen(input); 74 | uint8_t position = 0; 75 | uint8_t bit8x3[3] = {}; 76 | uint8_t bit6x4[4] = {}; 77 | 78 | while(inputLength--) { 79 | if(*input == '=') { 80 | break; 81 | } 82 | 83 | bit6x4[position++] = ::indexOf(*input++); 84 | 85 | if(position == 4) { 86 | ::to8x3(bit6x4, bit8x3); 87 | 88 | for(const auto &v: bit8x3) { 89 | *output++ = v; 90 | } 91 | 92 | position = 0; 93 | } 94 | } 95 | 96 | if(position) { 97 | for(uint8_t i = position; i < 4; i++) { 98 | bit6x4[i] = 0x00; 99 | } 100 | 101 | ::to8x3(bit6x4, bit8x3); 102 | 103 | for(uint8_t i = 0; i < position - 1; i++) { 104 | *output++ = bit8x3[i]; 105 | } 106 | } 107 | } 108 | 109 | size_t base64::decodeLength(const char* input) { 110 | auto inputLength = strlen(input); 111 | uint8_t equal = 0; 112 | 113 | input += inputLength - 1; 114 | 115 | while(*input-- == '=') { 116 | equal++; 117 | } 118 | 119 | return 6 * inputLength / 8 - equal; 120 | } --------------------------------------------------------------------------------