نقوم بضخ جهاز المشي

في الآونة الأخيرة ، قررت شراء شيء غريب جدًا لنفسي. نعم ، اشتريت جهاز المشي.



وسرعان ما أدركني أنه لا توجد إحصاءات مفصلة كافية مثل ركوب الدراجة. في حالة الدراجة ، يكتب التطبيق على الهاتف سرعي ومعدل ضربات القلب والإيقاع والرفع. من الغريب جدًا التحكم في جميع هذه المعلمات أثناء التدريب ، حتى تتمكن من الاطلاع على الرسوم البيانية ومقارنة نتائجك من وقت لآخر.

لذلك قررت أن أفعل شيئًا مشابهًا مع جهاز المشي: قم بتوصيله بهاتف ذكي أو جهاز لوحي من أجل جمع وعرض الإحصائيات.

كالعادة ، قصتي في شكل مقال نصي تقليدي ، وعبر الفيديو. كما تريد أكثر.

فيديو



مقالة - سلعة


التصميم


حتى في اللحظة التي كنت أجمع فيها جهاز المشي ، لاحظت أن جهاز التحكم عن بعد وحزام الجري نفسه كانا يربطان أربعة أسلاك فقط. على ما يبدو ، يتم استخدام بعضها لتشغيل وحدة التحكم ، لأن اللوحة نفسها متصلة بشبكة 220 فولت ، وهناك حاجة إلى الأسلاك المتبقية لإرسال إشارات التحكم في الاتجاه المعاكس - من وحدة التحكم إلى اللوحة القماشية ، فهي تتحكم في سرعة وزاوية المسار.

لقد قمت بتوصيل راسم الذبذبات بالتوازي مع هذه الأسلاك ، وتجربة تركيبات مختلفة.

ونتيجة لذلك ، اكتشفت أن كل شيء كان تقريبًا كما توقعت. أحد الأسلاك أرضي ، والآخر قوة 12 فولت. والباقي يرسل البيانات الرقمية.

في أحدها ، تتغير الإشارة عند تبديل السرعة والزاوية. هذا هو بالضبط ما أحتاجه! سعة الإشارة حوالي أربعة فولت. لكن البروتوكول لا يبدو كشيء قياسي ، والإشارة صاخبة جدًا ، عندما يكون المسار قيد التشغيل ، تحتاج إلى تصفيته بطريقة أو بأخرى.



السلك الأخير هو مجرد نبضات بتردد ثابت. على ما يبدو ، لوحدة التحكم لرؤية الاتصال بحزام الجري. إذا قمت بفصل هذا السلك ، فإن جهاز التحكم عن بعد يعطي خطأ على الفور.

من الواضح أن مؤشرات مستشعر النبض على هذه الأسلاك لا تنتقل ، لكنها ليست ضرورية. من الأفضل توصيل جهاز استشعار منفصل للصدر ، والذي كنت أستخدمه لفترة طويلة عند ركوب الدراجة. بالإضافة إلى ذلك ، اتضح أن مستشعر معدل ضربات القلب على جهاز المشي نفسه يكذب كثيرًا ، مما يقلل من القراءات.

تجميع الجهاز


لذا ، فإن المهمة التالية هي تجميع لوحة متصلة بالتوازي مع هذه الأسلاك ، وقراءة السرعة والزاوية الحالية ، وبطريقة ما تنقلها لاسلكيًا إلى جهاز لوحي أو هاتف ذكي.

مرة أخرى ، قررت استخدام جهاز الكمبيوتر أحادي اللوحة Onion Omega2. يجب أن يقوم بعمل ممتاز. من الضروري فقط خفض جهد الإمداد إلى 3.3 فولت وتصفية البيانات من التداخل.

لتقليل الجهد ، أستخدم الآن هذه الألواح الجاهزة مع محول DC-DC. يكلفون بعض بنس واحد ، ويمكن أن يتحمل ما يصل إلى بضع أمبيرات ، ويتم ضبط جهد الخرج بضغطة.



في نفس الوقت ، هذا المجلس لديه استنتاجات للحام مباشرة إلى لوحة أخرى ، إنه مريح للغاية. الشيء الرئيسي هو عدم تحويل تطور الجهد بعد التثبيت في الدائرة.

لتصفية الضوضاء على خط البيانات ، قمت بعمل مرشح RC منتظم: مقاوم 2.2 كيلو أوم ومكثف 22 بيكوفاراد. هذا من شأنه تصفية الضوضاء عالية التردد ، وترك إشارة منخفضة التردد.

اتضح وشاح صغير.



لقد قمت بتوصيله بأسلاك جهاز المشي لمعرفة مدى جودة ترشيح الإشارة عند تشغيلها ، وعلى ما يبدو ، أصبح شكل الموجة مثاليًا تقريبًا.



وحدة النواة


ومع ذلك ، ليس من السهل التحقق من أداء الحديد. كما رأينا
سابقًا على مرسمة الذبذبات ، تسير الإشارات بسرعة كبيرة ، ولا نستخدم متحكمًا صغيرًا ، بل كمبيوتر أحادي أوميجا 2 مزود بنظام Linux. في نظام Linux ، لن نتمكن من معالجة الإشارات من مساحة المستخدم بهذه السرعة. ولكن من الجوهر نستطيع! لذلك ، حان الوقت لكتابة وحدة نواة لينكس!

للقيام بذلك ، تحتاج إلى تنزيل مصادر Linux kernel ، في حالتنا هذه عبارة عن تجميع OpenWRT لـ Omega2 ، وإنشاء دليل يحتوي على التعليمات البرمجية المصدر لوحدتنا.

تشبه كتابة رمز الوحدة النمطية برمجة وحدة التحكم الدقيقة. نكتب أيضًا في لغة C ، وأيضًا كل شيء منخفض المستوى ، ونعمل أيضًا مع المقاطعات وننتقل أيضًا إلى استنتاجات GPIO. هنا فقط ، إلى جانب كل ما سبق ، ما زلنا نتفاعل مع مساحة المستخدم من خلال ملف زائف. وبالتالي ، تصبح وحدة kernel الخاصة بنا نوعًا من المحول بين الأجهزة والتطبيقات العادية. في الواقع ، يسمى هذا السائق.

في البداية ، لم أكن أعرف كيفية فك رموز الإشارات ، لذلك استنتجت ببساطة مدتها.



سرعان ما أصبح واضحًا أن الإشارات تم تشفيرها لمدة عالية المستوى. يبلغ طولها 600 ميكروثانية أو 1200 ميكروثانية. دائمًا ما يكون المستوى المنخفض 600 ميكروثانية باستثناء التسلسل الأولي.

ما مجموعه 17 مثل هذه القطرات صعودا وهبوطا. على ما يبدو ، هذا هو 16 بت من البيانات بالإضافة إلى التسلسل الأولي. لقد قمت بفك تشفيرها ، مع الأخذ في الاعتبار أن الاختلافات العالية الطويلة هي صفر منطقي ، والاختصارات هي وحدة منطقية وحصلت على ما حدث. رأيت على الفور البيانات التي أحتاجها!



16 بت ، كما تعلمون ، وحدتي بايت. يشير البايت الأول إلى نوع البيانات التي يتم إرسالها: زاوية الميل أو السرعة ، والبايت الثاني البيانات نفسها. السائق بسيط للغاية.

معلمة برنامج التشغيل الوحيدة هي رقم المنفذ.

/* 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)");

عند التهيئة ، قم بتكوينه للإدخال وتعيين المقاطعة ، والتي سيتم تشغيلها في كل مرة يتغير فيها المستوى.

/* 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;

في هذا الانقطاع ، ننظر أولاً في الوقت الحالي. بعد ذلك ، نستخدم هذه القيمة لحساب مقدار الوقت المنقضي منذ تشغيل المقاطعة الأخيرة ووضعها في مصفوفة. بالطبع ، نتذكر الوقت الحالي للحساب في المرة القادمة. بالإضافة إلى ذلك ، يجب إعادة تشغيل المؤقت الخاص.

/* 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;
}

الحيلة هي أنه إذا كان المؤقت لا يزال يعمل ، فهذا يعني أنه لم تكن هناك قطرات مستوى على الدبوس لفترة طويلة ، وبالتالي فقد حان الوقت لمعالجة المعلومات التي تم جمعها. في الوظيفة التي يستدعيها المؤقت ، يتم التحقق من وجود 34 نقطة بالضبط ، وبعد ذلك ننظر في مدة كل فاصل زمني. إذا كان هناك 600 ميكروثانية ، ثم 1200 ميكروثانية ، فإننا نأخذ 900 في الخارج. إذا كان الفاصل الزمني أقل ، فإننا نكتب واحدة في النتيجة ، ونحولها بمقدار بتة واحدة. بعد معالجة كل فاصل زمني ، نرسل النتيجة لفتح ملفات زائفة ، وبالتالي نقل البيانات إلى مساحة المستخدم.

/* 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;
}

خادم بايثون وكشف السرعة


ثم يبقى لكتابة نص Python النصي الذي سيقرأها من ملف زائف وإرسالها عبر الشبكة كسلاسل JSON. يبدو أن كل شيء واضح إلى حد ما. ومع ذلك ، إذا كان كل شيء بسيطًا مع زاوية الميل ، وكانت القيمة في البايت الثاني تتطابق تمامًا مع زاوية الميل كنسبة مئوية ، فعندئذٍ مع السرعة تبين أن كل شيء أكثر إرباكًا.

تعادل القيمة 9 كيلومترًا واحدًا في الساعة ، وقيمة 160 تقابل 18 كيلومترًا في الساعة. أي أن اعتماد البيانات على السرعة الحقيقية ليس واضحًا على الإطلاق. لقد كتبت جميع القيم يدويًا ، ودفعتها إلى Excel ، ورسمت وحصلت على منحنى متفاوت للغاية.



وهناك سرعات عندما تكون القراءات على جهاز التحكم عن بعد مختلفة ، لكن بيانات وسرعة المسار نفسه تبقى كما هي! على سبيل المثال ، 5.2 كم / ساعة و 5.3 كم / ساعة هي في الواقع نفس السرعات. الغش في كل مكان. أتساءل ما هي السرعة حقا هناك؟ قم بقياسه بطريقة ما ، ولكن اتركه لوقت لاحق.

وبصرف النظر عن نقل الببغاوات إلى كيلومتر في الساعة ، تبين أن البرنامج النصي بسيط للغاية. نقرأ البيانات من ملف Linux الزائف ، ونفك تشفيره ، ونقبل اتصالات الشبكة وننقل البيانات إلى العملاء المتصلين عبر الشبكة كسلسلة 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)

أعتقد أنه لا حاجة إلى إذن وأمان هنا. حالة جهاز المشي ليست من نوع البيانات التي أود حمايتها من المتسللين.

نضع هذا البرنامج النصي في بدء التشغيل ونزيل اللوحة داخل جهاز المشي. للأسف ، تناسب فقط في أنبوب معدني يربط وحدة التحكم بحزام التشغيل.



كما تعلمون ، يحمي المعدن إشارة الراديو ، لذلك أخرجت هوائي Wi-Fi من الأنبوب ، ولكن تحت غلاف بلاستيكي يخفي الأسلاك.



على هذا المطحنة "الذكية" مباشرة جاهزة. إنها تعرف بالفعل كيفية توزيع الإحصائيات عبر الشبكة. يبقى فقط لكتابة عميل لها!

عميل Android


ما في رأيي يجب أن يكون مثل هذا العميل. هذا هو تطبيق Android الذي سأقوم بتشغيله على جهاز لوحي أو هاتف ذكي ووضعه في أعلى شاشة جهاز المشي نفسه ، على التوالي ، يجب أن يعرض جميع المعلومات على التمارين على الشاشة ، ليحل محل عرض جهاز المشي نفسه. يجب أن يكون التطبيق قادرًا على العمل في الخلفية حتى أتمكن من مشاهدة الفيديو أثناء الركض دون مشاكل. أيضًا ، يجب أن يحتفظ بإحصاءات مفصلة عن الجري ، ومزامنة كل شيء مع السحابة ورسم الرسوم البيانية لاعتماد النبض على السرعة وزاوية الميل.

يجب أن يكون قلب مثل هذا التطبيق خدمة تعمل في الخلفية ، وتتصل بجهاز المشي في حلقة لا نهائية ، وتتلقى البيانات وتفككها. لا توجد صعوبات خاصة في هذا.

جهاز استشعار معدل ضربات القلب


أصعب شيء كان يعمل فجأة مع جهاز استشعار معدل ضربات القلب. تم اكتشاف العديد من المزالق. لدي جهاز مراقبة معدل ضربات القلب في الصدر هنا:



لقد استخدمته لفترة طويلة عندما أقود دراجة. إنه معيار تمامًا ، فهو يعمل على BLE a la Bluetooth Low Enegy ، ويمكن إقرانه مع كل من الهاتف وملاح Garmin دون أي مشاكل. لم أستطع حتى التفكير في أن العمل معه من طلبي سيكون غير واضح. تحتوي هذه المستشعرات على GUIDs القياسية لقراءات مختلفة.

لبدء تلقي معدل ضربات القلب ، يجب عليك أولاً تكوين جهاز مراقبة معدل ضربات القلب لإرسال القراءات بشكل دوري. يمكنني القيام بذلك فقط من خلال دراسة أمثلة غير عام وكتابة.
ونتيجة لذلك ، كتبت فصلًا للعمل مع مستشعر معدل ضربات القلب ، والذي يحاول تلقائيًا الاتصال به ويبلغ معدل ضربات القلب الحالي بشكل دوري.

تطبيق Samsung Health SDK


أما الإحصاءات والرسوم البيانية. قررت عدم إعادة اختراع العجلة ، ولكن استخدام ما أستخدمه بالفعل عند ركوب الدراجة ، أي تكوين صداقات مع تطبيق Samsung Health الرائع بطريقة أو بأخرى.

الآن سيبدو على الأرجح أنني أعلن عن Samsung مرة أخرى. ولكن على الدراجة ، أثبت هذا التطبيق نفسه جيدًا حقًا. لدهشتي ، أنه يتصل بجميع أجهزة الاستشعار دون مشاكل ، ويظهر كل من سرعة الإيقاع والعجلة ، ويملي الإحصائيات في سماعات الرأس ، ويعرض نفس الإحصائيات مع الرسوم البيانية ، ويعطي الإنجازات ، ويخزن كل شيء في السحابة.

أظهر البحث أن Samsung Health لديها SDK الخاص بها ، والذي ، على الرغم من أنه ليس واضحًا تمامًا ، لا يزال موثقًا: img-developer.samsung.com/onlinedocs/health/android/data/index.html

يعمل معها بشكل أساسي مع قاعدة بيانات تخزن مجموعة متنوعة من القراءات من الخطوات المتخذة وقياسات معدل ضربات القلب إلى السكر في الدم ومراحل النوم. لكننا الآن مهتمون بسجلات التمارين ، والتي تشمل كلا من القيم العددية مثل نوع التمرين والوقت والمسافة والمدة والسعرات الحرارية المحروقة ومصفوفات البيانات الحية مثل تاريخ معدل ضربات القلب والسرعة والإحداثيات.

يجب تخزين جميع هذه البيانات وإعدادها بشكل صحيح. البعض يحتاج إلى حساب.

حساب الارتفاع


على سبيل المثال ، رفع الارتفاع. من جهاز المشي ، نعرف زاوية التسلق في كل نقطة زمنية ، والتي تقاس بالنسبة المئوية. النسبة المئوية لزاوية الارتفاع هي نسبة المسافة المقطوعة إلى الصعود. اتضح أن السرعة الرأسية تساوي السرعة المعتادة مائلة المنحدر كنسبة مئوية ومقسمة على مائة. بمعرفة السرعة العمودية ، يمكننا حساب الارتفاع الحالي في كل لحظة من الزمن. ونتيجة لذلك ، يجب إدخالها في الإحداثيات الحالية ، على الرغم من أنها لا تتغير أثناء التمرين ولا يتم أخذها في الاعتبار.
استجابة لهذه البيانات ، سيعرض تطبيق Samsung Health مقدار التسلق المفترض ، بالإضافة إلى السرعة الرأسية في كل لحظة من التدريب.

حساب السعرات الحرارية


لكن كيف نحسب السعرات الحرارية؟ علاوة على ذلك ، يعد عد السعرات الحرارية أمرًا ضروريًا لـ Samsung Health. في الوقت نفسه ، يعد حرق السعرات الحرارية مؤشرًا غير دقيق للغاية ، والذي يعتمد على العديد من العوامل المختلفة. لست متأكدا مما إذا كان من المنطقي عدها.

لم أتوصل إلى شيء خاص بي وأستخدم Google الآلة الحاسبة فقط (https://42.195km.net/e/treadsim/) وقمت بنسخ الخوارزمية من جافا سكريبت الخاص بي (https://42.195km.net/e/treadsim/treadsim107 .js). عند المدخل ، يأخذ المسافة المقطوعة وزاوية الارتفاع والوزن.

يمكنني تحديد وزني يدويًا ، ولكن نظرًا لأننا نعمل مع Samsung Health ، يمكنني أخذ وزني الحالي من هناك. بعد كل شيء ، أستخدم المقاييس الذكية من Xiaomi ، والتي تتم مزامنتها مع Google Fit على هاتفي ، يتم مزامنة Google FIt من خلال تطبيق منفصل مع Samsung Health ، تتم مزامنة Samsung Health من خلال السحابة مع نفسها على الجهاز اللوحي ، حيث يتلقى تطبيقي بالفعل.

مظهر التطبيق


بصريا ، مهمة التطبيق هي عرض المؤشرات الرئيسية على نطاق واسع: السرعة ، الزاوية ، معدل ضربات القلب ، المسافة ، السعرات الحرارية. من الأفضل القيام بذلك باللون الأبيض على خلفية سوداء بحيث يكون استهلاك البطارية عند استخدام شاشة AMOLED ضئيلًا ، لأننا بالتأكيد نشير إلى أنه عند عرض نشاطنا ، يجب تشغيل الشاشة باستمرار.



يتم إخفاء الأزرار تلقائيًا عندما يكون جهاز المشي نشطًا. يمكنك بدء التدريب وإيقافه فقط بسرعة صفر.

وبالطبع ، تحتاج إلى دعم وضع "صورة داخل صورة". يتم ذلك في خطوط قليلة فقط. تحتاج فقط إلى الإشارة في البيان إلى أن النشاط يدعم هذا الوضع ، وفي الكود الانتقال إليه عند تصغير التطبيق. ونتيجة لذلك ، يمكنك مشاهدة YouTube على سبيل المثال ومشاهدة قراءات جهاز المشي في زاوية من الشاشة. اتضح أنها مريحة للغاية.



ولكن في هذه المرحلة تجاوزني أخيرًا ألم المطور لنظام Android ، لأنني أحصل بالفعل على أربعة أحجام مختلفة للشاشة: الهاتف والكمبيوتر اللوحي في الوضع العادي وهم أيضًا في وضع "صورة في صورة". وقد حدث ذلك أنه إذا قمت بتحديد حجم الخط العادي لحجم شاشة واحدة ، ففي كل الحالات يكون كل شيء صغيرًا جدًا ، ثم كبيرًا جدًا.

عند التطوير لنظام Android ، هناك العديد من فئات الشاشات ، ويمكنك جعل الإعدادات المختلفة تنطبق تلقائيًا عليها ، ولكن في حالتي لم يكن ذلك كافيًا.
ونتيجة لذلك ، اضطررت إلى حساب وتعيين أحجام الخطوط في الشفرة ، والتي أعتقد أنها خاطئة للغاية. ومع ذلك ، فإنه يعمل بشكل مثالي نتيجة لذلك.

نتيجة


وهذه هي النتيجة. نفتح التطبيق ، وننتظر الاتصال بجهاز المشي ومستشعر معدل ضربات القلب ، ونبدأ التدريب واستخدام جهاز المشي كالمعتاد.
في نهاية التمرين ، نوقف جهاز المشي. عند الوصول إلى سرعة صفر ، سيظهر زر "إنهاء التدريب". انقر فوقه ، ويتم إرسال الإحصائيات إلى Samsung Health. افتحه وشاهد جميع البيانات.







يمكنك رؤية الرسم البياني للنبض والسرعة والارتفاع ومقارنة تقدمك على فترات زمنية مختلفة ، كل هذا يتم تخزينه في السحابة ويمكن الوصول إليه من جميع الأجهزة.

يمكنك مزامنتها مع Google Fit. الجمال. أنا سعيد بالنتيجة. الآن الشيء الرئيسي هو عدم إلقاء الطبقات. يمكنك الإضافة إلى وظائف التطبيق بحيث تشبه التدريب إذا كنت كسولًا لفترة طويلة. لكنني كسول بالفعل للقيام بهذه الوظيفة.

All Articles