{"id":1123,"date":"2017-07-07T19:17:09","date_gmt":"2017-07-07T18:17:09","guid":{"rendered":"http:\/\/ofalcao.pt\/blog\/?p=1123"},"modified":"2017-07-07T19:42:36","modified_gmt":"2017-07-07T18:42:36","slug":"ev3-and-chromecast","status":"publish","type":"post","link":"https:\/\/ofalcao.pt\/blog\/2017\/ev3-and-chromecast","title":{"rendered":"EV3 and Chromecast"},"content":{"rendered":"<div class=\"seriesmeta\">This post is part 1 of 2 of \u00a0<a href=\"https:\/\/ofalcao.pt\/blog\/series\/ev3-and-chromecast\" class=\"series-304\" title=\"EV3 and Chromecast\">EV3 and Chromecast<\/a><\/div><p>The LEGO MINDSTOMS EV3 has a very small display, dificult to use for complex tasks.<\/p>\n<p>Most of the time I use it through SSH so the display doesn&#8217;t bother me but sometimes, when autonomy is needed, I find myself thinking how great would be if I could use something like like a large TV or a video projector.<\/p>\n<p>Yes, we can do it with something like a laptop or a Raspberry Pi as a &#8220;proxy&#8221;. But now we can also do it with a Google Chromecast:<\/p>\n<div class=\"jetpack-video-wrapper\"><iframe loading=\"lazy\" title=\"EV3 and Chromecast\" width=\"840\" height=\"473\" src=\"https:\/\/www.youtube.com\/embed\/klB222qTCg0?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<p>I installed lighttpd (a light web server) on the EV3 and configured it to listen to port 3000. Then I used <a href=\"https:\/\/github.com\/balloob\/pychromecast\">pychromecast<\/a> to make my Chromecast &#8220;play&#8221; JPEG files present on the EV3 web server &#8211; a JPEG file for each value or message that I want to show on TV.<\/p>\n<p>Here the script I used in the video:<\/p>\n<pre>#!\/usr\/bin\/python3\r\nfrom ev3dev.ev3 import *\r\nfrom time import sleep\r\nimport pychromecast\r\n\r\nus = UltrasonicSensor()\r\nbt = Button()\r\n\r\nDELAY = 0.01\r\nDELAY_PLAY = 1.75 # 1.5 NOT OK\r\n\r\nchromecasts = pychromecast.get_chromecasts()\r\ncast = next(cc for cc in chromecasts if cc.device.friendly_name == \"NOMAD\")\r\ncast.wait()\r\nmc = cast.media_controller\r\nprint(\"Blacking chromecast...\")\r\nmc.play_media('http:\/\/192.168.43.104:3000\/black.png', 'image\/png')\r\nsleep(5)\r\nmc.stop()\r\nmc.play_media('http:\/\/192.168.43.104:3000\/pressanykey.png', 'image\/png')\r\n\r\nwhile bt.any() == False:\r\n    sleep(DELAY)\r\n\r\nmc.stop()\r\n\r\nlast_dist=-1\r\nwhile True:\r\n    dist = us.distance_centimeters\/10\r\n    if dist != last_dist:\r\n        mc.play_media('http:\/\/192.168.43.104:3000\/'+str(round(dist))+'.png', 'image\/png')\r\n        sleep(DELAY_PLAY)\r\n        mc.stop()\r\n    last_dist = dist<\/pre>\n<p>&#8220;NOMAD&#8221; is the name I gave to my Chromecast on setup, the script finds it by name so I don&#8217;t have to bother with the IP address when I take it to another place or event.<\/p>\n<p>&#8220;192.168.43.104&#8221; is the IP address of my EV3. A better script will find it by itself.<\/p>\n<p>The JPEG files that contain the numbers were generated with another script:<\/p>\n<pre>#!\/usr\/bin\/python3\r\nfrom time import sleep\r\nfrom PIL import Image, ImageDraw, ImageFont\r\n\r\ntxt = Image.new('RGB', (320,240) )\r\nfnt = ImageFont.truetype(font=\"\/usr\/share\/fonts\/truetype\/dejavu\/DejaVuSansMono-Bold.ttf\", size=75)\r\nd = ImageDraw.Draw(txt)\r\n\r\nfor x in range(0,256):\r\n    # fill image with black\r\n    txt.paste((0,0,0) , [0,0,320,240] )\r\n    # (x,y) from top left , text , font, (R,G,B) color \r\n    d.text( (90,80), str(x).zfill(3) , font=fnt, fill=(255,255,255) )\r\n    txt.save('\/var\/www\/html\/'+str(x)+'.png')<\/pre>\n<p>This last script needs to be run with sudo because it writes to the webserver content folder (&#8220;\/var\/www\/html\/&#8221;) and since I let the default permissions unchanged the default (&#8216;robot&#8217;) account cannot write on it.<\/p>\n<p>This method is far from perfect &#8211; the Chromecast is good for streamed content but not so good for static content. I cannot switch from an image to another in less than 1.75 seconds (and not sure if even 1.75 doesn&#8217;t get me in troubles) and when doing it the image flikers. And the Chromecast caches the files so when I change anything (say the &#8220;press any key&#8221; image) I have to reboot the Chromecast to clear the cache.<\/p>\n<p>So this script is also very useful (and boy, how I hate rebooting something)<\/p>\n<pre>#!\/usr\/bin\/python3\r\n\r\nfrom time import sleep\r\nimport pychromecast\r\nchromecasts = pychromecast.get_chromecasts()\r\ncast = next(cc for cc in chromecasts if cc.device.friendly_name == \"NOMAD\")\r\ncast.wait()\r\ncast.reboot()<\/pre>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">This post is part 1  of 2 of \u00a0<a href=\"https:\/\/ofalcao.pt\/blog\/series\/ev3-and-chromecast\" class=\"series-304\" title=\"EV3 and Chromecast\">EV3 and Chromecast<\/a><\/div><p>The LEGO MINDSTOMS EV3 has a very small display, dificult to use for complex tasks. Most of the time I use it through SSH so the display doesn&#8217;t bother me but sometimes, when autonomy is needed, I find myself thinking how great would be if I could use something like like a large TV or &hellip; <a href=\"https:\/\/ofalcao.pt\/blog\/2017\/ev3-and-chromecast\" class=\"more-link\">Continuar a ler<span class=\"screen-reader-text\"> &#8220;EV3 and Chromecast&#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":[1],"tags":[],"series":[304],"class_list":["post-1123","post","type-post","status-publish","format-standard","hentry","category-sem-categoria","series-ev3-and-chromecast"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p2Mhyv-i7","_links":{"self":[{"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/posts\/1123","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=1123"}],"version-history":[{"count":0,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/posts\/1123\/revisions"}],"wp:attachment":[{"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/media?parent=1123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/categories?post=1123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/tags?post=1123"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/ofalcao.pt\/blog\/wp-json\/wp\/v2\/series?post=1123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}