Wii Nunchuk via RFM69HW to Duplo 9203 Remote Control Car Kit using ARM mbed on a FRDM-KL25Z

Dependencies:   CRC FastPWM RFM69 USBDevice WakeUp WiiChuk_compat mbed-rtos mbed tlc59108

Fork of wiiNunchuk_compat by Greg Brush

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 #include "WakeUp.h"
00004 #include "FastPWM.h"
00005 #ifndef M_PI
00006 #define M_PI           3.14159265358979323846
00007 #endif
00008 
00009 #include "WiiChuk_compat.hpp"
00010 #include "lib_crc.h"
00011 
00012 #include "tlc59108.h"
00013 
00014 //#include "USBSerial.h"
00015 
00016 #include "RFM69.h"
00017 #define GATEWAY_ID    2
00018 #define NODE_ID       1
00019 #define NETWORKID     100
00020 
00021 // Uncomment only one of the following three to match radio frequency
00022 //#define FREQUENCY     RF69_433MHZ    
00023 #define FREQUENCY     RF69_868MHZ
00024 //#define FREQUENCY     RF69_915MHZ
00025 
00026 const char *directions[8] = { "XR", "RR", "RX", "FR", "FX", "FF", "XF", "RF" };
00027 
00028 #define DEBUG 0
00029 #ifdef DEBUG
00030 #ifdef USBSerial
00031     USBSerial pc;
00032 #else
00033     Serial pc(USBTX, USBRX);
00034 #endif
00035 #else
00036     class Null : public Stream {
00037         public:
00038         Null(): Stream("null") {}
00039         void baud(int) {}
00040 
00041         protected:
00042         virtual int _getc() { return 0; }
00043         virtual int _putc(int c) { return 0; }
00044     };
00045     Null pc;
00046 #endif
00047 #if TARGET_KL25Z
00048 DigitalOut gnd(PTC6);
00049 PwmOut ir(PTA5);
00050 #endif
00051 #define pulse(x, y) ir = 0.5; wait_us(x); ir = 0.0; wait_us(y); 
00052 
00053 /* generated in bash from lirc raw codes */
00054 /*
00055 f=duplo.conf
00056 sed -n '/begin raw_codes/,/end raw_codes/s/name //p' $f|
00057     while read n; do
00058         echo -e "void $n() {$(
00059             grep $n -A 1 $f|tail -n1|
00060                 sed 's/$/ 1000/;s@\([0-9]\+\)[[:space:]]\+\([0-9]\+\)[[:space:]]*@\n\tpulse(\1, \2);@g'
00061         )\n}"
00062     done
00063 */
00064 
00065 void FX() {
00066     pulse(1042, 208);
00067     pulse(208, 208);
00068     pulse(208, 562);
00069     pulse(625, 208);
00070     pulse(208, 625);
00071     pulse(208, 1);
00072 }
00073 void XF() {
00074     pulse(229, 604);
00075     pulse(229, 188);
00076     pulse(229, 188);
00077     pulse(438, 333);
00078     pulse(625, 208);
00079     pulse(1250, 1);
00080 }
00081 void FF() {
00082     pulse(208, 625);
00083     pulse(208, 208);
00084     pulse(208, 208);
00085     pulse(417, 354);
00086     pulse(208, 208);
00087     pulse(208, 1042);
00088     pulse(208, 1);
00089 }
00090 void RX() {
00091     pulse(1042, 208);
00092     pulse(208, 188);
00093     pulse(229, 542);
00094     pulse(646, 1);
00095 }
00096 void XR() {
00097     pulse(208, 1042);
00098     pulse(208, 188);
00099     pulse(229, 542);
00100     pulse(625, 417);
00101     pulse(854, 1);
00102 }
00103 void RR() {
00104     pulse(229, 1021);
00105     pulse(229, 208);
00106     pulse(208, 542);
00107     pulse(229, 188);
00108     pulse(229, 1229);
00109     pulse(229, 1);
00110 }
00111 void RF() {
00112     pulse(208, 625);
00113     pulse(208, 208);
00114     pulse(208, 208);
00115     pulse(417, 333);
00116     pulse(208, 208);
00117     pulse(208, 208);
00118     pulse(208, 1);
00119 }
00120 void FR() {
00121     pulse(229, 1021);
00122     pulse(229, 188);
00123     pulse(229, 542);
00124     pulse(208, 208);
00125     pulse(208, 208);
00126     pulse(208, 625);
00127     pulse(417, 1);
00128 }
00129 void BB() {
00130     pulse(1042, 208);
00131     pulse(208, 208);
00132     pulse(208, 542);
00133     pulse(208, 417);
00134     pulse(208, 208);
00135     pulse(1042, 1);
00136 }
00137 
00138 int mag;
00139 bool central = true;
00140 Timer central_time;
00141 bool central_time_trip = 0;
00142 int stops_sent = 0;
00143 int direction = -1;
00144 Thread *thread;
00145 void ir_thread(void const *args) {
00146     while(1) {
00147         //if (!central)
00148             for(int x = 0; x < 40; x++) {
00149                 #if DEBUG
00150                 pc.printf(central? "stop %s %d\r\n" : "direction %s %d\r\n", directions[direction], stops_sent);
00151                 #endif
00152                 if (central || (x % 4) > mag) {
00153                     if (stops_sent < 50) {
00154                         stops_sent++;
00155                         BB();
00156                     }
00157                 } else {
00158                     stops_sent = 0;
00159                     switch(direction) {
00160                         case 0: XR(); break;
00161                         case 1: RR(); break;
00162                         case 2: RX(); break;
00163                         case 3: FR(); break;
00164                         case 4: FX(); break;
00165                         case 5: FF(); break;
00166                         case 6: XF(); break;
00167                         case 7: RF(); break;
00168                     }
00169                 }
00170                 Thread::wait(5);
00171             }
00172             Thread::wait(30);
00173     }
00174 }
00175 
00176 FastPWM speaker(PTA12);
00177 DigitalOut speaker_gnd(PTC4);
00178 //float speaker = 0;
00179 //float speaker_gnd = 0;
00180 
00181 //global variables used by interrupt routine
00182 volatile int i=512;
00183 float *wave;//[512];
00184  
00185 // Interrupt routine
00186 // used to output next analog sample whenever a timer interrupt occurs
00187 void Sample_timer_interrupt(void)
00188 {
00189     // send next analog sample out to D to A
00190     speaker = wave[i];
00191     // increment pointer and wrap around back to 0 at 128
00192     i = (i+1) & 0x07F;
00193 }
00194 
00195 int Siren_pitch = 1;
00196 void Siren_pitch_flip() {
00197     speaker.period(wave[--i]);
00198     if (i == 0)
00199         i = 128;
00200 }
00201 
00202 Ticker siren_pitch;
00203 Timeout siren_pitch_switch;
00204 bool Siren_last = 0;
00205 void Siren_faster() {
00206     siren_pitch.detach();
00207     siren_pitch.attach(&Siren_pitch_flip, 0.6/128);
00208 }
00209 void Siren_state(bool on) {
00210     if (Siren_last != on) {
00211         pc.printf("siren %d\r\n", on);
00212         siren_pitch.detach();
00213         Siren_pitch = 1;
00214         Siren_pitch_flip();
00215         siren_pitch.detach();
00216         if (on)
00217             siren_pitch.attach(&Siren_pitch_flip, 0.6/128);
00218         //siren_pitch_switch.attach(&Siren_faster, 2.0);
00219         speaker = on ? 0.5 : 0;
00220     }
00221     Siren_last = on;
00222 }
00223 
00224 DigitalOut Headlight_l(PTD7);
00225 DigitalOut Headlight_r(PTB8);
00226 
00227 #define Headlight_swap 3
00228 #define Headlight_flicker 15
00229 uint8_t Headlight_mode = 0;
00230 uint8_t Headlight_phase = 0;
00231 Ticker Headlight_pattern;
00232 void Headlight_interrupt() {
00233     Headlight_phase = (Headlight_phase + 1) % Headlight_flicker;
00234     Headlight_l = Headlight_mode == 1 || (Headlight_mode == 2) && ((Headlight_phase % 2) == 0) && (Headlight_phase * 2 > Headlight_flicker);
00235     Headlight_r = Headlight_mode == 1 || (Headlight_mode == 2) && ((Headlight_phase % 2) == 0) && (Headlight_phase * 2 < Headlight_flicker);
00236 }
00237 
00238 void Headlight_state(bool a, bool b) {
00239     uint8_t mode = b ? 1 : a ? 2 : 0;
00240     if (Headlight_mode != mode) {
00241         if (mode == 1)
00242             i = 512;
00243         Headlight_mode = mode;
00244         Headlight_pattern.detach();
00245         Headlight_interrupt();
00246         if (mode > 0)
00247             Headlight_pattern.attach_us(&Headlight_interrupt, (timestamp_t)(1000000/Headlight_swap/Headlight_flicker));
00248     }
00249 }
00250 
00251 uint8_t indicators = 0;
00252 uint8_t indicator_phase = 0;
00253 Ticker indicator_pattern;
00254 void indicator_interrupt() {
00255     indicator_phase++;
00256 }
00257 void indicator_init() {
00258     indicator_pattern.attach(&indicator_interrupt, 0.5);
00259 }
00260 
00261 TLC59108 *light_bar;
00262 #define light_bar_phases 10
00263 uint8_t light_bar_pattern[light_bar_phases+1][8] = {
00264     { 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
00265     { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
00266     { 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
00267     { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
00268     { 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 },
00269     { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 },
00270     { 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 },
00271     { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 },
00272     { 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 },
00273     { 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00 },
00274     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
00275 uint8_t light_bar_phase = 0;
00276 
00277 void light_bar_thread(const void*) {
00278     while(true) {
00279         Thread::wait(20);
00280         light_bar_phase = Siren_last ? (light_bar_phase + 1) % light_bar_phases : light_bar_phases;
00281         uint8_t* pattern = light_bar_pattern[light_bar_phase];
00282         pattern[4] = Headlight_l ? 0xFF : 0x00;
00283         pattern[5] = Headlight_r ? 0xFF : 0x00;
00284         pattern[6] = (indicator_phase % 2) == 0 && indicators == 1 ? 0xFF : 0x00;
00285         pattern[7] = (indicator_phase % 2) == 0 && indicators == 2 ? 0xFF : 0x00;
00286         pc.printf("light bar %d\r\n", light_bar->setBrightness(pattern));
00287     }
00288 }
00289 Thread *t_light_bar_thread;
00290 
00291 void light_bar_init() {
00292     light_bar = new TLC59108(PTE0, PTE1);
00293     pc.printf("light bar init %d\r\n", light_bar->setLedOutputMode(TLC59108::LED_MODE::PWM_INDGRP));
00294     light_bar->setGroupBrightness(100);
00295     t_light_bar_thread = new Thread(&light_bar_thread);
00296 }
00297 
00298 void sleep_loop(void const *argument) {
00299     while (true) {
00300         sleep();
00301         Thread::wait(100);
00302     }
00303 }
00304 
00305 #ifdef TARGET_KL25Z
00306     PwmOut r(LED_RED);
00307     //float r = 1.0f;
00308     PwmOut g(LED_GREEN);
00309     //float g = 1.0f;
00310     PwmOut b(LED_BLUE);
00311     //float b = 1.0f;
00312     DigitalOut nun_vdd(PTE31);
00313     WiiChuck *nun;
00314     uint8_t nun_settled = 0;
00315     bool nun_init(nunchuk *next) {
00316         //return false;
00317         for (int i = 0; i < 5; i++) {
00318             if (nun)
00319                 delete nun;
00320             nun_vdd = 1;
00321             Thread::wait(50);
00322             nun = new WiiChuck(PTE0, PTE1, pc);
00323             if (nun->Read(&next->X, &next->Y, &next->aX, &next->aY, &next->aZ, &next->C, &next->Z)) {
00324                 nun_settled = 0;
00325                 Thread::wait(10);
00326                 return true;
00327             }
00328             pc.printf("nun_init error\r\n");
00329             nun_vdd = 0;
00330             Thread::wait(50);
00331         }
00332         return false;
00333     }
00334     void nun_sleep() {
00335         nun_vdd = 0;
00336     }
00337     RFM69 radio(PTD2, PTD3, PTC5, PTD0, PTA13);
00338 #else
00339     WiiChuck *nun = new WiiChuck(p9, p10, pc);
00340     bool nun_init(nunchuk *) {}
00341     void nun_sleep() {}
00342 #endif
00343 
00344 Timer rx_last_contact;
00345 bool rx_last_contact_trip = 0;
00346 bool rx_to_snooze = true;
00347 bool rx_snoozing = false;
00348 bool rx_snoozed() {
00349     if (rx_snoozing) {
00350         pc.printf("still snoozing\r\n");
00351         Thread::signal_wait(0x1);
00352         pc.printf("signalled?\r\n");
00353     }
00354     return true;
00355 }
00356 
00357 void rx_snoozer(void const *mainThreadID) {
00358     pc.printf("snooze rx %f\r\n", rx_last_contact.read());
00359     
00360     rx_snoozing = true;
00361     radio.sleep();
00362     WakeUp::set((rx_last_contact_trip |= (rx_last_contact.read() < 60))? 2 : 10);
00363     deepsleep();
00364     stops_sent = 0;
00365     rx_snoozing = false;
00366     rx_to_snooze = true;
00367     pc.printf("unsnooze rx\r\n");
00368     osSignalSet((osThreadId)mainThreadID, 0x1);
00369 }
00370 
00371 int main()
00372 {
00373     RtosTimer rx_snooze(&rx_snoozer, osTimerOnce, (void*)osThreadGetId());
00374 
00375 #ifndef USBSerial
00376     pc.baud(115200);
00377 #endif
00378 
00379     r = g = b = 1;
00380     gnd = 0;
00381     ir.period_us(1000/38);
00382 
00383     speaker_gnd = 0;
00384     speaker = 0;
00385     //speaker.period(1.0/200000.0);
00386 
00387     /*
00388     speaker = 0.2;
00389     while(1) {
00390     speaker.period(.8/554.365);
00391     wait(.8);
00392     speaker.period(.8/523.251);
00393     wait(.8);
00394     }
00395     //speaker = 0.2;
00396     // set up a timer to be used for sample rate interrupts
00397     Ticker Sample_Period;
00398     // precompute 128 sample points on one sine wave cycle 
00399     // used for continuous sine wave output later
00400     for(int k=0; k<128; k++) {
00401         wave[k]=((1.0 + sin((float(k)/128.0*6.28318530717959)))/2.0);
00402         // scale the sine wave from 0.0 to 1.0 - as needed for AnalogOut arg 
00403     }
00404     // turn on timer interrupts to start sine wave output
00405     // sample rate is 500Hz with 128 samples per cycle on sine wave
00406     while(1) {
00407         Sample_Period.detach();
00408         Sample_Period.attach(&Sample_timer_interrupt, 1.0/(554.365*128));
00409         wait(.8);
00410         Sample_Period.detach();
00411         Sample_Period.attach(&Sample_timer_interrupt, 1.0/(523.251*128));
00412         wait(.8);
00413     }
00414     // everything else needed is already being done by the interrupt routine
00415     */
00416 
00417     nunchuk n1, n2;
00418     nunchuk *n = &n1, *next = &n2;
00419     bool sender = nun_init(n);
00420     if (sender) {
00421         pc.printf("chuck attached\r\n");
00422         radio.initialize(FREQUENCY, NODE_ID, NETWORKID);
00423         central_time.start();
00424     } else {
00425         pc.printf("chuck unavailable\r\n");
00426         radio.initialize(FREQUENCY, GATEWAY_ID, NETWORKID);
00427         // only relevant to the receiver
00428         thread = new Thread(ir_thread);
00429         rx_last_contact.start();
00430         // these break the nunchuk
00431         wave = new float[i];
00432         int m = i-128;
00433         for(int k = 0; k < m; k++) {
00434             // ramp up
00435             wave[i-k-1] = 1.0/(1000+k*400.0/m);
00436         }
00437         for(int k = 0; k < 128; k++) {
00438             // LFO
00439             wave[127-k] = 1.0/(1400+200*sin(k/128.0*6.28318530717959));
00440         }
00441         WakeUp::calibrate();
00442         light_bar_init();
00443         indicator_init();
00444    }
00445     radio.encrypt("0123456789054321");
00446     //radio.promiscuous(false);
00447     radio.setHighPower(true);
00448     radio.setPowerLevel(20);
00449     radio.rcCalibration();
00450     //radio.readAllRegs();
00451     pc.printf("temp %d\r\n", radio.readTemperature(-1));
00452 
00453     //Thread t_sleep_loop(&sleep_loop);
00454     bool read = false;
00455     while(1) {
00456         if (sender) {
00457             if (/*nun_settled++ > 2 && */central_time_trip |= (central_time.read() > 5)) {
00458                 #if DEBUG
00459                 pc.printf("snooze tx %f\r\n", central_time.read());
00460                 g = 0; Thread::wait(10); g = 1; Thread::wait(10);
00461                 #endif
00462                 nun_sleep();
00463                 radio.sleep();
00464                 Thread::wait(10);
00465                 WakeUp::set(2);
00466                 deepsleep();
00467                 nun_init(next);
00468                 #if DEBUG
00469                 r = 0; Thread::wait(10); r = 1; Thread::wait(10);
00470                 pc.printf("unsnooze tx\r\n");
00471                 #endif
00472             }
00473             nunchuk *last = n;
00474             n = next;
00475             next = last;
00476             read = nun->Read(&n->X, &n->Y, &n->aX, &n->aY, &n->aZ, &n->C, &n->Z);
00477             n->sum = 0;
00478             n->sum = calculate_crc8((char*)n, sizeof(struct nunchuk));
00479         }
00480         else if (rx_snoozed() && radio.receiveDone()) {
00481             rx_last_contact.reset();
00482             rx_last_contact_trip = 0;
00483             rx_snooze.stop();
00484             rx_to_snooze = true;
00485             //pc.printf("rssi %d\r\n", radio.RSSI);
00486             read = (radio.DATALEN == sizeof(struct nunchuk));
00487             if (read) {
00488                 memcpy((void*)next, (const void*)radio.DATA, radio.DATALEN);
00489                 uint8_t sum = next->sum;
00490                 next->sum = 0;
00491                 read = sum == calculate_crc8((char*)next, sizeof(struct nunchuk));
00492                 if (read) {
00493                     nunchuk *last = n;
00494                     n = next;
00495                     next = last;
00496                 }
00497             }
00498             if (radio.DATALEN > 30)
00499                 pc.printf((const char*)radio.DATA);
00500             if (!read)
00501                 pc.printf("len %d\r\n", radio.DATALEN);
00502         } else if (rx_to_snooze) {
00503             rx_snooze.start(100);
00504             rx_to_snooze = false;
00505         }
00506         if(read)
00507         {
00508             Siren_state(n->C);
00509             Headlight_state(n->C, n->Z);
00510             float x = n->X - 128, y = n->Y - 128;
00511             float R = x*x + y*y, p = atan2(y, x) * 4 / M_PI - 0.5;
00512             mag = sqrt(R)/42; if (mag > 2) mag = 10;
00513             //const char *directions[8] = { "XR", "RR", "RX", "FR", "FX", "FF", "XF", "RF" };
00514 
00515             int c = 7;
00516             if (p > -4) c = 0;
00517             if (p > -3) c = 1;
00518             if (p > -2) c = 2;
00519             if (p > -1) c = 3;
00520             if (p >  0) c = 4;
00521             if (p >  1) c = 5;
00522             if (p >  2) c = 6;
00523             if (p >  3) c = 7;
00524             direction = c;
00525 
00526 //#if DEBUG > 1
00527             pc.printf("%d: ", sizeof(struct nunchuk));
00528             pc.printf("x%3d y%3d c%1d z%1d -- ", n->X, n->Y, n->C, n->Z);
00529             pc.printf("x%d y%d z%d -- %8.2f %8.4f %s %d \r\n", n->aX, n->aY, n->aZ, R, p, directions[direction], mag);
00530 
00531             //radio.send(GATEWAY_ID, (const void*)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 50, false);
00532 //#endif
00533             if (sender)
00534                 if (central_time.read() < 1)
00535                     radio.send(GATEWAY_ID, (const void*)n, sizeof(struct nunchuk), false);
00536                 else
00537                     radio.sleep();
00538 
00539 #if DEBUG
00540             char wake[100];
00541 #endif
00542 #ifdef TARGET_KL25Z
00543             indicators = 0;
00544             if (R <= 0.1) {
00545                 double twitch = pow(n->aX - next->aX, 2.0) + pow(n->aY - next->aY, 2.0) + pow(n->aZ - next->aZ, 2.0);
00546 #if DEBUG > 1
00547                 pc.printf("twitch %f\r\n", twitch);
00548 #endif
00549                 r = 1.0f;
00550                 g = 1.0f;
00551                 b = 1.0f;
00552                 if (n->C || n->Z || /*nun_settled > 2 &&*/ twitch > 1000) {
00553                     Thread::wait(10);
00554 #if DEBUG
00555                     pc.printf("c %d z %d twitch %f\r\n", n->C, n->Z, twitch);
00556                     snprintf(wake, 100, "Wake for twitch %f\r\n", twitch);
00557                     pc.printf(wake);
00558                     radio.send(GATEWAY_ID, (const void*)wake, 100, false);
00559 #endif
00560                     central_time.reset();
00561                     central_time_trip = 0;
00562                 }
00563             } else {
00564                 switch(direction) {
00565                     // left
00566                     case 0: //XR(); break;
00567                     case 6: //XF(); break;
00568                     case 7: //RF(); break;
00569                     indicators = 2; break;
00570                     // right
00571                     case 2: //RX(); break;
00572                     case 3: //FR(); break;
00573                     case 4: //FX(); break;
00574                     indicators = 1; break;
00575                     // neither
00576                     case 1: //RR(); break;
00577                     case 5: //FF(); break;
00578                     break;
00579                 }
00580 #if DEBUG
00581                 snprintf(wake, 100, "Wake for R %f\r\n", R);
00582                 pc.printf(wake);
00583                 radio.send(GATEWAY_ID, (const void*)wake, 100, false);
00584 #endif
00585                 if (R < 40) {
00586                     if (!central) {
00587                         pc.printf("central\r\n");
00588                         central = true;
00589                     }
00590                 } else {
00591                     central_time.reset();
00592                     central_time_trip = 0;
00593                     if (central) {
00594                         pc.printf("go %s\r\n", directions[direction]);
00595                         central = false;
00596                     }
00597                 }
00598                 R = R/20000;
00599                 float pal[8][3] = {
00600                     {   0,   0,   1 },
00601                     {   0,   1,   1 },
00602                     {   0,   1,   0 },
00603                     {   1,   1,   0 },
00604                     {   1, 0.5,   0 },
00605                     {   1,   0,   0 },
00606                     {   1,   0,   1 },
00607                     { 0.5,   0,   1 },
00608                 };
00609                 c = (c + 4) % 8;
00610                 r = 1.0f - pal[c][0] * R;
00611                 g = 1.0f - pal[c][1] * R;
00612                 b = 1.0f - pal[c][2] * R;
00613             }
00614 #endif
00615         }
00616         else if (sender)
00617         {
00618             pc.printf("Error\r\n");
00619         }
00620         if (sender)
00621             wait(0.01);
00622         else
00623             Thread::wait(10);
00624     }
00625 }