├── .gitignore ├── LICENSE ├── Microbit.playground.zip ├── Microbit ├── Microbit.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── peterwallen.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── peterwallen.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── Microbit │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Microbit.swift │ ├── MicrobitUI.swift │ ├── MicrobitUIController.swift │ └── README.md ├── README.md ├── docs ├── images │ ├── makecode-Beacon.png │ ├── makecode-Events.png │ ├── makecode_Button.png │ ├── makecode_UART.png │ └── microbit_playground_menu.png └── index.md └── mkdocs.yml /.gitignore: -------------------------------------------------------------------------------- 1 | /site 2 | .DS_Store 3 | Microbit/.DS_Store 4 | Microbit/Microbit/.DS_Store 5 | Microbit.playground/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Peter Wallen 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 | -------------------------------------------------------------------------------- /Microbit.playground.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phwallen/microbit-swift/bef6036b7ffc1af973e768812941e87b5fd5f64a/Microbit.playground.zip -------------------------------------------------------------------------------- /Microbit/Microbit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1B43F8071FA33958002EB52E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B43F8061FA33958002EB52E /* AppDelegate.swift */; }; 11 | 1B43F80C1FA33958002EB52E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1B43F80A1FA33958002EB52E /* Main.storyboard */; }; 12 | 1B43F80E1FA33958002EB52E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1B43F80D1FA33958002EB52E /* Assets.xcassets */; }; 13 | 1B43F8111FA33958002EB52E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1B43F80F1FA33958002EB52E /* LaunchScreen.storyboard */; }; 14 | 1BBF74501FC6E9790009B385 /* MicrobitUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBF744F1FC6E9790009B385 /* MicrobitUI.swift */; }; 15 | 1BCF12911FDEB62100F608F8 /* MicrobitUIController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BCF12901FDEB62100F608F8 /* MicrobitUIController.swift */; }; 16 | 1BD69CA91FC6F63400C95A32 /* Microbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD69CA81FC6F63400C95A32 /* Microbit.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 1B02F2891FB0FA40005E32D7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 21 | 1B43F8031FA33958002EB52E /* Microbit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Microbit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 1B43F8061FA33958002EB52E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 1B43F80B1FA33958002EB52E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 24 | 1B43F80D1FA33958002EB52E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | 1B43F8101FA33958002EB52E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 26 | 1B43F8121FA33958002EB52E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 1BBF744F1FC6E9790009B385 /* MicrobitUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MicrobitUI.swift; sourceTree = ""; }; 28 | 1BCF12901FDEB62100F608F8 /* MicrobitUIController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MicrobitUIController.swift; sourceTree = ""; }; 29 | 1BD69CA81FC6F63400C95A32 /* Microbit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Microbit.swift; sourceTree = ""; }; 30 | /* End PBXFileReference section */ 31 | 32 | /* Begin PBXFrameworksBuildPhase section */ 33 | 1B43F8001FA33958002EB52E /* Frameworks */ = { 34 | isa = PBXFrameworksBuildPhase; 35 | buildActionMask = 2147483647; 36 | files = ( 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 1B43F7FA1FA33958002EB52E = { 44 | isa = PBXGroup; 45 | children = ( 46 | 1B43F8051FA33958002EB52E /* Microbit */, 47 | 1B43F8041FA33958002EB52E /* Products */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | 1B43F8041FA33958002EB52E /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 1B43F8031FA33958002EB52E /* Microbit.app */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | 1B43F8051FA33958002EB52E /* Microbit */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 1B43F8061FA33958002EB52E /* AppDelegate.swift */, 63 | 1BD69CA81FC6F63400C95A32 /* Microbit.swift */, 64 | 1BCF12901FDEB62100F608F8 /* MicrobitUIController.swift */, 65 | 1BBF744F1FC6E9790009B385 /* MicrobitUI.swift */, 66 | 1B43F80A1FA33958002EB52E /* Main.storyboard */, 67 | 1B43F80F1FA33958002EB52E /* LaunchScreen.storyboard */, 68 | 1B43F8121FA33958002EB52E /* Info.plist */, 69 | 1B43F80D1FA33958002EB52E /* Assets.xcassets */, 70 | 1B02F2891FB0FA40005E32D7 /* README.md */, 71 | ); 72 | path = Microbit; 73 | sourceTree = ""; 74 | }; 75 | /* End PBXGroup section */ 76 | 77 | /* Begin PBXNativeTarget section */ 78 | 1B43F8021FA33958002EB52E /* Microbit */ = { 79 | isa = PBXNativeTarget; 80 | buildConfigurationList = 1B43F8151FA33958002EB52E /* Build configuration list for PBXNativeTarget "Microbit" */; 81 | buildPhases = ( 82 | 1B43F7FF1FA33958002EB52E /* Sources */, 83 | 1B43F8001FA33958002EB52E /* Frameworks */, 84 | 1B43F8011FA33958002EB52E /* Resources */, 85 | ); 86 | buildRules = ( 87 | ); 88 | dependencies = ( 89 | ); 90 | name = Microbit; 91 | productName = Microbit; 92 | productReference = 1B43F8031FA33958002EB52E /* Microbit.app */; 93 | productType = "com.apple.product-type.application"; 94 | }; 95 | /* End PBXNativeTarget section */ 96 | 97 | /* Begin PBXProject section */ 98 | 1B43F7FB1FA33958002EB52E /* Project object */ = { 99 | isa = PBXProject; 100 | attributes = { 101 | LastSwiftUpdateCheck = 0900; 102 | LastUpgradeCheck = 0900; 103 | ORGANIZATIONNAME = "Peter Wallen"; 104 | TargetAttributes = { 105 | 1B43F8021FA33958002EB52E = { 106 | CreatedOnToolsVersion = 9.0.1; 107 | ProvisioningStyle = Automatic; 108 | }; 109 | }; 110 | }; 111 | buildConfigurationList = 1B43F7FE1FA33958002EB52E /* Build configuration list for PBXProject "Microbit" */; 112 | compatibilityVersion = "Xcode 8.0"; 113 | developmentRegion = en; 114 | hasScannedForEncodings = 0; 115 | knownRegions = ( 116 | en, 117 | Base, 118 | ); 119 | mainGroup = 1B43F7FA1FA33958002EB52E; 120 | productRefGroup = 1B43F8041FA33958002EB52E /* Products */; 121 | projectDirPath = ""; 122 | projectRoot = ""; 123 | targets = ( 124 | 1B43F8021FA33958002EB52E /* Microbit */, 125 | ); 126 | }; 127 | /* End PBXProject section */ 128 | 129 | /* Begin PBXResourcesBuildPhase section */ 130 | 1B43F8011FA33958002EB52E /* Resources */ = { 131 | isa = PBXResourcesBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | 1B43F8111FA33958002EB52E /* LaunchScreen.storyboard in Resources */, 135 | 1B43F80E1FA33958002EB52E /* Assets.xcassets in Resources */, 136 | 1B43F80C1FA33958002EB52E /* Main.storyboard in Resources */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXResourcesBuildPhase section */ 141 | 142 | /* Begin PBXSourcesBuildPhase section */ 143 | 1B43F7FF1FA33958002EB52E /* Sources */ = { 144 | isa = PBXSourcesBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | 1BD69CA91FC6F63400C95A32 /* Microbit.swift in Sources */, 148 | 1BCF12911FDEB62100F608F8 /* MicrobitUIController.swift in Sources */, 149 | 1BBF74501FC6E9790009B385 /* MicrobitUI.swift in Sources */, 150 | 1B43F8071FA33958002EB52E /* AppDelegate.swift in Sources */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXSourcesBuildPhase section */ 155 | 156 | /* Begin PBXVariantGroup section */ 157 | 1B43F80A1FA33958002EB52E /* Main.storyboard */ = { 158 | isa = PBXVariantGroup; 159 | children = ( 160 | 1B43F80B1FA33958002EB52E /* Base */, 161 | ); 162 | name = Main.storyboard; 163 | sourceTree = ""; 164 | }; 165 | 1B43F80F1FA33958002EB52E /* LaunchScreen.storyboard */ = { 166 | isa = PBXVariantGroup; 167 | children = ( 168 | 1B43F8101FA33958002EB52E /* Base */, 169 | ); 170 | name = LaunchScreen.storyboard; 171 | sourceTree = ""; 172 | }; 173 | /* End PBXVariantGroup section */ 174 | 175 | /* Begin XCBuildConfiguration section */ 176 | 1B43F8131FA33958002EB52E /* Debug */ = { 177 | isa = XCBuildConfiguration; 178 | buildSettings = { 179 | ALWAYS_SEARCH_USER_PATHS = NO; 180 | CLANG_ANALYZER_NONNULL = YES; 181 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 182 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 183 | CLANG_CXX_LIBRARY = "libc++"; 184 | CLANG_ENABLE_MODULES = YES; 185 | CLANG_ENABLE_OBJC_ARC = YES; 186 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 187 | CLANG_WARN_BOOL_CONVERSION = YES; 188 | CLANG_WARN_COMMA = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 192 | CLANG_WARN_EMPTY_BODY = YES; 193 | CLANG_WARN_ENUM_CONVERSION = YES; 194 | CLANG_WARN_INFINITE_RECURSION = YES; 195 | CLANG_WARN_INT_CONVERSION = YES; 196 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 197 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 199 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 200 | CLANG_WARN_STRICT_PROTOTYPES = YES; 201 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 202 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 203 | CLANG_WARN_UNREACHABLE_CODE = YES; 204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 205 | CODE_SIGN_IDENTITY = "iPhone Developer"; 206 | COPY_PHASE_STRIP = NO; 207 | DEBUG_INFORMATION_FORMAT = dwarf; 208 | ENABLE_STRICT_OBJC_MSGSEND = YES; 209 | ENABLE_TESTABILITY = YES; 210 | GCC_C_LANGUAGE_STANDARD = gnu11; 211 | GCC_DYNAMIC_NO_PIC = NO; 212 | GCC_NO_COMMON_BLOCKS = YES; 213 | GCC_OPTIMIZATION_LEVEL = 0; 214 | GCC_PREPROCESSOR_DEFINITIONS = ( 215 | "DEBUG=1", 216 | "$(inherited)", 217 | ); 218 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 219 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 220 | GCC_WARN_UNDECLARED_SELECTOR = YES; 221 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 222 | GCC_WARN_UNUSED_FUNCTION = YES; 223 | GCC_WARN_UNUSED_VARIABLE = YES; 224 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 225 | MTL_ENABLE_DEBUG_INFO = YES; 226 | ONLY_ACTIVE_ARCH = YES; 227 | SDKROOT = iphoneos; 228 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 229 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 230 | }; 231 | name = Debug; 232 | }; 233 | 1B43F8141FA33958002EB52E /* Release */ = { 234 | isa = XCBuildConfiguration; 235 | buildSettings = { 236 | ALWAYS_SEARCH_USER_PATHS = NO; 237 | CLANG_ANALYZER_NONNULL = YES; 238 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 248 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 255 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 256 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 257 | CLANG_WARN_STRICT_PROTOTYPES = YES; 258 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 259 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | CODE_SIGN_IDENTITY = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu11; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 279 | VALIDATE_PRODUCT = YES; 280 | }; 281 | name = Release; 282 | }; 283 | 1B43F8161FA33958002EB52E /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 287 | CODE_SIGN_STYLE = Automatic; 288 | DEVELOPMENT_TEAM = E8Z3YH23TC; 289 | INFOPLIST_FILE = Microbit/Info.plist; 290 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 291 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 292 | PRODUCT_BUNDLE_IDENTIFIER = com.wallen.Microbit; 293 | PRODUCT_NAME = "$(TARGET_NAME)"; 294 | SWIFT_VERSION = 4.0; 295 | TARGETED_DEVICE_FAMILY = "1,2"; 296 | }; 297 | name = Debug; 298 | }; 299 | 1B43F8171FA33958002EB52E /* Release */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 303 | CODE_SIGN_STYLE = Automatic; 304 | DEVELOPMENT_TEAM = E8Z3YH23TC; 305 | INFOPLIST_FILE = Microbit/Info.plist; 306 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 307 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 308 | PRODUCT_BUNDLE_IDENTIFIER = com.wallen.Microbit; 309 | PRODUCT_NAME = "$(TARGET_NAME)"; 310 | SWIFT_VERSION = 4.0; 311 | TARGETED_DEVICE_FAMILY = "1,2"; 312 | }; 313 | name = Release; 314 | }; 315 | /* End XCBuildConfiguration section */ 316 | 317 | /* Begin XCConfigurationList section */ 318 | 1B43F7FE1FA33958002EB52E /* Build configuration list for PBXProject "Microbit" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | 1B43F8131FA33958002EB52E /* Debug */, 322 | 1B43F8141FA33958002EB52E /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | 1B43F8151FA33958002EB52E /* Build configuration list for PBXNativeTarget "Microbit" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | 1B43F8161FA33958002EB52E /* Debug */, 331 | 1B43F8171FA33958002EB52E /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | /* End XCConfigurationList section */ 337 | }; 338 | rootObject = 1B43F7FB1FA33958002EB52E /* Project object */; 339 | } 340 | -------------------------------------------------------------------------------- /Microbit/Microbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Microbit/Microbit.xcodeproj/project.xcworkspace/xcuserdata/peterwallen.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phwallen/microbit-swift/bef6036b7ffc1af973e768812941e87b5fd5f64a/Microbit/Microbit.xcodeproj/project.xcworkspace/xcuserdata/peterwallen.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Microbit/Microbit.xcodeproj/xcuserdata/peterwallen.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Microbit.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Microbit/Microbit/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Microbit 4 | // 5 | // Created by Peter Wallen on 27/10/2017. 6 | // Copyright © 2017 Peter Wallen. 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 | 19 | // Test the view controllers used by the Microbit Playground. 20 | // This application does not use storyboards. 21 | // It's designed to simulate the Microbit Playground by setting the rootViewController 22 | // to an instance of MIcrobitUIController 23 | 24 | window = UIWindow(frame: UIScreen.main.bounds) 25 | window?.makeKeyAndVisible() 26 | 27 | let microbitUIController = MicrobitUIController() 28 | 29 | //microbitUIController.microbit = Microbit("BBC Microbit") 30 | microbitUIController.microbit = Microbit("BBC micro:bit [tizip]") 31 | 32 | window?.rootViewController = microbitUIController 33 | 34 | return true 35 | } 36 | 37 | func applicationWillResignActive(_ application: UIApplication) { 38 | // 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. 39 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 40 | } 41 | 42 | func applicationDidEnterBackground(_ application: UIApplication) { 43 | // 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. 44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 45 | } 46 | 47 | func applicationWillEnterForeground(_ application: UIApplication) { 48 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 49 | } 50 | 51 | func applicationDidBecomeActive(_ application: UIApplication) { 52 | // 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. 53 | } 54 | 55 | func applicationWillTerminate(_ application: UIApplication) { 56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 57 | } 58 | 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /Microbit/Microbit/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Microbit/Microbit/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /Microbit/Microbit/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Microbit/Microbit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Microbit/Microbit/Microbit.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Microbit.swift 3 | 4 | Created by Peter Wallen on 06/11/2017 5 | Version 1.0 6 | 7 | Copyright © 2018 Peter Wallen. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | 27 | 28 | This file contains the Microbit class and suporting types and protocol definitions. 29 | The Microbit claas encapusaltes a Swift implementation of an interface with the micro:bit 30 | using the Apple Core Bluetooth API. 31 | 32 | This interface supports applications developed for iOS, macOS, tvOS and watchOS 33 | */ 34 | 35 | import Foundation 36 | import CoreBluetooth 37 | 38 | /** 39 | The MicrobitDelegate protocol defines the methods that a delegate of a micro:bit object must adopt. 40 | */ 41 | public protocol MicrobitDelegate { 42 | 43 | func logUpdated(_ log:[String]) 44 | func advertisementData(url:String,namespace:Int64,instance:Int32,RSSI:Int) 45 | func serviceAvailable(service:ServiceName) 46 | func uartReceived(message:String) 47 | func pinGet(pins:[UInt8:UInt8]) 48 | func buttonPressed(button:String,action:MicrobitButtonType) 49 | func accelerometerData(x:Int16,y:Int16,z:Int16) 50 | func magnetometerData(x:Int16,y:Int16,z:Int16) 51 | func compass(bearing:Int16) 52 | func microbitEvent(type:Int16,value:Int16) 53 | func temperature(value:Int16) 54 | } 55 | /** 56 | Provide default dummy definitions for the MicrobitDelegate protocol 57 | to prevent unnecessary functions being implemeted in conforming classes. 58 | */ 59 | extension MicrobitDelegate { 60 | func logUpdated(_ log:[String]) {} 61 | func advertisementData(url:String,namespace:Int64,instance:Int32,RSSI:Int) {} 62 | func serviceAvailable(service:ServiceName) {} 63 | func uartReceived(message:String) {} 64 | func pinGet(pins:[UInt8:UInt8]) {} 65 | func buttonPressed(button:String,action:MicrobitButtonType) {} 66 | func accelerometerData(x:Int16,y:Int16,z:Int16) {} 67 | func magnetometerData(x:Int16,y:Int16,z:Int16) {} 68 | func compass(bearing:Int16) {} 69 | func microbitEvent(type:Int16,value:Int16) {} 70 | func temperature(value:Int16) {} 71 | } 72 | /** 73 | Services available from a micro:bit peripheral 74 | */ 75 | public enum ServiceName { 76 | case Event 77 | case DeviceInfo 78 | case Accelerometer 79 | case Magnetometer 80 | case Button 81 | case IOPin 82 | case LED 83 | case Temperature 84 | case UART 85 | } 86 | /** 87 | Button states enumerated by the micro:bit button service 88 | */ 89 | public enum MicrobitButtonType:UInt8 { 90 | case Up 91 | case Down 92 | case Long 93 | case Invalid 94 | } 95 | /** 96 | Magnetometer and Accelerometer reporting periods in milliseconds 97 | */ 98 | public enum PeriodType:UInt16 { 99 | case p1 = 1 100 | case p2 = 2 101 | case p5 = 5 102 | case p10 = 10 103 | case p20 = 20 104 | case p80 = 80 105 | case p160 = 160 106 | case p640 = 640 107 | } 108 | /** 109 | Available events that can be detected by the micro:bit using control.onEvent 110 | */ 111 | public enum MicrobitEvent:Int16 { 112 | case MES_DEVICE_INFO_ID = 1103 113 | case MES_SIGNAL_STRENGTH_ID = 1101 114 | case MES_DPAD_CONTROLLER_ID = 1104 115 | case MES_BROADCAST_GENERAL_ID = 2000 116 | } 117 | 118 | /** 119 | This class uses Core Bluetooth to implement an Application Programing Interface for the micro:bit. 120 | It implements parts of the Generic Attribute Profile (GATT) that forms the micro:bit bluetooth 121 | specification. 122 | 123 | [The microbit GATT Profile](https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html) 124 | 125 | For an overview of GATT see: [A developers guide to bluetooth](http://blog.bluetooth.com/a-developers-guide-to-bluetooth) 126 | 127 | For further information on the micro:bit's implemetation of bluetooth see Martin Woolley's articles: 128 | [Part1](http://blog.bluetooth.com/bbc-microbit-inspiring-generation-get-creative-coding) 129 | [Part2](http://blog.bluetooth.com/bluetooth-bbc-microbit) 130 | [Part3](http://blog.bluetooth.com/developing-applications-bbc-microbit) 131 | 132 | */ 133 | public class Microbit: NSObject,CBCentralManagerDelegate,CBPeripheralDelegate { 134 | 135 | // MARK: Properties 136 | 137 | /** 138 | public property containing an instance of the class implementing the 139 | MicrobitDelegate protocol 140 | */ 141 | public var delegate: MicrobitDelegate? 142 | /** 143 | This name must be provided when intializing an instance of Microbit class. It is used to scan for 144 | micro:bit peripheral. 145 | */ 146 | private var deviceName:String 147 | /** 148 | property represents the microbit client i.e the apple device. 149 | corebluetooth knows this as the Central Manager. 150 | */ 151 | private var centralManager : CBCentralManager! 152 | /** 153 | property repreesents the microbit computer 154 | corebluetooth knows this as a Peripheral 155 | */ 156 | private var microbitPeripheral : CBPeripheral! 157 | /** 158 | flag is set to true by centralManagerDidUpdateState if bluetooth LE 159 | is available. 160 | The microbit Bluetooth API can only be use if this flag is true 161 | */ 162 | private var bleON = false 163 | /** 164 | string buffer to hold diagnostic messages. 165 | Buffer holds a maximum of MAX_BUFFER_ENTRIES before oldest entry is removed 166 | */ 167 | public var log = [String]() 168 | private let MAX_BUFFER_ENTRIES = 100 169 | 170 | /** 171 | public variables containg device information. 172 | This variables only contain information once the appropriate device information characteristic 173 | has been discovered. Therefore this variables should not be read until the MicrobitDelegate function 174 | serviceAvaialble:serviceName:DeviceInfo has been called. 175 | */ 176 | public var modelNumber:String = "n/a" 177 | public var serialNumber:String = "n/a" 178 | public var firmwareRevision:String = "n/a" 179 | 180 | // MARK: GATT Profile 181 | 182 | // DEVICE INFORMATION 183 | let DeviceInfoUUID = CBUUID(string:"180A") 184 | // Read 185 | let ModelNumberCharacteristicUUID = CBUUID(string:"2A24") 186 | var modelNumberCharacteristic:CBCharacteristic? 187 | // Read 188 | let SerialNumberCharacteristicUUID = CBUUID(string:"2A25") 189 | var serialNumberCharacteristic:CBCharacteristic? 190 | // Read 191 | let FirmwareRevisionCharacteristicUUID = CBUUID(string:"2A26") 192 | var firmwareRevisionCharacteristic:CBCharacteristic? 193 | 194 | // ACCELEROMETER SERVICE 195 | let AccelerometerServiceUUID = CBUUID(string:"E95D0753-251D-470A-A062-FA1922DFA9A8") 196 | // Notify,Read 197 | let AccelerometerDataCharacteristicUUID = CBUUID(string:"E95DCA4B-251D-470A-A062-FA1922DFA9A8") 198 | var accelerometerDataCharacteristic:CBCharacteristic? 199 | // Write 200 | let AccelerometerPeriodCharacteristicUUID = CBUUID(string:"E95DFB24-251D-470A-A062-FA1922DFA9A8") 201 | var accelerometerPeriodCharacteristic:CBCharacteristic? 202 | 203 | // MAGNETOMETER SERVICE 204 | let MagnetometerServiceUUID = CBUUID(string: "E95DF2D8-251D-470A-A062-FA1922DFA9A8") 205 | // Notify, Read 206 | let MagnetometerDataCharacteristicUUID = CBUUID(string:"E95DFB11-251D-470A-A062-FA1922DFA9A8") 207 | var magnetometerDataCharacteristic:CBCharacteristic? 208 | // Write 209 | let MagnetometerPeriodCharacteristicUUID = CBUUID(string: "E95D386C-251D-470A-A062-FA1922DFA9A8") 210 | var magnetometerPeriodCharacterictic:CBCharacteristic? 211 | // Notify, Read 212 | let MagnetometerBearingCharacteristicUUID = CBUUID(string: "E95D9715-251D-470A-A062-FA1922DFA9A8") 213 | var magnetometerBearingCharacteristic:CBCharacteristic? 214 | 215 | // BUTTON SERVICE 216 | let ButtonServiceUUID = CBUUID(string: "E95D9882-251D-470A-A062-FA1922DFA9A8") 217 | // Notify, Read 218 | let ButtonAStateCharacteristicUUID = CBUUID(string: "E95DDA90-251D-470A-A062-FA1922DFA9A8") 219 | var buttonAStateCharacteristic:CBCharacteristic? 220 | // Notify, Read 221 | let ButtonBStateCharacteristicUUID = CBUUID(string: "E95DDA91-251D-470A-A062-FA1922DFA9A8") 222 | var buttonBStateCharacteristic:CBCharacteristic? 223 | 224 | // IO PIN SERVICE 225 | let IOpinServiceUUID = CBUUID( string:"E95D127B-251D-470A-A062-FA1922DFA9A8") 226 | // Write 227 | let PinDataCharacteristicUUID = CBUUID(string: "E95D8D00-251D-470A-A062-FA1922DFA9A8") 228 | var pinDataCharacteristic:CBCharacteristic? 229 | // Write 230 | let PinADCharacteristicUUID = CBUUID(string: "E95D5899-251D-470A-A062-FA1922DFA9A8") 231 | var pinADCharacteristic:CBCharacteristic? 232 | // Notify, Read, Write 233 | let PinIOCharacteristicUUID = CBUUID(string: "E95DB9FE-251D-470A-A062-FA1922DFA9A8") 234 | var pinIOCharacteristic:CBCharacteristic? 235 | 236 | // LED SERVICE 237 | let LEDServiceUUID = CBUUID(string:"E95DD91D-251D-470A-A062-FA1922DFA9A8") 238 | // Read,Write 239 | let LEDMAtrixStateCharacteristicUUID = CBUUID(string:"E95D7B77-251D-470A-A062-FA1922DFA9A8") 240 | var ledMatrixStateCharacteristic:CBCharacteristic? 241 | // Write 242 | let LEDTextCharacteristicUUID = CBUUID(string:"E95D93EE-251D-470A-A062-FA1922DFA9A8") 243 | var ledTextCharacteristic:CBCharacteristic? 244 | // Write 245 | let ScrollingDelayCharacteristicUUID = CBUUID(string:"E95D0D2D-251D-470A-A062-FA1922DFA9A8") 246 | var scrollingDelayCharacteristic:CBCharacteristic? 247 | 248 | // EVENT SERVICE 249 | let EventServiceUUID = CBUUID(string: "E95D93AF-251D-470A-A062-FA1922DFA9A8") 250 | // Client Requirement - a list of events on the microbit that the client should be informed of 251 | // Write 252 | let ClientRequirementCharacteristicUUID = CBUUID(string: "E95D23C4-251D-470A-A062-FA1922DFA9A8") 253 | var clientRequirementCharacteristic:CBCharacteristic? 254 | // Microbit Event - an event occuring on the microbit that the client has requested 255 | // Notify,Read 256 | let MicrobitEventCharacteristicUUID = CBUUID(string: "E95D9775-251D-470A-A062-FA1922DFA9A8") 257 | var microbitEventCharacteristic:CBCharacteristic? 258 | // Client Event - Events (commands) issued on the client and sent to the microbit 259 | // Write 260 | let ClientEventCharacteristicUUID = CBUUID(string: "E95D5404-251D-470A-A062-FA1922DFA9A8") 261 | var clientEventCharacteristic:CBCharacteristic? 262 | 263 | // TEMPERATURE SERVICE 264 | let TempertureServiceUUID = CBUUID(string:"E95D6100-251D-470A-A062-FA1922DFA9A8") 265 | // Notify,Read 266 | let TemperatureCharacteristicUUID = CBUUID(string:"E95D9250-251D-470A-A062-FA1922DFA9A8") 267 | var temperatureCharacteristic:CBCharacteristic? 268 | // Write 269 | let TemperaturePeriodCharacteristicUUID = CBUUID(string:"E95D1B25-251D-470A-A062-FA1922DFA9A8") 270 | var temperaturePeriodCharacteristic:CBCharacteristic? 271 | 272 | // UART SERVICE 273 | let UARTServiceUUID = CBUUID(string:"6E400001-B5A3-F393-E0A9-E50E24DCCA9E") 274 | // RX - Send data to microbit 275 | // Write 276 | let UART_RX_CharacteristicUUID = CBUUID(string:"6E400003-B5A3-F393-E0A9-E50E24DCCA9E") 277 | var uartRXcharacteristic:CBCharacteristic? 278 | // TX - Receive data from the microbit 279 | // Notify, Read 280 | let UART_TX_CharacteristicUUID = CBUUID(string:"6E400002-B5A3-F393-E0A9-E50E24DCCA9E") 281 | var uartTXcharacteristic:CBCharacteristic? 282 | 283 | // MARK: Initialization of class instance 284 | 285 | public init(_ deviceName:String) { 286 | self.deviceName = deviceName 287 | super.init() 288 | centralManager = CBCentralManager(delegate: self, queue: nil) 289 | } 290 | 291 | // MARK: Core bluetooth Central Manager Delegate methods 292 | 293 | public func centralManagerDidUpdateState(_ central: CBCentralManager) { 294 | if (central.state == .poweredOn) { 295 | bleON = true 296 | log("Bluetooth is available") 297 | } else { 298 | bleON = false 299 | log("Bluetooth switched off or not initialized") 300 | } 301 | } 302 | 303 | public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { 304 | var nameOfDeviceFound = "n/a" 305 | if let device = (advertisementData as NSDictionary).object(forKey: CBAdvertisementDataLocalNameKey) as? String { 306 | log("Possible device detected: \(device)") 307 | nameOfDeviceFound = device 308 | } 309 | if let serviceData = advertisementData[CBAdvertisementDataServiceDataKey] 310 | as? NSDictionary { 311 | serviceDataAnalyzer(serviceData: serviceData, RSSI: RSSI) 312 | } 313 | if (nameOfDeviceFound == deviceName) { 314 | log("OK Device \(nameOfDeviceFound) found - stop looking") 315 | // Stop scanning 316 | stopScanning() 317 | // Set as the periheral to use and establish connection 318 | microbitPeripheral = peripheral 319 | microbitPeripheral.delegate = self 320 | centralManager.connect(peripheral, options: nil) 321 | } else { 322 | log("Looking for \(deviceName)") 323 | } 324 | } 325 | 326 | public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { 327 | log("Connected to \(deviceName)") 328 | peripheral.discoverServices(nil) 329 | } 330 | 331 | // MARK: Core bluetooth Perioheral Delegate methods 332 | 333 | public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { 334 | log("Looking for peripheral services") 335 | for service in peripheral.services! { 336 | let thisService = service as CBService 337 | log("Service UUID = \(thisService.uuid)") 338 | peripheral.discoverCharacteristics(nil, for: thisService) 339 | } 340 | } 341 | 342 | 343 | public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { 344 | log("Discovering Characteristics") 345 | for characteristic in service.characteristics! { 346 | let thisCharacteristic = characteristic as CBCharacteristic 347 | log("Characteristic UUID = \(thisCharacteristic.uuid)") 348 | 349 | switch thisCharacteristic.uuid { 350 | case ModelNumberCharacteristicUUID : 351 | log("Model Number Charateristic found") 352 | modelNumberCharacteristic = thisCharacteristic 353 | microbitPeripheral.readValue(for: modelNumberCharacteristic!) 354 | case SerialNumberCharacteristicUUID : 355 | log("Serial Number Charateristic found") 356 | serialNumberCharacteristic = thisCharacteristic 357 | microbitPeripheral.readValue(for: serialNumberCharacteristic!) 358 | case FirmwareRevisionCharacteristicUUID : 359 | log("Firmware Revision Charateristic found") 360 | firmwareRevisionCharacteristic = thisCharacteristic 361 | delegate?.serviceAvailable(service: .DeviceInfo) 362 | microbitPeripheral.readValue(for: firmwareRevisionCharacteristic!) 363 | case ClientRequirementCharacteristicUUID : 364 | log("Writing to the client requirements characteristic") 365 | clientRequirementCharacteristic = thisCharacteristic 366 | delegate?.serviceAvailable(service: .Event) 367 | // write a value to force pairing 368 | registerEvents(events:[9010]) 369 | 370 | case ClientEventCharacteristicUUID : 371 | log("Client Event Characteristic Found") 372 | clientEventCharacteristic = thisCharacteristic 373 | case MicrobitEventCharacteristicUUID : 374 | log("Microbit event characteristic found") 375 | clientRequirementCharacteristic = thisCharacteristic 376 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 377 | case UART_RX_CharacteristicUUID : 378 | log("UART RX characteristic found") 379 | uartRXcharacteristic = thisCharacteristic 380 | case UART_TX_CharacteristicUUID : 381 | log("UART TX characteristic found") 382 | uartTXcharacteristic = thisCharacteristic 383 | delegate?.serviceAvailable(service: .UART) 384 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 385 | case LEDTextCharacteristicUUID : 386 | log("LED text characteristic found") 387 | ledTextCharacteristic = thisCharacteristic 388 | case ScrollingDelayCharacteristicUUID : 389 | log("LED scrolling text characteristic found") 390 | scrollingDelayCharacteristic = thisCharacteristic 391 | case LEDMAtrixStateCharacteristicUUID : 392 | log("LED matrix state characteristic found") 393 | ledMatrixStateCharacteristic = thisCharacteristic 394 | delegate?.serviceAvailable(service: .LED) 395 | case PinADCharacteristicUUID : 396 | log("Pin Analogue/Digital configuration characteristic found") 397 | pinADCharacteristic = thisCharacteristic 398 | case PinIOCharacteristicUUID : 399 | log("Pin Input/Output configuration characteristic found") 400 | pinIOCharacteristic = thisCharacteristic 401 | case PinDataCharacteristicUUID : 402 | log("Pin Data characteristic found") 403 | pinDataCharacteristic = thisCharacteristic 404 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 405 | delegate?.serviceAvailable(service: .IOPin) 406 | case ButtonAStateCharacteristicUUID : 407 | log("Button A state characteristic found") 408 | buttonAStateCharacteristic = thisCharacteristic 409 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 410 | case ButtonBStateCharacteristicUUID : 411 | log("Button B state characteristic found") 412 | buttonBStateCharacteristic = thisCharacteristic 413 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 414 | delegate?.serviceAvailable(service: .Button) 415 | case AccelerometerDataCharacteristicUUID : 416 | log("Accelerometer data characteristic found") 417 | accelerometerDataCharacteristic = thisCharacteristic 418 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 419 | case AccelerometerPeriodCharacteristicUUID : 420 | log("Accelerometer period characteristic found") 421 | accelerometerPeriodCharacteristic = thisCharacteristic 422 | delegate?.serviceAvailable(service: .Accelerometer) 423 | case MagnetometerDataCharacteristicUUID : 424 | log("Magnetometer data characteristic found") 425 | magnetometerDataCharacteristic = thisCharacteristic 426 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 427 | case MagnetometerPeriodCharacteristicUUID : 428 | log("Magnetometer period characteristic found") 429 | magnetometerPeriodCharacterictic = thisCharacteristic 430 | case MagnetometerBearingCharacteristicUUID : 431 | log("Magnetometer bearing characteristic found") 432 | magnetometerBearingCharacteristic = thisCharacteristic 433 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 434 | delegate?.serviceAvailable(service: .Magnetometer) 435 | case TemperatureCharacteristicUUID : 436 | log("Temperature reading characteristic found") 437 | temperatureCharacteristic = thisCharacteristic 438 | microbitPeripheral.setNotifyValue(true, for: thisCharacteristic) 439 | case TemperaturePeriodCharacteristicUUID : 440 | log("Temperature period characteristic found") 441 | temperaturePeriodCharacteristic = thisCharacteristic 442 | delegate?.serviceAvailable(service: .Temperature) 443 | default: 444 | break 445 | } 446 | } 447 | } 448 | 449 | public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { 450 | switch characteristic.uuid { 451 | case ModelNumberCharacteristicUUID : 452 | let dataBytes = characteristic.value! 453 | modelNumber = String(data: dataBytes, encoding: String.Encoding.utf8) ?? "n/a" 454 | log("Model number = \(modelNumber)") 455 | case SerialNumberCharacteristicUUID : 456 | let dataBytes = characteristic.value! 457 | serialNumber = String(data: dataBytes, encoding: String.Encoding.utf8) ?? "n/a" 458 | log("Serial number = \(serialNumber)") 459 | case FirmwareRevisionCharacteristicUUID : 460 | let dataBytes = characteristic.value! 461 | firmwareRevision = String(data: dataBytes, encoding: String.Encoding.utf8) ?? "n/a" 462 | log("Firmware revision number = \(firmwareRevision)") 463 | case UART_TX_CharacteristicUUID : 464 | let dataBytes = characteristic.value! 465 | let dataString = String(data: dataBytes, encoding: String.Encoding.utf8) ?? "Error reading message" 466 | delegate?.uartReceived(message: dataString) 467 | case PinDataCharacteristicUUID: 468 | let dataBytes = characteristic.value! 469 | var values = [UInt8:UInt8]() 470 | let sequence = stride(from: 0, to: dataBytes.count, by: 2) 471 | for element in sequence { 472 | values[dataBytes[element]] = dataBytes[element + 1] 473 | } 474 | delegate?.pinGet(pins: values) 475 | case ButtonAStateCharacteristicUUID : 476 | let dataBytes = characteristic.value! 477 | delegate?.buttonPressed(button: "A",action:MicrobitButtonType(rawValue: dataBytes[0])!) 478 | case ButtonBStateCharacteristicUUID : 479 | let dataBytes = characteristic.value! 480 | delegate?.buttonPressed(button: "B",action:MicrobitButtonType(rawValue: dataBytes[0]) ?? MicrobitButtonType.Invalid) 481 | case AccelerometerDataCharacteristicUUID : 482 | struct AccelerometerData { 483 | let x: Int16 484 | let y: Int16 485 | let z: Int16 486 | } 487 | let dataBytes = characteristic.value! 488 | let accelerometerData = dataBytes.withUnsafeBytes {(int16Ptr: UnsafePointer)->AccelerometerData in 489 | AccelerometerData(x: Int16(littleEndian: int16Ptr[0]), 490 | y: Int16(littleEndian: int16Ptr[1]), 491 | z: Int16(littleEndian: int16Ptr[2])) 492 | } 493 | delegate?.accelerometerData(x: accelerometerData.x, y: accelerometerData.y, z:accelerometerData.z) 494 | case MagnetometerDataCharacteristicUUID : 495 | struct MagnetometerData { 496 | let x: Int16 497 | let y: Int16 498 | let z: Int16 499 | } 500 | let dataBytes = characteristic.value! 501 | let magnetometerData = dataBytes.withUnsafeBytes {(int16Ptr: UnsafePointer)-> MagnetometerData in 502 | MagnetometerData(x: Int16(littleEndian: int16Ptr[0]), 503 | y: Int16(littleEndian: int16Ptr[1]), 504 | z: Int16(littleEndian: int16Ptr[2])) 505 | } 506 | delegate?.magnetometerData(x: magnetometerData.x, y: magnetometerData.y, z:magnetometerData.z) 507 | case MagnetometerBearingCharacteristicUUID : 508 | let dataBytes = characteristic.value! 509 | let magnetometerBearing = dataBytes.withUnsafeBytes{(int16Ptr:UnsafePointer)-> Int16 in Int16(littleEndian:int16Ptr[0])} 510 | delegate?.compass(bearing:magnetometerBearing) 511 | case MicrobitEventCharacteristicUUID : 512 | struct Event { 513 | let type: Int16 514 | let value: Int16 515 | } 516 | let dataBytes = characteristic.value! 517 | let eventData = dataBytes.withUnsafeBytes{(uint16ptr:UnsafePointer)->Event in 518 | Event(type: Int16(littleEndian:uint16ptr[0]), 519 | value:Int16(littleEndian:uint16ptr[1])) 520 | } 521 | delegate?.microbitEvent(type: eventData.type, value: eventData.value) 522 | case TemperatureCharacteristicUUID : 523 | let temperature = characteristic.value! 524 | delegate?.temperature(value: Int16(temperature[0])) 525 | default : 526 | break 527 | } 528 | } 529 | 530 | // MARK: microbit API 531 | 532 | /** 533 | Starts a scan for bluetooth peripherals regardless of the services being advertised. 534 | The scan will stop once the device name (specified when the class was instantiated) is found. 535 | */ 536 | public func startScanning() { 537 | if bleON { 538 | centralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:false]) 539 | log("Scanning for peripherals...") 540 | } 541 | } 542 | /** 543 | Stop scanning for bluetooth peripherals. This function will have no effect if a scan is not in 544 | progress 545 | */ 546 | public func stopScanning() { 547 | if bleON { 548 | centralManager.stopScan() 549 | log("Stop scanning for peripherals") 550 | } 551 | } 552 | /** 553 | Disconnect from the bluetooth peripheral. 554 | */ 555 | public func disconnect() { 556 | if bleON { 557 | if microbitPeripheral != nil { 558 | centralManager.cancelPeripheralConnection(microbitPeripheral) 559 | log("Disconnect peripheral") 560 | } else { 561 | log("Microbit peripheral is not connected") } 562 | } 563 | } 564 | /** 565 | Implements the LED Service - text and scrolling delay. 566 | - parameters: 567 | - message: a string to be scrolled across the micro:bit led matrix 568 | - scrollRate : an integer (0 - 32768) milliseconds speed the text is scrolled. 569 | */ 570 | public func ledText(message:String,scrollRate:Int16) { 571 | guard let scrollingDelayCharacteristic = scrollingDelayCharacteristic else {return} 572 | guard let ledTextCharacteristic = ledTextCharacteristic else {return} 573 | let scrollRateData = toData(scrollRate) 574 | if let messageData = message.data(using: String.Encoding.utf8){ 575 | microbitPeripheral.writeValue(scrollRateData, for: scrollingDelayCharacteristic, type: CBCharacteristicWriteType.withResponse) 576 | microbitPeripheral.writeValue(messageData, for: ledTextCharacteristic, type: CBCharacteristicWriteType.withResponse) 577 | } 578 | } 579 | /** 580 | Implements the LED Service - matrix state 581 | - parameters: 582 | - matrix: an array of 5 UInt8 bytes. The first 5 bits of each byte represents the leds in each row 583 | */ 584 | public func ledWrite(matrix:[UInt8]) { 585 | guard let ledMatrixStateCharacteristic = ledMatrixStateCharacteristic else {return} 586 | let data = Data(bytes:matrix) 587 | microbitPeripheral.writeValue(data, for: ledMatrixStateCharacteristic, type: CBCharacteristicWriteType.withResponse) 588 | } 589 | /** 590 | Implements the UART Service - sends a text string 591 | - parameters: 592 | - message: a string containing a maximum of 20 characters to be sent to the micro:bit 593 | */ 594 | public func uartSend(message:String) { 595 | guard let uartRXcharacteristic = uartRXcharacteristic else {return} 596 | if let messageData = message.data(using:String.Encoding.utf8) { 597 | microbitPeripheral.writeValue(messageData, for: uartRXcharacteristic, type: CBCharacteristicWriteType.withResponse) 598 | } 599 | } 600 | /** 601 | Implements the Pin IO Service - AD Configuration 602 | - parameters: 603 | - analougePins: a dictionary of UInt8:Bool pairs. Each pair indicates if a pin is to be configured 604 | as analouge (true) of digital (false). Only pins 0, 1, 2, 3, 4 and 10 have AD converters. 605 | */ 606 | public func pinConfigure(analougePins:[UInt8:Bool]) { 607 | guard let pinADCharacteristic = pinADCharacteristic else {return} 608 | var adPatternData = Data(bytes:[0x00,0x00,0x00,0x00]) 609 | for pin in analougePins { 610 | if pin.value == true { 611 | if pin.key < 8 { 612 | adPatternData[0] = adPatternData[0] + (1 << (pin.key)) 613 | } else if pin.key >= 8 && pin.key < 16 { 614 | adPatternData[1] = adPatternData[1] + (1 << (pin.key - 8)) 615 | } else { 616 | adPatternData[2] = adPatternData[2] + (1 << (pin.key - 16)) 617 | } 618 | } 619 | } 620 | microbitPeripheral.writeValue(adPatternData, for: pinADCharacteristic, type: CBCharacteristicWriteType.withResponse) 621 | } 622 | /** 623 | Implements the Pin IO Service - IO Configuration 624 | - parameters: 625 | - readPins: a dictionary of UInt8:Bool pairs. Each pair indicates if a pin is to be configured as write (true) or read (false). A maximum of 18 pins can be configured. 626 | */ 627 | public func pinConfigure(readPins:[UInt8:Bool]) { 628 | guard let pinIOCharacteristic = pinIOCharacteristic else {return} 629 | var ioPatternData = Data(bytes:[0x00,0x00,0x00,0x00]) 630 | for pin in readPins { 631 | if pin.value == true { 632 | if pin.key < 8 { 633 | ioPatternData[0] = ioPatternData[0] + (1 << (pin.key)) 634 | } else if pin.key >= 8 && pin.key < 16 { 635 | ioPatternData[1] = ioPatternData[1] + (1 << (pin.key - 8)) 636 | } else { 637 | ioPatternData[2] = ioPatternData[2] + (1 << (pin.key - 16)) 638 | } 639 | } 640 | } 641 | microbitPeripheral.writeValue(ioPatternData, for: pinIOCharacteristic, type: CBCharacteristicWriteType.withResponse) 642 | } 643 | /** 644 | Implements the PIN IO Service - Data write 645 | - parameters: 646 | - pinValues: a dictionary of UInt8:UInt8 pairs. Each pair represents the value to be written to a given pin. If the pin is configured as digital, only values 0 and 1 should be used. If the pin is configured as analogue values 0 - 255 can be used. 647 | */ 648 | public func pinSet(pinValues:[UInt8:UInt8]) { 649 | guard let pinDataCharacteristic = pinDataCharacteristic else {return} 650 | var valuesArray = [UInt8]() 651 | for pin in pinValues { 652 | valuesArray.append(pin.key) 653 | valuesArray.append(pin.value) 654 | } 655 | let pinValuesData = Data(bytes:valuesArray) 656 | microbitPeripheral.writeValue(pinValuesData, for: pinDataCharacteristic, type: CBCharacteristicWriteType.withResponse) 657 | } 658 | /** 659 | Implements the Accelerometer Service - sets the frequency accelerometer data is reported. 660 | - parameters: 661 | - period: the interval in milliseconds between the accelerometer reporting data. Only specific values are acceptable as defined by PeriodType. 662 | */ 663 | public func accelerometer(period:PeriodType) { 664 | guard let accelerometerPeriodCharacteristic = accelerometerPeriodCharacteristic else {return} 665 | let accelerometerPeriodData = toData(period.rawValue) 666 | microbitPeripheral.writeValue(accelerometerPeriodData, for: accelerometerPeriodCharacteristic, type: CBCharacteristicWriteType.withResponse) 667 | } 668 | /** 669 | Implements the Magnetometer Service - sets the frequency magnetometer data is reported. 670 | - parameters: 671 | - period: the interval in milliseconds between the magnetometer reporting data. Only specific values are acceptable as defined by PeriodType. 672 | */ 673 | public func magnetometer(period:PeriodType) { 674 | guard let magnetometerPeriodCharacteristic = magnetometerPeriodCharacterictic else {return} 675 | let magnetometerPeriodData = toData(period.rawValue) 676 | microbitPeripheral.writeValue(magnetometerPeriodData, for: magnetometerPeriodCharacteristic, type: CBCharacteristicWriteType.withResponse) 677 | } 678 | /** 679 | Implements the Temperature Service - sets the frequency temperature data is reported. 680 | - parameters: 681 | - period: the interval in milliseconds between temperature readings being sent from the micro:bit. A value in the range(0 - 65535) is acceptable. 682 | */ 683 | public func temperature(period:UInt16) { 684 | guard let temperaturePeriodCharacteristic = temperaturePeriodCharacteristic else {return} 685 | let temperaturePeriodData = toData(period) 686 | microbitPeripheral.writeValue(temperaturePeriodData, for: temperaturePeriodCharacteristic, type: CBCharacteristicWriteType.withResponse) 687 | } 688 | /** 689 | Implements the Event Service - Client Requirements 690 | - parameters: 691 | - events: an array of events in the range 0 - 32,768 that the swift application will listen for. 692 | */ 693 | public func registerEvents(events:[Int16]) { 694 | guard let clientRequirementCharacteristic = clientRequirementCharacteristic else {return} 695 | for event in events { 696 | var eventData = toData(event) 697 | eventData.append(contentsOf: [0x00,0x00]) 698 | microbitPeripheral.writeValue(eventData, for: clientRequirementCharacteristic, type: CBCharacteristicWriteType.withResponse) 699 | } 700 | } 701 | /** 702 | Implements the Event Service - Client Event 703 | - parameters: 704 | - event: an Event that the micro:bit is listening for 705 | - value: the value associated with the event 706 | */ 707 | public func raiseEvent(event:MicrobitEvent,value:UInt16) { 708 | guard let clientEventCharacteristic = clientEventCharacteristic else {return} 709 | var eventData = toData(event.rawValue) 710 | eventData.append(toData(value)) 711 | microbitPeripheral.writeValue(eventData, for: clientEventCharacteristic, type: CBCharacteristicWriteType.withResponse) 712 | } 713 | 714 | // MARK: Suppport utilities 715 | 716 | func toData(_ value: T) -> Data { 717 | var value = value 718 | return withUnsafeBytes(of: &value) { Data($0) } 719 | } 720 | 721 | func log(_ message:String) { 722 | let formatter = DateFormatter() 723 | formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" 724 | let date = Date() 725 | let dateString = formatter.string(from: date) 726 | log.append(dateString + " " + message) 727 | //print(dateString + " " + message) 728 | if log.count > MAX_BUFFER_ENTRIES { 729 | log.remove(at: 0) 730 | } 731 | delegate?.logUpdated(log) 732 | } 733 | 734 | func serviceDataAnalyzer(serviceData:NSDictionary,RSSI:NSNumber) { 735 | for data in serviceData { 736 | let id = "\(data.key)" 737 | let dataBytes = data.value as? Data ?? Data(bytes:[0x00]) 738 | var dataArray:[UInt8] = Array(repeating:0,count:dataBytes.count) 739 | dataBytes.copyBytes(to: &dataArray,count:dataArray.count) 740 | log("Service data: \(dataBytes.map { String(format: "%02x", $0) }.joined()),RSSI: \(RSSI)") 741 | if id == "FEAA" { 742 | dataBytes.withUnsafeBytes {(ptr: UnsafePointer) in 743 | let type = Int(dataBytes[0]) 744 | var url = " " 745 | var namespace:Int64 = 0 746 | var instance:Int32 = 0 747 | if type == 0 { 748 | var rawPtr = UnsafeRawPointer(ptr + 4) 749 | let typedPointer4 = rawPtr.bindMemory(to: Int64.self, capacity: 1) 750 | namespace = Int64(bigEndian:typedPointer4.pointee) 751 | rawPtr = UnsafeRawPointer(ptr + 14) 752 | let typedPointer14 = rawPtr.bindMemory(to: Int32.self, capacity: 1) 753 | instance = Int32(bigEndian:typedPointer14.pointee) 754 | } else { 755 | let text = dataBytes.subdata(in: 2.. Bool { 248 | guard let input = textField.text else {return false} 249 | if input.count < 21 { 250 | errorLabel.text = "" 251 | textField.resignFirstResponder() 252 | delegate?.uartSend(message:input) 253 | return true 254 | } else { 255 | errorLabel.text = "More than 20 characters entered" 256 | return false 257 | } 258 | } 259 | @objc func buttonAction(sender:UIButton) { 260 | print("clear output field") 261 | outputField.text = "" 262 | } 263 | } 264 | public class MicrobitPinIO { 265 | 266 | public var delegate:MicrobitUIDelegate? 267 | 268 | var ioButtonArray = [UIButton]() 269 | var adButtonArray = [UIButton]() 270 | var daButtonArray = [UIButton]() 271 | var daSliderArray = [UISlider]() 272 | var analogueLabelArray = [UILabel]() 273 | var ioStateArray = [Bool]() 274 | var adStateArray = [Bool]() 275 | var daStateArray = [Bool]() 276 | 277 | public init(view:UIView) { 278 | 279 | let hStackView = UIStackView() 280 | hStackView.axis = .horizontal 281 | hStackView.distribution = .equalSpacing 282 | hStackView.spacing = 0 283 | hStackView.translatesAutoresizingMaskIntoConstraints = false 284 | 285 | for tag in 0 ..< 10 { 286 | let vStackView = UIStackView() 287 | vStackView.axis = .vertical 288 | vStackView.distribution = .equalSpacing 289 | vStackView.spacing = 0 290 | vStackView.translatesAutoresizingMaskIntoConstraints = false 291 | 292 | let pinLabel = UILabel() 293 | pinLabel.text = String(tag) 294 | pinLabel.translatesAutoresizingMaskIntoConstraints = false 295 | vStackView.addArrangedSubview(pinLabel) 296 | 297 | let ioButton = UIButton(type:.system) 298 | ioButton.translatesAutoresizingMaskIntoConstraints = false 299 | ioButton.tag = tag 300 | ioButton.setTitle("W", for: .normal) 301 | ioButton.backgroundColor = .lightGray 302 | ioButton.addTarget(self,action: #selector(MicrobitPinIO.ioButtonAction),for: .primaryActionTriggered) 303 | vStackView.addArrangedSubview(ioButton) 304 | ioButtonArray.append(ioButton) 305 | ioStateArray.append(false) 306 | 307 | let adButton = UIButton(type:.system) 308 | adButton.translatesAutoresizingMaskIntoConstraints = false 309 | adButton.tag = tag 310 | adButton.setTitle("D", for: .normal) 311 | adButton.backgroundColor = .yellow 312 | adButton.addTarget(self,action: #selector(MicrobitPinIO.adButtonAction),for: .primaryActionTriggered) 313 | vStackView.addArrangedSubview(adButton) 314 | if tag > 3 { 315 | adButton.isEnabled = false 316 | adButton.backgroundColor = .clear 317 | adButton.setTitle(" ", for: .normal) 318 | } 319 | adButtonArray.append(adButton) 320 | adStateArray.append(false) 321 | 322 | let daButton = UIButton(type:.system) 323 | daButton.translatesAutoresizingMaskIntoConstraints = false 324 | daButton.tag = tag 325 | daButton.setTitle("⚫️", for: .normal) 326 | daButton.addTarget(self,action: #selector(MicrobitPinIO.daButtonAction),for: .primaryActionTriggered) 327 | vStackView.addArrangedSubview(daButton) 328 | daButtonArray.append(daButton) 329 | daStateArray.append(false) 330 | 331 | hStackView.addArrangedSubview(vStackView) 332 | } 333 | let margins = view.layoutMarginsGuide 334 | view.addSubview(hStackView) 335 | Layout.manager(hStackView,margins:margins,left:0,right:0,top:80,bottom: 230,pinH:.both,pinV:.top) 336 | 337 | let head1Label = UILabel() 338 | head1Label.text = "Pin I/O Control Panel" 339 | head1Label.textAlignment = .center 340 | head1Label.translatesAutoresizingMaskIntoConstraints = false 341 | view.addSubview(head1Label) 342 | Layout.manager(head1Label,margins:margins,left:0,right:0,top:50,bottom:70,pinH:.both,pinV:.top) 343 | 344 | let head4Label = UILabel() 345 | head4Label.text = "Use the the sliders below to set analogue values" 346 | head4Label.backgroundColor = .lightGray 347 | head4Label.textAlignment = .center 348 | head4Label.translatesAutoresizingMaskIntoConstraints = false 349 | //view.addSubview(head4Label) 350 | //Layout.manager(head4Label,margins:margins,left:0,right:0,top:310,bottom: 330,pinH:.both,pinV:.top) 351 | 352 | let head2Label = UILabel() 353 | head2Label.text = "Toggle the buttons below to read (R) or write (W) to the microbit pins" 354 | head2Label.backgroundColor = .lightGray 355 | head2Label.textAlignment = .center 356 | head2Label.translatesAutoresizingMaskIntoConstraints = false 357 | //view.addSubview(head2Label) 358 | //Layout.manager(head2Label,margins:margins,left:0,right:0,top:122,bottom: 140,pinH:.both,pinV:.top) 359 | 360 | let head3Label = UILabel() 361 | head3Label.text = "Toggle the buttons below to set the pins to analogue (A) or digital (D)" 362 | head3Label.backgroundColor = .lightGray 363 | head3Label.translatesAutoresizingMaskIntoConstraints = false 364 | //view.addSubview(head3Label) 365 | //Layout.manager(head3Label,margins:margins,left:0,right:0,top:190,bottom: 210,pinH:.both,pinV:.top) 366 | 367 | let hStackView1 = UIStackView() 368 | hStackView1.axis = .horizontal 369 | hStackView1.distribution = .equalSpacing 370 | hStackView1.spacing = 50 371 | hStackView1.translatesAutoresizingMaskIntoConstraints = false 372 | 373 | for tag in 0 ..< 4 { 374 | let vStackView = UIStackView() 375 | vStackView.axis = .vertical 376 | vStackView.distribution = .equalSpacing 377 | vStackView.spacing = 0 378 | vStackView.translatesAutoresizingMaskIntoConstraints = false 379 | 380 | let pinLabel = UILabel() 381 | pinLabel.text = String(tag) 382 | pinLabel.translatesAutoresizingMaskIntoConstraints = false 383 | vStackView.addArrangedSubview(pinLabel) 384 | 385 | let slider = UISlider() 386 | slider.translatesAutoresizingMaskIntoConstraints = false 387 | slider.isContinuous = false 388 | slider.minimumValue = 0 389 | slider.maximumValue = 255 390 | slider.tintColor = .green 391 | slider.tag = tag 392 | slider.isEnabled = false 393 | slider.widthAnchor.constraint(equalToConstant: 100).isActive = true 394 | slider.addTarget(self,action: #selector(MicrobitPinIO.sliderAction),for:.valueChanged) 395 | vStackView.addArrangedSubview(slider) 396 | daSliderArray.append(slider) 397 | 398 | let analogueLabel = UILabel() 399 | analogueLabel.text = "0" 400 | analogueLabel.translatesAutoresizingMaskIntoConstraints = false 401 | vStackView.addArrangedSubview(analogueLabel) 402 | analogueLabelArray.append(analogueLabel) 403 | 404 | hStackView1.addArrangedSubview(vStackView) 405 | } 406 | view.addSubview(hStackView1) 407 | Layout.manager(hStackView1,margins:margins,left:0,right:0,top:240,bottom: 340,pinH:.both,pinV:.top) 408 | } 409 | public func update(pins:[UInt8:UInt8]) { 410 | for pin in pins { 411 | if adStateArray[Int(pin.key)] { 412 | analogueLabelArray[Int(pin.key)].text = String(pin.value) 413 | daSliderArray[Int(pin.key)].value = Float(pin.value) 414 | } 415 | if pin.value > 0 { 416 | daButtonArray[Int(pin.key)].setTitle("🔴", for: .normal) 417 | } else { 418 | daButtonArray[Int(pin.key)].setTitle("⚫️", for: .normal) 419 | } 420 | } 421 | } 422 | 423 | @objc func ioButtonAction(sender:UIButton) { 424 | if ioStateArray[sender.tag] { 425 | ioStateArray[sender.tag] = false 426 | sender.setTitle("W", for: .normal) 427 | } else { 428 | ioStateArray[sender.tag] = true 429 | sender.setTitle("R", for: .normal) 430 | } 431 | var readConfig = [UInt8:Bool]() 432 | var pin:UInt8 = 0 433 | for state in ioStateArray { 434 | readConfig[pin] = state 435 | pin += 1 436 | } 437 | delegate?.pinSetfor(read: readConfig) 438 | } 439 | @objc func adButtonAction(sender:UIButton) { 440 | if adStateArray[sender.tag] { 441 | adStateArray[sender.tag] = false 442 | sender.setTitle("D", for: .normal) 443 | daSliderArray[sender.tag].isEnabled = false 444 | daButtonArray[sender.tag].isEnabled = true 445 | //analogueLabelArray[sender.tag].text = " " 446 | } else { 447 | adStateArray[sender.tag] = true 448 | sender.setTitle("A", for: .normal) 449 | daSliderArray[sender.tag].isEnabled = true 450 | daButtonArray[sender.tag].isEnabled = false 451 | } 452 | var analogueConfig = [UInt8:Bool]() 453 | var pin:UInt8 = 0 454 | for state in adStateArray { 455 | analogueConfig[pin] = state 456 | pin += 1 457 | } 458 | delegate?.pinSetfor(analogue: analogueConfig) 459 | } 460 | @objc func daButtonAction(sender:UIButton) { 461 | if daStateArray[sender.tag] { 462 | daButtonArray[sender.tag].setTitle("⚫️", for: .normal) 463 | daStateArray[sender.tag] = false 464 | delegate?.pinWrite(value: [UInt8(sender.tag):0]) 465 | } else { 466 | daButtonArray[sender.tag].setTitle("🔴", for: .normal) 467 | daStateArray[sender.tag] = true 468 | delegate?.pinWrite(value: [UInt8(sender.tag):1]) 469 | } 470 | } 471 | @objc func sliderAction(sender:UISlider) { 472 | print("pin IO switch - \(sender.tag) \(UInt8(sender.value))") 473 | analogueLabelArray[sender.tag].text = String(UInt8(sender.value)) 474 | delegate?.pinWrite(value: [UInt8(sender.tag):UInt8(sender.value)]) 475 | 476 | } 477 | } 478 | public class MicrobitAccelerometer { 479 | public var delegate:MicrobitUIDelegate? 480 | 481 | var xValue = UILabel() 482 | var yValue = UILabel() 483 | var zValue = UILabel() 484 | 485 | let periodControl = UISegmentedControl(items:["1ms","2ms","5ms","10ms","20ms","80ms","160ms","640ms"]) 486 | 487 | public init(view:UIView) { 488 | let vStackView = UIStackView() 489 | vStackView.axis = .vertical 490 | vStackView.distribution = .equalSpacing 491 | vStackView.spacing = 0 492 | vStackView.translatesAutoresizingMaskIntoConstraints = false 493 | 494 | let head1Label = UILabel() 495 | head1Label.text = "Accelerometer" 496 | head1Label.textAlignment = .center 497 | head1Label.translatesAutoresizingMaskIntoConstraints = false 498 | vStackView.addArrangedSubview(head1Label) 499 | 500 | let head2Label = UILabel() 501 | head2Label.text = "Frequency with which accelerometer data is reported in milliseconds" 502 | head2Label.textAlignment = .center 503 | head2Label.translatesAutoresizingMaskIntoConstraints = false 504 | vStackView.addArrangedSubview(head2Label) 505 | 506 | periodControl.translatesAutoresizingMaskIntoConstraints = false 507 | periodControl.addTarget(self,action: #selector(MicrobitAccelerometer.segmentedControlAction),for: .primaryActionTriggered) 508 | vStackView.addArrangedSubview(periodControl) 509 | 510 | let hStackView = UIStackView() 511 | hStackView.axis = .horizontal 512 | hStackView.distribution = .equalSpacing 513 | hStackView.spacing = 0 514 | hStackView.translatesAutoresizingMaskIntoConstraints = false 515 | 516 | let xLabel = UILabel() 517 | xLabel.translatesAutoresizingMaskIntoConstraints = false 518 | xLabel.text = "x:" 519 | hStackView.addArrangedSubview(xLabel) 520 | xValue = UILabel() 521 | xValue.widthAnchor.constraint(equalToConstant: 100).isActive = true 522 | xValue.translatesAutoresizingMaskIntoConstraints = false 523 | hStackView.addArrangedSubview(xValue) 524 | let yLabel = UILabel() 525 | yLabel.translatesAutoresizingMaskIntoConstraints = false 526 | yLabel.text = "y:" 527 | hStackView.addArrangedSubview(yLabel) 528 | yValue = UILabel() 529 | yValue.translatesAutoresizingMaskIntoConstraints = false 530 | yValue.widthAnchor.constraint(equalToConstant: 100).isActive = true 531 | hStackView.addArrangedSubview(yValue) 532 | let zLabel = UILabel() 533 | zLabel.translatesAutoresizingMaskIntoConstraints = false 534 | zLabel.text = "z:" 535 | hStackView.addArrangedSubview(zLabel) 536 | zValue = UILabel() 537 | zValue.translatesAutoresizingMaskIntoConstraints = false 538 | zValue.widthAnchor.constraint(equalToConstant: 100).isActive = true 539 | hStackView.addArrangedSubview(zValue) 540 | 541 | vStackView.addArrangedSubview(hStackView) 542 | 543 | let margins = view.layoutMarginsGuide 544 | view.addSubview(vStackView) 545 | Layout.manager(vStackView,margins:margins,left:0,right:0,top:50,bottom: 200,pinH:.both,pinV:.top) 546 | } 547 | 548 | public func update(x:Int16,y:Int16,z:Int16) { 549 | xValue.text = String(x) 550 | yValue.text = String(y) 551 | zValue.text = String(z) 552 | } 553 | public func setPeriod() { 554 | periodControl.selectedSegmentIndex = 7 555 | delegate?.accelerometerSet(period:.p640) 556 | } 557 | @objc func segmentedControlAction(sender:UISegmentedControl) { 558 | var periodType:PeriodType 559 | switch sender.selectedSegmentIndex { 560 | case 0 : periodType = PeriodType.p1 561 | case 1 : periodType = PeriodType.p2 562 | case 2 : periodType = PeriodType.p5 563 | case 3 : periodType = PeriodType.p10 564 | case 4 : periodType = PeriodType.p20 565 | case 5 : periodType = PeriodType.p80 566 | case 6 : periodType = PeriodType.p160 567 | case 7 : periodType = PeriodType.p640 568 | default : periodType = PeriodType.p640 569 | } 570 | delegate?.accelerometerSet(period: periodType) 571 | } 572 | } 573 | public class MicrobitMagnetometer { 574 | public var delegate:MicrobitUIDelegate? 575 | 576 | var xValue = UILabel() 577 | var yValue = UILabel() 578 | var zValue = UILabel() 579 | 580 | let periodControl = UISegmentedControl(items:["1ms","2ms","5ms","10ms","20ms","80ms","160ms","640ms"]) 581 | 582 | var compassImage = UIImageView() 583 | var lastBearing:CGFloat = 0.0 584 | var bearingValue = UILabel() 585 | 586 | public init(view:UIView) { 587 | let vStackView = UIStackView() 588 | vStackView.axis = .vertical 589 | vStackView.distribution = .equalSpacing 590 | vStackView.spacing = 0 591 | vStackView.translatesAutoresizingMaskIntoConstraints = false 592 | 593 | let head1Label = UILabel() 594 | head1Label.text = "Magnetometer" 595 | head1Label.textAlignment = .center 596 | head1Label.translatesAutoresizingMaskIntoConstraints = false 597 | vStackView.addArrangedSubview(head1Label) 598 | 599 | let head2Label = UILabel() 600 | head2Label.text = "Frequency with which magnetometer data is reported in milliseconds" 601 | head2Label.textAlignment = .center 602 | head2Label.translatesAutoresizingMaskIntoConstraints = false 603 | vStackView.addArrangedSubview(head2Label) 604 | 605 | periodControl.translatesAutoresizingMaskIntoConstraints = false 606 | periodControl.addTarget(self,action: #selector(MicrobitAccelerometer.segmentedControlAction),for: .primaryActionTriggered) 607 | vStackView.addArrangedSubview(periodControl) 608 | 609 | let hStackView = UIStackView() 610 | hStackView.axis = .horizontal 611 | hStackView.distribution = .equalSpacing 612 | hStackView.spacing = 0 613 | hStackView.translatesAutoresizingMaskIntoConstraints = false 614 | 615 | let xLabel = UILabel() 616 | xLabel.translatesAutoresizingMaskIntoConstraints = false 617 | xLabel.text = "x:" 618 | hStackView.addArrangedSubview(xLabel) 619 | xValue = UILabel() 620 | xValue.widthAnchor.constraint(equalToConstant: 100).isActive = true 621 | xValue.translatesAutoresizingMaskIntoConstraints = false 622 | hStackView.addArrangedSubview(xValue) 623 | let yLabel = UILabel() 624 | yLabel.translatesAutoresizingMaskIntoConstraints = false 625 | yLabel.text = "y:" 626 | hStackView.addArrangedSubview(yLabel) 627 | yValue = UILabel() 628 | yValue.translatesAutoresizingMaskIntoConstraints = false 629 | yValue.widthAnchor.constraint(equalToConstant: 100).isActive = true 630 | hStackView.addArrangedSubview(yValue) 631 | let zLabel = UILabel() 632 | zLabel.translatesAutoresizingMaskIntoConstraints = false 633 | zLabel.text = "z:" 634 | hStackView.addArrangedSubview(zLabel) 635 | zValue = UILabel() 636 | zValue.translatesAutoresizingMaskIntoConstraints = false 637 | zValue.widthAnchor.constraint(equalToConstant: 100).isActive = true 638 | hStackView.addArrangedSubview(zValue) 639 | 640 | vStackView.addArrangedSubview(hStackView) 641 | 642 | let margins = view.layoutMarginsGuide 643 | 644 | view.addSubview(vStackView) 645 | Layout.manager(vStackView,margins:margins,left:0,right:0,top:50,bottom: 200,pinH:.both,pinV:.top) 646 | 647 | let bearingLabel = UILabel() 648 | bearingLabel.text = "Compass Bearing" 649 | bearingLabel.translatesAutoresizingMaskIntoConstraints = false 650 | view.addSubview(bearingLabel) 651 | Layout.manager(bearingLabel,margins:margins,left:10,right:200,top:210,bottom: 250,pinH:.left,pinV:.top) 652 | 653 | bearingValue.text = "0" 654 | bearingValue.translatesAutoresizingMaskIntoConstraints = false 655 | view.addSubview(bearingValue) 656 | Layout.manager(bearingValue,margins:margins,left:210,right:250,top:210,bottom: 250,pinH:.left,pinV:.top) 657 | 658 | let emojiImage = "➸".image() 659 | compassImage.image = emojiImage 660 | //compassImage.layer.borderWidth = 1.0 661 | compassImage.translatesAutoresizingMaskIntoConstraints = false 662 | compassImage.transform = compassImage.transform.rotated(by: -1.55) 663 | view.addSubview(compassImage) 664 | Layout.manager(compassImage,margins:margins,left:150,right:250,top:250,bottom: 350,pinH:.left,pinV:.top) 665 | } 666 | public func setPeriod() { 667 | periodControl.selectedSegmentIndex = 7 668 | delegate?.accelerometerSet(period:.p640) 669 | } 670 | public func updateData(x:Int16,y:Int16,z:Int16) { 671 | xValue.text = String(x) 672 | yValue.text = String(y) 673 | zValue.text = String(z) 674 | } 675 | public func updateBearing(bearing:Int16) { 676 | bearingValue.text = String(bearing) 677 | let thisBearing = bearing.degreesToRadians 678 | compassImage.transform = compassImage.transform.rotated(by: -(lastBearing - thisBearing)) 679 | lastBearing = thisBearing 680 | } 681 | @objc func segmentedControlAction(sender:UISegmentedControl) { 682 | var periodType:PeriodType 683 | switch sender.selectedSegmentIndex { 684 | case 0 : periodType = PeriodType.p1 685 | case 1 : periodType = PeriodType.p2 686 | case 2 : periodType = PeriodType.p5 687 | case 3 : periodType = PeriodType.p10 688 | case 4 : periodType = PeriodType.p20 689 | case 5 : periodType = PeriodType.p80 690 | case 6 : periodType = PeriodType.p160 691 | case 7 : periodType = PeriodType.p640 692 | default : periodType = PeriodType.p640 693 | } 694 | delegate?.magnetometerSet(period: periodType) 695 | } 696 | } 697 | public class MicrobitTemperature { 698 | public var delegate:MicrobitUIDelegate? 699 | var temperatureValue = UILabel() 700 | var updateFlagLabel = UILabel() 701 | var updateFlag = true 702 | public init (view:UIView) { 703 | let vStackView = UIStackView() 704 | vStackView.axis = .vertical 705 | vStackView.distribution = .equalSpacing 706 | vStackView.spacing = 0 707 | vStackView.translatesAutoresizingMaskIntoConstraints = false 708 | 709 | let head1Label = UILabel() 710 | head1Label.text = "Temperature" 711 | head1Label.textAlignment = .center 712 | head1Label.translatesAutoresizingMaskIntoConstraints = false 713 | vStackView.addArrangedSubview(head1Label) 714 | 715 | let head2Label = UILabel() 716 | head2Label.text = "Frequency with which temperature is reported" 717 | head2Label.textAlignment = .center 718 | head2Label.translatesAutoresizingMaskIntoConstraints = false 719 | vStackView.addArrangedSubview(head2Label) 720 | 721 | let periodControl = UISegmentedControl(items:["100ms","500ms","1s","10s","30s","1m"]) 722 | periodControl.translatesAutoresizingMaskIntoConstraints = false 723 | periodControl.addTarget(self,action: #selector(MicrobitTemperature.segmentedControlAction),for: .primaryActionTriggered) 724 | vStackView.addArrangedSubview(periodControl) 725 | 726 | let hStackView = UIStackView() 727 | hStackView.axis = .horizontal 728 | hStackView.distribution = .equalSpacing 729 | hStackView.spacing = 0 730 | hStackView.translatesAutoresizingMaskIntoConstraints = false 731 | 732 | let temperatureLabel = UILabel() 733 | temperatureLabel.translatesAutoresizingMaskIntoConstraints = false 734 | temperatureLabel.text = "Temperature" 735 | hStackView.addArrangedSubview(temperatureLabel) 736 | 737 | temperatureValue.translatesAutoresizingMaskIntoConstraints = false 738 | temperatureValue.text = "0°C" 739 | let temperatureFont = UIFont.systemFont(ofSize: 30.0, weight: .bold) 740 | temperatureValue.font = temperatureFont 741 | hStackView.addArrangedSubview(temperatureValue) 742 | 743 | updateFlagLabel.translatesAutoresizingMaskIntoConstraints = false 744 | updateFlagLabel.text = "⚫️" 745 | hStackView.addArrangedSubview(updateFlagLabel) 746 | 747 | vStackView.addArrangedSubview(hStackView) 748 | 749 | let margins = view.layoutMarginsGuide 750 | 751 | view.addSubview(vStackView) 752 | Layout.manager(vStackView,margins:margins,left:0,right:360,top:50,bottom: 200,pinH:.left,pinV:.top) 753 | } 754 | public func update(temperature:Int16) { 755 | temperatureValue.text = String("\(temperature)°C") 756 | if updateFlag { 757 | updateFlagLabel.text = "🔴" 758 | updateFlag = false 759 | } else { 760 | updateFlagLabel.text = "⚫️" 761 | updateFlag = true 762 | } 763 | } 764 | @objc func segmentedControlAction(sender:UISegmentedControl) { 765 | var temperaturePeriod:UInt16 766 | switch sender.selectedSegmentIndex { 767 | case 0 : temperaturePeriod = 100 768 | case 1 : temperaturePeriod = 500 769 | case 2 : temperaturePeriod = 1000 770 | case 3 : temperaturePeriod = 10000 771 | case 4 : temperaturePeriod = 30000 772 | case 5 : temperaturePeriod = 60000 773 | default : temperaturePeriod = 10000 774 | } 775 | delegate?.temperatureSet(period: temperaturePeriod) 776 | } 777 | } 778 | public class MicrobitButtons { 779 | var aLight = UILabel() 780 | var aText = UILabel() 781 | var bLight = UILabel() 782 | var bText = UILabel() 783 | public init(view:UIView) { 784 | let headerLabel = UILabel() 785 | headerLabel.translatesAutoresizingMaskIntoConstraints = false 786 | headerLabel.text = "Button Service" 787 | headerLabel.textAlignment = .center 788 | view.addSubview(headerLabel) 789 | 790 | let margins = view.layoutMarginsGuide 791 | Layout.manager(headerLabel,margins:margins,left:0,right:0,top:50,bottom: 80,pinH:.both,pinV:.top) 792 | 793 | let aStackView = UIStackView() 794 | aStackView.axis = .vertical 795 | aStackView.distribution = .equalSpacing 796 | aStackView.spacing = 0 797 | aStackView.translatesAutoresizingMaskIntoConstraints = false 798 | 799 | let aTitle = UILabel() 800 | aTitle.translatesAutoresizingMaskIntoConstraints = false 801 | aTitle.text = "A Button" 802 | aStackView.addArrangedSubview(aTitle) 803 | aLight.translatesAutoresizingMaskIntoConstraints = false 804 | aLight.text = "⚫️" 805 | aStackView.addArrangedSubview(aLight) 806 | aText.translatesAutoresizingMaskIntoConstraints = false 807 | aText.text = "Up" 808 | aStackView.addArrangedSubview(aText) 809 | 810 | let bStackView = UIStackView() 811 | bStackView.axis = .vertical 812 | bStackView.distribution = .equalSpacing 813 | bStackView.spacing = 0 814 | bStackView.translatesAutoresizingMaskIntoConstraints = false 815 | 816 | let bTitle = UILabel() 817 | bTitle.translatesAutoresizingMaskIntoConstraints = false 818 | bTitle.text = "B Button" 819 | bStackView.addArrangedSubview(bTitle) 820 | bLight.translatesAutoresizingMaskIntoConstraints = false 821 | bLight.text = "⚫️" 822 | bStackView.addArrangedSubview(bLight) 823 | bText.translatesAutoresizingMaskIntoConstraints = false 824 | bText.text = "Up" 825 | bStackView.addArrangedSubview(bText) 826 | 827 | let hStackView = UIStackView() 828 | hStackView.axis = .horizontal 829 | hStackView.distribution = .equalSpacing 830 | hStackView.spacing = 0 831 | hStackView.translatesAutoresizingMaskIntoConstraints = false 832 | 833 | hStackView.addArrangedSubview(aStackView) 834 | hStackView.addArrangedSubview(bStackView) 835 | view.addSubview(hStackView) 836 | Layout.manager(hStackView,margins:margins,left:0,right:0,top:100,bottom: 200,pinH:.both,pinV:.top) 837 | } 838 | public func update(button:String,action:MicrobitButtonType) { 839 | if button == "A" { 840 | switch action { 841 | case .Down : 842 | aLight.text = "🔴" 843 | aText.text = "Down" 844 | case .Up : 845 | aLight.text = "⚫️" 846 | aText.text = "Up" 847 | case .Long : 848 | aLight.text = "🔵" 849 | aText.text = "Long" 850 | case .Invalid : 851 | aLight.text = "❌" 852 | aText.text = "Invalid" 853 | } 854 | } 855 | if button == "B" { 856 | switch action { 857 | case .Down : 858 | bLight.text = "🔴" 859 | bText.text = "Down" 860 | case .Up : 861 | bLight.text = "⚫️" 862 | bText.text = "Up" 863 | case .Long : 864 | bLight.text = "🔵" 865 | bText.text = "Long" 866 | case .Invalid : 867 | bLight.text = "❌" 868 | bText.text = "Invalid" 869 | } 870 | } 871 | } 872 | } 873 | public class MicrobitLED:NSObject,UITextFieldDelegate { 874 | public var delegate:MicrobitUIDelegate? 875 | var ledButtonArray = [UIButton]() 876 | var ledStateArray = [Bool]() 877 | var scrollRate:Int16 = 100 878 | var inputField = UITextField() 879 | let errorLabel = UILabel() 880 | public init(view:UIView) { 881 | super.init() 882 | let vStackView = UIStackView() 883 | vStackView.axis = .vertical 884 | vStackView.distribution = .equalSpacing 885 | vStackView.spacing = 0 886 | vStackView.translatesAutoresizingMaskIntoConstraints = false 887 | for col in 0 ... 4 { 888 | let hStackView = UIStackView() 889 | hStackView.axis = .horizontal 890 | hStackView.distribution = .equalSpacing 891 | hStackView.spacing = 0 892 | hStackView.translatesAutoresizingMaskIntoConstraints = false 893 | for row in 0 ... 4 { 894 | let ledButton = UIButton(type:.system) 895 | ledButton.translatesAutoresizingMaskIntoConstraints = false 896 | ledButton.tag = col * 5 + row 897 | ledButton.setTitle("⚫️", for: .normal) 898 | ledButton.addTarget(self,action: #selector(MicrobitLED.ledButtonAction),for: .primaryActionTriggered) 899 | ledStateArray.append(false) 900 | ledButtonArray.append(ledButton) 901 | hStackView.addArrangedSubview(ledButton) 902 | } 903 | vStackView.addArrangedSubview(hStackView) 904 | } 905 | view.addSubview(vStackView) 906 | let margins = view.layoutMarginsGuide 907 | Layout.manager(vStackView,margins:margins,left:0,right:150,top:50,bottom: 200,pinH:.left,pinV:.top) 908 | 909 | let textStackView = UIStackView() 910 | textStackView.axis = .vertical 911 | textStackView.distribution = .equalSpacing 912 | textStackView.spacing = 0 913 | textStackView.translatesAutoresizingMaskIntoConstraints = false 914 | 915 | let titleLabel = UILabel() 916 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 917 | titleLabel.text = "LED Services scroll speed" 918 | textStackView.addArrangedSubview(titleLabel) 919 | 920 | let scrollSpeed = UISegmentedControl(items:["Slow","Normal","Fast"]) 921 | scrollSpeed.translatesAutoresizingMaskIntoConstraints = false 922 | scrollSpeed.addTarget(self,action: #selector(MicrobitLED.scrollSpeedAction),for: .primaryActionTriggered) 923 | textStackView.addArrangedSubview(scrollSpeed) 924 | 925 | let nameLabel = UILabel() 926 | nameLabel.translatesAutoresizingMaskIntoConstraints = false 927 | nameLabel.text = "Enter Text to scroll on the matrix (max 20 chars)" 928 | textStackView.addArrangedSubview(nameLabel) 929 | 930 | inputField.backgroundColor = .lightGray 931 | inputField.delegate = self 932 | inputField.translatesAutoresizingMaskIntoConstraints = false 933 | textStackView.addArrangedSubview(inputField) 934 | 935 | let errorFont = UIFont.systemFont(ofSize: 10.0, weight: .regular) 936 | errorLabel.font = errorFont 937 | errorLabel.textColor = .red 938 | errorLabel.translatesAutoresizingMaskIntoConstraints = false 939 | textStackView.addArrangedSubview(errorLabel) 940 | 941 | view.addSubview(textStackView) 942 | Layout.manager(textStackView,margins:margins,left:200,right:0,top:50,bottom: 160,pinH:.both,pinV:.top) 943 | 944 | } 945 | func writeLEDmatrix() { 946 | var ledMatrix:[UInt8] = [0x00,0x00,0x00,0x00,0x00] 947 | var ledNumber = 0 948 | for led in ledStateArray { 949 | let ix = ledNumber % 5 950 | let iy = ledNumber / 5 951 | if led { 952 | let shift = 4 - ix 953 | ledMatrix[iy] = ledMatrix[iy] + (1 << shift) 954 | } 955 | ledNumber += 1 956 | } 957 | delegate?.ledSet(matrix: ledMatrix) 958 | } 959 | public func textFieldShouldReturn(_ textField:UITextField) -> Bool { 960 | guard let input = textField.text else {return false} 961 | if input.count <= 20 { 962 | errorLabel.text = "" 963 | textField.resignFirstResponder() 964 | delegate?.ledText(message: input, scrollRate: scrollRate) 965 | return true 966 | } else { 967 | errorLabel.text = "More than 20 characters entered" 968 | return false 969 | } 970 | } 971 | @objc func ledButtonAction(sender:UIButton) { 972 | if ledStateArray[sender.tag] { 973 | ledButtonArray[sender.tag].setTitle("⚫️", for: .normal) 974 | ledStateArray[sender.tag] = false 975 | writeLEDmatrix() 976 | } else { 977 | ledButtonArray[sender.tag].setTitle("🔴", for: .normal) 978 | ledStateArray[sender.tag] = true 979 | writeLEDmatrix() 980 | } 981 | } 982 | @objc func scrollSpeedAction(sender:UISegmentedControl) { 983 | switch sender.selectedSegmentIndex { 984 | case 0 : scrollRate = 500 985 | case 1 : scrollRate = 100 986 | case 2 : scrollRate = 10 987 | default : scrollRate = 100 988 | } 989 | } 990 | 991 | } 992 | public class MicrobitUIEvent:NSObject,UITextFieldDelegate { 993 | public var delegate:MicrobitUIDelegate? 994 | var eventValueArray = [UILabel]() 995 | var eventLabelArray = [UILabel]() 996 | var eventIndex = 0 997 | var inputField = UITextField() 998 | var eventType:MicrobitEvent = .MES_DPAD_CONTROLLER_ID 999 | var view:UIView! 1000 | public init(view:UIView) { 1001 | super.init() 1002 | self.view = view 1003 | let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MicrobitUIEvent.dismissKeyboard)) 1004 | tap.cancelsTouchesInView = false 1005 | view.addGestureRecognizer(tap) 1006 | 1007 | let v1StackView = UIStackView() 1008 | v1StackView.axis = .vertical 1009 | v1StackView.distribution = .equalSpacing 1010 | v1StackView.spacing = 0 1011 | v1StackView.translatesAutoresizingMaskIntoConstraints = false 1012 | 1013 | let title1Label = UILabel() 1014 | title1Label.translatesAutoresizingMaskIntoConstraints = false 1015 | title1Label.text = "Microbit Events" 1016 | v1StackView.addArrangedSubview(title1Label) 1017 | 1018 | for tag in 0 ... 4 { 1019 | let hStackView = UIStackView() 1020 | hStackView.axis = .horizontal 1021 | hStackView.distribution = .equalSpacing 1022 | hStackView.spacing = 0 1023 | hStackView.translatesAutoresizingMaskIntoConstraints = false 1024 | 1025 | let eventLabel = UILabel() 1026 | eventLabel.translatesAutoresizingMaskIntoConstraints = false 1027 | eventLabel.tag = tag 1028 | eventLabel.layer.borderWidth = 1.0 1029 | eventLabel.text = " " 1030 | eventLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true 1031 | eventLabel.backgroundColor = .lightGray 1032 | hStackView.addArrangedSubview(eventLabel) 1033 | eventLabelArray.append(eventLabel) 1034 | 1035 | let eventValueLabel = UILabel() 1036 | eventValueLabel.translatesAutoresizingMaskIntoConstraints = false 1037 | eventValueLabel.tag = tag 1038 | eventValueLabel.layer.borderWidth = 1.0 1039 | eventValueLabel.text = " " 1040 | eventValueLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true 1041 | eventValueLabel.backgroundColor = .lightGray 1042 | hStackView.addArrangedSubview(eventValueLabel) 1043 | eventValueArray.append(eventValueLabel) 1044 | 1045 | v1StackView.addArrangedSubview(hStackView) 1046 | } 1047 | view.addSubview(v1StackView) 1048 | let margins = view.layoutMarginsGuide 1049 | Layout.manager(v1StackView,margins:margins,left:0,right:150,top:50,bottom: 200,pinH:.left,pinV:.top) 1050 | 1051 | let eventStackView = UIStackView() 1052 | eventStackView.axis = .vertical 1053 | eventStackView.distribution = .equalSpacing 1054 | eventStackView.spacing = 0 1055 | eventStackView.translatesAutoresizingMaskIntoConstraints = false 1056 | 1057 | let title2Label = UILabel() 1058 | title2Label.translatesAutoresizingMaskIntoConstraints = false 1059 | title2Label.text = "Raise a Microbit event" 1060 | eventStackView.addArrangedSubview(title2Label) 1061 | 1062 | let title3Label = UILabel() 1063 | title3Label.translatesAutoresizingMaskIntoConstraints = false 1064 | title3Label.text = "Select an event type" 1065 | eventStackView.addArrangedSubview(title3Label) 1066 | 1067 | let eventType = UISegmentedControl(items:["DPAD CONTROLLER","DEVICE INFO","BROADCAST GENERAL","SIGNAL STRENGTH"]) 1068 | eventType.translatesAutoresizingMaskIntoConstraints = false 1069 | eventType.selectedSegmentIndex = 0 1070 | eventType.addTarget(self,action: #selector(MicrobitUIEvent.eventTypeAction),for: .primaryActionTriggered) 1071 | eventStackView.addArrangedSubview(eventType) 1072 | 1073 | let nameLabel = UILabel() 1074 | nameLabel.translatesAutoresizingMaskIntoConstraints = false 1075 | nameLabel.text = "Enter a value (Int16) for the event" 1076 | //eventStackView.addArrangedSubview(nameLabel) 1077 | 1078 | inputField.backgroundColor = .lightGray 1079 | inputField.delegate = self 1080 | inputField.translatesAutoresizingMaskIntoConstraints = false 1081 | inputField.placeholder = "Enter a value between 0 and 65535" 1082 | inputField.keyboardType = .numbersAndPunctuation 1083 | eventStackView.addArrangedSubview(inputField) 1084 | 1085 | view.addSubview(eventStackView) 1086 | Layout.manager(eventStackView,margins:margins,left:200,right:0,top:50,bottom: 175,pinH:.both,pinV:.top) 1087 | } 1088 | 1089 | public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 1090 | let currentText = textField.text ?? "" 1091 | let prospectiveText = (currentText as NSString).replacingCharacters(in: range, with: string) 1092 | return prospectiveText.containsOnlyCharactersIn(matchCharacters: "0123456789") && 1093 | prospectiveText.count <= 6 1094 | } 1095 | public func textFieldShouldReturn(_ textField:UITextField) -> Bool { 1096 | guard let input = (textField.text) else {return false} 1097 | if let value = UInt16(input) { 1098 | textField.resignFirstResponder() 1099 | delegate?.raiseEvent(event: eventType, value: value) 1100 | return true 1101 | } else { 1102 | return false 1103 | } 1104 | } 1105 | public func registerEvents(events:[Int16]) { 1106 | for event in events { 1107 | if eventIndex < eventLabelArray.count { 1108 | eventLabelArray[eventIndex].text = String(event) 1109 | eventIndex += 1 1110 | } 1111 | } 1112 | delegate?.event(register: events) 1113 | } 1114 | public func update(type:Int16,value:Int16) { 1115 | for event in eventLabelArray { 1116 | if event.text == String(type) { 1117 | eventValueArray[event.tag].text = String(value) 1118 | } 1119 | } 1120 | } 1121 | @objc func eventTypeAction(sender:UISegmentedControl) { 1122 | switch sender.selectedSegmentIndex { 1123 | case 0 : eventType = .MES_DPAD_CONTROLLER_ID 1124 | case 1 : eventType = .MES_DEVICE_INFO_ID 1125 | case 2 : eventType = .MES_BROADCAST_GENERAL_ID 1126 | case 3 : eventType = .MES_SIGNAL_STRENGTH_ID 1127 | default : eventType = .MES_DPAD_CONTROLLER_ID 1128 | } 1129 | } 1130 | @objc func dismissKeyboard() { 1131 | view.endEditing(true) 1132 | } 1133 | } 1134 | public class MicrobitUIAdvertisement { 1135 | var urlLabel = UILabel() 1136 | var namespaceLabel = UILabel() 1137 | var instanceLabel = UILabel() 1138 | var rssiLabel = UILabel() 1139 | 1140 | public init(view:UIView) { 1141 | 1142 | let v1StackView = UIStackView() 1143 | v1StackView.axis = .vertical 1144 | v1StackView.distribution = .equalSpacing 1145 | v1StackView.spacing = 0 1146 | v1StackView.translatesAutoresizingMaskIntoConstraints = false 1147 | 1148 | let title = UILabel() 1149 | title.translatesAutoresizingMaskIntoConstraints = false 1150 | title.text = "Microbit Advertisement Data" 1151 | v1StackView.addArrangedSubview(title) 1152 | 1153 | let h1StackView = UIStackView() 1154 | h1StackView.axis = .horizontal 1155 | h1StackView.distribution = .equalSpacing 1156 | h1StackView.spacing = 0 1157 | h1StackView.translatesAutoresizingMaskIntoConstraints = false 1158 | 1159 | urlLabel.translatesAutoresizingMaskIntoConstraints = false 1160 | h1StackView.addArrangedSubview(urlLabel) 1161 | namespaceLabel.translatesAutoresizingMaskIntoConstraints = false 1162 | h1StackView.addArrangedSubview(namespaceLabel) 1163 | instanceLabel.translatesAutoresizingMaskIntoConstraints = false 1164 | h1StackView.addArrangedSubview(instanceLabel) 1165 | 1166 | v1StackView.addArrangedSubview(h1StackView) 1167 | 1168 | let h2StackView = UIStackView() 1169 | h2StackView.axis = .horizontal 1170 | h2StackView.distribution = .equalSpacing 1171 | h2StackView.spacing = 0 1172 | h2StackView.translatesAutoresizingMaskIntoConstraints = false 1173 | 1174 | let rssiNameLabel = UILabel() 1175 | rssiNameLabel.translatesAutoresizingMaskIntoConstraints = false 1176 | rssiNameLabel.text = "RSSI (signal strength):" 1177 | h2StackView.addArrangedSubview(rssiNameLabel) 1178 | 1179 | rssiLabel.translatesAutoresizingMaskIntoConstraints = false 1180 | h2StackView.addArrangedSubview(rssiLabel) 1181 | 1182 | v1StackView.addArrangedSubview(h2StackView) 1183 | 1184 | view.addSubview(v1StackView) 1185 | let margins = view.layoutMarginsGuide 1186 | Layout.manager(v1StackView,margins:margins,left:0,right:350,top:100,bottom: 200,pinH:.left,pinV:.top) 1187 | } 1188 | public func upadateAdverisementData(url:String,namespace:Int64,instance:Int32,RSSI:Int) { 1189 | if url != " " { 1190 | urlLabel.text = "url = \(url)" 1191 | } else { 1192 | namespaceLabel.text = String("namespace = \(namespace) ") 1193 | instanceLabel.text = String("instance = \(instance)") 1194 | } 1195 | rssiLabel.text = String(RSSI) 1196 | if RSSI > 0 { 1197 | rssiLabel.backgroundColor = .blue 1198 | } 1199 | else if RSSI > -60 { 1200 | rssiLabel.backgroundColor = .red 1201 | } else { 1202 | rssiLabel.backgroundColor = .green 1203 | } 1204 | } 1205 | } 1206 | extension String { 1207 | func image() -> UIImage { 1208 | let size = CGSize(width: 30, height: 35) 1209 | UIGraphicsBeginImageContextWithOptions(size, false, 0); 1210 | UIColor.white.set() 1211 | let rect = CGRect(origin: CGPoint(x:0,y:0), size: size) 1212 | UIRectFill(CGRect(origin: CGPoint(x:0,y:0), size: size)) 1213 | (self as NSString).draw(in: rect, withAttributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 30)]) 1214 | let image = UIGraphicsGetImageFromCurrentImageContext() 1215 | UIGraphicsEndImageContext() 1216 | return image! 1217 | } 1218 | func containsCharactersIn(matchCharacters: String) -> Bool { 1219 | let characterSet = NSCharacterSet(charactersIn: matchCharacters) 1220 | //return self.rangeOfCharacterFromSet(characterSet) != nil 1221 | return self.rangeOfCharacter(from: characterSet as CharacterSet) != nil 1222 | } 1223 | func containsOnlyCharactersIn(matchCharacters: String) -> Bool { 1224 | let disallowedCharacterSet = NSCharacterSet(charactersIn: matchCharacters).inverted 1225 | return self.rangeOfCharacter(from: disallowedCharacterSet) == nil 1226 | } 1227 | 1228 | } 1229 | extension BinaryInteger { 1230 | var degreesToRadians:CGFloat {return CGFloat(Int(self)) * .pi / 180} 1231 | } 1232 | -------------------------------------------------------------------------------- /Microbit/Microbit/MicrobitUIController.swift: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | MicrobitUIController.swift 4 | 5 | Created by Peter Wallen on 11/12/2017 6 | Version 1.0 7 | 8 | Copyright © 2018 Peter Wallen. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | This file contains a number of view controllers used to provide a User Interface for demonstrating the 30 | Microbit Bluetooth Application Programming Interface (see Microbit.swift) 31 | */ 32 | import UIKit 33 | 34 | public class MicrobitUIController: UIViewController,UITableViewDelegate,UITableViewDataSource { 35 | public var microbit:Microbit? 36 | var tableView = UITableView() 37 | var menu = ["Accelerometer", 38 | "Beacon", 39 | "Buttons", 40 | "Events", 41 | "LED", 42 | "Magnetometer", 43 | "Pin IO", 44 | "Temperature", 45 | "UART", 46 | "Log"] 47 | 48 | public override func viewDidLoad() { 49 | super.viewDidLoad() 50 | view.backgroundColor = .white 51 | 52 | let margins = view.layoutMarginsGuide 53 | 54 | let title = UILabel() 55 | title.text = "Micro:bit Test App." 56 | title.textAlignment = .center 57 | let titleFont = UIFont.systemFont(ofSize: 30.0, weight: .bold) 58 | title.font = titleFont 59 | title.translatesAutoresizingMaskIntoConstraints = false 60 | view.addSubview(title) 61 | 62 | Layout.manager(title,margins:margins,left:0,right:0,top:20,bottom:50,pinH:.both,pinV:.top) 63 | 64 | tableView.delegate = self 65 | tableView.dataSource = self 66 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") 67 | tableView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height) 68 | tableView.translatesAutoresizingMaskIntoConstraints = false 69 | view.addSubview(tableView) 70 | 71 | Layout.manager(tableView,margins:margins,left:0,right:0,top:60,bottom:0,pinH:.both,pinV:.both) 72 | } 73 | 74 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 75 | return menu.count 76 | } 77 | 78 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 79 | let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) 80 | cell.textLabel?.text = menu[indexPath.row] 81 | return cell 82 | } 83 | public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 84 | switch menu[indexPath.row] { 85 | case "Accelerometer" : 86 | let newViewController = MicrobitAccelerometerController() 87 | newViewController.microbit = microbit 88 | present(newViewController, animated: true, completion: nil) 89 | case "Beacon" : 90 | let newViewController = MicrobitBeaconController() 91 | newViewController.microbit = microbit 92 | present(newViewController, animated: true, completion: nil) 93 | case "Buttons" : 94 | let newViewController = MicrobitButtonsController() 95 | newViewController.microbit = microbit 96 | present(newViewController, animated: true, completion: nil) 97 | case "Events" : 98 | let newViewController = MicrobitEventsController() 99 | newViewController.microbit = microbit 100 | present(newViewController, animated: true, completion: nil) 101 | case "LED" : 102 | let newViewController = MicrobitLEDController() 103 | newViewController.microbit = microbit 104 | present(newViewController, animated: true, completion: nil) 105 | case "Magnetometer" : 106 | let newViewController = MicrobitMagnetometerController() 107 | newViewController.microbit = microbit 108 | present(newViewController, animated: true, completion: nil) 109 | case "Pin IO" : 110 | let newViewController = MicrobitPinIOController() 111 | newViewController.microbit = microbit 112 | present(newViewController, animated: true, completion: nil) 113 | case "Temperature" : 114 | let newViewController = MicrobitTemperatureController() 115 | newViewController.microbit = microbit 116 | present(newViewController, animated: true, completion: nil) 117 | case "UART" : 118 | let newViewController = MicrobitUARTController() 119 | newViewController.microbit = microbit 120 | present(newViewController, animated: true, completion: nil) 121 | case "Log" : 122 | let newViewController = MicrobitLogController() 123 | newViewController.microbit = microbit 124 | present(newViewController, animated: true, completion: nil) 125 | default : 126 | break 127 | } 128 | } 129 | } 130 | class MicrobitLogController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 131 | var microbit:Microbit? 132 | var microbitLogView:MicrobitLogView? 133 | var microbitControl:MicrobitControl? 134 | override func viewDidLoad() { 135 | super.viewDidLoad() 136 | view.backgroundColor = .white 137 | let backButton = UIButton(type:.system) 138 | backButton.setTitle("Back",for: .normal) 139 | backButton.translatesAutoresizingMaskIntoConstraints = false 140 | backButton.addTarget(self,action: #selector(MicrobitLogController.backButton),for: .primaryActionTriggered) 141 | view.addSubview(backButton) 142 | 143 | let margins = view.layoutMarginsGuide 144 | 145 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 146 | 147 | microbitLogView = MicrobitLogView(view:view,viewSize:.large) 148 | microbitControl = MicrobitControl(view: view) 149 | microbitControl?.delegate = self 150 | } 151 | override func viewDidAppear(_ animated: Bool) { 152 | microbit?.delegate = self 153 | microbitLogView?.updateLogView(log: (microbit?.log)!) 154 | } 155 | @objc func backButton(sender:UIButton) { 156 | dismiss(animated: true, completion: nil) 157 | } 158 | // MARK: Implement required MicrobitUIDelegate functions 159 | func startScanning() { 160 | microbit?.startScanning() 161 | } 162 | func stopScanning() { 163 | microbit?.stopScanning() 164 | } 165 | func disconnect() { 166 | microbit?.disconnect() 167 | } 168 | // MARK: Implement required MicrobitDelegate functions 169 | func logUpdated(_ log: [String]) { 170 | microbitLogView?.updateLogView(log: log) 171 | } 172 | } 173 | class MicrobitAccelerometerController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 174 | var microbit:Microbit? 175 | var microbitLogView:MicrobitLogView? 176 | var microbitAccelerometer:MicrobitAccelerometer? 177 | var microbitControl:MicrobitControl? 178 | override func viewDidLoad() { 179 | super.viewDidLoad() 180 | view.backgroundColor = .white 181 | let backButton = UIButton(type:.system) 182 | backButton.setTitle("Back",for: .normal) 183 | backButton.translatesAutoresizingMaskIntoConstraints = false 184 | backButton.addTarget(self,action: #selector(MicrobitAccelerometerController.backButton),for: .primaryActionTriggered) 185 | view.addSubview(backButton) 186 | 187 | let margins = view.layoutMarginsGuide 188 | 189 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 190 | 191 | microbitAccelerometer = MicrobitAccelerometer(view:view) 192 | microbitAccelerometer?.delegate = self 193 | microbitControl = MicrobitControl(view: view) 194 | microbitLogView = MicrobitLogView(view:view,viewSize:.medium) 195 | microbitControl?.delegate = self 196 | } 197 | override func viewDidAppear(_ animated: Bool) { 198 | microbit?.delegate = self 199 | microbitLogView?.updateLogView(log: (microbit?.log)!) 200 | } 201 | @objc func backButton(sender:UIButton) { 202 | dismiss(animated: true, completion: nil) 203 | } 204 | // MARK: Implement required MicrobitUIDelegate functions 205 | func startScanning() { 206 | microbit?.startScanning() 207 | } 208 | func stopScanning() { 209 | microbit?.stopScanning() 210 | } 211 | func disconnect() { 212 | microbit?.disconnect() 213 | } 214 | func accelerometerSet(period: PeriodType) { 215 | microbit?.accelerometer(period: period) 216 | } 217 | // MARK: Implement required MicrobitDelegate functions 218 | func accelerometerData(x: Int16, y: Int16, z: Int16) { 219 | microbitAccelerometer?.update(x:x,y:y,z:z) 220 | } 221 | func logUpdated(_ log: [String]) { 222 | microbitLogView?.updateLogView(log: log) 223 | } 224 | func serviceAvailable(service:ServiceName) { 225 | if service == .Accelerometer { 226 | microbitAccelerometer?.setPeriod() 227 | } 228 | } 229 | } 230 | class MicrobitMagnetometerController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 231 | var microbit:Microbit? 232 | var microbitMagnetometer:MicrobitMagnetometer? 233 | var microbitLogView:MicrobitLogView? 234 | var microbitControl:MicrobitControl? 235 | override func viewDidLoad() { 236 | super.viewDidLoad() 237 | view.backgroundColor = .white 238 | let backButton = UIButton(type:.system) 239 | backButton.setTitle("Back",for: .normal) 240 | backButton.translatesAutoresizingMaskIntoConstraints = false 241 | backButton.addTarget(self,action: #selector(MicrobitMagnetometerController.backButton),for: .primaryActionTriggered) 242 | view.addSubview(backButton) 243 | 244 | let margins = view.layoutMarginsGuide 245 | 246 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 247 | 248 | microbitMagnetometer = MicrobitMagnetometer(view:view) 249 | microbitMagnetometer?.delegate = self 250 | microbitControl = MicrobitControl(view: view) 251 | microbitControl?.delegate = self 252 | microbitLogView = MicrobitLogView(view:view,viewSize:.small) 253 | } 254 | override func viewDidAppear(_ animated: Bool) { 255 | microbit?.delegate = self 256 | microbitLogView?.updateLogView(log: (microbit?.log)!) 257 | } 258 | @objc func backButton(sender:UIButton) { 259 | dismiss(animated: true, completion: nil) 260 | } 261 | // MARK: Implement required MicrobitUIDelegate functions 262 | func startScanning() { 263 | microbit?.startScanning() 264 | } 265 | func stopScanning() { 266 | microbit?.stopScanning() 267 | } 268 | func disconnect() { 269 | microbit?.disconnect() 270 | } 271 | func magnetometerSet(period: PeriodType) { 272 | microbit?.magnetometer(period: period) 273 | } 274 | // MARK: Implement required MicrobitDelegate functions 275 | func magnetometerData(x: Int16, y: Int16, z: Int16) { 276 | microbitMagnetometer?.updateData(x:x,y:y,z:z) 277 | } 278 | func compass(bearing: Int16) { 279 | microbitMagnetometer?.updateBearing(bearing: bearing) 280 | } 281 | func logUpdated(_ log: [String]) { 282 | microbitLogView?.updateLogView(log: log) 283 | } 284 | func serviceAvailable(service:ServiceName) { 285 | if service == .Magnetometer { 286 | microbitMagnetometer?.setPeriod() 287 | } 288 | } 289 | } 290 | class MicrobitButtonsController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 291 | var microbit:Microbit? 292 | var microbitButtons:MicrobitButtons? 293 | var microbitControl:MicrobitControl? 294 | var microbitLogView:MicrobitLogView? 295 | override func viewDidLoad() { 296 | super.viewDidLoad() 297 | view.backgroundColor = .white 298 | let backButton = UIButton(type:.system) 299 | backButton.setTitle("Back",for: .normal) 300 | backButton.translatesAutoresizingMaskIntoConstraints = false 301 | backButton.addTarget(self,action: #selector(MicrobitButtonsController.backButton),for: .primaryActionTriggered) 302 | view.addSubview(backButton) 303 | 304 | let margins = view.layoutMarginsGuide 305 | 306 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 307 | 308 | microbitButtons = MicrobitButtons(view:view) 309 | microbitControl = MicrobitControl(view: view) 310 | microbitControl?.delegate = self 311 | microbitLogView = MicrobitLogView(view:view,viewSize:.small) 312 | } 313 | override func viewDidAppear(_ animated: Bool) { 314 | microbit?.delegate = self 315 | microbitLogView?.updateLogView(log: (microbit?.log)!) 316 | } 317 | @objc func backButton(sender:UIButton) { 318 | dismiss(animated: true, completion: nil) 319 | } 320 | // MARK: Implement required MicrobitUIDelegate functions 321 | func startScanning() { 322 | microbit?.startScanning() 323 | } 324 | func stopScanning() { 325 | microbit?.stopScanning() 326 | } 327 | func disconnect() { 328 | microbit?.disconnect() 329 | } 330 | func buttonPressed(button: String, action: MicrobitButtonType) { 331 | microbitButtons?.update(button: button, action: action) 332 | } 333 | // MARK: Implement required MicrobitDelegate functions 334 | func logUpdated(_ log: [String]) { 335 | microbitLogView?.updateLogView(log: log) 336 | } 337 | func serviceAvailable(service:ServiceName) { 338 | if service == .Button { 339 | 340 | } 341 | } 342 | } 343 | class MicrobitTemperatureController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 344 | var microbit:Microbit? 345 | var microbitTemperature:MicrobitTemperature? 346 | var microbitControl:MicrobitControl? 347 | var microbitLogView:MicrobitLogView? 348 | override func viewDidLoad() { 349 | super.viewDidLoad() 350 | view.backgroundColor = .white 351 | let backButton = UIButton(type:.system) 352 | backButton.setTitle("Back",for: .normal) 353 | backButton.translatesAutoresizingMaskIntoConstraints = false 354 | backButton.addTarget(self,action: #selector(MicrobitTemperatureController.backButton),for: .primaryActionTriggered) 355 | view.addSubview(backButton) 356 | 357 | let margins = view.layoutMarginsGuide 358 | 359 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 360 | 361 | microbitTemperature = MicrobitTemperature(view:view) 362 | microbitTemperature?.delegate = self 363 | microbitControl = MicrobitControl(view: view) 364 | microbitControl?.delegate = self 365 | microbitLogView = MicrobitLogView(view:view,viewSize:.small) 366 | } 367 | override func viewDidAppear(_ animated: Bool) { 368 | microbit?.delegate = self 369 | microbitLogView?.updateLogView(log: (microbit?.log)!) 370 | } 371 | @objc func backButton(sender:UIButton) { 372 | dismiss(animated: true, completion: nil) 373 | } 374 | // MARK: Implement required MicrobitUIDelegate functions 375 | func startScanning() { 376 | microbit?.startScanning() 377 | } 378 | func stopScanning() { 379 | microbit?.stopScanning() 380 | } 381 | func disconnect() { 382 | microbit?.disconnect() 383 | } 384 | func temperatureSet(period: UInt16) { 385 | microbit?.temperature(period: period) 386 | } 387 | // MARK: Implement required MicrobitDelegate functions 388 | func logUpdated(_ log: [String]) { 389 | microbitLogView?.updateLogView(log: log) 390 | } 391 | func temperature(value: Int16) { 392 | microbitTemperature?.update(temperature: value) 393 | } 394 | func serviceAvailable(service:ServiceName) { 395 | if service == .Temperature { 396 | 397 | } 398 | } 399 | } 400 | class MicrobitLEDController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 401 | var microbit:Microbit? 402 | var microbitLED:MicrobitLED? 403 | var microbitControl:MicrobitControl? 404 | var microbitLogView:MicrobitLogView? 405 | override func viewDidLoad() { 406 | super.viewDidLoad() 407 | view.backgroundColor = .white 408 | let backButton = UIButton(type:.system) 409 | backButton.setTitle("Back",for: .normal) 410 | backButton.translatesAutoresizingMaskIntoConstraints = false 411 | backButton.addTarget(self,action: #selector(MicrobitTemperatureController.backButton),for: .primaryActionTriggered) 412 | view.addSubview(backButton) 413 | 414 | let margins = view.layoutMarginsGuide 415 | 416 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 417 | 418 | microbitLED = MicrobitLED(view:view) 419 | microbitLED?.delegate = self 420 | microbitControl = MicrobitControl(view: view) 421 | microbitControl?.delegate = self 422 | microbitLogView = MicrobitLogView(view:view,viewSize:.small) 423 | } 424 | override func viewDidAppear(_ animated: Bool) { 425 | microbit?.delegate = self 426 | microbitLogView?.updateLogView(log: (microbit?.log)!) 427 | } 428 | @objc func backButton(sender:UIButton) { 429 | dismiss(animated: true, completion: nil) 430 | } 431 | // MARK: Implement required MicrobitUIDelegate functions 432 | func startScanning() { 433 | microbit?.startScanning() 434 | } 435 | func stopScanning() { 436 | microbit?.stopScanning() 437 | } 438 | func disconnect() { 439 | microbit?.disconnect() 440 | } 441 | func ledText(message: String, scrollRate: Int16) { 442 | microbit?.ledText(message: message, scrollRate: scrollRate) 443 | } 444 | func ledSet(matrix: [UInt8]) { 445 | microbit?.ledWrite(matrix: matrix) 446 | } 447 | // MARK: Implement required MicrobitDelegate functions 448 | func logUpdated(_ log: [String]) { 449 | microbitLogView?.updateLogView(log: log) 450 | } 451 | func serviceAvailable(service:ServiceName) { 452 | if service == .LED { 453 | 454 | } 455 | } 456 | } 457 | class MicrobitPinIOController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 458 | var microbit:Microbit? 459 | var microbitPinIO:MicrobitPinIO? 460 | var microbitControl:MicrobitControl? 461 | var microbitLogView:MicrobitLogView? 462 | override func viewDidLoad() { 463 | super.viewDidLoad() 464 | view.backgroundColor = .white 465 | let backButton = UIButton(type:.system) 466 | backButton.setTitle("Back",for: .normal) 467 | backButton.translatesAutoresizingMaskIntoConstraints = false 468 | backButton.addTarget(self,action: #selector(MicrobitPinIOController.backButton),for: .primaryActionTriggered) 469 | view.addSubview(backButton) 470 | 471 | let margins = view.layoutMarginsGuide 472 | 473 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 474 | 475 | microbitPinIO = MicrobitPinIO(view:view) 476 | microbitPinIO?.delegate = self 477 | microbitControl = MicrobitControl(view: view) 478 | microbitControl?.delegate = self 479 | microbitLogView = MicrobitLogView(view:view,viewSize:.small) 480 | } 481 | override func viewDidAppear(_ animated: Bool) { 482 | microbit?.delegate = self 483 | microbitLogView?.updateLogView(log: (microbit?.log)!) 484 | } 485 | @objc func backButton(sender:UIButton) { 486 | dismiss(animated: true, completion: nil) 487 | } 488 | // MARK: Implement required MicrobitUIDelegate functions 489 | func startScanning() { 490 | microbit?.startScanning() 491 | } 492 | func stopScanning() { 493 | microbit?.stopScanning() 494 | } 495 | func disconnect() { 496 | microbit?.disconnect() 497 | } 498 | func pinWrite(value: [UInt8 : UInt8]) { 499 | microbit?.pinSet(pinValues: value) 500 | } 501 | func pinSetfor(read:[UInt8:Bool]) { 502 | microbit?.pinConfigure(readPins:read) 503 | } 504 | func pinSetfor(analogue: [UInt8:Bool]) { 505 | microbit?.pinConfigure(analougePins:analogue) 506 | } 507 | // MARK: Implement required MicrobitDelegate functions 508 | func pinGet(pins: [UInt8 : UInt8]) { 509 | microbitPinIO?.update(pins: pins) 510 | } 511 | func logUpdated(_ log: [String]) { 512 | microbitLogView?.updateLogView(log: log) 513 | } 514 | func serviceAvailable(service:ServiceName) { 515 | if service == .IOPin { 516 | 517 | } 518 | } 519 | } 520 | class MicrobitEventsController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 521 | var microbit:Microbit? 522 | var microbitUIEvent:MicrobitUIEvent? 523 | var microbitControl:MicrobitControl? 524 | var microbitLogView:MicrobitLogView? 525 | override func viewDidLoad() { 526 | super.viewDidLoad() 527 | view.backgroundColor = .white 528 | let backButton = UIButton(type:.system) 529 | backButton.setTitle("Back",for: .normal) 530 | backButton.translatesAutoresizingMaskIntoConstraints = false 531 | backButton.addTarget(self,action: #selector(MicrobitEventsController.backButton),for: .primaryActionTriggered) 532 | view.addSubview(backButton) 533 | 534 | let margins = view.layoutMarginsGuide 535 | 536 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 537 | 538 | microbitUIEvent = MicrobitUIEvent(view:view) 539 | microbitUIEvent?.delegate = self 540 | microbitControl = MicrobitControl(view: view) 541 | microbitControl?.delegate = self 542 | microbitLogView = MicrobitLogView(view:view,viewSize:.medium) 543 | } 544 | override func viewDidAppear(_ animated: Bool) { 545 | microbit?.delegate = self 546 | microbitLogView?.updateLogView(log: (microbit?.log)!) 547 | } 548 | @objc func backButton(sender:UIButton) { 549 | dismiss(animated: true, completion: nil) 550 | } 551 | // MARK: Implement required MicrobitUIDelegate functions 552 | func startScanning() { 553 | microbit?.startScanning() 554 | } 555 | func stopScanning() { 556 | microbit?.stopScanning() 557 | } 558 | func disconnect() { 559 | microbit?.disconnect() 560 | } 561 | func raiseEvent(event: MicrobitEvent, value: UInt16) { 562 | microbit?.raiseEvent(event: event, value: value) 563 | } 564 | func event(register: [Int16]) { 565 | microbit?.registerEvents(events: register) 566 | } 567 | // MARK: Implement required MicrobitDelegate functions 568 | func microbitEvent(type: Int16, value: Int16) { 569 | microbitUIEvent?.update(type: type, value: value) 570 | } 571 | func logUpdated(_ log: [String]) { 572 | microbitLogView?.updateLogView(log: log) 573 | } 574 | func serviceAvailable(service:ServiceName) { 575 | if service == .Event { 576 | microbitUIEvent?.registerEvents(events: [9010,9011,9012,9013,9014,9015]) 577 | } 578 | } 579 | } 580 | class MicrobitUARTController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 581 | var microbit:Microbit? 582 | var microbitUART:MicrobitUART? 583 | var microbitControl:MicrobitControl? 584 | var microbitLogView:MicrobitLogView? 585 | override func viewDidLoad() { 586 | super.viewDidLoad() 587 | view.backgroundColor = .white 588 | let backButton = UIButton(type:.system) 589 | backButton.setTitle("Back",for: .normal) 590 | backButton.translatesAutoresizingMaskIntoConstraints = false 591 | backButton.addTarget(self,action: #selector(MicrobitUARTController.backButton),for: .primaryActionTriggered) 592 | view.addSubview(backButton) 593 | 594 | let margins = view.layoutMarginsGuide 595 | 596 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 597 | 598 | microbitUART = MicrobitUART(view:view) 599 | microbitUART?.delegate = self 600 | microbitControl = MicrobitControl(view: view) 601 | microbitControl?.delegate = self 602 | microbitLogView = MicrobitLogView(view:view,viewSize:.medium) 603 | } 604 | override func viewDidAppear(_ animated: Bool) { 605 | microbit?.delegate = self 606 | microbitLogView?.updateLogView(log: (microbit?.log)!) 607 | } 608 | @objc func backButton(sender:UIButton) { 609 | dismiss(animated: true, completion: nil) 610 | } 611 | // MARK: Implement required MicrobitUIDelegate functions 612 | func startScanning() { 613 | microbit?.startScanning() 614 | } 615 | func stopScanning() { 616 | microbit?.stopScanning() 617 | } 618 | func disconnect() { 619 | microbit?.disconnect() 620 | } 621 | func uartSend(message: String) { 622 | microbit?.uartSend(message: message) 623 | } 624 | // MARK: Implement required MicrobitDelegate functions 625 | func uartReceived(message: String) { 626 | microbitUART?.outputField.text = message 627 | } 628 | func logUpdated(_ log: [String]) { 629 | microbitLogView?.updateLogView(log: log) 630 | } 631 | func serviceAvailable(service:ServiceName) { 632 | if service == .UART { 633 | 634 | } 635 | } 636 | } 637 | class MicrobitBeaconController:UIViewController,MicrobitUIDelegate,MicrobitDelegate { 638 | var microbit:Microbit? 639 | var microbitUIAdvertisement:MicrobitUIAdvertisement? 640 | var microbitControl:MicrobitControl? 641 | var microbitLogView:MicrobitLogView? 642 | override func viewDidLoad() { 643 | super.viewDidLoad() 644 | view.backgroundColor = .white 645 | let backButton = UIButton(type:.system) 646 | backButton.setTitle("Back",for: .normal) 647 | backButton.translatesAutoresizingMaskIntoConstraints = false 648 | backButton.addTarget(self,action: #selector(MicrobitBeaconController.backButton),for: .primaryActionTriggered) 649 | view.addSubview(backButton) 650 | 651 | let margins = view.layoutMarginsGuide 652 | 653 | Layout.manager(backButton,margins:margins,left:-50,right:0,top:20,bottom:40,pinH:.right,pinV:.top) 654 | 655 | microbitUIAdvertisement = MicrobitUIAdvertisement(view:view) 656 | microbitControl = MicrobitControl(view: view) 657 | microbitControl?.delegate = self 658 | microbitLogView = MicrobitLogView(view:view,viewSize:.medium) 659 | } 660 | override func viewDidAppear(_ animated: Bool) { 661 | microbit?.delegate = self 662 | microbitLogView?.updateLogView(log: (microbit?.log)!) 663 | } 664 | @objc func backButton(sender:UIButton) { 665 | dismiss(animated: true, completion: nil) 666 | } 667 | // MARK: Implement required MicrobitUIDelegate functions 668 | func startScanning() { 669 | microbit?.startScanning() 670 | } 671 | func stopScanning() { 672 | microbit?.stopScanning() 673 | } 674 | func disconnect() { 675 | microbit?.disconnect() 676 | } 677 | // MARK: Implement required MicrobitDelegate functions 678 | func advertisementData(url: String, namespace: Int64, instance: Int32, RSSI: Int) { 679 | microbitUIAdvertisement?.upadateAdverisementData(url: url, namespace: namespace, instance: instance, RSSI: RSSI) 680 | } 681 | func logUpdated(_ log: [String]) { 682 | microbitLogView?.updateLogView(log: log) 683 | } 684 | } 685 | -------------------------------------------------------------------------------- /Microbit/Microbit/README.md: -------------------------------------------------------------------------------- 1 | # Micro:bit Test App. 2 | This project is designed to test the Micro:bit Bluetooth interface contained in file Microbit.swift. 3 | 4 | It includes a User Inteface coded programatically rather than using Xcode's Interface Builder so that the 5 | Test App can be used as a Swift Playground. Therefore, AppDelegate sets and instance of MicrobitUIController as the 6 | root view controller. 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microbit-Swift 2 | Microbit-Swift is an application programming interface written in Swift for use with the micro:bit computer. It allows programs written for Apple devices to communicate with the micro:bit using Bluetooth Low Energy. 3 | 4 | For information about this project and how to use the API see: [Micro:bit Swift Bluetooth LE](https://phwallen.github.io/microbit-swift/) 5 | 6 | Microbit.playground.zip demonstrates the use of the API. It can be downloaded to an iPad that has **Swift Playgrounds** installed. 7 | 8 | The Microbit directory contains an Xcode project for testing the components of the playground. 9 | 10 | The source files for the Playground are: 11 | 12 | - Microbit.swift - The API can be used on any platform that supports Core Bluetooth i.e. macOS, iOS, tvOS and watchOS. To use: copy this file into your Xcode project. 13 | - MicrobitUIController.swift - A set of view controllers used by the Playground. 14 | - MicrobitUI - A set of custom views used by the view controllers. -------------------------------------------------------------------------------- /docs/images/makecode-Beacon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phwallen/microbit-swift/bef6036b7ffc1af973e768812941e87b5fd5f64a/docs/images/makecode-Beacon.png -------------------------------------------------------------------------------- /docs/images/makecode-Events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phwallen/microbit-swift/bef6036b7ffc1af973e768812941e87b5fd5f64a/docs/images/makecode-Events.png -------------------------------------------------------------------------------- /docs/images/makecode_Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phwallen/microbit-swift/bef6036b7ffc1af973e768812941e87b5fd5f64a/docs/images/makecode_Button.png -------------------------------------------------------------------------------- /docs/images/makecode_UART.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phwallen/microbit-swift/bef6036b7ffc1af973e768812941e87b5fd5f64a/docs/images/makecode_UART.png -------------------------------------------------------------------------------- /docs/images/microbit_playground_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phwallen/microbit-swift/bef6036b7ffc1af973e768812941e87b5fd5f64a/docs/images/microbit_playground_menu.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | The micro:bit is a great bit of kit for hobbyists, designers, educators and of course students. 3 | 4 | - It’s cheap. For under £13, you get a very versatile micro computer and, while I don’t condone cruelty to electronic components, if the worst happens it won’t break the bank to replace. For many in 2016 it was even cheaper, well free, when the BBC gave one to a million school children. 5 | - It’s been well designed. Based on the Nordic nRF51822 micro processor, the credit card size micro computer can be easily integrated into electronic projects taking ideas to fully working models. 6 | - It’s simple to program. Using the JavaScript Blocks Editor programs can be created and transferred to the micro:bit without complicated development environments. 7 | - It implements Bluetooth Low Energy (BLE). It provides an easy way of introducing BLE into projects and facilitating communication with devices like smart phones and tablets. 8 | - It’s open source. With the hardware licensed under Solderpad and the runtime software under MIT, designers are free to develop products based on the micro:bit design. 9 | 10 | Of course there is a plethora of devices that can do similar things to the micro:bit. Some are specific and promote particular features of chip manufactures while others like Arduino and Raspberry pi are general purpose but may require greater complexity and expense to achieve a requirement easy served by the micro:bit. 11 | 12 | One of the strengths of the micro:bit is it's implementation of BLE. Martin Woolley, the principal architect for the micro:bit bluetooth implementation, has produced some very interesting articles: 13 | 14 | - [Bluetooth and the micro:bit](http://blog.bluetooth.com/bbc-microbit-inspiring-generation-get-creative-coding) 15 | - [Designing the Profile for the micro:bit](http://blog.bluetooth.com/bluetooth-bbc-microbit) 16 | - [Developing Applications For The BBC Microbit](http://blog.bluetooth.com/developing-applications-bbc-microbit) 17 | 18 | Martin has written a number of applications for the Android Operating system that demonstrate how the micro:bit can be controlled using a smart phone. There are, however, few examples, at the time of writing, on developing applications for Apple devices such as iPhones and iPads. Apart from the fact there are lots of iPhones and iPads waiting to control micro:bits, there are a few other reasons for writing code for iOS devices: 19 | 20 | - IOS devices do not lend themselves to be hard-wired to external hardware via a USB connection. Apple view this type of interface as an external accessory which would have to be licensed under the Apple MFi program. This is hardly appropriate for ad hoc experimentation. This is where BLE comes in. Apple does a good job supporting Bluetooth through it’s Core Bluetooth API and the micro:bit does an equally good job implementing the bluetooth stack which allows iPads and iPhones to communicate with the micro:bit wirelessly. What’s more, Core Bluetooth is also supported on macOS, tvOS and watchOS, meaning your micro:bit can communicate with even more device you might own. 21 | - A few years ago, writing code to run on your own iPad or iPhone would have meant you signing-up for the apple developer program, but now if apps are only for devices you own all you need is an Apple ID which, if you are an apple user, you probably already have (if you want to distribute your code through the App store , you still need to join the developer program ,currently $99 a year). This means if you own an iPad, iPhone, iPod, mac, apple TV or apple watch you can write an app to communicate with your micro:bit project. 22 | - If writing apps all seems a bit over the top (all you want is to try out an idea), then, since 2016 there is another way of controlling a micro:bit from an iPad. It’s called Swift Playgrounds and it’s a free app from apple. It’s primary purpose is to teach coding, specifically using the Swift programming language. This done via an electronic publication known as a ‘playground book’. Students can download these playgrounds on different coding topics and interact with them in a fun way to learn computer programming. You can also use Swift Playgrounds to write code from scratch, perhaps to try some thing out, like controlling your micro:bit. 23 | 24 | This project describes an Application Programming Interface (API), that can be used to develop applications for Apple operating systems, that interact with the micro:bit. 25 | 26 | The API is written in the Swift programming language and exploits the Apple Core Bluetooth framework. It's easily installed in any Xcode project by simply copying [Microbit.swift](https://github.com/phwallen/microbit-swift/blob/master/Microbit/Microbit/Microbit.swift) into an Xcode project. 27 | 28 | For further information about Microbit.swift see: **Appendix A - Microbit API** 29 | 30 | # The Microbit Playground 31 | 32 | To demonstrate the API there is a [micro:bit Swift Playground](https://github.com/phwallen/microbit-swift/blob/master/Microbit.playground.zip). 33 | 34 | Usually when developing applications, you would use Xcode's powerful user interface building facilities, however, these are not available to Playgrounds so it is necessary to build user interfaces programmatically. The Micro:bit Playground includes the necessary view controllers and custom views to interact with the micro:bit API. 35 | 36 | The view controllers are contained in a single file: [MicrobitUIController](https://github.com/phwallen/microbit-swift/blob/master/Microbit/Microbit/MicrobitUIController.swift) and the custom views are in the file: [MicrobitUI.swift](https://github.com/phwallen/microbit-swift/blob/master/Microbit/Microbit/MicrobitUI.swift). 37 | 38 | ## Getting Started 39 | 40 | To use the micro:bit Swift Playground you need to do a few things: 41 | 42 | - Download the micro:bit playground. 43 | - Program the micro:bit. 44 | - Edit the micro:bit playground. 45 | - Test that iPad and micro:bit are talking to each other. 46 | ### Download the micro:bit playground. 47 | 48 | The micro:bit playground has been designed to run on any iPad that has [Swift Playgrounds](https://www.apple.com/uk/swift/playgrounds/) installed. You can install Swift Playgrounds for free by downloading it from the App Store. 49 | 50 | Download [Microbit.playground](https://github.com/phwallen/microbit-swift) on an iPad that supports Swift Playgrounds. Tap on: **Microbit.playground.zip** to open, and, tap the **Download** button. 51 | 52 | The Microbit.playground will be downloaded to the iPad. Providing Swift playgrounds is installed, you will see and option to **Open in "Playgrounds"**. 53 | 54 | Open the **Microbit** playground from the MyPlaygrounds screen. Test it works by tapping **Run My Code** at the bottom of the screen. You should see a screen that looks a bit like this: 55 | 56 | ![microbit_playground_menu](images/microbit_playground_menu.png) 57 | 58 | Select the **Log** option and check for the message "Bluetooth is available". If you don't see this message, check that Bluetooth is turned on in settings. 59 | 60 | ### Program the micro:bit 61 | 62 | You will need to write a simple program to test the playground, if you are new to using the micro:bit read: [Using micro:bit in 5 easy steps](http://microbit.org/guide/quick/). The easiest way to write a program is with the online [Javascript Blocks Editor](https://makecode.microbit.org). 63 | 64 | By default the editor does not include the Bluetooth package, so you you will need to add it using the **Add Package** option in the Advanced section of the packages list. For further information on how this is done [see this article](https://support.microbit.org/support/solutions/articles/19000062330-using-the-micro-bit-bluetooth-low-energy-uart-serial-over-bluetooth-). 65 | 66 | As a test, write a program that implements the Bluetooth Button Service. Arrange the blocks as shown in the following: 67 | ![makecode_Button](images/makecode_Button.png) 68 | To implement the button service, simply add the **bluetooth button service** block to the **on start** block. Also. add some code to indicate the 'state' of the program - 'S' program has started, 'C' program is connected to the iPad, 'D' the program has disconnected from the iPad. 69 | 70 | There is one more thing to do before downloading the program to the micro:bit. The pairing option determines how the micro:bit will pair with the iPad. For details about the various pairing option see the section on **Pairing**. For performing this test, turn off pairing. Select the cog in the top right of the editor and then select **Project Settings**. Now select - **No Pairing Required: Anyone can connect via Bluetooth.** 71 | 72 | You can now download the program to the micro:bit. 73 | 74 | ### Edit the micro:bit playground 75 | 76 | The micro:bit playground as supplied is very simple. It: 77 | 78 | - initialises a MicrobitUIController 79 | - initialises the MicrobitUIController.microbit property using the Microbit class. 80 | - passes the view controller to playground support for display. 81 | 82 | The Microbit initialiser requires a parameter which is the name the micro:bit advertises. Currently this is in the form of **BBC micro:bit [identifier]**. The identifier will vary between micro:bits. One way to find out the name of your micro:bit is to: 83 | 84 | - run the micro:bit playground. 85 | - select the **Log** option. 86 | - press the **Start Scan** button. 87 | - look for a log message that says **Possible device detected:**. 88 | - press the **Stop Scan** button. 89 | 90 | Providing the micro:bit is running a bluetooth program you should see the device name e.g. 91 | **BBC micro:bit [tizip]**. You can now edit the playground to set the correct name e.g. 92 | ``` 93 | vc.microbit = Microbit("BBC micro:bit [tizip]") 94 | ``` 95 | ### Test the button service 96 | 97 | - power the micro:bit and check that it displays **S** 98 | - run the micro:bit playground. 99 | - select the **Buttons** option. 100 | - press the **Start Scan** button. 101 | 102 | The log at the bottom of the playground screen should show a number of messages, including messages that Button state characteristics have been found and the serial number and firmware revision. Also the micro:bit should show a **C** on the led matrix. 103 | 104 | You can now try pressing the A button on the micro:bit and see if the indicator on the playground changes from black to red. Release the button and the indicator should return to black. Now press the button and hold it down for more than 2 seconds. The indicator should change from red to blue. Repeat the process using button B. If that works you have successfully set up your Swift playground to communicate with a micro:bit. 105 | 106 | You should be able to disconnect the micro:bit by pressing **Disconnect**. Check that the micro:bit now displays a **D**. Try connecting again by pressing the **Start Scan**. Disconnection and reconnection may cause the micro:bit program to fail particularly if a lot is running on the micro:bit e.g. managing a number of bluetooth services. If the communication has stoped, or, perhaps random LEDs are on, press the reset button on the micro:bit so that it shows **S** and press **Start Scan** to reconnect. 107 | 108 | ## Pairing 109 | 110 | Bluetooth pairing creates a trusted relationship between two devices and allows them to exchange encrypted data. The pairing procedure does add a level of complication that might not be necessary and, unfortunately, each time the micro:bit is reloaded with a program over USB, the pairing procedure must be repeated. Flashing the micro:bit 'over the air' maintains pairing details so avoids repeating the pairing procedure. To flash the micro:bit over the air you need the free micro:bit app available from the Apple app store. 111 | 112 | The Javascript Blocks Editor allows you to set one of three types of pairing when compiling a bluetooth program: 113 | 114 | - No Pairing Required: Anyone can connect via Bluetooth. 115 | - JustWorks pairing (default): Pairing is automatic once the pairing is initiated. 116 | - Passkey pairing: Pairing requires 6 digit key to pair. 117 | 118 | These options are set in the Project Setting menu accessed by pressing the cog icon at the top right of the Javascript Blocks Editor. 119 | 120 | For for general information about pairing the micro:bit with devices such as smart phones and tablets see: [Bluetooth Pairing](https://makecode.microbit.org/reference/bluetooth/bluetooth-pairing). 121 | 122 | **Note:** The official micro:bit pairing documentation refers to the need for an application such as 'nRF Master Control Panel' to trigger pairing when using an IOS device. This is not necessary if using the Microbit class described in this document. Initialisation of a Microbit object causes the Event Service - client requirements characteristic to be written, forcing the pairing to be triggered. 123 | 124 | The following is specific to pairing the micro:bit playground. 125 | 126 | ### No Pairing 127 | 128 | This option is ideal for testing, particularly if you need to repeatedly download code to the micro:bit. All you need to do is ensure the the Microbit class initialiser is called with the correct name of the micro:bit. See **Edit the micro:bit playground** above to find out how to find the name of your micro:bit. 129 | 130 | ### JustWorks pairing 131 | 132 | This option is the default if using the Javascript Blocks Editor. It provides security without the need for a passcode, but it does mean the following process must be repeated each time you download code to the micro:bit. 133 | To use 'JustWorks pairing' follow these steps: 134 | 135 | - Select the JustWorks option in Project Settings before downloading your code to the micro:bit. 136 | - Ensure the iPad has ‘forgotten’ the micro:bit - Go to Settings-Bluetooth and check MY DEVICES. If the BBC micro:bit is listed, select the information icon and select “Forget This Device” 137 | - Call the Microbit class initialiser with the name of the micro:bit the same way as described in the section on 'NO Pairing'. 138 | - Start the pairing process on the micro:bit (hold buttons A and B down while pressing and releasing the reset button). The micro:bit will display a pattern on the LED matrix. 139 | - Run the Micro:bit Playground and press **Start Scan**. 140 | - Reply to the alert on the iPad. The micro:bit will show a ✅ to indicate it has been paired. 141 | - Reset the micro:bit. 142 | - Once the micro:bit has been paired it will use an identifier without the 5 character extension i.e. the characters between the '[]', so remove the extension (including the square brackets) from the the Microbit initialiser call. 143 | e.g. 144 | ``` 145 | vc.microbit = Microbit("BBC micro:bit") 146 | ``` 147 | - The micro:bit is now paired with the iPad and should connect each time you start a scan. 148 | 149 | ### Passkey Pairing 150 | 151 | Passkey pairing requires a 6 digit random passkey, displayed on the micro:bit, to be entered on the iPad. To use Passkey pairing follow these steps. 152 | 153 | - Select the Passkey option in Project Settings before downloading to the micro:bit. 154 | - Ensure the iPad has ‘forgotten’ the micro:bit - Go to Settings-Bluetooth and check MY DEVICES. If the BBC micro:bit is listed, select the information icon and select “Forget This Device” 155 | - Call the Microbit class initialiser with the name of the micro:bit the same way as described in the section on 'NO Pairing'. 156 | - Start the pairing process on the micro:bit (hold buttons A and B down while pressing and releasing the reset button). The micro:bit will display a pattern on the LED matrix. 157 | - Run the Micro:bit Playground, select the Log option from the menu and press **Start Scan**. 158 | - The micro:bit will show an ⬅︎. Press button A and the micro:bit will scroll a 6 digit passkey that should be entered into the iPad when prompted. 159 | - The micro:bit will show a ✅ to indicate it has been paired 160 | - Reset the micro:bit. 161 | - Once the micro:bit has been paired it will use an identifier without the 5 character extension i.e. the characters between the '[]', so remove the extension (including the square brackets) from the the Microbit initialiser call. 162 | e.g. 163 | ``` 164 | vc.microbit = Microbit("BBC micro:bit") 165 | ``` 166 | - The micro:bit is now paired with the iPad and should connect each time you start a scan. 167 | 168 | # Bluetooth Services 169 | 170 | The Micro:bit Playground demonstrates the various Services available on the micro:bit. 171 | 172 | For further information regarding the Bluetooth Services available to the micro:bit see [The micro:bit GATT profile](https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html) 173 | 174 | ## Accelerometer 175 | 176 | For information on programming the micro:bit to use the accelerometer service see: [accelerometer reference](https://makecode.microbit.org/reference/bluetooth/start-accelerometer-service). 177 | 178 | - Start the Micro:bit Playground and select the **Accelerometer** option. 179 | - Press **Start Scan** to connect to the micro:bit 180 | - Select the frequency accelerometer data is reported e.g. intervals of 640 milliseconds 181 | 182 | The micro:bit reports the acceleration (milli g-force) in three directions: 183 | 184 | - x:acceleration in left and right direction. Holding the micro:bit edge connector towards you, if the value is 0, the micro:bit is horizontal. Tilting to the left gives a positive value, tilting to the right gives a negative value. 185 | - y:acceleration in the forward and backward direction. Holding the micro:bit edge connector towards you, if the value is 0, the micro:bit is horizontal. Tilting towards you gives a positive value, tilting away gives a negative value. 186 | - z:acceleration in up and down direction. If the LED matrix is facing upwards gives a negative number. If the LED matrix is facing down gives a positive number. If the value is 0, the micro:bit is vertical. 187 | 188 | ## Button 189 | 190 | For information on programming the micro:bit to use the button service see: [Button Service](https://makecode.microbit.org/reference/bluetooth/start-button-service). 191 | 192 | - Start the Micro:bit Playground and select the **Buttons** option. 193 | - Press **Start Scan** to connect to the micro:bit 194 | 195 | The indicators change on the Playground depending on which button is pressed and how long it's pressed for. 196 | 197 | ## Event 198 | 199 | Unlike the other micro:bit services, the Event service is enabled by default when you include the Bluetooth package, you do not have to explicitly enable the service . The service extends the micro:bit message bus over Bluetooth ([see the micro:bit runtime](https://lancaster-university.github.io/microbit-docs/concepts/) for further information about the micro:bit message bus). 200 | 201 | The service allows the Playground (or any smart phone or tablet of course) to interact with a micro:bit program and the micro:bit to communicate with the Playground. To demonstrate this, the Playground has been setup to respond to a specific set of user defined Event Service IDs (9010, 9011, 9012, 9013, 9014). You are free to define your own Events IDs for your own projects. The Playground also defines the following Events it can raise on the micro:bit message bus: 202 | 203 | - MES_DEVICE_INFO_ID 204 | - MES_SIGNAL_STRENGTH_ID 205 | - MES_DPAD_CONTROLLER_ID 206 | - MES_BROADCAST_GENERAL_ID 207 | 208 | The following micro:bit program demonstrates the Event service as implemented by the Micro:bit Playground: 209 | 210 | ![makecode-Events](images/makecode-Events.png) 211 | 212 | To demonstrate micro:bit Events: 213 | 214 | - Start the Micro:bit Playground and select the **Events** option. 215 | - Press **Start Scan** to connect to the micro:bit. 216 | 217 | The micro:bit program counts from -60 raising event 9010 on the micro:bit message bus every second. The Bluetooth Event Service will transmit the event and its corresponding value to the Playground. 218 | 219 | If you press button A on the micro:bit, it will raise event 9011 with the current contents of 'value1', if you press button B, event 9011 will be raised with an value of 0. 220 | 221 | The Playground allows you to select one of 4 events the will be transmitted to the micro:bit message bus. If you select MES_DPAD_CONTROLLER_ID and enter a number between 0 and 65535 it will displayed on the micro:bit LED matrix. 222 | 223 | ## LED 224 | 225 | For information on programming the micro:bit to use the LED service see: [LED Service](https://makecode.microbit.org/reference/bluetooth/start-led-service). 226 | 227 | - Start the Micro:bit Playground and select the **LED** option. 228 | - Press **Start Scan** to connect to the micro:bit. 229 | 230 | The matrix on the left-hand side of the Playground depicts the micro:bit LED matrix with each dot representing one of the 25 LEDs. If you press one of the dots the corresponding LED should light. 231 | 232 | You can also enter up to 20 characters of text which will be scrolled across the LED matrix. The speed of the scroll can be set as: 233 | 234 | - Slow (500 millisecond wait between showing each character). 235 | - Normal (100 millisecond wait between showing each character). 236 | - Fast (10 millisecond wait between showing each character). 237 | 238 | ## Magnetometer 239 | 240 | For information on programming the micro:bit to use the Magnetometer service see: [Magnetometer Service](https://makecode.microbit.org/reference/bluetooth/start-magnetometer-service). 241 | 242 | - Start the Micro:bit Playground and select the **Magnetometer** option. 243 | - Press **Start Scan** to connect to the micro:bit. 244 | 245 | When you connect to the micro:bit for the first time after reloading a program, the micro:bit will request that you calibrate the magnetometer see [calibrate compass](https://makecode.microbit.org/reference/input/calibrate-compass). 246 | 247 | The playground reports magnetometer data at specified intervals: 248 | 249 | - X is the magnetic field strength in the direction of magnetic north 250 | - Y is the magnetic field strength in the direction of magnetic east i.e. 90 degrees from magnetic north 251 | - Z is the magnetic field strength vertically down 252 | 253 | The playground also reports the compass bearing in degrees from North. 254 | 255 | ## IO Pin 256 | 257 | For information on programming the micro:bit to use the IO Pin service see: [IO Pin service](https://makecode.microbit.org/reference/bluetooth/start-io-pin-service). 258 | 259 | - Start the Micro:bit Playground and select the **Pin IO** option. 260 | - Press **Start Scan** to connect to the micro:bit. 261 | 262 | The Playground User Interface represents I/O pins 0 to 9 on the edge connector of the micro:bit. Each pin can be switched on (3 volts) by pressing the black dot or off (0 volts) by pressing the red dot. 263 | 264 | Pins 0 - 3 can be switched to Analogue (A) or Digital (D) by pressing the yellow buttons. In analogue mode the pin voltages can be set by moving the corresponding sliders. 265 | 266 | The voltage on the pins can be Written (W) or Read (R) by pressing the grey button. 267 | 268 | ## Temperature 269 | 270 | For information on programming the micro:bit to use the Temperature service see: [Temperature service](https://makecode.microbit.org/reference/bluetooth/start-temperature-service). 271 | 272 | - Start the Micro:bit Playground and select the **Temperature** option. 273 | - Press **Start Scan** to connect to the micro:bit. 274 | 275 | The playground reports the temperature of the micro:bit processor in centigrade. The interval the temperature is reported can be adjusted from 100 milliseconds to 1 minute. An indicator flashes to show a temperature reading has been taken. 276 | ## UART 277 | 278 | For information on using the UART service see: [UART service](https://makecode.microbit.org/reference/bluetooth/start-uart-service). 279 | 280 | The following micro:bit program can be used with the playground to demonstrate the UART service: 281 | 282 | ![makecode_UART](images/makecode_UART.png) 283 | 284 | - Start the Micro:bit Playground and select the **UART** option. 285 | - Press **Start Scan** to connect to the micro:bit. 286 | 287 | Press button A on the micro:bit and see the text ('Hello') sent to the playground. 288 | 289 | Type some text in the UART input field followed by '$' and press return on the iPad. The text will be transmitted to the micro:bit and scrolled across the LED matrix. 290 | 291 | # Beacon 292 | 293 | Beacons are not Bluetooth services, they utilise a bluetooth peripherals ability to advertise services. Beacons advertise small amounts of information, allowing devices like smartphones to detect their proximity to the beacon. There are various beacon message formats, which define the way bluetooth advertising packets are used. The micro:bit uses the 294 | [Eddystone format](https://lancaster-university.github.io/microbit-docs/ble/eddystone/), whereas Apple operating systems use the iBeacon format. 295 | 296 | The full potential of beacon technology is exploited on Apple platforms using the 'Core Location' API which is not supported by the micro:bit, however, it's still possible to demonstrate beacons using the micro:bit playground and Eddystone protocols. 297 | 298 | For programming information using Beacons see : 299 | [Advertise URL](https://makecode.microbit.org/reference/bluetooth/advertise-url) and 300 | 301 | [Advertise UID](https://makecode.microbit.org/reference/bluetooth/advertise-uid). 302 | 303 | The following micro:bit program broadcasts a UID every second. Each time the UID is broadcast it increments the instance element. This is necessary because, by default, the Apple software amalgamates similar messages, so if the UID remains constant, the playground would not show each broadcast message. 304 | 305 | ![makecode-Beacon](images/makecode-Beacon.png) 306 | 307 | To demonstrate the program: 308 | 309 | - Start the Micro:bit Playground and select the **Beacon** option. 310 | - Press **Start Scan** to connect to the micro:bit. 311 | 312 | The playground will show micro:bit advertisement data and the received signal strength indication (RSSI). The RSSI can be used as a crude indication of the proximity to the micro:bit. 313 | 314 | # Appendix A - Microbit API 315 | 316 | The Microbit API is contained in the Microbit.swift file which is simply added to an Xcode project. The file contains: 317 | 318 | - the **MicrobitDelegate** protocol 319 | - the **Microbit** class 320 | 321 | ## MicrobitDelegate Protocol 322 | 323 | Classes implementing the Microbit API should conform to the MicrobitDelegate protocol. Dummy implementations of the protocol functions are included in the Microbit.swift file eliminating the need to implement functions that are not required. 324 | 325 | The following delegate functions are available: 326 | 327 | - logUpdated(_ log:[String]). Called when a log message is added to the microbit log buffer. Parameter: 328 | - **log** the log buffer. 329 | - advertisementData(url:String,namespace:Int64,instance:Int32,RSSI:Int). Called when the bluetooth central manager reads advertisement data from the micro:bit. Parameters: 330 | - **url** the string specified by the micro:bit advertise url function. 331 | - **namespace** the integer specified by the micro:bit advertise UID namespace. 332 | - **instance** the integer specified by the micro:bit advertise UID instance. 333 | - **RSSI** the received signal strength indication variable of the advertisement. 334 | - serviceAvailable(service:ServiceName). Called when a micro:bit characteristic is discovered for a given service. Parameter: 335 | - **service** the name of Service as specified by the ServiceName enum. 336 | - uartReceived(message:String). Called when the UART TX characteristic is updated. Parameter: 337 | - **message** the micro:bit uart write string. 338 | - pinGet(pins:[UInt8:UInt8]). Called when one or more micro:bit pins have changed their value. Parameter: 339 | - **pins** dictionary of pin value pairs. 340 | - buttonPressed(button:String,action:MicrobitButtonType). Called when the state of one of the micro:bit buttons changes. Parameters: 341 | - **button** button 'A' or 'B' 342 | - **action** button state as specified by the MicrobitButtonType enum 343 | - accelerometerData(x:Int16,y:Int16,z:Int16). Called when the accelerometer data characteristic is updated. Parameters: 344 | - **x** acceleration in the 'x' direction. 345 | - **y** acceleration in the 'y' direction. 346 | - **z** acceleration in the 'z' direction. 347 | - magnetometerData(x:Int16,y:Int16,z:Int16). Called when the magnetometer data characteristic is updated. Parameters: 348 | - **x** magnetic field strength in the direction of magnetic north. 349 | - **y** magnetic field strength in the direction of magnetic east. 350 | - **z** magnetic field strength vertically down. 351 | - compass(bearing:Int16). Called when the magnetometer compass bearing characteristic is updated. Parameter: 352 | - **bearing** compass bearing from north in degrees. 353 | - microbitEvent(type:Int16,value:Int16). Called when a registered event is raised on the micro:bit. Parameter: 354 | - **type** event ID 355 | - **value** integer value associated with the event. 356 | - temperature(value:Int16). Called when the temperature is reported. Parameter: 357 | - **value** temperature in centigrade. 358 | 359 | ## Microbit Class 360 | The Microbit class forms the entire API. It should be initialised only once. 361 | 362 | ### Initialiser 363 | 364 | init(_ deviceName:String) 365 | 366 | Parameters: 367 | 368 | - **deviceName** Name of the micro:bit being searched for, or, the name of the micro:bit that should be connected. 369 | 370 | example: 371 | ``` 372 | let microbit = Microbit("BBC micro:bit [tizip]") 373 | ``` 374 | ### startScanning 375 | ``` 376 | func startScanning() 377 | ``` 378 | ### stopScanning 379 | ``` 380 | func stopScanning() 381 | ``` 382 | ### disconnect 383 | ``` 384 | func disconnect() 385 | ``` 386 | ### ledText 387 | ``` 388 | func ledText(message:String,scrollRate:Int16) 389 | ``` 390 | Parameters: 391 | 392 | - **message**: string to be scrolled across the micro:bit LED matrix 393 | - **scrollRate**: wait in milliseconds between showing each character 394 | ### ledWrite 395 | ``` 396 | func ledWrite(matrix:[UInt8]) 397 | ``` 398 | Parameter: 399 | 400 | - **matrix**: array of 5 bytes where the first 5 least significant bits represent a row of LEDs 401 | ### uartSend 402 | ``` 403 | func uartSend(message:String) 404 | ``` 405 | Parameter: 406 | 407 | - **message**: string to be sent to the micro:bit 408 | ### pinConfigure 409 | ``` 410 | func pinConfigure(analougePins:[UInt8:Bool]) 411 | ``` 412 | Parameter: 413 | 414 | - **analoguePins**:dictionary where keys are pin numbers and values are true if pin is analogue or false if pin is digital. Only pins 0, 1, 2, 3, 4 and 10 can be configured for analogue read or write. If other pins are configured they will be ignored. 415 | ``` 416 | func pinConfigure(readPins:[UInt8:Bool]) 417 | ``` 418 | Parameter: 419 | 420 | - **readPins**:dictionary where keys are pin numbers and values are true if pin is read or false if pin is write. Pins 0 - 18 are valid. 421 | ### pinSet 422 | ``` 423 | func pinSet(pinValues:[UInt8:UInt8]) 424 | ``` 425 | Parameter: 426 | 427 | - **pinValues**:dictionary where keys are pin numbers and values represent a variable voltage if pin is configured as analogue, or, 0: (set pin LOW) 1: (set pin HIGH) if pin is configured as digital. 428 | ### accelerometer 429 | ``` 430 | func accelerometer(period:PeriodType) 431 | ``` 432 | Parameter: 433 | 434 | - **period**: interval accelerometer data is reported. Valid PeriodType values are 1,2,5,10,20,80,160,640 milliseconds. 435 | ### magnetometer 436 | ``` 437 | func magnetometer(period:PeriodType) 438 | ``` 439 | Parameter: 440 | 441 | - **period**: interval magnetometer data is reported. Valid PeriodType values are 1,2,5,10,20,80,160,640 milliseconds. 442 | ### temperature 443 | ``` 444 | func temperature(period:UInt16) 445 | ``` 446 | Parameter: 447 | 448 | - **period**: interval temperature is reported in milliseconds. 449 | ### raiseEvent 450 | ``` 451 | func raiseEvent(event:MicrobitEvent,value:UInt16 452 | ``` 453 | Parameters: 454 | 455 | - **event**: an event that can be handled by the micro:bit. 456 | - **value**: a value associated with the event. 457 | ### registerEvents 458 | ``` 459 | func registerEvents(events:[Int16]) 460 | ``` 461 | Parameter: 462 | 463 | - **events**: an array of integers representing the events raised on the micro:bit that will be notified to the connected device. Note: event 9010 is automatically registered when the client requirements characteristic is discovered. This is done to force a pairing request. 464 | 465 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Micro:bit Swift Bluetooth LE 2 | --------------------------------------------------------------------------------