mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 10:29:21 +03:00
upd deps + build toc
This commit is contained in:
parent
69af4e882d
commit
56132501cd
878
package-lock.json
generated
878
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -18,13 +18,13 @@
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/image": "^0.16.2",
|
||||
"@astrojs/rss": "^2.3.1",
|
||||
"@astrojs/sitemap": "^1.2.1",
|
||||
"@astrojs/image": "^0.16.6",
|
||||
"@astrojs/rss": "^2.3.2",
|
||||
"@astrojs/sitemap": "^1.2.2",
|
||||
"@astrojs/solid-js": "^2.1.0",
|
||||
"@netlify/functions": "^1.4.0",
|
||||
"@sindresorhus/slugify": "^2.2.0",
|
||||
"astro": "^2.1.7",
|
||||
"astro": "^2.3.0",
|
||||
"astro-icon": "^0.8.0",
|
||||
"astro-netlify-cms": "^0.5.3",
|
||||
"hast-util-to-string": "^2.0.0",
|
||||
@ -36,10 +36,10 @@
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"sharp": "^0.32.0",
|
||||
"solid-js": "^1.6.15"
|
||||
"solid-js": "^1.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"sass": "^1.60.0"
|
||||
"sass": "^1.62.0"
|
||||
}
|
||||
}
|
||||
|
||||
23
src/components/TableOfContents.astro
Normal file
23
src/components/TableOfContents.astro
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
import { buildToc } from "@root/utils";
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import TableOfContentsHeading from "./TableOfContentsHeading.astro";
|
||||
|
||||
export interface Props {
|
||||
headings: MarkdownHeading[];
|
||||
}
|
||||
|
||||
const toc = buildToc(Astro.props.headings);
|
||||
---
|
||||
|
||||
<nav class="toc">
|
||||
<h2>Содержание</h2>
|
||||
<ul>
|
||||
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style lang="scss" is:global>
|
||||
.toc {
|
||||
}
|
||||
</style>
|
||||
16
src/components/TableOfContentsHeading.astro
Normal file
16
src/components/TableOfContentsHeading.astro
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
import type { MarkdownHeadingToc } from "@root/utils";
|
||||
|
||||
export interface Props {
|
||||
heading: MarkdownHeadingToc;
|
||||
}
|
||||
|
||||
const { heading } = Astro.props;
|
||||
---
|
||||
|
||||
<li>
|
||||
<a href={`#${heading.slug}`}>{heading.text}</a>
|
||||
<ul>
|
||||
{heading.subheadings.map((subheading) => <Astro.self heading={subheading} />)}
|
||||
</ul>
|
||||
</li>
|
||||
@ -412,7 +412,7 @@ Now after opening the port you can send commands, don't forget to set the carria
|
||||
|
||||
The web application will consist of just a few elements: an input box where we can write our commands, buttons to open and close the serial port, connection status. You can see the finished version [here](https://web-serial-example.netlify.app/).
|
||||
|
||||
### Create HTML markup and styles
|
||||
## Create HTML markup and styles
|
||||
|
||||
The HTML markup will look like this:
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@ tags:
|
||||
pubDate: 2022-11-04T21:00:00.000Z
|
||||
updatedDate: 2023-02-13T16:34:23.326Z
|
||||
---
|
||||
|
||||
# Что вы узнаете
|
||||
|
||||
По итогу вы узнаете, как отправлять и принимать данные от устройства с последовательным портом в браузере, разработаете демонстрационное веб-приложение, которое отправляет команды в микроконтроллер с последовательным портом и напишите прошивку для микроконтроллера [AVR128DA48](http://ww1.microchip.com/downloads/en/DeviceDoc/40002183A.pdf) для обработки этих команд. Готовый вариант проекта можно посмотреть [здесь](https://web-serial-example.netlify.app/), исходники загружены в [репозиторий](https://github.com/sergeyyarkov/avr128da48_web-serial-example) на GitHub.
|
||||
@ -49,17 +48,17 @@ WebSerial API - это браузерный API, который предоста
|
||||
|
||||
UART передает данные последовательно по одному биту в одном из трех режимов:
|
||||
|
||||
- односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику
|
||||
- полудуплексный режим - устройства могут передавать и принимать данные по очереди
|
||||
- полнодуплексный режим - устройства могут передавать и принимать данные одновременно
|
||||
* односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику
|
||||
* полудуплексный режим - устройства могут передавать и принимать данные по очереди
|
||||
* полнодуплексный режим - устройства могут передавать и принимать данные одновременно
|
||||
|
||||
Так как в этом проекте предполагается использование асинхронной версии UART, то соответственно необходимо устанавливать **одинаковую** скорость передачи битов в секунду (`baud rate`, `bps`) на двух устройствах. Наиболее распространенные скорости являются: **4800, 9600, 19.2 К, 57.6К и 115.2К.**
|
||||
|
||||
Данные передаются в виде так называемых `пакетов`, где каждый пакет содержит в себе:
|
||||
|
||||
- стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных.
|
||||
- биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов.
|
||||
- бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок.
|
||||
* стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных.
|
||||
* биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов.
|
||||
* бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок.
|
||||
|
||||
<figure>
|
||||
<img src="/media/uart-packet.jpg" width="700" height="300" alt="Структура UART пакета" format="webp">
|
||||
@ -84,7 +83,7 @@ UART передает данные последовательно по одно
|
||||
|
||||
Как я упомянул ранее, прошивка будет написана для микроконтроллера AVR128DA48. На самом деле неважно какой микроконтроллер вы используете, вам достаточно нужно уметь отправлять и принимать данные используя UART периферию вашего микроконтроллера.
|
||||
|
||||
Суть программы будет очень проста: необходимо написать некий **_обработчик команд_** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод.
|
||||
Суть программы будет очень проста: необходимо написать некий ***обработчик команд*** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод.
|
||||
|
||||
Постановка задачи ясна, теперь рассмотрим реализацию данной программы. Я буду использовать среду разработки MPLAB X IDE и язык C для написания прошивки.
|
||||
|
||||
@ -104,10 +103,10 @@ UART передает данные последовательно по одно
|
||||
#endif /* CONSTANTS_H */
|
||||
```
|
||||
|
||||
- `F_CPU` - тактовая частота микроконтроллера
|
||||
- `BAUD_RATE` - скорость передачи данных UART
|
||||
- `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные
|
||||
- `EOT` - End-of-Transmission, ASCII символ конца передачи данных
|
||||
* `F_CPU` - тактовая частота микроконтроллера
|
||||
* `BAUD_RATE` - скорость передачи данных UART
|
||||
* `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные
|
||||
* `EOT` - End-of-Transmission, ASCII символ конца передачи данных
|
||||
|
||||
## Инициализация USART
|
||||
|
||||
@ -157,11 +156,11 @@ void USART1_Initialize(void) {
|
||||
|
||||
Для полнодуплексного режима инициализация асинхронной версии USART происходит следующим образом:
|
||||
|
||||
- Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD`
|
||||
- Конфигурация размера `фрейма`, в нашем случае это 8 бит.
|
||||
- Конфигурация пина `TX` на выход и пина `RX` на вход.
|
||||
- Включение прерывания на то, когда закончился прием данных (когда пришел один пакет).
|
||||
- Включение приемника и передатчика.
|
||||
* Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD`
|
||||
* Конфигурация размера `фрейма`, в нашем случае это 8 бит.
|
||||
* Конфигурация пина `TX` на выход и пина `RX` на вход.
|
||||
* Включение прерывания на то, когда закончился прием данных (когда пришел один пакет).
|
||||
* Включение приемника и передатчика.
|
||||
|
||||
Теперь, рассмотрим функции отправки данных:
|
||||
|
||||
@ -213,9 +212,9 @@ void command_list(void);
|
||||
|
||||
Для решения этой задачи я предлагаю создавать массив структур, где в каждой структуре будет содержаться указатель на функцию которую нам нужно выполнить, и название самой команды.
|
||||
|
||||
- `command_define` будет создавать новую структуру в массиве.
|
||||
- `command_process` будет вызывать соответствующую функцию по имени команды.
|
||||
- `command_list` будет отправлять по UART информацию о существующих командах.
|
||||
* `command_define` будет создавать новую структуру в массиве.
|
||||
* `command_process` будет вызывать соответствующую функцию по имени команды.
|
||||
* `command_list` будет отправлять по UART информацию о существующих командах.
|
||||
|
||||
Реализовать задуманное можно следующим образом создав файл `command.c`:
|
||||
|
||||
@ -369,9 +368,9 @@ void hello_world(void) {
|
||||
|
||||
С помощью функции `command_define` мы можем определить, по какой переданной команде по USART запускать функцию:
|
||||
|
||||
- `led_toggle` будет переключать светодиод.
|
||||
- `hello_world` выводить сообщение “Hello world”.
|
||||
- `command_list` будет показывать список имеющихся команд.
|
||||
* `led_toggle` будет переключать светодиод.
|
||||
* `hello_world` выводить сообщение “Hello world”.
|
||||
* `command_list` будет показывать список имеющихся команд.
|
||||
|
||||
## Демонстрация работы
|
||||
|
||||
@ -379,10 +378,10 @@ void hello_world(void) {
|
||||
|
||||
Выберете ваше устройство из списка (в моем случае это `/dev/ttyACM1`) и установите следующие настройки:
|
||||
|
||||
- Скорость: **9600** бод
|
||||
- Количество битов: **8**
|
||||
- Четность: **None**
|
||||
- Стоповые биты: **1**
|
||||
* Скорость: **9600** бод
|
||||
* Количество битов: **8**
|
||||
* Четность: **None**
|
||||
* Стоповые биты: **1**
|
||||
|
||||
<figure>
|
||||
<img
|
||||
@ -414,7 +413,7 @@ void hello_world(void) {
|
||||
|
||||
Веб-приложение будет состоять всего из нескольких элементов: поле для ввода, куда мы сможем писать наши команды, кнопки для открытия и закрытия последовательного порта, статус соединения. Готовый вариант можно посмотреть [здесь](https://web-serial-example.netlify.app/).
|
||||
|
||||
### Создание разметки HTML и стилей
|
||||
## Создание разметки HTML и стилей
|
||||
|
||||
Разметка HTML будет выглядеть следующим образом:
|
||||
|
||||
@ -586,12 +585,12 @@ class SerialPortHandler {
|
||||
}
|
||||
```
|
||||
|
||||
- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) является классом, который кодирует строку в массив без знаковых 8-ми битных целых чисел [`Uint8Array`](https://webidl.spec.whatwg.org/#idl-Uint8Array).
|
||||
- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder#examples) наоборот, декодирует в строку. Экземпляры этих классов нам понадобятся, т.к мы будет оперировать данными с типом `Uint8Array`.
|
||||
- Функции `onConnect` и `onDisconnect` будут вызываться при подключении или отключении устройства.
|
||||
- Свойство [`options`](https://wicg.github.io/serial/#serialoptions-dictionary) содержит необходимые параметры, которые будут использоваться для передачи и приема данных.
|
||||
- Свойство [`port`](https://wicg.github.io/serial/#serialport-interface) - объект, который будет содержать методы для работы с портом и информацию об устройстве после установки соединения.
|
||||
- Функция `setupListeners` просто будет добавлять обработчики событий.
|
||||
* [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) является классом, который кодирует строку в массив без знаковых 8-ми битных целых чисел [`Uint8Array`](https://webidl.spec.whatwg.org/#idl-Uint8Array).
|
||||
* [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder#examples) наоборот, декодирует в строку. Экземпляры этих классов нам понадобятся, т.к мы будет оперировать данными с типом `Uint8Array`.
|
||||
* Функции `onConnect` и `onDisconnect` будут вызываться при подключении или отключении устройства.
|
||||
* Свойство [`options`](https://wicg.github.io/serial/#serialoptions-dictionary) содержит необходимые параметры, которые будут использоваться для передачи и приема данных.
|
||||
* Свойство [`port`](https://wicg.github.io/serial/#serialport-interface) - объект, который будет содержать методы для работы с портом и информацию об устройстве после установки соединения.
|
||||
* Функция `setupListeners` просто будет добавлять обработчики событий.
|
||||
|
||||
Далее рассматривается реализация этих методов.
|
||||
|
||||
@ -640,12 +639,12 @@ const port = await navigator.serial.requestPort({
|
||||
|
||||
После того как пользователь выбрал устройство, метод `requestPort` возвращает [`port`](https://wicg.github.io/serial/#serialport-interface), который можно открыть передав туда [параметры](https://wicg.github.io/serial/#serialoptions-dictionary):
|
||||
|
||||
- `baudRate` : скорость передачи данных.
|
||||
- `dataBits` : количество бит данных во фрейме (7 или 8).
|
||||
- `stopBits` : количество стоповых битов в конце пакета (1 или 2).
|
||||
- `parity` : режим четности ( `none` , `even` или `odd` ).
|
||||
- `bufferSize` : размер буферов чтения и записи, которые должны быть созданы.
|
||||
- `flowControl` : режим управления потоком ( `none` или `hardware` ).
|
||||
* `baudRate` : скорость передачи данных.
|
||||
* `dataBits` : количество бит данных во фрейме (7 или 8).
|
||||
* `stopBits` : количество стоповых битов в конце пакета (1 или 2).
|
||||
* `parity` : режим четности ( `none` , `even` или `odd` ).
|
||||
* `bufferSize` : размер буферов чтения и записи, которые должны быть созданы.
|
||||
* `flowControl` : режим управления потоком ( `none` или `hardware` ).
|
||||
|
||||
Свойство `baudRate` является единственным обязательным параметром, остальные же являются необязательными и имеют [значения по умолчанию](https://wicg.github.io/serial/#serialoptions-dictionary).
|
||||
|
||||
@ -893,8 +892,8 @@ setInterval(async () => {
|
||||
|
||||
# Полезные материалы
|
||||
|
||||
- [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial)
|
||||
- [Документация к WebSerial API](https://wicg.github.io/serial/)
|
||||
- [Подробное руководство по Stream API](https://web.dev/streams/)
|
||||
- [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors)
|
||||
- [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/)
|
||||
* [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial)
|
||||
* [Документация к WebSerial API](https://wicg.github.io/serial/)
|
||||
* [Подробное руководство по Stream API](https://web.dev/streams/)
|
||||
* [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors)
|
||||
* [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/)
|
||||
@ -1,4 +1,5 @@
|
||||
---
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants";
|
||||
import BaseHead from "@components/BaseHead.astro";
|
||||
@ -6,14 +7,16 @@ import BaseLayout from "@components/BaseLayout.astro";
|
||||
import ArticleContent from "@components/ArticleContent.astro";
|
||||
import ArticlePost from "@components/ArticlePost.astro";
|
||||
import OpenGraph from "@components/OpenGraph.astro";
|
||||
import TableOfContents from "@root/components/TableOfContents.astro";
|
||||
|
||||
export interface Props {
|
||||
lang?: string | undefined;
|
||||
article: CollectionEntry<"blog">;
|
||||
headings: MarkdownHeading[];
|
||||
}
|
||||
|
||||
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
||||
const { lang = DEFAULT_LANGUAGE, article } = Astro.props as Props;
|
||||
const { lang = DEFAULT_LANGUAGE, article, headings } = Astro.props as Props;
|
||||
const {
|
||||
data: { title, description, coverImage },
|
||||
} = article;
|
||||
@ -36,6 +39,7 @@ const {
|
||||
|
||||
<BaseLayout>
|
||||
<div class="container">
|
||||
<!-- <TableOfContents headings={headings} /> -->
|
||||
<ArticlePost lang={lang} article={article}>
|
||||
<ArticleContent>
|
||||
<slot />
|
||||
|
||||
@ -36,11 +36,11 @@ export async function getStaticPaths() {
|
||||
|
||||
type Props = { article: CollectionEntry<"blog">; availableOnLang: CollectionEntry<"blog">[] };
|
||||
|
||||
const { Content } = await Astro.props.article.render();
|
||||
const { Content, headings } = await Astro.props.article.render();
|
||||
const { article, availableOnLang } = Astro.props;
|
||||
---
|
||||
|
||||
<ArticleLayout lang={article.id.split("/")[0]} article={article}>
|
||||
<ArticleLayout lang={article.id.split("/")[0]} article={article} headings={headings}>
|
||||
{
|
||||
availableOnLang.length > 1 && (
|
||||
<div class="languages-available">
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
color-scheme: light;
|
||||
|
||||
/* Fonts */
|
||||
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif, Apple Color Emoji,
|
||||
Segoe UI Emoji;
|
||||
--font-family-base: "Rubik", var(--font-fallback);
|
||||
--font-family-monospace: "IBM Plex Mono", Menlo, monospace;
|
||||
--font-size-xs: 0.75rem;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { DEFAULT_LANGUAGE } from "@root/constants";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
@ -62,3 +63,20 @@ export function formatToArticleBlocks(articles: CollectionEntry<"blog">[]) {
|
||||
export function getUniqueTags(articles: CollectionEntry<"blog">[]): string[] {
|
||||
return Array.from(new Set(articles.map((a) => a.data.tags).flat()));
|
||||
}
|
||||
|
||||
export type MarkdownHeadingToc = MarkdownHeading & { subheadings: MarkdownHeading[] };
|
||||
|
||||
export function buildToc(headings: MarkdownHeading[]) {
|
||||
const toc: MarkdownHeadingToc[] = [];
|
||||
const parentHeadings = new Map();
|
||||
headings.forEach((h) => {
|
||||
const heading = { ...h, subheadings: [] };
|
||||
parentHeadings.set(heading.depth, heading);
|
||||
if (heading.depth === 1) {
|
||||
toc.push(heading);
|
||||
} else {
|
||||
parentHeadings.get(heading.depth - 1).subheadings.push(heading);
|
||||
}
|
||||
});
|
||||
return toc;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user