160. Board-Independent Status Indicator

The firmware must provide a system status indicator used by application logic to reflect runtime state.

The same application firmware must run on different hardware boards with incompatible GPIO wiring and control logic.

The target hardware board must be selected at build time and must never be chosen at runtime.

It must be impossible to build firmware that targets more than one hardware board at the same time.

The current codebase does not enforce this separation and cannot support multiple boards correctly.

Your task is to redesign the code so hardware differences are isolated behind a proper abstraction.

Program Flow:

  1. The program reads an integer N, representing the number of system events.
  2. The program reads N event values sequentially.
  3. An event value of 1 requests the status indicator to toggle.
  4. An event value of 0 requests no change.
  5. After each event, the current status indicator state is printed.

Input Format:
Input is provided via standard input (stdin).

  • First value:
    • N (integer)
    • Range: 1 ≤ N ≤ 20
  • Next N values:
    • Integer event codes (0 or 1)
    • Values may be separated by spaces or newlines

Output Format:
Print exactly N lines.

Each line must be exactly:

STATUS=ON 

or

STATUS=OFF 

Rules:

  • Initial status indicator state is OFF
  • Output order must match input order
  • No extra spaces or text

Example:

Example 1
Input:

5
1 1 0 1 1

Output:

STATUS=ON
STATUS=OFF
STATUS=OFF
STATUS=ON
STATUS=OFF

Constraints:

  • Exactly one hardware board must be selected at build time
  • Runtime board selection is forbidden
  • Application code must not access GPIO registers or hardware-specific APIs
  • Invalid build configurations must fail compilation
  • main() must not be modified
  • No dynamic memory allocation
  • No STL containers
  • Deterministic behavior required

Validation Requirement:
To validate portability:

  • Build and run the program with BOARD_TYPE_B selected
  • Then comment BOARD_TYPE_B, uncomment BOARD_TYPE_A, and rebuild
  • The program must compile and run correctly in both cases without changing application logic

 

 

 

Need Help? Refer to the Quick Guide below

Abstraction is the process of exposing only the essential features of an object while hiding the complex implementation details ("the wiring") from the user.

Think of a Car:

  • Abstraction (Interface): Steering wheel, pedals, gear stick. (What the user sees).
  • Implementation (Hidden Details): Fuel injection timing, combustion cycles, differential gears. (What happens inside).

In C++, we achieve this using Access Specifiers (public/private) and Abstract Classes (Interfaces).

Syntax & Usage

1. Data Abstraction (The Public API)

Designing a class where the user sees simple functions, but the complex logic happens privately.

class WiFiModule {
private:
    // Complex hidden details (User doesn't need to see these)
    void spi_write(uint8_t byte) { /* ... */ }
    void handshake_tcp() { /* ... */ }
    int socket_id;

public:
    // Simple Abstraction (User sees only this)
    void connect(const char* ssid, const char* pass) {
        spi_write(0x01); // Internal logic
        handshake_tcp(); // Internal logic
    }
};

int main() {
    WiFiModule wifi;
    // The user calls one simple function.
    // They don't know (or care) that it triggered 50 SPI transactions.
    wifi.connect("HomeNet", "1234");
}

2. Abstract Classes (Pure Interfaces)

Defining a blueprint that enforces what a device must do, without defining how.

// Abstract Base Class
class IMotor {
public:
    virtual void setSpeed(int speed) = 0; // Pure Virtual
    virtual void stop() = 0;
};

// The user code works with the "IMotor" abstraction,
// ignoring whether it's a DC Motor or Stepper Motor.
void emergency_shutdown(IMotor* m) {
    m->stop();
}

Abstraction vs. Encapsulation

These two are often confused but are distinct.

FeatureEncapsulationAbstraction
FocusInformation Hiding.Implementation Hiding.
GoalProtect data from external corruption.Reduce complexity for the user.
MechanismGetters/Setters, private variables.Interfaces, Abstract Classes.
AnalogyThe plastic casing around a wire.The simple "On/Off" switch.

Relevance in Embedded/Firmware

1. HAL (Hardware Abstraction Layer)

This is the textbook definition of abstraction in firmware.

You write code like GPIO_Write(PIN_5, HIGH).

  • Abstraction: "Set Pin 5 High".
  • Implementation: On AVR, this writes to PORTB. On STM32, it writes to BSRR register. On Linux, it writes to a file /sys/class/gpio. Your application logic relies on the abstraction, making it portable.

2. Reducing Cognitive Load

A Junior Developer can use a complex driver (e.g., a FAT32 filesystem wrapper) by just calling file.open() and file.write(). They don't need to understand sectors, clusters, or allocation tables to use it effectively.

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Leaky Abstractions

When implementation details "leak" out.

Example: A generic Motor class having a function setStepperMicrosteps(). This breaks the abstraction because not all motors are steppers.

❌ Over-AbstractionCreating wrappers around wrappers (Driver -> Hal -> LL -> Register). Too many layers add overhead and make debugging harder ("Spaghetti Code"). Keep it flat where possible.
✅ Design from the User's ViewWhen writing a class, write the main() code first (how you want to use it). Then implement the class to match that simple API.