Laser Sensing Display for UI interfaces in the real world

Dependencies:   mbed

Fork of skinGames_forktest by Alvaro Cassinelli

main.cpp

Committer:
mbedalvaro
Date:
2012-10-29
Revision:
32:52273c3291fe
Parent:
31:5f039cbddee8
Child:
33:43e8bc451ef0

File content as of revision 32:52273c3291fe:

#include "mbed.h"
#include "hardwareIO.h"
#include "mbedOSC.h"
#include "blobConfig.h"
#include "simpleLaserRenderer.h"

extern "C" void mbed_reset();

blobConfig blobconf;
simpleLaserSensingRenderer lsr;

// For tests:
DigitalOut myled(LED1);
DigitalOut myled2(LED2);
DigitalOut myled3(LED3);


// To test the time it takes for executing one loop in the main program:
//#define LOOPTIMECOMPUTE

// To get serial commands (for debug, or other things using a Terminal - for instance, a laser scan)
#define SERIAL_COMMANDS

//----------  ETHERNET / OSC related (in the future, put somewhere else...): -------------------------------------------
// mbed IP address (server):
#ifdef DHCP
EthernetNetIf eth;
#else
EthernetNetIf eth(
    IpAddr(10,0,0,2), //IP Address
    IpAddr(255,255,255,0), //Network Mask
    IpAddr(10,0,0,1), //Gateway
    IpAddr(10,0,0,1)  //DNS
);
#endif

//uint8_t serverMac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
uint8_t serverIp[]  = { 10, 0, 0, 2 }; // not needed perhaps!
int  serverPort  = 10000;

//uint8_t destIp[]  = {10, 0, 0, 3}; 
uint8_t destIp[]  = {255, 255, 255, 255};  // broadcast, so we can use several computers for sound, etc
int  destPort = 12000;

char *topAddress="/mbed";
char *subAddress[3]={ "/test1" , "/test2" , "/test3" };

OSCMessage recMes;
OSCMessage sendMes;

OSCClass osc;
//OSCClass osc(&recMes);  // instantiate OSC communication object, and set the receiver container from the OSC packets

void processOSC(UDPSocketEvent e);
// ----------------------------------------------------------------------------------------------------------------------

// Tickers:
Ticker timerForRendering;
//Ticker timerForSendingData; // better use a timer, so as not to interrupt the exact laser display ticker

// Timers:
Timer measureLoopPeriod;
//Timer measureUpdatePeriod;


// to get serial commands (not necessary perhaps)
void  processSerial();

int main() {

    // Initialize the hardware (laser powers, positions...):
    IO.init();

    // -------------------------------
    // Set the Ethernet port:
    printf("Setting up...\r\n");
    EthernetErr ethErr = eth.setup();
    if (ethErr) {
        printf("Error %d in setup.\r\n", ethErr);
        return -1;
    }
    printf("Setup OK\r\n");

    //(1) Sending message:
    // Set IP and Port:
    sendMes.setIp( destIp );
    sendMes.setPort( destPort );
    // Set data:
    // sendMes.setTopAddress(topAddress);

    //setting osc functionnality:
    //(2) Receiving:
    // recMes.setIp( serverIp ); // not needed?
    osc.setReceiveMessage(&recMes); // this sets the receiver container for the OSC packets (we can avoid doing this if we use osc.getMessage() to get messages)
    osc.begin(serverPort, &processOSC); // binds the upd (osc) messages to an arbitrary listening port ("server" port), and callback function
    // -------------------------------

    /* // sending seems not to work right after setting the osc object??
    wait(1);
    sendMes.setTopAddress("starting");
    sendMes.setSubAddress("");
    osc.sendOsc( &sendMes );
    */

    // initialize with the desired blob configuration:

    // Tested modes:
    blobconf.clearConfig();
//    blobconf.addOneElasticLoopContractCentral();
//  blobconf.addOneElasticContourFollowing();

//    blobconf.addOneRigidLoopBouncing();
//  blobconf.addOneRigidLoopBouncing();
//blobconf.addOneRigidLoopFollowing();
   // blobconf.addOneRigidLoopFollowing();
  //blobconf.addOneElasticLoopContractCentralFast();
 //blobconf.addOneRigidLoopTest();

// START WITH TWO FOLLOWING SPOTS (exhibition Tokyo Design Week):
  blobconf.initConfig(FOLLOWING_SPOTS, 2); // value is the nb of spots instantiated
// Make them of different color:
 blobconf.blobArray[0]->setBlueColor(1);
 blobconf.blobArray[1]->setGreenColor(1);
 // start in no stand by mode:
 blobconf.allResume();

    // Important: first, set the initial position for all the blobs, this will be useful because
    // when changing modes we can use the previous central position...
    // blobconf.setInitialPos(CENTER_AD_MIRROR_X, CENTER_AD_MIRROR_Y);

// draw the config once before activating the laser buffer:
    blobconf.draw();
    lsr.startFullDisplay();

    // RENRERER (attn: setConfigToRender must be called when the blobconf is set - i.e., the number of blobs and number of points/blob is fixed)
    lsr.setConfigToRender(&blobconf);

    // Timer on the rendering function of the oneLoop object:
    // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL); // the address of the object, member function, and interval (in seconds)
    timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL); // the address of the object, member function, and interval (in seconds)

    // Timer for sending OSC data:
    // timerForSendingData.attach(&blobconf, &blobConfig::sendConfData, 0.025); // time in seconds (25ms -> 40Hz)

    //==========================================   INFINITE LOOP (in USER PROGRAM CONTEXT) ===================================================================
#ifdef LOOPTIMECOMPUTE
    int timeCounterNum=1000;
#endif

    //measureUpdatePeriod.start();

    while (true) {

        if (lsr.endedFullDisplay()) {

            //  measureUpdatePeriod.stop();
            //  measureUpdatePeriod.reset();

            //  __disable_irq();

            // update config dynamics (this also could be threaded?):
            blobconf.update();


            // draw the config (note: each kind of blob renders differently)
            blobconf.draw();
            
            
        // (b)Sending Data: // PUT THIS IN AN INTERRUPT OR USE A TIMER!!! it may be TOO FAST...
        // NOTE: better use a timer, so the only ISR "ticker" is the laser rendering (otherwise the laser rendering will be interrupted by the sending of data - the other way is ok):
        // NOTE: timer for sending is now not COMMON to all blobs, but depends on the blob (it could depend on the CONFIG only, but this can be simulated by using 
        //       per blob timer counter. 
        blobconf.sendConfData();


        lsr.startFullDisplay(); // this start the point-display counter (wherever the actual laser is). Before update and draw, the counter needs to be reset.


            // __enable_irq();

            //     measureUpdatePeriod.start();

        }


        // COMMUNICATION:
        // (a) Reading commands:
        // Ethernet:
        Net::poll(); // this will take care of calling processOSC(UDPSocketEvent e) when a new packet arrives.

        // Serial:
#ifdef SERIAL_COMMANDS
        if (pc.readable()>0) processSerial();
#endif


        // text:
        /*
         sendMes.setTopAddress("/hello");
         sendMes.setSubAddress("/daito");  // ATTENTION: the host computer needs to know in advance how many points are in the loop (I did not implement "bundle" messages yet...)
         int x=(long)10;
         sendMes.setArgs( "i", &x);
         osc.sendOsc( &sendMes );
         */

#ifdef LOOPTIMECOMPUTE
        if (timeCounterNum>50)  myled = 0;
        // if (timeCounterNum%10==0) blobconf.sendConfData();
        if (timeCounterNum>100) {
            myled = 1;
            measureLoopPeriod.stop();
            sendMes.setTopAddress("/timeloop");
            sendMes.setSubAddress("/");
            //  long x=(long)(int(measureLoopPeriod.read_us()/100.0));
            // long x=(long)(blobconf.blobArray[0]->displaySensingBuffer.lsdTrajectory.size());
            // long x=(long)(blobconf.blobArray[0]->normRecenteringVector);
            long x=(long)(1000*blobconf.blobArray[0]->displaySensingBuffer.lsdTrajectory[0].intensity);
            sendMes.setArgs( "i", &x);
            osc.sendOsc( &sendMes );
            timeCounterNum=0;
            measureLoopPeriod.reset();
            measureLoopPeriod.start();
        } else timeCounterNum++;
#endif

    }
}

// ================= INTERPRET COMMAND =========================
// NOTE: the following arrays are GLOBAL (used in processOSC and processSerial, as well as in interpretCommand function):
// max of two addresses (top and sub), of a max length of 24 characters:
char address[2][24];
//long auxdata[2]; // to store a max of two arguments (note: we will only use LONGs)
int data[2]; // this is to have -1 as NO DATA, to detect errors.

//interpretCommand(const char& address[2][], const int& data[2]) {
void interpretCommand() {
    // (I) =========================================== SPECIAL FUNCTIONS (reset, rescan LUT, etc) ====================================================
    if ( !strcmp(address[0], "takeSnapshot" ) ) {
     int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
       
        // for test:
        for (int i=0; i<2 ; i++) {
            myled = 1;
            wait(0.1);
            myled = 0;
            wait(0.1);
        }

        // First, we need to disable the threaded display for the loop:
        timerForRendering.detach();

        // Then, do the scan (sending values on SERIAL port):
        IO.scan_serial(value);

        // Finally, start again threaded display:
        timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
        // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        
        }
    }

    else if ( !strcmp(address[0], "mbedReset" ) ) mbed_reset();
    
    else if (!strcmp(address[0], "showMirrorLimits")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
          timerForRendering.detach();
          IO.showLimitsMirrors(value);
          timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);     
        }
       }

    else if ( !strcmp(address[0], "calibrate" ) ) {
        // First, we need to disable the threaded display for the loop:
        timerForRendering.detach();
        // RESCAN (and save LUT table):
        IO.scanLUT();
        // Finally, start again threaded display:
        timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
        // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
    }

    // (II) ========================================= GLOBAL CONFIG and HARDWARE COMMANDS ===========================================

    else if ( !strcmp(address[0], "setAllColor" ) ) {
        int value=data[0]; // this is the color (RGB bits)
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
                blobconf.allSetColor(value);
    }
    }

    else if ( !strcmp(address[0], "setAllGreenColor" ) ) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            if (value==0) 
                blobconf.allSetGreen(1);
            else 
                blobconf.allSetGreen(0);
        }
    }
    
    else if ( !strcmp(address[0], "setAllBlueColor" ) ) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            if (value==0) 
                blobconf.allSetBlue(1);
            else 
                blobconf.allSetBlue(0);
        }
    }
    
    // NOTE: RED either afect the lock in, or some other red laser... not yet done. 
    
    else if ( !strcmp(address[0], "testPower" ) ) {
    // First, we need to disable the threaded display for the loop:
        timerForRendering.detach();
        
    // Note: arguments is first 3 bits to set the laser powers (3 LSB bits to set each color)
    // and then the second argument is the number of seconds to wait for the measurment 
        int value1=data[0], value2=data[1];
        if ((value1!=-1)&&(value2!=-1)) { // otherwise do nothing, this is a reception error (there was no data)
            
            // Set position of mirrors:
            IO.writeOutX(CENTER_AD_MIRROR_X);
            IO.writeOutY(CENTER_AD_MIRROR_Y);
            
            for (int i=0; i<3 ; i++) {
            myled3 = 1;
            wait(0.2);
            myled3 = 0;
            wait(0.2);
           }
            
            // Set laser power:
            IO.setRGBPower((unsigned char)value1);
            
            // Wait...
            wait(value2);// in seconds
            //Timer t;
            //t.start();
            //while(t.read_ms()<value2*1000);
            //t.stop();
            }
        
     // Finally, start again threaded display:
        timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);    
        
    }
    


    // SIMPLE BEHAVIOUR MODES (to be read from an XML file in the future):
    else if (!strcmp(address[0], "elastic_following")) { //
        timerForRendering.detach();
        
        blobconf.initConfig(ONE_ELASTIC_FOLLOWING);
        
        lsr.setConfigToRender(&blobconf);
        timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
        // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);

    } else if (!strcmp(address[0], "elastic_mouth")) { //
        timerForRendering.detach();
        
        blobconf.initConfig(ONE_ELASTIC_MOUTH);
        
        lsr.setConfigToRender(&blobconf);
        timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
        // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
    
     } else if (!strcmp(address[0], "elastic_mouth_small")) { //
        timerForRendering.detach();
        
         blobconf.initConfig(ONE_ELASTIC_MOUTH_SMALL);
         
        lsr.setConfigToRender(&blobconf);
        timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
        // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
    }
    else if (!strcmp(address[0], "spot_bouncing")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            timerForRendering.detach();
            
            blobconf.initConfig(BOUNCING_SPOTS, value); // value is the nb of spots instantiated

            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        }
    }

else if (!strcmp(address[0], "spot_lorentz")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            timerForRendering.detach();
           
            blobconf.initConfig(LORENTZ_SPOTS, value); // value is the nb of spots instantiated

            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        }
    }
   
   else if (!strcmp(address[0], "air_hockey")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            timerForRendering.detach();
            
            blobconf.initConfig(AIR_HOCKEY_GAME, value); // value is the nb of spots instantiated

            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        }
    }
      
   else if (!strcmp(address[0], "rain_mode")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            timerForRendering.detach();
            
            blobconf.initConfig(RAIN_MODE, value); // value is the nb of spots instantiated

            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        }
    }
    
   
   else if (!strcmp(address[0], "spot_following")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)

            timerForRendering.detach();
            
            blobconf.initConfig(FOLLOWING_SPOTS, value); // value is the nb of spots instantiated
            
            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        }
    }

    else if (!strcmp(address[0], "circular_pong")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)

            timerForRendering.detach();
            
            blobconf.initConfig(CIRCULAR_PONG_GAME, value); // value is the nb of spots instantiated
            
            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        }
    }
    
 else if (!strcmp(address[0], "fish_net")) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)

            timerForRendering.detach();
            
            blobconf.initConfig(FISH_NET_GAME, value); // value is the nb of spots instantiated
            
            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
        }
    }


    else if (!strcmp(address[0], "vertical_pinball")) {
           int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)

            timerForRendering.detach();
            
            blobconf.initConfig(VERTICAL_PINBALL_GAME, value); 
            
            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
    }
    }
    
      else if (!strcmp(address[0], "pac_man")) {
            timerForRendering.detach();
            
            blobconf.initConfig(PAC_MAN_GAME); 
            
            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
    }

   else if (!strcmp(address[0], "spot_tracking")) {
            timerForRendering.detach();
            
            blobconf.initConfig(ONE_TRACKING_SPOT); // value is the nb of spots instantiated
            
            lsr.setConfigToRender(&blobconf);
            timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
            // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
    }

    else if (!strcmp(address[0], "spot_test")) {
        timerForRendering.detach();
        // blobconf.computeBoundingBox();
        blobconf.clearConfig();
        blobconf.addOneRigidLoopTest();
        lsr.setConfigToRender(&blobconf);
        timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
        // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
    }

    // other:

    else if ( !strcmp(address[0], "standby" ) ) { // will put ALL the blobs in stand by mode (no update function)
        blobconf.allStandBy(); // will avoid the update function
    } 
    else if ( !strcmp(address[0], "resume" ) ) { 
        blobconf.allResume(); // Update function is called for all the blobs
    }

    // (III) ========================================= Loop control (parameters, etc) ===========================================
 
    else if (!strcmp( address[0], "speedFactor" ) ) {
      int value=data[0]; // value 100 means no change of speed. 200 is twice as fast, 50 is half as fast. 
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            for (int i=0; i< blobconf.numBlobs; i++) {
                //if ((!strcmp(blobconf.blobArray[i].spotName, "rigid_following"))||(!strcmp(blobconf.blobArray[i].spotName, "rigid_following"))) {
                blobconf.blobArray[i]->speedFactor((float)(1.0*value/100.0));
            }
      }
    }
    
  else if (!strcmp( address[0], "sizeFactor" ) ) {
      int value=data[0]; // value 100 means no change of sice. 200 is twice as big, 50 is half as big. 
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
          for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->sizeFactor((float)(1.0*value/100.0));
      }
    }
    
    // ADJUST MIRROR ANGLE CORRECTION:
    else if (!strcmp( address[0], "adjustPlusAngle" ) ) {
      int value=data[0]; // this is not a factor, but an additive quantity to the current delay
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
          for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.addDelayMirrors(value);
      }
    }
    else if (!strcmp( address[0], "adjustMinusAngle" ) ) {
      int value=data[0]; // this is not a factor, but an substractive quantity to the current delay 
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
        for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.addDelayMirrors(-value);
      }
    }
   
    else if (!strcmp( address[0], "adjustPlusAngle" ) ) {
      int value=data[0]; // value 100 means no change of speed. 200 is twice as fast, 50 is half as fast. 
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
          for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.addDelayMirrors(value);
      }
    }

// ADJUST MIN CONTRAST RATIO 
// (1) using multiplicative factor:
    else if (!strcmp( address[0], "adjustMultContrast" ) ) {
      int value=data[0]; // value 100 means no change of the current contrast value. 200 means a min contrast 
      // that is twice as large and 50 is half as large as before. 
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
        for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.multMinContrastRatio((float)(1.0*value/100.0));
      }
    } 
// (2) directly:
 else if (!strcmp( address[0], "adjustContrast" ) ) {
      int value=data[0];  // value is in PERCENT (100=1, 50 = 0.5...)
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
        for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setMinContrastRatio((float)(1.0*value/100.0));
      }
    }
    
 // ADJUST THRESHOLD RATIO:
    //(1) using multiplicative factor:
    else if (!strcmp( address[0], "adjustMultThreshold" ) ) {
      int value=data[0]; // value 100 means no change of the current contrast value. 200 means a min contrast 
      // that is twice as large and 50 is half as large as before. 
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
        for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.multThresholdFactor((float)(1.0*value/100.0));
      }
    } 
     //(2) directly:
    else if (!strcmp( address[0], "adjustThresholdFactor" ) ) {
      int value=data[0]; // value is in PERCENT (100=1, 50 = 0.5...)
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
        for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setThresholdFactor((float)(1.0*value/100.0));
      }
    } 
    
    // Adjust minimum acceptable intensity:
     else if (!strcmp( address[0], "adjustMinAcceptableIntensity" ) ) {
      int value=data[0]; // value is DIRECT value (0 to 255)
      if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)     
        for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setMinAcceptableIntensity(value);
      }
    } 
    
 // ===================== SEND DATA MODES =======================
 
    else if (!strcmp( address[0], "sendOSC" ) ) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            for (int i=0; i< blobconf.numBlobs; i++) {
                blobconf.blobArray[i]->sendOSC=(value>0);
            }
        }
    }

    else if (!strcmp( address[0], "sendArea" ) ) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            for (int i=0; i< blobconf.numBlobs; i++) {
                blobconf.blobArray[i]->sendingBlobArea=(value>0);
            }
        }
    }

    else if (!strcmp( address[0], "sendPos" ) ) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            for (int i=0; i< blobconf.numBlobs; i++) {
                blobconf.blobArray[i]->sendingLoopPositions=(value>0);
            }
        }
    }

    else if (!strcmp( address[0], "sendRegions" ) ) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            for (int i=0; i< blobconf.numBlobs; i++) {
                blobconf.blobArray[i]->sendingLoopRegions=(value>0);
            }
        }
    }

    else if (!strcmp( address[0], "sendTouched" ) ) {
        int value=data[0];
        if (value!=-1) { // otherwise do nothing, this is a reception error (there was no data)
            for (int i=0; i< blobconf.numBlobs; i++) {
                blobconf.blobArray[i]->sendingTouched=(value>0);
            }
        }
    }



}

//============= RECEIVE OSC COMMANDS =========================
// This is the callback function called when there are packets on the listening socket. It is not nice to have it
// here, but for the time being having a "wrapping global" is the simplest solution (we cannot pass a member-function pointer
// as handler to the upd object).
void processOSC(UDPSocketEvent e) {
    osc.onUDPSocketEvent(e);

    if (osc.newMessage) {
        // in fact, there is no need to check this if using the method of a global callback function - it is clear this is a new packet... however, it may be
        // interesting to use a timer, and process data (answers, etc) only after a certain amount of time, so as to avoid blocking the program in IRQ context...

        // Acquire the addresses and arguments and put them in the GLOBAL variables:
        strcpy(address[0],"");
        strcpy(address[1],"");
        for (int i=0; i<recMes.getAddressNum(); i++)  strcpy(address[i],recMes.getAddress(i)); // NOTE: up to the rest of the program to check if address[1] is really not null
        // Acquire data:
        data[0]=-1;
        data[1]=-1;
        for (int i=0; i<recMes.getArgNum(); i++) data[i]=(int)recMes.getArgInt(i);

        // Finally, interpret the command:
        interpretCommand();//address, data);

    }
}

//============= RECEIVE SERIAL COMMANDS =========================
//
// NOTE: - NUMERIC PARAMETERS have to be send BEFORE the command word. They must be sent as ASCII DEC, without end character.
//       - Commands words SHOULD NOT have numbers in it. They should be C compliant STRINGS (ended with character '0')
//       - order is irrelevant: we can send 10 RADIUS or RADIUS 10.

// String to store ALPHANUMERIC DATA (i.e., integers, floating point numbers, unsigned ints, etc represented as DEC) sent wirelessly:
char stringData[24]; // note: an integer is two bytes long, represented with a maximum of 5 digits, but we may send floats or unsigned int...
int indexStringData=0;//position of the byte in the string

// String to store COMMAND WORDS:
char stringCommand[24]; // note: an integer is two bytes long, represented with a maximum of 5 digits, but we may send floats or unsigned int...
int indexStringCommand=0;
bool commandReady=false; // will become true when receiving the byte 0 (i.e. the '/0' string terminator)

void processSerial() {

    while (pc.readable()>0) {


        char val =pc.getc();
        // pc.printf("Got :%d\n", incomingByte);
        //pc.putc(incomingByte);

        // Save ASCII numeric characters (ASCII 0 - 9) on stringData:
        if ((val >= '0') && (val <= '9')) { // this is 45 to 57 (included)
            stringData[indexStringData] = val;
            indexStringData++;
        }

        // Save ASCII letters in stringCommand:
        if ((val >= 'A') && (val <= 'z')) { // this is 65 to 122 (included)
            stringCommand[indexStringCommand] = val;
            indexStringCommand++;
        }
        // is command ready?
        if (val=='/') {
            commandReady=true;
            stringCommand[indexStringCommand] = 0; // string termination.
            indexStringCommand=0; // reset index string for acquiring next command
            //Serial.println(stringCommand);
        }

        // COMMANDS (with or without numeric parameters):
        if (commandReady==true) { // it means we can interpret the command string:
            commandReady=false;

            stringData[indexStringData] = 0 ;// string termination for numeric values;
            indexStringData=0;

            // PARSE DATA: (TO DO!!!!!!!!!!!!!!):

            // (a) Parse command (get address[0] and address[1]):
            //ex:  "/1/standBy" -- > address[0]="1" and address[1]="standBy"
            // address[2]

            // Serial.println(stringCommand);
            // Serial.println(stringData);

            // (b) Parse data:

            // char address[2][24];
            //long auxdata[2]; // to store a max of two arguments (note: we will only use LONGs)
            //int data[2]; // this is to have -1 as NO DATA, to detect errors.

            // FOR THE TIME BEING there is no parsing for serial commands:

            // SCANNING:
            if (!strcmp(stringCommand , "takeSnapshot")) {
                // First, we need to disable the threaded display for the loop:
                timerForRendering.detach();

                // Then, do the scan (sending values on serial port):
                IO.scan_serial(atoi(stringData));

                // Finally, start again threaded display:
                timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
                // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
            } else if (!strcmp(stringCommand , "REDON"))   IO.setRedPower(1); // pc.printf("%d\n",incomingByte);

            else if (!strcmp(stringCommand , "REDOFF"))  IO.setRedPower(0);

            else if  (!strcmp(stringCommand , "READVALUE")) pc.printf("Value read: %f", lockin.getSmoothValue());//lockin.getLastValue());/

            else if (!strcmp(stringCommand , "mbedReset"))  mbed_reset();

            else if (!strcmp(stringCommand , "calibrate")) {
                // First, we need to disable the threaded display for the loop:
                timerForRendering.detach();
                // RESCAN (and save LUT table):
                IO.scanLUT();
                // Finally, start again threaded display:
                timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL);
                // timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThreadONEBLOBONLY, RENDER_INTERVAL);
            }

            // FINALLY, interpret commands (but only after parsing):
            //  interpretCommand();//address, data);

        }
    }
}



// ================ MISCELANEA

/* EXAMPLE SEND/RECEIVE on PROCESSING:

// oscP5sendreceive by andreas schlegel
// example shows how to send and receive osc messages.
// oscP5 website at http://www.sojamo.de/oscP5

import oscP5.*;
import netP5.*;

OscP5 oscP5;
NetAddress myRemoteLocation;

void setup() {
  size(400,400);
  frameRate(25);
  // start oscP5, listening for incoming messages at port 12000
  oscP5 = new OscP5(this,12000);

  // myRemoteLocation is a NetAddress. a NetAddress takes 2 parameters,
  // an ip address and a port number. myRemoteLocation is used as parameter in
  // oscP5.send() when sending osc packets to another computer, device,
  // application. usage see below. for testing purposes the listening port
  // and the port of the remote location address are the same, hence you will
  // send messages back to this sketch.
  myRemoteLocation = new NetAddress("10.0.0.2",10000);
}


void draw() {
  background(0);
}

void mousePressed() {
  // in the following different ways of creating osc messages are shown by example
  OscMessage myMessage = new OscMessage("/mbed/test1");

  myMessage.add(123); // add an int to the osc message

  // send the message
  oscP5.send(myMessage, myRemoteLocation);
}


// incoming osc message are forwarded to the oscEvent method.
void oscEvent(OscMessage theOscMessage) {
  // print the address pattern and the typetag of the received OscMessage
  print("### received an osc message.");
  print(" addrpattern: "+theOscMessage.addrPattern());
  println(" typetag: "+theOscMessage.typetag());
}

*/