Cloudera Edge2AI:  MiNiFi Java Agent with Raspberry Pi and Thermal Camera and Air Quality Sensor - Part 1
Tags: airquality, apachenifi, apachenifi110, apachenifi19
Published: 2020-01-17 16:33:30

---
title: Cloudera Edge2AI:  MiNiFi Java Agent with Raspberry Pi and Thermal Camera and Air Quality Sensor - Part 1
published: true
date: 2020-01-09 16:24:00 UTC
tags: air-quality,apache-nifi,apache-nifi-1.10,apache-nifi-1.9
canonical_url: 
---

## MiNiFi with Thermal Cameras and Air Quality Sensors

### Cloudera Edge2AI:  MiNiFi Java Agent with Raspberry Pi and Thermal Camera and Air Quality Sensor - Part 1
**Use Case / Overview:**  
  
We need to track heat signatures, web camera images, gas and other readings from a remote workers office.   This is for occupancy analytics and safety monitoring.   We can extend this to field and remote sites where levels of temperatures, movements, noxious gases and other real world items may cause risk to our staff.  
  
For tracking room temperature and humidity we have other sensors:   [https://www.datainmotion.dev/2019/12/iot-series-minifi-agent-on-raspberry-pi.html](https://dev.to/tspannhw/iot-series-minifi-agent-on-raspberry-pi-4-with-enviro-hat-for-environmental-monitoring-and-analytics-l8d)  
[https://www.datainmotion.dev/2019/10/using-grovepi-with-raspberry-pi-and.html](https://dev.to/tspannhw/using-grovepi-with-raspberry-pi-and-minifi-agents-for-data-ingest-to-parquet-kudu-orc-kafka-hive-and-impala-2h9k)  
[https://www.datainmotion.dev/2019/09/powering-edge-ai-for-sensor-reading.html](https://dev.to/tspannhw/powering-edge-ai-for-sensor-reading-with-rpi-and-breakout-garden-efm-nifi-minifi-agents-gkj)  
[https://www.datainmotion.dev/2019/12/easy-deep-learning-in-apache-nifi-with.html](https://dev.to/tspannhw/easy-deep-learning-in-apache-nifi-with-djl-2d79)  
  
We can also track things like GPS, overhead plane traffic, regionalized social media reports, news, government reports, weather, mass transit status, traffic cameras, smoke, stock market, database, files, syslog, logs and anything else we may need to add to improve our machine learning and deep learning models.  
  
We want to send immediate alerts if anything is dangerous to the equipment or living things in the area.   We have deployed a pre-built TensorFlow Lite model to our edge device to execute on incoming images.   We can deploy our own models automagically via Apache NiFi - MiNiFi Agents and Edge Flow Manager:   [https://www.datainmotion.dev/2019/08/updating-machine-learning-models-at.html](https://dev.to/tspannhw/updating-machine-learning-models-at-the-edge-with-apache-nifi-and-minifi-3ama).  
  
  

**Hardware Component List:**

- Raspberry Pi 4
- USB Power Cable
- [Breakout Garden for Raspberry Pi (I2C + SPI)](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
- SGP30 Air Quality Sensor Breakout
- [MLX90640 Thermal Camera Breakout – Wide angle (110°)](https://shop.pimoroni.com/products/mlx90640-thermal-camera-breakout?variant=12549161746515Wide%20angle%20(110%C2%B0))

  

**Software Component List:**

- Raspian
- Python 3.7
- OpenJDK 8 Java
- Apache NiFi 1.10
- MiniFi Agent 0.6.0 Java
- Cloudera Edge Flow Manager (CEM)
- Apache Kudu
- Apache Kafka 2.x
- Cloudera Streams Messaging Manager
- Cloudera Manager 7.0.3
- Apache Hue
- Cloudera Schema Registry
- Apache NiFi Registry
- CDP on AWS with Kafka and NiFi Data Hub

**Source Code:**

[https://github.com/tspannhw/minifi-gasthermal](https://github.com/tspannhw/minifi-gasthermal)

[https://github.com/tspannhw/minifi-breakoutgarden](https://github.com/tspannhw/minifi-breakoutgarden)  
  
[https://github.com/tspannhw/table-ddl](https://github.com/tspannhw/table-ddl/tree/master)

**Example JSON Data:**

  

<style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #ffffff; background-color: #224fbc} span.s1 {font-variant-ligatures: no-common-ligatures} </style>

{"uuid": "sgp30\_uuid\_qmy\_20200104232746", "ipaddress": "192.168.1.221", "runtime": "0", "host": "garden2", "host\_name": "garden2", "macaddress": "dc:a6:32:32:98:20", "end": "1578180466.510303", "te": "0.027782917022705078", "systemtime": "01/04/2020 18:27:46", "cpu": 3.9, "diskusage": "111786.0 MB", "memory": 9.7, "equivalentco2ppm": "  405", "totalvocppb": "   16", "id": "20200104232746\_9379ae31-d848-4655-964f-92bd1b5e63fe"}

  

 { "node\_id\_0" : "511",

  "label\_0" : "container ship, containership, container vessel",

  "result\_0" : "0.439216",

  "node\_id\_1" : "650",

  "label\_1" : "megalith, megalithic structure",

  "result\_1" : "0.050980",

  "node\_id\_2" : "580",

  "label\_2" : "grand piano, grand",

  "result\_2" : "0.050980",

  "node\_id\_3" : "882",

  "label\_3" : "upright, upright piano",

  "result\_3" : "0.027451",

  "node\_id\_4" : "518",

  "label\_4" : "crane",

  "result\_4" : "0.023529",

  "uuid" : "tensorflow\_uuid\_mbk\_20200106194910",

  "ipaddress" : "192.168.1.221",

  "runtime" : "0",

  "host" : "garden2",

  "host\_name" : "garden2",

  "macaddress" : "dc:a6:32:32:98:20",

  "end" : "1578340151.1180859",

  "te" : "0.18831157684326172",

  "systemtime" : "01/06/2020 14:49:11",

  "cpu" : 45.8,

  "diskusage" : "109621.5 MB",

  "memory" : 50.2,

  "id" : "20200106194910\_098df463-4d9e-4326-9f8c-ad12fe55c7d2" }

  

**Example Thermal Data:**

[![](https://1.bp.blogspot.com/-o0cC0kGtCzM/XhKUtIm4oII/AAAAAAAAZIs/baqlrQPW_Wcl0IzZDi04na9JZ9MVdEPNwCPcBGAYYCw/s640/mlx90640-2020-01-05-20-52-14.gif)](https://1.bp.blogspot.com/-o0cC0kGtCzM/XhKUtIm4oII/AAAAAAAAZIs/baqlrQPW_Wcl0IzZDi04na9JZ9MVdEPNwCPcBGAYYCw/s1600/mlx90640-2020-01-05-20-52-14.gif)

  

**Example Web Cam Data:**

  

[![](https://1.bp.blogspot.com/-RftcYz-awxI/XhKUuOtalCI/AAAAAAAAZIw/XhQCfK3HDSA3Gqf0Ihgk3gAFZOjnQcshACPcBGAYYCw/s320/webcam2020-01-05_2051.jpg)](https://1.bp.blogspot.com/-RftcYz-awxI/XhKUuOtalCI/AAAAAAAAZIw/XhQCfK3HDSA3Gqf0Ihgk3gAFZOjnQcshACPcBGAYYCw/s1600/webcam2020-01-05_2051.jpg)

  

  

**Device:**

[![](https://1.bp.blogspot.com/-3-GIBowRaTY/Xg5g0CkNRvI/AAAAAAAAZGo/Tr4--F2ivMw_9_LX6SQQhDBUmLtu5NPMgCLcBGAsYHQ/s640/IMG-7028.jpg)](https://1.bp.blogspot.com/-3-GIBowRaTY/Xg5g0CkNRvI/AAAAAAAAZGo/Tr4--F2ivMw_9_LX6SQQhDBUmLtu5NPMgCLcBGAsYHQ/s1600/IMG-7028.jpg)
  

[![](https://1.bp.blogspot.com/-kXsenDBGk3s/Xg5g0AzM1aI/AAAAAAAAZGk/alZQ3oSLQxAWzpaj2wp9kLZhdBGvxjS0gCLcBGAsYHQ/s640/IMG-7029.jpg)](https://1.bp.blogspot.com/-kXsenDBGk3s/Xg5g0AzM1aI/AAAAAAAAZGk/alZQ3oSLQxAWzpaj2wp9kLZhdBGvxjS0gCLcBGAsYHQ/s1600/IMG-7029.jpg)
  

[![](https://1.bp.blogspot.com/-kblxP2Een9M/Xg5g0F9aRyI/AAAAAAAAZGs/YDpvD-HdK0sB0PPv7k20m_U74sPryd2lQCLcBGAsYHQ/s640/IMG-7030.jpg)](https://1.bp.blogspot.com/-kblxP2Een9M/Xg5g0F9aRyI/AAAAAAAAZGs/YDpvD-HdK0sB0PPv7k20m_U74sPryd2lQCLcBGAsYHQ/s1600/IMG-7030.jpg)

  

**Setup:**

We need to make sure the Raspberry Pi is up to date and has some software available such as git, curl and unzip.    I can then build the MLX library.

I had to install the sgp30-python using the install.sh from the github, don't use the pip3 install.   Initial calibration will take a while, so let that happen.

A demo is installed:   /home/pi/Pimoroni/sgp30/examples

**MLX Thermal Camera**

There is a nice Python utility that will build a GIF animation from the camera.   /opt/demo/mlx90640-library/python

Make sure you build all examples.

**Install Java for MiNiFi Agent**

sudo apt install openjdk-8-jdk openjdk-8-jre

  

Download MiNiFi from Cloudera or nifi.apache.org and copy zip/tar/gz to your device.

  

**MiNiFi Ingest:**

  
Web Camera Images  
Sensor JSON Logs  
Thermal Videos  
TensorFlow Lite Classification Results  
  

MiNiFi can grab my data from my device whether it's files, logs, images, sensor readings, python app calls, shell scripts, TCP, UDP or whatever.  Goodbye cron.
  

[![](https://1.bp.blogspot.com/-rjBFB1m-pNE/XhaAxisYmEI/AAAAAAAAZKg/5i4O-1xyg_4yv2U1rKPIoNkl9pFBlvG7QCLcBGAsYHQ/s640/localdataonagent.png)](https://1.bp.blogspot.com/-rjBFB1m-pNE/XhaAxisYmEI/AAAAAAAAZKg/5i4O-1xyg_4yv2U1rKPIoNkl9pFBlvG7QCLcBGAsYHQ/s1600/localdataonagent.png)

  

Building a MiNiFi flow and pushing that to make MiNiFi agents running on various devices is a snap.   I drag and drop a few components, set some parameters such which file to tail, what directory to list and what scripts to run and bam send it to the cloud.  
  
[![](https://1.bp.blogspot.com/-OhdGrLt6_NQ/XhaAtsmXQ5I/AAAAAAAAZKA/7vFuXKXnuDQC4qieVwTZImoG0ngpIvJCwCLcBGAsYHQ/s640/efmMiNiFiFlow.png)](https://1.bp.blogspot.com/-OhdGrLt6_NQ/XhaAtsmXQ5I/AAAAAAAAZKA/7vFuXKXnuDQC4qieVwTZImoG0ngpIvJCwCLcBGAsYHQ/s1600/efmMiNiFiFlow.png)  
  
  
It is very easy to monitor my progress with [EFM](https://www.datainmotion.dev/2019/05/cloudera-edge-management-introduction.html) event viewer or [REST API](https://dev.to/tspannhw/monitoring-cloudera-edge-flow-manager-and-cloudera-streams-messaging-manager-with-apache-nifi-387k).  
  

[![](https://1.bp.blogspot.com/-417m98hIZbQ/XhaAsnDjm4I/AAAAAAAAZJ4/KDN8nU_pOAQ9nE-SKg5eX5oHCs60eR0sQCLcBGAsYHQ/s640/cemEvents.png)](https://1.bp.blogspot.com/-417m98hIZbQ/XhaAsnDjm4I/AAAAAAAAZJ4/KDN8nU_pOAQ9nE-SKg5eX5oHCs60eR0sQCLcBGAsYHQ/s1600/cemEvents.png)
  

**NiFi Processing:**

  

- Receive From MiNiFi Agents
- Route Images to Image Processing
- QueryRecord to limit and route records
- Push to Kafka

  
[![](https://1.bp.blogspot.com/-0as14yGdtB8/XhaA2G6c0JI/AAAAAAAAZLE/eeBo2ipX-2c7l8y3jCMgLwnyDOKfxbjzwCLcBGAsYHQ/s640/thermalMiNiFiFlowToKafka.png)](https://1.bp.blogspot.com/-0as14yGdtB8/XhaA2G6c0JI/AAAAAAAAZLE/eeBo2ipX-2c7l8y3jCMgLwnyDOKfxbjzwCLcBGAsYHQ/s1600/thermalMiNiFiFlowToKafka.png)  
  
**Additional NiFi Processing in Second AWS Cluster**  
  
  

- Consume Kafka
- Insert into Kudu Table
  
[![](https://1.bp.blogspot.com/-ewSPTvB09t0/XhaApitG94I/AAAAAAAAZJg/LgNpCyrpiQMf5bbFID8t0yEb_LOqF4j6gCLcBGAsYHQ/s640/KafkaToKudu.png)](https://1.bp.blogspot.com/-ewSPTvB09t0/XhaApitG94I/AAAAAAAAZJg/LgNpCyrpiQMf5bbFID8t0yEb_LOqF4j6gCLcBGAsYHQ/s1600/KafkaToKudu.png)  
  

  
  
[![](https://1.bp.blogspot.com/-kBYHQcv3HeU/XhaAzB5P4YI/AAAAAAAAZKs/L11e1ptMzuo1VDhCr9eMjYenrbgAFY9rwCLcBGAsYHQ/s640/nifiprovenance.png)](https://1.bp.blogspot.com/-kBYHQcv3HeU/XhaAzB5P4YI/AAAAAAAAZKs/L11e1ptMzuo1VDhCr9eMjYenrbgAFY9rwCLcBGAsYHQ/s1600/nifiprovenance.png)  
  
We need to add topics to Kafka so we can send our messages for further processing.  

[![](https://1.bp.blogspot.com/-EJUrJ7tXfNA/XhaApkf6BgI/AAAAAAAAZJc/fyc1RU4Uc-geKxqeR2X9c6Aqc4VOvBV6QCLcBGAsYHQ/s640/addTopicGassSensorSMM.png)](https://1.bp.blogspot.com/-EJUrJ7tXfNA/XhaApkf6BgI/AAAAAAAAZJc/fyc1RU4Uc-geKxqeR2X9c6Aqc4VOvBV6QCLcBGAsYHQ/s1600/addTopicGassSensorSMM.png)
  

[![](https://1.bp.blogspot.com/-UVyxyi5zm94/XhaApl4ZZSI/AAAAAAAAZJY/_MvprX5UyroYDRqjTikpfq4zThm5-AUAwCLcBGAsYHQ/s640/addTopicWebCamCreateSMM.png)](https://1.bp.blogspot.com/-UVyxyi5zm94/XhaApl4ZZSI/AAAAAAAAZJY/_MvprX5UyroYDRqjTikpfq4zThm5-AUAwCLcBGAsYHQ/s1600/addTopicWebCamCreateSMM.png)

  

Before I can create Kafka topics, send messages or monitor Kafka, I need a Kafka cluster.   Using The Enterprise Data Cloud, Cloudera Data Platform lets me easily spin up a Kafka cluster on AWS or Azure (soon Google).
[![](https://1.bp.blogspot.com/-kOtwtvpc79Y/XhaAyujoxpI/AAAAAAAAZKo/suH2OajGhuMZKekRkxBBu54kwFVJupKqQCLcBGAsYHQ/s640/nifidatahub.png)](https://1.bp.blogspot.com/-kOtwtvpc79Y/XhaAyujoxpI/AAAAAAAAZKo/suH2OajGhuMZKekRkxBBu54kwFVJupKqQCLcBGAsYHQ/s1600/nifidatahub.png)  
  

[![](https://1.bp.blogspot.com/-rXLN0k8-RL8/XhaAqD7DyUI/AAAAAAAAZJk/6TGaQ8CMSQY5wkmU0dbnre8apMXqFh3wgCLcBGAsYHQ/s640/cdp12kafkahub.png)](https://1.bp.blogspot.com/-rXLN0k8-RL8/XhaAqD7DyUI/AAAAAAAAZJk/6TGaQ8CMSQY5wkmU0dbnre8apMXqFh3wgCLcBGAsYHQ/s1600/cdp12kafkahub.png)

  

If I don't like fancy web UIs or need to script this, there is a CDP CLI that I can create a cluster using a JSON template.
  

[![](https://1.bp.blogspot.com/-OYDUBY84UEM/XhaAqoGE1kI/AAAAAAAAZJo/FZGCT6Xo-9k4lUjvOevXShjehQIttdSFwCLcBGAsYHQ/s640/cdpbuildwithcli.png)](https://1.bp.blogspot.com/-OYDUBY84UEM/XhaAqoGE1kI/AAAAAAAAZJo/FZGCT6Xo-9k4lUjvOevXShjehQIttdSFwCLcBGAsYHQ/s1600/cdpbuildwithcli.png)

  

It is very easy to build NiFi and Kafka Data Hubs and make them available to users/developers in minutes.   So we are now ready to rock.
  

[![](https://1.bp.blogspot.com/-2w_h6IxbtC8/XhaArMKVDVI/AAAAAAAAZJs/nvQN8NNVE7kKuZWYV5-TyrPeuyimdUyPgCLcBGAsYHQ/s640/cdplist.png)](https://1.bp.blogspot.com/-2w_h6IxbtC8/XhaArMKVDVI/AAAAAAAAZJs/nvQN8NNVE7kKuZWYV5-TyrPeuyimdUyPgCLcBGAsYHQ/s1600/cdplist.png)
  

[![](https://1.bp.blogspot.com/-QrCjQlrv84Q/XhaAruMtTvI/AAAAAAAAZJw/pkz_4pjCtboQnXQUgjnhudzqiijXMWLXwCLcBGAsYHQ/s640/cdpmessaging.png)](https://1.bp.blogspot.com/-QrCjQlrv84Q/XhaAruMtTvI/AAAAAAAAZJw/pkz_4pjCtboQnXQUgjnhudzqiijXMWLXwCLcBGAsYHQ/s1600/cdpmessaging.png)
  

[![](https://1.bp.blogspot.com/-rRLCNsQvYRw/XhaAsDPgTkI/AAAAAAAAZJ0/pJk2Cb7Yt-kCV-ZEFTy0xfquQDK9ib1rgCLcBGAsYHQ/s640/cdptemplate.png)](https://1.bp.blogspot.com/-rRLCNsQvYRw/XhaAsDPgTkI/AAAAAAAAZJ0/pJk2Cb7Yt-kCV-ZEFTy0xfquQDK9ib1rgCLcBGAsYHQ/s1600/cdptemplate.png)

  

[![](https://1.bp.blogspot.com/-HJpJB1HXAZ8/XhaAtGEpQVI/AAAAAAAAZJ8/eJucuQlYiIQLbdaYeNQC__uqFHR_CqCjgCLcBGAsYHQ/s640/datahubs.png)](https://1.bp.blogspot.com/-HJpJB1HXAZ8/XhaAtGEpQVI/AAAAAAAAZJ8/eJucuQlYiIQLbdaYeNQC__uqFHR_CqCjgCLcBGAsYHQ/s1600/datahubs.png)

  

  

Since we have been producing and consuming thousands of messages in different topics to my cloud hosted Kafka cluster, let's see what's going on by using Cloudera's Streams Messaging Manager (SMM).   
  

[![](https://1.bp.blogspot.com/-G916gyCfcGE/XhaAuJn6JfI/AAAAAAAAZKE/6ff5CBjvENopCZOetLCGmR2hvdcw_dbbwCLcBGAsYHQ/s640/gassensorSMMExplorer.png)](https://1.bp.blogspot.com/-G916gyCfcGE/XhaAuJn6JfI/AAAAAAAAZKE/6ff5CBjvENopCZOetLCGmR2hvdcw_dbbwCLcBGAsYHQ/s1600/gassensorSMMExplorer.png)

[![](https://1.bp.blogspot.com/-wWhXd-KyRog/XhaAzstUD9I/AAAAAAAAZKw/M4mN2V3U_b0p6eZCdyeHHe9TPB9gUGGCACLcBGAsYHQ/s640/smmCameraData.png)](https://1.bp.blogspot.com/-wWhXd-KyRog/XhaAzstUD9I/AAAAAAAAZKw/M4mN2V3U_b0p6eZCdyeHHe9TPB9gUGGCACLcBGAsYHQ/s1600/smmCameraData.png)
  

[![](https://1.bp.blogspot.com/-A1NCoNbICOQ/XhaA0NOsQuI/AAAAAAAAZK0/F_M53R6KG5Y9CbugO3g--R9-oXHiox0mACLcBGAsYHQ/s640/smmCameraDataExplorer.png)](https://1.bp.blogspot.com/-A1NCoNbICOQ/XhaA0NOsQuI/AAAAAAAAZK0/F_M53R6KG5Y9CbugO3g--R9-oXHiox0mACLcBGAsYHQ/s1600/smmCameraDataExplorer.png)
  

[![](https://1.bp.blogspot.com/-hkmcfHCYOP4/XhaA0n56Z3I/AAAAAAAAZK4/ZOQapKP0OAAnXoTZdlXAa0Lcdfx9CE22wCLcBGAsYHQ/s640/smmGasData.png)](https://1.bp.blogspot.com/-hkmcfHCYOP4/XhaA0n56Z3I/AAAAAAAAZK4/ZOQapKP0OAAnXoTZdlXAa0Lcdfx9CE22wCLcBGAsYHQ/s1600/smmGasData.png)
  

[![](https://1.bp.blogspot.com/-R9_FyXzmrNo/XhaA1MFLh8I/AAAAAAAAZK8/ApHWkKYBO4IVg0WXfhbGkSn4IW_0K2DPQCLcBGAsYHQ/s640/smmTopics.png)](https://1.bp.blogspot.com/-R9_FyXzmrNo/XhaA1MFLh8I/AAAAAAAAZK8/ApHWkKYBO4IVg0WXfhbGkSn4IW_0K2DPQCLcBGAsYHQ/s1600/smmTopics.png)
  

[![](https://1.bp.blogspot.com/-RAxQwrmrGNE/XhaA1rxVBpI/AAAAAAAAZLA/cFSXKx6crX8yFCsr3HVwxURxYtuAA4cSQCLcBGAsYHQ/s640/srGas.png)](https://1.bp.blogspot.com/-RAxQwrmrGNE/XhaA1rxVBpI/AAAAAAAAZLA/cFSXKx6crX8yFCsr3HVwxURxYtuAA4cSQCLcBGAsYHQ/s1600/srGas.png)

  

  

Let's see the data now that it has landed in Impala/Kudu tables.   So easy to query my tables with Apache Hue.
  

[![](https://1.bp.blogspot.com/-gIvOlkFWwD0/XhaAvBay-8I/AAAAAAAAZKM/J28ers-ioUIrr0US-NAbM4loY3H7mUF4wCLcBGAsYHQ/s640/hueGasSensors.png)](https://1.bp.blogspot.com/-gIvOlkFWwD0/XhaAvBay-8I/AAAAAAAAZKM/J28ers-ioUIrr0US-NAbM4loY3H7mUF4wCLcBGAsYHQ/s1600/hueGasSensors.png)
  

[![](https://1.bp.blogspot.com/-bv4qIWC96wU/XhaAvu9cjpI/AAAAAAAAZKQ/r6M0R2L6rBYO2fLlQ7e0uwTtcyr6gw8LQCLcBGAsYHQ/s640/huewebcam.png)](https://1.bp.blogspot.com/-bv4qIWC96wU/XhaAvu9cjpI/AAAAAAAAZKQ/r6M0R2L6rBYO2fLlQ7e0uwTtcyr6gw8LQCLcBGAsYHQ/s1600/huewebcam.png)
  
We can see the data displayed in Slack channels.  
  
[![](https://1.bp.blogspot.com/-RZxCehq9cmk/XhaAyPVYjVI/AAAAAAAAZKk/TdkQEVRaP6oKnVvD9AjxQmCPFTIIkgLwgCLcBGAsYHQ/s640/messageInSlack.png)](https://1.bp.blogspot.com/-RZxCehq9cmk/XhaAyPVYjVI/AAAAAAAAZKk/TdkQEVRaP6oKnVvD9AjxQmCPFTIIkgLwgCLcBGAsYHQ/s1600/messageInSlack.png)  

  

I can see my tables have been built in [Kudu](https://github.com/tspannhw/table-ddl).  

[![](https://1.bp.blogspot.com/-vN5wSpH1TGg/XhaAwFveLFI/AAAAAAAAZKU/Xh3dHBpxStE4Usyje6kPhA2W8hC7U683ACLcBGAsYHQ/s640/kuduimpalagassensor.png)](https://1.bp.blogspot.com/-vN5wSpH1TGg/XhaAwFveLFI/AAAAAAAAZKU/Xh3dHBpxStE4Usyje6kPhA2W8hC7U683ACLcBGAsYHQ/s1600/kuduimpalagassensor.png)
  

[![](https://1.bp.blogspot.com/-kytJjsKnqyY/XhaAwlzzBWI/AAAAAAAAZKY/bKR-tvipIc8FjjbdXOKviKNaQ8nrzMpLACLcBGAsYHQ/s640/kuduimpalawebcam.png)](https://1.bp.blogspot.com/-kytJjsKnqyY/XhaAwlzzBWI/AAAAAAAAZKY/bKR-tvipIc8FjjbdXOKviKNaQ8nrzMpLACLcBGAsYHQ/s1600/kuduimpalawebcam.png)
  

[![](https://1.bp.blogspot.com/-2iUKpMz_kx8/XhaAxKlI38I/AAAAAAAAZKc/HN6klQfZO98CUrYBd_vbb2NpfGPks9OIgCLcBGAsYHQ/s640/kudutables.png)](https://1.bp.blogspot.com/-2iUKpMz_kx8/XhaAxKlI38I/AAAAAAAAZKc/HN6klQfZO98CUrYBd_vbb2NpfGPks9OIgCLcBGAsYHQ/s1600/kudutables.png)

  

**Cloud Storage - Kudu Tables:**

  

> CREATE TABLE webcam ( uuid STRING,  `end` STRING, systemtime STRING, runtime STRING, cpu DOUBLE, id STRING, te STRING,  
> host STRING,  
> macaddress STRING, diskusage STRING, memory DOUBLE, ipaddress STRING, host\_name STRING,  
> node\_id\_0 INT, label\_0 STRING, result\_0 DOUBLE,  
> node\_id\_1 INT, label\_1 STRING, result\_1 DOUBLE,  
> node\_id\_2 INT, label\_2 STRING, result\_2 DOUBLE,  
> node\_id\_3 INT, label\_3 STRING, result\_3 DOUBLE,  
> node\_id\_4 INT, label\_4 STRING, result\_4 DOUBLE,  
> PRIMARY KEY (uuid, `end`)  
> )  
> PARTITION BY HASH PARTITIONS 4  
> STORED AS KUDU  
> 
> TBLPROPERTIES ('kudu.num\_tablet\_replicas' = '1');

> CREATE TABLE gassensors ( uuid STRING,  `end` STRING, systemtime STRING, runtime STRING, cpu DOUBLE, id STRING, te STRING,  
> host STRING, equivalentco2ppm STRING,  totalvocppb STRING,  
> macaddress STRING,  diskusage STRING, memory DOUBLE, ipaddress STRING, host\_name STRING,  
> PRIMARY KEY (uuid, `end`)  
> )  
> PARTITION BY HASH PARTITIONS 4  
> STORED AS KUDU  
> TBLPROPERTIES ('kudu.num\_tablet\_replicas' = '1'); 

> CREATE TABLE bme280sensors ( uuid STRING,  `end` STRING, systemtime STRING, runtime STRING, cpu DOUBLE, id STRING,  
> te STRING,  
> host STRING,  
> macaddress STRING,  diskusage STRING, memory DOUBLE, ipaddress STRING,  
> host\_name STRING, bme280\_altitude STRING, bme280\_tempf STRING, max30105timestamp STRING,  
> max30105\_detected STRING, max30105\_delta STRING, max30105\_temp STRING, bme280\_tempc STRING,  
> max30105\_mean STRING, max30105\_value STRING, bme280\_altitude\_feet STRING, bme280\_pressure STRING,  
> starttime STRING, cputemp DOUBLE, imgnamep STRING, imgname STRING,  
> PRIMARY KEY (uuid, `end`)  
> )  
> PARTITION BY HASH PARTITIONS 4  
> STORED AS KUDU  
> TBLPROPERTIES ('kudu.num\_tablet\_replicas' = '1');

> 

**Python Libraries:**

  

> wget https://dl.google.com/coral/python/tflite\_runtime-1.14.0-cp37-cp37m-linux\_armv7l.whl 

> pip3 install scikit-image 

> pip3 install getmac 

> pip3 install psutilpip3 install --upgrade pip 

> pip3 install --upgrade setuptools 

> pip3 install tflite\_runtime-1.14.0-cp37-cp37m-linux\_armv7l.whl 

> pip3 install easydict -U 

> pip3 install scikit-learn -U 

> pip3 install opencv-python -U --user 

> pip3 install numpy -U 

> pip3 install mxnet -U 

> pip3 install gluoncv --upgrade 

> pip3 install tensorflow

  

**Summary:**

We read thermal images, sensors and camera images with a MiNiFi agent that sends this data via MQTT and HTTP(S) Site-to-Site (S2S).   We process MQTT, Kafka and S2S data streams with NiFi and easily push the final data into Kudu tables which we can query.  
  
So we easily ingest structured, unstructured and semistructured data with live analytics.  
  
In the second part of this article I will show you how we integrate with CDSW for additional machine learning on our data.  
  
  
**References**  

- [https://www.datainmotion.dev/2019/03/iot-series-sensors-utilizing-breakout.html?q=pimoroni](https://www.datainmotion.dev/2019/03/iot-series-sensors-utilizing-breakout.html?q=pimoroni)
- [https://shop.pimoroni.com/products/sgp30-air-quality-sensor-breakout](https://shop.pimoroni.com/products/sgp30-air-quality-sensor-breakout)
- [https://shop.pimoroni.com/products/mlx90640-thermal-camera-breakout?variant=12549161746515](https://shop.pimoroni.com/products/mlx90640-thermal-camera-breakout?variant=12549161746515)
- [https://github.com/pimoroni/mlx90640-library](https://github.com/pimoroni/mlx90640-library)
- [https://github.com/pimoroni/breakout-garden](https://github.com/pimoroni/breakout-garden)
- [https://github.com/pimoroni/sgp30-python](https://github.com/pimoroni/sgp30-python)
- [https://www.datainmotion.dev/2019/12/iot-series-minifi-agent-on-raspberry-pi.html](https://dev.to/tspannhw/iot-series-minifi-agent-on-raspberry-pi-4-with-enviro-hat-for-environmental-monitoring-and-analytics-l8d)
- [https://www.tensorflow.org/lite/guide/hosted\_models](https://www.tensorflow.org/lite/guide/hosted_models)
- [https://www.tensorflow.org/lite/guide/python](https://www.tensorflow.org/lite/guide/python)
- [https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/examples/python/](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/examples/python/)
- [https://github.com/tensorflow/examples/tree/master/lite/examples/image\_classification/raspberry\_pi](https://github.com/tensorflow/examples/tree/master/lite/examples/image_classification/raspberry_pi)
  
<style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #ffffff; background-color: #224fbc} span.s1 {font-variant-ligatures: no-common-ligatures} </style><style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #ffffff; background-color: #224fbc} span.s1 {font-variant-ligatures: no-common-ligatures} </style>