Physical Computing—Tino Selic



Code


Here you can find all my code from the assignments and my final project.



1. Introduction

Posted 25.10.22

Arduino Code

          
// A button that first turns on one LED, then another, and then turns both LEDs off again
int btnPin = 2;
int ledPin1 = 9;
int ledPin2 = 11;
int btnState = false;
int prevBtnState = false;
int counter = 0;
void setup() {
  // Open the serial port
  Serial.begin(9600);
  // set the button pin to be an input
  pinMode(btnPin, INPUT);
  // set the LED pin to be an output
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
}
void loop() {
  // read the button pin
  btnState = digitalRead(btnPin);
  // State 0
  if (btnState != prevBtnState && counter == 0) {
    // button pressed down
    if (btnState == HIGH) {
      Serial.println("Light 1 on, Light 2 off");
      digitalWrite(ledPin1, HIGH);
      digitalWrite(ledPin2, LOW);
    }
    // button released
    if (btnState == LOW) {
      counter++;
      Serial.print("Count: ");
      Serial.println(counter);
    }
    prevBtnState = btnState;
  }
  // State 1
  if (btnState != prevBtnState && counter == 1) {
    // button pressed down
    if (btnState == HIGH) {
      Serial.println("Light 1 on, Light 2 on");
      digitalWrite(ledPin1, HIGH);
      digitalWrite(ledPin2, HIGH);
    }
    // button released
    if (btnState == LOW) {
      counter++;
      Serial.print("Count: ");
      Serial.println(counter);
    }
    prevBtnState = btnState;
  }
  // State 2
  if (btnState != prevBtnState && counter == 2) {
    // button pressed down
    if (btnState == HIGH) {
      Serial.println("Light 1 off, Light 2 off");
      digitalWrite(ledPin1, LOW);
      digitalWrite(ledPin2, LOW);
    }
    // button released
    if (btnState == LOW) {
      counter = 0;
      Serial.print("Count: ");
      Serial.println(counter);
    }
    prevBtnState = btnState;
  }
}
          
        

4. Communication

Posted 21.11.22

Arduino Code

          
// For handling software debouncing of mechanical switch contacts
#include <Bounce2.h>

// Pins
int idlePin = 2;  // Handset
int dialPin = 9;  // Rotary-dial

// States
int state = 0;

// How many pulses have been detected for the dialled digit
int pulseCount = 0;

// Bounce objects
Bounce idleSwitch = Bounce();
Bounce dialSwitch = Bounce();

void setup() {
  // Open the serial port
  Serial.begin(9600);
  // Declare pin inputs and attach debounce ojects
  pinMode(idlePin, INPUT);
  idleSwitch.attach(idlePin);
  idleSwitch.interval(5);
  pinMode(dialPin, INPUT);
  dialSwitch.attach(dialPin);
  dialSwitch.interval(5);
}

void loop() {
  // Read the current state of all switches
  idleSwitch.update();
  dialSwitch.update();

  switch (state) {
    // Idle
    case 0:
      if (idleSwitch.rose()) {
        state = 1;
      }
      break;

    // Dial
    case 1:
      if (dialSwitch.rose()) {
        pulseCount++;
        // The digit 0 has 10 pulses
        if (pulseCount == 10) {
          pulseCount = 0;
        }
      }
      break;
  }

  // If the handset is placed on the telephone, the telephone becomes idle (no matter when)
  if (idleSwitch.fell()) {
    Serial.println(pulseCount);
    state = 0;
    pulseCount = 0;
  }
}
          
        

Processing Code

          
import processing.serial.*;

Serial myPort;  // Create object from Serial class
String str;      // Data received from the serial port

void setup()
{
  size(500, 500);
  // Open whatever port is the one you're using.
  // Change the 0 in  Serial.list()[0] to the correct device
  printArray(Serial.list());
  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 9600);
}

void draw()
{
  if ( myPort.available() > 0) { // If data is available,
    str = myPort.readStringUntil('\n'); // read it and store it in str
    if (str != null) {
      println(str);
      myPort.clear();
      background(0);
      fill(255);
      textSize(400);
      textAlign(CENTER, CENTER);
      text(str, width/2, height);
    }
  }
}
        
      

7. Final Project

Posted 11.10.22

Arduino Code

          
// Physical Computing Final Project by Tino Selic

// For handling software debouncing of mechanical switch contacts
#include < Bounce2.h>

// Libraries for RTC
#include < Wire.h>
#include < RTClib.h>

// Libraries for the OLED display
#include < Adafruit_GFX.h>
#include < Adafruit_SSD1306.h>

// Define an object of RTC_DS1307 class
RTC_DS1307 RTC;

// Storing the current time in a variable
//DateTime now = RTC.now();

// OLED display width and height in pixels
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#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);

// Maximum number of digits that can be dialled (Time: hh:mm = 4), must be static
#define MAXDIGITS 4

// Pins
int idlePin = 11;    // Handset
int dialPin = 12;    // Control of rotary-dial
int numberPin = 13;  // Number dial
int ringerPins[] = { 3, 4 };

// Modes
int mode = 0;

// How many pulses have been detected for the dialled number
int pulseCount = 0;

// The dialled number
int number = 0;

// Alarm variables
bool alarmValid = false;   // Check for correctly dialled time format
bool alarmActive = false;  // State of the alarm (on/off)
int alarmDigit = 0;
int alarmTime[4];
int alarmHours;
int alarmMinutes;

// Bounce objects
Bounce idleSwitch = Bounce();
Bounce dialSwitch = Bounce();
Bounce numberSwitch = Bounce();

void setup() {
  // Open the serial port
  Serial.begin(9600);
  // Print out test
  Serial.println(__FILE__);
  Serial.println("Compiled: "__DATE__", "__TIME__);
  // Declare pin inputs and attach debounce ojects
  pinMode(idlePin, INPUT_PULLUP);
  idleSwitch.attach(idlePin);
  idleSwitch.interval(5);

  pinMode(dialPin, INPUT_PULLUP);
  dialSwitch.attach(dialPin);
  dialSwitch.interval(5);

  pinMode(numberPin, INPUT_PULLUP);
  numberSwitch.attach(numberPin);
  numberSwitch.interval(5);

  // Set the current time and write it to the RTC chip
  RTC.begin();
  RTC.adjust(DateTime(__DATE__, __TIME__));
  if (!RTC.isrunning()) {
    Serial.println("RTC is NOT running!");
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }

  // 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();
}

void loop() {
  // Read the current state of all switches
  idleSwitch.update();
  dialSwitch.update();
  numberSwitch.update();

  // Print out the state of the handset
  if (idleSwitch.rose()) {
    Serial.println("Handset lifted");
  }
  if (idleSwitch.fell()) {
    Serial.println("Handset placed");
  }

  switch (mode) {
    // Idle
    case 0:
      // Display
      if (dialSwitch.read() == HIGH) {
        drawHome();
      }
      // Time-alarm comparison
      if (alarmActive && RTC.now().hour() == alarmHours && RTC.now().minute() == alarmMinutes && RTC.now().second() == 0) {
        Serial.println("Alarm ringing");
        mode = 10;
      }
      dialling();
      // Switch to alarm mode by lifting the handset
      if (idleSwitch.rose()) {
        Serial.println("Alarm Mode");
        mode = 1;
      }
      break;

    // Alarm Mode
    case 1:
      number = 0;
      // Display
      if (dialSwitch.read() == HIGH) {
        drawAlarm();
      }
      // Dialling
      if (numberSwitch.rose()) {
        pulseCount++;
      }
      // Check whether the dial has returned all the way
      if (dialSwitch.rose()) {
        // The number 0 has 10 pulses
        if (pulseCount == 10) {
          pulseCount = 0;
        }
        number = pulseCount;
        pulseCount = 0;

        alarmTime[alarmDigit] = number;
        Serial.println("Set alarm in 24h-format: ");
        Serial.println(alarmTime[alarmDigit]);
        alarmDigit++;
        // Check if the dialled time is in the correct 24-hour notation (hh:mm)
        if (alarmTime[0] > 2 || alarmTime[0] == 2 && alarmTime[1] > 3 || alarmTime[2] > 5) {
          Serial.println("Invalid time. Please use the 24h-format: hh:mm");
          display.clearDisplay();
          display.setTextSize(1);
          display.setTextColor(SSD1306_WHITE);
          display.setCursor(0, 0);
          display.println("ERROR: INVALID TIME");
          display.println("Please use the");
          display.println("24-hour notation:");
          display.println("hh:mm");
          display.display();
          alarmDigit = 0;
          alarmTime[0] = 0;
          alarmTime[1] = 0;
          alarmTime[2] = 0;
          alarmTime[3] = 0;
          alarmValid = false;
          delay(5000);
          display.clearDisplay();
        } else {
          alarmValid = true;
        }
        if (alarmDigit > 3) {
          alarmDigit = 0;
        }
      }
      // Confirm the alarm by placing the handset back on the telephone
      if (idleSwitch.fell()) {
        if (alarmValid) {
          alarmHours = alarmTime[0] * 10 + alarmTime[1];
          alarmMinutes = alarmTime[2] * 10 + alarmTime[3];
          alarmActive = true;
          alarmDigit = 0;
          Serial.print("Alarm set at: ");
          Serial.print(alarmHours);
          Serial.print(":");
          Serial.println(alarmMinutes);
          Serial.println("Idle");
          mode = 0;
        } else {
          Serial.println("Idle");
          alarmActive = false;
          alarmDigit = 0;
          alarmTime[0] = 0;
          alarmTime[1] = 0;
          alarmTime[2] = 0;
          alarmTime[3] = 0;
          mode = 0;
        }
      }
      break;

    // Alarm ringing
    case 10:
      // Display
      if (dialSwitch.read() == HIGH) {
        drawHome();
      }
      number = 0;
      // Ringtone: ringing, 1 x 0.4 seconds pause
      while (alarmActive) {
        for (int i = 0; i < 20; i++) {
          // Check if the handset is lifted to end the ringing
          idleSwitch.update();
          if (idleSwitch.rose()) {
            alarmActive = false;
            Serial.print("Ringing ended at: ");
            Serial.print(RTC.now().hour(), DEC);
            Serial.print(':');
            Serial.print(RTC.now().minute(), DEC);
            Serial.print(":");
            Serial.println(RTC.now().second(), DEC);
            break;
          }
          digitalWrite(ringerPins[0], i % 2);
          digitalWrite(ringerPins[1], 1 - i % 2);
          delay(40);  // time between hits
        }
        // seconds between ringing
        delay(1000);
      }
      // Stop ringing
      digitalWrite(ringerPins[0], LOW);
      digitalWrite(ringerPins[1], LOW);

      if (idleSwitch.fell()) {
        Serial.println("Alarm off");
        Serial.println("Idle");
        alarmActive = false;
        mode = 0;
      }
      break;

    default:
      mode = 0;
  }
}

void dialling() {
  if (numberSwitch.rose()) {
    pulseCount++;
  }
  // Check whether the dial has returned all the way
  if (dialSwitch.rose()) {
    // The number 0 has 10 pulses
    if (pulseCount == 10) {
      pulseCount = 0;
    }
    number = pulseCount;
    Serial.println(number);
    pulseCount = 0;
  }
}

void drawHome(void) {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);

  // Display date and time
  display.print("Date:  ");
  if (RTC.now().day() < 10) {
    display.print("0");
  }
  display.print(RTC.now().day(), DEC);
  display.print('/');
  if (RTC.now().month() < 10) {
    display.print("0");
  }
  display.print(RTC.now().month(), DEC);
  display.print('/');
  display.print(RTC.now().year(), DEC);
  display.println();
  display.print("Time:  ");
  if (RTC.now().hour() < 10) {
    display.print("0");
  }
  display.print(RTC.now().hour(), DEC);
  display.print(':');
  if (RTC.now().minute() < 10) {
    display.print("0");
  }
  display.print(RTC.now().minute(), DEC);
  display.print(':');
  if (RTC.now().second() < 10) {
    display.print("0");
  }
  display.print(RTC.now().second(), DEC);
  display.println();

  // Alarm on/off
  display.print("Alarm: ");
  if (alarmActive) {
    if (alarmHours < 10) {
      display.print("0");
    }
    display.print(alarmHours);
    display.print(":");
    if (alarmMinutes < 10) {
      display.print("0");
    }
    display.print(alarmMinutes);
    display.println();
  } else {
    display.print("off");
    display.println();
  }

  // Dial
  display.print("Dial:  ");
  display.print(number);
  display.println();

  display.display();
}

void drawAlarm(void) {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(37, 8);

  display.print(alarmTime[0]);
  display.print(alarmTime[1]);
  display.print(":");
  display.print(alarmTime[2]);
  display.print(alarmTime[3]);

  display.display();
}