Wall-E3 Replacing Mega 2560 With Teensy 3.5 Part II

Posted 12 December 2021,

Just after getting to the point noted at the end of Part I of this saga, I discovered that I had managed to kill the Teensy 3.5 I was using at the time. This post (and maybe subsequent ones) describes my efforts to determine what happened, and how to keep it from happening again.

Well, there was a small detour on the way to the forum…. After installing a Teensy 3.5 on the robot chassis and wiring everything up, I did something that killed the Teensy. I was connecting the charger to the robot, and noticed that the Teensy rebooted (I was running a sketch that blinked the on-board LED 5 times/sec). That should never happen, so I disconnected and reconnected the charging plug a few more times. Sometimes (but not all the time) the Teensy would reboot – and then it stopped responding entirely; the LED was still blinking, but it was much dimmer, and the Teensy would no longer respond to programming inputs. I measured the 3.3V regulated output, and it was now down to about 1.9V – ouch!

I spent a LOT of time looking around for the cause of the problem without finding anything really credible. I finally decided that having a Teensy input connected directly to the pin on the charging jack that gets disconnected from GND when the charging plug is inserted must be problematic somehow. So I added an RC filter to the line so that when the disconnect occurred, the signal presented to the Teensy pin would be filtered through the RC filter, as shown below:

This didn’t work either – a brand-new Teensy rebooted after just a few connect/disconnect cycles – rats!

Next I abandoned the idea of using the normally closed switch portion of the charging jack entirely, and decided instead to look at a stepped down (4:1 divider) version of the +12V input line with one of the Teensy’s many analog inputs, as shown below:

This too failed to protect the Teensy, and after a few connect/disconnect cycles it too was completely unresponsive; now I’m a multiple Teensy 3.5 killer – yikes!

After killing not one, but two Teensy’s, it was time to realize I simply did not know what was happening, so as usual I went back to basics. First I connected yet another expensive Teensy 3.5 to my PC via USB, but this time I simply placed it near, but not on, my robot chassis. Then I performed 10-20 charging plug connect/disconnect cycles, and confirmed that the Teensy still lived. While this step seems pretty stupid, it at least eliminated some form of magic that killed all Teensy’s within some radius of the robot chassis. Next, I placed this Teensy on the robot chassis, but not connected electrically to anything – just the same USB connection back to my PC. Another 10-20 connect/disconnect cycles with no complaints, and now I’m convinced whatever is killing Teensy’s is a conducted signal, not radiated.

Next, I connected the Teensy’s ground pin to the ground side of the 5V LDO output, which is also the ground side of the charging connector. Another 10/20 cycles with nothing bad happened, and now I’m convinced that whatever is killing Teensy’s is a conducted signal, but not something inherently on the ground line only.

At this point I realized that since I now had Teensy ground, USB ground, and robot power ground all connected together, I could now look at the +12V side of the charger power supply with my Hanmatek DOS1102 100MHz digital O’scope, and compare/contrast it to my lab power supply output when using the latter as a substitute charger input. After a bunch more connect/disconnect cycles with both my charger power supply and my lab power supply acting as a charger input, I noticed that the charger power supply exhibited very pronounced ‘ringing’ on the +12V line when repeatedly connected and disconnected from the charging jack, as shown below:

Charging PS ‘ringing’ during connect/disconnect cycles. Note scale is 5V/div

In the above photo, the vertical scale is 5V/div, meaning that some of the excursions are upwards of 15V (plus AND minus!) – plenty enough to kill a Teensy.

So now I knew what was happening, but not why. After some more playing around I realized that the really big ringing pulses were caused by just tapping the front of the charging plug against the front of the circular jack opening — and then I realized that the front surface of the charging plug isn’t insulated – it’s actually the same surface as the +12V inner cylinder!

Plug on left from Wall-E2 charging cable. Plug on right doesn’t cause problems

So, I began to understand that the why was because I was occasionally shorting the +12V output of the power supply to ground, effectively shorting out the power supply. The power supply doesn’t die because it has short-circuit protection, but the process of shorting and then opening the circuit causes (I think) the ringing.

So, why didn’t I see this before? I’ve been using this same power supply and jack for (literally) years with my older 4WD Wall-E2 robot, with no problems. The answer (I think) is shown in the following photo:

Charger connection port on Wall-E2 robot

As can be seen in the above photo, the charger jack on Wall-E2 features a lead-in ‘registration’ cone so that slightly off-center alignments can be accommodated. An unintended (and unknown till now) side-effect of this arrangement was to prevent the front face of the old charging plug from touching the grounded inner outer circumference of the jack.

Assuming all this holds water as I move forward, I can go back to looking at the normally-closed grounding switch on the charging jack for physical charger plug insertion detection, or leave it the way it is now with the 4:1 voltage divider on the +12 line to the charger module.

13 December 2021 Update:

Well, maybe not. I removed the probe lead-in collar from my old robot and put it on the new one, then re-ran the experiment where I look at the output of the 4:1 voltage divider wrt robot system ground, and I still see very large +/- excursions when I connect and disconnect the charging power supply using the original plug. So, There may be something else going on, or it may be that these perturbations have existed all the time, but the Mega2560 wasn’t negatively affected. When I do the same experiment with my lab power supply, I see almost no excursions or ‘ringing’. The next experiment will be to swap the connectors (conductor-faced one on my lab supply, insulator-faced on on my charging supply) and see what happens.

Well, not so clear; when I swapped plugs I could still see significant, but much reduced, excursions when connecting/disconnecting the charging supply. However, the lab supply looked pretty clean, with either connector. Here are the screen grabs from my scope for all four conditions

Charging PS, Old (conductor-faced) Plug
Charging PS, New (insulator-faced) Plug
Lab PS, New (insulator-faced) Plug
Lab PS, Old (conductor-faced) Plug

From the above plots, it looks like the charging PS with the new (insulator-faced) plug should be OK. We’ll see.

16 December 2021 Update:

After thinking about the situation for a while, I realized there was simply no way to avoid potentially dangerous (to a Teensy, at least) +/- voltage excursions on anything connected to the +12V charging input line, so “hoping for the best” is probably not the best plan moving forward. I started thinking about optical coupler ideas, and after sleeping on this for a couple of nights, I realized that I already had half of an optical coupler available; the TP5100 charger typically comes with a two-color (red/green) LED for visual display of charge/end-of-charge states. This LED typically isn’t installed, but the PCB pads are there and ready to go. So, If I installed the LED and placed a photodetector of some sort nearby, I could implement a non-conductive connection between the charger +12V input and my poor defenseless Teensy 3.5 – woohoo!

I poked around in my parts bin for a while, and came up with some photo transistors and a couple of small, simple GL5537 photoresistor parts. According to the datasheet, it exhibits a dark resistance in the meg-ohms and a fully-illuminated resistance in the single-digit ohms – perfect! So I whipped up a small breadboard circuit to test this out, as shown in the following photo:

GL5537 Photoresistor nose-to-nose wth the green/red dual color LED shipped with each TP5100 charger

I used a 20K resistor pullup to 3.3V to simulate a Teensy GPIO pin set for a digital input with a pullup resistor, and this worked great; with the LED OFF, the scope showed very nearly 3.3V, and nearly zero with the LED ON (I did have to cover the photoresistor/LED combination with an opaque shade to keep my lab overhead lights from interfering though). Now the +12V supply and anything electrically connected to it can perturbate all it wants to – it won’t be able to kill any more Teensys because the only electrical connection now is through the battery itself to the +5V LDO regulator and then through the 5/3.3V regulator on the Teensy – yay!!

The next step was to integrate the photoresistor and LED onto the TP5100 charging module. I was able to replace the voltage divider resistors on the perfboard holding the TP5100 charging module with the photoresistor and I installed the 2-color LED on the PCB pads provided on the TP5100 module, with the leads arranged so that the LED boresight pointed toward the photoresistor, as shown below:

With this setup, I can connect/disconnect either style charging plug to my heart’s content without damaging anything. The only question remaining is whether or not I should install a light-shield around the LED/photoresistor pair. I could do that easily with a small section of heat-shrink tubing, but I’m not sure it’s necessary; in addition, if I do that I’d lose the ability to visually confirm that the charger is actually charging. Something to think about, anyways.

15 January 2022 Update:

While the above solution worked perfectly for decoupling any charging plug connection transients from the Teensy(s), it meant that the charge status LED on the TP5100 module wasn’t very (i.e. not at all!) visible from the outside of the chassis, so I had no way to visually confirm that plugging in the charging cable was actually doing something. I thought about this for a while, and had the idea that this might be the perfect place for a ‘light pipe’. After some Google searching, I found this part.

This light pipe worked great, and there was just enough length for me to attach the input end to the TP5100 charge status LED and the output end to the front panel of the robot, as shown below:

black 4″ light pipe attached with hot glue to charge status LED
Output end of light pipe installed on Wall-E3 front panel.
Red ‘charging’ status light visible from across the room
TP5100 charge status LED changes to green to indicate full charge

Stay tuned,

Frank

Teensy, MPU6050 and Rowberg’s I2CDev Library, Take Two

Posted 17 November 2021,

This post describes my efforts, once again, to figure out how to get a Teensy running with Jeff Rowberg’s I2CDev library for the purpose of interfacing to a MPU6050 6-axis IMU with on-board DMP.

After some initial teething problems, I have been using the MPU6050 for years on my various autonomous wall-following robots using Arduino Mega 2560 controllers with great success. However, when I tried switching over from Arduino UNO/Mega controllers to the Teensy 3.x controller, Jeff Rowberg’s library wouldn’t compile at all. At the time I got the library to compile by replacing #include Wire.h with #include i2c_t3.h in i2cdev.h/cpp. See this post from almost two years (December 2019) ago for the gory details.

Unfortunately, two years later when I tried to change out the Mega 2560 for a Teensy 3.5 in Wall-E3 my latest autonomous wall-following robot, I ran into compile problems again – rats! Looking at the code, I saw that the maintainers had accepted my pull request to add a ‘I2CDEV_TEENSY_3X_WIRE’ identifier to I2Cdev.h to switch from <Wire.h> to <i2c_t3.h> as I had done previously, but I still couldn’t get the library to compile with a Teensy 3.x target.

So, back to basics. Fortunately, I had kept all the old versions of the required i2cdevlib files, so my two-year old project (Teensy_MPU6050_DMP6_V2.ino) still compiled and ran properly – whew. So now to figure out why it doesn’t compile with the newest version of the libraries.

So I created a new VS2019 Arduino project called Tensy_MPU6050_DMP6_V3, copied my code and other files from two years ago, and trimmed the program down to just the code required to print out MPU6050 headings every 200 mSec.

And here is a sample of the output:

This program, using the i2cdevlib files from two years ago, compiles and runs fine. The things that have changed in i2cdevlib since then. Comparing i2cdev.h as used in this program with the current i2dev.h, I see

Changelog diffs
I2CDEV_TEENSY_3X_WIRE for convenience in switching from Wire.h to i2c_t3.h
I2CDEV_TEENSY_3X_WIRE used to switch to i2c_t3.h (as opposed to manually as before)
added ‘void *wireObj=0’ parameter to end of every I2Cdev method signature

And the difference in the i2Cdev.cpp files:

‘void wireObj’ added to all i2Cdev class method implementations
All instances of ‘wire.’ are replaced by ‘useWire->’ where *useWire is defined as a TwoWire object

In the i2Cdev class methods that actually talk to the MPU6050 over the i2c bus (readBytes, readWords, writeBytes, writeWords) the new version uses a pointer to a ‘TwoWire’ object called ‘useWire’ instead of Wire. The ‘useWire’ object is defined at the top of each of these methods with the lines

but the declaration (invocation?) of the ‘TwoWire’ class is guarded by an #ifdef as shown

Declaration of ‘Wire’ as a ‘TwoWire’ object

Which means it isn’t included if ‘I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE’ is used. In my old version of I2Cdev.cpp, this didn’t matter, because all the class method declarations & definitions use the ‘Wire.’ object technique for invoking a ‘i2c_t3’ class method. However, the new code uses the ‘useWire->’ pointer technique which does require the ‘TwoWire’ declaration, only it’s not available for ‘I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE’ – ouch!

Comparing the old and new MPU6050.h files, the only changes of significance are:

  • The name of the class was changed from MPU6050 in the old file to MPU6050_Base in the new one, with ‘, void * wireObj = 0’ added as the last parameter in the constructor
  • GetCurrentFIFOPacket, setFIFOTimeout, and getFIFOTimeout methods were added
  • The entire #ifdef MPU6050_INCLUDE_DMP_MOTIONAPPS20 section was removed
  • void *wireObj and uint_32t fifoTimeout objects were defined

The new GetCurrentFIFOPacket method added by Homer Creutz replaces the inline method I used in my older code, and does a nicer job to boot, and the wireObj declaration makes possible changing all the method calls from ‘Wire.’ to ‘useWire_>’ style.

Comparing the old and new MPU6050.cpp files, the only changes of significance are:

  • The names for all method implementations are changed from MPU6050:: to MPU6050_Base::, with a ‘,wireObj(wireObj)’ parameter added at the end of the constructor method and most other methods as well
  • The implementation code for GetCurrentFIFOPacket, setFIFOTimeout, and getFIFOTimeout methods were added

Comparing MPU6050_6Axis_MotionApps_V6_12.h (the old version) to MPU6050_6Axis_MotionApps612.h (the new version), there were a lot of changes:

  • The implementation code was split out from the header file into a new MPU6050_6Axis_MotionApps612.cpp file
  • A new MPU6050_6Axis_MotionApps612 : public MPU6050_Base class is declared and all the dmpxxx methods are now part of this class.
  • All the hard-coded MPU6050 DMP firmware image is gone (in the cpp file?)
  • At the very bottom is the line ‘typedef MPU6050_6Axis_MotionApps612 MPU6050;’ declaring ‘MPU6050’ to be a object of type MPU6050_6Axis_MotionApps612

The new MPU6050_6Axis_MotionApps612.cpp file now contains the DMP firmware image and the definitions for all the methods declared in the .h file.

So, it looks like the whole problem with changing over from the old setup to the new one may be just the fact that the declaration (invocation?) of the ‘TwoWire’ class is guarded by an #ifdef that disables the line for ‘I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE’. So tomorrow (neeeeeeedddddd, sleeeeepppp!) I’ll try again with the new setup, with the additional condition added to the #ifdef line.

So, I created yet another VS2019 Arduino project called ‘Teensy_MPU6050_DMP6_V3’, identical in every way to ‘Teensy_MPU6050_DMP6_V2’ except this project includes MPU6050_6Axis_MotionApps612.h instead of MPU6050_6Axis_MotionApps_V6_12.h. To make things simpler, I copied all the reference files from the i2cdevlib folder into the project’s local folder, as shown below:

After editing I2Cdev.h as shown below

to utilize the i2c_t3.h version of the Wire library, I compiled it for Teensy 3.2 and got the following output:

As shown, all the compile errors reference a missing declaration for TwoWire, which I hope means that adding the ‘I2CDEV_TEENSY_3X_WIRE’ identifier to the guard around the code that declares TwoWire in i2Cdev.cpp.

I edited i2Cdev.cpp to add the ‘I2CDEV_TEENSY_3X_WIRE’ identifier to the guard around the code that declares TwoWire in I2Cdev.cpp, and edited I2CDev.h to do the same thing for the guard around the ‘class TwoWire’ declaration, and recompiled. This time the output was:

So I commented out the ‘

line and recompiled, still getting way too many errors – time to punt.

20 November 2021 Update:

As mentioned above, I decided to punt on the idea of incorporating the newest I2CDevlib files, as I keep getting lost trying to sort out the ‘Wire/TwoWire/useWire’ mess.

However, I did discover that the new library versions work just fine with an Arduino UNO or MEGA2560 as the target. So, I created an Arduino project called ‘Arduino_MPU6050_6Axis_MotionApps612’ and used it to tune the rate-controlled turn PID for the new robot. After the requisite number of false-starts and screwups, I was able to get pretty decent 45ยบ/sec and 90ยบ/sec turns with:

So, at this point I have an Arduino program ( Arduino_MPU6050_6Axis_MotionApps612) targeting the Arduino UNO that works fine with the new versions of the i2cdevlib libraries, and an older Arduino program (Teensy_MPU6050_DMP6_V3) targeting the Teensy 3.x that works fine with the 2-year old versions of the i2cdevlib libraries. The next step I think is to move ahead with the Teensy-targeted program using old libraries on the assumption that eventually Jeff Rowberg and company will either fix the current libraries to work properly with Teensy, or show me the error of my ways so that I can fix the Teensy compilation problem.

Wall-E3 – Different Form-factor for Better Turn Performance

Posted 20 October 2021

I have been struggling for years now with Wall-E2’s reluctance to turn smoothly, and I have been looking for ways to improve the situation. I have even thought of going back to the 3-wheel form-factor with two driven wheels and a 3rd caster wheel, just for the ability to make smooth angle adjustments. However, the 3-wheel layout has its own problems, like a tendency to get stuck when one or both drive wheel loses traction, and a tendency for the caster wheel axle to accumulate carpet fibers to the point where it won’t turn.

Recently I ran across a blog post called ‘4-wheeled robot design basics and challenges‘ that seemed to describe Wall-E2’s turning difficulties pretty accurately. In addition, this post went on to suggest how the 4-wheel form-factor could be modified to minimize the high side-slip friction exhibited by most 4-wheel drive robots. The author suggested that by reducing the front-back dimension and increasing the side-side dimension, the side-slip friction exhibited during turns could be minimized. The author showed (literally) that the problem with a traditional 4-wheel form factor is that the front and back wheel pairs have non-intersecting turn radii, as shown in the following animated GIF from the RonRobotics site:

Traditional 4-wheel robot form-factor exhibiting high side-slip friction during turns

If the form-factor is changed from the traditional ‘long and narrow’ style to a more ‘short and wide’ style, then the turning situation is more like the following animated GIF:

4-wheel drive form factor to minimize side-slip friction during turns

The author didn’t provide any numerical guidelines for how short and how wide, but the above animated GIF provides a pretty good idea.

I decided to see if I could use TinkerCad to model a form factor that might be more like the ‘short and wide’ form-factor. From previous work I already had most of the individual elements (wheels, motors, battery compartment, charger module, etc), so I did a ‘FrankenRobot’ copy as shown below:

traditional (left) and ‘short/wide’ form-factor (right). Orange disk represents turning circle

The orange disk shown in the above image was intended to model the turning circle. I manually adjusted the form factor until the turning circle more-or-less matched the layout in the ‘design 3’ image from the RonRobitics site.

After playing with this for a while, I came up with the following ‘final’ design for my new Wall-E3 short/wide robot:

Short-Wide Robot Version. Battery pack in green, TPS5100 charger in red, charging probe in orange

With the above form factor, there is plenty of room for the battery pack (4 x 18650 Li-ion) and the TPS51000 charging module. After arriving at the above layout, I started working fabrication details. At first I thought this was a perfect opportunity to use some Adafruit 20x20mm slotted extrusions and associated hardware I have had hanging around for years looking for a project. However, as I got into the design in TinkerCad, I realized I had a problem, as shown in the following screenshot:

Short-Wide Robot chassis using Adafruit 20x20mm slotted extrusions

The motors must be mounted either above or below the extrusion. This could be effected using the Adafruit ‘T’ plate, as shown above, but then the question becomes – “what to do about a bottom plate”? I could put in a second set of extrusions, but that would severely reduce ground clearance. I could maybe bend the bottom part of the ‘T’ plate to support a bottom plate, but bending a cast aluminum piece didn’t seem like a good idea. In addition, an even bigger problem was how to mount the top plate. The obvious solution would be to drill 4 holes in the top plate, and use the slotted channel threaded insert hardware to mount the plate. But, how does that work – the threaded insert pieces are made to slide freely in the channel until tightening the mounting screw causes them to bind to the sides of the channel – all fine and good, but that won’t work for a blind mounting arrangement; the inserts could be anywhere in (or out of) the channel – oops! I thought I might be able to find a way of fixing the inserts into place, but that didn’t seem like a good idea either. I could simply drill and tap 4 holes around the periphery of the extrusions, but then I’m using expensive extrusions just like flat plate – bummer!

Eventually I realized that a standard U-channel from McMaster Carr should do the trick. The motors can be mounted through the ‘bottom’ of the U, and the top/bottom plates can be mounted to the top/bottom of the U-channel. Much simpler, and because I can mount the motors low in the U-channel, I can maximize ground clearance – yay!

27 October 2021 Update:

The first try at McMaster-Carr U-channel wasn’t too successful – it was much thicker and heavier than I realized from the drawings, so I went back to their site and re-ordered a much lighter (and nicer) U-channel style. The following photo shows the difference.

First order on the left, second on the right

I got 2ea 24″ lengths of the lighter U-channel, and as an experiment I tried cutting 45ยบ bevels on both pieces to see how I might form right angles for the chassis. This worked out pretty nicely. In addition, I cut a 1/2″ section from the thicker U-channel, and then cut it in half to produce 2ea right-angle brackets for use in connecting two pieces of a right-angle corner. The following photos show the results:

I was very pleased with my first try at forming a right angle corner for my new chassis – it looks like using pieces of the thicker U-channel for connecting brackets will work. I don’t think I’ll need more than two screws per corner, as I will also have top & bottom plates that will add tremendously to the overall structure rigidity. Now I just have to get the dimensions correct so that my already-cut top/bottom plates will fit, and then do all the other stuff to produce a finished ‘Wall-E3’ chassis.

02 November 2021 Update

After the usual trials and tribulations, I think I have produced a decent 200 x 230 mm chassis, as shown in the following photos:

One minor ‘gotcha’ to using the thinner sidewall U-channel is that I managed to strip the threads on a couple of the threaded holes as I was installing the top plate. Fortunately I had some 3mm press-fit nuts left over from Wall-E2 and was able to convert the threaded hole to a press-fit nut installation without too much problem. Right now I have two holes converted to press-fit, but I suspect all sixteen holes will need to be treated at some point. So, back to McMaster-Carr where I ordered another 100 press-fit nuts, along with some additional 3x6mm flathead screws.

By the way, just as a plug for McMaster-Carr: I ordered and received a 90ยบ countersink bit so I can use flat-head screws on the chassis, but when I received it I realized the tip of the bit wouldn’t fit into my 3mm holes – bummer! So, I contacted McMaster-Carr and asked if I could return it for a refund, and (well within the 30 minute reply guarantee) I got a note back that they would refund the cost of the order, but I didn’t have to return the item – wow! I have been a loyal MC customer for decades, and they continue to impress me with their responsiveness and speedy deliveries. The next time you have a need for hardware, think of McMaster-Carr.

The next step is to add the motor drivers and motors to the prototype setup to wring out any mechanical issues there. As can be seen in the following photo, the current Wall-E2 setup has the motors mounted through the side walls equidistant from top and bottom. However, I plan to mount the motors as close as possible to the lower edge of the U-channel, so as to achieve the largest possible terrain clearance.

To facilitate this I designed and created a ‘motor mounting template’ with motor axle location holes that fits inside the sidewall U-channel, as shown below:

Wall-E3 motor mounting template

The template has two holes per motor location to allow the template to be placed either way (side tabs toward the top plate, or side tabs toward bottom plate) and still have one set of holes in the proper vertical location.

06 November 2021 Update:

As described in the companion post to this one, I assembled a basic system on a blank aluminum plate as shown in the following photo:

Basic system assembled on blank aluminum plate

After fabricating the aluminum chassis, I started transferring pieces from the prototype to the new chassis, as shown below

After transferring everything except the Teensy module and the 5V LDO regulator module, this is what the new chassis looks like:

Still lots of work, but it is now clear that everything fits comfortably into the chassis, including both motor drivers which are mounted on top of the chassis on the Wall-E2 robot. This will clear up lots of space on the top plate, so things should be a lot neater with Wall-E3.

07 November 2021 Update:

After getting the basic component layout established, I decided to see if I could get the robot to actually move forwards and backwards under computer control via the HC-05 Bluetooth link. So, I transferred all four wheels from Wall-E2 to Wall-E3, and temporarily hot-glued the Teensy 3.5 and its accompanying 5V LDO regulator/HC-05 module to the battery pack, as shown in the following photo:

And, after fixing the problem of one set of motors turning in the opposite direction as the other set, I was able to move Wall-E3 forwards and backwards utilizing the wireless link from my PC to the robot, as shown in the following short video:

The next thing I tried was some manually controlled ‘spin’ turns. To do this I added an overload to the function that does normal rate-controlled turns using an MPU6050 IMU sensor to make a turn through a defined angle span. The overloaded function simply starts a CW or CCW turn and waits for any key to stop the turn. Here’s a short video showing the result

CW & CCW ‘spin’ turns

As can be seen from the above video, Wall-E3’s turn behavior is MUCH smoother than that of Wall-E3, and is quite close to the behavior exhibited in the ‘wide’ form factor GIF that prompted the Wall-E3 redesign (repeated below for comparison):

4-wheel drive form factor to minimize side-slip friction during turns

With such good results from the above turns on hard-surface flooring, I decided to try my luck with my test carpet patch. Turns on carpet with Wall-E2 have always been a jerky procedure, with PID algorithm having to ramp motor current way up to overcome wheel side friction, and then way back down again when the wheels broke free. With Wall-E3, the turning behavior on carpet was indistinguishable from its behavior on hard flooring – both cases exhibited very smooth turning behavior around an almost constant turning axis, as shown in the following short video.

For comparison, here is Wall-E2 making a 180โฐ turn on carpet:

And here is the plot of motor speed vs time for the turn.

Average turn rate = 41.8 deg/sec

As can be seen from the above, the motor speed goes from max (255) to min (50) several times per second as the wheels alternately grip and then slide. In contrast, the Wall-E3 turns on both carpet and hard flooring were done with a constant half-speed (125) motor current, resulting in an angular rate of about 70 โฐ /sec on both carpet and hard surface.

20 November 2021 Update:

Unfortunately, when I tried to add MPU6050 capability to Wall-E3, I went down a rabbit hole due to changes in Jeff Rowberg’s I2CDevlib library for the MPU6050 that have made it incompatible with Teensy 3.x targets (see this post for the gory details). After chasing Alice for way too long, I finally figured out that I had two choices. I could use an older (as in 2 years older) version of the i2cdevlib libraries with a Teensy, or I could use the current versions with an Arduino UNO or MEGA. Because at this point I already had an Arduino UNO running with a MPU6050 and the new libraries, I decided to continue on that track and see if I could tune the PID engine for smooth(er) turns with the new form factor. Here’s the physical setup:

Wall-E3 with Arduino UNO/MPU6050 setup running the motors

Here’s the code:

After a number of iterations, I arrived at the following PID values:

Here are the Excel plots showing the turn performance:

At this point, I think I’m pretty much done with this post. The ‘rest of the story’ will continue in the companion ‘Replacing Mega 2560 With Teensy‘ post.

Stay Tuned,

Frank

Wall-E3 Replacing Mega 2560 With Teensy 3.5

Posted 23 October, 2021

For some time now, I have been contemplating a replacement for the outdated and obsolete Mega 2560 micro-controller as the main controller module of my autonomous wall-following robot. The robot actually sports three controllers – the Mega 2560 main controller runs the overall operating system, drives the motors, and manages the speaker, the front-facing LIDAR unit, and a number of LEDs. A Teensy 3.2 manages the forward-facing IR-LED charging station beacon detection/homing algorithm, and a Teensy 3.5 manages the seven VL53L0X LIDAR distance measuring modules. So, it would make a lot of sense to replace the 2560 with another Teensy, making all the software consistent, and giving the robot a significant boost in computing power. A Teensy 3.5 at 120MHz runs at almost 10 times the 16MHz rate of the 2560, has twice the Flash memory (512K vs 256) and 32 times the RAM (256K vs 8). However, until recently (see this post) it has been very difficult/impossible to perform over-the-air (OTA) firmware updates with any of the Teensy products – a deal-breaker for me as I do that quite often with Wall-E2. Now that OTA capability has been demonstrated, the only remaining hurdle was whether or not a Teensy 3.5 has sufficient I/O to replace the Mega 2560. To address this question I created an Excel spreadsheet to document the current I/O assignments of the 2560 and then tentatively assigned Teensy 3.5 pins to match, as shown below.

As can be seen from the above, the Teensy 3.5 has plenty of pins left over after all currently assigned Mega 2560 pin assignments have been covered, so that problem goes away too.

The last piece of the puzzle was just the inconvenience of changing out a currently-working Mega 2560 just to gain performance that may or may not really be necessary. However, now that I have decided to redo the entire chassis to address the turn side-slip problem (see this post), I plan to use the opportunity to start fresh with a Teensy 3.5 as the main controller and work out any problems with just the chassis, the motors and motor drivers and the Teensy before moving on to implementing the rest of Wall-E2’s capabilities.

Motor Drivers for Wall-E3:

I have a bunch of motor driver modules left over from an earlier motor driver study, so I decided to use two of the Pololu VNH5019 motor driver modules for Wall-E3 – one for the two left motors, and one for the two right motors.

01 November 2021 Update:

The first step in bringing up the Wall-E3 robot was to assemble a very basic system on a simulated chassis bottom plate, as shown in the following photo:

Prototype Wall-E3 System on simulated chassis baseplate

The basic system consists of a TP5100 charger system and a 8.4V 7200 mAH battery pack, an Adafruit 1NA169 high-side current sensor, a Teensy 3.5 and a auxiliary board containing a 5V LDO regulator and a HC-05 Bluetooth module. The Teensy’s program at this stage consists of the OTA update code developed earlier in this post and this post, plus a few lines to read the current sensor output, convert it to a current value, and send it to the BT serial port. Here’s the program:

And here is some of the output:

With this setup I noticed there were a lot of ‘dropouts’ in the sensor outputs, so I added a 1uF cap between the sensor Vout and GND. This seemed to cure the dropouts as shown in the next output snippet (after some charging time resulting in lower charging current)

04 November 2021 Update:

I installed two Pololu VNH5019 drivers onto the prototype bottom plate, and copied the associated motor support code from my latest Wall-E2 version. I modified ‘SetLeftMotorDirAndSpeed(bool bIsFwd, int speed)’ and ‘SetRightMotorDirAndSpeed(bool bIsFwd, int speed)’ for the VNH5019 pinout and truth table, and that was pretty much all I had to do. Then I copied all of ‘CheckForUserInput()’ from Wall-E2 (and added the ‘U/u’ case for OTA updates) so I could control the motor direction and speed via the OTA serial connection, and the motors and drivers behaved pretty much right away. Here’s a short video showing the action:

05 November 2021 Update

I finally got the new 200 x 230 mm aluminum chassis finished, as shown in the following photo:

10 November 2021 Update:

After getting the new aluminum chassis fabricated, it was time to start populating it with subsystems from the prototype chassis and from Wall-E2. As shown in the following photo, the next big milestone was to populate it with four Pololu D-20 motors, the battery/charger and 5V LDO regulator subsystems, and the two Pololu VNH5019 motor drivers from the proto chassis. With the addition of four 3D printed wheels/tires from Wall-E2, and Wall-E3 was officially ‘mobile’!

Basic self-contained Wall-E3 system

As can be seen above, the Teensy 3.5 controller and its companion 5V LDO regulator board/HC-05 Bluetooth module were simply hot-glued to the battery pack for initial testing; they will be moved to the top plate next.

After successful movement tests with the above arrangement (see the companion post), the next step was to install the Teensy 3.5 controller and the LDO/HC-05 module on the top plate, and permanently install the charging connector and the ON/OFF switch. I decided that a single hole punched into the center rear of the top plate would suffice for bringing necessary wiring up from the internal battery/motor bay onto the top plate, and placing the hole toward the rear would allow the top plate to be removed and placed upside down at the back of the chassis to allow access to the battery/motor bay as needed. As shown in the following photo, the top plate is secured upside-down on the main chassis by two of the normal mounting screws, thus keeping the top-plate-mounted subsystems from contacting the ground.

Top plate in the ‘open’ position

Also note that the motor/battery bay is much cleaner than the Wall-E2 layout, even though both motor drivers are in this bay rather than mounted to the top plate as they are for Wall-E2. The next photos below show the chassis closed up with the Teensy 3.5 and the 5V LDO module mounted on the top plate, with the charging connector and the ON/OFF switch permanently installed.

Front & rear aspect views of the ‘closed’ chassis

After getting everything closed up, I re-ran the ‘spin turn’ trials, as shown below:

‘Spin Turn’ trials with T3.5 and 5V LDO mounted to top plate

At this point, there are only three significant modules (the MPU6050 IMU, the ‘Run Current’ 1NA169 high-side current sensor, and the charging station beacon homing module) remaining to install on the top plate. The first step will be to install and test the MPU6050, as that will give Wall-E3 the capability to do angle-controlled turns.

20 November 2021 Update:

As described in the companion post to this one, my plan is to move forward with the Teensy program (Teensy_MPU6050_DMP6_V3.ino) and the older I2CDevlib/MPU6050 library versions while waiting for Jeff Rowberg and company to figure out why the current versions of this library are no longer compatible with the Teensy 3.x line of microcontrollers.

The first step in this process is to remove the Arduino UNO/Wixel Shield module from the robot, and get the already-installed Teensy 3.5 talking to the already-installed MPU6050 module using the above mentioned Teensy_MPU6050_DMP6_V3.ino and the older I2CDevlib/MPU6050 library versions. Here’s the hardware layout:

Teensy 3.5, MPU6050, 5V LDO Regulator/HC-05 BT module, ‘I_Run’ current sensor

And here’s some output from Teensy_MPU6050_DMP6_V3.ino showing a ‘live’ connection to the MPU6050:

At this point, we have demonstrated we can talk to the MPU6050 using Jeff Rowberg’s (old) I2CDevlib library and can pull off yaw values, so we should also be able to do the same turn-rate-controlled turns as before.

So now I have a Teensy project (Teensy_MPU6050_DMP6_V3) that demonstrates a working interface to the MPU6050, a prior project (T35_WallE3_V1) that demonstrated Teensy OTA updates using an HC-05 Bluetooth module, and a Teensy project demonstrating turn-rate controlled turns using the MPU6050 (T35_MPU6050_Demo_V2). The next step is to create yet another new project, this time cleverly called ‘T35_WallE3_V2’, to merge all these features into one project, and then hopefully proceed from there.

When I created the T35_WallE3_V2 project, the first thing I did was copy in T35_WallE3_V1.ino and supporting files, and then I tested it to make sure everything was OK. However, this project wouldn’t connect to the MPU6050 even though I was sure it was using the older I2CDevlib files. After a very careful comparison, I found there were a very few differences. When I instead used the support files from my Teensy_MPU6050_DMP6_V3 project, then T35_WallE3_V2 was able to connect to the MPU6050. The i2Cdev.h/cpp files were essentially identical. The MPU6050.h/cpp files were essentially identical. The MPU6050_6Axis_MotionApps_V6_12.h files were essentially identical, so I’m not really sure why the magic happened — but it did!

So now the T35_WallE3_V2 talks to the MPU6050. Next I enabled ‘board.txt’ and verified that I can still do OTA firmware updates.

21 November 2021 (an almost palindromic date 21_11_21) Update:

After verifying OTA update capability with T35_WallE3_V2, I started porting in turn-rate control code from the Teensy_MPU6050_DMP6_V3 project, consisting mainly of the PID values empirically found in the companion ‘form-factor’ post, and the PIDCalcs() function.

  • Replaced T35_WallE3_V2 ‘CheckForUserInput()’ with version from T35_MPU6050_Demo_V2
  • Replaced T35_WallE3_V2 ‘PIDCalcs()’ with version from T35_MPU6050_Demo_V2
  • Replaced T35_WallE3_V2 ‘SpinTurn()’ with version from T35_MPU6050_Demo_V2 and added TURN_START_SPEED constant.
  • Updated TurnRate_Kp/Ki/Kd to 1.8/1.0/0.4

22 November 2021 Update:

At this point I have T35_WallE3_V2 to the point where I can do OTA firmware updates via the HC-05 Bluetooth link, and I can remotely command CW & CCW turns, forward and backward, speed up/down. Here’s the snapshot of the code at this point.

Now that I have turn-rate-based turns working properly, it’s time to turn my attention to the other peripherals – namely the ‘I_total’ and ‘I_run’ 1A169 high-side current sensors, the battery voltage output from the 5V LDO regulator module, and the charge connection status line.

  • Ported GetTotalAmps(), GetRunningAmps(), IsStillCharging(), IsChargerConnected, and MonitorChargerUntilDone() from FourWD_WallE2_V12.ino.
  • Ported CHG_SUPP_PARAMETERS into PRE-SETUP
  • Ported charger support pin init into setup()

After these ports, I got a lot of ‘mySerial was not declared’ errors – not surprising as I needed ‘mySerial’ in the Arduino world to access ‘printf’ formatting, but the Teensy has that feature built-in. After replacing 5 instances of ‘mySerial.’ with ‘Serial1.’, I recompiled – still lots of errors – oh well.

In the Arduino code, I used a function called ‘GetAverageAnalogReading()’ to average A/D readings with a ‘CURRENT_AVERAGE_NUMBER’ parameter. However, in the Teensy world there is a function called

which causes all the ADC channels to average every reading by ‘num’ (where ‘num’ is internally forced to be 0,4,8,16, or 32). So, I can eliminate the ‘GetAverageAnalogReading()’ function entirely for the T3.5, and just use analogRead().

After some minor fumbling around to comment out or bypass non-relevant (to this phase, at least) code, I got the program to compile. Now to test for proper behavior…

Testing for the two 1NA169 current sensors went OK, but I hit a snag when testing the ‘Charger Connected’ line from the charging plug. This line is tied to GND with no jack inserted, and the GND connection is broken when the plug is fully inserted. The pin is initialized for INPUT_PULLUP, meaning the line will be pulled HIGH via the internal pullup resistor when the charging cable is plugged in, and pulled to GND when the plug is removed. My initial pin assignment for this pin was 36/A17, but I discovered this wouldn’t work as some analog inputs didn’t have pullup resistors available. So, I more or less randomly chose pin 28 on the other side for this, only to find out that for no apparent reason, the pullup on pin 28 no longer seemed to exist. The charge connect line indeed measured GND with the plug removed, but only about 0.1-0.2V with the plug engaged, instead of 3.3V as it should. After some screwing around, I finally constructed a very small test program to exercise pins 28-32, whereupon I found that 29-32 operated properly, but 28 refused to behave. I posted about this to the Teensy forum, and another forum member tried my program on one of their T3.5’s and reported that pin 28 behaved properly on their module – rats! In any case, I can move the line to pin 29 no problem, but it’s frustrating to realize that of all the pins I could have picked, I managed to hit the one that misbehaved :(.

25 November 2021 Update (Happy Thanksgiving!)

I’m writing this from the home of my stepson and his family in St. Louis, MO, where we are all gathered for the traditional Turkey Day dinner. Of course I brought my Wall-E3 robot so I could continue to work on bringing it along. While here I managed to get the CHG_CONNECT issue solved, but in the process may have killed another port on the Teensy 3.5. At the moment I have the CHG_CONNECT input assigned to pin 31, and the CHG_CONNECT_LED output assigned to pin 30. Pins 28 and 32 appear to be inactive and might have been casualties in some previous project, or casualties of this one – hard to tell. In any case that all appears to be working now.

The following photo shows the current pin assignments for the T3.5. These may well change before the whole adventure ends, but it’s a good starting point.

29 November 2021 Update:

Here’s my first cut at a system-level schematic for the new Wall-E3 robot:

Wall-E3 System Schematic

The above schematic has the same basic layout as for Wall-E2, with the following changes:

  • The Wixel RF serial extender module has been replaced by the HC-05 Bluetooth module (physically located on the small perf-board module along with the 5V LDO voltage regulator)
  • The left and right motor drivers are now VNH5019 parts instead of DRV8871
  • The MPU6050 & IR Detector modules now use their own I2C channel (SDA1/SCL1) instead of everything being serviced on the single I2C channel available on the Mega 2560
  • The VL53L0X ToF sensor array is still managed by a separate Teensy 3.5, but now is the only device on the primary (SDA0/SCL0) I2C bus

At this point I have verified proper operation for both VNH5019 motor drivers, both 1NA169 inline current sensors, the MPU6050 IMU and the HC-05 Bluetooth module.

The next big milestone is to get the second-deck mounted and verify proper operation of the forward-looking LIDAR and the VL53L0X side and rear ToF distance sensors.

Stay tuned,

Frank

Over-the-Air (OTA) Firmware Updates For Teensy 3 & 4.x, Part II

Posted 16 October 2021,

In my previous post on this subject, I described my effort to automate over-the-air (OTA) updates of a Teensy 3.5 using a C# command-line program called by a post-build script from Visual Studio 2019 with the Visual Micro Arduino IDE extension, combined with Joe Pasquariello’s find ‘FlasherX’ code. As the article described, I was successful in doing this, but the time required to transfer the .HEX file using the C# .NET serial interface appeared to be about 2-3 times slower than that required by using the Tera Term serial comms program manually.

So, I went back to the drawing board, and started searching for a way to automate Tera Term, rather than building my own serial port management applet. I found the Tera Term help index, and subsequently I found that there is indeed a quite rich macro language associated with Tera Term, so I started learning the language and trying to apply it to my problem of using Visual Micro’s post-build commands via the ‘board.txt’ feature to automate the process of OTA for the Teensy microcontroller.

After the normal amount of fumbling around with the macro language, I was able to create a reasonably functional macro file that accepts three arguments from the Visual Micro ‘board.txt’ post-build command line and then automates the process of uploading the associated .HEX file to my Teensy 3.5 and then rebooting the Teensy to run the updated program. The ‘board.txt’ contents are:

The post-build command launches the Tera Term Pro Macro interpreter (ttpmacro.exe) and passes three arguments; the first is the build path – the folder in which the compiled .HEX file will be placed. The second is the COM port number assigned to the HC-05 bluetooth link with my laptop, and the third is the project file name, i.e. ‘TeensyOTADemo.INO’.

The Tera Term macro uses the first and third argument to build a path to the compiler’s .HEX file for the project, i.e. <build_path>\<project file name minus extension>.HEX. Then it uses the COM port specified in the second argument to connect to the Teensy and send a ‘trigger character’ to force the Teensy into ‘update mode’, and upload the .HEX file. After the .HEX file has been uploaded, the macro responds to the ‘enter xxxx to update or 0 to abort’ response from the Teensy by sending back the xxxx value, which causes the Teensy to reboot and begin running the new program.

Here is the complete Tera Term macro file:

and here are two short videos showing the OTA update process. The first video shows the process from the PC’s point of view, and the second one shows the same thing, but from the Teensy’s point of view.

Teensy OTA from PC’s point of view
Teensy OTA from Teensy’s point of view

Summary:

Over-the-air (OTA) update of a Teensy microcontroller is now practical using Joe Pasquariello’s fine ‘FlasherX’ program, combined with a Tera Term macro that is launched using Visual Micro’s ‘board.txt’ post-build feature. The things you need to make this happen are:

  • Obviously any Teensy program must incorporate Joe Pasquariello’s ‘FlasherX’ functionality. See my previous post for my complete demo sketch that does this.
  • You must have a way of triggering the update functionality provided by ‘FlasherX’. In my demo I accomplished this via my ‘CheckForUserInput()’ function that runs each time ‘loop()’ executes. If this function detects the letter ‘U’ or ‘u’ on Serial1, it launches FlasherX’s ‘update_firmware’ function which does the rest.
  • You have to have a serial comms application to upload the .HEX file produced by compiling the program update. I used Tera Term for this, and it worked very well.
  • To automate the above process, you need a script file (macro) like the one I provided above to manage the upload process.

I created a new GitHub repository here containing the Tera Term macro file, the ‘board.txt’ file I used with VS/VM, and the OTA demo sketch I used to demonstrate this functionality. Enjoy!

Frank

27 October 2021 Update:

As part of my Wall-E3 project, I constructed a small perf-board module to carry the low-dropout (LD0) 5V Regulator, and added a HC-05 module for OTA updates to Wall-E3’s Teensy firmware, as shown in the following photo

5V regulator board with HC-05 BT module added for OTA updates

And here is a short video showing an OTA update:

Teensy OTA firmware update using HC-05 BT module and Tera Term script

Over-the-Air (OTA) Firmware Updates For Teensy 3.x & 4.x

Posted 23 September 2021

For some time now I have been following developments on Paul Stoffregen’s Teensy forum with respect to OTA updates for Teensy products. As it turns out, doing OTA updates is less than straightforward due to the Teensy architecture (I’m waving my hands here).

However, in recent months, thanks to Teensy forum member joepasquariello, there is a new set of programs that seem to address the Teensy OTA issue in a much more comprehensive manner. See this thread (starting with post #85) on the Teensy forum for the details. After reading through the posts, and asking some more dumb questions (which joe was fortunately willing to answer), I decided to give this a try to see if I could achieve OTA updates for a Teensy controller, and maybe make it as convenient as OTA updates are now with Arduino products using the Pololu Wixel RF serial pipe.

The FlasherX package is actually three different files; FlasherX.ino is a demo program that #includes FlashTxx.h and FlashTxx.cpp. The FlasherX.ino program is loaded into the target Teensy device in the normal manner, just like any other Teensy program. However, once it is there, it adds the capability to update the Teensy’s firmware using the .HEX file (produced by the normal compile process) transmitted over any serial port. Of course, in order to make this magic happen, the updated Teensy program must also have this capability, so that means that any Teensy program destined for OTA updates must include the basic ‘FlasherX’ components.

In order to get started, I dl’d all three files and programmed a Teensy 3.5 with the FlasherX.ino program using my normal VS2019/Visual Micro setup. Then I created a new Arduino Project in VS2019 called ‘TeensyFlasher_V1’ and copied the entire contents of FlasherX.ino. I compiled it for a T3.5 target and used my normal Teensy bootload process to transfer it to the Teensy 3.5. Here is the serial port output after the upload:

Initial output from FlasherX.ino after upload to Teensy 3.5

Next I used Tera Term’s ‘file send’ capability to send the FlasherX.hex file to the Teensy using the wired USB connection as just another serial port. When I first launched Tera Term I got an error dialog saying it “Cannot open COM11. Access denied” – reminding me that I had to close the normal connection in order for Tera Term to operate properly. Once I had done this, I used the ‘File->New Connection menu item in Tera Term to create a new connection to COM11 (the Teensy 3.5 USB port), as shown:

New connection to Teensy port

Next I used ‘File->Send File… to select the .HEX file created by the compiler, and transfer its contents over to the Teensy using the COM11 serial port

After selecting the .HEX file and clicking ‘Open’, the HEX contents were sent over the serial connection as shown below:

The reason for using the HEX file created from compiling FlasherX.ino (as opposed to a random Teensy app) is because FlasherX.ino already contains all the pieces needed to do OTA updates. Although a bit recursive, using the FlasherX app demonstrates that a Teensy device can be updated via a serial connection repeatedly, as long as the newly updated firmware contains the pieces needed to support the next OTA update.

The next step was to figure out how to get VS2019/Visual Micro to do the update automatically, so that the process emulates what happens with an Arduino controller when F5 is pressed. VS2019 supports post-build scripts, but they aren’t used in the Visual Micro environment. Instead, as I learned from VM guru Tim Leek, VM supports post-build operations via a special file called ‘board.txt’ in the project directory. If this file is present, properly formatted lines are treated as ‘build property overrides’. For instance, this is the line in ‘board.txt’ that calls a C# console app to actually communicate with the target Teensy.

This command launches the console app, and provides it with the path to the project, the path to the sketch, and the currently selected port number. See this thread on the VM forum for all the gory details.

I ran into a problem when I tried to use the above command line with my C# console app, because the arg[] string array in the console app was interpreting spaces in the strings in the calling command differently than expected, so when I sent three strings to the command, the console app thought I was only sending two – somehow concatenating the com port number into the second string (the sketch path). After playing around some more, I found that the following treatment:

when passed as an argument to this small code snippet

produced the following output:

With the correct number of arguments, and the comport number on its own line – yay. I have no idea why this works and the other formulations don’t, and why the second argument has to be have it’s quotes escaped with ‘\’, but hey – it works.

As it turns out, this exercise was a bit academic anyway, because both the {ProjectDir} and {sketch_path} expand to identical results, so I only need one of them and the comport number to proceed. So, the ‘final’ (for now) call argument list looks like:

which produces:

27 September 2021 Update

In order to do OTA updates, I need to be able to connect to one of the Teensy’s hardware serial ports. Up til now I have been just using the USB serial port for convenience, so I thought I would try using a FTDI Pro USB/Serial converter module to connect to Serial1 on the teensy, as shown in the following photo:

In the above photo, the normal USB connection provides 5V power for the Teensy, and also allows ‘direct’ programming via the normal VS2019/VM route. The FTDI module connection shows up as COM3, a ‘regular’ USB serial port. With this setup I was able to upload a .HEX file to the Teensy using Tera Term, confirming that I can indeed use a hardware serial port for this purpose.

04 October Update:

Between the last update and now, I spent a lot of time trying to get the command-line post-build console app working, mostly to no avail. I kept getting wrapped around the axle trying to deal with three different serial connections; two to the teensy itself, and one from the command-line app to VS2019’s Console service. This got to the point where at one time the ‘Console’ output was going to my computer display, which made the mouse jump around wildly, occasionally launching or closing apps! Even trying to restart the PC was a trial-and-error process, because about half the time the screen focus would jump away from the target button before I could get it clicked!

So, I decided to go back to basics and use just the FTDI serial adaptor to run the Teensy. This required using the Vcc connection from the FTDI module to power the Teensy, which worked OK once I figured out how to switch the FTDI module from 3.3V out to 5V out.

FTDI USB-serial module now powering Teensy 3.5

With this setup I am able to upload programs to the Teensy using Tera Term, as before, but now I don’t have the other USB connection to confuse things. It does mean however, that now I MUST use Tera Term to upload sketches, as I no longer have the capability to use the Teensy bootloader from VS2019.

With this simpler setup, ‘Console’ statements from the command-line app should go only to the VS2019 debug session, and serial port operations from the command-line app should go only to Teensy’s ‘serial1’ hardware serial port.

05 October 2021 Update:

I expanded the above operation to restore the USB connection to the PC, and created a small Arduino program to (at first) just echo serial data from one serial port to the other. Here’s the program:

Once I had this going to my satisfaction, I started adding pieces from my ‘TeensyFlashDemoV2’ project until I had a complete working program, as shown below:

Here’s the Arduino code:

and here is the companion C# command-line app:

And here is the command-line output from my last run:

In the above printout, most of the HEX lines were removed just to keep the line count down, but all 2977 lines were successfully sent to the Teensy. The lines

were emitted from the Teensy at the conclusion of the HEX file upload. The line

was from the command-line app comparing the number of lines sent with the number of lines received. The numbers matched, so ‘2977’ was sent to the Teensy to complete the update.

At this point I have a working automated update system except the COM port number and HEX file location was passed to the C# program as a set of fixed arguments (as shown below), to disconnect the command-line app from the Arduino sketch, instead of dynamically providing the same parameters from VM build properties

Now that I have some confidence that the command-line app does what it is supposed to do, I can go back and make that connection again, hopefully with better results this time.

A problem with the Arduino-to-C# command-line app is that the ‘build properties’ exposed by Visual Micro aren’t always the same, so what works for one sketch may or may not work for another. Here’s the ‘build properties’ output from my expanded ‘TeensySerialBridgeV1.ino’ file:

I need two things from the build properties – the COM port number, and the path to the .HEX file. From the above list, it appears that I could use either of the following properties,

but neither is very convenient, as the COM port number has to be extracted from a ‘known’ string. On other projects, I have seen a build property like ‘serial.port.number = 11’ which would be much more convenient, but it seems to have disappeared. For the path to the .HEX file, I should be able to use

or

but it appears the ‘final_output_path’ is a bit more directly related to the .HEX file location. So, to make this work using the VM post-build facility, the required ‘board.txt’ file should look like:

When the above is placed in ‘board.txt’ and the Arduino sketch is run, I got the following output:

Which appears to be correct, and the Teensy was obviously updated (got the USB disconnect/connect tones, and the LED tell-tale stopped and then resumed blinking). It looks like I might actually have a working program now – woo hoo!

06 October 2021 Update:

One minor snag occurred with the build property used to pass the COM port number to the command-line program; I had been using {serial.port.caption} and it turns out that this parameter (along with many other serial.port parameters) isn’t always available – things change depending on the COM port chosen in the Arduino program and the actual device connected (or not connected) to the PC. After another round of posts in the Visual Micro forum, Tim Leek pointed out that the ‘serial.port’ parameter should always be available. I had missed this on this because it isn’t grouped in with the rest of the ‘serial.xxx.xxx’ parameters. With the benefit of hindsight, I can see why this is so; the ‘serial.port’ parameter is probably pulled from the VS2019 IDE, while the other ‘serial.port.xxx’ parameters are hardware-specific. Once I switched my ‘board.txt’ to use the ‘serial.port’ parameter vs ‘serial.port.caption’, I started getting much more consistent results. Here’s the ‘final’ (I hope) board.txt configuration:

One other small snag; my ‘TeensySerialBridgeV1’ program was originally intended to just pass serial data back and forth between two terminal programs (Tera Term on one side, the Teensy sketch on the other). As a result, there was a built-in assumption that both serial connections would always be available, as shown in the following code snippet:

The first ‘while()’ statement causes the program to hang if the physical Teensy USB connection is missing. This gave me some grief when I removed the Teensy USB cable to prove I could do the entire update cycle with only the Serial1 hardware serial device connected – oops!

The final Teensy program is shown below:

09 October 2021 Update: The final product

After encountering and solving a few more ‘gotchas’, I think I now have a reliable Teensy OTA mechanism using the VS2019/Visual Micro IDE and Joe Pasquariello’s wonderful ‘FlasherX’ code . The ‘trick’ is using Visual Micro’s ‘board.txt’ feature to add a post-build command that runs each time the Teensy sketch is compiled, and a small C# command-line program to trigger the FlasherX update code and feed it the correct Hex file.

I created a very simple Teensy project called ‘TeensyOTADemo’ that does only two things; it flashes the built-in LED, and launches the FlasherX update code when triggered by a ‘U’ (for Update) ascii code on Serial1 (pins 0 & 1 on a T3.5). Then I created a small C# command-line program to actually send the trigger character and then transmit the .HEX file created as part of the normal compile process.

Then I created a ‘board.txt’ file containing a post-build command that launches the C# command-line program and feeds it two arguments – one containing the build path, and the other containing the COM port number to be used to connect to the Teensy’s Serial1 port.

Here’s the Teensy sketch:

And the C# command-line program:

And last, the ‘board.txt’ file contents that launches the C# command-line program:


Here’s a short video showing the update process. The clip starts with the ascii ‘U’ trigger code arriving at the Teensy Serial1 port, and ends with the update finished and the Teensy rebooted back into the ‘blink’ code.

09 October 2021 Update: HC-05 Bluetooth link:

The next step is to replace the FTDI module with an HC-05 Bluetooth link. I had some HC-05 modules from previous work, and I had done a similar project with an Arduino some time back – so how hard could it be?

I hooked up a HC-05 to the Teensy as shown in the following photo, using a bench power supply to power both the HC-05 and the Teensy 3.5

HC-05 and Teensy both powered by lap power supply

Then I paired the HC-05 with my laptop; this took a bit of fumbling around, as the laptop thought it was still paired with an HC-05 even though it’s been over two years since I last used it, and I had since forgotten that the default password was ‘1234’. In any case, I eventually got it paired, as shown, with an automatically generated COM port on COM4.

Next I fired up Tera Term and created a connection to COM4, as shown:

And then tried sending some ascii text through to the Teensy. This appeared to work fine, but when I tried a program update via the HC-05 link I got an error that indicated the FlasherX code didn’t like something I was sending it.

The ‘abort – bad hex line’ output happens before I try to send it the .HEX file contents, so it clearly isn’t choking on that; maybe some extra characters getting sent instead of just the “U” character?

Looking at the ‘FlasherX’ code, it appears that the offending characters/line is supposed to be printed out with the lines

which implies whatever caused the problem is non-printable – maybe an extra CR/LF? So, I added some debug code to update_firmware() to print out each line as it is read into the ‘line’ character array, so maybe I can see the problem.

Hmm, more mysteries. I reconnected the regular USB connection, and uploaded the new version of the sketch. Then I disconnected the USB connector, and used Tera Term to send the update command (“U”). However, this time the OTA Update code didn’t choke, and AFAICT performed a completely successful update via BT and the HC-05 – yay! Of course, I now have no idea what caused the problem to start with, and whether or not my addition of a few lines of debugging code made the difference.

So, I undid the changes to the update code and tried again. And, as shown below, the OTA update worked perfectly – double Yay!!

The printed out lines from the .hex file are from the previous run.

So now to try an OTA update using my command-line program instead of Tera Term. I disconnected TT from COM4, and then, after selecting COM4 in the VS2019 IDE, pressed F7 to start the process. This worked as well, but it appeared the transfer rate was considerably slower – no idea why.

Here’s a short video showing the OTA update using the command-line program and BT OTA.

Teensy OTA update using command-line program and HC-05 BT link

Just comparing the overall times from the two videos above the one using the command-line program runs approximately twice as long. It’s hard to believe it is only the use of the command-line program vs Tera Term that is causing the delay – but it is possible that TT was simply running at a higher baud rate – more study required.

In summary, however, slow rate or no, it is clear that we now can implement completely automated, seamless OTA update processes using VS2019 and Visual Micro. Now that the groundwork has been laid, I would assume that this same strategy could be implemented for other IDE’s as well.

I have started GitHub repositories for the Teensy demo sketch TeensyOTADemo and my TeensyFlash C# command-line program on my GitHub site, and I would welcome suggestions/improvements.

10 October 2021 Update:

I’m trying to determine what caused the slow update when using my command-line program and the HC-05 when doing an OTA update relative to the situation when using Tera Term to manually transfer the .HEX file to the Teensy. In both cases, the OTA link is through the HC-05, so that module shouldn’t be the cause. However, just to check I went back and checked the HC-05 settings using the ‘AT Command Mode’ trick discussed in this post. AFAICT, the HC-05 was set to 115200 baud rate. I also checked to see if I had somehow misconfigured the serial port created in my command-line program, but it is set to create a port at 115,200 BPS as well. So, I’m officially baffled.

To investigate the problem, did an OTA update via the HC-05 BT link, but using Tera Term to trigger the update process and send the .HEX file. Using this method, it took approximately 14 sec to transfer the entire 3142-line .HEX file. Then I did an OTA update using my command-line program, using the same HC-05 BT link. With this setup, the transfer took approximately 35 seconds – over twice the time. Watching with my scope, it is clear that the transfer takes place at a much slower rate when using the command-line program, even though (at least AFAICT) the programmed baud rates are identical in both cases. The two photos below show the transfer rate for both cases, using the same time scale for both.

File Transfer using Command-line Program and HC-05 BT Link
File Transfer using Tera Term and HC-05 BT Link

After a bit of web research on C# .NET serial port programming, I came across this post, which indicates that using .NET for this task was not a very wise move – bummer!

Stay tuned,

Frank

Another try at Wall Offset Tracking

Posted 11 September 2021

I’m making another try at getting wall offset capture and tracking right, after several failed attempts. This time I’m starting with a small ‘FourWD_WallTrackTest_V3’ project in VS2019. I think I actually got this working before, but an unfortunate laptop backup catastrophe cost me a couple of months of work, and it has taken me this long to recover. Here’s the current program:

With this code, I ran a test using PID values of 300,0,0 and an offset target of 40cm. The following short video and Excel plot show the results

Now all I have to do is make this same trick work for both the ‘inside offset’ and ‘outside offset’ starting conditions, and then for both the left-side and right-side tracking conditions, then I can integrate the code back into the main robot operating system.

18 September 2021 Update:

After getting the left-side tracking working reasonably well, I started working on right-side tracking, and discovered that what worked for the left side with no problems did not work particularly well for right-side tracking. After the usual number of programming mistakes I did get right-side tracking working, albeit with a significantly different PID parameter set. For left-side tracking, PID = (300,0,0) seemed to work OK, but for right-side tracking I wound up with PID = (300,0,300). Moreover, when I tried even very small (like 10) values for the I parameter, the robot lost track after just two or three oscillations. So, for the moment it looks like (300,0,0) for the left side, and (300,0,300) for the right side. Here’s a short video showing successful right-side tracking.

And here is the ‘finished’ FourWD_WallTrackTest_V3 code:

Stay tuned

Frank

Another Try at Wall Offset Tracking, Part II

Posted 22 June 2021

In my previous post on this subject, I described my effort to improve Wall-E2’s wall tracking performance by leveraging controlled-rate turns and the dual VL53L0X ToF LIDAR arrays. This post describes an enhancement to that effort, aimed at allowing the robot to start from a non-parallel orientation and still capture and track a desired wall offset.

The previous post showed that when starting from a parallel orientation either inside or outside the desired wall offset, the robot would make a turn toward the offset line, move straight ahead until achieving the desired offset, then turn down-track and start tracking the desired offset. The parallel starting condition was chosen to make things easier, but of course that isn’t realistic – the starting orientation may or may not be known. In previous work I created an entire function ‘RotateToParallelOrientation()’ to handle this situation, but I would rather not have to do that. It occurred to me that I might be able to eliminate this function entirely by utilizing the known characteristics of the triple-VL53L0X array. The linear array exhibits a definite relationship between ‘steering value’, (the difference between the front and rear sensor values, divided by 100) and the off-perpendicular orientation of the array. At perpendicular (parallel orientation of the robot), this value is nominally zero, and exhibits a reasonably linear relationship out to about +/- 40ยบ from perpendicular, as shown in the following plot from this post from a year ago.

Array distances and steering value for 30 cm offset. Note steering value zero is very close to parallel orientation

As can be seen above, the ‘Steering’ value is reasonably linear, with a slope of about -0.0625/deg. So, for instance, the calculated value for an offset of 30cm would be -0.0625*30 = -0.1875, which is very close to the actual plotted value above for 30ยบ

So, it should be possible to calculate the off-perpendicular angle of the robot, just from the measured steering value, and from that knowledge calculate the amount of rotation needed to achieve the desired offset line approach angle.

The desired approach angle was set more or less arbitrarily by

and this assumes an initial parallel orientation (i.e. 0ยบ offset in the above plot). If, for instance, the initial steering value was -0.1875, indicating that the robot was pointing 30ยบ away from parallel, then the code to compute the total cut (assuming we are tracking the wall on the left side of the robot) would look something like this:

So, for example, if the robot’s starting orientation was offset 30ยบ away from the wall, and 20cm inside the desired offset line of 30cm, we would have:

cutAngleDeg = WALL_OFFSET_TGTDIST_CM – (int)(Lidar_LeftCenter / 10.f);

cutAngleDeg = 30 – 10 = 20

adjCutAngleDeg = cutAngleDeg – 30 = -10ยบ, so the robot would actually turn 10ยบ CCW (back toward the wall) before starting to move to capture the desired offset line.

If, on the other hand, the robot was starting from outside the desired offset (say at 60cm), but with the same (away from wall) pointing angle, then the result would be

cutAngleDeg = WALL_OFFSET_TGTDIST_CM – (int)(Lidar_LeftCenter / 10.f);

cutAngleDeg = 30 – 60 = -30

adjCutAngleDeg = cutAngleDeg – 30 = -30 – 30 = -60ยบ, so the robot would actually turn 60ยบ CCW (back toward the wall) before starting to move to capture the desired offset line.

26 June 2021 Update:

I modified my test program to just report the rear, center, and front VL53L0X distances, plus the steering value and the computed off-parallel angle, using the -0.0625 slope value obtained from the above plots. Unfortunately, the results were wildly unrealistic, leading me to believe something was badly wrong somewhere along the line. So, I redid the plots, thinking maybe the left side VL53L0X sensors were different enough to make that much of a difference. When I plotted the steering value vs off-parallel angle for 10, 20, 30 & 40cm wall offsets, I got a significantly different plot, as shown below:

As can be seen in the above plot, the slope of steering values to off-parallel angles is nice and linear, and also quite constant over the range of wall offset distances from 10 to 40 cm; this is quite a bit different than the behavior of the right-side sensor array, but it is what it is. In any case, it appears that the slope is close to 1.4/80 = 0.0175, or almost twice the right-side slope derived from the previous right-side plots.

Using the value of 0.0175 in my GetSteeringAngle() function results, and comparing the calculated off-parallel angle with the actual measured angle, I get the following Excel plot.

As can be seen in the above plot, the agreement between measured and calculated off-parallel angles is quite good, using the average slope value of 0.0175.

The above plot shows the raw values that produced the first plot.

So, back to my ‘FourWD_WallTrackTest’ program to develop Wall-E2’s ability to capture and then track a particular desired offset. In my first iteration, I always started with Wall-E2 parallel to the wall, and calculated an intercept angle based on the difference between the robot’s actual and desired offsets from the near wall. This worked very nicely, but didn’t address what happens if the robot doesn’t start in a parallel orientation. However, when I tried to use the data from a previous post, the results were wildly off. Now it is time to try this trick again, using the above data instead. Because the measured steering value/off angle slopes for all selected wall offsets were essentially identical, I can eliminate the intermediate step of calculating the appropriate slope value based on the current wall offset distance.

So, I modified my ‘getSteeringAngle()’ function to drop the ‘ctr_dist_mm’ parameter and to use a constant 0.0175 slope value, as shown below:

With this modification, I was able to get pretty decent results; Wall-E2 successfully captured and tracked the desired offsets from three different ‘inside’ (robot closer to wall than the desired offset) orientations, as shown in the following short videos:

‘Inside’ capture, with initial orientation angle > desired approach angle
‘Inside’ capture with initial orientation angle < desired approach angle
‘Inside’ capture with negative initial orientation angle

04 July 2021 Update:

After succeeding with the ‘inside’ cases, I started working on the ‘outside’ ones. This turned out to be considerably more difficult, as the larger distances from the wall caused considerable variation in the VL53L0X measurements (lower SNR?), which in turn produced more variation in the starting and ‘cut’ angles. However, the result does seem to be reasonably reliable, as shown in the following videos.

‘outside’ capture with initial outward angle
‘outside’ capture with initial inward angle
‘outside’ capture with small outward angle.

05 July Update:

After getting the left-side tracking algorithm working reasonably well, I ported the ‘TrackLeftWallOffset()’ functionality to ‘TrackRightWallOffset()’. After making (and mostly correcting) the usual number of mistakes, I got it going reasonably well, as shown in the following short videos:

Right wall tracking, starting inside desired offset, oriented toward wall
Right wall tracking, starting inside desired offset, oriented away from wall
Right wall tracking, starting outside desired offset, oriented toward wall
Right wall tracking, starting outside desired offset, oriented away from wall

Here is the complete code for my wall capture/track test program:

Here is a link to the above file, plus all required library & ancillary files.

Stay tuned,

Frank

Another Try at Wall Offset Tracking

Posted 22 June 2021

About nine months ago (October 2020) I made a run at getting offset tracking to work (see here and here). This post describes yet another attempt at getting this right, taking advantage of recent work on controlled-rate turns. I constructed a short single-task program to do just the wall tracking task, hopefully simplifying things to the point where I can understand what is happening.

One of the big issues that arose in previous work was the inability to synch my TIMER5 ISR with the PID library’s ‘Compute()’ function. The PID library insists on managing the update timing internally, which meant there was no way to ensure that Compute() would be called every time the ISR ran. I eventually came to the conclusion that I simply could not use the PID library version, and instead wrote my own small function that did the Compute() function, but with the timing value passed in as an argument rather than being managed internally. This forces the PID calculations to actually update in synch with the TIMER5 interrupt. Here’s the new PIDCalcs() function:

As can be seen from the above, this is a very simple routine that just does one thing, and doesn’t incorporate any of the improvements (windup suppression, sample time changes, etc) available in the PID library. The calling function has to manage the persistent parameters, but that’s a small price to pay for clarity and the assurance that the output value will indeed be updated every time the function is called.

With this function in hand, I worked on getting the robot to reliably track a specified offset, but was initially stymied because while I could get it to control the motors so that the robot stayed parallel to the nearest wall, I still couldn’t get it to track a specific offset. I solved this problem by leveraging my new-found ability to make accurate, rate-controlled turns; the robot first turns by an amount proportional to it’s distance from the desired offset, moves straight ahead until the offset is met, and then turns the same number of degrees in the other direction. Assuming the robot started parallel to the wall, this results in it facing the direction of travel, at the desired offset and parallel to the nearest wall. Here is the code for this algorithm.

Here are a couple of videos showing the ability to capture and track a desired offset from either side.

In both of the above videos, the desired wall offset is 30 cm.

Stay Tuned,

Frank

Turn Rate PID Tuning, Part IV

Posted 10 June 2021,

In my last post on this issue, I described using a small test program to explore an in-line version of the PID (Proportional-Integral-Differential) algorithm for turn rate control with Wall-E2, my autonomous wall-following robot. This post describes some follow-on work on this same subject.

The fundamental problem with all the available Arduino PID libraries is they all require the user to wait in a loop for the PID::Compute() function to decide when to actually produce a new output value, and since this computation is inside the function, it is difficult or impossible to synchronize any other related timed element with the PID function. In my case where I want to control the turn rate of a robot, the input to the PID engine is, obviously, the turn rate, in degrees/sec. But, calculation of the turn rate is necessarily a timed function, i.e. (curent_heading – last_heading) / elapsed_time, where the ‘elapsed_time’ parameter is usually a constant. But, all the Arduino PID libraries use an internal private class member that defines the measurement period (in milliseconds), and this value isn’t available externally (well, it is, but only because the user can set the sample time – it can’t be read). So, the best one can do with the current libraries is to use the same constant for PID::SetSampleTime() and for any external time-based calculations, and hope there aren’t any synchronization issues. With this setup, it would be quite possible (and inevitable IMHO) for the PID::Compute() function to skip a step, or to be ‘phase-locked’ to producing an output that is one time constant off from the input.

The solution to this problem is to not use a PID library at all, and instead place the PID algorithm in-line with the rest of the code. This ensures that the PID calculation any related time-based calculations are operating on the same schedule. The downside of this arrangement is loss of generality; all the cool enhancements described by Brett Beauregard in his wonderful PID tutorial go away or have to be implemented in-line as well. From Brett’s tutorial, here’s ‘the beginner’s PID algorithm’:

The difficulty with all the current PID libraries is the ‘dt’ parameter in the above expression; the implementation becomes much easier if ‘dt’ is a constant – i.e. the time between calculations is a constant. However, this constraint also requires that the library, not the user program, controls the timing. This just doesn’t work when the ‘Input’ parameter above also requires a constant time interval for calculation. In my case of turn rate control, the turn rate calculation requires knowledge of the time interval between calculations, and the calculation itself should be done immediately after the turn rate is determined, using the same time interval. So, the turn rate is calculated and then PID::Compute() is called, but Compute() may or may not generate a new output value, because it can return without action if it’s internal time duration criteria isn’t met; see the problem? It may, or even might, generate a new output value each time, but there is no way to ensure that it will!

After figuring this out the hard way (by trying and failing to make the library work), I finally decided to forget the library – at least for my turn rate problem, and in-line all the needed code. Once I had it all running, I abstracted just the PID algorithm to its own function so I could use it elsewhere. This function is shown below:

As you can see, the explanatory comments are much bigger than the function itself, which is really just eight lines long. Also, it has a huge number of arguments, five of which are references that are updated by the function. This function wouldn’t win any awards for good design, as it has too many arguments (wide coupling), but it does have high cohesion (does just one thing), and the coupling is at least ‘data’ coupling only.

Once this function was implemented, the calling function (in this case ‘SpinTurn()’ looks like this:

In the above code, the lines dealing with ‘TIMSK5’ are there to disable and then re-enable the TIMER5 interrupt I have set up to update external sensor values every 100 mSec. I’m not really sure that this HAS to be done, but once I learned how to do it I figured it wouldn’t hurt, either โ˜บ

Now that I have this ‘PIDCalcs()’ function working properly, I plan to use it in several other places where I currently use the PID library; it’s just so much simpler now, and because all the relevant parameters are visible to the calling program, debugging is now a piece of cake where before it was just an opaque black box.

12 June 2021 Update:

After chasing down and eliminating a number of bugs and edge-case issues, I think I now have a pretty stable/working version of the ‘SpinTurn’ function, as shown below:

With this code in place, I made some 180ยบ turns at 45 & 90 deg/sec, both on my benchtop and on carpet, as shown in the plots and video below:

Average turn rate = 41.8 deg/sec
Average turn rate = 86 deg/sec
Average turn rate = 44.8 deg/sec
Average turn rate = 90.1 deg/sec

Stay Tuned!

Frank