{"id":13817545,"url":"https://github.com/eyal-lezmy/android-things-workshop","last_synced_at":"2025-05-15T20:32:35.560Z","repository":{"id":141783170,"uuid":"78584235","full_name":"eyal-lezmy/android-things-workshop","owner":"eyal-lezmy","description":"AKA the connected catapult!","archived":false,"fork":false,"pushed_at":"2018-03-30T22:04:22.000Z","size":2135,"stargazers_count":36,"open_issues_count":0,"forks_count":16,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-19T15:45:20.381Z","etag":null,"topics":["android","android-studio","androidthings","catapult","gpio","handson","iot","pwm","rainbow","raspberry-pi","raspberry-pi-3","servo-motor","workshop"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eyal-lezmy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-01-10T23:30:08.000Z","updated_at":"2023-09-08T17:19:16.000Z","dependencies_parsed_at":"2023-03-17T05:15:41.721Z","dependency_job_id":null,"html_url":"https://github.com/eyal-lezmy/android-things-workshop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyal-lezmy%2Fandroid-things-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyal-lezmy%2Fandroid-things-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyal-lezmy%2Fandroid-things-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eyal-lezmy%2Fandroid-things-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eyal-lezmy","download_url":"https://codeload.github.com/eyal-lezmy/android-things-workshop/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254418668,"owners_count":22068126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-studio","androidthings","catapult","gpio","handson","iot","pwm","rainbow","raspberry-pi","raspberry-pi-3","servo-motor","workshop"],"created_at":"2024-08-04T06:00:49.375Z","updated_at":"2025-05-15T20:32:33.859Z","avatar_url":"https://github.com/eyal-lezmy.png","language":"Java","readme":"# Hands-on Lab Android Things\n\nThis codelab will introduce you to the key concepts to creating your own IoT devices using the Android Things platform.\nBy the end of the codelab, you will build your first connected catapult and control it wirelessly.\n\nDo no harm! We know how tempting it could be, but don't try to hurt other members of the codelab with your catapult. It would only bring you trouble (hint: they may retaliate with a more powerful one).\n\n\n## Authors\n\nThis workshop has been prepared and is maintained by [Gautier Mechling](https://twitter.com/Nilhcem), [Romain Ménétrier](https://twitter.com/romemore) and [Eyal Lezmy](https://twitter.com/Eyal_Lezmy).\n\n\n## Setup\n\nClone the project, import it to [Android Studio](https://developer.android.com/studio/index.html), and build it.\n\n```bash\ngit clone https://github.com/eyal-lezmy/android-things-workshop.git\n```\n\n\n### Import the project\n\n* Start Android Studio\n* Select \"Open an existing Android Studio project\"\n* Open the project directory\n* Double click on the `build.gradle` file in the android-things-workshop directory\n* Click OK on the \"Import Project from Gradle\" screen without making changes\n\nPlease note that Android Studio might take several seconds to compile the project in the background for the first time. During this time you will see a spinner in the bottom status bar.\n\nWe recommend that you wait until this has finished before making code changes. This will allow Android Studio to pull in all the necessary components.\n\n### Understand the sample project\n\nThis project is composed of a single Java file: **MainActivity.java**. This file contains 2 methods:\n\n* `onCreate` is where you will instantiate your variables, and where you can call some methods to access IOs.\n* `onDestroy` is where you can free / close your IO resources\n\nMethods that deal with IO may throw an `IOException`. You will have to catch this checked exception, both in the onCreate and onDestroy:\n\n```java\ntry {\n  // access IOs here\n} catch (IOException e) {\n  Log.e(TAG, \"IO issue\", e);\n}\n```\n\n### How to get started with Android Things from scratch?\n\nTo save you some time, we have already prepared a sample project, flashed Android Things on Raspberry Pi boards, and set up the Wi-Fi.  \nIf you want to repeat this at home, here is what we did:\n\n- [Download and flash Android Things on the Raspberry Pi 3](https://developer.android.com/things/hardware/raspberrypi.html)\n- Use Android Studio (3 or above) to create a new Android Things project\n- Add dependencies in the `build.gradle` file\n\nThat's all!  \nWe've also built a paper catapult for you. How-to video [here](https://www.youtube.com/watch?v=9JvV8PWawLs).\n\n\n## Hardware\n\n### Discovering the Raspberry Pi\n\nThe following pinout diagram illustrates the locations of the available ports exposed by the breakout connectors of the Raspberry Pi:\n\n![Raspberry Pi pinout][raspberrypi-pinout]\n\nAs you can see, with the Raspberry Pi 3, you cannot connect more than 2 PWM devices. The Raspberry Pi also has 1 port to connect I²C, SPI and UART devices.  \nEach of these interfaces has a name (e.g.: \"BCM4\", \"UART0\", \"PWM0\"). When you connect a component (e.g. an LED) to the board, you will need to know the name of the connector to communicate with the component.\n\n### Discovering the Rainbow HAT\n\nThe Rainbow HAT is a board that features sensors, inputs, and displays you can directly plug onto the Raspberry Pi.  \nIt makes it easy to get started with Android Things, skipping the (fun, but time consuming) components wiring part.\n\nHere is a list of the different Rainbow HAT components, and their associated connector names:\n\n* **Red LED**: \"BCM6\"\n* **Green LED**: \"BCM19\"\n* **Blue LED**: \"BCM26\"\n* **Button A**: \"BCM21\"\n* **Button B**: \"BCM20\"\n* **Button C**: \"BCM16\"\n* **Piezo buzzer**: \"PWM1\"\n* **Segment display**: \"I2C1\"\n* **LED strip**: \"SPI0.0\"\n* **Temperature Sensor**: \"I2C1\"\n* **Additional PWM**: \"PWM0\"\n\nThis information will be useful later on, when we ask you to manipulate some specific components.\n\n### Servo motor\n\nDuring this workshop, you will also use an additional hardware component: an **SG90 9g Micro Servo**.\n\nA Servo is a type of geared motor that can only rotate 180 degrees. It is controlled by sending electrical pulses that tell the servo what position it should move to.  \nA servo has three wires, the brown wire is GND, the red one is 5V, and the orange one is PWM.\n\nNow you know that, your first task is to **connect the servo to the following connectors of the Rainbow HAT**.\n\n[Spoiler: that way!][servo]\n\n### Deploy your new project to the Raspberry Pi\n\nNow that the environment is set up, you can deploy this bootstrap project to the Raspberry Pi.  \n\nAs you may know, relying on a conference Wi-Fi for a workshop is like living on the edge. That's a risk we prefer not to take, so we decided to connect the Raspberry Pi to a different local Wi-Fi network.  \nYour development machine should be connected to the following Wi-Fi network to deploy your Android Things application to the Raspberry Pi.\n\n* **Security**: WPA2\n* **SSID**: SomethingsPC\n* **Password**: Somethings\n\nMake sure the Raspberry Pi is powered on, then use adb to connect the Raspberry Pi to your development computer over Wi-Fi.\n\n```bash\nadb connect \u003cRASPBERRY_IP\u003e\n```\n\nYou should see the following message:\n```\nconnected to \u003cRASPBERRY_IP:5555\u003e\n```\n\nNow, you can deploy the project on Android Studio, clicking on the \"Run\" menu \u003e \"Run 'app'\"  \nIn the device's logs (`adb logcat | grep Things`) you should see the following message:\n```\nAndroid Things project is ready!\n```\n\nCongratulations, you have deployed your first Android Things app!\n\n\n## Let's start writing some code!\n\n### Turning on LEDs\n\nSucceeding in turning on an LED is a necessary first step in understanding GPIOs (General Purpose Input Output).  \n\nAn LED is a GPIO device that glows when electricity is passed through it.  \nTo feed current to an LED, we will use the Gpio's `setValue(boolean)` method.  \nWhen the value is set to `true`, the LED is light on, when the value is set to `false`, the LED is off\n\nFirst, we'll need a reference to our LED in the `MainActivity.onCreate()`.\n\n```java\nprivate Gpio led;\n\nprotected void onCreate(Bundle savedInstanceState) {\n  super.onCreate(savedInstanceState);\n\n  PeripheralManager manager = PeripheralManager.getInstance();\n  led = manager.openGpio(\"BCM6\");\n  led.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);\n}\n```\n\nThe `PeripheralManagerService` is an Android Things helper class that provides references of components depending on their types and specified port names.  \nHere, we want a Gpio device on port \"BCM6\" (Rainbow HAT's red LED, [that one][rainbowled]), and we set its direction to initially low (we want the component to start as 'off').\n\nThen, we can light it up using\n\n```java\nled.setValue(true);\n```\n\nAlso, don't forget to close your LED in the `onDestroy()`\n```java\nled.close();\n```\n\nYour turn! Try to turn on the 3 Rainbow HAT LEDs (red, green, blue)\n\n**Question:** What does the following code do? Try it.\n```java\nwhile (true) {\n  led.setValue(!led.getValue());\n  Thread.sleep(1000);\n}\n```\n\n*Optional*: if you are familiar with Android, you probably noticed that we made a `sleep` call on the main thread.  \nFeel free to execute this code on a different thread if you want. Additionally, if you are an RxJava fan, you could also try to use Rx (`e.g. Observable.interval`) to blink your LEDs . Using a reactive library to blink LEDs ? Welcome to 2017... Or if you want to embrace the future, you could use Kotlin coroutines ;)\n\n\n### Buttons\n\nGreat! You know how to use Android to blink LEDs.  \nNow, we will create a light switch: when a user presses a button, it should light an LED. When the button is pressed again, the LED should be off.\n\nAndroid Things provides an official `Button` driver to let us deal with physical buttons:\n\n```java\n// First, you initialize your button\nButton button = new Button(\"BCM21\", Button.LogicState.PRESSED_WHEN_LOW);\n\n// Then, you listen for pressed events\nbutton.setOnButtonEventListener(new Button.OnButtonEventListener() {\n    @Override\n    public void onButtonEvent(Button button, boolean pressed) {\n        Log.d(TAG, \"Button A pressed:\" + pressed);\n    }\n});\n\n// Don't forget to close the device in the onDestroy.\nbutton.close();\n```\n\nTry it! You can run the following command to see the logs:\n```bash\nadb logcat | grep Things\n```\n\nNow that it works, add some code to turn on and off an LED when pressing a button.\n\n#### How does the Button driver work internally?\n\nSimilar to LEDs, buttons are GPIO components.  \nThe [Gpio](https://developer.android.com/things/sdk/pio/gpio.html) class provides an additional method: `registerGpioCallback()` that listens for input state changes.  \nThe button driver ([source code](https://github.com/androidthings/contrib-drivers/blob/master/button/src/main/java/com/google/android/things/contrib/driver/button/Button.java)) takes advantage of this method to detect pressed state.\n\n\n### Buzzer\n\nTurning on an LED when a button is pressed is cool, but turning on an LED, **while playing some sound** when a button is pressed is even cooler.  \n\nRemember: GPIOs are only for [`true`, `false`] binary operations. Either your component is fed by some current, either it is not.  \nWhile it is possible to use GPIO to play some sound, this would mean that you could play only a single and unique tone, that's quite limited.  \n\nThere's a way to play different tones, using PWM (Pulse Width Modulation) instead of GPIO.\n\nWith [PWM](https://developer.android.com/things/sdk/pio/pwm.html), you can either vary the duty cycle, or the frequency.  \n\nIf you vary the frequency, the buzzer will play different tones.\n\n```java\n// Initialize the buzzer\nPeripheralManager manager = PeripheralManager.getInstance();\nPwm pwm = manager.openPwm(\"PWM1\");\npwm.setPwmDutyCycle(50.0); // square wave\n\n// Play a note\nint frequency = 440;\npwm.setPwmFrequencyHz(frequency);\npwm.setEnabled(true);\n\n// Wait\nThread.sleep(500);\n\n// Stop playing the note\npwm.setEnabled(false);\n\n// Close the device when done (onDestroy)\npwm.close();\n```\n\nNow, integrate that code to your project, to play some sound when a button is pressed.\n\n**Question:** What does the following code do? Try it!\n```java\nint[] frequencies = new int[] { 261, 294, 329, 349, 392, 440, 493, 523 };\npwm.setEnabled(true);\n\nfor (int frequency : frequencies) {\n  pwm.setPwmFrequencyHz(frequency);\n  Thread.sleep(300);\n}\npwm.setEnabled(false);\n```\n\n#### Making things simpler\n\nAs for the button, there's an official [PWM speaker](https://github.com/androidthings/contrib-drivers/tree/master/pwmspeaker) driver that makes it easier to play tones\n\n```java\nSpeaker buzzer = new Speaker(\"PWM1\");\nbuzzer.play(frequency);\nbuzzer.stop();\nbuzzer.close(); // onDestroy()\n```\n\nIf you take a look at the PWM speaker driver [source code](https://github.com/androidthings/contrib-drivers/blob/master/pwmspeaker/src/main/java/com/google/android/things/contrib/driver/pwmspeaker/Speaker.java), you will be surprised by how small the implementation is.  \nIt is actually very similar to what we wrote manually.\n\nIf one day you have to create your own driver, you can see that it may be easier than you might think.\n\n\n## Using the servo motor\n\nNow that you are familiar with PWM, we will use the servo motor which also is a PWM component.  \n\nThere is an official driver that simplifies using PWM servo motors.  \nWe will use it; we already know how PWM works, and it will prevent us from writing too much code.\n\n### Setting up the Servo motor\n\nWhen creating a driver, or setting up a component. We have to read the documentation.  \nTake a look at the following picture, extracted from an official [SG90 documentation](http://www.micropik.com/PDF/SG90Servo.pdf)\n\n![servodoc][servodoc]\n\nWe can see the following information:\n\n* A ~1ms pulse means moving the servo to -90\n* A ~2ms pulse means moving the servo to 90\n\nThis translates into code like that:\n\n```java\nServo servo = new Servo(\"PWM0\");\nservo.setPulseDurationRange(1, 2);\nservo.setAngleRange(-90, 90);\nservo.setEnabled(true);\n```\n\n(again, don't forget to close the servo in the `onDestroy`)\n\nTo make the servo move, we have to provide it an angle. Examples:\n\n```java\nservo.setAngle(42d);\n// or\nservo.setAngle(servo.getMinimumAngle());\n// or\nservo.setAngle(servo.getMaximumAngle());\n```\n\nUsing this code snippet, move the servo to its minimum angle when pressing a button, and move it to its maximum angle when pressing the button again.  \nYou'll probably have to create a boolean variable that keeps the state of the PWM (whether it is at its minimum or maximum angle).\n\n**Note**: After implementing this code, you'll probably notice that setting a 1ms-2ms pulse duration range only makes the servo move around a 90° angle instead of 180°.  \nThe documentation values seem not to be perfect here. If you want to have a wider angle, use a wider range like 0.6ms-2.4ms for example.\n\n\n## Fasten the servo motor to the catapult\n\nOk, now you are able to move the servo to its minimum and maximum angle when pressing a button.  \nThe next step is to connect it to the catapult, so that the servo can lock and release the catapult when pressing a button.\n\n![servo2][servo2]\n\nNote: if it makes it easier for you, use some scotch tape to stick the catapult / servo to the table.\n\nYou have created an Android Things catapult. Take some time to have fun with it, and then, go to the next part\n\n\n## Wirelessly control the catapult\n\nWouldn't it be funnier if you can control the catapult from your phone / computer?\n\nThere are many ways to add remote control capabilities to an Android Things project. Among those:\n\n* Sending a push, via [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/)\n* Using Google's [Nearby Connections API](https://developers.google.com/nearby/) to control it over Wi-Fi\n* Embedding an HTTP server (such as [NanoHttpd](https://github.com/NanoHttpd/nanohttpd)) in the Android Things project\n\nKeep in mind this list is not exhaustive. We'll use today an easy way: embedding an HTTP server inside our catapult\n\n### Embedding an HTTP server\n\nNanoHttpd is a tiny web server, written in Java.  \nIt can be embedded to an Android project using a single import in the `build.gradle` file (_we have already imported the dependency, so you don't have to do it_).\n\nTo serve embedded web pages, you first need to create a class that extends `NanoHTTPD` and overrides a `serve` method.\n\n```java\npublic class HttpdServer extends NanoHTTPD {\n\n  private static final int PORT = 8888;\n\n  public interface OnFireTriggerListener {\n    void onFireTriggered();\n    void onArmTriggered();\n  }\n\n  private OnFireTriggerListener listener;\n\n  public HttpdServer(OnFireTriggerListener listener) {\n    super(PORT);\n    this.listener = listener;\n  }\n\n  @Override\n  public Response serve(IHTTPSession session) {\n    Map\u003cString, List\u003cString\u003e\u003e parameters = session.getParameters();\n      if (parameters.get(\"fire\") != null) {\n        listener.onFireTriggered();\n      } else if (parameters.get(\"armed\") != null) {\n        listener.onArmTriggered();\n      }\n\n    String html =\n      \"\u003chtml\u003e\u003chead\u003e\"\n      +  \"\u003cscript type=\\\"text/javascript\\\"\u003e\"\n      +  \" function fire() { window.location = '?fire=true'; }\"\n      +  \" function arm() { window.location = '?armed=true'; }\"\n      +  \"\u003c/script\u003e\u003c/head\u003e\"\n      + \"\u003cbody\u003e\"\n      + \"\u003cbutton style=\\\"width: 50%; height: 100%; font-size: 4em;\\\" onclick=\\\"fire();\\\"\u003eFIRE!\u003c/button\u003e\"\n      + \"\u003cbutton style=\\\"width: 50%; height: 100%; font-size: 4em;\\\" onclick=\\\"arm();\\\"\u003eArm Catapult!\u003c/button\u003e\"\n      + \"\u003c/body\u003e\u003c/html\u003e\";\n\n      return newFixedLengthResponse(html);\n    }\n}\n```\n\nThen, add the following code to your `MainActivity` class to start and stop the server:\n\n```java\nprivate HttpdServer httpdserver;\n\n// In your onCreate()\nhttpdserver = new HttpdServer(this);\nhttpdserver.start();\n\n// In your onDestroy()\nhttpdserver.stop();\n```\n\nNote: if you tried starting the server twice _(i.e.: 2 deployments without calling the `httpdserver.stop()` method)_, the embedded server may not work anymore (\"Unable to connect\").  \nDon't forget that you can consult the logs anytime (from Android Studio, in the \"Android Monitor\" tab at the bottom of the screen).  \nIf you see the following error: `java.net.BindException: Address already in use`, try uninstalling the app and deploy it again:\n`adb uninstall com.example.androidthings.myproject`\n\nNow, the Android Things device will embed an HTTP server.\n\nTo control it, start a web browser to the `http://\u003cRASPBERRY_IP\u003e:8888/` URL, and click on the button to move the servo motor remotely.\n\n\n## What's next?\n\nNow it's your turn! Try to improve your catapult the way you want, developing some features that make it unique.  \nIf you're a little lost on ideas, here are some suggestions of what you could do right now:\n\n### Show the world your awesomeness\n\nProud of what you achieved today? Tweet pics/videos using the `#ThingsLethalWeapon` hashtag.\n\n### Use the LED strip\n\nThe Rainbow HAT is named that way because it embeds an LED strip you can use to show the colors of the rainbow.\n\n```java\nApa102 ledstrip = RainbowHat.openLedStrip();\nledstrip.setBrightness(1);\nint[] rainbow = new int[RainbowHat.LEDSTRIP_LENGTH];\nfor (int i = 0; i \u003c rainbow.length; i++) {\n    rainbow[i] = Color.HSVToColor(255, new float[]{i * 360.f / rainbow.length, 1.0f, 1.0f});\n}\nledstrip.write(rainbow);\n\n// Close the device when done (onDestroy)\nledstrip.close();\n```\n\nTry to use this strip and create a colorful, or funky animation.\n\n![rainbow][rainbow]\n\n### Use the segment display\n\nThe Rainbow HAT features a nice 7-segment display. Sadly, we didn't use it so far.\n\nNow it's our chance. Use the following code to display some text:\n\n```java\nAlphanumericDisplay segment = RainbowHat.openDisplay();\nsegment.setBrightness(Ht16k33.HT16K33_BRIGHTNESS_MAX);\nsegment.display(\"BOOM\");\nsegment.setEnabled(true);\n// Close the device when done (onDestroy)\nsegment.close();\n```\n\nA 4 digit screen is kind of limited, but it's enough to display important words such as \"OMG!\", WTF?\", \"AHOY\", \"GROG\", and other YARR\"s.  \nIf you feel it's too restrictive, create a scrolling text animator.\n\n\n### Show the room temperature on the segment display\n\nThe Rainbow HAT also embeds a temperature sensor. Well... not that it's particularly important for a catapult to be aware of the room temperature, but who knows?  \n\n```java\nBmx280 sensor = RainbowHat.openSensor();\nsensor.setTemperatureOversampling(Bmx280.OVERSAMPLING_1X);\nAlphanumericDisplay segment = RainbowHat.openDisplay();\nsegment.setBrightness(Ht16k33.HT16K33_BRIGHTNESS_MAX);\nsegment.display(sensor.readTemperature());\nsegment.setEnabled(true);\n// Close the devices when done (onDestroy)\nsensor.close();\nsegment.close();\n```\n\n### Create your own melody\n\nWith the PWM speaker, you can play some melody.\n\n[Here is a list of frequencies per notes](https://www.arduino.cc/en/Tutorial/ToneMelody?action=sourceblock\u0026num=2)\n\n[And here is a code sample](https://github.com/androidthings/drivers-samples/tree/master/pwmspeaker)\n\n### Read the Android Things documentation\n\nFor more information about Android Things, the official documentation is available [here](https://developer.android.com/things/index.html)\n\n### Create a native companion app\n\nCreate a native iOS / Android / Desktop app that sends an HTTP GET request to the catapult to throw a projectile.  \nDon't use that ugly embedded web page anymore.\n\n### Use Firebase Cloud Messaging instead of NanoHttpd\n\nYou prefer using a Google service instead of a generic HTTP server? Try using [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/) or check out other [Firebase features](https://firebase.google.com/) you can use.\n\n### Integrate the catapult to a third-party service\n\nThrow a projectile when the CI build fails, or when your alarm clock rings for a sweet wake up.\n\n## A final word\n\nWe hope that you have enjoyed this workshop, and can't wait looking forward to your future Android Things projects.\n\n[raspberrypi-pinout]: https://raw.githubusercontent.com/eyal-lezmy/android-things-workshop/master/doc/assets/raspberrypi_pinout.png\n[servo]: https://raw.githubusercontent.com/eyal-lezmy/android-things-workshop/master/doc/assets/servo.jpg\n[rainbowled]: https://raw.githubusercontent.com/eyal-lezmy/android-things-workshop/master/doc/assets/rainbowredled.jpg\n[servodoc]: https://raw.githubusercontent.com/eyal-lezmy/android-things-workshop/master/doc/assets/servodoc.png\n[servo2]: https://raw.githubusercontent.com/eyal-lezmy/android-things-workshop/master/doc/assets/servo2.jpg\n[rainbow]: https://raw.githubusercontent.com/eyal-lezmy/android-things-workshop/master/doc/assets/rainbow.gif\n","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feyal-lezmy%2Fandroid-things-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feyal-lezmy%2Fandroid-things-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feyal-lezmy%2Fandroid-things-workshop/lists"}