Object Oriented Aquarium using the Arduino and Raspberry Pi

From Sensors in Schools
Jump to navigation Jump to search

What is Object-Oriented Programming

Object-Oriented Programming (OOP) is a programming paradigm or methodology that organizes and structures code around the concept of "objects." Objects are instances of classes, which are templates or blueprints for creating objects. OOP is a way of designing and modeling software that emphasizes the following key principles:

  • Objects: Objects are the fundamental building blocks of OOP. Each object represents a real-world entity, concept, or thing and encapsulates both data (attributes or properties) and behavior (methods or functions) related to that entity. For example, you could have objects representing cars, employees, bank accounts, or any other concept in your program.
  • Classes: A class is a blueprint or template for creating objects of a specific type. It defines the structure and behavior of objects of that class. The class specifies what data an object will have (attributes) and what operations it can perform (methods). Objects are created based on classes.
  • Encapsulation: Encapsulation is the practice of bundling data (attributes) and methods (functions) that operate on that data within a single unit, the object. It provides a way to control access to the internal state of an object and ensures that the object's data is accessed and modified through well-defined interfaces (methods).
  • Inheritance: Inheritance allows you to create a new class (a subclass or derived class) based on an existing class (a superclass or base class). The subclass inherits the attributes and methods of the superclass and can also add its own attributes and methods or override existing ones. Inheritance promotes code reuse and the creation of hierarchical relationships between classes.
  • Polymorphism: Polymorphism means "many forms." It allows objects of different classes to be treated as objects of a common superclass through a shared interface. This enables flexibility and extensibility in code. Polymorphism is often achieved through method overriding and interfaces in languages like Java and C#.
  • Abstraction: Abstraction is the process of simplifying complex systems by breaking them down into smaller, more manageable parts. In OOP, classes and objects provide a level of abstraction that hides the internal implementation details while exposing a well-defined interface.

OOP promotes modular and organized code, making it easier to design, understand, maintain, and extend software systems. It is widely used in various programming languages, including Java, C++, Python, C#, and many others. OOP is particularly suitable for modeling real-world systems and relationships between entities, making it a valuable approach for software development in a wide range of domains.

Example of Object-Oriented Programming on the Arduino

Here's a simple example of Object-Oriented Programming (OOP) on an Arduino using LEDs. In this example, we'll create a basic LED class to represent individual LEDs, allowing you to control their state (on/off) and behavior independently.

// Define the LED class
class LED {
  private:
    int pin; // The Arduino pin connected to the LED
    bool isOn; // LED state (on or off)

  public:
    // Constructor: Initialize the LED with a specific pin
    LED(int pin) {
      this->pin = pin;
      isOn = false; // Default state is off
      pinMode(pin, OUTPUT); // Set the pin as an output
    }

    // Method to turn the LED on
    void turnOn() {
      digitalWrite(pin, HIGH); // Set the pin voltage to HIGH
      isOn = true; // Update the state
    }

    // Method to turn the LED off
    void turnOff() {
      digitalWrite(pin, LOW); // Set the pin voltage to LOW
      isOn = false; // Update the state
    }

    // Method to toggle the LED state (on/off)
    void toggle() {
      if (isOn) {
        turnOff();
      } else {
        turnOn();
      }
    }
};
 
// Create instances of the LED class for two LEDs
LED led1(2); // Connect LED 1 to digital pin 2
LED led2(3); // Connect LED 2 to digital pin 3

void setup() {
  // No setup required in this example
}

void loop() {
  // Toggle the state of LED 1 every second
  led1.toggle();
  delay(1000);

  // Toggle the state of LED 2 every 500 milliseconds
  led2.toggle();
  delay(500);
}

In this example:

  • We define an LED class with private member variables pin and isOn, and public member functions (methods) to control the LED's state (turnOn, turnOff, and toggle).
  • The constructor LED(int pin) initializes an LED object with a specific Arduino pin, sets its initial state to off, and configures the pin as an output.
  • We create two instances of the LED class: led1 and led2, representing two LEDs connected to different digital pins.
  • In the setup() function, we don't need any setup in this example.
  • In the loop() function, we toggle the state of led1 every second and the state of led2 every 500 milliseconds, creating a simple alternating LED pattern.

This example demonstrates the principles of OOP by encapsulating the LED behavior within a class. Each LED object maintains its own state, and you can control and manipulate them independently, making it a scalable and organized approach for working with multiple LEDs or other components.

Object Oriented Example from SARCNET

Separate Arduino Library Files

Creating a separate library file in Arduino C++ allows you to encapsulate code and functionality into reusable libraries. Here, I'll provide a step-by-step guide to creating a custom Arduino library for controlling LEDs. We'll create a library called "LEDControl" that includes a simple example of turning an LED on and off.

Step 1: Create the Library Folder or create new Tabs in the Arduino IDE

Navigate to your Arduino Sketchbook directory. This is typically located in your Documents folder. Inside the Sketchbook directory, create a new folder called "libraries" if it doesn't already exist.

Step 2: Create the Library

Inside the "libraries" folder, create a new folder named "LEDControl." This folder will contain your custom library.

Step 3: Create the Library Files

Inside the "LEDControl" folder, create the following files:

LEDControl.h: This is the header file for your library, where you declare your class and its methods. LEDControl.cpp: This is the implementation file, where you define the actual functionality of your class.

Step 4: Write the Library Code

Here's an example of what your LEDControl.h and LEDControl.cpp files might look like:

LEDControl.h:

#ifndef LEDControl_h
#define LEDControl_h

#include <Arduino.h>

class LEDControl {
  private:
    int pin;
    bool isOn;

  public:
    LEDControl(int pin);
    void turnOn();
    void turnOff();
    void toggle();
};

#endif

LEDControl.cpp:

#include "LEDControl.h"

LEDControl::LEDControl(int pin) {
  this->pin = pin;
  isOn = false;
  pinMode(pin, OUTPUT);
}

void LEDControl::turnOn() {
  digitalWrite(pin, HIGH);
  isOn = true;
}

void LEDControl::turnOff() {
  digitalWrite(pin, LOW);
  isOn = false;
}

void LEDControl::toggle() {
  if (isOn) {
    turnOff();
  } else {
    turnOn();
  }
}

Step 5: Use the Library in Your Arduino Sketch

Now that you've created the library, you can use it in your Arduino sketch. Here's a simple example of how to use the "LEDControl" library to control an LED:

#include <LEDControl.h>

// Create an LEDControl object for an LED connected to digital pin 13
LEDControl led(13);

void setup() {
  // No setup needed in this example
}

void loop() {
  // Toggle the LED state every second
  led.toggle();
  delay(1000);
}


Step 6: Upload and Run Your Sketch

Upload your sketch to your Arduino board as you normally would. The "LEDControl" library will handle the LED control, making your code more modular and organized.

This example demonstrates how to create a custom Arduino library for LED control, but you can extend this approach to create libraries for other components and functionality, making your Arduino projects more maintainable and reusable.

this->pin = pin;

In the code snippet this->pin = pin;, this refers to the current instance of the class, and it's used to differentiate between a class member variable and a function parameter or a local variable that share the same name. This is often necessary when there is a naming conflict.

Here's a breakdown of the code:

  • this is a pointer to the current instance of the class. It allows you to access the class's member variables and methods within class methods.
  • pin on the left side of the assignment (this->pin) is a member variable of the class. It represents an attribute of the class that can hold data.
  • pin on the right side of the assignment (= pin) is a function parameter. It represents the value that's passed to the constructor when creating an instance of the class.

So, in the context of the code you provided:

LEDControl::LEDControl(int pin) {
  this->pin = pin;
  // ...
}
  • this->pin assigns the value of the pin parameter passed to the constructor to the pin member variable of the LEDControl class. This is done to initialize the class's pin with the value provided when creating an instance of the class.
  • By using this->pin, you're specifying that you want to assign the value to the class member variable pin rather than just assigning it to the local variable pin. This disambiguates which variable you're referring to, especially in cases where the parameter name and member variable name have the same name, as in this example.

Object-Oriented code for the Arduino reading a Potentiometer

Below is an example of an object-oriented Arduino sketch that reads the value of a potentiometer using an analog pin and converts it to a value between 4.0 and 9.0. In this example, we'll create a class called PotentiometerReader to encapsulate this functionality:

// Define the PotentiometerReader class
class PotentiometerReader {
private:
  int analogPin;
  float minValue;
  float maxValue;

public:
  // Constructor: Initialize the PotentiometerReader with an analog pin,
  // minimum value, and maximum value
  PotentiometerReader(int pin, float minVal, float maxVal) {
    analogPin = pin;
    minValue = minVal;
    maxValue = maxVal;
    pinMode(analogPin, INPUT);
  }

  // Read the potentiometer value and convert it to a range between 4.0 and 9.0
  float readValue() {
    int rawValue = analogRead(analogPin); // Read analog value (0-1023)
    float scaledValue = map(rawValue, 0, 1023, minValue * 100, maxValue * 100) / 100.0; // Map to range
    return scaledValue;
  }
};

// Create an instance of the PotentiometerReader class
PotentiometerReader potReader(A0, 4.0, 9.0); // Connect potentiometer to analog pin A0

void setup() {
  Serial.begin(9600); // Initialize serial communication
}

void loop() {
  // Read and print the potentiometer value
  float potValue = potReader.readValue();
  Serial.println("Potentiometer Value: " + String(potValue, 2)); // Print with 2 decimal places
  delay(500); // Delay for stability
}

In this code:

  • We define a PotentiometerReader class with private member variables analogPin (the analog pin connected to the potentiometer), minValue (the minimum value in the desired range), and maxValue (the maximum value in the desired range).
  • The constructor PotentiometerReader(int pin, float minVal, float maxVal) initializes the class with the specified analog pin and value range, and it configures the analog pin as an input.
  • The readValue() method reads the analog value from the potentiometer using analogRead(), maps it from the full range of 0-1023 to the desired range between minValue and maxValue, and returns the scaled value.
  • In the setup() function, we initialize serial communication to display the potentiometer value on the serial monitor.
  • In the loop() function, we continuously read and print the potentiometer value, delaying for stability.

Make sure to connect a potentiometer to analog pin A0 or adjust the analogPin parameter accordingly. This code will read the potentiometer value, map it to the specified range, and display it on the serial monitor.