Project 2 - Design and develop the web based Temperature control system using Beagle Bone Black.
Overview of the Project:
- Device Driver
- Write the device driver for MCP9808 Temperature Sensor.
- Probe function: Register the Platform device under driver/misc.
- Read function: It should send the i2c message to read the data from sensor and copy it to the user space.
- Alert pin connected to BBB as gpio interrupt. The temp sensor is programmed for Tlower and Tupper through dts file. Based on interrupt, Motor will be turned ON or OFF.
- Compatible string: it should be unique and same string should be entered in dts file.
- Dts file: The I2C instance and GPIO pins used should be configurable.
- Hardware Setup:
- MCP9808 Temperature sensor should be connected to the I2C and DC motor connected to the GPIO pin using the Driver IC LN293D.
- User Space Application
- Temperature must be monitored for every 15 mins and stored in the database (MySql).
- When the temperature exceeds the set value, the motor should turn On and Off when temperature comes down.
- Web page should be provide the following feature
- Set the Threshold Temperature
- Retrieve the data from database and plotted as a graph
Goals
- The student will have capability to build the customized image to the Embedded Target - Beagle bone black.
- The student will be able to connect the sensor and actuator to the embedded target.
Key Highlights of the Project:
- The project helps the student to understand the various steps involved in building the Embedded Linux target.
- It makes the students understand how to configure the pins exposed in the Beagle bone Header P8 and P9.
- It covers the various kernel subsystem like GPIO, I2C, Interrupt Handling, Platform device drivers.
- It also enables the write not only device driver but also the user space application to present the data to remote world.
Deliverables of this Project:
- Embedded Linux Image for the BBB - It contains U-Boot, Linux Kernel and Root File system.
- DTS source file - The file allows the user to configure the GPIO pins connected to Motor and Temperature sensor connected to I2C lines. The file can be compiled separately and updated to SD card if needed.
- Simple HTML web page capable of monitoring the temperature and controlling the motor.
- Database has to be capable of storing the temperature reading for 15 days.
Expected Outcomes:
- The students will learn the following things
- Able to build the custom kernel image for the embedded target.
- Able to configure the Gpio pins and Temperature sensor connected to target through DTS files.
- Able to learn the I2C and GPIO subsystem.
- Able to understand the remote monitoring of the device through internet.
Code:
mcp9808.c :
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MCP9808_TEMP_REG 0x05
#define MCP9808_CONFIG_REG 0x01
#define MCP9808_T_LOWER_REG 0x02
#define MCP9808_T_UPPER_REG 0x03
struct mcp9808_data {
struct i2c_client *client;
struct platform_device *pdev;
int irq_gpio;
int threshold;
};
static irqreturn_t mcp9808_alert_handler(int irq, void *data) {
struct mcp9808_data *mcp = data;
int temp;
u8 buf[2];
i2c_smbus_read_i2c_block_data(mcp->client, MCP9808_TEMP_REG, 2, buf);
temp = ((buf[0] & 0x0F) << 8) | buf[1];
if (temp > mcp->threshold) {
gpio_set_value_cansleep(mcp->irq_gpio, 1);
} else {
gpio_set_value_cansleep(mcp->irq_gpio, 0);
}
return IRQ_HANDLED;
}
static int mcp9808_probe(struct i2c_client *client) {
struct mcp9808_data *mcp;
struct platform_device *pdev;
struct device *dev = &client->dev;
int ret;
mcp = devm_kzalloc(dev, sizeof(struct mcp9808_data), GFP_KERNEL);
if (!mcp) {
dev_err(dev, "Failed to allocate memory\n");
return -ENOMEM;
}
mcp->client = client;
mcp->threshold = 0;
// Set T_LOWER and T_UPPER from device tree
struct device_node *np = dev->of_node;
if (np) {
u32 t_lower, t_upper;
of_property_read_u32(np, "t-lower", &t_lower);
of_property_read_u32(np, "t-upper", &t_upper);
i2c_smbus_write_word_data(mcp->client, MCP9808_T_LOWER_REG, t_lower);
i2c_smbus_write_word_data(mcp->client, MCP9808_T_UPPER_REG, t_upper);
dev_info(dev, "Set T_LOWER = %d and T_UPPER = %d\n", t_lower, t_upper);
}
// Register the platform device
pdev = platform_device_alloc("mcp9808", -1);
if (!pdev) {
dev_err(dev, "Failed to allocate platform device\n");
return -ENOMEM;
}
mcp->pdev = pdev;
ret = platform_device_add_data(pdev, mcp, sizeof(struct mcp9808_data));
if (ret) {
dev_err(dev, "Failed to add platform device data\n");
platform_device_put(pdev);
return ret;
}
ret = platform_device_add(pdev);
if (ret) {
dev_err(dev, "Failed to add platform device\n");
platform_device_unregister(pdev);
return ret;
}
// Register the interrupt handler
mcp->irq_gpio = of_get_named_gpio(dev->of_node, "alert-gpio", 0);
if (gpio_is_valid(mcp->irq_gpio)) {
ret = gpio_request(mcp->irq_gpio, "mcp9808-alert");
if (ret) {
dev_err(dev, "Failed to request gpio %d\n", mcp->irq_gpio);
platform_device_unregister(mcp->pdev);
return ret;
}
ret = gpio_direction_output(mcp->irq_gpio, 0);
if (ret) {
dev_err(dev, "Failed to set gpio %d direction\n", mcp->irq_gpio);
gpio_free(mcp->irq_gpio);
platform_device_unregister(mcp->pdev);
return ret;
}
ret = gpio_to_irq(mcp->irq_gpio);
if (ret < 0) {
dev_err(dev, "Failed to get irq from gpio %d\n", mcp->irq_gpio);
gpio_free(mcp->irq_gpio);
platform_device_unregister(mcp->pdev);
return ret;
}
ret = request_irq(ret, mcp9808_alert_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "mcp9808-alert", mcp);
if (ret) {
dev_err(dev, "Failed to request irq\n");
gpio_free(mcp->irq_gpio);
platform_device_unregister(mcp->pdev);
return ret;
}
dev_info(dev, "Registered interrupt handler\n");
}
dev_info(dev, "MCP9808 device detected\n");
return 0;
}
static void mcp9808_remove(struct i2c_client *client) {
struct mcp9808_data *mcp = i2c_get_clientdata(client);
if (gpio_is_valid(mcp->irq_gpio)) {
free_irq(gpio_to_irq(mcp->irq_gpio), mcp);
gpio_free(mcp->irq_gpio);
}
platform_device_unregister(mcp->pdev);
}
static const struct of_device_id mcp9808_of_match[] = {
{ .compatible = "microchip,mcp9808", },
{},
};
MODULE_DEVICE_TABLE(of, mcp9808_of_match);
static const struct i2c_device_id mcp9808_id[] = {
{ "mcp9808", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, mcp9808_id);
static struct i2c_driver mcp9808_driver = {
.driver = {
.name = "mcp9808",
.of_match_table = mcp9808_of_match,
},
.probe = mcp9808_probe,
.remove = mcp9808_remove,
.id_table = mcp9808_id,
};
module_i2c_driver(mcp9808_driver);
MODULE_AUTHOR("Author name");
MODULE_DESCRIPTION("MCP9808 temperature sensor driver");
MODULE_LICENSE("GPL v2");
Makefile:
obj-m += mcp9808.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
