Skip to content

Commit 134aafb

Browse files
committed
Add the ble_shining_kb demo.
1 parent 2404979 commit 134aafb

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
///
2+
/// ble_shining_kb.ino
3+
///
4+
/// created: 2020-11
5+
///
6+
/// Simulate an evil writing machine in an isolated hotel on winter.
7+
/// No hardware required other than an Nano33 ble.
8+
///
9+
10+
#include "Nano33BleHID.h"
11+
#include "signal_utils.h"
12+
13+
/* -------------------------------------------------------------------------- */
14+
15+
Nano33BleKeyboard bleKb("Shining Keyboard");
16+
17+
// Tracking index for the end of the writing animation ticker.
18+
int sTickerIndex = -1;
19+
20+
static const char kSentence[] = "All work and no play makes Jack a dull boy";
21+
static const int kNumTickerSteps = 4;
22+
23+
// How long it takes for the sentence to be written.
24+
static const int kSentenceDurationMilliseconds = 4029;
25+
26+
// How long it takes before the sentence is rewritten.
27+
static const int kSentenceDelayMilliseconds = 1977;
28+
29+
// How long one writing animation will run.
30+
static const int kSentenceTotalTimeMilliseconds = kSentenceDurationMilliseconds + kSentenceDelayMilliseconds;
31+
32+
// Safeguard to terminate this mess of an app before going crazy.
33+
static const int kTotalRuntime = 8 * kSentenceTotalTimeMilliseconds;
34+
35+
// Builtin LED animation delays when disconnect.
36+
static const int kLedBeaconDelayMilliseconds = 1185;
37+
static const int kLedErrorDelayMilliseconds = kLedBeaconDelayMilliseconds / 10;
38+
39+
// Builtin LED intensity when connected.
40+
static const int kLedConnectedIntensity = 30;
41+
42+
/* -------------------------------------------------------------------------- */
43+
44+
/** Utility struct to send a text through the Keyboard HID for a given time. */
45+
struct SentenceWriter {
46+
std::string sentence;
47+
int current_index;
48+
unsigned long duration_ms;
49+
50+
SentenceWriter(const char* str, unsigned long duration_ms)
51+
: sentence(str)
52+
, current_index(-1)
53+
, duration_ms(duration_ms)
54+
{}
55+
56+
void write(HIDKeyboardService &kb, unsigned long frame_time)
57+
{
58+
// Calculate the absolute time in the animation (in [0.0f, 1.0f[)
59+
float dt = frame_time / float(duration_ms);
60+
61+
// Add a smooth interpolation to add "lifeness".
62+
dt = smoothstep(0.0f, 1.0f, dt);
63+
64+
// Map absolute time to the letter index.
65+
const int index = floor(dt * sentence.size());
66+
67+
// When the index change, send its letter via the keyboard service.
68+
if (current_index != index) {
69+
current_index = index;
70+
const uint8_t letter = sentence[index];
71+
kb.sendCharacter(letter);
72+
}
73+
}
74+
} stringWriter(kSentence, kSentenceDurationMilliseconds);
75+
76+
/* -------------------------------------------------------------------------- */
77+
78+
void setup()
79+
{
80+
// General setup.
81+
pinMode(LED_BUILTIN, OUTPUT);
82+
83+
// Initialize both BLE and the HID.
84+
bleKb.initialize();
85+
86+
// Launch the event queue that will manage both BLE events and the loop.
87+
// After this call the main thread will be halted.
88+
MbedBleHID_RunEventThread();
89+
}
90+
91+
void loop()
92+
{
93+
// When disconnected, we animate the builtin LED to indicate the device state.
94+
if (bleKb.connected() == false) {
95+
animateLED(LED_BUILTIN, (bleKb.has_error()) ? kLedErrorDelayMilliseconds
96+
: kLedBeaconDelayMilliseconds);
97+
return;
98+
}
99+
100+
// When connected, we slightly dim the builtin LED.
101+
analogWrite(LED_BUILTIN, kLedConnectedIntensity);
102+
103+
// Stop when we reach a certain runtime (better safe than sorry).
104+
if (bleKb.connection_time() > kTotalRuntime) {
105+
return;
106+
}
107+
108+
// Retrieve the HIDService to update.
109+
auto *kb = bleKb.hid();
110+
111+
// Local time in the looping animation.
112+
unsigned long frame_time = bleKb.connection_time() % kSentenceTotalTimeMilliseconds;
113+
114+
// The animation is divided in two parts :
115+
if (frame_time < kSentenceDurationMilliseconds)
116+
{
117+
// Write the sentence using the StringWriter object.
118+
stringWriter.write(*kb, frame_time);
119+
}
120+
else
121+
{
122+
// Wait by writing dots at a different speed (using the same logic as a StringWriter).
123+
float dt = (frame_time-kSentenceDurationMilliseconds) / float(kSentenceDelayMilliseconds);
124+
dt = 1.0f - (1.0f - dt) * pow(dt, dt); // slow-out
125+
int index = floor(kNumTickerSteps*dt);
126+
if (sTickerIndex != index) {
127+
kb->sendCharacter('.');
128+
sTickerIndex = index;
129+
}
130+
}
131+
}
132+
133+
/* -------------------------------------------------------------------------- */

0 commit comments

Comments
 (0)