Roy Want / Mbed OS beaconCompileReadyFork
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2013 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #ifdef YOTTA_CFG_MBED_OS  // use minar on mbed OS
00018 #   include "mbed-drivers/mbed.h"
00019 #else
00020 #   include "mbed.h"
00021 #endif
00022 
00023 #include "ble/BLE.h"
00024 #include "EddystoneService.h"
00025 
00026 #include "PersistentStorageHelper/ConfigParamsPersistence.h"
00027 #include "stdio.h"
00028 
00029 // Instantiation of the main event loop for this program
00030 
00031 #ifdef YOTTA_CFG_MBED_OS  // use minar on mbed OS
00032 #   include "EventQueue/EventQueueMinar.h"
00033     typedef eq::EventQueueMinar event_queue_t;
00034 
00035 #else      // otherwise use the event classic queue
00036 #   include "EventQueue/EventQueueClassic.h"
00037     typedef eq::EventQueueClassic<
00038         /* event count */ 10
00039     > event_queue_t;
00040 
00041 #endif
00042 
00043 static event_queue_t eventQueue;
00044 
00045 EddystoneService *eddyServicePtr;
00046 
00047 /* Duration after power-on that config service is available. */
00048 static const int CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS = EDDYSTONE_DEFAULT_CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS;
00049 
00050 /* Values for ADV packets related to firmware levels, calibrated based on measured values at 1m */
00051 static const PowerLevels_t advTxPowerLevels = EDDYSTONE_DEFAULT_ADV_TX_POWER_LEVELS;
00052 /* Values for radio power levels, provided by manufacturer. */
00053 static const PowerLevels_t radioTxPowerLevels = EDDYSTONE_DEFAULT_RADIO_TX_POWER_LEVELS;
00054 
00055 DigitalOut configLED(CONFIG_LED, LED_OFF);
00056 
00057 static const int BLINKY_MSEC = 500;                       // How long to cycle config LED on/off
00058 static event_queue_t::event_handle_t handle = 0;         // For the config mode timeout
00059 static event_queue_t::event_handle_t BlinkyHandle = 0;   // For the blinking LED when in config mode
00060 
00061 static void blinky(void)  { configLED = !configLED; }
00062 
00063 static void configLED_on(void) {
00064     configLED = !LED_OFF;
00065     BlinkyHandle = eventQueue.post_every(blinky, BLINKY_MSEC);
00066 }
00067 
00068 static void configLED_off(void) {
00069     configLED = LED_OFF;
00070     if (BlinkyHandle) {
00071         eventQueue.cancel(BlinkyHandle);
00072         BlinkyHandle = NULL;
00073     }
00074 }
00075 
00076 /**
00077  * Callback triggered some time after application started to switch to beacon mode.
00078  */
00079 static void timeoutToStartEddystoneBeaconAdvertisements(void)
00080 {
00081     Gap::GapState_t state;
00082     state = BLE::Instance().gap().getState();
00083     if (!state.connected) { /* don't switch if we're in a connected state. */
00084         eddyServicePtr->startEddystoneBeaconAdvertisements();
00085         configLED_off();
00086     }
00087 }
00088 
00089 /**
00090  * Callback triggered for a connection event.
00091  */
00092 static void connectionCallback(const Gap::ConnectionCallbackParams_t *cbParams)
00093 {
00094     (void) cbParams;
00095     // Stop advertising whatever the current mode
00096     eddyServicePtr->stopEddystoneBeaconAdvertisements();
00097 }
00098 
00099 /**
00100  * Callback triggered for a disconnection event.
00101  */
00102 static void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *cbParams)
00103 {
00104     (void) cbParams;
00105     BLE::Instance().gap().startAdvertising();
00106     // Save params in persistent storage
00107     EddystoneService::EddystoneParams_t params;
00108     eddyServicePtr->getEddystoneParams(params);
00109     saveEddystoneServiceConfigParams(&params);
00110     // Ensure LED is off at the end of Config Mode or during a connection
00111     configLED_off();
00112     // 0.5 Second callback to rapidly re-establish Beaconing Service
00113     // (because it needs to be executed outside of disconnect callback)
00114     eventQueue.post_in(timeoutToStartEddystoneBeaconAdvertisements, 500 /* ms */);
00115 }
00116 
00117 // This section defines a simple push button handler to enter config or shutdown the beacon
00118 // Only compiles if "reset_button" is set in config.json in the "platform" section
00119 //
00120 #ifdef RESET_BUTTON
00121 
00122 InterruptIn button(RESET_BUTTON);
00123 DigitalOut shutdownLED(SHUTDOWN_LED, LED_OFF);
00124 
00125 static void shutdownLED_on(void) { shutdownLED = !LED_OFF; }
00126 static void shutdownLED_off(void) { shutdownLED = LED_OFF; }
00127 
00128 static int beaconIsOn = 1;                          // Button handler boolean to switch on or off
00129 static int buttonBusy;                              // semaphore to make prevent switch bounce problems
00130 
00131 static void freeButtonBusy(void) { buttonBusy = false; }
00132 
00133 // Callback used to handle button presses from thread mode (not IRQ)
00134 static void button_task(void) {
00135     eventQueue.cancel(handle);   // kill any pending callback tasks
00136 
00137     if (beaconIsOn) {
00138         beaconIsOn = 0;
00139         eddyServicePtr->stopEddystoneBeaconAdvertisements();
00140         configLED_off();    // just in case it's still running...
00141         shutdownLED_on();   // Flash shutdownLED to let user know we're turning off
00142         eventQueue.post_in(shutdownLED_off, 1000);
00143     } else {
00144 
00145         beaconIsOn = 1;
00146         eddyServicePtr->startEddystoneConfigAdvertisements();
00147         configLED_on();
00148         handle = eventQueue.post_in(
00149             timeoutToStartEddystoneBeaconAdvertisements,
00150             CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS * 1000 /* ms */
00151         );
00152     }
00153     eventQueue.post_in(freeButtonBusy, 750 /* ms */);
00154 }
00155 
00156 /**
00157  * Raw IRQ handler for the reset button. We don't want to actually do any work here.
00158  * Instead, we queue work to happen later using an event queue, by posting a callback.
00159  * This has the added avantage of serialising actions, so if the button press happens
00160  * during the config->beacon mode transition timeout, the button_task won't happen
00161  * until the previous task has finished.
00162  *
00163  * If your buttons aren't debounced, you should do this in software, or button_task
00164  * might get queued multiple times.
00165  */
00166 static void reset_rise(void)
00167 {
00168     if (!buttonBusy) {
00169         buttonBusy = true;
00170         eventQueue.post(button_task);
00171     }
00172 }
00173 #endif
00174 
00175 static void onBleInitError(BLE::InitializationCompleteCallbackContext* initContext)
00176 {
00177     /* Initialization error handling goes here... */
00178     (void) initContext;
00179 }
00180 
00181 
00182 static void bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext)
00183 {
00184     BLE         &ble  = initContext->ble;
00185     ble_error_t error = initContext->error;
00186 
00187     if (error != BLE_ERROR_NONE) {
00188         onBleInitError(initContext);
00189         return;
00190     }
00191 
00192     ble.gap().onDisconnection(disconnectionCallback);
00193 
00194     ble.gap().onConnection(connectionCallback);
00195 
00196     EddystoneService::EddystoneParams_t params;
00197     
00198     wait_ms(35); // Allow the RNG number generator to collect data
00199 
00200     // Determine if booting directly after re-Flash or not
00201     if (loadEddystoneServiceConfigParams(&params)) {
00202         // 2+ Boot after reflash, so get parms from Persistent Storage
00203         eddyServicePtr = new EddystoneService(ble, params, radioTxPowerLevels, eventQueue);
00204     } else {
00205         // 1st Boot after reflash, so reset everything to defaults
00206         /* NOTE: slots are initialized in the constructor from the config.json file */
00207         eddyServicePtr = new EddystoneService(ble, advTxPowerLevels, radioTxPowerLevels, eventQueue);
00208     }
00209 
00210     // Save Default params in persistent storage ready for next boot event
00211     eddyServicePtr->getEddystoneParams(params);
00212     saveEddystoneServiceConfigParams(&params);
00213     // Start the Eddystone Config service - This will never stop (only connectability will change)
00214     eddyServicePtr->startEddystoneConfigService();
00215 
00216     /* Start Eddystone config Advertizements (to initialize everything properly) */
00217     configLED_on();
00218     eddyServicePtr->startEddystoneConfigAdvertisements();
00219     handle = eventQueue.post_in(
00220         timeoutToStartEddystoneBeaconAdvertisements,
00221         CONFIG_ADVERTISEMENT_TIMEOUT_SECONDS * 1000 /* ms */
00222     );
00223 
00224    // now shut everything off (used for final beacon that ships w/ battery)
00225 #ifdef RESET_BUTTON
00226    eventQueue.post_in(button_task, 2000 /* ms */);
00227 #endif
00228 }
00229 
00230 void app_start(int, char *[])
00231 {
00232 
00233 #ifdef NO_LOGGING
00234     /* Tell standard C library to not allocate large buffers for these streams */
00235     setbuf(stdout, NULL);
00236     setbuf(stderr, NULL);
00237     setbuf(stdin, NULL);
00238 #endif
00239 
00240 #ifndef NO_4SEC_START_DELAY
00241     // delay ~4secs before starting to allow time the nRF51 hardware to settle
00242     // Also allows time to attach a virtual terimal to read logging output during init
00243     wait_ms(4000);
00244 #endif
00245     
00246 #ifdef RESET_BUTTON
00247     beaconIsOn = 1;             // Booting up, initialize for button handler
00248     buttonBusy = false;         // software debouncing of the reset button
00249     button.rise(&reset_rise);   // setup reset button
00250 #endif
00251 
00252     BLE &ble = BLE::Instance();
00253     ble.init(bleInitComplete);
00254 }
00255 
00256 #if !defined(YOTTA_CFG_MBED_OS)
00257 
00258 void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
00259     eventQueue.post(&BLE::processEvents, &context->ble);
00260     }
00261 
00262 int main() {
00263 
00264     BLE &ble = BLE::Instance();
00265     ble.onEventsToProcess(scheduleBleEventsProcessing);
00266 
00267     app_start(0, NULL);
00268 
00269     while (true) {
00270        eventQueue.dispatch();
00271     }
00272 
00273     return 0;
00274 }
00275 
00276 // *WARNING* HACK
00277 // avoid unecessary code to be pulled in,
00278 // should be fixed by mbed-os latter
00279 extern "C" {
00280 
00281 #if defined(TOOLCHAIN_GCC_ARM) || defined(TOOLCHAIN_GCC_CR)
00282 void exit(int) {
00283     while(true) {
00284     }
00285 }
00286 #endif
00287 
00288 int __aeabi_atexit(void *object, void (*dtor)(void* /*this*/), void *handle) {
00289     return 0;
00290 }
00291 
00292 int __cxa_atexit(void (*dtor)(void* /*this*/), void *object, void *handle) {
00293     return 0;
00294 }
00295 
00296 void __register_exitproc() {
00297 }
00298 
00299 void __call_exitprocs(int, void *f) {
00300 }
00301 
00302 void __cxa_finalize(void *handle) {
00303 }
00304 
00305 }
00306 
00307 
00308 #endif