Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
Jerry Miao
Published

Bucky-et Air Conditioner

The Bucky-et AC offers an eco-friendly, bucket-based cooling solution harnessing water's phase transition to combat extreme heat.

AdvancedShowcase (no instructions)2,262
Bucky-et Air Conditioner

Things used in this project

Story

Read more

Custom parts and enclosures

full assembaly

Schematics

Circuit Diagram

ideation

components

eagle file

Code

arduino code

C/C++
maion code to run lcd
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args)  write(args);
#else
#define printByte(args)  print(args,BYTE);
#endif

uint8_t arrow[8] = {0x00, 0x04, 0x06, 0x1F, 0x1F, 0x06, 0x04, 0x00};

/////Rotary Encoder Pin Assignment/////
#define rotaryPin_A 5
#define rotaryPin_B 6
#define rotaryButtonPin 7

#define EN_PIN    9  // LOW: Driver enabled. HIGH: Driver disabled
#define STEP_PIN  11  // Step on rising edge
#define RX_PIN    10  // SoftwareSerial pins
#define TX_PIN    8  //
#define DIR_PIN    12  //
#define Buzzer_PIN    13  //

#include <TMC2208Stepper.h>
TMC2208Stepper driver = TMC2208Stepper(RX_PIN, TX_PIN);

int currentLimit = 500;
int microstep = 2;

unsigned int maxMenuItems;     //number of menu items
unsigned char encoder_A = 0;
unsigned char encoder_B = 0;
unsigned char encoder_A_prev = 0;
unsigned char encoder_C = 1;  //encoder button
unsigned char encoder_C_prev = 1;
unsigned long currentTime;
unsigned long loopTime;
/////End of Rotary Encoder Declarations/////

//////Menu declarations///////
unsigned int menuPos = 0; //current menu position
unsigned int lastMenuPos = 0; //previous menu position
unsigned int parentMenuPos = 0; //parent menu position
bool humanInputActive = false;  //flag to indicate if input session is active
unsigned subMenuActive = 0;   //flag to indicate a sub selection marduarduenu session is active: 0 - main menu; 1 - number selection 1- 255; 2 - binary selection yes/no; 3 - time setting;
unsigned int subMenuPos = 0;  //sub menu rotary position
unsigned int subMenuClick = 0; //sub menu click counter
unsigned long CurrentTimeS;
unsigned long CurrentTimeE;

typedef struct menu_type
{

  menu_type()
    : code(0), text("")
  {
    //do nothing
  }

  unsigned int code; //code that indicate address (position) in the menu tree
  String text; //text to be displayed for the menu selection

}  menu_type;

menu_type menu[30] = {}; //initilizing menu array, use a number >= than the number of menu options


/////End of Menu declarations////

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  menuInit();
  Serial.begin(9600);
  Wire.begin();
  rotaryEncoderInit();
  

  lcd.init();
  lcd.backlight();
  lcd.createChar(1, arrow);

  lcd.setCursor(3, 0);
  lcd.print("Bucky-et AC");

  lcd.setCursor(5, 1);
  lcd.print("V 1.00");

  pinMode(rotaryPin_A, INPUT_PULLUP);
  pinMode(rotaryPin_B, INPUT_PULLUP);
  pinMode(rotaryButtonPin, INPUT_PULLUP);

  pinMode(Buzzer_PIN, OUTPUT);





  driver.beginSerial(115200);
  // Push at the start of setting up the driver resets the register to default
  driver.push();
  // Prepare pins
  pinMode(EN_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);

  driver.pdn_disable(true);     // Use PDN/UART pin for communication
  driver.I_scale_analog(false); // Use internal voltage reference
  driver.rms_current(currentLimit);      // Set driver current = 500mA, 0.5 multiplier for hold current and RSENSE = 0.11.
  driver.toff(2);               // Enable driver in software
  driver.mstep_reg_select(true);
  driver.microsteps(microstep);
  driver.dedge(false);

  digitalWrite(EN_PIN, LOW);    // Enable driver in hardware

  // motorReset();
}

void loop() {
  rotaryEncoderUpdate();

  // if (micros() < NextTime)
  //   NextTime = micros();

  // if (micros() - NextTime > interval) {
  //   digitalWrite(STEP_PIN, !digitalRead(STEP_PIN));
  //   NextTime = micros();         // reset for next pulse


  // digitalWrite(STEP_PIN, !digitalRead(STEP_PIN));
  // delay(1);
  // }
  digitalWrite(STEP_PIN, !digitalRead(STEP_PIN));
  delay(1);

}

void rotaryEncoderInit() {
  //Rotary encoder initialization
  currentTime = millis();
  loopTime = currentTime;
}

void rotaryEncoderUpdate() {

  //rotary encoder update, to be called from main loop()

  currentTime = millis();
  if (currentTime >= (loopTime + 1) ) {
    encoder_A = digitalRead(rotaryPin_A);
    encoder_B = digitalRead(rotaryPin_B);
    encoder_C = digitalRead(rotaryButtonPin);

    //handling knob rotation
    if ( (encoder_A == 0) && (encoder_A_prev == 1) ) {  //encoder changed position
      beep();

      if (encoder_B == 1) { //B is high, so encoder moved clockwise
        //Serial.println("encoder rotated CW");

        switch (subMenuActive) { //send encoder action to appropriate menu handler
          case 0: //main menu
            menuUpdate(2);
            break;

          case 1: //subMenu1
            subMenu1Update(2);
            break;

          case 2: //subMenu1
            subMenu2Update(2);
            break;

          default:
            menuUpdate(2);
            break;
        }


      } else {  //else, encoder moved counter-clockwise
        //Serial.println("encoder rotated CCW");

        switch (subMenuActive) {  //call the appropriate menuupdater depending on which sub menu is active
          case 0:
            menuUpdate(3);
            break;

          case 1:
            subMenu1Update(3);
            break;

          case 2: //subMenu1
            subMenu2Update(3);
            break;

          default:
            menuUpdate(3);
            break;
        }
      }

    }

    //handling push button
    if ( (encoder_C == 0) && (encoder_C_prev == 1) ) { //button pushed
      //Serial.println("encoder button closed.");
      longBeep();
      switch (subMenuActive) {  //call the appropriate menuupdater depending on which sub menu is active
        case 0:
          menuUpdate(1);
          break;

        case 1:
          subMenu1Update(1);
          break;

        case 2: //subMenu1
          subMenu2Update(1);
          break;

        default:
          menuUpdate(1);
          break;
      }

    } else if ( (encoder_C == 1) && (encoder_C_prev == 0) )  {  //button
      //Serial.println("encoder button opened.");

    }

    encoder_A_prev = encoder_A;
    encoder_C_prev = encoder_C;
    loopTime = currentTime;

  }

}



unsigned int menuVerifyPos(unsigned int menuCode) {
  //accepts a code that represents position in the menu
  //checks against the menu, verify it exist, and returns it
  //if the menu code given does not exist,
  //returns the closest code smaller than the one given

  bool confirmCode = false; //flag to keep track of whether code has been confirmed in menu tree

  for (unsigned int k = 0; k <= (maxMenuItems - 1); k++) {

    if (menuCode == menu[k].code) {  //found exact code, returns it
      menuPos = menu[k].code;
      confirmCode = true;
      lastMenuPos = menuCode;
      return menuPos;
    }

  }

  if (confirmCode == false) {
    menuPos = lastMenuPos;
    return menuPos;   //cannot find menu option, go back to previous menu option
  }

}

void updateMenuDisplay(unsigned int menuCode) {
  //prints menu selection to the LCD display
  //in order to have a scrolling menu effect, this code looks at item before and after current menu item and display them in a row


  String curMenu;
  String nextMenu;

  curMenu = findMenuTextFromCode(menuCode);
  nextMenu = findMenuTextFromCode((menuCode + 1));

  //starts printing to LCD
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.printByte(1);
  //char index, row index, on LCD
  lcd.print(curMenu);

  lcd.setCursor(1, 1);
  lcd.print(nextMenu); //print the next menu text in the 3rd row
}

String findMenuTextFromCode(unsigned int menuCode) {
  //accepts a code representing the code in menu, and returns the corresponding text

  for (unsigned int j = 0; j <= (maxMenuItems - 1); j++ ) {
    if (menuCode == menu[j].code) {
      return menu[j].text;
      break;
    }
  }
}


void beep()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1); 
  digitalWrite(LED_BUILTIN, LOW);
}

void longBeep()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(20); 
  digitalWrite(LED_BUILTIN, LOW);
}

void menuAction(unsigned int menuCode) {
  //gets called when rotary encoder is clicked,
  //check the current location inside the menu,
  //execute approriate actions
  //returns true if some action is executed
  //returns false if nothing is done
  //Serial.print("menuAction called with menuCode = ");
  //Serial.println(menuCode);

  switch (menuCode) {

    case 0:
      {
      }
      break;

    case 111:  //set frame rpm
      if (subMenuActive != 1) {  //initialize the subMenu3

        lcd.clear();
        subMenuActive = 1;
        subMenuPos = 0;
        subMenuClick = 0;
        subMenu1Update(0); //calls subMenu3Update with 0 (no action)

      } else if (subMenuActive == 1) {  //already initialized

        subMenuActive = 0;  //deactivate submenu, activate main menu
        lcd.clear();
        menuPos = 11;
        updateMenuDisplay(menuPos);
        //code to set time variables
      }
      break;

    case 12:
      digitalWrite(EN_PIN, !digitalRead(EN_PIN));

      break;

    case 13:
      digitalWrite(DIR_PIN, !digitalRead(DIR_PIN));

      break;


    case 141:
      if (subMenuActive != 2) {  //initialize the subMenu3

        lcd.clear();
        subMenuActive = 2;
        subMenuPos = 0;
        subMenuClick = 0;
        subMenu2Update(0); //calls subMenu3Update with 0 (no action)

      } else if (subMenuActive == 2) {  //already initialized

        subMenuActive = 0;  //deactivate submenu, activate main menu
        lcd.clear();
        menuPos = 14;
        updateMenuDisplay(menuPos);
        //code to set time variables
      }
      break;
  }
}

void menuInit() {
  //menu text display

  unsigned int i = 0;

  menu[i].code = 0;
  menu[i++].text = "EMERGENCY STOP";

  menu[i].code = 1;
  menu[i++].text = "MOTOR CONTROL";
  menu[i].code = 10;
  menu[i++].text = "RETURN";
  menu[i].code = 11;
  menu[i++].text = "SET INTERVAL";
  menu[i].code = 111;
  menu[i++].text = "INTERVAL:";

  menu[i].code = 12;
  menu[i++].text = "TOGGLE MOTOR";

  menu[i].code = 13;
  menu[i++].text = "REVERSE DIR";

  menu[i].code = 14;
  menu[i++].text = "SET MICROSTEPS";
  menu[i].code = 141;
  menu[i++].text = "MICROSTEPS:";

  menu[i].code = 2;
  menu[i++].text = "RESET";

  menu[i].code = 3;
  menu[i++].text = "SAVE SETTINGS";

  maxMenuItems = i + 1;

}

void menuUpdate(unsigned char encoderAction) {  //main menu navigation updater
  //called on by rotary encoder actions
  //parameter 0: no action from encoder
  //parameter 1: encoder click
  //parameter 2: encoder CW rotation
  //parameter 3: encoder CCW rotation

  //unsigned int verifiedMenuPos;

  if (encoderAction == 0) {
    //no action

  } else {  //triggered by action

    if (encoderAction == 1) {  //encoder click button

      //code below takes menu position to the next sub branch
      if (menuPos % 10 == 0) {  //if menu selection is at XX0 (zero at the end)
        menuPos = menuPos / 10; //go back up one level of menu (we use zero as the 'return' menu selection)
      } else {
        menuPos = menuPos * 10 + 1; //else, go further in one level of menu from the current selection
      }

      menuPos = menuVerifyPos(menuPos);
      menuAction(menuPos);  //click execution
      updateMenuDisplay(menuPos);

    } else if (encoderAction == 2) {  //encoder CW rotation

      if (menuPos % 10 == 9) {  //if current selection is at XX9
        //do nothing
      } else {
        menuPos ++;
      }

      menuPos = menuVerifyPos(menuPos);
      updateMenuDisplay(menuPos);


    } else if (encoderAction == 3) {  //encoder CCW rotation

      if (menuPos % 10 == 0) {  //if current selection is at XX0
        //do nothing
      } else {
        menuPos --;
      }

      menuPos = menuVerifyPos(menuPos);
      updateMenuDisplay(menuPos);
    }

    lastMenuPos = menuPos;

    //Serial.print("menuPos = ");
    //Serial.println(menuPos);

  }
}

void subMenu1Update(unsigned char encoderAction) { //sub menu type 1 updater: number selection 1 - 1000
  //called on by rotary encoder actions with a parameter to indicate:
  //parameter 0: no action from encoder
  //parameter 1: encoder click
  //parameter 2: encoder CW rotation
  //parameter 3: encoder CCW rotation

  if (encoderAction == 0) {

    // no action
  } else {

    if (encoderAction == 1) { //encoder click
      subMenuClick++;
      //Serial.print("subMenu1Update: click++; click = ");
      //Serial.println(subMenuClick);

      if (subMenuClick == 1) { //single click to exit sub menu
        lcd.clear();
        menuAction(lastMenuPos);   //calls menuAction with last main menu tree position
        return;
      }

    } else if (encoderAction == 2) {  //CW rotation
      if (interval >= 1000)
        interval = 1000;
      else
        interval += 1;

    } else if (encoderAction == 3) {  //CCW rotation

      if (interval <= 1)
        interval = 1;
      else
        interval -= 1;
    }

    //update 4th row of LCD to display sub menu position
    lcd.setCursor(0, 1);
    if (interval % 9 == 0 )
    {
      lcd.print("                ");
      lcd.setCursor(0, 1);
    }
    lcd.print(interval);
  }
}

void subMenu2Update(unsigned char encoderAction) { //sub menu type 1 updater: number selection 1 - 1000
  //called on by rotary encoder actions with a parameter to indicate:
  //parameter 0: no action from encoder
  //parameter 1: encoder click
  //parameter 2: encoder CW rotation
  //parameter 3: encoder CCW rotation

  if (encoderAction == 0) {

    // no action
  } else {

    if (encoderAction == 1) { //encoder click
      subMenuClick++;
      //Serial.print("subMenu1Update: click++; click = ");
      //Serial.println(subMenuClick);

      if (subMenuClick == 1) { //single click to exit sub menu
        lcd.clear();
        menuAction(lastMenuPos);   //calls menuAction with last main menu tree position
        return;
      }

    } else if (encoderAction == 2) {  //CW rotation
      if (microstep >= 256)
        microstep = 256;
      else
        microstep += microstep;

    } else if (encoderAction == 3) {  //CCW rotation

      if (microstep <= 1)
        microstep = 1;
      else
        microstep = microstep / 2;
    }

    //update 4th row of LCD to display sub menu position
    lcd.setCursor(0, 1);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    lcd.print(microstep);
    driver.microsteps(microstep);
  }
}

Sony Spresense Code

C/C++
Code for PWM fan control
const int fanPin = 6;      // the fan is connected to pin 6
const int maxSpeed = 255;  // maximum PWM value
const int minSpeed = 25;   // 10% of 255
const int stepDelay = 20;  // delay in milliseconds for each step

void setup() {
  pinMode(fanPin, OUTPUT);
}

void loop() {
  // Increase speed from off to max
  for (int speed = 0; speed <= maxSpeed; speed++) {
    analogWrite(fanPin, speed);
    delay(stepDelay);
  }
  delay(5000);
  
  // Decrease speed from max to 10%
  for (int speed = maxSpeed; speed >= minSpeed; speed--) {
    analogWrite(fanPin, speed);
    delay(stepDelay);
  }


  analogWrite(fanPin, 0);    // turn off the fan
  delay(5000);               // wait for 2 seconds
}

Credits

Jerry Miao
1 project • 1 follower

Comments