I2C
The I2C (Inter-Integrated Circuit) interface provides two-wire synchronous serial communication. Supports standard (100 kHz), fast (400 kHz), and fast-plus (1 MHz) modes with configurable voltage levels from 1.6V to 5.0V.
Please note that the AT1000 device only supports master mode for I2C communication. This means that it can initiate communication with slave devices, but it cannot act as a slave itself.
Some interfaces share the same GPIO pins, so only one can be used at a time. Check out the multiplexing tables in the introduction for more details.
Quick Start​
The example code below shows how to enable the I2C bus, set the baud rate, and transmit/receive data.
- NodeJS
- Python
import AT1000 from '@ikalogic/at1000';
let devices = await AT1000.findDevices(500); // Find devices with a 500ms timeout
let tester = devices[0]; // Target the first detected device
let i2c = tester.com.i2c(0); // Select the first I2C bus
await i2c.enable({ vcc: 3.3, baud_rate: 100000 }); // Enable I2C bus with 3.3V and 100kHz
// Define an I2C transaction
let i2c_transaction = {
address: 0x50, // I2C address of the slave device
start: true, // Start condition
stop: true, // Stop condition
nack_last_byte: false, // NACK the last byte
};
// Write request to the slave device, registers 0x10 and 0x20
await i2c.tx({ ...i2c_transaction, data: [0x10, 0x20] });
i2c_transaction.nack_last_byte = true;
// Read 4 bytes from the slave device
let data = await i2c.rx({ ...i2c_transaction, length: 4 });
// Disable I2C bus on AT1000 master
await i2c.disable();
from ikalogic_at1000 import AT1000
devices = await AT1000.find_devices(500) # Find devices with a 500ms timeout
tester = devices[0] # Target the first detected device
i2c = tester.com.i2c(0) # Select the first I2C bus
await i2c.enable({ 'vcc': 3.3, 'baud_rate': 100000 }) # Enable I2C bus with 3.3V and 100kHz
# Define an I2C transaction
i2c_transaction = {
"address": 0x50, # I2C address of the slave device
"start": True, # Start condition
"stop": True, # Stop condition
"nack_last_byte": False, # NACK the last byte
}
# Write request to the slave device, registers 0x10 and 0x20
await i2c.tx({ **i2c_transaction, "data": [0x10, 0x20] })
i2c_transaction["nack_last_byte"] = True
# Read 4 bytes from the slave device
data = await i2c.rx({ **i2c_transaction, "length": 4 })
# Disable I2C bus on AT1000 master
await i2c.disable()
API Reference​
Accessing I2C​
- NodeJS
- Python
const i2c = tester.com.i2c(id);
i2c = tester.com.i2c(id)
Parameters:
id(number): The I2C interface identifier (0-based index)
Returns: I2c instance
Methods​
configure(config)​
Configures the I2C interface parameters without changing the enabled state.
- NodeJS
- Python
await i2c.configure({
baud_rate: 400000,
vcc: 3.3
});
await i2c.configure({
'baud_rate': 400000,
'vcc': 3.3
})
Parameters:
config(I2cConfigPatch): Configuration object with optional properties:enabled(boolean, optional): Enable or disable the interfacebaud_rate(number, optional): I2C clock frequency - 100000, 400000, or 1000000vcc(number, optional): Logic level voltage in volts (1.6 to 5.0)
Valid baud rates:
- 100000 (100 kHz): Standard mode
- 400000 (400 kHz): Fast mode
- 1000000 (1 MHz): Fast-plus mode
Returns: Promise<I2cConfig> - The updated configuration
Exceptions:
- Throws validation error if
baud_rateis not one of the valid values - Throws validation error if
vccis outside the range [1.6, 5.0]
enable([config])​
Enables the I2C interface and optionally applies configuration.
- NodeJS
- Python
await i2c.enable({
baud_rate: 400000,
vcc: 3.3
});
await i2c.enable({
'baud_rate': 400000,
'vcc': 3.3
})
Parameters:
config(I2cEnableConfig, optional): Optional configuration to apply:baud_rate(number, optional): I2C clock frequency (100000, 400000, or 1000000)vcc(number, optional): Logic level voltage (1.6 to 5.0)
Returns: Promise<I2cConfig> - The updated configuration with enabled: true
Exceptions:
- Throws validation error if parameters are outside valid ranges
disable()​
Disables the I2C interface.
- NodeJS
- Python
await i2c.disable();
await i2c.disable()
Returns: Promise<I2cConfig> - The updated configuration with enabled: false
tx(transaction)​
Writes data to an I2C device.
- NodeJS
- Python
// Write to device at address 0x50
await i2c.tx({
address: 0x50,
data: [0x00, 0x10, 0xAA, 0xBB]
});
// Write with custom start/stop conditions
await i2c.tx({
address: 0x3C,
start: true,
stop: false, // No stop - for repeated start
data: [0x40, 0xFF]
});
# Write to device at address 0x50
await i2c.tx({
'address': 0x50,
'data': [0x00, 0x10, 0xAA, 0xBB]
})
# Write with custom start/stop conditions
await i2c.tx({
'address': 0x3C,
'start': True,
'stop': False, # No stop - for repeated start
'data': [0x40, 0xFF]
})
Parameters:
transaction(I2cTransactionWrite): Write transaction configuration:address(number, required): 7-bit I2C device addressdata(string | number[], required): Data to write (max 1024 bytes)start(boolean, optional, default:true): Generate START conditionstop(boolean, optional, default:true): Generate STOP conditionnack_last_byte(boolean, optional, default:false): Send NACK on last byte
Returns: Promise<number> - Number of bytes written
Exceptions:
- Throws validation error if data exceeds 1024 bytes
- Throws error if device doesn't acknowledge (NACK)
rx(transaction)​
Reads data from an I2C device.
- NodeJS
- Python
// Read 16 bytes from device at address 0x50
const data = await i2c.rx({
address: 0x50,
length: 16
});
// Read with custom conditions
const data2 = await i2c.rx({
address: 0x3C,
length: 8,
start: true,
stop: true,
nack_last_byte: true
});
# Read 16 bytes from device at address 0x50
data = await i2c.rx({
'address': 0x50,
'length': 16
})
# Read with custom conditions
data2 = await i2c.rx({
'address': 0x3C,
'length': 8,
'start': True,
'stop': True,
'nack_last_byte': True
})
Parameters:
transaction(I2cTransactionRead): Read transaction configuration:address(number, required): 7-bit I2C device addresslength(number, required): Number of bytes to read (1 to 1024)start(boolean, optional, default:true): Generate START conditionstop(boolean, optional, default:true): Generate STOP conditionnack_last_byte(boolean, optional, default:true): Send NACK on last byte
Returns: Promise<SerialData> - Received data as byte array (number[])
Exceptions:
- Throws validation error if
lengthis outside the range [1, 1024] - Throws error if device doesn't acknowledge (NACK)
Types​
I2cConfig​
Complete configuration object returned by configuration methods.
{
enabled: boolean,
baud_rate: number, // 100000, 400000, or 1000000
vcc: number // 1.6 to 5.0
}
I2cConfigPatch​
Partial configuration for the configure() method. All properties are optional.
{
enabled?: boolean,
baud_rate?: number, // 100000, 400000, or 1000000
vcc?: number // 1.6 to 5.0
}
I2cEnableConfig​
Optional configuration for the enable() method.
{
baud_rate?: number, // 100000, 400000, or 1000000
vcc?: number // 1.6 to 5.0
}
I2cTransactionWrite​
Write transaction parameters.
{
address: number, // 7-bit I2C address
data: string | number[], // Data to write (max 1024 bytes)
start?: boolean, // Generate START (default: true)
stop?: boolean, // Generate STOP (default: true)
nack_last_byte?: boolean // NACK last byte (default: false)
}
I2cTransactionRead​
Read transaction parameters.
{
address: number, // 7-bit I2C address
length: number, // Number of bytes to read (1-1024)
start?: boolean, // Generate START (default: true)
stop?: boolean, // Generate STOP (default: true)
nack_last_byte?: boolean // NACK last byte (default: true)
}
SerialData​
Output data type, always returned as a byte array.
number[] // Array of bytes (0-255)
Usage Example​
- NodeJS
- Python
import AT1000 from '@ikalogic/at1000';
// Find and connect to device
const devices = await AT1000.findDevices(500);
const tester = devices[0];
// Access I2C interface 0
const i2c = tester.com.i2c(0);
// Enable with 400 kHz clock and 3.3V logic
await i2c.enable({
baud_rate: 400000,
vcc: 3.3
});
// Write to EEPROM at address 0x50
await i2c.tx({
address: 0x50,
data: [0x00, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F] // "Hello" at address 0x0000
});
// Wait for write cycle to complete
await new Promise(resolve => setTimeout(resolve, 10));
// Read back from EEPROM
// First, set read address
await i2c.tx({
address: 0x50,
data: [0x00, 0x00],
stop: false // No stop for repeated start
});
// Then read data
const data = await i2c.rx({
address: 0x50,
length: 5
});
console.log("Read:", Buffer.from(data).toString()); // "Hello"
// Disable interface
await i2c.disable();
from ikalogic_at1000 import AT1000
import asyncio
# Find and connect to device
devices = await AT1000.find_devices(500)
tester = devices[0]
# Access I2C interface 0
i2c = tester.com.i2c(0)
# Enable with 400 kHz clock and 3.3V logic
await i2c.enable({
'baud_rate': 400000,
'vcc': 3.3
})
# Write to EEPROM at address 0x50
await i2c.tx({
'address': 0x50,
'data': [0x00, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F] # "Hello" at address 0x0000
})
# Wait for write cycle to complete
await asyncio.sleep(0.01)
# Read back from EEPROM
# First, set read address
await i2c.tx({
'address': 0x50,
'data': [0x00, 0x00],
'stop': False # No stop for repeated start
})
# Then read data
data = await i2c.rx({
'address': 0x50,
'length': 5
})
print(f"Read: {bytes(data).decode()}") # "Hello"
# Disable interface
await i2c.disable()
Common Patterns​
Register Read (Single Byte)​
- NodeJS
- Python
// Read register 0x75 from device at 0x68 (e.g., IMU WHO_AM_I register)
await i2c.tx({
address: 0x68,
data: [0x75],
stop: false
});
const whoAmI = await i2c.rx({
address: 0x68,
length: 1
});
console.log("WHO_AM_I:", whoAmI[0].toString(16));
# Read register 0x75 from device at 0x68 (e.g., IMU WHO_AM_I register)
await i2c.tx({
'address': 0x68,
'data': [0x75],
'stop': False
})
who_am_i = await i2c.rx({
'address': 0x68,
'length': 1
})
print(f"WHO_AM_I: {who_am_i[0]:02x}")
Register Write​
- NodeJS
- Python
// Write 0x80 to register 0x6B (e.g., PWR_MGMT_1 register)
await i2c.tx({
address: 0x68,
data: [0x6B, 0x80]
});
# Write 0x80 to register 0x6B (e.g., PWR_MGMT_1 register)
await i2c.tx({
'address': 0x68,
'data': [0x6B, 0x80]
})
Multi-Byte Register Read​
- NodeJS
- Python
// Read 6 bytes starting from register 0x3B (e.g., accelerometer data)
await i2c.tx({
address: 0x68,
data: [0x3B],
stop: false
});
const accelData = await i2c.rx({
address: 0x68,
length: 6
});
// Parse as 16-bit signed integers
const accelX = (accelData[0] << 8) | accelData[1];
const accelY = (accelData[2] << 8) | accelData[3];
const accelZ = (accelData[4] << 8) | accelData[5];
# Read 6 bytes starting from register 0x3B (e.g., accelerometer data)
await i2c.tx({
'address': 0x68,
'data': [0x3B],
'stop': False
})
accel_data = await i2c.rx({
'address': 0x68,
'length': 6
})
# Parse as 16-bit signed integers
accel_x = (accel_data[0] << 8) | accel_data[1]
accel_y = (accel_data[2] << 8) | accel_data[3]
accel_z = (accel_data[4] << 8) | accel_data[5]
EEPROM Operations​
- NodeJS
- Python
// Write to EEPROM (24C256 example)
const eepromAddr = 0x50;
const memAddr = [0x00, 0x10]; // Address 0x0010
const writeData = [0xAA, 0xBB, 0xCC, 0xDD];
await i2c.tx({
address: eepromAddr,
data: [...memAddr, ...writeData]
});
// Wait for write cycle (5ms typical)
await new Promise(resolve => setTimeout(resolve, 10));
// Read back
await i2c.tx({
address: eepromAddr,
data: memAddr,
stop: false
});
const readData = await i2c.rx({
address: eepromAddr,
length: 4
});
import asyncio
# Write to EEPROM (24C256 example)
eeprom_addr = 0x50
mem_addr = [0x00, 0x10] # Address 0x0010
write_data = [0xAA, 0xBB, 0xCC, 0xDD]
await i2c.tx({
'address': eeprom_addr,
'data': mem_addr + write_data
})
# Wait for write cycle (5ms typical)
await asyncio.sleep(0.01)
# Read back
await i2c.tx({
'address': eeprom_addr,
'data': mem_addr,
'stop': False
})
read_data = await i2c.rx({
'address': eeprom_addr,
'length': 4
})
Scanning I2C Bus​
- NodeJS
- Python
console.log("Scanning I2C bus...");
const foundDevices = [];
for (let addr = 0x08; addr < 0x78; addr++) {
try {
await i2c.tx({
address: addr,
data: []
});
foundDevices.push(addr);
console.log(`Found device at 0x${addr.toString(16).padStart(2, '0')}`);
} catch (e) {
// Device not found at this address
}
}
console.log(`Found ${foundDevices.length} device(s)`);
print("Scanning I2C bus...")
found_devices = []
for addr in range(0x08, 0x78):
try:
await i2c.tx({
'address': addr,
'data': []
})
found_devices.append(addr)
print(f"Found device at 0x{addr:02x}")
except:
# Device not found at this address
pass
print(f"Found {len(found_devices)} device(s)")
Repeated Start Transaction​
- NodeJS
- Python
// Combined write-read transaction with repeated start
// (More efficient than separate transactions)
// Write register address
await i2c.tx({
address: 0x3C,
data: [0x00],
stop: false // Keep bus active for repeated start
});
// Immediately read without releasing bus
const data = await i2c.rx({
address: 0x3C,
length: 16,
start: true, // Repeated START
stop: true
});
# Combined write-read transaction with repeated start
# (More efficient than separate transactions)
# Write register address
await i2c.tx({
'address': 0x3C,
'data': [0x00],
'stop': False # Keep bus active for repeated start
})
# Immediately read without releasing bus
data = await i2c.rx({
'address': 0x3C,
'length': 16,
'start': True, # Repeated START
'stop': True
})
Notes​
- I2C addresses are 7-bit values (0x08 to 0x77)
- The interface handles 10-bit addressing internally if needed
- Pull-up resistors are typically required on SDA and SCL lines (often integrated)
- Clock stretching is supported automatically
- Use
nack_last_byte: truewhen reading to signal end of transaction - Maximum transfer length is 1024 bytes per transaction