Уже давно я начинаю свой день с разблокировки телефона и пролистывания различных новостей и соцсетей, чтобы оставаться в курсе событий. Это не слишком хорошо отражается на моем душевном состоянии и я уже давно пытаюсь снизить время, проводимое перед экраном. Но я все еще хочу оставаться в курсе происходящего, особенно по утрам.
Недавно я приобрел матричный принтер на Ebay и подумал, что это прекрасная возможность создать собственную сводку новостей, отпечатанную и готовую к прочтению. Это я и сделал!
ASMR‑звуки принтера ниже 👇
В этой статье я покажу, что было использовано, как все настроить, а также PHP‑скрипт, который всем этим управляет.
«Полный код доступен в репозитории на Github»
Приобретение железа
Список необходимого для данного проекта сравнительно небольшой и, за исключением самого принтера, большая часть может быть найдена на Amazon или в других онлайн‑магазинах.
-
Матричный принтер
-
Блок питания
Принтером, который я приобрел, стал Star NP-10 из середины 80-х. Я не могу дать 100% гарантию, но по идее любой матричный принтер с последовательным портом должен сработать. Цены на них варьируются от 80 до 120 долларов, но я смог ухватить этот за примерно половину этой стоимости, потому что он был помечен как «не уверен, что работает».
Конечно, потребовалось почистить его и настроить картридж с красящей лентой (прямо как в печатной машинке, разве это не здорово?), но после этого он сразу запустился и отпечатал тестовую страницу.
Затем я подключил все друг к другу. Raspberry Pi подключена к WiFi и к последовательному порту принтера с помощью переходника с USB. После включения принтера и подключения к Pi по ssh, можно убедиться, что принтер доступен как /dev/usb/lp0
.
Итак, как же печатать на этой штуке?
Разбираемся с кодом принтера
Так как принтер доступен как lp0
, я хотел проверить, напечатается ли что‑либо, если просто отправить текст на него с помощью echo
. Я выполнил следующую команду:
echo "Hello, world!" > /dev/usb/lp0
Что выдало ошибку о том, что файл недоступен. Вот ведь, ошибка прав доступа. Это можно легко исправить с помощью chmod
:
sudo chmod 666 /dev/usb/lp0
Возможно, есть более корректный вариант решения вопроса, но после выполнения команды мне удалось отправить echo и текст успешно напечатался на принтере. Итак, мы можем отправлять сырые данные на принтер через этот файл, так что давайте попробуем пойти дальше.
Я использую php для повседневных задач, и эта — не исключение. Я написал простенький скрипт, который открывает файл с помощью fopen()
и записывает в него текст. Я попробовал отправить несколько предложений, несколько пустых строк, а также Unicode‑арт, но быстро обнаружил, что поддержка символов на принтере гораздо меньше, чем ожидалось.
Похоже, пришло время разобраться, как он работает на самом деле. Благодаря усилиям людей, собирающих различную информацию, мне удалось найти полный мануал к принтеру в pdf‑формате.
Оказалось, что либо из‑за веяний времени, либо из‑за определенных решений при производстве, у этого принтера очень специфический набор поддерживаемых символов. Примерно опираясь на Code Page 437 для IBM PC, он состоит по большей мере из стандартных цифр и букв, но также имеет небольшой набор спецсимволов, линий и блоков. Отлично!
Отправлять данные символы достаточно просто, можно просто использовать hex‑значения с помощью PHP следующим образом:
<?php
$horizontalDouble = "xCD";
$deg = "xF8";
echo str_repeat($horizontalDouble, 24);
echo '78' . $deg . 'F' . PHP_EOL;
Итак, мы разобрались, как печатать текст на нашем принтере, добавлять спецсимволы и оформлять текст. Теперь надо разобраться, что именно я хочу читать по утрам.
Сбор данных
Я хотел иметь 4 раздела для моей ежедневной сводки: погода, акции, заголовки главных новостей и несколько постов на reddit в топе. В конце концов, это то, что я обычно смотрю по утрам на телефоне.
Также, поскольку проект экспериментальный, я хотел избежать излишних трат при получении информации, а в идеале — избежать их совсем. К счастью, есть прекрасный репозиторий на Github со списком бесплатных и публичных API, так что я прошелся по нему и отобрал нужные.
-
Погода подтягивается с Open‑Meteo, API‑ключ не требуется.
-
Данные по состоянию акций можно получить с twelvedata благодаря бесплатному плану.
-
Заголовки новостей получаются с NYTimes, бесплатного плана которого достаточно для данного проекта.
-
Посты на reddit подтягиваются из бесплатного Reddit JSON (но пришлось заспуфить User‑Agent).
Для каждой из секций я написал простой PHP‑код для получения пэйлоада с эндпоинта API и сбора нужных данных в общий массив. Мне нужна была информация только для определенных акций, типов заголовков и сабреддитов. Если у какой‑либо из секций не было данных — скрипт просто завершается и перезапускается позже.
Это можно увидеть в сниппете получения заголовков новостей:
<?php
// Get news headlines data
echo "Fetching news headlines data..." . PHP_EOL;
$newsUrl = NEWS . "?api-key=" . NEWSKEY;
$newsData = [];
$newsAmount = 0;
$data = json_decode(file_get_contents($newsUrl), true);
if (!isset($data['results'])) {
die("Unable to retrieve news data");
}
foreach ($data['results'] as $article) {
if (
($article['type'] === 'Article') &&
(in_array($article['section'], ['U.S.', 'World', 'Weather', 'Arts'])) &&
($newsAmount < MAXNEWS)
) {
$newsData[] = $article;
$newsAmount++;
}
}
Константы NEWS
, NEWSKEY
и MAXNEWS
инициализируются в начале скрипта для упрощения редактирования.
При запуске скрипта собирается вся информация, которую я хочу увидеть на бумаге, но теперь нам нужно разобраться с форматированием и отправлением в печать.
Печать ежедневной сводки
Я мог бы просто печатать заголовок каждой секции, но это немного скучно. Я хотел добавить немного стиля проекту, так что я решил создать блок в начале, содержащий текущую дату, день недели и название сводки в красивой рамке.
Потребовалось немного вычислений, но мне удалось заставить все работать, используя комбинацию hex‑значений, упомянутых ранее, str_repeat
и знания того, что ширина страницы данного принтера — 80 символов.
Теперь мы просто проходим по каждой из секций и печатаем небольшой заголовок:
<?php
str_repeat($horizontalSingle, 3) . " WEATHER " . str_repeat($horizontalSingle, (PAGEWIDTH - 9)) . "n";
И затем печатаем нужные для секции данные:
<?php
" " . round(($weatherData['daily']['daylight_duration'][0] / 3600), 2) . "h of Sunlight - Sunrise: " . date('g:ia', strtotime($weatherData['daily']['sunrise'][0])) . " - Sunset: " . date('g:ia', strtotime($weatherData['daily']['sunset'][0])) . "n";
Для погоды и акций я был уверен, что они не будут достигать края страницы, так что просто записывал все длинными одиночными строками. Дело обстоит иным образом для заголовков новостей и постов на Reddit.
Если просто передавать длинную строку принтеру, он достаточно умен, чтобы разделить её и продолжить печать на следующей строке. Но я не хотел, чтобы он разрезал слово посередине и переносил его часть на новую строку, так что я написал небольшую функцию для обработки длины строки и возвращения массива строк с длиной, не превышающей ширину страницы.
<?php
function splitString($string, $maxLength = PAGEWIDTH) {
$result = [];
$words = explode(' ', $string);
$currentLine = '';
foreach ($words as $word) {
if (strlen($currentLine . $word) <= $maxLength) {
$currentLine .= ($currentLine ? ' ' : '') . $word;
} else {
if ($currentLine) {
$result[] = $currentLine;
$currentLine = $word;
} else {
// If a single word is longer than maxLength, split it
$result[] = substr($word, 0, $maxLength);
$currentLine = substr($word, $maxLength);
}
}
}
if ($currentLine) {
$result[] = $currentLine;
}
return $result;
}
После чего я могу использовать ее следующим образом:
<?php
foreach (splitString($redditPost) as $line) {
fwrite($printer, $line) . "n";
}
Теперь все, что осталось — запустить скрипт!
Использование и подведение итогов
Я могу запускать печать вручную просто выполняя php print.php
, но вместо этого я настроил крон‑задание, которое будет делать это вместо меня.
Каждое утро примерно в 8 утра начинается печать моей личной сводки. Я отрываю ее и просматриваю утром за чашкой кофе.
Может прозвучать глупо, но мне нравится иметь ограниченное количество новостей на одном листе бумаги. Это дает возможность дочитать и остановиться на этом вместо бесконечного скроллинга сайтов и приложений соцсетей.
Также это был весьма интересный проект и я надеюсь, что смогу найти больше применений для этого принтера. Использование настоящего железа (в особенности такого винтажного) всегда приносит мне удовольствие, а возможность интергировать его с современными технологиями или применить в необычных проектах разжигает интерес и позволяет вспомнить, почему я занялся разработкой в первую очередь.
Автор: MrPizzly