├── .swift-version ├── OBD2-Swift ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── Model │ ├── ScanState.swift │ ├── Response.swift │ ├── Package.swift │ ├── Mode.swift │ ├── ScanProtocol.swift │ └── Command.swift │ ├── Stream │ ├── StreamError.swift │ ├── StreamWriter.swift │ └── StreamReader.swift │ ├── OBD2Swift.h │ ├── Parser │ ├── StringDescriptor.swift │ ├── Descriptor09.swift │ ├── DescriptorProtocol.swift │ ├── Descriptor03.swift │ ├── Parser.swift │ └── Descriptor01.swift │ ├── Command │ ├── CommandMode03.swift │ ├── CommandMode01.swift │ ├── CommandMode02.swift │ ├── CommandMode09.swift │ └── DataRequest.swift │ ├── Common │ └── Macros.swift │ ├── Operations │ ├── StreamHandleOperation.swift │ ├── OpenOBDConnectionOperation.swift │ ├── CommandOperation.swift │ └── InitScanerOperation.swift │ ├── Classes │ ├── SensorDescriptor.swift │ ├── ProtocolModelDescription.swift │ ├── SensorList.swift │ └── GloablCalculator.swift │ ├── Observer │ └── SensorObserver.swift │ ├── Logger.swift │ ├── OBD2Facade.swift │ └── Scanner │ ├── StreamHolder.swift │ └── Scanner.swift ├── .swift-version.podspec ├── _Pods.xcodeproj ├── Example ├── Pods │ ├── Pods.xcodeproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── Target Support Files │ │ ├── OBD2-Swift │ │ │ ├── OBD2-Swift.modulemap │ │ │ ├── OBD2-Swift-dummy.m │ │ │ ├── OBD2-Swift-prefix.pch │ │ │ ├── OBD2-Swift-umbrella.h │ │ │ ├── OBD2-Swift.xcconfig │ │ │ └── Info.plist │ │ ├── Pods-OBD2-Swift_Tests │ │ │ ├── Pods-OBD2-Swift_Tests.modulemap │ │ │ ├── Pods-OBD2-Swift_Tests-acknowledgements.markdown │ │ │ ├── Pods-OBD2-Swift_Tests-dummy.m │ │ │ ├── Pods-OBD2-Swift_Tests-umbrella.h │ │ │ ├── Pods-OBD2-Swift_Tests.debug.xcconfig │ │ │ ├── Pods-OBD2-Swift_Tests.release.xcconfig │ │ │ ├── Info.plist │ │ │ ├── Pods-OBD2-Swift_Tests-acknowledgements.plist │ │ │ ├── Pods-OBD2-Swift_Tests-frameworks.sh │ │ │ └── Pods-OBD2-Swift_Tests-resources.sh │ │ └── Pods-OBD2-Swift_Example │ │ │ ├── Pods-OBD2-Swift_Example.modulemap │ │ │ ├── Pods-OBD2-Swift_Example-dummy.m │ │ │ ├── Pods-OBD2-Swift_Example-umbrella.h │ │ │ ├── Pods-OBD2-Swift_Example.debug.xcconfig │ │ │ ├── Pods-OBD2-Swift_Example.release.xcconfig │ │ │ ├── Info.plist │ │ │ ├── Pods-OBD2-Swift_Example-acknowledgements.markdown │ │ │ ├── Pods-OBD2-Swift_Example-acknowledgements.plist │ │ │ ├── Pods-OBD2-Swift_Example-frameworks.sh │ │ │ └── Pods-OBD2-Swift_Example-resources.sh │ ├── Manifest.lock │ └── Local Podspecs │ │ └── OBD2-Swift.podspec.json ├── Podfile ├── OBD2-Swift.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── OBD2-Swift-Example.xcscheme │ └── project.pbxproj ├── OBD2-Swift.xcworkspace │ └── contents.xcworkspacedata ├── Podfile.lock ├── OBD2-Swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ └── ViewController.swift └── Tests │ ├── Info.plist │ └── Tests.swift ├── .travis.yml ├── .gitignore ├── OBD2-Swift.podspec ├── LICENSE └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0 -------------------------------------------------------------------------------- /OBD2-Swift/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.swift-version.podspec: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/OBD2-Swift/OBD2-Swift.modulemap: -------------------------------------------------------------------------------- 1 | framework module OBD2_Swift { 2 | umbrella header "OBD2-Swift-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/OBD2-Swift/OBD2-Swift-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_OBD2_Swift : NSObject 3 | @end 4 | @implementation PodsDummy_OBD2_Swift 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'OBD2-Swift_Example' do 4 | pod 'OBD2-Swift', :path => '../' 5 | 6 | target 'OBD2-Swift_Tests' do 7 | inherit! :search_paths 8 | 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_OBD2_Swift_Tests { 2 | umbrella header "Pods-OBD2-Swift_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_OBD2_Swift_Example { 2 | umbrella header "Pods-OBD2-Swift_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_OBD2_Swift_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_OBD2_Swift_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/OBD2-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_OBD2_Swift_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_OBD2_Swift_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/OBD2-Swift/OBD2-Swift-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/OBD2-Swift.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - OBD2-Swift (0.1.0) 3 | 4 | DEPENDENCIES: 5 | - OBD2-Swift (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | OBD2-Swift: 9 | :path: ../ 10 | 11 | SPEC CHECKSUMS: 12 | OBD2-Swift: e67d5ab722ac375e5a7555e11be5f9010d8ec4e9 13 | 14 | PODFILE CHECKSUM: 14654a87cf1d0cfa9665269d5c812a7ce0140cee 15 | 16 | COCOAPODS: 1.2.1 17 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - OBD2-Swift (0.1.0) 3 | 4 | DEPENDENCIES: 5 | - OBD2-Swift (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | OBD2-Swift: 9 | :path: ../ 10 | 11 | SPEC CHECKSUMS: 12 | OBD2-Swift: e67d5ab722ac375e5a7555e11be5f9010d8ec4e9 13 | 14 | PODFILE CHECKSUM: 14654a87cf1d0cfa9665269d5c812a7ce0140cee 15 | 16 | COCOAPODS: 1.2.1 17 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Model/ScanState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScanState.swift 3 | // OBD2Swift 4 | // 5 | // Created by Hellen Soloviy on 5/30/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ScanState { 12 | case none 13 | case openingConnection 14 | case initializing 15 | case connected 16 | } 17 | 18 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Stream/StreamError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamError.swift 3 | // OBD2Swift 4 | // 5 | // Created by Sergiy Loza on 08.06.17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum WriterError: Error { 12 | case writeError 13 | } 14 | 15 | enum StreamReaderError: Error { 16 | case readError 17 | case noBytesReaded 18 | case ELMError 19 | } 20 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/OBD2-Swift/OBD2-Swift-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double OBD2_SwiftVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char OBD2_SwiftVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_OBD2_Swift_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_OBD2_Swift_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_OBD2_Swift_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_OBD2_Swift_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -workspace Example/OBD2-Swift.xcworkspace -scheme OBD2-Swift-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/OBD2Swift.h: -------------------------------------------------------------------------------- 1 | // 2 | // OBD2Swift.h 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 25/04/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for OBD2Swift. 12 | FOUNDATION_EXPORT double OBD2SwiftVersionNumber; 13 | 14 | //! Project version string for OBD2-Swift_Lib. 15 | FOUNDATION_EXPORT const unsigned char OBD2SwiftVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/OBD2-Swift/OBD2-Swift.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift/OBD2_Swift.framework/Headers" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift/OBD2_Swift.framework/Headers" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/OBD2-Swift.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OBD2-Swift", 3 | "version": "0.1.0", 4 | "summary": "A short description of OBD2-Swift.", 5 | "description": "TODO: Add long description of the pod here.", 6 | "homepage": "https://github.com/overswift/OBD2-Swift", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "overswift": "sergiy.loza@lemberg.co.uk" 13 | }, 14 | "source": { 15 | "git": "https://github.com/overswift/OBD2-Swift.git", 16 | "tag": "0.1.0" 17 | }, 18 | "platforms": { 19 | "ios": "8.0" 20 | }, 21 | "source_files": "OBD2-Swift/Classes/**/*" 22 | } 23 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Parser/StringDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DescriptorStringRepresentation.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 31/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class StringDescriptor : DescriptorProtocol { 12 | public var response : Response 13 | 14 | public required init(describe response : Response) { 15 | self.response = response 16 | self.mode = response.mode 17 | } 18 | 19 | public var mode : Mode 20 | 21 | public func getResponse() -> String? { 22 | guard let data = response.data else {return nil} 23 | return String.init(data: data, encoding: String.Encoding.ascii) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Parser/Descriptor09.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mode09.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 02/06/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Mode09Descriptor : DescriptorProtocol { 12 | public var response : Response 13 | 14 | public required init(describe response : Response) { 15 | self.response = response 16 | self.mode = response.mode 17 | } 18 | 19 | public var mode : Mode 20 | 21 | public func VIN() -> String? { 22 | guard var data = response.data else {return nil} 23 | //remove \u{01} 24 | data.removeFirst() 25 | return String.init(data: data, encoding: String.Encoding.ascii) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | # Pods/ 34 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift/OBD2_Swift.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "OBD2_Swift" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/OBD2-Swift/OBD2_Swift.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "OBD2_Swift" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Model/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 5/25/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Response : Hashable, Equatable { 12 | var timestamp : Date 13 | var mode : Mode = .none 14 | var pid : UInt8 = 0 15 | var data : Data? 16 | var rawData : [UInt8] = [] 17 | 18 | public var strigDescriptor : String? 19 | 20 | init() { 21 | self.timestamp = Date() 22 | } 23 | 24 | public var hashValue: Int { 25 | return Int(mode.rawValue ^ pid) 26 | } 27 | 28 | public static func ==(lhs: Response, rhs: Response) -> Bool { 29 | return false 30 | } 31 | 32 | public var hasData : Bool { 33 | return data == nil 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example/OBD2-Swift/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Command/CommandMode03.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommandMode03.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 07/06/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Command { 12 | 13 | public enum Mode03 : CommandType { 14 | 15 | public typealias Descriptor = Mode03Descriptor 16 | 17 | public var hashValue: Int { 18 | return Int(mode.rawValue) ^ 0 19 | } 20 | 21 | public static func == (lhs: Mode03, rhs: Mode03) -> Bool { 22 | return lhs.hashValue == rhs.hashValue 23 | } 24 | 25 | case troubleCode 26 | 27 | public var mode : Mode { 28 | return .DiagnosticTroubleCodes03 29 | } 30 | 31 | public var dataRequest : DataRequest { 32 | return DataRequest(from: "03") 33 | } 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import OBD2-Swift 4 | 5 | class Tests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | XCTAssert(true, "Pass") 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure() { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/OBD2-Swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /OBD2-Swift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint OBD2-Swift.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'OBD2-Swift' 11 | s.module_name = 'OBD2' 12 | s.version = '0.2.0' 13 | s.summary = 'Library which is manage connection to OBD2 and allow to observe obd data' 14 | s.homepage = 'https://github.com/lemberg/obd2-swift-lib' 15 | s.license = { :type => 'MIT', :file => 'LICENSE' } 16 | s.author = { 'overswift' => 'sergiy.loza@lemberg.co.uk' } 17 | s.source = { :git => 'https://github.com/lemberg/obd2-swift-lib.git', :tag => s.version } 18 | 19 | s.ios.deployment_target = '9.3' 20 | 21 | s.source_files = 'OBD2-Swift/Classes/**/*' 22 | 23 | end 24 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Command/CommandMode01.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommandMode01.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 07/06/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Command { 12 | 13 | public enum Mode01 : CommandType { 14 | 15 | public typealias Descriptor = Mode01Descriptor 16 | 17 | public var hashValue: Int { 18 | switch self { 19 | case .pid(number : let pid): 20 | return Int(mode.rawValue) ^ pid 21 | } 22 | } 23 | 24 | public static func ==(lhs: Mode01, rhs: Mode01) -> Bool { 25 | return lhs.hashValue == rhs.hashValue 26 | } 27 | 28 | case pid(number : Int) 29 | 30 | public var mode : Mode { 31 | return .CurrentData01 32 | } 33 | 34 | public var dataRequest : DataRequest { 35 | switch self { 36 | case .pid(number: let pid): 37 | return DataRequest(mode: mode, pid: UInt8(pid)) 38 | } 39 | } 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Command/CommandMode02.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommandMode02.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 07/06/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Command { 12 | 13 | public enum Mode02 : CommandType { 14 | 15 | public typealias Descriptor = Mode01Descriptor 16 | 17 | public var hashValue: Int { 18 | switch self { 19 | case .pid(number : let pid): 20 | return Int(mode.rawValue) ^ pid 21 | } 22 | } 23 | 24 | public static func ==(lhs: Mode02, rhs: Mode02) -> Bool { 25 | return lhs.hashValue == rhs.hashValue 26 | } 27 | 28 | case pid(number : Int) 29 | 30 | public var mode : Mode { 31 | return .FreezeFrame02 32 | } 33 | 34 | public var dataRequest : DataRequest { 35 | switch self { 36 | case .pid(number: let pid): 37 | return DataRequest(mode: mode, pid: UInt8(pid)) 38 | } 39 | } 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Lemberg Solutions 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Example/OBD2-Swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Parser/DescriptorProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DescriptorProtocol.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 5/25/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol DescriptorProtocol { 12 | 13 | var response : Response {get set} 14 | var mode : Mode {get set} 15 | var hasData : Bool {get} 16 | init(describe response : Response) 17 | } 18 | 19 | extension DescriptorProtocol { 20 | 21 | public var hasData : Bool { 22 | return response.hasData 23 | } 24 | } 25 | 26 | protocol PidDataHandler { 27 | 28 | associatedtype DataType 29 | var data:Data! { get } 30 | func handle() -> DataType 31 | } 32 | 33 | class FuelSystemStatus: PidDataHandler { 34 | 35 | typealias DataType = String 36 | var data:Data! 37 | 38 | func handle() -> String { 39 | var rvString : String = "" 40 | let dataA = data[0] 41 | 42 | switch dataA { 43 | case 0x01: 44 | rvString = "Open Loop" 45 | break 46 | case 0x02: 47 | rvString = "Closed Loop" 48 | break 49 | case 0x04: 50 | rvString = "OL-Drive" 51 | break; 52 | case 0x08: 53 | rvString = "OL-Fault" 54 | break 55 | case 0x10: 56 | rvString = "CL-Fault" 57 | break 58 | default: 59 | break 60 | } 61 | return rvString 62 | } 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## OBD2-Swift 5 | 6 | Copyright (c) 2017 overswift 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Command/CommandMode09.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mode09.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 02/06/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public extension Command { 13 | public enum Mode09 : CommandType { 14 | 15 | public typealias Descriptor = Mode09Descriptor 16 | 17 | case vin //Vehicle Identification Number 18 | case calibrationID //Only for ISO 9141-2, ISO 14230-4 and SAE J1850. 19 | case cvn //Calibration verification numbers 20 | case performanceTrackingMessage 21 | case ECUname 22 | 23 | public static func == (lhs: Mode09, rhs: Mode09) -> Bool { 24 | return lhs.hashValue == rhs.hashValue 25 | } 26 | 27 | public var hashValue: Int { 28 | return Int(mode.rawValue ^ pid) 29 | } 30 | 31 | public var mode : Mode { 32 | return .RequestVehicleInfo09 33 | } 34 | 35 | var pid : UInt8 { 36 | switch self { 37 | case .vin: 38 | return 0x02 39 | case .calibrationID: 40 | return 0x04 41 | case .cvn: 42 | return 0x06 43 | case .performanceTrackingMessage: 44 | return 0x07 45 | case .ECUname: 46 | return 0x0A 47 | } 48 | } 49 | 50 | public var dataRequest : DataRequest { 51 | return DataRequest(mode: mode, pid: pid) 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Command/DataRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Command.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 25/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class DataRequest { 12 | 13 | var description:String 14 | 15 | init(from string: String) { 16 | self.description = string 17 | } 18 | 19 | convenience init(mode : Mode, pid : UInt8, param : String? = nil) { 20 | var description = "" 21 | 22 | if pid >= 0x00 && pid <= 0x4E { 23 | description = NSString.init(format: "%02lx %02lx", mode.rawValue, pid) as String 24 | 25 | //Additional for freeze frame request 26 | // 020C00 instead of 020C 27 | if mode == .FreezeFrame02 { 28 | description += "00" 29 | } 30 | }else { 31 | description = NSString.init(format: "%02lx", mode.rawValue) as String 32 | } 33 | 34 | if let param = param { 35 | description += (" " + param) 36 | } 37 | 38 | self.init(from: description) 39 | } 40 | 41 | lazy var data: Data? = { 42 | let dataDescription = "\(self.description)\(kCarriageReturn)" 43 | return dataDescription.data(using: .ascii) 44 | }() 45 | } 46 | 47 | extension DataRequest: Equatable { 48 | 49 | public static func ==(lhs: DataRequest, rhs: DataRequest) -> Bool { 50 | return lhs.description == rhs.description 51 | } 52 | } 53 | 54 | extension DataRequest: Hashable { 55 | 56 | public var hashValue: Int { 57 | return description.hash 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Common/Macros.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Macros.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 08/06/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //------------------------------------------------------------------------------ 12 | // Macros 13 | // Macro to test if a given PID, when decoded, is an 14 | // alphanumeric string instead of a numeric value 15 | func IS_ALPHA_VALUE(pid : UInt8) -> Bool{ 16 | return (pid == 0x03 || pid == 0x12 || pid == 0x13 || pid == 0x1C || pid == 0x1D || pid == 0x1E) 17 | } 18 | 19 | // Macro to test if a given PID has two measurements in the returned data 20 | func IS_MULTI_VALUE_SENSOR(pid : UInt8) -> Bool { 21 | return (pid >= 0x14 && pid <= 0x1B) || 22 | (pid >= 0x24 && pid <= 0x2B) || 23 | (pid >= 0x34 && pid <= 0x3B) 24 | } 25 | 26 | 27 | func IS_INT_VALUE(pid : Int8, sensor : OBD2Sensor) -> Bool { 28 | return (pid >= 0x04 && pid <= 0x13) || 29 | (pid >= 0x1F && pid <= 0x23) || 30 | (pid >= 0x2C && pid <= 0x33) || 31 | (pid >= 0x3C && pid <= 0x3F) || 32 | (pid >= 0x43 && pid <= 0x4E) || 33 | (pid >= 0x14 && pid <= 0x1B && sensor.rawValue == 0x02) || 34 | (pid >= 0x24 && pid <= 0x2B && sensor.rawValue == 0x02) || 35 | (pid >= 0x34 && pid <= 0x3B && sensor.rawValue == 0x02) 36 | } 37 | 38 | func MORE_PIDS_SUPPORTED(_ data : [UInt8]) -> Bool { 39 | guard data.count > 3 else {return false} 40 | return ((data[3] & 1) != 0) 41 | } 42 | 43 | func NOT_SEARCH_PID(_ pid : Int) -> Bool { 44 | return (pid != 0x00 && pid != 0x20 && 45 | pid != 0x40 && pid != 0x60 && 46 | pid != 0x80 && pid != 0xA0 && 47 | pid != 0xC0 && pid != 0xE0) 48 | } 49 | 50 | 51 | let kCarriageReturn = "\r" 52 | 53 | let DTC_SYSTEM_MASK : UInt8 = 0xC0 54 | let DTC_DIGIT_0_1_MASK : UInt8 = 0x3F 55 | let DTC_DIGIT_2_3_MASK : UInt8 = 0xFF 56 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Operations/StreamHandleOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamHandleOperation.swift 3 | // OBD2Swift 4 | // 5 | // Created by Sergiy Loza on 30.05.17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class StreamHandleOperation: Operation, StreamDelegate { 12 | 13 | private(set) var input:InputStream 14 | private(set) var output:OutputStream 15 | 16 | var error:Error? { 17 | didSet { 18 | input.remove(from: .current, forMode: .defaultRunLoopMode) 19 | output.remove(from: .current, forMode: .defaultRunLoopMode) 20 | } 21 | } 22 | 23 | init(inputStream: InputStream, outputStream: OutputStream) { 24 | self.input = inputStream 25 | self.output = outputStream 26 | super.init() 27 | } 28 | 29 | override func main() { 30 | super.main() 31 | 32 | if isCancelled { 33 | return 34 | } 35 | 36 | self.input.delegate = self 37 | self.output.delegate = self 38 | 39 | input.schedule(in: .current, forMode: .defaultRunLoopMode) 40 | output.schedule(in: .current, forMode: .defaultRunLoopMode) 41 | execute() 42 | RunLoop.current.run() 43 | } 44 | 45 | func execute() { 46 | 47 | } 48 | 49 | func stream(_ aStream: Stream, handle eventCode: Stream.Event) { 50 | if aStream == input { 51 | inputStremEvent(event: eventCode) 52 | } else if aStream == output { 53 | outputStremEvent(event: eventCode) 54 | } 55 | if eventCode == .errorOccurred { 56 | self.error = aStream.streamError 57 | } 58 | } 59 | 60 | func inputStremEvent(event: Stream.Event) { 61 | 62 | } 63 | 64 | func outputStremEvent(event: Stream.Event) { 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Model/Package.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Package.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 25/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Package { 12 | 13 | var buffer: [UInt8] 14 | var length: Int 15 | 16 | init(buffer : [UInt8], length : Int) { 17 | self.buffer = buffer 18 | self.length = length 19 | } 20 | 21 | /* 22 | const char* test = "41 00 90 18 80 00 \r41 00 BF 9F F9 91 "; 23 | return [NSString stringWithCString:test encoding:NSASCIIStringEncoding]; 24 | */ 25 | var asciistr: [Int8] { 26 | return ascii() 27 | } 28 | 29 | var strigDescriptor: String { 30 | return String(cString: asciistr, encoding: String.Encoding.ascii) ?? "" 31 | } 32 | 33 | private func ascii() -> [Int8] { 34 | return buffer.map({Int8.init(bitPattern: $0)}) 35 | } 36 | 37 | var isOK: Bool { 38 | return Parser.string.isOK(strigDescriptor) 39 | } 40 | 41 | var isError: Bool { 42 | return Parser.string.isError(strigDescriptor) 43 | } 44 | 45 | var isStopped: Bool { 46 | return Parser.string.isStopped(strigDescriptor) 47 | } 48 | 49 | var isNoData: Bool { 50 | return Parser.string.isNoData(strigDescriptor) 51 | } 52 | 53 | var isSearching: Bool { 54 | return Parser.string.isSerching(strigDescriptor) 55 | } 56 | 57 | func isAuto(_ str : String) -> Bool { 58 | return Parser.string.isAuto(strigDescriptor) 59 | } 60 | 61 | var isData: Bool { 62 | return Parser.string.isDataResponse(strigDescriptor) 63 | } 64 | 65 | var isAT: Bool { 66 | return Parser.string.isATResponse(asciistr) 67 | } 68 | 69 | func isComplete() -> Bool { 70 | return Parser.string.isReadComplete(buffer) 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Stream/StreamWriter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamWriter.swift 3 | // OBD2Swift 4 | // 5 | // Created by Sergiy Loza on 30.05.17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class StreamWriter { 12 | 13 | private(set) var stream:OutputStream 14 | private(set) var data:Data 15 | 16 | init(stream: OutputStream, data: Data) { 17 | self.stream = stream 18 | self.data = data 19 | } 20 | 21 | func write() throws { 22 | print("Write to OBD \(String(describing: String(data: data, encoding: .ascii)))") 23 | 24 | while data.count > 0 { 25 | let bytesWritten = write(data: data) 26 | 27 | if bytesWritten == -1 { 28 | print("Write Error") 29 | throw WriterError.writeError 30 | } else if bytesWritten > 0 && data.count > 0 { 31 | print("Wrote \(bytesWritten) bytes") 32 | data.removeSubrange(0.. Int { 38 | var bytesRemaining = data.count 39 | var totalBytesWritten = 0 40 | 41 | while bytesRemaining > 0 { 42 | let bytesWritten = data.withUnsafeBytes { 43 | stream.write( 44 | $0.advanced(by: totalBytesWritten), 45 | maxLength: bytesRemaining 46 | ) 47 | } 48 | if bytesWritten < 0 { 49 | print("Can not OutputStream.write() \(stream.streamError?.localizedDescription ?? "")") 50 | return -1 51 | } else if bytesWritten == 0 { 52 | print("OutputStream.write() returned 0") 53 | return totalBytesWritten 54 | } 55 | 56 | bytesRemaining -= bytesWritten 57 | totalBytesWritten += bytesWritten 58 | } 59 | 60 | return totalBytesWritten 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Classes/SensorDescriptor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorDescriptor.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 27/04/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //------------------------------------------------------------------------------ 12 | // Sensor Descriptor Structures 13 | 14 | /* A structure to house a description of the type and range of a given sensor */ 15 | 16 | /* 17 | Some PIDs will return two measurements, thus we must take this into 18 | consideration as we build our decoding table. 19 | */ 20 | 21 | struct SensorDescriptor { 22 | init( _ pid : Int8, 23 | _ description : String, 24 | _ shortDescription : String, 25 | _ metricUnit : String, 26 | _ minMetricValue : Int, 27 | _ maxMetricValue : Int, 28 | _ imperialUnit : String, 29 | _ minImperialValue : Int, 30 | _ maxImperialValue : Int, 31 | _ calcFunction : ((Data)->(Float))?, 32 | _ convertFunction : ((Float)->(Float))?) { 33 | 34 | self.pid = pid 35 | self.description = description 36 | self.shortDescription = shortDescription 37 | self.metricUnit = metricUnit 38 | self.minMetricValue = minMetricValue 39 | self.maxMetricValue = maxMetricValue 40 | self.imperialUnit = imperialUnit 41 | self.minImperialValue = minImperialValue 42 | self.maxImperialValue = maxImperialValue 43 | self.calcFunction = calcFunction 44 | self.convertFunction = convertFunction 45 | } 46 | 47 | var pid : Int8 48 | var description : String 49 | var shortDescription : String 50 | var metricUnit : String 51 | var minMetricValue : Int 52 | var maxMetricValue : Int 53 | var imperialUnit : String 54 | var minImperialValue : Int 55 | var maxImperialValue : Int 56 | /* A function pointer definition for calculation functions */ 57 | var calcFunction : ((Data)->(Float))? 58 | /* A function pointer definition for conversion functions */ 59 | var convertFunction : ((Float)->(Float))? 60 | } 61 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Parser/Descriptor03.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Descriptor03.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 5/25/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Mode03Descriptor : DescriptorProtocol { 12 | public var response : Response 13 | 14 | public required init(describe response : Response) { 15 | self.response = response 16 | self.mode = response.mode 17 | } 18 | 19 | public var mode : Mode 20 | 21 | private var pid: UInt8 { 22 | return response.pid 23 | } 24 | 25 | public func getTroubleCodes() -> [String] { 26 | guard let data = response.data , data.count >= 2 else { 27 | // data length must be a multiple of 2 28 | // each DTC is encoded in 2 bytes of data 29 | print("data \(String(describing: response.data)) is NULL or dataLength is not a multiple of 2 \(response.data?.count ?? 0)") 30 | return [] 31 | } 32 | 33 | let systemCode: [Character] = [ "P", "C", "B", "U" ] 34 | 35 | var codes = [String]() 36 | 37 | for i in 0..> 6) 39 | 40 | //Out of range 41 | guard codeIndex <= 4 else {break} 42 | 43 | let c1 = systemCode[codeIndex] 44 | let a2 = Int(data[i] & DTC_DIGIT_0_1_MASK) 45 | let a3 = Int(data[i+1] & DTC_DIGIT_2_3_MASK) 46 | 47 | let c2 = String(format: "%02d", a2) 48 | let c3 = String(format: "%02d", a3) 49 | 50 | let code = "\(c1)\(c2)\(c3)" 51 | 52 | codes.append(code) 53 | 54 | if (data.count - (i+2)) < 2 && 55 | (data.count - (i+2)) % 2 != 0 { 56 | break 57 | } 58 | } 59 | 60 | return codes 61 | } 62 | 63 | public func isMILActive() -> Bool { 64 | return calcMILActive(data: response.rawData) 65 | } 66 | 67 | public func troubleCodeCount() -> Int { 68 | // 43 06 01 00 02 00 69 | // [..] - 06 is a dct count. 70 | //Second byte of DCT response is a DTC count. Like a pid in other modes. 71 | return Int(self.pid) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Operations/OpenOBDConnectionOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenOBDConnectionOperation.swift 3 | // OBD2Swift 4 | // 5 | // Created by Sergiy Loza on 30.05.17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class OpenOBDConnectionOperation: StreamHandleOperation { 12 | 13 | class func keyPathsForValuesAffectingIsFinished() -> Set { 14 | return ["inputOpen" as NSObject, "outOpen" as NSObject, "error" as NSObject] 15 | } 16 | 17 | class func keyPathsForValuesAffectingIsExecuting() -> Set { 18 | return ["inputOpen" as NSObject, "outOpen" as NSObject, "error" as NSObject] 19 | } 20 | 21 | private var inputOpen = false { 22 | didSet { 23 | if inputOpen { 24 | print("Input stream opened") 25 | input.remove(from: .current, forMode: .defaultRunLoopMode) 26 | } 27 | } 28 | } 29 | 30 | private var outOpen = false { 31 | didSet { 32 | if outOpen { 33 | print("Output stream opened") 34 | output.remove(from: .current, forMode: .defaultRunLoopMode) 35 | } 36 | } 37 | } 38 | 39 | override var isExecuting: Bool { 40 | let value = !(inputOpen && outOpen) && error == nil 41 | print("isExecuting \(value)") 42 | return value 43 | } 44 | 45 | override var isFinished: Bool { 46 | let value = (inputOpen && outOpen) || error != nil 47 | print("isFinished \(value)") 48 | return value 49 | } 50 | 51 | override func execute() { 52 | input.open() 53 | output.open() 54 | } 55 | 56 | override func inputStremEvent(event: Stream.Event) { 57 | if event == .openCompleted { 58 | inputOpen = true 59 | } else if event == .errorOccurred { 60 | print("Stream open error") 61 | self.error = input.streamError 62 | } 63 | } 64 | 65 | override func outputStremEvent(event: Stream.Event) { 66 | if event == .openCompleted { 67 | outOpen = true 68 | } else if event == .errorOccurred { 69 | print("Stream open error") 70 | self.error = output.streamError 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Example/OBD2-Swift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // OBD2-Swift 4 | // 5 | // Created by overswift on 06/21/2017. 6 | // Copyright (c) 2017 overswift. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Model/Mode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mode.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 5/25/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Mode: RawRepresentable { 12 | 13 | public typealias RawValue = UInt8 14 | 15 | case none 16 | case CurrentData01 17 | case FreezeFrame02 18 | case DiagnosticTroubleCodes03 19 | case ResetTroubleCodes04 20 | case OxygenSensorMonitoringTestResults05 //CAN ONLY 21 | case RequestOnboardMonitoringTestResultsForSMS06 //CAN ONLY 22 | case DiagnosticTroubleCodesDetected07 23 | case ControlOfOnboardComponent08 24 | case RequestVehicleInfo09 25 | 26 | public init?(rawValue: RawValue) { 27 | switch rawValue { 28 | case 0x01: 29 | self = .CurrentData01 30 | case 0x02: 31 | self = .FreezeFrame02 32 | case 0x03: 33 | self = .DiagnosticTroubleCodes03 34 | case 0x04: 35 | self = .ResetTroubleCodes04 36 | case 0x05: //CAN ONLY 37 | self = .OxygenSensorMonitoringTestResults05 38 | case 0x06: //CAN ONLY 39 | self = .RequestOnboardMonitoringTestResultsForSMS06 40 | case 0x07: 41 | self = .DiagnosticTroubleCodesDetected07 42 | case 0x08: 43 | self = .ControlOfOnboardComponent08 44 | case 0x09: 45 | self = .RequestVehicleInfo09 46 | default: 47 | self = .none 48 | } 49 | } 50 | 51 | public var rawValue: UInt8 { 52 | switch self { 53 | case .CurrentData01: 54 | return 0x01 55 | case .FreezeFrame02: 56 | return 0x02 57 | case .DiagnosticTroubleCodes03: 58 | return 0x03 59 | case .ResetTroubleCodes04: 60 | return 0x04 61 | case .OxygenSensorMonitoringTestResults05: 62 | return 0x05 63 | case .RequestOnboardMonitoringTestResultsForSMS06: 64 | return 0x06 65 | case .DiagnosticTroubleCodesDetected07: 66 | return 0x07 67 | case .ControlOfOnboardComponent08: 68 | return 0x08 69 | case .RequestVehicleInfo09: 70 | return 0x09 71 | case .none: 72 | return 0x00 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Operations/CommandOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommandOperation.swift 3 | // OBD2Swift 4 | // 5 | // Created by Sergiy Loza on 01.06.17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class CommandOperation: StreamHandleOperation { 12 | 13 | class func keyPathsForValuesAffectingIsFinished() -> Set { 14 | return ["readCompleted" as NSObject, "error" as NSObject] 15 | } 16 | 17 | private(set) var command:DataRequest 18 | private(set) var reader: StreamReader 19 | private var readCompleted = false { 20 | didSet { 21 | self.input.remove(from: .current, forMode: .defaultRunLoopMode) 22 | self.output.remove(from: .current, forMode: .defaultRunLoopMode) 23 | } 24 | } 25 | 26 | var onReceiveResponse:((_ response:Response) -> ())? 27 | 28 | init(inputStream: InputStream, outputStream: OutputStream, command: DataRequest) { 29 | self.command = command 30 | self.reader = StreamReader(stream: inputStream) 31 | super.init(inputStream: inputStream, outputStream: outputStream) 32 | } 33 | 34 | override var isFinished: Bool { 35 | if error != nil { 36 | return true 37 | } 38 | return readCompleted 39 | } 40 | 41 | override func execute() { 42 | guard let data = command.data else { return } 43 | let writer = StreamWriter(stream: output, data: data) 44 | do { 45 | try writer.write() 46 | } catch let error { 47 | print("Error \(error) on data writing") 48 | self.error = InitializationError.DataWriteError 49 | } 50 | } 51 | 52 | override func inputStremEvent(event: Stream.Event) { 53 | if event == .hasBytesAvailable { 54 | do { 55 | if try reader.read() { 56 | onReadEnd() 57 | } 58 | } catch let error { 59 | self.error = error 60 | } 61 | } 62 | } 63 | 64 | private func onReadEnd() { 65 | let package = Package(buffer: reader.readBuffer, length: reader.readBufferLenght) 66 | let response = Parser.package.read(package: package) 67 | onReceiveResponse?(response) 68 | readCompleted = true 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2017 overswift <sergiy.loza@lemberg.co.uk> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | OBD2-Swift 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Observer/SensorObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Observer.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 24/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public class ObserverType : NSObject {} 13 | 14 | // To bring Observer alive you must register it in ObserverQueue 15 | // unregister func deactivates observer 16 | 17 | public class Observer : ObserverType { 18 | private typealias DescriptorCallBack = (_ descriptor : T.Descriptor?)->() 19 | private typealias DescriptorArray = [(DescriptorCallBack)?] 20 | 21 | private var observers : [Int : DescriptorArray] = [:] 22 | 23 | public func observe(command : T, block : @escaping (_ descriptor : T.Descriptor?)->()){ 24 | let key = command.hashValue 25 | let array = observers[key] ?? [] 26 | let flatAray = array.flatMap({$0}) 27 | observers[key] = flatAray 28 | observers[key]?.append(block) 29 | } 30 | 31 | func dispatch(command : T, response : Response){ 32 | let described = T.Descriptor(describe: response) 33 | 34 | guard let callbackArray = observers[response.hashValue] else {return} 35 | 36 | for callback in callbackArray { 37 | callback?(described) 38 | } 39 | } 40 | 41 | func removeAll(){ 42 | observers.removeAll() 43 | } 44 | } 45 | 46 | public class ObserverQueue { 47 | 48 | public static let shared = ObserverQueue() 49 | 50 | private let observingQueue: OperationQueue = { 51 | let queue = OperationQueue() 52 | queue.maxConcurrentOperationCount = 1 53 | queue.name = "com.obd2.observers" 54 | return queue 55 | }() 56 | 57 | private init(){} 58 | 59 | private var observers = Set() 60 | 61 | open func register(observer : ObserverType){ 62 | observers.insert(observer) 63 | } 64 | 65 | open func unregister(observer : ObserverType){ 66 | observers.remove(observer) 67 | } 68 | 69 | func dispatch(command : T, response : Response) { 70 | observingQueue.addOperation { 71 | self.observers.forEach { 72 | if let obs = $0 as? Observer { 73 | obs.dispatch(command: command, response: response) 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Classes/ProtocolModelDescription.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseType.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 26/04/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | ///------------------------------------------- 13 | 14 | protocol NonCanResponseProtocol { 15 | var priority : UInt8 {get set} 16 | var targetAddress : UInt8 {get set} 17 | var ecuAddress : UInt8 {get set} 18 | var mode : UInt8 {get set} 19 | var pid : UInt8 {get set} 20 | var dataBytes : [UInt8] {get set} 21 | } 22 | 23 | struct J1850PWMResponse : NonCanResponseProtocol { 24 | var priority : UInt8 25 | var targetAddress : UInt8 26 | var ecuAddress : UInt8 27 | var mode : UInt8 28 | var pid : UInt8 29 | var dataBytes : [UInt8] 30 | } 31 | 32 | 33 | struct J1850VPWResponse : NonCanResponseProtocol { 34 | var priority : UInt8 35 | var targetAddress : UInt8 36 | var ecuAddress : UInt8 37 | var mode : UInt8 38 | var pid : UInt8 39 | var dataBytes : [UInt8] 40 | } 41 | 42 | 43 | struct KWP2000Response : NonCanResponseProtocol { 44 | var priority : UInt8 45 | var targetAddress : UInt8 46 | var ecuAddress : UInt8 47 | var mode : UInt8 48 | var pid : UInt8 49 | var dataBytes : [UInt8] 50 | } 51 | 52 | struct ISO9141Response : NonCanResponseProtocol { 53 | var priority : UInt8 54 | var targetAddress : UInt8 55 | var ecuAddress : UInt8 56 | var mode : UInt8 57 | var pid : UInt8 58 | var dataBytes : [UInt8] 59 | } 60 | 61 | 62 | ///------------------------------------------- 63 | 64 | protocol Can11bitResponseProtocol { 65 | var header1 : UInt8 {get set} 66 | var header2 : UInt8 {get set} 67 | var PCI : UInt8 {get set} 68 | var mode : UInt8 {get set} 69 | var dataBytes : [UInt8] {get set} 70 | } 71 | 72 | struct CAN11bitResponse : Can11bitResponseProtocol { 73 | var header1 : UInt8 74 | var header2 : UInt8 75 | var PCI : UInt8 76 | var mode : UInt8 77 | var dataBytes : [UInt8] 78 | } 79 | 80 | ///------------------------------------------- 81 | 82 | protocol Can29BitResponseProtocol : Can11bitResponseProtocol { 83 | var destinationAddress : UInt8 {get set} 84 | var sourceAddress : UInt8 {get set} 85 | var pid : UInt8 {get set} 86 | } 87 | 88 | struct CAN29BitResponse : Can29BitResponseProtocol { 89 | var header1 : UInt8 90 | var header2 : UInt8 91 | var PCI : UInt8 92 | var mode : UInt8 93 | var dataBytes : [UInt8] 94 | var destinationAddress : UInt8 95 | var sourceAddress : UInt8 96 | var pid : UInt8 97 | } 98 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Model/ScanProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScanProtocol.swift 3 | // OBD2Swift 4 | // 5 | // Created by Hellen Soloviy on 5/30/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let elmProtocolMap: [ScanProtocol] = [ 12 | .none, 13 | .J1850PWM, 14 | .J1850VPW, 15 | .ISO9141Keywords0808, 16 | .KWP2000SlowInit, 17 | .KWP2000FastInit, 18 | .CAN11bit500KB, 19 | .CAN29bit500KB, 20 | .CAN11bit250KB, 21 | .CAN29bit250KB, 22 | .CAN29bit250KB, 23 | .none, 24 | .none 25 | ] 26 | 27 | enum ScanProtocol: RawRepresentable { 28 | 29 | typealias RawValue = UInt16 30 | 31 | case none //= 0x0000 32 | case ISO9141Keywords0808 //= 0x0001 33 | case ISO9141Keywords9494 //= 0x0002 34 | case KWP2000FastInit //= 0x0004 35 | case KWP2000SlowInit //= 0x0008 36 | case J1850PWM //= 0x0010 37 | case J1850VPW //= 0x0020 38 | case CAN11bit250KB //= 0x0040 39 | case CAN11bit500KB //= 0x0080 40 | case CAN29bit250KB //= 0x0100 41 | case CAN29bit500KB //= 0x0200 42 | 43 | public init?(rawValue: RawValue) { 44 | switch rawValue { 45 | case 0x0001: 46 | self = .ISO9141Keywords0808 47 | case 0x0002: 48 | self = .ISO9141Keywords9494 49 | case 0x0004: 50 | self = .KWP2000FastInit 51 | case 0x0008: 52 | self = .KWP2000SlowInit 53 | case 0x0010: 54 | self = .J1850PWM 55 | case 0x0020: 56 | self = .J1850VPW 57 | case 0x0040: 58 | self = .CAN11bit250KB 59 | case 0x0080: 60 | self = .CAN11bit500KB 61 | case 0x0100: 62 | self = .CAN29bit250KB 63 | case 0x0200: 64 | self = .CAN29bit500KB 65 | default: 66 | self = .none 67 | } 68 | } 69 | 70 | var rawValue: UInt16 { 71 | switch self { 72 | case .ISO9141Keywords0808: 73 | return 0x0001 74 | case .ISO9141Keywords9494: 75 | return 0x0002 76 | case .KWP2000FastInit: 77 | return 0x0004 78 | case .KWP2000SlowInit: 79 | return 0x0008 80 | case .J1850PWM: 81 | return 0x0010 82 | case .J1850VPW: 83 | return 0x0020 84 | case .CAN11bit250KB: 85 | return 0x0040 86 | case .CAN11bit500KB: 87 | return 0x0080 88 | case .CAN29bit250KB: 89 | return 0x0100 90 | case .CAN29bit500KB: 91 | return 0x0200 92 | case .none: 93 | return 0x0000 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Stream/StreamReader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamReader.swift 3 | // OBD2Swift 4 | // 5 | // Created by Sergiy Loza on 31.05.17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | typealias BufferType = UInt8 12 | 13 | extension Array where Element == BufferType { 14 | 15 | func elmReadComplete() -> Bool { 16 | return last == Parser.string.kResponseFinishedCode 17 | } 18 | } 19 | 20 | extension String { 21 | 22 | var elmOK: Bool { 23 | return contains("OK") 24 | } 25 | 26 | var elmError: Bool { 27 | return contains("?") 28 | } 29 | 30 | var elmNoData: Bool { 31 | return contains("NO DATA") 32 | } 33 | 34 | var elmSearching: Bool { 35 | return contains("SEARCHING...") 36 | } 37 | } 38 | 39 | class StreamReader { 40 | 41 | private let bufferSize = 512 42 | 43 | private(set) var readBuffer = [BufferType]() 44 | private(set) var readBufferLenght = 0 45 | private(set) var stream:InputStream 46 | private(set) var response: String? 47 | 48 | init(stream: InputStream) { 49 | self.stream = stream 50 | } 51 | 52 | func read() throws -> Bool { 53 | 54 | var buffer = [UInt8].init(repeating: 0, count: bufferSize) 55 | let readLength = stream.read(&buffer, maxLength: bufferSize) 56 | print("Read \(readLength) bytes") 57 | 58 | guard readLength > 0 else { 59 | throw StreamReaderError.noBytesReaded 60 | } 61 | 62 | buffer.removeSubrange(readLength.. 0 && (readBufferLenght - 3) < readBuffer.count { 70 | readBuffer[(readBufferLenght - 3)] = 0x00 71 | readBufferLenght -= 3 72 | } 73 | 74 | let asciistr : [Int8] = readBuffer.map( { Int8(bitPattern: $0) } ) 75 | let respString = String(cString: asciistr, encoding: String.Encoding.ascii) ?? "" 76 | 77 | print(respString) 78 | 79 | if respString.elmError { 80 | throw StreamReaderError.ELMError 81 | } else { 82 | response = respString 83 | } 84 | 85 | return true 86 | } 87 | return false 88 | } 89 | } 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.swift 3 | // OBD2Swift 4 | // 5 | // Created by Hellen Soloviy on 5/31/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | //func print(_ string: String) { 13 | // Logger.shared.log(string) 14 | //// NSLog(string) 15 | //} 16 | 17 | enum LoggerMessageType { 18 | 19 | case debug 20 | case error 21 | case info 22 | case verbose //default 23 | case warning 24 | 25 | } 26 | 27 | 28 | enum LoggerSourceType { 29 | 30 | case console 31 | case file //default 32 | 33 | } 34 | 35 | open class Logger { 36 | 37 | static var sourceType: LoggerSourceType = .console 38 | static let queue = OperationQueue() 39 | 40 | static let filePaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("//OBD2Logger.txt") ?? "/OBD2Logger.txt" 41 | 42 | public static func warning(_ message:String) { 43 | newLog(message, type: .warning) 44 | } 45 | 46 | public static func info(_ message:String) { 47 | newLog(message, type: .info) 48 | } 49 | 50 | public static func error(_ message:String) { 51 | newLog(message, type: .error) 52 | 53 | } 54 | 55 | public static func shareFile(on viewController: UIViewController) { 56 | 57 | let activityVC = UIActivityViewController(activityItems: fileToShare(), applicationActivities: nil) 58 | viewController.present(activityVC, animated: true, completion: nil) 59 | 60 | } 61 | 62 | public static func fileToShare() -> [Any] { 63 | 64 | let comment = "Logger file" 65 | let fileURL = URL(fileURLWithPath: filePaths) 66 | return [comment, fileURL] as [Any] 67 | 68 | } 69 | 70 | 71 | public static func cleanLoggerFile() { 72 | 73 | do { 74 | try " ".write(toFile: filePaths, atomically: true, encoding: String.Encoding.utf8) 75 | } catch let error { 76 | print("Failed writing to log file: \(filePaths), Error: " + error.localizedDescription) 77 | } 78 | } 79 | 80 | 81 | private static func newLog(_ message:String, type: LoggerMessageType = .verbose) { 82 | 83 | queue.maxConcurrentOperationCount = 1 84 | queue.addOperation { 85 | 86 | let log = "[\(Date().description)] [\(type)] \(message)" 87 | 88 | var content = "" 89 | if FileManager.default.fileExists(atPath: filePaths) { 90 | content = try! String(contentsOfFile: filePaths, encoding: String.Encoding.utf8) 91 | } 92 | 93 | do { 94 | try "\(content)\n\(log)".write(toFile: filePaths, atomically: true, encoding: String.Encoding.utf8) 95 | 96 | } catch let error { 97 | print("Failed writing to log file: \(filePaths), Error: " + error.localizedDescription) 98 | } 99 | 100 | } 101 | 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /Example/OBD2-Swift/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 91 | wait 92 | fi 93 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/OBD2Facade.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OBD2Facade.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 24/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | protocol ScanDelegate { 13 | func didReceive() 14 | } 15 | 16 | open class OBD2 { 17 | 18 | public typealias CallBack = (Bool, Error?) -> () 19 | 20 | private(set) var host : String 21 | private(set) var port : Int 22 | 23 | private var scanner : Scanner 24 | 25 | public var stateChanged: StateChangeCallback? { 26 | didSet { 27 | scanner.stateChanged = stateChanged 28 | } 29 | } 30 | 31 | public convenience init(){ 32 | self.init(host : "192.168.0.10", port : 35000) 33 | } 34 | 35 | public init(host : String, port : Int){ 36 | self.host = host 37 | self.port = port 38 | 39 | self.scanner = Scanner(host: host, port: port) 40 | } 41 | 42 | var logger : Any? 43 | var cache : Any? 44 | 45 | public func connect(_ block : @escaping CallBack){ 46 | scanner.startScan { (success, error) in 47 | block(success, error) 48 | } 49 | } 50 | 51 | 52 | /// Disconnect from OBD 53 | public func disconnect() { 54 | scanner.disconnect() 55 | } 56 | 57 | 58 | /// Stop scaning but leave active connection to obd 59 | public func stopScan() { 60 | scanner.cancelScan() 61 | } 62 | 63 | 64 | /// Pause all requests to OBD 65 | open func pauseScan() { 66 | scanner.pauseScan() 67 | } 68 | 69 | /// Resume requests to OBD 70 | open func resumeScan() { 71 | scanner.resumeScan() 72 | } 73 | 74 | /// Send request to OBD once 75 | /// 76 | /// - Parameters: 77 | /// - command: command to send 78 | /// - notifyObservers: should be result will be send to command observers. Default is true 79 | /// - block: result of command execution 80 | public func request(command: T, notifyObservers: Bool = true, block: @escaping (_ descriptor: T.Descriptor?)->()){ 81 | let dataRequest = command.dataRequest 82 | 83 | scanner.request(command: dataRequest, response: { (response) in 84 | let described = T.Descriptor(describe: response) 85 | block(described) 86 | if notifyObservers { 87 | self.dispatchToObserver(command: command, with: response) 88 | } 89 | }) 90 | } 91 | 92 | 93 | /// Start send this command to OBD repetedly 94 | /// Result can be observed by registered observer 95 | /// 96 | /// - Parameter command: Command to execute 97 | public func request(repeat command: T) { 98 | let dataRequest = command.dataRequest 99 | scanner.startRepeatCommand(command: dataRequest) { (response) in 100 | self.dispatchToObserver(command: command, with: response) 101 | } 102 | } 103 | 104 | 105 | /// Stop send this command to OBD repetedly 106 | /// 107 | /// - Parameter command: Command to remover from repeat queue 108 | public func stop(repeat command: T) { 109 | let dataRequest = command.dataRequest 110 | scanner.stopRepeatCommand(command: dataRequest) 111 | } 112 | 113 | 114 | /// Check are this command alredy executing repeatedly 115 | /// 116 | /// - Parameter command: command to check 117 | /// - Returns: return true if command executing repetedly 118 | public func isRepeating(repeat command: T) -> Bool { 119 | let dataRequest = command.dataRequest 120 | return scanner.isRepeating(command: dataRequest) 121 | } 122 | 123 | private func dispatchToObserver(command : T, with response : Response){ 124 | ObserverQueue.shared.dispatch(command: command, response: response) 125 | } 126 | } 127 | 128 | 129 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/OBD2-Swift/OBD2_Swift.framework" 93 | fi 94 | if [[ "$CONFIGURATION" == "Release" ]]; then 95 | install_framework "$BUILT_PRODUCTS_DIR/OBD2-Swift/OBD2_Swift.framework" 96 | fi 97 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 98 | wait 99 | fi 100 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Model/Command.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Command.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 02/06/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public protocol CommandPrototype : Hashable { 13 | var mode : Mode {get} 14 | var dataRequest : DataRequest {get} 15 | } 16 | 17 | public protocol CommandType : CommandPrototype { 18 | associatedtype Descriptor : DescriptorProtocol 19 | } 20 | 21 | 22 | public struct Command { 23 | // This struct includes extension enum 24 | // Mode01 , Mode02 , Mode03 25 | // @see CommandMode01 - 09.swift 26 | 27 | public enum AT : CommandType { 28 | 29 | public typealias Descriptor = StringDescriptor 30 | 31 | case reset 32 | case headersOn 33 | case echoOff 34 | case voltage 35 | case `protocol` 36 | case protocolNumber 37 | case versionId 38 | case deviceDescription 39 | case readDeviceIdentifier 40 | case setDeviceIdentifier(String) 41 | 42 | public var hashValue: Int { 43 | return Int(mode.rawValue ^ mode.rawValue) 44 | } 45 | 46 | public static func == (lhs: AT, rhs: AT) -> Bool { 47 | return lhs.hashValue == rhs.hashValue 48 | } 49 | 50 | public var mode : Mode { 51 | return .none 52 | } 53 | 54 | public var dataRequest : DataRequest { 55 | switch self { 56 | case .reset: 57 | return DataRequest(from: "AT WS") 58 | case .headersOn: 59 | return DataRequest(from: "AT H1") 60 | case .echoOff: 61 | return DataRequest(from: "AT E0") 62 | case .voltage: 63 | return DataRequest(from: "AT RV") 64 | case .`protocol`: 65 | return DataRequest(from: "AT DP") 66 | case .protocolNumber: 67 | return DataRequest(from: "AT DPN") 68 | case .versionId: 69 | return DataRequest(from: "AT I") 70 | case .deviceDescription: 71 | return DataRequest(from: "AT @1") 72 | case .readDeviceIdentifier: 73 | return DataRequest(from: "AT @2") 74 | case .setDeviceIdentifier(let identifier): 75 | return DataRequest(from: "AT @2 " + identifier) 76 | } 77 | } 78 | 79 | } 80 | 81 | //TODO:- Create Descriptor for dynamic comand 82 | public enum Custom : CommandType { 83 | 84 | public typealias Descriptor = StringDescriptor 85 | 86 | case string(String) 87 | case digit(mode : Int, pid : Int) 88 | 89 | public var hashValue: Int { 90 | return Int(mode.rawValue ^ pid) 91 | } 92 | 93 | public static func == (lhs: Custom, rhs: Custom) -> Bool { 94 | return lhs.hashValue == rhs.hashValue 95 | } 96 | 97 | public var mode : Mode { 98 | switch self { 99 | case .string(let string): 100 | return encodeMode(from: string) 101 | case .digit(let mode, _): 102 | return Mode.init(rawValue: Mode.RawValue(mode)) ?? .none 103 | } 104 | } 105 | 106 | public var pid : UInt8 { 107 | switch self { 108 | case .string(let string): 109 | return encodePid(from: string) 110 | case .digit(_, let pid): 111 | return UInt8(pid) 112 | } 113 | } 114 | 115 | public var dataRequest : DataRequest { 116 | switch self { 117 | case .string(let string): 118 | let uppercasedStr = string.uppercased() 119 | return DataRequest(from: uppercasedStr) 120 | case .digit(let mode, let pid): 121 | let mode = Mode.init(rawValue: UInt8(mode)) ?? .none 122 | return DataRequest(mode: mode, pid: UInt8(pid)) 123 | } 124 | } 125 | 126 | private func encodeMode(from string : String) -> Mode { 127 | let str = string.replacingOccurrences(of: " ", with: "") 128 | let index = str.index(str.startIndex, offsetBy: 1) 129 | let modeSubStr = str.prefix(upTo: index) 130 | let modeRaw = UInt8(modeSubStr) ?? 0 131 | return Mode.init(rawValue: modeRaw) ?? .none 132 | } 133 | 134 | private func encodePid(from string : String) -> UInt8 { 135 | let str = string.replacingOccurrences(of: " ", with: "") 136 | let index = str.index(str.startIndex, offsetBy: 1) 137 | let pidSubStr = str.suffix(from: index) 138 | let pidRaw = UInt8(pidSubStr) ?? 0x00 139 | return pidRaw 140 | } 141 | 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Classes/SensorList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorList.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 27/04/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Supported OBD2 sensors list 13 | For detailed information read https://en.wikipedia.org/wiki/OBD-II_PIDs 14 | */ 15 | 16 | enum OBD2Sensor : UInt8 { 17 | case Supported01_20 = 0x00 18 | case MonitorStatusSinceDTCsCleared = 0x01 19 | case FreezeFrameStatus = 0x02 20 | case FuelSystemStatus = 0x03 21 | case CalculatedEngineLoadValue = 0x04 22 | case EngineCoolantTemperature = 0x05 23 | case ShorttermfueltrimBank1 = 0x06 24 | case LongtermfueltrimBank1 = 0x07 25 | case ShorttermfueltrimBank2 = 0x08 26 | case LongtermfueltrimBank2 = 0x09 27 | case FuelPressure = 0x0A 28 | case IntakeManifoldPressure = 0x0B 29 | case EngineRPM = 0x0C 30 | case VehicleSpeed = 0x0D 31 | case TimingAdvance = 0x0E 32 | case IntakeAirTemperature = 0x0F 33 | case MassAirFlow = 0x10 34 | case ThrottlePosition = 0x11 35 | case SecondaryAirStatus = 0x12 36 | case OxygenSensorsPresent = 0x13 37 | case OxygenVoltageBank1Sensor1 = 0x14 38 | case OxygenVoltageBank1Sensor2 = 0x15 39 | case OxygenVoltageBank1Sensor3 = 0x16 40 | case OxygenVoltageBank1Sensor4 = 0x17 41 | case OxygenVoltageBank2Sensor1 = 0x18 42 | case OxygenVoltageBank2Sensor2 = 0x19 43 | case OxygenVoltageBank2Sensor3 = 0x1A 44 | case OxygenVoltageBank2Sensor4 = 0x1B 45 | case OBDStandardsThisVehicleConforms = 0x1C 46 | case OxygenSensorsPresent2 = 0x1D 47 | case AuxiliaryInputStatus = 0x1E 48 | case RunTimeSinceEngineStart = 0x1F 49 | case PIDsSupported21_40 = 0x20 50 | case DistanceTraveledWithMalfunctionIndicatorLampOn = 0x21 51 | case FuelRailPressureManifoldVacuum = 0x22 52 | case FuelRailPressureDiesel = 0x23 53 | case EquivalenceRatioVoltageO2S1 = 0x24 54 | case EquivalenceRatioVoltageO2S2 = 0x25 55 | case EquivalenceRatioVoltageO2S3 = 0x26 56 | case EquivalenceRatioVoltageO2S4 = 0x27 57 | case EquivalenceRatioVoltageO2S5 = 0x28 58 | case EquivalenceRatioVoltageO2S6 = 0x29 59 | case EquivalenceRatioVoltageO2S7 = 0x2A 60 | case EquivalenceRatioVoltageO2S8 = 0x2B 61 | case CommandedEGR = 0x2C 62 | case EGRError = 0x2D 63 | case CommandedEvaporativePurge = 0x2E 64 | case FuelLevelInput = 0x2F 65 | case NumberofWarmUpsSinceCodesCleared = 0x30 66 | case DistanceTraveledSinceCodesCleared = 0x31 67 | case EvaporativeSystemVaporPressure = 0x32 68 | case BarometricPressure = 0x33 69 | case EquivalenceRatioCurrentO2S1 = 0x34 70 | case EquivalenceRatioCurrentO2S2 = 0x35 71 | case EquivalenceRatioCurrentO2S3 = 0x36 72 | case EquivalenceRatioCurrentO2S4 = 0x37 73 | case EquivalenceRatioCurrentO2S5 = 0x38 74 | case EquivalenceRatioCurrentO2S6 = 0x39 75 | case EquivalenceRatioCurrentO2S7 = 0x3A 76 | case EquivalenceRatioCurrentO2S8 = 0x3B 77 | case CatalystTemperatureBank1Sensor1 = 0x3C 78 | case CatalystTemperatureBank2Sensor1 = 0x3D 79 | case CatalystTemperatureBank1Sensor2 = 0x3E 80 | case CatalystTemperatureBank2Sensor2 = 0x3F 81 | case PIDsSupported41_60 = 0x40 82 | case MonitorStatusThisDriveCycle = 0x41 83 | case ControlModuleVoltage = 0x42 84 | case AbsoluteLoadValue = 0x43 85 | case CommandEquivalenceRatio = 0x44 86 | case RelativeThrottlePosition = 0x45 87 | case AmbientAirTemperature = 0x46 88 | case AbsoluteThrottlePositionB = 0x47 89 | case AbsoluteThrottlePositionC = 0x48 90 | case AcceleratorPedalPositionD = 0x49 91 | case AcceleratorPedalPositionE = 0x4A 92 | case AcceleratorPedalPositionF = 0x4B 93 | case CommandedThrottleActuator = 0x4C 94 | case TimeRunWithMILOn = 0x4D 95 | case TimeSinceTroubleCodesCleared = 0x4E 96 | 97 | // From this point sensors don't have full support yet 98 | case MaxValueForER_OSV_OSC_IMAP = 0x4F /* Maximum value for equivalence ratio, oxygen sensor voltage, 99 | oxygen sensor current and intake manifold absolute pressure 100 | */ 101 | case MaxValueForAirFlowRateFromMAFSensor = 0x50 102 | case FuelType = 0x51 103 | case EthanolFuelRatio = 0x52 104 | case AbsoluteEvapSystemVaporPressure = 0x53 105 | case EvapSystemVaporPressure = 0x54 106 | case ShortTermSecondaryOxygenSensorTrimBank_1_3 = 0x55 107 | case LongTermSecondaryOxygenSensorTrimBank_1_3 = 0x56 108 | case ShortTermSecondaryOxygenSensorTrimBank_2_4 = 0x57 109 | case LongTermSecondaryOxygenSensorTrimBank_2_4 = 0x58 110 | case FuelRailPressure_Absolute = 0x59 111 | case RelativeAcceleratorPedalPosition = 0x5A 112 | case HybridBatteryPackRemainingLife = 0x5B 113 | case EngineOilTemperature = 0x5C 114 | 115 | //OBD2SensorsVIN = 0x123, 116 | // OBD2Sensor = 0x, 117 | // Sensors should be added at this point for supporting count and last. 118 | 119 | } 120 | -------------------------------------------------------------------------------- /Example/OBD2-Swift/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // OBD2-Swift-lib-example 4 | // 5 | // Created by Max Vitruk on 25/04/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OBD2_Swift 11 | 12 | class ViewController: UIViewController { 13 | static var host = "192.168.0.10" 14 | static var port = 35000 15 | 16 | //var scanTool = ELM327(host: host , port: port) 17 | let obd = OBD2() 18 | 19 | @IBOutlet weak var dtcButton: UIButton! 20 | @IBOutlet weak var speedButton: UIButton! 21 | @IBOutlet weak var vinButton: UIButton! 22 | @IBOutlet weak var connectButton: UIButton! 23 | @IBOutlet weak var indicator: UIActivityIndicatorView! 24 | 25 | @IBOutlet weak var statusLabel: UILabel! 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | // Do any additional setup after loading the view, typically from a nib. 30 | 31 | //scanTool.sensorScanTargets = [0x0C, 0x0D] 32 | updateUI(connected: false) 33 | let observer = Observer() 34 | 35 | observer.observe(command: .pid(number: 12)) { (descriptor) in 36 | let respStr = descriptor?.shortDescription 37 | print("Observer : \(String(describing: respStr))") 38 | } 39 | 40 | ObserverQueue.shared.register(observer: observer) 41 | 42 | obd.stateChanged = { (state) in 43 | 44 | OperationQueue.main.addOperation { [weak self] in 45 | self?.onOBD(change: state) 46 | } 47 | } 48 | } 49 | 50 | func onOBD(change state:ScanState) { 51 | switch state { 52 | case .none: 53 | indicator.stopAnimating() 54 | statusLabel.text = "Not Connected" 55 | updateUI(connected: false) 56 | break 57 | case .connected: 58 | indicator.stopAnimating() 59 | statusLabel.text = "Connected" 60 | updateUI(connected: true) 61 | break 62 | case .openingConnection: 63 | connectButton.isHidden = true 64 | indicator.startAnimating() 65 | statusLabel.text = "Opening connection" 66 | break 67 | case .initializing: 68 | statusLabel.text = "Initializing" 69 | break 70 | } 71 | } 72 | 73 | func updateUI(connected: Bool) { 74 | dtcButton.isEnabled = connected 75 | speedButton.isEnabled = connected 76 | vinButton.isEnabled = connected 77 | connectButton.isHidden = connected 78 | } 79 | 80 | override func viewWillAppear(_ animated: Bool) { 81 | super.viewWillAppear(animated) 82 | } 83 | 84 | override func viewWillDisappear(_ animated: Bool) { 85 | super.viewWillDisappear(animated) 86 | } 87 | 88 | @IBAction func connect( _ sender : UIButton){ 89 | //obd.requestTroubleCodes() 90 | obd.connect { [weak self] (success, error) in 91 | OperationQueue.main.addOperation({ 92 | if let error = error { 93 | print("OBD connection failed with \(error)") 94 | self?.statusLabel.text = "Connection failed with error \(error)" 95 | self?.updateUI(connected: false) 96 | } 97 | }) 98 | } 99 | } 100 | 101 | 102 | @IBAction func requestSpeed( _ sender : UIButton) { 103 | 104 | let command = Command.Mode01.pid(number: 12) 105 | if obd.isRepeating(repeat: command) { 106 | sender.setTitle("Start repeat speed", for: .normal) 107 | obd.stop(repeat: command) 108 | } else { 109 | sender.setTitle("Stop repeat", for: .normal) 110 | obd.request(repeat: command) 111 | } 112 | } 113 | 114 | @IBAction func request( _ sender : UIButton) { 115 | //obd.requestTroubleCodes() 116 | obd.request(command: Command.Mode03.troubleCode) { (descriptor) in 117 | let respStr = descriptor?.getTroubleCodes() 118 | print(respStr ?? "No value") 119 | } 120 | } 121 | 122 | @IBAction func pause( _ sender : UIButton) { 123 | obd.pauseScan() 124 | } 125 | 126 | @IBAction func resume( _ sender : UIButton) { 127 | obd.resumeScan() 128 | } 129 | 130 | @IBAction func requestVIN( _ sender : UIButton) { 131 | //obd.requestVIN() 132 | obd.request(command: Command.Mode09.vin) { (descriptor) in 133 | let respStr = descriptor?.VIN() 134 | print(respStr ?? "No value") 135 | } 136 | 137 | obd.request(command: Command.Custom.string("0902")) { (descr) in 138 | print("Response \(String(describing: descr?.getResponse()))") 139 | } 140 | } 141 | 142 | } 143 | 144 | -------------------------------------------------------------------------------- /Example/OBD2-Swift.xcodeproj/xcshareddata/xcschemes/OBD2-Swift-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 80 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Scanner/StreamHolder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamHolder.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 25/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol StreamFlowDelegate { 12 | func didOpen(stream : Stream) 13 | func error(_ error : Error, on stream : Stream) 14 | func hasInput(on stream : Stream) 15 | } 16 | 17 | class StreamHolder: NSObject { 18 | 19 | var delegate : StreamFlowDelegate? 20 | 21 | var inputStream : InputStream! 22 | var outputStream : OutputStream! 23 | 24 | let obdQueue: OperationQueue = { 25 | let queue = OperationQueue() 26 | queue.name = "com.obd2.commands" 27 | queue.maxConcurrentOperationCount = 1 28 | return queue 29 | }() 30 | 31 | var cachedWriteData = Data() 32 | 33 | var host = "" 34 | var port = 0 35 | 36 | 37 | func createStreams() { 38 | var readStream: InputStream? 39 | var writeStream: OutputStream? 40 | Stream.getStreamsToHost(withName: host, port: port, inputStream: &readStream, outputStream: &writeStream) 41 | guard let a = readStream else { fatalError("Read stream not created") } 42 | guard let b = writeStream else { fatalError("Write stream not created") } 43 | self.inputStream = a 44 | self.outputStream = b 45 | } 46 | 47 | func close(){ 48 | self.inputStream.delegate = nil 49 | self.outputStream.delegate = nil 50 | 51 | self.inputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) 52 | self.outputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) 53 | 54 | self.inputStream.close() 55 | self.outputStream.close() 56 | } 57 | 58 | func writeCachedData() { 59 | 60 | // TODO: are we needed? 61 | // var status : Stream.Status = .error 62 | 63 | print("{ ") 64 | guard outputStream.streamStatus != .writing && inputStream.streamStatus != .writing else { 65 | print("Data is already writing..!") 66 | //TODO: test with new operation queue 67 | return 68 | } 69 | 70 | while cachedWriteData.count > 0 { 71 | let bytesWritten = write(data: cachedWriteData) 72 | print("bytesWritten = \(bytesWritten)") 73 | 74 | if bytesWritten == -1 { 75 | // ~hell 76 | print("Write Error") 77 | break 78 | } else if bytesWritten > 0 && cachedWriteData.count > 0 { 79 | print("Wrote \(bytesWritten) bytes from \(cachedWriteData.count) cashed bytes") 80 | cachedWriteData.removeSubrange(0.. Int { 94 | let bytesRemaining = data.count 95 | let totalBytesWritten = 0 96 | 97 | while bytesRemaining > 0 { 98 | let bytesWritten = data.withUnsafeBytes { 99 | outputStream.write( 100 | $0.advanced(by: totalBytesWritten), 101 | maxLength: bytesRemaining 102 | ) 103 | } 104 | if bytesWritten < 0 { 105 | print("Can not OutputStream.write() \(outputStream.streamError?.localizedDescription ?? "")") 106 | return -1 107 | } else if bytesWritten == 0 { 108 | print("OutputStream.write() returned 0") 109 | return totalBytesWritten 110 | } 111 | 112 | } 113 | return totalBytesWritten 114 | } 115 | func handleInputEvent(_ eventCode: Stream.Event){ 116 | if eventCode == .openCompleted { 117 | print("NSStreamEventOpenCompleted") 118 | delegate?.didOpen(stream: inputStream) 119 | 120 | } else if eventCode == .hasBytesAvailable { 121 | print("NSStreamEventHasBytesAvailable") 122 | delegate?.hasInput(on: inputStream) 123 | 124 | } else if eventCode == .errorOccurred { 125 | print("NSStreamEventErrorOccurred") 126 | 127 | if let error = inputStream.streamError { 128 | print(error.localizedDescription) 129 | delegate?.error(error, on: inputStream) 130 | } 131 | } 132 | } 133 | 134 | func handleOutputEvent(_ eventCode: Stream.Event){ 135 | if eventCode == .openCompleted { 136 | delegate?.didOpen(stream: outputStream) 137 | print("NSStreamEventOpenCompleted") 138 | 139 | } else if eventCode == .hasSpaceAvailable { 140 | print("NSStreamEventHasBytesAvailable") 141 | writeCachedData() 142 | 143 | } else if eventCode == .errorOccurred { 144 | print("NSStreamEventErrorOccurred") 145 | if let error = inputStream.streamError { 146 | print(error.localizedDescription) 147 | delegate?.error(error, on: outputStream) 148 | } 149 | } 150 | } 151 | } 152 | 153 | 154 | extension StreamHolder: StreamDelegate { 155 | 156 | public func stream(_ aStream: Stream, handle eventCode: Stream.Event) { 157 | if aStream == inputStream { 158 | handleInputEvent(eventCode) 159 | } else if aStream == outputStream { 160 | handleOutputEvent(eventCode) 161 | } else { 162 | print("Received event for unknown stream") 163 | } 164 | } 165 | } 166 | 167 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | 4) 25 | TARGET_DEVICE_ARGS="--target-device watch" 26 | ;; 27 | *) 28 | TARGET_DEVICE_ARGS="--target-device mac" 29 | ;; 30 | esac 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "${PODS_ROOT}*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | 4) 25 | TARGET_DEVICE_ARGS="--target-device watch" 26 | ;; 27 | *) 28 | TARGET_DEVICE_ARGS="--target-device mac" 29 | ;; 30 | esac 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "${PODS_ROOT}*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Parser/Parser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 25/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Parser { 12 | static let string = StringParser() 13 | static let package = PackageReader() 14 | 15 | class StringParser { 16 | let kResponseFinishedCode : UInt8 = 0x3E 17 | 18 | func toInt(hexString str: String) -> Int { 19 | return Int(strtoul(str, nil, 16)) 20 | } 21 | 22 | func toUInt8(hexString str: String) -> UInt { 23 | return strtoul(str, nil, 16) 24 | } 25 | 26 | func isReadComplete(_ buf: [UInt8]) -> Bool { 27 | return buf.last == kResponseFinishedCode 28 | } 29 | 30 | func isOK(_ str: String) -> Bool{ 31 | return str.contains("OK") 32 | } 33 | 34 | func isError(_ str: String) -> Bool { 35 | return str.contains("?") 36 | } 37 | 38 | func isNoData(_ str: String) -> Bool { 39 | return str.contains("NO DATA") 40 | } 41 | 42 | func isStopped(_ str: String) -> Bool { 43 | return str.contains("STOPPED") 44 | } 45 | 46 | func isSerching(_ str: String) -> Bool { 47 | return str.contains("SEARCHING...") 48 | } 49 | 50 | func isAuto(_ str : String) -> Bool { 51 | return str.hasPrefix("AUTO") 52 | } 53 | 54 | func isDataResponse(_ str : String) -> Bool { 55 | let unwrapStr = str.first ?? Character.init("") 56 | let str = String(describing: unwrapStr) 57 | let isDigit = Int(str) != nil 58 | return isDigit // || isSerching(str) 59 | } 60 | 61 | func isATResponse(_ str : [Int8]) -> Bool { 62 | guard let char = str.first else { return false } 63 | guard let int32 = Int32.init(exactly: char) else { return false } 64 | return isalpha(int32) == 0 65 | } 66 | 67 | func getProtocol(fro index: Int8) -> ScanProtocol { 68 | let i = Int(index) 69 | return elmProtocolMap[i] 70 | } 71 | 72 | func protocolName(`protocol`: ScanProtocol) -> String { 73 | switch `protocol` { 74 | case .ISO9141Keywords0808: 75 | return "ISO 9141-2 Keywords 0808" 76 | case .ISO9141Keywords9494: 77 | return "ISO 9141-2 Keywords 9494" 78 | case .KWP2000FastInit: 79 | return "KWP2000 Fast Init" 80 | case .KWP2000SlowInit: 81 | return "KWP2000 Slow Init" 82 | case .J1850PWM: 83 | return "J1850 PWM" 84 | case .J1850VPW: 85 | return "J1850 VPW" 86 | case .CAN11bit250KB: 87 | return "CAN 11-Bit 250Kbps" 88 | case .CAN11bit500KB: 89 | return "CAN 11-Bit 500Kbps" 90 | case .CAN29bit250KB: 91 | return "CAN 29-Bit 250Kbps" 92 | case .CAN29bit500KB: 93 | return "CAN 29-Bit 500Kbps" 94 | case .none: 95 | return"Unknown Protocol" 96 | } 97 | } 98 | } 99 | 100 | //Parsing command response 101 | class PackageReader { 102 | func read(package: Package) -> Response { 103 | return parseResponse(package: package) 104 | } 105 | 106 | private func optimize(package: inout Package){ 107 | while package.buffer.last == 0x00 || package.buffer.last == 0x20 { 108 | package.buffer.removeLast() 109 | } 110 | } 111 | 112 | private func compress(components: inout [String], outputSize: inout Int){ 113 | for (i,s) in components.enumerated() { 114 | components[i] = s.replacingOccurrences(of: (i - 1).description + ":", with: "") 115 | } 116 | 117 | /* Mode $01 PID $00 request makes multiple chunks value w/o data size description. 118 | Data size over 3 length like "41 00 BE 1B 30 13" could not be size descriptor 119 | Size descriptor : 00E become 0x0E => 14 (Int) 120 | */ 121 | if components.first?.count ?? 0 <= 4 { 122 | let headByteSyzeString = components.removeFirst() 123 | outputSize = Parser.string.toInt(hexString: headByteSyzeString) 124 | } 125 | } 126 | 127 | private func parseResponse(package p : Package) -> Response { 128 | var package = p 129 | optimize(package: &package) 130 | 131 | var response = Response() 132 | 133 | if !package.isError && package.isData { 134 | var responseComponents = package.strigDescriptor.components(separatedBy: "\r") 135 | 136 | var decodeBufLength = 0 137 | var decodeBuf = [UInt8]() 138 | 139 | //Remove package descriptors from array 140 | if responseComponents.count > 2 { 141 | compress(components : &responseComponents, outputSize : &decodeBufLength) 142 | } 143 | 144 | for resp in responseComponents { 145 | if Parser.string.isSerching(resp) && Parser.string.isStopped(resp){ 146 | // A common reply if PID search occuring for the first time 147 | // at this drive cycle 148 | break 149 | } 150 | 151 | // make byte array from string response 152 | let chunks = resp.components(separatedBy: " ").filter({$0 != ""}) 153 | 154 | for c in chunks { 155 | let value = Parser.string.toUInt8(hexString: c) 156 | decodeBuf.append(UInt8(value)) 157 | } 158 | }//TODO: - Handle negative 159 | 160 | if decodeBufLength == 0 { 161 | decodeBufLength = decodeBuf.count 162 | }else{ 163 | decodeBufLength = min(decodeBufLength, decodeBuf.count) 164 | decodeBuf.removeSubrange(decodeBufLength.. Response { 178 | var resp = Response() 179 | var dataIndex = 0 180 | 181 | let modeRaw = data[dataIndex] ^ 0x40 182 | resp.mode = Mode.init(rawValue: modeRaw) ?? .none 183 | dataIndex += 1 184 | 185 | if data.count > dataIndex { 186 | resp.pid = data[dataIndex] 187 | dataIndex += 1 188 | } 189 | 190 | //Byte shift specialy for freezeframe data 191 | // 42 0C 00 4E 20 192 | // 42 - Mode 193 | // 0C - Pid 194 | // 00 - Shifted 195 | // 4E 20 - Data equal to mode 1. 196 | if resp.mode == .FreezeFrame02 { 197 | dataIndex += 1 198 | } 199 | 200 | if data.count > dataIndex { 201 | var mutatingData = data 202 | mutatingData.removeSubrange(0.. Float { 19 | return Float(data[0]) 20 | } 21 | 22 | /*! 23 | method calcTime 24 | */ 25 | 26 | func calcTime(data : Data) -> Float { 27 | let dataA = Int16(data[0]) 28 | let dataB = Int16(data[1]) 29 | let result = (dataA * 256) + dataB 30 | return Float(result) 31 | } 32 | 33 | /*! 34 | method calcTimingAdvance 35 | */ 36 | 37 | func calcTimingAdvance(data : Data) -> Float { 38 | let dataA = Float(data[0]) 39 | return (dataA / 2) - 64 40 | } 41 | 42 | /*! 43 | method calcDistance 44 | */ 45 | 46 | func calcDistance(data : Data) -> Float { 47 | let dataA = Int16(data[0]) 48 | let dataB = Int16(data[1]) 49 | let result = (dataA * 256) + dataB 50 | return Float(result) 51 | } 52 | 53 | /*! 54 | method calcPercentage 55 | */ 56 | 57 | func calcPercentage(data : Data) -> Float { 58 | return (Float(data[0]) * 100) / 255 59 | } 60 | 61 | /*! 62 | method calcAbsoluteLoadValue 63 | */ 64 | 65 | func calcAbsoluteLoadValue(data : Data) -> Float { 66 | let dataA = Float(data[0]) 67 | let dataB = Float(data[1]) 68 | 69 | return (((dataA * 256) + dataB) * 100) / 255 70 | } 71 | 72 | /*! 73 | method calcTemp 74 | */ 75 | 76 | func calcTemp(data : Data) -> Float { 77 | let temp = Float(data[0]) 78 | return temp - 40 79 | } 80 | 81 | /*! 82 | method calcCatalystTemp 83 | */ 84 | 85 | func calcCatalystTemp(data : Data) -> Float { 86 | let dataA = Float(data[0]) 87 | let dataB = Float(data[1]) 88 | 89 | return (((dataA * 256) + dataB) / 10) - 40 90 | } 91 | 92 | /*! 93 | method calcFuelTrimPercentage 94 | */ 95 | 96 | func calcFuelTrimPercentage(data : Data) -> Float { 97 | let value = Float(data[0]) 98 | return (0.7812 * (value - 128)) 99 | } 100 | 101 | /*! 102 | method calcFuelTrimPercentage2 103 | */ 104 | func calcFuelTrimPercentage2(data : Data) -> Float { 105 | let value = Float(data[1]) 106 | return (0.7812 * (value - 128)) 107 | } 108 | 109 | 110 | /*! 111 | method calcEngineRPM 112 | */ 113 | 114 | func calcEngineRPM(data : Data) -> Float { 115 | let dataA = Float(data[0]) 116 | let dataB = Float(data[1]) 117 | 118 | return (((dataA * 256) + dataB) / 4) 119 | } 120 | 121 | /*! 122 | method calcOxygenSensorVoltage 123 | */ 124 | 125 | func calcOxygenSensorVoltage(data : Data) -> Float { 126 | let dataA = Float(data[0]) 127 | return (dataA * 0.005) 128 | } 129 | 130 | /*! 131 | method calcControlModuleVoltage 132 | */ 133 | 134 | func calcControlModuleVoltage(data : Data) -> Float { 135 | let dataA = Float(data[0]) 136 | let dataB = Float(data[1]) 137 | 138 | return (((dataA * 256) + dataB) / 1000) 139 | } 140 | 141 | /*! 142 | method calcMassAirFlow 143 | */ 144 | 145 | func calcMassAirFlow(data : Data) -> Float { 146 | let dataA = Float(data[0]) 147 | let dataB = Float(data[1]) 148 | 149 | return (((dataA * 256) + dataB) / 100) 150 | } 151 | 152 | /*! 153 | method calcPressure 154 | */ 155 | 156 | func calcPressure(data : Data) -> Float { 157 | let dataA = Float(data[0]) 158 | let dataB = Float(data[1]) 159 | 160 | return (((dataA * 256) + dataB) * 0.079) 161 | } 162 | 163 | /*! 164 | method calcPressureDiesel 165 | */ 166 | 167 | func calcPressureDiesel(data : Data) -> Float { 168 | let dataA = Float(data[0]) 169 | let dataB = Float(data[1]) 170 | 171 | return (((dataA * 256) + dataB) * 10) 172 | } 173 | 174 | /*! 175 | method calcVaporPressure 176 | */ 177 | 178 | func calcVaporPressure(data : Data) -> Float { 179 | let dataA = Float(data[0]) 180 | let dataB = Float(data[1]) 181 | 182 | return ((((dataA * 256) + dataB) / 4) - 8192) 183 | } 184 | 185 | /*! 186 | method calcEquivalenceRatio 187 | */ 188 | 189 | func calcEquivalenceRatio(data : Data) -> Float { 190 | let dataA = Float(data[0]) 191 | let dataB = Float(data[1]) 192 | 193 | return (((dataA * 256) + dataB) * 0.0000305) 194 | } 195 | 196 | /*! 197 | method calcEquivalenceVoltage 198 | */ 199 | 200 | func calcEquivalenceVoltage(data : Data) -> Float { 201 | let dataC = Float(data[2]) 202 | let dataD = Float(data[3]) 203 | 204 | return (((dataC * 256) + dataD) * 0.000122) 205 | } 206 | 207 | /*! 208 | method calcEquivalenceCurrent 209 | */ 210 | 211 | func calcEquivalenceCurrent(data : Data) -> Float { 212 | let dataC = Float(data[2]) 213 | let dataD = Float(data[3]) 214 | 215 | return (((dataC * 256) + dataD) * 0.00390625) - 128 216 | } 217 | 218 | /*! 219 | method calcEGRError 220 | */ 221 | 222 | func calcEGRError(data : Data) -> Float { 223 | let dataA = Float(data[0]) 224 | 225 | return ((dataA * 0.78125) - 100) 226 | } 227 | 228 | /*! 229 | method calcInstantMPG 230 | */ 231 | func calcInstantMPG(vss : Double, maf : Double) -> Double { 232 | var _vss = vss 233 | var _maf = maf 234 | 235 | if(_vss > 255) { 236 | _vss = 255; 237 | } 238 | 239 | if(_vss < 0) { 240 | _vss = 0 241 | } 242 | 243 | 244 | if(_maf <= 0) { 245 | _maf = 0.1 246 | } 247 | 248 | var mpg : Double = 0.0 249 | let mph : Double = (_vss * 0.621371) // convert KPH to MPH 250 | 251 | mpg = ((14.7 * 6.17 * 454 * mph) / (3600 * _maf)) 252 | 253 | return mpg 254 | } 255 | 256 | 257 | func calcMILActive(data : [UInt8]) -> Bool { 258 | guard data.count < 4 else { 259 | return false 260 | } 261 | 262 | let dataA = data[0] 263 | 264 | return (dataA & 0x80) != 0 265 | } 266 | 267 | //------------------------------------------------------------------------------ 268 | //MARK: - 269 | //MARK: Global Conversion Functions 270 | 271 | /* 272 | @method convertTemp 273 | @param value: the temperature in degress Celsius (C) 274 | @return: the temperature in degrees Fahrenheit (F) 275 | */ 276 | 277 | func convertTemp(value : Float) -> Float { 278 | return ((value * 9) / 5) + 32; 279 | } 280 | 281 | /* 282 | @method convertPressure 283 | @param value: the pressure in kiloPascals (kPa) 284 | @return: the pressure in inches of Mercury (inHg) 285 | */ 286 | 287 | func convertPressure(value : Float) -> Float { 288 | return (value / 3.38600); 289 | } 290 | 291 | /* 292 | @method convertPressure2 293 | @param value: the pressure in Pascals (Pa) 294 | @return: the pressure in inches of Mercury (inHg) 295 | */ 296 | 297 | func convertPressure2(value : Float) -> Float { 298 | return (value / 3386) 299 | } 300 | 301 | /* 302 | @method convertSpeed 303 | @param value: the speed in kilometers per hour (km/h) 304 | @return: the speed in miles per hour (mph) 305 | */ 306 | 307 | func convertSpeed(value : Float) -> Float { 308 | return (value * 62) / 100.0 309 | } 310 | 311 | /* 312 | @method convertAir 313 | @param value: the air flow in grams per second (g/s) 314 | @return: the air flow in pounds per minute (lb/min) 315 | */ 316 | 317 | func convertAir(value : Float) -> Float { 318 | return (value * 132) / 1000.0 319 | } 320 | 321 | /* 322 | @method convertDistance 323 | @param value: the distance in Km 324 | @return: the distance in Miles 325 | */ 326 | 327 | func convertDistance(value : Float) -> Float { 328 | return (value * 0.6213) 329 | } 330 | 331 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Operations/InitScanerOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InitScanerOperation.swift 3 | // OBD2Swift 4 | // 5 | // Created by Sergiy Loza on 30.05.17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum InitializationError: Error { 12 | case DefaultError 13 | case EchoOffError 14 | case DataWriteError 15 | case ProtocolError 16 | case ReaderError(reason:String) 17 | } 18 | 19 | class InitScanerOperation: StreamHandleOperation { 20 | 21 | class func keyPathsForValuesAffectingIsFinished() -> Set { 22 | return ["state" as NSObject, "error" as NSObject] 23 | } 24 | 25 | private(set) var currentPIDGroup: UInt8 = 0x00 { 26 | didSet { 27 | print("Set new pid group \(currentPIDGroup)") 28 | } 29 | } 30 | 31 | private(set) var `protocol`:ScanProtocol? { 32 | didSet { 33 | print("Set OBD protocol to \(String(describing: self.`protocol`))") 34 | } 35 | } 36 | 37 | var command: DataRequest? { 38 | switch state { 39 | case .reset: 40 | return Command.AT.reset.dataRequest 41 | case .echoOff: 42 | return Command.AT.echoOff.dataRequest 43 | case .`protocol`: 44 | return Command.AT.`protocol`.dataRequest 45 | case .version: 46 | return Command.AT.versionId.dataRequest 47 | case .search: 48 | return Command.Custom.digit(mode: 1, pid: 0).dataRequest 49 | default: 50 | return nil 51 | } 52 | } 53 | 54 | private var reader:StreamReader 55 | 56 | private var state: Scanner.State = .unknown { 57 | didSet{ 58 | if state == .complete { 59 | input.remove(from: .current, forMode: .defaultRunLoopMode) 60 | output.remove(from: .current, forMode: .defaultRunLoopMode) 61 | } 62 | } 63 | } 64 | 65 | //MARK: Overrides 66 | 67 | override var isFinished: Bool { 68 | if error != nil { 69 | return true 70 | } 71 | return state == .complete 72 | } 73 | 74 | override init(inputStream: InputStream, outputStream: OutputStream) { 75 | self.reader = StreamReader(stream: inputStream) 76 | super.init(inputStream: inputStream, outputStream: outputStream) 77 | } 78 | 79 | override func main() { 80 | super.main() 81 | } 82 | 83 | override func execute() { 84 | state.next() 85 | continueInitialization() 86 | } 87 | 88 | override func inputStremEvent(event: Stream.Event) { 89 | if event == .hasBytesAvailable { 90 | do { 91 | if try reader.read() { 92 | onReadEnd() 93 | state.next() 94 | continueInitialization() 95 | } 96 | } catch StreamReaderError.noBytesReaded { 97 | print("No bytes error") 98 | self.error = InitializationError.ReaderError(reason: "No bytes for read") 99 | } catch StreamReaderError.ELMError { 100 | print("ELM error") 101 | self.error = InitializationError.ReaderError(reason: "ELM Error") 102 | } catch { 103 | print("Unknown reader error") 104 | self.error = InitializationError.ReaderError(reason: "Unknown reader error") 105 | } 106 | } else if event == .errorOccurred { 107 | error = input.streamError 108 | } 109 | } 110 | 111 | override func outputStremEvent(event: Stream.Event) { 112 | if event == .errorOccurred { 113 | error = output.streamError 114 | } 115 | } 116 | 117 | //MARK: Private functions 118 | 119 | private func continueInitialization() { 120 | if state == .complete || error != nil { 121 | return 122 | } 123 | //Create new reader for comand 124 | self.reader = StreamReader(stream: input) 125 | 126 | //Get comand data and write it 127 | guard let data = command?.data else { return } 128 | let writer = StreamWriter(stream: output, data: data) 129 | do { 130 | try writer.write() 131 | } catch let error { 132 | print("Error \(error) on data writing") 133 | self.error = InitializationError.DataWriteError 134 | } 135 | } 136 | 137 | private func onReadEnd() { 138 | switch state { 139 | case .echoOff: 140 | guard let resp = reader.response, resp.elmOK else { 141 | print("Stop initialization, error during echo off") 142 | self.error = InitializationError.EchoOffError 143 | return 144 | } 145 | break 146 | case .`protocol`: 147 | guard let response = reader.response else { 148 | print("Handle protocol setup error") 149 | self.error = InitializationError.ProtocolError 150 | return 151 | } 152 | 153 | var searchIndex = 0 154 | 155 | if Parser.string.isAuto(response) { 156 | searchIndex += 1 157 | let index = reader.readBuffer[searchIndex] - 0x4E 158 | self.`protocol` = elmProtocolMap[Int(index)] 159 | } else { 160 | let index = reader.readBuffer[searchIndex] ^ 0x40 161 | self.`protocol` = elmProtocolMap[Int(index)] 162 | } 163 | 164 | break 165 | case .search: 166 | let buffer = reader.readBuffer 167 | 168 | let resp = Parser.package.decode(data: buffer, length: buffer.count) 169 | var extendPIDSearch = false 170 | 171 | let morePIDs = buildSupportedSensorList(data: resp.data!, pidGroup: Int(currentPIDGroup)) 172 | 173 | if !extendPIDSearch && morePIDs { 174 | extendPIDSearch = true 175 | } 176 | 177 | currentPIDGroup += extendPIDSearch ? 0x20 : 0x00 178 | 179 | if extendPIDSearch { 180 | if currentPIDGroup > 0x40 { 181 | currentPIDGroup = 0x00 182 | } 183 | }else{ 184 | currentPIDGroup = 0x00 185 | } 186 | 187 | 188 | break 189 | default: 190 | break 191 | } 192 | } 193 | } 194 | 195 | fileprivate func buildSupportedSensorList(data : Data, pidGroup : Int) -> Bool { 196 | 197 | let bytes = data.withUnsafeBytes { 198 | [UInt8](UnsafeBufferPointer(start: $0, count: data.count)) 199 | } 200 | 201 | let bytesLen = bytes.count 202 | 203 | if bytesLen != 4 { 204 | return false 205 | } 206 | 207 | var supportedSensorList = Array.init(repeating: 0, count: 16) 208 | 209 | /* if(pidGroup == 0x00) { 210 | // If we are re-issuing the PID search command, reset any 211 | // previously received PIDs 212 | */ 213 | 214 | var pid = pidGroup + 1 215 | var supported = false 216 | let shiftSize = 7 217 | 218 | for i in 0..<4 { 219 | for y in 0...7 { 220 | let leftShift = UInt8(shiftSize - y) 221 | supported = (((1 << leftShift) & bytes[i]) != 0) 222 | pid += 1 223 | 224 | if(supported) { 225 | if NOT_SEARCH_PID(pid) && pid <= 0x4E && !supportedSensorList.contains(where: {$0 == pid}){ 226 | supportedSensorList.append(pid) 227 | } 228 | } 229 | } 230 | } 231 | 232 | return MORE_PIDS_SUPPORTED(bytes) 233 | } 234 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Scanner/Scanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sanner.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 24/05/2017. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum ReadInputError: Error { 12 | case initResponseUnreadable 13 | } 14 | 15 | enum InitScannerError: Error { 16 | case outputTimeout 17 | case inputTimeout 18 | } 19 | 20 | public typealias StateChangeCallback = (_ state: ScanState) -> () 21 | 22 | class `Scanner`: StreamHolder { 23 | 24 | typealias CallBack = (Bool, Error?) -> () 25 | 26 | 27 | var defaultSensors: [UInt8] = [0x0C, 0x0D] 28 | 29 | var supportedSensorList = [Int]() 30 | 31 | private var repeatCommands = Set() 32 | 33 | var state: ScanState = .none { 34 | didSet { 35 | if state == .none { 36 | obdQueue.cancelAllOperations() 37 | } 38 | stateChanged?(state) 39 | } 40 | } 41 | 42 | var stateChanged: StateChangeCallback? 43 | 44 | var `protocol`: ScanProtocol = .none 45 | 46 | var currentPIDGroup: UInt8 = 0x00 47 | 48 | init(host: String, port: Int) { 49 | super.init() 50 | self.host = host 51 | self.port = port 52 | 53 | delegate = self 54 | } 55 | 56 | open func request(command: DataRequest, response : @escaping (_ response:Response) -> ()){ 57 | 58 | let request = CommandOperation(inputStream: inputStream, outputStream: outputStream, command: command) 59 | 60 | request.onReceiveResponse = response 61 | request.queuePriority = .high 62 | request.completionBlock = { 63 | print("Request operation completed") 64 | } 65 | 66 | obdQueue.addOperation(request) 67 | } 68 | 69 | 70 | open func startRepeatCommand(command: DataRequest, response : @escaping (_ response:Response) -> ()) { 71 | if repeatCommands.contains(command) { 72 | print("Command alredy on repeat loop and can be observed") 73 | return 74 | } 75 | repeatCommands.insert(command) 76 | request(repeat: command, response: response) 77 | } 78 | 79 | open func stopRepeatCommand(command: DataRequest) { 80 | repeatCommands.remove(command) 81 | } 82 | 83 | open func isRepeating(command: DataRequest) -> Bool { 84 | return repeatCommands.contains(command) 85 | } 86 | 87 | private func request(repeat command: DataRequest, response : @escaping (_ response:Response) -> ()) { 88 | 89 | let request = CommandOperation(inputStream: inputStream, outputStream: outputStream, command: command) 90 | 91 | request.queuePriority = .low 92 | request.onReceiveResponse = response 93 | request.completionBlock = { [weak self] in 94 | print("Request operation completed") 95 | if let error = request.error { 96 | print("Error occured \(error)") 97 | self?.state = .none 98 | } else { 99 | guard let strong = self else { return } 100 | if strong.repeatCommands.contains(command) { 101 | strong.request(repeat: command, response: response) 102 | } 103 | } 104 | } 105 | 106 | obdQueue.addOperation(request) 107 | } 108 | 109 | open func startScan(callback: @escaping CallBack){ 110 | 111 | if state != .none { 112 | return 113 | } 114 | 115 | state = .openingConnection 116 | 117 | obdQueue.cancelAllOperations() 118 | 119 | createStreams() 120 | 121 | // Open connection to OBD 122 | 123 | let openConnectionOperation = OpenOBDConnectionOperation(inputStream: inputStream, outputStream: outputStream) 124 | 125 | openConnectionOperation.completionBlock = { [weak self] in 126 | if let error = openConnectionOperation.error { 127 | print("open operation completed with error \(error)") 128 | self?.state = .none 129 | self?.obdQueue.cancelAllOperations() 130 | } else { 131 | self?.state = .initializing 132 | print("open operation completed without errors") 133 | } 134 | } 135 | 136 | obdQueue.addOperation(openConnectionOperation) 137 | 138 | // Initialize connection with OBD 139 | 140 | let initOperation = InitScanerOperation(inputStream: inputStream, outputStream: outputStream) 141 | 142 | initOperation.completionBlock = { [weak self] in 143 | if let error = initOperation.error { 144 | callback(false, error) 145 | self?.state = .none 146 | self?.obdQueue.cancelAllOperations() 147 | } else { 148 | self?.state = .connected 149 | callback(true, nil) 150 | } 151 | } 152 | 153 | obdQueue.addOperation(initOperation) 154 | } 155 | 156 | open func pauseScan() { 157 | obdQueue.isSuspended = true 158 | } 159 | 160 | open func resumeScan() { 161 | obdQueue.isSuspended = false 162 | } 163 | 164 | open func cancelScan() { 165 | repeatCommands.removeAll() 166 | obdQueue.cancelAllOperations() 167 | } 168 | 169 | open func disconnect() { 170 | cancelScan() 171 | inputStream.close() 172 | outputStream.close() 173 | state = .none 174 | } 175 | 176 | open func isService01PIDSupported(pid : Int) -> Bool { 177 | var supported = false 178 | 179 | for supportedPID in supportedSensorList { 180 | if supportedPID == pid { 181 | supported = true 182 | break 183 | } 184 | } 185 | 186 | return supported 187 | } 188 | } 189 | 190 | extension Scanner: StreamFlowDelegate { 191 | 192 | func didOpen(stream: Stream){ 193 | 194 | } 195 | 196 | func error(_ error: Error, on stream: Stream){ 197 | 198 | } 199 | 200 | func hasInput(on stream: Stream){ 201 | // 202 | // do { 203 | // 204 | // if state == .init { 205 | // try readInitResponse() 206 | // } else if state == .idle || state == .waiting { 207 | // waitingForVoltageCommand ? readVoltageResponse() : readInput() 208 | // 209 | // } else { 210 | // print("Error: Received bytes in unknown state: \(state)") 211 | // } 212 | // 213 | // } catch { 214 | // 215 | // print("Error: Init response unreadable. Need reconnect") 216 | // //TODO: try reconnect 217 | // } 218 | // 219 | // 220 | } 221 | } 222 | 223 | extension Scanner { 224 | enum State : UInt { 225 | case unknown = 1 226 | case reset = 2 227 | case echoOff = 4 228 | case version = 8 229 | case search = 16 230 | case `protocol` = 32 231 | case complete = 64 232 | 233 | static var all : [State] { 234 | return [.unknown, .reset, .echoOff, .version, .search, .`protocol`, .complete] 235 | } 236 | 237 | static func <<= (left: State, right: UInt) -> State { 238 | let move = left.rawValue << right 239 | return self.all.filter({$0.rawValue == move}).first ?? .unknown 240 | } 241 | 242 | mutating func next() { 243 | self = self <<= 1 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OBD2 Swift 2 | 3 | [![GitHub Release](https://img.shields.io/badge/release-none-red.svg)](https://github.com/lemberg/obd2-swift-lib) 4 | [![Swift Version](https://img.shields.io/badge/Swift-4.0%2B-orange.svg?style=flat)](https://github.com/lemberg/obd2-swift-lib) 5 | [![GitHub Platforms](https://img.shields.io/badge/platform-ios%20%7C%20macos%20-brightgreen.svg)](https://github.com/lemberg/obd2-swift-lib) 6 | [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/lemberg/obd2-swift-lib/blob/dev/LICENSE) 7 | [![By](https://img.shields.io/badge/By-Lemberg%20Solutions%20Limited-blue.svg?style=flat)](http://cocoapods.org/pods/PermissionsService) 8 | 9 | On-board diagnostics swift library. 10 | 11 | * [Why do you need it?](https://github.com/lemberg/obd2-swift-lib#why-you-need-it) 12 | * [Requirements](https://github.com/lemberg/obd2-swift-lib#requirements) 13 | * [Features](https://github.com/lemberg/obd2-swift-lib#features) 14 | * [How To Use](https://github.com/lemberg/obd2-swift-lib#how-to-use) 15 | * [Create connection](https://github.com/lemberg/obd2-swift-lib#where-to-start) 16 | * [Getting metrics](https://github.com/lemberg/obd2-swift-lib#and-what-about-getting-metrics) 17 | * [Observing](https://github.com/lemberg/obd2-swift-lib#ok-but-what-about-monitoring) 18 | * [Installation](https://github.com/lemberg/obd2-swift-lib#installation) 19 | * [As Embedded Framework](https://github.com/lemberg/obd2-swift-lib#manually-as-embedded-framework) 20 | * [CocoaPods](https://github.com/lemberg/obd2-swift-lib#cocoapods) 21 | * [Author](https://github.com/lemberg/obd2-swift-lib#author) 22 | * [License](https://github.com/lemberg/obd2-swift-lib#license) 23 | 24 | ## Why do you need it? 25 | 26 | #### OBD2?.. What? 27 | OBD or On-board diagnostics is a vehicle's self-diagnostic and reporting capability. OBD systems give access to the status of the various vehicle subsystems. 28 | Simply saying, OBD-II is a sort of computer which monitors emissions, mileage, speed, and other useful data. 29 | 30 | > More details you can get [here](https://en.wikipedia.org/wiki/On-board_diagnostics). 31 | 32 | #### Ok. And what about this? 33 | 34 | This is a library which can communicate with vehicles using OBD-II adapters. It is providing you with an opportunity for real-time vehicles diagnostics with several lines of code and without being nervous. The library will help you to connect with adapter and handle it's behaviour, like reconnect when something went wrong. And! You don't need to parse bytes response returned from adapter by yourself because OBD2 Swift will do it for your project. 35 | 36 | ## Requirements 37 | 38 | - iOS 9.0+ 39 | - Swift 3.0+ 40 | - Xcode 8.0+ 41 | - Mac OS X 10.0+ 42 | 43 | ## Features 44 | 45 | - [x] Supporting next Modes with almost all their PIDs: 46 | 47 | Mode | Description 48 | -----| ----------- 49 | Mode 01(02) | Sensors / Freeze Frame 50 | Mode 03 | Trouble Codes (DTC) 51 | Mode 04 | Reset Trouble Codes 52 | Mode 09 | Information 53 | 54 | - [x] Real-time connection with OBD-II 55 | - [x] OBD-II full described diagnostic sensors list 56 | - [x] Observer of connection and other metrics 57 | - [x] Several types of returning & requesting diagnostic response 58 | - [x] Logger, which can save your logs into a file and share it 59 | 60 | ## How to use? 61 | 62 | #### Where to start? 63 | 64 | First of all, create an `OBD2` object for requesting vehicles metrics. 65 | 66 | ```swift 67 | let obd = OBD2() 68 | ``` 69 | 70 | Create a connection between your application and adapter. 71 | 72 | > :exclamation: Remember that **all callbacks return not in the main thread**. 73 | 74 | ```swift 75 | obd.connect { [weak self] (success, error) in 76 | if let error = error { 77 | print("OBD connection failed with \(error)") 78 | 79 | } else { 80 | //perform something 81 | } 82 | } 83 | ``` 84 | Method `connect` would return you a response with an error if something went wrong and you can simply handle it, for example, show a message with reconnecting opportunity. 85 | 86 | If all goes okay, you have a connection! :sunglasses: 87 | 88 | > Class `OBD2` contain another methods for work with connection like `pauseScan()`, `resumeScan()` and `stopScan()` as well. 89 | 90 | #### And what about getting metrics? 91 | 92 | There is a simple way to get data from vehicle subsystems - to use requests. You can send it using `request(_:)` method of `OBD2 ` object. Use `struct Command` for choosing what you want to request. 93 | 94 | ```swift 95 | obd.request(command: Command.Mode09.vin) { (descriptor) in 96 | let respStr = descriptor?.VIN() 97 | print(respStr ?? "No value") 98 | } 99 | ``` 100 | 101 | #### What if I want to get another response? 102 | 103 | You can use `request(_:)` method with `emun Custom: CommandType`. It helps you to send and get data in `digit` and `string` format. 104 | 105 | ```swift 106 | obd.request(command: Command.Custom.digit(mode: 09, pid: 02)) { (descr) in 107 | if let response = descr?.response { 108 | print(response) 109 | } 110 | } 111 | ``` 112 | 113 | #### Ok, but what about monitoring? 114 | 115 | You can still use requests for doing this. There is a method `request(repeat:)` wich will send a request to OBD repeatedly. 116 | 117 | ```swift 118 | obd.request(repeat: Command.Mode01.pid(number: 12)) 119 | ``` 120 | 121 | #### Where is a response?! :scream: 122 | 123 | Response from this method will be returned to `Observer`. Choose `Mode` type and create an `Observer` object. 124 | 125 | ```swift 126 | let observer = Observer() 127 | ``` 128 | 129 | Tell him to observe with specific PID number and enjoy responses. :] 130 | 131 | ```swift 132 | observer.observe(command: .pid(number: 12)) { (descriptor) in 133 | let respStr = descriptor?.shortDescription 134 | print("Observer : \(respStr)") 135 | } 136 | ``` 137 | 138 | > :exclamation: To bring `Observer` alive you must register it in `ObserverQueue`. It is needed for returning diagnostics responses. 139 | > 140 | > ```swift 141 | > ObserverQueue.shared.register(observer: observer) 142 | >``` 143 | > Don't forget to do `unregister(_:)` when you don't need `Observer` anymore. 144 | 145 | #### How to stop it? 146 | 147 | `OBD2` object has method `isRepeating(repeat:)` wich takes `Command` as a parameter. Using it you can check if the specific command is repeating now and stop it. 148 | 149 | ```swift 150 | if obd.isRepeating(repeat: Command.Mode01.pid(number: 12)) { 151 | obd.stop(repeat: command) 152 | } 153 | ``` 154 | 155 | #### Can I use observer for other requests? 156 | 157 | Yep! Single request method can take `Bool` parameter `notifyObservers` wich is `true` by default. Using it you can manage which requests will return response not only in completion block but in observer block too. 158 | 159 | ## Installation 160 | 161 | ### Manually as Embedded Framework 162 | 163 | * Go to your project root git folder and clone OBD2 Swift as a git [submodule](https://git-scm.com/docs/git-submodule) by running the following command from the terminal. 164 | 165 | ```swift 166 | $ git submodule add https://github.com/lemberg/obd2-swift-lib.git 167 | ``` 168 | 169 | * Open obd2-swift-lib folder which was created. In the OBD2-Swift folder, you will found OBD2Swift.xcodeproj. Drag it into the Project Navigator of your project. 170 | 171 | * Select your project in the Xcode Navigation and then select your application target from the sidebar. After this, select the "General" tab and click on the + button under the "Embedded Binaries" section. 172 | 173 | * Select OBD2 Swift.framework from dialogue and that's all! :tada: 174 | 175 | > Don't forget to do `import OBD2Swift` in classes where you want to use this framework 176 | 177 | ### CocoaPods 178 | 179 | To install it, simply add this line to your Podfile: 180 | 181 | ```swift 182 | pod "OBD2-Swift" 183 | ``` 184 | 185 | > If you'll need, there is still a version written on **Swift 3** named `swift3`. 186 | 187 | Now you need to run `pod update` command from you project folder and that's it! 188 | 189 | ## Author 190 | 191 | ### [Lemberg Solutions](http://lemberg.co.uk) 192 | [![Logo](http://lemberg.co.uk/sites/all/themes/lemberg/images/logo.png)](https://github.com/lemberg) 193 | 194 | ## License 195 | 196 | OBD2 Swift is available under the [MTI license](https://directory.fsf.org/wiki/License:MTI). See the LICENSE file for more info. 197 | -------------------------------------------------------------------------------- /OBD2-Swift/Classes/Parser/Descriptor01.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Descriptor.swift 3 | // OBD2Swift 4 | // 5 | // Created by Max Vitruk on 5/25/17. 6 | // Copyright © 2017 Lemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Mode01Descriptor : DescriptorProtocol { 12 | public var response : Response 13 | var descriptor : SensorDescriptor 14 | 15 | required public init(describe response : Response) { 16 | self.response = response 17 | let pid = response.pid 18 | self.mode = response.mode 19 | 20 | guard pid >= 0x0 && pid <= 0x4E else { 21 | assertionFailure("Unsuported pid group") 22 | self.descriptor = SensorDescriptorTable[0] 23 | return 24 | } 25 | self.descriptor = SensorDescriptorTable[Int(pid)] 26 | } 27 | 28 | public var mode : Mode 29 | public var pid : UInt8 { 30 | return response.pid 31 | } 32 | 33 | public var valueMetrics : Float? { 34 | guard let data = response.data else {return nil} 35 | guard !isAsciiEncoded else {return nil} 36 | guard let exec = descriptor.calcFunction else {return nil} 37 | 38 | return exec(data) 39 | } 40 | 41 | public var valueImperial : Float? { 42 | guard let value = valueMetrics else {return nil} 43 | guard let exec = descriptor.convertFunction else {return nil} 44 | return exec(value) 45 | } 46 | 47 | public var unitsImperial : Float? { 48 | guard let value = valueMetrics else {return nil} 49 | guard let exec = descriptor.convertFunction else {return nil} 50 | return exec(value) 51 | } 52 | 53 | public var asciValue : String? { 54 | guard let data = response.data else {return nil} 55 | guard isAsciiEncoded else {return nil} 56 | return calculateStringForData(data: data) 57 | } 58 | 59 | public var description : String { 60 | return descriptor.description 61 | } 62 | 63 | public var shortDescription :String { 64 | return descriptor.shortDescription 65 | } 66 | 67 | public var unitImperial : String { 68 | return descriptor.imperialUnit 69 | } 70 | 71 | public var unitMetric : String { 72 | return descriptor.metricUnit 73 | } 74 | 75 | public var isAsciiEncoded : Bool { 76 | return IS_ALPHA_VALUE(pid: pid) 77 | } 78 | 79 | public func stringRepresentation(metric : Bool, rounded : Bool = false) -> String { 80 | if isAsciiEncoded { 81 | return asciValue ?? "" 82 | }else{ 83 | guard let value = self.value(metric: metric) else {return ""} 84 | let units = self.units(metric: metric) 85 | 86 | if rounded { 87 | return "\(Int.init(value)) \(units)" 88 | }else{ 89 | return "\(value) \(units)" 90 | } 91 | } 92 | } 93 | 94 | 95 | public func units(metric : Bool) -> String { 96 | return metric ? descriptor.metricUnit : descriptor.imperialUnit 97 | } 98 | 99 | public func value(metric : Bool) -> Float? { 100 | return metric ? valueMetrics : valueImperial 101 | } 102 | 103 | public func minValue(metric : Bool) -> Int { 104 | if isAsciiEncoded { 105 | return Int.min 106 | } 107 | return metric ? descriptor.minMetricValue : descriptor.minImperialValue 108 | } 109 | 110 | public func maxValue(metric : Bool) -> Int { 111 | if isAsciiEncoded { 112 | return Int.max 113 | } 114 | 115 | return metric ? descriptor.maxMetricValue : descriptor.maxImperialValue 116 | } 117 | 118 | //TODO: - Add multidescriptor to descriptor tables 119 | // public func isMultiValue() -> Bool { 120 | // return IS_MULTI_VALUE_SENSOR(pid: pid) 121 | // } 122 | 123 | //MARK: - String Calculation Methods 124 | 125 | private func calculateStringForData(data : Data) -> String? { 126 | switch pid { 127 | case 0x03: 128 | return calculateFuelSystemStatus(data) 129 | case 0x12: 130 | return calculateSecondaryAirStatus(data) 131 | case 0x13: 132 | return calculateOxygenSensorsPresent(data) 133 | case 0x1C: 134 | return calculateDesignRequirements(data) 135 | case 0x1D: 136 | return "" //TODO: pid 29 - Oxygen Sensor 137 | case 0x1E: 138 | return calculateAuxiliaryInputStatus(data) 139 | default: 140 | return nil 141 | } 142 | } 143 | 144 | private func calculateAuxiliaryInputStatus(_ data : Data) -> String? { 145 | var dataA = data[0] 146 | dataA = dataA & ~0x7F // only bit 0 is valid 147 | 148 | if dataA & 0x01 != 0 { 149 | return "PTO_STATE: ON" 150 | }else if dataA & 0x02 != 0 { 151 | return "PTO_STATE: OFF" 152 | }else { 153 | return nil 154 | } 155 | } 156 | 157 | private func calculateDesignRequirements(_ data : Data) -> String? { 158 | var returnString : String? 159 | let dataA = data[0] 160 | 161 | switch dataA { 162 | case 0x01: 163 | returnString = "OBD II" 164 | break 165 | case 0x02: 166 | returnString = "OBD" 167 | break 168 | case 0x03: 169 | returnString = "OBD I and OBD II" 170 | break 171 | case 0x04: 172 | returnString = "OBD I" 173 | break 174 | case 0x05: 175 | returnString = "NO OBD" 176 | break 177 | case 0x06: 178 | returnString = "EOBD" 179 | break 180 | case 0x07: 181 | returnString = "EOBD and OBD II" 182 | break 183 | case 0x08: 184 | returnString = "EOBD and OBD" 185 | break 186 | case 0x09: 187 | returnString = "EOBD, OBD and OBD II" 188 | break 189 | case 0x0A: 190 | returnString = "JOBD"; 191 | break 192 | case 0x0B: 193 | returnString = "JOBD and OBD II" 194 | break 195 | case 0x0C: 196 | returnString = "JOBD and EOBD" 197 | break 198 | case 0x0D: 199 | returnString = "JOBD, EOBD, and OBD II" 200 | break 201 | default: 202 | returnString = "N/A" 203 | break 204 | } 205 | 206 | return returnString 207 | } 208 | 209 | private func calculateOxygenSensorsPresent(_ data : Data) -> String { 210 | var returnString : String = "" 211 | let dataA = data[0] 212 | 213 | if dataA & 0x01 != 0 { 214 | returnString = "O2S11" 215 | } 216 | 217 | if dataA & 0x02 != 0 { 218 | returnString = "\(returnString), O2S12" 219 | } 220 | 221 | if dataA & 0x04 != 0 { 222 | returnString = "\(returnString), O2S13" 223 | } 224 | 225 | if dataA & 0x08 != 0 { 226 | returnString = "\(returnString), O2S14" 227 | } 228 | 229 | if dataA & 0x10 != 0 { 230 | returnString = "\(returnString), O2S21" 231 | } 232 | 233 | if dataA & 0x20 != 0 { 234 | returnString = "\(returnString), O2S22" 235 | } 236 | 237 | if dataA & 0x40 != 0 { 238 | returnString = "\(returnString), O2S23" 239 | } 240 | 241 | if dataA & 0x80 != 0 { 242 | returnString = "\(returnString), O2S24" 243 | } 244 | 245 | return returnString 246 | } 247 | 248 | private func calculateOxygenSensorsPresentB(_ data : Data) -> String { 249 | var returnString : String = "" 250 | let dataA = data[0] 251 | 252 | if(dataA & 0x01 != 0){ 253 | returnString = "O2S11" 254 | } 255 | 256 | if dataA & 0x02 != 0 { 257 | returnString = "\(returnString), O2S12" 258 | } 259 | 260 | if dataA & 0x04 != 0 { 261 | returnString = "\(returnString), O2S21" 262 | } 263 | 264 | if(dataA & 0x08 != 0) { 265 | returnString = "\(returnString), O2S22" 266 | } 267 | 268 | if dataA & 0x10 != 0 { 269 | returnString = "\(returnString), O2S31" 270 | } 271 | 272 | if dataA & 0x20 != 0 { 273 | returnString = "\(returnString), O2S32" 274 | } 275 | 276 | if dataA & 0x40 != 0 { 277 | returnString = "\(returnString), O2S41" 278 | } 279 | 280 | if dataA & 0x80 != 0 { 281 | returnString = "\(returnString), O2S42" 282 | } 283 | 284 | return returnString 285 | } 286 | 287 | private func calculateFuelSystemStatus(_ data : Data) -> String { 288 | var rvString : String = "" 289 | let dataA = data[0] 290 | 291 | switch dataA { 292 | case 0x01: 293 | rvString = "Open Loop" 294 | break 295 | case 0x02: 296 | rvString = "Closed Loop" 297 | break 298 | case 0x04: 299 | rvString = "OL-Drive" 300 | break; 301 | case 0x08: 302 | rvString = "OL-Fault" 303 | break 304 | case 0x10: 305 | rvString = "CL-Fault" 306 | break 307 | default: 308 | break 309 | } 310 | 311 | return rvString 312 | } 313 | 314 | private func calculateSecondaryAirStatus(_ data : Data) -> String { 315 | var rvString : String = "" 316 | let dataA = data[0] 317 | 318 | switch dataA { 319 | case 0x01: 320 | rvString = "AIR_STAT: UPS" 321 | break 322 | case 0x02: 323 | rvString = "AIR_STAT: DNS" 324 | break 325 | case 0x04: 326 | rvString = "AIR_STAT: OFF" 327 | break 328 | default: 329 | break 330 | } 331 | 332 | return rvString 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /Example/OBD2-Swift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 40 | 53 | 66 | 79 | 92 | 93 | 94 | 95 | 96 | 97 | 110 | 114 | 115 | 116 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /Example/OBD2-Swift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 016C4525A9DF18FC98A55AAF /* Pods_OBD2_Swift_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E144880A1951F1FF015E1D42 /* Pods_OBD2_Swift_Example.framework */; }; 11 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 12 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 13 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 14 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 15 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 273BA881D0B503C7C37377B9 /* Pods-OBD2-Swift_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OBD2-Swift_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example.debug.xcconfig"; sourceTree = ""; }; 20 | 3EA8E9D52CD628236396EAFE /* Pods-OBD2-Swift_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OBD2-Swift_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests.release.xcconfig"; sourceTree = ""; }; 21 | 607FACD01AFB9204008FA782 /* OBD2-Swift_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "OBD2-Swift_Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 27 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 28 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 30 | 845E970FE31FAFF7CCF10E08 /* Pods_OBD2_Swift_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OBD2_Swift_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 8BD1F9600CDD4D67A5F6339D /* Pods-OBD2-Swift_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OBD2-Swift_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example.release.xcconfig"; sourceTree = ""; }; 32 | 967E3968C2B3BFA56DA58FF9 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 33 | AD59134CD0A2B62C7157BA83 /* OBD2-Swift.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = "OBD2-Swift.podspec"; path = "../OBD2-Swift.podspec"; sourceTree = ""; }; 34 | B97A44C9329E830730530372 /* Pods-OBD2-Swift_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OBD2-Swift_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OBD2-Swift_Tests/Pods-OBD2-Swift_Tests.debug.xcconfig"; sourceTree = ""; }; 35 | B99C0CC5AC0B1360B3D7F1FD /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 36 | E144880A1951F1FF015E1D42 /* Pods_OBD2_Swift_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OBD2_Swift_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | 016C4525A9DF18FC98A55AAF /* Pods_OBD2_Swift_Example.framework in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | 607FACC71AFB9204008FA782 = { 52 | isa = PBXGroup; 53 | children = ( 54 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 55 | 607FACD21AFB9204008FA782 /* Example for OBD2-Swift */, 56 | 607FACE81AFB9204008FA782 /* Tests */, 57 | 607FACD11AFB9204008FA782 /* Products */, 58 | 70AD239EFDA3E268DFE81EEB /* Pods */, 59 | 98122E02889430A5105A1D63 /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | 607FACD11AFB9204008FA782 /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 607FACD01AFB9204008FA782 /* OBD2-Swift_Example.app */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 607FACD21AFB9204008FA782 /* Example for OBD2-Swift */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 75 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 76 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 77 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 78 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 79 | 607FACD31AFB9204008FA782 /* Supporting Files */, 80 | ); 81 | name = "Example for OBD2-Swift"; 82 | path = "OBD2-Swift"; 83 | sourceTree = ""; 84 | }; 85 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 607FACD41AFB9204008FA782 /* Info.plist */, 89 | ); 90 | name = "Supporting Files"; 91 | sourceTree = ""; 92 | }; 93 | 607FACE81AFB9204008FA782 /* Tests */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 97 | 607FACE91AFB9204008FA782 /* Supporting Files */, 98 | ); 99 | path = Tests; 100 | sourceTree = ""; 101 | }; 102 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 607FACEA1AFB9204008FA782 /* Info.plist */, 106 | ); 107 | name = "Supporting Files"; 108 | sourceTree = ""; 109 | }; 110 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | AD59134CD0A2B62C7157BA83 /* OBD2-Swift.podspec */, 114 | 967E3968C2B3BFA56DA58FF9 /* README.md */, 115 | B99C0CC5AC0B1360B3D7F1FD /* LICENSE */, 116 | ); 117 | name = "Podspec Metadata"; 118 | sourceTree = ""; 119 | }; 120 | 70AD239EFDA3E268DFE81EEB /* Pods */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 273BA881D0B503C7C37377B9 /* Pods-OBD2-Swift_Example.debug.xcconfig */, 124 | 8BD1F9600CDD4D67A5F6339D /* Pods-OBD2-Swift_Example.release.xcconfig */, 125 | B97A44C9329E830730530372 /* Pods-OBD2-Swift_Tests.debug.xcconfig */, 126 | 3EA8E9D52CD628236396EAFE /* Pods-OBD2-Swift_Tests.release.xcconfig */, 127 | ); 128 | name = Pods; 129 | sourceTree = ""; 130 | }; 131 | 98122E02889430A5105A1D63 /* Frameworks */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | E144880A1951F1FF015E1D42 /* Pods_OBD2_Swift_Example.framework */, 135 | 845E970FE31FAFF7CCF10E08 /* Pods_OBD2_Swift_Tests.framework */, 136 | ); 137 | name = Frameworks; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 607FACCF1AFB9204008FA782 /* OBD2-Swift_Example */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "OBD2-Swift_Example" */; 146 | buildPhases = ( 147 | DFF6433D23FCB1F33C50E668 /* [CP] Check Pods Manifest.lock */, 148 | 607FACCC1AFB9204008FA782 /* Sources */, 149 | 607FACCD1AFB9204008FA782 /* Frameworks */, 150 | 607FACCE1AFB9204008FA782 /* Resources */, 151 | BDC73313ABF071CF2CA189F5 /* [CP] Embed Pods Frameworks */, 152 | 6A5CED72B292CBEFEF3BF477 /* [CP] Copy Pods Resources */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = "OBD2-Swift_Example"; 159 | productName = "OBD2-Swift"; 160 | productReference = 607FACD01AFB9204008FA782 /* OBD2-Swift_Example.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 607FACC81AFB9204008FA782 /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastSwiftUpdateCheck = 0720; 170 | LastUpgradeCheck = 0910; 171 | ORGANIZATIONNAME = CocoaPods; 172 | TargetAttributes = { 173 | 607FACCF1AFB9204008FA782 = { 174 | CreatedOnToolsVersion = 6.3.1; 175 | DevelopmentTeam = J7LBYY3B34; 176 | LastSwiftMigration = 0910; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "OBD2-Swift" */; 181 | compatibilityVersion = "Xcode 3.2"; 182 | developmentRegion = English; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | en, 186 | Base, 187 | ); 188 | mainGroup = 607FACC71AFB9204008FA782; 189 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 607FACCF1AFB9204008FA782 /* OBD2-Swift_Example */, 194 | ); 195 | }; 196 | /* End PBXProject section */ 197 | 198 | /* Begin PBXResourcesBuildPhase section */ 199 | 607FACCE1AFB9204008FA782 /* Resources */ = { 200 | isa = PBXResourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 204 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 205 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 6A5CED72B292CBEFEF3BF477 /* [CP] Copy Pods Resources */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Copy Pods Resources"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-resources.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | BDC73313ABF071CF2CA189F5 /* [CP] Embed Pods Frameworks */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "[CP] Embed Pods Frameworks"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OBD2-Swift_Example/Pods-OBD2-Swift_Example-frameworks.sh\"\n"; 240 | showEnvVarsInLog = 0; 241 | }; 242 | DFF6433D23FCB1F33C50E668 /* [CP] Check Pods Manifest.lock */ = { 243 | isa = PBXShellScriptBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | inputPaths = ( 248 | ); 249 | name = "[CP] Check Pods Manifest.lock"; 250 | outputPaths = ( 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | shellPath = /bin/sh; 254 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 255 | showEnvVarsInLog = 0; 256 | }; 257 | /* End PBXShellScriptBuildPhase section */ 258 | 259 | /* Begin PBXSourcesBuildPhase section */ 260 | 607FACCC1AFB9204008FA782 /* Sources */ = { 261 | isa = PBXSourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 265 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | /* End PBXSourcesBuildPhase section */ 270 | 271 | /* Begin PBXVariantGroup section */ 272 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 273 | isa = PBXVariantGroup; 274 | children = ( 275 | 607FACDA1AFB9204008FA782 /* Base */, 276 | ); 277 | name = Main.storyboard; 278 | sourceTree = ""; 279 | }; 280 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 281 | isa = PBXVariantGroup; 282 | children = ( 283 | 607FACDF1AFB9204008FA782 /* Base */, 284 | ); 285 | name = LaunchScreen.xib; 286 | sourceTree = ""; 287 | }; 288 | /* End PBXVariantGroup section */ 289 | 290 | /* Begin XCBuildConfiguration section */ 291 | 607FACED1AFB9204008FA782 /* Debug */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 296 | CLANG_CXX_LIBRARY = "libc++"; 297 | CLANG_ENABLE_MODULES = YES; 298 | CLANG_ENABLE_OBJC_ARC = YES; 299 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 300 | CLANG_WARN_BOOL_CONVERSION = YES; 301 | CLANG_WARN_COMMA = YES; 302 | CLANG_WARN_CONSTANT_CONVERSION = YES; 303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INFINITE_RECURSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNREACHABLE_CODE = YES; 315 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 316 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 317 | COPY_PHASE_STRIP = NO; 318 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 319 | ENABLE_STRICT_OBJC_MSGSEND = YES; 320 | ENABLE_TESTABILITY = YES; 321 | GCC_C_LANGUAGE_STANDARD = gnu99; 322 | GCC_DYNAMIC_NO_PIC = NO; 323 | GCC_NO_COMMON_BLOCKS = YES; 324 | GCC_OPTIMIZATION_LEVEL = 0; 325 | GCC_PREPROCESSOR_DEFINITIONS = ( 326 | "DEBUG=1", 327 | "$(inherited)", 328 | ); 329 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 337 | MTL_ENABLE_DEBUG_INFO = YES; 338 | ONLY_ACTIVE_ARCH = YES; 339 | SDKROOT = iphoneos; 340 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 341 | }; 342 | name = Debug; 343 | }; 344 | 607FACEE1AFB9204008FA782 /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ALWAYS_SEARCH_USER_PATHS = NO; 348 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 349 | CLANG_CXX_LIBRARY = "libc++"; 350 | CLANG_ENABLE_MODULES = YES; 351 | CLANG_ENABLE_OBJC_ARC = YES; 352 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 353 | CLANG_WARN_BOOL_CONVERSION = YES; 354 | CLANG_WARN_COMMA = YES; 355 | CLANG_WARN_CONSTANT_CONVERSION = YES; 356 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 357 | CLANG_WARN_EMPTY_BODY = YES; 358 | CLANG_WARN_ENUM_CONVERSION = YES; 359 | CLANG_WARN_INFINITE_RECURSION = YES; 360 | CLANG_WARN_INT_CONVERSION = YES; 361 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 362 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 363 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 364 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 365 | CLANG_WARN_STRICT_PROTOTYPES = YES; 366 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 367 | CLANG_WARN_UNREACHABLE_CODE = YES; 368 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 369 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 370 | COPY_PHASE_STRIP = NO; 371 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 372 | ENABLE_NS_ASSERTIONS = NO; 373 | ENABLE_STRICT_OBJC_MSGSEND = YES; 374 | GCC_C_LANGUAGE_STANDARD = gnu99; 375 | GCC_NO_COMMON_BLOCKS = YES; 376 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 377 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 378 | GCC_WARN_UNDECLARED_SELECTOR = YES; 379 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 380 | GCC_WARN_UNUSED_FUNCTION = YES; 381 | GCC_WARN_UNUSED_VARIABLE = YES; 382 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 383 | MTL_ENABLE_DEBUG_INFO = NO; 384 | SDKROOT = iphoneos; 385 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 386 | VALIDATE_PRODUCT = YES; 387 | }; 388 | name = Release; 389 | }; 390 | 607FACF01AFB9204008FA782 /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | baseConfigurationReference = 273BA881D0B503C7C37377B9 /* Pods-OBD2-Swift_Example.debug.xcconfig */; 393 | buildSettings = { 394 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 395 | DEVELOPMENT_TEAM = J7LBYY3B34; 396 | INFOPLIST_FILE = "OBD2-Swift/Info.plist"; 397 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 399 | MODULE_NAME = ExampleApp; 400 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 401 | PRODUCT_NAME = "$(TARGET_NAME)"; 402 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 403 | SWIFT_VERSION = 4.0; 404 | }; 405 | name = Debug; 406 | }; 407 | 607FACF11AFB9204008FA782 /* Release */ = { 408 | isa = XCBuildConfiguration; 409 | baseConfigurationReference = 8BD1F9600CDD4D67A5F6339D /* Pods-OBD2-Swift_Example.release.xcconfig */; 410 | buildSettings = { 411 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 412 | DEVELOPMENT_TEAM = J7LBYY3B34; 413 | INFOPLIST_FILE = "OBD2-Swift/Info.plist"; 414 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 415 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 416 | MODULE_NAME = ExampleApp; 417 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 418 | PRODUCT_NAME = "$(TARGET_NAME)"; 419 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 420 | SWIFT_VERSION = 4.0; 421 | }; 422 | name = Release; 423 | }; 424 | /* End XCBuildConfiguration section */ 425 | 426 | /* Begin XCConfigurationList section */ 427 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "OBD2-Swift" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | 607FACED1AFB9204008FA782 /* Debug */, 431 | 607FACEE1AFB9204008FA782 /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | defaultConfigurationName = Release; 435 | }; 436 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "OBD2-Swift_Example" */ = { 437 | isa = XCConfigurationList; 438 | buildConfigurations = ( 439 | 607FACF01AFB9204008FA782 /* Debug */, 440 | 607FACF11AFB9204008FA782 /* Release */, 441 | ); 442 | defaultConfigurationIsVisible = 0; 443 | defaultConfigurationName = Release; 444 | }; 445 | /* End XCConfigurationList section */ 446 | }; 447 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 448 | } 449 | --------------------------------------------------------------------------------