Skip to content

Commit 6251d20

Browse files
committed
Enable Bluetooth iBeacon advertisement for ESP32
1 parent 60a648b commit 6251d20

24 files changed

+2254
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors.
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
/// Bluetooth address.
13+
public struct BluetoothAddress: Sendable {
14+
15+
// MARK: - Properties
16+
17+
/// Underlying address bytes (host endianess).
18+
public var bytes: ByteValue
19+
20+
// MARK: - Initialization
21+
22+
/// Initialize with the specifed bytes (in host endianess).
23+
public init(bytes: ByteValue = (0, 0, 0, 0, 0, 0)) {
24+
self.bytes = bytes
25+
}
26+
}
27+
28+
public extension BluetoothAddress {
29+
30+
/// The minimum representable value in this type.
31+
static var min: BluetoothAddress { return BluetoothAddress(bytes: (.min, .min, .min, .min, .min, .min)) }
32+
33+
/// The maximum representable value in this type.
34+
static var max: BluetoothAddress { return BluetoothAddress(bytes: (.max, .max, .max, .max, .max, .max)) }
35+
36+
/// A zero address.
37+
static var zero: BluetoothAddress { return .min }
38+
}
39+
40+
// MARK: - ByteValue
41+
42+
extension BluetoothAddress {
43+
44+
/// Raw Bluetooth Address 6 byte (48 bit) value.
45+
public typealias ByteValue = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
46+
47+
public static var bitWidth: Int { return 48 }
48+
49+
public static var length: Int { return 6 }
50+
}
51+
52+
// MARK: - Equatable
53+
54+
extension BluetoothAddress: Equatable {
55+
56+
public static func == (lhs: BluetoothAddress, rhs: BluetoothAddress) -> Bool {
57+
return lhs.bytes.0 == rhs.bytes.0
58+
&& lhs.bytes.1 == rhs.bytes.1
59+
&& lhs.bytes.2 == rhs.bytes.2
60+
&& lhs.bytes.3 == rhs.bytes.3
61+
&& lhs.bytes.4 == rhs.bytes.4
62+
&& lhs.bytes.5 == rhs.bytes.5
63+
}
64+
}
65+
66+
// MARK: - Hashable
67+
68+
extension BluetoothAddress: Hashable {
69+
70+
public func hash(into hasher: inout Hasher) {
71+
withUnsafeBytes(of: bytes) { hasher.combine(bytes: $0) }
72+
}
73+
}
74+
75+
// MARK: - Byte Swap
76+
77+
extension BluetoothAddress: ByteSwap {
78+
79+
/// A representation of this address with the byte order swapped.
80+
public var byteSwapped: BluetoothAddress {
81+
return BluetoothAddress(bytes: (bytes.5, bytes.4, bytes.3, bytes.2, bytes.1, bytes.0))
82+
}
83+
}
84+
85+
// MARK: - RawRepresentable
86+
87+
extension BluetoothAddress: RawRepresentable {
88+
89+
/// Initialize a Bluetooth Address from its big endian string representation (e.g. `00:1A:7D:DA:71:13`).
90+
public init?(rawValue: String) {
91+
self.init(rawValue)
92+
}
93+
94+
/// Initialize a Bluetooth Address from its big endian string representation (e.g. `00:1A:7D:DA:71:13`).
95+
internal init?<S: StringProtocol>(_ rawValue: S) {
96+
97+
// verify string length
98+
let characters = rawValue.utf8
99+
guard characters.count == 17,
100+
let separator = ":".utf8.first
101+
else { return nil }
102+
103+
var bytes: ByteValue = (0, 0, 0, 0, 0, 0)
104+
105+
let components = characters.split(whereSeparator: { $0 == separator })
106+
107+
guard components.count == 6
108+
else { return nil }
109+
110+
for (index, subsequence) in components.enumerated() {
111+
112+
guard subsequence.count == 2,
113+
let byte = UInt8(hexadecimal: subsequence)
114+
else { return nil }
115+
116+
withUnsafeMutablePointer(to: &bytes) {
117+
$0.withMemoryRebound(to: UInt8.self, capacity: 6) {
118+
$0.advanced(by: index).pointee = byte
119+
}
120+
}
121+
}
122+
123+
self.init(bigEndian: BluetoothAddress(bytes: bytes))
124+
}
125+
126+
/// Convert a Bluetooth Address to its big endian string representation (e.g. `00:1A:7D:DA:71:13`).
127+
public var rawValue: String {
128+
let bytes = self.bigEndian.bytes
129+
return bytes.0.toHexadecimal()
130+
+ ":" + bytes.1.toHexadecimal()
131+
+ ":" + bytes.2.toHexadecimal()
132+
+ ":" + bytes.3.toHexadecimal()
133+
+ ":" + bytes.4.toHexadecimal()
134+
+ ":" + bytes.5.toHexadecimal()
135+
}
136+
}
137+
138+
// MARK: - CustomStringConvertible
139+
140+
extension BluetoothAddress: CustomStringConvertible {
141+
142+
public var description: String { rawValue }
143+
}
144+
145+
// MARK: - Data
146+
147+
public extension BluetoothAddress {
148+
149+
init?<Data: DataContainer>(data: Data) {
150+
guard data.count == type(of: self).length
151+
else { return nil }
152+
self.bytes = (data[0], data[1], data[2], data[3], data[4], data[5])
153+
}
154+
}
155+
156+
// MARK: - Codable
157+
158+
#if !hasFeature(Embedded)
159+
extension BluetoothAddress: Codable { }
160+
#endif

esp32-led-blink-sdk/main/BridgingHeader.h

+14
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,17 @@
1515
#include "freertos/task.h"
1616
#include "driver/gpio.h"
1717
#include "sdkconfig.h"
18+
#include "nimble/ble.h"
19+
#include "nimble/transport.h"
20+
#include "host/ble_hs.h"
21+
#include "host/ble_gap.h"
22+
#include "esp_bt.h"
23+
#include "esp_task.h"
24+
#include "esp_nimble_cfg.h"
25+
#include "esp_log.h"
26+
//#include "nvs_flash.h"
27+
#include "esp_bt.h"
28+
29+
#ifndef MYNEWT_VAL_BLE_LL_WHITELIST_SIZE
30+
#define MYNEWT_VAL_BLE_LL_WHITELIST_SIZE CONFIG_BT_NIMBLE_WHITELIST_SIZE
31+
#endif
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors.
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
/// A Bluetooth value that is stored in the CPU native endianess format.
13+
public protocol ByteSwap {
14+
15+
/// A representation of this integer with the byte order swapped.
16+
var byteSwapped: Self { get }
17+
}
18+
19+
public extension ByteSwap {
20+
21+
/// Creates an instance from its little-endian representation, changing the
22+
/// byte order if necessary.
23+
///
24+
/// - Parameter value: A value to use as the little-endian representation of
25+
/// the new instance.
26+
init(littleEndian value: Self) {
27+
#if _endian(little)
28+
self = value
29+
#else
30+
self = value.byteSwapped
31+
#endif
32+
}
33+
34+
/// Creates an instance from its big-endian representation, changing the byte
35+
/// order if necessary.
36+
///
37+
/// - Parameter value: A value to use as the big-endian representation of the
38+
/// new instance.
39+
init(bigEndian value: Self) {
40+
#if _endian(big)
41+
self = value
42+
#else
43+
self = value.byteSwapped
44+
#endif
45+
}
46+
47+
/// The little-endian representation of this value.
48+
///
49+
/// If necessary, the byte order of this value is reversed from the typical
50+
/// byte order of this address. On a little-endian platform, for any
51+
/// address `x`, `x == x.littleEndian`.
52+
var littleEndian: Self {
53+
#if _endian(little)
54+
return self
55+
#else
56+
return byteSwapped
57+
#endif
58+
}
59+
60+
/// The big-endian representation of this value.
61+
///
62+
/// If necessary, the byte order of this value is reversed from the typical
63+
/// byte order of this address. On a big-endian platform, for any
64+
/// address `x`, `x == x.bigEndian`.
65+
var bigEndian: Self {
66+
#if _endian(big)
67+
return self
68+
#else
69+
return byteSwapped
70+
#endif
71+
}
72+
}

esp32-led-blink-sdk/main/CMakeLists.txt

+31-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ idf_component_register(
33
SRCS /dev/null # We don't have any C++ sources
44
PRIV_INCLUDE_DIRS "."
55
LDFRAGMENTS "linker.lf"
6+
REQUIRES bt driver
67
)
78

89
idf_build_get_property(target IDF_TARGET)
@@ -40,16 +41,16 @@ endforeach()
4041

4142
# Swift compiler flags to build in Embedded Swift mode, optimize for size, choose the right ISA, ABI, etc.
4243
target_compile_options(${COMPONENT_LIB} PUBLIC "$<$<COMPILE_LANGUAGE:Swift>:SHELL:
43-
-target riscv32-none-none-eabi
44-
-Xfrontend -function-sections -enable-experimental-feature Embedded -wmo -parse-as-library -Osize
45-
-Xcc -march=${march_flag} -Xcc -mabi=${mabi_flag}
46-
47-
-pch-output-dir /tmp
48-
-Xfrontend -enable-single-module-llvm-emission
49-
50-
${SWIFT_INCLUDES}
44+
-target riscv32-none-none-eabi
45+
-Xfrontend -function-sections -enable-experimental-feature Embedded -wmo -parse-as-library -Osize
46+
-Xcc -march=${march_flag} -Xcc -mabi=${mabi_flag}
47+
48+
-pch-output-dir /tmp
49+
-Xfrontend -enable-single-module-llvm-emission
50+
51+
${SWIFT_INCLUDES}
5152
52-
-import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h
53+
-import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h
5354
>")
5455

5556
# Enable Swift support in CMake, force Whole Module builds (required by Embedded Swift), and use "CMAKE_Swift_COMPILER_WORKS" to
@@ -64,4 +65,25 @@ target_sources(${COMPONENT_LIB}
6465
PRIVATE
6566
Main.swift
6667
Led.swift
68+
Error.swift
69+
NimBLE.swift
70+
Error.swift
71+
BluetoothAddress.swift
72+
ByteSwap.swift
73+
CompanyIdentifier.swift
74+
LowEnergyAddressType.swift
75+
LowEnergyAdvertisingData.swift
76+
Data.swift
77+
String.swift
78+
Hexadecimal.swift
79+
Encoder.swift
80+
GAPData.swift
81+
GAPDataType.swift
82+
GAPFlags.swift
83+
GAPShortLocalName.swift
84+
GAPManufacturerSpecificData.swift
85+
UInt128.swift
86+
UUID.swift
87+
iBeacon.swift
88+
Integer.swift
6789
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors.
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
/// Company identifiers are unique numbers assigned by the Bluetooth SIG to member companies requesting one.
13+
///
14+
/// Each Bluetooth SIG member assigned a Company Identifier may use the assigned value for any/all of the following:
15+
///
16+
/// * LMP_CompID (refer to the Bluetooth® Core Specification)
17+
/// * Company Identifier Code used in Manufacturer Specific Data type used for EIR and Advertising Data Types (refer to CSSv1 or later)
18+
/// * Company ID for vendor specific codecs (refer to Vol. 2, Part E, of the Bluetooth Core Specification, v4.1 or later)
19+
/// * As the lower 16 bits of the Vendor ID for designating Vendor Specific A2DP Codecs (refer to the A2DP v1.3 or later
20+
/// * VendorID Attribute in Device ID service record (when VendorIDSourceAttribute equals 0x0001, refer toDevice ID Profile)
21+
/// * 802.11_PAL_Company_Identifier (refer to Bluetooth Core Specification v3.0 + HS or later)
22+
/// * TCS Company ID (refer to Telephony Control Protocol [[WITHDRAWN](https://www.bluetooth.com/specifications)])
23+
///
24+
/// Each of the adopted specifications listed above can be found on the [Adopted Specifications Page](https://www.bluetooth.com/specifications)
25+
/// unless it is otherwise indicated as withdrawn.
26+
///
27+
/// - SeeAlso: [Company Identifiers](https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers)
28+
public struct CompanyIdentifier: RawRepresentable, Equatable, Hashable, Sendable {
29+
30+
public var rawValue: UInt16
31+
32+
public init(rawValue: UInt16) {
33+
self.rawValue = rawValue
34+
}
35+
}
36+
37+
// MARK: - ExpressibleByIntegerLiteral
38+
39+
extension CompanyIdentifier: ExpressibleByIntegerLiteral {
40+
41+
public init(integerLiteral value: UInt16) {
42+
self.init(rawValue: value)
43+
}
44+
}
45+
46+
// MARK: - CustomStringConvertible
47+
48+
extension CompanyIdentifier: CustomStringConvertible {
49+
50+
public var description: String {
51+
return rawValue.description
52+
}
53+
}

0 commit comments

Comments
 (0)