Kami mengembangkan situs web untuk mikrokontroler



Dengan munculnya berbagai jenis soket pintar, bola lampu, dan perangkat serupa lainnya ke dalam kehidupan kita, kebutuhan akan situs web pada mikrokontroler menjadi tidak dapat disangkal. Dan berkat proyek lwIP (dan adik laki-lakinya, uIP), Anda tidak akan mengejutkan siapa pun dengan fungsi seperti itu. Tetapi karena lwIP ditujukan untuk meminimalkan sumber daya, dalam hal desain, fungsionalitas, serta kegunaan dan pengembangan, situs semacam itu tertinggal jauh dari yang biasa kita gunakan. Bahkan untuk sistem tertanam, bandingkan, misalnya, dengan situs administrasi pada router termurah. Pada artikel ini kami akan mencoba mengembangkan situs di Linux untuk beberapa perangkat pintar dan menjalankannya di mikrokontroler.



Untuk berjalan di mikrokontroler, kita akan menggunakan Embox . RTOS ini mencakup server HTTP berkemampuan CGI. Kami akan menggunakan server HTTP yang dibangun ke dalam python sebagai server HTTP di Linux.



python3 -m http.server -d <site folder>
      
      





Situs statis



Mari kita mulai dengan situs statis sederhana yang terdiri dari satu atau lebih halaman.

Semuanya sederhana di sini, mari buat folder dan index.html di dalamnya. File ini akan diunduh secara default jika hanya alamat situs yang ditentukan di browser.



$ ls website/
em_big.png  index.html

      
      





Situs ini juga akan berisi logo Embox, file "em_big.png", yang akan kita sematkan di html.



Mari mulai server http:



python3 -m http.server -d website/
      
      





Masuk ke localhost: 8000 di browser







Sekarang tambahkan situs statis kita ke sistem file Embox. Ini dapat dilakukan dengan menyalin folder kita ke folder rootfs / template (template saat ini ada di folder conf / rootfs). Atau buat modul yang menentukan file untuk rootf di dalamnya.



$ ls website/
em_big.png  index.html  Mybuild

      
      





Konten Mybuild.



package embox.demo

module website {
    @InitFS
    source "index.html",
        "em_big.png",
}
      
      





Demi kesederhanaan, kami akan meletakkan situs kami langsung di folder root (penjelasan @InitFs tanpa parameter).



Kami juga perlu memasukkan situs kami ke dalam file konfigurasi mods.conf dan menambahkan server httd itu sendiri di sana:



    include embox.cmd.net.httpd    
    include embox.demo.website
      
      





Juga, mari kita mulai server dengan situs web kita selama startup sistem. Untuk melakukan ini, tambahkan baris ke file conf / system_start.inc:



"service httpd /",
      
      





Secara alami, semua manipulasi ini perlu dilakukan dengan konfigurasi untuk papan. Setelah itu, kita kumpulkan dan lari. Kami masuk ke browser ke alamat papan Anda. Dalam kasus saya, 192.168.2.128



Dan kami memiliki gambaran yang sama dengan situs lokal







Kami bukan spesialis dalam pengembangan web, tetapi telah mendengar bahwa berbagai kerangka digunakan untuk membuat situs web yang indah. Misalnya, AngularJS sering digunakan . Oleh karena itu, kami akan memberikan contoh lebih lanjut tentang penggunaannya. Namun di saat yang sama, kami tidak akan membahas secara detail dan mohon maaf terlebih dahulu jika di suatu tempat kami telah sangat menyesuaikan dengan desain web.



Apapun konten statis yang kita taruh di folder situs, misalnya file js atau css, kita bisa menggunakannya tanpa usaha tambahan.



Mari tambahkan app.js (situs sudut) ke situs kita dan di dalamnya beberapa tab. Kami akan meletakkan halaman untuk tab ini di folder parsial, gambar di folder gambar /, dan file css di css /.



$ ls website/
app.js  css  images  index.html  Mybuild  partials
      
      





Mari luncurkan situs web kami.







Setuju, situs tersebut terlihat jauh lebih akrab dan menyenangkan. Dan semua ini dilakukan di sisi browser. Seperti yang kami katakan, seluruh konteksnya masih statis. Dan kami dapat mengembangkannya di host seperti situs web biasa.



Secara alami, Anda dapat menggunakan semua alat pengembangan dari pengembang web umum. Jadi, membuka konsol di browser, kami menemukan pesan kesalahan bahwa favicon.ico tidak ada: Kami







menemukan bahwa ini adalah ikon yang ditampilkan di tab browser. Anda dapat, tentu saja, meletakkan file dengan nama ini, tetapi terkadang Anda tidak ingin menghabiskan uang untuk tempat ini. Izinkan saya mengingatkan Anda bahwa kami ingin menjalankan juga di mikrokontroler yang memiliki sedikit memori.



Pencarian di Internet segera menunjukkan bahwa Anda dapat melakukannya tanpa file, Anda hanya perlu menambahkan baris ke bagian kepala html. Meskipun kesalahan tidak mengganggu, selalu menyenangkan untuk membuat situs sedikit lebih baik. Dan yang paling penting, kami memastikan bahwa alat pengembang biasa cukup dapat diterapkan dengan pendekatan yang diusulkan.



Konten dinamis



CGI



Mari beralih ke konten dinamis. Common Gateway Interface (CGI) adalah antarmuka untuk interaksi server web dengan utilitas baris perintah, yang memungkinkan pembuatan konten dinamis. Dengan kata lain, CGI memungkinkan Anda menggunakan keluaran utilitas untuk menghasilkan konten dinamis.



Mari kita lihat beberapa skrip CGI:



#!/bin/bash

echo -ne "HTTP/1.1 200 OK\r\n"
echo -ne "Content-Type: application/json\r\n"
echo -ne "Connection: Connection: close\r\n"
echo -ne "\r\n"

tm=`LC_ALL=C date +%c`
echo -ne "\"$tm\"\n\n"
      
      





Pertama, kepala http dicetak ke keluaran standar, kemudian data halaman itu sendiri dicetak. keluaran dapat diarahkan ke mana saja. Anda cukup menjalankan skrip ini dari konsol. Kami akan melihat yang berikut ini:



./cgi-bin/gettime
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Connection: close

"Fri Feb  5 20:58:19 2021"
      
      





Dan jika bukan keluaran standarnya adalah soket, maka browser akan menerima data ini.



CGI sering diimplementasikan dengan skrip, bahkan skrip cgi dikatakan. Tetapi ini tidak perlu, hanya saja dalam bahasa scripting hal-hal seperti itu lebih cepat dan lebih nyaman. Sebuah utilitas yang menyediakan CGI dapat diimplementasikan dalam bahasa apapun. Dan karena kami fokus pada mikrokontroler, oleh karena itu, kami mencoba untuk berhati-hati dalam menghemat sumber daya. Mari lakukan hal yang sama di C.



#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char buf[128];
    char *pbuf;
    struct timeval tv;
    time_t time;

    printf(
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: application/json\r\n"
        "Connection: Connection: close\r\n"
        "\r\n"
    );


    pbuf = buf;

    pbuf += sprintf(pbuf, "\"");

    gettimeofday(&tv, NULL);
    time = tv.tv_sec;
    ctime_r(&time, pbuf);

    strcat(pbuf, "\"\n\n");

    printf("%s", buf);

    return 0;
}
      
      





Jika kita mengkompilasi kode ini dan menjalankannya, kita akan melihat keluaran yang persis sama seperti pada kasus skrip.



Di app.js kita, mari tambahkan penangan untuk memanggil skrip CGI untuk salah satu tab kita:



app.controller("SystemCtrl", ['$scope', '$http', function($scope, $http) {
    $scope.time = null;

    $scope.update = function() {
        $http.get('cgi-bin/gettime').then(function (r) {
            $scope.time = r.data;
        });
    };

    $scope.update();
}]);
      
      





Nuansa kecil untuk berjalan di Linux menggunakan server python built-in. Kita perlu menambahkan argumen --cgi ke baris peluncuran kita untuk mendukung CGI:



python3 -m http.server --cgi -d .
      
      









Pembaruan otomatis konten dinamis



Sekarang mari kita lihat properti lain yang sangat penting dari situs dinamis - pembaruan konten otomatis. Ada beberapa mekanisme pelaksanaannya:



  • Server Side Includes (SSI)
  • Acara yang Dikirim Server (SSE)
  • WebSockets
  • Dll


Server Side Includes (SSI)



Server Side Includes (SSI) . Ini adalah bahasa yang tidak rumit untuk membuat halaman web secara dinamis. Biasanya file yang menggunakan SSI dalam format .shtml.



SSI sendiri bahkan punya arahan kontrol, kalau lagi, dan seterusnya. Tetapi di sebagian besar contoh mikrokontroler yang kami temukan, digunakan sebagai berikut. Sebuah direktif dimasukkan ke dalam halaman .shtml yang memuat ulang seluruh halaman secara berkala. Ini bisa jadi, misalnya:



<meta http-equiv="refresh" content="1">
      
      





Atau:



<BODY onLoad="window.setTimeout("location.href='runtime.shtml'",2000)">
      
      





Dan dengan satu atau lain cara, konten dihasilkan, misalnya, dengan menyetel penangan khusus.



Keuntungan dari metode ini adalah kesederhanaannya dan kebutuhan sumber daya yang minimal. Namun di sisi lain, berikut adalah contoh tampilannya.







Refresh halaman (lihat tab) sangat terlihat. Dan memuat ulang seluruh halaman tampak seperti tindakan yang terlalu berlebihan.



Contoh standar dari FreeRTOS disediakan - https://www.freertos.org/FreeRTOS-For-STM32-Connectivity-Line-With-WEB-Server-Example.html



Acara yang dikirim server



Server-sent Events (SSE) adalah mekanisme yang memungkinkan koneksi setengah dupleks (satu arah) antara klien dan server. Klien dalam hal ini membuka koneksi dan server menggunakannya untuk mentransfer data ke klien. Pada saat yang sama, tidak seperti skrip CGI klasik, yang tujuannya adalah untuk menghasilkan dan mengirim respons ke klien, dan kemudian menyelesaikannya, SSE menawarkan mode "berkelanjutan". Artinya, server dapat mengirim data sebanyak yang diperlukan hingga selesai sendiri, atau klien menutup koneksi.



Ada beberapa perbedaan kecil dari skrip CGI biasa. Pertama, header http akan sedikit berbeda:



        "Content-Type: text/event-stream\r\n"
        "Cache-Control: no-cache\r\n"
        "Connection: keep-alive\r\n"
      
      





Koneksi, seperti yang Anda lihat, tidak dekat, tetapi tetap hidup, yaitu koneksi yang sedang berlangsung. Untuk mencegah browser menyimpan data dalam cache, Anda perlu menentukan Cache-Control no-cache. Terakhir, Anda perlu menentukan bahwa jenis data khusus Jenis konten teks / aliran acara digunakan.



Tipe data ini adalah format khusus untuk SSE :



: this is a test stream

data: some text

data: another message
data: with two lines

      
      





Dalam kasus kami, data perlu dikemas ke dalam baris berikut:



data: { β€œtime”: β€œ<real date>”}
      
      





Skrip CGI kita akan terlihat seperti:



#!/bin/bash

echo -ne "HTTP/1.1 200 OK\r\n"
echo -ne "Content-Type: text/event-stream\r\n"
echo -ne "Cache-Control: no-cache\r\n"
echo -ne "Connection: keep-alive\r\n"
echo -ne "\r\n"

while true; do
    tm=`LC_ALL=C date +%c`
    echo -ne "data: {\"time\" : \"$tm\"}\n\n" 2>/dev/null || exit 0
    sleep 1
done
      
      





Output jika Anda menjalankan skrip:



$ ./cgi-bin/gettime
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

data: {"time" : "Fri Feb  5 21:48:11 2021"}

data: {"time" : "Fri Feb  5 21:48:12 2021"}

data: {"time" : "Fri Feb  5 21:48:13 2021"}
      
      







Dan seterusnya, sekali dalam satu detik.



Hal yang sama di C:



#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char buf[128];
    char *pbuf;
    struct timeval tv;
    time_t time;

    printf(
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/event-stream\r\n"
        "Cache-Control: no-cache\r\n"
        "Connection: keep-alive\r\n"
        "\r\n"
    );

    while (1) {
        pbuf = buf;

        pbuf += sprintf(pbuf, "data: {\"time\" : \"");

        gettimeofday(&tv, NULL);
        time = tv.tv_sec;
        ctime_r(&time, pbuf);

        strcat(pbuf, "\"}\n\n");

        if (0 > printf("%s", buf)) {
            break;
        }

        sleep(1);
    }

    return 0;
}

      
      





Dan terakhir, kita juga perlu memberi tahu angular bahwa kita memiliki SSE, yaitu memodifikasi kode untuk controller kita:



app.controller("SystemCtrl", ['$scope', '$http', function($scope, $http) {
    $scope.time = null;

    var eventCallbackTime = function (msg) {
        $scope.$apply(function () {
            $scope.time = JSON.parse(msg.data).time
        });
    }

    var source_time = new EventSource('/cgi-bin/gettime');
    source_time.addEventListener('message', eventCallbackTime);

    $scope.$on('$destroy', function () {
        source_time.close();
    });

    $scope.update = function() {
    };

    $scope.update();
}]);
      
      





Kami meluncurkan situs, kami melihat yang berikut:







Terlihat bahwa, tidak seperti menggunakan SSI, halaman tidak membebani secara berlebihan, dan data diperbarui dengan lancar dan menyenangkan bagi mata.



Demo



Tentu saja contoh yang diberikan tidak nyata karena sangat sederhana. Tujuan mereka adalah untuk menunjukkan perbedaan antara pendekatan yang digunakan pada mikrokontroler dan sistem lain.



Kami membuat demo kecil dengan tugas nyata. Mengontrol LED, menerima data waktu nyata dari sensor kecepatan sudut (giroskop) dan tab dengan informasi sistem.



Situs ini dikembangkan di host. Anda hanya perlu membuat colokan kecil untuk meniru LED dan data dari sensor. Data sensor hanyalah nilai acak yang diterima melalui ACAK standar



#!/bin/bash

echo -ne "HTTP/1.1 200 OK\r\n"
echo -ne "Content-Type: text/event-stream\r\n"
echo -ne "Cache-Control: no-cache\r\n"
echo -ne "Connection: keep-alive\r\n"
echo -ne "\r\n"

while true; do
    x=$((1 + $RANDOM % 15000))
    y=$((1 + $RANDOM % 15000))
    z=$((1 + $RANDOM % 15000))
    echo -ne "data: {\"rate\" : \"x:$x y:$y z:$z\"}\n\n" 2>/dev/null || exit 0
    sleep 1
done
      
      





Kami hanya menyimpan status LED dalam sebuah file.



#!/bin/python3

import cgi
import sys

print("HTTP/1.1 200 OK")
print("Content-Type: text/plain")
print("Connection: close")
print()

form = cgi.FieldStorage()
cmd = form['cmd'].value

if cmd == 'serialize_states':
    with open('cgi-bin/leds.txt', 'r') as f:
        print('[' + f.read() + ']')

elif cmd == 'clr' or cmd == 'set':
    led_nr = int(form['led'].value)

    with open('cgi-bin/leds.txt', 'r+') as f:
        leds = f.read().split(',')
        leds[led_nr] = str(1 if cmd == 'set' else 0)
        f.seek(0)
        f.write(','.join(leds))
      
      





Hal yang sama diimplementasikan dengan mudah pada varian C. Jika mau, Anda dapat melihat kode di folder repositori (proyek / situs web).



Pada mikrokontroler, tentunya digunakan implementasi yang berinteraksi dengan periferal nyata. Tetapi karena ini hanyalah perintah dan driver, mereka di-debug secara terpisah. Oleh karena itu, transfer situs ke mikrokontroler tidak memakan waktu.



Tangkapan layar yang berjalan pada host terlihat seperti ini.







Dalam video singkat Anda dapat melihat pekerjaan pada mikrokontroler yang sebenarnya. Perhatikan bahwa tidak hanya ada komunikasi melalui http, tetapi juga, misalnya, mengatur tanggal menggunakan ntp dari baris perintah di Embox, dan tentu saja menangani periferal.





Secara mandiri, semua yang diberikan dalam artikel dapat direproduksi sesuai dengan instruksi di wiki kami



Kesimpulan



Dalam artikel tersebut, kami menunjukkan bahwa dimungkinkan untuk mengembangkan situs interaktif yang indah dan menjalankannya di mikrokontroler. Selain itu, ini dapat dilakukan dengan mudah dan cepat menggunakan semua alat pengembangan untuk host dan kemudian dijalankan dari mikrokontroler. Secara alami, pengembangan situs dapat dilakukan oleh perancang web profesional, sedangkan pengembang yang tertanam akan menerapkan logika perangkat. Yang sangat nyaman dan menghemat waktu ke pasar.



Tentu, Anda harus membayar untuk ini. Ya, SSE akan membutuhkan sumber daya yang sedikit lebih banyak daripada SSI. Tetapi dengan bantuan Embox, kami dengan mudah masuk ke STM32F4 tanpa pengoptimalan dan hanya menggunakan RAM 128 KB. Mereka tidak memeriksa apa pun yang kurang. Jadi biaya overhead tidak terlalu besar. Dan kenyamanan pengembangan dan kualitas situs itu sendiri jauh lebih tinggi. Dan pada saat yang sama, tentu saja, jangan lupa bahwa mikrokontroler modern telah berkembang pesat dan terus melakukannya. Lagi pula, perangkat dituntut untuk semakin cerdas.



All Articles