⭐C Language Comprehensive Project Practice: Building a Mini IoT Management Platform with ‘Grade Module + Sensor Module + File I/O’
Author: IoT Smart Academy
This article integrates everything learned previously: sequential/branching/looping, arrays, structures, pointers, file I/O, modularization into a comprehensive project that can serve as a final assignment. The article covers: Project Description + Module Division + Data Structure + Menu Design + Core Code + Experimental Tasks, all in one go.
1. Project Background: Creating a ‘Mini IoT Management Platform’
Scenario Setting:
A certain IoT training room requires a command-line management platform written in C to manage:
- Student Grade Information (for teachers to check grades and statistics)
- Sensor Device Information (for laboratory monitoring and debugging)
Technical Requirements (strongly tied to your classroom content):
- Language: Pure C Language
- Knowledge Points: Structures, Arrays, Pointers, File I/O, Modularization, Multi-file Programming
- Interface: Command-line menu (no graphical interface, no networking)
- Storage: Use CSV files (
<span>students.csv</span>,<span>sensors.csv</span>), which can be opened with Excel
Upon completing this project, you will be able to demonstrate:
- How to write a fully functional small system;
- How to modularize code, maintaining clear structure;
- How to give the program “memory” (file persistence).
2. Overall Structure: A Main Menu with Two Subsystems
After running the program, a main menu will appear:
===== IoT Comprehensive Management Platform =====
1. Student Grade Management System
2. Sensor Monitoring System
0. Exit System
===========================
Please enter your choice:
- Select 1 → Enter the 【Student Grade System】 submenu
- Select 2 → Enter the 【Sensor Monitoring System】 submenu

1. Student Grade Management System
===== Student Grade Management System =====
1. Enter Student
2. Show All
3. Query / Modify / Delete by Student ID
4. Sort by Grade / Sort by Student ID
5. Statistics (Highest/Lowest/Average/Pass Rate)
6. Filter by Grade Range
7. Batch Add Points
8. Save to CSV
9. Read from CSV
0. Return to Previous Level
===========================
Please enter your choice:

2. IoT Sensor Monitoring System (Recommended Menu)

===== IoT Sensor Monitoring System =====
1. Enter Device
2. Show All
3. Query / Modify / Delete by ID
4. Filter by Type (Temp/Humi/Smoke...)
5. Filter by Threshold (value >= threshold)
6. Sort by Reading in Descending Order
7. Statistics (Min/Max/Average)
8. Batch Calibrate (Uniform + offset)
9. Save to CSV
10. Read from CSV
0. Return to Previous Level
=============================
Please enter your choice:
In actual implementation, you can first implement the “basic functions” (entering/displaying/saving/reading), and then gradually add other items.
3. Module and File Division: The Project Directory Looks Like This
The project is divided into several modules, each responsible for a specific function:
project/
│
├─ main.c // Top-level menu + Process control
├─ student.h // Student module: Structure + Function declarations
├─ student.c // Student module: Function implementations
├─ sensor.h // Sensor module: Structure + Function declarations
├─ sensor.c // Sensor module: Function implementations
└─ util.h / util.c // Optional: Common utility functions
<span>student.*</span>specifically manages student data<span>sensor.*</span>specifically manages sensor data<span>main.c</span>is only responsible for the menu and calls<span>util.*</span>contains commonly used input functions (<span>readInt</span>,<span>readFloat</span>, etc.), for easy reuse
4. Data Structure Design: Two “Small Databases”
1. Student Data Structure (<span>student.h</span>)
#ifndef STUDENT_H
#define STUDENT_H
#define MAX_STU 300
typedef struct {
int id; // Student ID
char name[20]; // Name (suggest using pinyin without spaces)
float score; // Score
} Student;
typedef struct {
Student arr[MAX_STU];
int count; // Current number of students
} StudentDB;
/* —— Functions provided by the student module —— */
void initStudentDB(StudentDB *db);
void addStudents(StudentDB *db);
void showAllStudents(const StudentDB *db);
void queryUpdateDeleteStudent(StudentDB *db);
void sortStudentsByScore(StudentDB *db);
void sortStudentsById(StudentDB *db);
void statStudents(const StudentDB *db);
void filterStudentsByRange(const StudentDB *db);
void batchBonusStudents(StudentDB *db);
int saveStudentsCSV(const char *path, const StudentDB *db);
int loadStudentsCSV(const char *path, StudentDB *db);
#endif /* STUDENT_H */
2. Sensor Data Structure (<span>sensor.h</span>)
#ifndef SENSOR_H
#define SENSOR_H
#define MAX_SENSOR 200
typedef struct {
int id; // Device ID
char type[16]; // Type: Temp/Humi/Smoke...
char location[32]; // Location: Room101/Room202...
float value; // Current reading
} Sensor;
typedef struct {
Sensor arr[MAX_SENSOR];
int count;
} SensorDB;
/* —— Functions provided by the sensor module —— */
void initSensorDB(SensorDB *db);
void addSensors(SensorDB *db);
void showAllSensors(const SensorDB *db);
void queryUpdateDeleteSensor(SensorDB *db);
void filterSensorsByType(const SensorDB *db);
void filterSensorsByThreshold(const SensorDB *db);
void sortSensorsByValue(SensorDB *db);
void statSensors(const SensorDB *db);
void batchCalibrateSensors(SensorDB *db);
int saveSensorsCSV(const char *path, const SensorDB *db);
int loadSensorsCSV(const char *path, SensorDB *db);
#endif /* SENSOR_H */
3. Optional Utility Module (<span>util.h</span>)
#ifndef UTIL_H
#define UTIL_H
int readInt(const char *tip);
float readFloat(const char *tip);
#endif
5. Key Code ①: Utility Module <span>util.c</span> (optional but highly recommended)
/* util.c */
#include <stdio.h>
#include "util.h"
int readInt(const char *tip) {
int x;
printf("%s", tip);
scanf("%d", &x);
return x;
}
float readFloat(const char *tip) {
float x;
printf("%s", tip);
scanf("%f", &x);
return x;
}
6. Key Code ②: Student Module <span>student.c</span> (Core Functionality)
Here is a set of runnable core implementations, with some remaining functionalities that you can expand upon.
/* student.c */
#include "student.h"
#include "util.h"
#include <stdio.h>
#include <string.h>
/* Initialization */
void initStudentDB(StudentDB *db) {
db->count = 0;
}
/* Enter Student */
void addStudents(StudentDB *db) {
int n = readInt("Please enter the number of students to be recorded:");
if (n <= 0) {
printf("The number must be greater than 0.\n");
return;
}
if (db->count + n > MAX_STU) {
printf("Insufficient capacity, can record %d more.\n", MAX_STU - db->count);
return;
}
for (int i = 0; i < n; i++) {
Student s;
printf("The %d student (format: ID Name Score):\n", db->count + 1);
scanf("%d %19s %f", &s.id, s.name, &s.score);
db->arr[db->count++] = s;
}
printf("Recorded, current total number: %d\n", db->count);
}
/* Show All */
void showAllStudents(const StudentDB *db) {
if (db->count == 0) {
printf("No student data available.\n");
return;
}
printf("\nNo. ID Name Score\n");
for (int i = 0; i < db->count; i++) {
printf("%d %d %s %.2f\n",
i + 1,
db->arr[i].id,
db->arr[i].name,
db->arr[i].score);
}
}
/* Internal Utility: Find Index by Student ID */
static int findStudentPosById(const StudentDB *db, int id) {
for (int i = 0; i < db->count; i++) {
if (db->arr[i].id == id) return i;
}
return -1;
}
/* Query / Modify / Delete by Student ID (in one menu) */
void queryUpdateDeleteStudent(StudentDB *db) {
if (db->count == 0) {
printf("No student data available.\n");
return;
}
printf("\n1 Query by ID\n2 Modify Score\n3 Delete Student\n0 Return\n");
int c = readInt("Please select an operation:");
if (c == 0) return;
int id = readInt("Please enter the ID:");
int pos = findStudentPosById(db, id);
if (pos < 0) {
printf("Student ID %d not found.\n", id);
return;
}
if (c == 1) {
Student *p = &db->arr[pos];
printf("Found: ID=%d Name=%s Score=%.2f\n", p->id, p->name, p->score);
} else if (c == 2) {
float sc = readFloat("Please enter the new score:");
db->arr[pos].score = sc;
printf("Updated: ID=%d New Score=%.2f\n", id, sc);
} else if (c == 3) {
for (int i = pos; i < db->count - 1; i++)
db->arr[i] = db->arr[i + 1];
db->count--;
printf("Deleted ID %d, current number=%d\n", id, db->count);
} else {
printf("Invalid option.\n");
}
}
/* Sort by Score (Descending) */
void sortStudentsByScore(StudentDB *db) {
for (int i = 0; i < db->count - 1; i++) {
for (int j = 0; j < db->count - 1 - i; j++) {
if (db->arr[j].score < db->arr[j + 1].score) {
Student t = db->arr[j];
db->arr[j] = db->arr[j + 1];
db->arr[j + 1] = t;
}
}
}
printf("Sorted by score in descending order.\n");
}
/* Sort by Student ID (Ascending) */
void sortStudentsById(StudentDB *db) {
for (int i = 0; i < db->count - 1; i++) {
for (int j = 0; j < db->count - 1 - i; j++) {
if (db->arr[j].id > db->arr[j + 1].id) {
Student t = db->arr[j];
db->arr[j] = db->arr[j + 1];
db->arr[j + 1] = t;
}
}
}
printf("Sorted by ID in ascending order.\n");
}
/* Statistics (Highest/Lowest/Average/Pass Rate) */
void statStudents(const StudentDB *db) {
if (db->count == 0) {
printf("No student data available.\n");
return;
}
float max = db->arr[0].score;
float min = db->arr[0].score;
float sum = 0.0f;
int pass = 0;
for (int i = 0; i < db->count; i++) {
float x = db->arr[i].score;
if (x > max) max = x;
if (x < min) min = x;
sum += x;
if (x >= 60) pass++;
}
printf("Number: %d Highest: %.2f Lowest: %.2f Average: %.2f Pass Rate: %.2f%%\n",
db->count,
max,
min,
sum / db->count,
100.0f * pass / db->count);
}
/* Filter by Grade Range */
void filterStudentsByRange(const StudentDB *db) {
if (db->count == 0) {
printf("No student data available.\n");
return;
}
float L = readFloat("Please enter the lower limit:");
float R = readFloat("Please enter the upper limit:");
if (L > R) { float t = L; L = R; R = t; }
int found = 0;
printf("\n=== Students in the range [%.2f, %.2f] ===\n", L, R);
for (int i = 0; i < db->count; i++) {
float x = db->arr[i].score;
if (x >= L && x <= R) {
printf("%d %s %.2f\n",
db->arr[i].id,
db->arr[i].name,
x);
found = 1;
}
}
if (!found) printf("No matching records.\n");
}
/* Batch Add Points */
void batchBonusStudents(StudentDB *db) {
if (db->count == 0) {
printf("No student data available.\n");
return;
}
float off = readFloat("Please enter the bonus offset (e.g., +2 / -1.5):");
for (int i = 0; i < db->count; i++) {
db->arr[i].score += off;
if (db->arr[i].score < 0) db->arr[i].score = 0;
if (db->arr[i].score > 100) db->arr[i].score = 100;
}
printf("Batch bonus added (clipped to [0,100]).\n");
}
/* Save to CSV */
int saveStudentsCSV(const char *path, const StudentDB *db) {
FILE *fp = fopen(path, "w");
if (!fp) {
perror("Failed to open file for writing");
return 0;
}
fprintf(fp, "id,name,score\n");
for (int i = 0; i < db->count; i++) {
fprintf(fp, "%d,%s,%.2f\n",
db->arr[i].id,
db->arr[i].name,
db->arr[i].score);
}
fclose(fp);
return 1;
}
/* Read from CSV */
int loadStudentsCSV(const char *path, StudentDB *db) {
FILE *fp = fopen(path, "r");
if (!fp) {
perror("Failed to open file for reading");
return 0;
}
db->count = 0;
char line[256];
if (!fgets(line, sizeof(line), fp)) { // Skip header
fclose(fp);
return 0;
}
while (db->count < MAX_STU && fgets(line, sizeof(line), fp)) {
Student s;
if (sscanf(line, "%d,%19[^,],%f",
&s.id,
s.name,
&s.score) == 3) {
db->arr[db->count++] = s;
}
}
fclose(fp);
return 1;
}
7. Key Code ③: Sensor Module <span>sensor.c</span> (Core Functionality)
/* sensor.c */
#include "sensor.h"
#include "util.h"
#include <stdio.h>
#include <string.h>
/* Initialization */
void initSensorDB(SensorDB *db) {
db->count = 0;
}
/* Enter Device */
void addSensors(SensorDB *db) {
int n = readInt("Please enter the number of devices to be recorded:");
if (n <= 0) {
printf("The number must be greater than 0.\n");
return;
}
if (db->count + n > MAX_SENSOR) {
printf("Insufficient capacity, can record %d more.\n", MAX_SENSOR - db->count);
return;
}
for (int i = 0; i < n; i++) {
Sensor s;
printf("The %d device (format: id type location value):\n", db->count + 1);
scanf("%d %15s %31s %f", &s.id, s.type, s.location, &s.value);
db->arr[db->count++] = s;
}
printf("Recorded, current number of devices: %d\n", db->count);
}
/* Show All Devices */
void showAllSensors(const SensorDB *db) {
if (db->count == 0) {
printf("No device data available.\n");
return;
}
printf("\nID Type Location Value\n");
for (int i = 0; i < db->count; i++) {
printf("%d %-6s %-16s %.2f\n",
db->arr[i].id,
db->arr[i].type,
db->arr[i].location,
db->arr[i].value);
}
}
/* Internal Utility: Find Index by Device ID */
static int findSensorPosById(const SensorDB *db, int id) {
for (int i = 0; i < db->count; i++) {
if (db->arr[i].id == id) return i;
}
return -1;
}
/* Query / Modify / Delete by ID */
void queryUpdateDeleteSensor(SensorDB *db) {
if (db->count == 0) {
printf("No device data available.\n");
return;
}
printf("\n1 Query by ID\n2 Modify Reading\n3 Delete Device\n0 Return\n");
int c = readInt("Please select an operation:");
if (c == 0) return;
int id = readInt("Please enter the ID:");
int pos = findSensorPosById(db, id);
if (pos < 0) {
printf("Device ID %d not found.\n", id);
return;
}
if (c == 1) {
Sensor *p = &db->arr[pos];
printf("Found: ID=%d Type=%s Location=%s Value=%.2f\n",
p->id, p->type, p->location, p->value);
} else if (c == 2) {
float v = readFloat("Please enter the new reading:");
db->arr[pos].value = v;
printf("Updated: ID=%d New Reading=%.2f\n", id, v);
} else if (c == 3) {
for (int i = pos; i < db->count - 1; i++)
db->arr[i] = db->arr[i + 1];
db->count--;
printf("Deleted ID %d, current number of devices=%d\n", id, db->count);
} else {
printf("Invalid option.\n");
}
}
/* Filter by Type */
void filterSensorsByType(const SensorDB *db) {
if (db->count == 0) {
printf("No device data available.\n");
return;
}
char t[16];
printf("Please enter the type (e.g., Temp/Humi/Smoke):");
scanf("%15s", t);
int found = 0;
printf("\n=== Devices of type %s ===\n", t);
for (int i = 0; i < db->count; i++) {
if (strcmp(db->arr[i].type, t) == 0) {
printf("%d %s %s %.2f\n",
db->arr[i].id,
db->arr[i].type,
db->arr[i].location,
db->arr[i].value);
found = 1;
}
}
if (!found) printf("No devices of this type found.\n");
}
/* Filter by Threshold (>= threshold) */
void filterSensorsByThreshold(const SensorDB *db) {
if (db->count == 0) {
printf("No device data available.\n");
return;
}
float th = readFloat("Please enter the threshold (filter devices with readings >= threshold):");
int found = 0;
printf("\n=== Devices with readings >= %.2f ===\n", th);
for (int i = 0; i < db->count; i++) {
float x = db->arr[i].value;
if (x >= th) {
printf("%d %s %s %.2f\n",
db->arr[i].id,
db->arr[i].type,
db->arr[i].location,
x);
found = 1;
}
}
if (!found) printf("No devices exceeding the threshold.\n");
}
/* Sort by Reading in Descending Order */
void sortSensorsByValue(SensorDB *db) {
if (db->count <= 1) {
printf("Insufficient data, no need to sort.\n");
return;
}
for (int i = 0; i < db->count - 1; i++) {
for (int j = 0; j < db->count - 1 - i; j++) {
if (db->arr[j].value < db->arr[j + 1].value) {
Sensor t = db->arr[j];
db->arr[j] = db->arr[j + 1];
db->arr[j + 1] = t;
}
}
}
printf("Sorted by readings in descending order.\n");
}
/* Statistics (Min/Max/Average) */
void statSensors(const SensorDB *db) {
if (db->count == 0) {
printf("No device data available.\n");
return;
}
float min = db->arr[0].value;
float max = db->arr[0].value;
float sum = 0.0f;
for (int i = 0; i < db->count; i++) {
float x = db->arr[i].value;
if (x < min) min = x;
if (x > max) max = x;
sum += x;
}
printf("Number of devices: %d Minimum: %.2f Maximum: %.2f Average: %.2f\n",
db->count, min, max, sum / db->count);
}
/* Batch Calibrate: Uniformly add offset */
void batchCalibrateSensors(SensorDB *db) {
if (db->count == 0) {
printf("No device data available.\n");
return;
}
float off = readFloat("Please enter the calibration offset (e.g., +0.5 / -0.5):");
for (int i = 0; i < db->count; i++) {
db->arr[i].value += off;
}
printf("Batch calibrated (offset=%.2f).\n", off);
}
/* Save to CSV */
int saveSensorsCSV(const char *path, const SensorDB *db) {
FILE *fp = fopen(path, "w");
if (!fp) {
perror("Failed to open file for writing");
return 0;
}
fprintf(fp, "id,type,location,value\n");
for (int i = 0; i < db->count; i++) {
fprintf(fp, "%d,%s,%s,%.2f\n",
db->arr[i].id,
db->arr[i].type,
db->arr[i].location,
db->arr[i].value);
}
fclose(fp);
return 1;
}
/* Read from CSV */
int loadSensorsCSV(const char *path, SensorDB *db) {
FILE *fp = fopen(path, "r");
if (!fp) {
perror("Failed to open file for reading");
return 0;
}
db->count = 0;
char line[256];
if (!fgets(line, sizeof(line), fp)) { // Skip header
fclose(fp);
return 0;
}
while (db->count < MAX_SENSOR && fgets(line, sizeof(line), fp)) {
Sensor s;
if (sscanf(line, "%d,%15[^,],%31[^,],%f",
&s.id,
s.type,
s.location,
&s.value) == 4) {
db->arr[db->count++] = s;
}
}
fclose(fp);
return 1;
}
8. Key Code ④: Main Program <span>main.c</span> — Main Menu + Submenus
/* main.c */
#include <stdio.h>
#include "student.h"
#include "sensor.h"
#include "util.h"
/* Top-level Menu */
int mainMenu() {
int c;
printf("\n===== IoT Comprehensive Management Platform =====\n");
printf("1 Student Grade Management System\n");
printf("2 Sensor Monitoring System\n");
printf("0 Exit System\n");
printf("================================\n");
printf("Please enter your choice:");
scanf("%d", &c);
return c;
}
/* Student Submenu */
int studentMenu() {
int c;
printf("\n===== Student Grade Management System =====\n");
printf("1 Enter Student\n");
printf("2 Show All\n");
printf("3 Query/Modify/Delete by ID\n");
printf("4 Sort by Grade / Sort by ID\n");
printf("5 Statistics (Highest/Lowest/Average/Pass Rate)\n");
printf("6 Filter by Grade Range\n");
printf("7 Batch Add Points\n");
printf("8 Save to CSV\n");
printf("9 Read from CSV\n");
printf("0 Return to Previous Level\n");
printf("====================================\n");
printf("Please enter your choice:");
scanf("%d", &c);
return c;
}
/* Sensor Submenu */
int sensorMenu() {
int c;
printf("\n===== IoT Sensor Monitoring System =====\n");
printf("1 Enter Device\n");
printf("2 Show All\n");
printf("3 Query/Modify/Delete by ID\n");
printf("4 Filter by Type\n");
printf("5 Filter by Threshold\n");
printf("6 Sort by Reading in Descending Order\n");
printf("7 Statistics (Min/Max/Average)\n");
printf("8 Batch Calibrate\n");
printf("9 Save to CSV\n");
printf("10 Read from CSV\n");
printf("0 Return to Previous Level\n");
printf("====================================\n");
printf("Please enter your choice:");
scanf("%d", &c);
return c;
}
/* Main Function */
int main() {
StudentDB stuDB;
SensorDB senDB;
initStudentDB(&stuDB);
initSensorDB(&senDB);
while (1) {
switch (mainMenu()) {
case 1: { // Student System
int c;
while ((c = studentMenu()) != 0) {
switch (c) {
case 1: addStudents(&stuDB); break;
case 2: showAllStudents(&stuDB); break;
case 3: queryUpdateDeleteStudent(&stuDB); break;
case 4: {
printf("1 Sort by Grade 2 Sort by ID:");
int t; scanf("%d", &t);
if (t == 1) sortStudentsByScore(&stuDB);
else sortStudentsById(&stuDB);
showAllStudents(&stuDB);
} break;
case 5: statStudents(&stuDB); break;
case 6: filterStudentsByRange(&stuDB); break;
case 7: batchBonusStudents(&stuDB); break;
case 8:
if (saveStudentsCSV("students.csv", &stuDB))
printf("Saved to students.csv\n");
else
printf("Save failed\n");
break;
case 9:
if (loadStudentsCSV("students.csv", &stuDB)) {
printf("Read from students.csv, current number: %d\n", stuDB.count);
showAllStudents(&stuDB);
} else {
printf("Read failed\n");
}
break;
default:
printf("Invalid option.\n");
}
}
} break;
case 2: { // Sensor System
int c;
while ((c = sensorMenu()) != 0) {
switch (c) {
case 1: addSensors(&senDB); break;
case 2: showAllSensors(&senDB); break;
case 3: queryUpdateDeleteSensor(&senDB); break;
case 4: filterSensorsByType(&senDB); break;
case 5: filterSensorsByThreshold(&senDB); break;
case 6: sortSensorsByValue(&senDB); showAllSensors(&senDB); break;
case 7: statSensors(&senDB); break;
case 8: batchCalibrateSensors(&senDB); break;
case 9:
if (saveSensorsCSV("sensors.csv", &senDB))
printf("Saved to sensors.csv\n");
else
printf("Save failed\n");
break;
case 10:
if (loadSensorsCSV("sensors.csv", &senDB)) {
printf("Read from sensors.csv, current number of devices: %d\n", senDB.count);
showAllSensors(&senDB);
} else {
printf("Read failed\n");
}
break;
default:
printf("Invalid option.\n");
}
}
} break;
case 0:
printf("System exited, goodbye!\n");
return 0;
default:
printf("Invalid option, please re-enter.\n");
}
}
}
9. How to Compile and Run?
Assuming all files are in the same directory:
1) gcc (Linux / WSL / MinGW)
gcc main.c student.c sensor.c util.c -o iot_app
./iot_app
2) Dev-C++ / VS / Code::Blocks
- Create a “Console Project”;
- Add all
<span>main.c / student.c / sensor.c / util.c</span>to the project; - Ensure
<span>*.h</span>files are in the same directory; - Compile and run directly.
10. Grading Suggestions for the “Final Assignment”
Grading can be based on 4 dimensions:
-
Functionality (40 points)
- Student Grade Module: Basic Functions + Statistics (20 points)
- Sensor Module: Basic Functions + Statistics (15 points)
- Correct CSV File Read/Write (5 points)
Code Structure and Standards (30 points)
- Multi-file modularization, clear structure (10 points)
- Variable naming conventions, reasonable comments (10 points)
- No obvious duplicate code, appropriate function encapsulation (10 points)
Robustness and User Experience (20 points)
- Error inputs do not crash directly, provide prompts (10 points)
- Clear menus, neat output, complete information (10 points)
Expansion and Innovation (10 points)
- Add extended functions (e.g., topN, fuzzy queries, alarm statistics, etc.) (6 points)
- Simple project report or user manual (4 points)
11. Checklist for Students (Self-check before Submission)
- Can enter the main menu and two submenus normally
- Student Grade System: Can enter, display, query, modify, delete, sort, save/read
- Sensor System: Can enter, display, query, modify, delete, filter, sort, save/read
- Generated
<span>students.csv</span>and<span>sensors.csv</span>, can be opened correctly with Excel - No obvious array out-of-bounds, infinite loops, random crashes
- Code is clearly modularized, with basic comments
Complete code: Reply to the public account IoT Comprehensive Management Platform