├── .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 | [](https://github.com/lemberg/obd2-swift-lib)
4 | [](https://github.com/lemberg/obd2-swift-lib)
5 | [](https://github.com/lemberg/obd2-swift-lib)
6 | [](https://github.com/lemberg/obd2-swift-lib/blob/dev/LICENSE)
7 | [](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 | [](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 |
111 |
112 |
113 |
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 |
--------------------------------------------------------------------------------