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

index.js


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.clear();
 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() {
 process.exit(0);
 }, 3000);
});

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

 

PS:

Use controller PCF8574A. Cannot use controller PCF8574

Advertisements

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.

 nRF24L01_TX_test_c.ino


//********************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.setPALevel(RF24_PA_LOW);
 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"); 
 }
 Serial.println(); 
}

 nRF24L01_RX_test_c.ino


#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(count); 
 Serial.print(" has content: ");
 Serial.println(gotByte);
 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.

Rotary encoder used as infinite volume knob

I copied this code from Mr.Ralph S Bacon with these URL:
youtube: https://www.youtube.com/watch?v=J9cDEef0IbQ

code: https://dl.dropboxusercontent.com/u/20622434/RotaryEncoderInterrruptsLED.ino

The good thing of this code is using interrupt on pin3. The bad things is that I modified PinLED from pin 11 to pin 13 and it did not change the brightness of the PinLED.

The hardware connection has a note: Vcc –> 3.3V, CLK –> pin3, DT –> pin4, SW –> pin8. The range value will be [0, 100]


// Used for generating interrupts using CLK signal
const int PinA = 3;

// Used for reading DT signal
const int PinB = 4;

// Used for the push button switch
const int PinSW = 8;

// Simple PWM LED pin
#define PinLED 13

// Keep track of last rotary value
int lastCount = 50;

// Updated by the ISR (Interrupt Service Routine)
volatile int virtualPosition = 50;

// ------------------------------------------------------------------
// INTERRUPT INTERRUPT INTERRUPT INTERRUPT INTERRUPT 
// ------------------------------------------------------------------
void isr () {
 static unsigned long lastInterruptTime = 0;
 unsigned long interruptTime = millis();

// If interrupts come faster than 5ms, assume it's a bounce and ignore
 if (interruptTime - lastInterruptTime > 5) {
 if (digitalRead(PinB) == LOW)
 {
 virtualPosition-- ; // Could be -5 or -10
 }
 else {
 virtualPosition++ ; // Could be +5 or +10
 }

// Restrict value from 0 to +100
 virtualPosition = min(100, max(0, virtualPosition));

// Keep track of when we were here last (no more than every 5ms)
 lastInterruptTime = interruptTime;
 }
}

// ------------------------------------------------------------------
// SETUP SETUP SETUP SETUP SETUP SETUP SETUP 
// ------------------------------------------------------------------
void setup() {
 // Just whilst we debug, view output on serial monitor
 Serial.begin(9600);

// Rotary pulses are INPUTs
 pinMode(PinA, INPUT);
 pinMode(PinB, INPUT);
 pinMode(PinLED, OUTPUT);
 analogWrite(PinLED, virtualPosition);

// Switch is floating so use the in-built PULLUP so we don't need a resistor
 pinMode(PinSW, INPUT_PULLUP);

// Attach the routine to service the interrupts
 attachInterrupt(digitalPinToInterrupt(PinA), isr, LOW);

// Ready to go!
 Serial.println("Start");
}

// ------------------------------------------------------------------
// MAIN LOOP MAIN LOOP MAIN LOOP MAIN LOOP MAIN LOOP
// ------------------------------------------------------------------
void loop() {

// Is someone pressing the rotary switch?
 if ((!digitalRead(PinSW))) {
 virtualPosition = 50;
 while (!digitalRead(PinSW))
 delay(10);
 Serial.println("Reset");
 }

// If the current rotary switch position has changed then update everything
 if (virtualPosition != lastCount) {
 
 // Our LED gets brighter or dimmer
 analogWrite(PinLED, virtualPosition);

// Write out to serial monitor the value and direction
 Serial.print(virtualPosition > lastCount ? "Up :" : "Down:");
 Serial.println(virtualPosition);

// Keep track of this new value
 lastCount = virtualPosition ;
 }
}

 

LCD I2C display serial message

This testing module is used to display message received via serial on LCD with I2C driver. The purpose of this module is when you use 2 Arduino boards for broadcasting, one transmitter and one receiver. This module is used by the receiver to test the sending message. And this is the one-step-ahead for module nRF24L01, LoRa, Bluetooth or GSM testing. I give you 2 ways to display: direct display without buffering and buffering display in case you want to modified the message you receive (transfer message to number, interpreting the data, for instance, analog vaule 512 will equal 28ºC and calculating, subtracting offset …)
direct display without buffering


/*
 * Date: Wed, 03/05/2017
 * Desc: Receive message from serial
 * Display to LCD I2c
 * direct display to LCD without buffering
*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x3F 
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
 Serial.begin(9600);
 lcd.begin(16,2);
 lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
 lcd.setBacklight(HIGH);
 lcd.clear();
 lcd.print("Waiting message:");
}

void loop()
{
 // when characters arrive over the serial port...
 if (Serial.available()) {
 // wait a bit for the entire message to arrive
 delay(100);
 // clear the screen
 lcd.clear();
 // read all the available characters
 while (Serial.available() > 0) {
 // display each character to the LCD WITHOUT BUFFERING
 lcd.write(Serial.read());
 }
 }
}

 

buffering display


/*
 * Date: Wed, 03/05/2017
 * Desc: Receive message from serial
 * Display to LCD I2c
 * direct display to LCD with buffering
*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x3F 
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

// used for serial buffering
#define TERM_CHAR '\n'
#define BUF_LEN 16
int i = 0;
char incomingChar; 
char buf[BUF_LEN];

void setup()
{
 Serial.begin(9600);
 lcd.begin(16,2);
 lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
 lcd.setBacklight(HIGH);
 lcd.clear();
 lcd.print("Waiting message:");
}

void loop()
{
 // when characters arrive over the serial port...
 if (Serial.available()) {
 // wait a bit for the entire message to arrive
 delay(100);
 // clean buffer and index
 memset(buf,'\0',BUF_LEN); 
 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;
 else
 break;
 delay(1); // wait for another byte 
 }
 // clear the screen and display message
 lcd.clear();
 lcd.print("Serial message:");
 lcd.setCursor(0,1); // 2nd line 
 lcd.write(buf);
 }
}

you can make the different BUF_LEN (but maximum value will be 128)

I2C device scanner

In case you do not know exactly the address of I2C driver, you can use I2C_scanner to detect the address of device connected to SDA and SCL


/*
 * I2C address scanner
 */
 
#include <Wire.h>
 
void setup()
{
 Wire.begin();
 
 Serial.begin(9600);
 while (!Serial); // Leonardo: wait for serial monitor
 Serial.println("\nI2C Scanner");
}
 
 
void loop()
{
 byte error, address;
 int nDevices;
 
 Serial.println("Scanning...");
 
 nDevices = 0;
 for(address = 1; address < 127; address++ )
 {
 // The i2c_scanner uses the return value of
 // the Write.endTransmisstion to see if
 // a device did acknowledge to the address.
 Wire.beginTransmission(address);
 error = Wire.endTransmission();
 
 if (error == 0)
 {
 Serial.print("I2C device found at address 0x");
 if (address<16)
 Serial.print("0");
 Serial.print(address,HEX);
 Serial.println(" !");
 
 nDevices++;
 }
 else if (error==4)
 {
 Serial.print("Unknown error at address 0x");
 if (address<16)
 Serial.print("0");
 Serial.println(address,HEX);
 } 
 }
 if (nDevices == 0)
 Serial.println("No I2C devices found\n");
 else
 Serial.println("done\n");
 
 delay(5000); // wait 5 seconds for next scan
}

I2C driver for LCD 20×4 and 16×2

Step 0: config hardware, SDA = A4 and SCL = A5

Step 1: go to https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library download .zip file
unzip it and copy the folder inside (inside the folder will have a folder called examples, keywords.txt, file LiquidCrystal_I2C.cpp and file LiquidCrystal_I2C.h) to the Arduino library folder, for example, E:\arduino-1.6.10\libraries

rename the folder as exactly LiquidCrystal_I2C

Step 2: LCD 20x4


/*
 * Date: Sun, 30/04/2017
 * Desc: Testing LCD with I2C driver PCF8754
 * Address = 0x3F, SDA = A4, SCL = A5
*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x3F 
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

int n = 0;

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
 lcd.begin(20,4);
 
// Switch on the backlight
 lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
 lcd.setBacklight(HIGH);
 lcd.home(); // go home, cursor will be at (col,row)=(0,0)

lcd.print("Testing_LCD_20x4_I2C"); 
 lcd.setCursor(0,1); // go to the 2nd line
 lcd.print("libLiquidCrystal_I2C");
 lcd.setCursor(0,2); // go to the third line
 lcd.print("Test By drjquang_eng");
 lcd.setCursor(0,3); // go to the fourth line
 lcd.print("Counting to 100: ");
}

void loop()
{
 n++;
 if(n==100){
 n=0;
 lcd.setCursor(19,3); // go col 19 of line 3 to clear the last digit)
 lcd.print(" ");
 }
 // Backlight on/off every 3 seconds
 lcd.setCursor(18,3); // go col 18 of line 3
 lcd.print(n,DEC);
 lcd.setBacklight(LOW); // Backlight off
 delay(3000);
 lcd.setBacklight(HIGH); // Backlight on
 delay(3000); 
}

Step 3: unplug the LCD I2C driver so that you are be able to upload program to arduino. If you do not unplug, uploading will take forever (I think the LCD consumps too much power so that there is not enough power to erase EEPROM)

Then plug driver, hit RESET.

However, if you use LCD 16x2, you do not need to unplug LCD when uploading code

LCD 16x2


/*
 * Date: Sun, 02/05/2017
 * Desc: Testing LCD with I2C driver PCF8754
 * Address = 0x3F, SDA = A4, SCL = A5
*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x3F 
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

int n = 0;

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
 lcd.begin(16,2);
 
// Switch on the backlight
 lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
 lcd.setBacklight(HIGH);
 lcd.home(); // go home, cursor will be at (col,row)=(0,0)

lcd.print("Testing_LCD_16x2"); 
 lcd.setCursor(0,1); // go to the 2nd line
 lcd.print("Counting 100: ");
}

void loop()
{
 n++;
 if(n==100){
 n=0;
 lcd.setCursor(15,1); // go col 15 of line 1 to clear the last digit
 lcd.print(" ");
 }
 // Backlight on/off every 3 seconds
 lcd.setCursor(14,1); // go col 14 of line 1
 lcd.print(n,DEC);
 lcd.setBacklight(LOW); // Backlight off
 delay(500);
 lcd.setBacklight(HIGH); // Backlight on
 delay(500); 
}

 

Digital potentionmeter X9C104

Why do I use this module? There is a feedback resistor in Op-Amp negative amplifier and it is not handy when you use hand to adjust the feedback resistor so I decide to use digital potentionmeter as an auto-again adjustment (AAA)
Step 0: hardware connection

 pin 2, 3, 4 —> INC, U/D, CS

pin A0 –> RW

2 resistors 100Ω: pull-up at RH and pull-down at RL

Step 1: go to https://sites.google.com/site/tfagerscode/home/digipotx9cxxx download the library and paste it in your Arduino library.

Step 2:


/*
 * Date: Wed, 03/05/2017
 * Desc: Test digital potentionmeter X9C104
 * the initial pot_index will be zero
*/
#include <DigiPotX9Cxxx.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

DigiPot pot(2,3,4);

#define I2C_ADDR 0x3F 
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

char incomingChar;
int pot_index;
const int analogInPin = A0;
int sensorValue = 0; 
void setup() {
 // Return value of pot to zero
 for (int i=0; i<100; i++){ 
 pot.decrease(1);
 delay(10);
 }
 pot_index = 0;
 // Initial device
 Serial.begin(9600);
 Serial.println("Starting ...");

lcd.begin(16,2);
 lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
 lcd.setBacklight(HIGH);
 sensorValue = analogRead(analogInPin);
 display_lcd(pot_index, sensorValue);
 delay(1000);
}

void loop() {
 // receive '1' to increase, '0' to decrease
 if (Serial.available()){
 incomingChar = (char) Serial.read();
 if (incomingChar == '1'){ 
 if(pot_index >= 100){
 Serial.println("Pot index reaches maximum limit.");
 lcd.clear();
 lcd.print("Maximum limit"); 
 }else{
 pot.increase(1);
 pot_index ++;
 sensorValue = analogRead(analogInPin);
 display_lcd(pot_index, sensorValue);
 } 
 }
 else if (incomingChar == '0'){
 if (pot_index <= 0){
 Serial.println("Pot index reaches minimum limit.");
 lcd.clear();
 lcd.print("Minimum limit");
 }else{
 pot.decrease(1);
 pot_index --;
 sensorValue = analogRead(analogInPin);
 display_lcd(pot_index, sensorValue);
 } 
 }else{
 Serial.println("Something wrong happens.");
 lcd.clear();
 lcd.print("Error"); 
 } 
 }
}

void display_lcd(int p_index, int p_value){
 lcd.clear();
 lcd.print("pot index: ");
 lcd.setCursor(11,0);
 lcd.print(p_index);
 lcd.setCursor(0,1);
 lcd.print("pot value: ");
 lcd.setCursor(11,1);
 lcd.print(p_value);
}

There are two definition of pot, pot_index and pot_value. Pot_index will be from 0 to 100. That is the value of X9C104 we control. Pot_value is the analog feedback on pin A, it will be from 0 to 1023. However I added 2 resistor: pull-up resistor at pin RH and pull-down resistor at pin RL so the value of analog A0 will be from 8 to 1008 (or from 4 to 1004 ... ADC still has some tolerance, let's say)

 

Why needs amplifying? Something the signal is too small to determine is the reading value a noise or a real value? You will see the usefulness of digital potentionmeter in my project Dimmer with current feedback.