Tentang proyek ini
Proyek ini dimulai sebagai "ide pelacak GPS" sederhana dan ternyata menjadi "pelacak GPS serbaguna". Sebagai proyek pertama saya, kurva pembelajarannya curam, dan karenanya saya selalu terbuka untuk masukan, umpan balik, dan peningkatan desain! :)
Pelacak dimaksudkan untuk ditempatkan di mobil saya dan memiliki fitur berikut:
- Lacak koordinat GPS dan poskan lokasi terakhir yang diketahui ke dasbor cloud thinger.io IoT setiap 2 menit (ditampilkan di peta). Memposting ke thinger.io dengan permintaan HTTP POST.
- Membalas perintah SMS dan mengembalikan tautan peta Google ke lokasi saat ini atau terakhir yang diketahui (lokasi terakhir yang diketahui jika lokasi saat ini tidak tersedia).
- Kirim pemberitahuan SMS setiap XX kilometer (idenya adalah agar pelacak mengingatkan saya untuk mengosongkan tangki tangki minyak saya setiap 4000km). Ini berfungsi sebagai "odometer" perangkat lunak yang dapat disesuaikan.
Selama proyek ini saya segera menyadari betapa "terbatas" Arduino dalam hal memori yang tersedia dan saya harus belajar teknik untuk mengurangi overhead dan membuat kode yang efisien (setidaknya saya telah mencoba). Saya juga telah menggunakan pustaka ringan agar sesuai dengan semua yang ada di chip dan RAM yang tersedia.
Komponen yang digunakan adalah sebagai berikut (seperti pada daftar komponen):
- Perangkat GPS NEO-6M. Ini tampaknya menjadi perangkat GPS yang sangat populer yang tersedia murah di Ebay dan sejenisnya. Komunikasi dengan GPS akan menjadi serial perangkat keras.
- Antena GPS. Semua yang kompatibel dapat digunakan, namun, saya menemukan bahwa yang termurah dari Ebay tidak berfungsi dengan baik, yaitu penerimaan yang buruk / jumlah satelit yang rendah. Mungkin saya hanya kurang beruntung dengan antena pertama, tetapi saya harus mendapatkan antena lain dengan kualitas lebih baik untuk penerimaan yang stabil.
- Papan pengembangan SIM900 untuk konektivitas GSM dan GPRS. Proyek ini juga harus bekerja dengan SIM800- dan modul yang kompatibel, namun tidak ada jaminan. Komunikasi dengan SIM900 akan menjadi serial perangkat lunak.
- Arduino Leonardo papan. Saya telah menggunakan papan Leonardo untuk memiliki jalur serial perangkat keras khusus, karena kita membutuhkan dua baris serial. Meskipun dimungkinkan untuk menggunakan papan UNO biasa juga, Anda harus memutuskan sambungan GPS untuk mengunduh perangkat lunak dan juga Anda tidak akan memiliki monitor serial untuk debugging. Kami membutuhkan dua saluran serial (satu untuk GPS dan satu untuk papan SIM900); satu serial perangkat lunak dan satu serial perangkat keras.
- Pembaca kartu SD (Saya telah menggunakan pembaca kartu Adafruit yang kompatibel dengan 5V (membuat koneksi ke header SPI 5V menjadi mudah). Modul lain yang lebih murah mungkin berfungsi dengan baik. Kartu Micro SD akan digunakan untuk menyimpan jarak yang telah ditempuh. Peringatan: pastikan pembaca kartu SD Anda mendukung pasokan 5V jika mengikuti skema saya, banyak pembaca kartu SD hanya menggunakan 3.3V. Menggunakan level tegangan yang salah kemungkinan besar akan merusak elektronik. Komunikasi dengan pembaca kartu SD akan menjadi antarmuka SPI.
- LED dan resistor untuk membuat sirkuit indikator status (LED daya dan kunci GPS).
- Saya juga telah merancang enklosur 3D yang dapat dicetak dengan file STL terlampir yang dapat dicetak langsung pada printer 3D.
Pertama kita perlu menginstal perpustakaan yang diperlukan. Saya telah menggunakan perpustakaan berikut untuk proyek ini:
- NeoGPS untuk pelacakan dan decoding GPS. Dapat diinstal langsung dari pengelola perpustakaan di Arduino IDE. Info lebih lanjut:https://github.com/SlashDevin/NeoGPS
- Waktu perpustakaan (digunakan untuk konversi zona waktu UTC):https://github.com/PaulStoffregen/Time
- PetitFS untuk membaca/menulis ke kartu SD:https://github.com/greiman/PetitFS Pustaka FAT SD Ringan.
Mengapa tidak menggunakan perpustakaan yang tersedia untuk Arduino oleh thinger.io?
Meskipun perpustakaan yang disediakan oleh thinger.io sangat mudah digunakan dan akan menyederhanakan banyak hal secara signifikan, belum lagi sudah terintegrasi dalam IDE, perpustakaan ini menghabiskan hampir 80% penyimpanan di Arduino Leo, meninggalkan sedikit atau tidak ada ruang untuk kode yang tersisa. Jadi itu terlalu besar untuk proyek ini dan kami harus melakukannya dengan cara yang sulit. Untuk berkomunikasi dengan cloud thinger.io, kami akan menggunakan permintaan HTTP POST.
SMS perintah
Perintah yang tersedia pada SMS adalah sebagai berikut (huruf kapital semua). Ini adalah perintah yang didukung dalam kode yang disediakan; Anda dapat menambahkan / menghapus perintah untuk proyek / persyaratan Anda sendiri:
- "POS" mengembalikan koordinat dengan tautan Google Maps, jika koordinat tersedia. Jika tidak, lokasi terakhir yang diketahui akan dikembalikan.
- "GETKM" mengembalikan jarak saat ini sejak "reset" terakhir.
- "RESETKM" menyetel penghitung jarak ke 0 (setel ulang odometer).
Menyiapkan NeoGPS
Kami menggunakan perpustakaan NeoGPS untuk kinerja dan penggunaan sumber daya dibandingkan alternatif seperti TinyGPS++. Ini menghabiskan sangat sedikit RAM, dan ini diperlukan; jika tidak, kami akan mendapatkan peringatan untuk memori dan stabilitas rendah.
Setelah library terinstal, ubah file GPSPort.h di jalur instalasi perpustakaan (contoh yang diberikan adalah untuk OS X - untuk Windows Anda akan menemukan perpustakaan di lokasi yang berbeda)
Ganti semua konten di GPSPort.h dengan yang berikut ini:
#ifndef GPSport_h#define GPSport_h#define gpsPort Serial1#define GPS_PORT_NAME "Serial1"#define DEBUG_PORT Serial#endif
File ini berisi definisi yang digunakan oleh perpustakaan NeoGPS. Jika Anda menggunakan papan Arduino yang berbeda, di sinilah Anda menentukan jalur serial ke penerima GPS, mis. "Serial2", "Serial3" untuk Arduino MEGA.
Catatan tentang akurasi
Perlu dicatat bahwa GPS bukanlah cara yang paling akurat untuk mengukur dan mengakumulasi jarak, karena posisinya akan sedikit bergeser bahkan saat diam. Anda dapat mengujinya dengan berdiri diam di tempat yang sama dan mengamati bahwa koordinat GPS akan berbeda untuk setiap pembacaan. Untuk akurasi aplikasi ini kurang penting dan karenanya penyimpangan yang lebih kecil tidak masalah.
Namun saya telah mencoba untuk memperhitungkan penyimpangan kecil dalam koordinat dan perangkat lunak hanya menambahkan jarak untuk gerakan lebih dari 10m (semua gerakan di bawah 10m dianggap diam) selama 15 detik.
Juga perlu diingat bahwa jarak dihitung dalam garis lurus, sedangkan jarak sebenarnya yang ditempuh mobil tergantung pada jalan, tikungan, dll. Saya telah mengatur laju pengambilan sampel menjadi 15 detik, namun Anda dapat menurunkannya jika ingin lebih tinggi akurasi.
Menyiapkan PetitFS
Pustaka ini adalah pustaka super ringan untuk membaca/menulis ke kartu SD dengan format FAT. Butuh beberapa waktu bagi saya untuk mengetahui cara kerjanya, karena dokumentasinya hampir tidak ada dan beberapa tempat bahkan salah / ketinggalan jaman. Kode contoh perpustakaan yang disediakan bahkan tidak dapat dikompilasi. Itu datang dengan lot pembatasan (sebagai lawan dari perpustakaan "normal" seperti perpustakaan SD Arduino atau SDFAT):
- Tidak dapat membuat file. Hanya file yang ada yang dapat ditulisi.
- Tidak dapat memperluas ukuran file.
- Tidak dapat memperbarui stempel waktu file.
- Tidak dapat menambahkan data ke file (setiap kali menulis ulang file).
- Hanya satu file yang terbuka setiap saat.
Mengapa menggunakan perpustakaan kecil dan terbatas dengan banyak keunikan?
Ukuran, pada dasarnya. Saya sudah mencoba beberapa library diantaranya Arduino SD Library, SDFAT dan juga fat16lib. Semuanya terlalu besar untuk membuat semua kode muat pada chip, jadi untuk tidak menghapus fungsionalitas saya menggunakan perpustakaan ini (perpustakaan Arduino SD standar membutuhkan sekitar 12% lebih banyak ruang). Bahkan dengan semua keanehan dan keterbatasan, tetap menyediakan apa yang kita butuhkan untuk aplikasi ini:membaca dan menulis sederhana dari satu nilai untuk penyimpanan.
Jika Anda tidak menggunakan semua kode dan ada cukup ruang untuk memasukkan beberapa tambahan, akan jauh lebih mudah untuk bekerja dengan pustaka seperti pustaka SD standar.
Buka file pffArduino.h dari folder perpustakaan PetitFS. Ubah SD_CS_PIN ke 10. Ini adalah pin SS yang digunakan untuk berkomunikasi dengan kartu SD dengan SPI.
Buka file pffconf.h dari folder perpustakaan. Nonaktifkan opsi berikut dengan mengalihkan nilai yang ditetapkan dari 1 ke 0:
Dengan menonaktifkan opsi ini, program yang dikompilasi membutuhkan lebih sedikit ruang - yang diperlukan; Sketsa terakhir membutuhkan waktu sekitar. 96% penyimpanan.
Pada impor pertama perpustakaan Anda akan mendapatkan kesalahan kompilasi yang *dapat* diabaikan (kompilasi kedua kesalahan tidak muncul - masih tidak mengerti mengapa). Namun, jika Anda ingin memperbaikinya (ini akan muncul kembali setiap kali Anda memulai Arduino IDE -> kompilasi) tambahkan parameter pengembalian fungsi yang hilang "FRESULT" seperti yang ditunjukkan pada tangkapan layar di atas. Ini ada di file pff.cpp dalam folder perpustakaan.
Saya telah mencoba yang terbaik untuk mencari tahu bagaimana perpustakaan ini bekerja dan meskipun saya memiliki segalanya untuk bekerja, saya cukup yakin banyak hal dapat ditingkatkan juga. Jika Anda menemukan kesalahan atau perbaikan dalam rutinitas yang saya tulis, jangan ragu untuk berbagi! Saya sangat ingin belajar dan membangun lebih banyak pengalaman.
Siapkan kartu SD
Saya telah menggunakan kartu Micro SD untuk proyek ini. Karena perpustakaan tidak dapat membuat file itu sendiri, penting untuk membuat file "dist.txt" dan "settings.txt" pada kartu sebelum digunakan. Saya sarankan menyalin "dist.txt" dan "settings.txt" terlampir file dari halaman proyek ini, karena file-file ini sudah memiliki format yang benar dan berfungsi (perpustakaan sangat pilih-pilih format teks dan konten).
Sebelum Anda memasukkan file ke kartu Micro SD, pastikan untuk memformat kartu dengan benar (sebagai FAT32 ). Saya sarankan menggunakan "SD Card Formatter" resmi dari SD Association:https://www.sdcard.org/downloads/formatter/.
Memastikan kartu SD berfungsi (membaca / menulis ke file dengan benar)
Pustaka PetitFS sangat pilih-pilih pada file input. Jika Anda mem-boot perangkat dan tidak ada output yang ditampilkan di monitor serial (kosong) kemungkinan besar macet di "loop" di mana ia mencoba membaca file dari kartu tetapi tidak dapat karena alasan tertentu (fungsi inisialisasiSD()). Saya memiliki banyak file teks yang karena alasan tertentu tidak dapat dibaca, oleh karena itu saya telah menyertakan file teks referensi yang saya gunakan yang berfungsi. Tempatkan referensi ini berkas di kartu SD dan harus dapat membaca dan menulis dengan benar ke kartu tersebut.
Pilihan lain dapat mengisi file teks dengan nomor yang lebih besar daripada tulisannya. Saya belum menguji ini, tetapi karena perpustakaan tidak dapat memperluas ukuran file itu sendiri, saya berasumsi ini bisa menjadi masalah.
PetitFS akan menulis seluruh panjang larik karakter ke file, maka Anda akan melihat spasi kosong di depan nomor sebenarnya (kecuali nomornya cukup besar untuk mengisi larik - panjang "array" ditentukan dalam kode). Spasi ini harus disimpan saat membuat perubahan pada file - karena PetitFS tidak dapat mengubah ukuran file, hal ini dapat menyebabkan masalah jika jumlah karakter diubah.
Setel file "dist.txt" ke "0" jika Anda ingin odometer dimulai dari "0", atau nomor lain untuk memudahkan verifikasi bahwa itu berfungsi, mis. mengirimkan perintah "GETKM" untuk memverifikasi balasan SMS.
Di "settings.txt" Anda mengatur jarak pemicu notifikasi, jarak di mana odometer memicu SMS notifikasi (dalam meter).
Siapkan papan SIM900
Ada beberapa hal yang harus disiapkan pada board SIM900 sebelum kita dapat menggunakannya. Untuk detailnya, ada sumber yang bagus di https://lastminuteengineers.com/sim900-gsm-shield-arduino-tutorial/ untuk penyiapan papan ini.
Sumber daya
Papan Arduino tidak mampu memasok daya yang cukup, maka kita harus menggunakan catu daya eksternal. Karena paku dapat menarik hingga 2A, pastikan untuk menggunakan catu daya yang dapat mengalirkan setidaknya 2A pada 5V-9V DC; menggunakan konektor laras 5,5 mm.
Pemilih sumber daya
Di sebelah soket DC terdapat pemilih sumber daya . Untuk menggunakan sumber daya eksternal, gerakkan penggeser seperti yang ditunjukkan pada gambar di atas.
Pemilih serial
Setel papan untuk menggunakan Software Serial dengan menyelaraskan jumper seperti yang ditunjukkan di atas.
Pemicu perangkat lunak
Alih-alih menekan tombol daya secara manual setiap kali, Anda dapat menghidupkan/mematikan SIM900 di perangkat lunak. Untuk melakukannya jumper bernama R13 harus disolder. Board kemudian dinyalakan dengan menghubungkan pin SIM900 #9 ke pin Arduino #7 (seperti yang ditunjukkan pada skema).
Jika menjaga fungsi "daya hidup manual", fungsi "SIM900power()" dalam kode dapat dihapus.
Hapus kunci PIN kartu SIM
Pastikan untuk melepas kunci PIN pada kartu SIM sebelum digunakan. Ini dapat dilakukan dengan memasukkannya ke telepon biasa dan menghapus kunci pin dari menu pengaturan yang berlaku.
Perhatikan juga bahwa modul SIM900 dalam skema mungkin terlihat berbeda dari papan sebenarnya, namun tata letak pin benar yang merupakan bagian terpenting.
Versi firmware SIM900 (Penting!)
Sangat penting untuk memiliki versi firmware yang benar dimuat pada chip. Ini karena salah satu perintah untuk mengatur header HTTP POST dengan benar tidak didukung hingga firmware versi B10. Ini berarti Anda harus memiliki setidaknya versi B10 atau lebih tinggi agar komunikasi http berfungsi. Secara khusus, dengan versi firmware yang lebih rendah, itu tidak akan dapat mengatur "Jenis konten" di header http. Jika tipe konten tidak disetel ke "application/json" dalam permintaan posting, itu akan ditolak oleh server.
Untuk memeriksa versi firmware Anda, gunakan perintah AT berikut:
AT+CGMR
Chip SIM900 kemudian akan memberi Anda versi firmware saat ini di konsol keluaran. Letakkan yang berikut ini di akhir bagian setup() untuk mencetak versi firmware saat startup:
SIM900.println( F("AT+CGMR") );
Dalam kasus saya ini akan menunjukkan ini (sebelum saya memperbarui):
Revisi:1137B01SIM900M64_ST_AM
Ini adalah versi firmware tertua yang mungkin untuk chip ini ("B01"), jadi saya memperbarui ke versi B10:1137B10SIM900M64_ST . Firmware yang lebih baru juga akan berfungsi.
Saya tidak akan membahas cara memperbarui firmware dalam panduan ini, sudah ada panduan yang sangat baik tentang cara melakukannya:Pembaruan firmware SIM900 - ACOPTEX (meskipun prosesnya agak menyakitkan).
Saya tidak tahu apakah ini akan menjadi kasus untuk chip lain seperti SIM800, namun, karena ini adalah chip yang lebih baru, saya akan mengatakan bahwa kemungkinan besar ini sudah ada di sana.
Penyesuaian kode
Untuk mengadaptasi kode untuk proyek Anda sendiri, beberapa penyesuaian diperlukan:
- Ubah informasi APN (penyedia jaringan).
- Ubah URL thinger.io agar sesuai dengan milik Anda (URL menautkan permintaan pembaruan ke "ember" Anda sendiri dengan token akses). Ini tercakup dalam bab "integrasi thinger.io".
- Tetapkan zona waktu yang benar.
- Setel jarak pemicu untuk notifikasi SMS
- Setel (atau nonaktifkan) teks pemberitahuan SMS.
- Setel nomor telepon default untuk pemberitahuan.
Penyedia APN
void connectGPRS(){... SIM900.println( F("AT+SAPBR=3,1,\"APN\",\"TeleXXX\"") ); penundaan (1000); updateSIM900();...
Di bawah koneksiGPRS() fungsi Anda akan menemukan nama APN yang diberikan oleh penyedia jaringan Anda, yang ditampilkan di atas sebagai "TeleXXX". Ganti ini dengan nama APN Anda sendiri.
ATOKAT+CMGF=1OKAT+CNMI=1,2,0,0,0OKAT+SAPBR=3,1,"CONTYPE","GPRS"OKAT+SAPBR=3,1,"APN"," TeleXXX"OKAT+SAPBR=1,1OKAT+SAPBR=2,1+SAPBR:1,1,"36.57.240.233"OK
Atas:output dari fungsi connectGPRS() saat koneksi bekerja. Semua perintah harus mengembalikan status "OK".
Zona waktu
#define UTC_OFFSET 1 // setel offset zona waktu, yaitu 1 =UTC+1
Di bagian "tentukan", atur zona waktu sesuai dengan kebutuhan Anda. Kode disetel ke UTC+1 .
Pemberitahuan SMS
Saya telah menyiapkan pemberitahuan setiap 4000km untuk mengosongkan tangki tangkapan minyak saya. Saat saya menyadari bahwa kebanyakan orang tidak memiliki tangki penampung minyak, pemberitahuan ini kemudian harus diubah menjadi apa pun yang Anda inginkan (atau dinonaktifkan sama sekali).
void loop() {... // mengirimkan notifikasi melalui SMS jika jarak total melebihi 4000km if (TotalDistance> triggerDistance) { char sms_msg[160]; char distanceTotalMsg[10]; itoa( (totalJarak / 1000) , jarakTotalMsg, 10); sprintf(sms_msg, "Tangkap tangki kosong! Jarak saat ini:%skm", distanceTotalMsg); pesan teks =""; totalJarak =0; // menyetel nomor telepon default untuk memicu nomor notifikasi =DEFAULT_NUMBER; sendSMS(sms_msg); } ...}
Atas:bagian kode yang memicu notifikasi (di dalam loop utama() ).
Komentari / hapus bagian ini jika Anda tidak ingin notifikasi terpicu. Atau ubah teks menjadi sesuatu yang berguna.
Pemberitahuan dipicu setelah "odometer" (jarak terakumulasi) mencapai jarak yang dikonfigurasi yang diatur dalam file "settings.txt".
Nomor telepon default
Ini adalah nomor telepon tempat notifikasi yang dipicu dikirim (karena notifikasi tidak memiliki nomor "pengirim" untuk dibalas)
// nomor telepon untuk pemberitahuan yang dipicu#define DEFAULT_NUMBER "+4712345678"
Tunggu koneksi serial
Ini juga merupakan ide yang baik untuk menghapus komentar pada baris berikut dalam kode. Hal ini membuat board Arduino menunggu koneksi serial menjadi aktif, yaitu serial monitor untuk debugging. Dengan cara ini Anda tidak melewatkan pesan debug di awal kode sebelum baris serial menjadi aktif.
Ingatlah untuk menghapus / mengomentari baris sebelum menyalakan Arduino dari sumber daya eksternal, karena jika tidak, akan berhenti dalam loop tak terbatas hingga terhubung ke PC.
// while (!Serial); // tunggu port serial terhubung - untuk ATmega32u4 (Leonardo)
Integrasi Thinger.io
Saya tidak akan merinci cara mengatur thinger.io karena cukup mudah; Anda harus membuat akun dan "ember" untuk menerima data melalui situs web mereka, dan "perangkat" yang akan kami sambungkan. Sebuah "ember" adalah database untuk menerima data. "Perangkat" adalah titik koneksi untuk kode kami di mana kami memutuskan apa yang harus dilakukan dengan data (dalam kasus kami, isi database "bucket").
Buat "ember" seperti yang ditunjukkan di atas dengan nama dan deskripsi Anda sendiri.
Sekarang, lanjutkan dan buat "Perangkat HTTP" seperti yang dijelaskan dalam dokumentasi resmi:https://docs.thinger.io/quick-sart/devices/http-devices.
Gunakan pendek perangkat nama . Karena nama perangkat adalah bagian dari algoritme yang menghasilkan kunci otorisasi, saya menemukan bahwa nama perangkat yang lebih panjang juga menghasilkan kunci otorisasi yang lebih panjang. Masalah? Kunci otorisasi dengan cepat menjadi lebih lama dari buffer 256 karakter yang digunakan untuk mengirim string dari Arduino. Mungkin ada beberapa cara yang lebih baik untuk memperbaikinya, tetapi saya menemukan pendekatan termudah untuk menjaga nama perangkat tetap pendek dan menghindari masalah.
panggilan balik di perangkat' > setelan bagian pastikan pengaturan "tulis ember" mengarah ke ember yang dibuat sebelumnya. Ini memberitahu "perangkat" untuk menulis data yang masuk ke database kami.
panggilan balik di perangkat' > ikhtisar bagian membuat catatan tentang metode URL dan header otorisasi (tali panjang tanpa kata kunci "Pembawa").
Untuk mengirim data ke thinger.io, kami menggunakan "URL otorisasi" dalam permintaan HTTP POST. Anda kemudian harus mengganti URL dan kunci otorisasi dalam kode dengan kode Anda sendiri.
Di postDataThinger() fungsi Anda akan menemukan panggilan (kunci otorisasi sebenarnya diacak):
SIM900.println( F("AT+HTTPPARA=\"URL\",\"http://backend.thinger.io/v3/users/tom/devices/CT/callback/data?authorization =eyJhbGciOiJIUzI1NiIsInR5cdfkjowiuerdf.sdfsdf.wekrjciI6InRvcm1vZCJ9.AR1gWvIZB9KmtI-5Z12YXKuchPTGn58AkwBJSZQIoxQ\"") );
Anda kemudian harus mengganti URL dan otorisasi kunci dalam kode dengan milik Anda, dibuat dengan mengikuti petunjuk di tautan yang disediakan di atas.
http://backend.thinger.io/...
Secara default, URL otorisasi yang dihasilkan akan menjadi https . SIM900 tidak dukungan SSL (setidaknya saya belum membuatnya berfungsi), jadi pastikan untuk mengubah "https ://" hingga "http://". Thinger API juga mendukung koneksi non-SSL. Ini sangat penting. Jika Anda tetap menggunakan "https" itu tidak akan berfungsi. Saat semuanya berfungsi, monitor serial akan memberikan "200 - OK" balas saat mengirimkan permintaan posting http.
Setelah perintah AT "AT+HTTPACTION=1" (kirim permintaan HTTP POST) Anda akan menerima balasan seperti ini di monitor serial:
+HTTPACTION:1.200,0
Jika Anda menerima balasan "400 - permintaan buruk" atau sejenisnya..
+HTTPACTION:0,400,51
..kemungkinan besar ada yang salah dengan URL, mis. "https" bukan "http", sintaks yang buruk dari "kunci otorisasi", dll. Ketika Anda menerima pesan "200 - OK" data akan muncul di keranjang thinger seperti yang ditunjukkan di bawah ini. Anda juga dapat menerima 400 - "permintaan buruk" jika Anda tidak memiliki firmware yang benar seperti yang disebutkan sebelumnya.
Di atas adalah tampilan ember setelah data tiba (diacak karena alasan privasi). Konten (kolom data) diatur oleh sintaks permintaan HTTP POST dalam kode, tidak diperlukan pengaturan di thinger.io.
Di bawah ini adalah output serial dari permintaan HTTP POST seperti yang seharusnya terlihat ketika semuanya berfungsi. +HTTPACTION:1, 200, 0 menunjukkan bahwa pembaruan berhasil.
AT+HTTPINITOKAT+HTTPPARA="CID",1OKAT+HTTPPARA="URL","OKAT+HTTPPARA="CONTENT","application/json"OKAT+HTTPDATA=84,10000DOWNLOADOKAT+HTTPACTION=1OK+ HTTPACTION:1,200,0AT+HTTPTERMOK
Dasbor kemudian dapat dengan mudah diatur di thinger dengan widget peta menggunakan ember sebagai sumber data.
Lebih banyak data untuk hal-hal?
Apakah Anda ingin mendorong lebih banyak data daripada garis bujur, garis lintang, dan tanggal/waktu? Cukup tambahkan lebih banyak bidang data ke permintaan http seperti yang ditunjukkan di bawah ini.
Formatnya adalah { "nama bidang1" :bidang1, "nama bidang2" :bidang2, "nama bidang3" :bidang3 }
sprintf(httpContent, "{ \"longitude\" :%s , \"latitude\" :%s , \"date\" :\"%s %s\" }", tempstrLong, tempstrLat , tanggal1, waktu1);
Perintah sprintf di atas mengkompilasi string data yang dikirim ke thinger. Sintaksnya *sangat* ketat dan Anda harus menambahkan bidang data baru dengan cara yang sama persis. Contoh diberikan dalam kode (bagian komentar). Ide yang baik adalah membuat catatan dari cetakan serial monitor dari perintah yang akan menampilkan string. Anda kemudian menambahkan "field4" dan seterusnya..
Lampiran
Saya telah melampirkan enklosur lengkap yang dapat dicetak 3D. Ini dirancang agar sesuai dengan PCB yang digunakan untuk proyek ini. Sekrup M3 digunakan untuk pemasangan.
It's designed for a 7x5cm solder board for the LED "circuit" and not a breadboard. If using a breadboard, just use some glue instead. The GPS and solder board ("breadboard") is mounted in the top casing. Use small spacers for best mounting of PCBs in top casing.
I have also kept the mounting points in the top casing solid (no holes) to make for easier printing without supports. Open these with a 3mm drill bit.
It prints well on 0.2mm without supports.
Connection to car battery / power source
There are probably hundreds of ways of doing this, so I don't have the only answer, or best answer for that matter; how to wire it up to the car battery. It all depends on your application, but I'll quickly describe my solution.
I wanted the device to start with the car, hence not connected directly to the battery (and drawing power while the car is turned off). So I've connected it to the "cigarette socket" circuit that is already turning on/off with the car. If you want it to be online even when the car is off, you'll have to find a way to wire it to the battery. For most cars the cigarette socket turns off with the car, but you'll have to check this for your own. I will not show the exact wiring for mine as this will also be different for every car. You can also place a battery pack in between to keep the device going for hours after the car has been switched off (or stolen..).
You can of course also just use an adapter, like one of those USB phone chargers, but that defeats my purpose of hiding it (in case the car gets stolen). We also have two power sources, the Arduino board and the SIM900 module. I used a "China cheap-o matic" step-down module, that converts from 12V-5V (the actual input range was said to be 9V-20V). It's probably not good quality, but has been working ok so far :)
The step-down module transforms the 12V input to 5V output to two USB female connectors. I then connected the Arduino- and SIM900 module to each of these USB outputs to power them. There are probably other and more "professional" solutions, but this was cheap and worked well enough.
I have measured the power draw during GSM activity to around 110maH, so very little power consumption. It will probably draw more power in areas with poor GSM coverage.
Known issues
If an SMS command is received at the same time as the thinger.io http request is processed (while data is being pushed to thinger) the command will not be picked up by the software. In this case, you will not receive a SMS reply. Send a new command some seconds later and it will work again. I've not made a workaround for this, as its not a big problem. If someone should make a fix for this, please feel free to share.
Also, if the Arduino is started in an area without network coverage, it won't reconnect when the network is available again, as it only connects during startup. I might modify the code at some point to fix this, but at the moment it
Kode
- GPS_tracker_Leonardo_v2.ino
GPS_tracker_Leonardo_v2.inoArduino
#include #include #include #include #include "PF.h"#include "PetitSerial.h"#define UTC_OFFSET 1 // set time zone offset, i.e. 1 =UTC+1#define TXPin 8 // SIM900 Tx pin#define RXPin 9 // SIM900 Rx pin#define PWRPin 7 // SIM900 software power pin// phone number for triggered notification#define DEFAULT_NUMBER "+4712345678"FATFS fs; // file system object - for reading SD card// GSM variablesString textMessage; // holds the last received text messageString number =DEFAULT_NUMBER; // phone number from last text messagechar sms_msg[160]; // holds the SMS reply text// location variablesfloat Lat =0, Long =0;boolean valid_location =false; // initial valid location flaguint8_t num_sat;NeoGPS::Location_t prevFix; // holds previous location for distance calculationNMEAGPS gps; // parses the GPS charactersgps_fix fix; // holds on to the latest valuesconst char *googlePrefix ="http://maps.google.com/maps?q=";const char *filename ="DIST.TXT";const char *settings ="SETTINGS.TXT";// time variablesNeoGPS::time_t timeFix; // time object for current gps fixchar datePrint[13];char timePrint[10];// distance tracking variablesfloat totalDistance =0; // in meters// triggerdistance (odometer notification) is read from SD card on initfloat triggerDistance =4000000;SoftwareSerial SIM900( TXPin, RXPin ); // SIM900 Tx &Rx is connected to Arduino #8 void setup() { pinMode(3, OUTPUT); pinMode(4, OUTPUT); digitalWrite(3, HIGH); // turn on power LED Serial.begin(9600); // serial monitor /* the "while (!serial)" construct below must only be enabled for debugging purposes when connected to a PC. If this is kept in the code the program will stop in a loop when connected to external power sources, as no serial connection will be established */ // while (!Serial); // wait for serial port to connect - for ATmega32u4 (Leonardo) SIM900.begin(9600); // SIM900 module on pins #8 and #9 gpsPort.begin(9600); // GPS receiver on Serial1 pins #0 and #1 - defined in GPSport.h // initialize the SD card and reads standard setting and accumulated distance initializeSD(); // power up SIM900 with software trigger SIM900power(); SIM900.println( F("AT") ); // Handshaking with SIM900 delay(500); SIM900.println( F("AT+CMGF=1") ); // Configuring TEXT mode delay(500); SIM900.println( F("AT+CNMI=1,2,0,0,0") ); // Decides how newly arrived SMS messages should be handled delay(500); connectGPRS();}void loop() { while (gps.available( gpsPort )) { fix =gps.read(); num_sat =fix.satellites; if (fix.valid.location) { digitalWrite(4, HIGH); // sets GPS lock LED Lat =fix.latitude(); Long =fix.longitude(); // saves the first "GPS lock" flag - we now have useful data if (Lat !=0 &&Long !=0 &&!valid_location) { valid_location =true; prevFix =fix.location; } } if (fix.valid.date &&fix.valid.time) { timeFix =fix.dateTime; updateTime(); } // update thinger.io and write values to SD card only for valid gps fix // typically at startup before gps has locked in coordinates first time if (valid_location) { // updates the distance travelled every 15 seconds static const unsigned long REFRESH_INTERVAL_UPD =15000; // 15 seconds static unsigned long lastRefreshTime =millis(); if (millis() - lastRefreshTime>=REFRESH_INTERVAL_UPD) { lastRefreshTime +=REFRESH_INTERVAL_UPD; // calculates distance between current and previous fix in meters float distanceDelta =prevFix.DistanceKm(fix.location) * 1000; // only update if distance is greater than 10 meters and less than 10km // 10km check is implemented to avoid erroneous data reading from GPS if (distanceDelta> 10 &&distanceDelta <10000) { totalDistance +=distanceDelta; } // reset the calculation point for next loop (set "previous" location) prevFix =fix.location; } // writes distance travelled to SD card every 2 minutes // uploads coordinates to thinger.io every 2 minutes static const unsigned long REFRESH_INTERVAL_WRITE_SD =120000UL; // 2 minutes static unsigned long lastRefreshTimeWriteSD =millis(); if (millis() - lastRefreshTimeWriteSD>=REFRESH_INTERVAL_WRITE_SD) { lastRefreshTimeWriteSD +=REFRESH_INTERVAL_WRITE_SD; // file write to SD card begin char buf[9]; dtostrf(totalDistance, 8, 0, buf); if (PF.open(filename)) Serial.println( F("error open file") ); while (1) { UINT nr; if (PF.writeFile(buf, sizeof(buf), &nr)) Serial.println( F("error write file") ); if (nr ==sizeof(buf)) { PF.writeFile( 0, 0, &nr); // finalize write operation by writing a null pointer break; } } // Petit FS has no "close" operation on file // next section transfers data to thinger.io IoT cloud with HTTP POST request. // only update thinger.io after first successful GPS lock char httpContent[60]; char tempstrLong[10]; char tempstrLat[10]; dtostrf(Lat, 2, 6, tempstrLat); dtostrf(Long, 2, 6, tempstrLong); // data fields to thinger.io bucket. Access to bucket is given through URL authorization in the post function. // format is { "field1 name" :field1 , "field2 name" :field2 , "field3 name" :field3 } with exact byte count. sprintf(httpContent, "{ \"longitude\":%s , \"latitude\":%s , \"date\":\"%s %s\" }", tempstrLong, tempstrLat, datePrint, timePrint); char httpdataLen[20]; // exact byte count for the content must be added to HTTPDATA // otherwise HTTP POST request is invalid, i.e. status 400 is retured. sprintf(httpdataLen, "AT+HTTPDATA=%i,10000", strlen(httpContent)); postDataThinger(httpdataLen, httpContent); } } } // send SMS notification if the total distance exceeds configured limit if (totalDistance> triggerDistance) { char distanceTotalMsg[10]; itoa( (totalDistance / 1000) , distanceTotalMsg, 10); sprintf(sms_msg, "Empty catchtank! Current distance:%skm", distanceTotalMsg); textMessage =""; number =DEFAULT_NUMBER; totalDistance =0; sendSMS(sms_msg); } updateSerial();}void updateSerial(){ // read incoming buffer. reads content of any text message if (SIM900.available()> 0) { textMessage =SIM900.readString(); } if (textMessage.indexOf("POS")>=0) { extractSenderNumber(); textMessage =""; char latPrint[10]; dtostrf(Lat, 5, 6, latPrint); char LonPrint[10]; dtostrf(Long, 5, 6, LonPrint); if (num_sat>=3 &&valid_location) { sprintf(sms_msg, "Current location:Lat:%s, Long:%s. %s%s,+%s\n", latPrint, LonPrint, googlePrefix, latPrint, LonPrint); } else if (num_sat <3 &&valid_location) { sprintf(sms_msg, "No gps fix. Last seen %s%sat:Lat:%s, Long:%s. %s%s,+%s\n", datePrint, timePrint, latPrint, LonPrint, googlePrefix, latPrint, LonPrint); } else if (!valid_location) { sprintf(sms_msg, "Tom not found. Maybe he is in North-Korea?"); } sendSMS(sms_msg); } // returns the current total accumulated distance if (textMessage.indexOf("GETKM")>=0 ) { char sms_msg[32]; char distanceTotalMsg[10]; itoa( (totalDistance / 1000) , distanceTotalMsg, 10); sprintf(sms_msg, "Current distance:%skm", distanceTotalMsg); textMessage =""; sendSMS(sms_msg); } // resets the distance counter to 0 if (textMessage.indexOf("RESETKM")>=0) { totalDistance =0; char sms_msg[] ="Acknowledge:distance reset"; textMessage =""; sendSMS(sms_msg); }}void SIM900power(){ // power up SIM900 board from pin #7 (default) -> 2sec. signal pinMode(PWRPin, OUTPUT); digitalWrite(PWRPin, LOW); delay(1000); digitalWrite(PWRPin, HIGH); delay(2000); digitalWrite(PWRPin, LOW); delay(15000); // give module time to boot}void updateSIM900(){ // empty incoming buffer from SIM900 with read() delay(500); while (SIM900.available()) { // outputs buffer to serial monitor if connected Serial.write(SIM900.read()); }}void extractSenderNumber(){ uint8_t startPos =textMessage.indexOf("+", 6); uint8_t endPos =textMessage.indexOf(","); number =textMessage.substring(startPos, endPos - 1);}void sendSMS(char *content){ // really crappy string conversion since I was too lazy to do proper // char handling in the first place. // SMS is returned to the sender number. char numberChar[number.length() + 1]; number.toCharArray(numberChar, number.length() + 1); char cmd_sms[50]; sprintf(cmd_sms, "AT+CMGS=%c%s%c", 0x22, numberChar, 0x22); SIM900.println(cmd_sms); updateSIM900(); SIM900.print(content); updateSIM900(); SIM900.write(0x1A);}void connectGPRS(){ SIM900.println( F("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"") ); delay(1000); updateSIM900(); SIM900.println( F("AT+SAPBR=3,1,\"APN\",\"TeleXXX\"") ); delay(1000); updateSIM900(); SIM900.println( F("AT+SAPBR=1,1") ); delay(1000); updateSIM900(); SIM900.println( F("AT+SAPBR=2,1") ); delay(1000); updateSIM900();}void postDataThinger(char *httpDataLen, char* content){ SIM900.println( F("AT+HTTPINIT") ); delay(1000); updateSIM900(); SIM900.println( F("AT+HTTPPARA=\"CID\",1") ); delay(1000); updateSIM900(); SIM900.println( F("AT+HTTPPARA=\"URL\",\"http://backend.thinger.io/v3/users/tom/devices/CT/callback/data?authorization=eyJhbGciOiJIUzI1NiIsInR5cCI6lskjdflksjdfweruiowe19DVCIsInVzciI6InRvcm1vZCJ9.AR1gWvIZB9KmtI-5Z12YXKuchPTGn58AkwBJSZQIoxQ\"") ); delay(1000); updateSIM900(); SIM900.println( F("AT+HTTPPARA=\"CONTENT\",\"application/json\"") ); delay(1000); updateSIM900(); SIM900.println(httpDataLen); delay(1000); updateSIM900(); SIM900.println(content); delay(1000); updateSIM900(); SIM900.println( F("AT+HTTPACTION=1") ); delay(10000); updateSIM900(); SIM900.println( F("AT+HTTPTERM") ); delay(1000); updateSIM900();}// initialize SD card and retrieves stored distance valuevoid initializeSD(){ // first section read current distance from SD card char buf[10]; // buffer to hold retrieved distance value // Initialize SD card and file system. if (PF.begin(&fs)) Serial.println( F("error begin file") ); // Open file for read - saved accumulated total distance if (PF.open(filename)) Serial.println( F("error open file") ); while (1) { UINT nr; if (PF.readFile(buf, sizeof(buf), &nr)) Serial.println( F("error read file") ); if (nr ==sizeof(buf)) break; } // no close function for Petit FS. // retrieves stored distance value to program totalDistance =atof(buf); // second section read odometer notification trigger value char bufTrigger[10]; // buffer to hold trigger value if (PF.open(settings)) Serial.println( F("error open file") ); while (1) { UINT nr; if (PF.readFile(bufTrigger, sizeof(bufTrigger), &nr)) Serial.println( F("error read file") ); if (nr ==sizeof(bufTrigger)) break; } // retrieves odometer notification value triggerDistance =atof(bufTrigger);}// corrects time object with time zone offsetvoid updateTime(){ // set time from GPS data string setTime(timeFix.hours, timeFix.minutes, timeFix.seconds, timeFix.date, timeFix.month, timeFix.year); // calc current time zone time by offset value adjustTime(UTC_OFFSET * SECS_PER_HOUR); sprintf(datePrint, "%02d/%02d/%04d ", day(), month(), year()); sprintf(timePrint, "%02d:%02d:%02d ", hour(), minute(), second());}
Custom parts and enclosures
Top of casing for more space (taller). The mounting holes are closed and must be drilled after print (to make for easier printing)A compact version of the top casing (less space inside enclosure)Bottom part of casing - for SIM900 and Arduino board with cutouts for connectorsLocks SIM900 board in placeLocks SIM900 board in place (no mounting holes on SIM900 board)rename to "dist.txt" and place on SD card dist_1qOG9VMO2D.txtrename to "settings.txt" and place on SD card settings_iMpR6v81OB.txt Skema