-1

Am building a Rubik's cube solver machine and I need a way to read the output of the program so it can feed it to the stepper motors of the machine

The code outputs something like this (The code is in C++)

________________________| RUBIK'S CUBE SOLVER |________________________

Input :

enter code here

White Side : Red Side : Orange Side : Blue Side : Green Side : Yellow Side : 
-------------------------------------------------

Turn these sides of the Cube in Clockwise Direction by 90 degrees in this exact order...

Yellow
Yellow
White
Orange

...

Here's a snippet of the code, I was thinking if I could add some sort of function that reads the "cout" and send it directly to the arduino

  if (choice=='w')

{
cout<<"White"<<endl;
swap(white[7],white[3]);
swap(white[6],white[4]);
swap(white[0],white[2]);
swap(white[7],white[5]);
swap(white[0],white[4]);
...

So I basically want to send just the strings that contain the color into the Arduino so it can perform the movements

crispo12
  • 21
  • 4
  • Take the [tour], read [Ask], and [MCVE]. It sounds like you want to redirect the output from program into another and you have no clue how to write that second one that read the output of the first? – jwdonahue May 22 '18 at 04:53
  • It is hard to understand what it is you are asking. But I think you have one program, program X. That does the computations of the rubix cube? And you want to send that output to program Y? For the Arduino to use the stepper motors? 1st. Why? 2nd. Why are you not just running this on the arduino? – Sailanarmo May 22 '18 at 05:40
  • 1
    The output of your solver program is dedicated for humans. It's readable by machines (as it is probably somehow formal) if you apply a resp. [parser](https://en.wikipedia.org/wiki/Parsing). That said, I wouldn't recommend to make it such complicated. If you have access to the sources of the solver program - just change its output so that it's easy to process for your Arduino back-end. E.g. enumerate the possible moves and let the solver output these numbers in text form or even binary form. You may even incorporate the solver into your app. and pass the output internally (without any `cout`). – Scheff's Cat May 22 '18 at 06:56
  • 1
    To support Scheff's comment: Simply add an easily machine-readable output to a Serial port, where each character Y R G W O B represents one move, or is ignored ( newline / whitespace / other ) Eventually you need a feedback when the move is done, to not flood the actor arduino too fast. – datafiddler May 22 '18 at 15:47
  • Running it directly on the Arduino sounds actually really nice but I was scared the code didn't work on the Arduino (since I have never actually worked on the platform before), even though to code is fairly simple and it doesn't use any fancy libraries than iostream. PD: Sorry about the messy question, am fairly new in actually using the page. – crispo12 May 22 '18 at 23:14
  • 1
    It is operating system specific. You should tell what OS is running on the PC connected to your Arduino, and you need to explain how that connection is made. – Basile Starynkevitch May 25 '18 at 11:07

1 Answers1

0

Please do not post duplicate questions.

  1. The output of your solver (color commands) does not make much sense

    will give you headaches on the MCU side latter. Better way is to use the standard notation of rotation commands L,R,U,D,F,B like in here

    rotations

    You can distinct between CW/CCW directions by using lower/uppercase characters. This way you know exactly which slice and in which direction to rotate.

    Running solver code directly on MCU might be slow (especially if it is genere&test + backtracking based). In case of simple human like solver (like I used in the link above) MCU processing power should be more than enough.

  2. Now how to feed your data from solver (assuming Windows PC) into MCU?

    The easiest way is use RS232 but for that you need voltage converter like MAX232 (your MCU board probably has it or it use some USB chip as USB2RS232 converter in TTL voltages like FTDI or another small MCU).

    So what you should do is to write a parser that take in your file (or stream or what ever) on PC side (can be the same executable where you solve) convert the commands to single char U,D,L,R,F,B style and send that to RS232.

    The same can be done with USB but that is magnitude more complicated due to need for drivers and much much more on both PC and MCU side which is not a good idea to start with for a rookie.

    In windows you simply open file like COM1 set its properties and read/write your stuff from/to it (the same way like it would be a file). Here is a small Win32 C++/VCL lib port.h I wrote ages ago for stuff like this:

    //---------------------------------------------------------------------------
    //--- port class ver: 2.0 ------------------------------------------------
    //---------------------------------------------------------------------------
    #ifndef _port_h
    #define _port_h
    //---------------------------------------------------------------------------
    class port
            {
    public: HANDLE       hnd;
            DWORD        error;
            DWORD        rlen,wlen;
            DCB          rs232_state;
            COMMPROP     properties;
            COMMTIMEOUTS timeouts;
            COMSTAT      stat;
            port();
            ~port();
            int  open(AnsiString name);
            void close();
            int  get_stat();        // err,stat
            int  get_timeouts();    // timeouts
            int  set_timeouts();
            int  get_properties();  // properties
            int  get_rs232_state(); // rs232_state
            int  set_rs232_state();
            void rst_rs232_state();
            void out(BYTE data)  { WriteFile(hnd,&data,1,&wlen,NULL); }
            void in (BYTE *data) { ReadFile (hnd, data,1,&rlen,NULL); }
            void in (char *data) { ReadFile (hnd, data,1,&rlen,NULL); }
            void out(BYTE *data,DWORD len) { WriteFile(hnd,data,len,&wlen,NULL); }
            void in (BYTE *data,DWORD len) { ReadFile (hnd,data,len,&rlen,NULL); }
            };
    //---------------------------------------------------------------------------
    port::port()
            {
            rlen=0;
            wlen=0;
            error=0;
            hnd=(void*)0xFFFFFFFF;
            rst_rs232_state();
            }
    //---------------------------------------------------------------------------
    port::~port()
            {
            close();
            }
    //---------------------------------------------------------------------------
    int port::open(AnsiString name)
            {
            close();
            error=0;
            rlen=0;
            wlen=0;
            hnd=CreateFile( name.c_str(),GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_ALWAYS,0,NULL);
            error=GetLastError();
            if ((DWORD)hnd==0xFFFFFFFF) return 0;
            get_timeouts();
            get_properties();
            get_rs232_state();
    
            timeouts.ReadIntervalTimeout;
            timeouts.ReadTotalTimeoutMultiplier;
            timeouts.ReadTotalTimeoutConstant;
            timeouts.WriteTotalTimeoutMultiplier;
            timeouts.WriteTotalTimeoutConstant;
    
            properties.wPacketLength;
            properties.wPacketVersion;
            properties.dwServiceMask;
            properties.dwReserved1;
            properties.dwMaxTxQueue;
            properties.dwMaxRxQueue;
            properties.dwMaxBaud;
            properties.dwProvSubType;
            properties.dwProvCapabilities;
            properties.dwSettableParams;
            properties.dwSettableBaud;
            properties.wSettableData;
            properties.wSettableStopParity;
            properties.dwCurrentTxQueue;
            properties.dwCurrentRxQueue;
            properties.dwProvSpec1;
            properties.dwProvSpec2;
            properties.wcProvChar[1];
    
            return 1;
            }
    //---------------------------------------------------------------------------
    void port::close()
            {
            if ((DWORD)hnd==0xFFFFFFFF) return;
            CloseHandle(hnd);
            error=GetLastError();
            hnd=(void*)0xFFFFFFFF;
            }
    //---------------------------------------------------------------------------
    int port::get_stat()
            {
            if ((DWORD)hnd==0xFFFFFFFF) return 0;
            DWORD err;
            if (ClearCommError(hnd,&err,&stat)) return 1;
            error=GetLastError();
            return 0;
            }
    //---------------------------------------------------------------------------
    int port::get_timeouts()
            {
            if ((DWORD)hnd==0xFFFFFFFF) return 0;
            if (GetCommTimeouts(hnd,&timeouts)) return 1;
            error=GetLastError();
            get_stat();
            return 0;
            }
    //---------------------------------------------------------------------------
    int port::set_timeouts()
            {
            if ((DWORD)hnd==0xFFFFFFFF) return 0;
            if (SetCommTimeouts(hnd,&timeouts)) return 1;
            error=GetLastError();
            get_stat();
            return 0;
            }
    //---------------------------------------------------------------------------
    int port::get_properties()
            {
            if ((DWORD)hnd==0xFFFFFFFF) return 0;
            if (GetCommProperties(hnd,&properties)) return 1;
            error=GetLastError();
            get_stat();
            return 0;
            }
    //---------------------------------------------------------------------------
    int port::get_rs232_state()
            {
            if ((DWORD)hnd==0xFFFFFFFF) return 0;
            if (GetCommState(hnd,&rs232_state)) return 1;
            error=GetLastError();
            get_stat();
            return 0;
            }
    //---------------------------------------------------------------------------
    int port::set_rs232_state()
            {
            if ((DWORD)hnd==0xFFFFFFFF) return 0;
            if (SetCommState(hnd,&rs232_state)) return 1;
            error=GetLastError();
            get_stat();
            return 0;
            }
    //---------------------------------------------------------------------------
    void port::rst_rs232_state()
            {
            rs232_state.BaudRate    = CBR_9600;
            rs232_state.ByteSize    = 8;
            rs232_state.Parity      = NOPARITY;
            rs232_state.StopBits    = ONESTOPBIT;
            rs232_state.fOutxCtsFlow= FALSE;
            rs232_state.fOutxDsrFlow= FALSE;
            rs232_state.fOutX       = FALSE;
            rs232_state.fInX        = FALSE;
            rs232_state.fBinary     = FALSE;
            rs232_state.fRtsControl = RTS_CONTROL_DISABLE;
            }
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    #endif
    //---------------------------------------------------------------------------
    

    The only stuff it uses from VCL is AnsiString which is string datatype with character access indexed from 1 instead of 0. The AnsiString s; s.c_str(); just returns char* pointer so you can directly port all this to char* or string type you got at your disposal.

    When dealing with RS232 you should take in mind you need some sort of synchronization because the other side never knows if it receives data from the beginning or some data was send already prior to initialization. So you should add some start/end commands to your solver stream.

    You can also simultaneously read and write the RS232 but you need to use threads to implement this correctly. This is how I usually do it:

    #include <windows.h>
    //typedef uint32_t DWORD;           // uncomment this if no DWORD type is present
    //typedef uint16_t WORD;            // uncomment this if no WORD type is present
    //typedef uint8_t BYTE;             // uncomment this if no BYTE type is present
    #include "port.h"
    
    port com;
    const int _timeout=1;
    const BYTE _id_timeout=0;
    unsigned char q;
    
    // init or reconnect
    com.open("COM1"); // use number of your COM port to use can be different than 1 especially for USB converters see device manager)
    com.timeouts.ReadIntervalTimeout        =_timeout;
    com.timeouts.ReadTotalTimeoutMultiplier =_timeout;
    com.timeouts.ReadTotalTimeoutConstant   =_timeout;
    com.timeouts.WriteTotalTimeoutMultiplier=_timeout;
    com.timeouts.WriteTotalTimeoutConstant  =_timeout;
    com.set_timeouts();
    com.rs232_state.BaudRate=CBR_9600;
    com.set_rs232_state();
    
    // write
    q='L';
    com.out(q);
    
    // read (this can freeze if no data in RS232 and timeout set too big)
    q=_id_timeout;
    com.in (&q);
    if (q==_id_timeout) /* do something if timeout*/ ;
    else /* handle valid data*/ ;
    
    // exit
    com.close();
    

    In case your environment does not know BYTE, WORD, DWORD then change them with uint8_t, uint16_t, uint32_t or use typedef. The WinAPI functions need to include windows.h.

    As I mentioned before beware that reading COM port without actual data sending from other side might freeze your code so it is better to separate reads and writes to threads or have short timeouts and account for the loss of data due to freeze ups.

    Before moving to MCU you should check if your PC side code is working (as you can much easily debug the PC side than the MCU side). So code emulator of your MCU which will read the RS232 feed from your solver and print it somewhere like console or whatever so you can see if data is correct. Then connect RS232 by loop-link (short circuit RxD and Txd pins by a wire) ...

  3. The code on the MCU side

    should do more or less the same thing but you do not have winapi at your disposal so you need to use framework you have. Most MCU have overloaded iostream to RS232 so you just use cin/cout instead all of this. I do not code in Arduino framework (I use C++ (GCC) for MCUs) and there RS232/UART access depends on MCU chip architecture and version so can not help you there.

    The important thing to remember is that you need to set the RS232 protocol the same way as in the PC side. So the same number of start/stop/parity bits, the same BaudRate etc ...

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Where does that code should run? If it runs on a PC connected to the Arduino, under what OS? `DWORD` or `BYTE` are not standard C++ types. Your code won't compile on my Linux desktop! – Basile Starynkevitch May 25 '18 at 11:09
  • @BasileStarynkevitch It is all mentioned in the answer. As OP does not specify OS ... It is all for windows and the datatypes are described too. Yes on linux the RS232 access is diferent ... If you look at the bold lines it shoul dbe clear what runs on PC and what on MCU – Spektre May 25 '18 at 11:10
  • @BasileStarynkevitch I reformatted the Answer a bit to make it more clear. IIRC the datatypes are also defined as base types in windows.h somewhere. All my compilers know `BYTE/WORD/DWORD/QWORD` from 1990 especially on platforms I code for (not just Windows and not just C++). All HW oriented programing stuff I come in contact with use them. – Spektre May 25 '18 at 11:22