The RTOS SDK docs have this sample C code:
wifi_set_opmode(NULL_MODE); // set WiFi mode to null mode.
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); // light sleep
wifi_fpm_open();
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U,3);
gpio_pin_wakeup_enable(13, GPIO_PIN_INTR_LOLEVEL);
wifi_fpm_set_wakeup_cb(fpm_wakup_cb_func1); // Set wakeup callback
wifi_fpm_do_sleep(0xFFFFFFF);
Does NodeMCU expose any of this?
Looking to wifi.sleep implementation of nodemcu in wifi.c :
//wifi.sleep()
static int wifi_sleep(lua_State* L)
{
...
// set WiFi mode to null mode
wifi_set_opmode(NULL_MODE);
// set force sleep type
wifi_fpm_set_sleep_type(MODEM_SLEEP_T);
wifi_fpm_open();
wifi_fpm_do_sleep_return_value = wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME);
...
}
Then in order to call wifi_fpm_set_sleep_type with LIGHT_SLEEP_T, you will need to modify nodemcu and for wakeup you probably need call wifi.sleep(0) when input change.
Related
I have an application that uses an ESP8266 running ESP_RTOS_SDK version 3.4 and an STM8. It is solar powered, so minimising current consumption is crucial. It works in three modes:
between events: the ESP8266 is in deep sleep and the STM8 is collecting data
during an event: the ESP8266 is in light sleep and the STM8 wakes it up every 10 seconds with some data
after an event: the ESP8266 wakes up fully, connects to wifi, sends all of the collected data.
If I disable light sleep, everything works fine. With light sleep enabled, The light sleep itself works fine but the ESP8266 does not connect to wifi.
ESP-IDF light sleep is documented here. This is my light sleep function:
// -------------------------------------------------------------------
// in light sleep, the processor is stopped.
// we wake up on a WAKE=low
void light_sleep (void) {
gpio_wakeup_enable(GPIO_WAKE_PIN, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_sleep_enable_timer_wakeup (10000000L);
esp_light_sleep_start ();
vTaskDelay (1);
esp_sleep_disable_wakeup_source (ESP_SLEEP_WAKEUP_GPIO);
esp_sleep_disable_wakeup_source (ESP_SLEEP_WAKEUP_TIMER);
}
This is the code that I use to start the wifi:
// ------------------------------------------------------------------------------
static void app_wifi_start (void) {
wifi_config_t config = {};
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
strncpy((char *)&config.sta.ssid, wifi_config.remote_ssid, sizeof (config.sta.ssid));
strncpy((char *)&config.sta.password, wifi_config.remote_password, sizeof (config.sta.password));
if (strlen((char *)config.sta.password)) {
config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
}
config.sta.pmf_cfg.capable = true;
config.sta.pmf_cfg.required = false;
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &config) );
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
}
The return code from esp_wifi_connect () is ESP_OK.
My question is: how do I make wifi start after a light sleep?
Update: this is how I stop the wifi.
// ----------------------------------------------------------
static void app_wifi_stop (void) {
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler));
switch (current_mode) {
case WIFI_MODE_STA:
case WIFI_MODE_APSTA:
esp_wifi_disconnect ();
break;
case WIFI_MODE_AP:
break;
default:
break;
}
ESP_ERROR_CHECK(esp_wifi_stop ());
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
current_mode = WIFI_MODE_NULL;
sta_connected = false;
ap_connections = 0;
}
I don't know if you solved the problem. It's been a long time. Well... Before going into light sleep, you should put the wifi in power saving mode. Also, you must call esp_wifi_stop() to stop the wifi task. However, you can only call esp_wifi_stop() when you are sure that no service is using wifi in the background. For example, if you are connected to an MQTT broker, you must make sure your MQTT queue is empty and call esp_mqtt_client_stop(). So after that, you're ready to call esp_wifi_stop() and go into light sleep by calling esp_light_sleep_start();.
I need to setup my Esp32-D1-Mini to read RSSI from both my iPhone and Nexus 5x (not at the same time of course, in other words I'd like it to work with both operating systems and read RSSI from them continously).
I've managed to make it work with Bluetooth Classic but that only works with Android OS smartphones and I need to have it work with iOS devices as well, so I had to switch to using BLE and this looks much more complicated.
The Esp32 is setup to work as a Server which anyone can connect to.
It is supposed to wait for a device to connect to it and as soon as that device is connected the Esp32 should start reading RSSI strength continously (with a 1 second delay) until the other device disconnects then enter back in "sleep mode" and wait for another device to connect to it.
The code I have right now has a few problems which I struggle to solve:
It won't read any RSSI whatsoever from my iPhone, after it connects to the Esp32 using the LightBlue App
After my Android phone connects to the Esp32, using the nRF Connect app, it will read the RSSI values just once, instead of continuing to read them until the phone disconnects or it loses signal to the Esp32.
The iPhone will not reveal its Mac adress after connecting (no mac adress appears at all) which in response won't let my Esp32 read any RSSI values from the iPhone and from what I've understood I need to request pairing from my Esp32 when the iPhone wants to connect to it in order for the iPhone to reveal its MAC Address and get RSSI from it (but I didn't find any example of how to achieve that, at least not by using Arduino), that is because the Esp32 first needs a MAC Adress of the device I want to read RSSI from, being it already connected to the Esp32 or not; the iphone does not reveal its Mac Adress after connecting so I guess I have to force it to pair when any iOS device tries to connect to the Esp32 in order to fetch their Mac Address and read their rssi.
The RSSI values start from 0 when they are read from my Android phone and they go up; I've managed to write the code first time using Bluetooth Classic and I really liked the way the RSSI would show, which would be 0 when standing close to the Esp32 (after connecting to it) and when getting away decreasing to -5, -10, etc. I'd like to read the rssi in the same way if possible, not from 0 up.
See my BT Classic Code at the bottom.
This is my current code (I've used the Esp32 Ble Client example as a starting point):
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLEScan.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
const int PIN = 2;
bool deviceConnected = false;
static void my_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) {
Serial.print("RSSI status");Serial.println(param->read_rssi_cmpl.status);
Serial.print("RSSI ");Serial.println(param->read_rssi_cmpl.rssi);
Serial.print("Address ");Serial.println(BLEAddress(param->read_rssi_cmpl.remote_addr).toString().c_str());
}
void setup() {
Serial.begin(9600);
BLEDevice::init("Long name works now");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,BLECharacteristic::PROPERTY_READ|BLECharacteristic::PROPERTY_WRITE);
BLEDevice::setCustomGapHandler(my_gap_event_handler);
pCharacteristic->setValue("Hello World says Neil");
pService->start();
//BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
pinMode (PIN, OUTPUT);//Specify that Vibration Motor pin is output
}
void loop() { // put your main code here, to run repeatedly:
delay(1000);
}
I bassically want to have the BLE code doing what my BT Classic code does (I don't need help with setting up the distance meassuring and the kalman filter, I think I know how to configure them myself);
I only need help with getting the Esp32 to read the RSSI continously from any iOS & Android devices trying to connect to the Esp32.
Here is my Bluetooth Classic Code (working well but with Android OS devices only):
#include "esp_bt_main.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "BluetoothSerial.h" //Header File for Serial Bluetooth, will be added by default into Arduino
#include "esp_gap_bt_api.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "Kalman.h"
// My function prototypes
void gap_callback (esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param);
void spp_callback (esp_spp_cb_event_t event, esp_spp_cb_param_t *param);
//Creates the variables:
double measurement, filteredMeasurement;
Kalman myFilter(0.125,32,1023,0); //suggested initial values for high noise filtering
// this->q = process_noise;
// this->r = sensor_noise;
// this->p = estimated_error;
// this->x = intial_value; //x will hold the iterated filtered value
uint8_t link_master;
BluetoothSerial SerialBT;
int rssi = 0; //RSSI
/* Kalman filter variables */
double q; //process noise covariance
double r; //measurement noise covariance
double x; //value
double p; //estimation error covariance
double k; //kalman gain
//distance = pow(10, ((refOneMeter - recvRSSI) / (10 * estPathLossExp))) //formula for distance meassurement
// we need variables to hold refOneMeter, recvRSSI, and estPathLossExp
byte addr[6] = {0, 0, 0, 0, 0, 0}; //to keep MAC address of the remote device , 8 bit value
static int PAIRING_VARIANT_PIN; //The user will be prompted to enter a pin or an app will enter a pin for user
struct read_rssi_delta_param;
struct esp_bt_gap_cb_param_t::read_rssi_delta_param read_rssi_delta; //read rssi parameter struct
esp_bd_addr_t bda; //remote bluetooth device address
int8_t rssi_delta; //rssi delta value range -128 ~127, The value zero indicates that the RSSI is inside the Golden Receive Power Range, the Golden Receive Power Range is from ESP_BT_GAP_RSSI_LOW_THRLD to ESP_BT_GAP_RSSI_HIGH_THRLD
esp_err_t esp_bt_gap_read_rssi_delta(esp_bd_addr_t remote_addr); //This function is called to read RSSI delta by address after connected. The RSSI value returned by ESP_BT_GAP_READ_RSSI_DELTA_EVT.
BluetoothSerial ESP32Mini_Bluetooth; //Object for Bluetooth
//ESP32Mini_Bluetooth_GAP_READ_RSSI_DELTA_EVT; //read RSSI event
//ESP_BT_GAP_DEV_PROP_RSSI; // Received Signal strength Indication, value type is int8_t, ranging from -128 to 127
//ESP_BT_GAP_RSSI_LOW_THRLD; // Low RSSI threshold
//ESP_BT_GAP_RSSI_HIGH_THRLD; // RSSI threshold. High RSSI threshold
//esp_bd_addr_t bda; //remote bluetooth device address
//esp_bt_status_t stat; //read rssi status
const int PIN = 32;
const int CUTOFF = -60;
int incoming;
int best;
float distance = 0.0;
void setup() {
ESP32Mini_Bluetooth.setPin("4321"); //This should solve the Secured Pin Connection
SerialBT.begin();
Serial.begin(9600); //Start Serial monitor in 9600 ; this is the line where it initialize serial to 9600 baud speed
// this->q = process_noise;
// this->r = sensor_noise;
// this->p = estimated_error;
// this->x = intial_value; //x will hold the iterated filtered value
// float temp1 = ((-6) - (rssi));
// float temp2 = (temp1 / 100);
// distance= pow(10,temp2);
// distance = pow(10, (-6 - rssi) / (100));
ESP32Mini_Bluetooth.begin("Esp32-Mini"); //Name of your Bluetooth Signal
Serial.println("Bluetooth Device is Ready to Pair");
pinMode (PIN, OUTPUT);//Specify that Vibration Motor pin is output
esp_bt_gap_register_callback (gap_callback); //register the RSSI callback function by calling this line
//register SPP service callback to get remote address:
SerialBT.register_callback(spp_callback);
// //This is Legacy Pairing Code
//ESP_BT_GAP_PIN_REQ_EVT //Legacy Pairing Pin code request
//ESP_BT_GAP_CFM_REQ_EVT //Simple Pairing User Confirmation request.
//ESP_BT_GAP_KEY_NOTIF_EVT //Simple Pairing Passkey Notification
//ESP_BT_GAP_KEY_REQ_EVT //Simple Pairing Passkey request
}
void loop() {
delay(100);
if (SerialBT.hasClient()) { //this is where we get and handle RSSI value
//when we need RSSI call this:
esp_bt_gap_read_rssi_delta (addr); //now variable rssi contains RSSI level
byte b = rssi; //etc....
//reads measurement and filter it
measurement = (double) rssi; //read new value from rssi
filteredMeasurement = myFilter.getFilteredValue(measurement);
float temp1 = (3) - (rssi);
float temp2 = (temp1 / 100);
distance = pow(10,temp2);
if (rssi < -10)
{
digitalWrite(PIN, HIGH);
//ESP32Mini_Bluetooth.println("Vibration Motor ON");
//Serial.print ("PIN turned ON");
}
if (rssi > -10)
{
digitalWrite(PIN, LOW);
//ESP32Mini_Bluetooth.println("Vibration Motor OFF");
//Serial.print ("PIN turned OFF");
}
//ESP32Mini_Bluetooth.print("RSSI: "); //println add linefeed for end of printed string
//ESP32Mini_Bluetooth.println(rssi); //println add linefeed for end of printed string
//ESP32Mini_Bluetooth.print(", ");
//ESP32Mini_Bluetooth.println(filteredMeasurement);
ESP32Mini_Bluetooth.print(rssi);
ESP32Mini_Bluetooth.print(", ");
ESP32Mini_Bluetooth.println(distance);
//So building up the serial, we have filtered: <value> TAB
//we are adding filtered: <value> TAB distance: <value> TAB rssi: <value>
Serial.print ("filtered:");
Serial.print (filteredMeasurement);
Serial.print ("\t distance:");
Serial.print (distance);
Serial.print ("\t rssi:");
Serial.print (rssi);
delay (1000); //DELAY OF 1 SECONDS
}
else
{
// Disconnected state
digitalWrite(PIN, LOW);
// Serial.println ("Disconnected. PIN turned OFF");
Serial.println (rssi);
delay(1000); // wait 1s
}
if (ESP32Mini_Bluetooth.available()) //Check if we receive anything from Bluetooth // is telling that BT has received some data and it need to be processed
{
incoming = ESP32Mini_Bluetooth.read(); //Read what we recevive
Serial.print("Received:"); Serial.println(incoming);
digitalWrite(PIN, best > CUTOFF ? HIGH : LOW);
}
}
//RSSI callback function
void gap_callback (esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
if (event == ESP_BT_GAP_READ_RSSI_DELTA_EVT)
{
rssi = param->read_rssi_delta.rssi_delta; // it checks it has connection
Serial.print("\t RSSI Delta: ");
Serial.println (param->read_rssi_delta.rssi_delta);
}
}
//SPP service callback function (to get remote MAC address)
void spp_callback (esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
if (event == ESP_SPP_SRV_OPEN_EVT)
memcpy(addr, param->srv_open.rem_bda, 6);
}
I am writing an iOS application with Xamarin.iOS, but an answer in native iOS would be appreciated as well. My application uses a BLE write characteristic to send messages between devices. My code to create and add the service to the CBPeripheralManager:
_cbPeripheralManager.WriteRequestsReceived += WriteRequestReceived; // WriteRequestReceived is never called
// my read characteristic actually works fine
_readCharacteristic = new CBMutableCharacteristic(CBUUID.FromString(MyReadCharacteristicUuid), CBCharacteristicProperties.Read | CBCharacteristicProperties.Notify, null, CBAttributePermissions.Readable);
// but my write characteristic doesn't work when i try to write to it
_writeCharacteristic = new CBMutableCharacteristic(CBUUID.FromString(MyWriteCharacteristicUuid), CBCharacteristicProperties.Write | CBCharacteristicProperties.Notify, null, CBAttributePermissions.Writeable);
_service = new CBMutableService(CBUUID.FromString(MyServiceUuid), true);
_service.Characteristics = new CBCharacteristic[2] { _readCharacteristic, _writeCharacteristic };
_cbPeripheralManager.AddService(_service);
And my code to write to the characteristic of the peripheral (not showing the code for device and service/characteristic discovery for simplicity)
peripheral.WroteCharacteristicValue += handler; // handler is never called
peripheral.WriteValue(NSData.FromString("hello"), characteristic, CBCharacteristicWriteType.WithoutResponse);
Any idea what the problem could be?
I am updating an existing iOS VOIP application to use CallKit with PJSIP 2.6 and PJSUA2.
After some effort, the CallKit implementation seems to be working as expected. Incoming calls can be accepted or declined, and if accepted, will be connected and controlled with an in-app active call view controller.
The audio, however, does not appear to be properly connected at the pjsip end. There is no audio coming in from, or going out to the remote caller. The microphone audio appears to be routed back to the iPhone speaker.
The SIP audio ports should be connecting in callback function onCallMediaState:
virtual void onCallMediaState(OnCallMediaStateParam &prm) {
CallInfo ci = getInfo();
AudioMedia* audio_media = 0;
for (unsigned i = 0; i < ci.media.size(); i++) {
if (ci.media[i].type==PJMEDIA_TYPE_AUDIO && ( ci.media[i].status == PJSUA_CALL_MEDIA_ACTIVE ||
ci.media[i].status ==PJSUA_CALL_MEDIA_REMOTE_HOLD)) {
try {
audio_media = static_cast<AudioMedia*>(getMedia(i));
if(audio_media != 0)
{
Endpoint::instance().audDevManager().getCaptureDevMedia().startTransmit(*audio_media);
audio_media->startTransmit(Endpoint::instance().audDevManager().getPlaybackDevMedia());
}
} catch (std::exception ex) {
continue;
}
}
}
}
As described in Ticket#1941 at:
https://trac.pjsip.org/repos/ticket/1941:
I set the audio devices using:
ep->audDevManager().setNullDev();
immediately after the initialization of the Endpoint class (ep->libInit(epConfig);), and then:
I attempt to set the devices using pjsua_set_snd_dev() in CXProvider’s didActivate function, like this:
-(void) setSipSoundDevices {
pj_status_t status;
int captDev, playDev;
pjsua_get_snd_dev(&captDev, &playDev);
Endpoint::instance().audDevManager().setPlaybackDev(playDev);
Endpoint::instance().audDevManager().setCaptureDev(captDev);
}
pjsua_get_snd_dev(&captDev, &playDev) returns -99, -99 and the audio does not connect.
My question is this. How can I properly hook up the remote audio sources or ports, on an incoming call using PJSIP 2.6 and CallKit?
Might 2.5.5 work better in this regard?
Any insights are appreciated.
By and by I got the incoming call audio working properly. The crux of the matter was that even though the documentation from both Apple and SIP say that the audio has to be handled on the iOS end, you still have to set the SIP audio devices in the SIP layer in the provider delegate 'didActivate' and 'didDeactivate' functions. Because I use the PJSUA C++ layer, I had to drill down through the objc-c++ bridging layer to provide this functionality. ie.
-(void) activateSipSoundDevices {
pj_status_t status = pjsua_set_snd_dev(0, 0);
}
-(void) deactivateSipSoundDevices {
pj_status_t status = pjsua_set_null_snd_dev();
}
When initializing the SIP Account, be sure to set the null sound devices like:
ep->audDevManager().setNullDev();
Hope this helps.
I have an iOS app that makes some small network requests on app launch (resource updates, etc). If the user turns off cellular access for the app in iOS Settings, they get a prompt from iOS about network usage every time they launch. Is there a way to know programmatically that cellular data for this app has been disabled, so that I can disable the requests at startup?
So I found this on the apple dev forums from an Apple engineer (https://devforums.apple.com/message/1059332#1059332).
Another developer wrote in to DTS and thus I had a chance to
investigate this in depth. Alas, the news is much as I expected:
there is no supported way to detect that your app is in this state.
Nor is there a way to make a "no user interaction" network connection,
that is, request that the connection fail rather than present UI like
this. If these limitations are causing problems for your app, I
encourage you to file a bug describing your specific requirements.
https://developer.apple.com/bug-reporting/
So it looks like it is not possible to detect if cellular data for your app has been turned off.
Edit
I filed a radar for this requesting that it be added. I just got this notification in my radar
We believe this issue has been addressed in the latest iOS 9 beta.
I looked through the API diffs, but so far I can't find the new API.
As of iOS9, the capability to check the setting to enable/disable use of cellular data for your app (Settings/Cellular/AppName) is available using Apple's CTCellularData class. The following code will set cellularDataRestrictedState when it is run initially and then set it and log whenever it changes:
import CoreTelephony
var cellularDataRestrictedState = CTCellularDataRestrictedState.restrictedStateUnknown
let cellState = CTCellularData.init()
cellState.cellularDataRestrictionDidUpdateNotifier = { (dataRestrictedState) in
if cellularDataRestrictedState != .restrictedStateUnknown { // State has changed - log to console
print("cellularDataRestrictedState: " + "\(dataRestrictedState == .restrictedStateUnknown ? "unknown" : dataRestrictedState == .restricted ? "restricted" : "not restricted")")
}
cellularDataRestrictedState = dataRestrictedState
}
Unfortunately (as of iOS11) this seems to check only the state of the app's switch - if your app's switch is set to enabled and the user switches the Cellular Data master switch to disabled, this API will return the app's state as being "not restricted".
Just wanted to add an Objective C version of the above Swift code for future travellers.
- (void)monitorCanUseCellularData {
if (GCIsiOS9) {
CTCellularData *cellularData = [[CTCellularData alloc] init];
NSLog(#"%ld", cellularData.restrictedState);
// 0, kCTCellularDataRestrictedStateUnknown
[cellularData setCellularDataRestrictionDidUpdateNotifier:^(CTCellularDataRestrictedState state) {
NSLog(#"%ld", state);
self.canUseCellularData = cellularData.restrictedState ==2?true:false;
}];
}
}
I have found that the CTCellularData class needs some time to get to the correct value. In my implementation I call the didUpdateNotifier very early after appDidFinishLaunching. By the time my networking call are returning with errors I definitely have a correct value for the restricted state.
class CellularRestriction: NSObject {
private static var cellularData = CTCellularData()
private static var currentState = CTCellularDataRestrictedState.restrictedStateUnknown
static var isRestricted: Bool {
currentState = cellularData.restrictedState
return currentState == .restricted
}
static func prepare() {
if currentState == .restrictedStateUnknown {
cellularData.cellularDataRestrictionDidUpdateNotifier = { state in
currentState = cellularData.restrictedState // This value may be inconsistent, however the next read of isRestricted should be correct.
}
}
}
}
You can detect if cellular data disabled using NWPathMonitor class. (https://developer.apple.com/documentation/network/nwpathmonitor)
let cellMonitor = NWPathMonitor(requiredInterfaceType: .cellular)
cellMonitor.pathUpdateHandler = { path in
self.isCellConnected = path.status == .satisfied
}
Adding to dirkgroten's answer, you can use the Apple Reachability class, found here:
https://developer.apple.com/Library/ios/samplecode/Reachability/Introduction/Intro.html
It uses SCNetworkReachability, and is very straight forward to use, it will detect connectivity via Cell and WiFi as you will need to check both at start up.
There are lots of frameworks out there that will give you the status of your network connectivity, and of course you can roll your own. I've found AFNetworking to be one of the best. It has a singleton class called AFNetworkReachabilityManager that abstracts some of the complexities for you. Specifically you'll want to look at the two boolean properties:
reachableViaWWAN
reachableViaWiFi
There is also a reachability changed status block that you can set:
– setReachabilityStatusChangeBlock:
AFNetworking Github
AFNetworkReachabilityManager