DAQ Arduino with NodeJS serialport

In this tutorial, I will make a Data Acquisition (DAQ) procedure between PC and Arduino. PC (with NodeJS) will send command to Arduino via serialport, then Arduino will send response to PC so that PC can plot the data.

At code line of Arduino, I make the main loop do nothing by using serialEvent to listen command. If command is “gene”, Arduino will send 2 parameters tempe_data and humid_data with JSON format. The reason to random generate 2 parameters is I am too lazy to connect the DHT22.


 * Date: Mon, 12/06/2017
 * Desc: Arduino and NodeJS serialport
 * Info: Random generator temperature and humidity
 * to simulate DHT22, JSON format
 * only response data when receive request
// used for generating data
double tempe_data;
double humid_data;
// used for serial buffering
#define TERM_CHAR '\n'
#define BUF_LEN 16
int i = 0;
char incomingChar; 
char buf[BUF_LEN];
// used pinLED for test mode
int pinLED = 13;

void setup() {
 // Initialize serial port

// Initialize random generator

// Initialize pinLED for test mode
 pinMode(pinLED, OUTPUT);
 digitalWrite(pinLED, LOW);

void loop() {
 // do nothing

double generate_data(long offset, long min_range, long max_range){
 // data = offset + rand, for example if range = [0,4] offset will be 2, rand will be 2
 long randParam = random(min_range*100, max_range*100);
 double data = (double)offset + (double)randParam/100;
 return data;

// generate random temperature and humidity
// send with JSON format
void procedure(){
 // generate
 tempe_data = generate_data(2, -2, 2);
 humid_data = generate_data(3, -3, 3);
 // send
 Serial.print(F("{\"CH1\": "));
 // create a fake CH2 to test JSON
 Serial.print(F(", \"CH2\": "));

void serialEvent() {
 // when characters arrive over the serial port...
 if (Serial.available()) {
 // wait a bit for the entire message to arrive
 // clean buffer and index
 i = 0; 
 // read all the available characters
 while (Serial.available() > 0) {
 incomingChar = (char) Serial.read();
 if(incomingChar != TERM_CHAR && i != BUF_LEN) 
 buf[i++] = incomingChar;
 delay(1); // wait for another byte 
 // Activate function on right code
 if(String(buf) == "10"){
 digitalWrite(pinLED, HIGH);
 } else if(String(buf) == "20"){
 digitalWrite(pinLED, LOW);
 } else if(String(buf) == "gene"){
 } else{
 // to be continued

If you ask what is the role of pinLED and command "10" and "20", it is for testing. What is the character 'F' in Serial.print(F("{\"CH1\": "))? It is saving buffering bytes for transmitting serial data. What is the flaw of the code? It still does not make Arduino sleep and wake up.

About NodeJS role, I use express, socket.io and ejs to make a web server. I use serialport to receive and transmit data. I use chart.js to plot the data. What is the flaw of the code? The DAQ only happens in my PC, it cannot be accessed by another PC or my smartphone. Here comes the code


var express = require("express");
var app = express();
app.set("view engine", "ejs");
app.set("views", "./views");

var server = require("http").Server(app);
var io = require("socket.io")(server);

// Using serialport to communicate with COM3
var serialport = require("serialport");
var mySerial = new serialport("COM3", {
 baudrate: 9600,
 parser: serialport.parsers.readline("\n")

mySerial.on("open", function(){
 console.log("COM3 is opened.");
// close serialport
mySerial.on("close", function(){
 console.log("COM3 is closed.");
// listening data on CH1
mySerial.on("data", function(JSON_data){
 // var CH1_data = JSON.parse(JSON_data).CH1;
 // var CH2_data = JSON.parse(JSON_data).CH2;
 // console.log(CH1_data + "----" + CH2_data);
 // sending COM data to client
 // io.sockets.emit("com-data",CH1_data);

// Host server// SocketIO listening for connection and disconnection
io.on("connection", function(socket){
 console.log("Connecting client id: " + socket.id);
 socket.on("disconnect", function(){
 console.log("Client id: " + socket.id + " has disconnected");

// Service for loading
app.get("/", function(req, res){

// Testing serialport transmit every 2 seconds
var auto_counter = 0;
 mySerial.write("\n"); // Eliminate
 if(auto_counter == 21){
 auto_counter = 0;


<title>COM data</title>



COM3. This is my localhost

<h2 id="noidung"></h2>
</body> //Chart.defaults.global.responsive = true; //var ctx = document.getElementById("myChart"); var ctx = document.getElementById("myChart"); var counter; var myLineChart; createGraph() //--- declare function function createGraph(){ counter = 0; myLineChart = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: ['Temperature'], data: [], backgroundColor: [ 'rgba(255, 99, 132, 0.2)' ], borderColor: [ 'rgba(255,99,132,1)' ], borderWidth: 1 }, { label: ['Humidity'], data: [], backgroundColor: [ 'rgba(54, 162, 235, 0.2)' ], borderColor: [ 'rgba(54, 162, 235, 1)' ], borderWidth: 1 }] }, options: { animation: { duration: 0, // general animation time }, scales: { yAxes: [{ ticks: { max: 6, beginAtZero:true }, stacked: false }] }, elements: { line: { tension: 0, // disables bezier curves } } } }); } // goi dien len server var socket = io("http://localhost:3000"); // client listening socket.on("com-data", function(data){ // $("#noidung").html(data); // console.log(counter + "-----" + data); var obj = JSON.parse(data); $("#noidung").html(obj.CH1 + " ----- " + obj.CH2); console.log(counter + "-----" + obj.CH1); counter++; myLineChart.data.labels.push(counter); myLineChart.data.datasets[0].data.push(obj.CH1); myLineChart.data.datasets[1].data.push(obj.CH2); myLineChart.update(); if(counter > 10){ myLineChart.destroy(); //counter = 0; createGraph(); } // if(counter = 20){ // counter = 0; // myLineChart.destroy(); // } else{ // counter++; // myLineChart.data.labels.push(counter); // myLineChart.data.datasets[0].data.push(data); // myLineChart.update(); // } }); $(document).ready(function(){ }); </html>

To use chart.js, in the folder public there will be a folder called js containing Chart.js and Chart.min.js (Chart will capital C). Note that I used COM3

Connect Arduino with loaded program and node index.js, you will see the result:


There is still some residual in my code. OK my bad. I am too lazy to delete the residual.

The biggest question in my head now is how to publish it globally so that everyone can see the chart of random generated parameters? I think about mongoDB and Heroku may work.

See you.

LCD I2C Firmata, johnny-five and NodeJS

This tutorial will show you how directly control Arduino via Firmata, NodeJS (as a server) and library johnny-five.
Step 1: load Firmata into Arduino Uno

Examples –> Firmata –> StandardFirmate –> Upload

Step 2: create a folder johnny_i2c on your desktop (you have to install NodeJS first)

cmd –> change directory to folder johnny_i2c

npm init –> Enter all and type yes at the last row

npm install johnny-five

create a file name index.js in the folder johnny_i2c with content


var five = require("johnny-five");
var board = new five.Board();

board.on("ready", function() {

 var random = Math.random().toString(36).replace(/[^a-z]+/g, "").substr(0, 4).toUpperCase();

 // Controller: PCF8574A (Generic I2C)
 // Locate the controller chip model number on the chip itself.
 var l = new five.LCD({
 controller: "PCF8574A"
 l.useChar("heart"); // use symbol of heart
 l.cursor(0, 0).print("hello world I2C");
 l.cursor(1, 0).print("use PCF8574A :heart:");
 l.cursor(1, 16).blink(); // hide the cursor

// no need to Ctrl+C
 setTimeout(function() {
 }, 3000);

Step 3: run program (Command Prompt) node index.js



Use controller PCF8574A. Cannot use controller PCF8574

nRF24L01 with interrupt on receiver

I copied from Mr.ForceTronics with some modify, you can check these URL for reference:

youtube: https://www.youtube.com/watch?v=vzWcBAWpTx0

code: http://forcetronic.blogspot.com/2016/07/using-nrf24l01s-irq-pin-to-generate.html

You only act on response. That is the reason why interrupt is useful. The rest time you can rest to save time. On transmitter module, I send one byte of random number. On receiver module, I deploy pin IRQ to trigger interrupt on pin 3.


//********************Transmitter code**************************** 
#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/

const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
byte counter = 1; //used to count the packets sent
RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection
const uint64_t pAddress = 0xB00B1E5000LL; // Radio pipe addresses for the 2 nodes to communicate.

void setup() 
 Serial.begin(57600); //start serial to communicate process
 wirelessSPI.begin(); //Start the nRF24 module
 wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload
 wirelessSPI.enableAckPayload(); // Allow optional ack payloads
 wirelessSPI.openWritingPipe(pAddress); // pipe address that we will communicate over, must be the same for each nRF24 module
 wirelessSPI.stopListening(); //transmitter so stop listening for data
 randomSeed(analogRead(0)); //use random ADC value to seed random number algorithm

void loop() {
 delay(random(100,5000)); //Generate delay time between 100msec and 5 sec
 long randNumber = random(0, 255);
 counter = byte(randNumber);
 Serial.println("Sending packet"); 
 if (!wirelessSPI.write( &counter, 1 )){ //if the send fails let the user know over serial monitor
 Serial.println("packet delivery failed"); 


#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <avr/sleep.h> //library needed to use AVR based sleep API

const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
byte gotByte = 0; //used to store payload from transmit module
volatile int count = 0; //tracks the number of interrupts from IRQ
int pCount = 0; //tracks what last count value was so know when count has been updated
RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) 
const uint64_t pAddress = 0xB00B1E5000LL; //Create a pipe addresses for the 2 nodes to communicate over, the "LL" is for LongLong type

void setup() {
 wirelessSPI.begin(); //Start the nRF24 module
 wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload
 wirelessSPI.enableAckPayload(); //allows you to include payload on ack packet
 wirelessSPI.maskIRQ(1,1,0); //mask all IRQ triggers except for receive (1 is mask, 0 is no mask)
 wirelessSPI.setPALevel(RF24_PA_LOW); //Set power level to low, won't work well at higher levels (interfer with receiver)
 wirelessSPI.openReadingPipe(1,pAddress); //open pipe o for recieving meassages with pipe address
 wirelessSPI.startListening(); // Start listening for messages
 attachInterrupt(1, interruptFunction, FALLING); //Create interrupt: 0 for pin 2 or 1 for pin 3, the name of the interrupt function or ISR, and condition to trigger interrupt

void loop() {

if(pCount < count) { //If this is true it means count was interated and another interrupt occurred
 Serial.begin(57600); //start serial to communicate process
 Serial.print("Receive packet number ");
 Serial.print(" has content: ");
 Serial.end(); //have to end serial since it uses interrupts
 pCount = count; 

//This is the function called when the interrupt occurs (pin 2 goes high)
//this is often referred to as the interrupt service routine or ISR
//This cannot take any input arguments or return anything
void interruptFunction() {
 count++; //up the receive counter
 while(wirelessSPI.available()) { //get data sent from transmit
 wirelessSPI.read( &gotByte, 1 ); //read one byte of data and store it in gotByte variable

Actually it is one-part done while we have total 2 modules here. The transmitter has to send the message all the time while the receiver only works when interrupt is triggered. The ideal is when the receiver makes a request to the transmitter, the transmitter then sends the message. The definition of transmitter and receiver is no longer suitable. The new definition is requester and responser.


I am so lazy at this weekend. Maybe nextweek I will be better. I really need a good sleep.