---
title: "Byte encodings and checksums"
description: "TapHome script byte encodings: Base64 and base64url of binary data, hexadecimal text, CRC16 in Modbus, XMODEM, CCITT-FALSE or KERMIT variant, and CRC32."
url: "https://taphome.com/en/docs/script/byte-encodings/"
language: en
lastmod: 2026-11-06
generator: hugo
ai:
  topics: ["byte-encodings","packet-parser","modbus","scripting-language"]
  questions: ["How do I decode Base64 binary data in a TapHome script?","How do I turn bytes into hexadecimal text in a TapHome script?","How do I calculate a Modbus RTU CRC16 in a script?","Which Core version do the byte encoding functions need?"]
---

# Byte encodings and checksums

TapHome script functions TOBASE64 and FROMBASE64 (also base64url), TOHEX and FROMHEX, CRC16 and CRC32 — bytes as text and back, and checksums of frames.

Last updated: 2026-11-06

`TOBASE64`, `FROMBASE64`, `TOHEX`, `FROMHEX`, `CRC16` and `CRC32` turn bytes into text and back and compute checksums, usually together with the [encryption and hash functions](https://taphome.com/en/docs/script/encryption-and-hashes/). They only build values, so they work in every script. All of them need TapHome Next 2026.1.40991+.

`TOBASE64`, `TOHEX`, `CRC16` and `CRC32` take byte data like the encryption functions: a `collection<uint8>`, a blob or a string, used as ISO-8859-1 bytes (for UTF-8 pass `TOBYTES(text, "utf-8")`). `TOBASE64` and `TOHEX` return a string, `CRC16` and `CRC32` a `uint` number; `FROMBASE64` and `FROMHEX` take text and return a `collection<uint8>`.

## TOBASE64, FROMBASE64

`TOBASE64` turns byte data into Base64 text, `FROMBASE64` turns Base64 text back into bytes.

```text
TOBASE64(data [, variant])
FROMBASE64(text [, variant])
```

`variant` is `"standard"` (the default) or `"url"`, not case sensitive. `"url"` is base64url (RFC 4648 §5), which uses `-` and `_` instead of `+` and `/`. `TOBASE64` with `"url"` leaves out the `=` padding, as JWT and most web APIs expect (Python `base64.urlsafe_b64encode(data).rstrip(b"=")`); `FROMBASE64` with `"url"` accepts text with or without the padding.

**Returns** — `TOBASE64` a string, `FROMBASE64` a `collection<uint8>`.

**Remarks** — [ENCODE and DECODE](https://taphome.com/en/docs/script/data-type-conversions/#encode) with `"base64"` go through UTF-8 text, so bytes above `0x7F` do not survive them. Encrypted and other binary data goes through `TOBASE64` and `FROMBASE64`.

**Example**

```taphome
# standard Base64 of two bytes
TOBASE64(FROMHEX("fbff"));
# Result: "+/8="
# base64url: no padding, - and _ instead of + and /
TOBASE64(FROMHEX("fbff"), "url");
# Result: "-_8"
# decode base64url back to hex text
TOHEX(FROMBASE64("-_8", "url"));
# Result: "fbff"
```

**Errors** — *TOBASE64 Unknown variant. Valid values are 'standard', 'url'* (`FROMBASE64` names itself); *FROMBASE64 - '+' and '/' are not base64url characters, leave the "url" argument out for standard Base64*; *FROMBASE64 - argument must be a string*; *The provided text does not have the base64 format.*

**Available** — TapHome Next 2026.1.40991+.

## TOHEX, FROMHEX

`TOHEX` returns byte data as lower-case hexadecimal text, `FROMHEX` turns hexadecimal text back into bytes.

```text
TOHEX(data)
FROMHEX(text)
```

**Remarks**

- `FROMHEX` accepts upper- and lower-case digits and whitespace between them, as Python's `bytes.fromhex`. Any other character, including a `0x` prefix or `:` separators, is an error, and so is an odd number of digits. An empty string gives an empty collection.
- Unlike [BYTECOLLECTION](https://taphome.com/en/docs/script/text-string-and-byte-array/#bytecollection) from hexadecimal text, which silently ignores other characters and is limited to 2048 bytes, `FROMHEX` has no length limit, so it takes back anything `TOHEX` produced.

**Example**

```taphome
# three bytes as Base64
TOBASE64(FROMHEX("ff0080"));
# Result: "/wCA"
# Base64 back to bytes, then hex text
TOHEX(FROMBASE64("/wCA"));
# Result: "ff0080"
# upper case and spaces are accepted
TOHEX(FROMHEX("FF 00 80"));
# Result: "ff0080"
```

**Errors** — *FROMHEX - invalid hex digit 'x'* (remove a prefix or separators with `REPLACE`); *FROMHEX - the number of hex digits must be even, it is N*; *FROMHEX - argument must be a string*.

**Available** — TapHome Next 2026.1.40991+.

## CRC16, CRC32

`CRC16` returns the 16-bit CRC checksum of byte data, `CRC32` the CRC-32 of zlib, Ethernet and ZIP (Python `zlib.crc32`).

```text
CRC16(data [, variant])
CRC32(data)
```

`variant` selects the parameters of `CRC16`, not case sensitive. The check value is the CRC of the text `"123456789"`:

| `variant` | Check value | Also known as |
|---|---|---|
| `"MODBUS"` (default) | `0x4B37` | Modbus RTU frames |
| `"XMODEM"` | `0x31C3` | Python `binascii.crc_hqx(data, 0)` |
| `"CCITT-FALSE"` | `0x29B1` | `"IBM-3740"` is accepted as the same variant |
| `"KERMIT"` | `0x2189` | |

The check value of `CRC32` is `0xCBF43926` (3421780262).

**Returns** — a `uint` number, not bytes. To put the checksum into a frame, pack it with [PACKVALUE](https://taphome.com/en/docs/script/binary-data/#packvalue) in the byte order the protocol expects; Modbus RTU sends the low byte first.

**Example**

```taphome
TOSTRING(CRC16(FROMHEX("0103000A0001")), "X4");
# Result: "08A4"
TOSTRING(CRC32("123456789"), "X8");
# Result: "CBF43926"
```

**Example — append a Modbus RTU checksum**

```taphome
# frame bytes from hex text
VAR frame := FROMHEX("01 03 00 0A 00 01");
# empty buffer of 2 bytes for the checksum
VAR crc := BYTECOLLECTION(2);
# pack the CRC16 low byte first
PACKVALUE(crc, 0, LittleEndianUint16, CRC16(frame));
# append the checksum to the frame
APPEND(frame, crc);
# Result: frame is 01 03 00 0A 00 01 A4 08
```

**Errors** — *CRC16 Unknown variant. Valid values are 'MODBUS', 'XMODEM', 'CCITT-FALSE', 'KERMIT'*.

**Available** — TapHome Next 2026.1.40991+.

**See also** — [Encryption and hashes](https://taphome.com/en/docs/script/encryption-and-hashes/), [Binary data](https://taphome.com/en/docs/script/binary-data/), [Data type conversions](https://taphome.com/en/docs/script/data-type-conversions/).
