Antonovich.mehttps://antonovich.me/2024-02-07T18:50:14+03:00avmVictor Antonovich BlogСовместное использование OCserv и nginx на одном IP-адресе
2024-02-07T18:50:14+03:00https://antonovich.me/2024/02/07/ocserv-nginx-ip-share/В данной статье кратко описывается конфигурирование OCserv, сервера VPN-протокола Cisco AnyConnect на базе SSL, и HTTPS-сервера nginx с целью обеспечения их совместной работы на одном IP-адресе в ОС Linux (RHEL/CentOS/Fedora). Эта статья не является инструкцией по настройке OCserv "с нуля". Для такой настройки воспользуйтесь одной из множества имеющихся в Сети…
<p><a href="/media/uploads/openconnect.png" target="_blank"><img src="/media/uploads/openconnect.png" alt="ocserv" width="200" /></a>
<img src="https://github.githubassets.com/images/icons/emoji/heavy_plus_sign.png" alt="heavy_plus_sign" class="marked-emoji" />
<a href="/media/uploads/nginx.png" target="_blank"><img src="/media/uploads/nginx.png" alt="nginx" width="200" /></a></p>
<p>В данной статье кратко описывается конфигурирование <a href="https://ocserv.openconnect-vpn.net/index.html">OCserv</a>, сервера VPN-протокола Cisco AnyConnect на базе SSL, и HTTPS-сервера <a href="https://nginx.org/">nginx</a> с целью обеспечения их совместной работы на одном IP-адресе в ОС Linux (RHEL/CentOS/Fedora).<a name="more"></a></p>
<p><img src="https://github.githubassets.com/images/icons/emoji/warning.png" alt="warning" class="marked-emoji" /> Эта статья не является инструкцией по настройке OCserv "с нуля". Для такой настройки воспользуйтесь одной из множества имеющихся в Сети инструкций (например, <a href="https://habr.com/ru/articles/776256/">этой</a>).</p>
<p>Рассмотрим случай, когда у нас уже есть сервер nginx, обслуживающий сайт <code>domain.com</code>, и нам необходимо обеспечить подключение VPN-клиентов OCserv к субдомену <code>sub.domain.com</code>. Если оба домена резолвятся на один и тот же IP-адрес, возникает проблема одновременного обслуживания соединений HTTPS и VPN на одном и том же порту (443/tcp). Предлагаемое в данной статье решение позволяет обойтись без дополнительного прокси-сервера типа haproxy, используя только имеющиеся у nginx средства для работы с SSL-соединениями.</p>
<p>Для начала необходимо удостовериться, что nginx скомпилирован с модулями <code>stream_ssl_preread_module</code>/<code>http_realip_module</code>. В nginx из репозитория EPEL в RHEL/CentOS/Fedora данные модули присутствуют "из коробки". В случае иных дистрибутивов Linux, или если nginx был установлен из другого репозитория, убедитесь, что в результате выполнения следующей команды в списке опций компиляции nginx присутствуют оба модуля:</p>
<pre><code class="lang-bash">$ nginx -V 2>&1 | tr ' ' '\n' | grep -E 'stream_ssl_preread_module|http_realip_module'
--with-http_realip_module
--with-stream_ssl_preread_module
</code></pre>
<p>Далее, добавляем секцию <code>stream</code> в <code>/etc/nginx/nginx.conf</code>:</p>
<pre><code>...
stream {
map $ssl_preread_server_name $name {
sub.domain.com ocserv;
default https;
}
upstream https {
server 127.0.0.1:8443;
}
upstream ocserv {
server 127.0.0.1:9443;
}
server {
listen 443;
ssl_preread on;
proxy_protocol on;
proxy_pass $name;
}
}
http {
...
}
</code></pre>
<p>В данной секции определяются два <code>upstream</code> с именами <code>https</code> и <code>ocserv</code>, для подключений HTTPS и VPN соответственно. Модуль <code>stream_ssl_preread_module</code> читает имя сервера из заголовка SSL в переменную <code>$ssl_preread_server_name</code>, после чего в <code>map</code> оно преобразуется в имя апстрима, сохраняемое в переменную <code>$name</code>. Преобразование осуществляется следующим образом: если имя сервера для подключения совпадает с <code>sub.domain.com</code>, то выбирается апстрим <code>ocserv</code>, для всех же остальных имен выбирается апстрим <code>https</code>. После этого входящее подключение перенаправляется в выбранный апстрим посредством директивы <code>proxy_pass</code>, при этом добавляется заголовок <code>proxy_protocol</code>, в который сохраняется реальный IP-адрес клиента.</p>
<p>Далее необходимо поправить конфигурацию <code>/etc/nginx/conf.d/domain.conf</code>:</p>
<pre><code>server {
listen 127.0.0.1:8443 ssl http2 proxy_protocol;
listen [::1]:8443 ssl http2 proxy_protocol;
server_name domain.com;
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
...
}
</code></pre>
<p>В директиве <code>listen</code> необходимо указать те же адрес и порт, что и в секции <code>upstream https {}</code>. Помимо этого, с помощью опции <code>proxy_protocol</code> включается чтение записанного в секции <code>stream</code> заголовка, из которого получается реальный IP-адрес клиента с помощью директивы <code>real_ip_header</code>. Директива <code>set_real_ip_from</code> задает диапазон доверенных адресов источников, для которых разрешается использование данных из заголовка <code>proxy_protocol</code>.</p>
<p>В файле конфигурации <code>/etc/ocserv/ocserv.conf</code> необходимо поправить (или добавить) следующие директивы:</p>
<pre><code>...
listen-host = 127.0.0.1
listen-proxy-proto = true
tcp-port = 9443
server-cert = /etc/letsencrypt/live/domain.com/fullchain.pem
server-key = /etc/letsencrypt/live/domain.com/privkey.pem
...
</code></pre>
<p>Директива <code>listen-proxy-proto = true</code> включает чтение реального IP-адреса клиента VPN из заголовка, добавленного опцией <code>proxy_protocol</code> nginx. Директивы <code>server-cert</code> и <code>server-key</code> задают пути к SSL-сертификату. Обратите внимание, что данный сертификат является общим для nginx и OCserv, поэтому он должен включать в себя оба имени, <code>domain.com</code> и <code>sub.domain.com</code>.</p>
<p>Таким образом, описанные выше настройки представляют собой минимальный пример конфигурирования OCserv и nginx для их совместного использования на одном IP-адресе.</p>
Использование библиотеки usb-i2c-android для работы с I²C-устройствами в Android
2020-07-14T21:55:34+03:00https://antonovich.me/2020/07/14/usb-i2c-android/Android-библиотека usb-i2c-android предназначена для организации обмена данными с I²C-устройствами, подключенными с помощью OTG USB-адаптеров. Библиотека не требует специальных системных драйверов или прав доступа root. Список поддерживаемых адаптеров в usb-i2c-android версии 1.1.0: I2C-Tiny-USB Silicon Labs CP2112 Qinheng Microelectronics CH341 Пример работы I²C 0.96" OLED-дисплея, подключенного к телефону Pixel 3: В проект…
<p>Android-библиотека <a href="https://github.com/3cky/usb-i2c-android">usb-i2c-android</a> предназначена для организации обмена данными с <a href="https://ru.wikipedia.org/wiki/I%C2%B2C">I²C</a>-устройствами, подключенными с помощью <a href="http://developer.android.com/guide/topics/connectivity/usb/host.html">OTG</a> USB-адаптеров. Библиотека не требует специальных системных драйверов или прав доступа root.
<a name="more"></a></p>
<p>Список поддерживаемых адаптеров в <code>usb-i2c-android</code> версии 1.1.0:</p>
<ul>
<li><a href="https://github.com/harbaum/I2C-Tiny-USB">I2C-Tiny-USB</a></li>
<li><a href="http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf">Silicon Labs CP2112</a></li>
<li><a href="http://www.wch-ic.com/products/CH341.html">Qinheng Microelectronics CH341</a></li>
</ul>
<p>Пример работы I²C 0.96" OLED-дисплея, подключенного к телефону Pixel 3:</p>
<video width="600" controls>
<source src="/media/uploads/MVI_3202_800.webm" type='video/webm; codecs="vp8, vorbis"'>
</video><p>В проект также включено простое приложение-сканер устройств I²C:</p>
<p><img src="/media/uploads/MNML-July13-082554PM_240.gif" alt="" /></p>
<p>Для того, чтобы использовать библиотеку в своем проекте, необходимо добавить репозиторий jitpack.io в корневой файл <code>build.gradle</code>:</p>
<pre><code class="lang-gradle">allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
</code></pre>
<p>Включение библиотеки в список зависимостей проекта:</p>
<pre><code class="lang-gradle">dependencies {
implementation 'com.github.3cky:usb-i2c-android:1.1.0'
}
</code></pre>
<p>Перед вызовом функций библиотеки, в манифест приложения необходимо добавить ключ <code><uses-feature></code> для USB-хоста:</p>
<pre><code class="lang-xml"><?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="...">
<uses-feature android:name="android.hardware.usb.host" />
...
</manifest>
</code></pre>
<p>Пример кода, взаимодействующего с устройством I²C (упрощенный, за подробностями можно обратиться к примеру приложения в директории <a href="https://github.com/3cky/usb-i2c-android/tree/master/app">app</a> проекта):</p>
<pre><code class="lang-java">import com.github.ykc3.android.usbi2c.UsbI2cAdapter;
import com.github.ykc3.android.usbi2c.UsbI2cDevice;
import com.github.ykc3.android.usbi2c.UsbI2cManager;
...
// Get Android UsbManager
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
// Find all connected I²C adapters
UsbI2cManager usbI2cManager = UsbI2cManager.create(usbManager).build();
List<UsbI2cAdapter> i2cAdapters = usbI2cManager.getAdapters();
if (i2cAdapters.isEmpty()) {
return;
}
// Get first adapter
UsbI2cAdapter i2cAdapter = i2cAdapters.get(0);
// Request USB access permission
usbManager.requestPermission(i2cAdapter.getUsbDevice(), usbPermissionIntent);
...
// USB permission intent handler called with success result
// Open adapter
i2cAdapter.open();
// Get device with I²C address 0x42
UsbI2cDevice i2cDevice = i2cAdapter.getDevice(0x42);
// Read device register 0x01.
// Throws java.lang.IOException if device is not connected or I/O error caused
byte value = i2cDevice.readRegByte(0x01);
// Close adapter
i2cAdapter.close();
</code></pre>
kube-restrict-ip
2019-02-06T16:37:58+03:00https://antonovich.me/2019/02/06/kube-restrict-ip/TL;DR: kube-restrict-ip allows you to restrict access to specified TCP ports of the Kubernetes nodes to defined set of IP addresses. Configuration for kube-restrict-ip is provided via a ConfigMap and it can be reconfigured in a live cluster by creating or editing this ConfigMap. Okay, let's imagine we have a…
<p><strong>TL;DR:</strong> <a href="https://github.com/3cky/kube-restrict-ip">kube-restrict-ip</a> allows you to restrict access to specified TCP ports of the Kubernetes nodes to defined set of IP addresses. Configuration for <code>kube-restrict-ip</code> is provided via a <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/">ConfigMap</a> and it can be reconfigured in a live cluster by creating or editing this <code>ConfigMap</code>.</p>
<p><a name="more"></a>
Okay, let's imagine we have a Kubernetes cluster and deployed <a href="https://kubernetes.github.io/ingress-nginx/">NGINX Ingress Controller</a>. Its deployment descriptor specified use of <code>hostNetwork: true</code> for a great reason: this way NGINX running in a pod can directly see the network interfaces of the host machine where the pod was started. But the side effect of such setup is any service running in a pod will be directly accessible by the outside world, for example Prometheus metrics service running on port 10254.</p>
<p>Surely, we could manually add iptables rules for any node we need to restrict access to these ports, but it isn't in any way match Kubernetes ideology of the dynamic environment where nodes and pods can be created and destroyed at any moment. So, it will be great if these iptables rules will be also created automatically. And here we come to the <code>kube-restrict-ip</code>.</p>
<p><code>kube-restrict-ip</code> is a little utility written in Go that creates an iptables chain containing matching rules for user-specified IP addresses (hosts and CIDR ranges), and a rule in INPUT chain that routes requests bound to restricted ports to rules chain. All unmatched IPs will be rejected.</p>
<p>The simplest way to apply these restrictions to all nodes in a Kubernetes cluster is launch the <code>kube-restrict-ip</code> utility as a <a href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/">DaemonSet</a>. <code>kube-restrict-ip</code> repo includes an example <a href="https://github.com/3cky/kube-restrict-ip/blob/master/kube-restrict-ip.yaml">kube-restrict-ip.yaml</a> file that can be used to launch the kube-restrict-ip as a DaemonSet:</p>
<pre><code class="lang-bash">kubectl create -f kube-restrict-ip.yaml
</code></pre>
<p>After start <code>kube-restrict-ip</code> container will wait for a config file mounted to <code>/etc/kube-restrict-ip/config.yaml</code> mount point. This file can be provided via a <code>ConfigMap</code>, so it can be reconfigured in a live cluster by creating or editing this <code>ConfigMap</code>.</p>
<p><code>kube-restrict-ip</code> repo includes an example <a href="https://github.com/3cky/kube-restrict-ip/blob/master/config.yaml">config file</a> that could be used to create the <code>ConfigMap</code> in your cluster:</p>
<pre><code class="lang-bash">kubectl create configmap kube-restrict-ip --from-file=config.yaml --namespace=kube-system
</code></pre>
<p>Please note that the <code>ConfigMap</code> in the same namespace as the DaemonSet Pods, and named the <code>kube-restrict-ip</code> to match the DaemonSet spec. This is necessary for the <code>ConfigMap</code> to appear in the Pods' filesystems.</p>
<p>Let's look into example <code>config.yaml</code>:</p>
<pre><code class="lang-yaml">restrictedPorts:
- 9100
- 10254
allowedNetworks:
- 127.0.0.1
- 10.244.0.0/16
- 172.17.0.1/16
checkInterval: 60s
</code></pre>
<ul>
<li><code>restrictedPorts</code>: A list restricted TCP ports (required).</li>
<li><code>allowedNetworks</code>: A list allowed hosts and networks in CIDR notation (required). Please don't forget to add all internal networks here (docker, flannel, etc).</li>
<li><code>checkInterval</code>: The interval to check config for updates (optional, default 60s). The syntax is any format accepted by Go's <a href="https://golang.org/pkg/time/#ParseDuration">time.ParseDuration</a> function. Please note that <code>ConfigMap</code> updates are propagated in the Kubernetes cluster with some delay which by my observations is about a minute, so setting <code>checkInterval</code> to values less of several tens seconds makes little sense.</li>
</ul>
<p>There is another option not included in the example above:</p>
<ul>
<li><code>ipChain</code>: iptables chain name (optional, default "KUBE-RESTRICT-IP"). You could use it for specifying alternate rules chain name in case of multiple <code>kube-restrict-ip</code> running in a cluster.</li>
</ul>
<p>After applying this config file to <code>kube-restrict-ip</code>, the next iptables rules will be created at the Kubernetes node:</p>
<pre><code># iptables --list
Chain INPUT (policy ACCEPT)
target prot opt source destination
KUBE-RESTRICT-IP tcp -- 0.0.0.0/0 0.0.0.0/0 multiport dports 9100,10254 /* kube-restrict-ip */
...
Chain KUBE-RESTRICT-IP (1 references)
target prot opt source destination
RETURN all -- localhost anywhere
RETURN all -- 10.244.0.0/16 anywhere
RETURN all -- 172.17.0.0/16 anywhere
REJECT all -- anywhere anywhere reject-with icmp-port-unreachable
</code></pre>
<h4>Conclusion</h4>
<p><code>kube-restrict-ip</code> is an utility focused in providing a simple way to restrict access to the services in a Kubernetes cluster. You could use it to limit access to ports exposed by pods with <code>hostNetwork: true</code>, like Prometheus metrics services, and modify these restrictions on the fly using a Kubernetes <code>ConfigMap</code> mechanism.</p>
aqi-telegram-bot
2018-01-16T14:03:40+03:00https://antonovich.me/2018/01/16/aqi-telegram-bot/Выложил на гитхаб исходники своего телеграм-бота для мониторинга степени загрязнения уличного воздуха микрочастицами. Если вам тоже интересно узнать, чем вы дышите - прошу под кат за подробностями. PM2.5 и PM10 В городском воздухе всегда имеется некоторое количество так называемых мелкодисперсных частиц (particulate matter, PM). Наиболее крупные из них - это…
<p>Выложил на гитхаб <a href="https://github.com/3cky/aqi-telegram-bot">исходники</a> своего телеграм-бота для мониторинга степени загрязнения уличного воздуха микрочастицами. Если вам тоже интересно узнать, чем вы дышите - прошу под кат за подробностями.
<a name="more"></a></p>
<h3>PM2.5 и PM10</h3>
<p>В городском воздухе всегда имеется некоторое количество так называемых мелкодисперсных частиц (particulate matter, PM). Наиболее крупные из них - это хорошо знакомая нам всем пыль. Более мелкие частицы практически не осаждаются на поверхностях, но при этом представляют весьма реальную угрозу здоровью. Среди этих частиц есть два класса наиболее опасных - PM10 (размером до 10 микрометров) и PM2.5 (размером до 2.5 микрометров). Последние представляют наибольшую опасность, так как их малые размеры позволяют им практически беспрепятственно попадать через легкие внутрь организма и разноситься кровотоком по внутренним органам. Исследования Всемирной организации здравоохранения показали, что именно частицы PM2.5 повинны в огромном количестве смертей на планете, в частности от рака лёгких и от других заболеваний<sup class="footnote-ref" id="fnref-1"><a href="#fn-1">1</a></sup>.</p>
<h3>Измерение концентрации микрочастиц</h3>
<p>Источников частиц PM2.5 довольно много, как первичных (горение мусора), так и вторичных (реакции химических загрязнений с образованием твердых продуктов). Измерение их концентрации в воздухе представляло достаточно сложную задачу, доступную только для специализированных организаций с дорогостоящим оборудованием, до тех пор, пока не появились недорогие лазерные PM-датчики, например, SDS011<sup class="footnote-ref" id="fnref-2"><a href="#fn-2">2</a></sup>:</p>
<p><img src="/media/uploads/sds011.jpg" alt="SDS011" /></p>
<p>Данный датчик при стоимости около $15 позволяет измерять концентрации микрочастиц PM2.5 и PM10 и подключается к компьютеру посредством USB-TTL адаптера (обычно входящего в комплект датчика). Для измерения качества уличного воздуха датчик необходимо разместить в герметичном боксе, например, вот таком:</p>
<p><a href="/media/uploads/aqi_box.jpg" target="_blank"><img src="/media/uploads/aqi_box.jpg" alt="SDS011 - Outdoor" width="600" /></a></p>
<p>Необходимо иметь в виду, что датчик рассчитан на функционирование в условиях воздуха, не содержащего конденсата. В противном случае, например, в туман, датчик может существенно завышать показания. Проблема обычно решается дополнительным подогревом воздуха на входе датчика для устранения конденсата.</p>
<h3>AQI</h3>
<p>Для того, чтобы представить показания датчика в удобном для пользователя виде, необходимо преобразовать концентрации микрочастиц в некоторую оценку качества воздуха. Так как датчик измеряет одновременно и PM2.5, и PM10, обладающие разным вкладом в загрязнение воздуха с точки зрения влияния на здоровье человека, то получение интегральной оценки может быть нетривиальной задачей. К счастью, в мире есть организации, которые исследовали данный вопрос и выработали критерии для формирования такой оценки, и назвали ее <strong>AQI</strong> - Air Quality Index. Методики вычисления AQI по концентрациям микрочастиц несколько варьируются в зависимости от страны, но наиболее распространенной является оценка по методике Environmental Protection Agency (EPA) США, также известная как <strong>US AQI</strong><sup class="footnote-ref" id="fnref-3"><a href="#fn-3">3</a></sup>.</p>
<p>Индекс US AQI имеет два представления - в числовом и цветовом видах, и делится на шесть категорий, характеризующих угрозу здоровью:</p>
<table>
<tbody><tr>
<td>
<table border="2" cellspacing="0" style="width:480px; float:left;">
<tbody><tr style="vertical-align:top; background:#036;">
<td style="width:31%;"><b><span style="color:#fff; font-family:arial; font-size:1em;">Air Quality Index (AQI) Values</span></b></td>
<td style="background:#036; width:32%;"><span style="color:#fff; font-family:arial; font-size:1em;"><b>Levels of Health Concern</b></span></td>
<td style="background:#036; width:37%;"><span style="color:#fff; font-family:arial; font-size:1em;"><b>Colors</b></span></td>
</tr>
<tr style="vertical-align:top; background:#00e400;">
<td style="width:31%;">0 to 50</td>
<td style="width:32%;">Good</td>
<td style="width:37%;">Green</td>
</tr>
<tr style="vertical-align:top; background:#ff0;">
<td style="width:31%;">51 to 100</td>
<td style="width:32%;">Moderate</td>
<td style="width:37%;">Yellow</td>
</tr>
<tr style="vertical-align:top; background:#ff7e00;">
<td style="width:31%;">101 to 150</td>
<td style="width:32%;">Unhealthy for Sensitive Groups</td>
<td style="width:37%;">Orange</td>
</tr>
<tr style="vertical-align:top; background:#f00;">
<td style="width:31%;"><span style="color:#fff;">151 to 200</span></td>
<td style="width:32%;"><span style="color:#fff;">Unhealthy</span></td>
<td style="width:37%;"><span style="color:#fff;">Red</span></td>
</tr>
<tr style="vertical-align:top; background:#99004c;">
<td style="width:31%;"><span style="color:#fff;">201 to 300</span></td>
<td style="width:32%;"><span style="color:#fff;">Very Unhealthy</span></td>
<td style="width:37%;"><span style="color:#fff;">Purple</span></td>
</tr>
<tr style="vertical-align:top; background:#7e0023;">
<td style="width:31%;"><span style="color:#fff;">301 to 500</span></td>
<td style="width:32%;"><span style="color:#fff;">Hazardous</span></td>
<td style="width:37%;"><span style="color:#fff;">Maroon</span></td>
</tr>
</tbody></table>
</td>
</tr>
</tbody></table><h3>AQI-бот для Telegram</h3>
<p>Таким образом, для получения оценки качества воздуха надо периодически считывать показания датчика, преобразовывать их в значение AQI, сохранять для целей сбора статистики и выдавать пользователю по запросу. Я решил, что неплохо, если все эти данные будут доступны через <a href="https://telegram.org/">Telegram</a>, следовательно, вышеописанные операции должны выполняться ботом.</p>
<p>На данный момент бот раз в несколько минут опрашивает PM-датчик, сохраняет данные измеренных концентраций микрочастиц в базу на диске и позволяет получать текущее значение AQI (по команде <code>/aqi</code>), концентрации микрочастиц (команда <code>/pm</code>), а также отображать графики изменения этих параметров за час и сутки (команды <code>/aqi_hourly</code>, <code>/aqi_daily</code>, <code>/pm_hourly</code>, <code>/pm_daily</code>):</p>
<p><img src="/media/uploads/daily.jpg" alt="PM Daily" /></p>
<p>Технические подробности компиляции и запуска бота тут я рассматривать не буду, так как они описаны в <a href="https://github.com/3cky/aqi-telegram-bot/blob/master/README.md">README</a>.</p>
<h3>TODO</h3>
<p>Часть задумок пока еще не реализована, а именно:</p>
<ul>
<li>Подписка на уведомления о превышении заданного уровня AQI;</li>
<li>Возможность периодического постинга текущих значений AQI в выбранный канал;</li>
<li>Кэширование почасовых/посуточных графиков изменения AQI/PM.</li>
</ul>
<p>На этом пока всё. Традиционно, готов ответить на вопросы в комментариях. )</p>
<div class="footnotes">
<hr>
<ol><li id="fn-1"><p><a href="http://www.iarc.fr/en/media-centre/iarcnews/pdf/pr221_E.pdf">IARC: Outdoor air pollution a leading environmental cause of cancer deaths (PDF)</a><a href="#fnref-1" class="footnote">↩</a></p></li>
<li id="fn-2"><p><a href="http://ecksteinimg.de/Datasheet/SDS011%20laser%20PM2.5%20sensor%20specification-V1.3.pdf">SDS011 Laser PM2.5 Sensor specification (PDF)</a><a href="#fnref-2" class="footnote">↩</a></p></li>
<li id="fn-3"><p><a href="https://en.wikipedia.org/wiki/Air_quality_index#United_States">Air quality index - United States (Wikipedia)</a><a href="#fnref-3" class="footnote">↩</a></p></li>
</ol>
</div>
Плагин Telegram Uploader для Jenkins
2017-11-01T23:35:14+03:00https://antonovich.me/2017/11/01/jenkins-telegram-uploader/TL;DR Плагин Telegram Uploader для системы автоматизации сборочным процессом Jenkins CI, позволяет публиковать результаты сборки проекта в облачном хранилище Telegram. Зачем это нужно? Если вы используете Jenkins для сборки Android-приложений и вам не хватает возможности сразу же отправить собранный APK-файл в чат тестеров, откуда они могут напрямую скачать и установить…
<h2>TL;DR</h2>
<p>Плагин <a href="https://github.com/3cky/telegram-uploader-plugin" title="Telegram Uploader">Telegram Uploader</a> для системы автоматизации сборочным процессом <a href="https://jenkins.io/">Jenkins CI</a>, позволяет публиковать результаты сборки проекта в облачном хранилище <a href="https://telegram.org/">Telegram</a>.</p>
<h2>Зачем это нужно?</h2>
<p><a name="more"></a></p>
<p>Если вы используете Jenkins для сборки Android-приложений и вам не хватает возможности сразу же отправить собранный APK-файл в чат тестеров, откуда они могут напрямую скачать и установить приложение на свои устройства - этот плагин для вас. :)</p>
<h3>Установка</h3>
<p>Скачайте <a href="/media/uploads/telegram-uploader.hpi">hpi-пакет</a> и установите его через менеджер плагинов Jenkins (Manage Jenkins->Manage Plugins->Advanced->Upload Plugin).</p>
<p><em>Предупреждение</em>: для работы плагина нужна Java 1.8.</p>
<h3>Настройка</h3>
<p>После установки плагина необходимо прописать <a href="https://core.telegram.org/bots#3-how-do-i-create-a-bot">токен бота</a> в глобальных настройках Jenkins (Manage Jenkins->Configure System->Telegram Uploader):</p>
<p><img src="/media/uploads/telegram_uploader_plugin_global.png" alt="Telegram Uploader Global Config" /></p>
<p>После этого нужно сконфигурировать в нужной задаче (Job) послесборочное действие (post-build action) под названием "Upload artifacts to Telegram":</p>
<p><a href="/media/uploads/telegram_uploader_plugin_config.png" target="_blank"><img src="/media/uploads/telegram_uploader_plugin_config.png" alt="Telegram Uploader Job Config" width="900" /></a></p>
<p>Параметры:</p>
<ul>
<li><strong>Chat ID</strong> - идентификатор чата, в который необходимо загружать артефакты сборки, положительное число для приватных чатов, отрицательное для групп, строка <code>@channelname</code> для каналов. Обратите внимание, что Bot API не разрешает боту посылать сообщения первым, также нельзя отправлять сообщения в группы и каналы, членом которых бот не является.</li>
<li><strong>Caption</strong> - заголовок, длиной от 0 до 200 символов. Возможно использование переменных окружения Jenkins.</li>
<li><strong>Filter</strong> - <a href="http://ant.apache.org/manual/dirtasks.html#patterns">Ant-шаблон</a> фильтра имен загружаемых артефактов, может быть пустым, что эквивалентно выражению <code>**</code> (все файлы во всех поддиректориях).</li>
<li><strong>Disable notification</strong> - включает режим беззвучного уведомления о загруженных файлах.</li>
<li><strong>Fail build if upload is failed</strong> - определяет, отмечать или нет билд как неуспешный в случае возникновения проблем с загрузкой файлов.</li>
</ul>
<h3>Подводные камни</h3>
<ul>
<li>Плагин находится в альфа-стадии. Это значит, что при его работе возможны (и будут) ошибки. Пишите о них в <a href="https://github.com/3cky/telegram-uploader-plugin/issues">GitHub Issues</a>.</li>
<li>Артефакты для загрузки должны быть архивированы плагином <strong>Archive the artifacts</strong>. Как следствие, Telegram Uploader должен находиться в списке Post-build Actions после вышеупомянутого плагина.</li>
<li>К сожалению, мне неизвестен простой способ узнать ID чата в Telegram. Из известных мне способов самым простым является написать в чате боту и потом посмотреть поле <code>id</code> в результате вывода команды <code>curl -i -X GET https://api.telegram.org/bot<inser_token_here>/getUpdates</code>.</li>
<li>Плагин использует <a href="https://core.telegram.org/bots/api">интерфейс Telegram Bot API</a>, который на данный момент имеет ограничение на размер загружаемых документов в 50 мегабайт.</li>
<li>Ссылки в поле Caption на Android-клиенте некликабельны.</li>
</ul>
<p>Пока это всё. Пожелания и замечания по плагину можно писать в комментариях.</p>
Простая и эффективная антенна для приема ADS-B - своими руками!
2017-08-22T11:26:18+03:00https://antonovich.me/2017/08/22/diy-adsb-antenna/Как мы уже знаем, построить свой собственный самолетный радар - это интересно и несложно. Но наверняка многие из попробовавших сделать ADS-B приемник заметили, что дальность отслеживания самолетов при использовании простой штыревой антенны, мягко говоря, невелика, и обычно не превышает 100-150 км. В данной статье описывается, как, потратив совсем немного денег…
<p>Как мы <a href="/2017/1/5/diy-radar/">уже знаем</a>, построить свой собственный самолетный радар - это интересно и несложно. Но наверняка многие из попробовавших сделать ADS-B приемник заметили, что дальность отслеживания самолетов при использовании простой штыревой антенны, мягко говоря, невелика, и обычно не превышает 100-150 км. В данной статье описывается, как, потратив совсем немного денег и времени, сделать простую, но в то же время эффективную антенну для приема сигналов ADS-B на расстоянии до 400 км.<a name="more"></a></p>
<p>Антенна, которую мы будем делать, относится к так называемым коаксиальным коллинеарным антеннам. "Коаксиальная" означает, что она изготавливается из коаксиального кабеля, в нашем случае - из чрезвычайно распространенного и недорого антенного кабеля RG-6U. Коллинеарной антенна называется потому, что она состоит из нескольких элементов, размещенных друг над другом.</p>
<p>Кроме кабеля, нам также понадобятся изолента, рулетка, нож, бокорезы, стандартный антенный штекер типа F, а также пластиковая трубка внешним диаметром 12 мм, которая будет служить антенне корпусом:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_01.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_01.jpg" alt="Принадлежности и материалы" width="900" /></a></p>
<p>Все перечисленное можно купить в любом крупном магазине строительных материалов.</p>
<p>Постройка антенны начинается с нарезки кабеля на отрезки длиной 176 мм каждый:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_02.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_02.jpg" alt="Элементы антенны" width="900" /></a></p>
<p>Количество этих отрезков зависит от того, какое усиление антенны вы хотите получить. Наиболее популярные варианты - 5 элементов (общая длина антенны около 60 см) или 8 элементов (общая длина около 1 м). Как показывает опыт, 8-элементная антенна обеспечивает прием в среднем на 20% большего количества пакетов ADS-B, чем 5-элементная. В данной статье мы рассматриваем сборку 5-элементной антенны; сборка 8-элементной антенны ничем принципиально не отличается, кроме того, что на нарезку и соединение элементов придется потратить чуть-чуть больше времени.</p>
<p>Следующий шаг - отмеряем по 30 мм с каждого конца и ножом аккуратно надрезаем элементы по отметкам до центрального провода, после чего снимаем оплетку и изоляцию:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_03.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_03.jpg" alt="Элементы антенны с зачищенными концами" width="900" /></a></p>
<p>Длина оставшейся незачищенной части элемента определяет резонансные характеристики всей антенны в целом, поэтому важно проконтролировать, чтобы у каждого элемента она была близка к 116 мм.</p>
<p>После того, как все элементы подготовлены, начинаем соединять их в одно общее целое:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_04.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_04.jpg" alt="Соединение элементов антенны" width="900" /></a></p>
<p>Каждый элемент соединяется с другим путем введения до упора центральной жилы одного элемента под оплетку другого. Отрезок изоленты надевается на центральные жилы с целью не допустить короткого замыкания между центральными жилами и оплетками элементов.</p>
<p>Элементы соединяются таким образом друг с другом последовательно в шахматном порядке, в результате чего получается такая конструкция:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_06.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_06.jpg" alt="Полотно антенны в сборе" width="900" /></a></p>
<p>Далее для обеспечения прочности конструкции стыки элементов перетягиваются изоляционной лентой или термоусадочной трубкой, а также навинчивается разъем F-типа:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_08.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_08.jpg" alt="Антенна с зафиксированными элементами" width="900" /></a></p>
<p>Центральную жилу на другом конце антенны можно оставить как есть, или же соединить ее с оплеткой через резистор номиналом 75 Ом, что в теории может обеспечить несколько лучшие характеристики антенны:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_07.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_07.jpg" alt="Терминирующий резистор" width="640" /></a></p>
<p>На последнем этапе антенна для обеспечения жесткости и защиты от атмосферных воздействий помещается в пластиковую трубку-корпус, верхняя часть которой закрывается пластиковым колпачком:</p>
<p><a href="/media/uploads/adsb_collinear_antenna_09.jpg" target="_blank"><img src="/media/uploads/adsb_collinear_antenna_09.jpg" alt="Антенна в сборе" width="900" /></a></p>
<p>Антенна готова! Осталось только установить ее в месте, где обеспечивается максимальный обзор неба, подключить к приемнику, и я гарантирую, что результат вас приятно удивит. :)</p>
Радар своими руками, или Как найти собаколёт Шувалова
2017-01-05T15:36:42+03:00https://antonovich.me/2017/01/05/diy-radar/О чем речь? Все, кто хоть немного интересуется тайной жизнью российских чиновников, наверняка помнят недавнюю историю с личным самолетом вице-премьера Игоря Шувалова. Благодаря изучению открытых источников, в частности, данных с сервисов отслеживания авиарейсов PlaneFinder.net и FlightRadar24.com, Фонду борьбы с коррупцией Алексея Навального удалось выяснить, что самолет используется чиновником не только…
<p><a href="/media/uploads/pine-meme-radar.jpg" target="_blank"><img src="/media/uploads/pine-meme-radar.jpg" alt="" width="640" /></a><a name="more"></a></p>
<h3>О чем речь?</h3>
<p>Все, кто хоть немного интересуется тайной жизнью российских чиновников, наверняка помнят недавнюю <a href="https://navalny.com/p/4952/">историю</a> с личным самолетом вице-премьера Игоря Шувалова. Благодаря изучению открытых источников, в частности, данных с сервисов отслеживания авиарейсов <a href="https://planefinder.net/">PlaneFinder.net</a> и <a href="https://www.flightradar24.com/">FlightRadar24.com</a>, Фонду борьбы с коррупцией Алексея Навального удалось выяснить, что самолет используется чиновником не только для полетов на деловые встречи, но и для вывоза принадлежащих ему собак на различные международные выставки и конкурсы. Однако радость гражданского общества по поводу открывшихся перед ним перспектив по выявлению живущих не по средствам слуг народа оказалась недолгой - упомянутые выше сервисы отключили возможность следить за полетами вице-премьера и его собак, а все вопросы о правомерности такого решения были ими проигнорированы.</p>
<h3>Что делать?</h3>
<p>Если коммерческие сервисы отслеживания авиарейсов выступают на стороне государства и отказываются публиковать информацию о принадлежащих чиновникам самолетах, мы, граждане, можем добыть эти данные самостоятельно. Потратив около четырех тысяч рублей на оборудование и пару дней свободного времени на сборку и установку, каждый желающий может поучаствовать в проекте независимого отслеживания воздушных транспортных средств - <a href="https://www.adsbexchange.com/">ADSBexchange.com</a>.</p>
<h3>Как это работает?</h3>
<p><a href="/media/uploads/adsb_principle.jpg" target="_blank"><img src="/media/uploads/adsb_principle.jpg" alt="" width="900" /></a></p>
<p>Каждый современный самолет снабжен так называемым ADS-B транспондером - устройством, передающим на определенной частоте в ответ на запрос со стороны радиолокационной станции (РЛС) информацию о себе - уникальный идентификатор воздушного судна, а также данные о местоположении, скорости полета и некоторые другие. Важным тут является то, что принять и декодировать эту информацию может любой желающий, использовав для этого имеющуюся в свободном доступе недорогую бытовую аппаратуру - USB-приемник цифрового телевидения стандарта DVB-T, подключенный к одноплатному компьютеру Raspberry Pi с запущенной на нем программой-декодером.</p>
<p>Декодированная информация о самолетах, находящихся в зоне прямой видимости приемника, может быть просмотрена локально, но для отслеживания полного маршрута самолета от места вылета до пункта назначения необходимо объединить информацию, полученную от приемников со всех промежуточных точек. Именно для этого и предназначен сервис <a href="https://www.adsbexchange.com/">ADSBexchange.com</a>, создающий на базе полученных от локальных приемных станций данных глобальную карту авиарейсов - Global Radar View, функционально аналогичную таковой у сервисов типа PlaneFinder.net и FlightRadar24.com, но, в отличие от них, не скрывающий от конечных пользователей никакой информации об отслеживаемых самолетах. Вот, например, мы можем видеть, что на новогодние праздники вице-премьер опять улетел на свою дачу в Австрии:</p>
<p><a href="/media/uploads/sobakolet.jpg" target="_blank"><img src="/media/uploads/sobakolet.jpg" alt="" width="900" /></a></p>
<p>Чем больше приемных станций подключено к сервису, тем более полным является покрытие, и в случае России ситуация пока весьма печальная - достаточно взглянуть на <a href="http://www.adsbexchange.com/coverage-2/">карту</a> и сравнить количество станций у нас с количеством станций в Европе.</p>
<p><a href="/media/uploads/adsbexchange-coverage.jpg" target="_blank"><img src="/media/uploads/adsbexchange-coverage.jpg" alt="" width="900" /></a></p>
<p>Но в наших силах изменить сложившуюся ситуацию! Для этого всего лишь необходимо построить свою приемную станцию и включить ее в сеть ADSBexchange.</p>
<h3>Что для этого нужно?</h3>
<h4>1. Raspberry Pi</h4>
<p>Самый популярный одноплатный микрокомпьютер в мире, существует несколько моделей, отличающихся объемом памяти, частотой процессора и набором периферии. Для наших целей подойдет любая модель с портом Ethernet на борту, например, Raspberry Pi 3 Model B:</p>
<p><img src="/media/uploads/Raspberry_Pi_3_Model_B.jpg" alt="" /></p>
<p>Купить вместе с блоком питания и корпусом можно на Aliexpress по цене около 3000 р., например, <a href="https://www.aliexpress.com/item/2016-Raspberry-Pi-3-Model-B-Board-5V2-5A-Power-Charger-ABS-Case-Heat-Sink-For/32629675163.html">тут</a>. Можно поискать и у отечественных продавцов, но цена, разумеется, будет существенно выше.</p>
<h4>2. Карта памяти</h4>
<p>Для Raspberry Pi 3 нужна карта памяти формата MicroSD, более ранние модели используют полноразмерную карту SD. Рекомендуемый объем - 8ГБ, класс скорости - 10. Из проверенных производителей могу порекомендовать карты SanDisk или Transcend. Цена вопроса - около 300 р.</p>
<h4>3. USB-приемник DVB-T</h4>
<p><a href="/media/uploads/RTL2832-R820T.jpg" target="_blank"><img src="/media/uploads/RTL2832-R820T.jpg" alt="" width="400" /></a></p>
<p>Ключевые слова для поиска на Aliexpress - "RTL2832U R820T2", цена около 500 р., например, <a href="https://www.aliexpress.com/item/USB-2-0-Software-Radio-DVB-T-RTL2832U-R820T2-SDR-Digital-TV-Receiver-Stick/32506410349.html">вот такой</a>. Можно поискать у местных продавцов, но выглядящий внешне точно так же приемник может оказаться построенным на других чипах, поэтому нужно уточнить у продавца, что внутри стоит именно связка RTL2832U+R820T2.</p>
<h4>4. Антенна</h4>
<p>В комплекте с USB-приемником идет антенна, но она, мягко говоря, плохо подходит для приема сигналов самолетных транспондеров, так что радиус приема с ней будет невелик - от силы несколько десятков километров. Для того, чтобы получить радиус в сотни километров, ее придется заменить на более подходящую. Самый простой вариант - заменить штырь штатной антенны на трехэлементную коллинеарную антенну, которую можно согнуть из медной или стальной проволоки по следующему чертежу (кликабельно):</p>
<p><a href="/media/uploads/adsb-ant-drawing.gif" target="_blank"><img src="/media/uploads/adsb-ant-drawing.gif" alt="" width="400" /></a></p>
<p>Должно получиться что-то вроде этого:</p>
<p><a href="/media/uploads/collnear_ant.jpg" target="_blank"><img src="/media/uploads/collnear_ant.jpg" alt="" width="400" /></a></p>
<p>Наилучшим же вариантом, обеспечивающим максимальную дальность приема вплоть до 400 км, является использование <a href="/2017/8/22/diy-adsb-antenna/">коаксиальной коллинеарной антенны</a>.</p>
<p>Так как прием радиосигналов от самолетных транспондеров возможен только в пределах прямой видимости, то антенну нужно разместить вне помещения, в идеале - на крыше. Для этого можно использовать или USB-удлинитель длиной до 5 метров, поместив в герметичный бокс только приемник, или <a href="https://ru.wikipedia.org/wiki/Power_over_Ethernet">PoE</a> (в таком случае в бокс нужно будет поместить и Raspberry Pi).</p>
<h4>5. Софт</h4>
<p>На данный момент ADSBexchange использует модифицированный дистрибутив PiAware. Данный дистрибутив разработан компанией <a href="https://ru.flightaware.com/">FlightAware</a>, также предоставляющей коммерческий сервис отслеживания самолетов, но, увы, в исходном своем виде тоже скрывающий информацию о частных самолетах. Данный дистрибутив взят за основу для ADSBexchange, так как он очень прост в установке и настройке.</p>
<p>Пошаговая инструкция для Windows:</p>
<ul>
<li>Загрузите дистрибутив <a href="https://www.adsbexchange.com/downloads/ADSBexchange-img-1.2.zip">https://www.adsbexchange.com/downloads/ADSBexchange-img-1.2.zip</a> (868 МБ) и сохраните его на своем компьютере.</li>
<li>Разархивируйте файл ADSBexchange-img-1.2.zip.</li>
<li>Загрузите утилиту <a href="https://ru.flightaware.com/adsb/piaware/files/Win32DiskImager-0.9.5-binary.zip">Win32DiskImager</a> и запустите ее от имени администратора (для этого кликните файл правой кнопкой и выберите "Запустить от имени администратора").</li>
<li>Выберите файл ADSBexchange-img-1.2.img.</li>
<li>Вставьте SD-карту в кардридер компьютера.</li>
<li>Выберите буквенное обозначение SD-карты из соответствующего списка.</li>
<li>Кликните "записать" и дождитесь окончания в течение нескольких минут.</li>
<li>После окончания выньте карту памяти из кардридера и вставьте ее в Raspberry Pi.</li>
<li>Подключите все кабели (USB-питание, кабель Ethernet, USB-приемник) к Raspberry Pi. При этом на Raspberry Pi должен гореть красный и мигать зеленый светодиод, а возле сетевого разъема Ethernet — зеленый и желтый.</li>
<li>Подождите пару минут, пока Raspberry Pi загрузится.</li>
<li><a href="http://ru.flightaware.com/account/join/">Зарегистрируйте</a> новую учетную запись на сайте FlightAware.</li>
<li><a href="https://flightaware.com/adsb/piaware/claim">Привяжите</a> приемник к созданной учетной записи.</li>
<li>В настройках приемника (вкладка "My ADS-B") отредактируйте координаты местоположения приемника и высоту установки антенны над уровнем земли.</li>
<li>Через некоторое время приемник должен появиться на карте покрытия <a href="https://www.adsbexchange.com/active-feeds/">https://www.adsbexchange.com/active-feeds/</a>.</li>
<li>Отслеживаемые в данный момент времени вашей приемной станцией самолеты можно посмотреть, перейдя по ссылке "Web interface: view live data" вкладки "My ADS-B" на сайте FlightAware.</li>
</ul>
<h3>PROFIT!</h3>
<p>Теперь ваш приемник участвует сразу в двух сетях отслеживания авиарейсов - ADSBexchange и, в качестве бонуса, FlightAware. Распространяйте эту инструкцию, помогайте другим строить свои приемные станции, и собаколёту Шувалова не удастся укрыться от всевидящего ока гражданского общества!</p>
Прохождение Меркурия по диску Солнца - 2016
2016-05-09T19:30:00+03:00https://antonovich.me/2016/05/09/mercury-transit-2016/Сегодня, 9 мая 2016 года, все любители космоса следили за редким событием - прохождением Меркурия по солнечному диску. Событие действительно нечастое - подобные прохождения случаются 13 или 14 раз в столетие, весной или осенью, причем весенние транзиты случаются гораздо реже - следующий транзит в мае будет только в 2049 году!…
<p><a href="/media/uploads/mercury-transit.jpg" target="_blank"><img src="/media/uploads/mercury-transit.jpg" alt="Прохождение Меркурия по диску Солнца" title="Прохождение Меркурия по диску Солнца" width="640" /></a><a name="more"></a></p>
<p>Сегодня, 9 мая 2016 года, все любители космоса следили за редким событием - прохождением Меркурия по солнечному диску. Событие действительно нечастое - подобные прохождения случаются 13 или 14 раз в столетие, весной или осенью, причем весенние транзиты случаются гораздо реже - следующий транзит в мае будет только в 2049 году! Разумеется, я тоже не мог пройти мимо такого события и провел сессию астрофотографии, результат которой вы можете видеть на картинке выше (она кликабельная). Для съемки использовался фотоаппарат Canon SX50 HS на штативе с установленным солнечным фильтром Baader AstroSolar. Меркурий на фото - круглое пятно левее и ниже центра, пятна неправильной формы - солнечные пятна с номерами 2542 и 2543. Съемка производилась в формате RAW с последующей обработкой в <a href="http://ufraw.sourceforge.net/">UFRaw</a> и <a href="https://www.gimp.org/">GIMP</a>.</p>
Гудбай, HTTP!
2016-04-27T22:33:40+03:00https://antonovich.me/2016/04/27/https-only/С сегодняшнего дня, благодаря проекту Let' s Encrypt от Фонда Электронных Рубежей (Electronic Frontier Foundation), данный блог доступен по протоколу HTTPS, и только по нему (используется HSTS). В качестве бонуса включена поддержка HTTP/2. Безопасного всем серфинга!
<p>С сегодняшнего дня, благодаря проекту <a href="https://letsencrypt.org/">Let' s Encrypt</a> от <a href="https://www.eff.org/">Фонда Электронных Рубежей (Electronic Frontier Foundation)</a>, данный блог доступен по протоколу HTTPS, и только по нему (используется <a href="https://ru.wikipedia.org/wiki/HSTS">HSTS</a>). В качестве бонуса включена поддержка <a href="https://ru.wikipedia.org/wiki/HTTP/2">HTTP/2</a>.<a name="more"></a></p>
<p>Безопасного всем серфинга!</p>
mbusd 0.2.3
2016-04-25T18:10:00+03:00https://antonovich.me/2016/04/25/mbusd/Сегодня зарелизил очередную версию одного из самых своих "долгоиграющих" проектов - mbusd, представляющего собой шлюз между двумя вариантами (TCP/RTU) протокола АСУ ТП Modbus. Отличительные черты реализации: Кроссплатформенность: шлюз способен работать на большинстве POSIX-совместимых систем; Компактность: реализация изначально создавалась для работы во встраиваемых системах, ограниченных по частоте процессора и доступного ОЗУ;…
<p>Сегодня зарелизил очередную версию одного из самых своих "долгоиграющих" проектов - <a href="https://github.com/3cky/mbusd">mbusd</a>, представляющего собой шлюз между двумя вариантами (TCP/RTU) протокола АСУ ТП <a href="https://ru.wikipedia.org/wiki/Modbus">Modbus</a>.<a name="more"></a></p>
<p>Отличительные черты реализации:</p>
<ul>
<li>Кроссплатформенность: шлюз способен работать на большинстве POSIX-совместимых систем;</li>
<li>Компактность: реализация изначально создавалась для работы во встраиваемых системах, ограниченных по частоте процессора и доступного ОЗУ;</li>
<li>Работа в мультимастер-режиме: до 32 TCP-мастеров могут осуществлять мультиплексированный обмен с RTU-слейвами;</li>
<li>Отказоустойчивость: возможность автоматического выполнения перезапросов в случае несовпадения контрольной суммы в ответе RTU-слейва;</li>
<li>Поддержка RS-232/485-конвертеров как с автоматическим управлением направлением передачи, так и по сигналу RTS.</li>
</ul>
<p>Поддерживаемые коды функций Modbus:</p>
<ul>
<li>01: Read coil status</li>
<li>02: Read input status</li>
<li>03: Read holding registers</li>
<li>04: Read input registers</li>
<li>05: Force single coil</li>
<li>06: Preset single register</li>
<li>07: Read exception status</li>
<li>15: Force multiple coils</li>
<li>16: Preset multiple registers</li>
</ul>
<p>Лицензия проекта - BSD. Все желающие что-то изменить или улучшить могут сделать свой форк <a href="https://github.com/3cky/mbusd">проекта на GitHub</a>. Pull request'ы приветствуются. :)</p>
BkEmu 0.4.0
2016-02-08T23:34:00+03:00https://antonovich.me/2016/02/08/bkemu/Не прошло и года Прошло! :) И не один, а ровно три года минуло с выхода предыдущей версии эмулятора семейства PDP-11-совместимых советских 16-разрядных домашних компьютеров Электроника БК-0010/11М для платформы Android - BkEmu. Первые в жизни строчки кода, как первая любовь, не забываются никогда, и данный эмулятор - своеобразная дань памяти…
<p><a href="/media/uploads/Screenshot_2016-02-07-18-04-45.png" target="_blank"><img src="/media/uploads/Screenshot_2016-02-07-18-04-45.png" alt="BkEmu Screenshot" title="BkEmu Screenshot" width="640" /></a><a name="more"></a></p>
<p><del>Не прошло и года</del> Прошло! :) И не один, а ровно <em>три</em> года минуло с выхода предыдущей версии эмулятора семейства PDP-11-совместимых советских 16-разрядных домашних компьютеров <strong>Электроника БК-0010/11М</strong> для платформы Android - <a href="https://play.google.com/store/apps/details?id=su.comp.bk">BkEmu</a>. Первые в жизни строчки кода, как первая любовь, не забываются никогда, и данный эмулятор - своеобразная дань памяти компьютеру, открывшему для меня (и для тысяч других жителей Советского Союза) мир программирования.</p>
<p>Итак, сегодня я наконец-то выкатил версию 0.4.0 эмулятора самого первого в моей в жизни компьютера. Из нового - возможность сохранения текстов программ на SD-карту, экранный джойстик, улучшения в интерфейсе пользователя, а также обновленное оформление в стиле Material Design.</p>
<p>Время не стоит на месте, и исходные тексты программ, на которых учились программисты того времени, уже больше не публикуются в журналах, но зато теперь они доступны онлайн. В строгом соответствии с духом Open Source полностью доступны и <a href="https://github.com/3cky/bkemu-android">исходные тексты BkEmu</a>.</p>
<p>Разумеется, каждый новый релиз, исправляя старые ошибки, добавляет новые, так что все заинтересованные лица призываются оставлять отзывы здесь в комментариях или на <a href="https://github.com/3cky/bkemu-android/issues">GitHub</a>.</p>
<p>Enjoy! :)</p>
Космическая одиссея 2015 года
2015-07-23T23:45:24+03:00https://antonovich.me/2015/07/23/2015-a-space-odyssey/Сделал таймлапс (10х) из видео состоявшейся 5 июля этого года автоматической стыковки космического корабля "Прогресс-М28М" с МКС. С вальсом Штрауса из "Космической одиссеи 2001 года" Стэнли Кубрика смотрится отлично, по-моему.
<p>Сделал таймлапс (10х) из видео состоявшейся 5 июля этого года автоматической стыковки космического корабля "Прогресс-М28М" с МКС. С вальсом Штрауса из "Космической одиссеи 2001 года" Стэнли Кубрика смотрится отлично, по-моему.<a name="more"></a></p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/ZpDoo5yKClk" frameborder="0" allowfullscreen></iframe>Восстановление прошивки видеорегистратора Mio MiVue 518
2015-01-27T11:12:29+03:00https://antonovich.me/2015/01/27/mivue-fw-restore/Статья дополнена 19.04.2019. Итак, дурная голова рукам покоя не дает опытным путем выяснено, что при обновлении прошивки видеорегистратора Mio MiVue 518 проверка соответствия модели регистратора и прошивки не производится. Достаточно положить в корень SD-карты файл SD_CarDV.bin от другой модели, например, MiVue 568, включить устройство, дождаться, пока полосочка прогресса обновления добежит…
<p><strong>Статья дополнена 19.04.2019.</strong></p>
<p>Итак, <del>дурная голова рукам покоя не дает</del> опытным путем выяснено, что при обновлении прошивки видеорегистратора Mio MiVue 518 проверка соответствия модели регистратора и прошивки не производится. Достаточно положить в корень SD-карты файл <code>SD_CarDV.bin</code> от другой модели, например, MiVue 568, включить устройство, дождаться, пока полосочка прогресса обновления добежит до конца - и вуаля, имеем <del>кирпич</del> устройство, не подающее никаких признаков жизни, кроме включающего и выключающегося при нажатии кнопки питания светодиода.</p>
<p>Что делать? Конечно же, разбирать регистратор, извлекать флеш-память и прошивать правильным дампом. Но для начала этот дамп необходимо получить из прошивки, в чем нам помогут несколько консольных команд GNU/Linux.<a name="more"></a></p>
<p>После беглого взгляда на <code>SD_CarDV.bin</code> в любом просмотрщике файлов (например, Midnight Commander) становится ясно, что формат прошивки должен быть простым - видны строки с системными сообщениями, следовательно, данные не сжаты. Необходимо только выяснить, какая часть данных в прошивке относится к заголовку, за которым начинается собственно дамп. Для этого можно воспользоваться консольной утилитой <code>cmp</code>, выполняющей побайтное сравнение файлов. Сравним два файла с прошивками разных моделей:</p>
<pre><code>$ cmp -l -n 1024 SD_CarDV.bin SD_CarDV.bin.568
9 115 0
10 111 0
11 117 0
12 105 0
13 67 0
14 61 0
15 65 0
16 70 0
17 345 336
18 332 236
19 40 74
20 22 372
21 355 323
22 313 326
23 317 312
24 251 260
25 142 317
26 314 172
27 132 102
28 373 340
29 142 303
30 110 333
31 173 326
32 305 57
</code></pre>
<p>Итак, видно, что из первого килобайта прошивки отличаются только первые 32 байта (нумерация вывода <code>cmp</code> идет с 1). Логично предположить, что эти 32 байта и являются заголовком. Отрежем его с помощью команды <code>dd</code>:</p>
<p><code>dd bs=32 skip=1 if=SD_CarDV.bin of=SD_CarDV.rom</code></p>
<p>Посмотрим на первые 128 байтов дампа в шестнадцатеричный виде:</p>
<pre><code>$ xxd -l 128 -c 16 -g 1 SD_CarDV.rom
0000000: 06 00 00 ea fe ff ff ea fe ff ff ea fe ff ff ea ................
0000010: fe ff ff ea fe ff ff ea 20 ff 1f e5 fe ff ff ea ........ .......
0000020: d3 f0 21 e3 b0 10 9f e5 01 d0 a0 e1 d2 f0 21 e3 ..!...........!.
0000030: a8 10 9f e5 01 d0 a0 e1 d1 f0 21 e3 a0 10 9f e5 ..........!.....
0000040: 01 d0 a0 e1 d7 f0 21 e3 98 10 9f e5 01 d0 a0 e1 ......!.........
0000050: db f0 21 e3 90 10 9f e5 01 d0 a0 e1 df f0 21 e3 ..!...........!.
0000060: 88 10 9f e5 01 d0 a0 e1 d3 f0 21 e3 80 0a a0 e3 ..........!.....
0000070: 11 1f 19 ee 00 10 81 e1 01 10 81 e3 11 1f 09 ee ................
</code></pre>
<p>Чтобы понять, правильно ли мы определили начало дампа, попробуем дизассемблировать несколько его первых команд. Видеорегистратор построен на чипе <a href="http://www.a-i-t.com.tw/english/Products_01-1.php?id=19" title="AIT8427">AIT8427</a> производства тайваньской фирмы Alpha Imaging Technology. Никакой информации о нем, кроме той, что есть на страничке производителя, увы, нет. О процессоре сказано только то, что это "Embedded 32-bit CPU / 500MHz". Логично было бы предположить, что за "32-bit CPU" скрывается хорошо знакомый всем нам ARM. Для быстрой проверки воспользуемся <a href="http://www2.onlinedisassembler.com/odaweb/" title="ODA Online Disassembler">онлайн-дизассемблером ODA</a>:</p>
<p><img src="/media/uploads/%D0%A1%D0%BD%D0%B8%D0%BC%D0%BE%D0%BA_%D1%8D%D0%BA%D1%80%D0%B0%D0%BD%D0%B0_%D0%B8%D0%B7_2015-01-26_112814.png" alt="Disassembled dump start data" /></p>
<p>Так и есть, это ARM и первой командой является безусловный переход на процедуру инициализации. Отлично, теперь у нас есть дамп для прошивки, осталось только извлечь флеш-память и записать в нее дамп. Для этого разберем регистратор и найдем чип флеш-памяти:</p>
<p><img src="/media/uploads/IMG_20150125_214016.jpg" alt="W25Q64FV flash chip position on device board" /></p>
<p>Флеш-память выделена красным, ее тип - <a href="http://www.nexflash.com/NR/rdonlyres/05A6F2FD-83D2-4748-8394-65909AC2A8E3/0/W25Q64FV.pdf" title="W25Q64FV">W25Q64FV</a>, это флеш-память со SPI-интерфейсом, объемом 64 мегабита (то есть 8 мегабайтов). Для ее прошивки нужен SPI-программатор, в качестве которого я использовал одноплатный микрокомпьютер Raspberry Pi, у него имеется порт SPI, выведенный на 26-контактный штыревой краевой разъем. Поддержка программирования через данный порт есть у отличной утилиты <a href="http://flashrom.org/RaspberryPi" title="FlashRom">FlashRom</a>, единственное, что нужно сделать перед прошивкой - выравнять размер дампа и объем флеш-памяти:</p>
<p><code>$ truncate --size 8M SD_CarDV.rom</code></p>
<p><a href="http://www.win-raid.com/t58f16-Guide-Recover-from-failed-BIOS-flash-using-Raspberry-PI.html">Подключив</a> извлеченную из регистратора флеш-память к Raspberry Pi, запускаем прошивку следующей командой:</p>
<p><code>sudo ./flashrom -w SD_CarDV.rom -p linux_spi:dev=/dev/spidev0.0</code></p>
<p>Процесс стирания, записи и проверки займет около 10 минут, после сообщения об успехе отсоединяем флеш-память от "малинки" и впаиваем обратно в устройство. Затем включаем его и убеждаемся, что вместо "кирпича" у нас опять есть нормально функционирующий регистратор. Ура! :)</p>
<h5>Дополнение от 19.04.2019</h5>
<p>В некоторых новых регистраторах Mio (например, MiVue 733) файл прошивки немного отличается, стартовая последовательность <code>06 00 00 ea fe ff...</code> находится по смещению 0x2020, а вместо нее по смещению 0x20 находится другая последовательность:</p>
<pre><code>$ xxd -l 64 -c 16 -g 1 SD_CarDV.bin
00000000: 41 49 54 53 30 30 33 31 32 44 2e 31 31 34 33 20 AITS00312D.1143
00000010: ac ae 2f d1 48 c8 d1 70 05 17 cd d2 24 da ef 3d ../.H..p....$..=
00000020: 4d 43 52 32 01 00 00 40 0c 00 00 00 09 00 00 00 MCR2...@........
00000030: 02 00 00 00 64 6c 00 00 00 e0 11 00 01 00 00 00 ....dl..........
</code></pre>
<p><strong>Несмотря на это, инструкция по созданию дампа из прошивки остается прежней</strong> - всё так же отрезаем первые 32 байта и прошиваем получившийся дамп, предварительно выравняв его до размера флеш-памяти. Отличие связано, видимо, с тем, что в новых регистраторах процессор стартует не с нулевого адреса, а с адреса 0x2000.</p>
Проблема с maven-зависимостями при запуске android-приложения в Eclipse и ее решение
2014-11-16T17:55:00+03:00https://antonovich.me/2014/11/16/eclipse-m2e-android-run/На выходных решил уделить наконец-то время одному из своих Android-приложений, а именно BkEmu, и обнаружил, что после обновления Eclipse до версии 4.4 (Luna) отвалился запуск в эмуляторе через "Run As"->"Android Application". почему-то перестал подключать maven-зависимости в процесс сборки ADT. Включение экспорта контейнера Maven в настройках проекта ("Properties"->"Java Build Path"->"Order and…
<p>На выходных решил уделить наконец-то время одному из своих Android-приложений, а именно <a href="https://github.com/3cky/bkemu-android/">BkEmu</a>, и обнаружил, что после обновления Eclipse до версии 4.4 (Luna) отвалился запуск в эмуляторе через "Run As"->"Android Application".<a name="more"></a> почему-то перестал подключать maven-зависимости в процесс сборки ADT. Включение экспорта контейнера Maven в настройках проекта ("Properties"->"Java Build Path"->"Order and Export"->галочка на "Maven Dependencies") не только не решило проблему, но только усугубило ее - проект вовсе перестал собираться через Eclipse Build, так как ADT стал пытаться положить в результирующий APK все зависимости, даже с областями видимости <code>provided</code> и <code>test</code>.</p>
<p>Довольно продолжительные поиски решения в Google и эксперименты с версиями плагинов Eclipse не увенчались успехом. Конечно, можно отказаться от запуска приложения средствами Eclipse и работать исключительно через Maven Android Plugin, запуская приложение в эмуляторе через <code>mvn clean install android:deploy android:run</code>, но полная пересборка приложения занимает ощутимо больше времени, чем инкрементальная сборка в Eclipse, что критично на этапе экспериментов с добавляемым функционалом.</p>
<p>Для того, чтобы обойти данную проблему, было решено воспользоваться механизмом приватных библиотек ADT. При сборке средствами Eclipse ADT автоматически включает в build path библиотеки из директории <code>libs</code> в корне проекта, так что можно использовать Maven Dependency Plugin для того, чтобы на этапе сборки скопировать необходимые зависимости в данную директорию, после чего можно будет пользоваться стандартным механизмом ADT для запуска приложений в эмуляторе. Для этого необходимо добавить в <code>pom.xml</code> следующий блок:</p>
<pre><code class="lang-xml"><project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.9</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.basedir}/libs</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
...
</build>
[...]
</project>
</code></pre>
<p>Далее необходимо включить использование библиотек в <code>libs</code>, поставив галочку на "Properties"->"Java Build Path"->"Order and Export"->"Android Private Libraries":</p>
<p><img src="/media/uploads/android-private-libs.png" alt="Screenshot" /></p>
<p>Чтобы при изменении зависимостей в <code>libs</code> не накапливались устаревшие версии библиотек, необходимо добавить данную директорию в список очищаемых в конфигурации Maven Clean Plugin:</p>
<pre><code class="lang-xml"><project>
[...]
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.6.1</version>
<configuration>
<filesets>
<fileset>
<directory>libs</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
[...]
</project>
</code></pre>
<p>Теперь достаточно выполнить <code>mvn clean install</code>, после чего необходимые зависимости станут доступны для ADT. Конечно, это больше напоминает костыль, так как зависимости все-таки должны автоматически включаться через m2e-android, но на данный момент решения лучше мне найти пока не удалось.</p>
Сказано - сделано, или Через тернии к 3D-печати
2014-08-24T17:17:33+04:00https://antonovich.me/2014/08/24/3d-printer-done/Два с лишним месяца назад я начал сборку 3D-принтера. Как я и подозревал, заказ множества компонентов за бугром с доставкой Почтой России - занятие не для нетерпеливых (и, как оказалось, еще и не для слабонервных). Но вот позади ожидание, правка погнутой при доставке алюминиевой 6мм рамы, оправдавшиеся дурные предчувствия при…
<p>Два с лишним месяца назад я <a href="http://antonovich.me/2014/6/8/prusa-i3-bom/">начал сборку 3D-принтера</a>. Как я и подозревал, заказ множества компонентов за бугром с доставкой Почтой России - занятие не для нетерпеливых (и, как оказалось, еще и не для слабонервных). Но вот позади ожидание, правка погнутой при доставке алюминиевой 6мм рамы, оправдавшиеся дурные предчувствия при распаковке посылки с боросиликатным стеклом (полная осколков!) и радость от того, что заказал два стекла, а при доставке разбили только одно. Позади сборка и настройка. Что же получилось в итоге?<a name="more"></a></p>
<p><img src="/media/uploads/IMG_0855.JPG" alt="Prusa i3" title="Prusa i3" /></p>
<p>Итак, в результате имеем классический (vanilla) 3D-принтер модели Prusa i3 с рабочим полем 180x160x180мм, с подогреваемым столиком, печатающей головкой E3D v6, сопло 0.4мм, все это под управлением контроллера на базе Arduino Mega + RAMPS 1.4 с прошивкой Marlin (<a href="https://github.com/3cky/Marlin">https://github.com/3cky/Marlin</a>).</p>
<p><img src="/media/uploads/IMG_0853.JPG" alt="Heatbed" title="Heatbed" /></p>
<p>Нагреваемый столик MK2B, с возможностью питания от 12 и 24 вольт. Пришел поврежденным - был отколот уголок, пришлось восстанавливать с помощью шайб и суперклея с содой. Установлен на винтах M3, пружины взяты из шариковых авторучек, гайки самоконтрящиеся, запресованные в воротки (<a href="http://www.thingiverse.com/thing:6599">http://www.thingiverse.com/thing:6599</a>):</p>
<p><img src="/media/uploads/IMG_0841.JPG" alt="Heatbed leveling" title="Heatbed leveling" /></p>
<p>Нагреваемый столик с нижней стороны термоизолирован материалом "Изолон" (ППЭ-Л-1), приклеенным двусторонним скотчем 3M фольгой наружу и зафиксированным по краям синей малярной лентой 3M, той же, которой покрыто стекло столика.</p>
<p><img src="/media/uploads/IMG_0852.JPG" alt="Heatbed insulation" title="Heatbed insulation" /></p>
<p>Провод питания подогреваемого столика зафиксирован с помощью кронштейна (<a href="http://www.thingiverse.com/thing:204666">http://www.thingiverse.com/thing:204666</a>):</p>
<p><img src="/media/uploads/IMG_0842.JPG" alt="Heatbed cable guide" title="Heatbed cable guide" /></p>
<p>Теперь немного о концевых выключателях. Использованы механические концевики типа Makerbot Mech Endstop 1.2, трехпроводные, со светодиодными индикаторами.</p>
<p>Концевик оси X. Крепление стандартное, типа "вилка", слегка подрезанное, чтобы не цеплялось за раму:</p>
<p><img src="/media/uploads/IMG_0837.JPG" alt="X endstop" title="X endstop" /></p>
<p>Концевик оси Y, крепление тоже "вилка", плата установлена на кронштейнах на такой высоте, чтобы концевик срабатывал от касания алюминиевого основания столика:</p>
<p><img src="/media/uploads/IMG_0838.JPG" alt="Y endstop" title="Y endstop" /></p>
<p>Концевик оси Z, на основе модели <a href="http://www.thingiverse.com/thing:82519">http://www.thingiverse.com/thing:82519</a>, дополнительно просверлены отвестия для закрепления пластиковыми стяжками:</p>
<p><img src="/media/uploads/IMG_0836.JPG" alt="Z endstop" title="Z endstop" /></p>
<p>Для установки положения печатающей головки по высоте, на держателе мотора X-оси установлен регулируемый толкатель Z-концевика:</p>
<p><img src="/media/uploads/IMG_0839.JPG" alt="Z adjustable endstop" title="Z adjustable endstop" /></p>
<p>Общий вид экструдера с установленной печатающей головкой:</p>
<p><img src="/media/uploads/IMG_0843.JPG" alt="Wade extruder" title="Wade extruder" /></p>
<p>Провода экструдера закреплены стяжками на вертикальном стержне для обеспечения отсутствия контакта кабеля с рамой принтера:</p>
<p><img src="/media/uploads/IMG_0844.JPG" alt="Extruder cable" title="Extruder cable" /></p>
<p>Вид сзади:</p>
<p><img src="/media/uploads/IMG_0849.JPG" alt="Extruder back" title="Extruder back" /></p>
<p>Вид сверху:</p>
<p><img src="/media/uploads/IMG_0850.JPG" alt="Extruder top" title="Extruder top" /></p>
<p>Крепление печатающей головки выполнено с помощью алюминиевого кронштейна. Вентилятор охлаждения головки специально повернут на 45 градусов к плоскости каретки, чтобы обеспечить увод воздушного потока от радиатора в сторону:</p>
<p><img src="/media/uploads/IMG_0851.JPG" alt="Hot end" title="Hot end" /></p>
<p>Вид на детали экструдера, обеспечивающие подачу филамента. Прижимные пружины взяты из канцелярского дырокола, их конический профиль практически идеально подходит для установки на винты M4. Прижимный подшипник проточен для обеспечения отсутствия биений в поперечной плоскости при подаче филамента:</p>
<p><img src="/media/uploads/IMG_0840.JPG" alt="Extruder idler" title="Extruder idler" /></p>
<p>Вид на плату электроники. На все четыре драйвера шаговых двигателей и транзистор управления подогревом столика приклеены радиаторы. Установлен 50мм 12В вентилятор обдува (сверху, запитан от контактов не используемого пятого драйвера):</p>
<p><img src="/media/uploads/IMG_0846.JPG" alt="Electronics" title="Electronics" /></p>
<p>Держатель филамента выполнен из 5 мм шпилек с осью на подпружиненных подшипниках 605z, установленных в импровизированные держатели из бельевых прищепок:</p>
<p><img src="/media/uploads/IMG_0847.JPG" alt="Spool holder" title="Spool holder" /></p>
<h1>Примеры печати</h1>
<p>Калибровочные образцы (<a href="http://www.thingiverse.com/thing:5573">http://www.thingiverse.com/thing:5573</a>) и прочая мелочь (белый - PLA, черный - ABS, числа - температура печати):</p>
<p><img src="/media/uploads/IMG_0857.JPG" alt="Printed" title="Printed" /></p>
<p>Голова Магистра Йоды (PLA, <a href="http://www.thingiverse.com/thing:14104">http://www.thingiverse.com/thing:14104</a>):</p>
<p><img src="/media/uploads/IMG_20140802_210659.jpg" alt="Yoda" title="Yoda" /></p>
<p>Подвес для хрустального кристалла (ABS):</p>
<p><img src="/media/uploads/IMG_20140818_003738.jpg" alt="Holder" title="Holder" /></p>
Что нам стоит 3D-принтер построить
2014-06-08T17:13:58+04:00https://antonovich.me/2014/06/08/prusa-i3-bom/Итак, принято принципиальное решение о постройке 3D-принтера с целью реализации некоторых собственных идей, ну и, разумеется, чтобы просто не отставать от прогресса. :) В качестве базовой модели выбран RepRap Prusa i3 (вариант с алюминиевой рамой): Плюсы данной модели по сравнению с другими моделями RepRap: жесткая конструкция простая сборка уменьшенное количество…
<p>Итак, принято принципиальное решение о постройке 3D-принтера с целью реализации некоторых собственных идей, ну и, разумеется, чтобы просто не отставать от прогресса. :)<a name="more"></a></p>
<p>В качестве базовой модели выбран <a href="http://reprap.org/wiki/Prusa_i3">RepRap Prusa i3</a> (вариант с алюминиевой рамой):</p>
<p><img src="/media/uploads/Prusai3-metalframe.jpg" alt="Prusa i3" /></p>
<p>Плюсы данной модели по сравнению с другими моделями RepRap:</p>
<ul>
<li>жесткая конструкция</li>
<li>простая сборка</li>
<li>уменьшенное количество печатаемых пластиковых деталей</li>
</ul>
<p>Минусы:</p>
<ul>
<li>подвижный столик (ограничивает максимальную скорость печати)</li>
<li>раму можно изготовить только на специализированном оборудовании (лазерный или водяной резак)</li>
</ul>
<p><strong>Список необходимых материалов и принадлежностей<sup class="footnote-ref" id="fnref-*"><a href="#fn-*">1</a></sup></strong>:</p>
<ul>
<li><strong>Основные части:</strong><ul>
<li>Рама алюминиевая (лист 6мм) - <a href="http://reprapworld.com/?products_details&products_id=439&cPath=1645">€100</a> (с доставкой). Можно найти и <a href="http://www.ebay.com/sch/i.html?_odkw=prusa+i3+aluminum+frame&_osacat=0&_from=R40&_trksid=p2045573.m570.l1313.TR0.TRC0&_nkw=prusa+i3+aluminum+frame&_sacat=0">дешевле</a>, но эта мне больше других понравилась с эстетической точки зрения. :)</li>
<li>Набор печатаемых пластиковых деталей - спасибо Дмитрию (<a href="http://minicube.ru/">http://minicube.ru/</a>) - 2500р (<a href="/files/Prusa_i3.zip">STL-файлы</a>). </li>
<li>Печатающая головка с нагревателем E3D v6 HotEnd Full Kit - 1.75mm Universal - <a href="http://e3d-online.com/">£51.80</a>.</li>
<li>Нагреваемый стол MK2 - <a href="http://www.ebay.com/itm/Mk2a-three-dimensional-3D-printer-heated-bed-PCB-Heatbed-reprap-Mendel-/140961992624?pt=LH_DefaultDomain_0&hash=item20d1fd4fb0">$10.5</a>.</li>
<li>Стекло боросиликатное для нагреваемого стола 213x200x3мм - <a href="http://www.aliexpress.com/item/Free-shipping-3d-printer-ReprapMK2-Borosilicate-glass-for-headbed-size-213-200-3mm/1313851457.html">$26</a> (за 2 шт.)</li>
</ul>
</li>
<li><strong>Электроника:</strong><ul>
<li>Блок питания 12В 33.3А - <a href="http://www.ebay.com/itm/400611655363?ssPageName=STRK:MEWAX:IT&_trksid=p3984.m1423.l2649">AU $33.99</a>.</li>
<li>Комплект шаговых двигателей NEMA 17, длина 48мм, момент удержания 0.48Нм 42BYGHW811 (5 шт.) - <a href="http://www.aliexpress.com/item/5-pcs-4-lead-Nema-17-Stepper-Motor-42BYGHW811-4800g-cm-70-oz-in-48mm-2/585774195.html">$75</a>.</li>
<li><a href="http://www.ebay.com/itm/New-3D-Printer-Controller-Kit-RAMPS-1-4-MEGA-2560-R3-5-x-A4988-Motor-Driver-/390830195937?pt=Elektromechanische_Bauelemente&hash=item5aff4b84e1">3D Printer RAMPS Kit - $51.11</a><ul>
<li>Контроллер Arduino mega 2560.</li>
<li>Плата контроллера RAMPS 1.4.</li>
<li>Драйверы шаговых двигателей (5 шт.)</li>
<li>Комплект концевых выключателей (3 шт.)</li>
</ul>
</li>
<li>Терморезистор 100 КОм - <a href="http://www.ebay.com/itm/10pcs-100K-ohm-NTC-3950-Thermistors-for-Reprap-3D-printer-Mendel-Prusa-/251282404280?pt=LH_DefaultDomain_0&hash=item3a819933b8">$6.59</a> (10 шт.)</li>
</ul>
</li>
<li><strong>Валы осей - <a href="http://www.ebay.com/itm/RepRap-M8-Stainless-Steel-Smooth-Rods-8mm-for-Prusa-Mendel-i3-and-others-/321402115969?pt=LH_DefaultDomain_3&var=&hash=item4ad50f1781#shpCntId">£19,90</a>:</strong><ul>
<li><strong>Ось Z:</strong><ul>
<li>Валы М8 320мм (2 шт.)</li>
</ul>
</li>
<li><strong>Ось X:</strong><ul>
<li>Вал М8 20мм (для экструдера).</li>
<li>Валы М8 370мм (2 шт.)</li>
</ul>
</li>
<li><strong>Ось Y:</strong><ul>
<li>Валы М8 350мм (2 шт.)</li>
</ul>
</li>
</ul>
</li>
<li><strong>Шпильки:</strong><ul>
<li>Шпилька М5 300мм (2 шт.)</li>
<li>Шпилька М8 205мм (4 шт.)</li>
<li>Шпилька М10 380мм (2 шт.)</li>
</ul>
</li>
<li><strong>Подшипники:</strong><ul>
<li>Подшипники линейные LM8UU (10 шт.) - <a href="http://www.ebay.com/itm/141244775295?var=440341490894&ssPageName=STRK:MEWAX:IT&_trksid=p3984.m1423.l2649">AU $8.87</a>.</li>
<li>Подшипники радиальные 608zz (10 шт.) - <a href="http://www.ebay.com/itm/390773907375?var=660211350984&ssPageName=STRK:MEWAX:IT&_trksid=p3984.m1423.l2649">$3.69</a>.</li>
</ul>
</li>
<li><strong>Крепеж:</strong><ul>
<li>Шайбы М10 (8 шт.)</li>
<li>Гайки М10 (12 шт.)</li>
<li>Шайбы М8 (22 шт.)</li>
<li>Гайки М8 (22 шт.)</li>
<li>Шайбы усиленные М10 (4 шт.)</li>
<li>Винты M3x10 (23 шт.)</li>
<li>Винты M3x16 (6 шт.)</li>
<li>Винты M3x18 (5 шт.)</li>
<li>Винт M3x25.</li>
<li>Винты М3х30 (6 шт.)</li>
<li>Винты М3х40 (2 шт.)</li>
<li>Болт М8х60 (для податчика экструдера, с неполной нарезкой, искать в автомагазинах).</li>
<li>Гайка самоконтрящаяся M8.</li>
<li>Шайбы M8 - 5 шт.</li>
<li>Винты M4x50 - 2 шт.</li>
<li>Гайки M4 - 2 шт.</li>
<li>Шайбы усиленные M4 - 4 шт.</li>
<li>Гайки самоконтрящиеся M3 (50 шт.) - <a href="http://www.ebay.com/itm/261072238935?ssPageName=STRK:MEWAX:IT&_trksid=p3984.m1423.l2649">$5.76</a>.</li>
<li>Гайки М5 (2 шт.)</li>
<li>Гайка самоконтрящаяся М5.</li>
<li>Шайбы М5 (2 шт.)</li>
<li>Гайки М3 (6 шт.)</li>
<li>Шайбы М3 (2 шт.)</li>
</ul>
</li>
<li><strong>Разное:</strong><ul>
<li>Алюминиевый кронштейн для хотенда - <a href="http://www.ebay.com/itm/111293450340">€6,55</a>.</li>
<li>Пружины экструдера (2 шт.) - использованы конические выжимные пружины из канцелярского дырокола. </li>
<li>Ремни GT2 шириной 6мм (2 метра), шпули GT2 шириной 6мм (2 шт.) - <a href="http://www.ebay.com/itm/111311546458?ssPageName=STRK:MEWAX:IT&_trksid=p3984.m1423.l2649">$11.58</a>.</li>
<li>Муфты соединительные 5x5 мм (2 шт.) - <a href="http://www.ebay.com/itm/New-5-x-5mm-CNC-Motor-Jaw-Shaft-Coupler-5mm-To-5mm-Flexible-Coupling-07-/200816054119?pt=UK_BOI_Electrical_Components_Supplies_ET&hash=item2ec191cf67">GBP 3.30</a>. </li>
<li>Пружины стола 25мм (4шт.) - взяты из автоматических шариковых ручек.</li>
<li>Кабельный органайзер трубчатый спиральный 3мм - <a href="http://www.ebay.com/itm/3mm-70-5FT-21-5M-Spiral-Cable-Wire-Wrap-Tube-Computer-Manage-Cord-clear-/400344757670?pt=LH_DefaultDomain_0&hash=item5d366835a6">$6.64</a>.</li>
<li>Лента термостойкая полиимидная 25мм - <a href="http://www.ebay.com/itm/1pc-High-Temperature-Resistant-Kapton-Tape-25mm-X-100ft-/190540908543?pt=LH_DefaultDomain_0&hash=item2c5d1f87ff">$4.40</a>.</li>
</ul>
</li>
</ul>
<div class="footnotes">
<hr>
<ol><li id="fn-*"><p>Список основан на <a href="http://reprap.org/wiki/Prusa_i3_Buyers_Guide">Prusa i3 Buyers Guide</a> и <a href="http://abella.net/dev/prusai3-bom.xls">Prusa i3 Bill of Materials</a> (XLS), будет изменяться и дополняться в ходе постройки принтера.<a href="#fnref-*" class="footnote">↩</a></p></li>
</ol>
</div>
Сравнение характеристик щелочных батареек и аккумуляторов типоразмера АА
2014-06-05T14:36:08+04:00https://antonovich.me/2014/06/05/batteries-vs-accus/Давно известно, что не все йогурты батарейки одинаково полезны. Также ни для кого не является секретом, что щелочные (или, как их еще называют, алкалиновые) батарейки обладают лучшими характеристиками, чем солевые. Но насколько именно они хороши? В отличие от аккумуляторов, емкость которых в ампер-часах или ватт-часах указывается на упаковке, батарейки довольствуются…
<p>Давно известно, что не все <del>йогурты</del> батарейки одинаково полезны. Также ни для кого не является секретом, что <a href="http://ru.wikipedia.org/wiki/%D0%A9%D0%B5%D0%BB%D0%BE%D1%87%D0%BD%D0%BE%D0%B9_%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82">щелочные</a> (или, как их еще называют, алкалиновые) батарейки обладают лучшими характеристиками, чем <a href="http://ru.wikipedia.org/wiki/Элемент_Лекланше">солевые</a>. Но <em>насколько</em> именно они хороши? <a name="more"></a>В отличие от аккумуляторов, емкость которых в ампер-часах или ватт-часах указывается на упаковке, батарейки довольствуются расплывчатыми эпитетами типа "Super", "Turbo", "Plus", "РАБОТАЮТ ДО Х РАЗ ДОЛЬШЕ" и прочими, на которые не скупятся производители. В результате у покупателя может сложиться впечатление, что щелочные батарейки по своим характеристикам превосходят все другие источники питания аналогичного форм-фактора, в том числе и аккумуляторы. Но так ли это на самом деле?</p>
<p>Для выяснения реальных возможностей батареек я собрал небольшую экспериментальную установку, состоящую из популярного китайского зарядного устройства <a href="http://habrahabr.ru/post/150213/" title="IMAX B6">IMAX B6</a>, подключенного через USB-адаптер к микрокомпьютеру Raspberry Pi.</p>
<p><img src="/media/uploads/IMG_20140605_195156_web.jpg" alt="IMAX B6 and Raspberry Pi" title="IMAX B6 and Raspberry Pi" /></p>
<p>Пользуясь режимом разряда постоянным током (200 мА) до конечного напряжения 0.8В, я протестировал несколько популярных щелочных батареек типоразмера AA ("пальчиковых"). Подключенная к зарядному устройству "малинка" записывала в лог-файл напряжение на батарее в процессе ее разряда. Помимо этого, такой же тест прошли пара никель-металл-гидридных аккумуляторов того же типоразмера (Sanyo Eneloop 1800mAh и Varta Ready2Use 2100 mAh). По полученным данным были построены графики кривых разряда, а также произведена оценка емкости протестированных элементов питания.</p>
<p>Итак, сначала кривые разряда:</p>
<p><img src="/media/uploads/batteries.png" alt="Voltage over time" title="Voltage over time change" /></p>
<p>Результаты для меня оказались несколько неожиданными - хвалёные алкалиновые батарейки не такие уж и емкие, как их описывают производители! Даже несмотря на то, что напряжение на них сразу после начала разряда выше, чем на аккумуляторах (1.6...1.75В против 1.3...1.45В), под нагрузкой через некоторое время это различие нивелируется за счет того, что кривые разряда аккумуляторов имеют достаточно продолжительное плато, в пределах которого напряжение на них изменяется незначительно. Батарейки, напротив, такого плато практически не имеют, напряжение на них снижается в первом приближении линейно до уровня примерно 1В, после чего кривая разряда круто идет вниз.</p>
<p>Для получения оценки отданной в процессе разряда энергии (в джоулях), было произведен подсчет охватываемых кривыми разряда площадей на плоскости напряжение-время с последующим умножением на ток разряда. В результате получилась следующая табличка:</p>
<table>
<thead><tr>
<th>Элемент</th>
<th>Энергия, Дж</th>
<th>Емкость, мА·ч</th>
</tr>
</thead>
<tbody>
<tr>
<td>Panasonic Alkaline</td>
<td>6046.96</td>
<td>1442</td>
</tr>
<tr>
<td>Panasonic Everyday</td>
<td>6830.7</td>
<td>1637</td>
</tr>
<tr>
<td>Energizer Plus</td>
<td>7328.21</td>
<td>1798</td>
</tr>
<tr>
<td>Duracell Turbomax</td>
<td>7395.7</td>
<td>1773</td>
</tr>
<tr>
<td><em>Sanyo Eneloop</em></td>
<td>7513.83</td>
<td>1743</td>
</tr>
<tr>
<td>Panasonic Pro</td>
<td>7703.3</td>
<td>1853</td>
</tr>
<tr>
<td>Energizer Maximum</td>
<td>8310.02</td>
<td>2001</td>
</tr>
<tr>
<td><em>Varta Ready2Use</em></td>
<td>8310.19</td>
<td>2000</td>
</tr>
</tbody>
</table>
<p>В таблицу также добавлена измеренная зарядным устройством емкость (в мА·ч) для каждого из протестированных элементов питания.</p>
<p>Итак, из приведенных выше графика и таблицы можно сделать следующие выводы:</p>
<ul>
<li>Реальная емкость щелочных элементов не так уж и велика. Сравнимой с Ni-MH аккумуляторами емкостью обладают лишь самые дорогие батарейки.</li>
<li>Б<em>о</em>льшая емкость в мА·ч необязательно означает б<em>о</em>льшую запасаемую энергию (сравните результаты батарейки Duracell Turbomax и аккумулятора Sanyo Eneloop).</li>
<li>Батарейки имеет смысл использовать в устройствах с малым потреблением (например, пультах дистанционного управления, часах), где использование аккумуляторов экономически неоправданно, или в устройствах с расширенным температурным диапазоном (например, внешних датчиках метеостанций), так как в таких условиях аккумуляторы обладают повышенным саморазрядом.</li>
</ul>
<hr>
<h4>Немного о методике сбора и обработки экспериментальных данных</h4>
<p>Логгирование поступающих с зарядного устройства данных производилось модифицированным <a href="http://blog.dest-unreach.be/2012/01/29/imax-b6-charger-protocol-reverse-engineered">perl-скриптом от Niobos</a>. USB-порт был настроен в режим 9600 8N1 командой <code>stty</code>:</p>
<pre><code class="lang-bash">$ stty -F /dev/ttyUSB0 cs8 -cstopb -ixon raw speed 9600
</code></pre>
<p>Лог в текстовом виде сохранялся командой</p>
<pre><code class="lang-bash">$ b6_decode.pl < /dev/ttyUSB0 | tee out.txt
</code></pre>
<p>После окончания процесса разряда .txt-файлы преобразовывались в .dat-файлы с форматом, пригодном для построения графиков с помощью утилиты <a href="http://www.gnuplot.info/" title="gnuplot">gnuplot</a>. Оценка отданной при разряде элементами энергии производилась скриптом <code>estimate_energy.sh</code>.</p>
<p>Желающие поэкспериментировать самостоятельно могут скачать вышеупомянутые скрипты <a href="/files/batteries-vs-accus.zip">тут</a>.</p>
Захват HD-видеопотока с МКС в Linux
2014-05-21T14:33:56+04:00https://antonovich.me/2014/05/21/iss-stream-grab/Как известно, недавно NASA установила на МКС 4 видеокамеры высокого разрешения (1280х720) и организовала прямую трансляцию Земли. Зрелище это настолько завораживающее, что сразу возникает желание сохранить себе немного этой красоты. Сложность, однако, заключается в том, что сервис UStream, с помощью которого ведется трансляция, использует так называемую блочную (chunked) передачу потока,…
<p>Как известно, недавно NASA установила на МКС 4 видеокамеры высокого разрешения (1280х720) и организовала <a href="http://eol.jsc.nasa.gov/HDEV/">прямую трансляцию Земли</a>.</p>
<p><img src="http://antonovich.me/media/uploads/BmkYQzWCQAAk44b-580x328.jpg" alt="ISS HDEV Stream Screenshot" title="Пример картинки с камеры МКС" />
<a name="more"></a></p>
<p>Зрелище это настолько завораживающее, что сразу возникает желание сохранить себе немного этой красоты. Сложность, однако, заключается в том, что сервис UStream, с помощью которого ведется трансляция, использует так называемую блочную (chunked) передачу потока, в которой видеоданные передаются кусками по 5 секунд, при этом URL-адрес видеопотока постоянно меняется. Помимо этого, в трансляции довольно много пауз, связанных либо с тем, что станция находится на ночной стороне Земли (тогда экран полностью черный), либо с проблемами с каналом связи МКС-Земля, экран заполняется серым цветом. Также "сереет" экран на несколько секунд при переключении между камерами.</p>
<p>Таким образом, имеются две проблемы:</p>
<ol>
<li>Чем сохранять видеопоток с сервера UStream?</li>
<li>Как удалить из трансляции не несущие информации паузы?</li>
</ol>
<p>Для пользователей Linux данные проблемы решаются двумя консольными утилитами - <a href="https://github.com/chrippa/livestreamer">Livestreamer</a> и <a href="http://www.mplayerhq.hu/">mencoder</a>. Первая представляет собой универсальное решение для сохранения видеопотока с большого количества популярных сервисов видеотрансляции, а вторая предоставляет средства для обработки и перекодирования видеопотока, в том числе и удаления повторяющихся кадров.</p>
<p>Итак, первым делом устанавливаем самую свежую версию Livestreamer - на данный момент это 1.8.1:</p>
<p><code># pip install livestreamer</code></p>
<p>Последняя версия нужна потому, что более ранние версии некорректно работают с видеопотоками без звука, что и имеет место быть в случае трансляции с МКС.</p>
<p>Если установка прошла успешно, то команда</p>
<p><code>$ livestreamer http://www.ustream.tv/channel/iss-hdev-payload best</code></p>
<p>откроет видеопроигрыватель (по умолчанию VLC) с трансляцией в максимальном разрешении.</p>
<p>Для сохранения исходного видеопотока в файл можно воспользоваться ключом <code>-o</code>:</p>
<p><code>$ livestreamer http://www.ustream.tv/channel/iss-hdev-payload best -o iss-hdev.avi</code></p>
<p>И, наконец, сохранение видео в файл с удалением статичных кадров:</p>
<p><code>$ livestreamer http://www.ustream.tv/channel/iss-hdev-payload best -O | mencoder -ovc lavc -ofps 30 -noskip -vf decimate=-0:150 - -o iss-hdev-decimated.avi</code></p>
<p>Утилита mencoder входит в пакет MPlayer и может быть установлена штатным пакетным менеджером дистрибутива Linux. Для удаления повторяющихся кадров из исходного видеопотока используется режим пороговой децимации (<code>-vf decimate=-0:150</code>), где 150 - это минимальное количество отличий между кадрами, при котором они будут признаны различающимися. Данное значение подобрано опытным путем, при меньших значениях больше вероятность попадания в выходной файл "ночных" кадров из-за присутствующих в них шумов, а при бОльших значениях возможны пропуски кадров.</p>
<p>Кстати, на сайте <a href="http://heavens-above.com/">Heavens Above</a> можно получить расписание для пролетов МКС над выбранной точкой Земли. В свою очередь, данное расписание можно использовать для съемки интересующих мест, запуская приведенную выше команду и завершая ее через <code>kill</code> в нужное время с помощью планировщика <code>at</code>. Надо только помнить, что по умолчанию в расписании скрываются дневные витки, ибо сайт изначально рассчитан на наблюдение МКС с Земли, а не наоборот. :) Для включения отображения дневных витков нужно перевести переключатель "Показать пролеты" в состояние "Все".</p>
Здравствуй, мир!
2014-05-18T21:03:05+04:00https://antonovich.me/2014/05/18/helloworld/У программистов со времен K&R есть традиция - освоение нового языка начинать с программы, выводящей в консоль "Hello, world!" Персональный блог, конечно, не язык программирования, но это не повод нарушать традицию. В общем, здравствуй, мир! Отныне здесь буду делиться моими впечатлениями обо всем, что меня интересует в мире технологий, ну…
<p>У программистов <a href="http://ru.wikipedia.org/wiki/Hello,_world!">со времен K&R</a> есть традиция - освоение нового языка начинать с программы, выводящей в консоль "Hello, world!" Персональный блог, конечно, не язык программирования, но это не повод нарушать традицию. В общем, здравствуй, мир! Отныне здесь буду делиться <a href="/about/">моими</a> впечатлениями обо всем, что меня интересует в мире технологий, ну и вообще в мире.<a name="more"></a></p>