Android Things - Solid Foundations

Preview:

Citation preview

solid foundationsPaul BlundellXavi Rigau

CamerasGatewaysHVAC ControlSmart Meters

Point of SaleInventory ControlInteractive AdsVending Machines

Security SystemsSmart DoorbellsRoutersEnergy Monitors

Asset TrackingFleet ManagementDriver AssistPredictive Service

Ideal for powerful, intelligent devices on the edge that need to be secure.

IoT Developer Console

Automatic Security Updates

Signed Images Verified Boot

SoMArchitecture

Google Managed BSP

Displays are Optional

Consider Alternate UI

Supported protocols

Peripheral I/O

Communicate your Android Things device with external hardware components (usually called peripherals)

Various protocols can be used (depends on hardware)

General-Purpose Input/Output

GPIOPulse Width Modulation

PWM

Inter-Integrated Circuit

I2C

Universal Asynchronous

Receiver-Transmitter

UART

Serial Peripheral Interface

SPI

Peripheral I/O

Communicate your Android Things device with external hardware components (usually called peripherals)

Various protocols can be used (depends on hardware)

General-Purpose Input/Output

GPIOPulse Width Modulation

PWM

Inter-Integrated Circuit

I2C

Universal Asynchronous

Receiver-Transmitter

UART

Serial Peripheral Interface

SPI

Native Peripheral Input/Output

NPIO

General-Purpose Input/Output

GPIO

Pulse Width ModulationPWM

Xavi Rigau
TODO: These protocol diagrams, can they be replaced with anything better?

Serial Peripheral Interface

SPI

Inter-Integrated Circuit

I2C

Universal Asynchronous

Receiver-Transmitter

UART

Code example: use SPI for an LED strip

public class MainActivity extends Activity {

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); }

}

public class MainActivity extends Activity {

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

PeripheralManagerService service = new PeripheralManagerService(); }

}

public class MainActivity extends Activity {

private static final String SPI_PORT = "SPI1"; private SpiDevice device;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

PeripheralManagerService service = new PeripheralManagerService();

try { device = service.openSpiDevice(SPI_PORT); } catch (IOException e) { throw new IllegalStateException(SPI_PORT + " bus cannot be opened.", e); } }

}

public class MainActivity extends Activity { ... @Override protected void onDestroy() { super.onDestroy(); try { device.close(); } catch (IOException e) { Log.e("TUT", SPI_PORT + " bus cannot be closed, you may experience errors on next launch.", e); } }

}

public class MainActivity extends Activity { ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

...

try { device.setMode(SpiDevice.MODE0); device.setFrequency(1_000_000); device.setBitsPerWord(8); } catch (IOException e) { throw new IllegalStateException(SPI_PORT + " bus cannot be configured.", e); } }

}

public class MainActivity extends Activity { ... private Handler ledToggleHandler;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

...

ledToggleHandler = new Handler(Looper.getMainLooper()); }

}

public class MainActivity extends Activity { ... private Handler ledToggleHandler;

@Override protected void onStart() { super.onStart(); ledToggleHandler.post(toggleLed); }

@Override protected void onStop() { ledToggleHandler.removeCallbacks(toggleLed); super.onStop(); }

}

public class MainActivity extends Activity { ... private int colourValue; ... private final Runnable toggleLed = new Runnable() { @Override public void run() { try { colourValue = (colourValue == 0 ? 123 : 0); device.write(new byte[]{colourValue, colourValue, colourValue}, 3); } catch (IOException e) { throw new IllegalStateException(LED_PIN + " cannot be read/written.", e); } ledToggleHandler.postDelayed(this, TimeUnit.SECONDS.toMillis(1)); } };

}

Drivers

Driver Libraries Input Drivers

User Drivers

Read or write

from/to a peripheral

Reusable components

Abstract protocol details

Driver Libraries

Ws2801 ledstrip = Ws2801.create("SPI1", Ws2801.Mode.RGB);

ledstrip.write(new int[]{Color.rgb(123, 123, 123)});

Input Drivers

Framework redirects events to

theAndroid

event queue

Register an input driver

Use Services instead of Activities

BarActivityFooService

InputDriver inputDriver = InputDriver.builder(InputDevice.SOURCE_CLASS_BUTTON)

.setName("HelloInputDriver")

.setVersion(1)

.setKeys(new int[]{

KeyEvent.KEYCODE_T, KeyEvent.KEYCODE_H, KeyEvent.KEYCODE_I,

KeyEvent.KEYCODE_N, KeyEvent.KEYCODE_G, KeyEvent.KEYCODE_S

}).build();

UserDriverManager driverManager = UserDriverManager.getManager();

driverManager.registerInputDriver(inputDriver);

KeyEvent[] events = new KeyEvent[]{new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_T)};

inputDriver.emit(events);

Peripherals naming convention

Active peripherals

Passive peripherals

Output peripherals

Input peripherals

Input peripherals / Sensors

“A sensor detects or measures a physical property and responds to it.”

Output peripherals / Actuators

“An actuator is responsible for moving or controlling

a mechanism or system, or presenting data to the outside world.”

/*** HC-SR501*/class PirMotionSensor implements MotionSensor { …

/*** PS-1240*/class PiezoSoundActuator implements SoundActuator { ...

Clarity when searching classes

Clarity when reading code

Better team

discussions

Package structure convention

Grouping by

type

- activities- MainActivity- BasketActivity- CheckoutActivity

- services- LoginService- MoneyService

- utils- LoginUtils- HttpUtils

- peripherals- BarcodeScanningSensor- PriceInputSensor- HelpAlertSensor- FingerprintSensor- PriceDisplayActuator- MoneyDrawActuator

- models- Money- Username- Basket- Receipt- Barcode- Price

Grouping by

protocol

- activities- MainActivity- BasketActivity- CheckoutActivity

- services- LoginService- MoneyService

- utils- LoginUtils- HttpUtils

- gpio- BarcodeScanningSensor- PriceInputSensor- HelpAlertSensor

- i2c- FingerprintSensor

- pwm- PriceDisplayActuator- MoneyDrawActuator

- models- Money- Username- Basket- Receipt- Barcode- Price

Increased package

tackle index

Tidy, everything in its place and a place

for everything

Don’t always

remember the protocol

Context switching between packages

Grouping by domain

concepts

- login- MainActivity- LoginUtils- FingerprintSensor- LoginService- Username

- shopping- basket

- BasketActivity- PriceInputSensor- Basket

- checkout- CheckoutActivity- MoneyService- BarcodeScanningSensor- HelpAlertSensor- MoneyDrawActuator- Barcode

- receipt- PriceDisplayActuator- Receipt

- Money- Price- HttpUtils

Needs to be taught (DDD)

Easier to remember where to

look

All code in the same

place

Less chance of

conflicting changes

Peripherals MVP convention

Model

business logic

Presenter

co-ordination

View

user interface

Sensor

“A sensor detects or measures a physical property and responds to

it.”Model

business logic

Actuator

“An actuator is responsible for moving or controlling a mechanism or system,

or presenting data to the outside world.”

View

user interface

Faster debugging

and problem analysis

Clean code, more

maintainable

A clear separation of

concerns

Faster development

through agreed

standards

Clarity in discussions around class responsibiliti

es

Driver testing

Mock framework

classesTest driver behaviour

Extract collaborator

classes

Test driver behaviour

public class LightStripActuatorTest { @Mock Ws2801 driver;

private LightStripActuator actuator;

@Test

public void driverGoesRedWhenError() throws Exception { actuator.showError();

verify(driver).write({Color.RED});

}

}

Rely on mockspublic class Ws2801Test { @Mock SpiDevice device;

private Ws2801 driver; @Test

public void configures1MHzClockFrequencyWhenCreated() throws Exception {

driver = new Ws2801(device, Direction.NORMAL); verify(device).setFrequency(1_000_000);

}

@Test

public void writesToSpiDeviceWhenWriting() throws Exception { int[] anyColors = {Color.RED, Color.DKGRAY, Color.GREEN, Color.WHITE, Color.YELLOW};

driver.write(anyColors);

verify(device).write(any(byte[].class), anyInt()); }

}

Test collaborators easilypublic class ColorUnpackerTest { @Test public void orderedBytesWhenModeIsRBG() { Ws2801.Mode mode = Ws2801.Mode.RBG;

byte[] result = ColorUnpacker.getOrderedRgbBytes(mode, R, G, B);

assertBytesOrder(result, R, B, G); }

@Test public void orderedBytesWhenModeIsBGR() { Ws2801.Mode mode = Ws2801.Mode.BGR;

byte[] result = ColorUnpacker.getOrderedRgbBytes(mode, R, G, B);

assertBytesOrder(result, B, G, R); }}

Tips & Tricks

Always match .openSomething()

with .close()

Set a static IP addressor

use Android.local

On app crash need to redeploy

or adb shell am start

Only 1 app active at a time https://goo.gl/kLtvM7

Each board has different pin/bus address names

Threading, 3 choices

Thanks!Paul Blundell

Xavi Rigau@blundell_apps@xrigau

Questions?user drivers PWM

UARTGPIO

I2C

SPIinput

output

sensorsactuators

MVP

DDDPaul Blundell

Xavi Rigau@blundell_apps@xrigau

Recommended