Page 10 of 11

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 08:28
by HaWe
I tested the following code with the following result:
1) motors are running as expected (!)
2) nothing is shown on the display except the VM std menu
3) no reaction to button press, so the program cannot be stopped

I know I'm still stuck to the NXC syntax, so what would be the correct syntax like?

Code: Select all

// mt-demo.c
// 0.4
// Multitasking-Demo:
// 3 Motoren an die Motorausgänge A,B,C anschließen !
// die Motoren werden automatisch angesteuert,
// die Encoderwerte werden simultan angezeigt!

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "ev3_constants.h"
#include "ev3_command.h"
#include "ev3_button.h"
#include "ev3_timer.h"
#include "ev3_lcd.h"
#include "ev3_sound.h"
#include "ev3_output.h"

#define clreol  DRAW_OPT_CLEAR_EOL


void *DisplayValues(void *threadid) {
  while(true) {
    TextOut(0,56, "Enc.A:             "); NumOut(42,56, MotorRotationCount(OUT_A));
    TextOut(0,48, "Enc.B:             "); NumOut(42,48, MotorRotationCount(OUT_B));
    TextOut(0,40, "Enc.C:             "); NumOut(42,40, MotorRotationCount(OUT_C));
    Wait(40);
  }
  pthread_exit(0);
}


inline unsigned long Random(unsigned long x) {
   return (rand() % x);
}

inline void Motor(int outputs, int pwr) {
  int buf=pwr;
  pwr=abs(pwr);
  SetPower(outputs, pwr);
  if(buf>=0) {OnFwd(outputs);}
  else {OnRev(outputs);}
}


void *MotorControl(void *threadid) {
  int speed;

  while(true) {
    speed=Random(201) - 100; // ergibt eine Zufallszahl für die Motorleistung  zwischen -100 und +100
    Motor(OUT_A, speed);

    speed=Random(201) - 100;
    Motor(OUT_B, speed);
    
    speed=Random(201) - 100;
    Motor(OUT_C, speed);

    Wait( 200+ (Random(2801)) ); // ergibt eine Zufallszahl für die Aktionsdauer von 200 - 3000 ms
  }
  pthread_exit(0);
}

main()  {

  int i;
  unsigned short btn;


  // initialize
  OutputInit();
  LcdInit();
  ButtonLedInit();


  ResetAllTachoCounts(OUT_ABCD);

  pthread_t f2_thread, f1_thread;
  pthread_create(&f1_thread,NULL,MotorControl,NULL);
  pthread_create(&f2_thread,NULL,DisplayValues,NULL);
  pthread_join(f1_thread,NULL);
  pthread_join(f2_thread,NULL);
  
  while(1) {
    if (ButtonPressed(btn))  break;
    Wait(100);
  }
  

  OutputClose();
  OutputExit();
  ButtonLedClose();
  ButtonLedExit();
  LcdExit();
  
  
}

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 10:48
by gloomyandy
Doc,
Can't help with your LCD problems, but...

Neither of your threads ever exits. This means that the calls to pthread_join will block forever (pthread_join basically waits for the specified thread to exit), which in turn means your main thread never gets to the button stuff. So you need to arrange for your threads to exit. One way to do this would be to add a global variable like this....

Code: Select all

....
volatile int running = 0;

....

void *MotorControl(void *threadid)
{
...
  while(running)
  {
  .....
  }
  pthread_exit(0);
}

void *DisplayValuesl(void *threadid)
{
...
  while(running)
  {
  .....
  }
  pthread_exit(0);
}


main()
{
....
  running = 1;
  pthread_create(&f1_thread,NULL,MotorControl,NULL);
  pthread_create(&f2_thread,NULL,DisplayValues,NULL);
  while(1) {
    if (ButtonPressed(btn))  break;
    Wait(100);
  }
  running = 0;
  pthread_join(f1_thread,NULL);
  pthread_join(f2_thread,NULL);
  ...
}
good luck...

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 11:13
by HaWe
thx, Andy.
What do you mean by "you need to arrange for your threads to exit"?
Both tasks (motor + display) are continuous tasks, which are designed to run simultaneously for ever (in time slices), so why should they exit?
What do I understand wrong?

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 12:10
by gloomyandy
If your threads don't exit then your main thread will never get past the pthread_join call. This call waits for the specified thread to exit before returning:
http://pubs.opengroup.org/onlinepubs/96 ... _join.html
So your original program will not get past those two calls to pthread_join.

The other problem you will have to deal with at some point is what to do with those "continuous tasks" as at some point your program will need to exit. When it does what will happen to the threads you have running? There are a few options...
1. Your program will exit and the extra threads will simply be killed. As people explained earlier this may leave external hardware in an undefined state. You may be get away with it but it is not a good thing to do.
2. Your program will wait for the extra threads to exit, but in your case this will be a very long time as they will never do so, this is what will happen if you call thread_exit in your main thread, or if you call pthread_join without arranging for the thread to exit.
3. You can arrange to have the threads exit cleanly. This is what the example code I posted does. This is generally the best solution.

I know you don't want to have to deal with all of this stuff, but I really don't think you can avoid it. Spending a little time understanding what calls like pthread_join actually does will save you a lot of grief later. Something like the following might help (sorry it is in English not sure if there is a German version or not).
https://computing.llnl.gov/tutorials/pthreads/

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 12:45
by HaWe
this is just a test example, of course I know that I need to clean up later when it's finished.
But first of all I have to get them both running, after that I will think about killing and cleaning.

I now see the point about create and join, I will follow your proposal showed in your code, thanks!

Next question is, if I may use TextOut and NumOut as I'm used to for NXC or if there are any syntax or functional changes I have to consider ?

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 14:00
by HaWe
BTW,
@ Andy:
interesting IMO, because in your code I never thought that in the main() procedure now
pthread_join
will be called ever because I supposed the main task to be caught endlessly in the while loop as long as there has no btn been pressed ...


update:
1) Motors running correctly
2) program aborts by btn press
but
3) no screen output !

Code: Select all

// mt-demo.c
// 0.5
// Multitasking-Demo:
// 3 Motoren an die Motorausgänge A,B,C anschließen !
// die Motoren werden automatisch angesteuert,
// die Encoderwerte werden simultan angezeigt!

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "ev3_constants.h"
#include "ev3_command.h"
#include "ev3_button.h"
#include "ev3_timer.h"
#include "ev3_lcd.h"
#include "ev3_sound.h"
#include "ev3_output.h"

#define clreol  DRAW_OPT_CLEAR_EOL

volatile int running = 0;


void *DisplayValues(void *threadid) {
  while(running) {
    TextOut(0,56, "Enc.A:             "); NumOut(42,56, MotorRotationCount(OUT_A));
    TextOut(0,48, "Enc.B:             "); NumOut(42,48, MotorRotationCount(OUT_B));
    TextOut(0,40, "Enc.C:             "); NumOut(42,40, MotorRotationCount(OUT_C));
    Wait(40);
  }
  pthread_exit(0);
}


inline unsigned long Random(unsigned long x) {
   return (rand() % x);
}

inline void Motor(int outputs, int pwr) {
  int buf=pwr;
  pwr=abs(pwr);
  SetPower(outputs, pwr);
  if(buf>=0) {OnFwd(outputs);}
  else {OnRev(outputs);}
}





void *MotorControl(void *threadid) {
  int speed;

  while(running) {
    speed=Random(201) - 100; // ergibt eine Zufallszahl für die Motorleistung  zwischen -100 und +100
    Motor(OUT_A, speed);

    speed=Random(201) - 100;
    Motor(OUT_B, speed);
    
    speed=Random(201) - 100;
    Motor(OUT_C, speed);

    Wait( 200+ (Random(2801)) ); // ergibt eine Zufallszahl für die Aktionsdauer von 200 - 3000 ms
  }
  pthread_exit(0);
}

main()  {

  int i;
  unsigned short btn;


  // initialize
  OutputInit();
  LcdInit();
  ButtonLedInit();


  ResetAllTachoCounts(OUT_ABCD);
  
  running=1;

  pthread_t f2_thread, f1_thread;
  pthread_create(&f1_thread,NULL,MotorControl,NULL);
  pthread_create(&f2_thread,NULL,DisplayValues,NULL);
  
  while(1) {
    if (ButtonPressed(btn))  break;
    Wait(100);
  }
  
  running=0;
  
  pthread_join(f1_thread,NULL);
  pthread_join(f2_thread,NULL);

  OutputClose();
  OutputExit();
  ButtonLedClose();
  ButtonLedExit();
  LcdExit();
  

}
as soon as both threads will run I'll care about the terminating issue
Discussion on calling pthread_exit() from main():

There is a definite problem if main() finishes before the threads it spawned if you don't call pthread_exit() explicitly. All of the threads it created will terminate because main() is done and no longer exists to support the threads.
By having main() explicitly call pthread_exit() as the last thing it does, main() will block and be kept alive to support the threads it created until they are done.
So can anybody modify my code to work with both tasks?

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 15:23
by gloomyandy
Doc,
the code you have now is pretty good from a termination point of view. Both threads will exit cleanly when you set running=0 and the pthread_wait will wait for each in turn to complete. So i think from that point of view you are in pretty good shape.

Sorry can't help with the LCD stuff...

Andy

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 16:35
by HaWe
aah, now I see,
pthread_join waits for the stop of the thread, my (faulty) guess was it joins the thread to the timeslice.
Now that makes sense ;)

Now I'm also just curious about the display thing.

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 20:28
by HaWe
update:

also TextOutEx does not work as expected (for y: line numbers getting passed instead of real y coordinates): no screen output of anything

also I observed that the btn press only works with the center btn and then this interferes with the Lego VM and starts the demo.
No reaction to any different button.
But ButtonPressed() is actually expected to work like keypressed(): return TRUE if any button is pressed and FALSE if not.

So still too many issues...!

Code: Select all

// mt-demo.c
// 0.6
// Multitasking-Demo:
// 3 Motoren an die Motorausgänge A,B,C anschließen !
// die Motoren werden automatisch angesteuert,
// die Encoderwerte werden simultan angezeigt!

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "ev3_constants.h"
#include "ev3_command.h"
#include "ev3_button.h"
#include "ev3_timer.h"
#include "ev3_lcd.h"
#include "ev3_sound.h"
#include "ev3_output.h"

#define clreol  DRAW_OPT_CLEAR_EOL


volatile int flag_threadrun = 0;

//------------------------------------------------------------------------------


void *DisplayValues(void *threadid) {

  while(flag_threadrun) {
  
    TextOutEx(0, 1, "Enc.A:", clreol); NumOut(42, 1, MotorRotationCount(OUT_A));
    TextOutEx(0, 2, "Enc.B:", clreol); NumOut(42, 2, MotorRotationCount(OUT_B));
    TextOutEx(0, 3, "Enc.C:", clreol); NumOut(42, 3, MotorRotationCount(OUT_C));
    
    Wait(40);
  }
  pthread_exit(0);
}


//------------------------------------------------------------------------------

inline unsigned long Random(unsigned long x) {
   return (rand() % x);
}

inline void Motor(int outputs, int pwr) {
  int buf=pwr;
  pwr=abs(pwr);
  SetPower(outputs, pwr);
  if(buf>=0) {OnFwd(outputs);}
  else {OnRev(outputs);}
}



void *MotorControl(void *threadid) {
  int speed;

  while(flag_threadrun) {
    speed=Random(201) - 100; // ergibt eine Zufallszahl für die Motorleistung  zwischen -100 und +100
    Motor(OUT_A, speed);

    speed=Random(201) - 100;
    Motor(OUT_B, speed);

    speed=Random(201) - 100;
    Motor(OUT_C, speed);

    Wait( 200+ (Random(2801)) ); // ergibt eine Zufallszahl für die Aktionsdauer von 200 - 3000 ms
  }
  pthread_exit(0);
}


//------------------------------------------------------------------------------


main()  {

  unsigned short btn;


  // initialize
  OutputInit();
  LcdInit();
  ButtonLedInit();
  ResetAllTachoCounts(OUT_ABCD);

  flag_threadrun=1;

  pthread_t f2_thread, f1_thread;
  
  pthread_create(&f1_thread,NULL,MotorControl,NULL);
  pthread_create(&f2_thread,NULL,DisplayValues,NULL);

  while(1) {
    if (ButtonPressed(btn))  break;
    Wait(100);
  }

  flag_threadrun=0;

  pthread_join(f1_thread,NULL);
  pthread_join(f2_thread,NULL);

  OutputClose();
  OutputExit();
  ButtonLedClose();
  ButtonLedExit();
  LcdExit();

}
Am I the only one who's stalling on it?

Re: EV3 BCC / C software problem(s)

Posted: 29 Oct 2013, 22:36
by afanofosc
I would recommend not using the polarity switching API functions unless you never ever experience bad behavior while using them. In my experience, they don't work right all the time - at least not when executed as direct commands from the BricxCC IDE. I switched away from the polarity opcode and now use positive and negative powers exclusively with much more reliable motor control via the BricxCC tool windows. The equivalent in a program using the API I have created would be SetPower and On instead of OnFwd or OnRev. But if you never see weird behavior by using positive power levels only and calling either OnFwd or OnRev then keep on using that approach if you like.

TextOutEx is currently defined like this:

Code: Select all

char TextOutEx(int x, int y, char* str, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
//  byte pixelMode, fillMode;
//  if (CmdResolveDrawingMode(options, &pixelMode, &fillMode))
//    CmdDrawRect(x, y, width, height, pixelMode);
  return 0;
}
Which suggests that it is not yet functional. As shown in my lcd test sample program you can use LcdText instead for the time being. Just look at the sample code. NumOutEx is also not yet implemented.

Code: Select all

char NumOutEx(int x, int y, int value, unsigned long options)
{
  if (!LcdInitialized())
    return 1;
  return 0;
}
For now, to print a number to the LCD you will need to sprintf the number to a char* buffer and then use LcdText to draw the buffer to the screen. None of the drawing options are implemented yet so you won't get any clear to end of line or anything like that. I would use sprintf to get the combined text and number into a single char* buffer and draw it to the LCD using LcdText for now.

Your expectation for how ButtonPressed should work is totally wrong.

http://bricxcc.sourceforge.net/nbc/nxcd ... e15e5.html

ButtonPressed takes a constant indicating which button you want to check for and it returns true or false if that button is pressed when the function is called. In your code you pass in an uninitialized variable which would have a value of 0. That does not happen to match any of the defined button ID values supported by the EV3. I would use one of these constants:

Code: Select all

#define BTNEXIT   BTN1 //!< The exit (escape) button.
#define BTNRIGHT  BTN2 //!< The right button.
#define BTNLEFT   BTN3 //!< The left button.
#define BTNCENTER BTN4 //!< The enter button.
#define BTNUP     BTN5 //!< The up button.
#define BTNDOWN   BTN6 //!< The down button.
These are defined in ev3_constants.h. You might try passing in BTNEXIT, for example.

The code in ev3_button.c is modeled after the code Andy Shaw wrote for leJOS, iirc. So you could use the function in the sample program which waits up to a specified number of milliseconds for any button press and returns NONE or the id of the pressed button. Just look at the sample program for the button API.

John Hansen