Project 2 - Design and develop the web based Temperature control system using Beagle Bone Black.
Steps:
-
Declare the device information in the device tree by adding the compatible string and properties for the sensor.
-
Initialize the I2C interface for the sensor by registering it with the I2C subsystem.
-
Create a device driver structure that contains the necessary callbacks, such as probe(), read(), and other optional functions.
-
In the probe() function, register the platform device for the MCP9808 Temperature Sensor with the driver/misc subsystem.
-
In the read() function, send the appropriate I2C messages to read the temperature data from the sensor and copy it to the user space.
-
Set up the GPIO interrupt for the alert pin on the BBB and configure the interrupt handler.
-
Based on the interrupt, turn the motor on or off as per the temperature thresholds set through the DTS file
Once the device driver is written, We can write a user space application to read the temperature data at regular intervals and store it in a database. The web page can be developed using HTML and JavaScript to retrieve the temperature data from the database and plot it as a graph.
The expected outcome of this project is to learn the various steps involved in building an embedded Linux target, configuring the GPIO and I2C subsystem, and understanding the device driver development process.
#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>
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, const struct i2c_device_id *id) {
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 memoryn");
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 = �nd T_UPPER = %dn", t_lower, t_upper);
}
// Register the platform device
pdev = platform_device_alloc("mcp9808", -1);
if (!pdev)
{
dev_err(dev, "Failed to allocate platform devicen"); 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 datan");
platform_device_put(pdev);
return ret;
}
ret = platform_device_add(pdev);
if (ret) {
dev_err(dev, "Failed to add platform devicen");
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 %dn", 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 �irectionn", 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 xss=removed>dev_err(dev, "Failed to get irq from gpio %dn", 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 irqn");
gpio_free(mcp->irq_gpio);
platform_device_unregister(mcp->pdev);
return ret;
}
dev_info(dev, "Registered interrupt handlern");
}
dev_info(dev, "MCP9808 device detectedn");
return 0;
}
static int 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);
return 0;
}
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");
Creating web page:
INTERFACING WITH DATA BASE:
#include
#include
#include
#include
/// Prints out a MySQL error message and exits
///
/// This function should be called after a MySQL error has been encountered. This function will then
/// notify the user of the error that has occurred, clean up the existing MySQL connection, and then
/// exit the program.
///
/// @param The MySQL connection to clean up before exiting
void error_exit(MYSQL *con)
{
fprintf(stderr, "%sn", mysql_error(con));
if (con != NULL)
{
mysql_close(con);
}
exit(1);
}
int main(int argc, const char *argv[])
{
// Initialize a connection to MySQL
MYSQL *con = mysql_init(NULL);
if(con == NULL)
{
error_exit(con);
}
// Connect to MySQL
// Here we pass in:
// host name => localhost
// user name => bone
// password => bone
// database name => TempDB
if (mysql_real_connect(con, "localhost", "bone", "bone", "TempDB", 0, NULL, 0) == NULL)
{
error_exit(con);
}
// Create the TempMeas database (if it doesn't already exist)
if (mysql_query(con, "CREATE TABLE IF NOT EXISTS TempMeas(MeasTime DATETIME, Temp DOUBLE)"))
{
error_exit(con);
}
// Initialize a MySQL statement
MYSQL_STMT *stmt = mysql_stmt_init(con);
if (stmt == NULL)
{
error_exit(con);
}
// Set out insert query as the MySQL statement
const char *query = "INSERT INTO TempMeas(MeasTime, Temp) VALUES(NOW(), ?)";
if (mysql_stmt_prepare(stmt, query, strlen(query)))
{
error_exit(con);
}
// Create the MySQL bind structure to store the data that we are going to insert
double temp = 0.0;
MYSQL_BIND bind;
memset(&bind, 0, sizeof(bind));
bind.buffer_type = MYSQL_TYPE_DOUBLE;
bind.buffer = (char *)&temp;
bind.buffer_length = sizeof(double);
// Bind the data structure to the MySQL statement
if (mysql_stmt_bind_param(stmt, &bind))
{
error_exit(con);
}
// Insert multiple records into the database,
// with different data each time
for (int i = 0; i < 10>
{
temp = (float)i;
mysql_stmt_execute(stmt);
}
// Close the MySQL connection
mysql_close(con);
return 0;
}