Firmware Design


Final Project for Principles of Engineering
All of the Arduino IDE firmware code was compiled into a single script. This reflected our goal of a compact and polished project goal, as we attempted to minimize the number of Arduinos we needed in the system. With all the code compiled into a single Arduino, it is easy to observe how all the code runs in parallel.

#include 
#include 
//MOTOR SETUP:
Adafruit_MotorShield AFMS_bot = Adafruit_MotorShield(0x60); // Default address, no jumpers
Adafruit_MotorShield AFMS_top = Adafruit_MotorShield(0x61); // Rightmost jumper closed

Adafruit_DCMotor *M1 = AFMS_bot.getMotor(1);
Adafruit_DCMotor *M2 = AFMS_bot.getMotor(2);
Adafruit_DCMotor *M3 = AFMS_bot.getMotor(3);
Adafruit_DCMotor *M4 = AFMS_bot.getMotor(4);
Adafruit_DCMotor *M5 = AFMS_top.getMotor(5);
Adafruit_DCMotor *M6 = AFMS_top.getMotor(6);
Adafruit_DCMotor *M7 = AFMS_top.getMotor(7);
Adafruit_DCMotor *M8 = AFMS_top.getMotor(8);
        
int M1_s = 40; //set motor speeds
int M2_s = 40;
int M3_s = 40;
int M4_s = 40;
int M5_s = 40;
int M6_s = 40;
int M7_s = 40;
int M8_s = 40;
                

At the beginning of the Arduino code, we initialize settings and libraries we will use. We initialize the adafruit motor library, which allows to use motor shields for the Arduino uno effectively, and initialize each of our 8 motors and set their initial speed.

//LED SETUP
// You can choose the latch pin yourself.
const int ShiftPWM_latchPin=8;
const bool ShiftPWM_invertOutputs = false;
const bool ShiftPWM_balanceLoad = false;

#include    // include ShiftPWM.h after setting the pins!

// Function prototypes (telling the compiler these functions exist).
void breatheSleep(void);
void oneByOne(void);
void inOutTwoLeds(void);
void inOutAll(void);
void alternatingColors(void);
void hueShiftAll(void);
void randomColors(void);
void rgbLedRainbow(unsigned long cycleTime, int rainbowWidth);

// Here you set the number of brightness levels, the update frequency and the number of shift registers.
// These values affect the load of ShiftPWM.
// Choose them wisely and use the PrintInterruptLoad() function to verify your load.
unsigned char maxBrightness = 255;
unsigned char pwmFrequency = 75;
unsigned int numRegisters = 3;
unsigned int numOutputs = numRegisters*8;
unsigned int numRGBLeds = numRegisters*8/3;
unsigned int fadingMode = 0; //start with all LED's off.

unsigned long startTime = 0; // start time for the chosen fading mode
                

Further initialization include LED interaction setups, and defining LED function definitions for later. Each LED is hooked to 3 digital pins, and the mixture of RGB of each LED defined the final color the LED was. LED functions down below included input triggered patterns and so forth.

//Photodiode
byte photoDiode_pin = A1;
long pd_bright;
long offsetTime = 0; //Used to create offset

// set up mode variable
byte mode = 0;
byte prev_mode = 0;

//KNOCK VARIABLES
byte knock_pin = A3;
int state = 0; // Active = 1, Passive = 0
int counter = 0; // counts knocks up to 3
int impactval;
long prevTime = 0;

                

The final initialization relates to the photodiode and accelerometer, the electrical components on the protoboard. For these components, the variables that will be used to store counters (for counting the number of knocks) and the read values from the accelerometer and the photodiode initialized.

Further initialization include LED interaction setups, and defining LED function definitions for later. Each LED is hooked to 3 digital pins, and the mixture of RGB of each LED defined the final color the LED was. LED functions down below included input triggered patterns and so forth.

void setup() {
  AFMS_top.begin();
  AFMS_bot.begin();
  M1->setSpeed(M1_s);
  M2->setSpeed(M2_s);
  M3->setSpeed(M3_s);
  M4->setSpeed(M4_s);
  M5->setSpeed(M5_s);
  M6->setSpeed(M6_s);
  M7->setSpeed(M7_s);
  M8->setSpeed(M8_s);

  // Sets the number of 8-bit registers that are used.
  ShiftPWM.SetAmountOfRegisters(numRegisters);

  // SetPinGrouping allows flexibility in LED setup. 
  // If your LED's are connected like this: RRRRGGGGBBBBRRRRGGGGBBBB, use SetPinGrouping(4).
  ShiftPWM.SetPinGrouping(8);
  ShiftPWM.Start(pwmFrequency,maxBrightness);
  
  Serial.begin(9600);
  
}
                

AFter the initialization is complete, the Arduino code is set up. In the setup process, the motor speeds initialized above are applied, and the motors are now given their initial speeds. Also, the LED pins are grouped together for flexibility and simplification in commanding.

void loop() {
  //CHANGE NUMBER AS NECESSARY
  if ((millis()-prevTime)> 1000){
    counter = 0;
  }
  
  if (Serial.available() > 0){
    mode = Serial.read() - 48;
    startTime = millis();
  }
//  if (mode == prev_mode){
//    continue;
//  }
  pd_bright = map(analogRead(photoDiode_pin), 800, 1000, 0, 200);
//  pd_bright = map(analogRead(photoDiode_pin), 500, 900, 0, 255);
//  Serial.println(analogRead(photoDiode_pin));
  if (pd_bright < abs(100)){
    Serial.println("SLEEP");
  }
  detectKnock();
  if (counter == 1){
    mode = 3;
    startTime = millis();
  }
  else if (counter == 2){
    Serial.println("KNOCKKNOCK");
  }

  switch(mode){
    case 1:
      serialFlush();
      wave1();
      inOutAll();
      break;
    case 2:
      serialFlush();
      oneByOne();
      resetMotor();      
      break;
    case 3:
      resetMotor();
      M1->run(FORWARD);
      randomColors();
      break;
    case 4:
      resetMotor();
      M2->run(FORWARD);
      rgbLedRainbow(3000,numRGBLeds);
    default:
      breatheSleep();
      break;
  }
}

}
                

After all the variables are initialized, the code enters the main void loop. In this loop, there are two main interactions. The first main function of the main loop is the knock sensing. For the knocking, if a knock didn’t happen for over a certain period of time, we reset the knock counter to zero, as we only want consecutive knocks to register as “knocks”. When the counter registers one knock, LED toggling happens, and if it registers two knocks, it toggles a serial interaction with the raspberry pi.

The serial communication initially occurs with a self written “serial flush” function. Sometimes, due to us attempting two way communication between the Arduino and the Pi, there is unwanted noise and echo in the serial terminal. The serial flush function resets the serial terminal so that these noises do not hinder the communication process.

The switch-cases after the two major functions are different states the desktop companion is in. These states are accessed by the different inputs and interactions. Different cases equate to different emotions of the kinetic sculpture, and it will output different types of LED patterns and make different faces on the screen attached to the sculpture. Each of the LED pattern commands are linked here, in our github repository.