SunTracker_BLE

Dependencies:   BLE_API X_NUCLEO_6180XA1 X_NUCLEO_IDB0XA1 X_NUCLEO_IHM01A1 X_NUCLEO_IKS01A1 mbed

Fork of SunTracker_BLE by ST Expansion SW Team

Overview

The SunTracker is a demo application running on ST Nucleo-F401RE stacking a set of ST X-NUCLEO expansion boards.
Main features provided are:

  • A solar panel follows the light source, orienting the panel in order to achieve the best panel efficiency.
  • Orientation is controlled thanks to a couple of VL6180X FlightSense light sensors mounted on a X-NUCLEO-6180XA1 expansion board and driven by X-NUCLEO-IHM01A1 controlled stepper motor acting as actuator to orientate the panel.
  • The system features a progressive control on the stepper motor in order to modulate the panel rotation speed according to the light angle.
  • The application is also able to control the panel productivity reading the panel voltage through an ADC and proving feedback on the local display.
  • A manual orientation is possible by using the accelerometer on a X-NUCLEO-IKS01A1 expansion board that, according on board tilt, controls the speed and the rotate direction.
  • A remote control is available using a X-NUCLEO-IDB04A1 or a X-NUCLEO-IDB05A1 Bluetooth Low Energy expansion board. Remote control software is here.

/media/uploads/fabiombed/suntracker_server-client.png

Working Status

  • SunTracker has 3 working status visible on FlightSense display and switchable by pressing the User Button:

Status 0 (Idle)

  • Motor: Free Turning
  • Display: Waiting for User Button

Status 1

  • Motor: Driven by Light
  • Display: Direction and Light Intensity = Direction and Motor Speed

Status 2

  • Motor: Driven by Light
  • Display: Solar Panel Efficiency

Status 3

  • Motor: Driven by Accelerometer
  • Display: Direction and Accelerometer Intensity

Server Startup

  • When you plug the power supply, the word ‘PUSH’ is shown on display.
  • You can manually rotate the structure to assign the ‘Zero Point’. Then press the User Button to launch the application.
  • The display will show this status, which means that the structure is oriented to maximize the efficiency of the solar panel.
  • If there is a light displacement, the structure will rotate, left or right, to follow the light source and on display is shown the direction and the speed.
  • You can press the User Button to show the panel efficiency with 4 digits that represent the range from 0v (0000) to 3,3v (3300).
  • Further pressing the User Button you will manual rotate the panel by tilt the Server or Client accelerometer depending by BLE connection.

Client Startup

  • The Client application can remotely control the User Button and the Accelerometer functions.
  • Power on the Client AFTER the Server, it will automatically search for the SunTracker and will establish a BLE connection.
  • The Green Led on Nucleo Client board will be powered on.

Rotation Features

  • It has been implemented a block of rotation to avoid cables twist.
  • The blocking point can be set in the firmware by changing a constant.
  • You can manually rotate the structure to assign the ‘Zero Point’ before press the User Button to launch the application.
  • The system features a progressive control on the stepper motor in order to modulate the rotation speed according to the light or accelerometer angle.

List of Components

SERVER SunTracker_BLE

  • Stepper Motor 400’’ (Part Number 5350401) - To orientate the Mechanical Structure.
  • Solar Panel 0.446w (Part Number 0194127) - To capture sunlight and generate electrical current.
  • Power Supply 12v (Part Number 7262993) - To provide power supply at the Stepper Motor.
  • Flat Cable 6 ways (Part Number 1807010) - To plug VL6180X-SATEL with X-NUCLEO-6180XA1 (60cm length each x2).
  • Cable Connector (Part Number 6737694) - To plug the Flat Cable (x4).
  • Power Connector (Part Number 0487842) - To provide Power Supply to X-NUCLEO-IHM01A1.

CLIENT SunTracker_BLE_Remote

MECHANICAL STRUCTURE

Find here the STL files to print with a 3D printer.

/media/uploads/fabiombed/assembly.png

/media/uploads/fabiombed/mechanical_structure_and_motor_legs.png

FLAT CABLE ASSEMBLY

/media/uploads/fabiombed/flat_cable.png

HARDWARE SETUP

Nucleo ADC + Solar Panel

Connect Solar Panel cables to Nucleo Morpho PC_3 (white) and Nucleo Morpho GND (black). Connect a capacitor 10uF between PC_3 and GND to stabilize its voltage value shown on display.

EasySpin (L6474) + BLE

Hardware conflict between EasySpin DIR1 and BLE Reset, both on same Arduino Pin PA_8. Disconnect PA_8 between EasySpin and Nucleo by fold EasySpin Pin. PB_2 has been configured as EasySpin DIR1 in the firmware . Connect Nucleo Morpho PB_2 to FlightSense Arduino PA_8 by a wire.

FlightSense Satellites

In case of instability with I2C due to long flat cables, solder 4 SMD capacitors 47pF on FlightSense board in parallel between R15, R16, R17, R18 and plug 2 capacitors 15pF between FlightSense Arduino PB_8 and PB_9 to GND pin to cut-off noises over 720 KHz.

Arduino & Morpho Pinout

/media/uploads/fabiombed/arduino_pinout.png /media/uploads/fabiombed/morpho_pinout.png

Files at this revision

API Documentation at this revision

Comitter:
fabiombed
Date:
Fri Apr 22 14:40:14 2016 +0000
Parent:
17:582eba752042
Child:
19:b2c04428ffed
Commit message:
Added ticker for services

Changed in this revision

CustomSensorsService.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/CustomSensorsService.h	Tue Apr 19 08:53:07 2016 +0000
+++ b/CustomSensorsService.h	Fri Apr 22 14:40:14 2016 +0000
@@ -50,10 +50,10 @@
 
 /* BLE Services: Primary + 4 Secondary (Char Desk) */
 const UUID::LongUUIDBytes_t SENS_SERVICE_UUID           = {0x00,0x00,0x00,0x00,0x00,0x01,0x11,0xe1,0x9a,0xb4,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //Primary 
-const UUID::LongUUIDBytes_t SENS_STATUS_CHAR_UUID       = {0x00,0x00,0x04,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //Status                 0x00000400
-const UUID::LongUUIDBytes_t SENS_DIFFERENCE_CHAR_UUID   = {0x00,0x00,0x08,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //Difference Light/Mems  0x00000800
-const UUID::LongUUIDBytes_t SENS_POSITION_CHAR_UUID     = {0x00,0x00,0x10,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //Motor Position         0x00001000
-const UUID::LongUUIDBytes_t SENS_SUNPANEL_CHAR_UUID     = {0x00,0x00,0x20,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //SunPanel Voltage       0x00002000
+const UUID::LongUUIDBytes_t SENS_STATUS_CHAR_UUID       = {0x00,0x00,0x08,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //Status                 0x00000800
+const UUID::LongUUIDBytes_t SENS_DIFFERENCE_CHAR_UUID   = {0x00,0x00,0x10,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //Difference Light/Mems  0x00001000
+const UUID::LongUUIDBytes_t SENS_POSITION_CHAR_UUID     = {0x00,0x00,0x20,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //Motor Position         0x00002000
+const UUID::LongUUIDBytes_t SENS_SUNPANEL_CHAR_UUID     = {0x00,0x00,0x40,0x00,0x00,0x01,0x11,0xe1,0xac,0x36,0x00,0x02,0xa5,0xd5,0xc5,0x1b}; //SunPanel Voltage       0x00004000
                       
 //#define DIFFERENCE_DATA_LEN     2
 //#define POSITION_DATA_LEN       2
@@ -114,7 +114,7 @@
 
     void updateEnvStatus (uint16_t Sta, uint16_t TimeStamp) {
             if (ble.getGapState().connected && isEnabledStatusNotify ) { 
-                if (memcmp (&pastenvStatus[2], &Sta, STATUS_DATA_LEN) != 0) {
+                if (memcmp (&pastenvStatus[2], &Sta, 2) != 0) {
                     sendEnvStatus (Sta, TimeStamp);
                 }
             }
@@ -133,7 +133,7 @@
 
     void updateEnvDifference (uint16_t Dif, uint16_t TimeStamp) {
             if (ble.getGapState().connected && isEnabledDifferenceNotify ) { 
-                if (memcmp (&pastenvDifference[2], &Dif, DIFFERENCE_DATA_LEN) != 0) {
+                if (memcmp (&pastenvDifference[2], &Dif, 2) != 0) {
                     sendEnvDifference (Dif, TimeStamp);
                 }
             }
@@ -152,7 +152,7 @@
 
     void updateEnvPosition (uint16_t Pos, uint16_t TimeStamp) {
             if (ble.getGapState().connected && isEnabledPositionNotify ) { 
-                if (memcmp (&pastenvPosition[2], &Pos, POSITION_DATA_LEN) != 0) {
+                if (memcmp (&pastenvPosition[2], &Pos, 2) != 0) {
                     sendEnvPosition (Pos, TimeStamp);
                 }
             }
@@ -171,7 +171,7 @@
 
     void updateEnvSunpanel (uint16_t Sun, uint16_t TimeStamp) {
             if (ble.getGapState().connected && isEnabledSunpanelNotify ) { 
-                if (memcmp (&pastenvSunpanel[2], &Sun, SUNPANEL_DATA_LEN) != 0) {
+                if (memcmp (&pastenvSunpanel[2], &Sun, 2) != 0) {
                     sendEnvSunpanel (Sun, TimeStamp);            
                 }
             }
--- a/main.cpp	Tue Apr 19 08:53:07 2016 +0000
+++ b/main.cpp	Fri Apr 22 14:40:14 2016 +0000
@@ -100,10 +100,10 @@
 static CustomControlService *p_customcontrolservice = NULL;
 static CustomSensorService  *p_customsensorservice = NULL;
 
-#define FeatureStatus      0x00000400
-#define FeatureDifference  0x00000800
-#define FeaturePosition    0x00001000
-#define FeatureSunPanel    0x00002000
+#define FeatureStatus      0x00000800
+#define FeatureDifference  0x00001000
+#define FeaturePosition    0x00002000
+#define FeatureSunPanel    0x00004000
 
 #endif
 
@@ -114,9 +114,13 @@
 #define SET_MAX 200     // Set Motor MaxSpeed
 #define SET_MIN 100     // Set Motor MinSpeed
 #define STOP 1000       // Set Motor Stop Position
-#define TOLLERANCE 100  // Tollerance between Left and Right before Start Movement
-#define RANGE_1 200     // Range 1 for Motor Speed
+#define TOLLERANCE 80   // Tollerance between Left and Right before Start Movement
+#define RANGE_1 250     // Range 1 for Motor Speed
 #define RANGE_2 500     // Range 2 for Motor Speed
+#define TIMEOUT_STA 1   // Timeout for Ticker Status (in second)
+#define TIMEOUT_DIF 0.5 // Timeout for Ticker Difference (in second)
+#define TIMEOUT_POS 0.5 // Timeout for Ticker Position (in second)
+#define TIMEOUT_SUN 0.5 // Timeout for Ticker SunPanel (in second)
 
 /* Variables -----------------------------------------------------------------*/
 
@@ -135,6 +139,10 @@
 int16_t Status=0;      // Status Shown on Display: 0 = Idle, 1 = Motor Speed, 2 = Solar Panel Value, 3 = Manual Control [--> Send BLE]
 int16_t status_bb, status_t, status_b, status_l, status_r; // Babybear Status
 uint16_t TimeStamp=0;  // TimeStamp for BLE
+bool tickerSta=false;
+bool tickerDif=false;
+bool tickerPos=false;
+bool tickerSun=false;
 
 /* Initializations ------------------------------------------------------------*/
 
@@ -185,7 +193,8 @@
 
     Status++;
 
-#ifdef Sensors
+//#ifdef Sensors
+#ifdef Ble
     if (Status>3)  { Status=1; }
 #else
     if (Status>2)  { Status=1; }
@@ -221,7 +230,7 @@
 
 static void onDataReadCallback(const GattReadCallbackParams *eventDataP)
 {
-/*
+
     // if receive a manual Read request
 
     if (p_customsensorservice->isStatusHandle(eventDataP->handle))
@@ -245,7 +254,7 @@
         p_customsensorservice->sendEnvSunpanel(measure, TimeStamp);
 
     }
-*/  
+ 
 }
 
 // This Callback happen when it RECEIVE a WRITE
@@ -335,7 +344,7 @@
     // Setup BLE Advertising
     const static char DEVICE_NAME[] = BLE_DEV_NAME;
     p_BLEdev->gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
-    uint8_t dat[] = {0x01,0x80,0x00,0xFC,0x00,0x00};    
+    uint8_t dat[] = {0x01,0x80,0x00,0x00,0x78,0x00}; // Data Sent during Advertising, must respect a Standard ST
     p_BLEdev->gap().accumulateScanResponse(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA,dat,6);    
     p_BLEdev->gap().accumulateAdvertisingPayload(GapAdvertisingData::UNKNOWN);
     p_BLEdev->gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
@@ -541,6 +550,13 @@
      
 }
 
+/* Ticker --------------------------------------------------------------------*/
+
+void tickerSta_Callback(void) { tickerSta=true; }
+void tickerDif_Callback(void) { tickerDif=true; }
+void tickerPos_Callback(void) { tickerPos=true; }
+void tickerSun_Callback(void) { tickerSun=true; }
+
 /* Main ----------------------------------------------------------------------*/
 
 int main()
@@ -555,9 +571,10 @@
     mybutton.fall(&User_Button_Pressed);
 
     #ifdef Ble
-    static int INTLOOP=0;
-    #endif
-
+    Ticker tickerSta_ticker, tickerDif_ticker, tickerPos_ticker, tickerSun_ticker;
+    tickerSta_ticker.attach(tickerSta_Callback, TIMEOUT_STA);
+    #endif 
+    
     // Loop until push User Button to Set 0 Point
     printf("\r\nWait PUSH Button");
     strcpy(DisplayStr,"pusH");
@@ -568,23 +585,25 @@
         #endif
         
         #ifdef Ble
-        /*
-        INTLOOP++;
-        if (INTLOOP==100)
-        {
-            p_customsensorservice->sendEnvStatus(Status, TimeStamp);
-            p_customsensorservice->sendEnvDifference(diff, TimeStamp);
-            p_customsensorservice->sendEnvPosition(pos, TimeStamp);
-            p_customsensorservice->sendEnvSunpanel(measure, TimeStamp);
-            INTLOOP=0;
+        if (p_customsensorservice->isStatusNotificationEn()) {
+            if (tickerSta) {
+                tickerSta=false;
+                printf("\n\r\n\rSend BLE Display Status %d", Status);
+                //p_customsensorservice->sendEnvStatus(Status, TimeStamp);
+                p_customsensorservice->updateEnvStatus(Status, TimeStamp);    
+            }
         }
-        */    
-        //p_BLEdev->waitForEvent(); // Without it the CLIENT can't start to communicate
+        p_BLEdev->waitForEvent(); // Without it the CLIENT can't start to communicate
         #endif 
     }
 
     printf("\r\n\r\nStart Main Loop");
-    INTLOOP=0;
+
+    #ifdef Ble
+    tickerDif_ticker.attach(tickerDif_Callback, TIMEOUT_DIF);
+    tickerPos_ticker.attach(tickerPos_Callback, TIMEOUT_POS);
+    tickerSun_ticker.attach(tickerSun_Callback, TIMEOUT_SUN);
+    #endif 
     
     #ifdef EasySpin
     motor->Enable(); // To put the motor on hold by execute CmdEnable
@@ -605,37 +624,43 @@
         #ifdef FlightSense
         board->display->DisplayString(DisplayStr, 4);
         //printf("%s\n\r", DisplayStr);
-        #endif   
-        
-        #ifdef Ble
+        #endif
         
-        INTLOOP++;
-        if (INTLOOP==100)
-        {    
-            //if (p_customsensorservice->isStatusNotificationEn()) {
+        #ifdef Ble 
+        if (p_customsensorservice->isStatusNotificationEn()) {
+            if (tickerSta) {
+                tickerSta=false;
                 printf("\n\r\n\rSend BLE Display Status %d", Status);
-                p_customsensorservice->sendEnvStatus(Status, TimeStamp);
-                //p_customsensorservice->updateEnvStatus(Status, TimeStamp));            
-            //}
-            //if (p_customsensorservice->isDifferenceNotificationEn()) {
+                //p_customsensorservice->sendEnvStatus(Status, TimeStamp);
+                p_customsensorservice->updateEnvStatus(Status, TimeStamp);    
+            }
+        }
+        if (p_customsensorservice->isDifferenceNotificationEn()) {
+            if (tickerDif) {
+                tickerDif=false;
                 printf("\n\rSend BLE Difference %d lux/mems", diff); // Send BLE diff, no diff_abs
-                p_customsensorservice->sendEnvDifference(diff, TimeStamp);
-                //p_customsensorservice->updateEnvDifference(diff, TimeStamp));
-            //}
-            //if (p_customsensorservice->isPositionNotificationEn()) {            
+                //p_customsensorservice->sendEnvDifference(diff, TimeStamp);
+                p_customsensorservice->updateEnvDifference(diff, TimeStamp);
+            }
+        }
+        if (p_customsensorservice->isPositionNotificationEn()) {
+            if (tickerPos) {
+                tickerPos=false;       
                 printf("\n\rSend BLE Position %d", pos);
-                p_customsensorservice->sendEnvPosition(pos, TimeStamp);
-                //p_customsensorservice->updateEnvPosition(pos, TimeStamp));
-            //}
-            //if (p_customsensorservice->isSunpanelNotificationEn()) {            
-                printf("\n\rSend BLE Sunpanel %d mV", measure);            
-                p_customsensorservice->sendEnvSunpanel(measure, TimeStamp);
-                //p_customsensorservice->updateEnvSunpanel(measure, TimeStamp));
-            //}
-            INTLOOP=0;
-            //diff=0; // To reset it in case from Mems don't arrive a future value
+                //p_customsensorservice->sendEnvPosition(pos, TimeStamp);
+                p_customsensorservice->updateEnvPosition(pos, TimeStamp);
+            }
         }
-        
+        if (p_customsensorservice->isSunpanelNotificationEn()) {
+            if (tickerSun) {
+                tickerSun=false;      
+                printf("\n\rSend BLE SunPanel %d mV", measure);            
+                //p_customsensorservice->sendEnvSunpanel(measure, TimeStamp);
+                p_customsensorservice->updateEnvSunpanel(measure, TimeStamp);
+            }
+        }
+        //diff=0; // To reset it in case from Accelerometer doesn't arrive a future value (is better that Acc. always send a value)
+         
         p_BLEdev->waitForEvent();
         #endif