Options for Improving Timestamp Precision

After a quick test determined that my Arduino sketch will be dealing data changing at a faster rate than 1kHz, I switched the timestamp query from calling millis() to micros(). As per Arduino documentation, this change improved time resolution by 250 from 1 millisecond precision to 4 microsecond precision. Since I had time on my mind anyway, I took a research detour to learn how this might be improved further. After learning how much work it’d take, I weighed it against my project and decided… nah, never mind.

Hardware: ATmega328P

A web search for ATmega328P processor programming found good information on this page Developing in C for the ATmega328: Marking Time and Measuring Time. The highest possible timing resolution is a counter that increments upon every clock cycle of the processor. For an ATmega328P running at 16MHz, that’s a resolution of 62.5 nanoseconds from ticks(). This 16-bit counter overflows very quickly (once every 4.096 milliseconds) so there’s another 16 bit counter ticks_ro() that increments whenever ticks() overflows. Together they become a 32-bit counter that would overflow every 4.47 minutes, after that we’re on our own to track overflows.

However, ticks() and ticks_ro() are very specific to AVR microcontrollers and not (easily) accessible from Arduino code because that kills its portability. Other microcontrollers have similar concepts but they would not be called the same thing. (Example: ESP32 has cpu_hal_get_cycle_count())

Software: Encoder Library

Another factor in timing precision is the fact that I’m not getting the micros() value when the encoder position is updated. The encoder position counter is updated within the quadrature decoding library, and I call micros() sometime afterwards.

timestamp,position,count
16,0,448737
6489548,1,1
6490076,2,1
6490688,5,1
6491300,8,1
6491912,12,1
6492540,17,1
6493220,21,1
6493876,25,1

Looking at the final two lines of this excerpt, I see my code recorded encoder update from position 21 to 25 over a period of 6493876-6493220 = 656 microseconds. But 6493876 is only when my code ran, that’s not when the encoder clicked over from 24 to 25! There’s been a delay on the order of three-digit microseconds, an approximation derived from 656/(25-21) = 164.

One potential way to improve upon this is to add a variable to the Encoder library, tracking the micros() timestamp of the most recent position update. I can then query that timestamp from my code later, instead of calling micros() myself which pads an unknown delay. I found the encoder library source code at https://github.com/PaulStoffregen/Encoder. I found an update() function and saw a switch() statement that looked at pin states and updated counter as needed. I can add my micros() update in the cases that updated position. Easy, or so I thought.

Looking at the code more closely, I realized the function I found is actually in a comment. It was labeled the “Simple, easy-to-read “documentation” version 🙂” implying the actual code was not as simple or easy to read. I was properly warned as I scrolled down further and found… AVR assembly code. Dang! That’s hard core.

On the upside, AVR assembly code means it can access the hardware registers behind ticks() and ticks_ro() for the ultimate in timer resolution. On the downside, I don’t know AVR assembly and, after some thought, I decided I’m not motivated enough to learn it for this particular project.

This was a fun side detour and I learned things I hadn’t known before, but I don’t think the cost/benefit ratio makes sense for my Canon MX340 teardown project. I want to try some other easy things before I contemplate the harder stuff.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Captured CSV and Excel worksheets are included in the companion GitHub repository.

Motion Decoder Timestamp Switching to Microseconds

I want to decode motion reported by the rotation quadrature encoder inside a Canon Pixma MX340 multi-function inkjet. My first attempt comparing encoder count differences proved to be a failure, so I will have to incorporate those lessons before trying again.

The first lesson is that making decisions on changes between two iterations of a loop makes the code dependent on microcontroller execution speed. This is an unreliable metric because behavior would change if I compile it to run on different hardware. And even if the hardware is unchanged, doing different things within an iteration (like printing data out to serial port) would consume more or less time than than a different iteration. For some projects, such variability doesn’t matter much, but it has proven to matter greatly here.

To solve the loop iteration-to-iteration variability problem, I need to switch to a system where calculations are based on a measure of time. Arduino uses millis() for time stamps in many examples, so I’ll start with milliseconds as my time stamp against encoder readings. And for reference, I’ll also count how many loop iterations were spent at each encoder position. My earlier failure told me this number is occasionally greater than one even when the system is moving, I wanted to know more.

Here’s the output for machine startup:

timestamp,position,count
0,0,878490
10604,2,95584
11758,4,1871
11782,6,341550
15905,7,1
15906,8,1
15906,10,1
15907,13,1
15907,15,1
15908,19,1
15908,23,1

[...]

As expected, a lot of time was spent near position zero as the machine powered up. But as soon as the paper feed motor started turning in earnest, encoder position count started changing more dramatically changing once per encoder poll iteration. Critically, both were changing faster than the millisecond time stamp counter. Position 8 and 10 were both stamped with 15906. Position changed from 13 to 15 within the same millisecond, etc.

Now I know the Arduino sketch is running fast enough to keep up at some speed faster than 1 kHz, which I wasn’t sure about before. This is actually excellent news. The timestamp issue is thankfully easy to resolve, because Arduino framework also provides micros() so it was easy to switch to microseconds for my time stamps.

timestamp,position,count
16,0,448737
6489548,1,1
6490076,2,1
6490688,5,1
6491300,8,1
6491912,12,1
6492540,17,1
6493220,21,1
6493876,25,1

[...]

That looks much better. It’s not literally 1000 times better because, as Arduino documentation stated, micros() doesn’t always deliver microsecond resolution. For ATmega328-based Arduino boards like the Nano I’m using, the resolution is limited to four microseconds when running at 16MHz. And looking at my output, the timestamps are indeed all multiples of 4. Still a huge improvement but it made me wonder: can I do even better?


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Captured CSV and Excel worksheets are included in the companion GitHub repository.

Motion Decoder Fail: Zero Delta Between Two Samples

I have wired up an Arduino Nano to a retired Canon Pixma MX340 multi-function inkjet, reading the position reported by a rotary motion quadrature encoder in its paper feed motor gearbox assembly. I want to learn how that mechanism worked, and thought it would be helpful to process raw encoder data into a list of movements undertaken by the mechanism.

Looking at the graph I plotted earlier, this looked pretty straightforward. I added code in my Arduino sketch to poll encoder value in a tight loop and, when two consecutive readings have zero delta between them, treat that as a stopped segment between two movements.

This approach did not work at all! But its failure taught me more about this system’s behavior.

First lesson: as the print carriage moved around, it jostles the whole frame enough for this paper feed encoder to bounce one or two counts off target. I don’t know if it’s impossible/impractical to move this motor system by a single encoder position, or if the control system just decided it wasn’t important enough to bother. Either way, there are small drifts that were too small for me to see in my earlier graphs but would falsely break up zero-delta periods.

A closely related observation: the motor decelerates as it approached its target position, and it doesn’t always hit its target precisely by the time it coasted to a stop. (This is assuming a move of 1798 counts was intended to be a 1800 count move.)

Speaking of that coast phase, the graph shows it can take 20-30 milliseconds before everything comes to a stop. This is a problem, because the Arduino is polling encoder fast enough that it will see the same value multiple on consecutive polls. So if I’m deciding based on zero delta between two readings, it would prematurely conclude the gear train has stopped when it was merely at the very beginning of acceleration or the slow tail end of deceleration.

After one deceleration, the system might immediately start accelerating to execute another motion. One example is visible as the second and third movement in the graph above. From Arduino Nano’s perspective, the delta between two consecutive readings never reached zero between those two movements. Maybe the printer control board decided “slow enough” and started the next move before things actually stopped, or maybe the Arduino is not polling fast enough to catch that brief moment of zero rotation.

I had a laugh at how my system managed to poll encoder value at a rate that is simultaneously too fast and too slow. But the conclusion from these new observations is clear: I can’t get away with just two encoder samples and look for a zero delta between them, my code will have to base its decisions on more reliable factors.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Captured CSV and Excel worksheets are included in the companion GitHub repository.

Canon Pixma MX340 Decoder Round 2 Goals

I want to understand how the paper feed mechanism works in a Canon Pixma MX340 multi-function inkjet, starting with positions reported by its rotational quadrature encoder. Round 1 looked at raw position data polled every 10ms. This approach quickly ran into limitations, but it informed what I want to accomplish for round 2.

Seeing several moves of 1800 encoder count increments hinted this system acts as a position-based system (“Go to position X”) like a servo motor. This doesn’t necessarily exclude the possibility of occasionally acting as a velocity based system (“turn at this speed”) but I can start by focusing on the positioning aspect.

All of my observations show an acceleration to a target speed, and kept spinning at that speed until it was time to decelerate to stop at a target position. If there are any velocity-based operating modes, I haven’t seen any sign the velocity changes in response to any system behavior. I observed several different target speeds for different moves, but the speed seems to stay constant within a single move.

Given these observations, my goal for round 2 is to process encoder position to generate a list of timestamped relative positioning. A secondary goal is to also include peak velocity. I can calculate average velocity from timestamp and position. I expect average will be somewhat lower than peak due to the accelerate/decelerate stages. If there is significant deviation, I know either something went wrong in my code, or the rotational velocity varied during the move.

Here’s an example to illustrate my goal:

An excerpt from the power-up sequence, this was the first three movements. It was preceded by a long series of zeros before anything moved. Then there was a move of ~1800 counts over ~140 milliseconds. Then a short period of no motion. Then another move of ~2700 counts over ~240 milliseconds, immediately followed by a reversal of -1800 counts over 140 milliseconds before another period of stillness.

Round 1 gave me a long list of positions every 10 milliseconds. I want round 2 to give me something like:

millis,change,v-max-10ms
5151,0,0
140,1798,237
230,0,0
240,2700,148
140,-1800,-239
100,0,0

These numbers were not precisely calculated, merely to show the desired format. I want a much more concise description of motion than the position-every-10ms raw dump of round 1. This means putting more data processing logic in the Arduino. I tried the easy thing first and it did not go well, but I learned a lot from the failure.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Captured CSV and Excel worksheets are included in the companion GitHub repository.

Quadrature Decode MX340 Print

I’m fascinated by the print engine inside a Canon Pixma MX340 multi-function inkjet, there’s a lot I don’t understand but at least it felt like some level of understanding is attainable. Processing a lot of periodic encoder value reports, I saw its standby sequence was longer but not terribly more complex than the power-up sequence. But that’s not the main business of an inkjet print engine. Its business is printing!

Here’s a trace from printing a page, a process that took just under two minutes. (~110 seconds.) And wow, I see a lot of activity. The long shallow slope in the latter half of this graph is where actual printing was in progress, though the shallow slope line visible here is misleading.

Zooming in to that section, the actual motion is visible as a periodic advancement of a bit less than 1000 encoder counts, then held steady as the print carriage moved across to lay down ink. Then it advances another <1000 counts, and so on. This section appeared to be a single long shallow slope only because I had packed too many data points into a single graph and important detail has been lost. I zoomed into the graph at various locations and didn’t discover other artifacts, but I am still wary of drawing too many conclusions from visual inspection of this graph.

Despite that caveat, it’s pretty clear that preparatory work took more time than the actual printing process with ink on paper. This chart also tells me the photo interrupter sensor sampling wires are functioning correctly. Since paper is actually fed, this chart showed activity on the paper presence sensor (blue line) and the gearbox sensor of yet-unknown purpose (orange).

I think I’ve reached limits of the “dump periodic data to Excel, look at graph” analysis method. It was an interesting and easy way to make my first pass on analyzing this data, and it informs my goals for the next pass.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Captured CSV and Excel worksheets are included in the companion GitHub repository.

Quadrature Decode MX340 Standby

I’ve got an Arduino Nano connected to the internals of a Canon Pixma MX340 multi-function inkjet, specifically the quadrature encoder reporting on rotational motion of the paper feed motor gear assembly. After capturing the power-up sequence, I pressed the button again to capture the power-down standby sequence.

This standby sequence took twice as long as the power-up sequence, 18 seconds versus 9. Almost 11 seconds were spent rolling backwards relative to printing feed direction, traversing about 248,000 encoder counts. Eight times more than the backwards roll during power-up, which traversed ~31,000 encoder counts. That long rearward roll was followed by a forward roll for ~25,000 encoder counts.

During this roll, there were no paper motion and the print carriage sits in the parked position. What effect could this roll have? At the moment, my only guess is something preparing the ink cartridges to sit for a while. It would make sense for the power-up sequence to prepare the ink cartridges for use, and then for the standby sequence to prepare them for storage. Trying to figure out the details of this preparatory work is on the to-do list for later this teardown.

Besides that very long motion, there are several small movements back and forth. Looking at the data, I can see the times and duration differ between them, but each of those smaller movements traversed 1800 encoder counts. Reviewing the startup sequence, most of those small movements were also 1800 encoder counts. This is interesting. I will have to determine how many degrees of rotation corresponds to 1800 encoder counts, then look for a mechanism that corresponds to such a rotation during later disassembly. This little detail should be easier to understand than the actual printing process.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Captured CSV and Excel worksheets are included in the companion GitHub repository.

Quadrature Decode MX340 Startup

Whenever I turn on my Canon Pixma MX340 multi-function inkjet, I immediately hear motors whirring and I had always been curious: I haven’t even tried to print anything yet. What is it doing? Now that I’ve retired the machine and slowly taking it apart, I want to get some information hopefully leading to some answers. I have attached an Arduino Nano to two photo interrupter sensors and the quadrature encoder reporting position of the paper feed motor gearbox assembly. The Nano watches those sensors and report on their status every 10 milliseconds to a serial port as a list of comma-separated values. Thanks to the “everything is a file” nature of Linux, I could run “cat /dev/ttyUSB0 > capture.csv” and I had a file I could import into Excel.

Here is the startup sequence that has long intrigued me, in the form of an Excel scatter chart. Horizontal axis is the time stamp. This graph covered about 9 seconds with a data point every 10ms, resulting in dots densely packed into a line. Vertical axis is the encoder counter value, with the encoder as a gray line maxing out at almost 32000 counts. (Getting a number for count per rotation is on the to-do list.)

The two photo interrupter sensors did not change state throughout startup.

Due to the way I wired up the quadrature decoder, positive values on on this counter correspond to rotating the paper feed backwards. I usually think of printing as advancing forwards on the Y-axis, so this is reversed from that I would have chosen if I had known. But now that it is connected, I don’t care enough to fix this minor issue. If it starts causing problems I can flip the two wires and reverse their direction.

The fact this plot appears to be a series of straight lines tells me the motor does not vary its speed (significantly) once it is in motion.

Zooming on the initial set of movements, I see an acceleration curve before it reaches that speed, and there’s a deceleration curve at the end of each movement. My eyes did not perceive acceleration and deceleration, so this exercise is already telling me things I couldn’t pick up with my own eyes. I can also see the first two movements here are both backwards, but the second movement has a gentler slope reflecting a slower speed. Another difference I could not see with eyeballs.

Zooming in on a different part of the graph, this is the point of maximum backwards travel. On the big graph I saw a small bump on either side of the peak, now I see the motor decelerated to a stop before immediately accelerating back up in the same direction, only to decelerate to a stop again before going in the opposite direction. It then slowed down again to a stop before resuming in the same direction at a different speed. Both of these pauses are extremely short, only a few milliseconds if that.

What did that motion accomplish? In fact, what did any of these motions accomplish? I don’t have answers yet, but I’m excited I’m peeling back at least the first layer of this onion. Next: power down standby sequence.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Captured CSV and Excel worksheets are included in the companion GitHub repository.

Quadrature Decoding With Periodic Output

I had forgotten (then was reminded) that I already had Arduino Nano-based quadrature decoding capability on hand. After a quick check to verify it should be fast enough, I connected it up to my retired Canon MX340 multi-function inkjet to see what I can glean from its paper feed motor assembly. The initial test used the basic example included with Paul Stoffregen’s quadrature decoder library. It polls the encoder count in a tight loop and, whenever it sees a change, it prints the new value to serial port. I started it, turned on the MX340, and got a stream of numbers on Arduino IDE’s Serial Monitor. A good start.

As the motor started spinning, the change in encoder values came fast and furious. A backlog quickly developed, which resulted in data display lagging behind actual motor movement. This was easily resolved by kicking up the serial transmission baud rate above the slow-and-reliable 9600 baud. Looking on the drop-down list of baud rates supported by Arduino IDE serial monitor, I chose 250000 because it’s easier for me to remember right now as it’s what this MX340 itself uses.

But that still left a lot of data flying by as the motor spun. The next change to further reduce output was to change from “every time encoder changes” to “once every 10 milliseconds”. This seems to have reduced the output to a manageable flow, but I didn’t know what kind of processing to try next. Ideally I would take advantage of characteristics of the system to filter interesting data from extraneous noise, but I don’t know its characteristics yet.

So… I will learn its characteristics! To meet my objectives for this decoder project, I connected two more Arduino Nano digital input wires to photo interrupter sensors in this system. One likely reports paper status, and the other watching something inside a gearbox I plan to dissect later. Both their states are polled at the same 10ms interval and output to serial port. I also changed the serial output to be a set of comma-separated values. After my earlier success using Microsoft Excel to make sense of raw captured data, I will use it again now to get a basic outline of what this motor system is doing.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.

Arduino sketches are included in the companion GitHub repository.

Quadrature Decoding with Arduino

I want to understand the internal workings of a Canon Pixma MX340 multi-function inkjet. Right now my focus is on its paper feed motor assembly, and I want to record data reported by a quadrature rotation encoder inside that assembly. I want to track behavior over several seconds, possibly a minute of two, which gets a little unwieldy with a logic analyzer timeline interface. So I thought I should create a tool tailored to my project and I found a promising lead using an ESP32’s pulse counter (PCNT) peripheral.

As as I started preparing for the project, thinking through and writing down what I’d need to do, a lot of details felt very familiar in a “wait… I’ve done this before” way. I had forgotten I’ve played with quadrature encoders before! A search for “quadrature” on my project notebook (this blog site) found entries on reading the knob on a Toyota audio head unit, an inexpensive knob from Amazon, and investigative detour during Honda audio head unit adventures.

Following my earlier footsteps would be an easier way to go, because the Arduino IDE and Paul Stoffregen’s quadrature decoder library are already installed on my machine. But this will be the first time I apply it to something turned by a motor instead of by a human hand. Is it fast enough to keep up? Decoder library documentation says 100-127kHz sampling rate is possible on a Teensy 3, which was the library’s original target hardware. Running on an ATmega328 would be slower.

Aside: I found this Gammon forum thread listing technical detail on ATmega328 interrupt service routines, which laid out work just for ISR overhead that would take 5.125us before any ISR code actually runs. This puts a hard upper bound of ~200 kHz on response rate of an ISR that does nothing.

In the spirit of “try the easy thing first” I’ll start with ATmega328 Arduino. If it proves too slow, I have a Teensy LC somewhere, and I definitely have ESP8266 boards. In the unlikely case they all fail to meet my need, I can resume my examination of ESP32’s pulse counter (PCNT) peripheral.


This teardown ran far longer than I originally thought it would. Click here to rewind back to where this adventure started.