Hackster is hosting Hackster Holidays, Finale: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Tuesday!Stream Hackster Holidays, Finale on Tuesday!
RuthAnn
Published

Phone Controlled Dial Thermostat

This project modifies a manual dial thermostat to become an automated phone-controlled thermostat. Set your temperature via phone and enjoy!

BeginnerShowcase (no instructions)630
Phone Controlled Dial Thermostat

Story

Read more

Schematics

Physical Set Up

Code

i2c_test_byte.c

C/C++
#include <stdio.h>
#include <math.h>
#include "i2c.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define ADDR  0x25

// functions declared at bottom of this file
int mygetch(void);

int main(void) {
    int bus; //data communication number
    unsigned char tx=0; //data to transfer
    unsigned char rx=0; //data to receive
    int index = 0;
	char buffer[200]; // used by fgets to read character string typed by user.
    int mychar = 0; // input character
    int tempSet = 0; // temperature to set transfered by tx
    int tempReading = 0; //current temperature received from rx
    int inputdone = 0;
    float tempfloat = 0;

    bus = i2c_start_bus(1);//start data communication

    tx += 1;
    while (!inputdone) {//while we haven't received any characters from the user
        printf("\n\n");
        printf("Menu of Selections\n");
        printf("DO NOT PRESS CTRL-C when in this menu selection\n");
        printf("e - Exit Application\n");
        printf("s - enter Desired Temperature\n");
        printf("p - print current temp\n");
        mychar = (char) mygetch(); //when input is received, set mychar to character from user

        switch (mychar) {
        case 'p': //print current temperature
            printf("Print current Temp \n");

            tx = (unsigned char)tempSet; //used unsigned char so that we do not send a value greater than 255
                                         //Because of how we wrote the code on the MSP we must send garbage data so that we can receive data
            i2c_write_bytes(bus, ADDR, &tx,1);//sends tx to receive rx (thats how we wrote the code in code composer for MSP)
            i2c_read_bytes(bus, ADDR,&rx,1);//receives back the current temp
            printf("Current Temp: %03d\n", (int)rx);//print out
            usleep(200*1000); //pause
            break;

        case 'e':
            inputdone = 1; //exits the while loop
            break;
        case 's':
            printf("Enter Desired Temp Setpoint\n");
            fgets(buffer,190,stdin); //receives the character form the user
            buffer[strlen(buffer)-1] = '\0';  // get ride of '\n' in returned string
            if (buffer[0] != '\0') { //
                if (sscanf(buffer,"%f",&tempfloat) != 0) {  // check that it was a number entered
                    tempSet = tempfloat; //tempfloat was set to the enter character in the above line
                                         //and int the line above, tempSet is set to the value of tempfloat
                    printf("Desired Temp = %d\n",tempSet);
                }  else {
                    printf("Error: Non numerical value typed\n");
                }
            } else {
                printf("Error: TempSet not changed\n");
            }
            tx = (unsigned char)tempSet; //not greater than 255
            i2c_write_bytes(bus, ADDR, &tx,1);//sends tx , bus is the I2C connection, ADDR is the address of the slave (0x25),
                                              //&tx is the address of tx, 1 is the number of bytes

            i2c_read_bytes(bus, ADDR,&rx,1);//receives back the current temperature

            printf("TX: %03d  RX:%03d\n", (int)tx, (int)rx);

            usleep(200*1000); //pause
            break;
        default:

            break;
        }
    }

    i2c_close_bus(bus); //stop data communication
    return 0;

}

int mygetch(void)
{
	struct termios oldt,
	newt;
	int ch;
	tcgetattr( STDIN_FILENO, &oldt );
	newt = oldt;
	newt.c_lflag &= ~( ICANON | ECHO );
	tcsetattr( STDIN_FILENO, TCSANOW, &newt );
	ch = getchar();
	tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
	return ch;
}

i2c.h

C/C++
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>

int i2c_start_bus(unsigned char bus);
void i2c_close_bus(int fd);
int i2c_write(int file, unsigned char addr, unsigned char reg, unsigned char *value, unsigned char num);
int i2c_write_byte(int file, unsigned char addr, unsigned char value);
int i2c_read(int file, unsigned char addr, unsigned char reg, unsigned char *value, unsigned char num);
int i2c_read_byte(int file, unsigned char addr,unsigned char *value);
int i2c_write_bytes(int file, unsigned char addr, unsigned char *value, unsigned char num);
int i2c_read_bytes(int file, unsigned char addr, unsigned char *value, unsigned char num);

i2c.c

C/C++
#include "i2c.h"

int i2c_start_bus(unsigned char bus) {
	char filename[11];
	int fd;
	
	sprintf(filename, "/dev/i2c-%d", bus);
	
	fd = open(filename, O_RDWR);
	
	if (fd < 0)
		fprintf(stderr, "Failed to communicate with I2C bus #%d.\n", bus);
		
	return fd;
}

void i2c_close_bus(int fd) {
	close(fd);
}

int i2c_write_byte(int file, unsigned char addr, unsigned char value) {
	int i;
	unsigned char outbuf;
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[1];

	messages[0].addr  = addr;
	messages[0].flags = 0;
	messages[0].len   = 1;
	messages[0].buf   = &outbuf;

	outbuf = value;

	/* Transfer the i2c packets to the kernel and verify it worked */
	packets.msgs  = messages;
	packets.nmsgs = 1;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		perror("Unable to send data");
		return 1;
	}

	return 0;
}

int i2c_write_bytes(int file, unsigned char addr, unsigned char *value, unsigned char num) {
	int i;
	unsigned char outbuf[256];
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[1];

	messages[0].addr  = addr;
	messages[0].flags = 0;
	messages[0].len   = num;
	messages[0].buf   = outbuf;

	if (num > 255) {
		perror("Maximum Send num is 255");
		return 1;
	}
	for (i = 0; i <= num; i++)
		outbuf[i] = value[i];

	/* Transfer the i2c packets to the kernel and verify it worked */
	packets.msgs  = messages;
	packets.nmsgs = 1;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		perror("Unable to send data");
		return 1;
	}

	return 0;
}
	
int i2c_write(int file, unsigned char addr, unsigned char reg, unsigned char *value, unsigned char num) {
	int i;
	unsigned char outbuf[256];
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[1];

	messages[0].addr  = addr;
	messages[0].flags = 0;
	messages[0].len   = num + 1;
	messages[0].buf   = outbuf;

	/* The first byte indicates which register we'll write */
	outbuf[0] = reg;

	/* 
	 * The remaining bytes indicate the values that will be written.
	 * The maximum number of data bytes allowed is 255.
	 */
	for (i = 1; i <= num; i++)
		outbuf[i] = value[i - 1];

	/* Transfer the i2c packets to the kernel and verify it worked */
	packets.msgs  = messages;
	packets.nmsgs = 1;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		perror("Unable to send data");
		return 1;
	}

	return 0;
}

int i2c_read(int file, unsigned char addr, unsigned char reg, unsigned char *value, unsigned char num) {
	unsigned char outbuf;
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[2];

	/*
	 * In order to read a register, we first do a "dummy write" by writing
	 * 0 bytes to the register we want to read from.  This is similar to
	 * the packet in set_i2c_register, except it's 1 byte rather than 2.
	 */
	outbuf = reg;
	messages[0].addr  = addr;
	messages[0].flags = 0;
	messages[0].len   = sizeof(outbuf);
	messages[0].buf   = &outbuf;

	/* The data will get returned in this structure */
	messages[1].addr  = addr;
	messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
	messages[1].len   = num;
	messages[1].buf   = value;

	/* Send the request to the kernel and get the result back */
	packets.msgs	  = messages;
	packets.nmsgs	  = 2;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		perror("Unable to send data");
		return 1;
	}

	return 0;
}


int i2c_read_byte(int file, unsigned char addr,unsigned char *value) {
	unsigned char outbuf;
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[1];

	/* The data will get returned in this structure */
	messages[0].addr  = addr;
	messages[0].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
	messages[0].len   = 1;
	messages[0].buf   = value;

	/* Send the request to the kernel and get the result back */
	packets.msgs	  = messages;
	packets.nmsgs	  = 1;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		perror("Unable to send data");
		return 1;
	}

	return 0;
}


int i2c_read_bytes(int file, unsigned char addr, unsigned char *value, unsigned char num) {
	unsigned char outbuf;
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[1];


	/* The data will get returned in this structure */
	messages[0].addr  = addr;
	messages[0].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
	messages[0].len   = num;
	messages[0].buf   = value;

	/* Send the request to the kernel and get the result back */
	packets.msgs	  = messages;
	packets.nmsgs	  = 1;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		perror("Unable to send data");
		return 1;
	}

	return 0;
}

MSP430_ServoMotor.c

C/C++
/******************************************************************************
MSP430G2553 Project Creator

SE 423  - Dan Block
        Spring(2019)

        Written(by) : Steve(Keres)
College of Engineering Control Systems Lab
University of Illinois at Urbana-Champaign
 *******************************************************************************/

#include "msp430g2553.h"
#include "UART.h"

void print_every(int rate);

char newprint = 0;
int timecheck = 0;
int RXData = 0;
int TXData = 0;
int timecount= 0;
int tempSet = 85;
int tempReading = 0;
int ADC0_value = 0; //NO FLOATS OR DOUBLES because runs really slow


void main(void) {

    WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

    if (CALBC1_16MHZ ==0xFF || CALDCO_16MHZ == 0xFF) while(1);

    DCOCTL = CALDCO_16MHZ;    // Set uC to run at approximately 16 Mhz
    BCSCTL1 = CALBC1_16MHZ;

    //Initialize I2C for Orange Pi
    //http://coecsl.ece.illinois.edu/ge423/datasheets/MSP430Ref_Guides/MSP430x2xx_usersguide.pdf
    P1SEL |= 0xC0;
    P1SEL2 |= 0xC0;
    UCB0CTL1 = UCSSEL_3 + UCSWRST;
    UCB0CTL0 = UCSYNC + UCMODE_3;
    UCB0I2COA = 0x25;
    UCB0CTL1 &= ~UCSWRST;

    //Interrupts
    IFG2 &= ~UCB0RXIFG; //clear in source
    IE2 |= UCB0RXIE;

    //Initializing servo motor ports
    P2DIR |= BIT4;   //P2.4 output
    P2SEL |= BIT4;   //P2.4 --> TA1.2 option
    P2SEL2 &= ~(BIT4);//P2.4 TA1.2 option (0 by default)
    TA1CCTL0 = 0x0;   // Set everything to default
    TA1CCTL2 = OUTMOD_7; //TA1CCR2 reset/set for PWM wave
    TA1CCR0 = 40000;  //Compares to get 50Hz frequency
    TA1CTL = TASSEL_2 + MC_1 + ID_3; //SMCLK, up mode (ID_3 divide by 8)

    //Initialized the ADC10 Controller (To read temperature sensors)
    ADC10CTL0 = ADC10SHT_1 + ADC10ON + ADC10IE + ADC10DIV_0;//ADC10SHT_1 sample and hold time; ADC10ON turn on ADC; ADC10 SC start conversion and sampling; ADC10DIV_0 clock divider 0
    ADC10CTL1 = INCH_3;                                     //input A3 sample for the MUX so that we can read the DC values
    ADC10AE0 |= 0x08;                                       // PA.0 ADC option select (input analog enable)

    // Timer A Config
    TACCTL0 = CCIE;             // Enable Periodic interrupt
    TACCR0 = 16000;                // period = 1ms
    TACTL = TASSEL_2 + MC_1; // source SMCLK, up mode


    Init_UART(115200,1);    // Initialize UART for 115200 baud serial communication

    _BIS_SR(GIE);       // Enable global interrupt


    while(1) {  // Low priority Slow computation items go inside this while loop.  Very few (if anyt) items in the HWs will go inside this while loop

        // for use if you want to use a method of receiving a string of chars over the UART see USCI0RX_ISR below
        //      if(newmsg) {
        //          newmsg = 0;
        //      }

        // The newprint variable is set to 1 inside the function "print_every(rate)" at the given rate
        if ( (newprint == 1) && (senddone == 1) )  { // senddone is set to 1 after UART transmission is complete

            // only one UART_printf can be called every 15ms
            UART_printf("A%d R%d S%d\n\r",ADC10MEM,tempReading,tempSet);

            newprint = 0;
        }

    }
}


// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
    ADC10CTL0 |= ADC10SC + ENC; //start conversion and ENC allows the MUX to set for the AE0
}



// ADC 10 ISR - Called when a sequence of conversions (A7-A0) have completed
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void) {
    timecheck++; // Keep track of time for main while loop.
    print_every(500);  // units determined by the rate Timer_A ISR is called, print every "rate" calls to this function
    ADC0_value = (int)((((double)ADC10MEM)*81.0/183.0));//conversion of 0-1023 from ADC10MEM to 0-3300milliVolts
    //Changes the outputs of the motors according to the phone values
    tempReading = ADC0_value;
    if(tempReading > tempSet+4){ //has on offset of +/- 4 so that the motor isn't quickly fluctuating
        TA1CCR2 = 4500; //Turn off heat
    }
    else if(tempReading<tempSet-4)
    {
        TA1CCR2 = 2500;//Turn on heat
    }
}



// USCI Transmit ISR - Called when TXBUF is empty (ready to accept another character)
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void) {

    if((IFG2&UCA0TXIFG) && (IE2&UCA0TXIE)) {        // USCI_A0 requested TX interrupt
        if(printf_flag) {
            if (currentindex == txcount) {
                senddone = 1;
                printf_flag = 0;
                IFG2 &= ~UCA0TXIFG;
            } else {
                UCA0TXBUF = printbuff[currentindex];
                currentindex++;
            }
        } else if(UART_flag) {
            if(!donesending) {
                UCA0TXBUF = txbuff[txindex];
                if(txbuff[txindex] == 255) {
                    donesending = 1;
                    txindex = 0;
                } else {
                    txindex++;
                }
            }
        }

        IFG2 &= ~UCA0TXIFG;
    }

    if((IFG2&UCB0RXIFG) && (IE2&UCB0RXIE)) {    // USCI_B0 RX interrupt occurs here for I2C
        //RX code here.
        RXData =UCB0RXBUF; //Receiving data from the OrangePI off the RX buffer
        tempSet = RXData; //Saving the received temperature value to a new variable(change of variable for convenience)
        TXData = tempReading; //Setting the data to be send out to be the current temperature

        IFG2 &= ~UCB0RXIFG; //clear interrupt source
        IE2 &= ~UCB0RXIE; //USCI_B0 receive interrupt disabled
        IE2 |= UCB0TXIE;//Enable transfer buffer
    } else if ((IFG2&UCB0TXIFG) && (IE2&UCB0TXIE)) { // USCI_B0 TX interrupt
        //TX code here.
        UCB0TXBUF = TXData; //Writing the current reading the the transfer buffer.
        IE2 |= UCB0RXIE; //RX interrupt enabled
        IE2 &= ~UCB0TXIE;//disable transfer buffer
    }
}


// USCI Receive ISR - Called when shift register has been transferred to RXBUF
// Indicates completion of TX/RX operation
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {

    if((IFG2&UCA0RXIFG) && (IE2&UCA0RXIE)) {  // USCI_A0 requested RX interrupt (UCA0RXBUF is full)

        if(!started) {  // Haven't started a message yet
            if(UCA0RXBUF == 253) {
                started = 1;
                newmsg = 0;
            }
        } else {    // In process of receiving a message
            if((UCA0RXBUF != 255) && (msgindex < (MAX_NUM_FLOATS*5))) {
                rxbuff[msgindex] = UCA0RXBUF;

                msgindex++;
            } else {    // Stop char received or too much data received
                if(UCA0RXBUF == 255) {  // Message completed
                    newmsg = 1;
                    rxbuff[msgindex] = 255; // "Null"-terminate the array
                }
                started = 0;
                msgindex = 0;
            }
        }
        IFG2 &= ~UCA0RXIFG;
    }

    if((UCB0I2CIE&UCNACKIE) && (UCB0STAT&UCNACKIFG)) { // I2C NACK interrupt

        UCB0STAT &= ~UCNACKIFG;
    }
    if((UCB0I2CIE&UCSTPIE) && (UCB0STAT&UCSTPIFG)) { // I2C Stop interrupt

        UCB0STAT &= ~UCSTPIFG;
    }
    if((UCB0I2CIE&UCSTTIE) && (UCB0STAT&UCSTTIFG)) { //  I2C Start interrupt

        UCB0STAT &= ~UCSTTIFG;
    }
    if((UCB0I2CIE&UCALIE) && (UCB0STAT&UCALIFG)) {  // I2C Arbitration Lost interrupt

        UCB0STAT &= ~UCALIFG;
    }
}

// This function takes care of all the timing for printing to UART
// Rate determined by how often the function is called in Timer ISR
int print_timecheck = 0;
void print_every(int rate) {
    if (rate < 15) {
        rate = 15;
    }
    if (rate > 10000) {
        rate = 10000;
    }
    print_timecheck++;
    if (print_timecheck == rate) {
        print_timecheck = 0;
        newprint = 1;
    }

}

Credits

RuthAnn
2 projects • 2 followers

Comments