
NodeMCU adalah firmware interaktif , yang memungkinkan menjalankan interpreter Lua pada mikrokontroler ESP8266 (dukungan ESP32 sedang dalam pengembangan). Bersamaan dengan semua antarmuka perangkat keras biasa, ia memiliki modul WiFi dan sistem file SPIFFS .
Artikel ini menjelaskan modul baru untuk NodeMCU - sdm. SDM merupakan kependekan dari model driver sederhana dan menyediakan abstraksi model driver-perangkat untuk sistem. Pada bagian pertama artikel ini kita akan membahas model itu sendiri dan pada bagian kedua akan menjadi karya antarmuka pengguna web yang dibuat secara dinamis menggunakan sdm dengan beberapa komentar.
Dasar-dasar model driver
Dua komponen utama dari model ini adalah perangkat dan driver . Perangkat adalah representasi abstrak dari beberapa perangkat keras atau perangkat virtual. Masuk akal untuk menempatkan perangkat ke hierarki pohon, dengan mikrokontroler di atas, bus di tengah dan sensor sebagai dedaunan.
DEVICES + DRIVERS | +-----+ | +-----+ |1WIRE<----------------------+1WIRE| ++-+-++ | +-----+ | | | | +---------+ | +--------+ | +------+ | | | +------+DS1820| +---v----+ +---v----+ +---v----+ | | +------+ |DS1820|0| |DS1820|1| |DS1822|0| | | +---^----+ +---^----+ +---^----+ | | +------+ | | +--------------+DS1822| | | | | +------+ +-----------+------------------+ +
Driver perangkat adalah sepotong logika yang terkait dengan perangkat yang diberikan. Fungsi yang disediakan oleh driver disebut metode , wadah data yang terkait dengan driver disebut atribut . Baik metode dan atribut tinggal di dalam driver.
Atribut memiliki dua fungsi yang terkait dengannya: kait pengambil dan penyetel . Jadi atribut fungsionalitas metode superset, tetapi mereka juga mengambil lebih banyak memori (memori mikrokontroler langka, ingat?).
sdm.attr_add(drv,
Mengikat perangkat
Bagian rumit dari model driver adalah pengikatan device-driver. Prosesnya sendiri cukup sederhana: kami mencocokkan perangkat dengan masing-masing driver yang tersedia hingga cocok. Hanya dua bagian yang hilang - logika yang cocok dan beberapa data untuk dicocokkan.
Dalam sdm, logika yang cocok tinggal di driver dengan nama _poll()
. Ini adalah metode biasa yang disebut dengan gagang perangkat sebagai parameter dan mengembalikan true
atau false
jika perangkat dapat atau tidak dapat dilampirkan ke driver masing-masing.
sdm.method_add(drv, "_poll", nil, function(dev, drv, par) local attr = sdm.attr_data(sdm.local_attr_handle(dev, "id"))
Seperti terlihat pada contoh di atas, driver mencocokkan perangkat menggunakan atribut. Tetapi seperti disebutkan di atas, atribut hanya diasosiasikan dengan driver. Secara umum memang benar, tetapi ada beberapa atribut yang tidak dapat diambil melalui perangkat lunak. Ini adalah chip ID, pin bekas dll. Bagi mereka jenis atribut khusus telah ditambahkan ke atribut sdm - local . Atribut ini dikaitkan dengan satu instance perangkat dan biasanya tidak berubah.
Satu-satunya hal yang tersisa untuk dikatakan tentang pengikatan driver. Biasanya perangkat memerlukan semacam inisialisasi pada startup dan pembersihan setelah digunakan. Untuk tujuan ini sdm menggunakan _init()
dan _free()
.
Jika driver memiliki metode _init()
maka itu akan dipanggil secara otomatis setelah perangkat mengikat. Sama dengan _free()
.
sdm.method_add(drv, "_init", nil, function(dev, drv, par) sdm.device_rename(dev, sdm.request_name("DS18B20"))
Pembaca yang penuh perhatian mungkin akan bertanya: apa artinya "menyalin atribut" pada contoh di atas? Dan dia akan benar, karena ini ada hubungannya dengan atribut jenis ketiga yang belum kita diskusikan - atribut pribadi . Tidak masuk akal untuk memiliki semua data atribut yang dibagikan di antara semua perangkat contoh. Untuk keperluan ini sdm menyediakan mekanisme menyalin atribut dari driver dan menghubungkannya dengan perangkat. Ini membuat atribut driver prototipe atau templat.
Ringkasan cepat:
- atribut lokal digunakan untuk data yang tidak dapat diambil oleh perangkat lunak. Seperti ID perangkat, pin yang terhubung, dll.
- atribut driver digunakan untuk data yang dibagikan di antara semua instance perangkat yang terlampir pada driver ini.
- atribut pribadi disalin dari atribut driver dan menyimpan data yang terkait dengan hanya satu perangkat contoh. Jenis ini adalah yang paling umum.
Implementasi antarmuka pengguna web
Kode server
Ada proyek nodemcu-httpserver yang indah yang mengimplementasikan kode server untuk NudeMCU. Sayangnya sepertinya sudah mati. Itu digunakan sebagai dasar untuk server. Pertama, fungsi server dipindahkan ke LFS dan kemudian sedikit dimodifikasi untuk melayani satu halaman statis untuk setiap panggilan. Vue.js adalah pilihan sempurna untuk halaman web berbasis template. Jadi itu digunakan untuk frontend . Perlu dicatat bahwa NodeMCU mungkin tidak terhubung ke Internet. Karena itu, pustaka vue.js
harus hadir secara lokal dan dilayani oleh server NodeMCU.
Karena semua perangkat disusun dalam struktur pohon, mereka diakses seperti direktori: /ESP8266/ESP8266_1W/DS18S20-0
. Di sini /ESP8266
adalah halaman NodeMCU, /ESP8266/ESP8266_1W
adalah halaman bus 1Wire dan akhirnya /ESP8266/ESP8266_1W/DS18S20-0
adalah sensor suhu.
Seperti disebutkan sebelumnya, semua halaman perangkat dibuat dari satu halaman template yang disajikan pada setiap panggilan. Kode JS di dalam halaman ini kemudian membuat permintaan ke URL yang sama, diawali dengan /api
. Untuk contoh URL panggilan di atas adalah /api/ESP8266/ESP8266_1W/DS18S20-0
. Atas permintaan tersebut, server merespons dengan data spesifik perangkat yang disandikan JSON , yang mengisi halaman tersebut. Tentu saja, permintaan halaman HTML dapat dilewati jika hanya data mentah yang diperlukan.
Pohon perangkat
Konfigurasi perangkat awal dilakukan menggunakan struktur pohon perangkat sederhana . Ini seperti pohon perangkat , tetapi lebih sederhana. Ini menjelaskan konfigurasi perangkat keras termasuk atribut lokal perangkat.
local root={
Pengaturan perangkat keras
Di sinilah dimulai showcase. Untuk keperluan ini, sekelompok sensor terhubung ke NodeMCU:
1 Sensor kawat terhubung ke pin yang sama.

Halaman web dan driver
perangkat root
Tujuan utama perangkat root (alias ESP8266) adalah untuk menyediakan tempat bagi anak-anak untuk terhubung. Namun tidak terbatas untuk memiliki metode atau atribut yang terkait dengannya.
Cuplikan kode ini dari sini :
sdm.method_add(drv, "_init", nil, function(dev, drv, par) local attr = sdm.attr_handle(dev, "id")
Kode ini menambahkan atribut float
yang digunakan untuk menampung tipe build firmware. Nilainya diinisialisasi dalam kait _init()
yang merupakan fungsi khusus, yang berjalan satu kali ketika driver menempel ke perangkat.
Ini adalah halaman yang dihasilkan untuk perangkat root.

Di sini kita dapat melihat bahwa perangkat root memiliki satu metode heap
, dua atribut driver float
dan id
. Akhirnya, ia memiliki dua perangkat yang terhubung - SPI dan 1Wire bus.
SPI
Pengemudi SPI tidak terlalu menarik. Itu hanya memetakan fungsi NodeMCU SPI .

Mcp3208
MCP3208 adalah chip ADC . Ini mengukur tegangan dari nol ke ref dan mengembalikan kode 12 bit. Yang menarik dari implementasi driver ini adalah bahwa atribut ref
hanya akan hadir jika firmware mendukung aritmatika floating point. Jika tidak didukung maka alih-alih tegangan absolut, kode tegangan dikembalikan oleh metode single
dan differential
.
sdm.method_add(drv, "single", "Single ended measure 0|1|2|3|4|5|6|7", function(dev, channel)

Perhatikan juga bahwa perangkat ini memiliki atribut ref
ditandai sebagai pribadi . Ini diatur berdasarkan per perangkat.
1wire
1 Driver driver mengimplementasikan metode poll
- pencarian dinamis untuk perangkat .
Tepat setelah penemuan perangkat jenisnya tidak diketahui. Jadi alamat unik 1Wire -nya digunakan sebagai nama perangkat baru (byte diwakili sebagai angka yang dipisahkan oleh karakter _
).
sdm.method_add(drv, "poll", "Poll for devices", function(bus, pin) local children = sdm.device_children(bus) or {}
Ini adalah halaman awal untuk driver 1Wire .

Setelah mengeluarkan panggilan poll
dengan argumen 2
dan halaman yang menyegarkan, bagian anak-anak muncul. Perhatikan bahwa nama anak-anak dapat dibaca manusia. Ini karena fungsi device_rename()
dipanggil selama _init
mereka.

DS18S20
Setelah inisialisasi, driver DS18S20 memeriksa bahwa ID perangkat dimulai dengan 0x10
, yang merupakan kode keluarga perangkat. Ketika perangkat terpasang ke driver, namanya diganti menjadi DS18S20-X
, di mana DS18S20
adalah nama dasar dan X
adalah nomor instan.
sdm.method_add(drv, "_poll", nil, function(dev, drv, par) local attr = sdm.attr_data(sdm.local_attr_handle(dev, "id")) if attr == nil then return false end return (sdm.device_name(par) == "ESP8266_1W") and (attr:byte(1) == 0x10)

Atribut lokal id
dan datapin
tidak memiliki kait getter
dan setter
, jadi hanya nama mereka yang terlihat.
DS18B20
Driver DS18B20 hampir sama dengan driver DS18S20 . Satu-satunya perbedaan adalah metode precision
. Kedua driver DS18? 20 menganggap build integer dan tidak menggunakan divisi floating point.
sdm.attr_add(drv, "precision", "Precision (9|10|11|12)", 12, function(dev, precision) local attr = sdm.attr_dev_handle(dev, "precision") return sdm.attr_data(attr) end, function(dev, precision) local par = sdm.device_parent(dev) local attr = sdm.attr_dev_handle(dev, "precision") local ex = sdm.method_func(sdm.method_dev_handle(par, "exchange")) local modes = {[9]=0x1f, [10]=0x3f, [11]=0x5f, [12]=0x7f} if modes[precision] ~= nil then ex(par, dev, {0x4e, 0, 0, modes[precision]}) sdm.attr_set(attr, precision) end end )

Penggunaan memori
Memori bebas ESP8266 sekitar 40k . Kode server dipindahkan ke LFS , sehingga tidak memerlukan ruang RAM saat inisialisasi ( kode asli memakan waktu sekitar 10rb ).
SDM membutuhkan sekitar 10rb untuk 5 driver perangkat dan 5 perangkat. Sedikit lebih rendah untuk versi firmware non-mengambang. Jadi lebih baik untuk memilih driver manifes hanya driver yang diperlukan untuk tugas yang dihadapi. Tugas paling banyak memakan memori adalah untuk melayani perpustakaan vue.js

Dalam hal meminta data mentah JSON- encoded (menggunakan curl
), konsumsi memori puncak dapat dikurangi secara signifikan.

Alih-alih sebuah epilog
Salah satu metode pertama yang saya terapkan dengan sdm adalah pengikatan untuk
node.restart()
.
Mencoba menggunakan antarmuka pengguna web menghasilkan hasil yang aneh. Tepat setelah browser web mengeluarkan permintaan, chip dimulai kembali seperti yang diharapkan. Tetapi karena NodeMCU tidak menanggapi dengan baik permintaan HTTP, browser web mencoba lagi permintaan yang sama. Ketika server NodeMCU dihidupkan ulang dan dinyalakan kembali, browser terhubung, reset internal coba lagi dan disebut metode node.restart()
, sehingga memulai loop tak terbatas dari restart NodeMCU.