You might have noticed I took a little break from my blog recently. It was not intentional; things just got away from me a bit the last few months as I found a new job and had a nice vacation to see family in England (as well as a side trip to Edinburgh and the famous Fringe festival). Perhaps I will post more on the vacation another time; right now, I want to share my job news.
After a fantastic four years with CareEvolution, Inc., I recently accepted a software engineering position with Khan Academy. I am only a few weeks into my new position and I am still incredibly excited to have this opportunity. Not only am I working with some incredible people, we have tasked ourselves with an outstanding mission.
Our mission is to provide a free, world‑class education for anyone, anywhere.1
Leaving CareEvolution, Inc. was a difficult decision. Not only did it mean leaving behind extraordinary colleagues, it also meant leaving behind PowerShell, C#, Angular, and .NET as a part of my day-to-day profession. Instead, I will be working with React, Redux, Apollo, and Python. There is much for me to learn and, I hope, for me to blog about as I learn it. That said, I still love .NET things and will continue to tinker with them in my personal time2.
Of course, like my passion for .NET, some things will remain the same. Most significantly for me, the position is still remote and as such, provides me with great opportunities for personal growth as an offsite colleague and employee. I openly3 struggled with that while at CareEvolution, Inc. I hope that at Khan Academy, I can learn which parts of that struggle were down to the need for personal growth, and which, if any, were organisational. If I can, I will coalesce lessons I learn into a meaningful collection of tips that others might use to adapt their personal and organisational culture around remote work and off-site workers.
Finally, this blog is still my blog, these are my personal musings; nothing I post here represents the views of my employer. Thank you for your readership and your patience during my blog hiatus. As they say at work, onward!
At CareEvolution, we tend to develop using JavaScript on the front end, and C# on the back end (with some Python, PowerShell, CoffeeScript, R, SQL, and other languages thrown in when appropriate or technical debt dictates). We have hackathons every eight weeks where we get to be creative without the constraints of day-to-day work. We have a brown bag lunch talk every Wednesday. We work hard at embracing change, exploring new ways of doing things, and sharing what we have learned with each other. Quite often, leading figures in a particular technology emerge within our organisation: Brian knows JavaScript, Chris knows CSS, Brad knows SQL. While I doubt I know even half of the things about LINQ and its various implementations for database, Web API, or file interaction, I know enough to make it useful in my day to day work and I seem to be the one that employs it most in their code. I know LINQ.
I am certain this is going to sound familiar to many, but while my colleagues and I embrace all things as a collective, quite often a specific technology or its use will be avoided, derided, and hated by some. Whether driven by ignorance, a particular terrible experience, or prejudice1, these deep-seated feelings can create conflict and occasionally hinder progress. For me, my use of LINQ has been a cause for contention during code reviews. I have faced comments like "LINQ is too hard to understand", "loops are clearer", "it's too easy to get burned using LINQ", and "I don't know how to use it so I'd prefer not to see it". And that's all true; LINQ can be confusing, it can be complicated, it can be a debugging nightmare. LINQ can suck. Whether you use the C# language keywords or the dot notation (a debate almost as passionate as tabs versus spaces), LINQ can tie you up in knots and leave you wondering what you did to deserve this fresh hell. Yet any technology could be described the same way when one doesn't know anything about it or when early mistakes have left a bitter aftertaste.
In response to these dissenting voices, I usually indicate the years of academic learning and professional experience it takes us to learn how to code at all. None of it is particularly easy and straightforward without some education. Don't believe me? Go stick your mum or dad in front of Visual Studio and, assuming they have never learned anything about C# or programming, see how far they get on writing Hello World without your help. Without educational instruction, we would not know any of it and LINQ is no different. When review comments inevitably request that I change my code to use less LINQ, none at all, or more understood language features like `foreach` and `while` loops, it frustrates me. It frustrates me because I usually feel that LINQ was the right choice for the job. I feel like I am being told, "use something I already know so I don't have to learn."
Of course, this interpretation is hyperbole. In actuality, when presented with opposing views to our own, it is easy to commit the black or white fallacy and assume one must be right and the other wrong, when really we should accept that we both may have a point (or neither) and learn more about the opposing view. Since I find, when used appropriately, LINQ can provide the best, most sublime, most elegant solution to problems that require the manipulation of collections in C#, I desperately want others to see that. It is as much on me as anyone else to try and correct for the disparity between what I see and what others see when I write LINQ. So, with my next post we will begin a journey into the basics of LINQ, when to use it2, when to use dot notation over language keywords (or vice versa), and how to avoid some of the more common traps. We will begin with the cause of many confusing experiences; deferred execution.
we all know someone in the "That's new, I hate it" crowd [↩]
even I recognize LINQ is not a golden hammer; it's more of a chainsaw that kicks a little [↩]
CodeMash 2.0.1.5, the latest installment of the popular community-organised conference is fast approaching. This time, I will be attending with several of my colleagues from CareEvolution, which is sponsoring the NodeBot precompiler sessions. One such colleague and good friend, Brian Genisio (also a co-organiser of the Southeast Michigan JavaScript group more commonly known as SEMjs) has been working night and day for months to prepare for each of the two epic software and hardware hacking events that will be the NodeBot precompilers. Though myself and a few other friends (many of which you can meet in person at CodeMash) have assisted Brian over the last few weeks, the success of this event really is down to his vision and commitment. From creating documentation to submitting Johnny Five pull requests1, ordering components to building kits, Brian's efforts have been considerable; if you join us to hack NodeBots (and you really should), be sure to take a moment and show him your gratitude.
My biggest contribution to the NodeBots preparation was to organize and take part in a hack day at work where Brian, a few colleagues (Brandon Charnesky, Greg Weaver, and Kyle Neumeier), John Chapman (another co-organizer of SEMjs and the NodeBots precompilers), and I could test and finalize kits and components, review and update documentation, and give some of the challenges and components a dry run in the process. Participants at CodeMash will be able to take part in one of two competitions with their NodeBots; a sumo-inspired Battle Bots competition where bots can compete for supremacy in the ring, or a line racing time trial where bots must follow a track in the fastest time2. My main efforts during the hack day were to create a sample line-following bot and provide some example code as a starting point for our precompiler hackers. The examples for both the basic line follower and basic sumo bot, as well as some other examples for specific components, can be found on GitHub in the CodeMash NodeBots Docs repository. Instructions on getting started are available on the official CodeMash NodeBots website.
Healthcare and NodeBots?
Some of you may have been wondering: "why would a healthcare IT company like CareEvolution chose to sponsor an event hacking robots?" If you would like to know more, please come to our vendor session at CodeMash (2 p.m. on Thursday, January 8) where I will be presenting "We're Not All Robots: Hacking NodeBots, Healthcare, and the Workplace".
The Line-Following Hardware
Before hacking the code, I needed to work out how the hardware worked and build my bot. I started out with the IR (infrared) reflectance array component; an array of six IR emitters and corresponding receivers that will be the eye to see the line.
In the image above, you can see the front of the array as well as the cable to attach the array to the controller (we are using Arduino Uno clones for the precompilers). Using the pins already attached3, I connected the array to the board.
In the wiring diagram above, you can see each of the six analog pins on the Arduino going to one of the output pins (labelled 1-6) on the reflectance array4. Pin 13 of the Arduino has been connected to the LED ON pin of the reflectance array, which is used to activate the infrared LED's.
With everything connected, I used the usage code from the Johnny Five documentation to create a quick tester and verify that I was able to receive output from my reflectance array.
var five = require("johnny-five"),
board = new five.Board();
board.on("ready", function() {
var eyes = new five.IR.Reflect.Array({
emitter: 13,
pins: ["A0", "A1", "A2", "A3", "A4", "A5"]
});
eyes.on('line', function(err, lineValue) {
console.log( "Line Position: ", lineValue);
});
eyes.enable();
});
After verifying the reflectance array was wired and working, I followed the reference kit build instructions to create a robot chassis on which I could mount the reflectance array.
I then mounted the array at the front, near the wheels, using some padded double-sided tape (the array must be within a quarter of an inch of the line, so a little padding was required). To avoid confusion, the array was oriented so that its left (pin 1, according to the documentation) was also the bot's left (assuming the wheels are the front of the bot).
The Line-Following Software
With the bot constructed, I needed to tell it what to do. My aim was not to create the best line-following bot ever (that is a task that possibly awaits you at CodeMash), I merely wanted to make something that demonstrates the basic concepts.
The first thing that the bot needs to do is to "see". Although we had a little code to check the array worked, we had not actually calibrated the array. Calibration allows us to show the array the extremes that it is to understand, i.e. the materials that represent the existence and non-existence of a line. Thankfully, the Johnny Five driver for the reflectance array makes calibration easy with the calibrateUntil function.
var five = require("johnny-five");
var board = new five.Board();
var stdin = process.stdin;
stdin.setRawMode(true);
stdin.resume();
board.on("ready", function () {
var eyes = new five.IR.Reflect.Array({
emitter: 13,
pins: ["A0", "A1", "A2", "A3", "A4", "A5"]
});
var calibrating = true;
// Start calibration
// All sensors need to see the extremes so they can understand what a line is,
// so move the eyes over the materials that represent lines and not lines during calibration.
eyes.calibrateUntil(function () { return !calibrating; });
console.log("Press the spacebar to end calibration...");
stdin.on("keypress", function(chunk, key) {
if (!key || key.name !== 'space') return;
calibrating = false;
});
eyes.on("line", function(err, line) {
console.log(line);
});
eyes.enable();
});
In my updated code, I also added keyboard input capture so that the calibration mode could be exited via the space bar. Running this with my bot, I was able to drag a piece of paper with a thick black electrical tape line under the array and calibrate it. After calibration, I could see from the console output that my bot recognised the line and in which direction it had last seen it5.
Next, I needed to be able to move the bot based on the line position. For this, I added some simple wheel commands and thresholds. The code is shown below.
var five = require("johnny-five");
var board = new five.Board();
var stdin = process.stdin;
stdin.setRawMode(true);
stdin.resume();
board.on("ready", function () {
var wheels = {
left: new five.Servo({ pin: 9, type: 'continuous' }),
right: new five.Servo({ pin: 10, type: 'continuous' }),
stop: function () {
wheels.left.center();
wheels.right.center();
},
forward: function () {
wheels.left.ccw();
wheels.right.cw();
console.log("goForward");
},
pivotLeft: function () {
wheels.left.center();
wheels.right.cw();
console.log("turnLeft");
},
pivotRight: function () {
wheels.left.ccw();
wheels.right.center();
console.log("turnRight");
}
};
var eyes = new five.IR.Reflect.Array({
emitter: 13,
pins: ["A0", "A1", "A2", "A3", "A4", "A5"]
});
var calibrating = true;
var running = false;
wheels.stop();
// Start calibration
// All sensors need to see the extremes so they can understand what a line is,
// so move the eyes over the materials that represent lines and not lines during calibration.
eyes.calibrateUntil(function () { return !calibrating; });
console.log("Press the spacebar to end calibration and start running...");
stdin.on("keypress", function(chunk, key) {
if (!key || key.name !== 'space') return;
calibrating = false;
running = !running;
if (!running) {
wheels.stop();
console.log("Stopped running. Press the spacebar to start again...")
}
});
eyes.on("line", function(err, line) {
if(!running) return;
if (line < 1000) {
wheels.pivotLeft();
} else if (line > 4000) {
wheels.pivotRight();
} else {
wheels.forward();
}
console.log(line);
});
eyes.enable();
});
The first thing I added was a wheels object to encapsulate the motor controls. Movement is provided by two continuous servos attached to pins 9 and 10. After defining left and right servos, I created the following methods:
forward
Both servos turning such that they rotate toward the front of the bot
pivotLeft
The left servo rotates in reverse while the right servo rotates forward
pivotRight
The right servo rotates in reverse while the left servo rotates forward
stop
Both servos stop moving
Next, I made sure that stop() was called on startup to ensure the bot was not wandering around aimlessly. I then updated the space bar handling to act as a toggle that on first use stopped calibration and started the bot on its line following quest, but on subsequent uses merely stopped or started the line following. Finally, I added some thresholds to the line event handler to determine when the bot should drive forward and when it should pivot in either direction based on the value sent from the array.
And with that, my simple line-following robot was complete. It does a fair job at following a course, but it is in need of fine tuning if it is to win any races. Perhaps you will be up to the task when you take part in the CareEvolution-sponsored NodeBots precompilers at CodeMash 2.0.1.5. If you wish to take part in our hacking extravaganza, you will need to register, so be sure to reserve your spot.
which earned Brian the privilege of becoming a core committer [↩]
of course, you don't have to compete in either; you can just hack [↩]
thanks to the efforts of John Chapman, no one will need to solder pins to the reflectance arrays [↩]
pins 7 and 8 are unused as the reflectance sensors for those pins have been separated from the component [↩]
the `line` event from the array uses 0 to mean the line was last seen to the left and 5001 to mean it was last seen to the right; any value between 1 and 5000 means the line is under the array with the value indicating its position [↩]