"Умный дом" на основе Arduino

Умный дом

Эта статья является началом «Умного дома» на базе Arduino.




В этой части описано как сделать простой веб-интерфейс для управления (вкл/откл) пинами ардуино.


Посмотреть и понажимать можно здесь...



Понадобится роутер с установленой OpenWrt и возможностью подключения ардуино (USB, UART) или любой компьютер с image

И любая Ардуина.


Здесь я подробно описал процесс установки OpenWrt на роутер TL-MR3020.

Тут про установку сервера Lighttpd.

А здесь о том как подключить к роутеру Ардуино.




Итак, Вы установили OpenWrt и наладили связь с ардуиной как описано тут.

Теперь сделаем интерфейс с помощью которого можно будет включать и отключать «что-то» подключённое к пинам ардуино (D2 — D13).


Скачайте архив и распакуйте его в рабочую папку сервера (по умолчанию это /var/www), чтоб получилось так /var/www/knopki.
У Вас может быть своя папка.


Прошейте в ардуину этот скетч:


byte d2 = 0; // флаги
byte d3 = 0;
byte d4 = 0;
byte d5 = 0;
byte d6 = 0;
byte d7 = 0;
byte d8 = 0;
byte d9 = 0;
byte d10 = 0;
byte d11 = 0;
byte d12 = 0;
byte d13 = 0;

byte descript[5]; // массив

void setup() 
{
  Serial.begin(57600);
  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
}
  
void loop() 
{  
  if (Serial.available()>4) // ждём дескриптор и нужный символ
   {
    if (Serial.read()=='Y') // проверяем первый символ, если это 'Y', то продолжаем принимать, если нет, то выходим из цикла чтения
     {
      for (byte i=0; i < 5; i++)
        {
           descript[i] = Serial.read(); // добавляем символы в массив   
        } 
        
    if((descript[0] =='+') && (descript[1] =='=') && (descript[2] =='Z')) // проверяем дескриптор
     {
      switch (descript[3])
       {
         case 'o': // обновление
         glavnaia(); // отправка ответа
         break;
         
         case 'A': // d2 вкл
         digitalWrite(2, HIGH); // вкл d2
         d2 = 1; // ставим флаг в единицу (вкл)
         glavnaia(); // отправка ответа
         break;
         
         case 'a': // d2 откл
         digitalWrite(2, LOW); // откл d2
         d2 = 0; // ставим флаг в ноль (откл)
         glavnaia(); // отправка ответа
         break; 
 
         case 'B': // d3
         digitalWrite(3, HIGH);
         d3 = 1;
         glavnaia();
         break;
         
         case 'b': // d3
         digitalWrite(3, LOW);
         d3 = 0;
         glavnaia();
         break;          
  
         case 'C': // d4
         digitalWrite(4, HIGH);
         d4 = 1;
         glavnaia();
         break;
         
         case 'c': // d4
         digitalWrite(4, LOW);
         d4 = 0;
         glavnaia();
         break;   
  
         case 'D': // d5
         digitalWrite(5, HIGH);
         d5 = 1;
         glavnaia();
         break;
         
         case 'd': // d5
         digitalWrite(5, LOW);
         d5 = 0;
         glavnaia();
         break;  
  
         case 'E': // d6
         digitalWrite(6, HIGH);
         d6 = 1;
         glavnaia();
         break;
         
         case 'e': // d6
         digitalWrite(6, LOW);
         d6 = 0;
         glavnaia();
         break;   
  
         case 'F': // d7
         digitalWrite(7, HIGH);
         d7 = 1;
         glavnaia();
         break;
         
         case 'f': // d7
         digitalWrite(7, LOW);
         d7 = 0;
         glavnaia();
         break;  
 
         case 'G': // d8
         digitalWrite(8, HIGH);
         d8 = 1;
         glavnaia();
         break;
         
         case 'g': // d8
         digitalWrite(8, LOW);
         d8 = 0;
         glavnaia();
         break;  
 
         case 'H': // d9
         digitalWrite(9, HIGH);
         d9 = 1;
         glavnaia();
         break;
         
         case 'h': // d9
         digitalWrite(9, LOW);
         d9 = 0;
         glavnaia();
         break; 
         
         case 'I': // d10
         digitalWrite(10, HIGH);
         d10 = 1;
         glavnaia();
         break;
         
         case 'i': // d10
         digitalWrite(10, LOW);
         d10 = 0;
         glavnaia();
         break;         
 
         case 'J': // d11
         digitalWrite(11, HIGH);
         d11 = 1;
         glavnaia();
         break;
         
         case 'j': // d11
         digitalWrite(11, LOW);
         d11 = 0;
         glavnaia();
         break;  
        
         case 'K': // d12
         digitalWrite(12, HIGH);
         d12 = 1;
         glavnaia();
         break;
         
         case 'k': // d12
         digitalWrite(12, LOW);
         d12 = 0;
         glavnaia();
         break;         
      
         case 'M': // d13
         digitalWrite(13, HIGH);
         d13 = 1;
         glavnaia();
         break;
         
         case 'm': // d13
         digitalWrite(13, LOW);
         d13 = 0;
         glavnaia();
         break;
 
         default:
         glavnaia();
       }
     }
   
    else // если дескриптор ложный, то очищаем буфер
      {
        for(byte i=0; i < 255; i++) 
         {
           Serial.read();    
         } 
      } 
     }    // конец if (Serial.read()=='Y')
   }    // конец чтение порта
 
} // конец loop


void glavnaia() // отправка данных
 {
      Serial.print(d2);//0
      Serial.print(",");
      Serial.print(d3);//1
      Serial.print(",");
      Serial.print(d4);//2
      Serial.print(",");
      Serial.print(d5);//3
      Serial.print(",");
      Serial.print(d6);//4
      Serial.print(",");
      Serial.print(d7);//5
      Serial.print(",");
      Serial.print(d8);//6
      Serial.print(",");
      Serial.print(d9);//7 
      Serial.print(",");
      Serial.print(d10);//8
      Serial.print(",");
      Serial.print(d11);//9
      Serial.print(",");
      Serial.print(d12);//10
      Serial.print(",");
      Serial.println(d13);//11 отсылается 12 значений
 }




Алгоритм обмена данными между ардуиной и роутером
Роутер отправляет в ардуину запрос от клиента состоящий из дескриптора (Y+=Z) и управляющего символа (например 'А'- вкл d2). Дескриптор позволит отфильтровать возможный мусор и исключит случайные срабатывания.

Ардуина обрабатывает управляющий символ внутри функции switch (например включает светодиод) и отправляет ответ роутеру, который в свою очередь отдаст его клиенту.

Работа с клиентом описана ниже.



Теперь в браузере зайдите по адресу ваш_роутер/knopki/

Если связь установлена, то Вы увидите это:



Нажмите на D13 — загорится светодиод на ардуине и кнопка подсветится.

Теперь осталось приспособить реле и включать везде и всё.







Если связи между ардуиной и роутером нет, то будет только красная надпись stDэто индикатор работы, когда связь установлена, надпись становится серой.



Подождите минуту и пообновляйте страницу. Если связь не устанавливается, то проверьте настройки роутера.
Проверьте права на файлы, правильность путей и устройств.




Как работает: Обновление страницы.

index.html раз в три секунды (интервал можно изменить) запрашивает данные у ардуины (отправляя ей символ о) с помощью функции ajax (ajax позволяет не перегружать страницу).


/*обновление*/
show();
setInterval(show,3000);  /* частота обновления в милисекундах */

function show(){  /* функция обновления */
 
            $.ajax({ 
                type: "GET",
	        url: "box2.php?df=o", /* отправка символа о */
                timeout:300,           
                cache: false,       
                success: function(data){ 
...



Запрос передаётся php-файлу (box2.php) находящемуся на сервере, который в свою очередь отправляет его в ардуину.


<?php
if($fp = fsockopen("localhost", 3002, $errno, $errstr, 1)) // открываем порт, в качестве посредника между роутером и ардуиной выступает ser2net
   { 
      fwrite($fp, 'Y+=Z'); // отправляем в порт дескриптор Y+=Z
      fwrite($fp, $_GET['df']); // отправляем в порт символ полученый от html странички
      stream_set_timeout($fp, 0, 150000); // полезный таймаут, если ответа нет, то поток закроется через 150 мс
      $bufft = fgets($fp); // получаем ответ от ардуины
      fclose($fp); // закрываем порт
      echo $bufft; // отправляем ответ клиенту 
   } 

else
   { 
      usleep(70000);
      $fp = fsockopen("localhost", 3002, $errno, $errstr, 1);
      fwrite($fp, 'Y+=Z'); 
      fwrite($fp, $_GET['df']);
      stream_set_timeout($fp, 0, 150000);
      $bufft = fgets($fp);
      fclose($fp);
      echo $bufft;
   }
?> 



Ардуина получает команду, обрабатывает её и отправляет ответ, который по той же цепочке возвращается html-страничке (index.html).


...
 switch (descript[3])
       {
         case 'o': // обновление
         glavnaia(); // отправка ответа
         break;
...
void glavnaia() // отправка данных
 {
      Serial.print(d2);//0
      Serial.print(",");
      Serial.print(d3);//1
...



Html-страничка разбирает ответ и выводит на экран нужную кнопку.


...
 success: function(data){   
                                          
                           var vars = data.split(","); /* разбор строки принятой от ардуино */
                           if(vars.length == dlina){ /* проверка длины данных (количество блоков разделённых запятой) */
                               
                               /*d2*/
                               if(vars[0] == 1) { $(".d2otkl").show(); $(".d2vkl").hide(); }  /* в зависимости от принятого флага скрывает/показавыет кнопку вкл или откл */
                               else if(vars[0] == 0) { $(".d2otkl").hide(); $(".d2vkl").show(); } 
                               ...
                               /* эта часть отвечает за цвет индикатора работы (stD) */
                               $('#std').html("<v2>stD</v2>"); /* если данные приняты правильно, то надпись stD будет серой */ 
                             }
                          else 
                             {
                               $('#std').html("<v>stD</v>"); /* если данные приняты НЕправильно, то надпись stD будет красной */
                             } 


Если открыть ещё одну страничку (или зайти с другого устройства) и нажать какую-то кнопку, то на первой страничке (в течении 3 сек.) эта кнопка тоже станет включённой.
Для этого и нужно обновление.





Как работает: Нажатие на кнопку.

Нажатие на кнопку работает так же как и «обновление», в ардуину отсылается символ включения или отключения (в зависимости от состояния), ардуина выполняет действие и посылает в ответ строку с флагами состояния (единица или ноль).

Ответ разбирается в html-странице и в зависимости от флагов выводит на экран нужную кнопку.

Для лучшего понимания откройте файл index.html из архива, и посмотрите комментарии.


Изменение дизайна

Названия кнопок меняются в конце файла index.html


...
<div class="knop d2vkl">D2</div> <!-- здесь менять названия кнопок -->
<div class="knop d2otkl">D2</div> <!-- чтобы убрать кнопку удалите оба блока -->
...


Расположение и размер кнопок задаются в файле knopki.css


...
.d2vkl{
top: 20px; /*координаты кнопок*/
left: 20px; /*координаты кнопок*/
box-shadow: 0 0 10px 3px rgba(0,0,0,0.3); /*цвет и размер тени кнопки*/
-webkit-transition-duration: 0.6s; /*плавность появления*/
-o-transition-duration: 0.6s;
-moz-transition-duration: 0.6s;
transition-duration: 0.6s;
}

.d2vkl:hover{ /*наведение мыши на кнопку*/
box-shadow: 0 0 2px 1px rgba(0,0,0,0.3);
}
...


...
.knop {
position: absolute;
width: 200px; /*ширина для всех кнопок*/
height: 100px; /*высота для всех кнопок*/
display: none;
cursor: pointer; 
font-size: 30px; /*размер текста на кнопках*/
font-weight: 600; /*ширина текста на кнопках*/
font-family: Arial, Helvetica, sans-serif; /*шрифт*/
color: #161616; /*цвет текста на кнопках*/
text-shadow: 0px 1px 2px #7c7c7c; /*цвет и размер тени кнопок*/
text-align: center;
line-height: 3.2;
}


Цвет фона меняется в файле style.css


body {
background:#202020; /* цвет фона */
}
...




На этом первая часть заканчивается, в следующей части добавлен диммер и запись значений в EEPROM...

«Дизаин» конечно «страшненький», но, это пока всего лишь шаблон для демонстрации функций.


Вот тут можно скачать библиотеку для разгона Arduino.


Обсудить на форуме...


  • 0
  • 20443

Комментарии (13)

0
Очень понятное и информативное описание статей! Огромное автору спасибо за колоссальный труд! Делаю проект на базе mr3020 и этот сайт ооочень облегчил жизнь и сократил время на изучение материала!!! Дошёл до этого этапа, всё работает, но возникает один вопрос… почему-то Google Crome плохо работает с сайтом… нажимаешь кнопку, диод загорелся, кнопка стала нажатой и чтобы её нажать ещё раз (для отключения) приходится перейти на любую другую вкладку и вернутся обратно, тогда кнопка отжимается и диод гаснет… при этом IE работает отлично. В чём может быть причина?
0
Я пользуюсь фирефоксом, за хром ничего сказать не могу.

Попробуйте в файле index.html увеличить таймауты с 300 на 500:

      ...
      timeout:500,           
      cache: false,   
      ...
0
А ещё лучше, сделайте как описано здесь.
0
Увеличение таймаута ничего не дало. Как Здесь... нет смысла делать т.к. у меня проект (сайт) будет управлять роботом и манипулятором, при этом с голосовым сопровождением… а 300Кб на такой сайт точно не хватит)) Буду разбираться)) Спасибо.
0
Воткните флешку и получите сколько угодно места, работать будет со стопроцентной надёжностью и максимальной скоростью.
0
Вы избавитесь от «ужасов» пхп и ненужной прослойки в виде ser2net.
0
понял, спасибо!
0
Доброго времени суток. По ходу работа проделана большая, и что ещё важно — то что вы поделились
с нами. Спасибо. Скажите, если сервер с PHP — можно ли прикрутить MySQL и в домашний каталог залить
CMS — Joomla например?
0
Можно, но на роутере это будет с трудом ворочаться.
0
Доброго дня. Помогите плиз разобраться:
как считать данные со входов ардуины в страницу?
код в ардуине:

byte in24 = 0; // флаги
byte in26 = 0;
byte in28 = 0;
byte in30 = 0;
byte in32 = 0;
byte in34 = 0;
byte in36 = 0;
byte in38 = 0;
byte in40 = 0;
byte in42 = 0;
byte in44 = 0;
byte in46 = 0;

void setup() 
{
  Serial.begin(57600);
  pinMode(24, INPUT);
  pinMode(26, INPUT);
  pinMode(28, INPUT);
  pinMode(30, INPUT);
  pinMode(32, INPUT);
  pinMode(34, INPUT);
  pinMode(36, INPUT);
  pinMode(38, INPUT);
  pinMode(40, INPUT);
  pinMode(42, INPUT);
  pinMode(44, INPUT);
  pinMode(46, INPUT);
 
  
}
  
void loop() 
{  
     if(digitalRead(24)==HIGH){    // сработал датчик in24
        in24 = 1; // ставим флаг в единицу (вкл)
    glavnaia(); }  
    else{
      in24 = 0;
    }         
             ////////
      if(digitalRead(26)==HIGH){    
        in26 = 1;
    glavnaia(); }
    else{
      in26 = 0;
    }
         
               ///////////////////////
     if(digitalRead(28)==HIGH){    
        in28 = 1; 
    glavnaia(); }
    else{
      in28 = 0;
    }
         
             ///////////////////////
     if(digitalRead(30)==HIGH){    
        in30 = 1; 
    glavnaia();  }
    else{
      in30 = 0;
    }
         
             ////////////////////////
     if(digitalRead(32)==HIGH){    
        in32 = 1; 
     glavnaia();  }
    else{
      in32 = 0;
    }
        
             /////////////////////////////
     if(digitalRead(34)==HIGH){    
        in34 = 1; 
    glavnaia();     }
    else{
      in34 = 0;
    }
         
             //////////////////////////
     if(digitalRead(36)==HIGH){    
        in36 = 1; 
    glavnaia();    }
    else{
      in36 = 0;
    }
          
             ////////////////////////////////
     if(digitalRead(38)==HIGH){    
        in38 = 1;
    glavnaia();    }
    else{
      in38 = 0;
    }
          
             //////////////////////////////
     if(digitalRead(40)==HIGH){    
        in40 = 1; 
    glavnaia();    }
    else{
      in40 = 0;
    }
          
             ///////////////////////////
     if(digitalRead(42)==HIGH){    
        in42 = 1; 
    glavnaia();    }
    else{
      in42 = 0;
    }
          
             ///////////////////////////
     if(digitalRead(44)==HIGH){    
        in44 = 1; 
    glavnaia();     }
    else{
      in44 = 0;
    }
         
             ///////////////////////
     if(digitalRead(46)==HIGH){    
        in46 = 1; 
    glavnaia();     }
    else{
      in46 = 0;
    }            

} // конец loop


void glavnaia() // отправка данных
 {
      Serial.print(in24);//0
      Serial.print(",");
      Serial.print(in26);//1
      Serial.print(",");
      Serial.print(in28);//2
      Serial.print(",");
      Serial.print(in30);//3
      Serial.print(",");
      Serial.print(in32);//4
      Serial.print(",");
      Serial.print(in34);//5
      Serial.print(",");
      Serial.print(in36);//6
      Serial.print(",");
      Serial.print(in38);//7 
      Serial.print(",");
      Serial.print(in40);//8
      Serial.print(",");
      Serial.print(in42);//9
      Serial.print(",");
      Serial.print(in44);//10
      Serial.print(",");
      Serial.println(in46);//11 отсылается 12 значений
 }


Код скрипта в странице:

...............
<div id="in24" class="but"> </br></br>Датчик 1 </div>
...............
<script>
var inpts = 12; /* количество входов разделённых запятой в ответе от ардуино */

$(document).ready(function(){
/*обновление*/
show();
setInterval(show,3000);  /* частота обновления в милисекундах */

function show(){  /* функция обновления */
 
            $.ajax({ /**************** получение данных с ардуины  ??? *****/
                    success: function(data){   
                                          
                           var vars = data.split(","); /* разбор строки принятой от ардуино */
                           if(vars.length == inpts)    /* проверка длины данных (количество входов разделённых запятой) */
						   { 
                               
                               /*in24*/
                               if(vars[0] == 1) {$('#in24').css({'background-image':'url(./images/active/001.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[0] == 0) {$('#in24').css({'background-image':'url(./images/001.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in26*/
                               if(vars[1] == 1) {$('#in26').css({'background-image':'url(./images/active/002.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[1] == 0) {$('#in26').css({'background-image':'url(./images/002.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in28*/
                               if(vars[2] == 1) {$('#in28').css({'background-image':'url(./images/active/003.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[2] == 0) {$('#in28').css({'background-image':'url(./images/003.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in30*/
                               if(vars[3] == 1) {$('#in30').css({'background-image':'url(./images/active/004.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[3] == 0) {$('#in30').css({'background-image':'url(./images/004.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in32*/
                               if(vars[4] == 1) {$('#in32').css({'background-image':'url(./images/active/005.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[4] == 0) {$('#in32').css({'background-image':'url(./images/005.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in34*/
                               if(vars[5] == 1) {$('#in34').css({'background-image':'url(./images/active/006.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[5] == 0) {$('#in34').css({'background-image':'url(./images/006.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in36*/
                               if(vars[6] == 1) {$('#in36').css({'background-image':'url(./images/active/007.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[6] == 0) {$('#in36').css({'background-image':'url(./images/007.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in38*/
                               if(vars[7] == 1) {$('#in38').css({'background-image':'url(./images/active/008.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[7] == 0) {$('#in38').css({'background-image':'url(./images/008.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in40*/
                               if(vars[8] == 1) {$('#in40').css({'background-image':'url(./images/active/009.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[8] == 0) {$('#in40').css({'background-image':'url(./images/009.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in42*/
                               if(vars[9] == 1) {$('#in42').css({'background-image':'url(./images/active/010.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[9] == 0) {$('#in42').css({'background-image':'url(./images/010.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in44*/
                               if(vars[10] == 1) {$('#in44').css({'background-image':'url(./images/active/011.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[10] == 0) {$('#in44').css({'background-image':'url(./images/011.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
							   
							   /*in46*/
                               if(vars[11] == 1) {$('#in46').css({'background-image':'url(./images/active/012.png)','border': '#d11f30 1px solid','color': '#d11f30'})}  
                               else if(vars[11] == 0) {$('#in46').css({'background-image':'url(./images/012.png)','border': '#b9b9b9 1px solid','color': '#b9b9b9'})} 
                                                          
                                                    }
                                        }
                   });  

} } )
</script>

Если все датчики по нулям ардуина ничего в порт не отправляет.
Устанавливаю первый вход в 1 и ардуина посылает в порт данные
В putty данные считываю cat /dev/ttyACM0 вижу 1,0,0,0,0,0,0,0,0,0,0,0.(всё хорошо)
Ваш файл box.php лежит в папке на сервере — покажите плиз как правильно считать и отображать данные со входов.
С уважением Алексей.
0
Первый раз putty показала правильно данные, а остальные пять раз — только мусор…
Пытаюсь использовать box.php добавляя код в ардуину и соответственно код в скрипт страницы…

...
void loop() 
{  
  if (Serial.available()>4) // ждём дескриптор и нужный символ
   {
    if (Serial.read()=='Y') // проверяем первый символ, если это 'Y', то продолжаем принимать, если нет, то выходим из цикла чтения
     {
      for (byte i=0; i < 5; i++)
        {
           descript[i] = Serial.read(); // добавляем символы в массив   
        } 
        
    if((descript[0] =='+') && (descript[1] =='=') && (descript[2] =='Z')) // проверяем дескриптор
     {
      switch (descript[3])
       {
         case 'o': // обновление
         glavnaia(); // отправка ответа
         break;
...

Страница не реагирует…
Захожу на страницу box.php — //192.168.1.100/box.php — вижу вывод echo $bufft — левые данные — при перезагрузке страницы
то-же самое — разная левая информация.
У меня ардуина DUE — от автосброса подключён резистор на 200 Ом + >> reset (опытным путём — никакие кондёры не помогали).
Помогите плиз разобраться.
0
С одной проблемой разобрался — питание роутера брал с компа — в порту был мусор — подключил на
отдельный блок питания — всё чисто: на запрос Y+=Zo putty показывает при входах на корпус 0,0,0,0,0,0,0,0,0,0,0,0
при входах на плюс 3,3в — последовательно заполняет ноли единицами.
0
Разобрался с входами и отображением на странице — всё работает.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.