Saving your work on the Model 16 in TRSDOS-II and BASIC/BASICG

(This is emulating the Model 16 using the trs80gp emulator and saving/load from virtual disk.)

Setting up a fresh disk

If you’re already in BASIC, just open another trs80gp emulator to create the new disk first.

  • Insert a new disk by going to Diskette -> :1 <empty> -> <<unformatted dmk>>
  • FORMAT 1 and “Y” when asked to Continue?
Formatting progress
  • Once formatting completes select Diskette -> :1 * <<unformatted>> -> Export… -> [fill out “File name:” field] -> [Save]
  • Now load the saved disk with Diskette -> :1 * <<unformatted>> -> Replace… -> [select the disk file you just created] -> [Open]
  • DIR 1 should show the empty disk in drive 1

Saving your work

  • Compose your program in BASIC or BASICG (graphics BASIC)
  • The filespec of TRSDOS-II files is filename/ext.password:drive_number (if you’re used to DOS 8.3 names or Windows, your habitual “extension” would be a “password” in TRSDOS-II)
  • To save a program named “COS” with a “BAS” extension on Drive 1, type SAVE "COS/BAS:1"
  • SYSTEM to exit basic.
  • DIR 1 should should the directory of your disk with the COS/BAS showing. In my example below, I also tried to save “COS.BAS” which is actually “COS” with a password of “BAS”
Model 16 TRSDOS
DIR command output on the Model 16

Loading your work

  • You can PRINT or DUMP your saved file with the COS/BAS filespec as before, but it will be a somewhat binary output and not plain text like you might expect from modern programming language files.
  • Ultimately, you will have to go back to BASIC to reload with the same filespec (LOAD "COS/BAS:1“)


Is Solar Worth It? A Florida Panhandle Perspective

If you’re considering solar, you can find solar installers via EnergySage (referral link) and get a $25 Amazon Gift Card when you go solar. I found the installer that we ultimately used, which was a lot better option than using one of the many door-to-door solicitors that show up in the neighborhood.

Perspective on our Placement for Solar

Our house is in Cantonment, FL, which is at 30.6°N latitude. If you’ve been to the Florida beaches in early spring after spending the winter somewhere [reasonably north of Florida], your skin is well aware of how much more intense the sun is. Aside from cities in Florida, only San Antonio, Austin, and Houston in Texas, New Orleans in Louisiana, and Mobile in Alabama are further south than we are.

The back roof of our house also points pretty much due south, and as you can see, the south panels are the most productive through the day:

Lifetime energy generation after about 4 years, per panel.

Specs on our system

We have 48 290W panels with micro inverters on each panel so that each panel is monitorable. They were definitely more expensive this way and based on outage turnaround time that prompted me to write a script to monitor, it would probably cost $200-400 worth of lost production waiting to fix the problem. Given that that’s about 1% of the cost of the solar array and the price differential was easily 10-20% the extra “observability” into individual panels seems to be too steep a premium (but the more traditional panels also had the aluminum storm door look versus the fancy new ones!)

We also have a solar collector for hot water (that also requires that we stick with electric for the water heater.) The tank is 80 gallons, with half of the tank always heated by electric as a backup. The solar collector will heat up the other half by circulating to between the tank and the roof.

Unlike the photovoltaic panels, the solar part of the water heater doesn’t produce usable hot water on its own in the winter months. It will, however, warm up enough during the day to assist in heating by supplying 80-90℉ water to the electric side. This will be offset by the fact that if it gets near or below freezing, the water in the tank has to circulate to the collector on the roof so that it doesn’t freeze. That’s not a huge problem in the winter here, because it collects enough warm water on those days to get through the night… but for places that get significant snows *plus* power outages, that could be a recipe for disaster.


At the time, the solar installs were financed at 100%, with the option of either financing it all at a high single digit rate or 30% down via 12 months same as cash in anticipation of the tax credit money and 70% on a 12 year loan at low single digit APR. The full financing at the higher rate was pretty much like borrowing money on your future utility bills at a high interest rate in order to keep the tax credit.

87% of the solar loan we have goes to paying off the solar panels (vs. the water heater), so about $235/month.

Return on Investment

Energy Generation Report

According to our energy bills, the charge for electricity is about $0.14/kWh. This includes fees that move up and down in more of a step-like manner, but not fixed charges. At 18MWh/year that means we’re generating about $210/month in electricity vs. the $235/month payment. There are two things to keep in mind: First, the payments only last 12 years, but the solar panels are supposed to be at at least 80% efficiency for 20. Second, the return on investment improves if the price of energy increases faster than the efficiency of the solar cells decays.


Solar is not a no brainer. If you want to do it for the environment, that might put you over the top in terms of return on investment. The solar water heater (left out of this post) is a lot harder to measure, but you probably want to live in a place that doesn’t get near freezing in the winter.

Raspberry Pi Zero W to monitor Enphase Envoy Solar Array

I decided to set up some form of monitoring for my solar installation after a fuse and the breaker panel broke down leaving me without solar generation for a couple stretches during near-peak, up to about 1,400 kWh, or about $140-210 worth of solar generation.

Missing output

Components (physical and software)

  • A RaspberryPi Zero W on the same wireless network as the Envoy controller was set up on (initially used PiBakery to configure hostname/wifi/username/password, but the project is a little bit stale at this point).
  • A Nexmo account (part of Vonage APIs now) to allow for SMS alerts on zero output when the sun is up.
  • RubySunrise for only emailing alerts from dusk until dawn.
  • Ruby Gmail and a Gmail account for email informational “down” alerts just to be aware that the cron job is running.
  • cron and gmail


These are described in the source code repo as well

  • ENVOY_HOST for me was envoy.local, but depending on your DNS situation, your mileage may vary. I got my local DNS in a weird enough state that I just looked up the envoy.local IP on my wireless router’s status page and used that.
  • USERNAME and PASSWORD are the Gmail username and app-specific password credentials I generated for the gmail account I used.
  • INVERTER_COUNT is compared to the number of inverters you should have so that even if the array is producing, you can still generate an error if one of them isn’t reporting (only valid when producing)
  • LATITUDE and LONGITUDE plucked from a site that displays your geolocation… this, along with your TZ represented in a form within the TZInfo::Timezone list, and RubySunrise allow you to figure out if the sun’s up.
  • NEXMO* are api keys and config from the Nexmo site (NEXMO_SMS_TO is your personal mobile to alert to)
  • TO_EMAIL is the email to actually mail to


.config must be of the form that follows but the rest of the code can be cloned from envoy-rpi-zero-monitor

# crontab runs every hours and inits rbenv to use the right ruby version because
# I didn't really care about "production readiness"... it's a Raspberry Pi Zero W
0 * * * * cd /home/twill/envoy-rpi-zero-monitor && eval "$(rbenv init -)" && ruby read-envoy.rb


This all depends on having an Enphase Enlighten Envoy (and a bunch of other random “E” names) as your solar monitor, but if you have a relatively recent solar install and your technician needed to configure the monitor for your wifi, then you probably have a similar device with a pollable endpoint. Look at your wireless router’s web console and you’ll see that monitor:

If you browse to that name or the IP address associated, you’ll probably get a web page with status. If you reload with the network tab up, you’ll probably see it retrieve the data via a .json endpoint:

From there, you can build your own monitor around it.

Referencing one trait from another trait in factory_bot

Sometimes you want to DRY up traits by referencing one trait from another trait in factory_bot. I tried searching on “inheriting traits” (that’s just for one factory inheriting traits from another and was in a factory_bot issue in GitHub). I accidentally stumbled upon the answer in a slightly unrelated StackOverflow question about calling a trait from another trait with params in factory_girl.

Ultimately, you use the trait name from the first trait as a method invocation in the referencing trait:

FactoryBot.define do
  factory :user do
    trait :with_supervisor do
      # complex set up might go
      # here
      after(:create) do |user|
        supervisor { create(:user) }
    trait :with_organization do
      with_supervisor # invoke the other trait first

RAW_POST_DATA in rspec rails for Rails 5.2 and beyond

The last time I was trying to specify RAW_POST_DATA in rspec was probably Rails 3 or 4, but I ran into a situation trying to test an edge case for error handling where I wanted that same functionality. I quickly found this issue [Unable to POST raw request body], but didn’t immediately figure out what wasn’t being set correctly.

In this case the test setup I was using was setting multipart/form-data instead of application/xml on the content types:

{:HTTP_ACCEPT=>"application/xml", :HTTP_CONTENT_TYPE=>"multipart/form-data", :CONTENT_TYPE=>"multipart/form-data"}

Because of this, the Rails controller tests that rspec hooks into was trying to break following malformed xml down to parameters:

   Expected response to be a <400: bad_request>, but was a <422: Unprocessable Entity>
   Response body: <errors>
       <error>["<test>\n  <data", "nbsp;\n  <!"] are not permitted parameters</error>

I finally noticed that the mime-type might be involved. In this code, Content-Type was also an issue, so:

  • Removed HTTP_CONTENT_TYPE from the headers
  • Set CONTENT_TYPE header to 'application/xml' instead of 'multipart/form-data' to prevent automatic params parsing in this case.
  • Passed as: :xml into the test to get the 'mime-type' correct.

Ultimately, if your code hasn’t boxed you in, then the as: :xml and passing raw data as a parameter should work:

post things_path, params: raw_xml_data, headers: non_form_data_headers, as: :xml

## replacement for the following:
# @request.env['RAW_POST_DATA'] = raw_xml_data
# post things_path

Tandy/Radio Shack TRS-80 Model 16 Graphics

TRS-80 Model 16 Nostalgia

I grew up with a TRS-80 Model I with Level 2 Basic and a TRS-80 Model 16 with 128K of RAM that were our first computers in the house prior to me getting my first computer, a Commodore 128. There was a book about Level 2 Basic that came with the TRS-80 Model I that was (as far as I know) a giveaway, but I never saw the TRS-80 Model I running except when it was in my grandmother’s house in Old Louisville on the 3rd floor when my dad’s younger brothers were playing around with a simple number guessing game program.

One thing that intrigued me about the TRS-80 Model I was its very simple graphics commands, SET and RESET which operated on a 48×128 (row x column) graphics grid as well as PEEK and POKE commands which could address a 2×3 grid of those same graphics grid points, but on the 16×64 character grid.

BASICG? What is this

I played around with the Model 16 and its 8″ disks and BASIC in there, but it wasn’t until I stumbled on PDF documentation for the Tandy TRS-80 model II that I discovered that the business computers (Model-II / Model-12 / Model-16) actually supported graphics at all. Looking at the Model_2_Computer_Graphics_26-4104.pdf scanned reference, it turns out BASICG (must be all uppercase in TRS-DOS) is the way to load the BASIC graphics interpreter.

I’m not sure if the TRS-DOS disks that came with the Model 16 my dad had had this interpreter, but the trs80gp emulator includes BASICG on the default boot disk for Model 16 mode:

What’s this BASICG on the TRSDOS disk?

(It wasn’t until after the TRS-80 Model 16 was gone that I learned about the DIR command via using MS-DOS, but that would have been helpful for digging in more… Ironically, my Commodore 128 supported a DIRECTORY command, so … SO CLOSE BUT SO FAR)

The TRS-80 Model 16 screen is 640×240 (x,y) addressable monochrome pixels. Unlike the Model I with Level 2 BASIC, BASICG has some more advanced commands… LINE, CIRCLE, PAINT… also GET to read the contents of the screen and that can later be PUT.

Interestingly enough, although the CIRCLE documentation say that the third argument is “r”/radius

CIRCLE command documentation

actual usage in the emulator appears to behave like the argument is a diameter instead:

Circles drawn on text

I’m definitely looking for more resources on the Model 16, so if you have background, manuals, etc., email me at thomas at thisdomain.

String#tr in ruby (like tr in Linux) complete with figuring out slashes.

It seems like I’ve seen quite a few programming puzzles in the last few weeks that involved translating mistyped input in which the hands were shifted (right) on the keyboard. My first thought was the tr utility in *nix operating systems, but didn’t immediately go looking for or notice that ruby has a tr method on string. However, after doing a trivial implementation involving keyboard rows like the following, I stumbled on the tr method.

  # initial array of characters/strings to shift back to the left with [index-1]
    'qwertyuiop[]\\', # need to escape the backslash or else debugging pain
    "asdfghjkl;'", # double-quotes here because single quote embedded

Attempting to rewrite this for .tr presented a few challenges, however. If you are substituting for \, -, or ~, you have to escape the characters. You also have to escape them from their string representation, which makes for some head-spinning levels of escaping (zsh users who run shell commands through kubectl might be familiar with this pain as well):

# puts '\\~-'.tr('\\', 'a') # doesn't match because \ is passed to tr and not escaped
# puts '\\~-'.tr('\\\\', 'a') # now \\ is passed to tr, which is
# puts '\\~-'.tr("\\\\\\", 'a') # with double quotes, you need an extra pair, for 6 total.
# puts '\\~-'.tr('\\~', 'b') # the escaping backslash needs to be doubled
# puts '\\~-'.tr("\\\~", 'b') # the escaping backslash needs to be tripled
# puts '\\~-'.tr('\\-', 'c') # the escaping backslash needs to be doubled
# puts '\\~-'.tr("\\\-", 'c') # the escaping backslash needs to be tripled

So if you’re going to use translate to “shift” hands back to the left, the two arguments to tr, SHIFTED_KEYBOARD_ROWS and UNSHIFTED_KEYBOARD_ROWS would have to be defined with the following escaping:

      'wertyuiop[]\\\\', # 4x backslash = backslash

    'qwertyuiop[]', # need to escape the backslash or else debugging pain

  def self.translate(string), UNSHIFTED_KEYBOARD_ROWS)

Tracing / Debugging ruby output like set -x in bash

In writing some shell scripts in ruby, I decided that I needed to be able to debug (trace) the lines that were being executed. I even ran across a closed StackOverflow question looking for the same thing.

eval { |c| %Q|puts "+ #{c.gsub("\"", "\\"").strip}"| }.zip(code).join($/)

After playing around with one of the other answers (see above), I ended up taking a different tactic to try and figure out how to debug the scripts. (By the way, the above code breaks if you have line breaks in a single statement, like the following contrived example):

y = 2
      + 4

The important search term here is “trace”, or Tracer to be exact.

Take the following example:

bind = binding
p bind

bind.local_variable_set(:bind, 2)

p bind

p binding.local_variables

bind = binding

p eval("bind", bind)

if 2 > 3
  puts 2
  puts 3

if true
  puts "looks like the if true is compiled out"

if you run the above (contained in a filename binding.rb) using ruby -r tracer binding.rb then you get the following:

#0:/home/tpowell/.rbenv/versions/2.5.5/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:Kernel:<:       return gem_original_require(path)
#0:binding.rb:1::-: bind = binding
#0:binding.rb:2::-: p bind
#0:binding.rb:4::-: bind.local_variable_set(:bind, 2)
#0:binding.rb:6::-: p bind
#0:binding.rb:8::-: p binding.local_variables
#0:binding.rb:10::-: bind = binding
#0:binding.rb:12::-: p eval("bind", bind)
#0:binding.rb:10::-: bind = binding
#0:binding.rb:14::-: if 2 > 3
#0:binding.rb:17::-:   puts 3
#0:binding.rb:22::-:   puts "looks like the if true is compiled out"
looks like the if true is compiled out

One interesting difference between how ruby -r tracer works from set -x is that the ruby tracer appears skips evaluating the if true at all. The above runs were against ruby 2.5.5. Looking at 3.0.0 (and as far back as 2.6.x), I only get the output of the script:

looks like the if true is compiled out

Looking at Tracer, it’s using set_trace_func under the hood:

set_trace_func proc { |event, file, line, id, binding, classname|
  printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname

Adding that in the 2.6.x+ world returns:

c-return binding.rb:1  set_trace_func   Kernel
    line binding.rb:5
  c-call binding.rb:5     binding   Kernel
c-return binding.rb:5     binding   Kernel
    line binding.rb:6
  c-call binding.rb:6           p   Kernel
  c-call binding.rb:6     inspect   Kernel
c-return binding.rb:6     inspect   Kernel

That output can be filtered by the event type , but the lines of code themselves aren’t output and apparently set_trace_func was apparently obsoleted as of 2.1.10. TracePoint is the updated way to accomplish this:

trace = do |tp|
  p tp

But we still have the same problem:

[:trace, :bind]

A crude solution I’ve found around this is to read the line from the file mentioned in the TracePoint from within the block (and this apparently doesn’t end up with a stack overflow).

trace = do |tp|
  puts "+ #{ { |f| f.each_line.to_a[tp.lineno-1] }}"


Which produces a somewhat set -x output:

+ bind = binding
+ p bind
+ bind.local_variable_set(:bind, 2)
+ p bind
+ p binding.local_variables
[:trace, :bind]
+ bind = binding
+ p eval("bind", bind)
+ bind = binding
+ if 2 > 3
+   if 3 > 2
+     puts 3
+   puts "looks like the if true is compiled out"
looks like the if true is compiled out