diff --git a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LBLE/src/LBLEPeriphral.h b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LBLE/src/LBLEPeriphral.h index 3933a4d..0b5e08c 100644 --- a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LBLE/src/LBLEPeriphral.h +++ b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LBLE/src/LBLEPeriphral.h @@ -323,7 +323,7 @@ class LBLECharacteristicBuffer : public LBLECharacteristicBase virtual int notify(bt_handle_t connection); virtual int indicate(bt_handle_t connection); -private: +protected: LBLEValueBuffer m_data; LBLECharacteristicWrittenInfo m_writtenInfo; }; diff --git a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/examples/JoyStick/JoyStick.ino b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/examples/JoyStick/JoyStick.ino new file mode 100644 index 0000000..8b10b98 --- /dev/null +++ b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/examples/JoyStick/JoyStick.ino @@ -0,0 +1,103 @@ +/* + This example configures LinkIt 7697 to a reciver of the iOS LinkIt Remote App + + created Aug 2017 +*/ +#include + +LRemoteJoyStick stickLeft; +LRemoteJoyStick stickRight; +LRemoteLabel labelLeft; +LRemoteLabel labelRight; + +void setup() { + //Initialize serial and wait for port to open: + Serial.begin(9600); + + Serial.println("Start configuring remote"); + + // Initialize GPIO + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, 0); + + // Setup the Remote Control's UI canvas + LRemote.setName("LinkIt 7697"); + + // We use a landscape since it is easir to contorl joysticks + LRemote.setOrientation(RC_LANDSCAPE); + LRemote.setGrid(4, 2); + + // Add left stick + stickLeft.setPos(0, 1); + stickLeft.setSize(1, 1); + stickLeft.setColor(RC_ORANGE); + LRemote.addControl(stickLeft); + + // Add Right stick + stickRight.setPos(3, 1); + stickRight.setSize(1, 1); + stickRight.setColor(RC_BLUE); + LRemote.addControl(stickRight); + + // Add a simple text label + labelLeft.setText("(0, 0)"); + labelLeft.setPos(0, 0); + labelLeft.setSize(1, 1); + labelLeft.setColor(RC_GREY); + LRemote.addControl(labelLeft); + + labelRight.setText("(0, 0)"); + labelRight.setPos(3, 0); + labelRight.setSize(1, 1); + labelRight.setColor(RC_GREY); + LRemote.addControl(labelRight); + + + // Start broadcasting our remote contoller + // This method implicitly initialized underlying BLE subsystem + // to create a BLE peripheral, and then + // start advertisement on it. + LRemote.begin(); + Serial.println("begin() returned"); +} + +void checkAndUpdateLabel(LRemoteLabel& label, LRemoteJoyStick& stick) { + if(stick.isValueChanged()){ + LRemoteDirection d = stick.getValue(); + // d.x and d.y are the value from the Joystick component: + // d.x : -100 ~ 100, where 0 is center, -100 is leftmost, and 100 is rightmost. + // d.y : -100 ~ 100, where 0 is center, -100 is bottommost, and 100 is topmost. + + // you can print d directly. + Serial.println(d); + + // in this example, we simply pass the value of d.x/d.y + // back to the LinkIt Remote app. + label.updateText(d.toString()); + } +} + +void loop() { + // check if we are connect by some + // BLE central device, e.g. an mobile app + if(!LRemote.connected()) { + Serial.println("waiting for connection"); + delay(1000); + } else { + // The interval between button down/up + // can be very short - e.g. a quick tap + // on the screen. + // We could lose some event if we + // delay something like 100ms. + delay(15); + } + + // Process the incoming BLE write request + // and translate them to control events + LRemote.process(); + + // refer to this function to know how to parse the values from joystick. + checkAndUpdateLabel(labelLeft, stickLeft); + checkAndUpdateLabel(labelRight, stickRight); + +} \ No newline at end of file diff --git a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.cpp b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.cpp index f83e65b..d9075bd 100644 --- a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.cpp +++ b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.cpp @@ -3,9 +3,59 @@ #include #include "LRemote.h" #include +#include "SalasQueue.h" LRemoteClass LRemote; +// custom BLE characteristic +class LBLECharacteristicEvent : public LBLECharacteristicBuffer { +public: + static const size_t EVENT_QUEUE_SIZE = 8; + + LBLECharacteristicEvent(LBLEUuid uuid, uint32_t permission) : + LBLECharacteristicBuffer(uuid, permission), + m_eventQueue(EVENT_QUEUE_SIZE) + {} + + LBLECharacteristicEvent(LBLEUuid uuid) : + LBLECharacteristicBuffer(uuid), + m_eventQueue(EVENT_QUEUE_SIZE) + {} + + virtual uint32_t onWrite(void *data, uint16_t size, uint16_t offset) { + // process the event + const auto ret = LBLECharacteristicBuffer::onWrite(data, size, offset); + + // check if we're reaching a "complete" RCEventInfo + // if so, pusht the event into event queue + const size_t totalWritten = size + offset; + if(totalWritten >= m_data.size()) { + LOG_I(common, "ARD:LRemote:RCEventInfo done, push into Q"); + m_eventQueue.push(*(RCEventInfo*)&m_data[0]); + } + + return ret; + } + + // get "unprocessed" events + size_t eventCount() { + return m_eventQueue.count(); + } + + RCEventInfo popEvent() { + return m_eventQueue.pop(); + } + +public: + Queue m_eventQueue; + +protected: + // User should use popEvent() instead of getValue. + void getValue(uint8_t* buffer, uint16_t size, uint16_t offset) { + return LBLECharacteristicBuffer::getValue(buffer, size, offset); + } +}; + // UUID definition static LBLEUuid rcServiceUUID("3f60ab39-1710-4456-930c-7e9c9539917e"); static LBLEService rcService(rcServiceUUID); @@ -27,7 +77,7 @@ static LBLECharacteristicString rcNames("3f60ab39-1717-4456-930c-7e9c9539917e"); // String of control names, // separated by the \n // ASCII character -static LBLECharacteristicBuffer +static LBLECharacteristicEvent rcEvent("b5d2ff7b-6eff-4fb5-9b72-6b9cff5181e7"); // Array of UINT8[4], // (sequence, event, // event data_byte1, @@ -191,13 +241,8 @@ bool LRemoteClass::connected() { return LBLEPeripheral.connected(); } void LRemoteClass::process() { // check if new event coming in - if (rcEvent.isWritten()) { - const LBLECharacteristicWrittenInfo &written = rcEvent.getLastWrittenInfo(); - - RCEventInfo event = {0}; - - // retrieve the control's event info - rcEvent.getValue((uint8_t *)&event, sizeof(event), 0); + while (rcEvent.eventCount()) { + const RCEventInfo event = rcEvent.popEvent(); // scan and update event info // make sure that the "sequence number" is incremented @@ -208,8 +253,7 @@ void LRemoteClass::process() { control.m_lastEvent = event; control.m_lastEvent.seq = oldSeq + 1; - event.processedSeq = event.seq; - rcEvent.setValueBuffer((uint8_t *)&event, sizeof(event)); + LOG_I(common, "ARD:LRemote:process() ctrl:%d seq:%d", i, oldSeq); } } diff --git a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.h b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.h index a013261..4daefe4 100644 --- a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.h +++ b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/LRemote.h @@ -3,8 +3,9 @@ #include #include #include +#include "Printable.h" -#define PROTOCOL_VERSION (3) +#define PROTOCOL_VERSION (4) // Background / primary color of the UI control enum RCColorType { @@ -33,6 +34,7 @@ enum RCControlType { RC_CIRCLEBUTTON = 3, RC_SWITCHBUTTON = 4, RC_SLIDER = 5, + RC_ANALOG = 6, }; // Internal data structure for BLE events @@ -225,6 +227,50 @@ class LRemoteSlider : public LRemoteUIControl { int16_t m_initValue; }; +// This class encapsulate the return value of a Joystick +struct LRemoteDirection : public Printable{ + int8_t x; // 0: neutral, -100: left-most, 100: right-most + int8_t y; // 0: newtral, -100: bottom-most, 100: top-most + + LRemoteDirection(uint16_t data) { + x = static_cast(data >> 8); + y = static_cast(data & 0xFF); + } + + virtual size_t printTo(Print& p) const { + return p.print(toString()); + } + + virtual String toString() const { + String t = "(x:"; + t += x; + t += " y:"; + t += y; + t += ")"; + return t; + } +}; + +class LRemoteJoyStick : public LRemoteUIControl { +public: + LRemoteJoyStick() + : LRemoteUIControl(){ + setType(RC_ANALOG); + } + + bool isValueChanged() { return hasEvent(); } + + // returns value according to remote slider value + LRemoteDirection getValue() { + consumeEvent(); + LRemoteDirection c(m_lastEvent.data); + return c; + } + +protected: + +}; + class LRemoteClass { public: LRemoteClass() diff --git a/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/SalasQueue.h b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/SalasQueue.h new file mode 100644 index 0000000..f9d2cb5 --- /dev/null +++ b/middleware/third_party/arduino/hardware/arduino/mt7697/libraries/LRemote/src/SalasQueue.h @@ -0,0 +1,124 @@ +// Modified from https://github.com/sdesalas/Arduino-Queue.h +/* + * Queue.h + * + * By Steven de Salas + * + * Defines a templated (generic) class for a queue of things. + * Used for Arduino projects, just #include "Queue.h" and add this file via the IDE. + * + * Examples: + * + * Queue queue(10); // Max 10 chars in this queue + * queue.push('H'); + * queue.push('e'); + * queue.count(); // 2 + * queue.push('l'); + * queue.push('l'); + * queue.count(); // 4 + * Serial.print(queue.pop()); // H + * Serial.print(queue.pop()); // e + * queue.count(); // 2 + * queue.push('o'); + * queue.count(); // 3 + * Serial.print(queue.pop()); // l + * Serial.print(queue.pop()); // l + * Serial.print(queue.pop()); // o + * + * struct Point { int x; int y; } + * Queue points(5); + * points.push(Point{2,4}); + * points.push(Point{5,0}); + * points.count(); // 2 + * + */ + +#ifndef SALAS_QUEUE_H +#define SALAS_QUEUE_H + +#include + +template +class Queue { + private: + int _front, _back, _count; + T *_data; + int _maxitems; + public: + Queue(int maxitems = 256) { + _front = 0; + _back = 0; + _count = 0; + _maxitems = maxitems; + _data = new T[maxitems + 1]; + } + ~Queue() { + delete[] _data; + } + inline int count(); + inline int front(); + inline int back(); + void push(const T &item); + T peek(); + T pop(); + void clear(); +}; + +template +inline int Queue::count() +{ + return _count; +} + +template +inline int Queue::front() +{ + return _front; +} + +template +inline int Queue::back() +{ + return _back; +} + +template +void Queue::push(const T &item) +{ + if(_count < _maxitems) { // Drops out when full + _data[_back++]=item; + ++_count; + // Check wrap around + if (_back > _maxitems) + _back -= (_maxitems + 1); + } +} + +template +T Queue::pop() { + if(_count <= 0) return T(); // Returns empty + else { + T result = _data[_front]; + _front++; + --_count; + // Check wrap around + if (_front > _maxitems) + _front -= (_maxitems + 1); + return result; + } +} + +template +T Queue::peek() { + if(_count <= 0) return T(); // Returns empty + else return _data[_front]; +} + +template +void Queue::clear() +{ + _front = _back; + _count = 0; +} + +#endif \ No newline at end of file