Hello Experts,
I’m currently working on an RFID-Based Door Access Control System ( hobby project ) that manages room access through RFID authentication, time-based shift validation, and event logging. The system will use PIC Microcontroller and interfaces with several peripherals:
System Components:
Operational Flow:
1. Initialization:
Upon star-tup, the MCU initializes all peripherals including UART, I2C, GPIO, LCD, RTC, EEPROM, RFID reader, relays, LEDs, buzzer, and buttons.
2. RFID Authentication:
When an RFID card is scanned using the EM-18 reader, the card ID is transmitted to the MCU via UART. The MCU checks the card ID against the stored employee data.
3. Shift Validation:
The current time is retrieved from the RTC DS1307 via I2C. The system compares this time with the shift timings assigned to the cardholder. If the card is valid and the time falls within the allowed shift, access is granted.
4. Access Control:
- The relay is activated to unlock the door.
- A green LED lights up and the buzzer sounds briefly.
- The LCD displays a success message.
- The entry event is logged to EEPROM with card ID, timestamp, and status.
5. Access Denial:
If the card is invalid or the time is outside the allowed shift:
- A red LED lights up and the buzzer sounds differently.
- The LCD displays a denial message.
- No access is granted and the event may be logged for auditing.
6. Exit Mechanism:
- The user presses a button to exit the room.
- The relay is activated again to unlock the door.
- The exit event is logged to EEPROM with timestamp and status.
7. System Health Monitoring:
- The system periodically checks the status of critical peripherals (RTC, EEPROM, RFID reader).
- If a fault is detected, the system logs the error and alerts the user via LEDs or buzzer.
8. Debugging Support:
- Debug messages are sent via UART or displayed on the LCD.
- These messages help developers trace issues during development and deployment.
but as the project grows, maintaining all the logic inside a single or few files is getting messy. I want to structure the project for better modularity, portability, and maintainability. Ideally, if I switch from PIC to STM32, or upgrade a peripheral (for example, use a different EEPROM), I shouldn’t need to rewrite my main application just swap or modify the driver.
My plan is to clearly separate the project into layers so that:
The goal is to keep the application layer hardware-independent. For example, the app would simply call EEPROM_Write() without caring whether it’s talking to an AT24C02 or another chip that detail is handled by the driver, and the driver itself calls the MCU-specific functions from the platform layer.
Here’s the structure I’m planning to follow:
I’d really appreciate some expert feedback on the structure I’ve come up with.
Does this kind of layered approach make sense for a large-sized embedded project like this?
And are there any better practices or commonly used approach to achieve s modularity, portability, and maintainability in such systems?
Thanks for any advice or suggestions. I’d like to hear how others usually handle this kind of project organization.
I’m currently working on an RFID-Based Door Access Control System ( hobby project ) that manages room access through RFID authentication, time-based shift validation, and event logging. The system will use PIC Microcontroller and interfaces with several peripherals:
System Components:
- PIC18F45K22 microcontroller Unit (MCU)
- EM-18 RFID Reader (UART communication)
- RTC DS1307 (I2C communication)
- EEPROM (I2C communication)
- 16x2 LCD Display
- Relay Module
- Buzzer
- Red and Green LEDs
- Push Buttons
- PC for data loading via UART
Operational Flow:
1. Initialization:
Upon star-tup, the MCU initializes all peripherals including UART, I2C, GPIO, LCD, RTC, EEPROM, RFID reader, relays, LEDs, buzzer, and buttons.
2. RFID Authentication:
When an RFID card is scanned using the EM-18 reader, the card ID is transmitted to the MCU via UART. The MCU checks the card ID against the stored employee data.
3. Shift Validation:
The current time is retrieved from the RTC DS1307 via I2C. The system compares this time with the shift timings assigned to the cardholder. If the card is valid and the time falls within the allowed shift, access is granted.
4. Access Control:
- The relay is activated to unlock the door.
- A green LED lights up and the buzzer sounds briefly.
- The LCD displays a success message.
- The entry event is logged to EEPROM with card ID, timestamp, and status.
5. Access Denial:
If the card is invalid or the time is outside the allowed shift:
- A red LED lights up and the buzzer sounds differently.
- The LCD displays a denial message.
- No access is granted and the event may be logged for auditing.
6. Exit Mechanism:
- The user presses a button to exit the room.
- The relay is activated again to unlock the door.
- The exit event is logged to EEPROM with timestamp and status.
7. System Health Monitoring:
- The system periodically checks the status of critical peripherals (RTC, EEPROM, RFID reader).
- If a fault is detected, the system logs the error and alerts the user via LEDs or buzzer.
8. Debugging Support:
- Debug messages are sent via UART or displayed on the LCD.
- These messages help developers trace issues during development and deployment.
but as the project grows, maintaining all the logic inside a single or few files is getting messy. I want to structure the project for better modularity, portability, and maintainability. Ideally, if I switch from PIC to STM32, or upgrade a peripheral (for example, use a different EEPROM), I shouldn’t need to rewrite my main application just swap or modify the driver.
My plan is to clearly separate the project into layers so that:
- The application logic (RFID authentication, shift checking, logging, etc.) is isolated from hardware-specific code.
- The drivers are written in a generic way so they can be reused across platforms.
- The MCU-specific implementations (like register-level UART, I2C, GPIO) live in a dedicated platform layer.
The goal is to keep the application layer hardware-independent. For example, the app would simply call EEPROM_Write() without caring whether it’s talking to an AT24C02 or another chip that detail is handled by the driver, and the driver itself calls the MCU-specific functions from the platform layer.
Here’s the structure I’m planning to follow:
Code:
RFID_Access_System/
src/ → application logic (RFID, logging, display, etc.)
drivers/ → generic hardware drivers (UART, I2C, LCD, EEPROM, etc.)
platform/ → MCU-specific code (PIC, ATmega, STM32, etc.)
include/ → all header files
config/ → pin mappings, constants, and macros
utils/ → diagnostics, error codes
README.md
Does this kind of layered approach make sense for a large-sized embedded project like this?
And are there any better practices or commonly used approach to achieve s modularity, portability, and maintainability in such systems?
Thanks for any advice or suggestions. I’d like to hear how others usually handle this kind of project organization.
Last edited: