Alvaro Cassinelli
/
skinGames_II
save loops
Diff: main.cpp
- Revision:
- 0:df6fdd9b99f0
- Child:
- 1:3be7b7d050f4
diff -r 000000000000 -r df6fdd9b99f0 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Dec 02 04:39:15 2014 +0000 @@ -0,0 +1,1103 @@ +#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); + DigitalOut ledSwitchOne(LED_SWITCH_ONE); + +// 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 + + int renderFraction=1;// when 1, the blob needs to be rendered completely before update; 2 means half of it, etc. 0 means render all the time. + +// NEW (1.4.2014): change modes by switch for fast demos: +int totalNumberDemos=13; +int currentDemoMode=0; +void changeMode(int mode); + +//---------- 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 of the mbed + 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 measureReadPeriod; // timer for reading hardare or communication data (I will read in ms) +#define periodReadingData 30 + +//Timer measureUpdatePeriod; + + +// to get serial commands (not necessary perhaps) +void processSerial(); + +int main() { + + // Initialize the hardware (laser powers, positions...): + IO.init(); + ledSwitchOne=0; + + // ------------------------------- + // Set the Ethernet port: + // pc.printf("Setting up...\r\n"); + //printf("Setting up...\r\n"); + EthernetErr ethErr = eth.setup(); + if (ethErr) { + // pc.printf("Error %d in setup.\r\n", ethErr); + // return -1; + } + // pc.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: + + // blobconf.initConfig(ONE_ELASTIC_FOLLOWING); + + // 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, 1); // 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(); + + measureReadPeriod.start(); + + while (true) { + + // Process sensing buffer and compute light forces (for all blobs here, or in the update function for each one) + blobconf.processSensedData(); + + if (lsr.endedFractionDisplay(renderFraction)) {// (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(); + + } else { + + // do a delay to equilibrate the timings? + //wait_us(6700); + } + + + // COMMUNICATION and HARDWARE CONTROL: + + // (a) Reading commands: + if (measureReadPeriod.read_ms()>periodReadingData) { + measureReadPeriod.stop(); + + // 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 + + // Potentiometer, switches, etc: + //(1) Check for change of threshold mode button (switch one): + //!!! ATTENTION: this does not work very well to say the truth: bouncing+adc settings problems... better use a TWO STATE SWITCH: + bool stateswitch; + if (IO.switchOneCheck(stateswitch)) { + //if (stateswitch) pc.printf("Setting AUTO threshold mode\n"); else pc.printf("Setting FIXED threshold mode\n"); + // for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setThresholdMode((stateswitch? 1 : 0)); + currentDemoMode=(currentDemoMode+1)%totalNumberDemos; + // ledSwitchOne=(stateswitch? 1 :0); // this switch has a built-in led + ledSwitchOne=1; wait_ms(500) ; ledSwitchOne=0; + changeMode(currentDemoMode); + blobconf.allResume(); + } + if (IO.twoStateSwitchCheck(stateswitch)) { // if there is a change... + for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setThresholdMode((stateswitch? 1 : 0)); + } + + //(2) in case the the second switch was pressed, check the current pot value and update the fixed threshold (regardless of the threshold mode) + // NOTE: using the potentiometer FAILS because I cannot properly switch back and forth between ADC modes (burst and normal) + if (IO.switchTwoCheck(stateswitch)) { + timerForRendering.detach(); + IO.showLimitsMirrors(5); // in seconds + timerForRendering.attach(&lsr, &simpleLaserSensingRenderer::laserRenderThread, RENDER_INTERVAL); + + // IO.updatePotValue(); + // for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setFixedThreshold(IO.potValue); + // pc.printf("Got :%d\n", IO.potValue); + } + + if (rotaryEncoder1.CheckNew()) { + for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setFixedThreshold(rotaryEncoder1.Get()); + //pc.printf("Fixed Threshold :%d\n", rotaryEncoder1.Get()); + } + + // (3) Change additional mirror delay from rotary encoder: + if (rotaryEncoder2.CheckNew()) { + for (int i=0; i< blobconf.numBlobs; i++) blobconf.blobArray[i]->displaySensingBuffer.setDelayMirrors(rotaryEncoder2.Get()); + // pc.printf("Mirror delay :%d\n", rotaryEncoder2.Get()); + } + + + // Restart the timer: + measureLoopPeriod.reset(); + measureReadPeriod.start(); + } + + // 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>1000) { + myled = 1; + measureLoopPeriod.stop(); + sendMes.setTopAddress("/timeloop"); + sendMes.setSubAddress("/"); + long x=(long)(int(measureLoopPeriod.read_us()/1000.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], "setAllRandomColor" ) ) { + blobconf.randomizeAllColors(); + } + + 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); + + if ( blobconf.numBlobs>1) + for (int i=0; i< blobconf.numBlobs; i++) { + blobconf.blobArray[i]->displaySensingBuffer.setDelayMirrors(2); + blobconf.blobArray[i]->angleCorrectionForceLoop=-8;// in degrees + } + } + } + + 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], "setSpeed" ) ) { + int value=data[0]; // value 1 means a speed of 0.1, value 10, a speed of 1, etc. + 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]->setSpeed((float)(0.1*value)); + } + } + } + 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], "setSize" ) ) { + int value=data[0]; // this is the direct size of the scafold + 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]->setSize((float)(1.0*value)); + } + } + 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]->addAngleCorrection(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]->addAngleCorrection(-value); + } + + // ADJUST MIRROR DELAY (angle or mirror delay are equivalent in case of circular blobs): + else if (!strcmp( address[0], "adjustPlusDelay" ) ) { + 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); + } + } + else if (!strcmp( address[0], "adjustMinusDelay" ) ) { + 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); + } + } + + else if (!strcmp( address[0], "adjustRenderFraction" ) ) { // for all spots... + 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) + renderFraction=value; + if (renderFraction>lsr.configTotalPoints) renderFraction=0;// update ALL the time (per-point) + if (renderFraction<1) renderFraction=1; + } + } + + else if (!strcmp( address[0], "adjustPlusRenderFraction" ) ) { + 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) + renderFraction+=value; + if (renderFraction>lsr.configTotalPoints) renderFraction=0;// meaning: update ALL the time (per-point) + } + } + + else if (!strcmp( address[0], "adjustMinusRenderFraction" ) ) { + 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) + renderFraction=((renderFraction-value)>=1? renderFraction-value : 1); + } + } + + + + // THRESHOLD MODE, and PARAMETERS: + //(1) Set the threshold mode: + else if (!strcmp( address[0], "autoThreshold" ) ) { + int value=data[0]; // 1 (auto) or 0 (fixed) + 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.setThresholdMode(value); + } + // set led and switch state (global value of threshold): + IO.setSwitchOneState(value>0); + } + + // (a) AUTO THRESHOLD: + // MINIMUM 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)); + } + } + // THRESHOLD FACTOR: + //(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)); + } + } + // MINIMUM ACCEPTABLE INTENSITY: + // 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); + } + } + + // (b) FIXED THRESHOLD: + // Adjust fixedThreshold (directly): + else if (!strcmp( address[0], "adjustFixedThreshold" ) ) { + 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.setFixedThreshold(value); + } + } + + else if (!strcmp( address[0], "printParameters" ) ) { + pc.printf("Fraction display for the config: %d\n", renderFraction); + blobconf.printParameters(); + } + + // ===================== 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); + + } + } +} + +void changeMode(int mode) { + + strcpy(address[0],""); + strcpy(address[1],""); + data[0]=-1; + data[1]=-1; + +switch(mode) { + case 0: + strcpy(address[0],"spot_following"); + data[0]=1; + interpretCommand(); + break; + case 1: + strcpy(address[0],"spot_following"); + data[0]=2; + interpretCommand(); + break; + case 2: + strcpy(address[0],"spot_bouncing"); + data[0]=1; + interpretCommand(); + break; + case 3: + strcpy(address[0],"spot_bouncing"); + data[0]=4; + interpretCommand(); + break; + case 4: + strcpy(address[0],"spot_lorentz"); + data[0]=1; + interpretCommand(); + break; + case 5: + strcpy(address[0],"spot_lorentz"); + data[0]=2; + interpretCommand(); + break; + case 6: + strcpy(address[0],"rain_mode"); + data[0]=2; + interpretCommand(); + break; + case 7: + strcpy(address[0],"rain_mode"); + data[0]=5; + interpretCommand(); + break; +case 8: + strcpy(address[0],"air_hockey"); + data[0]=1; + interpretCommand(); + break; + case 9: + strcpy(address[0],"spot_tracking"); + data[0]=1; + interpretCommand(); + // here, let's make the min contrast smaller, for finger tracking: + strcpy(address[0],"adjustContrast"); + data[0]=120; + interpretCommand(); + break; + case 10: +strcpy(address[0],"elastic_mouth"); +interpretCommand(); + break; + case 11: + strcpy(address[0],"elastic_following"); + interpretCommand(); + break; + case 12: + strcpy(address[0],"elastic_mouth_small"); + interpretCommand(); + break; + //case 9: + //strcpy(address[0],"pac_man"); + //interpretCommand(); + //break; + //case 10: + // strcpy(address[0],"circular_pong"); + // data[0]=3; + // interpretCommand(); + //break; + // case 10: + // strcpy(address[0],"vertical_pinball"); + // data[0]=2; + // interpretCommand(); + //pbreak; + //case 11: + //strcpy(address[0],"fish_net"); + // data[0]=3; + // interpretCommand(); + //break; + + } + + } + + + +// ================ 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()); +} + +*/