{"id":931,"date":"2016-07-05T19:32:48","date_gmt":"2016-07-05T18:32:48","guid":{"rendered":"http:\/\/ofalcao.pt\/blog\/?p=931"},"modified":"2016-07-05T23:29:33","modified_gmt":"2016-07-05T22:29:33","slug":"wedo-2-0-tilt-sensor","status":"publish","type":"post","link":"https:\/\/ofalcao.pt\/blog\/2016\/wedo-2-0-tilt-sensor","title":{"rendered":"WeDo 2.0 Tilt Sensor"},"content":{"rendered":"<div class=\"seriesmeta\">This post is part 6 of 6 of \u00a0<a href=\"https:\/\/ofalcao.pt\/blog\/series\/wedo-2-0-reverse-engineering\" class=\"series-269\" title=\"WeDo 2.0 - reverse engineering\">WeDo 2.0 - reverse engineering<\/a><\/div><p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>Thanks to Google, I already knew <a href=\"https:\/\/github.com\/LLK\/scratch-flash\/issues\/1040\">how to set the Tilt Sensor<\/a> to \u00abmode 1\u00bb and then read it. Unfortunately I was not able to understand the data format used by the sensor until I read the recently released SDK.<\/p>\n<p>So the Tilt Sensor has 3 operating modes:<\/p>\n<ul>\n<li>Angle (the default mode) = 0<\/li>\n<li>Tilt = 1<\/li>\n<li>Crash = 2<\/li>\n<\/ul>\n<p>In this post I&#8217;ll explain how to read just the Tilt Mode as is similar to the original WeDo Tilt Sensor operating mode (and also because I still have to organize all my SDK notes).<\/p>\n<p>The WeDo 2.0 ports can also be configured to read analog values in 3 formats: RAW (0), PERCENTAGE (1) and SI (2). Again, I will use just the SI format.<\/p>\n<p>To change the operating mode to Tilt Mode we write this command to the Input Command characteristic (0x003a):<\/p>\n<pre>0102012201010000000201<\/pre>\n<p>The meaning is similar to the command used in last post for setting the RGB LED to Absolute Mode, the only difference is the Device Type (22h is the Tilt Sensor).<\/p>\n<p>In Tilt Mode and SI Format there are 6 possible values we can read from the Tilt Sensor:<\/p>\n<p>TILT_SENSOR_DIRECTION_NEUTRAL = 0<br \/>\nTILT_SENSOR_DIRECTION_BACKWARD = 3<br \/>\nTILT_SENSOR_DIRECTION_RIGHT = 5<br \/>\nTILT_SENSOR_DIRECTION_LEFT = 7<br \/>\nTILT_SENSOR_DIRECTION_FORWARD = 9<br \/>\nTILT_SENSOR_DIRECTION_UNKNOWN = 10<\/p>\n<p>These values are read from the SensorValue characteristic (0x0032) in a 6-byte format where the first 2 bytes are always &#8220;0201&#8221; and the other 4 bytes are a <a href=\"http:\/\/www.h-schmidt.net\/FloatConverter\/IEEE754.html\">IEEE754<\/a> float format representation of the value &#8211; and now I finally know how to understand those values!<\/p>\n<p>So with just a crude python script we can use the WeDo 2.0 Hub with a Tilt Sensor to control a Mindstorms EV3 car:<\/p>\n<div class=\"jetpack-video-wrapper\"><iframe loading=\"lazy\" title=\"EV3 robot wireless control with WeDo 2.0\" width=\"840\" height=\"473\" src=\"https:\/\/www.youtube.com\/embed\/fn1R9g6sh4Q?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/div>\n<pre>#!\/usr\/bin\/env python\r\n\r\n# run with sudo\r\n\r\nfrom ev3dev.auto import *\r\nfrom gattlib import GATTRequester\r\nfrom time import sleep\r\n\r\nBTdevice = \"hci0\"\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # BlueTooth 4.0 BLE capable device\r\n\r\nWeDo2HubAddress\u00a0 = \"A0:E6:F8:1E:58:57\"\r\n\r\nSensorValue_hnd\u00a0 = 0x32\r\nInputCommand_hnd = 0x3a\r\n\r\n# assume TiltSensor at Port #1, change to Mode 1 (Tilt) and Unit = SI\r\n# TiltCmdMode1= \"0102012201010000000201\"\r\nTiltCmdMode1 = str(bytearray([01,02,01,22,01,01,00,00,00,02,01]))\r\n\r\n# Sensor value should read:\r\n# \"0201\" + a 4 byte value (LITTLE_ENDIAN)\r\n# representing a float in IEEE754 format\r\n# http:\/\/www.h-schmidt.net\/FloatConverter\/IEEE754.html\r\n\r\nTILT_SENSOR_DIRECTION_NEUTRAL\u00a0 = \"0000\" # 0\r\nTILT_SENSOR_DIRECTION_BACKWARD = \"4040\" # 3\r\nTILT_SENSOR_DIRECTION_RIGHT\u00a0\u00a0\u00a0 = \"a040\" # 5\r\nTILT_SENSOR_DIRECTION_LEFT\u00a0\u00a0\u00a0\u00a0 = \"e040\" # 7\r\nTILT_SENSOR_DIRECTION_FORWARD\u00a0 = \"1041\" # 9\r\nTILT_SENSOR_DIRECTION_UNKNOWN\u00a0 = \"2041\" # 10\r\n\r\nDELAY\u00a0\u00a0\u00a0\u00a0\u00a0 = 0.3\r\nSTEP\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = 0.175\r\nDT\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = 75\r\n\r\ntilt_data=[0,0,0,0,0,0]\r\n\r\nm1 = LargeMotor(OUTPUT_A)\r\nm2 = LargeMotor(OUTPUT_B)\r\n\r\nm1.run_direct()\r\nm2.run_direct()\r\nm1.duty_cycle_sp=0\r\nm2.duty_cycle_sp=0\r\n\r\nreq = GATTRequester(WeDo2HubAddress,True,BTdevice)\r\nsleep(DELAY)\r\n\r\n\r\n# try reset - none works\r\n# req.write_by_handle(InputCommand_hnd,\"\\x22\\x44\\x11\\xAA\")\r\n# req.write_by_handle(InputCommand_hnd,\"\\x44\\x11\\xAA\")\r\n# sleep(DELAY)\r\n\r\n# Configure Tilt Sensor\r\nreq.write_by_handle(InputCommand_hnd,TiltCmdMode1)\r\nsleep(DELAY)\r\n\r\nwhile True:\r\n\u00a0 # read Sensor\r\n\u00a0 data=req.read_by_handle(SensorValue_hnd)[0]\r\n\r\n\u00a0 if data:\r\n\u00a0\u00a0\u00a0 for i,b in enumerate(data):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 tilt_data[i]=ord(b)\r\n\r\n\u00a0\u00a0\u00a0 if (tilt_data[4] == 0x00) and (tilt_data[5] == 0x00):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 print(\"NEUTRAL\")\r\n\u00a0\u00a0\u00a0 elif (tilt_data[4] == 0x40) and (tilt_data[5] == 0x40):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 print(\"BACKWARD\")\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=-DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=-DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 sleep(STEP)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0 elif (tilt_data[4] == 0xa0) and (tilt_data[5] == 0x40):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 print(\"RIGHT\")\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=-DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 sleep(STEP)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0 elif (tilt_data[4] == 0xe0) and (tilt_data[5] == 0x40):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 print(\"LEFT\")\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=-DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 sleep(STEP)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0 elif (tilt_data[4] == 0x10) and (tilt_data[5] == 0x41):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 print(\"FORWARD\")\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=DT\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 sleep(STEP)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m1.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 m2.duty_cycle_sp=0\r\n\u00a0\u00a0\u00a0 elif (tilt_data[4] == 0x20) and (tilt_data[5] == 0x41):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 print(\"UNKNOWN\")\r\n\r\n\u00a0 else:\r\n\u00a0\u00a0\u00a0 print(\"No data\")\r\n\r\n\u00a0 sleep(0.1)<\/pre>\n<p>Just a warning note: I ran a first version of this script in my Ubuntu laptop (without the EV3 parts) and it <span style=\"text-decoration: underline;\">always<\/span> fails at first run (&#8220;No data&#8221;, no matter what I do with the Tilt Sensor) but if I abort the script and run it again while the Hub is still in discovering mode then it <span style=\"text-decoration: underline;\">always<\/span> works. But when the same script runs in the EV3 it <span style=\"text-decoration: underline;\">almost always<\/span> works at first try.<\/p>\n<p>And of course I do have a good explanation: gremlins! What else?<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">This post is part 6  of 6 of \u00a0<a href=\"https:\/\/ofalcao.pt\/blog\/series\/wedo-2-0-reverse-engineering\" class=\"series-269\" title=\"WeDo 2.0 - reverse engineering\">WeDo 2.0 - reverse engineering<\/a><\/div><p>&nbsp; &nbsp; Thanks to Google, I already knew how to set the Tilt Sensor to \u00abmode 1\u00bb and then read it. Unfortunately I was not able to understand the data format used by the sensor until I read the recently released SDK. So the Tilt Sensor has 3 operating modes: Angle (the default mode) = &hellip; <a href=\"https:\/\/ofalcao.pt\/blog\/2016\/wedo-2-0-tilt-sensor\" class=\"more-link\">Continuar a ler<span class=\"screen-reader-text\"> &#8220;WeDo 2.0 Tilt Sensor&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[98,271],"tags":[],"series":[269],"class_list":["post-931","post","type-post","status-publish","format-standard","hentry","category-python","category-wedo-2-0","series-wedo-2-0-reverse-engineering"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p2Mhyv-f1","_links":{"self":[{"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/posts\/931","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/comments?post=931"}],"version-history":[{"count":0,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/posts\/931\/revisions"}],"wp:attachment":[{"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/media?parent=931"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/categories?post=931"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/tags?post=931"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/series?post=931"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}