Kami memompa treadmill

Baru-baru ini, saya memutuskan pembelian yang sangat aneh untuk diri saya sendiri. Ya, saya membeli treadmill.



Dan segera kesadaran itu datang kepada saya bahwa tidak ada statistik yang cukup rinci seperti ketika mengendarai sepeda. Dalam kasus sepeda, aplikasi di telepon menulis kecepatan, detak jantung, irama, dan angkat saya. Sangat penasaran untuk mengontrol semua parameter ini selama pelatihan, untuk dapat melihat grafik dan membandingkan hasil Anda dari waktu ke waktu.

Jadi saya memutuskan untuk melakukan sesuatu yang mirip dengan treadmill: hubungkan ke smartphone atau tablet untuk mengumpulkan dan menampilkan statistik.

Seperti biasa, cerita saya dalam bentuk artikel teks tradisional, dan melalui video. Anda lebih suka.

Video



Artikel


Rancangan


Bahkan pada saat saya mengumpulkan treadmill, saya perhatikan bahwa remote control dan sabuk pengaman itu sendiri hanya menghubungkan empat kabel. Rupanya, beberapa dari mereka digunakan untuk menyalakan konsol, karena kanvas itu sendiri terhubung ke jaringan 220 volt, dan kabel yang tersisa diperlukan untuk mengirimkan sinyal kontrol dalam arah yang berlawanan - dari konsol ke kanvas, mereka mengontrol kecepatan dan sudut trek.

Saya menghubungkan paralel osiloskop ke kabel-kabel ini, mencoba berbagai kombinasi.

Akibatnya, saya menemukan bahwa semuanya hampir sama dengan yang saya harapkan. Salah satu kabel ground, dan yang lainnya adalah daya 12 volt. Sisanya mengirimkan data digital.

Di salah satu dari mereka, sinyal berubah ketika mengganti kecepatan dan sudut. Inilah yang saya butuhkan! Amplitudo sinyal sekitar empat volt. Tetapi protokolnya tidak terlihat seperti sesuatu yang standar, dan sinyalnya sangat berisik, ketika trek menyala, Anda perlu memfilternya.



Kabel terakhir hanya pulsa dengan frekuensi konstan. Rupanya, bagi konsol untuk melihat koneksi ke sabuk berjalan. Jika Anda melepas kabel ini, remote control segera memberikan kesalahan.

Indikasi dari sensor pulsa pada kabel-kabel ini jelas tidak ditransmisikan, tetapi tidak perlu. Lebih baik menghubungkan sensor dada yang terpisah, yang sudah lama saya gunakan saat mengendarai sepeda. Selain itu, ternyata sensor detak jantung pada treadmill itu sendiri tergeletak banyak, meremehkan bacaan.

Perakitan perangkat


Jadi, tugas selanjutnya adalah merakit papan yang menghubungkan paralel dengan kabel-kabel ini, membaca kecepatan dan sudut saat ini, dan entah bagaimana mentransfernya secara nirkabel ke tablet atau smartphone.

Sekali lagi, saya memutuskan untuk menggunakan komputer papan tunggal Onion Omega2. Dia harus melakukan pekerjaan dengan sangat baik. Hanya perlu menurunkan tegangan suplai ke 3,3 volt dan memfilter data dari gangguan.

Untuk mengurangi tegangan, sekarang saya menggunakan papan yang sudah jadi ini dengan konverter DC-DC. Harganya beberapa sen, dapat bertahan hingga beberapa ampere, dan tegangan output disesuaikan dengan twist.



Pada saat yang sama, papan ini memiliki kesimpulan untuk menyolder langsung ke papan lain, sangat nyaman. Hal utama adalah tidak memuntir twist tegangan setelah pemasangan di sirkuit.

Untuk menyaring kebisingan pada jalur data, saya membuat filter RC biasa: resistor 2,2 kilo-ohm dan kapasitor 22 picofarad. Ini harus menyaring suara frekuensi tinggi, meninggalkan sinyal frekuensi rendah.

Ternyata syal yang cukup kecil.



Saya menghubungkannya ke kabel treadmill untuk melihat seberapa baik sinyal disaring ketika dihidupkan dan, tampaknya, bentuk gelombangnya menjadi hampir sempurna.



Modul kernel


Namun, tidak mudah untuk memeriksa kinerja setrika. Seperti yang kita lihat
sebelumnya pada osiloskop, sinyalnya berjalan sangat cepat, dan kami tidak menggunakan mikrokontroler, melainkan komputer papan tunggal Omega2 dengan Linux. Di Linux, kami tidak akan dapat memproses sinyal dari ruang pengguna dengan begitu cepat. Tapi dari intinya kita bisa! Karena itu, inilah saatnya untuk menulis modul kernel Linux!

Untuk melakukan ini, Anda perlu mengunduh sumber kernel Linux, dalam kasus kami ini adalah rakitan OpenWRT untuk Omega2, dan buat direktori dengan kode sumber modul kami di dalamnya.

Menulis kode modul sangat mirip pemrograman mikrokontroler. Kami juga menulis dalam C, juga semuanya level rendah, kami juga bekerja dengan interupsi dan juga beralih ke kesimpulan GPIO. Hanya di sini, selain semua hal di atas, kami masih berinteraksi dengan ruang pengguna melalui file pseudo. Dengan demikian, modul kernel kami menjadi semacam adaptor antara perangkat keras dan aplikasi biasa. Sebenarnya, ini disebut driver.

Pada awalnya, saya tidak tahu bagaimana cara memecahkan kode sinyal, jadi saya hanya menyimpulkan durasinya.



Segera menjadi jelas bahwa sinyal dikodekan dengan durasi tingkat tinggi. Panjangnya 600 mikrodetik atau 1200 mikrodetik. Level rendah selalu sepanjang 600 mikrodetik kecuali untuk urutan awal.

Sebanyak 17 tetes seperti itu naik dan turun. Ternyata, ini adalah 16 bit data ditambah urutan awal. Saya membuat decoding, mengambil sebagai dasar bahwa perbedaan panjang yang panjang adalah nol logis, dan yang pendek adalah unit logis dan saya mendapatkan apa yang terjadi. Saya segera melihat data yang saya butuhkan!



16 bit, seperti yang Anda tahu, dua byte. Byte pertama menunjukkan jenis data yang dikirim: sudut kemiringan atau kecepatan, dan byte kedua data itu sendiri. Pengemudi sangat sederhana.

Satu-satunya parameter driver adalah nomor port.

/* Module parameters */
static u8 receive_pin = 11;
module_param(receive_pin, byte, S_IRUGO);
MODULE_PARM_DESC(receive_pin,"Treadmill receiver pin number (default 11)");

Saat menginisialisasi, konfigurasikan untuk input dan atur interupsi, yang akan dipicu setiap kali level di atasnya berubah.

/* Allocate and init the timer */
data_recv_timer = kzalloc(sizeof(struct hrtimer), GFP_KERNEL);
if (!data_recv_timer) {
    pr_err("treadmill: can't allocate memory for timer\n");
    treadmill_free();
    return -1;
}
hrtimer_init(data_recv_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data_recv_timer->function = recv_timer_callback;

Dalam gangguan ini, pertama-tama kita melihat waktu saat ini. Selanjutnya, kami menggunakan nilai ini untuk menghitung berapa banyak waktu telah berlalu sejak interupsi terakhir terpicu dan memasukkannya ke dalam array. Tentu saja, kita ingat waktu sekarang untuk perhitungan di waktu berikutnya. Selain itu, Anda harus memulai kembali timer khusus.

/* IRQ fired every rising/falling edge of receiver pin */
static irq_handler_t treadmill_irq_handler(unsigned int irq,
    void *dev_id, struct pt_regs *regs)
{
    u64 now = ktime_to_us(ktime_get_boottime());
    u8 value = gpio_get_value(receive_pin);
    u64 time_passed;
    reset_recv_timer();

    if ((timings_pos & 1) == value)
    {
        time_passed = now - last_time;
        if (timings_pos < TIMINGS_BUFFER_SIZE)
        {
            timings[timings_pos] = time_passed;
            timings_pos++;
        }
        last_time = now;
    }

    /* Announce that the IRQ has been handled correctly */
    return (irq_handler_t) IRQ_HANDLED;
}

Kuncinya adalah bahwa jika timer masih bekerja, itu berarti bahwa tidak ada level drop pada pin untuk waktu yang lama, dan karenanya sudah waktunya untuk memproses informasi yang dikumpulkan. Dalam fungsi yang disebut timer, diperiksa bahwa ada 34 tetes, setelah itu kita melihat berapa lama setiap interval. Jika ada 600 mikrodetik, kemudian 1200 mikrodetik, maka kita bawa 900 ke luar negeri. Jika intervalnya kurang, maka kita tulis satu di hasilnya, digeser satu bit. Setelah memproses setiap interval, kami mengirim hasilnya ke file pseudo, sehingga mentransfer data ke ruang pengguna.

/* Timer */
static enum hrtimer_restart recv_timer_callback(struct hrtimer *timer)
{
    int i, p;
    u16 data;

    if (timings_pos != 34) {
        pr_debug("treadmill: invalid edges count: %d", timings_pos);
        timings_pos = 0; 
        return HRTIMER_NORESTART;
    }

    data = 0;   
    for (i = 2; i < timings_pos; i += 2)
    {
        data >>= 1;
        if (timings[i] < 900) // 600us = 1, 1200 us = 0
            data |= 0x8000;
    }
    
    for (p = 0; p < 2; p++) {
        for (i = 0; i < treadmill_number_opens; i++) {
            if (!(opened_files[i]->f_mode & FMODE_READ)) continue;
            ((struct cfile_t*)opened_files[i]->private_data)->receiver_buffer[
                ((struct cfile_t*)opened_files[i]->private_data)->receiver_write_pos++
                % RECEIVER_BUFFER_SIZE] = (data >> (8 * p)) & 0xFF;
        }
    };
    wake_up_interruptible(&wq_data);

    timings_pos = 0; 
   
    return HRTIMER_NORESTART;
}

Server python dan deteksi kecepatan


Kemudian tetap menulis skrip Python yang akan membacanya dari pseudo-file dan mengirimkannya melalui jaringan sebagai string JSON. Tampaknya semuanya cukup mudah. Namun, jika semuanya sederhana dengan sudut kemiringan, dan nilai dalam byte kedua persis sesuai dengan sudut kemiringan sebagai persentase, maka dengan kecepatan semuanya ternyata jauh lebih membingungkan.

Nilai 9 setara dengan satu kilometer per jam, dan nilai 160 sesuai dengan 18 kilometer per jam. Artinya, ketergantungan data pada kecepatan nyata sama sekali tidak jelas. Saya menulis semua nilai secara manual, mengarahkannya ke Excel, diplot dan mendapat kurva yang sangat tidak rata.



Dan ada kecepatan ketika pembacaan pada remote berbeda, tetapi data dan kecepatan trek itu sendiri tetap sama! Misalnya, 5,2 km / jam dan 5,3 km / jam sebenarnya kecepatannya sama. Di mana-mana curang. Saya bertanya-tanya, berapa kecepatan sebenarnya? Mengukurnya entah bagaimana, tetapi tinggalkan untuk nanti.

Terlepas dari transfer burung beo ini ke kilometer per jam, skripnya ternyata sangat sederhana. Kami membaca data dari pseudo-file Linux, mendekodekannya, menerima koneksi jaringan dan mentransfer data ke klien yang terhubung melalui jaringan sebagai string JSON.

class TreadmillServer:
    def __init__(self, device = "/dev/treadmill", port = 11010, interface = '0.0.0.0'):
        self._device = device
        self._port = port
        self._interface = interface
        self._working = False
        self._clients = []
        self._server_sock = None
        self.incline = 0
        self.speed = 0

    def start(self):
        self._working = True
        self._server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._server_sock.bind((self._interface, self._port))
        self._server_sock.listen(10)
        print("Listening port", self._port)
        Thread(target=self._port_listener, name="Treadmill port listener", daemon=True).start()
        Thread(target=self._device_listener, name="Treadmill device listener", daemon=True).start()

    def stop(self):
        self._working = False
        if self._server_sock != None:
            try:
                self._server_sock.close()
            except:
                pass
            self._server_sock = None

    def __del__(self):
        self.stop()

    def _port_listener(self):
        while self._working and self._server_sock:
            try:
                conn, addr = self._server_sock.accept()
                print('Connected: {0}'.format(addr))
                TreadmillClientConnection(self, conn, addr)
            except Exception as e:
                print("Error:", e)

Saya pikir tidak diperlukan otorisasi dan keamanan di sini. Keadaan treadmill bukan jenis data yang ingin saya lindungi dari peretas.

Kami memasukkan skrip ini ke startup dan menghapus papan di dalam treadmill. Sayangnya, itu hanya muat di pipa logam yang menghubungkan konsol ke sabuk berjalan.



Seperti yang Anda tahu, logam melindungi sinyal radio, jadi saya membawa antena Wi-Fi keluar dari pipa, tetapi di bawah selubung plastik yang menyembunyikan kabel.



Untuk hal ini treadmill "pintar" langsung sudah siap. Dia sudah tahu bagaimana cara mendistribusikan statistik melalui jaringan. Tetap menulis klien untuknya!

Klien Android


Apa yang menurut saya harus menjadi klien seperti itu. Ini adalah aplikasi Android yang akan saya jalankan di tablet atau smartphone dan meletakkannya di atas tampilan treadmill itu sendiri, masing-masing, itu harus menampilkan semua informasi tentang latihan di layar, menggantikan tampilan treadmill itu sendiri. Aplikasi harus bisa bekerja di latar belakang, sehingga saya bisa menonton video tanpa masalah saat jogging. Selain itu, ia harus menjaga statistik terperinci tentang pengoperasian, menyinkronkan semuanya dengan cloud dan menggambar grafik ketergantungan pada kecepatan dan sudut kemiringan.

Inti dari aplikasi semacam itu adalah layanan yang berjalan di latar belakang, terhubung ke treadmill dalam loop tanpa akhir, menerima data, dan menerjemahkannya. Tidak ada kesulitan khusus dalam hal ini.

Sensor detak jantung


Hal yang paling sulit adalah bekerja dengan sensor detak jantung. Banyak perangkap ditemukan. Saya memiliki monitor detak jantung dada di sini:



Saya sudah lama menggunakannya saat mengendarai sepeda. Ini cukup standar, ini berfungsi pada BLE ala Bluetooth Low Enegy, dapat dipasangkan dengan telepon dan navigator Garmin tanpa masalah. Saya bahkan tidak dapat berpikir bahwa bekerja dengannya dari aplikasi saya akan sangat tidak terlihat. Sensor tersebut memiliki GUID standar untuk pembacaan yang berbeda.

Untuk mulai menerima detak jantung, Anda harus terlebih dahulu mengonfigurasi monitor detak jantung Anda untuk mengirim bacaan secara berkala. Saya bisa melakukan ini hanya dengan mempelajari contoh yang tidak berfungsi dan dengan mengetik.
Sebagai hasilnya, saya menulis sebuah kelas untuk bekerja dengan sensor detak jantung, yang secara otomatis mencoba menghubungkannya dan secara berkala melaporkan detak jantung saat ini.

Samsung Health SDK


Adapun statistik dan grafik. Saya memutuskan untuk tidak menemukan kembali roda, tetapi untuk menggunakan apa yang sudah saya gunakan saat mengendarai sepeda, yaitu entah bagaimana berteman dengan aplikasi Samsung Health yang luar biasa.

Sekarang mungkin akan terlihat seperti saya mengiklankan Samsung lagi. Tetapi pada sepeda, aplikasi ini telah benar-benar membuktikan dirinya dengan sangat baik. Yang mengejutkan saya, ini terhubung ke semua sensor tanpa masalah, menunjukkan irama dan kecepatan roda, dan menentukan statistik di headphone, itu menunjukkan statistik yang sama dengan grafik, dan memberikan pencapaian, dan menyimpan segala sesuatu di awan.

Pencarian menunjukkan bahwa Samsung Health memiliki SDK sendiri, yang, meskipun tidak sepenuhnya dapat dipahami, masih didokumentasikan: img-developer.samsung.com/onlinedocs/health/android/data/index.html

Bekerja dengannya pada dasarnya bekerja dengan database yang menyimpan berbagai bacaan dari langkah-langkah yang diambil dan pengukuran detak jantung hingga gula darah dan fase tidur. Tetapi sekarang kami tertarik pada catatan latihan, yang mencakup nilai skalar seperti jenis olahraga, waktu, jarak, durasi, kalori yang terbakar, dan susunan data langsung seperti riwayat denyut jantung, kecepatan, dan koordinat.

Semua data ini harus disimpan dan dipersiapkan dengan benar. Beberapa perlu dihitung.

Perhitungan tinggi badan


Misalnya mengangkat tinggi. Dari treadmill, kita tahu sudut pendakian di setiap titik waktu, yang diukur dalam persen. Persentase sudut ketinggian adalah rasio jarak yang ditempuh dengan pendakian. Ternyata kecepatan vertikal sama dengan kecepatan biasa kali kemiringan sebagai persentase dan dibagi seratus. Mengetahui kecepatan vertikal, kita dapat menghitung ketinggian saat ini di setiap saat. Akibatnya, itu harus dimasukkan ke dalam koordinat saat ini, meskipun pada kenyataannya selama latihan mereka tidak berubah dan tidak diperhitungkan.
Menanggapi data ini, aplikasi Samsung Health akan menunjukkan berapa banyak yang seharusnya saya naik, serta kecepatan vertikal pada setiap saat pelatihan.

Penghitungan Kalori


Tapi bagaimana cara menghitung kalori? Selain itu, penghitungan kalori adalah keharusan bagi Samsung Health. Pada saat yang sama, kalori yang terbakar adalah indikator yang sangat tidak akurat, yang tergantung pada banyak faktor. Tidak yakin apakah masuk akal untuk menghitungnya.

Saya tidak datang dengan sesuatu dari saya sendiri dan hanya google kalkulator (https://42.195km.net/e/treadsim/) dan menyalin algoritma dari javascript saya (https://42.195km.net/e/treadsim/treadsim107 .js). Di pintu masuk, ia menempuh jarak yang ditempuh, sudut ketinggian dan ... berat.

Saya dapat mengatur berat badan saya secara manual, tetapi karena kami bekerja dengan Samsung Health, saya dapat mengambil berat badan saya saat ini dari sana. Lagi pula, saya menggunakan skala pintar dari Xiaomi, yang disinkronkan dengan Google Fit di ponsel saya, Google FIt melalui aplikasi terpisah disinkronkan dengan Samsung Health, Samsung Health melalui cloud disinkronkan dengan dirinya sendiri di tablet, di mana aplikasi saya sudah menerimanya.

Penampilan aplikasi


Secara visual, tugas aplikasi adalah tampilan skala besar dari indikasi utama: kecepatan, sudut, detak jantung, jarak, kalori. Lebih baik melakukannya dalam warna putih dengan latar belakang hitam sehingga konsumsi baterai saat menggunakan layar AMOLED minimal, karena kami jelas menunjukkan bahwa saat menampilkan aktivitas kami, layar harus dihidupkan terus-menerus.



Tombol-tombol secara otomatis disembunyikan ketika treadmill aktif. Anda dapat memulai dan menghentikan pelatihan hanya dengan kecepatan nol.

Dan tentu saja, Anda perlu membuat dukungan untuk mode "gambar dalam gambar". Ini dilakukan hanya dalam beberapa baris. Anda hanya perlu menunjukkan dalam manifes bahwa aktivitas mendukung mode ini, dan dalam kode masuk ke dalamnya ketika meminimalkan aplikasi. Hasilnya, Anda dapat menonton, misalnya, YouTube dan melihat bacaan treadmill di sudut layar. Ternyata sangat nyaman.



Tetapi pada tahap ini saya akhirnya disalip oleh rasa sakit pengembang untuk Android, karena saya sudah mendapatkan empat ukuran layar yang berbeda: ponsel dan tablet dalam mode normal dan mereka juga dalam mode "gambar dalam gambar". Dan kebetulan bahwa jika saya memilih ukuran font normal untuk satu ukuran layar, maka dalam kasus lain semuanya terlalu kecil, maka terlalu besar.

Saat mengembangkan untuk Android, ada beberapa kategori layar, dan Anda dapat membuat pengaturan yang berbeda berlaku secara otomatis untuk mereka, tetapi dalam kasus saya ini tidak cukup.
Akibatnya, saya harus menghitung dan mengatur ukuran font dalam kode, yang menurut saya sangat salah. Namun, itu berfungsi dengan sempurna sebagai hasilnya.

Hasil


Dan inilah hasilnya. Kami membuka aplikasi, menunggu koneksi dengan treadmill dan sensor detak jantung, memulai pelatihan dan menggunakan treadmill seperti biasa.
Di akhir latihan, kami menghentikan treadmill. Setelah mencapai kecepatan nol, tombol "selesai pelatihan" akan muncul. Klik itu, dan statistik dikirim ke Samsung Health. Buka dan lihat semua data.







Anda dapat melihat grafik denyut nadi, kecepatan dan kenaikan, membandingkan kemajuan Anda pada interval waktu yang berbeda, semua ini disimpan di cloud dan dapat diakses dari semua perangkat.

Anda dapat menyinkronkannya dengan Google Fit. Kecantikan. Saya senang dengan hasilnya. Sekarang hal utama adalah tidak membuang kelas. Anda dapat menambah fungsionalitas aplikasi sehingga menyerupai pelatihan jika saya malas untuk waktu yang lama. Tapi saya sudah terlalu malas untuk melakukan fungsi ini.

All Articles