/**
  * Updates the temperature sample of this instance of MicroBitThermometer
  * only if isSampleNeeded() indicates that an update is required.
  *
  * This call also will add the thermometer to fiber components to receive
  * periodic callbacks.
  *
  * @return MICROBIT_OK on success.
  */
int MicroBitThermometer::updateSample()
{
    if(!(status & MICROBIT_THERMOMETER_ADDED_TO_IDLE))
    {
        // If we're running under a fiber scheduer, register ourselves for a periodic callback to keep our data up to date.
        // Otherwise, we do just do this on demand, when polled through our read() interface.
        fiber_add_idle_component(this);
        status |= MICROBIT_THERMOMETER_ADDED_TO_IDLE;
    }

    // check if we need to update our sample...
    if(isSampleNeeded())
    {
        int32_t processorTemperature;
        uint8_t sd_enabled;

        // For now, we just rely on the nrf senesor to be the most accurate.
        // The compass module also has a temperature sensor, and has the lowest power consumption, so will run the cooler...
        // ...however it isn't trimmed for accuracy during manufacture, so requires calibration.

        sd_softdevice_is_enabled(&sd_enabled);

        if (sd_enabled)
        {
            // If Bluetooth is enabled, we need to go through the Nordic software to safely do this
            sd_temp_get(&processorTemperature);
        }
        else
        {
            // Othwerwise, we access the information directly...
            uint32_t *TEMP = (uint32_t *)0x4000C508;

            NRF_TEMP->TASKS_START = 1;

            while (NRF_TEMP->EVENTS_DATARDY == 0);

            NRF_TEMP->EVENTS_DATARDY = 0;

            processorTemperature = *TEMP;

            NRF_TEMP->TASKS_STOP = 1;
        }


        // Record our reading...
        temperature = processorTemperature / 4;

        // Schedule our next sample.
        sampleTime = system_timer_current_time() + samplePeriod;

        // Send an event to indicate that we'e updated our temperature.
        MicroBitEvent e(id, MICROBIT_THERMOMETER_EVT_UPDATE);
    }

    return MICROBIT_OK;
};
/**
 * A pairing request has been sucessfully completed.
 * If we're in pairing mode, display a success or failure message.
 *
 * @note for internal use only.
 */
void MicroBitBLEManager::pairingComplete(bool success)
{
	this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;

	if(success)
    {
		this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
        fiber_add_idle_component(this);
    }
}
/**
  * Reads the acceleration data from the accelerometer, and stores it in our buffer.
  * This only happens if the accelerometer indicates that it has new data via int1.
  *
  * On first use, this member function will attempt to add this component to the
  * list of fiber components in order to constantly update the values stored
  * by this object.
  *
  * This technique is called lazy instantiation, and it means that we do not
  * obtain the overhead from non-chalantly adding this component to fiber components.
  *
  * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails.
  */
int MicroBitAccelerometer::updateSample()
{
    if(!(status & MICROBIT_ACCEL_ADDED_TO_IDLE))
    {
        fiber_add_idle_component(this);
        status |= MICROBIT_ACCEL_ADDED_TO_IDLE;
    }

    // Poll interrupt line from accelerometer.
    // n.b. Default is Active LO. Interrupt is cleared in data read.
    if(!int1)
    {
        int8_t data[6];
        int result;

        result = readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6);
        if (result !=0)
            return MICROBIT_I2C_ERROR;

        // read MSB values...
        sample.x = data[0];
        sample.y = data[2];
        sample.z = data[4];

        // Normalize the data in the 0..1024 range.
        sample.x *= 8;
        sample.y *= 8;
        sample.z *= 8;

#if CONFIG_ENABLED(USE_ACCEL_LSB)
        // Add in LSB values.
        sample.x += (data[1] / 64);
        sample.y += (data[3] / 64);
        sample.z += (data[5] / 64);
#endif

        // Scale into millig (approx!)
        sample.x *= this->sampleRange;
        sample.y *= this->sampleRange;
        sample.z *= this->sampleRange;

        // Indicate that pitch and roll data is now stale, and needs to be recalculated if needed.
        status &= ~MICROBIT_ACCEL_PITCH_ROLL_VALID;

        // Update gesture tracking
        updateGesture();

        // Indicate that a new sample is available
        MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE);
    }

    return MICROBIT_OK;
};
/**
  * Default constructor.
  *
  * Adds itself as a fiber component, and also configures itself to be the
  * default EventModel if defaultEventBus is NULL.
  */
MicroBitMessageBus::MicroBitMessageBus()
{
	this->listeners = NULL;
    this->evt_queue_head = NULL;
    this->evt_queue_tail = NULL;
    this->queueLength = 0;

	fiber_add_idle_component(this);

	if(EventModel::defaultEventBus == NULL)
		EventModel::defaultEventBus = this;
}
/**
  * Constructor.
  * Create a representation of the EventService
  * @param _ble The instance of a BLE device that we're running on.
  * @param _messageBus An instance of an EventModel which events will be mirrored from.
  */
MicroBitEventService::MicroBitEventService(BLEDevice &_ble, EventModel &_messageBus) :
        ble(_ble),messageBus(_messageBus)
{
    GattCharacteristic  microBitEventCharacteristic(MicroBitEventServiceMicroBitEventCharacteristicUUID, (uint8_t *)&microBitEventBuffer, 0, sizeof(EventServiceEvent),
    GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);

    GattCharacteristic  clientEventCharacteristic(MicroBitEventServiceClientEventCharacteristicUUID, (uint8_t *)&clientEventBuffer, 0, sizeof(EventServiceEvent),
    GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);

    GattCharacteristic  clientRequirementsCharacteristic(MicroBitEventServiceClientRequirementsCharacteristicUUID, (uint8_t *)&clientRequirementsBuffer, 0, sizeof(EventServiceEvent), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);

    microBitRequirementsCharacteristic = new GattCharacteristic(MicroBitEventServiceMicroBitRequirementsCharacteristicUUID, (uint8_t *)&microBitRequirementsBuffer, 0, sizeof(EventServiceEvent), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);

    microBitRequirementsCharacteristic->setReadAuthorizationCallback(this, &MicroBitEventService::onRequirementsRead);

    clientEventBuffer.type = 0x00;
    clientEventBuffer.reason = 0x00;

    microBitEventBuffer = microBitRequirementsBuffer = clientRequirementsBuffer = clientEventBuffer;

    messageBusListenerOffset = 0;

    // Set default security requirements
    microBitEventCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
    clientEventCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
    clientRequirementsCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
    microBitRequirementsCharacteristic->requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);

    GattCharacteristic *characteristics[] = {&microBitEventCharacteristic, &clientEventCharacteristic, &clientRequirementsCharacteristic, microBitRequirementsCharacteristic};
    GattService         service(MicroBitEventServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

    ble.addService(service);

    microBitEventCharacteristicHandle = microBitEventCharacteristic.getValueHandle();
    clientEventCharacteristicHandle = clientEventCharacteristic.getValueHandle();
    clientRequirementsCharacteristicHandle = clientRequirementsCharacteristic.getValueHandle();

    ble.onDataWritten(this, &MicroBitEventService::onDataWritten);

    fiber_add_idle_component(this);
}
/**
  * Constructor.
  * Create a representation of the IOPinService
  * @param _ble The instance of a BLE device that we're running on.
  * @param _io An instance of MicroBitIO that this service will use to perform
  *            I/O operations.
  */
MicroBitIOPinService::MicroBitIOPinService(BLEDevice &_ble, MicroBitIO &_io) :
        ble(_ble), io(_io)
{
    // Create the AD characteristic, that defines whether each pin is treated as analogue or digital
    GattCharacteristic ioPinServiceADCharacteristic(MicroBitIOPinServiceADConfigurationUUID, (uint8_t *)&ioPinServiceADCharacteristicBuffer, 0, sizeof(ioPinServiceADCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);

    // Create the IO characteristic, that defines whether each pin is treated as input or output
    GattCharacteristic ioPinServiceIOCharacteristic(MicroBitIOPinServiceIOConfigurationUUID, (uint8_t *)&ioPinServiceIOCharacteristicBuffer, 0, sizeof(ioPinServiceIOCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);

    // Create the Data characteristic, that allows the actual read and write operations.
    ioPinServiceDataCharacteristic = new GattCharacteristic(MicroBitIOPinServiceDataUUID, (uint8_t *)ioPinServiceDataCharacteristicBuffer, 0, sizeof(ioPinServiceDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);

    ioPinServiceDataCharacteristic->setReadAuthorizationCallback(this, &MicroBitIOPinService::onDataRead);

    ioPinServiceADCharacteristicBuffer = 0;
    ioPinServiceIOCharacteristicBuffer = 0;
    memset(ioPinServiceIOData, 0, sizeof(ioPinServiceIOData));

    // Set default security requirements
    ioPinServiceADCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
    ioPinServiceIOCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
    ioPinServiceDataCharacteristic->requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);

    GattCharacteristic *characteristics[] = {&ioPinServiceADCharacteristic, &ioPinServiceIOCharacteristic, ioPinServiceDataCharacteristic};
    GattService         service(MicroBitIOPinServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

    ble.addService(service);

    ioPinServiceADCharacteristicHandle = ioPinServiceADCharacteristic.getValueHandle();
    ioPinServiceIOCharacteristicHandle = ioPinServiceIOCharacteristic.getValueHandle();

    ble.gattServer().write(ioPinServiceADCharacteristicHandle, (const uint8_t *)&ioPinServiceADCharacteristicBuffer, sizeof(ioPinServiceADCharacteristicBuffer));
    ble.gattServer().write(ioPinServiceIOCharacteristicHandle, (const uint8_t *)&ioPinServiceIOCharacteristicBuffer, sizeof(ioPinServiceIOCharacteristicBuffer));

    ble.onDataWritten(this, &MicroBitIOPinService::onDataWritten);
    fiber_add_idle_component(this);
}
예제 #7
0
/**
 * Poll to see if new data is available from the hardware. If so, update it.
 * n.b. it is not necessary to explicitly call this funciton to update data
 * (it normally happens in the background when the scheduler is idle), but a check is performed
 * if the user explicitly requests up to date data.
 *
 * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails.
 *
 * @note This method should be overidden by the hardware driver to implement the requested
 * changes in hardware.
 */
int FXOS8700::requestUpdate()
{
    // Ensure we're scheduled to update the data periodically
    if(!(MicroBitAccelerometer::status & MICROBIT_ACCEL_ADDED_TO_IDLE))
    {
        fiber_add_idle_component((MicroBitAccelerometer *)this);
        MicroBitAccelerometer::status |= MICROBIT_ACCEL_ADDED_TO_IDLE;
    }

    // Poll interrupt line from device (ACTIVE LOW)
    if(int1.getDigitalValue() == 0)
    {
        uint8_t data[12];
        int16_t s;
        uint8_t *lsb = (uint8_t *) &s;
        uint8_t *msb = lsb + 1;
        Sample3D accelerometerSample;
        Sample3D compassSample;
        int result;

        // Read the combined accelerometer and magnetometer data.
        result = i2c.readRegister(address, FXOS8700_OUT_X_MSB, data, 12);

        if (result !=0)
            return MICROBIT_I2C_ERROR;

        
        // read sensor data (and translate into signed little endian)
        *msb = data[0];
        *lsb = data[1];
        accelerometerSample.x = s;

        *msb = data[2];
        *lsb = data[3];
        accelerometerSample.y = s;

        *msb = data[4];
        *lsb = data[5];
        accelerometerSample.z = s;

        *msb = data[6];
        *lsb = data[7];
        compassSample.x = s;

        *msb = data[8];
        *lsb = data[9];
        compassSample.y = s;

        *msb = data[10];
        *lsb = data[11];
        compassSample.z = s;

        // scale the 14 bit accelerometer data (packed into 16 bits) into SI units (milli-g), and translate to ENU coordinate system
        MicroBitAccelerometer::sampleENU.x = (-accelerometerSample.y * MicroBitAccelerometer::sampleRange) / 32;
        MicroBitAccelerometer::sampleENU.y = (accelerometerSample.x * MicroBitAccelerometer::sampleRange) / 32;
        MicroBitAccelerometer::sampleENU.z = (accelerometerSample.z * MicroBitAccelerometer::sampleRange) / 32;

        // translate magnetometer data into ENU coordinate system and normalise into nano-teslas
        MicroBitCompass::sampleENU.x = FXOS8700_NORMALIZE_SAMPLE(-compassSample.y);
        MicroBitCompass::sampleENU.y = FXOS8700_NORMALIZE_SAMPLE(compassSample.x);
        MicroBitCompass::sampleENU.z = FXOS8700_NORMALIZE_SAMPLE(compassSample.z);

        MicroBitAccelerometer::update();
        MicroBitCompass::update();
    }

    return MICROBIT_OK;
}
예제 #8
0
/**
  * Initialises the radio for use as a multipoint sender/receiver
  *
  * @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
  */
int MicroBitRadio::enable()
{
    // If the device is already initialised, then there's nothing to do.
    if (status & MICROBIT_RADIO_STATUS_INITIALISED)
        return MICROBIT_OK;

    // Only attempt to enable this radio mode if BLE is disabled.
    if (ble_running())
        return MICROBIT_NOT_SUPPORTED;

    // If this is the first time we've been enable, allocate out receive buffers.
    if (rxBuf == NULL)
        rxBuf = new FrameBuffer();

    if (rxBuf == NULL)
        return MICROBIT_NO_RESOURCES;

    // Enable the High Frequency clock on the processor. This is a pre-requisite for
    // the RADIO module. Without this clock, no communication is possible.
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_HFCLKSTART = 1;
    while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);

    // Bring up the nrf51822 RADIO module in Nordic's proprietary 1MBps packet radio mode.
    setTransmitPower(MICROBIT_RADIO_DEFAULT_TX_POWER);
    setFrequencyBand(MICROBIT_RADIO_DEFAULT_FREQUENCY);

    // Configure for 1Mbps throughput.
    // This may sound excessive, but running a high data rates reduces the chances of collisions...
    NRF_RADIO->MODE = RADIO_MODE_MODE_Nrf_1Mbit;

    // Configure the addresses we use for this protocol. We run ANONYMOUSLY at the core.
    // A 40 bit addresses is used. The first 32 bits match the ASCII character code for "uBit".
    // Statistically, this provides assurance to avoid other similar 2.4GHz protocols that may be in the vicinity.
    // We also map the assigned 8-bit GROUP id into the PREFIX field. This allows the RADIO hardware to perform
    // address matching for us, and only generate an interrupt when a packet matching our group is received.
    NRF_RADIO->BASE0 = MICROBIT_RADIO_BASE_ADDRESS;

    // Join the default group. This will configure the remaining byte in the RADIO hardware module.
    setGroup(this->group);

    // The RADIO hardware module supports the use of multiple addresses, but as we're running anonymously, we only need one.
    // Configure the RADIO module to use the default address (address 0) for both send and receive operations.
    NRF_RADIO->TXADDRESS = 0;
    NRF_RADIO->RXADDRESSES = 1;

    // Packet layout configuration. The nrf51822 has a highly capable and flexible RADIO module that, in addition to transmission
    // and reception of data, also contains a LENGTH field, two optional additional 1 byte fields (S0 and S1) and a CRC calculation.
    // Configure the packet format for a simple 8 bit length field and no additional fields.
    NRF_RADIO->PCNF0 = 0x00000008;
    NRF_RADIO->PCNF1 = 0x02040000 | MICROBIT_RADIO_MAX_PACKET_SIZE;

    // Most communication channels contain some form of checksum - a mathematical calculation taken based on all the data
    // in a packet, that is also sent as part of the packet. When received, this calculation can be repeated, and the results
    // from the sender and receiver compared. If they are different, then some corruption of the data ahas happened in transit,
    // and we know we can't trust it. The nrf51822 RADIO uses a CRC for this - a very effective checksum calculation.
    //
    // Enable automatic 16bit CRC generation and checking, and configure how the CRC is calculated.
    NRF_RADIO->CRCCNF = RADIO_CRCCNF_LEN_Two;
    NRF_RADIO->CRCINIT = 0xFFFF;
    NRF_RADIO->CRCPOLY = 0x11021;

    // Set the start random value of the data whitening algorithm. This can be any non zero number.
    NRF_RADIO->DATAWHITEIV = 0x18;

    // Set up the RADIO module to read and write from our internal buffer.
    NRF_RADIO->PACKETPTR = (uint32_t)rxBuf;

    // Configure the hardware to issue an interrupt whenever a task is complete (e.g. send/receive).
    NRF_RADIO->INTENSET = 0x00000008;
    NVIC_ClearPendingIRQ(RADIO_IRQn);
    NVIC_EnableIRQ(RADIO_IRQn);

    NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_RSSISTART_Msk;

    // Start listening for the next packet
    NRF_RADIO->EVENTS_READY = 0;
    NRF_RADIO->TASKS_RXEN = 1;
    while(NRF_RADIO->EVENTS_READY == 0);

    NRF_RADIO->EVENTS_END = 0;
    NRF_RADIO->TASKS_START = 1;

    // register ourselves for a callback event, in order to empty the receive queue.
    fiber_add_idle_component(this);

    // Done. Record that our RADIO is configured.
    status |= MICROBIT_RADIO_STATUS_INITIALISED;

    return MICROBIT_OK;
}