MicroPython Data Types and Variables Cheat Sheet

micropython cheat sheets

MicroPython Data Types and Variables

Data Types Overview

MicroPython supports the principal Python 3 data types used in embedded applications. Variables refer to objects rather than having fixed declared types. Memory usage, integer range and floating-point precision depend on the MicroPython firmware build.

Type Example Mutable Typical use
int count = 100 No Counters and bit masks
float voltage = 3.3 No Sensor measurements
bool enabled = True No Status flags
str name = “Sensor” No Messages and labels
bytes packet = b”\x01\x02″ No Fixed binary packets
bytearray buffer = bytearray(16) Yes Reusable I/O buffers
list samples = [1, 2, 3] Yes Sample collections
tuple point = (10, 20) No Fixed groups
dict config = {“rate”: 100} Yes Configuration
set pins = {1, 2, 3} Yes Unique values
NoneType result = None No Unavailable result

Variables and Assignment

Assign a value to create or rebind a variable:

temperature = 24.5
sample_count = 0
sensor_enabled = True
device_name = "Environmental Monitor"

A name may subsequently refer to another type, although consistent types make firmware easier to maintain.

reading = 100
reading = 23.75
reading = "Unavailable"

Variable names are case-sensitive, cannot begin with a digit and cannot be Python keywords. Prefer sample_count to names that shadow built-ins such as list or int.

Multiple assignment and unpacking:

x, y, z = 10, 20, 30
minimum = maximum = 0
minimum, maximum = maximum, minimum

Integers

Integers represent whole numbers and are useful for register values, counts and binary protocol fields.

counter = 125
offset = -20
binary_mask = 0b10101010
hex_value = 0xA5
octal_value = 0o17
Notation Example Decimal value
Decimal 255 255
Binary 0b11111111 255
Hexadecimal 0xFF 255
Octal 0o377 255
Operator Example Result
Addition 17 + 5 22
Subtraction 17 – 5 12
Multiplication 17 * 5 85
True division 17 / 5 3.4
Floor division 17 // 5 3
Remainder 17 % 5 2
Power 17 ** 2 289

Floor division rounds towards negative infinity:

print(-7 // 3)  # -3
print(-7 % 3)   # 2

MicroPython integer range and long-integer support depend on the firmware build. Do not assume either a fixed 32-bit limit or unlimited precision.

Extract bytes from an integer:

value = 0x1234
low_byte = value & 0xFF
high_byte = (value >> 8) & 0xFF

print(hex(low_byte))   # 0x34
print(hex(high_byte))  # 0x12

Floating-Point Numbers

voltage = 3.3
current = 0.125
temperature = -12.75
frequency = 1.5e6
Notation Value
1.5e6 1,500,000
2.2e-3 0.0022
4.7e-6 0.0000047

MicroPython builds may use IEEE 754 single-precision or double-precision floats. Single precision offers approximately 6–7 significant decimal digits; double precision approximately 15–16. Binary floating-point cannot represent every decimal fraction exactly.

Use tolerances rather than direct equality for measured values:

measured = 3.299
target = 3.3
tolerance = 0.01

if abs(measured - target) <= tolerance:
    print("Within tolerance")

An approximate conversion from a scaled 16-bit ADC reading to voltage, assuming a linear 0–3.3 V range:

raw = 32768
reference_voltage = 3.3
voltage = raw * reference_voltage / 65535

print(voltage)

The actual ADC voltage relationship depends on the board, calibration, attenuation and reference.

Boolean Values and Truth Testing

bool has the values True and False.

sensor_ready = True
alarm_active = False

if sensor_ready and not alarm_active:
    print("Measurement permitted")
Operator Meaning
and Both operands truthy
or At least one operand truthy
not Logical negation
==, != Equality and inequality
<, >, <=, >= Ordering comparisons

False, None, numeric zero and empty strings or collections are false in a condition. A sensor reading of zero can nevertheless be valid.

reading = 0

if reading is not None:
    print("Valid reading:", reading)

Use is None for an unavailable value rather than testing whether the reading is truthy.

Strings

Strings hold Unicode text and are immutable.

device_name = "Pico Sensor"
status = 'Ready'
message = "Temperature: 25 C"
Operation Example Result
Length len(“ESP32”) 5
First character “ESP32″[0] “E”
Slice “ESP32″[0:3] “ESP”
Concatenate “GPIO” + “15” “GPIO15”
Uppercase “ready”.upper() “READY”
Split “10,20”.split(“,”) [“10”, “20”]
Strip whitespace ” OK \n”.strip() “OK”

String formatting, with each statement on a separate line:

temperature = 24.75
message = "Temperature: {:.1f} C".format(temperature)
print(message)

Expected output:

Temperature: 24.8 C

Modern MicroPython builds also support f-strings:

count = 25
print(f"Samples: {count}")

Repeated string concatenation allocates new objects and may increase garbage-collection activity.

Bytes and Bytearrays

Use bytes for immutable binary data and bytearray for mutable buffers.

command = b"\x01\x02\xA5"
print(len(command))  # 3
print(command[0])   # 1
buffer = bytearray(4)
buffer[0] = 0x10
buffer[1] = 0x20
buffer[2] = 0x30
buffer[3] = 0x40

Byte elements are integers from 0 to 255. Assigning outside that range raises an error.

Convert a 16-bit integer to big-endian bytes and back on firmware supporting these methods:

value = 0x1234
data = value.to_bytes(2, "big")
restored = int.from_bytes(data, "big")

print(data)      # b'\x12\x34'
print(restored)  # 4660

For repeated peripheral reads, use a preallocated buffer where the API supports it:

from machine import I2C, Pin

i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)
buffer = bytearray(8)

i2c.readfrom_into(0x48, buffer)

The I2C example uses Raspberry Pi Pico GP1 and GP0. Address 0x48, read length and pull-up resistors must suit the connected device.

Lists

Lists are ordered and mutable.

samples = [120, 125, 118, 130]

print(samples[0])
print(samples[-1])

samples[1] = 126
samples.append(135)
Operation Expression
Length len(samples)
Append samples.append(135)
Remove last item samples.pop()
Slice samples[1:3]
Sum sum(samples)
Minimum min(samples)
Maximum max(samples)

Calculate a sample average, guarding against an empty list:

samples = [1020, 1018, 1025, 1022]

if samples:
    average = sum(samples) / len(samples)
    print(average)

An unbounded list can exhaust a small heap. A circular buffer maintains fixed storage:

samples = [0] * 8
index = 0

def add_sample(value):
    global index
    samples[index] = value
    index = (index + 1) % len(samples)

for value in range(12):
    add_sample(value)

print(samples)

The resulting storage order is [8, 9, 10, 11, 4, 5, 6, 7]. Track the insertion index when reading the samples chronologically.

Tuples

Tuples are ordered and immutable. They are useful for fixed groups of values and multiple return values.

coordinates = (10, 20)
x, y = coordinates

print(x)
print(y)
def measurement():
    temperature = 24.5
    humidity = 55.0
    return temperature, humidity

temp, humidity = measurement()
print(temp, humidity)

A tuple itself is immutable, but a mutable object contained within a tuple can still be modified.

Dictionaries

Dictionaries map keys to values.

configuration = {
    "sample_interval": 1000,
    "threshold": 30.0,
    "enabled": True
}

interval = configuration["sample_interval"]
configuration["threshold"] = 32.0
configuration["mode"] = "automatic"
enabled = configuration.get("enabled", False)
Operation Expression
Lookup configuration[“threshold”]
Lookup with default configuration.get(“mode”, “manual”)
Add or replace configuration[“enabled”] = False
Remove configuration.pop(“mode”)
Iterate keys configuration.keys()
Iterate values configuration.values()
Iterate pairs configuration.items()

Accessing a missing key with square brackets raises KeyError. Use .get() when a default is appropriate. Dictionaries offer readable configuration storage but consume more memory than simple scalar variables.

Sets

Sets contain unique, hashable values. Their iteration order is not a positional sequence.

active_channels = {1, 2, 3}
active_channels.add(4)
active_channels.discard(2)
a = {1, 2, 3}
b = {3, 4, 5}

common = a & b
combined = a | b
difference = a - b
symmetric = a ^ b
Operator Meaning
& Intersection
\| Union
– Difference
^ Symmetric difference

Sets are useful for membership checks and removing duplicates, but can have significant memory overhead for small collections.

None and Missing Measurements

Use None to represent the absence of a value rather than using zero when zero could be valid.

def get_valid_reading(raw):
    if raw < 0:
        return None
    return raw

reading = get_valid_reading(-1)

if reading is None:
    print("Invalid reading")
else:
    print(reading)

Prefer is None and is not None for identity checks.

Type Inspection and Conversion

value = 100

print(type(value))
print(isinstance(value, int))
Conversion Example Result
Integer int(“123”) 123
Float float(“3.5”) 3.5
String str(123) “123”
Boolean bool(0) False
List list((1, 2)) [1, 2]
Tuple tuple([1, 2]) (1, 2)
Bytes bytes([1, 2]) b”\x01\x02″
Bytearray bytearray(4) Four zero bytes

Validate text received from a serial port or network before using it numerically:

received = "125"

try:
    value = int(received)
except ValueError:
    value = None

Variable Scope and Object References

Assignments inside functions normally create local bindings.

def calculate_total():
    count = 10
    return count * 2

print(calculate_total())

Use global when a function needs to rebind a module-level name:

sample_count = 0

def increment_count():
    global sample_count
    sample_count += 1

increment_count()
print(sample_count)

Mutating an existing global list does not rebind the name:

samples = []

def add_sample(value):
    samples.append(value)

add_sample(25)
print(samples)

Assignment shares an object reference rather than copying the object:

a = [1, 2, 3]
b = a
b[0] = 99

print(a)  # [99, 2, 3]

Create a shallow copy when separate list storage is required:

a = [1, 2, 3]
b = a.copy()
b[0] = 99

print(a)  # [1, 2, 3]
print(b)  # [99, 2, 3]

== compares values; is compares object identity. Do not use is as a general replacement for equality.

Constants

Uppercase names conventionally identify values that should not change:

MAX_SAMPLES = 100
ADC_REFERENCE = 3.3

MicroPython offers an integer constant optimisation:

from micropython import const

LED_PIN = const(15)
BUFFER_SIZE = const(128)
STATUS_MASK = const(0x80)

const() does not make arbitrary Python objects immutable. Optimisation behaviour depends on the firmware and use of the constant.

Bitwise Operations

Operator Purpose Example
& AND value & 0x0F
\| OR value \| 0x80
^ XOR value ^ 0x01
~ NOT ~value
<< Left shift value << 2
>> Right shift value >> 2

Set, clear, toggle and test bit 3:

register = 0

register |= (1 << 3)   # Set
register &= ~(1 << 3)  # Clear
register ^= (1 << 3)   # Toggle

if register & (1 << 3):
    print("Bit 3 is set")

Extract a three-bit field beginning at bit 2:

register = 0b10110110
field = (register >> 2) & 0b111

print(field)  # 5

Apply masks when fixed-width unsigned behaviour is required. Python integers are not inherently limited to 8 or 16 bits.

Memory-Efficient Buffers

A memoryview accesses existing buffer storage without copying the underlying bytes:

buffer = bytearray(16)
header = memoryview(buffer)[:4]

header[0] = 0xAA
header[1] = 0x55

print(buffer[0])  # 170
print(buffer[1])  # 85

Creating the view itself uses some memory, but the underlying bytes are shared.

Inspect the MicroPython managed heap:

import gc

gc.collect()
print("Free heap:", gc.mem_free())
print("Allocated:", gc.mem_alloc())

These values do not necessarily describe all physical RAM on the board.

Common Errors and Diagnostics

Symptom Likely cause Corrective action
TypeError Incompatible operand types Convert explicitly
ValueError Invalid numeric string or value Validate input
IndexError Collection index out of range Check bounds
KeyError Missing dictionary key Use .get() or test membership
MemoryError Heap exhausted Reuse buffers; bound collections
ZeroDivisionError Divisor is zero Validate before division
OverflowError Numeric operation exceeds supported range Check build and numeric representation
Unexpected float comparison Rounding or precision Compare with tolerance
Unexpected list modification Shared mutable reference Copy where appropriate
UnboundLocalError Local name referenced before assignment Review scope and global
Cannot modify bytes Immutable object Use bytearray
Invalid byte assignment Value outside 0–255 Validate or mask
Zero reading ignored Truth-value check Check explicitly against None

Quick Reference

Task Expression
Inspect type type(value)
Test type isinstance(value, int)
Convert to integer int(value)
Convert to float float(value)
Convert to string str(value)
Get length len(collection)
Fixed-size list [0] * 16
Mutable byte buffer bytearray(16)
Last list element items[-1]
Shallow list copy items.copy()
Add list element items.append(value)
Safe dictionary lookup config.get(“key”, default)
Test missing result value is None
Low byte value & 0xFF
High byte (value >> 8) & 0xFF
Set bit 3 value \|= (1 << 3)
Clear bit 3 value &= ~(1 << 3)
Toggle bit 3 value ^= (1 << 3)
Test bit 3 bool(value & (1 << 3))
Free heap gc.mem_free()
Garbage collection gc.collect()

Practical Data Handling Checklist

  1. Use integers for counters, register fields and bit masks.
  2. Confirm float precision before relying on fine calibration calculations.
  3. Use bytearray for reusable binary I/O buffers.
  4. Bound the size of lists in continuously running applications.
  5. Distinguish zero measurements from unavailable measurements.
  6. Validate serial and network input before converting it.
  7. Use explicit masks for fixed-width protocol fields.
  8. Account for shared references when modifying collections.
  9. Avoid allocations inside hard interrupt callbacks.
  10. Check managed heap usage under realistic operating conditions.
  11. Verify firmware support for less common methods.
  12. Test memory behaviour over extended operation.

Related posts

MicroPython vs Python 3 Differences Cheat Sheet

MicroPython Programming Quick Reference Cheat Sheet