5. Week 5 - Input & Output device¶
We have tested many input and output devices connected to a microcontroller related to the project.
Day 1 - Addressable RGB
“How to connect Addressable RGB LED”
When the input value is less than 10, a green light will trigger. When the value is between 10 and 50 an orange light will trigger. When the value is greater than 50, a red light will trigger:
LED code¶
#include <FastLED.h>
#define LED_PIN 6
#define NUM_LEDS 11
float LEDNUM;
CRGB leds[NUM_LEDS];
void setup() {
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
}
void loop() {
Serial.println(LEDNUM);
while (Serial.available() == 0) {}
LEDNUM = Serial.parseFloat();
Serial.println(LEDNUM);
if (LEDNUM < 10) {
// Green
fill_solid(leds, NUM_LEDS, CRGB::Green); // For green, set PWM to 0 (assuming a common cathode RGB LED)
}
else if (LEDNUM >= 10 && LEDNUM < 50) {
// Orange
fill_solid(leds, NUM_LEDS, CRGB::Orange); // Adjust brightness for desired orange
}
else if (LEDNUM >= 50 && LEDNUM < 100) {
// Red
fill_solid(leds, NUM_LEDS, CRGB::Red);
}
else {
for (int i = 0; i <= 5; i++) {
leds[i] = CRGB ( 255, 0, 0);
FastLED.show();
delay(100);
for (int i = 0; i <= 5; i++) {
leds[i] = CRGB ( 0, 0, 0);
FastLED.show();
delay(100);
// Red
}
}
}
Serial.println(LEDNUM);
FastLED.show();
}
Day 2 - MQ2 Gas Sensor
When LPG, CO, or Smoke is detected near the sensor, the recorded values will be displayed in the serial monitor. If a monitor is connected, the values will also be displayed there.
MQ2 Sensor¶
#define MQ_PIN (0) //define which analog input channel you are going to use
#define RL_VALUE (5) //define the load resistance on the board, in kilo ohms
#define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO,
//which is derived from the chart in datasheet
/**********************Software Related Macros***********************************/
#define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase
#define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interal(in milisecond) between each samples in the
//cablibration phase
#define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation
#define READ_SAMPLE_TIMES (5) //define the time interal(in milisecond) between each samples in
#include <LiquidCrystal.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h> //adding the libary of the LCD I2C
LiquidCrystal_I2C lcd(0x27, 16, 2); //stating the address and size of the lcd
/*********************Application Related Macros*********************************/
#define GAS_LPG (0)
#define GAS_CO (1)
#define GAS_SMOKE (2)
/****************************Globals**********************************************/
float LPGCurve[3] = {2.3,0.21,-0.47}; //two points are taken from the curve.
//with these two points, a line is formed which is "approximately equivalent"
//to the original curve.
//data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59)
float COCurve[3] = {2.3,0.72,-0.34}; //two points are taken from the curve.
//with these two points, a line is formed which is "approximately equivalent"
//to the original curve.
//data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000, 0.15)
float SmokeCurve[3] ={2.3,0.53,-0.44}; //two points are taken from the curve.
//with these two points, a line is formed which is "approximately equivalent"
//to the original curve.
//data format:{ x, y, slope}; point1: (lg200, 0.53), point2: (lg10000, -0.22)
float Ro = 10; //Ro is initialized to 10 kilo ohms
void setup()
{
Serial.begin(9600); //UART setup, baudrate = 9600bps
lcd.init(); //Initializing the lcd
lcd.backlight(); //starting the backlight of the lcd
Serial.print("Calibrating...\
");
Ro = MQCalibration(MQ_PIN); //Calibrating the sensor. Please make sure the sensor is in clean air
lcd.begin(16, 2); //when you perform the calibration
Serial.print("Calibration is done...\
");
Serial.print("Ro=");
Serial.print(Ro);
Serial.print("kohm");
Serial.print("\
");
lcd.print("Calibration is done...\
");
lcd.print("Ro=");
lcd.print(Ro);
lcd.print("kohm");
lcd.print("\
");
}
void loop()
{
Serial.print("LPG:");
Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_LPG) );
Serial.print( "ppm" );
Serial.print(" ");
Serial.print("CO:");
Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_CO) );
Serial.print( "ppm" );
Serial.print(" ");
Serial.print("SMOKE:");
Serial.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_SMOKE) );
Serial.print( "ppm" );
Serial.print("\n");
lcd.setCursor(0, 0);
lcd.print("LPG:");
lcd.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_LPG) );
//lcd.print( "ppm" );
lcd.print(" ");
lcd.setCursor(9, 0);
lcd.print("CO:");
lcd.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_CO) );
//lcd.print( "ppm" );
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("SMOKE:");
lcd.print(MQGetGasPercentage(MQRead(MQ_PIN)/Ro,GAS_SMOKE) );
//lcd.print( "ppm" );
lcd.print(" ");
delay(200);
}
/**************** MQResistanceCalculation **************************************
Input: raw_adc - raw value read from adc, which represents the voltage
Output: the calculated sensor resistance
Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
across the load resistor and its resistance, the resistance of the sensor
could be derived.
**********************************************************************************/
float MQResistanceCalculation(int raw_adc)
{
return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc));
}
/*************************** MQCalibration **************************************
Input: mq_pin - analog channel
Output: Ro of the sensor
Remarks: This function assumes that the sensor is in clean air. It use
MQResistanceCalculation to calculates the sensor resistance in clean air
and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about
10, which differs slightly between different sensors.
**********************************************************************************/
float MQCalibration(int mq_pin)
{
int i;
float val=0;
for (i=0;i<CALIBARAION_SAMPLE_TIMES;i++) { //take multiple samples
val += MQResistanceCalculation(analogRead(mq_pin));
delay(CALIBRATION_SAMPLE_INTERVAL);
}
val = val/CALIBARAION_SAMPLE_TIMES; //calculate the average value
val = val/RO_CLEAN_AIR_FACTOR; //divided by RO_CLEAN_AIR_FACTOR yields the Ro
//according to the chart in the datasheet
return val;
}
/*************************** MQRead *******************************************
Input: mq_pin - analog channel
Output: Rs of the sensor
Remarks: This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
The Rs changes as the sensor is in the different consentration of the target
gas. The sample times and the time interval between samples could be configured
by changing the definition of the macros.
**********************************************************************************/
float MQRead(int mq_pin)
{
int i;
float rs=0;
for (i=0;i<READ_SAMPLE_TIMES;i++) {
rs += MQResistanceCalculation(analogRead(mq_pin));
delay(READ_SAMPLE_INTERVAL);
}
rs = rs/READ_SAMPLE_TIMES;
return rs;
}
/*************************** MQGetGasPercentage ********************************
Input: rs_ro_ratio - Rs divided by Ro
gas_id - target gas type
Output: ppm of the target gas
Remarks: This function passes different curves to the MQGetPercentage function which
calculates the ppm (parts per million) of the target gas.
**********************************************************************************/
int MQGetGasPercentage(float rs_ro_ratio, int gas_id)
{
if ( gas_id == GAS_LPG ) {
return MQGetPercentage(rs_ro_ratio,LPGCurve);
} else if ( gas_id == GAS_CO ) {
return MQGetPercentage(rs_ro_ratio,COCurve);
} else if ( gas_id == GAS_SMOKE ) {
return MQGetPercentage(rs_ro_ratio,SmokeCurve);
}
return 0;
}
/*************************** MQGetPercentage ********************************
Input: rs_ro_ratio - Rs divided by Ro
pcurve - pointer to the curve of the target gas
Output: ppm of the target gas
Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm)
of the line could be derived if y(rs_ro_ratio) is provided. As it is a
logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic
value.
**********************************************************************************/
int MQGetPercentage(float rs_ro_ratio, float *pcurve)
{
return (pow(10,( ((log(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
}
Day 3 - Buzzer
“How to connect Buzzer to Nano”
The buzzer will let out a tone of the programmed melody based on the notes.
HW-508 (Buzzer) code¶
// -------------------------------------------------
// Copyright (c) 2022 HiBit <https://www.hibit.dev>
// -------------------------------------------------
#include "pitches.h"
#define BUZZER_PIN 2
int melody[] = {
NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4,
NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4,
NOTE_G4, NOTE_C4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_E4, NOTE_F4,
NOTE_G4, NOTE_C4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_E4, NOTE_F4,
NOTE_G4, NOTE_C4,
NOTE_DS4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4,
NOTE_D4,
NOTE_F4, NOTE_AS3,
NOTE_DS4, NOTE_D4, NOTE_F4, NOTE_AS3,
NOTE_DS4, NOTE_D4, NOTE_C4,
NOTE_G4, NOTE_C4,
NOTE_DS4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4,
NOTE_D4,
NOTE_F4, NOTE_AS3,
NOTE_DS4, NOTE_D4, NOTE_F4, NOTE_AS3,
NOTE_DS4, NOTE_D4, NOTE_C4,
NOTE_G4, NOTE_C4,
NOTE_DS4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4,
NOTE_D4,
NOTE_F4, NOTE_AS3,
NOTE_D4, NOTE_DS4, NOTE_D4, NOTE_AS3,
NOTE_C4,
NOTE_C5,
NOTE_AS4,
NOTE_C4,
NOTE_G4,
NOTE_DS4,
NOTE_DS4, NOTE_F4,
NOTE_G4,
NOTE_C5,
NOTE_AS4,
NOTE_C4,
NOTE_G4,
NOTE_DS4,
NOTE_DS4, NOTE_D4,
NOTE_C5, NOTE_G4, NOTE_GS4, NOTE_AS4, NOTE_C5, NOTE_G4, NOTE_GS4, NOTE_AS4,
NOTE_C5, NOTE_G4, NOTE_GS4, NOTE_AS4, NOTE_C5, NOTE_G4, NOTE_GS4, NOTE_AS4,
NOTE_GS5, NOTE_AS5, NOTE_C6, NOTE_G5, NOTE_GS5, NOTE_AS5,
NOTE_C6, NOTE_G5, NOTE_GS5, NOTE_AS5, NOTE_C6, NOTE_G5, NOTE_GS5, NOTE_AS5
};
int durations[] = {
8, 8, 16, 16, 8, 8, 16, 16,
8, 8, 16, 16, 8, 8, 16, 16,
8, 8, 16, 16, 8, 8, 16, 16,
8, 8, 16, 16, 8, 8, 16, 16,
4, 4,
16, 16, 4, 4, 16, 16,
1,
4, 4,
16, 16, 4, 4,
16, 16, 1,
4, 4,
16, 16, 4, 4, 16, 16,
1,
4, 4,
16, 16, 4, 4,
16, 16, 1,
4, 4,
16, 16, 4, 4, 16, 16,
2,
4, 4,
8, 8, 8, 8,
1,
2,
2,
2,
2,
2,
4, 4,
1,
2,
2,
2,
2,
2,
4, 4,
8, 8, 16, 16, 8, 8, 16, 16,
8, 8, 16, 16, 8, 8, 16, 16,
4, 16, 16, 8, 8, 16, 16,
8, 16, 16, 16, 8, 8, 16, 16
};
void setup()
{
pinMode(BUZZER_PIN, OUTPUT);
}
void loop()
{
int size = sizeof(durations) / sizeof(int);
for (int note = 0; note < size; note++) {
//to calculate the note duration, take one second divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int duration = 2000 / durations[note];
tone(BUZZER_PIN, melody[note], duration);
//to distinguish the notes, set a minimum time between them.
//the note's duration + 30% seems to work well:
int pauseBetweenNotes = duration * 1.30;
delay(pauseBetweenNotes);
//stop the tone playing:
noTone(BUZZER_PIN);
}
}
Day 4 - Screen Panel
“How to connect I2C with 1602A QAPASS”
“How to connect I2C with 1602A QAPASS for Nano”
The screen will display the content that is mentioned in the code.
1602A QAPASS (Screen/Panel) code¶
//#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup()
{
Serial.begin(9600);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
{
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
// Draw a single pixel in white
display.drawPixel(10, 10, SSD1306_WHITE);
// Show the display buffer on the screen. You MUST call display() after
// drawing commands to make them visible on screen!
display.display();
delay(2000);
testdrawstyles(); // Draw 'stylized' characters
}
void loop()
{
}
void testdrawstyles(void)
{
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.println(F("FABLAB test"));
display.setTextColor(SSD1306_WHITE);
//(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
display.println("is a"); //6-digit after decimal point
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(SSD1306_WHITE);
display.print(F("Success!"));
display.display();
delay(2000);
}
Personal Notes
- Arduino IDE is difficult to learn at first but with enough practice and research it gets slightly easier to use.
- TinkerCAD is in amazing online simulator for testing out different microcontrollers and connections without requiring the physical devices.
- Using AI to generate Arduino IDE code makes life much easier. However, if you do not understand the basic code requirements, you will have a hard time debugging and adjusting the code to satisfy your requirements.
Last update:
September 15, 2024