Replacing HC-SRO4 Ultrasonic Sensors with VL53L0X Arrays Part III

In the previous two posts on this subject, I described my efforts to replace the ultrasonic ‘ping’ sensors on my autonomous wall-following robot Wall-E2 with ST Microelectronics VL53L0X ‘Time-of-Flight’ LIDAR proximity sensors, with the aim of using an array of the ToF sensors to solve the problem of wall tracking at a specified offset. Part II demonstrated just such a capability, but only on the right side, and not in a way that was fully integrated with the rest of the robot operating system.

This post describes the work to fully integrate the new ‘parallel-find’ and ‘OffsetCapture/Track’ algorithms into the rest of the robot’s autonomous wall-following operating system. The robot operating system is a loop that runs every 200 mSec.  Each time through the loop the current operating mode is determined in GetOpMode(), as follows:

  • Dead Battery:  Battery died before robot could find and connect to a charging station
  • IR Homing: Robot has detected a charging station and is homing on it’s IR signal
  • Charging: Robot is currently charging on one of the available charging stations
  • Wall Follow:  nothing else is going on, so continue tracking the nearest wall

In the above list, all but the last (Wall Follow)  are ‘blocking’ in the sense that once they start, they run to completion without interruption.  The ‘Wall Follow’ mode processing path however, typically makes a small adjustment to the left/right motor speeds depending on the situation and then exits back to the main loop, where GetOpMode() runs again.  So wall following involves multiple passes through GetOpModes() so that higher-priority tasks (like dead battery recognition, IR homing, and battery charging are executed in a timely manner.

The result of GetOpMode() is passed to a CASE statement where actions appropriate to the determined mode are executed as follows:

  • MODE_CHARGING:   This mode blocks until charging is complete
  • MODE_IRHOMING:  This mode blocks while homing station to the charge station.  If the robot isn’t ‘hungry’, it attempts to go around the charging station rather than connecting to it.
  • MODE_DEADBATTERY:  This mode blocks forever
  • MODE_WALLFOLLOW: This is the default robot mode – whenever one of the above ‘special’ modes doesn’t apply. The MODE_WALLFOLLOW mode is further broken down into tracking cases as determined in GetTrackingDir(), which returns one of TRACKING_NONE, TRACKING_LEFT, TRACKING_RIGHT, or TRACKING_NEITHER.

With the replacement of the ‘ping’ sensors with the VL53L0X array) Wall-E2 can now accurately determine its orientation with respect to a nearby wall.  So I now believe I can make the offset capture operation a blocking call, significantly simplifying the MODE_WALLFOLLOW code.  Here is the code for the TRACKING_RIGHT case:

In the above code the robot first checks for obstacles, and either maneuvers to capture the desired wall offset (first time through this case) or continues to track the previously captured offset.

 

 

 

Integrating Heading-based Wall Tracking into Wall-E2

Posted 09 September 2019

After successfully demonstrating heading-based wall tracking with my little 2-motor robot, I am now trying to add this capability to Wall-E2’s repertoire.

Current Algorithm:

Every MIN_PING_INTERVAL_MSEC millisec Wall-E2 measures left/right/forward distances, and then calls ‘GetOpMode()’ to determine its current operating mode.  The currently defined modes are:

  • MODE_NONE: (0) default case
  • MODE_CHARGING (1):  connected to the charger and not finished
  • MODE_IRHOMING (2): coded IR signal from a charger has been detected
  • MODE_WALLFOLLOW (3):  trying to run parallel to a wall
  • MODE_DEADBATTERY (4): battery exhausted

The MODE_WALLFOLLOW operational mode is returned from GetOpMode() when none of the other modes are active – in other words it is the real ‘default mode’.  The MODE_WALLFOLLOW mode has three distinct sub-modes;

    • TRACKING_LEFT (1): actively tracking the left wall
    • TRACKING_RIGHT (2): actively tracking the right wall
    • TRACKING_NEITHER (3): not tracking either wall (both distances > 200cm)

TRACKING_LEFT & TRACKING_RIGHT are identical for purposes of heading-based wall tracking, and the TRACKING_NEITHER case isn’t relevant, so we just have to come up with a way of integrating heading-based tracking into either the LEFT or RIGHT case.  The TRACKING_LEFT case block is shown below:

The first thing that happens is a check for an obstacle within the MIN_OBS_DIST_CM or the ‘stuck’ condition (determined via a call to ‘IsStuck()’).  If this test passes, a check is made for an obstacle that is farther away than MIN_OBS_DIST_CM but closer than STEPTURN_DIST_CM, so a gradual ‘step-turn’ can be made to negotiate an upcoming corner.  If that check too fails, then the robot is assumed to be tracking the left wall, safely far from any oncoming obstacles.  In this case, a new set of motor speeds is computed using a PID setup to keep the left PING distance constant – no consideration is given for whether or not the PING distance is appropriate – just that it is either greater or less than the one measured in the last pass.

So, the thing we want to change is the way the robot responds to PING distances to the left wall.  We want the robot to acquire and maintain a selected standoff distance from the wall being tracked (the LEFT wall in this case).  Pretty much by definition, this requires two distinct activities – the first to acquire the selected standoff distance, and the second to maintain that distance for the duration of the wall-tracking activity.  So now we have three state variables for wall tracking, and only the last one (ACQUIRE/MAINTAIN) is new

  •  TRACKING_LEFT/RIGHT
    • NAV_WALLTRK (as opposed to NAV_STEPTURN)
      • PHASE_ACQUIREDIST/PHASE_MAINTAINDIST

In the PHASE_ACQUIREDIST phase, a heading change of up to 20 deg is made in the appropriate direction to move toward the selected offset distance.  The situation is then monitored in subsequent loop passes to determine if a larger or small heading cut is required.  When the robot gets close to the desired distance, all the added heading offset is removed and the robot enters the PHASE_MAINTAINDIST phase.  In this phase, small(er) ping distance variations cause larg(er) heading changes, resulting in a sawtooth pattern about the selected distance.

 

Alzheimer’s Light Strobe Therapy Project

Posted 24 March, 2019

A friend told me about a recent medical study at MIT where lab mice (genetically engineered to form amyloid plaques in their brains to emulate a syndrome commonly associated with Alzheimer’s) were subjected a 40Hz strobe light several hours per day.  After repeated exposures, the mice showed significantly reduced plaque density in their brains, leading the researchers to speculate that ‘light strobe therapy’ might be an effective treatment for Alzheimer’s in humans.

The friend’s spouse has been diagnosed with Alzheimer’s, so naturally he was keen to try this with his spouse, and asked me if I knew anything about strobe lights and strobe timing, etc.  I told him I could probably come up with something fairly quickly, and so I started a project to design and fabricate a light strobe therapy box.

The project involves a 3D printed housing and 9V battery clip, along with a white LED and a Sparkfun Pro Micro 5V/16MHz microcontroller, as shown in the following schematic.

Strobe Therapy schematic

I had a reflector hanging around from another project, so I used it just as much for the aesthetics as for functionality, and I designed and printed up a 2-part cylindrical housing. I also downloaded and printed a 9V battery clip to hold the battery, as shown in the following photos

Finished Strobe Therapy Unit

Internal parts arrangement

Closeup showing Sparkfun Pro Micro microcontroller

The program to generate the 40Hz strobe pulses is simplicity itself.  I used the Arduino ‘elapsedMillis’ library for more accurate frequency tuning, but ‘delay()’ would probably be close enough as well.

 

I’m not sure if this will do any good, but I was happy to help someone whose loved one is suffering from this cruel disease.

Frank

 

WallE2 Robot in Arduino #include file hell

Posted 02 February 2019

After a vacation from my WallE2 autonomous wall-following robot code to recover from rotator cuff surgery (and creating/testing my new digital tensionometer), a week or so ago I decided it was time to get back into WallE2 mode.   At the time I thought this would be a piece of cake, as I had left WallE2 in pretty good shape, code-wise back in September 2018  (at least that’s what I thought!).

Instead, For the past couple of weeks I have been enduring what can only be described as “#include file hell”.   The first time I tried to compile my main program, I saw a couple of warnings in an i2cDev related library.   The code still compiled, but I take all warnings very seriously, and these weren’t there the last time I compiled the code.

So, I started trying to figure out what, if anything, had changed, and how to go about fixing whatever problem had caused the warnings to pop up.   Unfortunately, everything I did made things worse – and worse – and worse.   Nothing made sense.   Jeff Rowberg, the creator of the fine i2cDev collection of i2c device drivers was mystified, as was Tim Leek, the main guy on the Visual Micro forum.   Arggghhhh!

So, Jeff Rowberg suggested I try compiling the project in the Arduino IDE rather than in VS2017/Visual Micro, to eliminate any issues caused by that environment.   Up until this point I had actually  never used the Arduino IDE, much preferring the more helpful and feature rich VS2017/Visual Micro IDE. But, what the heck – how hard could it be?

Well, the answer was –  DAMNED HARD!   Using the Arduino IDE after the VS/VM environment was like moving backwards in time from the 21st century to the stone age –   having to rub sticks together to make a compile happen!   Moreover, the Arduino IDE created even more (and different) problems than I had experienced so far, meaning that I not only wasn’t draining the swamp, but the alligators were getting even more numerous!   Some of the ‘features’ of the Arduino IDE:

  • When the IDE is first launched, it comes up with the last .INO file loaded.   If you want a different file, it launches a new IDE instance with file->open; soon your desktop is littered with IDE instances.
  • When it tries to find a library based on a ‘#include <libraryName.h>’ line, it can’t handle [library name]-master as is common with libraries downloaded from GitHub
  • It requires exact name matching, including capitalization.   So ‘#include <libraryName>’ will not match with the ‘Arduino\Libraries\Libraryname’ folder.
  • Editing is clunky, and there’s no such thing as Intellisense.

After running around in circles with my hair on fire for the last week or so, making my wife miserable with my griping and inundating Tim Leek and Jeff Rowberg with ever-more-desperate cries for help, I finally decided that I was simply going to have to start over from scratch with my robot program (some 3000+ lines of code in the main program and over a dozen custom libraries), and just build it up piece by piece until everything works again – groan.   It’s not like I don’t have backups and wasn’t using revision control – I do and I was; it’s just that the programs that compiled cleanly back in September are generating warnings and errors now, and everything I do makes the problem worse!

Since the original problem seemed to be related to the library that runs the DFRobots MPU6050 module, I decided to start there.   After struggling up the learning curve on the Arduino IDE, I also decided I would make sure that each program step would compile cleanly in  both the VS/VM and Arduino IDE’s before proceeding to the next step.   I reasoned that since the Arduino IDE is much pickier about library locations and names, I could use it as sort of an editorial check on VS/VM; if it works in the Arduino IDE, the VS/VM setup will have  no problem.

For the DFRobots MPU6050 6DOF IMU module, I started with Jeff Rowberg’s ‘MPU6050_DMP6.INO’ example program buried way down in the ‘i2cdevlib-master\Arduino\MPU6050\examples\MPU6050_DMP6’.   According to the i2cDev ReadMe, I could either put the entire i2cDev-master folder in Arduino\Libraries and let the linker figure it out, or just put the required files in the project (solution folder for VS/VM, ‘sketchbook folder’ for Arduino IDE) folder.   I elected for the latter (local files) option, as I was at least a little suspicious that part or all of my original problem was caused by the compiler/linker loading from the wrong library folder in the i2dDev folder tree.   In addition, I completely removed the i2cDev folder tree from my PC and re-downloaded it from GitHub, placing it in a completely unrelated folder so that neither environment could possibly find it.   Then I copied the required header/.cpp files from the hidden i2cDev folder tree into the project folder.   In VS/VM I created a project called ‘MPU6050_DMP6_Example’ and copied the Arduino versions of I2Cdev.cpp/.h, MPU6050.cpp/.h, MPU6050_6Axis_Motion.h, and helper_3dmath.h into it. Then I started working to get this project to compile both in the VS/VM & Arduino IDE’s.

I’ve now gotten it to compile and link in the Arduino IDE (albeit with the same warnings I started with just before I went down the rabbit hole into header file hell).   However, I can’t get it to compile in VS/VM – it blows a whole bunch of errors of the form – apparently one error for each MP6050 function)

These errors proved impossible to correct, and nobody on either the Arduino or Visual Micro forums seemed to be able to help.   Finally in desperation I uninstalled and re-installed the Visual Micro extension to VS2017, and  that didn’t solve the problem either – exactly the same behavior.

So, last night I uninstalled VS2017 entirely from my system, and deleted the entire contents of the temp folder being used for temporary compile files.   On my system this was  C:\Users\Frank\AppData\Local\Temp.

03 February 2019

This morning I reinstalled VS2017CE and, using the Tools & Extensions menu, reinstalled Visual Micro. I left everything pretty much at the default settings (including the IDE selection and IDE location entries).   The only thing non-standard with the setup was the inclusion of ‘https://raw.githubusercontent.com/sparkfun/Arduino_Boards/master/IDE_Board_Manager/package_sparkfun_index.json’ in the ‘Optional additional boards manager urls’ field.   This was apparently left over from my previous installation.   I’m not worried about this particular setting, but it does indicate that not everything about the previous incarnation of Visual Micro was actually removed from my system.

After installing VS/VM, I ran through a few of my simpler projects, and so far they have all compiled w/o problems (or had understandable and easily fixable problems).   I also compiled each program in the Arduino IDE,  taking care to follow the Arduino IDE restrictions (no “-master” in library folder names, exact capitalization, etc).

  • BlinkTest.ino – very simple, no #includes
  • ClassTest.ino – Very simple class construction project – no #includes
  • DigitalScale.ino – Several #includes, including the HX-711 load cell library
  • StepperSpeedCtrl – uses   #include <Stepper.h>
  • AdaFruit_BTLE_UART_Test.ino – uses 7 different library #includes
  • Arduino_IMU6050_Test4.ino – uses i2cDev and MPU6050 libraries, along with SBWire, elapsedMillis, and PrintEx.   this compiled OK, but with the same warnings (overrun & ‘one definition rule’) as when I first started this odyssey.   Fortunately, that’s all that happened – I didn’t get the ‘(.text.startup+0x1e4): undefined reference to MPU6050::initialize()’ error – yay!   This program also compiles in the Arduino IDE, with the same exact warnings.   So, it appears I may be back where I started on this odyssey, with a program using the MPU6050 libraries that compiles OK but with one   understandable/fixable warning (the overrun warning) and one mystery warning (the ‘one definition rule’ warning)
  • MPU6050_DMP6_Example:   spoke too soon!   This program blows the same  ‘(.text.startup+0x1e4): undefined reference to MPU6050::initialize()’ errors as before in VM, but compiles fine (albeit with the same two warnings as always)   in the Arduino IDE.
  • Arduino_IMU6050_Test4:   This is a program I created some time ago, and I found that it compiles/links fine (stil with the overrun/ODR warnings), both VS/VM and Arduino IDE

In desperation, I decide to create a completely new Arduino project in VS/VM, copy the ‘known-good’ code from  Arduino_IMU6050_Test4 into it, and then start hacking it down to the point where it fails.   Surprise surprise, when I did this, the new project (UnDefTest2) failed right away in VS/VM, blowing LOTS of linker errors!   Moreover, it compiled/linked fine in Arduino IDE – how could this be?   There MUST be something different about the VS/VM environment between  Arduino_IMU6050_Test4 and UnDefTest4 – but what?   After putting the two projects up side-by-side (this is where a dual monitor setup comes in REAL handy), I finally twigged to the difference; in the ‘working’ version, the local header/cpp files had been ‘added’ to the project’s ‘Header Files’ and ‘Source Files’ folders via the Solution Explorer (right-click on the folder icon, select ‘Add Existing…’, select the desired files, click OK). As soon as I added the relevant files to the UnDefTest4 project, it compiled/linked fine – YAY!!

I could not believe what I was seeing!   For some reason, VS/VM refused to process header/cpp files in the same folder as the .INO file, even though I had carefully checked the ‘Local Files Override Library Files’ option in the ‘Vmicro->Compiler’ menu.   At the same time, the Arduino IDE  always searches the local folder before anything else, so simply placing the relevant files in the local folder does the trick.   The fact that Visual Micro requires an additional (and non-intuitive) step for this boggles the mind.

04 February 2019

OK, when I started all this foolishness I was trying to find out (and fix) whatever was causing the ‘One Definition Rule’ (ODR) violation warning I was getting on all my programs that used the MPU6050.   I really  really hate warnings, and I was determined to get to the bottom of this, and I finally did!

The ‘one definition rule’ (ODR) warning is caused when the compiler/linker sees code that can produce two different definitions for the same object. If that can happen, EVER, then an ODR violation warning is issued. Believe it or not, that is exactly what happens when the compiler processes MPU6050.H – it sees that there are some conditions for which two different descriptions of the MPU6050 class could exist – and says “no no”. The relevant portion of the class definition is shown below:

When the compiler sees these lines, it says to itself; “Hmm, the way this is written, it is theoretically possible for there to exist  two different versions  of ‘Class MPU6050’, one with just two private member variables (devAddr & buffer) and one with four (with the addition of dmpPacketBuffer & dmpPacketSize), and this is a strict no-no; I’m going to whack that programmer across the head with an ODR violation!”

If the #ifdefined and #endif lines are commented out – the ODR warning goes away

Now, I suspect nobody has ever had a problem with this issue, as it would be very unlikely to have a project where BOTH versions of MPU6050 are in play, but of course the compiler doesn’t see it this way.

On a slightly different, but related subject, the OTHER warning was due to a potential integer overrun in the dmpGetGravity() function, as shown below:

if the last line of the above calculation is changed to (note the addition of ‘UL’)
– (int32_t)qI[2] * qI[2] + (int32_t)qI[3] * qI[3]) / (2 * 16384UL);
then this warning goes away as well.

Mission accomplished!   I now have MPU6050 code that compiles without errors (or warnings!!) in both the VS/VM and Arduino IDE environments.   Along the way I learned more than I ever wanted to know about ‘One Definition Rule’ violations and the innards of both the VS/VM environment and the Arduino IDE.

to paraphrase a quote attributed to Abraham of Lincoln:

I feel like the man who was tarred and feathered and ridden out of town on a rail. To the man who asked him how he liked it, he said: “If it wasn’t for the honor of the thing, I’d rather walk.”

 

Stay tuned,

 

Frank

 

 

 

Digital Tension Scale, Part V

posted 09 January 2019,

In my previous post on this subject I described the components I planned to use for my Digital Tension Scale project, and also the design for a box that would mount directly on the S-shaped load cell assembly.

This post describes the ‘final’ (to the extent than anything I do can be considered final) assembly of the completed system into my 3D-printed housing, and the results of some initial battery-powered testing.

As shown in the following photos, the major components (Teensy 3.2 microcontroller, HC-05 Bluetooth Module, HX-711 load cell amp/A-D, and Sparkfun ‘Basic’ LiPo Charger) were mounted on perfboard, which was then in turn attached to the box lid via a set of custom-printed standoffs.   A short piece of ribbon cable connects the Teensy to the LCD display.   The general idea behind this physical layout is to allow easy access to the electronics for troubleshooting, and to allow for battery charging and/or Teensy programming without having to open the box.

3D-printed housing. Note the glow from the Sparkfun charger LED

View of housing showing the access port for supplying USB power and/or programming the Teensy

View with the lid and electronics board removed. The LCD display is face down in its cutout

Exploded view showing all system components

Showing connections from load cell to HX-711

Top view showing how load cell attaches to the housing

Closeup showing load cell lead routing and power/programming port

End view showing charging port

 

Preliminary Testing Results:

At this point I have everything running on battery power only inside the box, and I have been able to demonstrate remote data capture on my PC using the HC-05 BT link.   The following image shows the data taken from my rowing machine, and a short video demonstrating the setup.

Complete Code:

Here is the complete Teensy 3.2 program as it stands today.   As you can see if you inspect the code, I have the Teensy low-power stuff turned OFF for the moment (that’s the purpose of the ‘#define NO_SNOOZE’ statement.

 

Schematic:

Future Work:

  • Do some more work to reduce power consumption to extend the battery life.   I got the ‘Snooze’ feature to work on the Teensy, but that only reduces the Teensy’s power consumption; it does nothing directly to reduce the power consumption of the other components.   I tried using a MOSFET to turn the HC-05 BT module on & off, and found this to be impractical, as then the module loses its connection to the remote data collection device.    I have also tried removing power from the LCD module, but that also turned out to be problematic.

Stay tuned,

Frank

 

 

Digital Tension Scale, Part IV

Posted 25 December 2018

In my copious free time I have been refining the design for a low power battery operated tensionometer.   In my last post on the subject, I had described the components I had planned to use, and in the ensuing weeks I have been working on implementing this design.   There are several challenges in this project:

Bluetooth Link:

There are a huge number of Bluetooth products out there in the Maker-verse, with varying degrees of Arduino support, and widely varying performance characteristics.   To add to the confusion, there is ‘regular’ Bluetooth and the more recent  ‘BLE’ (Bluetooth Low Energy) which are completely incompatible with each other.   As I now understand it, BLE is synonymous with Bluetooth 4.0+ (the iPhone 4S was the first smartphone to implement the new standard).   However, the most common product in use in the Arduino world seems to be the venerable HC-05 ‘regular’ Bluetooth module, available from your local grocery store (well, not quite, but from almost everywhere else!) for not much more than a few pennies

I had no previous experience with BT modules, so this part of the project took some time, and was the last major part to be accomplished.   After receiving my HC-05 modules from China, I used this tutorial to get started.   The real challenge for this part of the project wasn’t getting the HC-05 hooked up to the microcontroller – it was sorting through all the layers of BT-related settings on my Win 10 laptop to pair with the HC-05 device and determine which serial port did what.

  • In Windows 10, I used the ‘Bluetooth and other devices settings’ panel (Settings -> Devices -> Bluetooth and other devices) to find and pair to the HC-05.   The device shows up as ‘HC-05’ and the default pairing password is “1234”.
  • When the HC-05 is paired it automatically sets up at least two serial ports that show up in device manager as ‘Standard Serial over Bluetooth’ ports, as shown below.   However, only one of these ports is actually usable for two-way communication, and it isn’t clear to me why, or how to tell which is which;   I had to experiment with each available ‘SSoB’ port to figure out which to use  (so far, it seems like the highest-numbered port is the proper one).
  • After the HC-05 is paired and the com ports are set up, then any serial terminal app (I used RealTerm) can be used to communicate between the PC and the microcontroller via the HC-05.
  • On the microcontroller (I used a Teensy 3.2 with multiple hardware serial ports), I wired the HC-05 to Serial1 leaving Serial (Serial0) available for normal communication between the Teensy and my Visual Studio 2017 Community Edition/Visual Micro add-on for Arduino development platform.

Physical Layout:

The original idea behind this project was to create a self-contained battery-operated digital weight scale that could display weight values on a local display, but could also stream the data live to a remote recording station like a laptop or smartphone. The ‘self-contained’ part requires that all the electronics be mounted on the S-shaped load cell assembly itself, and to that end I designed a housing that connects to the two bolts that hold the arms of the load cell.   The idea is that all the electronics save the LCD display and the battery will be mounted to the underside of the box lid so that servicing would be easier.   Also, by mounting everything to the lid, I can make cutouts for the charger and Teensy USB connectors for easy charging and reprogramming.   After several iterations in TinkerCad, I came up with the following design

Looking up at the underside of the box lid, showing all modules except the battery and the LCD display

Showing the top of the lid with the mounting bracket for the load cell

Isometric view with transparent box walls. The LCD display module is under the battery.   Note the cutouts for the charging and programming USB-C connections

Module Integration:

I had previously tested each module individually, but hadn’t had all of them working at the same time.   I had tested the HC-05 with an Arduino Mega, and I had tested the load cell with both a Sparkfun Pro Micro and with a Teensy 3.2, and I had tested the Nokia LCD display with a Teensy 3.2, but I hadn’t put everything together.   So I wired everything up on my half-size ASP plugboard and got it all working together with a simple program (included below) that exercised the LCD Display, the load cell, the BT module, and the battery charger, as shown in the following photos

RealTerm Serial Terminal Program showing load cell readouts collected wirelessly via the Bluetooth HC-05 modules

HC-05 Bluetooth, HX-711 Load Cell Amp, Sparkfun Charger, and Teensy Microcontroller modules integrated together. Note disconnected USB cable showing that the circuit is running on battery power. The scale is currently measuring 1.8 liters of water in the suspended plastic bag (note the ‘1.8 Kg’ reading on the LCD display)

 

Software:

The software used for the above integration tests is a reasonably complete sketch for day-to-day use of the digital weight scale.   It displays the measured weight on the LCD display, and also sends it to the USB serial port for display on a directly connected PC, and to the HC-05 Bluetooth module for display/capture via a BT-connected laptop or smartphone.   This program is shown below:

However, this program depends on the proper calibration of the load cell, which I have been doing with a separate sketch (also included below):

What I need to do now is to combine these two programs into a single sketch with ‘operating’ and ‘calibration’ modes.   My calibration program already does this to some degree, as it waits 5 seconds on startup for the operator to send the ‘y’ key via the direct-connect serial port. If the ‘y’ character is detected within this window, then the program starts the calibration sequence; otherwise it starts taking measurements as normal.   This behavior needs to be expanded somewhat in that it should accept a calibration command either through the direct-connect serial port (Serial) or via the BT port (Serial1).

Low Power Operation:

I have already done some experimentation on low-power operation of the Teensy 3.2, using Colin Duffy’s fine ‘Snooze’ library, and have determined that I can easily drop the Teensy’s operating current from around 20-30 mA to about 1-2 mA by putting it to sleep during periods of load cell inactivity.   Assuming I get the full 2500 mA hours out of the battery, then I can expect something like 1000 hours or about 40 days between recharges.   However, more work needs to be done to get the low power mode fully operational.

 

Stay Tuned!

Frank

 

Digital Tension Scale, Part III

Posted 25 November, 2018

Over the Thanksgiving weekend I had a chance to do some more work on my digital tension scale project.   My wife and I drove to St. Louis to visit our kids and grand-kids, and its a 6-hour drive each way.   I make sure we have an audio book going for my wife, and as a consequence I get 6 hours of (mostly) uninterrupted geek time to work on things like this project.

As I mentioned in Part II, my goal is to construct a battery-operated tensionometer that can be mounted directly on the dual-hook S-shaped tension block, as defined by the features delineated in Part II.   After doing some more web research, I came up with the following possible components for the system:

Display:

Apparently, the Nokia 5110 84×48 pixel monochrome LCD display used in prehistoric times as the display in Nokia cellphones has found a second career as a simple, low power display for battery-operated devices like the one I envision.

 

Nokia 5110 Monochrome LCD display

Battery & Battery Charger:

The LCD display will operate quite nicely from 3.3V, so as long as I can come up with a 3.3V micro-controller (like the Teensy 3.2) and a small, capable LiPo charger, I should be in business.   For this component, I plan to use the Adafruit PowerBoost 1000C and something like the 3.7V 2500 mAh battery as shown below

Adafruit PowerBoost 1000C single-cell LiPo charger

3.7V 2500 mAh LiPo battery

 

I don’t think I’ll need the 5V boosted output from the PB1000C and the entire thing may be a bit of overkill for this project, but I had them hanging around from a previous project, so…

MicroController:

For this I plan to use one of  Paul Stoffregen’s magical Teensy 3.2’s.   Again this is probably  way overkill for the project, but…

Wireless Connection:

This component is the one for which I have the least understanding and confidence.   I currently use a Pololu Wixel for wireless serial comms and programming with my autonomous robot, but I haven’t figured out how to use it with a Teensy, and I thought maybe there were better solutions out there by now anyway.   So, after some more web searching I found that many ‘makers’ are using the HC-05/6 Bluetooth modules for this purpose.   Hopefully with this module I’ll be able to use a Bluetooth connection from the tensionometer to my laptop or even my cellphone to perform calibrations and collect real-time tension data.

Combining all these with the HX-711 load cell amplifier, I came up with the following system schematic.

 

Stay tuned!

Frank

 

Digital Tension Scale, Part II

Posted 17 November 2018

In a previous post on this subject, I described a digital tension scale arrangement using a load cell incorporated into a 2-hook tension measurement setup, interfaced to a common HX711 ADC board, and a Sparkfun Pro Micro ATMega32U4 microcontroller.

After (finally) getting the setup to work and getting some initial real-time tension measurements on our rowing machine, I decided to see if I could improve the usability of the overall system, with the goal of constructing a fully automatic battery powered tension scale, with the ability to communicate wirelessly to my PC for data acquisition and programming.

Desired Features:

  • Easy calibration:   The system should offer a calibration option when connected to a PC/Smartphone, but otherwise should use the last calibration data (stored in EEPROM) for measurements.
  • Battery operated: This implies a low-power mode to extend battery life if using primary batteries, and/or a charging arrangement if using secondary (rechargeable) cells.
  • Local display:   A low power display (LCD?) for local tension measurement display
  • Wireless capability:   A wireless connection to a PC or smartphone for real-time data acquisition.
  • Small size:   I would like to mount the entire system on the 2-hook tension measurement assembly itself.

Easy Calibration:

The calibration procedure associated with my previous post was a PITA, to say the least, so I decided to attack this problem first.   I modified the software to allow the user to skip calibration entirely or to calibrate the tensionometer automatically using any known weight.   The new software is shown below:

When run on my PC, this produced the following output

Here are a couple of photos showing the calibration process with my current setup

‘Tare Weight’ configuration, used to zero out the contribution from the bucket

Calibration configuration. Bucket now contains 1.8L water (1.8 Kg)

After calibrating, I tested the the system by measuring tension vs time with an elastic strap I am using as part of my rotator cuff surgery rehab, as shown in the following short video clip

The data from this experiment was captured on my PC and plotted in Excel, as shown below

Tension vs time for the orange elastic strap shown in the video

Then, at the request of my physical therapist, I measured the real-time tension for single & double orange straps, and single/double green straps, as shown below

Single and double green strap tension vs time

Single and double orange strap tension vs time

 

Stay tuned!

Frank

 

 

Digital Tension Scale

Posted 27 October 2018

I recently underwent rotator cuff repair surgery on my left (dominant) shoulder, and am now starting the rehab process.   My PT person was adamant that I not re-start my normal rowing routine for at least six weeks post-op, due to the possibility that I could re-tear the tendon.   This made me curious as to what the tension really was on my arms when rowing, so I decided to try and build a digital dynamic tension sensor, capable of plotting rowing strap tension in real time.

To start, I had to educate myself on the world of strain gauges and load cells, and what the differences are.   As I came to understand, what I wanted was a  load cell configured for tension measurement, with a  strain gauge as the active sensing element in the load cell.   So, I started searching for load cells, and was immediately inundated with ‘too much information’.   This deluge is certainly better than the old days where I had to search through paper (really, no internet!) magazines and catalogs, but at least you didn’t have to worry about overload headaches! ;-).

Anyway, I found this item ‘Degraw 40Kg Tension Load Cell and HX711 Combo Pack Kit‘, as shown in the screenshot below

Amazon catalog item for Degraw load cell

This looked perfect for my intended use, as I could hook one end onto my rowing machine strap, and connect some sort of handle to the other end.   Now all I had to do was figure out how to hook the thing up and get it to work.   Fortunately Degraw also provided a sketch of the hookup using an Arduino Uno, so that part was pretty easy.

Degraw-provided hookup diagram

After a bit more research, I found a nice HX711 library by bogde and some example programs, and got the whole thing to work using an Arduino Mega 2560.   Once I got a program running with some preliminary (but believable) results, I started thinking about how I was going to manage the physical aspects of hooking this assemblage to the rowing machine and recording dynamic tension.   I couldn’t really just let the HX711 board hang by the strain gauge wires while connected with jumpers to the Mega board, as the #28 strain gauge leads would surely break.   So, I came up with the idea of somehow attaching the HX711 board and a small microcontroller to the load cell assembly, and then connecting the whole thing to my laptop with a USB cable.   Hopefully the USB cable would be long enough to allow full extension of the rowing machine strap so I could collect full rowing cycle data.

After some digging around in my parts cabinets, I came up with two candidates for the ‘small microcontroller’ part of the plan; a 3.3V Teensy 3.2, and a 5V/16MHz Sparkfun Pro Micro.   I tried the Pro Micro at first, and almost immediately went down the rabbit hole (my term for getting lost in some technical wonderland without a clue how to get back) trying to figure out how to program the device – a challenge due to the way it handles com ports through the USB connector (it actually implements two different ones, depending on whether the boot loader or the user firmware is running – yowie!).   After climbing my way out of the rabbit hole, I decided to try the Teensy 3.2 instead, as I familiar with it from several other projects.   With the Teensy, I got a test program running and started taking data with known weights attached to the load cell. The way I did this was to suspend a plastic bucket from the load cell, and poured water into the bucket one liter (1Kg) at a time while recording data.   This was successful because I got good data, but unsuccessful because the data didn’t make much sense, as shown in the plot below

Results of pouring 1L (1Kg) water at a time into bucket suspended from load cell, using a 3.3VTeensy 3.2

As can be seen, the data was anything but the stairstep function I was expecting to see.   At this point I wasn’t sure if I had a hardware problem or a software problem, or something else entirely, so I sent an email to Degraw Product support with the above plot attached, asking if they had any insight into the problem.   Amazingly, they replied almost immediately, and offered to send me another load cell unit gratis so I could eliminate their hardware as the cause of the problem.   Although I was quite pleased with their offer of support, I thought maybe the 3.3V supply of the Teensy 3.2 might be causing the non-linearity (the HX711 advertises 2.7-5V operation but the lower voltage might be causing output linearity problems).   So, I tried again with the Sparkfun Pro Micro, and this time I managed to make the programming magic work. Then when I did the same test as above with the 5V Pro Micro instead of the 3.3V Teensy, I got the plot shown below.

Tension vs time plot created by pouring 1L (1Kg) of water at a time into bucket suspended from load cell, using Sparkfun 5V Pro Micro

So, now that I had the software and microcontroller problems solved, I started working on the mounting issue.   After a few minutes in TinkerCad and some quality time with my PowerSpec 3D PRO 3D printer, I had a mounting platform that clipped onto the two vertical rods in the ‘S-shaped’ tension load cell, as shown in the images below.

Reverse side of assembly, showing mounting plate clips attached to load cell vertical members

Sparkfun Pro Micro and HX711 board mounted on load cell

After getting all this set up, it was time to take some real data. Since I was still in the ‘no rowing’ zone after my surgery, I enlisted my lovely wife to do the honors while I recorded the data.   We have an Avari magnetic rowing machine, which thankfully doesn’t make much noise.   I recorded a total of 12 rowing cycles on two different ‘wave’ settings (I’m still not sure what the different ‘wave’ settings mean) at the lowest tension level, with the results shown below

As shown in the above plot, the peak tension reading was around 18Kg (about 40 lbs).    I’ve included a short video of the test below.

IR Modulation Processing Algorithm Development. Part IV

Posted 07 June 2017

I seem to have grabbed a tiger by the tail in my continued collaboration with my old friend and mentor John Jenkins on this project to extract the estimated magnitude of a square-wave modulated IR signal in the presence of ‘flooding’ from ambient light sources.  The whole thing started out innocently enough when John was at our house a few weeks ago and I showed him my spiffy wall-following autonomous robot Wall-E2.  I mentioned at the time that I was having some trouble getting Wall-E2 to successfully home in on an IR beam to mate with a charging station, and he suggested that a square-wave modulated signal might do the trick, as it would allow me (and Wall-E2) to discriminate between the IR beam and the other interfering signals.

I should have known right then that I was in trouble, as John had that look in his eyes – the one that says “Hmm, that’s an interesting problem…..”.  I have seen that look any number of times over the 40 years or so that I have known him, and it  always results in me tearing up my previous work (and my previous assumptions) and starting over again.  The only saving graces in all this are a) It’s a  lot of fun when it happens, b) I’m retired now so I don’t care how long it takes or how much work is involved, and c) I’m a masochist at heart! ;-).

So, here we are.  John and I have been exchanging emails over the last week or so, discussing the ‘best’ way to solve this problem.  One of the first things that happened was John started blabbering about ‘Degenerate N-path Bandpass Filters’, and I had no idea what he was talking about (I  hate it when that happens!).  Of course I couldn’t tell John that I was completely ignorant, so I made the appropriate noises and raced to educate myself before he discovered my ignorance.  I found a neat video that explained the technique in a way that even I could understand. The video focused on implementing the filter technique in CMOS hardware, but the general technique is applicable to the Arduino world as well, as long as the modulation frequency is low enough to accommodate the lower clock speeds.

John came up with a brilliant graphical illustration of the algorithm used for implementing a N-path band-pass filter.  Even more amazing, he did it in Excel, so the whole damned thing is live – wow!   As shown in the ‘dead’ version below, there are two ‘channels’ – an in-phase (I) and quadrature (Q) ‘channel’.  Both channels start with a group of samples spanning exactly 1/4 cycle  that are summed together to form a ‘sample group’.  In the I channel, each group is given a sign in the sequence ‘+’, ‘+’, ‘-‘, ‘-‘, and in the Q channel this same group is given a sign in the sequence  ‘-‘, ‘+’, ‘+’, ‘-‘.  The four groups in each channel are summed to form the I & Q ‘cycle sums’, and each such sum is added to a N-element circular buffer (one each for the I & Q channels).  The running sum of all elements in each circular buffer are the band-pass filtered I & Q components of the input signal.  The final result is formed by adding the absolute values of the I & Q running sums.

Loader Loading...
EAD Logo Taking too long?

Reload Reload document
| Open Open in new tab

Download

Page 1 of the above document shows the sign sequence for the in-phase and quadrature ‘channels’.  Note that the input to both channels is the same ‘sum-of-5-samples’ group, but the sign changes in a different sequence for the I & Q ‘channels’.  Each 1/4 cycle of the input signal is treated in the same fashion.  The horizontal time scale is specific for the planned 500Hz modulation signal.

Page 2 depicts the real heart of the algorithm, as it shows how each ‘sample sum’ (1/4-cycle sum-of-samples) feeds into the final circular summing buffer.  Four ‘sample sum’ groups (comprising one signal cycle) are summed into a ‘cycle sum’ for both the I & Q channels, and each such ‘cycle sum’ is loaded into the corresponding I or Q circular summing buffer.  The latest ‘cycle sum’ element replaces the oldest element, and therefore the  circular summing buffer for each channel represents a N-element  band-pass filter, where N is the length of the circular summing  buffer.  This diagram has a  lot of information in it, so it can take some time to get comfortable with it (it did for me, anyway).

Some other random details:

  • This diagram is set up for a 64-element circular summing buffer, but shorter or longer is OK too.  The band-pass filter bandwidth is inversely proportional to the buffer length.
  • The times noted in the labels at the top are for a presumed 500Hz modulation, with a period of 2000uSec.
  • The notation m = n/5 comes from the fact that (in this particular implementation) there are 20 samples/cycle, which means that each 1/4 cycle group of contains 5 samples.  For 64 elements, each comprising 1 entire cycle of samples, there are 64X20 = 1280 samples. So in this implementation, m/4 = 64 ==> m = 256 = n/5 ==> n = 1280 total samples represented in the circular buffer at any one time.

Page 3 shows the contents of one of the two circular buffers, after each complete ‘rotation’ of the buffer.  After 64 complete cycles the buffer is full, as shown in the ’00’ column.  64 cycles later, the buffer contents are as shown in the ’01’ column, and so on.  The text below shows the procedure for start and running the buffer.  Note in this text that “next cycle value” and “current cycle value” are the same thing, and the “input pointer” and “output pointer” variables are incremented  MOD N, (N = 64 here).

Pages 4-12 show the input signal, the I/Q channel values, and the I & Q circular buffer running sums for different phase offsets between the transmitted and received signals (transmit & receive frequencies are the same in all cases).  Page 4, for instance, shows the situation for the receiver perfectly in phase with the transmitted signal.  If we look at the I & Q summed values at integral cycle boundaries (-1, 0, +1, etc) we see that the I signal is at 128, and the Q signal is at 0, giving abs(I) + abs(Q) = 128 + 0 = 128.  If the same calculation is performed for all the other phase relationships (i.e. pages 5-12) at the same points, the answer will always be the same, i.e. 128.  This shows that the band-pass filter implementation works as intended, even without any phase-locking requirement.  This treatment assumes that the Tx & Rx clocks are identical, so that the 1/4-cycle sample groups span exactly 1/4 cycle.  Any difference between the two clocks will show up as ripple on the results, proportional to the difference between the two frequencies.  This error term should not be significant for the typical single-board-computer crystal-controlled clocks.

I sent this post off to John for approval, and got the following email back, with some additional clarifying comments:

  • n-path filter is degenerate (my term) because it only uses two paths — I and Q — vice many as shown in video (very good video btw,learn a lot from it too)
  • possible topics for more detail  related to last paragraph (but beware that perfect is enemy of good enough or something like that):
    • output ripple
      • frequency ==> difference between tx and rx freqs should be very small
        • only spec found was +/-50ppm, so 100ppm delta worst-case w/o aging, etc
        • 100ppm at 500Hz ==> 0.05Hz
        • believe ripple will be twice difference in freq because of this being a fullwave synchronous rectifier (not verified), so 0.1Hz
      • magnitude  ==> variations due to sample(s) from one 1/4-cycle group getting into an adjacent 1/4-cycle group as tx and rx phases slide past each other at <<0.1cycle/sec
        • non-50/50 duty cycle is most likely cause
        • with 5-samples per 1/4-cycle group normalized ripple could vary from 1.0 to 0.8;
          • not a problem for the relative  outputs of two sensors for steering
          • could be important in cases where absolute magnitude is needed because bpf  (eg, 10Hz) would not average signal for long enough to average out ripple this slow ((eg, 1 cycle every 10sec)
          • increasing # of samples per 1/4-cycle group will reduce this effect
    • no antialias filter is used so # of samples  per 1/4-cycle group needs to be significantly higher than nyquist requirement for 2/bw to avoid issues (unless clock rate is servo’d to correct freq by monitoring and setting Q value to zero)

More to come – stay tuned!

Frank