Practical Guide to C Language Pointers: Upgraded IoT Sensor Monitoring System (Menu + Structs + Pointers + Sorting + Alarms)

⭐Practical Guide to C Language Pointers: Upgraded IoT Sensor Monitoring System (Menu + Structs + Pointers + Sorting + Alarms)

Author: IoT Smart Academy

This article skips the concepts and directly dives into the project: integrating “structs + arrays + pointers” to create an upgraded sensor monitoring system that can be demonstrated in class and easily replicated by students.

Features:

  • All functionalities are implemented using pointer parameters for modifications
  • Supports add/view/update/delete/filter/sort/statistics/batch calibration
  • The code is single-file runnable, suitable for Dev-C++ / VS / Linux g++ environments

🧭 Overview of System Functions (Menu)

  • 1 Add Sensor
  • 2 Show All
  • 3 Query by ID
  • 4 Update Reading by ID (Pointer Modification)
  • 5 Delete Device by ID (Array Shift)
  • 6 Filter by Type (Temp/Humi/Smoke…)
  • 7 Filter Alarms by Threshold (β‰₯threshold)
  • 8 Sort by Reading in Descending Order (Pointer Swap Structs)
  • 9 Statistics for Min/Max/Average
  • 10 Batch Calibration (All Readings +offset)
  • 0 Exit

πŸ’» Complete Runnable Code (Copy and Use)

#include <stdio.h>
#include <string.h>

#define MAX_SENSOR 200

typedef struct {
    int   id;            // ID
    char  type[16];      // Type: Temp/Humi/Smoke/…
    char  location[32];  // Location: Room101/Lab/…
    float value;         // Current Reading
} Sensor;

typedef struct {
    Sensor arr[MAX_SENSOR];
    int    count;        // Current Device Count
} SensorDB;

/* β€”β€” Function Declarations β€”β€” */
void initDB(SensorDB *db);
void addSensors(SensorDB *db);
void showAll(const SensorDB *db);
Sensor* findById(SensorDB *db, int id);
void updateById(SensorDB *db);
void deleteById(SensorDB *db);
void filterByType(const SensorDB *db);
void filterByThreshold(const SensorDB *db);
void sortByValueDesc(SensorDB *db);
void statistics(const SensorDB *db);
void batchCalibrate(SensorDB *db);

int menu();

/* β€”β€” Main Program β€”β€” */
int main() {
    SensorDB db;
    initDB(&db);

    while (1) {
        switch (menu()) {
            case 1:  addSensors(&db);        break;
            case 2:  showAll(&db);           break;
            case 3: {
                int id;
                printf("Please enter the ID to query:");
                scanf("%d", &id);
                Sensor *p = findById(&db, id);
                if (p) {
                    printf("Found: id=%d type=%s loc=%s value=%.2f\n",
                           p->id, p->type, p->location, p->value);
                } else {
                    printf("Device with ID %d not found.\n", id);
                }
            } break;
            case 4:  updateById(&db);        break;
            case 5:  deleteById(&db);        break;
            case 6:  filterByType(&db);      break;
            case 7:  filterByThreshold(&db); break;
            case 8:  sortByValueDesc(&db);   break;
            case 9:  statistics(&db);        break;
            case 10: batchCalibrate(&db);    break;
            case 0:
                printf("System exited, goodbye!\n");
                return 0;
            default:
                printf("Invalid option, please try again.\n");
        }
    }
}

/* β€”β€” Menu β€”β€” */
int menu() {
    int c;
    printf("\n===== Upgraded IoT Sensor Monitoring System =====\n");
    printf("1. Add Sensor\n");
    printf("2. Show All\n");
    printf("3. Query by ID\n");
    printf("4. Update Reading by ID\n");
    printf("5. Delete Device by ID\n");
    printf("6. Filter by Type\n");
    printf("7. Filter Alarms by Threshold (>=)\n");
    printf("8. Sort by Reading Descending\n");
    printf("9. Statistics for Min/Max/Average\n");
    printf("10. Batch Calibration (+offset)\n");
    printf("0. Exit\n");
    printf("=====================================\n");
    printf("Please enter an option:");
    scanf("%d", &c);
    return c;
}

/* β€”β€” Initialize Database β€”β€” */
void initDB(SensorDB *db) {
    db->count = 0;
}

/* β€”β€” Add Sensors β€”β€” */
void addSensors(SensorDB *db) {
    int n;
    printf("Please enter the number of sensors to add:");
    scanf("%d", &n);
    if (n <= 0) { printf("Number must be greater than 0.\n"); return; }
    if (db->count + n > MAX_SENSOR) {
        printf("Insufficient capacity, can add %d more.\n", MAX_SENSOR - db->count);
        return;
    }
    for (int i = 0; i < n; i++) {
        Sensor s;
        printf("Please enter (id type location value):\n");
        printf("Example: 101 Temp Room101 26.5\n");
        scanf("%d %15s %31s %f", &s.id, s.type, s.location, &s.value);

        db->arr[db->count++] = s;
    }
    printf("Added, current device count: %d\n", db->count);
}

/* β€”β€” Show All (Read-Only, using const pointer) β€”β€” */
void showAll(const SensorDB *db) {
    if (db->count == 0) { printf("No data available.\n"); return; }
    printf("\nID\tType\tLocation\t	Value\n");
    for (int i = 0; i < db->count; i++) {
        printf("%d\t%-5s\t%-16s\t%.2f\n",
               db->arr[i].id, db->arr[i].type, db->arr[i].location, db->arr[i].value);
    }
}

/* β€”β€” Find by ID (returns pointer for subsequent modification) β€”β€” */
Sensor* findById(SensorDB *db, int id) {
    for (int i = 0; i < db->count; i++) {
        if (db->arr[i].id == id) return &db->arr[i];
    }
    return NULL;
}

/* β€”β€” Update Reading (Pointer Modification of Original Data) β€”β€” */
void updateById(SensorDB *db) {
    int id; float v;
    printf("Please enter ID and new reading (id value):");
    scanf("%d %f", &id, &v);
    Sensor *p = findById(db, id);
    if (p) {
        p->value = v;  // Direct modification via pointer
        printf("Updated: id=%d new value=%.2f\n", id, p->value);
    } else {
        printf("Device with ID %d not found.\n", id);
    }
}

/* β€”β€” Delete (by ID, subsequent elements shift) β€”β€” */
void deleteById(SensorDB *db) {
    if (db->count == 0) { printf("No data available.\n"); return; }
    int id, pos = -1;
    printf("Please enter the ID to delete:");
    scanf("%d", &id);
    for (int i = 0; i < db->count; i++) {
        if (db->arr[i].id == id) { pos = i; break; }
    }
    if (pos == -1) { printf("ID %d not found.\n", id); return; }

    for (int i = pos; i < db->count - 1; i++) db->arr[i] = db->arr[i + 1];
    db->count--;
    printf("Deleted ID %d, current device count: %d\n", id, db->count);
}

/* β€”β€” Filter by Type (Read-Only) β€”β€” */
void filterByType(const SensorDB *db) {
    if (db->count == 0) { printf("No data available.\n"); return; }
    char t[16]; int found = 0;
    printf("Please enter type (e.g., Temp/Humi/Smoke):");
    scanf("%15s", t);

    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\t%-5s\t%-16s\t%.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 (Read-Only) β€”β€” */
void filterByThreshold(const SensorDB *db) {
    if (db->count == 0) { printf("No data available.\n"); return; }
    float th; int found = 0;
    printf("Please enter threshold (show devices with >= threshold):");
    scanf("%f", &th);

    printf("\n=== Devices with >= %.2f ===\n", th);
    for (int i = 0; i < db->count; i++) {
        if (db->arr[i].value >= th) {
            printf("%d\t%-5s\t%-16s\t%.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 above threshold.\n");
}

/* β€”β€” Sort by Reading in Descending Order (Pointer Swap Structs) β€”β€” */
void sortByValueDesc(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 tmp = db->arr[j];
                db->arr[j] = db->arr[j + 1];
                db->arr[j + 1] = tmp;
            }
        }
    }
    printf("Sorted by reading in descending order.\n");
    showAll(db);
}

/* β€”β€” Statistics β€”β€” */
void statistics(const SensorDB *db) {
    if (db->count == 0) { printf("No data available.\n"); return; }
    float min = db->arr[0].value, max = db->arr[0].value, sum = 0.0f;
    for (int i = 0; i < db->count; i++) {
        if (db->arr[i].value < min) min = db->arr[i].value;
        if (db->arr[i].value > max) max = db->arr[i].value;
        sum += db->arr[i].value;
    }
    printf("Device Count: %d\nMin: %.2f\nMax: %.2f\nAverage: %.2f\n",
           db->count, min, max, sum / db->count);
}

/* β€”β€” Batch Calibration (All Readings +offset) β€”β€” */
void batchCalibrate(SensorDB *db) {
    if (db->count == 0) { printf("No data available.\n"); return; }
    float offset;
    printf("Please enter calibration offset (e.g., +0.5 or -0.5):");
    scanf("%f", &offset);
    for (int i = 0; i < db->count; i++) {
        db->arr[i].value += offset;
    }
    printf("Batch calibrated (offset=%.2f).\n", offset);
    showAll(db);
}

πŸͺ„ How to Teach in Class, “Both Professional and Not Intimidating”

  • All functions that modify data: parameters are either pointers or wrapped “database” with pointers (<span>SensorDB *db</span>)

    Memory Tip: To modify, you need to provide the address; functions use pointers to modify in place.

  • Read-only functions: parameters use <span>const SensorDB *db</span>, clearly indicating “read-only, no modification”, more professional.

  • Deletion Logic: after finding the position, “shift and cover”, no dynamic memory, suitable for introductory courses.

  • Sorting/Statistics: reuse previously learned bubble sort and max/min/avg templates, but change the objects to struct arrays.

  • **<span>findById</span> returns <span>Sensor*</span>: can directly use <span>p->value=...</span>, experiencing the power of “direct modification via pointers”.

πŸ§ͺ Suggested Test Cases for Class (Live Demonstration by Teacher)

1 Add Number=4
101 Temp Room101 26.5
102 Temp Lab     27.8
201 Humi Store   60.2
301 Smoke Hall   0.12

2 Show All
7 Threshold=27.0
6 Type=Temp
4 Update: id=201 New Value=65.0
8 Sort (Descending)
9 Statistics
10 Batch Calibration: offset=-0.5
5 Delete: id=301
2 Show All

Students can visually see:Data has indeed been “modified in place”.

🧱 Homework (Step-by-Step, All Based on This Code)

  1. Add:Sort by ID (ascending) and display.
  2. Add:Filter by ID (location contains substring).
  3. Add:Export devices above threshold to array B (return count), experiencing “pointers + return count”.
  4. Add:Statistics for average value of each type (Temp/Humi/Smoke respectively).
  5. Consider: Saving/Loading <span>SensorDB</span> to/from disk (text or simple binary), forming a “small system with persistence”.

🧩 One-Screen Command Card (For Students)

  • To modify, pass the address:<span>func(&x)</span><span> / </span><code><span>func(db)</span><span> (struct pointer)</span>
  • Function uses pointers:<span>void func(SensorDB *db)</span>
  • Access struct pointer:<span>p->value</span><span> (equivalent to </span><code><span>(*p).value</span><span>)</span>
  • Array is base address:<span>arr</span> can be passed, inside function <span>arr[i]</span> / <span>*(arr+i)</span><span> both work</span>
  • Read-only parameters add <span>const</span><span>, comes with a "safety sign"</span>

Leave a Comment