@@ -13,6 +13,90 @@ export read_byte, write_byte, read_byte_data, write_byte_data
1313export read_block_data, write_block_data
1414export 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
76160end
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
151230Read bytes from I2C device into buffer. Returns number of bytes read.
231+ Throws an error with detailed diagnostics on failure.
152232"""
153233function 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)
157238end
158239
159240"""
160241 i2c_write(dev::I2CDevice, buf::Vector{UInt8}) -> Int
161242
162243Write bytes to I2C device. Returns number of bytes written.
244+ Throws an error with detailed diagnostics on failure.
163245"""
164246function 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)
168251end
169252
@@ -190,17 +273,19 @@ Read a single byte from the device (no register address).
190273"""
191274function 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 ]
195278end
196279
197280"""
198- write_byte(dev::I2CDevice, value::UInt8) -> Int
281+ write_byte(dev::I2CDevice, value::UInt8)
199282
200283Write a single byte to the device (no register address).
201284"""
202285function 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
204289end
205290
206291"""
@@ -210,7 +295,7 @@ Read a byte from a specific register.
210295"""
211296function 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 ]
215300end
216301
@@ -221,7 +306,9 @@ Write a byte to a specific register.
221306"""
222307function 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
225312end
226313
227314"""
@@ -231,7 +318,7 @@ Read a 16-bit word from a specific register.
231318"""
232319function 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 )
236323end
237324
@@ -243,7 +330,9 @@ Write a 16-bit word to a specific register.
243330function 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
247336end
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
278367end
@@ -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
317406end
0 commit comments