2018
November
- Some Notes About Android Virtual Devices
Running AVD emulator from the Windows command line/Powershell/mingw32 This assumes you’ve got Android Studio installed. First you need to have the tools path in your $PATH variable, to do this edit Path in Windows’ Environment variables to include for example: C:\Users\Max\AppData\Local\Android\Sdk\tools Then you can use emulator.exe from anywhere. The Android Studio docs fail to mention this. $ emulator -no-boot-anim -accel auto -writable-system -no-snapshot -gpu host -avd K10_API_22 Installing Google Play Services I’ve been messing around with older device profiles, and trying to run things in Chrome on Android 5.0 (API level 21). You’ll need to sideload the Google Play services system APKs (from a gapps package for your target system, e.g. for Android 5.0, but you might find that the /system partition is full. No problem, you can sideload to /sdcard and symlink: $ adb remount # Fails... $ adb push .\GmsCore.apk /system/priv-app $ adb: error: failed to copy '.\GmsCore.apk' to '/system/priv-app/GmsCore.apk': No space left on device # Works... $ adb push .\GmsCore.apk /sdcard/system/priv-app $ adb push .\GoogleLoginService.apk /sdcard/system/priv-app $ adb push .\GoogleServicesFramework.apk /sdcard/system/priv-app $ adb push .\Phonesky.apk /sdcard/system/priv-app # Symlink them $ adb shell ln -s /sdcard/system/priv-app/GmsCore.apk /system/priv-app/GmsCore.apk $ adb shell ln -s /sdcard/system/priv-app/GoogleLoginService.apk /system/priv-app/GoogleLoginService.apk $ adb shell ln -s /sdcard/system/priv-app/GoogleServicesFramework.apk /system/priv-app/GoogleServicesFramework.apk $ adb shell ln -s /sdcard/system/priv-app/Phonesky.apk /system/priv-app/Phonesky.apk $ adb shell stop $ adb shell start Messing around with /etc/hosts It’s trivial to edit the /etc/hosts on your AVD, but it only works if you have blank new line at the end of the hosts file. $ adb pull /etc/hosts hosts $ nano hosts # make your changes to the hosts file locally... $ adb push hosts /etc/hosts
August
- A custom font
Out of the box Pico-8 has one font. It’s the utilitarian answer to most problems. 3px by 5px and fairly legible. This font isn’t always good enough. How about making your own font? I’ve seen lots of approaches for creating fonts in Pico-8 – mostly using sprites for each glyph of the font, but then you’re limited to 8x8px and a have to mess with pal modes if you want to adjust colors on-the-fly. My approach uses the raw pixel data encoded in strings that are rendered using calls to pset, falling back to the built-in font for any missing glyphs. First, here are my helper functions… -- merge tables function mt(t1,t2) local new = {} for k,v in pairs(t1) do new[k] = v end for k,v in pairs(t2) do new[k] = v end return new end -- less verbose way to do ternary operations function ternary(cond ,t ,f ) if cond then return t else return f end end I had to come up with a compact way to represent the glyphs. Initially I used something like: { glyph = "o", pixels = { 0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0, }} But that contains a lot of tokens and lots of repeated/dead values to represent the empty pixels in a glyph. Eventually I moved to something like: {"o", "0.1.3.1.1.3.2.3.2.3.2.3.2.3.1.1.3"} That’s the same pixel data, but encoded in to a much shorter string. The first number indicates our starting parity – this can be 0 or 1 and tells us if the top/left pixel is empty or filled. Each time we have a . the parity flips, so with the example of our “o” glyph it starts with 0 for 1 count, then 1 for 3 counts, 0 for 1 count, 1 for 1 count, etc. When the remaining pixels in a glyph are 0 we can just omit them from the glyph data as they’re not drawn. The real wins are in the more simple/empty glyphs, such as “.” for example. Before: { glyph = ".", pixels = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, }} After: {".", "0.32.1"} Making this change took me from an initial token count of roughly 2800 to a much more manageable count of 560-ish. So with that technical talk out of the way, here’s the complete glyph set in code, and the function to render text. I’ll give some usage examples after. global_y = 0 function write(str,opts) opts = opts or {} _opts = { x=0, y=global_y, c=7 } opts = mt(_opts,opts) font = { w=5, h=7, -- first num indicates starting value, 0 or 1 -- each number indicates number of 0 or 1 in a row -- flips at each . glyphs = { {"0", "0.1.3.1.1.3.2.3.2.3.2.3.2.3.1.1.3"}, {"1", "0.2.1.3.2.2.1.1.1.4.1.4.1.4.1.2.5"}, {"2", "0.1.3.1.1.3.1.4.1.3.1.3.1.3.1.3.5"}, {"3", "0.1.3.1.1.3.1.4.1.2.2.5.2.3.1.1.3"}, {"4", "0.3.2.2.1.1.1.1.1.2.2.3.6.4.1.4.1"}, {"5", "1.6.4.1.5.3.5.2.3.1.1.3"}, {"6", "0.1.3.1.1.3.2.4.4.1.1.3.2.3.1.1.3"}, {"7", "1.5.4.1.4.1.3.1.3.1.3.1.3.1"}, {"8", "0.1.3.1.1.3.2.3.1.1.3.1.1.3.2.3.1.1.3"}, {"9", "0.1.3.1.1.3.2.3.1.1.4.4.2.3.1.1.3"}, {"a", "0.1.3.1.1.3.2.3.7.3.2.3.2.3.1"}, {"b", "1.4.1.1.3.2.3.5.1.1.3.2.3.5"}, {"c", "0.1.3.1.1.3.2.4.1.4.1.4.1.3.1.1.3.1"}, {"d", "1.4.1.1.3.2.3.2.3.2.3.2.3.5"}, {"e", "1.6.4.1.4.4.1.1.4.1.4.5"}, {"f", "1.6.4.1.4.4.1.1.4.1.4.1"}, {"g", "0.1.3.1.1.3.2.3.2.4.1.1.4.3.1.1.3"}, {"h", "1.1.3.2.3.2.3.7.3.2.3.2.3.1"}, {"i", "1.5.2.1.4.1.4.1.4.1.4.1.2.5"}, {"j", "1.5.2.1.4.1.4.1.2.1.1.1.2.1.1.1.3.2"}, {"k", "1.1.3.2.2.1.1.1.1.1.2.2.3.1.1.1.2.1.2.1.1.1.3.1"}, {"l", "1.1.4.1.4.1.4.1.4.1.4.1.4.5"}, {"m", "1.1.3.3.1.3.1.1.1.2.3.2.3.2.3.2.3.1"}, {"n", "1.1.3.3.2.2.1.1.1.2.2.3.3.2.3.2.3.1"}, {"o", "0.1.3.1.1.3.2.3.2.3.2.3.2.3.1.1.3"}, {"p", "1.4.1.1.3.2.3.5.1.1.4.1.4.1"}, {"q", "0.1.3.1.1.3.2.3.2.3.2.3.1.1.3.5.1"}, {"r", "1.4.1.1.3.2.3.5.1.1.1.1.2.1.2.1.1.1.3.1"}, {"s", "0.1.3.1.1.3.2.5.3.5.2.3.1.1.3"}, {"t", "1.5.2.1.4.1.4.1.4.1.4.1.4.1"}, {"u", "1.1.3.2.3.2.3.2.3.2.3.2.3.1.1.3"}, {"v", "1.1.3.2.3.1.1.1.1.1.2.1.1.1.2.1.1.1.3.1.4.1"}, {"w", "1.1.3.2.3.2.3.2.1.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1"}, {"x", "1.1.3.2.3.1.1.1.1.1.3.1.3.1.1.1.1.1.3.2.3.1"}, {"y", "1.1.3.1.1.1.1.1.3.1.4.1.4.1.4.1.4.1"}, {"z", "1.5.4.1.3.1.3.1.3.1.3.1.4.5"}, {" ", "0.35"}, {".", "0.32.1"}, {",", "0.27.1.3.1"}, {"!", "0.2.1.4.1.4.1.4.1.4.1.9.1"}, {"?", "0.1.3.1.1.3.1.4.1.3.1.3.1.9.1"}, {"'", "0.3.1.3.1"}, {"\"", "0.1.1.1.1.1.1.1.1"}, {":", "0.12.1.14.1"}, {";", "0.12.1.14.1.3.1"}, {"(", "0.3.1.3.1.3.1.4.1.4.1.5.1.5.1"}, {")", "0.1.1.5.1.5.1.4.1.4.1.3.1.3.1"}, {"[", "0.1.3.2.1.4.1.4.1.4.1.4.1.4.3"}, {"]", "0.1.3.4.1.4.1.4.1.4.1.4.1.2.3"}, {"{", "0.2.2.3.1.4.1.3.2.4.1.4.1.4.2"}, {"}", "0.1.2.4.1.4.1.4.2.3.1.4.1.3.2"}, {"<", "0.3.1.3.1.3.1.3.1.5.1.5.1.5.1"}, {">", "0.1.1.5.1.5.1.5.1.3.1.3.1.3.1"}, {"-", "0.16.3"}, {"+", "0.12.1.3.3.3.1"}, {"/", "0.4.1.3.1.4.1.3.1.3.1.4.1.3.1"}, {"\\", "1.1.5.1.4.1.5.1.5.1.4.1.5.1"}, } } _x = opts.x _y = opts.y letters = {} for i=1,#str do letter = sub(str,i,i) add(letters,letter) end for k,v in pairs(letters) do letter = nil for _glyph in all(font.glyphs) do if _glyph[1] == v then letter = _glyph[2] end end local _sx = _x local _sy = _y if letter != nil then local _px = _sx local _py = _sy pixels = {} local _p = sub(letter,1,1) local prev = nil for i=2,#letter do cur = sub(letter,i,i) if type(prev) == "number" and cur != "." then cur = sub(letter, i-1, i) end if cur == "." then _p = ternary(_p == "0", "1", "0") else for j=1,tonum(cur) do add(pixels, _p) end end prev = tonum(cur) end for i,p in pairs(pixels) do i = i - 1 if i % font.w == 0 then _py += 1 _sx = _x - (font.w * (i / font.w)) end _px = _sx + i if p == "0" then pset(_px, _py, opts.c) end end else font.w = 8 print(v, _sx, _y + ((font.h - 2)/2), opts.c) end _x += font.w + 1 end global_y += font.h + 2 end If you’re still with me, congratulations! Here is how you use the function: function _draw() cls() global_y = 0 -- reset on each draw call write("hello world") -- passing a color is possible! write("this is max 🐱", {c=12}) write("my font brings all") write("the devs to the yard") write("0123456789") write("abcdefghijklm") write("nopqrstuvwxyz") write(".,?!:;()[]{}-+/\\<>") write("yes, this is a lot") write("nicer to read! ♥") write("") write("█▒🐱⬇️░✽●♥☉웃⌂⬅️😐") write("♪🅾️◆…➡️★⧗⬆️ˇ∧❎▤▥") write("@#$%^&*") end And this is how it looks:
- A Foray Into Home Automation
This is an overview of why and how I decided to automate part of my home, and build my first IoT device…
- A Dialog System
This is a flexible dialog system I’ve come up with. It has the following features: automatically wrap long lines of text automatically page super long text to additional dialogs delay control per-letter display First, here are some of the helper functions that I’m using… -- draw a box with a frame -- arguments: -- x,y position -- w,h dimensions -- p padding -- fg,bg foreground and background colors function box(x,y,w,h,p,fg,bg) rectfill(x,y,x+w,y+h,bg) rect(x+p,y+p,x+w-p-1,y+h-p-1,fg) end -- joins two tables function jt(table1,table2) local new = {} for k,v in pairs(table1) do new[k] = v end for k,v in pairs(table2) do new[k] = v end return new end -- sleep for a set number of seconds (using 30fps mode) function sleep(s) for i=1,s*30 do flip() end end And lastly, the meat of the dialog system. I’ve scattered some comments through-out the code to try to explain what I’m doing. I’ll provide some initialisation examples after… function dialog(d) d = d or {} -- defaults _d = { a = true, bg = 5, c = false, dl = 5, fg = 7, p = 8, t = true, ts = 0.05, txt = "", w = 128, y = 96 } -- slightly dynamic defaults _d.x = (128 / 2) - (_d.w / 2) _d.h = 128 - _d.y -- merge passed options with default _d = jt(_d, d) -- text _ch = 6 -- char height _wi = _d.w - (_d.p * 2) _wrap = (_wi / 4) - 1 -- 27 local lines = {} local limit = ceil(#_d.txt / _wrap) local _tb = _d.txt while limit > 0 do local substr = sub(_tb, 1, _wrap) -- grab first bit -- ensure line doesn't have a space at the start if sub(substr,1,1) == " " then substr = sub(substr, 2) end add(lines, substr) -- stick it in our lines table _tb = sub(_tb, _wrap + 1, -1) -- get rest of text limit -= 1 end -- lines limit _ll = flr((_d.h - _d.p - _ch) / _ch) if _d.t == true then -- type it out box(_d.x, _d.y, _d.w, _d.h, _d.p / 2, _d.fg, _d.bg) for l=1,_ll do local _l = lines[l] or "" -- vertical alignment local va = _d.y + _d.p + ((l - 1) * 6) local _lld = _ll while _lld > 1 do if #lines < _lld then va += (_ch / 2) end _lld -= 1 end -- center it if necessary local ha = _d.x + _d.p if _d.c == true then ha = _d.x + (_d.w / 2) - (#_l * 2) end local chars = {} local limit = #_l local ci = 0 -- split line in to chars while limit > 0 do local _ch = sub(_l, 1, ci + 1) add(chars, _ch) ci += 1 limit -= 1 end for c=1,#chars do print(chars[c], ha, va, _d.fg) if _d.a == true then sfx(0) end sleep(_d.ts) -- tiny delay end end else -- print normally box(_d.x, _d.y, _d.w, _d.h, _d.p / 2, _d.fg, _d.bg) for l=1,_ll do local _l = lines[l] or "" -- vertical alignment local va = _d.y + _d.p + ((l - 1) * 6) local _lld = _ll while _lld > 1 do if #lines < _lld then va += (_ch / 2) end _lld -= 1 end -- center it if necessary local ha = _d.x + _d.p if _d.c == true then ha = _d.x + (_d.w / 2) - (#_l * 2) end print(_l, ha, va, _d.fg) if _d.a == true then sfx(0) end end end if #lines > _ll then local _nl = lines -- delete strings we've used for i=1,_ll do del(_nl,lines[1]) end -- join thr remaining local str = "" if #_nl > 1 then for i=1,#_nl do str = str .. _nl[i] end else str = _nl[1] end sleep(_d.dl) -- delay before next -- call recursive local n = jt(_d, {txt=str}) dialog(n) else sleep(_d.dl) -- delay before next end end Whew that was a lot of code. Here’s how you can use it: -- short, centered, delay of 2 dialog({ txt = "hello,", c = true, dl = 2 }) -- short, centered dialog({ txt = "my name is inigo montoya", c = true }) -- short, centered dialog({ txt = "you killed my father", c = true }) -- short, centered, not typed dialog({ txt = "prepare to die!", c = true, t= false }) -- long, typed, multiple message boxes dialog({txt="this is a lot of text that will wrap on to multiple lines and hopefully use a recursive message box in order to show all of the text ♥"}) You can see how this all looks below:
July
- Pocket Sprite Laptop Mount
I’ve been playing with this Pocket Sprite a bit lately. It’s a tiny ESP32-based device that can emulate Gameboy, NES and various other classic game systems.
May
- Extra small extra macro keys v2.0
Well I took another look at the design of my macro keys project sooner than I expected. I started over with the design of the 3D-printed part of this project, and then moved the buttons and guts over from the previous version.
- Extra small extra macro keys v1.0
After my previous macro key project back in January that used full-size arcade buttons, I was itching to make a more compact set of macro keys.
March
- New Printer Standoffs and a Rebuild
There are lots of mods and additions for the Anycubic Kossel on Thingiverse. I’ve been looking to reduce the footprint of my 3D printer for a while now as it currently shares the same desk as my PC. I came across a set of standoff legs and immediately downloaded and printed them.
- Shower Bracket Spacer
I’m gradually working my way around the house, looking at little niggling problems and thinking of ways to fix them with 3D printing. Today I addressed an issue in the shower.
- New Filament!
It’s time for more filament! That PrimaValue Natural is serving me well, but I’m in need of a change of colour. I’ve still got about 1/2 of the spool left, but it’ll be fine so long as I bag it well and stick it in a cupboard.
February
- An Audio Amp v2.0
I went back to the drawing board with the amp design, this time with the goal to make the layout more symmetrical and to move the AUX input to the back of the amp.
- Kitchen Tongs Spacer
We’ve got this pair of wooden kitchen tongs that have been through the dishwasher one too many times and so their binding has lost it’s elasticity. To fix this, I quickly modelled and printed a spacer to clip between the tongs, to restore some rigidity.
- An Audio Amp v1.0
My wife and I like to have the radio playing all night in our bedroom. Our radio only has one mono speaker and it sits on one side of the bed which means for it to be audible on both sides of the bed one person has to have a loud speaker next to their pillow.
- 3D Printing a new remote control
I bought a 3D printer back in November last year and have been steadily filling my home office with 3D-printed miniatures, things to fiddle with, and functional solutions to problems that I didn’t realise needed fixing since then.
- New Filament!
I bought some new filament! The 1kg of black that came with the printer has lasted me about 3 months, and so it was time to buy some more.
January
- What's in the box?
Well it’s a bunch of buttons. This is a macro keyboard, by some description. I had a bunch of arcade buttons kicking around from previous hobbies, and thought it would be nice to put them to some use.
- LED Lighting and a Kill Switch
For the first (and definitely not the last) modification to my printer, I’ve added some LED lights. I’ve used a cheap strip of USB-powered RGB LEDs, and they’re held in place around the top of the frame with their adhesive backing and some zip ties (for extra support!)
- Recycled Parts
I spent some time around new year 3D printing Dungeons and Dragons (DnD) characters for a friend. His main character is an Elven archer or something. We went to looking online for models that fit the bill. There are lots of free DnD miniature models on Thingiverse to download and print.