Advertisement
Guest User

BusBeacon.ino

a guest
Feb 12th, 2015
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.00 KB | None | 0 0
  1. //To do
  2. //Allows user to set bus#, route#, MaxWaitTime, MinTimeToCathcBus, Time to Check
  3. //Animations speed up and red in color as bus approaches
  4. //Format URL from parameters
  5. //Add a button to start the beacon for an hour outside the desired hours
  6.  
  7. #include "neopixel/neopixel.h"
  8. #include "SparkTime/SparkTime.h"
  9. #include "rest_client.h"
  10. #include "jsmnSpark.h"
  11. #include "application.h"
  12. #include "math.h"  //for breathing neopixel pattern
  13.  
  14. #pragma SPARK_NO_PREPROCESSOR
  15.  
  16. #define TOKEN_STRING(js, t, s) \
  17.     (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \
  18.     && strlen(s) == (t).end - (t).start)
  19.  
  20. #define TOKEN_PRINT(t) \
  21.     Serial.print(", type: "); Serial.print((t).type); Serial.print(" size: "); Serial.print((t).size); \
  22.     Serial.print(" start: "); Serial.print((t).start); Serial.print(" end: "); Serial.println((t).end)
  23.  
  24. /* IMPORTANT TO CHANGE THE NUMBER OF TOKENS TO MATCH YOUR DATA CLOSELY */
  25. #define NUM_TOKENS 150
  26. #define MAX_OBJ_SIZE 50
  27. #define HOSTNAME "api.pugetsound.onebusaway.org"
  28.  
  29.  
  30. //Neopixel related
  31. #define PIXEL_COUNT 8  
  32. #define PIXEL_PIN D1
  33. #define PIXEL_TYPE WS2812B
  34.  
  35. //Bus related
  36. //#define BusToCheck "73"
  37. int HoursToCheck[2] = {0,24};
  38. int MinTimeToCatchBus = 7;
  39. //String BusNum = "44";
  40. //String StopId = "1_67612";
  41. //String AccessToken = "XX";  //access token for onebusaway API
  42. //String MinutesBefore = "13";
  43. //String MinutesAfter = "15";
  44.  
  45. //debugging related
  46. int attempt = 1;
  47. int led = D7;
  48. int BeaconRuns = 0;
  49.  
  50. Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
  51. void StartVisualBeacon(uint16_t BusComingInMins);
  52. void CheckForBus();
  53. void ResetCore();
  54. RestClient client = RestClient(HOSTNAME);
  55. String response;
  56.  
  57. void setup() {
  58.   Serial.begin(9600); // Make sure serial terminal is closed before powering up Core
  59.   pinMode(led, OUTPUT);
  60.  
  61.   long unsigned CurrentMillis = millis();
  62.   // Open serial terminal and hit any key - halts execution - required to use serial on windows - Now Waits only a minute for key press
  63.  while(!Serial.available() && millis()-CurrentMillis < 60000)
  64.  {
  65.      SPARK_WLAN_Loop();
  66.      delay(1000);
  67.  }
  68.   //while(!Serial.available()) Spark.process();  // Open serial terminal and hit any key - halts execution - required to use serial on windows -
  69.   Time.zone(-8); //SEA
  70.   //initialize neopixels
  71.   digitalWrite(led, HIGH);
  72.   //Initialize neopixels
  73.   strip.begin();
  74.   strip.show();
  75.   Serial.println("Starting in 3 secs..");
  76.   delay(3000);  //in case want to reflash, this would be the chance
  77.   digitalWrite(led, LOW);
  78. }
  79.  
  80.  
  81. void loop() {
  82.     //Check if the hour is when user wants to be notified
  83.     if (Time.hour()>=HoursToCheck[0] && Time.hour()<=HoursToCheck[1]) {
  84.     Serial.println("Good time to check for bus");
  85.     Serial.print("Hour now: ");
  86.     Serial.println(Time.hour());
  87.     Serial.print("Minute now: ");
  88.     Serial.println(Time.minute());
  89.     Serial.print("Attempt this session: ");
  90.     Serial.println(attempt);
  91.     //check if Bus is coming
  92.     CheckForBus();
  93.     attempt=attempt+1;
  94.     if (attempt>9) ResetCore();  //Spark has been a good boy, Reset every 10 minutes if no bus is coming
  95.     }
  96.     else
  97.     {
  98.     Serial.println("Not a good time to check for Bus. Get a cab");
  99.     }
  100.     Spark.process();
  101.     delay(10000);  //check for hour every 10 secs - change to sleep
  102. }
  103.  
  104. void CheckForBus() {
  105.    
  106.   response = ""; // Clear the response String
  107.   Serial.println("");
  108.   Serial.println("Initializing GET request");
  109.   Serial.println("Printing response string before the request");
  110.   Serial.println(response);
  111.   Serial.println("");
  112.  
  113.   jsmn_parser p;
  114.   jsmntok_t tok[NUM_TOKENS];
  115.  
  116.   //pointers
  117.   int i, r;  
  118.   char obj[MAX_OBJ_SIZE];
  119.  
  120.   //Token indices and names
  121.   //indexes for tokens
  122.   int PredictedIndex[2] = {26, 27}; //TokenName Index, TokenValue Index
  123.   int RouteNameIndex[2] = {40, 41};
  124.   int PredictedArrivalTimeIndex[2] = {30, 31};
  125.   int ScheduledArrivalTimeIndex[2] = {44, 45};
  126.  
  127.   //char Predicted[MAX_OBJ_SIZE];
  128.   char BusArrivalTimeString[MAX_OBJ_SIZE];  
  129.   //char BusArrivalTimeString_trunc[MAX_OBJ_SIZE];  
  130.   //double RouteName;
  131.   double BusArrivalTime;
  132.   int TimeToArrival;
  133.  
  134.   obj[0] = Serial.read(); // Flush the serial buffer to pause next time through
  135.          
  136.  
  137.  // GET request
  138.  
  139.   //String url = String("/api/where/arrivals-and-departures-for-stop/" + StopId + ".json?key=" + AccessToken + "&minutesBefore=-" + MinutesBefore + "&minutesAfter=" + MinutesAfter + "&includeReferences=false");
  140.   //Serial.println(url);  //doesn't work as RESTClient library expect a const char* path
  141.   //int statusCode = client.get(url, &response);
  142.   int statusCode = client.get("/api/where/arrivals-and-departures-for-stop/1_29215.json?key=XX&minutesBefore=-12&minutesAfter=15", &response);
  143.   //67612- bellevue
  144.   //29215- seattle
  145.  
  146.  
  147.   if(statusCode != 200) {
  148.     Serial.print(F("Error code from server: "));
  149.     Serial.println(statusCode);
  150.     ResetCore(); //To bad, I think this is breaking the flow
  151.     return;
  152.   }
  153.  
  154.   Serial.print("Response body from server: ");
  155.   Serial.println(response);
  156.   Serial.println(" ");
  157.   Serial.print("Response length is: ");
  158.   Serial.print(response.length());
  159.   Serial.println(" ");
  160.  
  161.   if (response.length() < 300 || response.length() > 2000) {   //Quick and dirt way to check if only one bus is coming
  162.   Serial.println("No bus is coming");
  163.   Serial.println(" ");
  164.   for(uint16_t k=0;k<5;k++){
  165.   Spark.process();
  166.   digitalWrite(led, HIGH);
  167.   delay(10000);
  168.   digitalWrite(led, LOW);} //~50 secs
  169.   return;  //exit to loop and wait another 10 secs
  170.   }
  171.  
  172.   //A bus is coming
  173.   // Parse response from server
  174.     jsmn_init(&p);
  175.     r = jsmn_parse(&p, response.c_str(), tok, NUM_TOKENS);
  176.  
  177.     // Determine status code
  178.     if (r == JSMN_SUCCESS) {
  179.     Serial.println("Parsed successfully.");
  180.     }
  181.     else if(r == JSMN_ERROR_INVAL) {
  182.     Serial.println("Bad token, JSON string is corrupted!");
  183.     for(uint16_t k=0;k<5;k++){
  184.     Spark.process();
  185.     delay(10000);} //~50 secs
  186.     return;
  187.     }
  188.     else if(r == JSMN_ERROR_NOMEM) {
  189.     Serial.println("Not enough tokens, JSON string is too large! Increase NUM_TOKENS.");
  190.     for(uint16_t k=0;k<5;k++){
  191.     Spark.process();
  192.     delay(10000);} //~50 secs
  193.     return;
  194.     }
  195.     else if(r == JSMN_ERROR_PART) {
  196.     Serial.println("JSON string is too short, expecting more JSON data!");
  197.     for(uint16_t k=0;k<5;k++){
  198.     Spark.process();
  199.     delay(10000);} //~50 secs
  200.     return;
  201.     }
  202.     else {
  203.     Serial.println("Parse failed! Unknown Error.");
  204.     for(uint16_t k=0;k<5;k++){
  205.     Spark.process();
  206.     delay(10000);} //~50 secs
  207.     return;
  208.     }
  209.     // Check few tokens names to make sure the parsing worked correctly
  210.     if ( TOKEN_STRING(response.c_str(), tok[PredictedIndex[0]], "predicted") ) {
  211.         Serial.println("Predicted tokenName is correct");
  212.         if ( TOKEN_STRING(response.c_str(), tok[PredictedArrivalTimeIndex[0]], "predictedArrivalTime") ) {
  213.          Serial.println("PredictedArrivalTime tokenName is correct");
  214.          Serial.print("Now checking for: ");
  215.          Serial.println("44");
  216.          //check for bus name
  217.          if ( TOKEN_STRING(response.c_str(), tok[RouteNameIndex[1]], "44") || TOKEN_STRING(response.c_str(), tok[RouteNameIndex[1]], "44E")) {
  218.              //strlcpy(Predicted, &response.c_str()[tok[PredictedIndex[2]].start], (tok[PredictedIndex[2]].end - tok[PredictedIndex[2]].start + 1));
  219.             Serial.println("BusToCheck checks out");
  220.             if ( TOKEN_STRING(response.c_str(), tok[PredictedIndex[1]], "true") ) {
  221.                 //Use predicted arrival time - truncate the trailing 0000 - otherwise it causes ovf (for both strtod and atof)
  222.                 strlcpy(BusArrivalTimeString, &response.c_str()[tok[PredictedArrivalTimeIndex[1]].start], (tok[PredictedArrivalTimeIndex[1]].end-3 - tok[PredictedArrivalTimeIndex[1]].start + 1));
  223.                 BusArrivalTime = strtod(BusArrivalTimeString, NULL);
  224.                 //BusArrivalTime=atof(BusArrivalTimeString);
  225.                 Serial.print("\nPrediction is true. BusArrivalTimeString:  ");
  226.                 Serial.print(BusArrivalTimeString);
  227.                 Serial.print(" and BusArrivalTime: ");
  228.                 Serial.print(BusArrivalTime);
  229.             }
  230.             else {
  231.                 //Use scheuduled arrival time
  232.                 strlcpy(BusArrivalTimeString, &response.c_str()[tok[ScheduledArrivalTimeIndex[1]].start], (tok[ScheduledArrivalTimeIndex[1]].end-3 - tok[ScheduledArrivalTimeIndex[1]].start + 1));
  233.                 // Convert string to double, contains numerical value of milliseconds to arrival
  234.                 BusArrivalTime = strtod(BusArrivalTimeString, NULL);
  235.                 //BusArrivalTime=atof(BusArrivalTimeString);
  236.                 Serial.print("\nPrediction is false. BusArrivalTimeString:  ");
  237.                 Serial.print(BusArrivalTimeString);
  238.                 Serial.print(" and BusArrivalTime: ");
  239.                 Serial.print(BusArrivalTime);
  240.             }
  241.            
  242.             //calculate minutes to arrival
  243.             TimeToArrival = (BusArrivalTime - Time.now())/60;
  244.             Serial.print("\nYour bus is coming in ");
  245.             Serial.print(TimeToArrival);
  246.             Serial.println(" mins. Pack your bags");
  247.             Spark.process();
  248.             StartVisualBeacon(TimeToArrival);  //CYAN
  249.             Spark.process();
  250.              }  //bus is coming loop ends
  251.          else {
  252.              Serial.print("\nYour bus is not coming but ");
  253.              //Provide the bus# thats incoming 
  254.              i=RouteNameIndex[1];
  255.              strlcpy(obj, &response.c_str()[tok[i].start], (tok[i].end - tok[i].start + 1));
  256.              Serial.print(obj); Serial.println(" is coming");
  257.              //lay low for a minute
  258.              for(uint16_t k=0;k<5;k++){
  259.              Spark.process();
  260.              delay(10000);} //~50 secs
  261.              return;
  262.              } //some bus is coming loop ends
  263.         }
  264.            
  265.        
  266.     else {
  267.         i=PredictedArrivalTimeIndex[1];
  268.         Serial.print("\nToken:");
  269.         Serial.print(i);
  270.         Serial.print(" expected: predictedArrivalTime but instead is ");
  271.         strlcpy(obj, &response.c_str()[tok[i].start], (tok[i].end - tok[i].start + 1));
  272.         Serial.println(obj);
  273.         //TOKEN_PRINT(tok[PredictedArrivalTimeIndex[1]]);
  274.         Serial.println("TokenIndex error, please debug");}}
  275.     else {
  276.         i=PredictedIndex[1];
  277.         Serial.print("\nToken:");
  278.         Serial.print(i);
  279.         Serial.print(" expected: predicted but instead is ");
  280.         strlcpy(obj, &response.c_str()[tok[i].start], (tok[i].end - tok[i].start + 1));
  281.         Serial.println(obj);
  282.         //TOKEN_PRINT(tok[PredictedArrivalTimeIndex[1]]);
  283.         Serial.println("TokenIndex error, please debug");}
  284.  
  285.  
  286. } //end of HourIsRight()
  287.  
  288.  
  289.  
  290.  
  291.  
  292. void StartVisualBeacon(uint16_t BusComingInMins) {
  293.    
  294.     //Define colors here
  295.     //START WITH CYAN
  296.     uint8_t Rstart = 0;
  297.     uint8_t Gstart = 255;
  298.     uint8_t Bstart = 255;
  299.     uint32_t c = strip.Color(Rstart,Gstart,Bstart);  //Cyan
  300.     //END WITH RED
  301.     uint8_t Rend = 200; //2/3 of maximum red
  302.     uint8_t Gend = 0;
  303.     uint8_t Bend = 0;
  304.     //temp variables
  305.     uint8_t Rnew, Gnew, Bnew;
  306.  
  307.     //NumIterations for color transitions
  308.     int n = 10000;
  309.    
  310.     Serial.println("Begining Visual Beacon");
  311.     Serial.print("Minute now: ");
  312.     Serial.println(Time.minute());
  313.     float val;
  314.     //uint16_t i,j;
  315.     //unsigned long currentMillis;
  316.     Serial.println("Begining Cyan animations");
  317.     long unsigned CurrentMillis = millis();  //.
  318.     for(uint16_t j=0;j<(BusComingInMins-MinTimeToCatchBus)*6000;j++){
  319.     val = (exp(sin((millis()-CurrentMillis+3000)/2000.0*3.14)) - 0.36787944)*108.0;   //Math here: http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ 4 secs cycle, plotted in matlab
  320.     //starting animations at sine argument 0 is ideal. Math says that number is 3000. Thats the reason for offset
  321.     strip.setBrightness(val);
  322.     for(uint16_t i=0; i<strip.numPixels(); i++) {
  323.     strip.setPixelColor(i, c);
  324.     strip.show();
  325.     delay(1);}
  326.     //Serial.println(j);
  327.     }
  328.     Serial.println(" Cyan animations end");
  329.     Serial.print("Minute now: ");
  330.     Serial.println(Time.minute());
  331.     Serial.println("Begining color transitions");
  332.     //slowly transition from Cyan to Red and glow RED at reduced brightness until a minute before the bus comes to the stop
  333.     //idea is to notify the user that the bus is just leaving the stop
  334.    
  335.     for (uint16_t k = 0; k < n; k++) // larger values of 'n' will give a smoother/slower transition. takes about 1.5 mins
  336.     {
  337.         Rnew = Rstart + (Rend - Rstart) * k / n;
  338.         Gnew = Gstart + (Gend - Gstart) * k / n;
  339.         Bnew = Bstart + (Bend - Bstart) * k / n;
  340.        
  341.         // Set pixel color here.
  342.         for(uint16_t i=0; i<strip.numPixels(); i++) {
  343.         strip.setPixelColor(i, strip.Color(Rnew, Gnew, Bnew));
  344.         strip.show();
  345.         }
  346.         delay(10);
  347.     }
  348.     Serial.println("Ending color transitions");
  349.     Serial.print("Minute now: ");
  350.     Serial.println(Time.minute());
  351.    
  352.    
  353.     //Make the strip glow RED at reduced brightness until a minute before the bus comes to the stop to notify the user that the bus has just left
  354.     Serial.println(" Red animations begin");
  355.     for(uint16_t l=0;l<MinTimeToCatchBus-1;l++){
  356.     for(uint16_t k=0;k<5;k++){
  357.     Spark.process();
  358.     delay(10000);}
  359.     }
  360.    
  361.     Serial.println(" Red animations end");
  362.     Serial.print("Minute now: ");
  363.     Serial.println(Time.minute());
  364.    
  365.     //set strip to zero color on exit
  366.     Rstart = 127;
  367.     Rend=0;
  368.     for(uint16_t j=0; j<n/10; j++) {  //dont need that smooth here
  369.     Rnew = Rstart + (Rend - Rstart) * j / n;    
  370.     // Set pixel color here.
  371.         for(uint16_t i=0; i<strip.numPixels(); i++) {
  372.         strip.setPixelColor(i, strip.Color(Rnew, Gnew, Bnew));
  373.         strip.show();
  374.         }
  375.         delay(10);
  376.     }
  377.     //for good luck - make sure they are off
  378.     for(uint16_t i=0; i<strip.numPixels(); i++) {
  379.     strip.setPixelColor(i, 0,0,0);
  380.     strip.setBrightness(0);
  381.     strip.show();}
  382.    
  383.     Serial.println("Exiting animation loop");
  384.     Serial.print("Minute now: ");
  385.     Serial.println(Time.minute());
  386.    
  387.     //Update Beacons Runs
  388.     BeaconRuns = BeaconRuns+1;
  389.     //Core has been a good boy, call reset if this was second beacon run
  390.     if (BeaconRuns>0) ResetCore();
  391.    
  392. }
  393.  
  394. void ResetCore(){
  395.     Serial.print("Core reboot at: ");
  396.     Serial.print(Time.hour());
  397.     Serial.print(":");
  398.     Serial.print(Time.minute());
  399.     //Spark.sleep(SLEEP_MODE_DEEP, 30);  //Deep sleep for 30 secs, resets on wakeup as well
  400.     Serial.end();
  401.     delay(3000); //lets the serial data out
  402.     System.reset();
  403. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement