2
\$\begingroup\$

I am starting a project where I want to be able to control a couple of motors via my PC.
I have 3 DC motors with dual speed (2 separate coils, old wiper motors).

I am currently using a PIC microcontroller (8bit) on a Microchip Curiosity Board and now want to be able to set a couple of variables on my PC, and shoot it towards the microcontroller WITHOUT having to re-program the microcontroller.

The uC will change a couple of outputs, so the motors change speed on regular basis automatically, from slow to fast. The reason I want this done via the PC is so that my father (who has zero experience and I do not want him to mess around in the code) can enter the data and press 'enter' and the programs start running.

My question, how do I set this up? I was thinking to use an Excel file, and save it as .csv each time you want to do a run. When this works I want to expand the program with more variables

I think I will have to use UART, with this converter which I used before. How can I make the Python software talk with the microcontroller? I am having trouble getting up with search words for this project. What would be good tags for similar (starter) projects like this?

enter image description here https://nl.farnell.com/ftdi/umft230xb-01/ft230xq-usb-to-uart-breakout-board/dp/2081336

My experience with controlling a uC via the PC is few, so please go easy. I have no idea how to start at this.

enter image description here

enter image description here

EDIT: i am using the UMFT230XB, not the UMFT201XB. Changed it in this topic.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ C# is quite an easy programming language to get into (especially if you're already fluent in C). Google the terms 'C# windows forms' and 'C# SerialPort'. You'll find tons of resources and examples. VBA in Excel on the other hand is quite the pain IMO. \$\endgroup\$
    – Velvet
    Commented May 30, 2023 at 13:34
  • 2
    \$\begingroup\$ Use Python or C# or whatever you feel like to send data to MCU over serial interface. \$\endgroup\$
    – Justme
    Commented May 30, 2023 at 13:37
  • \$\begingroup\$ Look for serial examples; lots showing how to connect serial to PICs. \$\endgroup\$
    – rdtsc
    Commented May 30, 2023 at 13:38
  • \$\begingroup\$ If it doesn't have to look pretty... With UART you can just invent yet another icky serial ASCII protocol and go with an USB to UART converter, assuming the UART is available on the MCU. It's not really clear if you also need to store the data in NVM or not - if so you should implement a safe/programming mode because you won't be able to write to NVM on the fly. Also, the MCU you picked is not necessarily ideal for this application. \$\endgroup\$
    – Lundin
    Commented May 30, 2023 at 14:14
  • \$\begingroup\$ First write the MCU code, and get it so that you can control the device using a serial terminal. Once you've got that working, get a python serial library. That will let you read and write text programmatically similar to how you would with the terminal. Technically it's more efficient to just send raw data over the serial port instead of ascii text, but the latter is more user friendly. \$\endgroup\$
    – Drew
    Commented May 30, 2023 at 16:19

2 Answers 2

3
\$\begingroup\$

You could use I2C, but it might be easier to use serial communication with a USB to serial converter (TTL level output). Then you just have to set up the UART on the MCU to something that agrees with the PC (maybe start with the ubiquitous 9600 n 8 1).

As far as software at the PC end you can find various examples using various programming languages. To start from scratch, I'd probably consider Python or C#.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ OP points to UMFT201 device, which is USB-I2C. The similar UMFT230 is the USB-UART device. \$\endgroup\$
    – glen_geek
    Commented May 30, 2023 at 14:15
  • \$\begingroup\$ Thanks! My mistake, i am indeed using the UMFT230, which is used for USB-UART. NOT I2C! I am currently using a Microchip Curiosity Board with MPLAB (Microchips IDE). Are you suggesting that i add Python or C# on top of that? How would that work? \$\endgroup\$ Commented May 31, 2023 at 5:28
  • \$\begingroup\$ You can write a GUI program that runs on the PC using the language of your choice (you can find example programs written in various popular languages) and accesses the PC's serial port. If you search for something like PIC PC GUI you should find some examples of PC programs that you can modify, so you don't have to start from nothing. You don't want to run the GUI on the PIC, just on the PC. Then there will be a simple PIC program (probably with a protocol you invent) to take bytes up and down from the PC program via the serial port and store them as you suggest. \$\endgroup\$ Commented May 31, 2023 at 11:33
  • \$\begingroup\$ It should be possible to access the serial port using Excel as the GUI (eg. via VBA) but I'm not sure the effort would be worthwhile compared to just writing a PC program. \$\endgroup\$ Commented May 31, 2023 at 11:39
1
\$\begingroup\$

This is a common challenge faced when developing or integration embedded systems.

Hyphothesis:

All (All is a strong quantifier for sure) embedded systems need to be able to receive data from a host/master system to change their behaviour during their lifecycle.

Assumptions:

  1. The Embedded system (ES) in question receives some sort of calibration/configuration values during the production process.
  2. The ES changes its behaviour during normal operation based on external input (Fieldbus, WiFi-Connection, Key-Pads...)
  3. The ES can receive data to end it'soperations (Think remote detonation of ordnance or a "End Comms" command for spacecraft)

Typical approaches:

  1. Use a common method of interfacing with a standard way of interfacing (Plug a ethernet cable into your PC and fire up a SSH terminal to connect with a industrial controller e.g.)
  2. Us a common method of interfacing with a purpose build way of interfacing (Plug a ethernet cable into your PC and write a protocoll to communicate with your gadget)
  3. Us a purpose build method of interfacing with a standard way of interfacing (Build a ethernet to parallel to USB to I2C converter and fire up a SSH terminal to connect with your gadget)
  4. Us a purpose build method of interfacing with a purpose build way of interfacing (Choose your own physical means of communication as well as the protocoll)

For small scale hobby projects, the (2) approach is the most suiteable.

Here is why:

Most "DIY-Grade" embedded boards (Arduino, Curiosity Boards, and so on... ) come with a USB (common way) interface useable by the uC firmware. But, the complexity and overhead associated with meeting standard protocols (USB-Device or other...) are commonly not woth the benefits.

This is, except:

  1. You are forced to meet certain standards (Comercial products, desired inter-operability, .... )
  2. You are forced to meet reliabilty standards (Device must not fail in operation, device is situated in remote locations without physical access, ...)
  3. You find a ready-to-use software library online and your application allows the overhead .....

And here is how:

To implement a Host-Device interface, you first have to make some decisions:

  1. Which physical interface to use (Choose what is available, e.g. USB or UART/USB)
  2. Choose what protocol constraints you will have to meet (Does it need to be realtime, what bandwidth, error protection, error correction, multi-slave, multi-master, topology and so on)
  3. What are your software requirements on the system side? (Upload calibration values once, exchange information - status or commands - during operation, and so on...)
  4. What type of interface to use on the host-side (GUI interface with buttons, command line, script, remote commands, web-based, whatever)

For your application:

I'd recommend to following approach:

  1. Use a UART/USB converter board and plug it into your PC/Laptop. It will act as a Serial-Port.
  2. I would go with a fixed-frame, fixed-command, single-master/slave, memory-map based protocoll (More on that later)
  3. The protocoll should allow to write/read data to the RAM of the gadget during runtime
  4. I would go with a simple GUI allowing your father to easily use the gadget ( Single button for every command and a big "are you sure?" frame if he clicks anything he should not)

Let's get rolling with the protocol:

We set out to design a fixed-frame, fixed-command, single-master/slave, memory-map protcoll. Your can google for ModBus-RTU for example.

The basic operation mode is: Your PC sends a sequence of bytes to the gadget, the gadget reads them and make decisions based on the contents and then replys "OK" or "N-OK", or "OK, and the data you requested is 83" to the PC.

Fixed-Frame:

The frame (The sequence of bytes) is the same for every command (Contents can change of course). E.g: 2Bytes

So, your PC will send two bytes for every request and will then wait for the gadget to respond two bytes.

Fixed-Command:

The frame contains a command (Stored in the first byte). The meaning of a given command (E.G command '1':'Write to Memory') does not change depending on the state of your gadget. So, commands stay the same during runtime.

Single-Master/Slave:

This means, that in your protocol only a single master (The one who sends commands) and a single Slave (The one who responds "OK" or "N-OK") exists at any given time.

Memory-Map:

This means, that there is a dedicated memory area in your gadgets uC where the data received/to be send by/to the master is stored. Your main application "reads" this memory and performs actions based on it's contents.

Let's get rolling with the firmware:

Here is some "psuedo/C code" on how to implement the protocol.

The PC sends two bytes per command with the following structure:

(4Bits Command)(4Bits Address)(8Bit Data)

and the gadget respondes with:

(8Bits status Flag)(8Bit Data)

volatile uint8_t dataArray[16]; //Here the data to be send/received is stored
volatile uint8_t uartBuffer[2]; //Here the raw UART transmissions are stored

#define CMD_ERROR 0U
#define CMD_SUCCESS 1U

void Uart_Transreceive(){
    //Wait for new data to arrive on the UART
    //And then store it into the Buffer
    for( uint8_t idx = 0; idx < 2; idx++ ){
        while (UART_WaitForNewDataByte()){/*NOP*/}
        uartBuffer[idx] = UART_GetCurrentDataByte();
    }
    //Frame fully received so interprete it
    //This puts the data to send in the buffer
    Uart_HandleFrameRx();
    
    //Now send the data 
    for( uint8_t idx = 0; idx < 2; idx++ ){
        while (!UART_CanSendNewByte()){/*NOP*/}
         UART_SetCurrentDataByte(uartBuffer[idx]);
    }
}

void Uart_HandleFrameRx(){
    //Check to first byte in the buffer (The command/Address)
    //The command is stored in the upper 4-Bits of the byte
    //and the address is stored in the lower 4-bits
    //This gives us 16 commands and 16 data address to write/read to/from
    uint8_t command = ( uartBuffer[0] & 0xF0 ) >> 4; //Get upper for bits and align them to the rigth
    uint8_t address = uartBuffer[0] & 0x0F;
    switch(command){
        case 0 : { dataArray[address] = uartBuffer[1]; break;}      //Write to memory what was received
        case 1 : { uartBuffer[1] = dataArray[address] ; break;}     //Read from memory what is to be send
        /* Implement other commands 0...15 to your liking */
        /* But always use the default case to catch errors */
        default : { uartBuffer[0] = CMD_ERROR; uartBuffer[1] = 0; return;}      //The return here is important!
    }
    //Now signal success to the Host pc
    //This is only exxecuted if the default case is not executed
    //So a valid command was received
    uartBuffer[0] = CMD_SUCCESS; 
}

void Motor_RunProgram(){
    //Lets use the data received
    Motor1_SetSpeed(dataArray[1]); //Speed stored at adr=1
    Motor1_SetParamXY(dataArray[XY]); //You get the idea....
    
//Now run the motor and wait until it has finished
//This must be blocking, so must return only
//after the motor has stopped running its sequence

    //And now reset the "active" bit
    dataArray[0] = 0;
}

int main(void){
    /* Setup and so on */
    UART_SetupInterface_8N1_9600Baud();


    while (true){
        //Now lets run the actual "protocol"
        //This will block until two bytes are received and processed
        //When a special "Begin Motor programm" was received
        //Your motor programm is run and then returns here
        //NOTE: During the motor programm, no new commands can be processed
        Uart_Transreceive();
        
        if ( dataArray[0] == 1 )
            Motor_RunProgram();
    }
}

Now let's get rolling with the host software:

Here is some "psuedo/C#" code to use for your GUI application.

This can ofcourse be enhanced!

    class Programm{
    
    static SerialPort sp;
    void Main(){
        /* Do your Setup etc... */
        
        //See your windows "Device-Manager" on which COM-Port to use
        //Configure as 8N1 9600 as commonly used
        sp = new SerialPort("COM1",9600,8,1);
    }
    
    static byte SendCommand(byte command, byte address, byte data){
        //Build the first byte (4Bits Command)(4Bits Address)(8Bit Data)
        //and populate the buffer with it
        byte[] buffer = new byte[2];
        buffer[0] = ( command & 0x0F << 4 )  | ( address & 0x0F );
        buffer[1] = data;
        
        //Now send the data and wait a short time for the
        //response ... can be enhanced (Timeouts and so on)
        sp.Send(buffer,2);
        Thread.Sleep(1000);
        sp.Read(buffer,2);
        
        //Now check if there was an error
        //The first byte should always be "success"
        if ( buffer[0] != 1 ) {/* Error happend */ }
        //and returnt the data received from the gadget
        //if a read command was issued
        return buffer[1];
    }
    
    void Button_RunMotor1WithGivenParameters(){
        //Lets say your programm consists of four parameters
        //So send these four parameters to the gagdet
        //And the start the programm
        //See the firmware
        
        //Sets speed to value 15 (Write command to adr = 1
        SendCommand(0,1,15); 
        //Write generic param (Write command to adr = XY with value ABC
        SendCommand(0,XY,ABC);
        
        //read back some param
        //Reads back the data stored in the address 7 in the gadget
        byte param = SendCommand(1,7,0); 
        
        //And now execute the motor programm
        //Writes '1' to the dataAraay[0] check in the main Loop
        SendCommand(0,0,1);     
        
    }
}

And: Tadaaaaaaa .... of you go!*

You now have an idea of the uC and the PC side as well as some knowledge on how to implement such a protocol.

There are many protocols used commonly for these tasks. One is ModBus-RTU

\$\endgroup\$
6
  • \$\begingroup\$ This "This means, that there is a dedicated memory area in your gadgets uC where the data received/to be send by/to the master is stored. Your main application "reads" this memory and performs actions based on it's contents." is awful unless you are controlling something that already has a register interface. There's a reason many peripherals evolved to FIFOs for data transfer. Let the RX interrupt smartly pick the next available queue slot instead of requiring the PC to figure it out (and having race conditions). \$\endgroup\$
    – Ben Voigt
    Commented May 30, 2023 at 21:39
  • \$\begingroup\$ The other aspects of your recommended protocol (using serial, fixed-length or at least fixed-length command header, and command vocabulary that doesn't depend on current state) are good. Some additional common helpful protocol features might be nice, such as sequence number, timestamp, checksum. And just let the commands be real commands. "Set motor speed" is a good command. \$\endgroup\$
    – Ben Voigt
    Commented May 30, 2023 at 21:44
  • \$\begingroup\$ @BenVoigt Thank you for your response. I agree, that a "command-based" interface where all required data for a command (E.g Run Motor for x Seconds with y speed and z parameter) is included in a dedicated frame, is in most cases more desireable. On the other hand, i disagree with you, as this complicates the protocoll design drastically - especially if many commands are to be implemented. But, i also can see many positive aspects in your proposal: Build a queue for "write/read" commands and let logic control the sequence to avoid race conditions - though, this can also be complex! \$\endgroup\$ Commented May 30, 2023 at 22:17
  • \$\begingroup\$ @BenVoigt I commonly favour the "register" based access for my hobby projects for three reasons: (1) It is simple an ligthweight on the controller side, (2) the driver layer can be implemented easily on the host side, (3) correct access procedures can be - for me - implemented better on the host side. Usually, i use a raw 32Bit Interface with n-Slots. I implement read/write and read/write-n-block along with checksums and SOF/EOF delimiters. On the host side (C#, usually) i use dedicated objects for every command (Run Motor has x,y,z params) which are set and then written. See next..... \$\endgroup\$ Commented May 30, 2023 at 22:21
  • \$\begingroup\$ @BenVoigt After all parameters have been written to the uC i trigger a flag as shown to trigger the command (So race conditions are avoided, as parameters are "latched" and checked before using them). This can be done nicely with a little help of Reflection and XML. This allows me to build even "bigger" interfaces relatively quick and simple - of course, by no means perfect! \$\endgroup\$ Commented May 30, 2023 at 22:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.