From 008db0d14a3ffe6b818f64bc55653610014b317f Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Sun, 21 Nov 2021 23:48:25 -0500 Subject: [PATCH 01/10] Update I2C.swift --- Sources/I2C.swift | 135 +++++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 42 deletions(-) diff --git a/Sources/I2C.swift b/Sources/I2C.swift index 7df0ecb..080962d 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -70,18 +70,23 @@ extension SwiftyGPIO { public protocol I2CInterface { func isReachable(_ address: Int) -> Bool - func setPEC(_ address: Int, enabled: Bool) + @discardableResult func setPEC(_ address: Int, enabled: Bool) -> Bool func readByte(_ address: Int) -> UInt8 func readByte(_ address: Int, command: UInt8) -> UInt8 func readWord(_ address: Int, command: UInt8) -> UInt16 func readData(_ address: Int, command: UInt8) -> [UInt8] func readI2CData(_ address: Int, command: UInt8) -> [UInt8] - func writeQuick(_ address: Int) - func writeByte(_ address: Int, value: UInt8) - func writeByte(_ address: Int, command: UInt8, value: UInt8) - func writeWord(_ address: Int, command: UInt8, value: UInt16) - func writeData(_ address: Int, command: UInt8, values: [UInt8]) - func writeI2CData(_ address: Int, command: UInt8, values: [UInt8]) + func tryReadByte(_ address: Int) -> UInt8? + func tryReadByte(_ address: Int, command: UInt8) -> UInt8? + func tryReadWord(_ address: Int, command: UInt8) -> UInt16? + func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? + func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? + @discardableResult func writeQuick(_ address: Int) -> Bool + @discardableResult func writeByte(_ address: Int, value: UInt8) -> Bool + @discardableResult func writeByte(_ address: Int, command: UInt8, value: UInt8) -> Bool + @discardableResult func writeWord(_ address: Int, command: UInt8, value: UInt16) -> Bool + @discardableResult func writeData(_ address: Int, command: UInt8, values: [UInt8]) -> Bool + @discardableResult func writeI2CData(_ address: Int, command: UInt8, values: [UInt8]) -> Bool // One-shot rd/wr not provided } @@ -118,6 +123,20 @@ public final class SysFSI2C: I2CInterface { #endif } + public func tryReadByte(_ address: Int) -> UInt8? { + setSlaveAddress(address) + + let r = i2c_smbus_read_byte() + + if r < 0 { return nil } + + #if swift(>=4.0) + return UInt8(truncatingIfNeeded: r) + #else + return UInt8(truncatingBitPattern: r) + #endif + } + public func readByte(_ address: Int, command: UInt8) -> UInt8 { setSlaveAddress(address) @@ -134,6 +153,20 @@ public final class SysFSI2C: I2CInterface { #endif } + public func tryReadByte(_ address: Int, command: UInt8) -> UInt8? { + setSlaveAddress(address) + + let r = i2c_smbus_read_byte_data(command: command) + + if r < 0 { return nil; } + + #if swift(>=4.0) + return UInt8(truncatingIfNeeded: r) + #else + return UInt8(truncatingBitPattern: r) + #endif + } + public func readWord(_ address: Int, command: UInt8) -> UInt16 { setSlaveAddress(address) @@ -150,6 +183,20 @@ public final class SysFSI2C: I2CInterface { #endif } + public func tryReadWord(_ address: Int, command: UInt8) -> UInt16? { + setSlaveAddress(address) + + let r = i2c_smbus_read_word_data(command: command) + + if r < 0 { return nil } + + #if swift(>=4.0) + return UInt16(truncatingIfNeeded: r) + #else + return UInt16(truncatingBitPattern: r) + #endif + } + public func readData(_ address: Int, command: UInt8) -> [UInt8] { var buf: [UInt8] = [UInt8](repeating:0, count: 32) @@ -164,6 +211,19 @@ public final class SysFSI2C: I2CInterface { return buf } + public func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? { + var buf: [UInt8] = [UInt8](repeating:0, count: 32) + + setSlaveAddress(address) + + let r = i2c_smbus_read_block_data(command: command, values: &buf) + + if r < 0 { return nil } + + return buf + } + + public func readI2CData(_ address: Int, command: UInt8) -> [UInt8] { var buf: [UInt8] = [UInt8](repeating:0, count: 32) @@ -178,70 +238,64 @@ public final class SysFSI2C: I2CInterface { return buf } - public func writeQuick(_ address: Int) { + public func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? { + var buf: [UInt8] = [UInt8](repeating:0, count: 32) + + setSlaveAddress(address) + + let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf) + + if r < 0 { return nil } + + return buf + } + + public func writeQuick(_ address: Int) -> Bool { setSlaveAddress(address) let r = i2c_smbus_write_quick(value: I2C_SMBUS_WRITE) - if r < 0 { - perror("I2C write failed") - abort() - } + return r >= 0 } - public func writeByte(_ address: Int, value: UInt8) { + public func writeByte(_ address: Int, value: UInt8) -> Bool { setSlaveAddress(address) let r = i2c_smbus_write_byte(value: value) - if r < 0 { - perror("I2C write failed") - abort() - } + return r >= 0 } - public func writeByte(_ address: Int, command: UInt8, value: UInt8) { + public func writeByte(_ address: Int, command: UInt8, value: UInt8) -> Bool { setSlaveAddress(address) let r = i2c_smbus_write_byte_data(command: command, value: value) - if r < 0 { - perror("I2C write failed") - abort() - } + return r >= 0 } - public func writeWord(_ address: Int, command: UInt8, value: UInt16) { + public func writeWord(_ address: Int, command: UInt8, value: UInt16) -> Bool { setSlaveAddress(address) let r = i2c_smbus_write_word_data(command: command, value: value) - if r < 0 { - perror("I2C write failed") - abort() - } + return r >= 0 } - public func writeData(_ address: Int, command: UInt8, values: [UInt8]) { + public func writeData(_ address: Int, command: UInt8, values: [UInt8]) -> Bool { setSlaveAddress(address) let r = i2c_smbus_write_block_data(command: command, values: values) - if r < 0 { - perror("I2C write failed") - abort() - } + return r >= 0 } - public func writeI2CData(_ address: Int, command: UInt8, values: [UInt8]) { + public func writeI2CData(_ address: Int, command: UInt8, values: [UInt8]) -> Bool { setSlaveAddress(address) let r = i2c_smbus_write_i2c_block_data(command: command, values: values) - if r < 0 { - perror("I2C write failed") - abort() - } + return r >= 0 } public func isReachable(_ address: Int) -> Bool { @@ -270,15 +324,12 @@ public final class SysFSI2C: I2CInterface { return true } - public func setPEC(_ address: Int, enabled: Bool) { + public func setPEC(_ address: Int, enabled: Bool) -> Bool { setSlaveAddress(address) let r = ioctl(fd, I2C_PEC, enabled ? 1 : 0) - if r != 0 { - perror("I2C communication failed") - abort() - } + return r >= 0 } private func setSlaveAddress(_ to: Int) { From 173c14eb08bbc4ca3938f240570d525e85026feb Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Mon, 22 Nov 2021 00:08:58 -0500 Subject: [PATCH 02/10] Fix minor bug in setPEC --- Sources/I2C.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/I2C.swift b/Sources/I2C.swift index 080962d..30de382 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -329,7 +329,7 @@ public final class SysFSI2C: I2CInterface { let r = ioctl(fd, I2C_PEC, enabled ? 1 : 0) - return r >= 0 + return r == 0 } private func setSlaveAddress(_ to: Int) { From bbc5a07557e313034a3e2e14f9b0d32f8dbe0055 Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Mon, 22 Nov 2021 10:08:40 -0500 Subject: [PATCH 03/10] Update README.md to document I2C changes --- README.md | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 733e38b..3973028 100644 --- a/README.md +++ b/README.md @@ -27,22 +27,24 @@ Examples of *[device libraries](#libraries)* and *[complete projects](#awesome- ##### Content: +- [Summary](#summary) + - [Content:](#content) - [Supported Boards](#supported-boards) - [Installation](#installation) -- [Your First Project: Blinking Leds And Sensors](#your-first-project-blinking-leds-and-sensors) +- [Your First Project: Blinking leds and sensors](#your-first-project-blinking-leds-and-sensors) - [Usage](#usage) - - [GPIO](#gpio) - - [SPI](#spi) - - [I2C](#i2c) - - [PWM](#pwm) - - [Pattern-based signal generator via PWM](#pattern-based-signal-generator-via-pwm) - - [UART](#uart) - - [1-Wire](#1-wire) + - [GPIO](#gpio) + - [SPI](#spi) + - [I2C](#i2c) + - [PWM](#pwm) + - [Pattern-based signal generator via PWM](#pattern-based-signal-generator-via-pwm) + - [UART](#uart) + - [1-Wire](#1-wire) - [Examples](#examples) - [Built with SwiftyGPIO](#built-with-swiftygpio) - - [Device Libraries](#libraries) - - [Awesome Projects](#awesome-projects) - - [Support Libraries](#support-libraries) + - [Libraries](#libraries) + - [Awesome Projects](#awesome-projects) + - [Support libraries](#support-libraries) - [Additional documentation](#additional-documentation) @@ -314,6 +316,16 @@ func readData(_ address: Int, command: UInt8) -> [UInt8] func readI2CData(_ address: Int, command: UInt8) -> [UInt8] ``` +There are also versions of the read functions that return an optional value with `nil` indicating a read failure: + +```swift +func tryReadByte(_ address: Int) -> UInt8? +func tryReadByte(_ address: Int, command: UInt8) -> UInt8? +func tryReadWord(_ address: Int, command: UInt8) -> UInt16? +func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? +func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? +``` + Reading and writing data blocks supports two modes, a standard SMBus mode (`readData` and `writeData`) that prepends the length of the block before the actual data, and an old style I2C mode (`readI2CData` and `writeI2CData`) that just send the data without additional metadata. Depending on the device, only one of the two modes will be supported. Let's suppose that we want to read the seconds register (id 0) from a DS1307 RTC clock, that has an I2C address of 0x68: @@ -322,16 +334,16 @@ Let's suppose that we want to read the seconds register (id 0) from a DS1307 RTC print(i2c.readByte(0x68, command: 0)) //Prints the value of the 8bit register ``` -You should choose the same way one of the write functions available, just note that `writeQuick` is used to perform quick commands and does not perform a normal write. SMBus's quick commands are usually used to turn on/off devices or perform similar tasks that don't require additional parameters. +You should choose the same way one of the write functions available, just note that `writeQuick` is used to perform quick commands and does not perform a normal write. SMBus's quick commands are usually used to turn on/off devices or perform similar tasks that don't require additional parameters. The write functions return `true` on success and `false` on failure. ```swift -func writeQuick(_ address: Int) +func writeQuick(_ address: Int) -> Bool -func writeByte(_ address: Int, value: UInt8) -func writeByte(_ address: Int, command: UInt8, value: UInt8) -func writeWord(_ address: Int, command: UInt8, value: UInt16) -func writeData(_ address: Int, command: UInt8, values: [UInt8]) -func writeI2CData(_ address: Int, command: UInt8, values: [UInt8]) +func writeByte(_ address: Int, value: UInt8) -> Bool +func writeByte(_ address: Int, command: UInt8, value: UInt8) -> Bool +func writeWord(_ address: Int, command: UInt8, value: UInt16) -> Bool +func writeData(_ address: Int, command: UInt8, values: [UInt8]) -> Bool +func writeI2CData(_ address: Int, command: UInt8, values: [UInt8]) -> Bool ``` While using the I2C functionality doesn't require additional software to function, the tools contained in `i2c-tools` are useful to perform I2C transactions manually to verify that everything is working correctly. From 2d46c99971d14229649e677cf1305f47a8890946 Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Mon, 6 Dec 2021 17:00:15 -0500 Subject: [PATCH 04/10] Added readRaw --- Sources/I2C.swift | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Sources/I2C.swift b/Sources/I2C.swift index 30de382..bc64d15 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -76,11 +76,15 @@ public protocol I2CInterface { func readWord(_ address: Int, command: UInt8) -> UInt16 func readData(_ address: Int, command: UInt8) -> [UInt8] func readI2CData(_ address: Int, command: UInt8) -> [UInt8] + func readRaw(_ address: Int, length: UInt) -> [UInt8] + func tryReadByte(_ address: Int) -> UInt8? func tryReadByte(_ address: Int, command: UInt8) -> UInt8? func tryReadWord(_ address: Int, command: UInt8) -> UInt16? func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? + func tryReadRaw(_ address: Int, length: UInt) -> [UInt8]? + @discardableResult func writeQuick(_ address: Int) -> Bool @discardableResult func writeByte(_ address: Int, value: UInt8) -> Bool @discardableResult func writeByte(_ address: Int, command: UInt8, value: UInt8) -> Bool @@ -249,7 +253,33 @@ public final class SysFSI2C: I2CInterface { return buf } - + + public func readRaw(_ address: Int, length: UInt) -> [UInt8] { + var buf: [UInt8] = [UInt8](repeating:0, count: length) + + setSlaveAddress(address) + + let r = read( i2cId, &buf, length ) + + if r < 0 { + perror("I2C read failed") + abort() + } + return buf + } + + public func tryReadRaw(_ address: Int, length: UInt) -> [UInt8]? { + var buf: [UInt8] = [UInt8](repeating:0, count: length) + + setSlaveAddress(address) + + let r = read( i2cId, &buf, length ) + + if r < 0 { return nil } + + return buf + } + public func writeQuick(_ address: Int) -> Bool { setSlaveAddress(address) From 2726426dcc84853b9b49be71be78b6ca242fe14b Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Mon, 6 Dec 2021 18:45:56 -0500 Subject: [PATCH 05/10] Revert "Added readRaw" This reverts commit 2d46c99971d14229649e677cf1305f47a8890946. --- Sources/I2C.swift | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/Sources/I2C.swift b/Sources/I2C.swift index bc64d15..30de382 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -76,15 +76,11 @@ public protocol I2CInterface { func readWord(_ address: Int, command: UInt8) -> UInt16 func readData(_ address: Int, command: UInt8) -> [UInt8] func readI2CData(_ address: Int, command: UInt8) -> [UInt8] - func readRaw(_ address: Int, length: UInt) -> [UInt8] - func tryReadByte(_ address: Int) -> UInt8? func tryReadByte(_ address: Int, command: UInt8) -> UInt8? func tryReadWord(_ address: Int, command: UInt8) -> UInt16? func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? - func tryReadRaw(_ address: Int, length: UInt) -> [UInt8]? - @discardableResult func writeQuick(_ address: Int) -> Bool @discardableResult func writeByte(_ address: Int, value: UInt8) -> Bool @discardableResult func writeByte(_ address: Int, command: UInt8, value: UInt8) -> Bool @@ -253,33 +249,7 @@ public final class SysFSI2C: I2CInterface { return buf } - - public func readRaw(_ address: Int, length: UInt) -> [UInt8] { - var buf: [UInt8] = [UInt8](repeating:0, count: length) - - setSlaveAddress(address) - - let r = read( i2cId, &buf, length ) - - if r < 0 { - perror("I2C read failed") - abort() - } - return buf - } - - public func tryReadRaw(_ address: Int, length: UInt) -> [UInt8]? { - var buf: [UInt8] = [UInt8](repeating:0, count: length) - - setSlaveAddress(address) - - let r = read( i2cId, &buf, length ) - - if r < 0 { return nil } - - return buf - } - + public func writeQuick(_ address: Int) -> Bool { setSlaveAddress(address) From 6d9109f71ac7631beb3bdd5c80b6ef5d8b55f350 Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Tue, 7 Dec 2021 12:38:06 -0500 Subject: [PATCH 06/10] Added read raw commands --- Sources/I2C.swift | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/Sources/I2C.swift b/Sources/I2C.swift index 30de382..ded3b81 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -76,11 +76,15 @@ public protocol I2CInterface { func readWord(_ address: Int, command: UInt8) -> UInt16 func readData(_ address: Int, command: UInt8) -> [UInt8] func readI2CData(_ address: Int, command: UInt8) -> [UInt8] + func readRaw(_ address: Int, length: Int) -> [UInt8] + func tryReadByte(_ address: Int) -> UInt8? func tryReadByte(_ address: Int, command: UInt8) -> UInt8? func tryReadWord(_ address: Int, command: UInt8) -> UInt16? func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? + func tryReadRaw(_ address: Int, length: Int) -> [UInt8]? + @discardableResult func writeQuick(_ address: Int) -> Bool @discardableResult func writeByte(_ address: Int, value: UInt8) -> Bool @discardableResult func writeByte(_ address: Int, command: UInt8, value: UInt8) -> Bool @@ -128,7 +132,7 @@ public final class SysFSI2C: I2CInterface { let r = i2c_smbus_read_byte() - if r < 0 { return nil } + guard r >= 0 else { return nil } #if swift(>=4.0) return UInt8(truncatingIfNeeded: r) @@ -158,7 +162,7 @@ public final class SysFSI2C: I2CInterface { let r = i2c_smbus_read_byte_data(command: command) - if r < 0 { return nil; } + guard r >= 0 else { return nil } #if swift(>=4.0) return UInt8(truncatingIfNeeded: r) @@ -188,7 +192,7 @@ public final class SysFSI2C: I2CInterface { let r = i2c_smbus_read_word_data(command: command) - if r < 0 { return nil } + guard r >= 0 else { return nil } #if swift(>=4.0) return UInt16(truncatingIfNeeded: r) @@ -216,9 +220,9 @@ public final class SysFSI2C: I2CInterface { setSlaveAddress(address) - let r = i2c_smbus_read_block_data(command: command, values: &buf) + let r = i2c_smbus_read_block_data(command: command, values: &buf) - if r < 0 { return nil } + guard r >= 0 else { return nil } return buf } @@ -243,13 +247,39 @@ public final class SysFSI2C: I2CInterface { setSlaveAddress(address) - let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf) + let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf) - if r < 0 { return nil } + guard r >= 0 else { return nil } return buf } - + + public func readRaw(_ address: Int, length: Int) -> [UInt8] { + var buf: [UInt8] = [UInt8](repeating:0, count: length) + + setSlaveAddress(address) + + let r = read( fd, &buf, length ) + + if r < 0 { + perror("I2C read failed") + abort() + } + return buf + } + + public func tryReadRaw(_ address: Int, length: Int) -> [UInt8]? { + var buf: [UInt8] = [UInt8](repeating:0, count: length) + + setSlaveAddress(address) + + let r = read( fd, &buf, length ) + + guard r >= 0 else { return nil } + + return buf + } + public func writeQuick(_ address: Int) -> Bool { setSlaveAddress(address) From b27f89308ed3f2c7cadcb41e3b62532f2629d52b Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Thu, 16 Dec 2021 12:15:54 -0500 Subject: [PATCH 07/10] Added function to do write and read in one operation. Changed "try" variants to throw error rather than return optional. --- Sources/I2C.swift | 121 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/Sources/I2C.swift b/Sources/I2C.swift index ded3b81..6770cfe 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -28,6 +28,8 @@ import Darwin.C #endif +import Foundation + extension SwiftyGPIO { public static func hardwareI2Cs(for board: SupportedBoard) -> [I2CInterface]? { @@ -77,13 +79,15 @@ public protocol I2CInterface { func readData(_ address: Int, command: UInt8) -> [UInt8] func readI2CData(_ address: Int, command: UInt8) -> [UInt8] func readRaw(_ address: Int, length: Int) -> [UInt8] + func writeAndRead(_ address: Int, write: [UInt8], readLength: UInt) -> [UInt8] - func tryReadByte(_ address: Int) -> UInt8? - func tryReadByte(_ address: Int, command: UInt8) -> UInt8? - func tryReadWord(_ address: Int, command: UInt8) -> UInt16? - func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? - func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? - func tryReadRaw(_ address: Int, length: Int) -> [UInt8]? + func tryReadByte(_ address: Int) throws -> UInt8 + func tryReadByte(_ address: Int, command: UInt8) throws -> UInt8 + func tryReadWord(_ address: Int, command: UInt8) throws -> UInt16 + func tryReadData(_ address: Int, command: UInt8) throws -> [UInt8] + func tryReadI2CData(_ address: Int, command: UInt8) throws -> [UInt8] + func tryReadRaw(_ address: Int, length: Int) throws -> [UInt8] + func tryWriteAndRead(_ address: Int, write: [UInt8], readLength: UInt) throws -> [UInt8] @discardableResult func writeQuick(_ address: Int) -> Bool @discardableResult func writeByte(_ address: Int, value: UInt8) -> Bool @@ -127,12 +131,12 @@ public final class SysFSI2C: I2CInterface { #endif } - public func tryReadByte(_ address: Int) -> UInt8? { + public func tryReadByte(_ address: Int) throws -> UInt8 { setSlaveAddress(address) let r = i2c_smbus_read_byte() - guard r >= 0 else { return nil } + guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } #if swift(>=4.0) return UInt8(truncatingIfNeeded: r) @@ -157,12 +161,12 @@ public final class SysFSI2C: I2CInterface { #endif } - public func tryReadByte(_ address: Int, command: UInt8) -> UInt8? { + public func tryReadByte(_ address: Int, command: UInt8) throws -> UInt8 { setSlaveAddress(address) let r = i2c_smbus_read_byte_data(command: command) - guard r >= 0 else { return nil } + guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } #if swift(>=4.0) return UInt8(truncatingIfNeeded: r) @@ -187,12 +191,12 @@ public final class SysFSI2C: I2CInterface { #endif } - public func tryReadWord(_ address: Int, command: UInt8) -> UInt16? { + public func tryReadWord(_ address: Int, command: UInt8) throws -> UInt16 { setSlaveAddress(address) let r = i2c_smbus_read_word_data(command: command) - guard r >= 0 else { return nil } + guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } #if swift(>=4.0) return UInt16(truncatingIfNeeded: r) @@ -215,14 +219,14 @@ public final class SysFSI2C: I2CInterface { return buf } - public func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? { + public func tryReadData(_ address: Int, command: UInt8) throws -> [UInt8] { var buf: [UInt8] = [UInt8](repeating:0, count: 32) setSlaveAddress(address) let r = i2c_smbus_read_block_data(command: command, values: &buf) - guard r >= 0 else { return nil } + guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } return buf } @@ -242,14 +246,14 @@ public final class SysFSI2C: I2CInterface { return buf } - public func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? { + public func tryReadI2CData(_ address: Int, command: UInt8) throws -> [UInt8] { var buf: [UInt8] = [UInt8](repeating:0, count: 32) setSlaveAddress(address) let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf) - guard r >= 0 else { return nil } + guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } return buf } @@ -268,18 +272,48 @@ public final class SysFSI2C: I2CInterface { return buf } - public func tryReadRaw(_ address: Int, length: Int) -> [UInt8]? { - var buf: [UInt8] = [UInt8](repeating:0, count: length) + public func tryReadRaw(_ address: Int, length: Int) throws -> [UInt8] { + var buf: [UInt8] = [UInt8](repeating:0, count: Int( length )) setSlaveAddress(address) let r = read( fd, &buf, length ) - guard r >= 0 else { return nil } + guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } return buf } + + public func writeAndRead(_ address: Int, write: [UInt8], readLength: UInt) -> [UInt8] { + var readBuf: [UInt8] = [UInt8](repeating:0, count: Int( readLength ) ) + + setSlaveAddress(address) + + let r = i2c_write_and_read (write: write, read: &readBuf ) + + + if r < 0 { + perror("I2C read failed") + abort() + } + return readBuf + } + + + public func tryWriteAndRead(_ address: Int, write: [UInt8], readLength: UInt) throws -> [UInt8] { + var readBuf: [UInt8] = [UInt8](repeating:0, count: Int( readLength )) + + setSlaveAddress(address) + + let r = i2c_write_and_read (write: write, read: &readBuf ) + + guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } + + return readBuf + } + + public func writeQuick(_ address: Int) -> Bool { setSlaveAddress(address) @@ -402,6 +436,7 @@ public final class SysFSI2C: I2CInterface { var data: UnsafeMutablePointer? //union: UInt8, UInt16, [UInt8]33 } + private func smbus_ioctl(rw: UInt8, command: UInt8, size: Int32, data: UnsafeMutablePointer?) -> Int32 { if fd == -1 { openI2C() @@ -570,6 +605,51 @@ public final class SysFSI2C: I2CInterface { data: &data) } + // Swift implementation of the i2c RW ioctl + + struct i2c_rw_msg { + var addr: UInt16 + var flags: UInt16 + var len: UInt16 + var buf: UnsafeMutablePointer + } + + struct i2c_rdwr_ioctl_data + { + var msgs: UnsafePointer + var nmsgs: UInt32 + }; + + private func i2c_write_and_read (write: [UInt8], read: inout [UInt8] ) -> Int32 + { + guard write.count<=I2C_DEFAULT_PAYLOAD_LENGTH && read.count<=I2C_DEFAULT_PAYLOAD_LENGTH else { fatalError("Invalid data length, can't send more than \(I2C_DEFAULT_PAYLOAD_LENGTH) bytes!") } + + var writeData = write + + return writeData.withUnsafeMutableBufferPointer { writePtr in + return read.withUnsafeMutableBufferPointer { readPtr in + + let msgs = [ i2c_rw_msg( addr: UInt16( currentSlave ), + flags: 0, + len: UInt16( writePtr.count ), + buf: writePtr.baseAddress! ), + i2c_rw_msg( addr: UInt16( currentSlave ), + flags: I2C_M_RD, + len: UInt16( readPtr.count ), + buf: readPtr.baseAddress! ) + ] + + return msgs.withUnsafeBufferPointer { msgsPtr in + + var cmd = i2c_rdwr_ioctl_data( msgs: msgsPtr.baseAddress!, nmsgs: 2 ) + + return ioctl( fd, I2C_RDWR, &cmd ) + + } + } + } + } + } // MARK: - I2C/SMBUS Constants @@ -593,6 +673,9 @@ internal let I2C_SMBUS: UInt = 0x720 internal let I2C_DEFAULT_PAYLOAD_LENGTH: Int = 32 internal let I2CBASEPATH="/dev/i2c-" +internal let I2C_M_RD: UInt16 = 0x0001 + + // MARK: - Darwin / Xcode Support #if os(OSX) || os(iOS) private var O_SYNC: CInt { fatalError("Linux only") } From e8d4915655084dd4df8752b609aaa547f3d3aecd Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Thu, 16 Dec 2021 12:29:48 -0500 Subject: [PATCH 08/10] Update README to document changes to I2C --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3973028..738cd93 100644 --- a/README.md +++ b/README.md @@ -314,16 +314,22 @@ func readByte(_ address: Int, command: UInt8) -> UInt8 func readWord(_ address: Int, command: UInt8) -> UInt16 func readData(_ address: Int, command: UInt8) -> [UInt8] func readI2CData(_ address: Int, command: UInt8) -> [UInt8] +func readRaw(_ address: Int, length: Int) -> [UInt8] +func writeAndRead(_ address: Int, write: [UInt8], readLength: UInt) -> [UInt8] + ``` -There are also versions of the read functions that return an optional value with `nil` indicating a read failure: +There are also versions of the read functions that throw an error to indicate a read failure: ```swift -func tryReadByte(_ address: Int) -> UInt8? -func tryReadByte(_ address: Int, command: UInt8) -> UInt8? -func tryReadWord(_ address: Int, command: UInt8) -> UInt16? -func tryReadData(_ address: Int, command: UInt8) -> [UInt8]? -func tryReadI2CData(_ address: Int, command: UInt8) -> [UInt8]? +func tryReadByte(_ address: Int) throws -> UInt8 +func tryReadByte(_ address: Int, command: UInt8) throws -> UInt8 +func tryReadWord(_ address: Int, command: UInt8) throws -> UInt16 +func tryReadData(_ address: Int, command: UInt8) throws -> [UInt8] +func tryReadI2CData(_ address: Int, command: UInt8) throws -> [UInt8] +func tryReadRaw(_ address: Int, length: Int) throws -> [UInt8] +func tryWriteAndRead(_ address: Int, write: [UInt8], readLength: UInt) throws -> [UInt8] + ``` Reading and writing data blocks supports two modes, a standard SMBus mode (`readData` and `writeData`) that prepends the length of the block before the actual data, and an old style I2C mode (`readI2CData` and `writeI2CData`) that just send the data without additional metadata. Depending on the device, only one of the two modes will be supported. From 1e99dce9c913f97648765168f540186b16fffbb7 Mon Sep 17 00:00:00 2001 From: Craig Altrenburg Date: Sat, 18 Dec 2021 19:07:46 -0500 Subject: [PATCH 09/10] Added missing length parameter to the readI2CData calls. --- README.md | 4 ++-- Sources/I2C.swift | 21 +++++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 738cd93..c1f3819 100644 --- a/README.md +++ b/README.md @@ -313,7 +313,7 @@ func readByte(_ address: Int) -> UInt8 func readByte(_ address: Int, command: UInt8) -> UInt8 func readWord(_ address: Int, command: UInt8) -> UInt16 func readData(_ address: Int, command: UInt8) -> [UInt8] -func readI2CData(_ address: Int, command: UInt8) -> [UInt8] +func readI2CData(_ address: Int, command: UInt8, length: UInt8) -> [UInt8] func readRaw(_ address: Int, length: Int) -> [UInt8] func writeAndRead(_ address: Int, write: [UInt8], readLength: UInt) -> [UInt8] @@ -326,7 +326,7 @@ func tryReadByte(_ address: Int) throws -> UInt8 func tryReadByte(_ address: Int, command: UInt8) throws -> UInt8 func tryReadWord(_ address: Int, command: UInt8) throws -> UInt16 func tryReadData(_ address: Int, command: UInt8) throws -> [UInt8] -func tryReadI2CData(_ address: Int, command: UInt8) throws -> [UInt8] +func tryReadI2CData(_ address: Int, command: UInt8, length: UInt8) throws -> [UInt8] func tryReadRaw(_ address: Int, length: Int) throws -> [UInt8] func tryWriteAndRead(_ address: Int, write: [UInt8], readLength: UInt) throws -> [UInt8] diff --git a/Sources/I2C.swift b/Sources/I2C.swift index 6770cfe..52d4e33 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -77,7 +77,7 @@ public protocol I2CInterface { func readByte(_ address: Int, command: UInt8) -> UInt8 func readWord(_ address: Int, command: UInt8) -> UInt16 func readData(_ address: Int, command: UInt8) -> [UInt8] - func readI2CData(_ address: Int, command: UInt8) -> [UInt8] + func readI2CData(_ address: Int, command: UInt8, length: UInt8) -> [UInt8] func readRaw(_ address: Int, length: Int) -> [UInt8] func writeAndRead(_ address: Int, write: [UInt8], readLength: UInt) -> [UInt8] @@ -85,7 +85,7 @@ public protocol I2CInterface { func tryReadByte(_ address: Int, command: UInt8) throws -> UInt8 func tryReadWord(_ address: Int, command: UInt8) throws -> UInt16 func tryReadData(_ address: Int, command: UInt8) throws -> [UInt8] - func tryReadI2CData(_ address: Int, command: UInt8) throws -> [UInt8] + func tryReadI2CData(_ address: Int, command: UInt8, length: UInt8) throws -> [UInt8] func tryReadRaw(_ address: Int, length: Int) throws -> [UInt8] func tryWriteAndRead(_ address: Int, write: [UInt8], readLength: UInt) throws -> [UInt8] @@ -232,12 +232,12 @@ public final class SysFSI2C: I2CInterface { } - public func readI2CData(_ address: Int, command: UInt8) -> [UInt8] { + public func readI2CData(_ address: Int, command: UInt8, length: UInt8) -> [UInt8] { var buf: [UInt8] = [UInt8](repeating:0, count: 32) setSlaveAddress(address) - let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf) + let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf, length) if r < 0 { perror("I2C read failed") @@ -246,12 +246,15 @@ public final class SysFSI2C: I2CInterface { return buf } - public func tryReadI2CData(_ address: Int, command: UInt8) throws -> [UInt8] { + public func tryReadI2CData(_ address: Int, command: UInt8, length: UInt8) throws -> [UInt8] { + + guard length <= I2C_DEFAULT_PAYLOAD_LENGTH else { throw POSIXError( POSIXError.EINVAL ) } + var buf: [UInt8] = [UInt8](repeating:0, count: 32) setSlaveAddress(address) - let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf) + let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf, length) guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } @@ -512,15 +515,17 @@ public final class SysFSI2C: I2CInterface { } } - private func i2c_smbus_read_i2c_block_data(command: UInt8, values: inout [UInt8]) -> Int32 { + private func i2c_smbus_read_i2c_block_data(command: UInt8, values: inout [UInt8], length: UInt8) -> Int32 { var data = [UInt8](repeating:0, count: I2C_DEFAULT_PAYLOAD_LENGTH) + data[0] = length; + let r = smbus_ioctl(rw: I2C_SMBUS_READ, command: command, size: I2C_SMBUS_I2C_BLOCK_DATA, data: &data) if r >= 0 { - for i in 0.. Date: Sat, 18 Dec 2021 21:22:31 -0500 Subject: [PATCH 10/10] Fixed Buffer length bug. --- Sources/I2C.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/I2C.swift b/Sources/I2C.swift index 52d4e33..94b5b17 100644 --- a/Sources/I2C.swift +++ b/Sources/I2C.swift @@ -233,11 +233,11 @@ public final class SysFSI2C: I2CInterface { public func readI2CData(_ address: Int, command: UInt8, length: UInt8) -> [UInt8] { - var buf: [UInt8] = [UInt8](repeating:0, count: 32) + var buf: [UInt8] = [UInt8](repeating:0, count: Int(length)) setSlaveAddress(address) - let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf, length) + let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf, length: length) if r < 0 { perror("I2C read failed") @@ -250,11 +250,11 @@ public final class SysFSI2C: I2CInterface { guard length <= I2C_DEFAULT_PAYLOAD_LENGTH else { throw POSIXError( POSIXError.EINVAL ) } - var buf: [UInt8] = [UInt8](repeating:0, count: 32) + var buf: [UInt8] = [UInt8](repeating:0, count: Int(length)) setSlaveAddress(address) - let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf, length) + let r = i2c_smbus_read_i2c_block_data(command: command, values: &buf, length: length) guard r >= 0 else { throw POSIXError( POSIXError.Code( rawValue: errno ) ?? POSIXError.EIO ) } @@ -585,6 +585,7 @@ public final class SysFSI2C: I2CInterface { for i in 1...values.count { data[i] = values[i-1] } + data[0] = UInt8(values.count) return smbus_ioctl(rw: I2C_SMBUS_WRITE,