final project -- weekly assignments -- about me -- fab academy

Week 15: Big Computers Week

I originally had the plan to make a simple, first shot at a user interface for my placer project during this assignment. It needs one, anyway, so why not do it for an assignment? The world (and, mostly, work) had other plans, and the placer project is not far enough along even now to make a user interface for it. The brain boards, with their built-in interfaces for being in contact with a computer, are not even running at all yet - For lack of time, though.

So, for now, I need an interface to calibrate the wheels of the clock. The magnetic encoders do provide an index signal, so they can self-zero on startup, but they need to know the offset resulting from the angle at which the magnet is glued into the wheel. So, I need a way to have the wheel slowly turning under manual control, and read back what the encoder does. Not a huge user interface, but a good start for fiddling with something new.

It would have been really cool, during this assignment, to have a fablab and a local instructor to ask for help. Our local instructor, though, while enthusiastic, was too loaded with non-fabacademy tasks to be of any real assistance, so resources were limited.

Choosing a Toolchain

After looking at different options during our group assignment I was about to get going with Python. But while installing Python on my mac would have been easy (it comes with a pre-installed python 2, and there are packages for python 3), getting PyQT to run seems to be a royal PITA including staining my system with stuff I really don't want to have. With what little time I had, I decided to use processing instead, since it's just a simple download of the whole environment to use.

Building a Simple GUI

Processing is a very nice, but very basic system for building all kinds of automation. In drawing the user interface, it is a lot like openSCAD, you really draw the interface from simple geometric components lice rectangles:

stroke(0);
fill(255);
rect(width/2, 92, 120, 22, 8);

A lot of drawing geometry later, a simple user interface emerges:

The user interface of the calibration tool

There is absolutely no functional context behind those geometric elements, they are just drawn (time and time again while the draw routine loops). Any kind of functionality has to be realized on top. This can either be done in the drawing loop, or by waiting for events - in this case, the event of a mouse button being pressed. The routine that is called in that case then has to check where the heck the mouse is, then act appropriately:

if(mouseY > 110 && mouseY < 190) {

This first check is a special case: All three buttons are at the same height in the window, so checking the vertical position of the mouse is done centrally. If the mouse was at the right height when being clicked, another three checks test the horizontal position:

if(mouseX > ((width / 2) - 140) && mouseX < ((width / 2) - 60)) {

In case the mouse was in one of the buttons, a message is sent to the microcontroller and the background of the button is changed to show it's active. A similar, but way simpler routine is hooked to the mouse button being released. It resets all button background and sends a stop command to the microcontroller.

Serial communication is handled by the serial library, which also allows for one hook to be used: Whenever a byte comes in. The routine ends up a bit more complicated, as it has to count stars to see the primitive preamble, then reassemble the four bytes into one 32 bit integer:

if(recStatus <= 5) // no message header
{
   if(receivedChar == '*')
   {
     recStatus++;
   }
   else
   {
     recStatus = 0;
   }
}
else // message header
{
   if(recStatus == 6) // first encoder byte
   {
     firstByte = receivedChar;
     recStatus++;
   }
   else if(recStatus == 7) // second encoder byte
   {
     secondByte = receivedChar;
     recStatus++;
   }
   else if(recStatus == 8) // third encoder byte
   {
     thirdByte = receivedChar;
     recStatus++;
   }
   else if(recStatus == 9) // fourth encoder byte
   {
     encoderValue = (firstByte << 24) | (secondByte << 16) | (thirdByte << 8) | receivedChar;
     recStatus = 0;
     println("Received Message.");
   }
   else
   {
     recStatus = 0;
   }
}

The Microcontroller Part

The software running on the motor board is a spin-off of the software I did for the Networking and Communications group assignment. Like back then, it uses the pins meant to be endstop inputs as an UART connection, even though in this case with a USB-to-UART cable (very much like the FTDI cables a lot of people use, but with a CP2104).

Messages it receives are always single bytes, so all the message processing can easily be done in the receive interrupt:

void __attribute__((__interrupt__, __auto_psv__)) _U1RXInterrupt(void)
{
  unsigned char receivedChar; // temporary buffer for received byte
 
  receivedChar = U1RXREG; // read from UART

Until here, we have received a byte (which triggered the interrupt) and have read it from the UART into receivedChar. Now, we look up what it is:


  switch(receivedChar)
  {
      ...
    case 100: // "Motor backwards!"
      LATBbits.LATB14 = 1; // set motor direction
      pwmSetDutyCycle(5000); // start motor at a slow spin
      setLED(GREEN,ON); // indicate that the motor is on
      break;
      ...
  }

The other cases are handled similarly. Now, to wrap up the interrupt routine, we acknowledge the interrupt, so new ones can occur:


  IFS0bits.U1RXIF = 0; // reset interrupt flag
}

A second addition since the networking assignment is a routine that sends out the encoder position. It is called by the system timer every 100ms, and sends out a string of six stars '*', followed by four bytes of binary data - the 32 bit counter of the quadrature interface.

Result


The source files for the microcontroller side are packed here, the processing sketch here.

final project -- weekly assignments -- about me -- fab academy

Creative Commons License
This work by Christoph Nieß is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.