K O logo
IoT

IoT — Data visualization

Author

Kalle

Date Published

This AI-generated image depicts a futuristic data center with large screens displaying real-time data visualizations and analytics. Rows of workstations with monitors analyze various sensor data and global metrics.

After collecting data the next step is to use this information. And one of the best ways to do so is paint beautiful graphs based on the numbers. There are many tools to support you like kibana or grafana which are both open source software and capable to display large amounts of data coming from live streams. If you want to get your hands a bit dirty you can also go for one of the bazillion javascript frameworks. Both ways have benefits and drawbacks, for larger projects with possibly huge datasets, kibana and co. are the way to go. Those tools come with query languages to generate graphs from data stores like elastic search, are battle tested in big data environments and basically industry standard, so support and general knowledgebase is great. The drawbacks are a more complicated setup and hosting than a browser based javascript solution. In principle, with each solution path you have to decide what data you want to display and how you want to display it.

For this project or comparably sized ones I recommend setting up a custom visualization with javascript to save the effort of hosting another data storing solution and a dedicated visualization service. If you already have a complete kibana/grafana or comparable setup this is another story, then you should just add your project to the existing setup. If not the hosting of a static web app is pretty easy, even firebase has a hosting solution included.

So the first step is to install the firebase-tools as a global package to control your firebase projects from the terminal.

1npm install -g firebase-tools

Then use create-react-app to bootstrap a new react based web application. Run the following command to create a new folder (APP_NAME) containing the code for the application:

1npx create-react-app APP_NAME

When the initialization process is complete you can start a development server to start working on the web app with the command npm start run in the project folder. With the command npm run build you can create an optimized build to serve over the internet.

To publish the web site to firebase start by signing in with the command firebase login and continue with firebase init. During the init process you have to make some choices:

  • Which Firebase CLI features do you want to setup for this folder?: Hosting
  • What do you want to use as your public directory? build
  • Configure as a single-page app (rewrite all urls to /index.html)? yes

With firebase initialized you can now run firebase deploy to publish your application, remember to run npm run build to have a freshly built version in the build folder.

When you got familiar with the basic project setup you need to install two more dependencies: [@firebase/app](https://www.npmjs.com/package/firebase) and [recharts](https://www.npmjs.com/package/recharts). The firebase package let's you connect to firebase and access your data, the recharts package provides composable react-components to build up charts and graphs.

1npm install firebase recharts

To connect to your firestore instance start by initializing the firebase app and then get the reference to the firestore:

1import app from "firebase/app";
2import "firebase/firestore";
3
4const config = {
5 apiKey: YOUR_API_KEY,
6 databaseURL: YOUR_DATABASE_DOMAIN,
7 projectId: YOUR_PROJECT_ID,
8};
9
10const firebaseApp = app.initializeApp(config);
11
12const firestore = firebaseApp.firestore();
13const settings = { timestampsInSnapshots: true };
14firestore.settings(settings);

With the working firestore reference you can now query data by using the firestore API:

1function subscribeToSensorData(onUpdate) {
2 return firestore
3 .collection("sensorData") // choose the collection of your data
4 .orderBy("date", "desc") // order the saved documents by date descending
5 .limit(40) // only take the last 40 entries
6 .onSnapshot((snapshotData) => {
7 onUpdate(
8 // on an update deliver the data to the callback
9 // transform the snapshot data to the actual objects
10 snapshotData.docs.map((documentSnapshot) => {
11 const data = documentSnapshot.data();
12
13 return {
14 ...data,
15 date: new Date(data.date.seconds * 1000),
16 };
17 }),
18 );
19 });
20}

But when you call this function and try to read data you will get this error:

The image shows a red error message in a console, indicating an "Uncaught Error in onSnapshot: Error: Missing or insufficient permissions." The error is related to Firestore and includes a stack trace with references to various JavaScript files and line numbers.

This means that you have to configure the rules of the database so that reads are allowed. To change the rules go to the database settings in the firebase console and choose the rules tab. The following ruleset will allow all reads and deny writes without admin access.

The image shows a Firestore security rules script. It defines access rules for a Firestore database. The script allows read access to all documents (`allow read: if true;`) but denies write access (`allow write: if false;`). The rules apply to all documents in the database.

Now after allowing reads, the next step is to connect the firebase data with the actual app. A React component extending the Component class can hold an instance state object which will trigger a re-render whenever it gets updated by the setState method of the component. So this state is ideal to hold the graph data. To subscribe to the firestore updates and save the result in the instance state extend the App as follows:

1class App extends Component {
2 // initial state with an empty data array
3 state = { sensorData: [] }
4
5 // method that gets called when this component
6 // instance got inserted into the view
7 componentDidMount() {
8 this.unsubscribe = subscribeToSensorData(this.onSensorData)
9 }
10
11 // method that gets called when this component
12 // instance will be removed
13 componentWillUnmount() {
14 if (this.unsubscribe) {
15 this.unsubscribe()
16 }
17 }
18
19 // method to store the given data in the component state
20 onSensorData = sensorData => {
21 this.setState({ sensorData })
22 }
23
24 render() {...}
25
26}

The App component now has access to the stored sensorData and finally you can start to render a chart with it. The library Recharts provides a lot of functionality and you should definitely have a look into the documentation recharts.org

Here is an example of a line-chart displaying the temperature over the date:

1...
2 render() {
3 return (
4 <LineChart width={500} height={300} data={this.state.sensorData}>
5 <XAxis dataKey="date" />
6 <YAxis
7 orientation="left"
8 type="number"
9 yAxisId="temperature"
10 domain={[-50, 50]}
11 />
12 <Line
13 dot={false}
14 type="monotone"
15 dataKey="temperature"
16 yAxisId="temperature"
17 stroke="#ee2142"
18 />
19 <Legend
20 content={
21 <div>
22 <div style={{ color: '#ee2142' }}>Temperature in °C</div>
23 </div>
24 }
25 />
26 </LineChart>
27 )
28 }
29...

Tasks

  • choose fitting charts to display the temperature and humidity values
  • deploy your chart(s) to firebase
This AI-generated image depicts a futuristic, tech-enhanced greenhouse with plants organized on shelves. The space features digital displays and sensors monitoring growth, all powered by Raspberry Pi technology.

IoT — Building an automated greenhouse Pt. I

Start building an automated greenhouse using a Raspberry Pi, sensors, and an analog-digital converter. This guide covers setting up the hardware, enabling SPI, connecting a soil moisture sensor, and writing a Node.js program to read and send sensor data to the cloud.

Mailing List

If you want to receive updates on new posts, leave your email below.