PJRC's Encoder library

Hey, peeps!

I found out about http://maslowcnc.com a couple of days ago. It’s an open-source CNC router that uses pen-plotter-style kinematics. Since I came late to the party, I couldn’t back them on Kickstarter and now I’m thinking about building my own, but using a Particle-based control board instead of the one they’ve designed that’s based on the Arduino Mega.

I think I can do the PCB ok, but porting the firmware might be more difficult for me.

Their firmware relies on PJRC’s Encoder library.

In the Encoder library there are two calls in utility/direct_pin_read.h that seem to be Arduino-/AVR-specific:

  • portInputRegister()
  • digitalPinToBitMask()

I’d love to be able to create an open-source, drop-in replacement control board using the photon, but I’d need some help porting the Encoder library. Any tips on where to start?

I’m eager to learn, but my raw AVR or ARM skillz are pretty non-existent, and my C++ is novice at best.

Anyone have any pointers on porting this to Particle?



1 Like

@peekay123 any thoughts? You’ve ported a lot of libraries from Arduino.

Is it worth the time/effort? Or would the time be better spent finding/creating a different library that could do quadrature encoding with a similar-ish API?

I think in 0.6.0, we added more Arduino API for compatability. It seems to have both of the functions mentioned above

Porting might not be too challenging :slight_smile:

Update: did a quick compile test and it seems like the interrupts stuff/function is not playing nice. Definitely need to have them changed for our platform.

@jtzemp, there is assembler in the ISR code which most likely has to be adapted. It is not a straight-forward port unfortunately. The STM32F205 timer channels do have a quadrature mode but that, again, would require “below the HAL” code to implement assuming all the pin mapping is amenable to it.


Thanks so much for your feedback, @peekay123 and @kennethlimcp!

I wrote this code quite some time ago for a rotary encoder knob that you turn by hand. I found that software debouncing was not super reliable for me so I added 0.1uF capacitors to ground to each input and used input pullup. Maybe this simple code will show how easy it can be.

Since you are in control of the steppers, you may have to change the maximum speed (inches per second or meters per second) that the router can move such that this encoder reading code should work. As I recall, there is a lot of mechanical momentum in the Maslow whiteboard style CNC, so it may not be a problem at all.

This could be improved by using the pinReadFast() function that has been added since I wrote this. You have to check the datasheet for which pins have interrupts etc.

void doEncoderA();
void doEncoderB();

int encoderA = A7;
int encoderB = A6;

volatile bool A_set = false;
volatile bool B_set = false;
volatile int encoderPos = 0;

int prevPos = 0;
int value = 0;

void setup() {
  pinMode(encoderA, INPUT_PULLUP);
  pinMode(encoderB, INPUT_PULLUP);
  attachInterrupt(encoderA, doEncoderA, CHANGE);
  attachInterrupt(encoderB, doEncoderB, CHANGE);

void loop() {
    if (prevPos != encoderPos) {
        prevPos = encoderPos;

void doEncoderA(){
  if( digitalRead(encoderA) != A_set ) {  // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      encoderPos += 1;

// Interrupt on B changing state, same as A above
void doEncoderB(){
   if( digitalRead(encoderB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ) 
      encoderPos -= 1;