/*
file: prg355_lab2.ino
author: student name
student #: 123456789
email-id: [email protected]
date: today's date
purpose: solution to PRG355 lab #2 using C++ structs, and
ESP32-S3, LCD1602, RGB LED, and Potentiometer.
*/
#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <cstdint>
#include <cstring>
/*
uint8_t is a data type defined in the C and C++ programming languages,
specifically within the <stdint.h> header file.
It represents an unsigned integer that is exactly 8 bits wide.
Range:
As an 8-bit unsigned integer, uint8_t can store values from 0 to 255
(2^8 - 1).
Memory Efficiency:
It's often used when memory is a concern, as it represents the smallest
addressable unit of data (a byte) and avoids allocating more memory
than necessary.
Bitwise Operations:
uint8_t is frequently employed in situations where bit-level
manipulation is required, such as working with hardware registers,
network protocols, or image processing, where data is often
structured in bytes.
*/
#define COLS 16
#define ROWS 2
class Lab2Circuit {
private: // Lab2Circuit encapulates an RGB LED, a Potentiometer, and a LCD1602 LCD panel
uint8_t red_pin, green_pin, blue_pin, pot_pin;
LiquidCrystal_I2C *lcd; // pointer to LiquidCrystal_I2C object
public:
Lab2Circuit(uint8_t r, uint8_t g, uint8_t b, uint8_t pot, LiquidCrystal_I2C *ptr_lcd); // 5 parameter constructor
void set_color(uint8_t r, uint8_t g, uint8_t b);
void refresh_lcd(void);
int read_potentiometer(void);
void set_cursor(uint8_t r, uint8_t c);
void print(const char *s);
// student functions to complete:
void scroll_message(const char* message, int row, int d);
};
// Lab2Circuit public: member functions
Lab2Circuit::Lab2Circuit(uint8_t r, uint8_t g, uint8_t b, uint8_t pot, LiquidCrystal_I2C *ptr_lcd) {
red_pin = r; // assign GPIO pin values from ESP32-S3 to internal unit8_t variables
green_pin = g;
blue_pin = b;
pot_pin = pot;
lcd = ptr_lcd; // assign pointer to address of actual LCD object in memory
lcd->init(); // and initialize LCD screen
lcd->backlight();
lcd->clear();
lcd->setCursor(0, 0); // writing to line 1 of LCD screen
lcd->print("RGB LED Lab #2");
pinMode(red_pin, OUTPUT); // initialize LED's for OUTPUT
pinMode(green_pin, OUTPUT);
pinMode(blue_pin, OUTPUT);
// turn off initially
set_color(0, 0, 0);
}
void Lab2Circuit::refresh_lcd(void) {
int i;
for(i = 0; i < COLS; i++) {
lcd->setCursor(i, 0); // row 1
lcd->write(' ');
lcd->setCursor(i, 1); // row 2
lcd->write(' ');
}
}
void Lab2Circuit::set_color(uint8_t r, uint8_t g, uint8_t b) {
analogWrite(red_pin, 255 - r); // common RGB anode logic: invert values
analogWrite(green_pin, 255 - g);
analogWrite(blue_pin, 255 - b);
}
// read and return value obtained from potentiometer
int Lab2Circuit::read_potentiometer(void) {
return analogRead(pot_pin);
}
void Lab2Circuit::set_cursor(uint8_t r, uint8_t c) {
lcd->setCursor(c, r);
}
void Lab2Circuit::print(const char *s) {
int i;
for(i=0; s[i] != '\0' && i < COLS; i++) {
lcd->write(s[i]);
}
}
// global variables
// GPIO pin values used on ESP32-S3
const uint8_t pot_pin = 1; // potentiometer input
const uint8_t red = 10;
const uint8_t green = 7;
const uint8_t blue = 4;
LiquidCrystal_I2C lcd(0x27, 16, 2);
Lab2Circuit* lab2; // pointer, not constructed yet
void setup() {
Serial.begin(115200);
Wire.begin(8, 9); // SDA=GPIO8, SCL=GPIO9 for ESP32-S3-DevKitC-1
delay(1000); // give time for power-up
// instantiate (create) an Lab2Circuit object in memory named 'lab2'
// creating this object will automatically invoke the constructor to perform
// initializations of the LCD panel, assign pin numbers, and startup
// functions required by the hardware components.
lab2 = new Lab2Circuit(red, green, blue, pot_pin, &lcd);
/*
creating the Lab2Circuit object in memory using new (dynamic memory) is required with
an Arduino sketch because the object must be declared in the setup() so that
hardware can be fully initialized. The declaration of the pointer (outside of setup())
is required to allow visibilty (scope) for the loop() otherwise a local instance
of the class would be accessible in loop().
*/
}
/*
The loop( ) function in the Arduino programming environment is the
continuously running part of every sketch where your program's primary
operations are performed. After the setup( ) function executes once,
the microcontroller repeatedly runs the code within the loop()'s curly
braces, executing instructions line by line before jumping back to the
start of the loop() to repeat. This allows your Arduino to handle
dynamic tasks like reading sensors, controlling actuators, or
responding to input in a perpetual cycle.
*/
void loop() {
int pot_value = lab2->read_potentiometer(); // read potentiometer (0 to 4095 on ESP32-S3)
int section = pot_value / 1365; // divide into 3 sections (0–2)
lab2->set_cursor(1, 0); // set LCD cursor position to row 1 column 0
if(section == 0) {
lab2->set_color(255, 0, 0); // red
lab2->scroll_message("RED", 1, 500);
}
else if(section == 1) {
lab2->set_color(0, 255, 0); // green
lab2->scroll_message("GREEN", 1, 500);
}
else {
lab2->set_color(0, 0, 255); // blue
lab2->scroll_message("BLUE", 1, 500);
}
delay(200); // pause for 200 milliseconds
}