Redbear Nano firmware using Sandroide and showing how to send custom strings and parameters to the Android application

Dependencies:   BLE_API MbedJSONValue mbed nRF51822

Fork of BLE_RedBearNano-SAndroidEDevice by gio wild

Committer:
giowild
Date:
Fri Oct 06 10:05:45 2017 +0000
Revision:
4:2ef9b332fa7c
Parent:
3:16b7d807fa8c
Send Custom strings/parameters from Nano to smartphone

Who changed what in which revision?

UserRevisionLine numberNew contents of line
giowild 0:8d3dd6e411bf 1 /*
giowild 0:8d3dd6e411bf 2
giowild 0:8d3dd6e411bf 3 Copyright (c) 2016 Giovanni Lenzi
giowild 0:8d3dd6e411bf 4
giowild 0:8d3dd6e411bf 5 */
giowild 0:8d3dd6e411bf 6
giowild 0:8d3dd6e411bf 7 /*
giowild 0:8d3dd6e411bf 8 * This firmware is a port the RedBear BLE Shield Arduino Sketch(https://github.com/RedBearLab/nRF8001/blob/master/examples/BLEControllerSketch/BLEControllerSketch.ino),
giowild 0:8d3dd6e411bf 9 * to Redbear Nano (http://redbearlab.com/blenano/).
giowild 0:8d3dd6e411bf 10 * After connection of Nano to PC using the provided MK20 USB board,
giowild 0:8d3dd6e411bf 11 * you need to download the compiled firmware to your local disk
giowild 0:8d3dd6e411bf 12 * and drag and drop it to the newly created MBED drive.
giowild 0:8d3dd6e411bf 13 * Once flashed, you may access your Nano device with the NanoChat test application
giowild 0:8d3dd6e411bf 14 * or from any SAndroidE powered application (http://es3.unibs.it/SAndroidE/)
giowild 0:8d3dd6e411bf 15 */
giowild 0:8d3dd6e411bf 16
giowild 0:8d3dd6e411bf 17 #include "mbed.h"
giowild 0:8d3dd6e411bf 18 #include "ble/BLE.h"
giowild 2:6c45738bba43 19 //#include "UARTService.h"
giowild 2:6c45738bba43 20 #include "Queue.h"
giowild 2:6c45738bba43 21
giowild 2:6c45738bba43 22
giowild 2:6c45738bba43 23 //#define MAX_REPLY_LEN (UARTService::BLE_UART_SERVICE_MAX_DATA_LEN)
giowild 0:8d3dd6e411bf 24
giowild 0:8d3dd6e411bf 25 #define BLE_UUID_TXRX_SERVICE 0x0000 /**< The UUID of the Nordic UART Service. */
giowild 0:8d3dd6e411bf 26 #define BLE_UUID_TX_CHARACTERISTIC 0x0002 /**< The UUID of the TX Characteristic. */
giowild 0:8d3dd6e411bf 27 #define BLE_UUIDS_RX_CHARACTERISTIC 0x0003 /**< The UUID of the RX Characteristic. */
giowild 0:8d3dd6e411bf 28
giowild 0:8d3dd6e411bf 29 #define TXRX_BUF_LEN 20
giowild 0:8d3dd6e411bf 30
giowild 0:8d3dd6e411bf 31 // pin modes
giowild 0:8d3dd6e411bf 32 #define PIN_INPUT 0 // digital input pin
giowild 0:8d3dd6e411bf 33 #define PIN_OUTPUT 1 // digital output pin
giowild 0:8d3dd6e411bf 34 #define PIN_ANALOG 2 // analog pin in analogInput mode
giowild 0:8d3dd6e411bf 35 #define PIN_PWM 3 // digital pin in PWM output mode
giowild 0:8d3dd6e411bf 36 #define PIN_SERVO 4 // digital pin in Servo output mode
giowild 0:8d3dd6e411bf 37 #define PIN_NOTSET 5 // pin not set
giowild 0:8d3dd6e411bf 38
giowild 2:6c45738bba43 39 #define NO_GROUP 0 // no_group means that current pin is sampled and transmitted individually
giowild 2:6c45738bba43 40
giowild 2:6c45738bba43 41
giowild 2:6c45738bba43 42 #define ANALOG_MAX_VALUE 1024 // this is uint16 max value: 65535
giowild 2:6c45738bba43 43 #define DEFAULT_SAMPLING_INTERVAL 1000 // 1 second
giowild 2:6c45738bba43 44 #define DEFAULT_DELTA 10 // this is uint16 in range [0-65535], 655 is 1% delta
giowild 2:6c45738bba43 45
giowild 0:8d3dd6e411bf 46 BLE ble;
giowild 2:6c45738bba43 47 Queue *recvQueue = NULL, *toSendQueue =NULL;
giowild 0:8d3dd6e411bf 48
giowild 0:8d3dd6e411bf 49 // The Nordic UART Service
giowild 0:8d3dd6e411bf 50 static const uint8_t uart_base_uuid[] = {0x71, 0x3D, 0, 0, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
giowild 0:8d3dd6e411bf 51 static const uint8_t uart_tx_uuid[] = {0x71, 0x3D, 0, 3, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
giowild 0:8d3dd6e411bf 52 static const uint8_t uart_rx_uuid[] = {0x71, 0x3D, 0, 2, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
giowild 0:8d3dd6e411bf 53 static const uint8_t uart_base_uuid_rev[] = {0x1E, 0x94, 0x8D, 0xF1, 0x48, 0x31, 0x94, 0xBA, 0x75, 0x4C, 0x3E, 0x50, 0, 0, 0x3D, 0x71};
giowild 0:8d3dd6e411bf 54
giowild 0:8d3dd6e411bf 55 uint8_t payloadTicker[TXRX_BUF_LEN] = {0,};
giowild 0:8d3dd6e411bf 56 uint8_t txPayload[TXRX_BUF_LEN] = {0,};
giowild 0:8d3dd6e411bf 57 uint8_t rxPayload[TXRX_BUF_LEN] = {0,};
giowild 0:8d3dd6e411bf 58
giowild 0:8d3dd6e411bf 59 GattCharacteristic txCharacteristic (uart_tx_uuid, txPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
giowild 0:8d3dd6e411bf 60 GattCharacteristic rxCharacteristic (uart_rx_uuid, rxPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
giowild 0:8d3dd6e411bf 61 GattCharacteristic *uartChars[] = {&txCharacteristic, &rxCharacteristic};
giowild 0:8d3dd6e411bf 62 GattService uartService(uart_base_uuid, uartChars, sizeof(uartChars) / sizeof(GattCharacteristic *));
giowild 2:6c45738bba43 63 //UARTService *m_uart_service_ptr;
giowild 2:6c45738bba43 64
giowild 0:8d3dd6e411bf 65
giowild 0:8d3dd6e411bf 66 static const int maxPins = 30;
giowild 0:8d3dd6e411bf 67 uint8_t pinTypes[maxPins];
giowild 2:6c45738bba43 68 uint8_t pinGroups[maxPins];
giowild 0:8d3dd6e411bf 69 uint16_t prevValues[maxPins];
giowild 0:8d3dd6e411bf 70
giowild 2:6c45738bba43 71 int pinSamplingIntervals[maxPins];
giowild 2:6c45738bba43 72 int pinTimers[maxPins];
giowild 2:6c45738bba43 73 uint16_t pinDelta[maxPins];
giowild 2:6c45738bba43 74
giowild 1:ff6ec7837f46 75 DigitalInOut digitals[] = {P0_0,P0_7,P0_8,P0_9,P0_10,P0_11,P0_15,P0_19,P0_28,P0_29};
giowild 1:ff6ec7837f46 76 int mapDigitals[] = { 0,-1,-1,-1,-1,-1,-1, 1,2,3,4,5,-1,-1,-1, 6,-1,-1,-1, 7,-1,-1,-1,-1,-1,-1,-1,-1, 8, 9,-1};
giowild 0:8d3dd6e411bf 77 AnalogIn analogs[] = {P0_1, P0_2, P0_3, P0_4, P0_5, P0_6};
giowild 0:8d3dd6e411bf 78 int mapAnalogs[] = {-1, 0, 1, 2, 3, 4, 5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
giowild 0:8d3dd6e411bf 79
giowild 2:6c45738bba43 80 void enqueueItem(Queue *queue, uint8_t *buf, int len) {
giowild 3:16b7d807fa8c 81 if (ble.getGapState().connected) {
giowild 3:16b7d807fa8c 82 NODE *item = NULL;
giowild 3:16b7d807fa8c 83 item = (NODE*) malloc(sizeof (NODE));
giowild 3:16b7d807fa8c 84 memcpy(item->data.payload, buf, len);
giowild 3:16b7d807fa8c 85 item->data.length = len;
giowild 3:16b7d807fa8c 86 Enqueue(queue, item);
giowild 3:16b7d807fa8c 87 }
giowild 2:6c45738bba43 88 }
giowild 2:6c45738bba43 89
giowild 0:8d3dd6e411bf 90 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
giowild 0:8d3dd6e411bf 91 {
giowild 0:8d3dd6e411bf 92 //pc.printf("Disconnected \r\n");
giowild 0:8d3dd6e411bf 93 //pc.printf("Restart advertising \r\n");
giowild 0:8d3dd6e411bf 94 ble.startAdvertising();
giowild 0:8d3dd6e411bf 95 }
giowild 0:8d3dd6e411bf 96
giowild 2:6c45738bba43 97
giowild 2:6c45738bba43 98 NODE *enqItem = NULL;
giowild 2:6c45738bba43 99 int bytesCmd = 0;
giowild 2:6c45738bba43 100 bool endsOnNewLine = true;
giowild 2:6c45738bba43 101 #define NOT_FOUND -1
giowild 2:6c45738bba43 102
giowild 2:6c45738bba43 103 void WrittenHandler(const GattWriteCallbackParams *params)
giowild 2:6c45738bba43 104 {
giowild 2:6c45738bba43 105 uint8_t buf[TXRX_BUF_LEN];
giowild 2:6c45738bba43 106 uint16_t bytesRead, i;
giowild 2:6c45738bba43 107
giowild 2:6c45738bba43 108 if (params->handle == txCharacteristic.getValueAttribute().getHandle()) {
giowild 2:6c45738bba43 109 ble.readCharacteristicValue(params->handle, buf, &bytesRead);
giowild 2:6c45738bba43 110
giowild 2:6c45738bba43 111 if (enqItem == NULL){
giowild 2:6c45738bba43 112 endsOnNewLine = buf[0]=='{';
giowild 0:8d3dd6e411bf 113 }
giowild 2:6c45738bba43 114
giowild 2:6c45738bba43 115 if (!endsOnNewLine) {
giowild 2:6c45738bba43 116 enqueueItem(recvQueue, buf, bytesRead);
giowild 2:6c45738bba43 117 } else {
giowild 2:6c45738bba43 118 for (i=0; i<bytesRead; i++) {
giowild 2:6c45738bba43 119 if (endsOnNewLine && (buf[i]=='\n' || bytesCmd>QUEUE_STRING_LENGTH)) {
giowild 2:6c45738bba43 120 if (enqItem != NULL && enqItem->data.length>0) {
giowild 2:6c45738bba43 121 // push string to queue
giowild 2:6c45738bba43 122 Enqueue(recvQueue, enqItem);
giowild 2:6c45738bba43 123 }
giowild 2:6c45738bba43 124 enqItem = NULL;
giowild 2:6c45738bba43 125 } else {
giowild 2:6c45738bba43 126 // enqueue character
giowild 2:6c45738bba43 127 if (enqItem == NULL) {
giowild 2:6c45738bba43 128 enqItem = (NODE*) malloc(sizeof (NODE));
giowild 2:6c45738bba43 129 bytesCmd = 0;
giowild 2:6c45738bba43 130 }
giowild 2:6c45738bba43 131 enqItem->data.payload[bytesCmd++]=buf[i];
giowild 2:6c45738bba43 132 enqItem->data.length = bytesCmd;
giowild 2:6c45738bba43 133 }
giowild 2:6c45738bba43 134 }
giowild 2:6c45738bba43 135 }
giowild 2:6c45738bba43 136 }
giowild 0:8d3dd6e411bf 137 }
giowild 0:8d3dd6e411bf 138
giowild 2:6c45738bba43 139 uint16_t readPin(uint8_t pin) {
giowild 2:6c45738bba43 140 uint8_t mode = pinTypes[pin];
giowild 2:6c45738bba43 141 if (mode==PIN_INPUT) { // exists and is initialized as digital output
giowild 2:6c45738bba43 142 return digitals[mapDigitals[pin]].read()==0?0:ANALOG_MAX_VALUE;
giowild 2:6c45738bba43 143 } else if (mode==PIN_OUTPUT) { // exists and is initialized as digital output
giowild 2:6c45738bba43 144 return digitals[mapDigitals[pin]].read()==0?0:ANALOG_MAX_VALUE;
giowild 2:6c45738bba43 145 } else if (mode==PIN_ANALOG) { // exists and is initialized as digital output
giowild 2:6c45738bba43 146 return analogs[mapAnalogs[pin]].read_u16(); // 10 bits only
giowild 2:6c45738bba43 147 }
giowild 2:6c45738bba43 148 return 0;
giowild 2:6c45738bba43 149 }
giowild 2:6c45738bba43 150
giowild 2:6c45738bba43 151 uint16_t readPinOld(int pin) {
giowild 0:8d3dd6e411bf 152 uint8_t mode = pinTypes[pin];
giowild 0:8d3dd6e411bf 153 if (mode==PIN_INPUT) { // exists and is initialized as digital output
giowild 0:8d3dd6e411bf 154 mode = 0;
giowild 0:8d3dd6e411bf 155 return (((uint16_t)mode)<<8 | (uint16_t)(digitals[mapDigitals[pin]].read()));
giowild 0:8d3dd6e411bf 156 } else if (mode==PIN_OUTPUT) { // exists and is initialized as digital output
giowild 0:8d3dd6e411bf 157 mode = 1;
giowild 0:8d3dd6e411bf 158 return (((uint16_t)mode)<<8 | (uint16_t)(digitals[mapDigitals[pin]].read()));
giowild 0:8d3dd6e411bf 159 } else if (mode==PIN_ANALOG) { // exists and is initialized as digital output
giowild 0:8d3dd6e411bf 160 mode = 2;
giowild 0:8d3dd6e411bf 161 uint16_t value = analogs[mapAnalogs[pin]].read_u16();
giowild 0:8d3dd6e411bf 162 uint8_t value_lo = value;
giowild 0:8d3dd6e411bf 163 uint8_t value_hi = value>>8;
giowild 0:8d3dd6e411bf 164 mode = (value_hi << 4) | mode;
giowild 0:8d3dd6e411bf 165 return (((uint16_t)mode)<<8) | (uint16_t)value_lo;
giowild 0:8d3dd6e411bf 166 }
giowild 0:8d3dd6e411bf 167 return 0;
giowild 0:8d3dd6e411bf 168 }
giowild 0:8d3dd6e411bf 169
giowild 2:6c45738bba43 170 void sendPinValue(uint8_t pin, uint16_t value) {
giowild 0:8d3dd6e411bf 171 uint8_t buf[TXRX_BUF_LEN];
giowild 0:8d3dd6e411bf 172 buf[0]='G';
giowild 0:8d3dd6e411bf 173 buf[1]=pin;
giowild 2:6c45738bba43 174 buf[2]=(uint8_t)(value>>8);
giowild 2:6c45738bba43 175 buf[3]= (uint8_t) ((value<<8)>>8);
giowild 2:6c45738bba43 176 enqueueItem(toSendQueue, buf, 4);
giowild 2:6c45738bba43 177 /*int len = sprintf((char *)buf,"{\"pin\":%d,\"v\":%4.3f}",pin,(float)value/(float)ANALOG_MAX_VALUE);
giowild 2:6c45738bba43 178 enqueueItem(toSendQueue,buf, len);*/
giowild 2:6c45738bba43 179
giowild 0:8d3dd6e411bf 180 prevValues[pin] = value;
giowild 0:8d3dd6e411bf 181 }
giowild 0:8d3dd6e411bf 182
giowild 0:8d3dd6e411bf 183
giowild 2:6c45738bba43 184 void sendGroup(uint8_t groupno) {
giowild 2:6c45738bba43 185 uint8_t buf[TXRX_BUF_LEN], i=0;
giowild 2:6c45738bba43 186 buf[i++]='G';
giowild 2:6c45738bba43 187 for (uint8_t pin=0; pin<maxPins;pin++){
giowild 2:6c45738bba43 188 if (pinGroups[pin]==groupno) {
giowild 2:6c45738bba43 189 uint16_t value = readPin(pin);
giowild 2:6c45738bba43 190 buf[i++] = pin;
giowild 2:6c45738bba43 191 buf[i++] = (uint8_t)(value>>8);
giowild 2:6c45738bba43 192 buf[i++] = (uint8_t) ((value<<8)>>8);
giowild 2:6c45738bba43 193 prevValues[pin] = value;
giowild 2:6c45738bba43 194 }
giowild 2:6c45738bba43 195 }
giowild 2:6c45738bba43 196 if (i>1) { // at least 1 pin value to send
giowild 2:6c45738bba43 197 enqueueItem(toSendQueue, buf, i);
giowild 2:6c45738bba43 198 }
giowild 0:8d3dd6e411bf 199 }
giowild 0:8d3dd6e411bf 200
giowild 2:6c45738bba43 201 bool isInputPin(uint8_t pin) {
giowild 2:6c45738bba43 202 if (pin<maxPins){
giowild 2:6c45738bba43 203 uint8_t type = pinTypes[pin];
giowild 2:6c45738bba43 204 return type==PIN_INPUT||type==PIN_ANALOG;
giowild 0:8d3dd6e411bf 205 }
giowild 2:6c45738bba43 206 return false;
giowild 0:8d3dd6e411bf 207 }
giowild 0:8d3dd6e411bf 208
giowild 0:8d3dd6e411bf 209 void m_status_check_handle(void)
giowild 0:8d3dd6e411bf 210 {
giowild 0:8d3dd6e411bf 211 for (int pin=0; pin<maxPins;pin++){
giowild 0:8d3dd6e411bf 212 if (pinTypes[pin]==PIN_INPUT||pinTypes[pin]==PIN_ANALOG) {
giowild 0:8d3dd6e411bf 213 uint16_t value = readPin(pin);
giowild 0:8d3dd6e411bf 214 //uint16_t prevValue = 33;
giowild 0:8d3dd6e411bf 215 if (prevValues[pin] != value) {
giowild 0:8d3dd6e411bf 216 sendPinValue(pin,value);
giowild 0:8d3dd6e411bf 217 }
giowild 0:8d3dd6e411bf 218 }
giowild 0:8d3dd6e411bf 219 }
giowild 0:8d3dd6e411bf 220 }
giowild 0:8d3dd6e411bf 221
giowild 2:6c45738bba43 222
giowild 2:6c45738bba43 223 static volatile int gcd=-1;
giowild 2:6c45738bba43 224 Ticker *pTicker;
giowild 2:6c45738bba43 225 //Timeout timeout;
giowild 2:6c45738bba43 226 static volatile bool recalcTimer = false;
giowild 2:6c45738bba43 227 static volatile bool triggerSensorPolling = false;
giowild 2:6c45738bba43 228 static volatile bool gcdChanged =false;
giowild 2:6c45738bba43 229
giowild 2:6c45738bba43 230 int calc_gcd(int n1,int n2) {
giowild 2:6c45738bba43 231 int lgcd=1;
giowild 2:6c45738bba43 232 for(int i=2; i <= n1 && i <= n2; ++i)
giowild 2:6c45738bba43 233 {
giowild 2:6c45738bba43 234 // Checks if i is factor of both integers
giowild 2:6c45738bba43 235 if(n1%i==0 && n2%i==0)
giowild 2:6c45738bba43 236 lgcd = i;
giowild 2:6c45738bba43 237 }
giowild 2:6c45738bba43 238 return lgcd;
giowild 2:6c45738bba43 239 }
giowild 2:6c45738bba43 240
giowild 2:6c45738bba43 241 void check_pin_changed(void)
giowild 2:6c45738bba43 242 {
giowild 2:6c45738bba43 243 uint8_t buf[QUEUE_STRING_LENGTH];
giowild 2:6c45738bba43 244 if (gcd>0) {
giowild 2:6c45738bba43 245 for (int pin=0; pin<maxPins;pin++){
giowild 2:6c45738bba43 246 if (isInputPin(pin)) {
giowild 2:6c45738bba43 247 if (pinTimers[pin] < 0) {
giowild 2:6c45738bba43 248 pinTimers[pin] = pinSamplingIntervals[pin];
giowild 2:6c45738bba43 249 } else {
giowild 2:6c45738bba43 250 pinTimers[pin]-=gcd;
giowild 2:6c45738bba43 251 }
giowild 2:6c45738bba43 252 if (pinTimers[pin]==0) {
giowild 2:6c45738bba43 253 pinTimers[pin] = pinSamplingIntervals[pin];
giowild 2:6c45738bba43 254 uint16_t value = readPin(pin);
giowild 2:6c45738bba43 255 if (abs(prevValues[pin]-value) >= pinDelta[pin]) {
giowild 2:6c45738bba43 256 if (pinGroups[pin]!=NO_GROUP) { // enqueue sending operation for group
giowild 2:6c45738bba43 257 int len = sprintf((char *)buf,"R%c",pinGroups[pin]);
giowild 2:6c45738bba43 258 enqueueItem(recvQueue, buf, len);
giowild 2:6c45738bba43 259 } else { // send the pin
giowild 2:6c45738bba43 260 sendPinValue(pin,value);
giowild 2:6c45738bba43 261 }
giowild 2:6c45738bba43 262 }
giowild 2:6c45738bba43 263 }
giowild 2:6c45738bba43 264 }
giowild 2:6c45738bba43 265 }
giowild 2:6c45738bba43 266 }
giowild 2:6c45738bba43 267 }
giowild 2:6c45738bba43 268
giowild 2:6c45738bba43 269 void calc_timer_interval()
giowild 2:6c45738bba43 270 {
giowild 2:6c45738bba43 271 gcd = -1;
giowild 2:6c45738bba43 272 for (int pin=0; pin<maxPins;pin++){
giowild 2:6c45738bba43 273 if (isInputPin(pin) && pinSamplingIntervals[pin]>0) {
giowild 2:6c45738bba43 274 uint8_t buf[TXRX_BUF_LEN];
giowild 2:6c45738bba43 275 int len = sprintf((char *)buf,"TIMER %d@%d",pin,pinSamplingIntervals[pin]);
giowild 2:6c45738bba43 276 //int len = sprintf((char *)buf,"check-gcd");
giowild 2:6c45738bba43 277 enqueueItem(toSendQueue, buf, len);
giowild 2:6c45738bba43 278
giowild 2:6c45738bba43 279 if (gcd==-1) {
giowild 2:6c45738bba43 280 gcd = pinSamplingIntervals[pin];
giowild 2:6c45738bba43 281 } else {
giowild 2:6c45738bba43 282 gcd = calc_gcd(gcd,pinSamplingIntervals[pin]);
giowild 2:6c45738bba43 283 }
giowild 2:6c45738bba43 284 }
giowild 2:6c45738bba43 285 }
giowild 2:6c45738bba43 286 }
giowild 2:6c45738bba43 287
giowild 2:6c45738bba43 288 bool initPin(uint8_t pin, uint8_t type){
giowild 2:6c45738bba43 289 bool ret=false,wasset=true,armTimer=false;
giowild 2:6c45738bba43 290
giowild 2:6c45738bba43 291 //uint8_t buf[TXRX_BUF_LEN];
giowild 2:6c45738bba43 292 //buf[0]='Y';buf[1]=pin;buf[2]=type;
giowild 2:6c45738bba43 293 //int len = sprintf((char *)buf,"ASD%d-%c",pin,pin);
giowild 2:6c45738bba43 294 //enqueueItem(toSendQueue, buf, 3);
giowild 2:6c45738bba43 295
giowild 2:6c45738bba43 296 if (pin<maxPins) { // "initPin(): Pin number out of bounds"
giowild 2:6c45738bba43 297 wasset = pinTypes[pin]!=PIN_NOTSET;
giowild 2:6c45738bba43 298 if ((type==PIN_INPUT||type==PIN_OUTPUT) && mapDigitals[pin]>=0) {
giowild 2:6c45738bba43 299 if (type==PIN_INPUT) digitals[mapDigitals[pin]].input(); // initialize as input
giowild 2:6c45738bba43 300 if (type==PIN_OUTPUT) digitals[mapDigitals[pin]].output(); // initialize as input
giowild 2:6c45738bba43 301 pinTypes[pin] = type; // mark the pin as initialized
giowild 2:6c45738bba43 302 ret =true;
giowild 2:6c45738bba43 303 } else if (type==PIN_ANALOG && mapAnalogs[pin]>=0) {
giowild 2:6c45738bba43 304 pinTypes[pin] = type; // mark the pin as initialized
giowild 2:6c45738bba43 305 ret =true;
giowild 2:6c45738bba43 306 }
giowild 2:6c45738bba43 307 if (!wasset && ret && (type==PIN_INPUT||type==PIN_ANALOG)) armTimer=true;
giowild 2:6c45738bba43 308 }
giowild 2:6c45738bba43 309 if (armTimer) {
giowild 2:6c45738bba43 310 pinSamplingIntervals[pin] = DEFAULT_SAMPLING_INTERVAL;
giowild 2:6c45738bba43 311 //pinTimers[pin]=pinSamplingIntervals[pin];
giowild 2:6c45738bba43 312 recalcTimer = true;
giowild 2:6c45738bba43 313 }
giowild 2:6c45738bba43 314
giowild 2:6c45738bba43 315 return ret;
giowild 2:6c45738bba43 316 }
giowild 2:6c45738bba43 317
giowild 2:6c45738bba43 318 bool initPin(uint8_t pin, uint8_t type, uint8_t group){
giowild 2:6c45738bba43 319 bool ret = initPin(pin, type);
giowild 2:6c45738bba43 320 if (ret){
giowild 2:6c45738bba43 321 pinGroups[pin]=group;
giowild 2:6c45738bba43 322 }
giowild 2:6c45738bba43 323 return ret;
giowild 2:6c45738bba43 324 }
giowild 2:6c45738bba43 325
giowild 2:6c45738bba43 326 void changeDelta(uint8_t pin, uint16_t delta) {
giowild 2:6c45738bba43 327 uint8_t buf[TXRX_BUF_LEN];
giowild 2:6c45738bba43 328 int len = sprintf((char *)buf,"DELTA %d@%d",pin,delta);
giowild 2:6c45738bba43 329 enqueueItem(toSendQueue, buf, len);
giowild 2:6c45738bba43 330
giowild 2:6c45738bba43 331 //float fdelta = delta / ANALOG_MAX_VALUE;
giowild 2:6c45738bba43 332 if (delta > ANALOG_MAX_VALUE) delta=ANALOG_MAX_VALUE;
giowild 2:6c45738bba43 333 if (isInputPin(pin)) {
giowild 2:6c45738bba43 334 pinDelta[pin] = delta;
giowild 2:6c45738bba43 335 }
giowild 2:6c45738bba43 336 }
giowild 2:6c45738bba43 337
giowild 2:6c45738bba43 338 void changeDeltaPercent(uint8_t pin, float fdelta) {
giowild 2:6c45738bba43 339 changeDelta(pin, (uint16_t)(fdelta*ANALOG_MAX_VALUE));
giowild 2:6c45738bba43 340 }
giowild 2:6c45738bba43 341 void changeSamplingInterval(uint8_t pin, int interval) {
giowild 2:6c45738bba43 342 if (isInputPin(pin)) {
giowild 2:6c45738bba43 343 pinSamplingIntervals[pin]= interval;
giowild 2:6c45738bba43 344 recalcTimer = true;
giowild 2:6c45738bba43 345 }
giowild 2:6c45738bba43 346 }
giowild 2:6c45738bba43 347
giowild 2:6c45738bba43 348 bool writeDigital(uint8_t pin, bool value){
giowild 2:6c45738bba43 349 if (mapDigitals[pin]>=0) {
giowild 2:6c45738bba43 350 digitals[mapDigitals[pin]].write(value?1:0);
giowild 2:6c45738bba43 351 //sendPinValue(pin,readPin(pin));
giowild 2:6c45738bba43 352 }
giowild 2:6c45738bba43 353 }
giowild 2:6c45738bba43 354
giowild 2:6c45738bba43 355 void parseRedBearCmd(uint8_t* cmdString){
giowild 2:6c45738bba43 356 uint8_t buf[TXRX_BUF_LEN];
giowild 2:6c45738bba43 357 memset(buf, 0, TXRX_BUF_LEN);
giowild 2:6c45738bba43 358 int len=0, scanned=-1, sampling=-1;
giowild 2:6c45738bba43 359 float fdelta=-1;
giowild 2:6c45738bba43 360
giowild 2:6c45738bba43 361 uint8_t startOffset = cmdString[0]==0?1:0;
giowild 2:6c45738bba43 362 uint8_t index = startOffset;
giowild 2:6c45738bba43 363 uint8_t cmd = cmdString[index++], pin=cmdString[index++], mode=PIN_NOTSET, group=NO_GROUP;
giowild 2:6c45738bba43 364 pin = pin>=48?pin-48:pin;
giowild 2:6c45738bba43 365
giowild 2:6c45738bba43 366 switch (cmd) {
giowild 2:6c45738bba43 367 case '{':
giowild 2:6c45738bba43 368 //snprintf((char*) buf, MAX_REPLY_LEN, "ERROR: Unknown char\n");
giowild 2:6c45738bba43 369 //m_uart_service_ptr->writeString((char*)buf);
giowild 2:6c45738bba43 370 break;
giowild 2:6c45738bba43 371 case 'Y':
giowild 2:6c45738bba43 372 uint8_t value2write = cmdString[index++]-48;
giowild 2:6c45738bba43 373 value2write = value2write>=48?value2write-48:value2write;
giowild 2:6c45738bba43 374 writeDigital(pin,value2write!=0);
giowild 2:6c45738bba43 375 break;
giowild 2:6c45738bba43 376
giowild 2:6c45738bba43 377 case 'M': //pc.printf("Querying pin %u mode\n",pin);
giowild 2:6c45738bba43 378 buf[0]=cmd;buf[1]=pin;buf[2]=pinTypes[pin];
giowild 2:6c45738bba43 379 enqueueItem(toSendQueue, buf, 3);
giowild 2:6c45738bba43 380 break;
giowild 2:6c45738bba43 381
giowild 2:6c45738bba43 382 case 'S': // set pin mode
giowild 2:6c45738bba43 383 mode = cmdString[index++];
giowild 2:6c45738bba43 384 mode = mode>=48?mode-48:mode;
giowild 2:6c45738bba43 385 group = cmdString[index++];
giowild 2:6c45738bba43 386 if (initPin(pin, mode, group)) { // analogs are already initialized
giowild 2:6c45738bba43 387 //if (initPin(pin, mode)) { // analogs are already initialized
giowild 2:6c45738bba43 388 sendPinValue(pin,readPin(pin));
giowild 2:6c45738bba43 389 }
giowild 2:6c45738bba43 390 break;
giowild 2:6c45738bba43 391
giowild 2:6c45738bba43 392 case 'D': // delta to consider value changed (as percentage [0-1] of Voltage range)
giowild 2:6c45738bba43 393 scanned = sscanf( (char *)&cmdString[2], "%f", &fdelta);
giowild 2:6c45738bba43 394
giowild 2:6c45738bba43 395 if (scanned==1 && fdelta>=0 && fdelta<=1) {
giowild 2:6c45738bba43 396 len = sprintf((char *)buf,"DELTA%d@%f",(int)pin,fdelta);
giowild 2:6c45738bba43 397 enqueueItem(toSendQueue, buf, len);
giowild 2:6c45738bba43 398 changeDeltaPercent(pin, fdelta);
giowild 2:6c45738bba43 399 /*changeDelta ( pin,((uint16_t)cmdString[index+0]) << 8 |
giowild 2:6c45738bba43 400 ((uint16_t)cmdString[index+1]) );*/
giowild 2:6c45738bba43 401 } else {
giowild 2:6c45738bba43 402 len = sprintf((char *)buf,"DELTA%d@ERR",(int)pin);
giowild 2:6c45738bba43 403 enqueueItem(toSendQueue, buf, len);
giowild 2:6c45738bba43 404 }
giowild 2:6c45738bba43 405 break;
giowild 2:6c45738bba43 406
giowild 2:6c45738bba43 407 case 'I': // sampling interval
giowild 2:6c45738bba43 408 scanned = sscanf( (char *)&cmdString[2], "%d", &sampling);
giowild 2:6c45738bba43 409
giowild 2:6c45738bba43 410 if (scanned==1 && sampling>=0) {
giowild 2:6c45738bba43 411 len = sprintf((char *)buf,"SAMPL%d@%d",(int)pin,sampling);
giowild 2:6c45738bba43 412 enqueueItem(toSendQueue, buf, len);
giowild 2:6c45738bba43 413 changeSamplingInterval( pin, sampling);
giowild 2:6c45738bba43 414 /*changeSamplingInterval( pin,((int)cmdString[index+0]) << 24 |
giowild 2:6c45738bba43 415 ((int)cmdString[index+1]) << 16 |
giowild 2:6c45738bba43 416 ((int)cmdString[index+2]) << 8 |
giowild 2:6c45738bba43 417 ((int)cmdString[index+3]) );*/
giowild 2:6c45738bba43 418 } else {
giowild 2:6c45738bba43 419 len = sprintf((char *)buf,"SAMPL%d@ERR",(int)pin);
giowild 2:6c45738bba43 420 enqueueItem(toSendQueue, buf, len);
giowild 2:6c45738bba43 421 }
giowild 2:6c45738bba43 422 break;
giowild 2:6c45738bba43 423
giowild 2:6c45738bba43 424 case 'G': //pc.printf("Reading pin %u\n",pin);
giowild 2:6c45738bba43 425 switch (pinTypes[pin]) {
giowild 2:6c45738bba43 426 case PIN_INPUT:
giowild 2:6c45738bba43 427 case PIN_ANALOG:
giowild 2:6c45738bba43 428 sendPinValue(pin,readPin(pin));
giowild 2:6c45738bba43 429 break;
giowild 2:6c45738bba43 430 case PIN_OUTPUT: // TODO: send warning pin not readable (is an output)
giowild 2:6c45738bba43 431 default: // TODO: send warning pin not initialized
giowild 2:6c45738bba43 432 buf[0]=PIN_NOTSET;buf[1]=PIN_NOTSET;buf[2]=PIN_NOTSET;
giowild 2:6c45738bba43 433 enqueueItem(toSendQueue, buf, 3);
giowild 2:6c45738bba43 434 break;
giowild 2:6c45738bba43 435 }
giowild 2:6c45738bba43 436 break;
giowild 2:6c45738bba43 437
giowild 2:6c45738bba43 438 case 'T':
giowild 2:6c45738bba43 439 switch (pinTypes[pin]) {
giowild 2:6c45738bba43 440 case PIN_OUTPUT:
giowild 2:6c45738bba43 441 uint8_t value2write = cmdString[index++];
giowild 2:6c45738bba43 442 if (mapDigitals[pin]>=0) {
giowild 2:6c45738bba43 443 digitals[mapDigitals[pin]].write(value2write==0?0:1);
giowild 2:6c45738bba43 444 sendPinValue(pin,readPin(pin));
giowild 2:6c45738bba43 445 }
giowild 2:6c45738bba43 446 break;
giowild 2:6c45738bba43 447 case PIN_INPUT: // TODO: send warning pin not writable (is an input)
giowild 2:6c45738bba43 448 case PIN_ANALOG: // TODO: send warning pin not writable (is an input)
giowild 2:6c45738bba43 449 default: // TODO: send warning pin not initialized
giowild 2:6c45738bba43 450 buf[0]='T';buf[1]='T';buf[2]='T';
giowild 2:6c45738bba43 451 enqueueItem(toSendQueue, buf, 3);
giowild 2:6c45738bba43 452 break;
giowild 2:6c45738bba43 453 }
giowild 2:6c45738bba43 454 break;
giowild 2:6c45738bba43 455 case 'R':
giowild 2:6c45738bba43 456 // pin variable contains the group, not the pin number
giowild 2:6c45738bba43 457 sendGroup(pin);
giowild 2:6c45738bba43 458 break;
giowild 2:6c45738bba43 459 default:
giowild 2:6c45738bba43 460 // echo received buffer
giowild 2:6c45738bba43 461 enqueueItem(toSendQueue, &cmdString[startOffset], strlen((char *)&cmdString[startOffset]));
giowild 2:6c45738bba43 462 break;
giowild 2:6c45738bba43 463 }
giowild 2:6c45738bba43 464 }
giowild 2:6c45738bba43 465
giowild 2:6c45738bba43 466
giowild 2:6c45738bba43 467 void triggerSensor(){
giowild 2:6c45738bba43 468 triggerSensorPolling=true;
giowild 2:6c45738bba43 469 }
giowild 2:6c45738bba43 470
giowild 2:6c45738bba43 471
giowild 2:6c45738bba43 472 void changeGcdTiming(){
giowild 2:6c45738bba43 473 uint8_t buf[TXRX_BUF_LEN];
giowild 2:6c45738bba43 474 int len = sprintf((char *)buf,"check-gcd %d",gcd);
giowild 2:6c45738bba43 475 enqueueItem(toSendQueue, buf, len);
giowild 2:6c45738bba43 476 }
giowild 2:6c45738bba43 477
giowild 4:2ef9b332fa7c 478 AnalogIn ain(P0_6);
giowild 4:2ef9b332fa7c 479 void fnCustomParameter() {
giowild 4:2ef9b332fa7c 480 unsigned short val,max;
giowild 4:2ef9b332fa7c 481 int i;
giowild 4:2ef9b332fa7c 482 if (ble.getGapState().connected) {
giowild 4:2ef9b332fa7c 483 i=0;
giowild 4:2ef9b332fa7c 484 max=0;
giowild 4:2ef9b332fa7c 485 while(i++<300) { // read the max of 3 consecutive samples = 3*100 (100=10ms/100us)
giowild 4:2ef9b332fa7c 486 val = ain.read_u16();
giowild 4:2ef9b332fa7c 487 if (val>max) max=val;
giowild 4:2ef9b332fa7c 488 wait_us(100);
giowild 4:2ef9b332fa7c 489 }
giowild 4:2ef9b332fa7c 490
giowild 4:2ef9b332fa7c 491 // enqueue into bluetooth queue
giowild 4:2ef9b332fa7c 492 uint8_t buf[TXRX_BUF_LEN];
giowild 4:2ef9b332fa7c 493 int len = sprintf((char *)buf,"W%d",val);
giowild 4:2ef9b332fa7c 494 enqueueItem(toSendQueue, buf, len);
giowild 4:2ef9b332fa7c 495 }
giowild 4:2ef9b332fa7c 496 }
giowild 4:2ef9b332fa7c 497
giowild 2:6c45738bba43 498
giowild 0:8d3dd6e411bf 499 int main(void)
giowild 0:8d3dd6e411bf 500 {
giowild 0:8d3dd6e411bf 501 for (int i=0;i<maxPins;i++) {
giowild 0:8d3dd6e411bf 502 pinTypes[i] = PIN_NOTSET;
giowild 0:8d3dd6e411bf 503 prevValues[i] = 0;
giowild 2:6c45738bba43 504 pinSamplingIntervals[i] = -1;
giowild 2:6c45738bba43 505 pinTimers[i]=-1;
giowild 2:6c45738bba43 506 pinDelta[i]=DEFAULT_DELTA;
giowild 2:6c45738bba43 507 pinGroups[i]=NO_GROUP;
giowild 0:8d3dd6e411bf 508 }
giowild 2:6c45738bba43 509
giowild 4:2ef9b332fa7c 510 initPin(28,PIN_OUTPUT);
giowild 4:2ef9b332fa7c 511 initPin(29,PIN_OUTPUT);
giowild 4:2ef9b332fa7c 512 writeDigital(28,0);
giowild 4:2ef9b332fa7c 513 writeDigital(29,1);
giowild 4:2ef9b332fa7c 514
giowild 0:8d3dd6e411bf 515 ble.init();
giowild 0:8d3dd6e411bf 516 ble.onDisconnection(disconnectionCallback);
giowild 0:8d3dd6e411bf 517 ble.onDataWritten(WrittenHandler);
giowild 0:8d3dd6e411bf 518
giowild 0:8d3dd6e411bf 519 // setup advertising
giowild 0:8d3dd6e411bf 520 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
giowild 0:8d3dd6e411bf 521 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
giowild 0:8d3dd6e411bf 522 ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
giowild 0:8d3dd6e411bf 523 (const uint8_t *)"MyNano", sizeof("MyNano") - 1);
giowild 0:8d3dd6e411bf 524 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
giowild 0:8d3dd6e411bf 525 (const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid));
giowild 0:8d3dd6e411bf 526 // 100ms; in multiples of 0.625ms.
giowild 0:8d3dd6e411bf 527 ble.setAdvertisingInterval(160);
giowild 0:8d3dd6e411bf 528
giowild 0:8d3dd6e411bf 529 ble.addService(uartService);
giowild 0:8d3dd6e411bf 530
giowild 0:8d3dd6e411bf 531 ble.startAdvertising();
giowild 0:8d3dd6e411bf 532
giowild 2:6c45738bba43 533 //ticker.attach(m_status_check_handle, 0.2);
giowild 2:6c45738bba43 534
giowild 2:6c45738bba43 535 // Create a UARTService object (GATT stuff).
giowild 2:6c45738bba43 536 //UARTService myUartService(ble);
giowild 2:6c45738bba43 537 //m_uart_service_ptr = &myUartService;
giowild 2:6c45738bba43 538
giowild 0:8d3dd6e411bf 539 Ticker ticker;
giowild 2:6c45738bba43 540 //pTicker = &ticker;
giowild 2:6c45738bba43 541
giowild 2:6c45738bba43 542 //Ticker pinTicker;
giowild 2:6c45738bba43 543 //pinTicker.attach(triggerSensor, 5);
giowild 2:6c45738bba43 544
giowild 2:6c45738bba43 545 Ticker gcdTicker;
giowild 2:6c45738bba43 546 gcdTicker.attach(changeGcdTiming, 5);
giowild 4:2ef9b332fa7c 547
giowild 4:2ef9b332fa7c 548 Ticker customParameterTicker;
giowild 4:2ef9b332fa7c 549 customParameterTicker.attach(fnCustomParameter, 5);
giowild 2:6c45738bba43 550
giowild 2:6c45738bba43 551 recvQueue = ConstructQueue(40);
giowild 2:6c45738bba43 552 toSendQueue = ConstructQueue(40);
giowild 2:6c45738bba43 553
giowild 2:6c45738bba43 554 uint8_t buf[QUEUE_STRING_LENGTH];
giowild 2:6c45738bba43 555 NODE *deqItem = NULL;
giowild 2:6c45738bba43 556 /*
giowild 2:6c45738bba43 557 // set pin 7 as VCC and pin 28 as GND
giowild 2:6c45738bba43 558 int len = sprintf((char *)buf,"S%c%c",7,1);
giowild 2:6c45738bba43 559 enqueueItem(recvQueue, buf, len);
giowild 2:6c45738bba43 560 len = sprintf((char *)buf,"S%c%c",28,1);
giowild 2:6c45738bba43 561 enqueueItem(recvQueue, buf, len);
giowild 2:6c45738bba43 562 len = sprintf((char *)buf,"Y%c%c",7,'1');
giowild 2:6c45738bba43 563 enqueueItem(recvQueue, buf, len);
giowild 2:6c45738bba43 564 len = sprintf((char *)buf,"Y%c%c",28,'0');
giowild 2:6c45738bba43 565 enqueueItem(recvQueue, buf, len);*/
giowild 0:8d3dd6e411bf 566
giowild 0:8d3dd6e411bf 567 while(1)
giowild 0:8d3dd6e411bf 568 {
giowild 2:6c45738bba43 569 if (ble.getGapState().connected) {
giowild 2:6c45738bba43 570 if (recalcTimer) {
giowild 2:6c45738bba43 571 recalcTimer =false;
giowild 2:6c45738bba43 572 calc_timer_interval();
giowild 2:6c45738bba43 573 //gcdChanged =true;
giowild 2:6c45738bba43 574 if (gcd>0) {
giowild 2:6c45738bba43 575 ticker.attach(NULL,5);
giowild 2:6c45738bba43 576 ticker.attach(triggerSensor, 0.001*gcd);
giowild 2:6c45738bba43 577 } else {
giowild 2:6c45738bba43 578 ticker.attach(NULL,5);
giowild 2:6c45738bba43 579 }
giowild 2:6c45738bba43 580 } else if (!isEmpty(toSendQueue)) {
giowild 2:6c45738bba43 581 //while (!isEmpty(toSendQueue)) {
giowild 2:6c45738bba43 582 deqItem = Dequeue(toSendQueue);
giowild 2:6c45738bba43 583 //memset(buf, 0, QUEUE_STRING_LENGTH); // useless
giowild 2:6c45738bba43 584 memcpy(buf, (uint8_t *)deqItem->data.payload, deqItem->data.length);
giowild 2:6c45738bba43 585 ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, deqItem->data.length);
giowild 2:6c45738bba43 586 //ble.updateCharacteristicValue(m_uart_service_ptr->getRXCharacteristicHandle(), buf, deqItem->data.length);
giowild 2:6c45738bba43 587 free(deqItem);
giowild 2:6c45738bba43 588 //}
giowild 2:6c45738bba43 589 } else if (!isEmpty(recvQueue)) {
giowild 2:6c45738bba43 590 //if (!isEmpty(recvQueue)) {
giowild 2:6c45738bba43 591 deqItem = Dequeue(recvQueue);
giowild 2:6c45738bba43 592 memset(buf, 0, QUEUE_STRING_LENGTH); // maybe useless: TO CHECK its handling in parseRedBearCmd
giowild 2:6c45738bba43 593 memcpy(buf, (uint8_t *)deqItem->data.payload, deqItem->data.length);
giowild 2:6c45738bba43 594 //ble.updateCharacteristicValue(m_uart_service_ptr->getRXCharacteristicHandle(), buf, deqItem->data.length);
giowild 2:6c45738bba43 595 //ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, deqItem->data.length);
giowild 2:6c45738bba43 596 parseRedBearCmd(buf);
giowild 2:6c45738bba43 597 free(deqItem);
giowild 2:6c45738bba43 598 //}
giowild 2:6c45738bba43 599 //} else if (!isEmpty(toSendQueue)) {
giowild 2:6c45738bba43 600 } else if (triggerSensorPolling) {
giowild 2:6c45738bba43 601 triggerSensorPolling = false;
giowild 2:6c45738bba43 602 check_pin_changed();
giowild 2:6c45738bba43 603 } else {
giowild 2:6c45738bba43 604 ble.waitForEvent();
giowild 2:6c45738bba43 605 }
giowild 2:6c45738bba43 606 } else {
giowild 2:6c45738bba43 607 ble.waitForEvent();
giowild 2:6c45738bba43 608 }
giowild 2:6c45738bba43 609
giowild 2:6c45738bba43 610
giowild 0:8d3dd6e411bf 611 }
giowild 0:8d3dd6e411bf 612 }
giowild 0:8d3dd6e411bf 613
giowild 0:8d3dd6e411bf 614
giowild 0:8d3dd6e411bf 615
giowild 0:8d3dd6e411bf 616
giowild 0:8d3dd6e411bf 617
giowild 0:8d3dd6e411bf 618
giowild 0:8d3dd6e411bf 619
giowild 0:8d3dd6e411bf 620
giowild 0:8d3dd6e411bf 621
giowild 0:8d3dd6e411bf 622
giowild 0:8d3dd6e411bf 623
giowild 0:8d3dd6e411bf 624
giowild 0:8d3dd6e411bf 625
giowild 0:8d3dd6e411bf 626
giowild 0:8d3dd6e411bf 627
giowild 0:8d3dd6e411bf 628