Skip to content

Commit

Permalink
added a frameside lua app to solve glitchy draw calls, added battery …
Browse files Browse the repository at this point in the history
…level display from updated simple_frame_app
  • Loading branch information
CitizenOneX committed Aug 9, 2024
1 parent a2f7c88 commit dc32435
Show file tree
Hide file tree
Showing 6 changed files with 582 additions and 292 deletions.
79 changes: 79 additions & 0 deletions assets/frame_app.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
-- we store the data from the host quickly from the data handler interrupt
-- and wait for the main loop to pick it up for processing/drawing
-- app_data.text is complete text, ready for printing
-- app_data.wip is accumulating chunks and will be copied over to app_data.text when complete
local app_data = { text = "", wip = "" }

-- Frame to phone flags
BATTERY_LEVEL_FLAG = "\x0c"

-- Phone to Frame flags
NON_FINAL_CHUNK_FLAG = 0x0a
FINAL_CHUNK_FLAG = 0x0b


-- every time byte data arrives just extract the data payload from the message
-- and save to the local app_data table so the main loop can pick it up and print it
-- format of [data] (a multi-line text string) is:
-- first digit will be 0x0a/0x0b non-final/final chunk of long text
-- followed by string bytes out to the mtu
function data_handler(data)
if string.byte(data, 1) == NON_FINAL_CHUNK_FLAG then
-- non-final chunk
app_data.wip = app_data.wip .. string.sub(data, 2)
elseif string.byte(data, 1) == FINAL_CHUNK_FLAG then
-- final chunk
app_data.text = app_data.wip .. string.sub(data, 2)
app_data.wip = ""
end
end

-- draw the current text on the display
-- Note: For lower latency for text to first appear, we could draw the wip text as it arrives
-- keeping track of horizontal and vertical offsets to continue drawing subsequent packets
function print_text()
local i = 0
for line in app_data.text:gmatch("([^\n]*)\n?") do
if line ~= "" then
frame.display.text(line, 1, i * 60 + 1)
i = i + 1
end
end

end

-- Main app loop
function app_loop()
local last_batt_update = 0
while true do
rc, err = pcall(
function()
print_text()
frame.display.show()
frame.sleep(0.04) -- ~25fps

-- periodic battery level updates
local t = frame.time.utc()
if (last_batt_update == 0 or (t - last_batt_update) > 180) then
pcall(frame.bluetooth.send, BATTERY_LEVEL_FLAG .. string.char(math.floor(frame.battery_level())))
last_batt_update = t
end
end
)
-- Catch the break signal here and clean up the display
if rc == false then
-- send the error back on the stdout stream
print(err)
frame.display.text(" ", 1, 1)
frame.display.show()
frame.sleep(0.04) -- TODO was this too quick, is that why?
break
end
end
end

-- register the handler as a callback for all data sent from the host
frame.bluetooth.receive_callback(data_handler)

-- run the main app loop
app_loop()
42 changes: 23 additions & 19 deletions lib/bluetooth.dart → lib/brilliant_bluetooth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,25 @@ class BrilliantDevice {
String get uuid => device.remoteId.str;

Stream<BrilliantDevice> get connectionState {
return FlutterBluePlus.events.onConnectionStateChanged
// changed to only listen for connectionState data coming from the Frame device rather than all events from all devices as before
return device.connectionState
.where((event) =>
event.connectionState == BluetoothConnectionState.connected ||
(event.connectionState == BluetoothConnectionState.disconnected &&
event.device.disconnectReason != null &&
event.device.disconnectReason!.code != 23789258))
event == BluetoothConnectionState.connected ||
(event == BluetoothConnectionState.disconnected &&
device.disconnectReason != null &&
device.disconnectReason!.code != 23789258))
.asyncMap((event) async {
if (event.connectionState == BluetoothConnectionState.connected) {
if (event == BluetoothConnectionState.connected) {
_log.info("Connection state stream: Connected");
try {
return await BrilliantBluetooth._enableServices(event.device);
return await BrilliantBluetooth._enableServices(device);
} catch (error) {
_log.warning("Connection state stream: Invalid due to $error");
return Future.error(BrilliantBluetoothException(error.toString()));
}
}
_log.info(
"Connection state stream: Disconnected due to ${event.device.disconnectReason!.description}");
"Connection state stream: Disconnected due to ${device.disconnectReason!.description}");
// Note: automatic reconnection isn't suitable for all cases, so it might
// be better to leave this up to the sdk user to specify. iOS appears to
// use FBP's native autoconnect, so if Android behaviour would change then
Expand All @@ -76,28 +77,31 @@ class BrilliantDevice {
// }
return BrilliantDevice(
state: BrilliantConnectionState.disconnected,
device: event.device,
device: device,
);
});
}

// logs each string message (messages without the 0x01 first byte) and provides a stream of the utf8-decoded strings
Stream<String> get stringResponse {
return FlutterBluePlus.events.onCharacteristicReceived
.where((event) => event.value[0] != 0x01)
// changed to only listen for data coming through the Frame's rx characteristic, not all attached devices as before
return _rxChannel!.onValueReceived
.where((event) => event[0] != 0x01)
.map((event) {
if (event.value[0] != 0x02) {
_log.info("Received string: ${utf8.decode(event.value)}");
if (event[0] != 0x02) {
_log.info("Received string: ${utf8.decode(event)}");
}
return utf8.decode(event.value);
return utf8.decode(event);
});
}

Stream<List<int>> get dataResponse {
return FlutterBluePlus.events.onCharacteristicReceived
.where((event) => event.value[0] == 0x01)
// changed to only listen for data coming through the Frame's rx characteristic, not all attached devices as before
return _rxChannel!.onValueReceived
.where((event) => event[0] == 0x01)
.map((event) {
_log.fine("Received data: ${event.value.sublist(1)}");
return event.value.sublist(1);
_log.fine("Received data: ${event.sublist(1)}");
return event.sublist(1);
});
}

Expand Down Expand Up @@ -145,7 +149,7 @@ class BrilliantDevice {
}

final response = await _rxChannel!.onValueReceived
.timeout(const Duration(seconds: 1))
.timeout(const Duration(seconds: 10))
.first;

return utf8.decode(response);
Expand Down
4 changes: 2 additions & 2 deletions lib/display_helper.dart → lib/frame_helper.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'bluetooth.dart';
import 'brilliant_bluetooth.dart';

enum HorizontalAlignment {
left,
Expand All @@ -12,7 +12,7 @@ enum VerticalAlignment {
bottom,
}

class DisplayHelper {
class FrameHelper {
static const _lineHeight = 60;

static const Map<int, int> charWidthMapping = {
Expand Down
Loading

0 comments on commit dc32435

Please sign in to comment.