1

Тема: Arduino WEB server

Вітаю. На днях потрапила в руки плата Arduino з мікроконтролером ATmega32 і модуль W5100.
Разом цей бутерброд виглядає так: http://tehnopage.ru/_pu/1/28347233.jpg
Одразу виникла ідея зробити на його базі сервер щоб через звичайний браузер можна було наприклад моніторити температуру в приміщенні і зовні а також керувати певними навантаженнями.

чистий HTML код вийшов такий:

HTML
<!DOCTYPE HTML5>
<html>
    <head>
        <title>LED switch & Temperatures</title>
        <meta charset=utf-8>

CSS
    <style>
            body{
                background-color: #ccc;
                text-align: center;
            }

            button {
                padding: 15px 25px;
                font-size: 24px;
                text-align: center;
                cursor: pointer;
                outline: none;
                color: #000;
                border: none;
                border-radius: 15px;
                box-shadow: 0 9px #555;
                border: 2px solid #555;
            }
            button:hover {
                color: #330;
                text-shadow: 0px 0px 35px #ff5, 0px 0px 35px #ff5, 0px 0px 35px #ff5;
                border: 2px solid #ff5;
                box-shadow: 0 9px #665;
            }
            button:active {
                color: #ff0;
                box-shadow: 0 3px #333;
                transform: translateY(6px);
                border: 2px solid #ff5;
            }

            H1{
                color: #000;font-family:  Georgia, 'Times New Roman', Times, serif;
                margin-top: 0px;
            }

            p{font-size: 24px; float: center;}

            div {
                margin: 10px;
                high: 50px;
                background: #fff;
                border: 1px dashed #000;
                padding: 15px;
                font: 18pt Tahoma;
            }
        </style>

    </head>
    <body>
        <!--<hr style=color:#ccc>-->
        <h1>WEB сервер "Ардуіно" 192.168.0.20</h1>
        <div>
            <h1>Керування навантаженнями</h1>
            <p>
                <a href=\"/$2\"><button style=background-color:#5c5>LED 1</button></a>
                <a href=\"/$3\"><button style=background-color:#c55>LED 2</button></a>
                <a href=\"/$5\"><button style=background-color:#c55>LED 3</button></a>
                <a href=\"/$7\"><button style=background-color:#c55>LED 4</button></a>
            </p>
        </div>
        <div style=color:#000><h1>Датчики температури</h1>T1:&numsp;23.50°С&emsp;T2:&numsp;23.50°С&emsp;T3:&numsp;23.50°С&emsp;T4:&numsp;23.50°С</div>
</html>

далі я це все закинув в сішний код для ардуінки:

HTML + C

ініціалізація і настройка плати і всяка всячина, кому цікаво
#include <SPI.h>             
#include <Ethernet.h>       
#include <OneWire.h>
#include <DallasTemperature.h>

#define LED_1 8
#define LED_2 11
#define LED_3 9
#define LED_4 12
bool led1_state = false;
bool led2_state = false;
bool led3_state = false;
bool led4_state = false;

#define ONE_WIRE_BUS 2
#define TEMPERATURE_PRECISION 9

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress TSensor[10];

short int DeviceCount = 0;
short int index;

boolean newInfo = 0;
byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDA, 0x02 };
IPAddress ip(192,168,0,20);
EthernetServer server(80);

void setup(void)
{
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  pinMode(LED_3, OUTPUT);
  pinMode(LED_4, OUTPUT);
  Ethernet.begin(mac, ip);
  server.begin();
  sensors.begin();
  oneWire.reset_search();
  DeviceCount=sensors.getDeviceCount();
  for(index=0; index<DeviceCount; index++)
  {
    sensors.getAddress(TSensor[index], index);
    sensors.setResolution(TSensor[index], TEMPERATURE_PRECISION);
  }
}

void loop(void)
{
  EthernetClient client = server.available();         //приймаємо данні які відправляє клієнт
  if(client)
  {                                                   //якщо запит закінчується пустою стрічкою
    boolean currentLineIsBlank = true;                //ставим мітку кінця запиту
    while (client.connected())                        //доки є підключення до клієнта
    {                     
      if (client.available())                         //якщо клієнт активний
      {                       
        char c = client.read();                       //зчитуємо передану інфу в змінну "с"
                                                   
        if(newInfo && c == ' ')
        {                                             //якщо змінна нової інфи = 1 і "с" пуста стрічка
          newInfo = 0;                                //то обнуляємо змінну нової інфи
       
        if(c == '$')
        {                                             //якщо в "с" записаний $
                                                      //ставиму змінну нової інфи як true
          newInfo = 1;                               
        }
                                         
        if(newInfo == 1){             //перевіряємо яке число іде після $   якщо є нова інформація             
            switch (c)
            {
                case '1':
                  digitalWrite(LED_1, HIGH);
                  led1_state = true;
                  break;
                case '2':
                  digitalWrite(LED_1, LOW);
                  led1_state = false;                 
                  break;

той самий код ще 4 рази тільки на інших портах
                case '3':
                  digitalWrite(LED_2, HIGH);
                  led2_state = true;                 
                  break;
                case '4':
                  digitalWrite(LED_2, LOW);
                  led2_state = false;                 
                  break;
                case '5':
                  digitalWrite(LED_3, HIGH);
                  led3_state = true;
                  break;
                case '6':
                  digitalWrite(LED_3, LOW);
                  led3_state = false;                 
                  break;
                case '7':
                  digitalWrite(LED_4, HIGH);
                  led4_state = true;                 
                  break;
                case '8':
                  digitalWrite(LED_4, LOW);
                  led4_state = false;                 
                  break;
                default:
                  break;

            }   
        }
        if (c == '\n') {                              //якщо "с" дорівнює символу нової стрічки
          currentLineIsBlank = true;                  //починаємо нову стрічку
        }
        else if (c != '\r') {                         //інакше якщо "с" дорівнює поверненню курсора на початок стрічки
          currentLineIsBlank = false;                 //отримуємо символ стрічки
        }

        if (c == '\n' && currentLineIsBlank) {        //виводимо HTML сторінку
          client.println("HTTP/1.1 200 OK");         
          client.println("Content-Type: text/html");
          client.println("Connection: close"); 
          client.println("Refresh: 5");              //автоматичне оновлення кожні 5 секунд
          client.println();
          sensors.requestTemperatures();       //запит температур від датчиків
          client.println("<!DOCTYPE HTML>");         
          client.println("<html><head>");                   
          client.print("<title>LED switch & Temperatures</title>");   
          client.print("<meta charset=utf-8>"); 

CSS + всяка всячина
          client.print("<style>body{ background-color: #ccc; text-align: center;}</style>");
         
          client.print("<style>button {background-color:#c55; padding: 15px 25px; font-size: 24px;  text-align: center; cursor: pointer; outline: none; color: #000; border: none; border-radius: 15px; box-shadow: 0 9px #555; border: 2px solid #555;}");
          client.print("button:hover { color: #330;  text-shadow: 0px 0px 35px #ff5, 0px 0px 35px #ff5, 0px 0px 35px #ff5; border: 2px solid #ff5; box-shadow: 0 9px #665;}");
          client.print("button:active { color: #ff0; box-shadow: 0 3px #333; transform: translateY(6px); border: 2px solid #ff5;}</style>");
          client.print("<style>  H1{ color: #000;font-family:  Georgia, 'Times New Roman', Times, serif; margin-top: 0px;}</style>");
         
          client.print("<style>p{font-size: 24px; float: center;}</style>");
          client.print("<style>  div { margin: 10px; high: 50px; background: #fff; border: 1px dashed #000; padding: 15px; font: 18pt Tahoma;}</style>");

         client.print("</head><body><h1>WEB сервер ''Ардуіно'' 192.168.0.20</h1><div><h1>Керування навантаженнями</h1><p>");     

 
         
          if(led1_state){
            client.print("<a href=\"/$2\"><button style=background-color:#5c5>LED 1</button></a>");
          }else{
            client.print("<a href=\"/$1\"><button>LED 1</button></a>");
          }
          if(led2_state){
            client.print("<a href=\"/$4\"><button style=background-color:#5c5>LED 2</button></a>");
          }else{
            client.print("<a href=\"/$3\"><button>LED 2</button></a>");
          }
          if(led3_state){
            client.print("<a href=\"/$6\"><button style=background-color:#5c5>LED 3</button></a>");
          }else{
            client.print("<a href=\"/$5\"><button>LED 3</button></a>");
          }
          if(led4_state){
            client.print("<a href=\"/$8\"><button style=background-color:#5c5>LED 4</button></a>");
          }else{
            client.print("<a href=\"/$7\"><button>LED 4</button></a>");
          }         
           
          client.println("</p></div><div style=color:#000>");
          client.println("<h1>    </h1>");
          client.println("<h1>Датчики температури</h1>");
          client.println("");
          for(index=0; index<DeviceCount; index++)
          {
            client.println("Темп.№");
            client.print(index+1);
            client.println(":&numsp;");
            client.print(sensors.getTempC(TSensor[index]));
            client.println("°С&emsp;");
          }
          client.println("</div></body></html>");                  //закриваємо тег HTML

          break;                                      //вихід з циклу
        }
     
      }
     
    }
    delay(1);                                          //затримка на отримання нових даних
    client.stop();                                     //закриваємо з'єднання
  }
}]

цікава іграшка була до поки я не відкрив цей сайт в двох окремих вкладках...
суть в чому, по дефолту в CSS стилях я прописав що кнопка червона, далі якщо я отримав з сайту якусь цифру то відповідно я на порт подаю високий рівень і далі в залежності від цього рівня я відсилаю на сторінку той  чи інший код і якщо порт активний то я примусово приписую кнопці зелений колір(як бачте по синтаксису коду на сі пряміше прописати колір не виходило)...

if(led1_state){
            client.print("<a href=\"/$2\"><button style=background-color:#5c5>LED 1</button></a>");
          }else{
            client.print("<a href=\"/$1\"><button>LED 1</button></a>");

а по натисканню кнопки першого діода коли він вимкненийя маю в URL 192.168.0.20/$2   і відповідно 192.168.0.20/$1 якщо він увімкнений...

а тепер ми маємо дві вкладки на якій ми натиснули ту саму кнопку і в одній вкладці ми маємо 192.168.0.20/$2 а в іншій 192.168.0.20/$1 і обидві вкладки автоматично оновлюються кожні 5 секунд (це потрібно для оновлення температур в реальному часі)

і навантаження починає само по собі то вмикатись то вимикатись (а навантаження це може бути наприклад світло в кімнаті)

Як зробити так щоб після автоматичного оновлення сторінки там в мене після IP адреси залишалось пусте поле?

і ще може хто знає як зробити адекватніше запуск сайту з флешки як ось тут http://tehnopage.ru/ethernet-arduino-control їхній код в мене не працює, а розібратись що і як в них працює я теж не можу, але в них адекватніше бо оновлюється тільки потрібний елемент сторінки а не вся сторінка і весь сайт живе на флешці...

Подякували: 221VOLT1

2 Востаннє редагувалося raxp (23.10.2016 15:49:08)

Re: Arduino WEB server

...частина коду підсвічування натиснутих і віджатих кнопок мого WEB-сервера при використанні мета-тега 'refresh' для автоматичного оновлення:

Прихований текст

Socket.SendText(
  '<html><head><title>Управление Xilinx-программатором over TCP/IP | RAMEDIA</title>' +

  '<STYLE TYPE="text/css"><!--BODY {background-color: black; font-family: Verdana; color: white; font-size: 9px} --> </STYLE>' +
  '<style>a{color:#668791;text-decoration: none; font:10px verdana} a:hover {color:lime}</style>' +

  '<meta http-equiv="Content-Type" content="text/html; Charset=windows-1251">' +
  //'<META HTTP-EQUIV="Refresh" CONTENT="' + SL.Values['XILINX WEB-Server Interval Update'] + ';URL=">' +

'<br><FORM ACTION="" method="POST">' +
  'The path to the JED: <input type="edit" name="sh4" size="24" value="' + jeds + '"<br><br>' +
  '<br><button TYPE=SUBMIT name="opt" value="1" style="width:294px;height:20px;background:' +
  getstatus3(false) + ';color:white;font-size:9pt;">задать новый путь на сервере</button><br>' +

  '<button TYPE=SUBMIT name="write" value="2" style="width:98px;height:40px;background:' + getstatus3(false) + ';color:white;font-size:9pt;">WRITE</button>' +
  '<button TYPE=SUBMIT name="verify" value="3" style="width:98px;height:40px;background:' + getstatus3(false) + ';color:white;font-size:9pt;">VERIFY</button>' +
  '<button TYPE=SUBMIT name="erase" value="4" style="width:98px;height:40px;background:' + getstatus3(false) + ';color:white;font-size:9pt;">ERASE</button><br>' +
  '<button TYPE=SUBMIT name="connect" value="5" style="width:98px;height:40px;background:' + getstatus3(false) + ';color:white;font-size:9pt;">CONNECT</button>' +
  '<button TYPE=SUBMIT name="reset" value="6" style="width:98px;height:40px;background:' + getstatus3(false) + ';color:white;font-size:9pt;">RESET</button>' +
  '<button TYPE=SUBMIT name="close" value="7" style="width:98px;height:40px;background:' + getstatus3(true) + ';color:white;font-size:9pt;">CLOSE</button></FORM>' +

сенс у використанні не GET, a POST запитів для кнопок і ось чому:
http://ipic.su/img/img7/fs/Bezymyannyj.1477226401.png

і ще може хто знає як зробити адекватніше запуск сайту з флешки

доцільніше не використовувати буратіно і шілди взагалі (енергоспоживання, габарити, ціна). Все те ж саме можна реалізувати на одному модулі ESP за допомогою Lua на базі SoC ESP8266 за 1.65$, в тому числі:
- сайт з кнопками http://raxp2.blogspot.com/2015/10/dio-e … wi-fi.html
- температурними датчиками http://raxp2.blogspot.com/2015/03/wi-fi-esp8266.html + http://raxp2.blogspot.com/2016/10/esp8266-wi-fi.html

Білоруський журнал «Радиолюбитель»
Lead Radar systems engineer & Software developer of industrial automation
Мої розробки та відеоблог
Подякували: Діма, Djalin2

3 Востаннє редагувалося Діма (27.10.2016 18:14:51)

Re: Arduino WEB server

raxp
доцільніше не використовувати буратіно і шілди взагалі (енергоспоживання, габарити, ціна). Все те ж саме можна реалізувати на одному модулі ESP за допомогою Lua на базі SoC ESP8266 за 1.65$, в тому числі:
- сайт з кнопками http://raxp2.blogspot.com/2015/10/dio-e … wi-fi.html
- температурними датчиками http://raxp2.blogspot.com/2015/03/wi-fi-esp8266.html + http://raxp2.blogspot.com/2016/10/esp8266-wi-fi.html

що потрапило в руки тим і бавлюсь, можливо і доцільніше, а можливо і ні... як на мене ардуінка значно простіша і на ню можна навішати купу всякої всячини...

може я дуже туплю.. але в мене ніяк не виходить відкрити сторінку з флешки...
наприклад я маю на SD флешці сайт Index.html ... який чином має виглядати код щоб я по айпішнику плати просто відкривав цей сайт... тобто не обробляємо ніякі запити нічим не керуємо, просто підключили в мережу і відкрився сайт...

я не розумію яким чином це відбувається і поки що про GET i POST запити можна і не згадувати

бо я намагався розвивати сайт і в мене просто перестало вантажити з написам "stack Overflow"
в ню просто не влазить вже більше кода на її скромні 32кб а з флешки просто глухо, ніяк не виходить, тобто мені потрібно якось зробити окремо сайт і окремо код, бо хардкодити HTML в C++ це не по феншую...

4 Востаннє редагувалося raxp (28.10.2016 06:32:20)

Re: Arduino WEB server

на SD флешці сайт Index.html ... який чином має виглядати код щоб я по айпішнику плати просто відкривав цей сайт... тобто не обробляємо ніякі запити нічим не керуємо, просто підключили в мережу і відкрився сайт.

ось запити клієнта-браузера і доведеться обробляти: при отриманні запиту на Index.html в url, читаємо з флешки текст з файлу - 'Index.html':

Прихований текст

client.println("HTTP/1.1 200 OK");         
client.println("Content-Type: text/html");

File dataFile = SD.open("log.txt", FILE_READ);
    if (dataFile)
    {
      while (dataFile.available() && pos == dataFile.position())
      {
        value = dataFile.read();
        client.println(value);
        pos ++;
      }
      dataFile.close();
    }

і пишемо його в сокет. При отриманні запиту на малюнки в index.htm, читаємо з флешки бiнарно малюнки з файлу та пишемо в сокет.

як на мене ардуінка значно простіша і на ню можна навішати купу всякої всячини...

надлишкова буратіно і навішати всячини можна не тільки на неї.

Видача сайту на Lua:

Прихований текст

srv=net.createServer(net.TCP) 
srv:listen(80,function(conn) 

   local rnrn=0
   local Status = 0
   local DataToGet = 0
   local method=""
   local url=""
   local vars=""

  conn:on("receive",function(conn,payload)
  
    if Status==0 then
        _, _, method, url, vars = string.find(payload, "([A-Z]+) /([^?]*)%??(.*) HTTP")
        -- print(method, url, vars)                          
    end

    if string.find(url, "/") ~= nil then
     --print ("Slash found")
     local invurl=string.reverse(url)
     local a,b=string.find(invurl, "/", 1)
     url=string.sub(url, string.len(url)-(a-2))
     --print ("Neue URL= " .. url)
    end
        
    if string.len(url)>= 25 then
        url = string.sub (url,1,25)
    --    print ("cut down URL")
    end
    
   
    DataToGet = -1

    if url == "favicon.ico" then
        conn:send("HTTP/1.1 404 file not found")
        return
    end    

    
    conn:send("HTTP/1.1 200 OK\r\n\r\n")

    
    if url==nil then
        url="index.htm"
    end
    
    if url=="" then
        url="index.htm"
    end
    
    local foundmatch = 0
    local a = {'index.htm','about.htm','small.png'}
    for _,v in pairs(a) do
        if v == url then
            foundmatch=1
            break
        end
    end

if foundmatch == 0 then
    url="index.htm"
end

    
    
        
    -- it wants a file in particular
    if url~="" then
        DataToGet = 0
        return
    end    

   -- conn:send("<html><body><h1>NodeMCU IDE</h1>")
  
    
   -- conn:send("</body></html>")

  end)
  
  conn:on("sent",function(conn) 
    if DataToGet>=0 and method=="GET" then
        if file.open(url, "r") then            
            file.seek("set", DataToGet)
            local line=file.read(512)
            file.close()
            if line then
                conn:send(line)
                DataToGet = DataToGet + 512    

                if (string.len(line)==512) then
                    return
                end
            end
        end        
    end

    conn:close() 
  end)
end)

    
print("listening, free:", node.heap())

Білоруський журнал «Радиолюбитель»
Lead Radar systems engineer & Software developer of industrial automation
Мої розробки та відеоблог
Подякували: Діма1

5

Re: Arduino WEB server

raxp написав:

ось запити клієнта-браузера і доведеться обробляти: при отриманні запиту на Index.html в url, читаємо з флешки текст з файлу - 'Index.html':

вперто не стартує з флешки... чи то флешка коцана, чи то руки криві, чи то лижі не їдуть...

вирішив проблему значно простіше, всього в одну стрічку, не знаю як раніше не додумався...

client.print("<meta http-equiv=\"refresh\" content=\"2;url=http://192.168.0.20\" />");

і оновлюється так швидко що здається температури міняються в реальному часі і кілька клієнтів не конфліктують і тицялки приліпив так що навантаженнями можна кляцати не тільки з сайту... тепер ще треба придумати навіщо це мені...