Warning: The magic method Restrict_Widgets::__wakeup() must have public visibility in /var/www/storepeter.dk/public_html/wp-content/plugins/restrict-widgets/restrict-widgets.php on line 72

Warning: Cannot modify header information - headers already sent by (output started at /var/www/storepeter.dk/public_html/wp-settings.php:589) in /var/www/storepeter.dk/public_html/wp-includes/feed-rss2.php on line 8
Bluetooth – StorePeter https://storepeter.dk The world need chaos - I am here to help Sat, 25 Feb 2017 13:02:08 +0000 en-US hourly 1 https://wordpress.org/?v=7.0.1 Badevægt – wiibb.c – dobbelt-v dobbelt-i dobbelt-b https://storepeter.dk/linux/badevaegt-wiibb-c-dobbelt-v-dobbelt-i-dobbelt-b Thu, 09 Feb 2017 21:13:36 +0000 http://peter.storepeter.dk/?p=1057 Continue reading ]]> Vi så sidste gang at når først vores Wii Balance Board var parret, dukkede det automatisk op i device-træet som en HID input enhed som kunne findes via DEVPATH environment variablen sat af udev.  Nedenfor er en anden måde at se hvilke input devices der er

$ cat /proc/bus/input/devices
....
I: Bus=0005 Vendor=057e Product=0306 Version=0600
N: Name="Nintendo Wii Remote Balance Board"
P: Phys=
S: Sysfs=/devices/platform/soc/3f201000.serial/tty/ttyAMA0/hci0/hci0:11/0005:057E:0306.0002/input/input1
U: Uniq=
H: Handlers=event1 js0 
B: PROP=0
B: EV=b
B: KEY=10000 0 0 0 0 0 0 0 0 0
B: ABS=f0000

enheden  er tilgængelig både som input1 og js0 der er den gamle devicetype for joysticks. Al ny kode bør bruge input.

Flowet i en vejning er:

  • udev kalder via /etc/udev/rules.d/90-wiibb.rules wiibb.sh
  • wiibb.sh er et shell-script der klarer det overordnede program-flow, start af programmer,  debug logs,  sende resultatet videre,  lukke bluetooth ned…
  • /usr/local/bin/wiibb skrevet i C wiibb.c laver selve vejningen.

funktionalitet skal være:

  • Ingen parrings funktionalitet i wiibb.sh eller wiibb.c
  • tryk på knappen på Wii-Fit for at tænde den
  • blink LED et par gange – sker automatisk
  • forbind program med enheden – udev kalder wiibb.sh
  • tænd LED – indikerer at vi måler  – wiibb.sh
  • aflæs sensorer og middel den indtil den er stabil – wiibb.c
  • sluk LED – sker automatisk ved bluetooth disconnect
  • luk enheden ned – disconnect wii-fit wiibb.sh
  • gem/udskriv målingen og tidspunktet –wiibb.sh

Det første man skal gøre er at  læse manual-siden, desværre er der ingen for input.  Det næste er at  læse include filer, og da det er en driver, er det også værd at kigge i kildeteksten til linux-kernen, og så er der google.   Her er hvad jeg skimmede igennem:

kildeteksten til selve driveren behøver man sikkert ikke at konsultere, men det godt at vide at der er fuld dokumentation.

Vores Wii Balance Board er tilgængelig via  /dev/input/event1, Events får man fat i via read() ind i en structur der indeholder time-stamp, type, code, value, for at se på hvad det er vi har med at gøre, har jeg skrevet dette lille C-program

/*
 * read_events.c - Copyright (c) 2017 peter@lorenzen.us
 * This program is free software, BSD-licensed
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/input.h>

void usage(char *mess)
{
	printf("read_events path\n");
	printf("f.ex path=/dev/input/event1\n");
	printf("%s\n",mess);
	exit(1);
}
int main(int argc, char **argv)
{
	int fd;
	if ((fd = open(argv[1], O_RDONLY)) < 0) {
		usage("cannot open device");
	}
	char name[80] = "Got nothing expected Nintendo";
	int r = ioctl(fd, EVIOCGNAME(sizeof(name)), name);
	printf("Events for \"%s\" on device %s\n", name, argv[1]);
	int first_sec=0;
        struct input_event ev;
	while (read(fd, &ev, sizeof(ev)) == sizeof(ev)) {
                if (first_sec==0) first_sec = ev.time.tv_sec;
                int sec = ev.time.tv_sec - first_sec;
                int usec = ev.time.tv_usec;     // to avoid warning 
                printf("sec %d.%06d type=0x%04x code=0x%04x value=0x%08x\n", sec, usec, ev.type, ev.code, ev.value);
	}
	perror("error reading event");
}

Output af read_events dev/input/event1 ser således ud:

Events for "Nintendo Wii Remote Balance Board" on device /dev/input/event1
sec 0.803470 type=0x0003 code=0x0012 value=0x00000018
sec 0.803470 type=0x0000 code=0x0000 value=0x00000000
sec 0.805909 type=0x0003 code=0x0011 value=0x00000013
sec 0.805909 type=0x0003 code=0x0012 value=0x00000011
sec 0.805909 type=0x0003 code=0x0013 value=0x00000041
sec 0.805909 type=0x0000 code=0x0000 value=0x00000000
sec 0.823412 type=0x0003 code=0x0011 value=0x00000014
sec 0.823412 type=0x0003 code=0x0012 value=0x0000000f
sec 0.823412 type=0x0003 code=0x0013 value=0x00000048
sec 0.823412 type=0x0000 code=0x0000 value=0x00000000
...

Fra /usr/include/linux/input.h finder vi disse definitioner

/* excerpts from <linux/input.h> */
struct input_event {
	struct timeval time;
	__u16 type;
	__u16 code;
	__s32 value;
};
/* Event types */
#define EV_SYN			0x00
#define EV_KEY			0x01
#define EV_ABS			0x03
/* Absolute axes */
#define ABS_HAT0X		0x11
#define ABS_HAT0Y		0x11
#define ABS_HAT1X		0x12
#define ABS_HAT1Y		0x13

Dermed kan vi vi skrive en første version af vores program wiibb.c

/*
 * wiibb.c - Copyright (c) 2017 peter@lorenzen.us
 * This program is free software - BSD-licensed
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/input.h>

void usage(char *mes)
{
        printf("Usage: wiibb event_device\n");
        printf(" print sensor values and total weight\n");
        if (mes) printf("%s\n",mes);
        exit(1);
}

time_t start_sec;

typedef struct {
        int usec;       // 12:10:10     seconds:msec:usec
        int button;
        int sensor[4];  //  br tr bl tl bottom/top right/left
        int total;
} sample_t;

void handle_sample(sample_t *s)
{
        static int prev_usec = 0;
        printf("%d.%03d %d %d %d %d %d %d\n",
                s->usec>>20, (s->usec & 0xfffff)>>10, (s->usec - prev_usec)>>10,
                s->total, s->sensor[0], s->sensor[1], s->sensor[2], s->sensor[3]);
        prev_usec = s->usec;
}

int main(int argc, char **argv)
{
        if (argc != 2) {
                usage(NULL);
        }
        int fd;
        if ((fd = open( argv[argc-1], O_RDONLY)) < 0) {
                usage("cannot open device");
        }
        char name[80] = "Got nothing expected Nintendo";
        int r = ioctl(fd, EVIOCGNAME(sizeof(name)), name);
        printf("# %s \"%s\" margin=%d, divisor=%d option_d=%d\n",
                getenv("DEVNAME"), name, margin, divisor, option_d);
        if (r<0) {
                usage("ioctl name failed");
        }
        printf("# t.ms + 10g br tr bl tl\n");
        sample_t s = { .usec=-1, .button=0, .sensor={0,0,0,0}};
        struct input_event ev;
        while (read(fd, &ev, sizeof(ev)) == sizeof(ev)) {
                if (start_sec==0) {
                        start_sec = ev.time.tv_sec;
                } else if (ev.time.tv_sec > (60+start_sec)) {
                        printf("timeout after 60 seconds\n");
                        exit(0);
                }
                if (ev.type == EV_SYN) {        // end if this time_stamp,
                        s.usec = (ev.time.tv_sec - start_sec) * 1024*1024;      // >> 20 will give seconds
                        s.usec += ev.time.tv_usec;
                        s.total = s.sensor[0]+s.sensor[1]+s.sensor[2]+s.sensor[3];
                        handle_sample( &s);
                } else if (ev.type == EV_KEY) {
                        if (ev.value) {
                                printf("button pressed\n ");
                                exit(0);
                        }
                } else if (ev.type == EV_ABS) {
                        s.sensor[0x3 & ev.code] = ev.value;
                } else {
                        printf("UNKNOWN type=0x%x code=0x%x value=0x%x\n", ev.type, ev.code, ev.value);
                }
        }
        perror("wiibb: error reading");
}

Nu ser output fra programmet således ud

# /dev/input/event16 "Nintendo Wii Remote Balance Board" margin=10, divisor=100 option_d=1
# t.ms + 10g br tr bl tl
0.876 2 256  0 72 56 128
0.903 26 280 33 73 54 120
0.906 3 298 37 80 53 128
0.925 18 297 37 80 52 128
0.926 1 284 33 85 44 122
0.943 17 296 34 84 42 136
0.960 17 284 28 95 33 128
0.966 6 273 29 85 32 127
1.017 74 274 30 92 26 126

Det ser ud som om der er støj på dataene, så lad os kigge nærmere på dem via gnuplot, vi tager en 5 sekunders måling og plotter resultat.  For at komme fri af nul-punktet vejer vi 4 styk 500gram Blå cirkel kaffe som jeg har bragt hjem fra Danmark til dette formål, scriptet ser således ud:

#!/bin/sh
# wiibb -a > fil; wiibb_gnuplot.sh < fil
# t.ms + 10g br tr bl tl
sec=1;kg=3;br=4;tr=5;bl=6;tl=7

FILE=$(basename $1 .log)-sensor

# scale total by  1/4 to have equal scales
awk 'NF==7 { $3=$3/4;print $0}' < $1 > $FILE.dat

f=`head -1 $FILE.dat | cut -f1 -d:`
t=`tail -1 $FILE.dat | cut -f1 -d:`
echo gnuplot of $FILE.dat over $t seconds to $FILE.png

gnuplot << EOF
set term png size 1024,512
set output "$FILE.png"
set autoscale
set xlabel "seconds"
set ylabel "sensor"
set y2label "kg"
set y2tics
set link y2 via y/25 inverse y*25
plot ["$f":"$t"] \
        '$FILE.dat' using 1:$kg title "kg" with lines lw 3, \
        '$FILE.dat' using 1:$br title "bottom right" with lines, \
        '$FILE.dat' using 1:$tr title "top right" with lines, \
        '$FILE.dat' using 1:$bl title "bottom left" with lines, \
        '$FILE.dat' using 1:$tl title "top left" with lines
EOF

og plottet bliver:

Da jeg prøvede nogle af de eksisterende programmer i mit første indlæg, var der meget flicker på de sidste cifre, og som man ser er der periodisk støj af forskellig størrelse på de forskellige sensorer,  første indskydelse er at det hidrører fra netstøj og er på en eller anden måde relateret til 60 Hz.

Et simpel lavpas filtrering kan filtrere støjen væk der er  50-60 samples pr sekund. Inden vi laver vores endelige C-program laver vi lige et par test offline i AWK.

Lavpas filteret bidrager med 1% af forskellen til resultatet hver gang, da der er 50-60 samples pr sekund vil det tage et nogle sekunder før resultatet vil stabilisere sig, derfor bruger vi en meget større faktor 20% når forskellen er mere end 5 kg, idet vi antager at støjen er markant mindre end det.

Når resultatet ikke ændrer sig mere end +- 0.1 kg inden for 1 sekund, betragter vi resultatet som stabilt og målingen afsluttes.

Parametrene kan justeres ved at sætte env-variablerne FACTOR=0.01 og MARGIN=10 (0.1kg),

#!/bin/sh
# wiibb /dev/input/event1 > fil; plot_filter.sh < fil
# t.ms + xgram br tr bl tl
sec=1;xgram=3;br=4;tr=5;bl=6;tl=7
FILE=$(basename $1 .log)-filter

awk 'BEGIN {
        margin='${MARGIN:-0.1}'
        lowpass = 0
        last_lp = 0
        printf("# t raw lowpass diff sec_settled\n");}
NF==7 {
        kg = $3/100.0
        diff = lowpass - kg     
        if ((diff > 5) || (diff < -5)) {        # more than 5 kg diff this is not noise
                factor = 0.2    # adjust fast
        } else {
                factor = '${FACTOR:-0.01}'
        }
        lowpass -= diff * factor
        if ((last_lp < (lowpass-margin)) || (last_lp > (lowpass+margin)) || (kg < 5)) {
                last_lp = lowpass;
                last_time = $1
        } else if (($1-last_time) > 1.0) {
                printf("# at t = %1.3f settled at %1.3f kg +- %1.3f for %s seconds\n", $1, lowpass, margin, $1-last_time) 
                printf("at t = %1.3f settled at %1.3f kg +- %1.3f for %s seconds\n", $1, lowpass, margin, $1-last_time) > "/dev/tty"
        }
        printf("%1.3f %1.2f %1.2f %1.2f %d # last=%1.3f\n", $1, kg, lowpass, diff, 100*($1-last_time), last_time)
}' <$1 >$FILE.dat

if [ $# = 2 ]; then
        from=$1
        to=$2
else
        from=`awk '! /^#/ {print $1;exit}' $FILE.dat`
        to=`awk 'END {print $1;exit}' $FILE.dat`
fi

echo gnuplot of $FILE.dat $from - $to seconds to $FILE.png

gnuplot << EOF
set term png size 1024,512
set output "$FILE.png"
set autoscale
set xlabel "seconds"
set ylabel "kg"
set ytics nomirror
set y2label "settled in msec."
set y2tics
set link y2 via y*10 inverse y/10
plot ["$from":"$to"] \
        '$FILE.dat' using 1:2 title "raw" with lines, \
        '$FILE.dat' using 1:3 title "lowpass factor ${FACTOR:-0.01}" with lines, \
        '$FILE.dat' using 1:4 title "diff between two kg samples" with lines, \
        '$FILE.dat' using 1:5 title "ms settled within ${MARGIN:-0.1} kg" with lines
EOF

Plottet af min vejning ser således ud

Det endelige C-program

Så er der bare tilbage at kopiere funktionaliteten fra AWK-script over i C-programmet, det mest funktionen handle_sample() der skal rettes til, resultatet er:

 

/*
 * wiibb.c - Copyright (c) 2017 peter@lorenzen.us
 * This program is free software - BSD-licensed
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/input.h>

void usage(char *mes)
{
        printf("Usage: wiibb [-d] event_device\n");
        printf(" filter and determine the weight on the balance board\n");
        if (mes) printf("%s\n",mes);
        exit(1);
}

int option_d=0;
#define dprint(...) option_d && fprintf(stderr, __VA_ARGS__)
#define ddprint(...) (option_d>1) && fprintf(stderr, __VA_ARGS__)
int margin = 10;        // 100 gram
int divisor = 100;
time_t start_sec;

typedef struct {
        int usec;       // 12:10:10     seconds:msec:usec
        int button;
        int sensor[4];  //  br tr bl tl bottom/top right/left
        int total;
} sample_t;


void handle_sample(sample_t *s)
{
        static int prev_usec = 0;
        static int xgram = 0;   // each unit is ten gram
        static int last_xgram = 0;
        static int last_usec = 0;
        int diff = xgram - s->total;
        if ((diff > 500) || (diff < -500)) {    // more than 5 kg diff this is not noise
                xgram -= diff / 5;      // adjust fast
        } else {
                xgram -= diff / divisor;
        }
        dprint("%d.%03d %d %d %d %d %d %d",
                s->usec>>20, (s->usec & 0xfffff)>>10, (s->usec - prev_usec)>>10,
                s->total, s->sensor[0], s->sensor[1], s->sensor[2], s->sensor[3]);
        ddprint("# last_usec: %d.%03d xgram: %d last_xgram: %d\n",
                last_usec>>20, (last_usec & 0xfffff)>>10, xgram, last_xgram );
        dprint("\n");
        prev_usec = s->usec;
        if ((last_xgram < (xgram-margin)) || (last_xgram > (xgram+margin)) || (xgram < 500)) {
                last_xgram = xgram;     // start a new  stable
                last_usec = s->usec;
        } else if ((s->usec-last_usec) > (1024*1024)) { // stable for a second, we are DONE
                char date_time[100];
                time_t sec = start_sec + s->usec/(1024*1024);
                strftime(date_time, sizeof(date_time), "%Y%m%d-%H%M", localtime(&sec));
                printf("#date: %s kg: %1.2f lbs: %1.1f\n", date_time, xgram/100.0, xgram/45.4);
                exit(0);
        }
}

int main(int argc, char **argv)
{
        if (argc==3) {
                if (strcmp(argv[1], "-d")==0) {
                        option_d = 1;
                } else if (strcmp(argv[1], "-dd")==0) {
                        option_d = 2;
                }
        } else if (argc != 2) {
                usage(NULL);
        }
        char *env;
        if ((env = getenv("MARGIN"))) {
                margin = atoi(env);     // 100 gram
        }
        if ((env = getenv("DIVISOR"))) {
                divisor = atoi(env);
        }
        int fd;
        if ((fd = open( argv[argc-1], O_RDONLY)) < 0) {
                usage("cannot open device");
        }
        char name[80] = "Got nothing expected Nintendo";
        int r = ioctl(fd, EVIOCGNAME(sizeof(name)), name);
        dprint("# %s \"%s\" margin=%d, divisor=%d option_d=%d\n",
                getenv("DEVNAME"), name, margin, divisor, option_d);
        if (r<0) {
                usage("ioctl name failed");
        }
        dprint("# t.ms + 10g br tr bl tl\n");
        sample_t s = { .usec=-1, .button=0, .sensor={0,0,0,0}};
        struct input_event ev;
        while (read(fd, &ev, sizeof(ev)) == sizeof(ev)) {
                if (start_sec==0) {
                        start_sec = ev.time.tv_sec;
                } else if (ev.time.tv_sec > (60+start_sec)) {
                        printf("timeout after 60 seconds\n");
                        exit(0);
                }
                if (ev.type == EV_SYN) {        // end if this time_stamp,
                        s.usec = (ev.time.tv_sec - start_sec) * 1024*1024;      // >> 20 will give seconds
                        s.usec += ev.time.tv_usec;
                        s.total = s.sensor[0]+s.sensor[1]+s.sensor[2]+s.sensor[3];
                        handle_sample( &s);
                } else if (ev.type == EV_KEY) {
                        if (ev.value) {
                                printf("button pressed\n ");
                                exit(0);
                        }
                } else if (ev.type == EV_ABS) {
                        s.sensor[0x3 & ev.code] = ev.value;
                } else {
                        printf("UNKNOWN type=0x%x code=0x%x value=0x%x\n", ev.type, ev.code, ev.value);
                }
        }
        perror("wiibb: error reading");
}

 

Det endelige wrapper script wiibb.sh

I begyndelse køres scriptet med debug print på så der bliver lidt statistik at kigge på senere, og måske optimere MARGIN og DIVISOR eller hvad der nu måtte vise sig. Filerne ligger under /var/log/wiibb

!/bin/bash
# called via this udev rule:
# SUBSYSTEM=="input", DEVPATH=="*:057E:0306*", ACTION=="add" RUN+="/bin/sh -c '/usr/local/sbin/wiibb.sh >>/tmp/wiibb.log 2>&1'"
# we rely on DEVPATH and DEVNAME, the rest we get from sysfs

#export PATH=/usr/local/bin:/usr/local/sbin:$PATH
set -x
DEVICE=/sys/$DEVPATH/device
LED=$DEVICE/leds/*:057E:0306*:blue:p0/brightness
# sets HID_UNIQ
. $DEVICE/uevent
MAC=$(echo $HID_UNIQ | tr a-f A-F)
DATE_TIME=$(date "+%Y%m%d-%H%M")
set 

echo Wii Board $MAC accessed via $DEVNAME $DEVPATH | sudo tee /dev/kmsg >/dev/console

echo 1 > $LED; sleep 1; # when it turns off it indicates device is off

/usr/local/sbin/wiibb -dd $DEVNAME  >/tmp/wiibb-$DATE_TIME.log1 2>/tmp/wiibb-$DATE_TIME.log2

if dpkg --compare-versions $(bluetoothctl -v) lt 5.43 ; then
        hcitool dc $MAC         # use hcitool to disconnect if bluetoothctl is old
else
        echo disconnect $MAC | bluetoothctl
fi

KG=$(cut -f4 -d' ' /tmp/wiibb-$DATE_TIME.log1)
mkdir -p /var/log/wiibb
cat /tmp/wiibb-$DATE_TIME.log1 /tmp/wiibb-$DATE_TIME.log2 | xz -z >/var/log/wiibb/$DATE_TIME-$KG.xz
rm -f /tmp/wibb-$DATE_TIME*

echo "WiiBB $MAC $_DATE_TIME $KG kg"| sudo tee /dev/kmsg >/dev/console

/etc/udev/rules.d/90-wiibb.rules

# Udev wiibb Call wiibb.sh when ever Wii Balanaceboard is turned on
SUBSYSTEM=="input", ENV{DEVNAME}=="/dev/input/event*", DEVPATH=="*:057E:0306*", ACTION=="add", RUN+="/bin/sh -c '/usr/local/sbin/wiibb.sh $DEVNAME >>/tmp/wiibb.log 2>&1'"

 

Nødvendige programmer der skal installeres

Listen her bliver sikkert mere udførlig engang jeg skal installere det på en ny maskine, men der skal ihverfald installeres disse pakker

sudo apt-get install xz gnuplot
# raspbian pre Stretch (før sommer 2017) installer gnuplot5
sudo apt-get install libxwiimote-dev xwiimote libxwiimote2

]]>
Hello World fra min Badevægt https://storepeter.dk/linux/hello-world-fra-min-badevaegt Thu, 02 Feb 2017 12:07:32 +0000 http://peter.storepeter.dk/?p=1009 Continue reading ]]> Der er mange der med succes har brugt Wii Balance Board (Wii-Fit) som badevægt. Der er da også ganske meget information tilgængelig:

Plus dem vi så på i mit sidste indlæg Badevægt, problemet med de løsninger var permanent parring. så det skal vi lige have styr på først.

Der er sket meget med bluetooth på Linux siden 2008, da den oprindelige kode blev skrevet, dengang klarede applikationerne stortset det hele selv.  Og de løsninger jeg præsenterede sidste gang baserede sig alle på den rå bluetooth adgang.

I dag er abstrakstions niveauet hævet, og mange af de basale operationer klares af Bluez, som bluetooth frame-work hedder under Linux. Dermed kan man parre Wii-Fit direkte fra en GUI Bluetooth manager, eller hvis man er mere til kommando-linien så brug bluetoothctl således:

$ bluetoothctl
[NEW] Controller B8:27:EB:C6:64:03 raspberrypi [default]
[NEW] Device 34:AF:2C:2D:A8:C2 Nintendo RVL-WBC-01
[CHG] Device 34:AF:2C:2D:A8:C2 Connected: yes
[CHG] Device 34:AF:2C:2D:A8:C2 Connected: no
[CHG] Device 34:AF:2C:2D:A8:C2 Connected: yes
# herover kommer der løbende status
# nedenfor en kommando prompt - prøv help
[bluetooth]# scan on
[bluetooth]# pair 34:AF:2C:2D:A8:C2
[bluetooth]# trusted 34:AF:2C:2D:A8:C2
# når man trykker på knappen bliver enheden tændt og forbundet
# for at bruge enheden udfør nedenstående
[bluetooth]# connect 34:AF:2C:2D:A8:C2
# og når vi er færdige med at bruge den
[bluetooth]# disconnect 34:AF:2C:2D:A8:C2


Der er allerede en driver hid-wiimote installeret under Linux, og når enheden først er parret vil den dukke op når Wii-Fit bliver tændt og fjernet når man disconnecter. Nedenfor er output fra dmesg:

$ dmesg
...
wiimote 0005:057E:0306.0004: unknown main item tag 0x0
wiimote 0005:057E:0306.0004: hidraw2: BLUETOOTH HID v6.00 Gamepad [Nintendo RVL-WBC-01] on b8:27:eb:c6:64:03
wiimote 0005:057E:0306.0004: New device registered
wiimote 0005:057E:0306.0004: detected device: Nintendo Wii Balance Board
wiimote 0005:057E:0306.0004: detected extension: Nintendo Wii Balance Board
input: Nintendo Wii Remote Balance Board as /devices/platform/soc/3f201000.uart/tty/ttyAMA0/hci0/hci0:13/0005:057E:0306.0004/input/input3
...
wiimote 0005:057E:0306.0004: Device removed

Så prøver vi: batteri, ud, batteri ind igen og Wii-Fit bliver automatisk detekteret igen når vi trykker på knappen, men ikke med den samme device.  Så problemet er allerede løst, der er en hid-driver, så nu kommer vi til det svære LÆS MANUALEN: man xwiimote

Her står bl.a. hvor man kan finde sin wii-fit device i mit tilfælde:

ls -l /sys/module/hid_wiimote/drivers/hid:wiimote/0005:057E:0306.0005

-r--r--r-- 1 root root 4096 Jan 27 02:42 bboard_calib
-r--r--r-- 1 root root 4096 Jan 27 02:42 country
-r--r--r-- 1 root root 4096 Jan 27 02:42 devtype
lrwxrwxrwx 1 root root    0 Jan 27 02:42 driver -> ../../../../../../../../../bus/hid/drivers/wiimote
-rw-rw-r-- 1 root root 4096 Jan 27 02:42 extension
drwxr-xr-x 3 root root    0 Jan 27 02:31 hidraw
drwxr-xr-x 3 root root    0 Jan 27 02:41 input
drwxr-xr-x 3 root root    0 Jan 27 02:42 leds
-r--r--r-- 1 root root 4096 Jan 27 02:42 modalias
drwxr-xr-x 2 root root    0 Jan 27 02:42 power
drwxr-xr-x 3 root root    0 Jan 27 02:42 power_supply
-r--r--r-- 1 root root 4096 Jan 27 02:42 report_descriptor
lrwxrwxrwx 1 root root    0 Jan 27 02:42 subsystem -> ../../../../../../../../../bus/hid
-rw-r--r-- 1 root root 4096 Jan 27 02:42 uevent

Og der er jo det hele.  HID device med Kalibrerings data, lysdiode, batteri information, osv.

På manualsiden var der også en henvisning til  xwiishow, et C-program med kildetekst, der viser status på alle Wii-enheder dynamisk, med fin character grafik. Alle sensor-værdierne kan ses og lysdioden kan tændes og slukkes, knappen er dog ikke med.

HID enheder kan normalt bruges uden at der skal installeres drivere.

Hello World

I min verden er det første program man laver et der skriver “Hello World”, blot for at få alt det basale på plads og vise at der er hul igennem, så det vil jeg også gøre her. Den eneste output enhed der er på Wii-Fit er den blå lysdiode, så det bliver den.

Jeg vil gerne måle vægten automatisk hver gang der er nogen der bruger den, ikke noget med at man skal starte et program via GUI eller kommando-linie

Under Linux er det udev der holder styr på devices der kommer og går.  Vi kan følge med i hvad der sker via:

$ udevadm monitor # only relevant lines shown

monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[275.636718] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
UDEV  [275.639448] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
KERNEL[275.702511] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002 (hid)
KERNEL[275.712277] add      /module/hid_wiimote (module)
KERNEL[275.712913] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/hidraw/hidraw1 (hidraw)
KERNEL[275.713030] add      /bus/hid/drivers/wiimote (drivers)
UDEV  [275.713266] add      /module/hid_wiimote (module)
UDEV  [275.713363] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002 (hid)
UDEV  [275.715145] add      /bus/hid/drivers/wiimote (drivers)
UDEV  [275.718351] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/hidraw/hidraw1 (hidraw)
KERNEL[275.771624] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/leds/0005:057E:0306.0002:blue:p0 (leds)
UDEV  [275.776623] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/leds/0005:057E:0306.0002:blue:p0 (leds)
KERNEL[275.841784] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18 (input)
KERNEL[275.841969] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/event16 (input)
KERNEL[275.842166] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/js1 (input)
KERNEL[275.842271] change   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002 (hid)
UDEV  [275.847737] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18 (input)
KERNEL[275.851425] change   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/power_supply/wiimote_battery_34:af:2c:2d:69:fe (power_supply)
UDEV  [275.854009] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/js1 (input)
...
KERNEL[277.005331] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/event16 (input)
KERNEL[277.028347] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/js1 (input)
KERNEL[277.060483] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18 (input)
KERNEL[277.060543] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/leds/0005:057E:0306.0002:blue:p0 (leds)
UDEV  [277.070917] add      /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/event16 (input)
UDEV  [277.075802] change   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002 (hid)
UDEV  [277.079230] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/event16 (input)
UDEV  [277.079341] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18/js1 (input)
UDEV  [277.079990] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/leds/0005:057E:0306.0002:blue:p0 (leds)
UDEV  [277.080135] change   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/power_supply/wiimote_battery_34:af:2c:2d:69:fe (power_supply)
UDEV  [277.081084] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/input/input18 (input)
KERNEL[277.084245] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/power_supply/wiimote_battery_34:af:2c:2d:69:fe (power_supply)
KERNEL[277.084431] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/hidraw/hidraw1 (hidraw)
KERNEL[277.084509] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002 (hid)
UDEV  [277.086365] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/power_supply/wiimote_battery_34:af:2c:2d:69:fe (power_supply)
UDEV  [277.088054] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002/hidraw/hidraw1 (hidraw)
UDEV  [277.089331] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256/0005:057E:0306.0002 (hid)
KERNEL[279.064318] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
UDEV  [279.065836] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)

Den enkleste måde at få vores program til at starte når Wii-Fit bliver tændt er at lave en en ny regel: /etc/udev/rules.d/99-wiibb.rules

# to see what we have available just print all ENV to file
# you could get same information using this command "udevadm monitor -p"
DRIVERS=="wiimote", RUN+="/bin/sh -c '(date;set) >>/tmp/wiibb-log'"

FIXME: Virker ikke under Ubuntu-16.10 efter reboot, da / var monteret read-only da systemd-udevd  blev startet  see discussion here: http://askubuntu.com/questions/883843/udev-rules-scripts-has-only-read-only-access-to-ubuntu16-10

Så tænder vi og slukker et par gange, og kigger på de mange variable vi kan se i /tmp/wiibb-log,  Her lægger vi specielt mærke  til:

  • Der bliver oprettet flere devices, den sidste har ACTION=change SUBSYSTEM=hid, det bruger vi reglen for at kalde vores program
  • HID_NAME=“Nintendo RVL-WBC-01” udelukker andre Wii enheder
  • HID_UNIQ er mac-adressen for Wii-Fit
  • DEVPATH fortæller hvor vi kan få fat i enheden denne gang

Næste trin er at lade udev kalde et program der morser “hello world” og derefter slukker enheden, og det er præcis hvad dette shell-script gør:

#!/bin/bash
# wiibb.sh Copyright Peter@Lorenzen.us Januar 2017, This is BSD-style free software
# called with DEVPATH and HID_UNIQ set
export PATH=/usr/local/bin:/usr/local/sbin:$PATH
LED=/sys/$DEVPATH/leds/$(basename $DEVPATH):blue:p0/brightness
MAC=$(echo $HID_UNIQ | tr a-f A-F)

h="...."; e="."; l=".-.."; o="---"; w=".--"; r=".-."; d="-.."

led_on() { (echo 1; sleep $1; echo 0) > $LED; }

echo Wii Board $MAC installed at $DEVPATH | sudo tee /dev/kmsg >/dev/console

echo connect $MAC | bluetoothctl

text="hello world"

for word in $text; do
        chars=(`echo $word | fold -w1`)
        for ch in "${chars[@]}"; do
                eval 'morse="${'"$ch"'}"'
                c_array=(`echo $morse | fold -w1`)
                for cm in "${c_array[@]}"; do
                        case $cm in
                        .) led_on 1; sleep 1;;
                        -) led_on 3; sleep 1;;
                        esac
                done
                sleep 2
        done
        sleep 4
done
echo 1 > $LED; sleep 1; # when it turns off it indicates device is off

echo disconnect $MAC | bluetoothctl

echo Wii Board $MAC done | sudo tee /dev/kmsg >/dev/console

Hmm – Det virkede ikke for mig og ganske rigtigt der er en BUG i bluetoothctl der kun kommer til udtryk når man kører det fra et script, interaktivt virker det fint.  Ubuntu-16.10 har bluez-5.41,  Raspian har bluez-5.23.  Problemet er løst i bluez-5.43.  Jeg har beskrevet hvordan man installerer den nyeste version fra Zesty under Ubuntu 16.04 eller 16.10 her http://askubuntu.com/a/884062/655086, Der sker følgende:

#!/bin/bash
sudo apt-get install devscripts debhelper dh-autoreconf flex bison libdbus-glib-1-dev libglib2.0-dev  libcap-ng-dev libudev-dev l
ibreadline-dev libical-dev check dh-systemd libebook1.2-dev

wget https://launchpad.net/ubuntu/+archive/primary/+files/bluez_5.43.orig.tar.xz
wget https://launchpad.net/ubuntu/+archive/primary/+files/bluez_5.43-0ubuntu1.debian.tar.xz
wget https://launchpad.net/ubuntu/+archive/primary/+files/bluez_5.43-0ubuntu1.dsc

tar xf bluez_5.43.orig.tar.xz
cd bluez-5.43
tar xf ../bluez_5.43-0ubuntu1.debian.tar.xz
# install patches relevant for rpi-3 bluetooth
. /etc/os-release
if [ $ID = raspbian ]; then
    wget https://gist.github.com/pelwell/c8230c48ea24698527cd/archive/3b07a1eb296862da889609a84f8e10b299b7442d.zip
    cd debian/patches
    unzip ../../3b07a1eb296862da889609a84f8e10b299b7442d.zip
    for i in c8230c48ea24698527cd-3b07a1eb296862da889609a84f8e10b299b7442d/*;do
        mv $i .
        basename $i >> series
    done
    rmdir c8230c48ea24698527cd-3b07a1eb296862da889609a84f8e10b299b7442d
    cd ../..
fi
# end of Raspian related patches
debchange --local=~lorenzen 'Backport to Xenial'
debuild -b -j4
cd ..
sudo dpkg -i *.deb

Ovenstående virker også under Raspian, alternativt se her

http://stackoverflow.com/questions/41707164/connect-ble-devices-with-raspberry-pi-3-b

Man kunne også blot  installere bluez fra debian stretch, eller vente et par måneder så er det standard

how-to-use-stretch-testing-packages

Når det så virker installeres shell-scriptet i /usr/local/sbin/wiibb.sh, og  en opdateret version af vores udev-rule /etc/udev/rules.d/99-wiibb.rules skal være som nedenfor

# Whenever a Wii Balance Board is turned on wiibb.sh is called
# wiibb.sh reads the weight and turns the device off when done
SUBSYSTEM=="hid", ENV{HID_NAME}=="Nintendo RVL-WBC-01" ACTION=="change", DRIVERS=="wiimote", RUN+="/usr/sbin/wiibb.sh"

Hvis man så er radio-amatør (oz1dmk),  kan man forvisse sig om at hele frame-worket virker, der er endda log-entries i dmesg.

Det var sådan set det hele, resten er blot at skrive et program til at læse vægten.  Vælg et programmerings-sprog og kom igang.

Næste gang laver vi lige et sådant program i C

]]>
Min Badevægt https://storepeter.dk/linux/min-badevaegt Thu, 26 Jan 2017 14:19:12 +0000 http://peter.storepeter.dk/?p=990 Continue reading ]]> En af mine kolegaer Jakob Stoustrup udsatte sig selv for en Matematisk Slankekur, heri var der bl.a. en fin kurve der viste udviklingen i hans vægt mens projektet stod på.  Sådan en vil jeg også have, og meget gerne med en tilsvarende nedadgående slope.

Kurven er altså ikke min, alene af den grund af min jo er pund.

Jakob må have en magisk maskine der overfører dataene fra vægten til WEB-en, Mon ikke det involverer mindst en kugle-pen, en A4-side og Matlab.  Jeg har tænkt mig at lave min alene med Bash, “The VI development kit”, og et stykke konsum-elektronik.

På Amazon kan man købe masser af bluetooth badevægte, så i stedet for at kigge ned på vægten kan man se hvad man vejer på sin telefon.  Det er trods alt lidt for moderne til mig, og havde jeg nu købt en af dem var denne artikel færdig allerede nu.

Typisk er der heller ikke nogen dokumentation på hvordan man får data ud at tingesten.

 

Det viser sig at der for år tilbage var en masse artikler om hvordan man kunne lave en “IofT badevægt” baseret på en Wii balance board (2007), det er en robust lille plastik sag, hvor der er en vægt bygget ind i de 4 fødder.  ideen var at disse værdier overføres til spille-konsolen via bluetooth i reeltid. Min dreng blev meget skuffet da han så den og jeg fortalte ham at vi ikke også fik en Wii.  Det ville ellers “også” have være en færdig løsning som man kan se i denne video Wii-Fit

Hvis man googler Wii Scale kommer der en masse projekter op, der er færdige lukkede programmer og open-source projekter af varierende kvalitet.  Der altså dokumentation tilgængelig og dermed er der ingen vej tilbage.

Jeg købte et par hos b&h photo for $15 stykket, leveret

 

Wii-Fit og Mac

De kom dagen efter og det første jeg prøvede var at installere http://snosrap.com/wiiscale/ (2013) på min Mac (MacOS 10.12) og inden for et minut kunne jeg se at jeg vejede for meget!.

Applikationen kan sættes op til at gemme resultaterne i en csv-fil, og der kan kaldes en WEB-side, så faktisk er der alt jeg har brug for, MEEEN jeg kunne ikke få den til at gemme vægten, det skyldes sikkert bit-rot, applicationen er skrevet til OSX-10.8.

Kildetekst til at parre Mac-en med Wii-Fit  permanent er tilgengængelig https://github.com/snosrap/WiiPair.git men selve applications kildeteksten kan jeg ikke finde.

Wii-Fit og Linux f.ex. Ubuntu på PC, eller Raspberry Pi

Der er mange der har leget med Wii-Fit og Linux, der er inspiration her:

De  bygger alle sammen videre på hinanden, og de kræver at der er en masse programmel installeret dele som ikke er supporteret på moderne Linux, og der er problemer med permanent parring med Wii-Fit. Men bortset fra det er det muligt at få hul igennem relativt let, man skal blot parre den hver gang.

Den bedste permanente løsning jeg har set er en blyant tapet til undersiden så man nemt kan parre vægten hver eneste gang man bruger den.

Det ville aldrig gå i min husholdning, Så selv om der kommer mere og mere glimmer på så er der ingen der har kunnet/villet fixe fundamentet.

Men kan du leve med blyant løsningen, så kan du godt bestille en Wii-Fit idag, vil du have en noget mere elegant løsning må du vente på mit næste indlæg

]]>