CNCjs handwheel pendant (detailed howto for nerds)

Did I follow correctly that probing in the green area below you get 3.7V, but probing in the red area you don’t have signal ?

Solder issue on the other side of the board then ? Sorry if I got this wrong.

You are correct as to what I was testing and my result, or lack of result I suppose. The other side appears fine which is why this makes no sense :slight_smile:

I am going to pick up some solder braid this evening to clean it all up and start over. It’s gotta be an issue with the wiring somewhere that I am not seeing. I want to make sure there is no solder I am missing that is jumping any of the pins together.

1 Like

I may be having a oops moment here. I was under the assumption that this PCB had traces that connected each of the rows together (vertically) this whole time. After further looking, I tested various points and none of them are in fact connected. This leads me to believe you are connecting or jumping together on the bottom side? What does your bottom side of the board look like? I was just soildering each point to it’s own hole, thinking that it was connecting the all together down the row to bring the voltage down through the resistors and back into the gpio.

Correct, the plated holes are not connected so you need to make solder blobs between adjacent holes:

Mystery solved then. Let us know if it works!

I added solder to the bottom to jump the points as you have here. It looks like it is logging the “DIAL TURNED” events you put in place. I’m not sure if this is just because I am using an arduino to simulate this, but I don’t actually see anything moving in my test setup in CNCjs. I will try and move it to the CNC tomorrow to see if it does anything, but the console makes me think there may still be something else going on (outside of my original issue).

    pi@torchedflags:~/cncjs-pendant-raspi-jogdial$ node bin/cncjs-pendant-raspi-jogdial 
? Specify which port you want to use? /dev/ttyACM0
Connected to ws://localhost:8000?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IiIsIm5hbWUiOiJjbmNqcy1wZW5kYW50IiwiaWF0IjoxNTkwNDY1MzAwLCJleHAiOjE1OTMwNTczMDB9.XC2iEfD8EOmGa9nfYoNJQTfjuASTb27K7mOLOpR78Ys
Connected to port "/dev/ttyACM0" (Baud rate: 115200)
{ port: '/dev/ttyACM0',
  baudrate: 115200,
  controllerType: 'Grbl',
  inuse: true }
[GRBL in]Grbl 0.9j ['$' for help]
[GRBL out]$$
[GRBL in]$0=10 (step pulse, usec)
[GRBL in]$1=25 (step idle delay, msec)
[GRBL in]$2=0 (step port invert mask:00000000)
[GRBL in]$3=0 (dir port invert mask:00000000)
[GRBL in]$4=0 (step enable invert, bool)
[GRBL in]$5=0 (limit pins invert, bool)
[GRBL in]$6=0 (probe pin invert, bool)
[GRBL in]$10=3 (status report mask:00000011)
[GRBL in]$11=0.010 (junction deviation, mm)
[GRBL in]$12=0.002 (arc tolerance, mm)
[GRBL in]$13=0 (report inches, bool)
[GRBL in]$20=0 (soft limits, bool)
[GRBL in]$21=0 (hard limits, bool)
[GRBL in]$22=0 (homing cycle, bool)
[GRBL in]$23=0 (homing dir invert mask:00000000)
[GRBL in]$24=25.000 (homing feed, mm/min)
[GRBL in]$25=500.000 (homing seek, mm/min)
[GRBL in]$26=250 (homing debounce, msec)
[GRBL in]$27=1.000 (homing pull-off, mm)
[GRBL in]$100=250.000 (x, step/mm)
[GRBL in]$101=250.000 (y, step/mm)
[GRBL in]$102=250.000 (z, step/mm)
[GRBL in]$110=500.000 (x max rate, mm/min)
[GRBL in]$111=500.000 (y max rate, mm/min)
[GRBL in]$112=500.000 (z max rate, mm/min)
[GRBL in]$120=10.000 (x accel, mm/sec^2)
[GRBL in]$121=10.000 (y accel, mm/sec^2)
[GRBL in]$122=10.000 (z accel, mm/sec^2)
[GRBL in]$130=200.000 (x max travel, mm)
[GRBL in]$131=200.000 (y max travel, mm)
[GRBL in]$132=200.000 (z max travel, mm)
[GRBL in]ok
ENABLED
DISABLED
ENABLED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X1 SELECTED
ERROR: uponDialPulseTimeout / unsupported signal
DISABLED
ENABLED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X1 SELECTED
ERROR: createServer smooth jogging / unsupported signal
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X1 SELECTED
ERROR: uponJoggingTimer / unsupported signal
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X1 SELECTED
ERROR: uponJoggingTimer / unsupported signal
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X1 SELECTED
ERROR: uponJoggingTimer / unsupported signal
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X1 SELECTED
ERROR: uponJoggingTimer / unsupported signal
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X1 SELECTED
ERROR: uponJoggingTimer / unsupported signal
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DISABLED
ERROR: uponJoggingTimer / unsupported signal
SMOOTH JOGGING STOPPED
[GRBL out]…;
[GRBL in]error: Expected command letter
ENABLED
DISABLED
ENABLED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X1 SELECTED
START JOGGING Y mm in the positive direction at speed 100 mm/min
[GRBL out]$J=G91 G21 Y30 F100
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X1 SELECTED
DIAL TURNED
[GRBL out]$J=G91 G21 Y30 F100
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X1 SELECTED
[GRBL out]$J=G91 G21 Y30 F100
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X1 SELECTED
[GRBL out]$J=G91 G21 Y30 F100
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X1 SELECTED
DIAL TURNED
[GRBL out]$J=G91 G21 Y30 F100
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X1 SELECTED
[GRBL out]$J=G91 G21 Y30 F100
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DISABLED
SMOOTH JOGGING STOPPED
[GRBL out]…;
[GRBL in]error: Expected command letter
ENABLED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X10 SELECTED
START JOGGING Y mm in the positive direction at speed 1000 mm/min
DIAL TURNED
[GRBL out]$J=G91 G21 Y30 F1000
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X10 SELECTED
[GRBL out]$J=G91 G21 Y30 F1000
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X10 SELECTED
[GRBL out]$J=G91 G21 Y30 F1000
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X10 SELECTED
[GRBL out]$J=G91 G21 Y30 F1000
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X10 SELECTED
[GRBL out]$J=G91 G21 Y30 F1000
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X10 SELECTED
[GRBL out]$J=G91 G21 Y30 F1000
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
Y AXIS SELECTED
X10 SELECTED
[GRBL out]$J=G91 G21 Y30 F1000
DIAL TURNED
[GRBL in]error: Bad number format
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
START JOGGING X mm in the negative direction at speed 5000 mm/min
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
DIAL TURNED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
SMOOTH JOGGING STOPPED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DIAL TURNED
X AXIS SELECTED
X100 SELECTED
DISABLED

I am seeing responses in the CNCJS console such as:
client> $J=G91 G21 X-30 F5000
error: Bad number format
client> …;
error: Expected command letter

Again, this may just be due to it no actually being set up to my shapeoko and going through the Arduino, but if there’s anything that stands out to you before I make it to my machine test, let me know :slight_smile:

I appreciate all this help, so close!

1 Like

Not sure what’s going on with that “Bad Number format” error, my guess would be that there is some extra control character being sent after the feedrate value, it could be the “\n” sent at the end of each G-code string in the code, and somehow the GRBL on your Arduino does not like that. Let’s see how it works on the actual machine (I would be a little surprised if it behaved differently, but who knows)

Plugged it into the shapeoko today and everything ran just fine. Must be a difference between grbl on my Arduino vs the CNC. Thanks for your help through all of this :slight_smile:

2 Likes

So, I worked off Julien’s code to add an additional feed hold switch on top of the one that is built into the pendant.

Here are pictures and this is the link to the thread I have explaining the steps involved:
Adding Feed Hold Switch with 12V Light - Voltage Step-up

2 Likes

Hello all,

I just wanted to comment and say thank you for the how to on this, as it simplified my own desires for a jog control considerably. I also wanted to note that, rather than messing around with a PI hat and all of that mess, it’s actually quite easy to just solder a couple of resistors into the controller itself. In my case I used a standard ribbon-cable connector to plug right into the RPi header, and did the voltage division at the source.

1 Like

And my connection to the RPi…

2 Likes

Hi Julien,

I just stumbled upon this and it’s exactly what i’m looking for.

Do you know if this will work the same on an Arduino Uno setup running GRBL 1.1h?

Thank you for such an awesome project.

Brandon

Hi Brandon,

The Shapeoko controller is basically an Arduino Uno at the core, so yes this should work as is to drive a CNC running any version of GRBL

1 Like

Thanks for the quick response.

I’ll let you know how it goes!

Hi Julien,
1st - Awesome write up and project - many thanks for all the details and help I found in here, you knowledge is outstanding.
I came across CNCjs some time ago and have been using it steadily as a web server from my rpi2 for over a year now.
After months of researching pendants and all the various wired, BT, wireless pendant projects I decided on yours. It seemed to fit what I was looking for and what I had available to build from scratch.
I made some changes and used a rotary encoder instead of the handwheel jogwheel, 3 pole axis selector instead of the 4pole and swapped the LED function to work in reverse. i.e pendant loaded = LED on. Enable button pushed = LED off mainly due to the LED used is on my enable button.
Since I used the encoder I also worked out that I had to rewire the A & B return as PULL_UP as well as include them in the config.tx editing.
Ill just say that getting to the stage I am at has been a long, enjoyable, learning curve but there have been (and still are plenty of) frustrating times - mainly due to my ‘0’ knowledge of coding.
My issues stated from the beginning when trying to npm install but serialport kept failing to load and would exit. After many weeks and slowly understand javascript I tried changing the version numbers of the node modules in package.json. - 1st success, it loaded.
Next I had issues trying to run the bin file via node, since I had updated serialport there was part of the script which were no longer accepted. something about a ‘callback was no longer required’.
Spent further weeks reading other bin files and also the boilerplate bin file to eventually make changes so it loaded without errors. - 2nd success bin file loads and i get to the port selection list.
Here is where im at and im well and truly stumped with my minimal knowledge.
I don’t know why Im not connecting through the port to send the Gcode signals.
I can see them in the console and everything works as I think it is intended too.
If you could help me figure out where I am going wrong would be really appreciated.
Apologise in advance if this is long winded… 1st time on one of these sites.

cncjs-pendant-raspi-jogdial.zip (5.4 KB)

1 Like

Hi @Santia3o,

First of all hats off for your resilience in making this work, it can get frustrating at times and it seems like you are almost there.

When you launch the pendant, right after serial port selection it should normally connect to the CNCjs server. If it doesn’t, chances are it’s not using the correct port number. The code uses a default value of 8000, but you can modify it by passing --socketPort xxxx to command line with a specific xxxx value.

So, the very first thing is to check which port your CNCjs server is using and pass that in the command line, or modify the CNCjs startup command to tell it to use 8000.

When you use CNCjs from a browser like you usually do, is there a number showing up just after the IP address in the URL ? That would be the server port, try that.

1 Like

Thanks Julien,
Im a believer in learning by giving it a go and not so much taking the easy option of asking for the answers straight away.
I have checked and confirmed that im using the correct port number, which is 8000, as well as the correct baud rate and also changed the option ‘secret key stored’ to the .cncrc.cfg file.
Why its not connecting is above my skill level to debug. :frowning:

Can you confirm if I should be loading the server first and connecting to it prior to running the pendant script?

Thank you once again for the help and also replying back so quick. :pray:

I get the console confirmation but as its not connecting to a port I have no control of the cnc

So if you open a browser on the Pi at http://127.0.0.1:8000, it opens up the CNCjs client window correctly?

Yes, the CNCjs server needs to be running first, when the pendant code is invoked.

Do you have CNCjs set up to autostart ?
For the sake of testing it could be interesting to start it manually in a command shell, i.e.

CNCjs --port 8000

then launch the pendant from another window using

node bin/cncjs-pendant-raspi-jogdial --socket-port 8000

Does it work then ?

1 Like

Thats good news, I was banking that CNCjs would need to be running prior to the pendant since it was clear in your ‘how to’ steps but just wanted to confirm.
Yes, loading the server at http://127.0.0.1:8000 works and so does http://[IP_address_of_the_raspberry_Pi]:8000/#/workspace from a seperate browser on my MacBook.
I don’t have them setup through pm2 just yet mainly because I wanted to be confidant that the pendant worked. I was afraid that if I tried to implement all the steps without any checks, it would be difficult to find the root cause of the issue or issues on failure to operate.
Currently my steps are: Load CNCjs manually via the console
Run cncjs to start the server, and visit http://yourhostname:8000/ to view the web interface.
I then confirm via MacBook by opening another server page using http://[IP_address_of_the_raspberry_Pi]:8000/#/workspace.
When the server loads I choose the port /dev/ttyUSB0 I confirm I have control of my CNC, I then proceed to load the pendant via the console:
node bin/cncjs-pendant-raspi-jogdial
Im then presented with 2 x port options /dev/ttyAMA0 (which I believe was the port you were using) and the port I am occupying on my CNC, /dev/ttyUSB0.
after choosing the later it allows me to view the console operation of the pendant as per your debugging code but thats it.
IMO, it seems that the serialport code is not applying the additional required specifics to creatServer for the socket. (if that makes sense)
It most likely would have to be due to the changes I made in the bin file to allow it to load with latest version of SerialPort 9.x
The changes were made at:
LINE 63 if (option.list) {

and

LINE 414 SerialPort.list().then(function (ports) {

Server loaded confirmation

I noticed your server starts on local IP 127.0.1.1, not 127.0.0.1, can you confirm 127.0.1.1 is the address of “localhost” on your setup ?
(i.e. type more /etc/hosts and check what IP address appears in the “localhost” line?)

Alternatively, try launching the pendant with added parameter --socket-address 127.0.1.1