Dalam publikasi ini, saya akan membagikan pengalaman saya tentang membuat perangkat IoT dari awal: dari munculnya ide dan penerapannya dalam perangkat keras hingga pembuatan firmware untuk pengontrol dan antarmuka web untuk mengelola perangkat yang dibuat melalui Internet.
Sebelum membuat perangkat ini, saya:
- Hampir tidak mengerti sirkuit. Hanya pada level prinsip kerja
resistor / transistor ... Saya tidak punya pengalaman dalam membuat sirkuit yang rumit. - Tidak pernah merancang papan sirkuit.
- Komponen SMD tidak pernah disolder. Tingkat besi solder berada pada tingkat kawat solder dan semacam relay.
- Saya belum pernah menulis program yang sedemikian rumit untuk mikrokontroler. Seluruh pengalaman berada pada level "menyalakan LED di Arduino", dan saya pertama kali bertemu dengan pengontrol ESP8266.
- Saya menulis sedikit C ++ untuk "kakak", tapi itu lebih dari selusin tahun yang lalu dan semuanya sudah lama terlupakan.
Tentu saja, pengalaman bekerja sebagai programmer (terutama Microsoft .NET) dan pemikiran sistemik membantu saya memahami topik tersebut. Saya pikir pembaca akan dapat melakukannya. Tautan dan artikel bermanfaat di laut Internet. Yang paling, menurut pendapat saya, menarik, dan membantu untuk memahami topik, saya bawa artikel.
Pernyataan masalah
Saya tinggal di sebuah rumah pribadi di dekat Minsk, dan kolam renang saya sendiri, meskipun kerangka yang paling sederhana, adalah bagian integral dari rangkaian "manfaat" yang diperoleh banyak orang di rumah pedesaan. Dalam iklim kita yang tidak stabil, ternyata berenang di kolam tidak nyaman jika berada di luar ruangan: air mendingin di malam hari, dan cuaca berangin di siang hari tidak membuat berenang nyaman. Tahun lalu, dengan tangan saya sendiri, saya membangun kubah geodesik yang lebih penuh di atas kolam, meletakkan bukit dan menggantung bungee - anak-anak senang.

Laporan foto pembangunan kubah di Flickr.
Tahun ini saya melangkah lebih jauh dan memutuskan untuk mengatur pemanas kolam dari boiler gas,
yang berfungsi untuk memanaskan rumah di musim dingin dan memanaskan air panas di musim panas.
Di musim panas, sirkuit "pemanas" boiler dengan bantuan katup beralih ke pemanas
kolam renang. Air kolam dipanaskan dengan bantuan penukar panas titanium, sirkuit utama yang melewati pendingin (air panas tanpa kotoran) dari sirkuit pemanas, dan air sekunder dari kolam, dipompa oleh pompa resirkulasi dari sistem filtrasi. Karena saya menggunakan kolam renang dengan klorinator (banyak topik menarik dijelaskan di ForumHouse ), airnya mengandung sedikit garam dan penukar panas titanium diperlukan. Anda tidak bisa hanya mengambil dan membiarkan air langsung melalui boiler - jika tidak, Anda akan merusak semua pipa dengan garam.

Melewati penukar panas, pembawa panas yang dipanaskan oleh boiler dengan suhu sekitar 70-90 ° C mengeluarkan panas ke air dari kolam, memanaskannya dengan beberapa derajat. Pendingin itu sendiri mendingin beberapa puluh derajat, dan kembali ke ketel agar kembali
pemanasan. Rasio pendinginan air dari boiler ke pemanas air kolam tergantung pada banyak faktor: kapasitas penukar panas dan kecepatan sirkulasi air di sirkuit primer dan sekunder.
Pipa yang terhubung dari kolam ke penukar panas adalah pipa polietilen biasa, yaitu pipa itu
saat ini digunakan untuk memasok air dingin ke rumah-rumah pribadi. Murahnya, kemampuan untuk menahan tekanan yang layak, tidak adanya korosi - ini adalah keunggulan utama pipa tersebut. Untuk semua, tanpa kecuali, pipa polietilen, suhu operasi dibatasi hingga 40 derajat Celcius. Pada prinsipnya, ini lebih dari cukup untuk kolam renang.
Namun, ada kemungkinan darurat yang tinggi jika pompa
resirkulasi air kolam air akan berhenti karena suatu alasan, dan boiler akan terus memanaskan penukar panas: dalam hal ini, air di sirkuit sekunder dari penukar panas akan naik cukup cepat ke suhu sirkuit primer, yang berarti bahwa bagian-bagian pipa polietilen yang berdekatan dengan penukar panas akan meleleh, dan air dari kolam akan meluap semua ruang di sekitar.
Harus dimungkinkan untuk melindungi penukar panas dari panas.
Perbaikan cepat
Untuk mengatasi masalah ini, sensor aliran yang beroperasi berdasarkan prinsip efek aula dimasukkan dalam sirkuit sirkuit resirkulasi air kolam. Selain itu, sensor suhu terletak di sirkuit sekunder
penukar panas, memberikan pertahanan tingkat kedua, melacak kemungkinan panas berlebih.
Tidak mungkin mengontrol panas berlebih hanya dengan sensor suhu: sistem memiliki inersia besar: setelah penghentian tiba-tiba air di rangkaian kolam, pada
mematikan boiler, suhu masih terus naik untuk beberapa waktu, seperti boiler masih menggerakkan air yang dipanaskan di sepanjang sirkuit oleh inersia, mencegah overheating "saya, kekasihku".
Karena itu, penting untuk merespons sesegera mungkin: yaitu menghentikan aliran air di sirkuit
kolam renang.
Sensor aliran digunakan seperti itu . Kasing plastik dan kurangnya kontak sensor dengan air memungkinkan untuk digunakan dalam air asin.
Sensor suhu, diputuskan untuk menggunakan Dallas DS18B20, mereka mudah untuk menghubungkan beberapa bagian sekaligus pada satu bus 1-Wire .

Diputuskan untuk menggantung sepasang sensor pada input dan output dari kedua sekunder dan primer
sirkuit: total 4 sensor. Keuntungan tambahan dari pendekatan ini adalah
kemampuan untuk memonitor parameter sistem: Anda dapat memantau seberapa banyak cairan pendingin di sirkuit primer didinginkan dan berapa banyak air dari kolam yang dipanaskan di sirkuit sekunder. Jadi - untuk memantau optimalitas pemanasan dan memprediksi waktu pemanasan.
Lokasi sensor pada penukar panas dan pipa saluran masuk Parameter Perangkat
Prototipe pertama perangkat ini dibangun atas dasar Arduino Uno, dan berhasil diluncurkan.

Tetapi kemudian menjadi jelas bahwa saya ingin lebih. Dipanaskan 16 meter kubik air, bahkan adil
beberapa derajat tidak cepat. Dan saya ingin langsung memonitor parameter pemanasan dari pekerjaan, hidupkan / matikan. Tetapi pada saat yang sama akan menarik untuk memotret jadwal pemanasan, misalnya, per hari.
Ya, karena kita sudah mendapatkan perangkat IoT, lalu mengapa kita tidak sekaligus mengontrol aktivasi jarak jauh dari pool chlorinator dan memompanya?
Kerangka Acuan
Jadi, diputuskan untuk mengembangkan perangkat - pengendali kolam multifungsi. Dia harus dapat:
- Untuk mengontrol pemanasan kolam melalui penukar panas, menyalakan / mematikan boiler gas untuk air pemanas.
- Cegah overheating penukar panas dengan memonitor keberadaan aliran air kolam di sirkuit sekunder dan suhu berlebih dari sirkuit sekunder.
- Tampilkan statistik pemanasan secara real time (suhu di inlet dan outlet kedua sirkuit).
- Rekam (log) nilai suhu dalam memori flash. Tampilkan data untuk
periode tertentu dalam bentuk grafik. - Menggunakan relay, dapat menghidupkan / mematikan pompa kolam dan klorinator.
- Kelola semua parameter perangkat dari jarak jauh melalui server web mikro internal.
Ada juga godaan untuk mengacaukan Blink, MQTT. Tapi dari "lonceng dan peluit" ini pada tahap pertama
Diputuskan untuk menolak. Dan terlebih lagi, saya tidak ingin mengambil kemungkinan kontrol di suatu tempat di luar. Server web bawaan untuk tujuan saya sudah cukup. Dan keamanan dipastikan oleh fakta bahwa Anda dapat memasuki jaringan rumah dari dunia luar hanya melalui VPN.
Perangkat keras
Sebagai pengontrol, diputuskan untuk menggunakan ESP8266 yang murah dan populer. Itu sempurna untuk keperluan saya, kecuali untuk satu hal: tingkat sinyal yang cocok dari sensor 5-volt dengan logika pengontrol 3,3-volt. Pada prinsipnya, sensor Dallas tampaknya bekerja pada 3 volt, tetapi saya memiliki garis yang cukup panjang dari pengontrol ke sensor, sekitar 7 meter. Karena itu, lebih baik menambah tegangan.
Ditentukan bahwa perlu memiliki perangkat keras:
- ESP8266 controller atau kakaknya ESP32 (sebagai modul DevKit ).
- Penyelarasan level sinyal untuk sensor.
- Regulator daya adalah bagian 5 volt dari rangkaian.
- Modul kontrol relai.
- Jam RTC + memori flash untuk logging.
- Layar LCD 2 garis paling sederhana untuk menampilkan nilai sensor saat ini dan status perangkat serta relai.
- Beberapa tombol fisik untuk mengontrol keadaan perangkat tanpa akses melalui web.
Banyak komponen dari daftar dijual sebagai modul untuk Arduino dan banyak modul yang kompatibel dengan logika 3.3v. Namun, saya tidak ingin "macet" semua ini di papan tempat memotong roti dengan bundel kawat, karena saya ingin memiliki "perangkat" yang indah dan rapi. Ya, dan untuk uang yang diberikan kepada orang Cina untuk modul-modul, Anda dapat sepenuhnya menggambar dan memesan papan sirkuit cetak Anda sendiri, dan harapan kedatangannya akan dikompensasi oleh instalasi yang relatif cepat dan dapat diandalkan.
Sekali lagi, saya perhatikan bahwa ini adalah pengalaman pertama saya di sirkuit dan dalam mendesain perangkat keras dari hal-hal seperti itu. Saya harus belajar banyak. Memang, dalam spesialisasi saya, saya agak menyendiri dari mikrokontroler. Tetapi untuk melakukan segala yang "berlutut" tidak memungkinkan semangat perfeksionisme yang hidup dalam diri saya.
Diagram sirkuit
Ada banyak program di pasaran yang memungkinkan Anda menggambar sirkuit dan papan sirkuit cetak. Tanpa pengalaman di bidang ini, saya langsung menyukai EasyEDA - editor online gratis yang memungkinkan Anda untuk menggambar diagram sirkuit dengan indah, memeriksa bahwa tidak ada yang terlupakan dan semua komponen memiliki koneksi, menggambar papan sirkuit cetak, dan kemudian segera memesan produksinya.
Kesulitan pertama yang saya temui: ada banyak opsi untuk pengontrol DevKit ESP8266 atau ESP32, beberapa di antaranya berbeda di lokasi pin dan tujuannya, dan beberapa bahkan lebarnya. Diputuskan untuk menggambar sirkuit sehingga memungkinkan untuk menempatkan DevKit dengan lebar dan dengan lokasi terminal apa pun, dan di sisi-sisinya - 2 baris pasangan lubang jumper, dan kemudian kabel untuk menghubungkan terminal yang diperlukan, sehubungan dengan pengontrol yang dibeli secara khusus.
Tempatkan di bawah pengontrol dan 2 baris jumper berpasangan: JH1 dan JH2 dalam diagram:

Lokasi pin input 5v dan output 3.3v dari catu daya built-in stabilizer, serta GND, bagi saya sama untuk DevKit yang berbeda, tetapi saya masih memutuskan untuk memainkannya dengan aman dan juga membuat mereka jumper: JP1, JP2, JP3 pada diagram.
Saya memutuskan untuk menandatangani jumper dengan menghubungkannya ke komponen sirkuit dengan fungsi yang mungkin akan mereka lakukan.
Dan inilah yang terlihat dengan DevKit ESP8266, yang akhirnya saya beli dan instal Di sini D1 (GPIO5) dan D2 (GPIO4) bertanggung jawab untuk bus I2C, D5 (GPIO14) untuk 1-Wire, D6 (GPIO12) - untuk menerima pulsa dari sensor aliran.
Diagram sirkuit:

(gambar dapat diklik)
Meskipun hadir di papan ESP8266 dari regulator daya built-in untuk 3.3v, kita masih perlu memiliki 5 volt untuk menyalakan sensor dan LCD, dan 12 volt untuk menyalakan relay. Diputuskan untuk membuat daya papan 12 volt, dan menempatkan regulator tegangan AMS1117-5.0 pada input, memberikan 5 volt yang diinginkan pada output.
Untuk mencocokkan level sinyal pada bus 1-Wire, saya menggunakan transistor efek medan BSS138 c dengan tegangan "pull-up" di kedua sisi.

Sangat baik tentang pencocokan level ditulis dalam artikel Matching level logis perangkat 5V dan 3.3V .
Untuk menyesuaikan level sinyal sensor aliran, saya hanya menggunakan pembagi tegangan di resistor. Sensor aliran hanyalah perangkat kolektor terbuka . Beberapa sensor mungkin sudah memiliki resistor pull-up bawaan, ini harus dipertimbangkan:

Biru dalam diagram adalah sebutan skematis dari perakitan sensor aliran. Di sebelah kanan konektor adalah pembagi tegangan yang dipilih oleh saya untuk memiliki tingkat maksimum 3,3 volt pada output.
Di bus I2C, saya menggantung jam real-time DS3231SN dan memori flash AT24C256C untuk menyimpan log. Memori flash yang terpasang pada ESP8266 tidak cocok, karena memiliki sejumlah kecil siklus penulisan ulang (10 ribu berbanding 1 juta untuk AT24Cxxx, menurut lembar data).
Kontrol relay diatur pada sekelompok chip PCF8574AT dan ULN2803A.

Chip pertama adalah expander port mikrokontroler I2C. Status output aktif atau input PCF8574AT dipilih dengan memilih alamat pada bus I2C.
Chip ini memiliki beberapa fitur menarik, dijelaskan dengan baik dalam artikel I2C port expander PCF8574 .
Chip tidak dapat secara langsung mengontrol beban (relai). Untuk ini, digunakan matriks transistor ULN2803A. Ada fitur: matriks dapat dengan mudah menarik outputnya dengan beban ke tanah, yang berarti bahwa jika tegangan suplai diterapkan ke kutub kedua relai, arus akan mengalir melalui belitan relai dan kontak relai akan menutup. Sayangnya, dengan penyertaan ini, kita mendapatkan efek samping: nilai sinyal dari pengontrol terbalik, dan semua relay "klik" ketika sirkuit dihidupkan. Saya belum menemukan cara menghapus fitur ini.
Informasi lebih lanjut tentang chip dijelaskan di sini .
Port expander PCF8574AT juga dapat digunakan sebagai input: tombol perangkat keras dapat digantung pada beberapa input untuk itu, membaca nilainya di bus I2C. Dalam diagram, pin 4-7 dapat digunakan untuk membaca status tombol. Hal utama adalah jangan lupa untuk secara terprogram mengaktifkan pengetatan bawaan dari nutrisi terkait.
Pada saat yang sama, saya meninggalkan kabel ke matriks transistor, kalau-kalau Anda tiba-tiba ingin menghubungkan relay tambahan. Untuk kemungkinan koneksi, saya membawa semua kabel ke konektor (lebih tepatnya, ke lubang di bawahnya di mana kabel dapat disolder atau konektor DIP 2,54 mm standar dapat disolder).
Pin expander port INT dapat digunakan untuk merespons dengan cepat dengan menekan tombol. Ini dapat dihubungkan ke port bebas pada pengontrol dan mengatur pemicu interupsi untuk mengubah status pin ini.
Layar LCD dua baris juga dikendalikan melalui expander PCF8574AT. Poin utama: layar didukung oleh 5 volt, sedangkan layar itu sendiri dikendalikan oleh logika 3-volt. Omong-omong, adaptor Arduino standar untuk I2C tidak dirancang untuk tegangan ganda. Saya menemukan ide koneksi semacam itu di suatu tempat di Internet, sayangnya, saya kehilangan tautan, jadi saya tidak mengutip sumbernya.
Papan sirkuit
Saat mendesain papan, ternyata bagian-bagian biasa dengan kaki memakan terlalu banyak ruang, dan banyak keripik dalam desain DIP tidak mudah ditemukan. Setelah membaca di Internet bahwa pemasangan SMD tidak begitu rumit, dan dengan keterampilan yang tepat, bahkan lebih menyita waktu, saya memutuskan untuk merancang papan untuk bagian SMD. Dan saya tidak salah. Ternyata motherboard yang ringkas dan indah, tempat saya dengan mudah meletakkan semua yang saya butuhkan. Bagian SMD, dengan besi solder yang bagus, fluks dan solder, ternyata sangat mudah dipasang.
Di papan tulis, saya menambahkan beberapa lubang persegi untuk prototipe, jika saya tiba-tiba ingin menyolder sesuatu yang lain.
Saya membuat papan sirkuit tercetak berukuran 97x97 mm. Ini cocok dengan mudah ke dalam kotak listrik pemotongan standar. Selain itu, papan dengan ukuran kurang dari 100x100 murah untuk diproduksi. Produksi batch minimal 5 papan sesuai dengan biaya tata letak yang dikembangkan 5 USD, pengiriman mereka ke Belarus biaya 9 USD lagi.

Desain papan terletak di situs web EasyEDA dan tersedia untuk semua orang.
Saya perhatikan bahwa di foto controller di bawah ini muncul contoh pertama dari papan, di mana saya "memutar" banyak hal yang tidak perlu dan tidak perlu (dengan harapan menggunakan batch minimal 5 papan di proyek lain). Di sini dan di EasyEDA saya memposting versi "bersih" dari semua hal yang tidak perlu ini.

Foto kedua sisi papanSisi depan:

Sisi belakang:

Bagian perangkat lunak
Untuk memprogram mikrokontroler, mengingat backlog dalam bentuk prototipe pada Arduino Uno, diputuskan untuk menggunakan lingkungan Arduino dengan
ESP8266 Arduino Core diinstal. Ya, Anda dapat menggunakan
Lua pada ESP8266, tetapi mereka mengatakan ada hang. Saya, mengingat fungsi kritis yang dilakukan, tidak mau sama sekali.
Lingkungan Arduino sendiri tampaknya agak ketinggalan zaman bagi saya, tetapi, untungnya, ada
ekstensi untuk Visual Studio dari Visual Mikro. Lingkungan memungkinkan Anda untuk menggunakan petunjuk kode IntelliSence, dengan cepat beralih ke deklarasi fungsi, kode refactor: secara umum, segala sesuatu yang dimungkinkan oleh lingkungan untuk komputer "dewasa". Versi berbayar dari Visual Micro juga memungkinkan Anda untuk dengan mudah men-debug kode, tetapi saya puas dengan opsi gratis.
Struktur proyek
Proyek ini terdiri dari file-file berikut:
Struktur Proyek di Visual Studio File | Janji temu |
---|
WaterpoolManager.ino
| Deklarasi variabel dasar dan konstanta. Inisialisasi. Loop utama.
|
HeaterMainLogic.ino
| Logika dasar mengendalikan relai boiler (sesuai suhu) dan relay bantu.
|
Sensors.ino
| Baca Data Sensor
|
Pengaturan.ino
| Pengaturan perangkat, menyimpannya ke memori flash pengontrol
|
LCD.ino
| Output informasi pada LCD
|
ClockTimer.ino
| Pembacaan Jam RTC, atau Simulasi Jam
|
Relays.ino
| Relay kontrol hidup / mati
|
ButtonLogic.ino
| Logika reaksi terhadap keadaan tombol perangkat keras
|
BacaButtonStates.ino
| Baca status tombol perangkat keras
|
EEPROM_Logging.ino
| Pencatatan data sensor di EEPROM
|
WebServer.ino
| Server web built-in untuk manajemen perangkat dan tampilan status
|
Halaman web
| Halaman server web disimpan dalam folder ini.
|
index.h
| Halaman utama untuk menampilkan status perangkat. Membaca keadaan saat ini dengan panggilan ajax. Refresh setiap 5 detik.
|
loggraph.h
| Menampilkan log data sensor dan status relai dalam grafik. Pustaka jqPlot digunakan - semua konstruksi dilakukan di sisi klien. Permintaan ke controller hanya pergi ke file biner - salinan data dari EEPROM.
|
logtable.h
| juga, tetapi dalam bentuk tabel
|
pengaturan
| Mengelola pengaturan perangkat: mengatur batas suhu, aliran air, frekuensi logging data
|
waktu.h
| Pengaturan waktu saat ini
|
| Perpustakaan
|
EepromLogger.cpp
| Perpustakaan Flash Log
|
EepromLogger.h
|
crc8.cpp
| Menghitung 8-bit CRC untuk perpustakaan
|
crc8.h
|
TimeSpan.cpp
| Struktur untuk mengatur rentang waktu
|
TimeSpan.h
|
Sensor jajak pendapat
Saat perangkat dimulai, ia mencari sensor suhu di bus OneWire dan menyimpan alamatnya di array tempSensAddr. Sensor dimasukkan dalam urutan responsnya di bus dan urutannya tidak berubah di masa mendatang. Indeks sensor terakhir dalam array disimpan (perangkat dapat bekerja dengan 4 atau lebih sedikit sensor):
while (ds.search(tempSensAddr[lastSensorIndex]) && lastSensorIndex < 4) { Serial.print("ROM ="); for (byte i = 0; i < 8; i++) { Serial.print(' '); Serial.print(tempSensAddr[lastSensorIndex][i], HEX); } if (OneWire::crc8(tempSensAddr[lastSensorIndex], 7) != tempSensAddr[lastSensorIndex][7]) { Serial.print(" CRC is not valid!"); } else lastSensorIndex++; Serial.println(); } ds.reset_search(); lastSensorIndex--; Serial.print("\r\nTemperature sensor count: "); Serial.print(lastSensorIndex + 1, DEC); , (). Serial LCD : // Read sensor values and print temperatures ds.reset(); ds.write(0xCC, TEMP_SENSOR_POWER_MODE); // Request all sensors at the one time ds.write(0x44, TEMP_SENSOR_POWER_MODE); // Acquire temperatures delay(1000); // Delay is required by temp. sensors char tempString[10]; for (byte addr = 0; addr <= lastSensorIndex; addr++) { ds.reset(); ds.select(tempSensAddr[addr]); ds.write(0xBE, TEMP_SENSOR_POWER_MODE); // Read Scratchpad tempData[addr] = ds.read() | (ds.read() << 8); // Read first 2 bytes which carry temperature data int tempInCelsius = (tempData[addr] + 8) >> 4; // In celsius, with math rounding Serial.print(tempInCelsius, DEC); // Print temperature Serial.println(" C"); }
Menurut datasheet, sensor memerlukan setidaknya 750 ms penundaan antara meminta nilai suhu dan menerima respons dari sensor. Oleh karena itu, kode memperkenalkan penundaan dengan margin kecil.
Namun, keterlambatan ini, ketika seluruh perangkat hanya menunggu jawaban, dapat diterima di awal, tetapi sama sekali tidak pantas untuk menunggu setiap waktu dengan pemungutan suara sensor secara teratur. Oleh karena itu, kode rumit berikut ditulis, dipanggil setiap 50 ms oleh timer:
#define TEMP_MEASURE_PERIOD 20 // Time of measuring, * TEMP_TIMER_PERIODICITY ms #define TEMP_TIMER_PERIODICITY 50 // Periodicity of timer calling, ms timer.attach_ms(TEMP_TIMER_PERIODICITY, tempReadTimer); int tempMeasureCycleCount = 0; void tempReadTimer() // Called many times in second, perform only one small operation per call { tempMeasureCycleCount++; if (tempMeasureCycleCount >= TEMP_MEASURE_PERIOD) { tempMeasureCycleCount = 0; // Start cycle again } if (tempMeasureCycleCount == 0) { ds.reset(); ds.write(0xCC, TEMP_SENSOR_POWER_MODE); // Request all sensors at the one time ds.write(0x44, TEMP_SENSOR_POWER_MODE); // Acquire temperatures } // Between phases above and below should be > 750 ms int addr = TEMP_MEASURE_PERIOD - tempMeasureCycleCount - 1; if (addr >= 0 && addr <= lastSensorIndex) { ds.reset(); ds.select(tempSensAddr[addr]); ds.write(0xBE, TEMP_SENSOR_POWER_MODE); // Read Scratchpad tempData[addr] = ds.read() | (ds.read() << 8); // Read first 2 bytes which carry temperature data } }
Pada awal setiap siklus tempMeasureCycleCount, sensor diminta untuk membaca nilainya. Setelah sekitar 50 siklus berlalu (dan totalnya adalah 50 * 20 = 1000 ms = 1 detik), nilai setiap sensor dibaca, satu per satu. Semua pekerjaan dipecah menjadi beberapa bagian sehingga kode yang berjalan pada penghitung waktu tidak memakan banyak waktu dari pengontrol.
Nilai sensor aliran dihitung sebagai berikut. Dengan gangguan pada pin tempat sensor menggantung, kami meningkatkan nilai penghitung kutu yang berasal dari sensor aliran:
pinMode(FLOW_SENSOR_PIN, INPUT); attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), flow, RISING); // Setup Interrupt volatile int flow_frequency; // Flow sensor pulses int flowMeasureCycleCount = 0; void flow() // Flow sensor interrupt function { flow_frequency++; }
Dalam timer yang sama di mana sensor suhu diinterogasi, satu detik kami mengambil nilai centang ini dan menerjemahkannya ke dalam liter menggunakan konstanta FLOW_SENSOR_CONST, nilai yang dapat ditemukan dalam karakteristik sensor:
flowMeasureCycleCount++; if (flowMeasureCycleCount >= 1000 / TEMP_TIMER_PERIODICITY) { flowMeasureCycleCount = 0; litersInMinute = (flow_frequency / FLOW_SENSOR_CONST); // Pulse frequency (Hz) = FLOW_SENSOR_CONST*Q, Q is flow rate in L/min. flow_frequency = 0; // Reset Counter }
Mencatat data dari sensor dan status perangkat
Dalam mengembangkan mekanisme logging, fakta bahwa perangkat dapat tiba-tiba dimatikan, mis. hampir setiap saat. Saat Anda berhenti merekam, kami harus dapat mengembalikan semua yang direkam hingga saat terakhir. Pada saat yang sama, kami tidak dapat secara konstan menulis ulang area memori flash yang sama (misalnya, judul tertentu di tempat tertentu, mengingat alamat tempat rekaman terakhir kali diputar), untuk menghindari "penghapusan" percepatan flash drive di tempat ini.
Setelah beberapa "penumpukan" model rekaman berikut ini diciptakan dan diimplementasikan:

Setiap catatan adalah catatan yang berisi informasi tentang nilai arus aliran air, suhu sensor, serta status perangkat yang dikodekan dalam byte (masing-masing bit menunjukkan apakah relai aktif atau tidak, apakah pemanasan diaktifkan atau tidak):
struct LogEvent { unsigned char litersInMinute = 0; unsigned char tempCelsius[4]{ 0, 0, 0, 0 }; unsigned char deviceStatus = 0; }
Setelah setiap catatan, ada byte checksum CRC , yang menunjukkan apakah catatan itu ditulis dengan benar dan secara umum, apakah setidaknya ada sesuatu yang ditulis di lokasi memori ini.
Karena akan terlalu mahal untuk merekam data pada waktu saat ini ( stempel waktu ) untuk setiap catatan dalam hal volume, data disusun dalam blok besar, dengan N catatan di masing-masing. Stempel waktu untuk setiap blok dicatat hanya satu kali, untuk sisanya - dihitung berdasarkan informasi tentang frekuensi logging.
unsigned int logRecordsInBlock = 60 * 60 / loggingPeriodSeconds; // 1 block for hour unsigned int block_size = sizeof(Block_Header) + logRecordsInBlock * (record_size + crcSize); unsigned int block_count = total_storage_size / block_size;
Misalnya, dengan frekuensi logging sekali setiap 30 detik, kami akan memiliki 120 entri di blok, dan ukuran blok akan sekitar 840 byte. Secara total, kami dapat memuat 39 blok dalam memori flash drive yang berukuran 32 kilobyte. Dengan organisasi seperti itu, ternyata setiap blok dimulai pada alamat yang didefinisikan secara ketat dalam memori, dan "menjalankan" semua blok bukanlah masalah.
Dengan demikian, dengan pemecahan mendadak dalam catatan selama penutupan terakhir perangkat, kita akan memiliki blok yang belum selesai (yaitu, di mana beberapa catatan hilang). Ketika perangkat dihidupkan, algoritma mencari header blok valid terakhir (timestamp + crc). Dan terus merekam, mulai dengan blok selanjutnya. Perekaman dilakukan secara siklis: blok terbaru menimpa data blok tertua.
Saat membaca, semua blok dibaca berurutan. Blok yang tidak valid (blok yang tidak lulus CRC untuk cap waktu) diabaikan seluruhnya. Rekaman di setiap blok dibaca hingga pertemuan dari catatan tidak valid pertama (mis. Rekaman yang terputus terakhir kali jika blok tidak direkam seluruhnya). Sisanya diabaikan.
Untuk setiap catatan, waktu saat ini dihitung berdasarkan stempel waktu blok dan nomor seri catatan di blok.
LCD
Perangkat ini menggunakan layar QC1602A, yang mampu menampilkan 2 baris 16 karakter. Baris pertama menampilkan informasi terkini tentang nilai sensor saat ini: aliran dan suhu. Jika batas yang ditentukan terlampaui, tanda seru muncul di dekat nilai. Baris kedua menunjukkan status relai pemanas dan pompa, serta waktu yang berlalu sejak pemanasan dihidupkan atau dimatikan. Setiap 5 detik, tampilan di baris kedua secara singkat menunjukkan batas saat ini. Foto tampilan dalam berbagai mode ditampilkan di akhir publikasi.
Grafik
Ketika diminta melalui server web built-in, data logging dibaca dalam bentuk biner menggunakan JavaScript:
var xhttp = new XMLHttpRequest(); xhttp.open("GET", "logs.bin", true); xhttp.responseType = "arraybuffer"; xhttp.onprogress = updateProgress; xhttp.onload = function (oEvent) { var arrayBuffer = xhttp.response; if (arrayBuffer) { var byteArray = new Uint8Array(arrayBuffer); … }}; xhttp.send(null);
Membacanya dalam beberapa format non-biner yang populer, seperti ajax, akan menjadi kemewahan yang tidak dapat diterima untuk pengontrol, terutama karena besarnya jumlah yang harus dikembalikan oleh server http bawaan.
Untuk alasan yang sama, perpustakaan JavaScript jqPlot digunakan untuk membuat grafik, dan file perpustakaan JS sendiri diambil dari CDNs populer.
Contoh dari jadwal perangkat:

Terlihat jelas bahwa pada sekitar jam 9:35 alat dihidupkan untuk pemanasan, ketel mulai secara bertahap memanaskan sirkuit pemanas (sensor T3, T4), setelah itu suhu rangkaian kolam mulai meningkat (sensor T1, T2). Di suatu tempat sekitar 10:20, ketel beralih ke memanaskan air panas di rumah, suhu sirkuit pemanas turun. Kemudian setelah 10 menit, ketel kembali untuk memanaskan air kolam. Pada pukul 10:50 terjadi kecelakaan: pompa untuk sirkulasi air di kolam tiba-tiba mati. Aliran air turun tajam ke nol, relai pemanas dimatikan (garis putus-putus merah pada grafik ke-2), mencegah panas berlebih. Namun perangkat ini masih tetap dalam kondisi pemanasan (garis merah pada grafik ke-2). Yaitu jika pompa dihidupkan lagi dan suhunya normal, perangkat akan kembali memanas. Saya perhatikan bahwa setelah pompa darurat dimatikan, suhu di sirkuit air kolam (T1, T2) mulai meningkat tajam karena terlalu panas dari penukar panas. Dan jika bukan karena pemadaman boiler yang tajam, akan ada masalah.
Server web tertanam
Untuk berkomunikasi dengan dunia luar, kelas standar ESP8266WebServer digunakan . Ketika perangkat mulai, itu diinisialisasi sebagai titik akses dengan kata sandi standar yang ditentukan dalam #define AP_PASS. Halaman web terbuka secara otomatis untuk memilih jaringan wi-fi yang tersedia dan memasukkan kata sandi. Setelah memasukkan kata sandi, perangkat reboot dan menghubungkan ke titik akses yang ditentukan.
Perangkat jadi
Perangkat yang sudah selesai ditempatkan di kotak pemotongan standar untuk perkabelan. Lubang untuk LCD terpotong di dalamnya, dan lubang untuk konektor.

Foto fasad perangkat dalam berbagai modeDengan tampilan waktu yang berlalu setelah menyalakan:

Dengan batas yang ditampilkan:

Kesimpulan
Sebagai kesimpulan, saya ingin mengatakan bahwa, dengan mengembangkan perangkat seperti itu, saya mendapatkan pengalaman hebat dengan sirkuit, desain PCB, keterampilan instalasi untuk komponen SMD, dalam arsitektur dan pemrograman mikrokontroler, saya ingat C ++ yang hampir terlupakan dan penanganan memori yang hati-hati dan sumber daya pengontrol terbatas lainnya. Pengetahuan tentang HTML5, JavaScript, dan keterampilan debugging skrip di browser juga bermanfaat sampai batas tertentu.
Keterampilan ini dan kesenangan yang diterima selama pengembangan perangkat adalah manfaat utama yang diperoleh. Dan kode sumber perangkat, diagram sirkuit, papan sirkuit cetak - silakan gunakan, ubah. Semua kode sumber proyek ada di GitHab. Perangkat keras dalam proyek publik di EasyEDA. Saya mengumpulkan data pada chip yang digunakan dalam proyek pada drive jaringan .