mirror of
https://github.com/m3y54m/Embedded-Engineering-Roadmap.git
synced 2026-10-10 16:28:24 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e0ba15dc9 | ||
|
|
4c52895477 | ||
|
|
2e2ca453c9 | ||
|
|
b83bdda42f | ||
|
|
8704cf6234 | ||
|
|
ad4a5406d9 | ||
|
|
2c70f347de | ||
|
|
752f36a921 |
@@ -99,7 +99,7 @@ jobs:
|
||||
path: _site
|
||||
|
||||
release:
|
||||
name: Release ${{ needs.build.outputs.version }}
|
||||
name: Publish release
|
||||
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' && needs.build.outputs.release == 'true'
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
@@ -30,6 +30,7 @@ Contributions from everyone are welcomed. To keep the roadmap practical, accessi
|
||||
- Contributions should match the topics and structure of the roadmap.
|
||||
- Areas where contributors have experience or genuine interest should be focused on.
|
||||
- If a new topic is thought to make the roadmap more complete, it may be suggested. New topics should be proposed thoughtfully, considering their usefulness and relevance for other learners.
|
||||
- Connections between topics on the map are listed in `site/map.json` under `links` as `["Topic", "Other topic", "why they are related"]`, using the names shown on the map. Connect topics that a learner should study together or that depend on each other, and keep the reason to one short sentence. Tests fail if a name is misspelled, a pair is listed twice or a topic has no connection.
|
||||
|
||||
## 6. Versioning and Releases
|
||||
|
||||
|
||||
+34
-5
@@ -155,8 +155,33 @@ function topicChips(nodes) {
|
||||
}, swatch(n), n.title, h('span', { class: 'n' }, n.total))));
|
||||
}
|
||||
|
||||
function related(node) {
|
||||
// Hand-picked connections of a topic (from map.json), each with the reason it is listed.
|
||||
function relatedTopics(node) {
|
||||
const seen = new Set();
|
||||
const items = [];
|
||||
for (const box of node.boxes || []) {
|
||||
for (const { box: other, reason } of box.related || []) {
|
||||
if (seen.has(other)) continue;
|
||||
seen.add(other);
|
||||
items.push({ other, reason });
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
function curatedList(items) {
|
||||
return h('ul', { class: 'related' }, items.map(({ other, reason }) => {
|
||||
const target = other.topics[0];
|
||||
return h('li', {}, h('button', { type: 'button', onclick: () => navigate(target, { reveal: true }) },
|
||||
swatch(target),
|
||||
h('span', {}, other.text),
|
||||
h('span', { class: 'why' }, reason)));
|
||||
}));
|
||||
}
|
||||
|
||||
function related(node, skip = new Set()) {
|
||||
const items = [...node.links]
|
||||
.filter((link) => !skip.has(link.source === node ? link.target : link.source))
|
||||
.sort((a, b) => b.weight - a.weight)
|
||||
.map((link) => {
|
||||
const other = link.source === node ? link.target : link.source;
|
||||
@@ -282,13 +307,13 @@ function overview(root) {
|
||||
section(null, h('div', { class: 'stats' },
|
||||
h('div', { class: 'stat' }, h('b', {}, stats.topics), h('span', {}, 'topics')),
|
||||
h('div', { class: 'stat' }, h('b', {}, stats.resources), h('span', {}, 'resources')),
|
||||
h('div', { class: 'stat' }, h('b', {}, stats.links), h('span', {}, 'connections')))),
|
||||
h('div', { class: 'stat' }, h('b', {}, state.plan ? state.plan.connections.filter((c) => c.reason).length : stats.links), h('span', {}, 'connections')))),
|
||||
state.plan ? section('Diagram topics by area', areaSummary()) : null,
|
||||
...groups.map((g) => section(g.title, topicChips(g.topics))),
|
||||
section('How to explore', h('ul', { class: 'howto' },
|
||||
h('li', {}, 'Map follows the roadmap diagram: topics sit in Software, Hardware or both, and are colored by importance.'),
|
||||
h('li', {}, 'Use the Areas chips to highlight a single area or only the Software ∩ Hardware cross-section.'),
|
||||
h('li', {}, 'Hover or select a topic to see lines to the topics it shares resources with (solid) or is mentioned by (dotted).'),
|
||||
h('li', {}, 'Hover or select a topic to see solid lines to its related topics. Fainter dashed lines come from resources that share or mention a topic.'),
|
||||
h('li', {}, 'Outline lists every topic of the README; use it to browse topics that are not on the map.'),
|
||||
h('li', {}, 'Press ', h('kbd', {}, '/'), ' to search topics and resources.'))),
|
||||
section(null, h('p', { class: 'res-note' }, 'Built from the ',
|
||||
@@ -325,7 +350,7 @@ function renderPanel(node) {
|
||||
node.importance ? h('span', { class: `pill imp-pill ${node.importance}` }, IMPORTANCE[node.importance]) : null,
|
||||
h('span', { class: 'pill' }, plural(node.total, 'resource')),
|
||||
node.children.length ? h('span', { class: 'pill' }, plural(node.children.length, 'subtopic')) : null,
|
||||
node.links.length ? h('span', { class: 'pill' }, `${node.links.length} connected`) : null)));
|
||||
relatedTopics(node).length ? h('span', { class: 'pill' }, `${relatedTopics(node).length} related`) : null)));
|
||||
|
||||
if (node === root) {
|
||||
panel.append(...overview(root));
|
||||
@@ -333,7 +358,11 @@ function renderPanel(node) {
|
||||
}
|
||||
if (node.description.length) panel.append(section('About', description(node.description)));
|
||||
if (node.children.length) panel.append(section('Subtopics', topicChips(node.children)));
|
||||
if (node.links.length) panel.append(section('Connected topics', related(node)));
|
||||
const curated = relatedTopics(node);
|
||||
if (curated.length) panel.append(section('Related topics', curatedList(curated)));
|
||||
const listed = new Set(curated.flatMap(({ other }) => other.topics));
|
||||
const evidence = node.links.filter((link) => !listed.has(link.source === node ? link.target : link.source));
|
||||
if (evidence.length) panel.append(section(curated.length ? 'Also linked by resources' : 'Linked by resources', related(node, listed)));
|
||||
panel.append(resources(node));
|
||||
}
|
||||
|
||||
|
||||
+31
-4
@@ -118,15 +118,41 @@ export function linkDiagram(map, data) {
|
||||
return null;
|
||||
};
|
||||
|
||||
// A connection joins two boxes. `reason` is set for the hand-picked ones in map.json; the rest
|
||||
// are evidence only (a shared resource, or one topic's text naming another).
|
||||
const pairs = new Map();
|
||||
const pairFor = (a, b) => {
|
||||
const [p, q] = a.id < b.id ? [a, b] : [b, a];
|
||||
const id = `${p.id}|${q.id}`;
|
||||
if (!pairs.has(id)) pairs.set(id, { a: p, b: q, reason: null, weight: 0, shared: 0, links: [] });
|
||||
return pairs.get(id);
|
||||
};
|
||||
|
||||
const topicBoxes = new Map(boxes.filter((b) => !b.header).map((b) => [b.text, b]));
|
||||
const invalidLinks = [];
|
||||
for (const box of boxes) box.related = [];
|
||||
for (const [from, to, reason] of map.links || []) {
|
||||
const a = topicBoxes.get(from);
|
||||
const b = topicBoxes.get(to);
|
||||
if (!a || !b || a === b || typeof reason !== 'string' || !reason.trim()) {
|
||||
invalidLinks.push(`${from} - ${to}`);
|
||||
continue;
|
||||
}
|
||||
const pair = pairFor(a, b);
|
||||
if (pair.reason) {
|
||||
invalidLinks.push(`${from} - ${to} (listed twice)`);
|
||||
continue;
|
||||
}
|
||||
pair.reason = reason.trim();
|
||||
a.related.push({ box: b, reason: pair.reason });
|
||||
b.related.push({ box: a, reason: pair.reason });
|
||||
}
|
||||
|
||||
for (const link of data.links) {
|
||||
const a = boxFor(link.source);
|
||||
const b = boxFor(link.target);
|
||||
if (!a || !b || a === b) continue;
|
||||
const [p, q] = a.id < b.id ? [a, b] : [b, a];
|
||||
const id = `${p.id}|${q.id}`;
|
||||
if (!pairs.has(id)) pairs.set(id, { a: p, b: q, weight: 0, shared: 0, links: [] });
|
||||
const pair = pairs.get(id);
|
||||
const pair = pairFor(a, b);
|
||||
pair.weight += link.weight;
|
||||
pair.shared += link.shared.length;
|
||||
pair.links.push(link);
|
||||
@@ -139,6 +165,7 @@ export function linkDiagram(map, data) {
|
||||
boxFor,
|
||||
connections: [...pairs.values()],
|
||||
unmatched: boxes.filter((b) => !b.header && !b.topics.length).map((b) => b.text),
|
||||
invalidLinks,
|
||||
invalid: boxes.filter((b) => !b.header && !IMPORTANCE_LEVELS.includes(b.importance)).map((b) => `${b.text}: ${b.importance}`),
|
||||
};
|
||||
}
|
||||
|
||||
+21
-10
@@ -21,12 +21,9 @@
|
||||
.start-strip,
|
||||
.map-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.start-strip > *,
|
||||
@@ -328,15 +325,25 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.link.mention,
|
||||
.link-all.mention {
|
||||
.link.evidence,
|
||||
.link-all.evidence {
|
||||
stroke-dasharray: 5 5;
|
||||
stroke-opacity: 0.45;
|
||||
}
|
||||
|
||||
.link-end {
|
||||
fill: var(--accent);
|
||||
}
|
||||
|
||||
.link-all {
|
||||
stroke-opacity: 0.4;
|
||||
}
|
||||
|
||||
.link-all.curated {
|
||||
stroke-width: 1.2px;
|
||||
stroke-opacity: 0.28;
|
||||
}
|
||||
|
||||
.layer-linksAll {
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s;
|
||||
@@ -350,10 +357,6 @@
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.link-end {
|
||||
fill: var(--accent);
|
||||
}
|
||||
|
||||
.credit-main {
|
||||
fill: var(--text);
|
||||
font-size: 14px;
|
||||
@@ -411,11 +414,19 @@
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
/* One swipeable row per strip; the fade at the right edge shows there is more. */
|
||||
.start-strip,
|
||||
.map-toolbar {
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
padding-right: 28px;
|
||||
mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
|
||||
}
|
||||
|
||||
.start-strip::-webkit-scrollbar,
|
||||
.map-toolbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.start-chip,
|
||||
|
||||
+210
@@ -258,6 +258,216 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
["C", "Memory Management", "Pointers, the stack and the heap are managed by hand in C"],
|
||||
["C++", "Memory Management", "Ownership, RAII and smart pointers decide who frees memory"],
|
||||
["Rust", "Memory Management", "Ownership and borrowing replace manual memory management"],
|
||||
["Python", "Memory Management", "Garbage collection; MicroPython has to fit in small RAM"],
|
||||
["C", "C++", "C++ builds on C, and both are mixed in one firmware project"],
|
||||
["C", "Compilers / GCC", "The toolchain that turns C code into firmware"],
|
||||
["C", "Linux Kernel", "The kernel and its drivers are written in C"],
|
||||
["C", "GDB", "Source-level debugging of C programs"],
|
||||
["C++", "Design Patterns", "Classic object-oriented patterns are taught in C++"],
|
||||
["C++", "Qt Framework", "Qt is a C++ framework"],
|
||||
["Python", "Bash Scripting", "Both automate tooling, tests and build tasks"],
|
||||
["Python", "SIL / HIL Testing", "Test rigs and HIL benches are often scripted in Python"],
|
||||
["Python", "Edge AI", "Models are usually trained in Python before deployment"],
|
||||
["Rust", "Embedded Security", "Memory safety removes a whole class of vulnerabilities"],
|
||||
["Assembly", "Computer Architecture", "Instructions map directly to registers and the memory model"],
|
||||
["Assembly", "Compilers / GCC", "Reading compiler output and writing startup code"],
|
||||
["Assembly", "Interrupts", "Vector tables and context saving are done in assembly"],
|
||||
|
||||
["Algorithms & Data Structures", "Memory Management", "The data structure you pick decides memory use and allocation"],
|
||||
["Algorithms & Data Structures", "Digital Signal Processing", "Filters and transforms are algorithms with tight time and memory budgets"],
|
||||
["Algorithms & Data Structures", "Problem-Solving Skills & Critical Thinking", "Choosing and breaking down an approach"],
|
||||
["Design Patterns", "State Machines", "The State pattern; many embedded designs are state machines"],
|
||||
["State Machines", "Interrupts", "Events and interrupts drive state transitions"],
|
||||
["State Machines", "RTOS Basics", "RTOS tasks are usually written as event-driven state machines"],
|
||||
["State Machines", "Digital Design", "Finite-state machines are also the core of sequential logic in hardware"],
|
||||
|
||||
["JTAG / SWD", "OpenOCD", "OpenOCD drives JTAG/SWD debug probes"],
|
||||
["JTAG / SWD", "GDB", "GDB reaches the target through a JTAG/SWD probe"],
|
||||
["JTAG / SWD", "Bootloader / DFU", "Flashes the first bootloader and recovers bricked boards"],
|
||||
["OpenOCD", "GDB", "OpenOCD is the GDB server for most microcontrollers"],
|
||||
["GDB", "Compilers / GCC", "GDB reads the debug information the compiler emits"],
|
||||
["GDB", "Problem-Solving Skills & Critical Thinking", "Debugging is structured problem solving"],
|
||||
|
||||
["Agile / SCRUM", "V-Model", "Two common process models; safety projects often combine them"],
|
||||
["Agile / SCRUM", "CI/CD Pipelines", "Short iterations rely on automated builds and tests"],
|
||||
["Agile / SCRUM", "Teamwork & Collaborative Abilities", "Scrum is built around close teamwork"],
|
||||
["Agile / SCRUM", "Organizational & Time Management Skills", "Sprint planning and prioritising work"],
|
||||
["V-Model", "Standards & Certifications", "Certified projects follow V-model verification"],
|
||||
["V-Model", "SIL / HIL Testing", "Integration and system testing on SIL and HIL benches"],
|
||||
["Git", "SVN", "Both are version control systems; Git is the common choice today"],
|
||||
["Git", "CI/CD Pipelines", "Pipelines start from commits and pull requests"],
|
||||
["Git", "Teamwork & Collaborative Abilities", "Branches and code review are how teams share code"],
|
||||
["Communication Skills", "Teamwork & Collaborative Abilities", "Collaboration depends on clear communication"],
|
||||
|
||||
["Compilers / GCC", "Make / CMake", "Build systems drive the compiler and linker"],
|
||||
["Make / CMake", "CI/CD Pipelines", "Pipelines run the same build commands"],
|
||||
["Make / CMake", "Buildroot / Yocto", "Buildroot and Yocto are build systems for whole Linux images"],
|
||||
["Bash Scripting", "CI/CD Pipelines", "Most pipeline steps are shell commands"],
|
||||
["Bash Scripting", "Operating System Fundamentals", "The shell and processes are core OS concepts"],
|
||||
["Docker", "CI/CD Pipelines", "Containers give pipelines a reproducible toolchain"],
|
||||
["Docker", "Buildroot / Yocto", "Yocto builds are commonly run in containers"],
|
||||
|
||||
["TDD & Unit Testing", "CI/CD Pipelines", "Tests run automatically on every change"],
|
||||
["TDD & Unit Testing", "Hardware Simulation / Emulation", "Tests run on a host against simulated hardware"],
|
||||
["SIL / HIL Testing", "Hardware Simulation / Emulation", "SIL runs the firmware against simulated hardware"],
|
||||
["SIL / HIL Testing", "CI/CD Pipelines", "HIL rigs can run as pipeline stages"],
|
||||
["Standards & Certifications", "Embedded Security", "Security standards such as IEC 62443"],
|
||||
["Standards & Certifications", "AUTOSAR", "Automotive software is developed to ISO 26262"],
|
||||
["Standards & Certifications", "PCB Design / EMC", "EMC compliance testing is a certification requirement"],
|
||||
|
||||
["Embedded Security", "Bootloader / DFU", "Secure boot and signed firmware updates"],
|
||||
["Embedded Security", "IoT", "Connected devices are exposed to attacks and need secure updates"],
|
||||
["Embedded Security", "TCP/IP", "TLS runs on top of TCP/IP"],
|
||||
["Embedded GUI", "Qt Framework", "Qt is a common GUI toolkit on embedded Linux"],
|
||||
["Embedded GUI", "SPI", "Small displays are often driven over SPI"],
|
||||
["Embedded GUI", "DMA", "DMA moves frame buffers to the display"],
|
||||
["IoT", "MQTT", "A common messaging protocol for IoT devices"],
|
||||
["IoT", "CoAP", "A lightweight request/response protocol for constrained devices"],
|
||||
["IoT", "Wi-Fi", "A typical uplink for mains-powered devices"],
|
||||
["IoT", "LoRa", "Long-range, low-power links"],
|
||||
["IoT", "NB-IoT", "Cellular connectivity for low-power devices"],
|
||||
["IoT", "Power Management", "Battery-powered nodes need low-power design"],
|
||||
["Edge AI", "Digital Signal Processing", "Features are extracted from sensor signals before inference"],
|
||||
["Edge AI", "Sensors & Actuators", "Models run on sensor data"],
|
||||
["Edge AI", "Power Management", "Inference has to fit a small energy budget"],
|
||||
["Edge AI", "FPGA Development", "FPGAs are used as inference accelerators"],
|
||||
["AUTOSAR", "CAN", "AUTOSAR communication stacks run over CAN"],
|
||||
["AUTOSAR", "Ethernet", "Automotive Ethernet is part of the AUTOSAR stack"],
|
||||
["AUTOSAR", "RTOS Basics", "AUTOSAR OS is an OSEK-based real-time operating system"],
|
||||
|
||||
["Operating System Fundamentals", "RTOS Basics", "Scheduling, tasks and synchronisation carry over"],
|
||||
["Operating System Fundamentals", "Linux Kernel", "Processes, memory and drivers as implemented in Linux"],
|
||||
["Operating System Fundamentals", "Threading / Parallelism", "Threads and scheduling basics"],
|
||||
["Operating System Fundamentals", "Memory Management", "Virtual memory and allocators"],
|
||||
["Operating System Fundamentals", "Computer Architecture", "MMU, privilege levels and caches"],
|
||||
["Linux Kernel", "Device Drivers", "Drivers are kernel modules"],
|
||||
["Linux Kernel", "U-Boot", "U-Boot loads the kernel and the device tree"],
|
||||
["Linux Kernel", "Buildroot / Yocto", "The kernel is configured and built as part of the image"],
|
||||
["Linux Kernel", "Memory Technologies & File Systems", "Flash-friendly file systems and block layers"],
|
||||
["Device Drivers", "I2C", "Linux drivers talk to devices over buses like I2C"],
|
||||
["Device Drivers", "SPI", "Linux drivers talk to devices over buses like SPI"],
|
||||
["Device Drivers", "GPIO", "Pins are exposed to drivers through the GPIO subsystem"],
|
||||
["Device Drivers", "Interrupts", "Drivers handle device interrupts"],
|
||||
["Device Drivers", "USB", "The USB stack needs host and device drivers"],
|
||||
["Device Drivers", "PCIe", "PCIe devices need kernel drivers"],
|
||||
["U-Boot", "Bootloader / DFU", "The same job on Linux boards: initialise hardware and load the OS"],
|
||||
["U-Boot", "Buildroot / Yocto", "Both build and ship the bootloader with the image"],
|
||||
["Threading / Parallelism", "IPC", "Threads and processes exchange data through IPC"],
|
||||
["Threading / Parallelism", "RTOS Basics", "RTOS tasks, mutexes and priorities"],
|
||||
["IPC", "RTOS Basics", "Queues and semaphores are the RTOS form of IPC"],
|
||||
|
||||
["RTOS Basics", "FreeRTOS", "The most common first RTOS"],
|
||||
["RTOS Basics", "Zephyr", "The same scheduling, synchronisation and timing concepts"],
|
||||
["RTOS Basics", "QNX", "The same scheduling, synchronisation and timing concepts"],
|
||||
["RTOS Basics", "µC/OS", "The same scheduling, synchronisation and timing concepts"],
|
||||
["RTOS Basics", "RT-Thread", "The same scheduling, synchronisation and timing concepts"],
|
||||
["RTOS Basics", "Interrupts", "ISRs hand work over to tasks"],
|
||||
["RTOS Basics", "Timers / Counters", "The tick timer and software timers"],
|
||||
["FreeRTOS", "Memory Management", "FreeRTOS offers several heap allocation schemes"],
|
||||
["FreeRTOS", "TCP/IP", "FreeRTOS+TCP is its TCP/IP stack"],
|
||||
["Zephyr", "Bluetooth", "Zephyr ships its own Bluetooth stack"],
|
||||
["Zephyr", "Thread", "Zephyr integrates OpenThread"],
|
||||
["QNX", "IPC", "A microkernel built around message passing"],
|
||||
["QNX", "Standards & Certifications", "Certified for safety-critical use"],
|
||||
|
||||
["GPIO", "Interrupts", "Pin edges raise interrupts"],
|
||||
["GPIO", "PWM", "PWM is a timer-driven GPIO function"],
|
||||
["GPIO", "Principles of Electric Circuits", "Pull-ups, drive strength and current limits"],
|
||||
["GPIO", "Breadboarding", "The first experiment: an LED and a button on a breadboard"],
|
||||
["ADC / DAC", "Sensors & Actuators", "Analog sensors are read through an ADC"],
|
||||
["ADC / DAC", "DMA", "DMA moves samples without using the CPU"],
|
||||
["ADC / DAC", "Digital Signal Processing", "Sampling theory and filtering"],
|
||||
["ADC / DAC", "Timers / Counters", "Timers trigger conversions at a fixed rate"],
|
||||
["Timers / Counters", "PWM", "PWM is generated by timer hardware"],
|
||||
["Timers / Counters", "Interrupts", "Timer events raise interrupts"],
|
||||
["Timers / Counters", "Clock Management", "Timer rates derive from the clock tree"],
|
||||
["Timers / Counters", "Watchdog", "A watchdog is a special timer"],
|
||||
["PWM", "Sensors & Actuators", "Drives motors, LEDs and servos"],
|
||||
["PWM", "Control Theory", "A PID loop outputs a PWM duty cycle"],
|
||||
["PWM", "Oscilloscope", "Duty cycle and frequency are checked on a scope"],
|
||||
["Watchdog", "RTOS Basics", "Tasks must check in; a stuck task triggers a reset"],
|
||||
["Interrupts", "Computer Architecture", "Vector table, priorities and the exception model"],
|
||||
["DMA", "SPI", "High-throughput transfers without CPU load"],
|
||||
["DMA", "UART", "Streaming serial data without CPU load"],
|
||||
["Clock Management", "Power Management", "Clock gating and frequency scaling save power"],
|
||||
["Clock Management", "UART", "Baud rates derive from the clock"],
|
||||
["Clock Management", "Oscilloscope", "Checking oscillator and clock output"],
|
||||
["Power Management", "Interrupts", "Wake-up sources are interrupts"],
|
||||
["Power Management", "Bluetooth", "BLE is designed around duty cycling"],
|
||||
["Power Management", "Hardware Design Basics", "Power supply and battery design"],
|
||||
["Bootloader / DFU", "Memory Technologies & File Systems", "Firmware lives in flash and updates need safe writes"],
|
||||
["Bootloader / DFU", "USB", "The USB DFU class updates firmware"],
|
||||
|
||||
["Memory Technologies & File Systems", "Memory Management", "RAM, flash and EEPROM sizes set the memory budget"],
|
||||
["Memory Technologies & File Systems", "SPI", "SPI flash and SD cards"],
|
||||
["Hardware Simulation / Emulation", "Digital Design", "Logic is verified in simulation before hardware exists"],
|
||||
["Hardware Simulation / Emulation", "FPGA Development", "HDL designs are simulated before synthesis"],
|
||||
["Hardware Simulation / Emulation", "Principles of Electric Circuits", "Circuits can be simulated (SPICE) before they are built"],
|
||||
["Sensors & Actuators", "I2C", "Most digital sensors use I2C"],
|
||||
["Sensors & Actuators", "SPI", "Faster sensors use SPI"],
|
||||
["Sensors & Actuators", "Digital Signal Processing", "Raw readings are filtered and calibrated"],
|
||||
["Sensors & Actuators", "Control Theory", "Closed-loop control reads sensors and drives actuators"],
|
||||
["Digital Signal Processing", "Control Theory", "Filters and controllers share the same math"],
|
||||
["Digital Signal Processing", "Basic Math & Calculus", "Transforms and filters are applied calculus"],
|
||||
["Control Theory", "Basic Math & Calculus", "Differential equations and transfer functions"],
|
||||
|
||||
["UART", "Logic / Protocol Analyzer", "Inspect serial frames and baud rate"],
|
||||
["UART", "USB", "USB-to-UART bridges are everywhere"],
|
||||
["UART", "GSM / LTE", "Modems are controlled with AT commands over UART"],
|
||||
["I2C", "Logic / Protocol Analyzer", "Decode addresses and ACK/NACK"],
|
||||
["I2C", "SPI", "Both are on-board buses; learn when to choose which"],
|
||||
["I2C", "Hardware Design Basics", "Pull-up sizing and bus capacitance"],
|
||||
["SPI", "Logic / Protocol Analyzer", "Decode the clock, chip select and data lines"],
|
||||
["Bluetooth", "Wi-Fi", "Both share the 2.4 GHz band, often on one radio chip"],
|
||||
["Bluetooth", "Matter", "Matter devices are commissioned over Bluetooth LE"],
|
||||
["Bluetooth", "UWB", "UWB is often paired with Bluetooth for secure ranging"],
|
||||
["Wi-Fi", "TCP/IP", "Wi-Fi carries IP traffic"],
|
||||
["Wi-Fi", "Matter", "Matter runs on Wi-Fi and Thread"],
|
||||
["Thread", "Matter", "Matter runs on Wi-Fi and Thread"],
|
||||
["Thread", "Zigbee", "Both are mesh networks on IEEE 802.15.4 radios"],
|
||||
["LoRa", "NB-IoT", "Competing low-power wide-area options"],
|
||||
["Ethernet", "TCP/IP", "Ethernet is the link layer under TCP/IP"],
|
||||
["Ethernet", "EtherCAT", "Industrial protocols that run on Ethernet hardware"],
|
||||
["Ethernet", "Profinet", "Industrial protocols that run on Ethernet hardware"],
|
||||
["USB", "PCIe", "Both are high-speed serial links with host and device roles"],
|
||||
["PCIe", "FPGA Development", "FPGAs often connect to hosts over PCIe"],
|
||||
["Modbus", "UART", "Modbus RTU runs over serial lines such as RS-485"],
|
||||
["Modbus", "TCP/IP", "Modbus TCP runs over TCP/IP"],
|
||||
["EtherCAT", "FPGA Development", "EtherCAT slave controllers are often built in FPGAs"],
|
||||
["GSM / LTE", "LTE-M / 5G", "The same cellular family, newer generations"],
|
||||
["LTE-M / 5G", "NB-IoT", "Both are LTE-based low-power IoT standards"],
|
||||
["MQTT", "TCP/IP", "MQTT runs over TCP"],
|
||||
["MQTT", "CoAP", "Two IoT messaging styles: broker-based and request/response"],
|
||||
["CoAP", "UDP", "CoAP runs over UDP"],
|
||||
["TCP/IP", "UDP", "The two main transport protocols"],
|
||||
["CAN", "LIN", "LIN is the low-cost sub-bus beneath CAN in vehicles"],
|
||||
["CAN", "FlexRay", "FlexRay adds determinism and bandwidth beyond CAN"],
|
||||
["CAN", "Logic / Protocol Analyzer", "Decode frames and arbitration"],
|
||||
["CAN", "Oscilloscope", "Check the differential signal and bus termination"],
|
||||
["MOST", "CAN", "MOST carries media in a vehicle while CAN carries control"],
|
||||
["MOST", "Ethernet", "Automotive Ethernet is replacing MOST"],
|
||||
|
||||
["Basic Math & Calculus", "Principles of Electric Circuits", "Circuit analysis uses algebra and calculus"],
|
||||
["Principles of Electric Circuits", "Electronics Fundamentals", "Components build on circuit laws"],
|
||||
["Principles of Electric Circuits", "Multimeter", "Verify voltages and currents in real circuits"],
|
||||
["Electronics Fundamentals", "Digital Design", "Logic gates are built from transistors"],
|
||||
["Electronics Fundamentals", "Hardware Design Basics", "Choosing components and sizing circuits"],
|
||||
["Electronics Fundamentals", "Oscilloscope", "Observe how signals change over time"],
|
||||
["Digital Design", "Computer Architecture", "CPUs are built from digital logic"],
|
||||
["Digital Design", "FPGA Development", "HDL describes digital logic"],
|
||||
["Multimeter", "Breadboarding", "Continuity and voltage checks on prototypes"],
|
||||
["Multimeter", "Soldering / Rework", "Checking for shorts after soldering"],
|
||||
["Oscilloscope", "Logic / Protocol Analyzer", "Analog view and decoded digital view of the same signals"],
|
||||
["Oscilloscope", "PCB Design / EMC", "Measuring noise and ringing"],
|
||||
["Breadboarding", "Hardware Design Basics", "Prototype first, then design the board"],
|
||||
["Breadboarding", "Soldering / Rework", "From breadboard to soldered prototype"],
|
||||
["Hardware Design Basics", "PCB Design / EMC", "Schematics become board layout"],
|
||||
["PCB Design / EMC", "Soldering / Rework", "Boards must allow assembly and repair"]
|
||||
],
|
||||
"groups": {
|
||||
"Operating Systems": ["osBase", "linux", "rtos"],
|
||||
"Microcontrollers": ["rtos", "mcu"],
|
||||
|
||||
+17
-12
@@ -206,11 +206,12 @@ export function createMapView({ root, plan, data, build, onSelect }) {
|
||||
};
|
||||
creditLine(fy + 35, 'Creator & Maintainer: ', CREDITS.author);
|
||||
if (build) creditLine(fy + 53, 'Revision: ', `${build.version} · Last update: ${build.date}`);
|
||||
const logoSize = 52;
|
||||
const badge = { w: 103, h: 36 };
|
||||
svg('image', { href: 'assets/logo.svg', x: fr - logoSize, y: fy, width: logoSize, height: logoSize }, credits);
|
||||
const logoSize = badge.h;
|
||||
const iconY = fy + 9;
|
||||
svg('image', { href: 'assets/logo.svg', x: fr - logoSize, y: iconY, width: logoSize, height: logoSize }, credits);
|
||||
const badgeX = fr - logoSize - SPACE - badge.w;
|
||||
svg('image', { href: 'assets/cc-by-sa.svg', x: badgeX, y: fy + (logoSize - badge.h) / 2, width: badge.w, height: badge.h }, link(CREDITS.licenseUrl));
|
||||
svg('image', { href: 'assets/cc-by-sa.svg', x: badgeX, y: iconY, width: badge.w, height: badge.h }, link(CREDITS.licenseUrl));
|
||||
const licenseText = svg('text', { x: badgeX - 14, y: fy + 22, 'text-anchor': 'end', class: 'credit-text' }, link(CREDITS.licenseUrl));
|
||||
licenseText.textContent = 'This work is licensed under the Creative Commons';
|
||||
const licenseText2 = svg('text', { x: badgeX - 14, y: fy + 40, 'text-anchor': 'end', class: 'credit-text' }, link(CREDITS.licenseUrl));
|
||||
@@ -293,8 +294,8 @@ export function createMapView({ root, plan, data, build, onSelect }) {
|
||||
h('strong', {}, topic.title),
|
||||
topic.areas.length ? h('div', { class: 'tip-areas' }, areaDots(topic.areas), areaLabel(topic.areas)) : null,
|
||||
h('div', { class: 'tip-meta' }, [importance ? IMPORTANCE[importance] : null, plural(topic.total, 'resource'),
|
||||
topic.links && topic.links.length ? `${topic.links.length} connected` : null].filter(Boolean).join(' · ')),
|
||||
h('div', { class: 'tip-hint' }, 'Click for details and resources'),
|
||||
box && box.related.length ? `${box.related.length} related` : null].filter(Boolean).join(' · ')),
|
||||
h('div', { class: 'tip-hint' }, 'Click this topic to open its details'),
|
||||
].filter(Boolean));
|
||||
tip.hidden = false;
|
||||
moveTip(e);
|
||||
@@ -325,8 +326,8 @@ export function createMapView({ root, plan, data, build, onSelect }) {
|
||||
function drawConnection(connection, layer, cls) {
|
||||
svg('path', {
|
||||
d: curve(connection.a, connection.b),
|
||||
class: `${cls} ${connection.shared ? 'shared' : 'mention'}`,
|
||||
'stroke-width': (1.4 + Math.min(3, Math.sqrt(connection.weight) * 0.7)).toFixed(2),
|
||||
class: `${cls} ${connection.reason ? 'curated' : 'evidence'}`,
|
||||
'stroke-width': connection.reason ? 2.4 : 1.4,
|
||||
}, layer);
|
||||
if (cls !== 'link') return;
|
||||
for (const box of [connection.a, connection.b]) {
|
||||
@@ -335,7 +336,8 @@ export function createMapView({ root, plan, data, build, onSelect }) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const connection of connections) drawConnection(connection, layers.linksAll, 'link-all');
|
||||
// The overview of all connections only shows the hand-picked ones.
|
||||
for (const connection of connections) if (connection.reason) drawConnection(connection, layers.linksAll, 'link-all');
|
||||
|
||||
const isWithin = (node, topic) => {
|
||||
for (let n = node; n; n = n.parent) if (n === topic) return true;
|
||||
@@ -353,7 +355,7 @@ export function createMapView({ root, plan, data, build, onSelect }) {
|
||||
for (const b of own) lit.add(b);
|
||||
for (const connection of connections) {
|
||||
if (!(own.has(connection.a) || own.has(connection.b))) continue;
|
||||
if (!connection.links.some((l) => isWithin(l.source, topic) || isWithin(l.target, topic))) continue;
|
||||
if (!connection.reason && !connection.links.some((l) => isWithin(l.source, topic) || isWithin(l.target, topic))) continue;
|
||||
drawConnection(connection, layers.links, 'link');
|
||||
lit.add(connection.a);
|
||||
lit.add(connection.b);
|
||||
@@ -423,10 +425,11 @@ export function createMapView({ root, plan, data, build, onSelect }) {
|
||||
fitButton.title = 'Show the whole page';
|
||||
}
|
||||
|
||||
const pageFitWidth = (size) => Math.max(page.w, (page.h + 40) / (size ? size.h / size.w : page.h / page.w)) + 40;
|
||||
|
||||
function fitPage() {
|
||||
const size = canvasSize();
|
||||
const aspect = size ? size.h / size.w : page.h / page.w;
|
||||
const width = Math.max(page.w, (page.h + 40) / aspect) + 40;
|
||||
const width = pageFitWidth(size);
|
||||
fitMode = 'page';
|
||||
setView({ x: (page.w - width) / 2, y: 0, w: width });
|
||||
fitButton.title = 'Fit the width';
|
||||
@@ -446,7 +449,9 @@ export function createMapView({ root, plan, data, build, onSelect }) {
|
||||
const size = canvasSize();
|
||||
if (!size) return;
|
||||
const scale = (size.w / v.w) * factor;
|
||||
if (scale < (size.w / page.w) * MIN_ZOOM || scale > MAX_ZOOM) return;
|
||||
// Limits only stop a step that goes further out of range; the whole-page view is always reachable.
|
||||
const minScale = Math.min((size.w / page.w) * MIN_ZOOM, size.w / pageFitWidth(size), size.w / v.w);
|
||||
if (factor > 1 ? scale > MAX_ZOOM : scale < minScale) return;
|
||||
const [px, py] = clientX == null ? [v.x + v.w / 2, v.y + v.h / 2] : toChart(clientX, clientY);
|
||||
fitMode = 'custom';
|
||||
setView({ x: px - (px - v.x) / factor, y: py - (py - v.y) / factor, w: v.w / factor });
|
||||
|
||||
@@ -71,6 +71,17 @@ test('places topics in the map areas and their cross-section', () => {
|
||||
assert.equal(find('Projects').areas.length, 0, 'getting-started topics are not on the map');
|
||||
});
|
||||
|
||||
test('every map topic is connected to related topics, each connection with a reason', () => {
|
||||
const plan = linkDiagram(map, data);
|
||||
assert.deepEqual(plan.invalidLinks, [], 'map.json "links" name existing topics, once, with a reason');
|
||||
// Soft skills describe the engineer, not the technology, so some of them stand alone.
|
||||
const alone = plan.boxes.filter((b) => !b.header && !b.areas.includes('SOFT SKILLS') && !b.related.length);
|
||||
assert.deepEqual(alone.map((b) => b.text), [], 'topics without a connection');
|
||||
const has = (a, b) => plan.connections.some((c) => c.reason && [c.a.text, c.b.text].sort().join('|') === [a, b].sort().join('|'));
|
||||
assert.ok(has('C', 'Memory Management'));
|
||||
assert.ok(has('Rust', 'Memory Management'));
|
||||
});
|
||||
|
||||
test('lays the map out on one A4 page with a minimum spacing between boxes', () => {
|
||||
const layout = buildLayout(linkDiagram(map, data));
|
||||
assert.deepEqual(layout.missing, [], 'every cluster in map.json has a place in layout.js');
|
||||
|
||||
+7
-7
@@ -409,7 +409,8 @@ button {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: var(--surface-solid);
|
||||
background: color-mix(in srgb, var(--surface-solid) 72%, transparent);
|
||||
backdrop-filter: blur(3px);
|
||||
box-shadow: var(--shadow);
|
||||
font-size: 13px;
|
||||
pointer-events: none;
|
||||
@@ -426,10 +427,11 @@ button {
|
||||
}
|
||||
|
||||
.tip-hint {
|
||||
margin-top: 4px;
|
||||
color: var(--accent);
|
||||
margin-top: 6px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Outline view */
|
||||
@@ -715,11 +717,9 @@ button {
|
||||
|
||||
.related .why {
|
||||
grid-column: 2;
|
||||
overflow: hidden;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.res-head {
|
||||
|
||||
Reference in New Issue
Block a user