Skip to content

Commit 6aa2e6c

Browse files
committed
Add I2C error reporting
1 parent 470e4c5 commit 6aa2e6c

File tree

1 file changed

+117
-28
lines changed

1 file changed

+117
-28
lines changed

deploy/src/i2c.jl

Lines changed: 117 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,90 @@ export read_byte, write_byte, read_byte_data, write_byte_data
1313
export read_block_data, write_block_data
1414
export read_bytes, read_bytes!, write_bytes
1515

16+
#=============================================================================
17+
I2C Device Handle
18+
=============================================================================#
19+
20+
const I2C_BUFFER_SIZE = 64 # Max buffer for read_bytes/write_bytes
21+
22+
mutable struct I2CDevice
23+
fd::RawFD
24+
bus::Int
25+
address::UInt8
26+
path::String
27+
# Pre-allocated buffers (shared between SMBus and RDWR operations)
28+
ioctl_buf::Vector{UInt8} # For SMBusIoctlData or I2CRdwrIoctlData
29+
msgs_buf::Vector{UInt8} # For I2CMsg array (2 msgs max)
30+
data_buf::Vector{UInt8} # SMBus data (34 bytes) or RDWR reg+data
31+
end
32+
33+
#=============================================================================
34+
Error Code Handling
35+
=============================================================================#
36+
37+
# Get last errno value
38+
get_errno() = ccall(:__errno_location, Ptr{Cint}, ())[]
39+
40+
# Common errno values for I2C operations (Linux)
41+
const ERRNO_NAMES = Dict{Cint, Tuple{String, String}}(
42+
1 => ("EPERM", "Operation not permitted - check process privileges"),
43+
2 => ("ENOENT", "No such file or directory - I2C bus device does not exist"),
44+
5 => ("EIO", "I/O error - bus communication failure, check wiring and pull-ups"),
45+
6 => ("ENXIO", "No such device or address - device did not ACK, check address and connections"),
46+
9 => ("EBADF", "Bad file descriptor - device handle is invalid or closed"),
47+
11 => ("EAGAIN", "Resource temporarily unavailable - retry the operation"),
48+
13 => ("EACCES", "Permission denied - add user to i2c group or run as root"),
49+
14 => ("EFAULT", "Bad address - internal buffer error"),
50+
16 => ("EBUSY", "Device or resource busy - another process is using the I2C bus"),
51+
19 => ("ENODEV", "No such device - I2C adapter not found or not loaded"),
52+
22 => ("EINVAL", "Invalid argument - check address range (0x03-0x77) and parameters"),
53+
62 => ("ETIME", "Timer expired - device took too long to respond"),
54+
84 => ("EILSEQ", "Illegal byte sequence - protocol error on bus"),
55+
110 => ("ETIMEDOUT", "Connection timed out - device not responding, check power and wiring"),
56+
121 => ("EREMOTEIO", "Remote I/O error - NACK received, device rejected transfer"),
57+
)
58+
59+
"""
60+
format_i2c_error(operation::String, dev::Union{I2CDevice, Nothing}, errno::Cint;
61+
reg::Union{UInt8, Nothing}=nothing, value::Union{UInt8, Nothing}=nothing) -> String
62+
63+
Format a detailed error message for I2C operations including errno interpretation.
64+
"""
65+
function format_i2c_error(operation::String, dev::Union{I2CDevice, Nothing}, errno::Cint;
66+
reg::Union{UInt8, Nothing}=nothing, value::Union{UInt8, Nothing}=nothing)
67+
# Build context string
68+
ctx_parts = String[]
69+
if dev !== nothing
70+
push!(ctx_parts, "bus=$(dev.bus)")
71+
push!(ctx_parts, "addr=0x$(string(dev.address, base=16, pad=2))")
72+
end
73+
if reg !== nothing
74+
push!(ctx_parts, "reg=0x$(string(reg, base=16, pad=2))")
75+
end
76+
if value !== nothing
77+
push!(ctx_parts, "value=0x$(string(value, base=16, pad=2))")
78+
end
79+
ctx = isempty(ctx_parts) ? "" : " [$(join(ctx_parts, ", "))]"
80+
81+
# Look up errno details
82+
if haskey(ERRNO_NAMES, errno)
83+
name, desc = ERRNO_NAMES[errno]
84+
return "I2C $operation failed$ctx: $name (errno=$errno) - $desc"
85+
else
86+
return "I2C $operation failed$ctx: errno=$errno (unknown error code)"
87+
end
88+
end
89+
90+
"""
91+
i2c_error(operation::String, dev::Union{I2CDevice, Nothing}; kwargs...)
92+
93+
Throw an error with detailed I2C diagnostics.
94+
"""
95+
function i2c_error(operation::String, dev::Union{I2CDevice, Nothing}; kwargs...)
96+
errno = get_errno()
97+
error(format_i2c_error(operation, dev, errno; kwargs...))
98+
end
99+
16100
#=============================================================================
17101
ioctl Constants
18102
=============================================================================#
@@ -75,23 +159,6 @@ struct I2CRdwrIoctlData
75159
_pad::UInt32
76160
end
77161

78-
#=============================================================================
79-
I2C Device Handle
80-
=============================================================================#
81-
82-
const I2C_BUFFER_SIZE = 64 # Max buffer for read_bytes/write_bytes
83-
84-
mutable struct I2CDevice
85-
fd::RawFD
86-
bus::Int
87-
address::UInt8
88-
path::String
89-
# Pre-allocated buffers (shared between SMBus and RDWR operations)
90-
ioctl_buf::Vector{UInt8} # For SMBusIoctlData or I2CRdwrIoctlData
91-
msgs_buf::Vector{UInt8} # For I2CMsg array (2 msgs max)
92-
data_buf::Vector{UInt8} # SMBus data (34 bytes) or RDWR reg+data
93-
end
94-
95162
#=============================================================================
96163
ioctl Wrapper
97164
=============================================================================#
@@ -111,7 +178,13 @@ function open_device(bus::Int, address::Integer)
111178
path = "/dev/i2c-$bus"
112179
fd = ccall(:open, Cint, (Cstring, Cint), path, 0x0002) # O_RDWR = 0x0002
113180
if fd < 0
114-
error("Failed to open I2C device: $path")
181+
errno = get_errno()
182+
if haskey(ERRNO_NAMES, errno)
183+
name, desc = ERRNO_NAMES[errno]
184+
error("I2C open_device failed [path=$path]: $name (errno=$errno) - $desc")
185+
else
186+
error("I2C open_device failed [path=$path]: errno=$errno (unknown error code)")
187+
end
115188
end
116189

117190
raw_fd = RawFD(fd)
@@ -120,8 +193,14 @@ function open_device(bus::Int, address::Integer)
120193
# Set slave address
121194
ret = ioctl(raw_fd, UInt(I2C_SLAVE), Clong(addr))
122195
if ret < 0
196+
errno = get_errno()
123197
ccall(:close, Cint, (Cint,), fd)
124-
error("Failed to set I2C slave address: 0x$(string(addr, base=16))")
198+
if haskey(ERRNO_NAMES, errno)
199+
name, desc = ERRNO_NAMES[errno]
200+
error("I2C set_slave_address failed [bus=$bus, addr=0x$(string(addr, base=16, pad=2))]: $name (errno=$errno) - $desc")
201+
else
202+
error("I2C set_slave_address failed [bus=$bus, addr=0x$(string(addr, base=16, pad=2))]: errno=$errno (unknown error code)")
203+
end
125204
end
126205

127206
# Pre-allocate shared buffers
@@ -149,21 +228,25 @@ end
149228
i2c_read(dev::I2CDevice, buf::Vector{UInt8}) -> Int
150229
151230
Read bytes from I2C device into buffer. Returns number of bytes read.
231+
Throws an error with detailed diagnostics on failure.
152232
"""
153233
function i2c_read(dev::I2CDevice, buf::Vector{UInt8})
154234
n = ccall(:read, Cssize_t, (Cint, Ptr{UInt8}, Csize_t),
155235
Base.cconvert(Cint, dev.fd), buf, length(buf))
236+
n < 0 && i2c_error("read", dev)
156237
return Int(n)
157238
end
158239

159240
"""
160241
i2c_write(dev::I2CDevice, buf::Vector{UInt8}) -> Int
161242
162243
Write bytes to I2C device. Returns number of bytes written.
244+
Throws an error with detailed diagnostics on failure.
163245
"""
164246
function i2c_write(dev::I2CDevice, buf::Vector{UInt8})
165247
n = ccall(:write, Cssize_t, (Cint, Ptr{UInt8}, Csize_t),
166248
Base.cconvert(Cint, dev.fd), buf, length(buf))
249+
n < 0 && i2c_error("write", dev)
167250
return Int(n)
168251
end
169252

@@ -190,17 +273,19 @@ Read a single byte from the device (no register address).
190273
"""
191274
function read_byte(dev::I2CDevice)
192275
ret = smbus_access(dev, I2C_SMBUS_READ, UInt8(0), I2C_SMBUS_BYTE)
193-
ret < 0 && error("SMBus read_byte failed")
276+
ret < 0 && i2c_error("read_byte", dev)
194277
return dev.data_buf[1]
195278
end
196279

197280
"""
198-
write_byte(dev::I2CDevice, value::UInt8) -> Int
281+
write_byte(dev::I2CDevice, value::UInt8)
199282
200283
Write a single byte to the device (no register address).
201284
"""
202285
function write_byte(dev::I2CDevice, value::UInt8)
203-
return smbus_access(dev, I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE)
286+
ret = smbus_access(dev, I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE)
287+
ret < 0 && i2c_error("write_byte", dev; value=value)
288+
return ret
204289
end
205290

206291
"""
@@ -210,7 +295,7 @@ Read a byte from a specific register.
210295
"""
211296
function read_byte_data(dev::I2CDevice, reg::UInt8)
212297
ret = smbus_access(dev, I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA)
213-
ret < 0 && error("SMBus read_byte_data failed for register 0x$(string(reg, base=16))")
298+
ret < 0 && i2c_error("read_byte_data", dev; reg=reg)
214299
return dev.data_buf[1]
215300
end
216301

@@ -221,7 +306,9 @@ Write a byte to a specific register.
221306
"""
222307
function write_byte_data(dev::I2CDevice, reg::UInt8, value::UInt8)
223308
dev.data_buf[1] = value
224-
return smbus_access(dev, I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA)
309+
ret = smbus_access(dev, I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA)
310+
ret < 0 && i2c_error("write_byte_data", dev; reg=reg, value=value)
311+
return ret
225312
end
226313

227314
"""
@@ -231,7 +318,7 @@ Read a 16-bit word from a specific register.
231318
"""
232319
function read_word_data(dev::I2CDevice, reg::UInt8)
233320
ret = smbus_access(dev, I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA)
234-
ret < 0 && error("SMBus read_word_data failed for register 0x$(string(reg, base=16))")
321+
ret < 0 && i2c_error("read_word_data", dev; reg=reg)
235322
return UInt16(dev.data_buf[1]) | (UInt16(dev.data_buf[2]) << 8)
236323
end
237324

@@ -243,7 +330,9 @@ Write a 16-bit word to a specific register.
243330
function write_word_data(dev::I2CDevice, reg::UInt8, value::UInt16)
244331
dev.data_buf[1] = UInt8(value & 0xFF)
245332
dev.data_buf[2] = UInt8((value >> 8) & 0xFF)
246-
return smbus_access(dev, I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA)
333+
ret = smbus_access(dev, I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA)
334+
ret < 0 && i2c_error("write_word_data", dev; reg=reg)
335+
return ret
247336
end
248337

249338
#=============================================================================
@@ -272,7 +361,7 @@ function read_bytes!(dev::I2CDevice, reg::UInt8, dest::AbstractVector{UInt8})
272361
unsafe_store!(Ptr{I2CRdwrIoctlData}(pointer(dev.ioctl_buf)), rdwr)
273362

274363
ret = ioctl(dev.fd, UInt(I2C_RDWR), pointer(dev.ioctl_buf))
275-
ret < 0 && error("I2C_RDWR read failed for register 0x$(string(reg, base=16))")
364+
ret < 0 && i2c_error("read_bytes", dev; reg=reg)
276365

277366
return count
278367
end
@@ -311,7 +400,7 @@ function write_bytes(dev::I2CDevice, reg::UInt8, data::AbstractVector{UInt8})
311400
unsafe_store!(Ptr{I2CRdwrIoctlData}(pointer(dev.ioctl_buf)), rdwr)
312401

313402
ret = ioctl(dev.fd, UInt(I2C_RDWR), pointer(dev.ioctl_buf))
314-
ret < 0 && error("I2C_RDWR write failed for register 0x$(string(reg, base=16))")
403+
ret < 0 && i2c_error("write_bytes", dev; reg=reg)
315404

316405
return ret
317406
end

0 commit comments

Comments
 (0)