Course: COMP 233
Time: 12:30 PM - 01:25 PM on Monday, Wednesday, Friday
Location: Chambers Technology Center 113 (CTC 113)
Motivation: the challenges of building software that touches the physical world.
Industry 4.0 is not about writing more code. It is about engineering systems that interact with nature, machines, and data.
Sense → process → decide → act (repeated continuously)
A discipline for building systems that last.
In liability cases, lawyers typically try to show a specific product issue contributed to the accident, such as a manufacturing defect, design defect, software malfunction, inadequate testing, failure to warn, or misleading marketing about the system’s capabilities.
| Programming | Software Engineering |
|---|---|
| Make it work | Make it work reliably over time |
| Single developer focus | Team focus, handoffs, documentation |
| Short-term code | Long-term system maintenance |
| Local correctness | System-level correctness + safety |
Constraints and trade-offs are unavoidable.
A connected environment for software.
| Sensor | Measures | Why It Matters |
|---|---|---|
| Temperature | Heat | Safety, quality control |
| Accelerometer | Motion / vibration | Fault detection, wear |
| GPS | Location | Tracking assets, logistics |
| Pressure | Force | Industrial control, monitoring |
{
"deviceId": "plant-hydration-042",
"deviceType": "soil_moisture_sensor",
"location": {
"plantId": "tomato-17"
},
"measurement": {
"type": "soil_moisture",
"value": 23.4,
"unit": "percent"
},
"timestamp": "2026-01-12T09:14:32Z",
"conditions": {
"temperatureC": 21.8,
}
}
| Challenge | What It Means | Impact |
|---|---|---|
| Noise | Measurements are imperfect | False alarms, wrong decisions |
| Missing data | Sensor downtime | Gaps in monitoring |
| Scale | Too much data to store/process | Cost + performance issues |
Timing is part of correctness.
| Problem | Example | Result |
|---|---|---|
| High latency | Slow network | Delayed response |
| Overload | Too many events | Dropped messages |
| Wrong timing | Out-of-order events | Incorrect decisions |
Complexity comes from connections.
| Failure | Cause | Effect |
|---|---|---|
| Incompatible interfaces | API mismatch | System breaks at runtime |
| Inconsistent data | Different formats | Wrong results |
| Single point of failure | Central dependency | Outage spreads |
Connectivity increases attack surface.
| Asset | Risk | Example |
|---|---|---|
| Devices | Tampering | Compromised sensor |
| Data | Leakage | Exposed telemetry |
| Control | Hijacking | Unauthorized commands |
Systems must keep working under stress.
Design for faults, not perfect conditions.
Most work happens after version 1.
| Changes Often | Lasts Longer |
|---|---|
| Tools and frameworks | Data and system behavior |
| Hardware generations | Interfaces and requirements |
| Deployment platforms | Engineering fundamentals |
Industry 4.0 systems require engineering discipline: correct behavior, real-time performance, secure integration, and long-term maintenance.
Join Slack
[ ] Open Slack link
[ ] Click "Join"
[ ] Confirm participation
Communication Hub
┌─────────┐
│ Slack │
└───┬─────┘
│ team channels
▼
┌─────────┐
│ Teams │
└─────────┘
Create Team Channel
Naming convention
team-architecture
team-embedded
team-quality
...
[ ] Create channel
[ ] Join channel
[ ] Confirm created
Slack Workspace
┌───────────────────────┐
│ #team-architecture │
│ #team-embedded │
│ #team-quality │
└───────────────────────┘
Meet In Person + Post Intro
[ ] Meet team members
[ ] Introduce yourself
[ ] Share your contribution
[ ] Post summary in Slack
Intro Template
"Hi, I'm __.
I will contribute by __.
My focus is __."
GitHub Backlog Labels
[ ] Review backlog
[ ] Add team label to issues
team-architecture
team-embedded
team-quality
...
GitHub Project
┌──────────────┐
│ Issues │
└─────┬────────┘
│ add labels
▼
┌──────────────┐
│ Team Work │
└──────────────┘
Create Issues + Demo Slide Task
Per team:
[ ] Create ≥ 5 issues
- PO: user stories
- Tester: testing tasks
- All: stories + tasks
Per member:
[ ] Assign yourself ≥ 1 task
One team task:
Title: Sprint 1 Demo Slide [Team]
Labels: sprint-demo + team-*
Upload 1 slide to the ticket
Deliverables
┌─────────────────────────────┐
│ 5+ issues created │
│ Labels applied │
│ Tasks assigned │
│ Sprint 1 demo slide ticket │
└─────────────────────────────┘
Industry 4.0 Challenges
Read:
"The Essence of Industry 4.0"
Task:
[ ] Identify 12 challenges (Section V)
[ ] Sort from biggest → smallest
challenge for the
plant water project
[ ] Write 1 sentence per challenge
explaining your reasoning
Analysis Focus
┌───────────────────────┐
│ Context: Water Plant │
│ Constraints │
│ Scale │
│ Integration │
└───────────────────────┘
Software Paradigms
Read:
"Software Engineering Tutorial"
Pages: 8–14
Task:
[ ] Identify development paradigms
[ ] Sort by suitability
for our project
[ ] 1 sentence explanation
per paradigm
Decision Criteria
┌───────────────────────┐
│ Change frequency │
│ System complexity │
│ Hardware dependency │
│ Risk │
└───────────────────────┘
Key Terms (Definitions)
Explain in 1–2 sentences:
[ ] Internet of Things (IoT)
[ ] Cyber-Physical Systems (CPS)
[ ] Machine-to-Machine (M2M)
[ ] Smart Factory
[ ] Fourth Industrial Revolution
Goal
┌───────────────────────┐
│ Clear terminology │
│ Correct scope │
│ No buzzwords │
└───────────────────────┘
Traditional Paradigms vs IoT
Task:
[ ] Identify 2 challenges
traditional software
paradigms face with:
- IoT
- CPS
- M2M
[ ] Explain each challenge
in a brief paragraph
Hint
┌──────────────────────────┐
│ Assumptions break down │
│ Timing matters │
│ Hardware is involved │
└──────────────────────────┘
What We Are Evaluating
[ ] Reading comprehension
[ ] Ability to rank & justify
[ ] Conceptual clarity
[ ] Connection to project
[ ] Structured reasoning
Mindset
┌───────────────────────┐
│ Think like an engineer│
│ Not just a reader │
│ Not memorization │
└───────────────────────┘
Defining what systems must do before deciding how to build them.
The light sensor system should measure ambient light levels and report for monitoring.
The light sensor system should provide light level measurement and a dashboard with status updated every hour.
Purpose: IoT Light Sensor system to monitor ambient light levels and present system status.
Epics are broad categories of work that can be broken down into smaller, actionable tasks or user stories
An epic describes what should be done, not how it will be implemented.
Epic Summary Examples:
User stories are specific features or tasks that can be completed within a short time frame, often within a sprint
As a [user role],
I want to [desired action],
Because [reason or benefit]
User stories describe what the user needs or expects from the system, not how the system is implemented.
Implementation tasks are specific, actionable steps required to complete a user story or
feature.
They focus on how the system is built and include:
Exercise: Create the epics shown on the right directly in the GitHub backlog.
Epic
User Story
Task
Code
Issue
Ticket
git commit -m "Implements #3 - Light Sensor Monitoring"
What is needed for the software product?
Ensures the product is built as per specifications. ("Are we building the product right?")
Purpose: Acts as a quality gate to ensure readiness for the next stage
Confirms the product meets user needs. ("Are we building the right product?")
| Requirement ID | Requirement | Verification Ticket | Validation Ticket | Verification Method | Validation Method |
|---|---|---|---|---|---|
| REQ-001 | System must measure soil moisture levels | #VT-001 | #VD-001 | Unit Test: Sensor data accuracy | Functional Test: Moisture detection in real soil |
| REQ-002 | System must water plants when moisture is below threshold | #VT-002 | #VD-002 | Integration Test: Sensor-to-actuator response | Acceptance Test: Ensure plants are watered correctly |
| REQ-003 | System must notify users of low water levels in the tank | #VT-003 | #VD-003 | System Test: Notification accuracy | User Test: Validate notifications received |
| REQ-004 | System must allow users to set custom thresholds | #VT-004 | #VD-004 | Interface Test: Threshold input functionality | Usability Test: Confirm thresholds match user input |
| Requirement | REQ-001 | REQ-002 | REQ-003 | REQ-004 |
|---|---|---|---|---|
| REQ-001 | - | X | X | - |
| REQ-002 | X | - | - | X |
| REQ-003 | X | ? | - | - |
| REQ-004 | - | X | X | - |
| Requirement | REQ-001 | REQ-002 | REQ-003 | REQ-004 |
|---|---|---|---|---|
| REQ-001: Measure soil moisture | - | X | X | - |
| REQ-002: Water plants when moisture is low | X | - | - | X |
| REQ-003: Notify user when water levels are low | X | - | - | - |
| REQ-004: User can set watering thresholds | - | X | X | - |
| Requirement | Description |
|---|---|
| REQ-001 | Purchase moisture sensors to detect soil moisture levels |
| REQ-002 | Order Raspberry Pi devices for controlling the system and running the software |
| REQ-003 | Develop system plan for sensor and device integration |
| REQ-004 | Ensure all components meet quality standards for outdoor usage |
| REQ-005 | Test sensors and Raspberry Pi setup for functionality and connectivity |
| Requirement | REQ-001 | REQ-002 | REQ-003 | REQ-004 | REQ-005 |
|---|---|---|---|---|---|
| REQ-001 | - | - | - | - | - |
| REQ-002 | X | - | - | X | - |
| REQ-003 | X | - | - | - | X |
| REQ-004 | - | X | X | - | - |
| REQ-005 | - | - | X | - | - |
Project: Automated Plant Watering System
| Requirement | REQ-001 | REQ-002 | REQ-003 | REQ-004 | REQ-005 |
|---|---|---|---|---|---|
| REQ-001: Measure soil moisture | - | X | X | - | - |
| REQ-002: Water plants when moisture is low | X | - | - | X | - |
| REQ-003: Notify user when water levels are low | X | - | - | - | X |
| REQ-004: User can set watering thresholds | - | X | X | - | - |
| REQ-005: Test sensors and Raspberry Pi | - | - | X | - | - |
| Requirement Ticket | LT Task | Verification Tickets | Validation Tickets |
|---|---|---|---|
| REQ-001: Purchase moisture sensors | LT-001: Purchase Sensors | VT-001: Verify sensor functionality | VT-002: Validate sensor accuracy in real-world conditions |
| REQ-002: Order Raspberry Pi devices | LT-002: Order Raspberry Pi | VT-003: Verify compatibility with sensors | VT-004: Validate Raspberry Pi performance under load |
| REQ-003: Develop system integration plan | LT-003: Develop System Plan | VT-005: Verify integration of sensors and Raspberry Pi | VT-006: Validate system flow against project requirements |
| REQ-004: Ensure quality standards for components | LT-004: Ensure Quality Standards | VT-007: Verify component durability and reliability | VT-008: Validate quality compliance with industry standards |
| REQ-005: Test sensors and Raspberry Pi | LT-005: Test Sensors and Raspberry Pi | VT-009: Verify sensors' response to moisture levels | VT-010: Validate system functionality after testing |
What is the primary purpose of verification in software development?
What does a Requirements Traceability Matrix (RTM) help with?
Which of the following best describes an Epic?
What is the main purpose of verification and validation tickets?
What is the role of tasks in project management?
+----------------+ +-----------------+
| Component | ------ | Component 2 |
+----------------+ +-----------------+
+------------+ +------------+
| Entity | ------- | Entity 2 |
+------------+ +------------+
@startuml
[Component 1] --> [Component 2]
@enduml
@startuml
entity "Entity" {
+ID: int
+Name: string
}
entity "Entity2" {
+ID: int
+Details: string
}
"Entity" -- "Entity2"
@enduml
name: Create Weekly Issue
on:
schedule:
- cron: '0 0 * * 5' # Every Friday
jobs:
create-issue:
...
| Level | Purpose |
|---|---|
| Feature | User value |
| User Story | User need |
| Task | Implementation |
| Artifact | Focus |
|---|---|
| Feature | Value |
| User Story | Behavior |
| Task | Work |
| Element | Example |
|---|---|
| As a | registered user |
| I want | to reset my password |
| So that | I can regain access |
| Letter | Meaning |
|---|---|
| I | Independent |
| N | Negotiable |
| V | Valuable |
| E | Estimable |
| S | Small |
| T | Testable |
| Story | Criteria |
|---|---|
| Password reset | Email link sent |
| Password reset | Link expires |
| Status | Meaning |
|---|---|
| Ready | Can enter sprint |
| Not Ready | Needs refinement |
| Level | Focus |
|---|---|
| User Story | Behavior |
| Task | Work |
| Task Type | Example |
|---|---|
| Design | Define API |
| Build | Implement logic |
| Test | Write tests |
| State | Meaning |
|---|---|
| To Do | Not started |
| In Progress | Being worked on |
| Done | Completed |
| Points | Meaning |
|---|---|
| 1 | Very simple |
| 3 | Simple |
| 5 | Moderate |
| 8 | Complex |
| 13+ | High risk ⚠️ |
| Estimate | Signal |
|---|---|
| 1–3 | Well understood |
| 5–8 | Some uncertainty |
| 13+ | Too large → refine |
| Measure | Used For |
|---|---|
| Story Points | Complexity |
| Man Days | Scheduling |
| Velocity | Forecasting |
| Measure | Used For |
|---|---|
| Story Points | Complexity |
| Man Days | Scheduling |
| Velocity | Forecasting |
| Milestone | Represents |
|---|---|
| Sprint 1 | 1-week sprint |
| Issues | User stories / tasks |
| Progress | % completed |
| Ticket Field | Purpose |
|---|---|
| Assignee | Primary owner |
| Reviewer | Quality check |
| Labels | Role / area |
Organizing people, roles, and responsibilities in complex systems.
What shall I do next?
Let's start this task
Conway's Law: "Organizations design systems that mirror their team and communication structures."
Define the team structure.
Which structure do you recommend for our project?
function checkLightStatus(sensor) {
// Requirements defined light states
if (sensor.isLightOn) {
// Architecture defined data flow
// QA tested detection logic
reportLightOn(sensor); // Dev implementation
} else {
reportLightOff(sensor);
}
// Product and UX defined reporting
logLightStatus(sensor);
}
| Function | Requirements | Architecture | Development | Testing |
|---|---|---|---|---|
| Function 1 | ||||
| Function 2 | ||||
| Function 3 | ||||
| Function 4 |
function reportLightStatus(sensor) {
if (featureFlags.isLightReportingEnable{
if (sensor.isLightOn) {
sendStatus("on"); // Backend reporting
} else {
sendStatus("off");
}
} else {
console.log("Light reporting is off");
}
logLightEvent(sensor); // Tracking
}
function reportLightStatus(sensor) {
if (sprint3FeatureFlags.isLightReportingEnable{
if (sensor.isLightOn) {
sendLightOn(sensor); // Sprint 3: Dev task
} else {
sendLightOff(sensor);
}
}
// Sprint 5: Log light event
sprint5LogLightEvent(sensor); // Sprint 5: QA validation
}
function reportLightStatus(sensor) {
if (leadFeatureFlag.isLightReportingEnable{
if (sensor.isLightOn) {
sendLightOn(sensor); // Senior dev implementation
}
} else {
console.log("Light reporting is off");
}
// Tracking handled by project lead
logLightEvent(sensor);
}
| Team Structure | Pros | Cons |
|---|---|---|
| Cross-functional Teams |
|
|
| Modular Teams |
|
|
| Product-Oriented Teams |
|
|
| Agile/Scrum Teams |
|
|
| Hierarchical Teams |
|
|
Team > Communication > ⚠ > Software > Product
Phase
Escalation Path
| V-Model Phase | Phase Timing | Escalation Responsibility | When to Escalate |
|---|---|---|---|
| Requirements Analysis | Anytime but specifically during requirements gathering | Product Owner | Unclear, conflicting, or incomplete requirements and roles |
| System Design | Before but also after implementation begins | Architecture | Design inconsistencies or integration risks |
| Implementation | During active development and testing | Frontend / Backend / Embedded | Blocking defects, unresolved bugs, or scope confusion |
| Unit Testing | Immediately after implementation | Testing | Failed unit tests or testability issues |
| Integration Testing | After components are combined | Quality | Interface failures or non-functional integrations |
| System Testing | After full system assembly | Testing | System-wide failures or unexpected behavior |
| Acceptance Testing | Before release or deployment | Client | Acceptance criteria not met or client concerns |
Reduce Escalation → Norming
Slack Workspace
### User Story
As a <role>,
I want <goal>,
so that <benefit>.
### Context
Brief background or motivation for this story.
### Acceptance Criteria
- [ ] Criterion 1
- [ ] Criterion 2
- [ ] Criterion 3
### Notes
Optional design notes, assumptions, or references.
@startuml
title IoT Light Sensor - End to End Flow
actor "User" as U
component "Frontend" as FE
component "Backend API" as BE
database "Database" as DB
component "Sensor Device" as S
S --> BE: POST /sensor\n{room_id, light_state, timestamp}
BE --> DB: store sensor event
DB --> BE: query latest state\nand history
BE --> FE: GET /dashboard\nstate + trends
U --> FE: view dashboard
@enduml
Standard Backlog View
Reduce Escalation - Performing
Biggest Challenge: Lack of adequate skill sets
git checkout main
git pull origin main
git checkout -b feature/issue-24-slack-integration
git commit -m "Issue #24: integrate GitHub with Slack"
git push origin feature/issue-24-slack-integration
Demo only what runs.
DoD checklist (quick):
[ ] Can a teammate run it in <10 minutes?
[ ] Inputs and outputs are visible
[ ] Failure mode is explained
[ ] Link: PR / issue / commit is provided
Action template:
- Problem:
- Change:
- Owner:
- Due:
- Evidence (how we know it improved):
Samples...
Examples (pick one pattern):
1) "Dashboard shows latest 24h history for one room."
2) "API validates payloads with clear errors."
3) "Sensor events appear on UI within N seconds."
Simple capacity rule:
- Commit to 70% of what you think you can do
- Reserve 30% for integration + unknowns
"Leave room for reality"
Ready-to-plan test:
- Can the assignee start without asking 3 questions?
- Can a reviewer verify "done" quickly?
- Is the interface (API/DB/UI) defined?
Team ✓ > Communication > Software > Product
Standard processes for IoT and CPS: requirements, verification, secure delivery, and safe updates.
Nature is a first-class stakeholder: energy use, sustainability, and environmental impact are part of system design.
Checklist and vocabulary for “what good looks like”.
{
"csf": "govern",
"artifact": "risk_entry",
"risk_id": "R-001",
"decision": "accept_with_controls",
"severity": "high",
"owner": "team_lead",
"controls": ["tests", "staged_update"],
"evidence": ["ticket", "pr", "tests"]
}
{
"csf": "identify",
"artifact": "asset_record",
"device_id": "uuid",
"type": "soil_sensor",
"firmware": "1.3.0",
"owner": "greenhouse-a",
"location": "room-3",
"data_class": "internal"
}
{
"csf": "protect",
"artifact": "security_controls",
"encryption": ["tls", "at_rest"],
"auth": "token_rbac",
"secure_defaults": true,
"signed_updates": true,
"rollback": true
}
{
"csf": "detect",
"artifact": "alert_rule",
"signal": "humidity",
"condition": "<5 or >95",
"severity": "high",
"action": "alert",
"channel": "oncall"
}
{
"csf": "respond",
"artifact": "incident",
"incident_id": "INC-202",
"severity": "high",
"containment": "quarantine_device",
"status": "open",
"owner": "oncall"
}
{
"csf": "recover",
"artifact": "recovery_record",
"incident_id": "INC-202",
"strategy": "staged_rollout",
"rollback": true,
"verification": "health_checks",
"followup": "add_tests"
}
{
"framework": "NIST SSDF",
"goal": "secure_by_design",
"scope": ["code", "dependencies", "build", "release"],
"principle": "shift_left",
"outputs": ["evidence", "traceability", "repeatability"]
}
{
"ssdf": "prepare",
"artifact": "secure_dev_policy",
"owner": "engineering",
"required_checks": ["tests", "lint", "sast"],
"branch_rules": ["pr_required", "1_reviewer", "checks_green"],
"secrets_policy": "no_secrets_in_repo",
"training": "owasp_basics",
"evidence": "policy_versioned"
}
{
"ssdf": "protect",
"artifact": "supply_chain_controls",
"repo": ["protected_main", "signed_commits"],
"secrets": ["vault", "rotate"],
"dependencies": "sbom",
"artifacts": "signed",
"access": "least_privilege",
"evidence": "audit_logs"
}
{
"ssdf": "produce",
"artifact": "secure_build_pipeline",
"design": ["threat_model", "secure_defaults"],
"reviews": "pr_review_required",
"testing": ["unit", "integration", "sast"],
"build_gate": "fail_on_high",
"traceability": "ticket_to_pr",
"evidence": "pipeline_reports"
}
def test_usage_date_rejects_injection(client):
payloads = [
"2026-02-02' OR '1'='1",
"2026-02-02--",
"$ne",
'{"$gt":""}',
"../etc/passwd",
"not-a-date"
]
{
"ssdf": "respond",
"artifact": "vuln_workflow",
"intake": "security_issue_template",
"triage": ["severity", "exploitability"],
"fix": ["patch", "regression_tests"],
"release": "staged_rollout",
"verify": "post_update_checks",
"evidence": "advisory_or_ticket"
}
{
"ssdf": "release_loop",
"artifact": "release_record",
"gates": ["review", "tests", "sast", "sbom", "signing"],
"deploy": "staged",
"monitor": "health_checks",
"rollback": true,
"traceability": "tickets",
"evidence": "release_notes"
}
Know which device is operating.
{
"device": {
"device_id": "6f9c2c4e-3a91-4c2b-9e6d-1b7a9c21e4a8",
"type": "light_sensor",
"model": "LS-100",
"hardware_rev": "A",
"firmware_version": "1.2.3"
},
"provisioning": {
"provisioned_at": "2026-02-02T18:12:00Z",
"provisioned_by": "factory",
"initial_config": "secure_defaults",
"credentials": "unique_per_device"
}
}
Goal: reduce attack surface.
Goal: devices remain safe after deployment.
Goal: prove correct and secure behavior.
In CPS, constraints are requirements. They must be tested and shown as evidence.
For CPS, TDD helps keep updates safe and predictable.
// tests first (pseudo)
expect(shouldAlert({ lux: 8 })).toBe(true); // too dark
expect(shouldAlert({ lux: 22 })).toBe(false); // normal
expect(shouldAlert({ lux: 45 })).toBe(false); // bright but acceptable
// minimal implementation
function shouldAlert(r) {
return r.lux < 10;
}
This is how teams keep “quality” consistent over time.
In CPS, updates are engineering work, not an afterthought.
CPS teams often use V Model thinking for evidence, even inside Agile delivery.
Keep it disciplined: Definition of Done includes tests and evidence.
In IoT/CPS: process is part of the product.
In Industry 4.0 systems, physical devices and cyber physical systems do not interact directly. All sensing, reasoning, and control happens through data. A digital twin operates at the same data interface as the real sensor and can therefore simulate it.
# twin_sim.py
def predicted_lux(ts, cloud_cover, cfg):
h = ts.hour + ts.minute / 60
if h < cfg.sunrise_hour or h > cfg.sunset_hour:
return cfg.night_lux
span = cfg.sunset_hour - cfg.sunrise_hour
x = (h - cfg.sunrise_hour) / span
daylight = math.sin(math.pi * x)
attenuation = 1.0 - 0.75 * cloud_cover
return cfg.night_lux + (cfg.peak_lux - cfg.night_lux) * daylight * attenuation
# twin_eval.py
pred = [d["lux_pred"] for d in readings]
obs = [d["lux_obs"] for d in readings]
errors = [o - p for o, p in zip(obs, pred)]
mae = sum(abs(e) for e in errors) / len(errors)
print("Mean Absolute Error:", round(mae, 2))
If the device changes behavior, the deviation grows - even if tests still pass.
device_id, ts,
lux
lux from hardwarelux from the model{
"device_id": "ls-100-0001",
"room_id": "room-101",
"ts": "2026-02-04T18:00:00Z",
"lux_source": "real",
"lux": 128.4
}
pytesttwin_sim.py and twin_eval.pyname: twin-gate
on:
verify:
runs-on: ubuntu-latest
steps:
...
- name: Twin simulate (writes to DB)
env:
MONGO_URI: ${{ secrets.MONGO_URI }}
DB_NAME: light_sensor_db
DEVICE_ID: ls-100-0001
run: python twin/twin_sim.py
{
"device_id": "ls-100-0001",
"timestamp": "...",
"lux": 215.4
}
"schema_version": "1.1"
“Architecture represents the decisions you wish you could get right early.”
: Ralph Johnson
| Part | Responsibility | Boundary Question |
|---|---|---|
| Sensor Device | Measures light and emits events | What is done on device vs server? |
| Firmware | Packages readings, handles retries | How much logic lives on constrained hardware? |
| Ingestion API | Validates and accepts payloads | What is trusted at the edge? |
| Authentication | Identifies devices and users | How are devices provisioned and revoked? |
| Storage | Persists sensor events | Events only, or current state too? |
| Read Model | Stores current room state | How fresh must reads be? |
| Rules Engine | Evaluates thresholds and conditions | Sync calls, or async events? |
| Alerting | Notifies on anomalies | Who is notified and how often? |
| Dashboard | Displays room state and history | Read model shape and latency? |
| API Gateway | Single entry point for clients | How much routing and policy lives here? |
| Logging | Captures system behavior | What must be observable in production? |
| Deployment | Runs and scales services | Where are scaling and failure handled? |
{
"room_id": "CTC-114",
"light_state": "ON",
"lux": 350,
"timestamp": "2026-02-09T09:15:00-08:00",
"device_id": "sensor-ctc-114-01"
}
| Style | Best When | Main Risk | Typical Signal |
|---|---|---|---|
| Monolith | Small team, fast iteration, simple ops | Scaling and release coordination | One repo, one deploy |
| Layered | Clear separation and testability | Layer violations, slow cross layer changes | UI → API → domain → data |
| Microservices | Independent ownership and scaling | Operational complexity, distributed debugging | Many deploys, strong contracts |
| Event Driven | Streams, decoupling, replay, audit | Event schema governance, ordering, replay complexity | Bus + projections |
| Diagram | Purpose |
|---|---|
| Component | Service boundaries and dependencies |
| Deployment | Runtime nodes and network boundaries |
| Package | Code organization and allowed dependencies |
| Class | Core data structures and relationships |
| Diagram | Purpose |
|---|---|
| Sequence | Messages exchanged over time |
| Activity | Workflow and decision logic |
| State Machine | Valid states and transitions |
| Use Case | Actors and system goals |
| Year | Item | What It Is | Context / Impact |
|---|---|---|---|
| 1980s | State Machines | Formal modeling of states and transitions | Adopted from automata theory and control systems |
| Early 1990s | Class Diagrams | Object oriented structure modeling | Booch method and OMT (Rumbaugh) |
| 1994 | Sequence Diagrams | Time ordered message interaction | OOSE by Ivar Jacobson |
| 1995 | Use Case Diagrams | Actors and system goals | Requirements driven modeling |
| 1996 | Activity Diagrams | Workflow and control flow modeling | Business process representation |
| 1997 | UML 1.0 | Unified Modeling Language | Standardization by Booch, Rumbaugh, Jacobson (OMG) |
| 1997 | Component Diagrams | Service and module boundaries | Architecture level modeling |
| 1997 | Deployment Diagrams | Runtime nodes and infrastructure | Operational and physical layout |
| 2009 | PlantUML | Text based UML diagram generator | Diagrams as code, version controlled |
@startuml
title Layered (Nested): Sensor → Backend → Database + Frontend
skinparam componentStyle rectangle
skinparam shadowing false
top to bottom direction
package "Frontend" {
component "Web Dashboard"
}
package "Backend" {
component "API"
component "Rules"
}
package "Database" {
database "DB"
}
package "Sensor" {
component "Sensor Device"
}
"Sensor Device" --> "API": POST /events
"Web Dashboard" --> "API": GET /rooms/{id}
"API" --> "Rules"
"API" --> "DB": read/write
@enduml
@startuml
title Client Server Deployment (Room Light Sensor)
skinparam componentStyle rectangle
skinparam shadowing false
left to right direction
node "Client: Browser" as Browser {
artifact "Dashboard UI" as UI
}
node "Client: Sensor Device" as Sensor {
artifact "Firmware" as FW
}
node "Server" {
artifact "HTTP API" as API
database "Sensor DB" as DB
}
FW --> API: HTTPS POST /events
UI --> API: HTTPS GET /rooms/{id}
API --> DB: SQL queries
@enduml
@startuml
title Monolith (Room Light Sensor)
skinparam componentStyle rectangle
skinparam shadowing false
left to right direction
component "RoomLightApp (single deploy)" as App {
[Ingestion]
[Query API]
[Rules Engine]
[Alerting]
[Admin UI]
}
database "Sensor DB" as DB
node "Sensor Device" as Sensor
node "User Browser" as Browser
Sensor --> App: POST /events
Browser --> App: GET /dashboard
App --> DB: read/write
[Ingestion] --> [Rules Engine]
[Rules Engine] --> [Alerting]
[Query API] --> DB
[Ingestion] --> DB
@enduml
@startuml
title Microservices (Room Light Sensor)
skinparam componentStyle rectangle
skinparam shadowing false
left to right direction
node "Sensor Device" as Sensor
node "User Browser" as Browser
component "API Gateway" as GW
component "Ingestion Service" as Ingest
component "Room Query Service" as Query
component "Rules Service" as Rules
component "Alert Service" as Alert
queue "Event Bus" as Bus
database "Events Store" as EventsDB
database "Rooms Read Model" as RoomsDB
Sensor --> GW: POST /events
Browser --> GW: GET /rooms/{id}
GW --> Ingest: forward event
Ingest --> EventsDB: append event
Ingest --> Bus: publish LightEvent
Rules --> Bus: subscribe LightEvent
Rules --> Alert: create alert
Rules --> RoomsDB: update room state
GW --> Query: forward query
Query --> RoomsDB: read room state
@enduml
@startuml
title Event Driven Architecture (Room Light Sensor)
skinparam componentStyle rectangle
skinparam shadowing false
left to right direction
component "Ingestion" as Ingest
queue "Event Bus" as Bus
component "Projection Builder" as Project
component "Rules Engine" as Rules
component "Dashboard API" as API
database "Event Store" as ES
database "Read Model" as RM
Ingest --> ES: append LightEvent
Ingest --> Bus: publish LightEvent
Project --> Bus: subscribe LightEvent
Project --> RM: build room state
Rules --> Bus: subscribe LightEvent
Rules --> Bus: publish AlertEvent
API --> RM: query room state
@enduml
| Concept | Description |
|---|---|
| Definition | Architecture that evolves continuously as the system changes |
| Assumption | Requirements, scale, and technology will change over time |
| Practice | Architecture work is part of regular development, not a one time phase |
| Environment | DevOps, CI/CD, cloud native systems |
| Diagram Rule | Why it matters |
|---|---|
| Every component shows a version | Version changes can break architecture assumptions |
| Languages are explicit | Example: Python 3.11, Java 21 |
| Databases are explicit | Example: PostgreSQL 16, MongoDB 8.0 |
| Infrastructure is explicit | Example: Linux 6.x, Nginx 1.26 |
| Diagrams are time scoped | Architecture is valid for a specific point in time |
Example in diagrams:
API Service (Python 3.11)
Database (MongoDB 8.0)
| Question | Pick |
|---|---|
| Who does what? | Use case |
| Who talks to whom, in what order? | Sequence |
| What is the pipeline logic? | Activity |
| What states are legal? | State machine |
@startuml
title Use Case Diagram (Room Light Sensor)
skinparam shadowing false
left to right direction
actor "Building Admin" as Admin
actor "Sensor Device" as Sensor
rectangle "Room Light System" {
usecase "Submit Light Reading" as UC1
usecase "View Room Status" as UC2
usecase "Configure Threshold" as UC3
usecase "Receive Alert" as UC4
}
Sensor --> UC1
Admin --> UC2
Admin --> UC3
Admin --> UC4
UC1 ..> UC4: include
@enduml
@startuml
title Sequence Diagram: Sensor Sends Event
skinparam shadowing false
skinparam participantStyle rectangle
actor "Sensor" as S
participant "API" as API
participant "Validator" as V
database "DB" as DB
participant "Rules Engine" as R
participant "Alert Service" as A
S -> API: POST /events
API -> V: validate(payload)
V --> API: ok
API -> DB: insert LightEvent
API -> R: evaluate(LightEvent)
R -> A: create alert
note right of R
only if threshold violated
end note
API --> S: 202 Accepted
@enduml
@startuml
title Sequence Diagram: User Views Room
skinparam shadowing false
skinparam participantStyle rectangle
actor "User" as U
participant "Dashboard" as D
participant "API" as API
database "Read Model DB" as RM
U -> D: open room page
D -> API: GET /rooms/CTC-114
API -> RM: query current state
RM --> API: RoomState
API --> D: JSON RoomState
D --> U: render UI
@enduml
@startuml
title Activity Diagram: Ingestion Pipeline
skinparam shadowing false
start
:Receive payload;
:Parse JSON;
if (Schema valid?) then (yes)
if (Auth ok?) then (yes)
: Store LightEvent;
: Update RoomState;
if (Threshold violated?) then (yes)
: Create Alert;
else (no)
endif
: Return 202 Accepted;
else (no)
: Return 401 Unauthorized;
endif
else (no)
: Return 400 Bad Request;
endif
stop
@enduml
@startuml
title State Machine: Room Light State
skinparam shadowing false
[*] --> Unknown
Unknown --> On: LightEvent state=ON
Unknown --> Off: LightEvent state=OFF
On --> Off: LightEvent state=OFF
Off --> On: LightEvent state=ON
On --> Unknown: no events (timeout)
Off --> Unknown: no events (timeout)
@enduml
.puml in the repo (source of truth)
diagrams/
room_light_layered_component.puml
room_light_client_server_deploy.puml
room_light_monolith_component.puml
room_light_microservices_component.puml
room_light_event_driven_component.puml
room_light_sequence_ingest.puml
room_light_state_machine.puml
| Aspect | Description |
|---|---|
| Purpose | Standard visual language to model software structure and behavior |
| Focus | Classes, components, interactions, states, workflows |
| Typical Diagrams | Class, Sequence, Use Case, Activity, State Machine, Component |
| Strength | Widely taught, intuitive, good for communication |
| Limitation | Weak at expressing constraints, versions, and evolution over time |
Source (PDF):
OMG UML 2.5.1 Specification (PDF)
| Aspect | Description |
|---|---|
| Purpose | Formally describe software architecture and its constraints |
| Focus | Components, connectors, configurations, versions |
| Examples | C4 Model, Acme, AADL, Wright, ArchiMate |
| Strength | Precise, analyzable, supports evolution and reasoning |
| Limitation | Less visual, steeper learning curve |
Sources (PDF):
An Introduction to Software Architecture: SEI (PDF)
| Attribute | Meaning for Room Light Sensor | Typical Design Lever |
|---|---|---|
| Performance | Ingest throughput and dashboard latency | Read model, caching, batching |
| Scalability | More rooms and devices without rework | Partitioning, async processing |
| Reliability | Survive network loss and device churn | Retry, queues, idempotency |
| Maintainability | Change rules and endpoints safely | Clear boundaries, tests |
| Security | Device identity and request integrity | Auth, signing, least privilege |
| Observability | Debugging and audit trail | Logs, metrics, traces |
| Step | Deliverable | Diagram Fit |
|---|---|---|
| 1. Requirements | Key flows and quality goals | Use case, activity |
| 2. Boundaries | Component diagram with externals | Component |
| 3. Runtime | Nodes and message flow | Deployment, sequence |
| 4. Data | Events, schema, storage choices | Class, component |
| 5. Risks | Failure modes and mitigations | Table + notes |
| 6. Document | PlantUML in repo, SVG exported | All |
| Option | Best When | Main Risk | Trigger to Reconsider |
|---|---|---|---|
| Monolith | Small team, fast iteration | Scaling and release coupling | Teams block each other on deploys |
| Microservices | Clear ownership and independent scaling | Operational overhead | Need independent deploy cadence |
| Event Driven | Stream processing and loose coupling | Schema governance | Many consumers need the same events |
| Question | Answer |
|---|---|
| Nodes + boundaries | Deployment |
| Lowest ops overhead | Monolith |
| Messages over time | Sequence |
| Main stable contract | Event schema |
| Service boundaries | Component |
Code changes often; data meaning must remain stable.
| Centric | Starts with | Common failure |
|---|---|---|
| Hardware | device constraints | payload semantics drift |
| Software | services and endpoints | DTOs diverge per team |
| Data | canonical model | requires governance |
One schema defines meaning across firmware, API, database, and UI.
{
"schema_version": "1.0",
"event_id": "evt-20260211-000123",
"event_type": "LightEvent",
"timestamp": "2026-02-11T08:15:00-08:00",
"room_id": "CTC-114",
"device_id": "sensor-ctc-114-01",
"light_state": "ON",
"lux": 350,
"meta": {
"firmware_version": "0.4.2",
"battery_pct": 92,
"signal_rssi_dbm": -58,
"seq": 9912
}
}
| Bucket | Fields |
|---|---|
| Context | room_id, device_id |
| Data | lux, light_state |
| Metadata | seq, firmware_version, rssi |
| Field | Format | Example |
|---|---|---|
| event_id | uuid v4 or prefixed uuid | evt-7f3a... or 7f3a... |
| timestamp | ISO 8601 | 2026-02-11T08:15:00-08:00 |
| room_id | canonical string | CTC-114 |
| device_id | canonical string | sensor-ctc-114-01 |
| light_state | enum | ON |
| Noun | Primary key | Stored in | Why it is stable |
|---|---|---|---|
| Room | room_id | rooms | maps to physical reality |
| Device | device_id | devices | provisioned identity |
| LightEvent | event_id | light_events | audit unit |
| RoomState | room_id | room_state | projection per room |
| Alert | alert_id | alerts | decision evidence |
| Rule | Do | Avoid | Why |
|---|---|---|---|
| One name per concept | room_id everywhere | roomId, room, rid | prevents mapping bugs |
| One type per field | lux integer | "350" string | safe queries and ranges |
| One time standard | timestamp ISO + Mongo Date | local time only | safe ordering and TTL |
| Enums | ON/OFF/UNKNOWN | free text | validation and UI logic |
Rule: validate at the boundary, guardrail at the database.
| Layer | Purpose | Strength |
|---|---|---|
| API validation | reject bad input | best error messages |
| DB validation | prevent drift | last line of defense |
| Type generation | share contract | fewer integration bugs |
{
"collMod": "light_events",
"validator": {
"$jsonSchema": {
"bsonType": "object",
"required": ["_id","event_id","ts","room_id","device_id","light_state"],
"properties": {
"_id": { "bsonType": "string" },
"event_id": { "bsonType": "string" },
"ts": { "bsonType": "date" },
"room_id": { "bsonType": "string" },
"device_id": { "bsonType": "string" },
"light_state": { "enum": ["ON","OFF","UNKNOWN"] },
"lux": { "bsonType": "int", "minimum": 0 }
},
"additionalProperties": false
}
},
"validationAction": "error"
}
| Collection | Purpose | Key fields |
|---|---|---|
| rooms | room metadata | room_id |
| devices | device registry | device_id, room_id |
| light_events | append only events | event_id, room_id, ts |
| room_state | current state projection | room_id |
| alerts | rule outcomes | alert_id, room_id |
| MongoDB feature | Use in our project | Benefit |
|---|---|---|
| Document model | store LightEvent as one document | easy ingestion, minimal joins |
| Compound indexes | (room_id, ts desc) | fast recent history queries |
| TTL indexes | optional retention on light_events | automatic cleanup |
| Validators | schema guardrails | data quality over time |
| Change Streams | push updates to UI (optional) | near real time dashboards |
| Collection | Index | Query it supports |
|---|---|---|
| devices | unique(device_id) | auth and registry lookups |
| devices | (room_id, device_id) | list devices in room |
| light_events | (room_id, ts desc) | recent events per room |
| light_events | (device_id, meta.seq desc) | detect duplicates or gaps |
| alerts | (room_id, created_ts desc) | show recent alerts per room |
| room_state | unique(room_id) | dashboard read by room |
Store and log decisions as data.
{
"alert_id": "alrt-20260211-000045",
"schema_version": "1.0",
"timestamp": "2026-02-11T08:15:02-08:00",
"room_id": "CTC-114",
"device_id": "sensor-ctc-114-01",
"type": "LIGHT_STUCK_ON",
"severity": "WARN",
"explain": {
"rule": "on_duration_minutes > 120",
"value": 131
},
"linked_event_id": "evt-20260211-000123"
}
| Rule | Example |
|---|---|
| ON duration threshold | ON > 120 minutes |
| Noise filtering | ignore brief toggles < 1 minute |
| Dedup | use event_id + meta.seq |
Pseudo logic
1) fetch recent events for room (sorted by ts)
2) find last OFF -> next ON segment
3) compute duration ON
4) if duration > threshold:
write Alert + link last event
The alert result should be reproducible from stored events.
Mongo supports both operational queries and analytic style pipelines.
// recent events for room (fast with index)
db.light_events
.find({ room_id: "CTC-114" })
.sort({ ts: -1 })
.limit(200)
// write alert as a first class document
db.alerts.insertOne({
alert_id: "alrt-20260211-000045",
room_id: "CTC-114",
type: "LIGHT_STUCK_ON",
severity: "WARN"
})
| Concept | Example |
|---|---|
| baseline | median of last 10 readings |
| drop threshold | delta >= 200 lux |
| state condition | only if light_state=ON |
{
"type": "SUDDEN_LUX_DROP",
"severity": "INFO",
"explain": {
"baseline_median": 340,
"current": 120,
"delta": 220,
"window": 10
}
}
Explain fields make alerts defensible and debuggable.
schema/)One schema + language bindings
repo/
schema/
light_event/1.0.json
room_state/1.0.json
alert/1.0.json
backend/
validate/
frontend/
types/
embedded/
payload/
{
"schema_version": "1.0",
"room_id": "CTC-114",
"light_state": "ON",
"lux": 350,
"last_event_id": "evt-20260211-000123",
"cached_at": "2026-02-11T08:15:05-08:00"
}
UI stores projections, not the raw firehose.
| Change | Allowed | Rule | Example |
|---|---|---|---|
| Add optional field | Yes | backward compatible | meta.temperature_c |
| Add required field | New version | bump schema_version | require building_id |
| Rename field | Not directly | support both then deprecate | roomId -> room_id |
| Change enum values | Rare | new version + mapping | avoid ON/OFF changes |
| Topic | Expected direction |
|---|---|
| Schema validation | boundary + guardrail |
| Identifiers | uuid, canonical keys |
| Events vs state | truth vs projection |
| Indexes | (room_id, ts desc) |
| Alerts | explain fields + links |
{
"$jsonSchema": {
"bsonType": "object",
"required": ["_id","room_id","building","room_number","created_at","updated_at"],
"properties": {
"_id": {"bsonType":"string"},
"room_id": {"bsonType":"string"},
"building": {"bsonType":"string"},
"room_number": {"bsonType":"string"},
"floor": {"bsonType":["int","long"]},
"tags": {"bsonType":"array","items":{"bsonType":"string"}},
"created_at": {"bsonType":"date"},
"updated_at": {"bsonType":"date"}
},
"additionalProperties": false
}
}
{
"$jsonSchema": {
"bsonType": "object",
"required": ["_id","device_id","room_id","device_type","status","provisioned_at"],
"properties": {
"_id": {"bsonType":"string"},
"device_id": {"bsonType":"string"},
"room_id": {"bsonType":"string"},
"device_type": {"enum":["light_sensor"]},
"status": {"enum":["ACTIVE","DISABLED","RETIRED"]},
"firmware_version": {"bsonType":"string"},
"provisioned_at": {"bsonType":"date"},
"last_seen_at": {"bsonType":"date"}
},
"additionalProperties": false
}
}
{
"$jsonSchema": {
"bsonType": "object",
"required": ["_id","event_id","schema_version","ts","room_id","device_id","light_state"],
"properties": {
"_id": {"bsonType":"string"},
"event_id": {"bsonType":"string"},
"schema_version": {"bsonType":"string"},
"ts": {"bsonType":"date"},
"room_id": {"bsonType":"string"},
"device_id": {"bsonType":"string"},
"light_state": {"enum":["ON","OFF","UNKNOWN"]},
"lux": {"bsonType":["int","long"],"minimum":0},
"meta": {
"bsonType":"object",
"properties": {
"seq": {"bsonType":["int","long"],"minimum":0},
"battery_pct": {"bsonType":["int","long"],"minimum":0,"maximum":100}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
}
{
"$jsonSchema": {
"bsonType": "object",
"required": ["_id","room_id","light_state","last_event_id","last_ts","updated_at"],
"properties": {
"_id": {"bsonType":"string"},
"room_id": {"bsonType":"string"},
"light_state": {"enum":["ON","OFF","UNKNOWN"]},
"lux": {"bsonType":["int","long"],"minimum":0},
"last_event_id": {"bsonType":"string"},
"last_ts": {"bsonType":"date"},
"updated_at": {"bsonType":"date"}
},
"additionalProperties": false
}
}
{
"$jsonSchema": {
"bsonType": "object",
"required": ["_id","alert_id","ts","room_id","type","severity","linked_event_id"],
"properties": {
"_id": {"bsonType":"string"},
"alert_id": {"bsonType":"string"},
"ts": {"bsonType":"date"},
"room_id": {"bsonType":"string"},
"device_id": {"bsonType":"string"},
"type": {"enum":["LIGHT_STUCK_ON","SUDDEN_LUX_DROP","DEVICE_OFFLINE"]},
"severity": {"enum":["INFO","WARN","CRITICAL"]},
"linked_event_id": {"bsonType":"string"},
"explain": {"bsonType":"object"}
},
"additionalProperties": false
}
}
Same data
Different screens
Different decisions
| Decision | Why it matters |
|---|---|
| Target resolution | controls information density |
| Input method | mouse vs touch vs none |
| Rendering runtime | browser vs native UI |
More pixels ≠ better UI
More pixels = more options
But you still must choose:
- font size
- spacing
- what to hide
- what to summarize
| Target | Typical constraint | UI consequence | Visualization bias |
|---|---|---|---|
| Phone | small viewport + touch | stacked layout, bigger tap rooms | summaries, short tables, small multiples |
| Embedded | fixed screen + limited compute | few screens, minimal interaction | single KPI, status, trend sparkline |
| Desktop | large viewport + mouse/keyboard | multi panel dashboards | wide tables, comparisons, drill downs |
| Good | Risk |
|---|---|
| top metrics + list | hidden context |
| filters as chips | filter overload |
Phone pattern:
1) KPI summary row
2) short list (top N)
3) tap → details view
| Good | Risk |
|---|---|
| one screen status | no exploration |
| large readable text | low data density |
Embedded pattern:
- ONE KPI
- ONE status color
- ONE small trend (sparkline)
- ONE timestamp ("last updated")
| Good | Risk |
|---|---|
| dense tables | visual clutter |
| multi panel view | too many widgets |
Desktop pattern:
Left: filters
Center: table
Right: chart summary
Bottom: details / logs
| Browser reality | Design response |
|---|---|
| unknown viewport | breakpoints |
| variable input | keyboard + touch safe UI |
| network variability | loading states + caching |
/* Example idea: cards reflow */
.dashboard {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.card {
flex: 1 1 320px; /* grows, shrinks, min width */
}
| Screen | Prefer first | Then add |
|---|---|---|
| Phone | chart summary / KPIs | short table (top N) |
| Embedded | KPI + status | sparkline |
| Desktop | table + filters | chart panel |
| Rule | Why | Example |
|---|---|---|
| Do not squeeze text | readability collapses first | reduce columns instead |
| Summarize before detail | small screens need a story | KPI row → drill down |
| Use progressive disclosure | prevents clutter | details panel / modal |
| Make interaction match input | touch needs bigger targets | buttons not tiny links |
| Decision | Locks you in |
|---|---|
| Fixed layout | embedded only assumptions |
| Responsive layout | more testing across devices |
| Chart heavy UI | rendering cost and legibility risk |
Treat display as architecture, not styling.
Hardware → Layout → Visualization
If you ignore hardware,
your dashboard will fail in production.
What was acceptable security in 2012 may not be acceptable in 2026.
| Year | Bluetooth Version | Security Impact | Legal Risk Implication |
|---|---|---|---|
| 2010 | BLE 4.0 | Basic pairing; limited privacy protections | Considered acceptable at the time |
| 2014 | BLE 4.2 | Improved LE Secure Connections (stronger encryption) | New baseline for “reasonable security” |
When laws or standards evolve, backlog ordering must change. Compliance-driven work can move ahead of features.
This order increases long-term legal and operational risk.
Legal requirements are structural, not optional.
A room light sensor deployed in California, Germany, or Japan may face different regulatory obligations.
| Region | Key Regulation | Impact on IoT App |
|---|---|---|
| European Union | GDPR (General Data Protection Regulation) | Strong privacy rights, deletion, data minimization |
| United States (California) | CCPA / CPRA | Access and deletion rights, transparency obligations |
| United States (Federal) | FCC + FTC | RF compliance and reasonable security expectations |
| United Kingdom | UK GDPR + IoT Security Regulations | Secure-by-design and credential requirements |
Without secure update capability, compliance cannot be maintained.
Legal defensibility depends on security controls and logging.
You cannot promise compliance without patchability.
| Acronym | Definition | Why it matters in a room light sensor IoT app |
|---|---|---|
| FCC | Federal Communications Commission | If your sensor uses WiFi/BLE, radio compliance and interference constraints matter |
| FTC | Federal Trade Commission | Security and privacy claims must match what your app actually does |
| NIST | National Institute of Standards and Technology | Common reference for reasonable security controls and evidence |
| CCPA | California Consumer Privacy Act | If data can relate to a person/household, rights to access and delete may apply |
| HIPAA | Health Insurance Portability and Accountability Act | Applies if light sensor data is tied to patient identity in healthcare contexts |
| Acronym | Definition | Why it matters in a room light sensor IoT app |
|---|---|---|
| CSF | Cybersecurity Framework (NIST CSF) | A simple structure for controls: Govern/Identify/Protect/Detect/Respond/Recover |
| SSDF | Secure Software Development Framework (NIST) | Defines secure build and release practices that support legal defensibility |
| RBAC | Role Based Access Control | Limits who can view room level readings or change automation settings |
| TLS | Transport Layer Security | Encrypts API traffic from sensor gateway to backend |
| TTL | Time To Live (data expiry) | Enforces retention policies by deleting old sensor readings automatically |
| Acronym | Definition | Why it matters in a room light sensor IoT app |
|---|---|---|
| API | Application Programming Interface | Backend endpoints must be authenticated, logged, and rate limited |
| SBOM | Software Bill of Materials | Supports supply chain accountability for firmware and dependencies |
| CVE | Common Vulnerabilities and Exposures | Known issues in dependencies can affect negligence posture |
| NTP | Network Time Protocol | Consistent timestamps make logs credible during investigations |
| PII | Personally Identifiable Information | Room + timestamp + device owner can become personal data in context |
| Acronym | Definition | Room Light Sensor IoT Example |
|---|---|---|
| EMI | Electromagnetic Interference | Sensor radio must not disrupt nearby medical or lab equipment. |
| EMC | Electromagnetic Compatibility | Device must tolerate interference while operating correctly in Room 114. |
| UL | Underwriters Laboratories (Safety Certification) | Power supply and enclosure must meet electrical safety expectations. |
| SLA | Service Level Agreement | Contract may require 99% uptime for building lighting controls. |
| DPIA | Data Protection Impact Assessment | Assessment may be required if occupancy patterns are inferred from lighting data. |
| IR | Incident Response | Defined process if unauthorized dashboard access occurs. |
| MTTR | Mean Time To Recovery | Metric for how quickly lighting service is restored after firmware failure. |
| MTBF | Mean Time Between Failures | Hardware reliability metric for deployed room sensors. |
| FOTA | Firmware Over The Air | Secure remote updates to room light sensors with rollback capability. |
| ISO | International Organization for Standardization | ISO 27001-style controls may influence enterprise deployment requirements. |
| Area | Federal (examples) | California (examples) | Room light sensor notes |
|---|---|---|---|
| Wireless / RF | FCC | Deployment policies | WiFi/BLE modules and interference constraints |
| Consumer protection | FTC | State consumer law | No deceptive accuracy/security claims in dashboard or marketing |
| Privacy | Sector laws (context) | CCPA | Room_id can become personal in smart home or workplace contexts |
| Healthcare | HIPAA; FDA (context) | State privacy overlays | If deployed in clinics, tie to patient info triggers stronger rules |
| Building energy | Federal programs (context) | Title 24 | If used for lighting controls, Title 24 acceptance tests matter |
| Layer | What law cares about | Room light sensor example evidence |
|---|---|---|
| Hardware | interference, safety, reliability | module cert refs; HW revision; calibration notes |
| Firmware | update integrity, safe defaults | signed releases; rollback plan; update logs per device_id |
| Backend | security, access control, logging | RBAC design; auth logs; rate limiting settings |
| Data | privacy, retention, breach readiness | data inventory; TTL policy; deletion workflow logs |
| UI | transparency, accurate claims | screenshots of notices; settings; claim text version history |
| Ops | response readiness, accountability | incident runbook; postmortem; alert rules |
User Story: As a release manager, I need to attach RF compliance evidence to each hardware release so we can support certification and audit requests.
Room Light Sensor Example: For the Room 114 sensor batch, the release folder records module certification id and antenna type used.
User Story: As a deployment engineer, I need a calibration and acceptance workflow so the installed system meets safety expectations in real environments.
Room Light Sensor Example: When Room 114 is commissioned, store threshold values and operator id, then schedule recalibration after HVAC season change.
User Story: As a security engineer, I need secure device provisioning so devices cannot be deployed with shared credentials.
Room Light Sensor Example: Provision Room 114 device with unique token; provisioning log records device_id and timestamp, not the secret.
User Story: As an incident responder, I need to map incidents to hardware revisions so we can scope impact and recalls accurately.
Room Light Sensor Example: If a sensor batch in Room 114–120 misreports, filter by HW revision and deployment date to scope impact.
User Story: As a compliance owner, I need supplier and component evidence so we can defend reasonable procurement practices.
Room Light Sensor Example: For each room sensor, store BOM snapshot and confirm no known CVE in the WiFi SDK version.
User Story: As a security engineer, I need firmware updates to be signed and verified so unauthorized code cannot run on devices.
Room Light Sensor Example: When Room 114 sensor updates to v1.3.2, the device logs signature verification success and stores the previous version for rollback.
User Story: As an operations engineer, I need rollback support so devices can recover from failed updates without unsafe behavior.
Room Light Sensor Example: If Room 114 begins toggling rapidly after update, safe mode holds lights steady and rolls back to the last good version.
User Story: As a product owner, I need test evidence for any accuracy claim so we can defend statements under consumer protection scrutiny.
Room Light Sensor Example: If the app claims 95% correct light_state detection, store the test set from rooms with different daylight conditions.
User Story: As a safety reviewer, I need a documented default safe state so the system behaves predictably when sensor input is missing or inconsistent.
Room Light Sensor Example: If Room 114 sensor stops reporting for 10 minutes, backend sets status to 'unknown' and prevents automated toggles.
User Story: As an administrator, I need role based dashboard access so only authorized users can view or change settings.
Room Light Sensor Example: commonly can view aggregated building statistics, but only building ops can change Room 114 thresholds.
User Story: As a backend engineer, I need rate limiting so the system resists brute force and denial of service scenarios.
Room Light Sensor Example: If an attacker spams /api/rooms/114/state, throttle requests and alert ops.
User Story: As an incident responder, I need a consistent logging schema so we can reconstruct timelines and root causes.
Room Light Sensor Example: A single incident report can show: sensor reading spikes → backend decision → UI toggle → operator override.
User Story: As a privacy owner, I need a data inventory so we know what we collect, where we store it, and why.
Room Light Sensor Example: Inventory explicitly lists: room_id, light_state, timestamp, device_id, and whether each is needed for function.
User Story: As a user, I want to request deletion of data associated with my device so my data is not retained beyond purpose.
Room Light Sensor Example: Deleting Room 114 device data removes raw readings and severs links from device registry while keeping aggregated anonymous stats.
User Story: As a system owner, I need automatic TTL deletion so retention is enforced consistently without manual steps.
Room Light Sensor Example: A nightly job expires sensor readings older than 30 days for each room, with a report of deleted counts.
User Story: As a product owner, I need a privacy preserving mode so deployments can reduce risk by collecting fewer identifiers.
Room Light Sensor Example: In privacy mode, payload omits device_id at the API boundary and uses a short lived token instead.
User Story: As a compliance owner, I need to audit who accessed sensitive data so we can investigate incidents and support accountability.
Room Light Sensor Example: Every request to /api/rooms/114/history is logged with user id and purpose tag.
User Story: As an incident commander, I need a breach runbook so the team can respond quickly and preserve evidence.
Room Light Sensor Example: If unauthorized access occurs, the runbook captures affected rooms, timeframe, and exported datasets.
User Story: As a security reviewer, I need controls for third party data sharing so we do not leak more than necessary.
Room Light Sensor Example: If exporting to a building analytics tool, share only aggregated daily counts, not per minute room_id history.
User Story: As a compliance owner, I need a control matrix mapping legal expectations to NIST outcomes so we can demonstrate reasonable security.
Room Light Sensor Example: Matrix row: 'reasonable security' → CSF Protect → TLS everywhere → config + penetration test notes.
User Story: As a project lead, I want to review CSF outcomes each sprint so security controls remain visible and measurable.
Room Light Sensor Example: Sprint demo: show rate limit logs for room endpoints and a dashboard of failed auth attempts.
User Story: As a release manager, I need a secure release checklist aligned with NIST SSDF so releases are consistent and defensible.
Room Light Sensor Example: Release v1.4 includes: signed firmware, TLS config check, TTL job test, rollback test evidence.
User Story: As a hospital security officer, I need audit logs for all configuration changes so we can investigate incidents and meet accountability expectations.
Room Light Sensor Example: Changing Room 114 daylight threshold requires admin role and produces an audit log entry.
User Story: As a safety reviewer, I need a fail safe mode so inconsistent readings do not create unsafe lighting behavior.
Room Light Sensor Example: If lux value fluctuates near threshold, debounce prevents rapid toggling in Room 114.
User Story: As a building operator, I need privacy preserving analytics so we can report trends without exposing room level behavior.
Room Light Sensor Example: Weekly report shows building-wide counts rather than Room 114 minute-by-minute history.
For a detailed exploration of software update maintenance and legal alignment, see the research article:
https://se4cps.github.io/lab/research/2025-04-23-edi40.html
This source provides empirical context, dependency analysis, and maintenance frameworks that inform legal requirements for IoT software updates.
+-------------+ HTTPS/REST +--------------+ SQL +--------------+
| Sensor | -----------> | Backend API | -----> | Database |
| (ESP32/PI) | | (Flask/Py) | | (MongoDB) |
+-------------+ +--------------+ +--------------+
| |
| MQTT (optional) | WebSocket/REST
v v
+-------------+ +-------------+
| Edge Gateway| | Dashboard |
+-------------+ +-------------+
| Aspect | Verification | Validation | Testing |
|---|---|---|---|
| Primary focus | Specs + artifacts | Outcomes | Runtime behavior |
| Methods | Reviews, static analysis | UAT, field trials | Unit→System, regression |
| IoT Light Sensor | API/DB contract review | Facilities confirms value | Send payload; verify DB + UI |
Requirements <-------------------------> Acceptance Tests
System Design <-------------------------> System Tests
Component Des. <-------------------------> Integration Tests
Implementation <-------------------------> Unit Tests
UNIT
- Many tests
- Very fast
- Fully automated
- Run on every commit
- Low cost per test
COMPONENT
- Fewer than unit
- Automated
- Slightly slower
- Mock dependencies
INTEGRATION
- Moderate count
- Slower (DB/network)
- CI pipeline stage
- Real infrastructure
SYSTEM
- Few tests
- Much slower
- Staging environment
- Full stack
ACCEPTANCE
- Very few
- Often manual or semi-auto
- Business validation
- Highest execution cost
+----------------------------+
| ACCEPTANCE |
+----------------------------+
▲
+----------------------------+
| SYSTEM |
+----------------------------+
▲
+----------------------------+
| INTEGRATION |
+----------------------------+
▲
+----------------------------+
| COMPONENT |
+----------------------------+
▲
+----------------------------+
| UNIT |
+----------------------------+
LEFT SIDE (Build) RIGHT SIDE (Test) ROLE
Requirements ↔ Acceptance Test Client / Product Owner
System Design ↔ System Test QA / Test Engineer
Component Design ↔ Integration Test QA + Dev
Implementation ↔ Unit Test Developer
Key Idea:
Each development artifact has a matching
verification activity and responsible role.
{
"device_id": "ls-0081",
"room_id": "CTC-114",
"lux": 312,
"light_state": "ON",
"timestamp": "2026-02-23T08:15:30Z",
"fw_version": "1.2.0",
"nonce": "c4b8f0c3-2a0d-4d25-9f1c-acde1b7b7b8a"
}
Required: device_id, room_id, lux, light_state, timestamp, nonce
Ranges: lux 0..20000
Enums: light_state ON|OFF|AUTO
Time: timestamp within +/- 2 minutes (configurable)
Replay: nonce must be unique (reject duplicates)
History
- Popularized with early xUnit frameworks
- Strongly adopted with TDD (2000s)
- Became standard structure for unit tests
Concept
Arrange → Prepare inputs + environment
Act → Execute behavior
Assert → Verify outcome
Core Principle:
One behavior per test.
UNIT
Arrange: payload
Act: validate()
Assert: return value
COMPONENT
Arrange: HTTP request + mock DB
Act: handler()
Assert: status + JSON
INTEGRATION
Arrange: DB + migrations
Act: POST endpoint
Assert: row inserted
SYSTEM
Arrange: deployed stack
Act: simulate sensor input
Assert: dashboard updates
AAA scales because the structure
of testing never changes.
TDD loop:
1) Write failing test
2) Implement minimum to pass
3) Refactor (keep tests green)
AAA:
- Arrange
- Act
- Assert
| Level | Arrange | Act | Assert |
|---|---|---|---|
| Unit | payload | validate() | "ok" |
| Component | HTTP request | handler() | status 201 |
| Integration | DB ready | POST API | row saved |
| System | full system | send reading | UI updates |
// validatePayload.js
function validatePayload(p) {
if (!p.device_id) return "missing_device_id";
if (!p.room_id) return "missing_room_id";
if (p.lux < 0 || p.lux > 20000) return "bad_lux";
if (p.light_state !== "ON" &&
p.light_state !== "OFF" &&
p.light_state !== "AUTO") return "bad_state";
return "ok";
}
// Arrange
const payload = {
device_id:"ls-1",
room_id:"CTC-114",
lux:999999, // invalid
light_state:"ON"
};
// Act
const result = validatePayload(payload);
// Assert
console.assert(
result === "bad_lux",
"Unit Test failed: expected bad_lux"
);
console.log("Unit test complete");
// validatePayload.js
function validatePayload(p) {
if (!p.device_id) return "missing_device_id";
if (!p.room_id) return "missing_room_id";
if (p.lux < 0 || p.lux > 20000) return "bad_lux";
if (p.light_state !== "ON" &&
p.light_state !== "OFF" &&
p.light_state !== "AUTO") return "bad_state";
return "ok";
}
// Arrange
const validPayload = {
device_id: "ls-1",
room_id: "CTC-114",
lux: 350,
light_state: "ON"
};
// Act
const result = validatePayload(validPayload);
// Assert
console.assert(
result === "ok",
"Unit Test failed: expected ok"
);
console.log("Positive unit test complete");
// validatePayload.js (BUGGY VERSION)
function validatePayload(p) {
if (!p.device_id) return "missing_device_id";
if (!p.room_id) return "missing_room_id";
// ❌ BUG: wrong logic (should be OR, not AND)
if (p.lux < 0 && p.lux > 20000) return "bad_lux";
if (p.light_state !== "ON" &&
p.light_state !== "OFF" &&
p.light_state !== "AUTO") return "bad_state";
return "ok";
}
// Arrange
const invalidPayload = {
device_id: "ls-1",
room_id: "CTC-114",
lux: 999999, // invalid
light_state: "ON"
};
// Act
const result = validatePayload(invalidPayload);
// Assert (correct expectation)
console.assert(
result === "bad_lux",
"Unit Test failed: expected bad_lux"
);
console.log("Test finished");
// validatePayload.js
function validatePayload(p) {
if (!p.device_id) return "missing_device_id";
if (!p.room_id) return "missing_room_id";
if (p.lux < 0 || p.lux > 20000) return "bad_lux";
if (p.light_state !== "ON" &&
p.light_state !== "OFF" &&
p.light_state !== "AUTO") return "bad_state";
return "ok";
}
// Arrange
const invalidPayload = {
device_id: "ls-1",
room_id: "CTC-114",
lux: 999999, // invalid
light_state: "ON"
};
// Act
const result = validatePayload(invalidPayload);
// Assert (INTENTIONALLY WRONG EXPECTATION)
console.assert(
result === "ok", // <-- this is wrong
"Unit Test failed: expected ok"
);
console.log("Failing unit test complete");
// Simple handler
function postLight(req) {
if (req.body.lux < 0 || req.body.lux > 20000) {
return { status: 400, saved: false };
}
return { status: 201, saved: true };
}
// Arrange
const request = {
body: { lux: 300 }
};
// Act
const response = postLight(request);
// Assert
console.assert(
response.status === 201,
"Expected 201"
);
console.assert(
response.saved === true,
"Expected saved=true"
);
curl -X POST http://localhost:3000/api/v1/telemetry/light \
-H "Content-Type: application/json" \
-d '{
"device_id":"ls-1",
"room_id":"CTC-114",
"lux":300,
"light_state":"ON"
}'
# Arrange
Valid JSON payload
# Act
curl -X POST http://localhost:3000/api/v1/telemetry/light \
-H "Content-Type: application/json" \
-d '{"lux":300,"light_state":"ON"}'
# Assert
201
{ "saved": true }
# Arrange
Invalid JSON (lux too high)
# Act
curl -X POST http://localhost:3000/api/v1/telemetry/light \
-H "Content-Type: application/json" \
-d '{"lux":999999,"light_state":"ON"}'
# Assert
400
{ "error": "bad_lux" }
// Arrange
Valid payload ready
// Act
POST /api/v1/telemetry/light
{
"room_id": "CTC-114",
"lux": 300,
"nonce": "n-1"
}
// Expect backend to write to DB
-- Assert: row inserted
SELECT lux
FROM light_readings
WHERE room_id='CTC-114'
ORDER BY ts DESC
LIMIT 1; -- expect 300
-- Assert: replay blocked
SELECT COUNT(*)
FROM light_readings
WHERE nonce='n-1'; -- expect 1
# Arrange
System deployed
Sensor online
Room CTC-114 exists
# Act
Send reading:
lux = 300
room_id = CTC-114
# Assert
Dashboard shows:
Room CTC-114
Lux = 300
Status = ON
function isEven(n) {
return n % 2 === 0;
}
// Arrange
const number = 4;
// Act
const result = isEven(number);
// Assert
console.assert(
result === true,
"Expected 4 to be even"
);
IoT Light Sensor Flow:
Sensor → API → DB → Dashboard → Alert
Pipeline-driven automation.
IoT Example:
- JSON schema correct?
- API contract valid?
- Database constraints enforced?
- Unit tests passing?
IoT Example:
- Does automation reduce energy waste?
- Is anomaly detection useful?
- Does dashboard meet stakeholder needs?
1. Unit: sensor value parsing
2. Integration: sensor → backend
3. System: full pipeline
4. Acceptance: user workflow validation
Trigger:
if lux < threshold:
alert()
Continuous execution.
Example:
"Generate test cases for sensor API"
Output:
- Documentation
- Sample code
No direct execution.
Example:
"Deploy backend service"
Agent:
- Executes deployment script
- Returns status
Workflow:
1. Run tests
2. Build container
3. Deploy
4. Monitor logs
5. Notify team
GenAI → Output text
AI Agentic → Execute task
Agentic AI → Coordinate system operations
Input:
- FastAPI routes
- Sensor schema
Output:
- Structured documentation
Agent:
- Analyze function
- Generate pytest
- Suggest missing scenarios
Example:
Generate FastAPI endpoint:
POST /sensor/data
CI Agent:
- Execute tests
- Parse output
- Generate report
docker build .
docker push registry/app
kubectl apply -f deployment.yaml
1. Detect anomaly
2. Generate patch
3. Run regression tests
4. Deploy fix
5. Monitor metrics
Download from ollama.com
ollama pull llama3
ollama serve
python -m venv venv
venv\Scripts\activate
pip install langchain
pip install langchain-ollama
from langchain_ollama import ChatOllama
llm = ChatOllama(model="llama3")
response = llm.invoke("Explain IoT testing")
print(response.content)
Automation + V-Model + Agentic Systems
= Scalable Engineering Practice
Time: 12:30 AM - 1:30 PM
Good overall performance. Next step: focus on improving system design and resource-aware thinking for IoT systems.
Goal for next assessment: move the average into the 80–85% range.
Midterm Practice Questions
Why is software engineering important in the development of IoT/CPS systems?
E
What are some key differences between traditional software engineering and software engineering in IoT/CPS?
All of the above
What are the different levels of requirements in software engineering?
E
Why are requirements important for IoT/CPS systems?
E
What are the differences between Unstructured Requirements, Epics, User Stories, and Tasks in software engineering?
C
What is the difference between User Stories and Tasks?
D
What components does a User Story typically have in software engineering?
E
What is the typical structure of a User Story in software engineering?
E
What is a key difference between the Waterfall model and the V-Model in software development?
B
What is a key difference between the V-Model and Agile Model in software development?
C
What are the phases of the V-Model in software development?
B
Why can Scrum be integrated into the V-Model in software development?
E
How does Conway's Law relate to team structure and software architecture?
A
Why can Scrum be integrated into the V-Model in software development?
B, C, D
How does the V-Model mitigate the risks associated with Conway's Law?
B
How does the V-Model mitigate the risks with the Form, Storm, Norm, and Perform?
E
Does the V-Model natively support cross-functional teams in software development?
B
Which aspects require the V-Model?
What is a key difference between the Incremental Model and the Sequential (Waterfall) Model?
B
What is the definition of software architecture?
B
Which of the following are architectural decisions in software development?
A, B, C
What is the API-first approach in architectural design?
C
What is the role of feature toggles in architectural design?
A
How do feature toggles support the V-Model in real applications?
A
Why does the event-driven model match well with IoT/CPS systems?
A, C
Why is the layered model a good model for IoT/CPS systems?
C
Why are IoT/CPS systems impacted by legal constraints?
A, B, C
In theory, are legal requirements more aligned with the Sequential or Incremental Model?
A
How does the V-Model align with legal requirements?
C
In a RACI chart, who is legally chargeable?
Accountable
Do legal requirements prevent errors in software, hardware, or data?
B
What mechanism do accountable roles use to mitigate risks in software development?
A
What is the difference between testing, verification, and validation?
All of the above
RACI Chart for a Software Development Project
| Task | Responsible | Accountable | Consulted | Informed |
|---|---|---|---|---|
| Requirement Gathering | ? | Project Manager | Stakeholders | ? |
| Design | Architect | Project Manager | ? | Stakeholders |
| Implementation | Developers | Technical Lead | Architect | Project Manager |
| Testing | QA Team | QA Lead | Developers | Project Manager |
| Deployment | DevOps Engineer | Technical Lead | QA Team | Stakeholders |
In a RACI chart, who is responsible for the Implementation task?
Developers
How much of the V-Model's effort is typically allocated to testing?
50%
Does testing in the V-Model happen in parallel with implementation or after implementation?
After implementation
Which test levels in the V-Model require coding skills?
Unit Testing
What are the steps in the Arrange, Act, Assert (AApattern for unit tests?
All of the above
Will this unit test case be OK or NOK?
// Arrange
const a = 2;
const b = 3;
const expectedSum = 5;
// Act
const result = a + b - 1;
// Assert
console.assert(result === expectedSum, `Expected ${expectedSum}, but got ${result}`);
What does Test-Driven Development (TDmean in software development?
Writing tests before the code is implemented
How does Test-Driven Development (TDalign with the V-Model in software development?
Both emphasize early testing and validation
Why is Test-Driven Development (TDconsidered difficult in software development?
It demands a thorough understanding of the requirements and design upfront
As a tester, if the software fails, do you go test levels down or up?
Down
Which test levels in V-Model for IoT/CPS can be more/less automated?
More automated: Unit Testing, Component Testing, Integration Testing
Less likely to be automated: System Testing, Acceptance Testing
How can sensor errors be identified and mitigated during testing in IoT/CPS systems?
All of the above
During testing, the sensor is occasionally not working correctly. Please a paragraph with a plan how to find the error?
A legal regulation states that from March/2025 indoor plants cannot be watered using our system. How do you recommend addressing the issue in the current (MVP 1) plant watering project? How does it impact our process? Please provide a paragraph.
Why are unit tests easier to automate compared to component tests?
Correct Answer: All of the above.
Will the following unit test pass or fail?
function add(a, {
return a + b + b;
}
function unittests() {
// Arrange
const a = 10, b = 5;
// Act
const result = add(a, b);
// Assert
console.assert(result === 15, `Test failed: expected 15, got ${result}`);
}
unittests();
Will the following unit test pass or fail?
function multiply(a, {
return a * b;
}
function unittests() {
// Arrange
const a = 4, b = 3;
// Act
const result = multiply(a, b);
// Assert
console.assert(result === 12, `Test failed: expected 12, got ${result}`);
}
unittests();
Will the following unit test pass or fail?
function subtract(a, {
return a - b;
}
function unittests() {
// Arrange
const a = 10, b = 5;
// Act
const result = subtract(a, b);
// Assert
console.assert(result === 4, `Test failed: expected 4, got ${result}`);
}
unittests();
Our flower inventory management system will be installed for outdoor plants in a different climate room. Please list five ways it will impact our current prototype and why?
In a flower inventory management system, where multiple features such as stock tracking, price updates, and order management are being developed simultaneously, there is little capacity for extensive review processes before merging. The need to push frequent updates quickly, such as adding new flowers to the database, adjusting prices based on seasonal demand, or managing promotions, requires a workflow that can handle continuous integration and deployment
Please explain three reasons why Feature GitFlow might not be suitable for this system?
When adding one feature to the flower inventory management system, what type of issue should you create?
Correct Answer: User Story Issue.
When planning a major upgrade to the flower inventory management system, which will include multiple related features, what type of issue should you create?
Correct Answer: Epic Issue.
Context:
Question:
Design systems that minimize environmental impact.
Goal: build IoT systems that are efficient, reliable, and environmentally responsible.
| Data Structure | Usage in IoT |
|---|---|
| Ring Buffer | Fixed memory, overwrites old data, good for streams |
| Array | Fast access, predictable memory, fixed-size datasets |
| Linked List | Dynamic size, flexible updates, more memory overhead |
| Hash Table | Fast lookup, useful for caching, extra memory cost |
| Queue | FIFO processing, task scheduling, event handling |
Question:
Which line must change to turn this into a ring buffer?
class Storage:
def __init__(self, size):
self.size = size
self.data = [None]*size
self.index = 0
def append(self, value):
self.data[self.index] = value
# CHANGE THIS LINE
self.index = self.index + 1
Correct change:
self.index = (self.index + 1) % self.size
Result: The list becomes a ring buffer instead of growing forever.
class Storage:
def __init__(self, size):
self.size = size
self.data = [None]*size
self.index = 0
def append(self, value):
self.data[self.index] = value
# RING BUFFER
self.index = (self.index + 1) % self.size
Battery-driven systems need software that reduces energy usage while preserving core functionality.
A light sensor can change how often it sends data based on room conditions.
function getSyncInterval(lightChanged) {
if (!lightChanged) return "6 hours";
return "30 minutes";
}
console.log(getSyncInterval(true));
Goal: reduce network traffic and battery usage.
function getSyncInterval(hoursSinceChange){
if(hoursSinceChange < 6)
return "6 hours";
if(hoursSinceChange < 24)
return "2 hours";
return "30 minutes";
}
Goal: extend device lifetime when the battery is low.
function getSyncInterval(battery){
if (battery > 60)
return "30 minutes";
if (battery > 20)
return "2 hours";
return "6 hours";
}
| Connectivity | Typical Bandwidth |
|---|---|
| Bluetooth Low Energy | 0.1: 2 Mbps |
| Bluetooth Classic | 1: 3 Mbps |
| 4G LTE | 10: 100 Mbps |
| Wi-Fi | 100: 1000 Mbps |
| Sampling Rate | Sensor Data Volume |
|---|---|
| 1 sample / minute | Very Low Traffic |
| 1 sample / second | Moderate Traffic |
| 10 samples / second | High Traffic |
| 100 samples / second | Very High Traffic |
Higher sampling rates increase network usage and battery consumption.
// Streaming
{ "lux":420 }
{ "lux":418 }
{ "lux":421 }
// Compression
{ "l":[420,418,421] }
// Batching
{ "lux":[420,418,421] }
// Raw GPS coordinates
[
[37.7749, -122.4194],
[37.7750, -122.4195],
[37.7752, -122.4197]
]
// Polyline encoded
"c|peFf`ejVq}@n}@"
3 GPS points → short encoded string
| JSON | Protobuf |
|---|---|
|
|
Convert light sensor data to a smaller format.
data = {
"sensor_id":12,
"lux":420
}
msg = LightReading(**data)
binary = msg.SerializeToString()
JSON
{
"sensor_id":12,
"lux":420
}
Protobuf
08 0C 10 A4 03
Problem: Busy loops waste CPU and battery.
// BAD: Continuous polling
while(true){
readLightSensor();
}
Idea: Run code only when an event occurs.
// GOOD: Event-based sensor trigger
lightSensor.onChange((lux) => {
sendLightReading(lux);
});
// High CPU usage
while(true){
readLight();
}
// Better approach
setInterval(readLight,5000);
Read sensor every 5 seconds instead of continuously.
A battery-powered light sensor sends brightness data continuously.
How can we reduce resource usage?
Reads and sends data 1/second.
void loop() {
float lux = lightMeter.readLightLevel(); // 1/s
Serial.println(lux); // Send data every second
delay(1000); // Wake CPU again after 1s
}
⚠ Device wakes 60 times per minute even if light is stable.
Send data on change.
float lastLux = -1;
void loop() {
float lux = lightMeter.readLightLevel();
if (abs(lux - lastLux) > 10) { // delta >10
Serial.println(lux);
lastLux = lux;
}
delay(5000); // Wake only every 5 seconds
}
✓ Device wakes 12 times per minute
✓ Transmits only when light changes
Think about how each constraint impacts our light sensor IoT project.
Question:
How does each constraint influence system design?
| Constraint | Impact on Project |
|---|---|
| Battery | ? |
| Bandwidth | ? |
| Connectivity | ? |
| Storage | ? |
| CPU | ? |
| Topic \ Sprint | SP#8 | SP#9 | SP#10 | SP#11 | SP#12 |
|---|---|---|---|---|---|
| Requirements Engineering | |||||
| Team Structure | |||||
| Development Process | |||||
| Architecture Design | |||||
| Legal Constraints | |||||
| Testing / Verification / Validation | |||||
| Resource Constraints | |||||
| Cyber Security | |||||
| Release Process | |||||
| Testing & Bug Fixing | |||||
| Final Presentation |
Present in 10 minutes
Testing |██████░░░░░░░░░░░░░░░░| 20%
Production |████████████████████░░| 80%
{
"examples": [
{ "temp": 22.5, "unit": "C" },
{ "temp": 72.5, "unit": "F" },
{ "temp_raw": "10101101", "format": "binary" },
{ "temp": null },
{ "temp": "high" }
]
}
{
"data": {
"light": 350
},
"metadata": {
"light": {
"unit": "lux",
"type": "int"
},
"timestamp": "2026-03-20T10:30:00Z",
"device_id": "sensor_101"
}
}
IoT Devices
│
▼
JSON Data Stream
│
┌───────────┼───────────┐
▼ ▼ ▼
MongoDB Cassandra Redis
(Document) (Wide Col) (Key-Value)
│ │ │
└───────┬───┴───────┬───┘
▼ ▼
IndexedDB Applications
(Browser) (Dashboards)
T-Shaped Skill
│ Depth
│
────┼──── Breadth
│
IoT = Embedded + Cloud + Data
Devices Available
Test Need: ██████████
Devices: ███
Gap → Untested cases
Lab Real World
Clean Noisy
Stable Variable
Predictable Uncertain
→ Mismatch Risk
Device Location
Lab → Easy Access
Field → Hard Access
Fix Cost ↑ Distance ↑
Old FW → New FW
[v1] → [v2]
Risk:
- Fail → Device breaks
- No rollback
→ Careful updates
System State
Visible: ███
Hidden: ███████
→ Hard to diagnose issues
Devices
Sensor A → JSON
Sensor B → CSV
Sensor C → Binary
→ Integration Layer Needed
Scenario:
Scan a QR code to check room status (light, occupancy) from anywhere.
Question:
What are the resource limitations?
User → Scan QR
│
▼
Mobile App
│
▼
Cloud / API
│
▼
IoT Sensor
│
▼
Room Status
Learn → Test → Reflect
Q1 Q2 Q3 Q4 Q5
│ │ │ │ │
└──► Understanding
Duration: ~10 min
Team Flow
Yesterday → Today → Blockers
│ │ │
└──────────► Progress ◄─┘
Time: ~5–10 min
Protecting connected systems, data, and infrastructure.
Zero Trust: No device or user is trusted by default
| Aspect | Light Sensor System |
|---|---|
| Device Identification | Unique device_id assigned to each sensor |
| Device Configuration | Configured room_id, sampling rate, thresholds |
| Data Protection | Encrypt sensor data and device credentials |
| Cybersecurity State Awareness | Monitor device status and abnormal readings |
| Logical Access to Interfaces | Restrict API/database access via roles |
| Software Update | Secure OTA updates with validation checks |
Controls who can access data
Restrict access via unique keys
postgresql://username:password@host:port/database?sslmode=require
Restrict DB/API access to specific IP addresses
| Model | Who Controls Access? | Access Logic | Example | Supported by Relational DBs |
|---|---|---|---|---|
| RBAC | System (via Roles) | User has role with permission | Doctor role can read patient record | ✅ Widely Supported |
| DAC | Record Owner | Owner grants access | Patient grants doctor access | ✅ Partially Supported |
| MAC | System (via Labels) | Access requires clearance | Only staff with "Confidential" clearance can read | ❌ Rarely Supported |
CREATE ROLE iot_user;
CREATE ROLE iot_admin;
CREATE ROLE iot_device;
CREATE USER alice WITH PASSWORD 'secure123';
GRANT user TO alice;
GRANT READ ON light_data TO user;
GRANT ALL ON light_data TO admin;
GRANT INSERT ON light_data TO device;
REVOKE READ ON light_data FROM user;
REVOKE ALL PRIVILEGES
ON ALL TABLES IN SCHEMA public
FROM PUBLIC;
| Database | RBAC Support | IP-Based Access | API Key Control |
|---|---|---|---|
| SQLite | ❌ | ❌ | External Only |
| PostgreSQL | ✅ Column-Level | ✅ via pg_hba.conf | External Only |
| MariaDB | ✅ Roles + Fine-Grained | ✅ via host-based grants | External Only |
| OracleDB | ✅ RBAC + VPD | ✅ via network ACLs | External Only |
| MongoDB | ✅ Roles | ✅ via network/IP allowlist | External Only |
| Redis | ⚠️ Basic ACLs | ✅ via bind/firewall | External Only |
| Plain Text | Encrypted Text |
|---|---|
| Device ID: LightSensor_01 | LightSensor_01 |
| Light Level: 300 lux | 300 lux |
| User SSN: 123-45-6789 | U2FsdGVkX1+gTx4kV7K9Zw== |
CREATE TABLE light_sensor_data (
id SERIAL PRIMARY KEY,
device_id TEXT,
light_level INTEGER,
location TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE user_data (
user_id SERIAL PRIMARY KEY,
name TEXT,
ssn TEXT
);
-- Enable extension
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Insert (encrypt sensitive field)
INSERT INTO user_data (name, ssn)
VALUES (
'Alice',
pgp_sym_encrypt('123-45-6789', 'secret_key')
);
-- Sensor data (NOT encrypted)
INSERT INTO light_sensor_data (device_id, light_level)
VALUES ('LS_01', 300);
-- Decrypt SSN
SELECT
name,
pgp_sym_decrypt(ssn::bytea, 'secret_key') AS ssn
FROM user_data;
-- Read sensor data (plain)
SELECT device_id, light_level
FROM light_sensor_data;
| DB | At Rest | Transit | Column | TDE |
|---|---|---|---|---|
| SQLite | ❌ | ❌ | ❌ | ❌ |
| PostgreSQL | ✅ | ✅ | ✅ | ❌ |
| MariaDB | ✅ | ✅ | ✅ | ✅ |
| OracleDB | ✅ | ✅ | ✅ | ✅ |
| MongoDB | ✅ | ✅ | ✅ | ⚠️ |
| Redis | ⚠️ | ✅ | ❌ | ❌ |
user = input("Enter username: ")
query = f"SELECT * FROM users WHERE name = '{user}'"
cursor.execute(query)
Input: alice' OR '1'='1
Query: SELECT * FROM users WHERE name = 'alice' OR '1'='1'
Bypasses authentication and returns all users
user = input("Enter username: ")
query = "SELECT * FROM users WHERE name = %s"
cursor.execute(query, (user,))
Using parameters prevents SQL injection
import logging
logging.basicConfig(level=logging.INFO)
query = "SELECT * FROM patients"
logging.info(f"Running query: {query}")
cursor.execute(query)
query = "SELECT * FROM patients WHERE name = %s"
params = ("Alice",)
logging.info("Query: %s | Params: %s", query, params)
cursor.execute(query, params)
log_statement = 'all'general_log = ON
An IoT-enabled Light Sensor App is compromised when a hacker gains access to a user's device and manipulates watering schedules. The device was not properly secured and had default credentials. What security measure would have reduced the risk?
The Light Sensor App detects an unusual spike in watering requests from a single account, causing overwatering and potential damage to plants. What monitoring or anomaly detection strategy should be implemented?
| Connectivity Technology | Market Share (%) |
|---|---|
| Wi-Fi | 31% |
| Bluetooth | Approximately 27% |
| Cellular IoT (2G, 3G, 4G, 5G, LTE-M, NB-IoT) | Approximately 22% |
| Other Technologies (e.g., Zigbee, LoRa) | Approximately 20% |
| Bluetooth Version | Release Year | Security Features |
|---|---|---|
| Bluetooth 1.0 & 1.0B | 1999 | Basic encryption (weak security), no pairing method |
| Bluetooth 2.0 + EDR | 2004 | Improved Data Rate, basic encryption and pairing |
| Bluetooth 3.0 + HS | 2009 | High-Speed transfer, improved security with AES encryption |
| Bluetooth 4.0 & 4.1 | 2010 & 2013 | Low Energy (LE) with stronger encryption, secure simple pairing |
| Bluetooth 4.2 | 2014 | Improved security with better privacy features, improved LE encryption |
| Bluetooth 5.0 & 5.1 | 2016 & 2019 | Improved range and connection security, more robust LE encryption |
| Bluetooth 5.2 | 2020 | Support for LE Audio, improved encryption, and secure data transfer |
A plant watering system’s point-to-point Wi-Fi connection was compromised, allowing attackers to intercept and manipulate watering data. This disruption caused the system to water plants incorrectly. What security measures should have been implemented?
CREATE EXTENSION IF NOT EXISTS pgcrypto;
| Technical Aspect | PGP Encryption (Asymmetric) | Symmetric Encryption (AES, DES) |
|---|---|---|
| Key Type | Public-Private Key Pair | Single Shared Secret Key |
| Encryption Speed | Slower; computationally intensive | Faster; suitable for large data |
| Key Management | Complex; public key distribution required | Simpler; secure key sharing essential |
| Security Strength | High; secure over insecure channels | Medium to High; risk if key compromised |
| Use Case Examples | Email encryption, digital signatures | File encryption, database encryption |
Reference: PostgreSQL pgcrypto Documentation
DROP TABLE secure_data;
CREATE TABLE IF NOT EXISTS secure_data(
id SERIAL PRIMARY KEY,
sensitive_info BYTEA NOT NULL
);
INSERT INTO secure_data (sensitive_info)
VALUES (pgp_sym_encrypt('Secret Information', 'my_strong_password'));
SELECT id, ENCODE(sensitive_info, 'hex') AS encrypted_info
FROM secure_data;
SELECT id, pgp_sym_decrypt(sensitive_info, 'my_strong_password')
AS decrypted_info
FROM secure_data;
Key Concept: Diffie-Hellman allows two parties to establish a shared secret over an insecure channel, used for symmetric key encryption
SET password_encryption = 'scram-sha-256';
CREATE ROLE username WITH LOGIN PASSWORD 'your_secure_password';
SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolcanlogin
FROM pg_roles;
SET password_encryption = 'md5';
CREATE ROLE username WITH LOGIN PASSWORD 'your_secure_password';
CREATE USER Bob PASSWORD 'bob_pass_123!';
CREATE USER Alice PASSWORD 'alice_pass_123!';
-- Grant full access to Bob
GRANT ALL PRIVILEGES ON Flowers TO Bob;
-- Grant read-only access to Alice
GRANT SELECT ON Flowers TO Alice;
-- Show all users and roles
SELECT rolname FROM pg_roles;
-- Switch user temporarily
SET ROLE Alice;
-- Switch back
RESET ROLE;
-- Switch to user 'Alice' (read-only)
SET ROLE Alice;
-- Successful query (Alice can SELECT)
SELECT * FROM flowers;
-- Failed query (Alice cannot DELETE)
DELETE FROM flowers WHERE id = 1;
RESET ROLE;
-- Switch to user 'bob' (full access)
SET ROLE Bob;
-- Successful query (Bob can DELETE)
DELETE FROM flowers WHERE id = 1;
RESET ROLE;
REVOKE INSERT ON flowers FROM Bob;
CREATE ROLE viewer;
CREATE ROLE manager;
CREATE USER alice PASSWORD 'alice_pass';
CREATE USER bob PASSWORD 'bob_pass';
GRANT viewer TO alice;
GRANT manager TO bob;
GRANT SELECT ON flowers_core TO viewer;
GRANT SELECT, INSERT, UPDATE ON flowers_pricing TO manager;
GRANT alice TO current_user;
SET ROLE alice;
SELECT * FROM flowers_core;
SELECT * FROM flowers_pricing;
RESET ROLE;
CREATE TABLE flowers_core (
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE flowers_pricing (
id INTEGER PRIMARY KEY,
price REAL
);
INSERT INTO flowers_core (id, name) VALUES (1, 'Rose');
INSERT INTO flowers_pricing (id, price) VALUES (1, 2.99);
GRANT SELECT ON flowers_core TO viewer;
GRANT SELECT, INSERT, UPDATE ON flowers_pricing TO manager;
| Technical Area | Importance of Logging |
|---|---|
| Query Performance | Identify slow queries and optimize them |
| Security | Track unauthorized access attempts |
| Error Diagnosis | Understand and debug failed operations |
| Auditing | Maintain compliance and trace changes |
| Replication & Backup | Ensure consistency and troubleshoot issues |
-- In postgresql.conf:
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d.log'
log_statement = 'all'
log_min_error_statement = error
log_min_messages = warning
GRANT USAGE ON SCHEMA public TO Alice;
GRANT SELECT ON team1_flowers TO Alice;
-- GRANT ALL PRIVILEGES ON team1_flowers TO Alice;
GRANT Alice TO current_user;
SET ROLE Alice;
SELECT * FROM team1_flowers;
RESET ROLE;
sqlite3 mydatabase.db
.echo ON
.tables
.schema
sqlite3 mydatabase.db
.echo ON
.output my_log.txt
.schema
| Technical Aspect | SQLCipher | Standard SQLite |
|---|---|---|
| Encryption Method | Transparent AES-256 Encryption | No built-in encryption |
| Data-at-rest Security | Strong; protects database files on disk | Weak; plaintext data storage |
| Performance Impact | Moderate overhead due to encryption operations | Minimal; faster access (unencrypted) |
| Use Cases | Mobile apps, sensitive data storage | General-purpose lightweight storage |
| Key Management | Application-managed passphrase | Not applicable |
Reference: SQLCipher GitHub Documentation
sqlcipher mydatabase.db
sqlite> PRAGMA key = 'your_secure_password';
sqlite> CREATE TABLE flowers(id INTEGER, name TEXT);
sqlite> INSERT INTO flowers VALUES (1, 'Rose');
sqlite> .quit
| Term | Description | Example |
|---|---|---|
| Vulnerability | A weakness in a system, application, or network that can be exploited by attackers. | Buffer overflow, SQL injection, Cross-Site Scripting (XSS). |
| Zero-Day Vulnerability | A security flaw that is exploited before the vendor has issued a patch or fix. | Stuxnet worm exploited multiple zero-day vulnerabilities in Siemens SCADA systems. |
| Zero-Day Attack | An attack that targets a zero-day vulnerability, often before it is discovered by the vendor. | Exploiting an unknown flaw in a browser before the vendor releases a patch. |
| Patch | A software update designed to fix vulnerabilities, bugs, or other issues in a system or application. | Security patch to fix a vulnerability in Windows OS. |
| Patch Management | The process of acquiring, testing, and deploying patches to systems and applications. | Regular updates to fix known vulnerabilities in server software. |
| CVE (Common Vulnerabilities and Exposures) | A publicly disclosed list of known cybersecurity vulnerabilities, each assigned a unique identifier. | CVE-2021-34527: Microsoft PrintNightmare vulnerability. |
| Exploit | A piece of software or code that takes advantage of a vulnerability to cause unintended behavior. | Exploiting a buffer overflow to gain remote code execution. |
| Vulnerability Scanner | Tools used to identify potential vulnerabilities in systems, applications, or networks. | Nessus, OpenVAS, Qualys. |
| Security Advisory | A notification from the vendor or organization about a discovered vulnerability and available patches. | Microsoft Security Advisory for the Windows SMBv1 vulnerability. |
| Security Flaw | A weakness or defect in a system or software that can be exploited to cause harm. | Outdated cryptographic algorithms in a secure communication protocol. |
| Security Update | An update aimed specifically at fixing security-related vulnerabilities. | Update to fix a vulnerability in Apache web server's SSL/TLS configuration. |
A security flaw unknown to the vendor, leaving systems exposed until a patch is released.
{
"deviceId": "LS-1001",
"lux": 320
}
val sensor = sensorManager
.getDefaultSensor(Sensor.TYPE_LIGHT)
func readLux(v: Float) {
print(v)
}
val req = mapOf("lux" to 320)
api.post("/sensor", req)
let body = ["lux": 320]
post("/sensor", body)
fetch("https://api/app/sensor", {
method: "POST"
})
if(user.region != "US"){
denyAccess()
}
a.b(c){
return d+e;
}
const key = "SECRET123"
// flow
App → API → Auth → Device
IoT sensors track soil moisture and sync data from on-device SQLite to a PostgreSQL server.
Orders are tracked in PostgreSQL, while IoT delivery boxes log temperature and humidity.
A smart thermostat was remotely accessed without user consent.
What authentication and logging controls should prevent this?
An attacker replaced an IoT device’s firmware update with malicious code.
How should update mechanisms be secured?
Delivering, updating, and maintaining systems in continuous operation.
| Testing Type | Environment | Testers | Purpose |
|---|---|---|---|
| Alpha Testing | In-house (Lab environment) | Developers & Internal QA | Detect major bugs before external release |
| Beta Testing | Limited real-world environment | End-users (selected group) | Gather user feedback & identify usability issues |
| Gamma Testing | Final pre-release stage | End-users (public or wider audience) | Ensure system stability before full deployment |
| Item | Light Sensor App |
|---|---|
| Alpha, Beta, and Gamma Testing | |
| Release Channels (App Store, .apk) | |
| Staged Releases and Rollbacks | |
| Feedback Channels and User Analytics | |
| V-Process Model for Validation and Verification | |
| Continuous Integration & Continuous Deployment (CI/CD) | |
| Over-the-Air (OTUpdates for IoT Devices | |
| Canary Releases for Risk Mitigation | |
| Security & Compliance Checks (IEC 62443, NIST Guidelines) | |
| Digital Twin Testing for Virtual Release Simulation |
Tagging versions for testing phases in IoT-Light-Sensor
v1.0.0-alpha - Alpha testingv1.0.0-beta - Beta testingv1.0.0-rc.1 - Gamma (Release Candidate) testing
# Create and push an Alpha tag
git tag v1.0.0-alpha
git push origin v1.0.0-alpha
# Create and push a Beta tag
git tag v1.0.0-beta
git push origin v1.0.0-beta
# Create and push a Release Candidate (Gammtag
git tag v1.0.0-rc.1
git push origin v1.0.0-rc.1
v1.0.0)
# Create a tag locally
git tag -a v1.0.0 -m "v1.0.0 - Alpha Release"
git push origin v1.0.0
# Create a GitHub release using GitHub CLI
gh release create v1.0.0 \
--title "Version 1.0.0" \
--notes "Initial alpha release of IoT-Light-Sensor."
gh release create v1.0.0 \
--title "v1.0.0 - Initial Release" \
--notes "Release Notes:
## 🚀 Features
- Added support for (#45)
- Implemented (#52)
## 🔧 Improvements
- Optimized sensor data
- UI for better usability
## 🐛 Bug Fixes
- Fixed API timeout issue (#60)
- Resolved UI bug in dashboard settings (#65)
## 🔒 Security Patch
- Patched vulnerability (#70)
wingetbrew install ghsudo apt install gh or
brew install gh
gh --version
from flask import Flask, jsonify
import random
app = Flask(__name__)
@app.route('/')
def homepage():
if random.random() <= 0.1:
return jsonify({"feature": "NewFeature"})
return jsonify({"feature": "CurrentFeature"})
app.run(debug=True)
from flask import request
import geocoder
@app.route('/')
def check_geolocation():
g = geocoder.ip('me') # Get location based on IP
country = g.country
if country == 'United States':
# Stage 1: U.S. only
return "Feature available in U.S."
else:
# Full rollout
return "Feature available globally"
navigator.geolocation.getCurrentPosition(function(position) {
var lat = position.coords.latitude;
var lon = position.coords.longitude;
fetch(`https://api..?lat=${lat}&lng=${lon}`)
.then(response => response.json())
.then(data => {
if(data.countryName === 'United States') {
// Stage 1: Available in U.S.
console.log("Feature available in U.S.");
} else {
// Full release: Global rollout
console.log("Feature available globally.");
}
});
});
Title: "Watering schedule not triggering"
Description:
- Expected watering at 10 AM, but no action occurred.
- Soil moisture reading: 18% (should trigger watering).
- Logs show no errors.
Steps to reproduce:
1. Set watering threshold to 20%.
2. Wait for moisture to drop below 20%.
3. Observe no watering action.
🚀 Feature Request
@dev-team Please add notification if soil moisture low?
Currently, users have to check manually.
🔍 Steps to reproduce:
1. Set up a plant with a low moisture threshold.
2. Observe that no notification is sent.
<script src="googletagmanager.com/gtag/js?id=G-"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX'); // Replace with your GA ID
</script>
v1.2.0 Git tag (stable release)requirements.txt, package.json.env, YAML configsapp:1.2.0| Component | Embedded System Version | Backend System Version |
|---|---|---|
| Operating System | ||
| Server | ||
| Programming Language (Python) | ||
| Programming Language (C/C++) | ||
| Database | ||
| API |
1.0.0
1.0.0
Developers / QA
Internal testing
Selected users
Public users
This software version is supported for 12 months from release. During this period, security patches and critical bug fixes will be provided. After the support window, the version will be deprecated and no further updates will be issued. Users are responsible for upgrading to a supported version to maintain security and system reliability.
git fetch --tags
git log --oneline v0.9.0..v1.0.0
git tag
gh release view v1.0.0
## Sensor
## Backend
## Database
## Frontend
## Notification
git log --pretty=format:"%h %s"
gh pr list --state merged
gh issue list
git describe --tags
feat: add notification rule
fix: correct timestamp parsing
docs: update README
refactor: clean backend logic
{
"app/": "Backend",
"dashboard/": "Frontend",
"twin/": "Digital Twin",
".github/workflows/": "CI/CD"
}
git log v0.9.0..v1.0.0 \
--pretty=format:"- %s" \
| grep "feat\|fix\|docs"
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
## Features
- Added light alert rule
## Fixes
- Fixed timestamp bug
## Improvements
- Optimized dashboard
on:
workflow_dispatch:
if: github.ref == 'refs/heads/production'
LAST_TAG="$(git tag --list 'v*' \
--sort=-version:refname | head -n 1)"
NEXT_TAG="v${MAJOR}.${MINOR}.$((PATCH + 1))"
git tag "$TAG" "${GITHUB_SHA}"
git push origin "$TAG"
gh release create "$TAG" \
--generate-notes \
--latest
{
"openapi": "3.0.0",
"paths": {
"/api/light": {
"get": {
"summary": "Get light status"
}
}
}
}
app.get('/api/light', (req, res) => {
res.json({ status: "ON" });
});
npx swagger-jsdoc -d config.js -o swagger.json
npx swagger-ui-express
steps:
- run: npm install
- run: npm run generate-docs
- run: npm run deploy-docs
GET /api/light
Response:
{
"status": "ON",
"timestamp": "2026-04-03T10:00:00Z"
}
curl -X POST $SLACK_WEBHOOK \
-H 'Content-type: application/json' \
--data '{
"text": "🚨 Backend is DOWN"
}'
curl -f https://api.example.com/health \
|| curl -X POST $SLACK_WEBHOOK \
--data '{"text":"API Failure"}'
*/5 * * * * /scripts/health-check.sh
{
"component": "sensor",
"timestamp": "2026-04-03T18:25:43Z",
"level": "error",
"message": "Light sensor read failure",
"device_id": "sensor-001"
}
{
"component": "embedded",
"timestamp": "2026-04-03T18:25:45Z",
"level": "error",
"message": "GPIO interrupt timeout",
"firmware_version": "1.2.0"
}
{
"component": "backend",
"timestamp": "2026-04-03T18:25:47Z",
"level": "error",
"message": "Database connection failed",
"endpoint": "/api/light",
"status_code": 500
}
{
"component": "frontend",
"timestamp": "2026-04-03T18:25:50Z",
"level": "error",
"message": "Failed to fetch API data",
"url": "/api/light",
"browser": "Chrome"
}
LOG_ENDPOINT = "https://iot-light-sensor..."
def send_log(error_message):
log = {
"component": "backend",
"timestamp": datetime.utcnow().isoformat() + "Z",
"level": "error",
"message": error_message
}
requests.post(LOG_ENDPOINT, json=log)
try:
response = requests.get("https://iot-light...")
response.raise_for_status()
except Exception as e:
send_log(str(e))
# decide how to proceed
How AI supports building and running an IoT indoor light monitoring system.
-- Developer prompt to LLM:
"Write a Python function that reads
lux from a BH1750 sensor on ESP32
and posts JSON to /api/reading"
-- LLM generates:
def read_and_post(room_id):
lux = bh1750.read()
requests.post("/api/reading", json={
"room": room_id,
"lux": lux,
"ts": time.time()
})
def test_valid_lux_posts_correctly():
mock_post(status=200)
read_and_post("room_a")
assert last_post["lux"] > 0
def test_null_lux_is_skipped():
mock_sensor(returns=None)
result = read_and_post("room_a")
assert result is None
def test_network_failure_is_logged():
mock_post(raises=ConnectionError)
read_and_post("room_a")
assert "error" in log.last()
-- Your prompt to the LLM:
"Write pytest unit tests for this function.
Cover: normal lux value, null return,
network timeout, and lux above 65000."
-- LLM generates 4 test functions.
-- You review each one and check:
- does the test assert the right thing?
- does the mock match the real interface?
- is the edge case realistic?
-- Human judgment still needed:
- are these the right edge cases?
- does the test reflect real hardware?
| Embedded | BH1750_sensor2.ino |
| Backend | app.py |
| Frontend | dashboard.html |
Wire.begin() must come firstReal session: github.com/copilot/c/f2d66782...
read_and_post() function in editorsend_data_api.pyUnderstanding where AI fails and why engineering discipline is still required in IoT systems.
Every concept revisited through the room light sensor app.
read_lux() before writing any testEven raw sensor data can become personal data when it reveals who was where and when. The lux graph on the right is evidence of occupancy: and therefore falls under privacy law.
validateLux(-1)Friday Apr 11: office light switched on ~8 PM then off: visible as a sharp late-day plateau. Mon–Sun readings reflect natural daylight only (office light was off).
The system is working: data flows continuously. But two sensors reading differently signals a hardware or placement inconsistency, not a software bug.
Good data quality needs: raw lux stored (not just normalised), sensor ID tagged per reading, and outlier detection to flag implausible jumps automatically.
A window sensor tells you about sunlight; a table sensor tells you about room use. Both are useful but serve different questions. Label each sensor's physical location in the database.
The dashboard shows when light was present, which correlates with occupancy: but only when the office light is on. Natural light alone cannot confirm someone was in the room.
window / tableread_lux(): which SSDF phase?README.md
/ (root)
app/embedded/: Arduino sketches & PCBapp/backend/: Flask APIapp/frontend/: Dashboardapp/architect/: ADRs, diagrams, interfacesapp/devOps/: CI/CD configsapp/embedded/BH1750_test/BH1750_test.ino
0x23 (ADDR=LOW) or 0x5C (ADDR=HIGH)app/embedded/BH1750_test/BH1750_test.ino
void setup() {
Serial.begin(115200);
Wire.begin(21, 22); // SDA, SCL
if (lightMeter.begin()) {
Serial.println("BH1750 started!");
} else {
Serial.println("Error initializing BH1750");
}
}
void loop() {
float lux = lightMeter.readLightLevel();
Serial.print("Light: ");
Serial.print(lux);
Serial.println(" lx");
delay(1000);
}
BH1750_test.ino · lines 1–2
#include <Wire.h>
#include <BH1750.h>
readLightLevel()
BH1750_test.ino · line 7
Wire.begin(21, 22); // SDA=21, SCL=22
Wire.setClock(400000)BH1750_test.ino · lines 6, 18–21
Serial.begin(115200);
Serial.print("Light: ");
Serial.print(lux);
Serial.println(" lx");
BH1750_test.ino · lines 9–13
if (lightMeter.begin()) {
Serial.println("BH1750 started!");
} else {
Serial.println("Error initializing BH1750");
}
if (!lightMeter.begin()) while(1) delay(100);BH1750_test.ino · lines 17–22
float lux = lightMeter.readLightLevel();
Serial.print("Light: ");
Serial.print(lux);
Serial.println(" lx");
delay(1000);
BH1750_test.ino · line 22
delay(1000); // blocking: 1 second
// Non-blocking alternative:
unsigned long last = 0;
void loop() {
if (millis() - last >= 1000) {
last = millis();
float lux = lightMeter.readLightLevel();
}
// WiFi tasks run here
}
app/embedded/PCB Design.png
README.md · Data Structure
{
"meta": {
"entity": "room_light_event",
"version": "1.0",
"source": "indoor light sensor"
},
"data": {
"room_id": "string | integer",
"light_state": "ON | OFF",
"timestamp": "ISO-8601"
}
}
app/architect/diagrams/01_component_high_level.png
app/architect/diagrams/02_component_detailed.png
app/architect/diagrams/Event-Driven Architecture.png
app/architect/diagrams/Use Case Diagram.png
app/architect/diagrams/Activity Diagram.png
if (ON_duration > 12h) → send alert
app/architect/diagrams/04_deployment_architecture.puml
.github/workflows/ · app/devOps/readme.md
main or pull requestRENDER_API_KEY · MONGO_URI in GitHub Secrets
RELEASE_NOTES.md · README.md
app/backend/ · requirements.txt · runtime.txt
app/architect/api/readme.md · Sensor Endpoints
POST /api/events
Content-Type: application/json
{
"device_id": "ls-100-0001",
"room_id": "room-101",
"event_type": "light.measurement",
"lux": 342.5,
"light_state": "ON",
"timestamp": "2026-02-10T15:30:00Z",
"meta": {
"battery_pct": 87,
"signal_rssi_dbm": -45,
"firmware_version": "1.0.0"
}
}
// Success → 202 Accepted
{
"event_id": "evt_20260210_001",
"processed_at": "2026-02-10T15:30:01Z"
}
app/architect/api/readme.md · Dashboard Endpoints
GET /api/sensor/current?room_id=room-101
// Response 200 OK
{
"room_id": "room-101",
"lux": 342.5,
"state": "ON",
"timestamp": "2026-02-10T15:30:00Z",
"freshness_seconds": 2
}
// Cache: 3 seconds (reduces DB load)
// Served from: room_state collection
app/architect/api/readme.md · Dashboard Endpoints
GET /api/history
?room_id=room-101
&limit=100
&from=2026-02-10T00:00:00Z
&to=2026-02-10T23:59:59Z
// Response 200 OK
{
"room_id": "room-101",
"count": 100,
"events": [
{
"lux": 342.5,
"state": "ON",
"timestamp": "2026-02-10T15:30:00Z"
}
]
}
// Cache: 30 seconds · max limit: 500
app/architect/api/readme.md · Dashboard Endpoints
GET /api/stats?room_id=room-101
// Response 200 OK
{
"room_id": "room-101",
"period": "today",
"avg_lux": 285.5,
"min_lux": 12.0,
"max_lux": 850.0,
"sample_count": 28800,
"energy_wh": 230.4
}
// Cache: 5 minutes
// Reads from: daily_usage collection
app/architect/api/readme.md · Usage Endpoints
// Get daily stats for a specific date
GET /api/usage/2026-02-10
// Save a day's aggregated usage
POST /api/usage/save
{
"room_id": "room-101",
"date": "2026-02-10",
"on_seconds": 28800,
"energy_wh": 230.4
}
// Aggregate across periods
GET /api/usage/statistics
?room_id=room-101
&period=weekly // daily | weekly | monthly
app/architect/api/readme.md · app/architect/interface/sensor-backend.md
# Flask validation (pseudocode)
def validate_event(data):
# lux range
assert 0 <= data["lux"] <= 120000
# device_id pattern: ls-NNN-NNNN
assert re.match(r"^ls-\d{3}-\d{4}$",
data["device_id"])
# timestamp within 5-min of server time
delta = abs(now() - parse(data["timestamp"]))
assert delta <= timedelta(minutes=5)
# light_state enum
assert data["light_state"] in {"ON","OFF","UNKNOWN"}
app/architect/api/readme.md · Rate Limiting section
# POST /api/events
# Limit: 1000 req / hour / device_id
# GET endpoints
# Limit: 10,000 req / hour / IP
# 429 response when exceeded:
{
"error": "rate_limit_exceeded",
"retry_after_seconds": 3600,
"limit": 1000,
"window": "1h"
}
app/architect/interface/backend-frontend.md
# Allowed origins (CORS)
ALLOWED_ORIGINS = [
"https://se4cps.github.io", # GitHub Pages
"https://iot-dashboard.onrender.com",
"http://localhost:5500" # dev
]
ALLOWED_METHODS = ["GET", "POST", "OPTIONS"]
# Structured error response
{
"error": "validation_failed",
"detail": "lux must be between 0 and 120000",
"timestamp": "2026-02-10T15:30:00Z",
"request_id": "req_abc123"
}
app/architect/interface/sensor-backend.md
# Transport: HTTPS POST (TLS 1.3)
# Endpoint: /api/events
# Retry: 3 attempts, exponential backoff
# attempt 1: wait 1s
# attempt 2: wait 2s
# attempt 3: wait 4s
# Retry on: 500, 502, 503, 504
# Timeout per attempt: 10s
# Full event payload
{
"device_id": "ls-100-0001",
"room_id": "room-101",
"event_type": "light.measurement",
"lux": 342.5,
"light_state": "ON",
"timestamp": "2026-02-10T15:30:00Z",
"meta": {
"battery_pct": 87,
"signal_rssi_dbm": -45,
"firmware_version": "1.0.0"
}
}
app/architect/decisions/001-event-driven-architecture.md
# Decision: Event-Driven Architecture
# Rejected options:
# - Layered Monolith → requires polling
# (2-day battery life unacceptable)
# - Microservices → over-engineered for MVP
# Chosen: Event-Driven
# Battery: 2 days → 30 days (15x improvement)
# Bus (MVP): in-memory Python queue
# Bus (prod): RabbitMQ or Kafka (planned)
# 4 event types:
# 1. measurement → DB persist
# 2. state_change → notification trigger
# 3. anomaly → alert generate
# 4. alert → observability log
app/architect/data/database-schema.md · Collection 1
// event document
{
event_id: "evt_20260210_001",
schema_version: "1.0",
ts: ISODate("2026-02-10T15:30:00Z"),
room_id: "room-101",
device_id: "ls-100-0001",
light_state: "ON", // ON | OFF | UNKNOWN
lux: 342.5,
meta: {
seq: 1234,
battery_pct: 87,
signal_rssi_dbm: -45,
firmware_version: "1.0.0",
power_mw: 2400,
motion_detected: true
}
}
// Indexes
db.event.createIndex({ device_id:1, ts:-1 })
db.event.createIndex({ room_id:1, ts:-1 })
db.event.createIndex({ ts:1 }, { expireAfterSeconds: 7776000 })
app/architect/data/database-schema.md · Collection 2
// room_state document (one per room)
{
_id: "room-101",
room_id: "room-101",
light_state: "ON",
lux: 342.5,
power_mw: 2400,
motion_detected: true,
last_event_id: "evt_20260210_001",
last_ts: ISODate("2026-02-10T15:30:00Z"),
updated_at: ISODate("2026-02-10T15:30:00.123Z")
}
// Upsert on every incoming event
db.room_state.updateOne(
{ room_id: "room-101" },
{ $set: { light_state:"ON", lux:342.5,
updated_at: new Date() } },
{ upsert: true }
)
app/architect/data/database-schema.md · Collection 3
// daily_usage document
{
_id: "room-101-20260210",
room_id: "room-101",
date: "2026-02-10",
on_seconds: 28800,
off_seconds: 57600,
avg_lux: 285.5,
energy_wh: 230.4,
updated_at: ISODate("2026-02-10T23:59:59Z")
}
// Aggregation to build it
db.event.aggregate([
{ $match: { room_id:"room-101",
ts: { $gte: ISODate("2026-02-10T00:00:00Z"),
$lt: ISODate("2026-02-11T00:00:00Z") }}},
{ $group: { _id: null,
avg_lux: { $avg: "$lux" },
energy_wh: { $sum: { $divide:["$meta.power_mw",3600000] }}
}}
])
app/architect/data/database-schema.md · Collection 4
// device document
{
_id: "ls-100-0001",
device_id: "ls-100-0001",
room_id: "room-101",
device_type: "light_sensor",
status: "ACTIVE", // ACTIVE|DISABLED|RETIRED
firmware_version: "1.0.0",
hardware: {
mcu: "ESP32 Thing Plus",
light_sensor: "VEML7700",
power_sensor: "INA260",
motion_sensor: "PIR"
},
provisioned_at: ISODate("2026-02-01T10:00:00Z"),
last_seen_at: ISODate("2026-02-10T15:30:00Z")
}
// Indexes
db.device.createIndex({ device_id:1 }, { unique:true })
db.device.createIndex({ room_id:1 })
db.device.createIndex({ last_seen_at:-1 }) // offline detect
app/architect/data/database-schema.md · Collection 5
// room document
{
_id: "room-101",
room_id: "room-101",
building: "CTC",
room_number: "113",
floor: 1,
tags: ["classroom", "computer-lab"],
area_sqft: 1200,
max_occupancy: 30,
created_at: ISODate("2026-02-01T00:00:00Z"),
updated_at: ISODate("2026-02-01T00:00:00Z")
}
// Indexes
db.room.createIndex({ room_id:1 }, { unique:true })
db.room.createIndex({ building:1, floor:1 })
// Seed for CTC 113
db.room.insertOne({
room_id: "room-101", building:"CTC",
room_number:"113", floor:1,
tags:["classroom","computer-lab"],
created_at: new Date()
})
app/architect/data/database-schema.md · Collection 6
// alert document
{
alert_id: "alert-20260210-001",
ts: ISODate("2026-02-10T15:30:00Z"),
room_id: "room-101",
device_id: "ls-100-0001",
type: "LIGHT_STUCK_ON",
// types: LIGHT_STUCK_ON | SUDDEN_LUX_DROP
// DEVICE_OFFLINE | SENSOR_ANOMALY
severity: "WARN",
// levels: INFO | WARN | CRITICAL
linked_event_id: "evt_20260210_001",
explain: {
duration_hours: 13.5,
threshold_hours: 12
}
}
// TTL: auto-delete after 90 days
db.alert.createIndex({ ts:1 },
{ expireAfterSeconds: 7776000 })
app/architect/data/database-schema.md · Data Relationships
// Cardinalities
Room (1) ──── (N) Device
Room (1) ──── (1) RoomState
Room (1) ──── (N) Event
Room (1) ──── (N) DailyUsage
Room (1) ──── (N) Alert
Device (1) ── (N) Event
// Storage per room (90 days)
// event ~800 MB (28,800 docs/day)
// room_state 200 B (1 doc forever)
// daily_usage 13.5 KB (1 doc/day, 2-yr TTL)
// device 300 B (reference)
// alert ~22 KB (~10 alerts/day)
// Total: ~850 MB per room
// 10 rooms → ~8.5 GB
// 100 rooms → ~85.0 GB
app/architect/data/database-schema.md · Query Performance
// Target query times
// GET current state → < 10 ms (actual ~5 ms)
// GET last 100 events → < 50 ms (actual ~30 ms)
// Daily aggregation → <200 ms (actual ~150 ms)
// Weekly stats → <500 ms (actual ~400 ms)
// Key compound indexes:
db.event.createIndex({ device_id:1, ts:-1 })
db.event.createIndex({ room_id:1, ts:-1 })
// Caching layer (Flask)
// /api/sensor/current → cache 3 s
// /api/history → cache 30 s
// /api/stats → cache 5 min
app/architect/decisions/ · app/architect/api/readme.md
# Planned improvements
# 1. Authentication (high priority)
# Add: Authorization: Bearer <token>
# to all POST endpoints
# 2. Replace in-memory queue with RabbitMQ
# producer.publish(event)
# consumer.subscribe(handle_event)
# 3. Schema migration v1.0 → v1.1
db.event.updateMany(
{ "meta.power_mw": { $exists: false } },
{ $set: { "meta.power_mw": 0 } }
)
# 4. Upgrade Atlas M0 → M10
# M0: 512 MB · M10: 10 GB · ~$57/mo
dashboard/ · dashboard/app.py · dashboard/requirements.txt
#f5a623 · glassmorphism effectsdashboard/
dashboard/
├── app.py # Flask app + all routes
├── requirements.txt # Flask · pymongo · gunicorn
├── dashboard.html # standalone HTML (root)
├── diagram.html # architecture diagram view
├── templates/
│ ├── dashboard.html # Jinja2 template
│ └── diagram.html
└── tests/
└── app_test.py # unit tests
dashboard/app.py
from flask import Flask, jsonify, request
from pymongo import MongoClient
from dotenv import load_dotenv
import os, certifi, pytz
load_dotenv()
app = Flask(__name__)
# MongoDB connection (TLS + certifi)
client = MongoClient(
os.getenv("MONGO_URI"),
tlsCAFile=certifi.where()
)
db = client["light_sensor_db"]
dashboard/app.py · generate_sensor_reading()
import random, datetime, pytz
def generate_sensor_reading():
pst = pytz.timezone("America/Los_Angeles")
hour = datetime.datetime.now(pst).hour
# Vary lux by time of day
base = 30 if 8 <= hour <= 18 else 10
lux = round(random.uniform(0, base + 20), 2)
return lux # range: 0–50
def classify_lux(lux):
if lux < 5: return "Dark", "#333", "🌑"
if lux < 15: return "Dim", "#666", "🌘"
if lux < 30: return "Normal", "#f5a623", "🌤"
if lux < 45: return "Bright", "#ffd700", "☀️"
return "Very Bright", "#fff", "🌞"
dashboard/app.py · /api/sensor
@app.route("/api/sensor")
def get_sensor():
lux = generate_sensor_reading()
level, color, icon = classify_lux(lux)
return jsonify({
"lux": lux,
"level": level,
"color": color,
"icon": icon,
"timestamp": datetime.datetime.utcnow()
.isoformat() + "Z"
})
# Example response:
# {
# "lux": 28.4,
# "level": "Normal",
# "color": "#f5a623",
# "icon": "🌤",
# "timestamp": "2026-02-10T15:30:00Z"
# }
dashboard/app.py · history + stats routes
@app.route("/api/history")
def get_history():
room = request.args.get("room_id", "living")
limit = int(request.args.get("limit", 50))
col = db[f"room_{room}"]
docs = list(col.find({},{"_id":0})
.sort("timestamp",-1)
.limit(limit))
return jsonify({"room_id": room,
"count": len(docs),
"events": docs})
@app.route("/api/stats")
def get_stats():
room = request.args.get("room_id","living")
col = db[f"room_{room}"]
docs = list(col.find({}, {"lux":1}))
luxes = [d["lux"] for d in docs if "lux" in d]
return jsonify({
"avg_lux": round(sum(luxes)/len(luxes),2),
"min_lux": min(luxes),
"max_lux": max(luxes),
"count": len(luxes)
})
dashboard/app.py · /api/usage/save · /api/usage/statistics
@app.route("/api/usage/save", methods=["POST"])
def save_usage():
data = request.json
data["saved_at"] = datetime.datetime.utcnow()
db["daily_usage"].insert_one(data)
return jsonify({"status": "saved"})
@app.route("/api/usage/statistics")
def usage_statistics():
today = datetime.date.today().isoformat()
# Exclude today: partial day
docs = list(db["daily_usage"].find(
{"date": {"$ne": today}}, {"_id":0}
))
weekly = sum(d.get("hours",0) for d in docs[-7:])
monthly = sum(d.get("hours",0) for d in docs[-30:])
return jsonify({"weekly_hours": weekly,
"monthly_hours": monthly})
dashboard/app.py · /api/rooms/<room> · /api/rooms/all/<date>
# Per-room usage
@app.route("/api/rooms/<room>")
def room_data(room):
doc = db[f"room_{room}"].find_one(
sort=[("timestamp", -1)]
)
return jsonify(doc or {})
# All rooms for a date
@app.route("/api/rooms/all/<date>")
def all_rooms(date):
rooms = ["living","bedroom","kitchen",
"bathroom","office","garage"]
result = {}
for r in rooms:
doc = db[f"room_{r}"].find_one(
{"date": date}, {"_id":0}
)
result[r] = doc or {}
return jsonify(result)
dashboard/app.py · /api/user/login
from hashlib import pbkdf2_hmac
import os
@app.route("/api/user/login", methods=["POST"])
def user_login():
data = request.json
username = data["username"]
password = data["password"]
user = db["user_logins"].find_one(
{"username": username}
)
if not user:
# First-time: register with hashed pw
salt = os.urandom(16)
hashed = pbkdf2_hmac(
"sha256", password.encode(),
salt, 100000
)
db["user_logins"].insert_one({
"username": username,
"password": hashed, "salt": salt
})
return jsonify({"status": "registered"})
# Verify existing user
hashed = pbkdf2_hmac(
"sha256", password.encode(),
user["salt"], 100000
)
ok = hashed == user["password"]
return jsonify({"status":"ok" if ok else "fail"})
dashboard/app.py · admin · device · alert logging
# Admin access log
@app.route("/api/log/admin", methods=["POST"])
def log_admin():
db["admin_logs"].insert_one({
**request.json,
"ts": datetime.datetime.utcnow()
})
return jsonify({"status": "logged"})
# Device activation log
@app.route("/api/log/device", methods=["POST"])
def log_device():
db["device_activity"].insert_one(request.json)
return jsonify({"status": "logged"})
# Alert: light on too long (>40 min)
@app.route("/api/log/alert", methods=["POST"])
def log_alert():
data = request.json
# triggered when light ON > 40 minutes
db["alerts"].insert_one(data)
return jsonify({"status": "alert_logged"})
dashboard/templates/dashboard.html · layout
<!-- Top-level layout -->
<body>
<div class="app-container">
<!-- Left sidebar -->
<aside class="sidebar">
<div class="clock">...</div>
<div class="mini-calendar">...</div>
<div class="donut-chart">...</div>
<div class="room-bars">...</div>
</aside>
<!-- Main content -->
<main class="content">
<div class="room-cards">...</div>
<div class="gauge-panel">...</div>
<div class="controls">...</div>
<div class="stats-section">...</div>
</main>
</div>
</body>
dashboard/templates/dashboard.html · gauge-panel
<!-- SVG gauge: 0–50 lux range -->
<svg class="gauge" viewBox="0 0 200 120">
<path class="gauge-bg"
d="M 20,100 A 80,80 0 0,1 180,100"
fill="none" stroke="#333" stroke-width="16"/>
<path class="gauge-fill"
id="gauge-arc"
d="M 20,100 A 80,80 0 0,1 180,100"
fill="none" stroke="#f5a623"
stroke-width="16"
stroke-dasharray="0 251"/>
<text id="gauge-value" x="100" y="95"
text-anchor="middle"
font-size="24" fill="#fff">--</text>
<text x="100" y="115"
text-anchor="middle"
font-size="10" fill="#888">lux</text>
</svg>
// JS update
function updateGauge(lux) {
const pct = lux / 50;
const dash = pct * 251;
document.getElementById("gauge-arc")
.style.strokeDasharray = `${dash} 251`;
document.getElementById("gauge-value")
.textContent = lux.toFixed(1);
}
dashboard/templates/dashboard.html · room-cards
<!-- Generated for each of 6 rooms -->
<div class="room-card" data-room="living">
<div class="room-icon">🛋️</div>
<div class="room-name">Living Room</div>
<div class="room-lux" id="lux-living">--</div>
<div class="room-status"
id="status-living">--</div>
<div class="room-duration"
id="dur-living">0 min</div>
</div>
// Poll every 3 seconds
setInterval(() => {
fetch("/api/sensor")
.then(r => r.json())
.then(d => {
document.getElementById("lux-living")
.textContent = d.lux + " lx";
});
}, 3000);
dashboard/templates/dashboard.html · sidebar
// Real-time PST clock
function updateClock() {
const pst = new Date().toLocaleString(
"en-US",
{ timeZone: "America/Los_Angeles",
hour: "2-digit", minute: "2-digit",
second: "2-digit", hour12: true }
);
document.getElementById("clock")
.textContent = pst;
}
setInterval(updateClock, 1000);
// Mini calendar navigation
function prevMonth() { currentMonth--; renderCalendar(); }
function nextMonth() { currentMonth++; renderCalendar(); }
function renderCalendar() {
// builds grid of <td> for each day
// highlights today
}
dashboard/templates/dashboard.html · donut-chart
<!-- SVG donut: hours used / 24 -->
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40"
fill="none" stroke="#222"
stroke-width="12"/>
<circle cx="50" cy="50" r="40"
fill="none" stroke="#f5a623"
stroke-width="12"
stroke-dasharray="0 251"
id="donut-arc"
transform="rotate(-90 50 50)"/>
<text x="50" y="55"
text-anchor="middle"
font-size="14" fill="#fff"
id="donut-label">0h</text>
</svg>
function updateDonut(hoursOn) {
const pct = hoursOn / 24;
const arc = pct * 251;
donutArc.style.strokeDasharray =
`${arc} 251`;
donutLabel.textContent = hoursOn + "h";
}
dashboard/templates/dashboard.html · screensaver · wallpaper
// Screensaver: activates after 15 s idle
let idleTimer;
function resetIdle() {
clearTimeout(idleTimer);
idleTimer = setTimeout(showScreensaver, 15000);
}
document.addEventListener("mousemove", resetIdle);
document.addEventListener("keydown", resetIdle);
function showScreensaver() {
document.getElementById("screensaver")
.style.display = "flex"; // shows PST clock
}
// 12 preset wallpapers
const WALLPAPERS = [
"linear-gradient(135deg,#1a1a2e,#16213e)",
"linear-gradient(135deg,#0d0d0d,#1a0a0a)",
// ... 10 more gradients / photo URLs
];
function setWallpaper(i) {
document.body.style.background = WALLPAPERS[i];
}
dashboard/templates/dashboard.html · auto-save
// Save usage to localStorage AND MongoDB
function saveUsage() {
const payload = {
date: new Date().toISOString().slice(0,10),
hours: roomDurations["living"] / 3600,
wallpaper_id: currentWallpaper
};
// 1. Local persistence
localStorage.setItem(
"usage_" + payload.date,
JSON.stringify(payload)
);
// 2. Remote persistence
fetch("/api/usage/save", {
method: "POST",
headers: {"Content-Type":"application/json"},
body: JSON.stringify(payload)
});
}
// Auto-save every 5 minutes
setInterval(saveUsage, 300000);
dashboard/tests/app_test.py
import sys
sys.path.insert(0, "..")
from app import get_sensor_level, generate_sensor_reading
# Test 1: lux=10 → "Dark"
def test_sensor_status_dark():
assert get_sensor_level(10) == "Dark"
# Test 2: lux=30 → "Normal"
def test_sensor_status_normal():
assert get_sensor_level(30) == "Normal"
# Test 3: 100 readings stay within 0–50
def test_generate_sensor_range():
for _ in range(100):
val = generate_sensor_reading()
assert 0 <= val <= 50, \
f"Out of range: {val}"
app/architect/interface/backend-frontend.md
# GET /api/sensor/current?room_id=room-101
# Cache: 3 s · Returns: lux · state · freshness
# GET /api/history
# ?room_id=room-101 &limit=500
# ?start_time=ISO &end_time=ISO
# Cache: 30 s
# GET /api/stats?room_id=room-101
# Cache: 5 min
# Returns: avg_lux · min · max · count
# CORS allowed origins:
# https://se4cps.github.io
# https://iot-dashboard.onrender.com
# http://localhost:5001
# Methods: GET · OPTIONS only
# Error shape:
# { "error": "...", "detail": "...",
# "timestamp": "...", "request_id": "..." }
dashboard/ · app/architect/interface/backend-frontend.md
# Key improvements
# 1. Replace simulated lux with real sensor
# fetch("/api/sensor/current?room_id="+room)
# instead of generate_sensor_reading()
# 2. Fix gauge range
# const MAX_LUX = 65535; // BH1750 max
# const arc = (lux / MAX_LUX) * 251;
# 3. Add session token after login
# const { token } = await fetch("/api/user/login")
# localStorage.setItem("token", token)
# 4. Per-room polling
# rooms.forEach(r =>
# fetch(`/api/sensor/current?room_id=${r}`)
# )
# 5. Replace setInterval with
# Page Visibility API to pause when hidden
dashboard/app.py · dashboard/tests/app_test.py · embedded/BH1750_test.ino
# Weekend test checklist
# EMBEDDED
# [ ] BH1750 reads lux every 3 s without freeze
# [ ] Serial monitor shows no 0.0 lux gaps
# [ ] WiFi reconnects after router reboot
# BACKEND (Render.com)
# [ ] POST /api/events accepted (202) all weekend
# [ ] No 429 rate-limit hits in logs
# [ ] MongoDB Atlas event count grows as expected
# DASHBOARD
# [ ] Room cards update every 3 s
# [ ] Usage stats persist across browser refresh
70 closed · 21 open · 91 tickets across 10 milestones
You built a real Industry 4.0 system: the skills transfer forward.
The V-Model does not disappear: it becomes the skeleton inside every autonomous pipeline.
Pure Software Engineering
IoT / CPS Software Engineering
The physical world makes every bug consequential.
Sequential phases · manual hand-offs · fixed verification gates · human review at every checkpoint
AI co-pilots at every phase · humans retain control · continuous feedback replaces one-way hand-offs
The V dissolves into a continuous cognitive loop · AI agents manage the full pipeline end-to-end
Click any cover to open on Amazon
Class Schedule:
Time: 12:30 PM: 1:25 PM on Monday, Wednesday, Friday
Location: Benerd School of Education 208
In this course on Software Engineering within Industry 4.0 ecosystems, we will explore the integration and application of IoT and Cyber-Physical Systems (CPS). The course takes a dual perspective approach.
| Teaching Assistant: | TBD |
| Email: | TBD |
| Office Location: | Student Support Center |
| Office Hours: | By appointment |
| Instructor: | Dr. Solomon Berhe |
| Email: | sberhe@pacific.edu |
| Zoom: | Zoom Meeting Link |
| Office: | CTC 117 |
| Office Hours: | Mon/Wed/Fri, 2:00–3:15 PM (or by appointment) |
Textbooks:
| Homework: | Includes written exercises and programming assignments. Submit via Canvas or GitHub. |
| Team Projects: | Two projects (design, development, and application integration) |
| Exams: | One midterm and one final. commonly may bring one handwritten note sheet (8.5x11) |
All course materials, announcements, and assignments will be posted on Canvas. Check regularly for updates
commonly are expected to act with integrity, honesty, and responsibility. Violations of the Honor Code will be reported to the Office of Student Conduct and Community Standards
For more details, see the University Academic Honesty Policy
Source Document: Course Syllabus
Please inform me of the light status in the room
Light ON / OFF
Timestamped
Receive
Validate
Store
{
"meta": {
"entity": "room_light_event",
"description": "Single light sensor observation per room",
"source": "indoor light sensor",
"version": "1.0",
"created_at": "ISO-8601 timestamp"
},
"data": {
"room_id": "string | integer",
"light_state": "ON | OFF",
"timestamp": "ISO-8601 timestamp"
}
}
Current status
History
Light ON > 12h
┌───────────────┐ ┌────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 1. Planning │ → │ 2. Daily Scrum │ → │ 3. Refinement │ → │ 4. Demo / Review │ → │ 5. Retro │
│ What to build │ │ Sync & unblock │ │ Prepare backlog │ │ Show results │ │ Improve process │
└───────────────┘ └────────────────┘ └──────────────────┘ └──────────────────┘ └─────────────────┘
Same order · Same cadence · Every week
Individuals and interactions
Working software
Customer collaboration
Responding to change
Foundational principles for iterative development
Reviewing what commonly found when using Copilot across the full engineering lifecycle: requirements, architecture, implementation, testing, and verification.
Prompt given to Copilot:
Generate user requirements for an IoT indoor light sensor system that monitors ambient lux levels in a smart building and sends alerts when high lux is detected outside regular work hours.
What did Copilot produce? What did it leave out?
Copilot proposed a 6-layer pipeline with clear separation of concerns.
Commonly correctly identified this as the main architectural benefit.
Prompt: Write an API endpoint that receives POST requests with sensor readings, validates input, stores in MongoDB, and returns a status response.
sensorId, lux, timestampCopilot produces a functional scaffold, not a production-ready service.
Treat AI output like a PR from a new contributor: read every line before merging.
Prompt: Generate unit tests for classifyLight(lux).
'dark': lux < 50'dim': 50 ≤ lux < 150'normal': 150 ≤ lux ≤ 500'bright': lux > 500Copilot produced 38 test cases covering boundaries, null, NaN, Infinity, and negatives.
This is standard test technique: AI executes it reliably for pure functions.
Prompt: Describe a system test plan that checks end-to-end data flow from sensor reading to dashboard, including latency and alert accuracy.
These require domain knowledge that AI cannot infer from a prompt.
sensor_readings(sensorId, timestamp desc): dashboard querieslux ≥ 0, sensorId requiredCopilot produced the schema; commonly correctly noted missing range validation.
Three techniques from the paper applied to the lux sensor system: data preprocessing, RAG with re-ranking, and chain-of-thought prompting.
All three homework examples address this root problem.
Research idea: simplify + enrich raw sensor data before the LLM sees it.
Accuracy: 37.3% → 43.3%
Commonly applied 1-min averages (720 → 60 values), rolling stats, and scalar quantization (32-bit float → 8-bit int, 4× compression).
Without metadata: LLM sees "15." With metadata: "Room B server room, ceiling, south window, 15 lux at 11 PM outside work hours."
Research idea: automatically retrieve domain knowledge + labeled demonstrations instead of hard-coding expert rules.
Accuracy: 43.3% → 59.3% (+ demos: 66.7%)
Commonly noted: MongoDB history already contains this: the KB is curated from existing sprint data.
Too many docs (m=5) → LLM output becomes noisy and inconsistent.
Commonly identified this as the key trade-off: more retrieval ≠ better answers.
Research idea: assign the LLM a domain-expert role and require step-by-step reasoning before a final answer.
Accuracy: 66.7% → 77.8%
ANALYSIS: cross-reference schedule + compare all rooms.
ANSWER: "Lights left on; check motion sensor and relay; ~0.6 kWh/hr energy waste."
Without CoT: "high lux anomaly detected." With CoT: actionable diagnosis with energy cost.
Each technique adds independently; the combination achieves expert-level reasoning.
Commonly confirmed: combination outperforms any single technique alone (ablation result).
Commonly noted: KB is extensible: one team can add domain docs without rewriting reasoning logic.
lux / max_rangeint((lux / 300) × 255) → 8-bit integerSmaller payload = faster transmission + cheaper storage. Vector space enables semantic similarity.
Answer: B: Integrates computation, networking & physical processes
Answer: D: Mechanical production powered by water and steam
Answer: A: Electrification, steel, and mass production
Answer: C: Electronics, IT systems, and programmable automation
Answer: B: CPS + IoT + AI + real-time data integration
Answer: A: A high-level requirement grouping related features
Answer: C: As a [role], I want [action], Because [reason]
Answer: D: Defines testable conditions a user story must meet
Answer: B: How the system is built using chosen technologies
Answer: A: A prioritized list of epics, stories, and tasks
Answer: D: Systems mirror their team's communication structure
Answer: B: Sync progress, share plans, surface blockers
Answer: C: Cross-functional product teams working in sprints
Answer: A: Physical changes are costly, slow, and hard to reverse
Answer: D: Reflecting on process and defining team improvements
Answer: C: Encrypt data, control access, verify signed updates
Answer: A: Address security and testing earlier in development
Answer: B: Define IoT device requirements for procurement and design
Answer: D: Recover: restore service, verify stability, prevent recurrence
Answer: C: Energy use and environmental impact shape system design
Answer: C: Components, interfaces, and early decisions
Answer: A: Clear structural boundaries between components
Answer: B: Microservices: independent ownership and deployment
Answer: D: Service boundaries and dependencies
Answer: A: Small team, fast iteration, simple ops
Answer: D: Deliver compliance corrections to deployed devices
Answer: C: Evolves with technology, standards, and case law
Answer: B: Legal risk can move compliance ahead of features
Answer: A: Data layer: privacy, retention, access
Answer: D: Liability increases as regulations and threats evolve
Answer: B: Artifacts (requirements, design, code) are correct
Answer: D: System delivers real user and business value
Answer: A: System test: full integrated stack
Answer: C: Arrange, Act, Assert
Answer: B: Map each dev phase to a corresponding test level
Answer: B: Alternate active/sleep states to cut power draw
Answer: D: Filter and aggregate at the edge before sending
Answer: A: Lower bandwidth at cost of more local processing
Answer: C: Deep sleep between sensor reads extends battery life
Answer: A: Unsanitized user input merged into database queries
Answer: C: Role-based control maps permissions to defined roles
Answer: D: Securely sharing one key across many devices is hard
Answer: B: Multiple layered controls each catch what others miss
Answer: C: New code exposed to a small user subset first
Answer: A: Automate build, test, deploy for faster safe delivery
Answer: B: Traffic gradually shifts back to the stable release
Answer: C: Beta testing with real external users before GA
Answer: D: Detect faults before failure, preventing downtime
Answer: B: Edge AI infers on-device, cutting latency and bandwidth
Answer: A: Virtual simulation for safe model training and testing
Answer: D: Reuse pretrained weights, cutting data and compute needs
Close out the project, finalise your backlog, and leave the codebase production-ready.
Due: April 27
README.md: project overview, architecture, how to run,
limitations_old,
_backup, temp, and unused scripts
main
requirements.txt
v2.0Reflect on how the design would change if you built the same IoT light sensor app for an indoor garage instead of a smart office building.
Due: April 27
What is the primary difference in the sensing environment between a smart office building and an indoor garage?
How would your sensor thresholds and trigger logic change for a garage?
What new safety requirements would the garage context introduce that the office version did not need?
Would you keep the same edge-cloud architecture, or change it? Why?
Name one legal or compliance constraint specific to a garage and explain how it would affect your design.
How many weekly sprints would you recommend for the garage adaptation project, and what would each sprint deliver?
What skill levels and roles would you require for the garage IoT project team (embedded, backend, database, etc.)?
How would you plan testing given that the garage is in a different location (same city) than the R&D office?
What hardware would you need for development and testing of the garage IoT light sensor app?
Please ask anytime sberhe@pacific.edu
Table of Contents | Office Hours: Mon, Wed 2:00 PM - 3:00 PM, CTC 117 | sberhe@pacific.edu | Syllabus | Join Zoom | Backlog | Project | Slack Registration | PlantUML Editor | Postman Workspace | Live Demo