diff --git a/Gemfile.lock b/Gemfile.lock index fcd43b2..6960804 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,31 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.5.2) + addressable (2.6.0) public_suffix (>= 2.0.2, < 4.0) - adsf (1.4.0) + adsf (1.4.1) rack (>= 1.0.0, < 3.0.0) - bitly (1.1.0) + bitly (1.1.2) httparty (>= 0.7.6) multi_json (~> 1.3) oauth2 (>= 0.5.0, < 2.0) builder (3.2.3) coderay (1.1.2) colored (1.2) - concurrent-ruby (1.0.5) - cri (2.13.0) + concurrent-ruby (1.1.4) + cri (2.15.3) colored (~> 1.2) ddmemoize (1.0.0) ddmetrics (~> 1.0) ref (~> 2.0) ddmetrics (1.0.1) ddplugin (1.0.2) - faraday (0.12.2) + faraday (0.15.4) multipart-post (>= 1.2, < 3) - ffi (1.9.25) + ffi (1.10.0) formatador (0.2.5) fssm (0.2.10) - guard (2.14.2) + guard (2.15.0) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) lumberjack (>= 1.0.12, < 2.0) @@ -35,72 +35,73 @@ GEM shellany (~> 0.0) thor (>= 0.18.1) guard-compat (1.2.1) - guard-nanoc (2.1.3) + guard-nanoc (2.1.4) guard (~> 2.8) guard-compat (~> 1.0) nanoc (>= 4.3.8, < 5.0) - haml (5.0.3) + haml (5.0.4) temple (>= 0.8.0) tilt hamster (3.0.0) concurrent-ruby (~> 1.0) - httparty (0.15.6) + httparty (0.16.3) + mime-types (~> 3.0) multi_xml (>= 0.5.2) - jwt (1.5.6) + json_schema (0.20.1) + jwt (2.1.0) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) lumberjack (1.0.13) - method_source (0.9.0) - mime-types (3.1) + method_source (0.9.2) + mime-types (3.2.2) mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mini_portile2 (2.3.0) - multi_json (1.12.2) + mime-types-data (3.2018.0812) + mini_portile2 (2.4.0) + multi_json (1.13.1) multi_xml (0.6.0) multipart-post (2.0.0) - nanoc (4.9.3) + nanoc (4.11.0) addressable (~> 2.5) - cri (~> 2.8) + cri (~> 2.15) ddmemoize (~> 1.0) ddmetrics (~> 1.0) ddplugin (~> 1.0) hamster (~> 3.0) + json_schema (~> 0.19) parallel (~> 1.12) ref (~> 2.0) slow_enumerator_tools (~> 1.0) tomlrb (~> 1.2) nenv (0.3.0) - nokogiri (1.8.1) - mini_portile2 (~> 2.3.0) + nokogiri (1.10.1) + mini_portile2 (~> 2.4.0) notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) - oauth2 (1.4.0) - faraday (>= 0.8, < 0.13) - jwt (~> 1.0) + oauth2 (1.4.1) + faraday (>= 0.8, < 0.16.0) + jwt (>= 1.0, < 3.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - parallel (1.12.1) - pry (0.11.3) + parallel (1.13.0) + pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) public_suffix (3.0.3) - rack (2.0.3) - rake (12.1.0) + rack (2.0.6) + rake (12.3.2) rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - rb-kqueue (0.2.5) - ffi (>= 0.5.0) + rb-inotify (0.10.0) + ffi (~> 1.0) rdiscount (2.2.0.1) ref (2.0.0) - rouge (3.2.1) + rouge (3.3.0) ruby_dep (1.5.0) - rubypants (0.6.0) - sass (3.5.1) + rubypants (0.7.0) + sass (3.7.3) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -109,9 +110,9 @@ GEM slow_enumerator_tools (1.1.0) systemu (2.6.5) temple (0.8.0) - thor (0.20.0) - tilt (2.0.8) - tomlrb (1.2.7) + thor (0.20.3) + tilt (2.0.9) + tomlrb (1.2.8) PLATFORMS ruby @@ -128,7 +129,6 @@ DEPENDENCIES nanoc (~> 4.0) nokogiri rake - rb-kqueue (>= 0.2) rdiscount rouge rubypants @@ -136,4 +136,4 @@ DEPENDENCIES systemu BUNDLED WITH - 1.16.3 + 1.17.2 diff --git a/content/technical/2019/01/linux-conf-au-rust-epaper-badge.md b/content/technical/2019/01/linux-conf-au-rust-epaper-badge.md index d367dbb..9fe8e61 100644 --- a/content/technical/2019/01/linux-conf-au-rust-epaper-badge.md +++ b/content/technical/2019/01/linux-conf-au-rust-epaper-badge.md @@ -1,23 +1,31 @@ -This week I attended linux.conf.au (for the first time) in Christchurch, New +This week I attended [linux.conf.au] (for the first time) in Christchurch, New Zealand. It's a week long conference covering Linux, open open source software and hardware, privacy, security and much more. The theme this year was [IoT]. In line with the theme I built a digital conference badge to take to the conference. It used a tri-colour e-Paper display and was powered by a Rust -program I built running on Raspbian Linux. This post describes how it was -built, how it works, and how it fared at the conference. +program I built running on [Raspbian Linux][Raspbian]. This post describes how +it was built, how it works, and how it fared at the conference. The [source +code is on GitHub][source]. -### Building +
+ The badge in its final state after the conference. +
The badge in its final state after the conference
+
+ +## Building After booking my tickets in October I decided I wanted to build a digital conference badge. I'm not entirely sure what prompted me to do this but it was -a combination of seeing projects like the [BADGEr] in past, the theme of +a combination of seeing projects like the [BADGEr] in the past, the theme of linux.conf.au 2019 being IoT, and an excuse to write more Rust. Since it was ostensibly a Linux conference it also seemed appropriate for it to run Linux. -Over the next few weeks I collected the parts and adaptors to build the badge. The main components were: +Over the next few weeks I collected the parts and adaptors to build the badge. +The main components were: -* [Raspberry Pi Zero W] - AU$15.00 -* [Pimoroni Inky pHAT] e-Paper display - AU$38.00 +* [Raspberry Pi Zero W] — AU$15.00 +* [Pimoroni Inky pHAT] e-Paper display — AU$38.00 +* 4800mAh/3.7V USB battery pack that I already owned The Raspberry Pi Zero W is a single core 1Ghz ARM SoC with 512Mb RAM, Wi-FI, Bluetooth, microSD card slot, and mini HDMI. The Inky pHAT is a 212x104 pixel @@ -25,40 +33,35 @@ tri-colour (red, black, white) e-Paper display. It takes about 15 seconds to refresh the display but it draws very little power in between updates and the image persists even when power is removed. -I powered the badge with a several year old 4800mAh USB battery pack that I -already owned. Some rough calculations suggested that it should run for many -hours on this battery but I didn't actually test this until day 1 of the -conference. - ### Support Crates The first part of the project involved building a Rust driver for the controller in the e-Paper display. That involved determining what controller -the display used, as Pimoroni did not document the display they used for the -Inky pHAT. Searching online for some of the comments in the Python driver -suggested the display was possibly a HINK-E0213A07 - Electronic Paper display -from Holitech Co. Further searching based on [the datasheet for that -display][HINK-E0213A07] suggested that the controller was a [Solomon Systech -SSD1675][SSD1675]. Cross referencing the display datasheet, [SSD1675 -datasheet], and the [Python source of Pimoroni's Inky pHAT -driver][https://github.com/pimoroni/inky] suggested I was on the right track. +the display used, as Pimoroni did not document it. Searching online for some of +the comments in [the Python driver][inky] suggested the display was possibly a +HINK-E0213A07 from Holitech Co. Further searching based on [the datasheet for +that display][HINK-E0213A07] suggested that the controller was a [Solomon +Systech SSD1675][SSD1675]. Cross referencing the display datasheet, [SSD1675 +datasheet], and the [Python source of Pimoroni's Inky pHAT driver][inky] +suggested I was on the right track. I set about building the Rust driver for the SSD1675 using the [embedded HAL -traits]. These traits allow embedded Rust drivers to be built against a -defacto-standard set of traits that allow the driver to be used in any -environment that implements the traits. For example I make use of traits for -[SPI] devices, and [GPIO] pins, which are implemented for -[Linux][embedded-linx], as well as say, [STM32 dev boards]. This allows the -driver to be written once and be potentially usable on may different devices. +traits][embedded-hal]. These traits allow embedded Rust drivers to be built +against a de facto standard set of traits that allow the driver to be used in +any environment that implements the traits. For example I make use of traits +for [SPI] devices, and [GPIO] pins, which are implemented for +[Linux][linux-embedded-hal], as well as say, [the STM32F30x family of +microcontrollers][stm32f30x-hal]. This allows the driver to be written once and +used on many devices. The result was the [ssd1675 crate]. It's a so called no-std crate. That means it does not use the Rust standard library, instead sticking only to the core -library. This allows the crate to be used in devices and microcontrollers +library. This allows the crate to be used on devices and microcontrollers without features like file systems, or heap allocators. The crate also makes use of the [embedded-graphics crate][embedded-graphics], which makes it easy to draw text and basic shapes on the display in a memory efficient manner. -Whilst testing the ssd1675 crate I also built another crate, [profont], which +While testing the ssd1675 crate I also built another crate, [profont], which provides 7 sizes of the [ProFont font] for embedded graphics. The profont crate was published 24 Nov 2018, and ssd1675 was published a month later on 26 Dec 2018. @@ -68,62 +71,116 @@ was published 24 Nov 2018, and ssd1675 was published a month later on 26 Dec Now that I had all the prerequisites in place I could start working on the badge proper. I had a few goals for the badge and its implementation: -* I wanted the badge to have some interactive component. +* I wanted it to have some interactive component. * I wanted there to be some sort of Internet aspect to tie in with the IoT theme of the conference. * I wanted the badge to be entirely powered by a single, efficient Rust binary, - that did not shell out to other commands, or anything like that. + that did not shell out to other commands or anything like that. * Ideally it would be relatively power efficient.
- Photo of e-Paper display showing my name, websize, IP address and uname information. + An early revision of the badge from 6 Jan 2019 showing my name, website, badge IP, and kernel info.
An early revision of the badge from 6 Jan 2019
I settled on having the badge program serve up a web page with some information -about the project, myself, and some live stats of the Raspberry Pi (uptime, -free RAM, etc.). The plain text version of the page looked like this: +about the project, myself, and some live stats of the Raspberry Pi (OS, kernel, +uptime, free RAM). The plain text version of the page looked like this: -[Insert sample response hre] + Hi I'm Wes! -The interactive part came in the form of a virtual 'hi' counter. Each HTTP POST -to the `/hi` endpoint incremented the count, which was shown on the badge. The -badge showed a URL to access in order to view the page, the URL was just the -badge's IP address on the conference Wi-Fi. To provide a little protection -against abuse I added code that only allowed a give IP to increment the count -once per hour. + Welcome to my conference badge. It's powered by Linux and + Rust running on a Raspberry Pi Zero W with a tri-colour Inky + pHAT ePaper dispay. The source code is on GitHub: -When building the badge software these are some of the details and goals I implemented: + https://github.com/wezm/linux-conf-au-2019-epaper-badge -* Wi-Fi going away -* IP address changing -* Memory use of large POSTs (don + + Say Hello + --------- + + 12 people have said hi. + + Say hello in person and on the badge. To increment the hello + counter on the badge: + + curl -X POST http://10.0.0.18/hi + + + About Me + -------- + + I'm a software developer from Melbourne, Australia. I + currently work at GreenSync building systems to help make + better use of renewable energy. + + Find me on the Internet at: + + Email: wes@wezm.net + GitHub: https://github.com/wezm + Mastodon: https://mastodon.social/@wezm + Twitter: https://twitter.com/wezm + Website: http://www.wezm.net/ + + + Host Information + ---------------- + + (_\)(/_) OS: Raspbian GNU/Linux + (_(__)_) KERNEL: Linux 4.14.79+ + (_(_)(_)_) UPTIME: 3m + (_(__)_) MEMORY: 430.3 MB free of 454.5 MB + (__) + + + .------------------------. + | Powered by Rust! | + '------------------------' + / + / + _~^~^~_ + \) / o o \ (/ + '_ - _' + / '-----' \ + +The interactive part came in the form of a virtual "hello" counter. Each HTTP +POST to the `/hi` endpoint incremented the count, which was shown on the badge. +The badge displayed the URL of the page. The URL was just the badge's IP +address on the conference Wi-Fi. To provide a little protection against abuse I +added code that only allowed a given IP to increment the count once per hour. + +When building the badge software these are some of the details and things I +strived for: + +* Handle Wi-Fi going away +* Handle IP address changing * Prevent duplicate submissions * Pluralisation of text on the badge and on the web page * Automatically shift the text as the count requires more digits -* If the web page is requested with an `Accept` header that doesn't include - `text/html` (E.g. `curl`) then the response is plain text and the method to - "say hello" is a curl command. If the user agent indicates they accept HTML - then the page is HTML and conttains a form with a button to, "say hello". +* Serve plain text and HTML pages: + * If the web page is requested with an `Accept` header that doesn't include + `text/html` (E.g. `curl`) then the response is plain text and the method to, + "say hello", is a `curl` command. + * If the user agent indicates they accept HTML then the page is HTML and + contains a form with a button to, "say hello". * Avoid aborting on errors: * I kind of ran out of time to handle all errors well, but most are handled gracefully and won't abort the program. In some cases a default is used in - the face of an eror. In other cases I just resorted to logging a mesasge and + the face of an error. In other cases I just resorted to logging a message and carrying on. * Keep memory usage low: * The web server efficiently discards any large POST requests sent to it, to - avoid exhausing RAM. Typical RAM stats showed around 420Mb free of the - 512Mb most of the time, of which some is taken for the GPU. The Rust - program itself using about 4Mb. -* Be relatively power efficient + avoid exhausting RAM. + * Typical RAM stats showed the Rust program using about 3Mb of RAM. +* Be relatively power efficient: * Use Rust instead of a scripting language * Only update the display when something it's showing changes - * Only check for changes every 15 seconds (the rest of the time that tread just slept) + * Only check for changes every 15 seconds (the rest of the time that thread just sleeps) * Put the display into deep sleep after updating -I used [hyper] for the HTTP server. To get a feel for the limits of the device -I did some rudimentary HTTP benchmarking with [wrk] and concluded that 300 requests -per second was was probably going to be fine. ;) +I used [hyper] for the HTTP server built into the binary. To get a feel for the +limits of the device I did some rudimentary HTTP benchmarking with [wrk] and +concluded that 300 requests per second was was probably going to be fine. `;-)` Running 10s test @ http://10.0.0.18:8080/ 4 threads and 100 connections @@ -136,41 +193,38 @@ per second was was probably going to be fine. ;) ### Mounting -When I started the project I imagined it would hang it around my neck like -conference lanyard. When departure day arrived I still hadn't worked out how -this would work in practice (power delivery being a major concern). In the end -I settled on attaching it to the strap on my backpack. My bag has lots of +When I started the project I imagined it would hang it around my neck like a +conference lanyard. By the time departure day arrived I still hadn't worked out +how this would work in practice (power delivery being a major concern). In the +end I settled on attaching it to the strap on my backpack. My bag has lots of webbing so there were plenty of loops to hold it in place. I was also able to -use the velcro covered holes intended for water tubes to get the cable neatly +use the Velcro covered holes intended for water tubes to get the cable neatly into the bag. ## At the Conference -I had everything pretty much working for the start of the conference, although -I did make some final improvements and add a systemd unit to automatically start -and restart the Rust binary once I arrived in my accommodation on the Sunday -before the conference. At this point there were still two unknowns: battery -life and how the Raspberry Pi would handle coming in and out of Wi-Fi range. -The Wi-Fi turned out fine: automatically reconnecting whenever it came into -range of the Wi-Fi. +I had everything pretty much working for the start of the conference. Although +I did make some improvements and add a [systemd unit] to automatically start +and restart the Rust binary. At this point there were still two unknowns: +battery life and how the Raspberry Pi would handle coming in and out of Wi-Fi +range. The Wi-Fi turned out fine: It automatically reconnected whenever it +came into range of the Wi-Fi.
- Badge display a count of zero. + Badge displaying a count of zero.
Ready for day 1
### Reception -At this point I had not had time to test battery life, so day one I hooked it -up and hoped for the best. Day 1 was a success! I had my first few people talk -to me about the badge and increment the counter. Battery life was good too. -After 12 hours of uptime the battery was still showing it was half full. Later -in the week I left the badge running overnight to hit 24 hours uptime. The -battery level indicator was on the last light so I suspect there wasn't much -juice left. +Day 1 was a success! I had several people talk to me about the badge and +increment the counter. Battery life was good too. After 12 hours of uptime the +battery was still showing it was half full. Later in the week I left the badge +running overnight and hit 24 hours uptime. The battery level indicator was on +the last light so I suspect there wasn't much juice left.
- Badge display showing a hello count of 1. + Me with badge display showing a hello count of 1.
Me after receiving my first hello on the badge
@@ -184,28 +238,31 @@ pixels. The change was a success, most people scanned the QR code from this point on.
- Badge display now including QR code. + Badge display now including QR code.
Badge display showing the newly added QR code
On day 2 I also ran into [E. Dunham][edunham], and rambled briefly about my badge project and that it was built with Rust. To my absolute delight [the project was featured in their talk the next day][edunham-talk]. The project was -mentioned and linked on slide and I was asked to raise my hand in case anyone +mentioned and linked on a slide and I was asked to raise my hand in case anyone wanted to chat afterwards.
- Photo of E. Dunham's slide with a link to my git repo. + Photo of E. Dunham's slide with a link to my git repo.
Photo of E. Dunham's slide with a link to my git repo
-At the end of the talk the audience was asked to talk about a Rust project they -were working on. Each person to do so got a little plush Ferris. I spoke about -[Read Rust] and am now the proud owner of a litle Ferris. +At the end of the talk the audience was encouraged to tell the rest of the room +about a Rust project they were working on. Each person that did so got a little +plush [Ferris]. I spoke about [Read Rust]. -[Insert Ferris here] +
+ Photo of a small orange plush crab. +
Plush Ferris
+
-### CHANGEME What did I learn? +## Conclusion By the end of the conference the badge showed a count of 12. It had worked flawlessly over the five days. @@ -214,35 +271,54 @@ Small projects with a fairly hard deadline are a good way to ensure it's seen through to completion. Also a great motivator to publish some open source code. I think I greatly overestimated the number of people that would interact with -the badge. Of those that did I think most tapped the button to increase the +the badge. Of those that did, I think most tapped the button to increase the counter and didn't read much else on the page. For example no one commented on -the system stats at the bottom of the page. I had imagined the badge as a sort -of digital business card but this did not really eventuate in practice. +the system stats at the bottom. I had imagined the badge as a sort of digital +business card but this did not really eventuate in practice. -Attaching the Pi and display to my bag worked out pretty well although I did -have to be careful when putting my bag on and it was easy to catch on my -clothes. Also one day it started raining on the way back to the accommodation. -I had not factored that in at all and given it wasn't super easy to take on and -off I ended up shielding it with my hand all the way back. +Attaching the Pi and display to my bag worked out pretty well. I did have to be +careful when putting my bag on as it was easy to catch on my clothes. Also one +day it started raining on the walk back to the accommodation. I had not +factored that in at all and given it wasn't super easy to take on and off I +ended up shielding it with my hand all the way back. -TODO: Link to code -TODO: Add images +### Would I Do It Again? -### Would I do it again? +Maybe. If I were to do it again I might do something less interactive and +perhaps more informational but updated more regularly. I might try to tie the +project into a talk submission too. For example, I could have submitted a talk +about using the embedded Rust ecosystem on a Raspberry Pi and made reference to +the badge in the talk or used it for examples. I think this would give more +info about the project to a bunch of people at once and also potentially teach +them something at the same time. -If I were to do it again I might do something less interactive and perhaps more -informational but updated more regularly. For example showing the previous and -next talks to attend. Perhaps even allowing free text. - - -Maybe. I think if I were to do something along these lines again I might try to tie into a talk submission. -For example I could have submitted a talk about using the embedded Rust ecosystem on a Rasperry Pi and made -reference to the badge in the talk or used it for examples. I think this would give more info about the project -to a bunch of people at once and also potentially teach them something at the same time. +All in all it was a fun project and excellent conference. If you're interested, +[the Rust source for the badge is on GitHub][source]. [BADGEr]: https://wyolum.com/projects/badger/ -[HINK-E0213A07]: https://www.unisystem-displays.com/en/fileuploader/download/download/?d=0&file=custom%2Fupload%2Ffile%2F6f3084488018ca68c5bf0a26460e7c57%2FHINK-E0213A07-V1.1-Spec.pdf -[SSD1675]: http://www.solomon-systech.com/en/product/advanced-display/bistable-display-driver-ic/SSD1675/ -[SSD1675 datasheet]: https://www.buydisplay.com/download/ic/SSD1675A.pdf [edunham-talk]: https://youtu.be/uCnnhMleoKA?t=530 [edunham]: http://edunham.net/ +[embedded-graphics]: https://crates.io/crates/embedded-graphics +[embedded-hal]: https://crates.io/crates/embedded-hal +[Ferris]: http://rustacean.net/ +[GPIO]: https://en.wikipedia.org/wiki/General-purpose_input/output +[HINK-E0213A07]: https://www.unisystem-displays.com/en/fileuploader/download/download/?d=0&file=custom%2Fupload%2Ffile%2F6f3084488018ca68c5bf0a26460e7c57%2FHINK-E0213A07-V1.1-Spec.pdf +[hyper]: https://hyper.rs/ +[inky]: https://github.com/pimoroni/inky +[IoT]: https://en.m.wikipedia.org/wiki/Internet_of_things +[linux-embedded-hal]: https://crates.io/crates/linux-embedded-hal +[linux.conf.au]: https://2019.linux.conf.au/ +[Pimoroni Inky pHAT]: https://shop.pimoroni.com/products/inky-phat +[ProFont font]: https://web.archive.org/web/20180412214402/http://tobiasjung.name/profont/ +[profont]: https://crates.io/crates/profont +[Raspberry Pi Zero W]: https://www.raspberrypi.org/products/raspberry-pi-zero-w/ +[Raspbian]: https://raspbian.org/ +[Read Rust]: https://readrust.net/ +[source]: https://github.com/wezm/linux-conf-au-2019-epaper-badge +[SPI]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface +[ssd1675 crate]: https://crates.io/crates/ssd1675 +[SSD1675 datasheet]: https://www.buydisplay.com/download/ic/SSD1675A.pdf +[SSD1675]: http://www.solomon-systech.com/en/product/advanced-display/bistable-display-driver-ic/SSD1675/ +[stm32f30x-hal]: https://crates.io/crates/stm32f30x-hal +[systemd unit]: https://www.freedesktop.org/software/systemd/man/systemd.unit.html +[wrk]: https://github.com/wg/wrk diff --git a/content/technical/2019/01/linux-conf-au-rust-epaper-badge.yaml b/content/technical/2019/01/linux-conf-au-rust-epaper-badge.yaml index 8c46c12..db704df 100644 --- a/content/technical/2019/01/linux-conf-au-rust-epaper-badge.yaml +++ b/content/technical/2019/01/linux-conf-au-rust-epaper-badge.yaml @@ -1,9 +1,9 @@ --- -title: Rust powered linux.conf.au e-Paper badge -extra: TODO +title: My Rust Powered linux.conf.au E-Paper Badge +extra: For linux.conf.au 2019 I built a digital conference badge using a Raspberry Pi Zero, tri color e-Paper display and a custom Rust program. kind: article section: technical -created_at: 2019-01-26 16:17:00.000000000 +10:00 +created_at: 2019-01-27 11:00:00.000000000 +11:00 keywords: - rust - linux diff --git a/output/images/2019/badge-at-end-of-conference.jpg b/output/images/2019/badge-at-end-of-conference.jpg new file mode 100644 index 0000000..fc2616c Binary files /dev/null and b/output/images/2019/badge-at-end-of-conference.jpg differ diff --git a/output/images/2019/badge-at-end-of-conference_thumb.jpg b/output/images/2019/badge-at-end-of-conference_thumb.jpg new file mode 100644 index 0000000..283ad95 Binary files /dev/null and b/output/images/2019/badge-at-end-of-conference_thumb.jpg differ diff --git a/output/images/2019/badge-early-revision_thumb.jpg b/output/images/2019/badge-early-revision_thumb.jpg new file mode 100644 index 0000000..182bf6c Binary files /dev/null and b/output/images/2019/badge-early-revision_thumb.jpg differ diff --git a/output/images/2019/badge-edunham-talk_thumb.jpg b/output/images/2019/badge-edunham-talk_thumb.jpg new file mode 100644 index 0000000..92bb798 Binary files /dev/null and b/output/images/2019/badge-edunham-talk_thumb.jpg differ diff --git a/output/images/2019/badge-first-hello_thumb.jpg b/output/images/2019/badge-first-hello_thumb.jpg new file mode 100644 index 0000000..4d27b14 Binary files /dev/null and b/output/images/2019/badge-first-hello_thumb.jpg differ diff --git a/output/images/2019/badge-plush-ferris.jpg b/output/images/2019/badge-plush-ferris.jpg new file mode 100644 index 0000000..867181f Binary files /dev/null and b/output/images/2019/badge-plush-ferris.jpg differ diff --git a/output/images/2019/badge-plush-ferris_thumb.jpg b/output/images/2019/badge-plush-ferris_thumb.jpg new file mode 100644 index 0000000..ab3b9b8 Binary files /dev/null and b/output/images/2019/badge-plush-ferris_thumb.jpg differ diff --git a/output/images/2019/badge-sunday-night_thumb.jpg b/output/images/2019/badge-sunday-night_thumb.jpg new file mode 100644 index 0000000..8a90e28 Binary files /dev/null and b/output/images/2019/badge-sunday-night_thumb.jpg differ diff --git a/output/images/2019/badge-with-qr-code_thumb.jpg b/output/images/2019/badge-with-qr-code_thumb.jpg new file mode 100644 index 0000000..0c8bc67 Binary files /dev/null and b/output/images/2019/badge-with-qr-code_thumb.jpg differ