0
<< предыдущая заметкаследующая заметка >>
23 сентября 2023
Простейший Чипльдуктор

Почти сутки не мог взять себя в руки и приступить к чистке ведер грибов, что принес из леса. А всё потому, что у меня не было спокойного радио. Пришлось сперва его снова настроить. Снова — потому что чипльдуктор у меня был, но убилась флешка. Вы спросите: чего я не делал бэкап? Отвечу: она и убилась, когда я ее вынул и пытался сделать бэкап — не все кардридеры одинаково полезны.

Как сделать простейший Чипльдуктор?

Чипльдуктором называется простой онлайн-радиоприемник, который торчит в розетке и позволяет включать-выключать радио Чипльдук или любую другую онлайн-радиостанцию, а также регулировать громкость. В качестве управления мы будем использовать кнопки на звуковой карте. Вам понадобится:

1. Дешевый одноплатный чип с Линуксом — Raspberry PI, Orange, Banana или что-то похожее.
2. Звуковая карта USB c кнопками.
3. USB-колонки.

Звуковая карта с кнопками на Aliexpress стоит 70 руб. Колонки я уже несколько лет всем настоятельно советую ОКЛИК OK-330. За просто смешные 300 руб в любом Ашане или компьютерном ларьке вы получаете охрененного качества и громкости звук — но только при условии хорошего источника питания. Впрочем Raspberry PI дает вполне хорошее питание в своем USB-разъеме.

Итак, краткая инструкция (для себя писал), что делать с Raspberry.

Установка системы Raspberry Pi OS

Этот процесс достаточно описан на raspberrypi.com - ставим Raspberry Pi OS (то, что раньше называлось Raspbian). Есть какие-то официальные инсталляторы на флешкарту, но я всегда делал через dd. Не знаю, как сейчас, еще полгода назад была пара граблей, связанных с доступом ssh, дефолтным паролем и выключенным изначально диапазоном высокоскоростного вайфая Wifi5.

подробнее о граблях
1) Включаем из коробки ssh: в партицию boot надо дописать не только пустой файл с именем ssh чтобы включился доступ по ssh.

2) Туда же положить файл wpa_supplicant.conf чтобы сразу поднялся файфай сетки LLEO с паролем ytgfhjkm

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=RU

network={

ssid="LLEO"

psk="ytgfhjkm"

key_mgmt=WPA-PSK

proto=WPA2
}

3) В последней версии тамошней системы нетрадиционно ориентированные люди убрали "ради вашей безопасности" дефолтный логин/пароль. Блять, можно подумать, кому-то удалось достучаться к чужой Raspberry по ssh сквозь NAT домашнего роутера. А если хакер уже во внутренней домашней сети или каким-то образом Raspberry получила белый IP в обход роутера, то что, такой админ не в состоянии сам уследить за паролями? Серьезно? Там ещё была какая-то телега, типа законодательство разных стран теперь запрещает выпускать устройства с дефолтными паролями. Грета блядь Тумберг и философский камень. Для установки пароля предлагается запустить на Raspberry какой-то инсталляционный софт, дебилы блять, а ничего, что у меня нет шнура её к монитору подключить? Короче, решение такое: надо сгенерировать хэш пароля 'raspberry' echo 'raspberry' | openssl passwd -6 -stdin и, добавив перед ним логин с двоеточкой, записать готовую строку в userconf.txt на той же boot-партиции:

pi:$6$ss0.VZ5qwrBCLimA$vO8dlrAjiPUSa9weEbu6VLpwlUe8SqpPUA0SawgfPw61QkePS1lGQnv2hZzXa5Wyzvj4DyXdHDlLk7/Ek7Ju/1

И вот тогда после установки системы на флешку можно логиниться по ssh в режиме headless с логином pi и паролем raspberry, не подключая дисплей и не открывая весь этот графический ужас.

4) Последняя небольшая вишенка говна на малиновом пироге: мало кто знает, что Raspberry (например, 4) умеет Wifi-5. Но диапазон у него не включен. И включается он вручную весьма нетрадиционным заклинанием, как это модно у линуксоидов:

echo "Так было:"
sudo iw reg get
echo "Теперь включаем диапазон:"
sudo iw reg set US
echo "Появились новые диапазоны:"
sudo iw reg get

Простейший способ его всякий раз включить при старте системы:

sudo crontab -e

И прописать там в конце:
@reboot iw reg set US

Обживаем систему

Итак, система настроена. Поставить полезные и любимые утилитки:

sudo apt install mc catdoc docx2txt enca figlet sshfs mp3info make gcc curl whois libjpeg-progs davfs2

Поставить PHP (он нам пригодится для key.php):

sudo apt install php

Себе я ставлю заодно сервер и свой движок — nginx, mysql, memcache, но...

но это вам не нужно
php-mbstring php-curl php-mysqli libnginx-mod-rtmp mariadb-server mariadb-client php-fpm memcached php-memcache

Желательно везде вместо php-... писать php8.0-..., потому что нынешний Raspberrian ставит по умолчанию php 7.4. Про memcache: сервер называется memcached, а плагин php-memcache, не php-memcached! Потому что такой есть тоже, но тяжелый, медленный и ненужный. Не забываем затем нахер apache2, который поставил php:

apt purge apache2

Настройка MYSQL, чтоб два раза не вставать

sudo mysql -u root

CREATE DATABASE my_base DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;

GRANT ALL PRIVILEGES ON my_base.* TO lleo@localhost IDENTIFIED BY 'MyPassword' WITH GRANT OPTION;

SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_TRANS_TABLES', '');
SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_ALL_TABLES', '');
FLUSH PRIVILEGES;

Установить пароль:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'mypassword';

Добавить в /etc/mysql/my.cnf:

[mysqld]
sql_mode=NO_ENGINE_SUBSTITUTION

Перезапустить, чтобы сработало.

nginx

Простейший конфиг nginx для php /etc/nginx/sites-enabled/site.conf

server {

listen 80 default_server;

listen [::]:80 default_server;

server_name _;

index index.html index.htm index.php;

client_max_body_size 500M;

location / {

root /home/www;

try_files $uri /index.php?$args;

location ~ \.php$ {

fastcgi_split_path_info ^(.+\.php)(/.+)$;

try_files $fastcgi_script_name =404;

fastcgi_pass unix:/var/run/php/php-fpm.sock;

fastcgi_index index.php;

include fastcgi_params;

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

client_max_body_size 500m;

client_body_buffer_size 128k;

proxy_connect_timeout 90;

proxy_send_timeout 90;

proxy_read_timeout 90;

proxy_buffer_size 4k;

proxy_buffers 4 32k;

proxy_busy_buffers_size 64k;

proxy_temp_file_write_size 64k;

}

}
}

Но php толком не заработает, пока в начало файла /etc/nginx/fastcgi_params не добавить строчку:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Но всё это, напоминаю, только если вам нужен ещё и сервер. Для чипльдуктора не нужен.

Не забываем установить локаль ru_RU.UTF-8, задолбало:

sudo dpkg-reconfigure locales


ОБЖИВАЕМ ЗВУКОВУЮ КАРТУ

Звуковая карта USB обычно видится сразу и драйверов не требует - если конечно вы не пытаетесь это делать на своем старом роутере с USB-разъемом, там звуковая карта сильно удивляет систему и обычно нужны драйвера, но их тоже можно найти. Первым делом лучше всего дать права пользователю, пригодится:

usermod -aG audio lleo

Далее нам нужно узнать про нашу звуковую карту ровно три вещи. Ее код на USB-шине, ее номер в системе и ее имя устройства громкости. Начинаем с кода, смотрим, какие USB-девайсы у нас вообще есть в USB:

lsusb

Raspberry покажет что-то типа такого:

типа такого
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 8086:0808 Intel Corp. USB PnP Sound Device
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Собственно 8086:0808 - это и есть номер нашей USB-звуковушки с кнопками. Это то, как карту видит USB. Запомнили его. Теперь смотрим, какие видит аудиоустройства система:

sudo aplay -l

Выдает кучу мусора (еще есть вывод звука в HDMI, например), но я вижу свою USB-Sound как устройство номер 3, запомним этот номер 3:

куча мусора
<?php...card 3:Device[USB PnP Sound Device],device 0:USB Audio[USB Audio]Subdevices:1/1

Subdevice#0: subdevice #0

Наконец, смотрим, что в карте номер 3 видит микшер:

amixer -c 3

у меня такое
<?php
Simple mixer control
'Speaker',0

Capabilities:pvolume pswitch pswitch-joined

Playback channels:Front Left-Front Right

Limits:Playback 0-151

Mono:Front Left:Playback 60[40%] [-17.13dB] [on]Front Right:Playback 60[40%] [-17.13dB] [on]
...

Из чего заключаю, что устройство регулировки громкости сегодня называется 'Speaker'. Сука, при прошлой инсталляции системы ты у меня называлось 'PCM', а еще раньше 'Headphone'! Итак, запомнили: номер USB 8086:0808, карта номер 3, устройство громкости Speaker.

Осталось лишь поставить самую простую и легкую утилитку для воспроизведения mp3:

sudo apt install mpg123

Итак, звуковая система настроена.

УПРАВЛЕНИЕ ПРОЦЕССАМИ ОТ КНОПОК

Как сделать управление от кнопок звуковой карты? Я использую две софтинки. Демон keyboardoid, который я написал на C, при нажатии кнопок он вызовет key.php, который уже разбирается, что именно было нажато и какие действия выполнить. Итак, набираем gcc keyboardoid.c и получившийся a.out переименовываем в keyboardoid для ясности. Демон готов:

keyboardoid.c
<?php#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <dirent.h>
#include <linux/input.h>

#define VENDORID 0x1c4f
#define PRODUCTID 0x0002int16_t fd= -1;char path[300];int init_keyboard(unsigned int VENDOR,unsigned int PRODUCT) {fd=-1;int count=0;struct dirent**files=NULL;struct input_id id;count=scandir("/dev/input",&files,0,0)-1;

while(count>=0) {

if (fd==-1&&strncmp(files[count]->d_name,"event",5)==0) {sprintf(path,"/dev/input/%s",files[count]->d_name);fd=open(path,O_RDONLY);

if(fd>=0) {

if(ioctl(fd,EVIOCGID,(void*)&id)<0)perror("ioctl EVIOCGID");

else {

if(id.vendor==VENDOR&&id.product==PRODUCT&&id.bustype==BUS_USB) {printf("Device found at %s ",path); }

else {close(fd);fd= -1;

}

}

} else {fprintf(stderr,"Error opening %s:\t",path);perror("");

}

}free(files[count--]);

}free(files);int grab=1;

if(fd>=0)ioctl(fd,EVIOCGRAB,&grab);

else {fprintf(stderr,"Device not found or access denied. ");

returnEXIT_FAILURE;

}

return0;
}
char remap_codes(int scancode){char r=' ';

switch(scancode) {// NumLock = off + N // NumLock = oncase0x45:r=0; break;// 000 -> 0 0 0 // 000 -> --- NO ---case0x52:r='0'; break; case0x6E:r='0'; break;

case0x4f:r='1'; break; case0x6B:r='1'; break;

case0x50:r='2'; break; case0x6C:r='2'; break;

case0x51:r='3'; break; case0x6D:r='3'; break;

case0x4b:r='4'; break; case0x69:r='4'; break;

case0x4c:r='5'; break;//--- NO ---case0x4d:r='6'; break; case0x6A:r='6'; break;

case0x47:r='7'; break; case0x66:r='7'; break;

case0x48:r='8'; break; case0x67:r='8'; break;

case0x49:r='9'; break; case0x68:r='9'; break;

case0x53:r='.'; break; case0x6F:r='.'; break;

case0x60:r='E'; break;// Entercase0x62:r='C'; break;// /case0x37:r='Z'; break;// *case0x4a:r='M'; break;// —case0x4e:r='P'; break;// +case0x0E:r='B'; break;// Backspacedefault:printf("Scancode:0x%X — [%c]\n",scancode,(unsigned char)scancode);r=(unsigned char)scancode;

}

returnr;
}
char read_keyboard() {struct input_event ev;ssize_t n;

while(true) {n=read(fd, &ev,sizeof(struct input_event));

if(n== (ssize_t)-1) {

if (errno==EINTR) continue;

else break;

} else if(n!=sizeof ev) {errno=EIO;

break;

}

if(ev.type==EV_KEY&&ev.value==EV_KEY) returnremap_codes(ev.code);

}

return'!';
}
int connect_keyboard(int argc,char*arg) {char name[256] ="Unknown";

if(argc>1) {// пробуем отсканировать VEND:PRODunsigned int VEND=VENDORID,PROD=PRODUCTID;

if(2==sscanf(arg,"%x:%x",&VEND,&PROD)) {printf("\nConnecting to device %04x:%04x... ",VEND,PROD);

if(init_keyboard(VEND,PROD)!=0) {printf("FAILED\n"); return(1); }printf("OK\n");

} else {// пробуем найти pathsnprintf(path,200,"%s",arg);printf("\nConnecting to device %s ... ",path);

if((fd=open(path,O_RDONLY)) == -1) {printf("FAILED\n"); return(1); }printf("OK\n");

}

} else {// откроем дефолтный девайсprintf("\nConnecting to default device %04x:%04x... ",VENDORID,PRODUCTID);

if(init_keyboard(VENDORID,PRODUCTID)!=0) {printf("FAILED\n"); return(1); }printf("OK\n");

}ioctl(fd,EVIOCGNAME(sizeof(name)),name);fprintf(stderr,"Reading from: %s (%s)\n",path,name);

return0;
}
int main(int argc,char*argv[]) {printf("Runnung... (Example: \"sudo ./keyboardnoid 1C4F:0002 \"echo \\\"%%c\\\" >> /tmp/111.txt\")");char r=0;char action[300]="notify-send -t 300 \"KeyCode: %c\"";

if(argc>2)snprintf(action,1024,"%s",argv[2]);// скопировать строку акции

// коннектимсяwhile(1) { if(0==connect_keyboard(argc,argv[1])) break;sleep(2); }// 1 секwhile(true) {

if(!(r=read_keyboard())) continue;// printf("Scancode:0x%X — [%c]\n",r,(unsigned char)r);if(r=='!') {// обрыв связиsleep(1);close(fd);printf("port_disconnected\n");snprintf(path,100,action,'?');system(path);// подать сигналwhile(true) { if(0==connect_keyboard(argc,argv[1])) break;sleep(2); }// 10 секsnprintf(path,100,action,'!');system(path);// подать сигнал что все в порядкеcontinue;

}

if(r==0x0D||r==0x0A||r=='"'||r=='\'')r='@';// не надо нам энтеров и кавычек!snprintf(path,100,action,r);printf("ACTION: `%s`\n",path);system(path);

}
}

Демон отлавливает кнопки от любой клавиатуры - звуковая карта тоже создает устройство «клавиатура». Он крохотный, написан один раз навсегда и просто висит в памяти, занимая там самый мизер, какой вообще может занимать софт на Линуксе (я его для совсем бедных памятью линуксов изначально делал), а уж при нажатии кнопки вызывает уже специальный скрипт для разбора, чего с этой кнопкой делать. При запуске ему нужно указать номер USB-устройства - упаси вас боже перехватить управление своей собственной клавиатуры, как у меня однажды было при настройке, пришлось заходить по ssh и вырубать демона. Вторым аргументом демону передаем в кавычках строку произвольную запуска любой процедуры, где вместо %c демон передаст полученный код или последовательность. У меня запускается демон так: keyboardoid 8086:0808 "/usr/bin/nohup /home/work/audio/key.php \"%c\" А передает скан код в key.php, который будет запускать радио и регулировать громкость, я его набросал таким (впишите в первых строках свои настройки аудиокарты):

key.php

<?php#!/usr/bin/php<?php// настройки аудиокарты$hw='3';// 0 1$nm="Speaker";// PCM Speaker Headphoneif($argc!=2) die("no keycodes\n");$c=$argv[1];$s=(strlen($c)>1?$c:sprintf("0x%02X",ord($c)) );

echo"===> [$c] ".$s."\n";

if($s=="0xA5")play("http://radio.4duk.ru/4duk128.mp3");// верхняя — Чипльдукelseif($s=="0xA4")stop();// средняя — СТОПelseif($s=="0xA3")play("http://hermitage.hostingradio.ru/hermitage128.mp3");// нижняя — Эрмитажelseif($s=="0x72")volume(-0.1);// — Громкость убавитьelseif($s=="0x73")volume(+0.1);// — Громкость добавитьelseif($s=="0x71") { }// пока не придумал задачу для этой кнопкиelse die("==> Unknown CODE\n");

functionvolume($add) { global$hw,$nm;$file="/tmp/mixer_volume.txt";$v=intval(false===($v=@file_get_contents($file)) ?50:$v);

if($add!=0) {$v=$v+$add*(max($v,10));$v=ceil($v);$v=min($v,100);$v=max($v,1);file_put_contents($file,$v);chmod($file,0666);

}exec("/usr/bin/amixer -c ".$hw." sset ".$nm." ".$v."%");
}

functionplay($s) { global$hw;stop();exec("/usr/bin/nohup /usr/bin/mpg123 -q -a hw:".$hw." \"".$s."\" > /dev/null 2>&1 </dev/null &");
}

functionstop() {exec("/usr/bin/killall mpg123");
}
?>

Осталось проверить пути файлов и вписать демона в старт системы. Я не стал париться, а просто вписал в sudo crontab -e две строки при старте - включение диапазона WiFi5 и запуск моего файлика /home/work/onstart.sh:

@reboot iw reg set US
@reboot /home/work/onstart.sh

Там я при старте системы негромко (40%) проигрываю сэмпл - полезно знать, что система готова или в квартире был сбой энергии все устройства перегрузились. И запускаю демона:

#!/bin/sh

echo "Restart: "`date` >> /home/work/onstart.log

/usr/bin/amixer -c 3 sset Speaker 40%
/usr/bin/mpg123 -a hw:3 "/home/work/audio/sample/eto_ja.mp3" 2>&1 >/dev/null

/home/work/audio/keyboardoid 8086:0808 "/usr/bin/nohup /home/work/audio/key.php \"%c\""

Вот и всё. Как видно в скрипте, на верхнюю кнопку я прописал свое любимое радио Чипльдук http://radio.4duk.ru/4duk128.mp3

На нижнюю - спокойное питерское радио Эрмитаж http://hermitage.hostingradio.ru/hermitage128.mp3

Штуку с колонками положил на холодильник, там у меня и розетка удобная, и не мешает никому, и до кнопок дотянуться удобно. Вот теперь можно чистить грибы!

<< предыдущая заметка следующая заметка >>
пожаловаться на эту публикацию администрации портала
архив понравившихся мне ссылок

Комментарии к этой заметке скрываются - они будут видны только вам и мне.

Оставить комментарий