Using Blend Micro (Arduino Uno + nRF8001) to gather environmental sensor data and send to mobile via notifications. Here's my main loop:
void loop()
{
aci_loop();
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_TEMPERATURE_MEASUREMENT_TX)
&& (false == radio_ack_pending)
&& (true == timing_change_done))
{
f = dht.readTemperature(true); // Read temperature as Fahrenheit from DHT
if (lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_TEMPERATURE_MEASUREMENT_TX, (uint8_t *)&f, 4))
{
aci_state.data_credit_available--;
Serial.print(F("Temperature Sent: "));
Serial.print(f);
Serial.println(F("* F"));
radio_ack_pending = true;
}
}
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX)
&& (false == radio_ack_pending)
&& (true == timing_change_done))
{
h = dht.readHumidity(); // Read humidity from DHT
if (lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX, (uint8_t *)&h, 4))
{
aci_state.data_credit_available--;
Serial.print(F("Humidity Sent: "));
Serial.print(h);
Serial.println(F(" %"));
radio_ack_pending = true;
}
}
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_CARBON_MONOXIDE_LEVEL_TX)
&& (false == radio_ack_pending)
&& (true == timing_change_done))
{
int VoutAn = analogRead(MQ7PIN); //Read Vout in analog from MQ7
float Vout = (5.0/1023)*VoutAn;
co = 100.468*(pow(((5/Vout)-1),-1.43));
if (lib_aci_send_data(PIPE_AIR_QUALITY_SENSOR_CARBON_MONOXIDE_LEVEL_TX, (uint8_t *)&co, 4))
{
aci_state.data_credit_available--;
Serial.print(F("CO Sent: "));
Serial.print(co);
Serial.println(F(" ppm"));
radio_ack_pending = true;
}
}
}
I would like to cycle through and send data from each sensor so it is constantly updated on mobile device. But here it will only send notification for the first characteristic to which I subscribe. Is the problem in how I'm handling it in this loop, or in the ACI loop?
void aci_loop()
{
static bool setup_required = false;
// We enter the if statement only when there is a ACI event available to be processed
if (lib_aci_event_get(&aci_state, &aci_data))
{
aci_evt_t * aci_evt;
aci_evt = &aci_data.evt;
switch(aci_evt->evt_opcode)
{
case ACI_EVT_DEVICE_STARTED: // As soon as you reset the nRF8001 you will get an ACI Device Started Event
{
aci_state.data_credit_available = aci_evt->params.device_started.credit_available;
switch(aci_evt->params.device_started.device_mode)
{
case ACI_DEVICE_SETUP: //When the device is in the setup mode
aci_state.device_state = ACI_DEVICE_SETUP;
Serial.println(F("Evt Device Started: Setup"));
setup_required = true;
break;
case ACI_DEVICE_STANDBY:
aci_state.device_state = ACI_DEVICE_STANDBY;
Serial.println(F("Evt Device Started: Standby"));
if (aci_evt->params.device_started.hw_error)
{
delay(20); //Magic number used to make sure the HW error event is handled correctly.
}
else
{
lib_aci_connect(180/* in seconds */, 0x0100 /* advertising interval 100ms*/);
Serial.println(F("Advertising started"));
}
break;
}
}
break; //ACI Device Started Event
case ACI_EVT_CMD_RSP:
//If an ACI command response event comes with an error -> stop
if (ACI_STATUS_SUCCESS != aci_evt->params.cmd_rsp.cmd_status)
{
//ACI ReadDynamicData and ACI WriteDynamicData will have status codes of
//TRANSACTION_CONTINUE and TRANSACTION_COMPLETE
//all other ACI commands will have status code of ACI_STATUS_SCUCCESS for a successful command
Serial.print(F("ACI Status of ACI Evt Cmd Rsp"));
Serial.println(aci_evt->params.cmd_rsp.cmd_status, HEX);
Serial.print(F("ACI Command 0x"));
Serial.println(aci_evt->params.cmd_rsp.cmd_opcode, HEX);
Serial.println(F("Evt Cmd respone: Error. Arduino is in a while(1); loop"));
while (1);
}
break;
case ACI_EVT_PIPE_STATUS:
Serial.println(F("Evt Pipe Status"));
// Check if the peer has subscribed to any particleBox characterisitcs for notifications
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_TEMPERATURE_MEASUREMENT_TX)
&& (false == timing_change_done) )
{
// Request a change to the link timing as set in the GAP -> Preferred Peripheral Connection Parameters
lib_aci_change_timing_GAP_PPCP();
timing_change_done = true;
}
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_RELATIVE_HUMIDITY_TX)
&& (false == timing_change_done) )
{
// Request a change to the link timing as set in the GAP -> Preferred Peripheral Connection Parameters
lib_aci_change_timing_GAP_PPCP();
timing_change_done = true;
}
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_CARBON_MONOXIDE_LEVEL_TX)
&& (false == timing_change_done) )
{
// Request a change to the link timing as set in the GAP -> Preferred Peripheral Connection Parameters
lib_aci_change_timing_GAP_PPCP();
timing_change_done = true;
}
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_PM10_CONCENTRATION_TX)
&& (false == timing_change_done) )
{
// Request a change to the link timing as set in the GAP -> Preferred Peripheral Connection Parameters
lib_aci_change_timing_GAP_PPCP();
timing_change_done = true;
}
if (lib_aci_is_pipe_available(&aci_state, PIPE_AIR_QUALITY_SENSOR_PM25_CONCENTRATION_TX)
&& (false == timing_change_done) )
{
// Request a change to the link timing as set in the GAP -> Preferred Peripheral Connection Parameters
lib_aci_change_timing_GAP_PPCP();
timing_change_done = true;
}
break;
case ACI_EVT_TIMING: // Link timing has changed
Serial.print(F("Timing changed: "));
Serial.println(aci_evt->params.timing.conn_rf_interval, HEX);
break;
case ACI_EVT_CONNECTED:
radio_ack_pending = false;
timing_change_done = false;
aci_state.data_credit_available = aci_state.data_credit_total;
Serial.println(F("Evt Connected"));
break;
case ACI_EVT_DATA_CREDIT:
aci_state.data_credit_available = aci_state.data_credit_available + aci_evt->params.data_credit.credit;
/**
Bluetooth Radio ack received from the peer radio for the data packet sent.
This also signals that the buffer used by the nRF8001 for the data packet is available again.
*/
radio_ack_pending = false;
break;
case ACI_EVT_PIPE_ERROR:
/**
Send data failed. ACI_EVT_DATA_CREDIT will not come.
This can happen if the pipe becomes unavailable by the peer unsubscribing to the Heart Rate
Measurement characteristic.
This can also happen when the link is disconnected after the data packet has been sent.
*/
radio_ack_pending = false;
//See the appendix in the nRF8001 Product Specication for details on the error codes
Serial.print(F("ACI Evt Pipe Error: Pipe #:"));
Serial.print(aci_evt->params.pipe_error.pipe_number, DEC);
Serial.print(F(" Pipe Error Code: "));
Serial.println(aci_evt->params.pipe_error.error_code, HEX);
//Increment the credit available as the data packet was not sent.
//The pipe error also represents the Attribute protocol Error Response sent from the peer and that should not be counted
//for the credit.
if (ACI_STATUS_ERROR_PEER_ATT_ERROR != aci_evt->params.pipe_error.error_code)
{
aci_state.data_credit_available++;
}
break;
case ACI_EVT_DISCONNECTED:
// Advertise again if the advertising timed out
if(ACI_STATUS_ERROR_ADVT_TIMEOUT == aci_evt->params.disconnected.aci_status)
{
Serial.println(F("Evt Disconnected -> Advertising timed out"));
{
Serial.println(F("nRF8001 going to sleep"));
lib_aci_sleep();
aci_state.device_state = ACI_DEVICE_SLEEP;
}
}
if(ACI_STATUS_EXTENDED == aci_evt->params.disconnected.aci_status)
{
Serial.print(F("Evt Disconnected -> Link lost. Bluetooth Error code = "));
Serial.println(aci_evt->params.disconnected.btle_status, HEX);
lib_aci_connect(180/* in seconds */, 0x0050 /* advertising interval 50ms*/);
Serial.println(F("Advertising started"));
}
break;
case ACI_EVT_HW_ERROR:
Serial.print(F("HW error: "));
Serial.println(aci_evt->params.hw_error.line_num, DEC);
for(uint8_t counter = 0; counter <= (aci_evt->len - 3); counter++)
{
Serial.write(aci_evt->params.hw_error.file_name[counter]); //uint8_t file_name[20];
}
Serial.println();
lib_aci_connect(30/* in seconds */, 0x0100 /* advertising interval 50ms*/);
Serial.println(F("Advertising started"));
break;
}
}
else
{
// Serial.println(F("No ACI Events available"));
// No event in the ACI Event queue and no event in ACI comman queue
// Arduino can go to sleep now
}
/* setup_required is set to true when the device starts up and enters setup mode.
* It indicates that do_aci_setup() should be called. The flag should be cleared if
* do_aci_setup() returns ACI_STATUS_TRANSACTION_COMPLETE.
*/
if(setup_required)
{
if (SETUP_SUCCESS == do_aci_setup(&aci_state))
{
setup_required = false;
}
}
}