Создаёт web-интерфейс для управления и скетч для ардуино
Будучи не понаслышке знакомым с трудностями, которые испытывают строители «умных домов», решил запилить конструктор, который всё сделает сам, включая скетч для ардуины, и сервер HomestD для обмена данными. Подключение переферии к ардуине остаётся на совести хозяина, однако в скетче написано, что и куда.
Пользователю останется только скачать архив с файлами, распаковать его на целевом устройстве и загрузить в ардуину готовый скетч. Установка каких-либо дополнительных пакетов и серверов не требуется.
HomestD можно использовать на любом компьютере работающем под управлением или на роутере с прошивкой OpenWrt.
Для работы на роутере не потребуются дополнительные накопители (флешка, sd-карта).
Подключение ардуины
… к компьютеру не должно вызвать затруднений, а о том, как подключить ардуину к роутеру (по USB или UARTу) можно прочесть в сети.
При подключении к UARTу никаких пакетов устанавливать не нужно, только подпаяться к контактам и отредактировать файл /etc/inittab.
Пример для TL-MR3020:
nano /etc/inittab
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
#ttyATH0::askfirst:/bin/ash --login
Внешний вид
Идея web-интерфейса достаточно проста и минималистична.
Главный экран интерфейса.
Если кому-то не по душе тёмные цвета, то подправить css-файл не составит труда.
На главном экране расположены кнопки с названиями помещений, нажатие на которые открывает панель с кнопками управления этим помещением.
Здесь могут располагаться несколько кнопок (D2, D3 и т.д.) для включения чего-либо с возвратом состояния.
Несколько кнопок для отправки сигнала (SENTSIG1 и т.д), не требующего подтверждения.
И несколько полей (INDATA1 и т.д) для приёма каких-либо данных/сигналов.
Крестит закрывает панель.
Названия кнопок можно изменять по своему усмотрению и менять местами.
Пример:
Кнопка Info скрывает панель с информацией о работоспособности системы.
Надпись Connect! говорит о том, что всё хорошо, а Count update: — счётчик запросов (браузер с определённым интервалом запрашивает у ардуины данные). Интервал можно менять.
Если что-то произойдёт, то на экране появится сообщение ERROR, а в Info будет описана ошибка.
Алгоритм работы описан в конце.
Конструктор
Я записал коротенькое видео по работе с конструктором:
Конструктор прост и понятен. Откройте конструктор в соседней вкладке:
В браузере должны быть включены cookie.
Конструктор работает не на всех мобильных устройствах, то есть «умный дом» будет работать на чём угодно, а вот конструировать надо на обычном компе.
Первая страница:
Здесь выбирается количество помещений (максимум 10). Предположим, что будет два помещения (прихожая и кухня), тогда выберите 2 и нажмите «Далее».
На следующей странице нужно придумать название «умного дома» (будет написано на вкладке браузера) и вписать его в поле Название страницы.
В поля Адрес сервера и Порт сервера ничего писать не нужно (сделано на будущее).
Названия помещений у нас уже придуманы (прихожая и кухня), вписываем их и нажимаем кнопку «Далее».
В названии вашего умного дома и названиях помещений, можно использовать буквы, цифры, пробел и нижнее_подчёркивание.
В дальнейшем Вы можете это исправить в файле index.html.
Здесь Вы увидите главный экран своего будущего интерфейса:
Нажмите «Прихожая»…
Выберите, сколько вы хотите кнопок для включения чего-либо с возвратом статуса (Количество кнопок вкл/откл), кнопок для отправки сигнала не требующего подтверждения (Количество кнопок отправки сигнала) и полей для приёма каких-либо данных (Количество полей для приёма информации).
Для примера выбрано по одной кнопке (максимум по пять).
Теперь закройте панель кнопкой Х, проделайте то же самое с «Кухней» и нажмите кнопку «Далее»…
Появится главный экран с кнопкой «Скачать архив».
Можно открыть «Прихожую» или «Кухню» и посмотреть, что получилось…
Поля для приёма данных заполняются при появлении сигнала.
На этом работа с конструктором закончена, нажмите и переходите к следующей части.
HomestD
Распаковав архив, у Вас появится папка — mydomXXXXXXXXXX, переименуйте её так, чтоб получилось mydom, и перейдите в неё.
Переименуйте файл indexXXXXXXXXX.html в index.html, а файл domXXXXXXXXX.ino переместите в папку со скетчами.
В папке mydom останутся файлы index.html, jquery.js и style.css.
Откройте файл index.html и в двенадцатой строчке — var flagobnov = 0, переправьте ноль на единицу — var flagobnov = 1.
Дополнительные пояснения в конце.
Скачайте и установите библиотеку CyberLib, а затем загрузите скетч (domXXXXXXXXX.ino) в ардуину.
И наконец остаётся последний шаг — скачать программу homestd для вашего устройства, переименовать (для удобства) homestdXXX в homestd и скопировать в папку mydom.
В итоге содержимое папки mydom будет выглядеть так: homestd, index.html, jquery.js и style.css.
- Для обычного компьютера (linux).
- Для OSX.
- Для RaspberryPi.
- Для роутера MR3020 (Atheros).
- Для роутера ASUS WL500gp2 (Broadcom).
HomestD — это web-сервер и сервер для ардуины. Назначение — это обмен данными между web-клиентом (браузер) и ардуиной. То есть homestd принимает запросы от клиента по протоколу TCP (протокол UDP будет добавлен в следующей версии) и передаёт их ардуине, и одновременно принимает данные от ардуины, которые забирает web-клиент.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <time.h>
#include <pthread.h>
char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n";
char response_css[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/css; charset=UTF-8\r\n\r\n";
char response_js[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/js; charset=UTF-8\r\n\r\n";
char response_text[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/text; charset=UTF-8\r\n\r\n";
char response_403[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html><html><head><title>403</title>"
"<style>body { background-color: #312f2f }"
"h1 { font-size:4cm; text-align: center; color: #666;}</style></head>"
"<body><h1>403</h1></body></html>\r\n";
#define BUFSIZE 1024
#define ARRAY_SIZE 90000
#define BREADSIZE 512
char send1_array[ARRAY_SIZE] = {0,};
char send2_array[ARRAY_SIZE] = {0,};
char patch_to_dir[64] = {0,};
char fpfile[64] = {0,};
char buffer[BUFSIZE] = {0,};
int count_simvol = 0;
char device[32]={0,};
unsigned long int speedport = 0;
unsigned int PORTW = 0;
char bRead[BREADSIZE] = {0,};
int wr_fdb = 0;
char str_iz_file[BREADSIZE] = {0,};
int counterr = 0;
int count_reciv = 0;
int fd;
void error_log(char *my_error)
{
memset(fpfile, 0, 64 * sizeof(char));
snprintf(fpfile, (int)strlen(patch_to_dir) + (int)strlen("Error.log "), "%s%s", patch_to_dir, "Error.log");
time_t t;
time(&t);
FILE *f;
f = fopen(fpfile, "a");
if(f == NULL)
{
printf("Error open Error.log\n");
exit(0);
}
fprintf(f, "%s", ctime( &t));
fprintf(f, "%s\n\n", my_error);
printf("%s\nError write to %sError.log.\n", my_error, patch_to_dir);
fclose(f);
exit(0);
}
void warning_access_log(char *war_ac)
{
memset(fpfile, 0, 64 * sizeof(char));
snprintf(fpfile, (int)strlen(patch_to_dir) + (int)strlen("Warning_Access.log "), "%s%s", patch_to_dir, "Warning_Access.log");
time_t t;
time(&t);
FILE *f;
f = fopen(fpfile, "a");
fprintf(f, "%s", ctime( &t));
fprintf(f, "%s\n\n", war_ac);
printf("%s\nWrite to %sAccess_Warning.log.\n\n\n", war_ac, patch_to_dir);
fclose(f);
}
void read_in_file(char *name_file)
{
count_simvol = 0;
memset(send1_array, 0, ARRAY_SIZE * sizeof(char));
memset(fpfile, 0, 64 * sizeof(char));
snprintf(fpfile, (int)strlen(patch_to_dir) + (int)strlen(name_file) + 1, "%s%s", patch_to_dir, name_file);
FILE *file;
file = fopen(fpfile,"r");
if(file == NULL) error_log("Error open file");
int ch;
while(ch = getc(file), ch != EOF)
{
send1_array[count_simvol] = (char) ch;
count_simvol++;
if(count_simvol == ARRAY_SIZE - 2) break;
}
fclose(file);
}
void error_to_filebd(char *db_error)
{
if(wr_fdb == 1)
{
memset(fpfile, 0, 64 * sizeof(char));
snprintf(fpfile, (int)strlen(patch_to_dir) + (int)strlen("file.db "), "%s%s", patch_to_dir, "file.db");
FILE *f;
f = fopen(fpfile, "w");
fprintf(f, "%s", db_error);
fclose(f);
printf("Write to file.db - %s\n", db_error);
}
memset(str_iz_file, 0, BREADSIZE);
strncpy(str_iz_file, db_error, 13);
}
void * thread_func()
{
int i = 0;
int err_count1 = 0;
for(;;)
{
int bytes = 0;
memset(bRead, 0, BREADSIZE * sizeof(char));
counterr = 0;
if((bytes = read(fd, bRead, BREADSIZE - 1)) == -1)
{
warning_access_log("Error_Read_from_Arduino.");
}
for(i = 0; i <= bytes; i++)
{
if(bRead[i] == '\n') break;
}
if(bRead[0] == 'A' && bRead[strlen(bRead)-2] == 'Z')
{
err_count1 = 0;
}
else
{
tcflush(fd, TCIFLUSH);
err_count1++;
if(err_count1 > 5)
{
err_count1 = 0;
error_to_filebd("NOT A_Z_SIM \n");
}
printf("Not_A-Z_bRead: %s\n\n", bRead);
continue;
}
if(strcmp(bRead, str_iz_file)==0)
{
printf("StrOK:%s\n\n", bRead);
continue;
}
else
{
if(wr_fdb == 1)
{
char fpfile_2[64] = {0,};
snprintf(fpfile_2, (int)strlen("file.db ") + (int)strlen(patch_to_dir), "%s%s", patch_to_dir, "file.db");
FILE *f;
f = fopen(fpfile_2, "w");
if(f == 0) warning_access_log("NOT open file.db Arduina.");
fprintf(f, "%s", bRead);
fclose(f);
}
memcpy(str_iz_file, bRead, BREADSIZE);
printf("NotStr:%s\n\n", bRead);
}
} // END (while) ardu
return 0;
} // END thread_func
void * thread2_func()
{
for(;;)
{
sleep(1);
counterr++;
if(counterr > 2) error_to_filebd("NOT CONNECT \n");
}
return 0;
}
void open_port()
{
fd = open(device, O_RDWR | O_NOCTTY);
if(fd == -1) error_log("Error - NOT open /dev/ttyX");
else
{
struct termios options;
tcgetattr(fd, &options);
switch(speedport)
{
case 4800:
cfsetispeed(&options, B4800);
cfsetospeed(&options, B4800);
break;
case 9600:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
break;
case 19200:
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
break;
case 38400:
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
break;
case 57600:
cfsetispeed(&options, B57600);
cfsetospeed(&options, B57600);
break;
case 115200:
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
break;
default:
error_log("Error - Speed_port");
break;
}
options.c_cflag |= (CLOCAL | CREAD);
options.c_iflag = IGNCR;
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 1;
options.c_lflag = ICANON;
options.c_oflag = 0;
options.c_oflag &= ~OPOST;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &options);
}
}
int main(int argc, char *argv[])
{
if(argc != 6) error_log("Not argumets.");
strncpy(device, argv[1], 31);
speedport = strtoul(argv[2], NULL, 0);
PORTW = strtoul(argv[3], NULL, 0);
strncpy(patch_to_dir, argv[4], 63);
wr_fdb = atoi(argv[5]);
open_port();
sleep(2);
tcflush(fd, TCIFLUSH);
warning_access_log("START");
int pt1 = 1;
pthread_t ardu_thread;
int result = pthread_create(&ardu_thread, NULL, &thread_func, &pt1);
if(result != 0) error_log("Error - creating thread.");
int pt2 = 1;
pthread_t counterr_thread;
int result2 = pthread_create(&counterr_thread, NULL, &thread2_func, &pt2);
if(result2 != 0) error_log("Error - creating thread2.");
int one = 1, client_fd;
struct sockaddr_in svr_addr, cli_addr;
socklen_t sin_len = sizeof(cli_addr);
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) error_log("Not socket.");
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
svr_addr.sin_family = AF_INET;
svr_addr.sin_addr.s_addr = INADDR_ANY;
svr_addr.sin_port = htons(PORTW);
if(bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1)
{
close(sock);
error_log("Error bind.");
}
if(listen(sock, 10) == -1)
{
close(sock);
error_log("Error listen.");
}
int dev_echo = strlen(device) + 18;
char otvet[BREADSIZE] = {0,};
char to_Ardu[64] = {0,};
for(;;)
{
client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
if(client_fd == -1) continue;
memset(buffer, 0, BUFSIZE);
read(client_fd, buffer, BUFSIZE - 1);
if((strstr(buffer, "file.db")) != NULL)
{
memset(otvet, 0, BREADSIZE);
int c_sim = 0;
for(c_sim = 0; c_sim <= BREADSIZE - 1; c_sim++)
{
if(str_iz_file[c_sim] == '\n') break;
}
snprintf(otvet, 59 + c_sim, "%s%s", response_text, str_iz_file);
write(client_fd, otvet, c_sim + 58);
close(client_fd);
printf("Trans otvet.\n");
}
else if((strstr(buffer, "comanda")) != NULL)
{
memset(to_Ardu, 0, 64);
snprintf(to_Ardu, dev_echo, "echo 'Y+=Z%c%c%c' > %s", buffer[13], buffer[14], buffer[15], device);
system(to_Ardu);
close(client_fd);
warning_access_log(buffer);
printf("To Ardu: %s\n", to_Ardu);
}
else if((strstr(buffer, "GET / ")) != NULL)
{
memset(send2_array, 0, ARRAY_SIZE * sizeof(char));
read_in_file("index.html");
int len_ara = count_simvol + (int)strlen(response) + 1;
snprintf(send2_array, len_ara, "%s%s", response, send1_array);
write(client_fd, send2_array, count_simvol + 59);
close(client_fd);
warning_access_log(buffer);
printf("Trans index.html.\n\n");
}
else if((strstr(buffer, "style.css")) != NULL)
{
memset(send2_array, 0, ARRAY_SIZE * sizeof(char));
read_in_file("style.css");
int len_ara = count_simvol + (int)strlen(response_css) + 1;
snprintf(send2_array, len_ara, "%s%s", response_css, send1_array);
write(client_fd, send2_array, count_simvol + 58);
close(client_fd);
warning_access_log(buffer);
printf("Trans style.css.\n\n");
}
else if((strstr(buffer, "jquery.js")) != NULL)
{
memset(send2_array, 0, ARRAY_SIZE * sizeof(char));
read_in_file("jquery.js");
int len_ara = count_simvol + (int)strlen(response_js) + 1;
snprintf(send2_array, len_ara, "%s%s", response_js, send1_array);
write(client_fd, send2_array, count_simvol + 57);
close(client_fd);
warning_access_log(buffer);
printf("Trans jquery.js.\n\n");
}
else
{
write(client_fd, response_403, sizeof(response_403) - 1);
close(client_fd);
warning_access_log(buffer);
}
}
} //END main
// gcc -Wall -Wextra -Werror homestd.c -o homestd -lpthread
// ./homestd /dev/ttyUSB0 57600 80 /var/www/vse/tpl/mydom2/ 0
// make package/homestd/compile V=s
Подключаем ардуину, копируем папку mydom в любое удобное место на целевом устройстве, например в корень (путь будет выглядеть так — /mydom) и запускаем командой:
sudo /mydom/homestd /dev/ttyUSB0 57600 80 /mydom/ 0
На роутере без sudo.
Первый параметр — /dev/ttyUSB0, путь к ардуине. Узнать можно так:
ls /dev/tty*
Второй параметр — 57600, скорость «сом»-порта.
Третий параметр — TCP порт. Порт можно указать любой, однако если у Вас больше нет никаких серверов занимающих стандартный (80) порт, то укажите его. Если система ставится на роутер, то скорее всего там есть «web-морда» и 80-ый порт будет занят. Тогда укажите что-нибудь другое, например 82 (заходить в «умный дом» так — адрес:82).
Четвёртый параметр — путь к папке mydom (слеш / в конце обязателен).
Пятый параметр — может быть 0 или 1. Если указать 1, тогда в папке mydom будет создаваться текстовый файл file.db, в который будут записываться данные полученные от ардуины. Это сделано для того, чтоб можно было забирать эти данные и заносить куда-либо.
Все действия homestd, сопровождаются записью в файл Access_Warning.log
Ошибки записываются в файл Error.log
Если всё заработало, то переходите в браузер и начинайте пользоваться. Если что-то не так, то приступайте к поиску ошибок и пишите в комментах…
Пояснения
К скетчу…
Задача ардуины — принимать команды от сервера, выполнять действие и через каждые 440мс отправлять статус/информацию обратно.
Для кнопок для включения чего-либо формируются флаги (d2, d3...) принимающие значения 1 или 0, эти значения присваиваются им в функции «switch(cod_comand)», во время включения/отключения чего-либо.
...
case 100:
D2_High;
d2 = 1;
clear_port();
break;
case 101:
D2_Low;
d2 = 0;
clear_port();
break;
...
Функция «void trans()» отправляет эти значения (вместе с другими данными) серверу.
void trans()
{
Serial.print('A');
Serial.print(' ');
Serial.print(0);
Serial.print(' ');
Serial.print(d2);
Serial.print(' ');
Serial.print(d3);
...
Команды от кнопок для отправки сигнала не требующего подтверждения просто обрабатываются в функции «switch(cod_comand)».
...
case 106:
// какая-то реакция на кнопку SENTSIG1
clear_port();
break;
case 107:
// какая-то реакция на кнопку SENTSIG2
clear_port();
break;
...
Данные, которые будут выводиться в полях для приёма каких-либо данных, нужно поместить в функцию «void trans()». Например, нужно отправить показания температуры, тогда пишем:
...
Serial.print(temp); // INDATA3
...
temp — это какая-то переменная, в которую вы записываете показания датчика.
В интерфейсе, в поле «INDATA3» будет Ваша температура. Также можно посылать какую-то строку, не разделённую пробелами, например, так:
...
Serial.print("okey"); // INDATA3
...
К файлу index.html…
Браузер с интервалом 680мс запрашивает данные у ардуины…
...
setInterval(show,680);
...
… получает ответ в текстовом виде (данные разделены пробелами) и раскладывает их по переменным.
...
/* приём */
if(vars[2] == 1) { $('.d2otkl').show(); $('.d2vkl').hide(); }
else if(vars[2] == 0) { $('.d2otkl').hide(); $('.d2vkl').show(); }
$('#indata3').html('INDATA3' + ' ' + vars[3]);
if(vars[4] == 1) { $('.d3otkl').show(); $('.d3vkl').hide(); }
else if(vars[4] == 0) { $('.d3otkl').hide(); $('.d3vkl').show(); }
...
Если устанавливаете систему там, где качество связи плохое (например на даче), то есть пинги туда очень длинные, то появятся ошибки «timeout». Чтобы это исправить, нужно увеличить таймаут запроса:
...
show();
setInterval(show,680);
function show(){
if(flagobnov == 1) {
$.ajax({
type: "POST",
url: "file.db",
timeout:560, /* эта цифра (в миллисекундах)*/
cache: false,
...
По умолчанию стоит 560мс, увеличивайте её с шагом в 100 мс и пробуйте. Соответственно нужно увеличивать и setInterval(show,680), так же на 100 мс.
Изменять названия кнопок (D2, D3, SENTSIG1 и т.д.) можно здесь:
...
<div class='knop kon d2vkl'>D2</div>
<div class='knop koff d2otkl'>D2</div>
<div class='knop kon sent1'>SENTSIG1</div>
...
Изменять названия полей для приёма данных (INDATA3, INDATA5 и т.д.) можно здесь:
...
$('#indata3').html('INDATA3' + ' ' + vars[3]);
...
Браузер постоянно запрашивает данные и тем самым создаёт трафик. Чтобы этого избежать, можно либо закрывать страницу, либо раскомментировать этот блок:
/*slmode++;
if(slmode > 70)
{
$(".pansl").show(300);
flagobnov = 0;
slmode = 0;
}*/
Тогда через ~минуту страница будет закрываться полупрозрачной панелью и обновления остановятся. Клик на панель уберёт её и обновления возобновляться.
На этом пока всё, в следующей части будет добавлен UDP клиент/сервер и работа с GPIO на RPi.
П.С. Вначале с конструктором происходили непонятные вещи, но теперь всё должно работать исправно.
Комментарии (67)
Opengamer
11.09.2016 09:13Решал похожую задачу для автоматизации эскейп квестов, пришел к выводу, что эффективнее будет купить Lan Shield для Arduino (к примеру W5100 с аппаратной реализацией стека протоколов TCP/IP и UDP) и поднять на нем web сервер.
А точнее два web сервера, один для периодических асинхронных запросов со стороны клиента, которому важно моментально обновление информации (реализуется запросом с web интерфейса с таймаутом ~ 12 секунд, web сервер отвечает на него JSONом или в конце таймаута ~ 10 секунд, или если в Arduino за счет изменения чего-либо устанавливается флаг somethingnew, после получения ответа тут же отправляется следующий аналогичный запрос). Второй web сервер на другом порту слушает get запросы, парсит их и преобразует в команду для выполнения на Arduino. Два web сервера нужны так как использованная Arduino библиотека не позволяет подключаться нескольким клиентам одновременно, а клиент на первом сервере висит постоянно.
Уверен, что есть легковесные библиотеки позволяющие обойти это ограничение, но не было времени поискать и сравнить производительность. Если кто-нибудь подскажет — буду благодарен.stDistarik
11.09.2016 09:23пришел к выводу, что эффективнее будет купить Lan Shield для Arduino (к примеру W5100 с аппаратной реализацией стека протоколов TCP/IP и UDP) и поднять на нем web сервер.
В моём случае вместо W5100 выступает самый дешёвый роутер с OpenWrt. Во-первых есть возможность установки VPN (что в случае доступа извне просто необходимо), во-вторых неограниченое количество подключённых клиентов со скоростью запросов хоть каждые 100мс (всё упирается в пропускную способность инета), то есть при желании реакция будет мгновенная. Ну, а по цене, разница в ~500 рублей не так уж и обременительно.
Ну и конечно же роутер выполняет свои основные функциии.Opengamer
11.09.2016 09:56-1В моем случае просто не нужен был доступ извне, достаточно было LAN соединения, ну и конечно не хотелось использовать дополнительных девайсов. А вот по поводу длинных запросов вместо периодических коротких подумайте, при недостатке ресурсов на ардуине может сильно помочь.
stDistarik
11.09.2016 11:13не хотелось использовать дополнительных девайсов
Роутер есть почти у всех, а W5100 не только дополнительный девайс, но ещё и лишнее звено в цепи.
А вот по поводу длинных запросов вместо периодических коротких подумайте
Нет никаких длинных запросов, если всё работает в локальной сети, то запросы можно делать с интервалом хоть в 50мс: ардуина спокойно получает и отдаёт запросы со скоростью хоть 50мс и даже не вспотеет. Главное чтоб длинна пакета успевала проходить.
Уарт ардуины настроен на 57600, впустить она должна 4 символа, это ~1мс, выплюнуть должна, предположим 40 символом (ответ на двадцать кнопок или состояний каких-то концевиков, то есть 1 или 0), это около 8мс, ну и прибавьте сюда внутреннюю обработку — это от нескольких мкс до нескольких мс. То есть выйдет где-то 10-15мс.
А если система стот за городом (на даче), то там инет медленный и надо увеличить интервал запросов, в примере стоит 680мс, этого хватает чтоб пакет добежал до посёлка МГА и венулся обратно.Opengamer
11.09.2016 11:52Роутер есть почти у всех, в W5100 не только дополнительный девайс, но ещё и лишнее звено в цепи.
Не у всех роутеры с USB, а расположить Arduino рядом с роутером проблематично. Если ни с первым ни со вторым проблем нет — ваше решение предпочтительнее.
ардуина спокойно получает и отдаёт запросы со скоростью хоть 50мс и даже не вспотеет.
Если на самой Arduino не крутится какой-то сложной, ресурсоемкой логики и нет необходимости пересылать значения аналоговых датчиков (10 разрядов для Arduino для каждого аналогового порта), тогда не вспотеет.
Конечно логику в вашем подходе можно перенести в HomestD, используя Arduino только в качестве портов ввода\вывода (в случае если в логики нет ничего требующего очень быстрого реагирования), тогда она тоже все должна успевать.
PS Я понимаю акцент на конструкторе веб интерфейса, если бы мне он попался на годик раньше — я был бы очень рад.stDistarik
11.09.2016 12:06Не у всех роутеры с USB,
USB не нужно.
Arduino рядом с роутером проблематично
Можно прямо в роутер запихать (тут всё от задачи зависит), а можно отнести ардуину куда угодно, хоть в кладовку, и подключить её по RS-485 в двунаправленном варианте (полная синхронность).
Если на самой Arduino не крутится какой-то сложной, ресурсоемкой логики и нет необходимости пересылать значения аналоговых датчиков (10 разрядов для Arduino для каждого аналогового порта), тогда не вспотеет.
Скорость считывания АЦП ардуины 100мкс (0.0001 с), куда уж быстрее. Вам вряд ли понадобится и сотая часть этой чудовищной скорости.
Если на самой Arduino не крутится какой-то сложной, ресурсоемкой логики
Вы ардуиной собираетесь сайты парсить? О какой ресурсоемкой логики Вы говорите?
Opengamer
11.09.2016 13:51Я понял, что я решал специфически совсем другую задачу и описывал все применительно к ней. Извините, что влез.
Мне необходимо было сделать web интерфейс доступный только в LAN, для Arduino, которая управляла всем квестом, в том числе и воспроизведением звука и обработкой первичной информации и динамической индикацией на 4 4-х разрядных таймера, поэтому при частых запросах она вела себя некорректно. При этом оператору было критично получать информацию как можно быстрее. Поэтому я и решил воспользоваться долгими запросами.
Передача всей информации по последовательному порту занимала около 70 миллисекунд, в том числе из-за передачи значений с аналоговых датчиков (4 символа против 1 у цифровых датчиков) и большого количества промежуточной информации о состоянии того или иного задания, хранимой в Arduino. Это было слишком много. Применение долгих запросов позволило занимать эти 70 мс только при изменении чего либо важного, что решило мою проблему.
В моем контексте использование роутера мне в голову не пришло, и было бы неудобно, так как в одном месте располагается несколько квестов, и в каждом может быть не по одной Arduino. Причем мне при моем подходе достаточно html + javascript на локальной машине для запросов к любой Arduino и отображения всей необходимой информации и управления всем, чем я захочу.
Ваш конструктор несомненно очень полезная вещь для вашего случая и позволяет не особо вдаваясь в подробности реализации сделать управление чем-либо с веба, если бы он мне только попался раньше — мне было бы значительно проще реализовать мою схему.stDistarik
11.09.2016 14:06Да, мне с самого начала показалось, что мы говорим на разных языках)
Причем мне при моем подходе достаточно html + javascript
У меня на клинте тоже используется только html + javascript (ajax).
…
А что такое квест? Это сленг какой-то или игра?Opengamer
11.09.2016 14:14Реалити квест, комната или несколько комнат в реальном мире, в которых от 2 до 4 игроков запирают на 60 минут, а им нужно решая разные загадки найти выход. Можно провести прямую аналогию с компьютерными квестами, только перенесенными в реальный мир. Википедия — Эскейп-рум
Не буду приводить конкретные ссылки на квесты, а то будет смотреться как реклама :)
Opengamer
11.09.2016 14:27У меня на клинте тоже используется только html + javascript (ajax).
Да клиентская реализация у меня аналогичная.
Отличие на самом деле в том, что у меня обмен идет от локального компа\или смартфона напрямую до Arduino (с W5100 модулем), а у вас оно реализовано клиент — роутер — Arduino, так как взаимодействие идет еще и из интернета.stDistarik
11.09.2016 14:36Так точно!
В скором времени хочу на сокетах сделать.
Ну и UDP обязательно, но там много сложностей с конструктором для моб. девайсов.
kalobyte
11.09.2016 09:33как раз вчера искал что-то подобное типа веб сервер + скетч для ардуины
хочу в одной конторе сделать открывалку двери с телефонов и расширением для браузера в виде кнопки на панели инструментов
для телефонов есть хороший софт
http://automagic4android.com/
а если просто кнопки нужны, то есть попроще https://play.google.com/store/apps/details?id=com.idlegandalf.httprequestwidget
stDistarik
11.09.2016 09:35Вопрос к модераторам, как быть со ссылкой на сам конструктор?
alexpp
11.09.2016 10:31Используйте гитхаб и его вики.
stDistarik
11.09.2016 10:46Я не могу установить конструктор на гитхаб или иной ресурс, там сложный механизм с кучей исполняемых файлов (РНР, СИ) и скриптов.
x893
11.09.2016 10:47+3Перечитал три раза — так и не понял как мне настроить, что бы холодильник сам заказывал помидоры когда осталось 2 помидора внутри. Так же интересует настройка для куриных яиц, колбасы и сыра.
stDistarik
11.09.2016 11:15Установите в холодильник устройство, которое будет подсчитывать яйца и помидоры, и сообщать об этом ардуине. Ардуина в свою очередь сообщит Вам об этом.
GrishaTav
11.09.2016 11:19А почему не используете какой-нибудь общеизвестный протокол между сервером и Ардуино, например, Modbus/TCP? К вашей системе ведь не подключишь ни одного «чужого» устройства.
stDistarik
11.09.2016 11:20Мне не очень нравится концепция — master-slave. Какое устройство Вы хотите подключить?
GrishaTav
11.09.2016 11:40Ну, например, промышленный модуль ввода-вывода. Я, собственно, уже давно сделал себе умный дом на двух модулях ввода-вывода дискретных сигналов + мобильной SCADA на телефоне. Все работает по Modbus/TCP, доступ снаружи через VPN.
Но хочется добавить в систему что-то самодельное, чтобы поглубже изучить тему.stDistarik
11.09.2016 11:52Ну, например, промышленный модуль ввода-вывода
Я плохо знаком с этим девайсом, опишите его, что он должен делать?GrishaTav
11.09.2016 12:16Мои железки — Moxa E2210, 12 входов, 8 выходов. К входам и выходам подключены разнообразные датчики и реле, есть порт Ethernet. Управляю с помощью SCADA с мобильника по протоколу Modbus/TCP. Плюс именно в этих модулях есть еще и своя простенькая программируемая логика на уровне «сработал датчик на входе 1 -> установить регистр 10 в 1 и подать импульс длительностью 30 сек на выход 5». Соотвественно, все входы, выходы и регистры доступны снаружи по Модбасу.
stDistarik
11.09.2016 12:35Написано, что у Вашей железяки есть HTTР протокол, значит можно подключится к серверу какому-либо.
Можно по Модбасу подключится к ардуине.
А у железки нет ннапример УАРТА?stDistarik
11.09.2016 12:44Вобще конечно, еслиб у меня валялась подобная железка, я бы обязательно добавил возможность подключения по модбасу. Но железки нет, а на ардуине можно реализовать всё что угодно ИМХО.
Да и цена Вашей железки 20000 рублей!GrishaTav
11.09.2016 13:24Да, железка недешевая, но это же серьезное промышленное решение. Ну и купил я ее еще по старому курсу, тогда цена не так пугала.
На Ардуину я купил два шилда — RS-485 и Ethernet. В принципе, Modbus RTU через RS-485 я осилил, а вот с TCP пока не получилось.
Думаю, что Вам надо обязательно добавить Modbus как на сервер, так и на Ардуину. Свой протокол — это путь в никуда при наличии открытого аналога.stDistarik
11.09.2016 13:44Свой протокол — это путь в никуда при наличии открытого аналога
Мой протокол надёжный и работает уже года два-три, он простой, он двунаправленный и не требует денег.
В моих проектах мне его хватает. Выложил чтоб люди пользовались, да вот ссылку на сам конструктор модераторы не дают выложить, а без него вообще нет смысла.GrishaTav
11.09.2016 14:04Модбас тоже простой и надежный, не требует денег и работает уже почти 40 лет. Да, он мастер/слейв, но еще не факт, что другие варианты лучше подходят для автоматизации.
Думаю, поддержка этого протокола никак не сможет повредить вашему проекту, а скорее даже наоборот.stDistarik
11.09.2016 14:10Думаю, поддержка этого протокола никак не сможет повредить вашему проекту, а скорее даже наоборот.
Вне всяких сомнений, вот только железяки нет.
…
Честно говоря, мне кажется в недалёком будущем (с полноценным приходом IOT) во все уголки мира, появятся какие-то протоколы отличные от нынешних.GrishaTav
11.09.2016 15:34Такие железяки промышленного уровня начинаются где-то от 10000 руб. Китайские поделия с Алиэкспресс — где-то от 3000-4000 руб, ну у которых заявлена какая-то поддержка Modbus/TCP и имеются входы/выходы, которыми можно управлять. Так что по деньгам все нормально.
Новые протоколы — да пожалуйста, Apple HomeKit. Управление голосом через Сири или с дисплея Айфончика через встроенное приложение, все круто и дорого, как и положено. Думаю прикупить себе какой-нибудь совместимый девайс и попробовать.
GrishaTav
11.09.2016 13:03HTTP у нее для встроенного веб-сервера, но он убогий, выводит не все функции. Конечно, можно смотреть входы и щелкать выходами. Но у меня две таких, поэтому я решил купить нормальную СКАДу для мобильника и сделать более-менее пристойный юзер-интерфейс.
Насчет UART у этой — не помню, я специально покупал дорогую с Ethernet.
С UART (RS-485) есть аналогичные и гораздо более дешевые железки в огромном ассортименте. В большинстве своем они поддерживают «классический» Modbus RTU. Я бы мог, конечно, поставить такие, а потом один шлюз из Modbus RTU в Modbus/TCP. Но для этого надо по всей квартире тянуть еще и двухпроводной кабель, да и хотелось более прогрессивного решения.
GrishaTav
11.09.2016 13:41Вспомнил, эта железка еще и работает по CGI-запросам и свой первый вариант я сделал как раз на этом интерфейсе. Тогда это был единственный вариант для мобильника на Windows Mobile. Но потом у меня появился Айфон, а под iOS появилась отличная SCADA с поддержкой Modbus/TCP.
Bluefox
12.09.2016 10:26Поставте ioBroker. Он поддерживает modbus/TCP, а ардуинки я советую подключать по MQTT. Ну или mySensors.
stDistarik
11.09.2016 11:25Странно, комментов уже не мало, а никто не поинтересовался самим конструктором. Просто получается парадокс, статья о конструкторе, а поюзать его нельзя.
CyberBot
11.09.2016 13:21Привет, Дмитрий!
Если помнишь подобный проект http://cyber-place.ru/showthread.php?t=1854
То судя по количеству установок модуля, людей не очень сильно интересуют подобные конструкторы
igor2503
11.09.2016 12:23+1Дмитрий, исправьте ОПАСНЫЕ ошибки в Вашем проекте: логи в линуксе ДОЛЖНЫ писаться во вполне определённое место, это место "/var/log/___". Правило это «придумано» не просто так. OS должна знать куда можно вести постоянную запись. В OpenWrt это особо критично! Сбрасывая логи в /mydom/Access_Warning.log Вы каждый раз перезаписываете overlay( флэш память). Раз на тысячный( при 2х обращениях в секунду, это не долго) роутер вам скажет «пока».
Если Вам нравится что бы всё было в одном месте, то можно сбрасывать логи в /var/log/mydom/, а у себя( в папке проекта) сделать симлинк на эту папку.stDistarik
11.09.2016 12:25логи в линуксе ДОЛЖНЫ писаться во вполне определённое место
Ваше замечание вполне правомерно, сделал я это для удобства пользователя (в следующей версии сделаю как положено). Что же касается частоты перезаписи, то при запросе состояния (2 обращения в секунду) лог не пишется, ни к чему это:
if((strstr(buffer, "file.db")) != NULL) { memset(otvet, 0, BREADSIZE); int c_sim = 0; for(c_sim = 0; c_sim <= BREADSIZE - 1; c_sim++) { if(str_iz_file[c_sim] == '\n') break; } snprintf(otvet, 59 + c_sim, "%s%s", response_text, str_iz_file); write(client_fd, otvet, c_sim + 58); close(client_fd); printf("Trans otvet.\n"); }
Лог пишется только при запросе страниц (index,css,js), а это редко происходит, команда тоже пишется, но она оставлена на первое время, чтоб можно было посмотреть если что.igor2503
11.09.2016 13:52И ещё пару советов, т.к. Вы базируетесь на роутере, как по мне, это хорошая идея,
1) и даже если логов не много, сделайте контроль размера файлов. В роутере обычно не много свободной памяти. И растить tmpfs это есть плохо ;-). В идеале — логов быть не должно. Логи только для отладки — включаемой дополнительной командой.
И ещё,
2) сделайте поддержку нескольких «ардуин»( uart`ов). Очень удобно ардуины( микроконтролеры) располагать поближе к точкам коммутации электричества. А их обычно много. Перейти на RS485, это сложновато для большинства «ардуинщиков», а вот usb-hub это не сложно ;-).stDistarik
11.09.2016 15:40Логи только для отладки — включаемой дополнительной командой
Была такая мысль, но позже решил, что надо просто писать определённое время, скажем дво-трое суток, а потом удалять, оставляя последний.
сделайте поддержку нескольких «ардуин»( uart`ов). Очень удобно ардуины( микроконтролеры) располагать поближе к точкам коммутации электричества. А их обычно много. Перейти на RS485, это сложновато для большинства «ардуинщиков», а вот usb-hub это не сложно ;-).
Если я правильно понял, то Вы предлагаете втыкать несколько ардуин в один роутер через хаб?igor2503
11.09.2016 19:08ДА, но только в данном контексте. И я понимаю какая это
глупостьне эфективность. Но всему своё время.
Но насколько я понял из Вашей статьи Ваш проект нацелен на начинающих «ардуинщиков» для которых 485 шина это уже сложно, условно многозадачный скетч для ардуины — это «запредельно», а вот купить китайский усб-хаб, пяток переходников usb-uart и пяток самых дешёвых ардуин — не проблема. По началу и отлаживать это всё будет проще. И ограничение по длине проводов от переходника до микроконтроллера не сильно жесткое.
Судя по картинкам — датчики уже появились, следующее, скорее всего, это управление IR устройствами (телек, кондей и.т.д.) — тут точно разные комнаты и разные места установки.stDistarik
12.09.2016 05:28Можно прямо сейчас подключать несколько ардуин через хаб, делать несколько папок с интерфейсами (/mydom1, /mydom2, /mydom3), запускать несколько копий серверов на разных портах (и разных /dev/tty*) и заходить на ардуины так:
1-я ардуина — 192.168.5.197
2-я ардуина — 192.168.5.197:81
3-я ардуина — 192.168.5.197:82
И так далее…
На странички можно добавить перекрёстные ссылки.
Длина USB — вот проблема, метра три не больше. А склепать двунаправленный RS-485 по моему не так уж и сложно, тогда длина станет не важна.
bezdnacom
11.09.2016 14:10Интересная статья! Вот только не покидает меня один вопрос: конструктор генерирует сайт только для десктопа? Или есть возможность генерации для мобильных браузеров?
stDistarik
11.09.2016 14:24Он сделан и под десктоп и под мобильный браузер. У меня работет на Маке, айпаде, айфоне, андройде. Он просто масштабируется.
Вот на HTC one:
DeEF_NSK
13.09.2016 09:23+1похоже на упрощеную версию blynk.cc, или я ошибаюсь?
stDistarik
13.09.2016 09:47Я не знаю что такое blynk.cc.
stDistarik
13.09.2016 10:02Если Вы про интерфейс, то идея перекочевала вот отсюда.
DeEF_NSK
13.09.2016 10:15я про всю идею вашего конструктора
stDistarik
13.09.2016 11:14Покажите конструктор blynk.cc
stDistarik
13.09.2016 14:40Починю свой констркктор и ознакомлюсь с blynk.cc, впрочем, если вас не затруднит, расскажите вкратце, как он работает и что нужно для установки на роутер или иной комп?
stDistarik
13.09.2016 14:58И ещё, я хотел узнать, что Вы называете упрощённым. Подключить к ардуине можно любые устройства (не знаю, может что-то упустил) и ими можно будет управлять через мой интерфейс
doom369
13.09.2016 10:05похоже на упрощеную версию blynk.cc, или я ошибаюсь?
Упрощенную и страшную.stDistarik
13.09.2016 14:51-2Упрощенную и страшную.
Что касается внешнего вида, то я написал, что интерфейс предельно простой. Ну, а если Вам страшно, то проходите мимо. Или Вы просто подлизнуть хотели?
stDistarik
13.09.2016 16:27-1Достоиная однобокость.
Phenixoid
13.09.2016 10:40Не прочел комментарии выше, но из беглого теста по Вашей ссылке установлено, что поля названий помещений неправильно отрабатывают названия из более чем 1 слова. Например: «Комната родителей».
Допустим, на этапе вбивки названий помещений набираем:- 1. Зал
- 2. Кухня
- 3. Комната родителей
- 4. Детская
- 5. Коридор
- 6. Туалет
stDistarik
13.09.2016 10:44Я забыл написать, что нужно вместо пробелов ставить нижнее_подчёркивание. Извиняюсь. Попробуйте ещё раз.
stDistarik
13.09.2016 11:05Похоже, что конструктор работает не правильно, где-то я забыл разделить пользователей и всё смешивается в кучу. Будк починять.
DEM_dwg
Вы меня Дмитрий, конечно извините.
Но все таки умный дом, это прежде всего выполнение каких то действий, самим оборудованием(в данном случае малинкой).
У Вас же, просто интерфейс включения и выключения приборов.
stDistarik
Дык я и написал, что это конструктор интерфейса. Подключение же приборов к ардуине или другому МК, это задача хозяина. Я же не знаю, что Вы собираетесь подключать.
Конструктор предоставляет Вам управление любыми подключёнными к МК устройствами (лампочки, диммеры, датчики, сенсоры и т.д.).
Управление GPIO малины будет позже.
stDistarik
Правильнее будет объяснить мою идею так: большинство людей с лёгкостью подключают к ардуине различные датчики и реле, а вот как управлять этим удалённо для них проблема. Вот для этого и сделан конструктор, который создаёт интерфейс, скетч для ардуины и в придачу сервер.