import * as crypto from "crypto" ; import * as fs from "fs-plus" ; import * as getmac from "getmac" ; import { Guid } from "guid-typescript" ; import * as _ from "lodash" ; import * as opn from "opn" ; import * as os from "os" ; import * as path from "path" ; import * as vscode from "vscode" ; import * as WinReg from "winreg" ; import { BoardProvider } from "../boardProvider" ; import { ArduinoCommands } from "../common/Commands" ; import { TypeNotSupportedError } from "../common/Error/SystemErrors/TypeNotSupportedError" ; import { ResourceNotFoundError } from "../common/Error/OperationFailedErrors/ResourceNotFoundError" ; import { OperationCanceledError } from "../common/Error/OperationCanceledError" ; import { OperationFailedError } from "../common/Error/OperationFailedErrors/OperationFailedError" ; import { FileNames , OSPlatform , ScaffoldType } from "../constants" ; import { DialogResponses } from "../DialogResponses" ; import { FileUtility } from "../FileUtility" ; import { TelemetryContext } from "../telemetry" ; import { delay , getEnumKeyByEnumValue , getRegistryValues } from "../utils" ; import { Board } from "./Interfaces/Board" ; import { reject } from "bluebird" ; import { ArduinoDeviceBase } from "./ArduinoDeviceBase" ; import { DeviceType } from "./Interfaces/Device" ; import { DeviceConfig , TemplateFileInfo } from "./Interfaces/ProjectTemplate" ; import { FileNotFoundError } from "../common/Error/OperationFailedErrors/FileNotFound" ; import { DirectoryNotFoundError } from "../common/Error/OperationFailedErrors/DirectoryNotFoundError" ; import { SystemResourceNotFoundError } from "../common/Error/SystemErrors/SystemResourceNotFoundError" ; import { AzureConfigFileHandler } from "./AzureComponentConfig" ; import { ComponentType } from "./Interfaces/Component" ; const importLazy = require ( "import-lazy" ) ; const forEach = importLazy ( ( ) => require ( "lodash.foreach" ) ) ( ) as typeof import ( "lodash.foreach" ) ; const trimStart = importLazy ( ( ) => require ( "lodash.trimstart" ) ) ( ) ; interface SerialPortInfo { path : string ; manufacturer : string ; vendorId : string ; productId : string ; } const constants = { outputPath : "./.build" , platformLocalFileName : "platform.local.txt" , cExtraFlag : 'compiler.c.extra_flags=-DCORRELATIONID="' , cppExtraFlag : 'compiler.cpp.extra_flags=-DCORRELATIONID="' , traceExtraFlag : " -DENABLETRACE=" , informationPageUrl : "https://aka.ms/AA35xln" } ; enum ConfigDeviceOptions { ConnectionString = "Config Device Connection String" , UDS = "Config DPS Unique Device Secret (UDS)" , DPS = "Config DPS credentials" , CRC = "Generate CRC for OTA" } enum DeviceConnectionStringAcquisitionMethods { Input = "Input IoT Hub Device Connection String" , Select = "Select IoT Hub Device Connection String" } export class AZ3166Device extends ArduinoDeviceBase { static get serialport ( ) : any { if ( ! AZ3166Device . _serialport ) { AZ3166Device . _serialport = require ( "node-usb-native" ) . SerialPort ; } return AZ3166Device . _serialport ; } private static _serialport : any ; private componentId : string ; get id ( ) : string { return this . componentId ; } private static _boardId = "devkit" ; private azureConfigFileHandler : AzureConfigFileHandler ; static get boardId ( ) : string { return AZ3166Device . _boardId ; } constructor ( context , channel , telemetryContext , devicePath , templateFiles ? ) { super ( context , devicePath , channel , telemetryContext , DeviceType . MXChipAZ3166 ) ; this . channel = channel ; this . componentId = Guid . create ( ) . toString ( ) ; if ( templateFiles ) { this . templateFiles = templateFiles ; } const projectFolder = devicePath + "/.." ; this . azureConfigFileHandler = new AzureConfigFileHandler ( projectFolder ) ; } name = "AZ3166" ; get board ( ) : Board { const boardProvider = new BoardProvider ( this . boardFolderPath ) ; const az3166 = boardProvider . find ( { id : AZ3166Device . _boardId } ) ; if ( ! az3166 ) { throw new SystemResourceNotFoundError ( "AZ3166 board" , `board id ${ AZ3166Device . _boardId }` , "board list" ) ; } return az3166 ; } get version ( ) : string { const packageRootPath = this . getArduinoPackagePath ( ) ; let version = "0.0.1" ; if ( fs . existsSync ( packageRootPath ) ) { const versions = fs . readdirSync ( packageRootPath ) ; if ( versions [ 0 ] ) { version = versions [ 0 ] ; } } return version ; } async create ( ) { this . createCore ( ) ; } async preCompileAction ( ) { await this . generatePlatformLocal ( ) ; return true ; } async preUploadAction ( ) { const isStlinkInstalled = await this . stlinkDriverInstalled ( ) ; if ( ! isStlinkInstalled ) { const message = "The ST-LINK driver for DevKit is not installed. Install now?" ; const result = await vscode . window . showWarningMessage ( message , DialogResponses . yes , DialogResponses . skipForNow , DialogResponses . cancel ) ; if ( result === DialogResponses . yes ) { const installUri = "http://www.st.com/en/development-tools/stsw-link009.html" ; opn ( installUri ) ; return true ; } else if ( result !== DialogResponses . cancel ) { return false ; } } await this . generatePlatformLocal ( ) ; return true ; } async configDeviceSettings ( ) { const deviceSettingType = await this . selectDeviceSettingType ( ) ; let credentials = "" ; switch ( deviceSettingType ) { case ConfigDeviceOptions . CRC : await this . generateCrc ( this . channel ) ; return ; case ConfigDeviceOptions . ConnectionString : credentials = await this . getDeviceConnectionString ( ) ; await this . logAndSetCredentials ( credentials , ConfigDeviceOptions . ConnectionString ) ; return ; case ConfigDeviceOptions . DPS : credentials = await this . getDPSCredentialsFromInput ( ) ; await this . logAndSetCredentials ( credentials , ConfigDeviceOptions . DPS ) ; return ; case ConfigDeviceOptions . UDS : credentials = await this . getUDSStringFromInput ( ) ; await this . logAndSetCredentials ( credentials , ConfigDeviceOptions . UDS ) ; return ; default : throw new TypeNotSupportedError ( "device setting type" , `${ deviceSettingType }` ) ; } } param credentials param deviceSettingType param deviceSettingsType private async logAndSetCredentials ( credentials , deviceSettingType ) { console . log ( credentials ) ; const res = await this . setDeviceConfig ( credentials , deviceSettingType ) ; if ( ! res ) { throw new OperationFailedError ( "flush configuration to device" , "" , "Please check out error message in the output channel." ) ; } let deviceSettingTypeForLog ; switch ( deviceSettingType ) { case ConfigDeviceOptions . ConnectionString : deviceSettingTypeForLog = "device connection string" ; break ; case ConfigDeviceOptions . DPS : deviceSettingTypeForLog = "DPS credentials" ; break ; case ConfigDeviceOptions . UDS : deviceSettingTypeForLog = "Unique Device String (UDS)" ; break ; default : throw new TypeNotSupportedError ( "device setting type" , `${ deviceSettingType }` ) ; } vscode . window . showInformationMessage ( `Successfully configure ${ deviceSettingTypeForLog }.` ) ; } /**
     * Select device setting type.
     */ private async selectDeviceSettingType ( ) { const configSelectionItems = await this . getConfigDeviceSettingTypeOptions ( ) ; const configSelection = await vscode . window . showQuickPick ( configSelectionItems , { ignoreFocusOut : true , matchOnDescription : true , matchOnDetail : true , placeHolder : "Select an option" } ) ; if ( ! configSelection ) { throw new OperationCanceledError ( "Config device settings option selection cancelled." ) ; } const deviceSettingsType = getEnumKeyByEnumValue ( ConfigDeviceOptions , configSelection . label ) ; return deviceSettingsType ; } /**
     * Get config device setting type options.
     */ private async getConfigDeviceSettingTypeOptions ( ) { const devciceConfigFilePath = this . extensionContext . asAbsolutePath ( path . join ( FileNames . resourcesFolderName , FileNames . templatesFolderName , FileNames . configDeviceOptionsFileName ) ) ; const configSelectionItemsFilePath = await FileUtility . readFile ( ScaffoldType . Local , devciceConfigFilePath , "utf8" ) ; const configSelectionItemsContent = JSON . parse ( configSelectionItemsFilePath as string ) ; const configSelectionItems = [ ] ; configSelectionItemsContent . configSelectionItems . forEach ( ( element ) => { configSelectionItems . push ( { label : element . label , description : "" , detail : element . detail } ) ; } ) ; return configSelectionItems ; } /**
     * Get device connection string.
     * Either get from workspace config or input one.
     */ private async getDeviceConnectionString ( ) { let deviceConnectionStringFromConfig ; const componentConfig = await this . azureConfigFileHandler . getComponentByType ( ScaffoldType . Workspace , ComponentType . IoTHubDevice ) ; if ( componentConfig ) { deviceConnectionStringFromConfig = componentConfig . componentInfo ?. values . iotHubDeviceConnectionString ; } let deviceConnectionString ; if ( deviceConnectionStringFromConfig ) { const deviceConnectionStringAcquisitionMethodSelection = await this . selectDeviceConnectionStringAcquisitionMethod ( deviceConnectionStringFromConfig ) ; if ( deviceConnectionStringAcquisitionMethodSelection . label === DeviceConnectionStringAcquisitionMethods . Select ) { deviceConnectionString = deviceConnectionStringFromConfig ; return deviceConnectionString ; } } deviceConnectionString = await this . getInputDeviceConnectionString ( ) ; return deviceConnectionString ; } /**
     * Select method to get device connection string: input a new one or get from
     * configuration.
     */ private async selectDeviceConnectionStringAcquisitionMethod ( deviceConnectionString ) { const deviceConnectionStringAcquisitionOptions = this . getDeviceConnectionStringAcquisitionOptions ( deviceConnectionString ) ; const deviceConnectionStringAcquisitionSelection = await vscode . window . showQuickPick ( deviceConnectionStringAcquisitionOptions , { ignoreFocusOut : true , placeHolder : "Choose an option:" } ) ; if ( ! deviceConnectionStringAcquisitionSelection ) { throw new OperationCanceledError ( "Device connection string acquisition method selection cancelled." ) ; } return deviceConnectionStringAcquisitionSelection ; } private getDeviceConnectionStringAcquisitionOptions ( deviceConnectionString ) { let deviceConnectionStringAcquisitionOptions = [ ] ; const inputDeviceConnectionStringOption = { label : DeviceConnectionStringAcquisitionMethods . Input , description : "" , detail : "" } ; let hostName = "" ; let deviceId = "" ; const hostnameMatches = deviceConnectionString . match ( /HostName=(.*?)(;|$)/ ) ; if ( hostnameMatches ) { hostName = hostnameMatches [ 0 ] ; } const deviceIDMatches = deviceConnectionString . match ( /DeviceId=(.*?)(;|$)/ ) ; if ( deviceIDMatches ) { deviceId = deviceIDMatches [ 0 ] ; } if ( deviceId && hostName ) { deviceConnectionStringAcquisitionOptions = [ { label : DeviceConnectionStringAcquisitionMethods . Select , description : "" , detail : `Device Information: ${ hostName } ${ deviceId }` } , inputDeviceConnectionStringOption ] ; } else { deviceConnectionStringAcquisitionOptions = [ inputDeviceConnectionStringOption ] ; } return deviceConnectionStringAcquisitionOptions ; } private async getInputDeviceConnectionString ( ) { const option = this . getInputDeviceConnectionStringOptions ( ) ; const deviceConnectionString = await vscode . window . showInputBox ( option ) ; if ( ! deviceConnectionString ) { const message = "Need more information on how to get device connection string?" ; const result = await vscode . window . showWarningMessage ( message , DialogResponses . yes , DialogResponses . no ) ; if ( result === DialogResponses . yes ) { opn ( constants . informationPageUrl ) ; } throw new OperationCanceledError ( "Fail to get input device connection string." ) ; } return deviceConnectionString ; } private getInputDeviceConnectionStringOptions ( ) { const option = { value : "HostName=<Host Name>;DeviceId=<Device Name>;SharedAccessKey=<Device Key>" , prompt : `Please input device connection string here.` , ignoreFocusOut : true , validateInput : ( deviceConnectionString ) => { if ( ! deviceConnectionString ) { return "Please provide a valid device connection string." ; } if ( deviceConnectionString . indexOf ( "HostName" ) === - 1 || deviceConnectionString . indexOf ( "DeviceId" ) === - 1 || deviceConnectionString . indexOf ( "SharedAccessKey" ) === - 1 ) { return "The format of the IoT Hub Device connection string is invalid." ; } return ; } } ; return option ; } /**
     * Get DPS credentials from input box.
     */ private async getDPSCredentialsFromInput ( ) { const option = this . getDPSConnectionStringOptions ( ) ; const dpsCredential = await vscode . window . showInputBox ( option ) ; if ( ! dpsCredential ) { throw new OperationCanceledError ( "DPS credentials input cancelled." ) ; } return dpsCredential ; } private getDPSConnectionStringOptions ( ) { const option = { value : "DPSEndpoint=global.azure-devices-provisioning.net;IdScope=<Id Scope>;DeviceId=<Device Id>;SymmetricKey=<Symmetric Key>" , prompt : `Please input DPS credentials here.` , ignoreFocusOut : true , validateInput : ( deviceConnectionString ) => { if ( ! deviceConnectionString ) { return "Please provide a valid DPS credentials." ; } if ( deviceConnectionString . indexOf ( "DPSEndpoint" ) === - 1 || deviceConnectionString . indexOf ( "IdScope" ) === - 1 || deviceConnectionString . indexOf ( "DeviceId" ) === - 1 || deviceConnectionString . indexOf ( "SymmetricKey" ) === - 1 ) { return "The format of the DPS credentials is invalid." ; } return ; } } ; return option ; } /**
     * Get UDS string from input box.
     */ private async getUDSStringFromInput ( ) { const option = this . getUDSStringOptions ( ) ; const UDS = await vscode . window . showInputBox ( option ) ; if ( ! UDS ) { throw new OperationCanceledError ( "UDS string input cancelled." ) ; } return UDS ; } private getUDSStringOptions ( ) { function generateRandomHex ( ) { const chars = "0123456789abcdef" . split ( "" ) ; let hexNum = "" ; for ( let i = 0 ; i < 64 ; i ++ ) { hexNum += chars [ Math . floor ( Math . random ( ) * 16 ) ] ; } return hexNum ; } const option = { value : generateRandomHex ( ) , prompt : `Please input Unique Device String (UDS) here.` , ignoreFocusOut : true , validateInput : ( UDS ) => { if ( /^([0-9a-f]){64}$/i . test ( UDS ) === false ) { return "The format of the UDS is invalid. Please provide a valid UDS." ; } return "" ; } } ; return option ; } param configValue param option private async setDeviceConfig ( configValue , option ) { try { await vscode . commands . executeCommand ( ArduinoCommands . CloseSerialMonitor , null , false ) ; } catch ( ignore ) { } const platform = os . platform ( ) ; if ( platform === OSPlatform . WIN32 ) { return await this . flushDeviceConfig ( configValue , option ) ; } else { return await this . flushDeviceConfigUnixAndMac ( configValue , option ) ; } } private flushDeviceConfigUnixAndMac ( configValue , option ) { return new Promise ( async ( resolve , reject ) => { let comPort = "" ; let command = "" ; try { comPort = await this . chooseCOM ( ) ; console . log ( `Opening ${ comPort }.` ) ; } catch ( error ) { return reject ( error ) ; } if ( option === ConfigDeviceOptions . ConnectionString ) { command = "set_az_iothub" ; } else if ( option === ConfigDeviceOptions . DPS ) { command = "set_az_iotdps" ; } else { command = "set_dps_uds" ; } let errorRejected = false ; let az3166 ; try { az3166 = this . board ; } catch ( error ) { return reject ( error ) ; } const port = new AZ3166Device . serialport ( comPort , { baudRate : az3166 . defaultBaudRate , dataBits : 8 , stopBits : 1 , xon : false , xoff : false , parity : "none" } ) ; const rejectIfError = ( err ) : boolean => { if ( errorRejected ) return true ; if ( err ) { errorRejected = true ; reject ( err ) ; try { port . close ( ) ; } catch ( ignore ) { } } return true ; } ; const executeSetAzIoTHub = async ( ) : Promise < void > => { try { const data = `${ command } "${ configValue }"\r\n` ; let restDataLength = data . length ; while ( restDataLength > 0 ) { const start = data . length - restDataLength ; const length = Math . min ( 100 , restDataLength ) ; restDataLength -= length ; const dataChunk = data . substr ( start , length ) ; await this . sendDataViaSerialPort ( port , dataChunk ) ; await delay ( 1000 ) ; } port . close ( ) ; } catch ( ignore ) { } if ( errorRejected ) { return ; } else { resolve ( true ) ; } } ; port . on ( "open" , async ( ) => { await vscode . window . showInformationMessage ( "Please hold down button A and then push and release the reset button to enter configuration mode. After enter configuration mode, click OK." , "OK" ) ; executeSetAzIoTHub ( ) . then ( ( ) => resolve ( true ) ) . catch ( error => reject ( error ) ) ; } ) ; port . on ( "error" , ( error ) => { if ( errorRejected ) return ; console . log ( error ) ; rejectIfError ( error ) ; } ) ; } ) ; } private flushDeviceConfig ( configValue , option ) { return new Promise ( async ( resolve , reject ) => { let comPort = "" ; let command = "" ; try { comPort = await this . chooseCOM ( ) ; console . log ( `Opening ${ comPort }.` ) ; } catch ( error ) { reject ( error ) ; } if ( option === ConfigDeviceOptions . ConnectionString ) { command = "set_az_iothub" ; } else if ( option === ConfigDeviceOptions . DPS ) { command = "set_az_iotdps" ; } else { command = "set_dps_uds" ; } let configMode = false ; let errorRejected = false ; let commandExecuted = false ; let gotData = false ; let az3166 ; try { az3166 = this . board ; } catch ( error ) { return reject ( error ) ; } const port = new AZ3166Device . serialport ( comPort , { baudRate : az3166 . defaultBaudRate , dataBits : 8 , stopBits : 1 , xon : false , xoff : false , parity : "none" } ) ; const rejectIfError = ( err ) : boolean => { if ( errorRejected ) return true ; if ( err ) { errorRejected = true ; reject ( err ) ; try { port . close ( ) ; } catch ( ignore ) { } } return true ; } ; const executeSetAzIoTHub = async ( ) : Promise < void > => { try { const data = `${ command } "${ configValue }"\r\n` ; const maxDataLength = 256 ; await this . sendDataViaSerialPort ( port , data . slice ( 0 , maxDataLength ) ) ; if ( data . length > maxDataLength ) { await delay ( 1000 ) ; await this . sendDataViaSerialPort ( port , data . slice ( maxDataLength ) ) ; } await delay ( 1000 ) ; port . close ( ) ; } catch ( ignore ) { } if ( errorRejected ) { return ; } else { resolve ( true ) ; } } ; port . on ( "open" , ( ) => { port . write ( "\r\nhelp\r\n" , ( error ) => { if ( rejectIfError ( error ) ) return ; } ) ; } ) ; port . on ( "data" , ( data ) => { gotData = true ; const output = data . toString ( ) . trim ( ) ; if ( commandExecuted ) return ; if ( output . includes ( "set_" ) ) { commandExecuted = true ; configMode = true ; executeSetAzIoTHub ( ) . then ( ( ) => resolve ( true ) ) . catch ( error => reject ( error ) ) ; } else { configMode = false ; } if ( configMode ) { forEach ( output . split ( "\n" ) , line => { if ( line ) { line = trimStart ( line . trim ( ) , "#" ) . trim ( ) ; if ( line && line . length ) { console . log ( "SerialOutput" , line ) ; } } } ) ; } } ) ; port . on ( "error" , ( error ) => { if ( errorRejected ) return ; console . log ( error ) ; rejectIfError ( error ) ; } ) ; setTimeout ( ( ) => { if ( errorRejected ) return ; if ( ! gotData || ! configMode ) { vscode . window . showInformationMessage ( "Please hold down button A and then push and release the reset button to enter configuration mode." ) . then ( ( ) => { port . write ( "\r\nhelp\r\n" , ( error ) => { rejectIfError ( error ) ; } ) ; } ) ; } } , 10000 ) ; } ) ; } private async getComList ( ) { const lists = await AZ3166Device . serialport . list ( ) ; return lists ; } private async chooseCOM ( ) { return new Promise ( async ( resolve , reject ) => { const comList = await this . getComList ( ) ; let az3166 ; try { az3166 = this . board ; } catch ( error ) { return reject ( error ) ; } const list = _ . filter ( comList , com => { if ( com . vendorId && com . productId && az3166 . vendorId && az3166 . productId && com . vendorId . toLowerCase ( ) . endsWith ( az3166 . vendorId ) && com . productId . toLowerCase ( ) . endsWith ( az3166 . productId ) ) { return true ; } else { return false ; } } ) ; if ( list && list . length ) { let comPort = list [ 0 ] . path ; if ( list . length > 1 ) { comPort = list [ 0 ] . path ; } if ( ! comPort ) { reject ( new ResourceNotFoundError ( "choose COM" , "avaliable COM port." , "Please check COM port settings." ) ) ; } resolve ( comPort ) ; } else { reject ( new Error ( "No AZ3166 board connected." ) ) ; } } ) ; } private async sendDataViaSerialPort ( port , data ) { return new Promise ( ( resolve , reject ) => { try { port . write ( data , ( err ) => { if ( err ) { reject ( err ) ; } else { port . drain ( ( ) => resolve ( true ) ) ; } } ) ; } catch ( err ) { reject ( err ) ; } } ) ; } private async stlinkDriverInstalled ( ) { const platform = os . platform ( ) ; if ( platform === OSPlatform . WIN32 ) { try { const pathString = await getRegistryValues ( WinReg . HKLM , "\\SYSTEM\\ControlSet001\\Control\\Class\\{88bae032-5a81-49f0-bc3d-a4ff138216d6}" , "Class" ) ; if ( pathString ) { return true ; } else { return false ; } } catch ( error ) { return false ; } } return true ; } private async generatePlatformLocal ( ) { const arduinoPackagePath = this . getArduinoPackagePath ( ) ; function getHashMacAsync ( ) { return new Promise ( resolve => { getmac . getMac ( ( err , macAddress ) => { if ( err ) { reject ( err ) ; } const hashMacAddress = crypto . createHash ( "sha256" ) . update ( macAddress , "utf8" ) . digest ( "hex" ) ; resolve ( hashMacAddress ) ; } ) ; } ) ; } if ( ! fs . existsSync ( arduinoPackagePath ) ) { const suggestedOperation = 'Please install it from https://www.arduino.cc/en/main/software and use "Arduino: Board Manager" to install your device packages. Restart VS Code to apply to changes.' ; throw new ResourceNotFoundError ( `generate ${ constants . platformLocalFileName } file` , "Arduino IDE" , suggestedOperation ) ; } const files = fs . readdirSync ( arduinoPackagePath ) ; for ( let i = files . length - 1 ; i >= 0 ; i -- ) { if ( files [ i ] === ".DS_Store" ) { files . splice ( i , 1 ) ; } } if ( files . length === 0 || files . length > 1 ) { throw new FileNotFoundError ( `generate ${ constants . platformLocalFileName } file` , `files under Arduino package installation path ${ arduinoPackagePath }` , "Please clear the path and reinstall the package for Devkit." ) ; } const directoryName = path . join ( arduinoPackagePath , files [ 0 ] ) ; if ( ! fs . isDirectorySync ( directoryName ) ) { throw new DirectoryNotFoundError ( `generate ${ constants . platformLocalFileName } file` , "Arduino package for MXChip IoT Devkit" , "Please follow guide to install the DevKit package." ) ; } const fileName = path . join ( directoryName , constants . platformLocalFileName ) ; if ( ! fs . existsSync ( fileName ) ) { const enableTrace = 1 ; const hashMacAddress = await getHashMacAsync ( ) ; const targetFileName = path . join ( directoryName , constants . platformLocalFileName ) ; const content = `${ constants . cExtraFlag }${ hashMacAddress }" ${ constants . traceExtraFlag }${ enableTrace }\r\n` + `${ constants . cppExtraFlag }${ hashMacAddress }" ${ constants . traceExtraFlag }${ enableTrace }\r\n` ; fs . writeFileSync ( targetFileName , content ) ; } } private getArduinoPackagePath ( ) { const platform = os . platform ( ) ; let arduinoPackagePath = "" ; const homeDir = os . homedir ( ) ; if ( platform === OSPlatform . WIN32 ) { arduinoPackagePath = path . join ( homeDir , "AppData" , "Local" , "Arduino15" , "packages" ) ; } else if ( platform === OSPlatform . DARWIN ) { arduinoPackagePath = path . join ( homeDir , "Library" , "Arduino15" , "packages" ) ; } else if ( platform === OSPlatform . LINUX ) { arduinoPackagePath = path . join ( homeDir , ".arduino15" , "packages" ) ; } return path . join ( arduinoPackagePath , "AZ3166" , "hardware" , "stm32f4" ) ; } }