Skip to content

Commit 346c8c6

Browse files
authored
Merge pull request #20 from Thokoop/feature/support-48-flaps
Added support for 48 flaps per module
2 parents 1a465ec + 2a5f3c2 commit 346c8c6

File tree

7 files changed

+83
-33
lines changed

7 files changed

+83
-33
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
![format badge](https://github.com/jhoff/Split-Flap-Display/actions/workflows/format-check.yml/badge.svg?branch=main)
44
[![](https://dcbadge.limes.pink/api/server/https://discord.gg/RCvks4XXXH?style=flat)](https://discord.gg/RCvks4XXXH)
55

6-
76
Looking for support? Find help on discord ☝️
87

98
Firmware for the modular Split Flap Display created by [Morgan Manly](https://github.com/ManlyMorgan/Split-Flap-Display)

src/SplitFlapDisplay.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ void SplitFlapDisplay::init() {
1212
displayOffset = settings.getInt("displayOffset");
1313
magnetPosition = settings.getInt("magnetPosition");
1414
maxVel = settings.getFloat("maxVel");
15+
charSetSize = settings.getInt("charset");
1516

1617
std::vector<int> settingAddresses = settings.getIntVector("moduleAddresses");
1718
for (int i = 0; i < numModules; i++) {
@@ -31,7 +32,9 @@ void SplitFlapDisplay::init() {
3132
Serial.println();
3233

3334
for (uint8_t i = 0; i < numModules; i++) {
34-
modules[i] = SplitFlapModule(moduleAddresses[i], stepsPerRot, moduleOffsets[i] + displayOffset, magnetPosition);
35+
modules[i] = SplitFlapModule(
36+
moduleAddresses[i], stepsPerRot, moduleOffsets[i] + displayOffset, magnetPosition, charSetSize
37+
);
3538
}
3639

3740
SDAPin = settings.getInt("sdaPin");
@@ -157,7 +160,18 @@ void SplitFlapDisplay::writeChar(char inputChar, float speed) {
157160
moveTo(targetPositions, speed);
158161
}
159162

163+
String sanitizeInput(const String &input) {
164+
String sanitized = input;
165+
166+
// Replace problematic characters
167+
sanitized.replace("'", "'\\'");
168+
sanitized.replace("%", "%%");
169+
170+
return sanitized;
171+
}
172+
160173
void SplitFlapDisplay::writeString(String inputString, float speed, bool centering) {
174+
inputString = sanitizeInput(inputString);
161175
String displayString = inputString.substring(0, numModules);
162176

163177
if (centering) {

src/SplitFlapDisplay.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class SplitFlapDisplay {
3333
void testCount();
3434
void testRandom(float speed = MAX_RPM);
3535
int getNumModules() { return numModules; }
36+
int getCharsetSize() const { return charSetSize; }
3637
void setMqtt(SplitFlapMqtt *mqttHandler);
3738

3839
private:
@@ -48,9 +49,10 @@ class SplitFlapDisplay {
4849
int moduleOffsets[MAX_MODULES];
4950
int displayOffset;
5051

51-
float maxVel; // Max Velocity In RPM
52-
int stepsPerRot; // number of motor steps per full rotation of character
53-
// drum
52+
float maxVel; // Max Velocity In RPM
53+
int charSetSize; // 37 for standard, 48 for extended
54+
int stepsPerRot; // number of motor steps per full rotation of character
55+
// drum
5456
int magnetPosition; // position of drum wheel when magnet is detected
5557
int SDAPin; // SDA pin
5658
int SCLPin; // SCL pin

src/SplitFlapDisplay.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ JsonSettings settings = JsonSettings("config", {
3737
{"sclPin", JsonSetting(9)},
3838
{"stepsPerRot", JsonSetting(2048)},
3939
{"maxVel", JsonSetting(15.0f)},
40+
{"charset", JsonSetting(37)},
4041
// Operational States
4142
{"mode", JsonSetting(0)}
4243
});

src/SplitFlapModule.cpp

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,33 @@
22

33
// Array of characters, in order, the first item is located on the magnet on the
44
// character drum
5-
const char SplitFlapModule::chars[37] = {' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
6-
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
7-
'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
8-
const int SplitFlapModule::numChars = sizeof(SplitFlapModule::chars) / sizeof(SplitFlapModule::chars[0]);
9-
int SplitFlapModule::charPositions[37]; // to be written in init function
5+
const char SplitFlapModule::StandardChars[37] = {' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
6+
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
7+
'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
108

11-
bool hasErrored = false;
9+
const char SplitFlapModule::ExtendedChars[48] = {
10+
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
11+
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4',
12+
'5', '6', '7', '8', '9', '\'', ':', '?', '!', '.', '-', '/', '$', '@', '#', '%',
13+
};
1214

13-
// Not used but useful to have
14-
// const int SplitFlapModule::motorPins[] = {P06, P05, P04, P03}; // List of
15-
// pins to set as OUTPUT const int SplitFlapModule::HallEffectPIN = P17; //Input
16-
// pin for Hall effect sensor
15+
bool hasErrored = false;
1716

1817
// Default Constructor
19-
SplitFlapModule::SplitFlapModule() : address(0), position(0), stepNumber(0), stepsPerRot(0) { // default values
20-
magnetPosition = 710; // sort of guessing
18+
SplitFlapModule::SplitFlapModule()
19+
: address(0), position(0), stepNumber(0), stepsPerRot(0), chars(StandardChars), numChars(37), charSetSize(37) {
20+
magnetPosition = 710;
2121
}
2222

2323
// Constructor implementation
24-
SplitFlapModule::SplitFlapModule(uint8_t I2Caddress, int stepsPerFullRotation, int stepOffset, int magnetPos)
25-
: address(I2Caddress), position(0), stepNumber(0), stepsPerRot(stepsPerFullRotation) {
24+
SplitFlapModule::SplitFlapModule(
25+
uint8_t I2Caddress, int stepsPerFullRotation, int stepOffset, int magnetPos, int charsetSize
26+
)
27+
: address(I2Caddress), position(0), stepNumber(0), stepsPerRot(stepsPerFullRotation), charSetSize(charsetSize) {
2628
magnetPosition = magnetPos + stepOffset;
29+
30+
chars = (charsetSize == 48) ? ExtendedChars : StandardChars;
31+
numChars = (charsetSize == 48) ? 48 : 37;
2732
}
2833

2934
void SplitFlapModule::writeIO(uint16_t data) {
@@ -49,6 +54,13 @@ void SplitFlapModule::writeIO(uint16_t data) {
4954

5055
// Init Module, Setup IO Board
5156
void SplitFlapModule::init() {
57+
float stepSize = (float) stepsPerRot / (float) numChars;
58+
float currentPosition = 0;
59+
for (int i = 0; i < numChars; i++) {
60+
charPositions[i] = (int) currentPosition;
61+
currentPosition += stepSize;
62+
}
63+
5264
uint16_t initState = 0b1111111111100001; // Pin 15 (17) as INPUT, Pins 1-4 as OUTPUT
5365
writeIO(initState);
5466

@@ -67,19 +79,11 @@ void SplitFlapModule::init() {
6779
delay(initDelay);
6880

6981
stop();
70-
71-
// Generate Character Position Array
72-
float stepSize = (float) stepsPerRot / (float) numChars;
73-
float currentPosition = 0;
74-
for (int i = 0; i < numChars; i++) {
75-
charPositions[i] = (int) currentPosition;
76-
currentPosition += stepSize;
77-
}
7882
}
7983

8084
int SplitFlapModule::getCharPosition(char inputChar) {
8185
inputChar = toupper(inputChar);
82-
for (int i = 0; i < numChars; i++) {
86+
for (int i = 0; i < charSetSize; i++) {
8387
if (chars[i] == inputChar) {
8488
return charPositions[i];
8589
}

src/SplitFlapModule.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class SplitFlapModule {
88
// Constructor declarationS
99
SplitFlapModule(); // default constructor required to allocate memory for
1010
// SplitFlapDisplay class
11-
SplitFlapModule(uint8_t I2Caddress, int stepsPerFullRotation, int stepOffset, int magnetPos);
11+
SplitFlapModule(uint8_t I2Caddress, int stepsPerFullRotation, int stepOffset, int magnetPos, int charSetSize);
1212

1313
void init();
1414

@@ -19,6 +19,7 @@ class SplitFlapModule {
1919
int getMagnetPosition() const { return magnetPosition; } // position where magnet is detected
2020
int getCharPosition(char inputChar); // get integer position given single character
2121
int getPosition() const { return position; } // get integer position
22+
int getCharsetSize() const { return numChars; } // getter for charset size
2223

2324
bool readHallEffectSensor(); // return the value read by the hall effect
2425
// sensor
@@ -41,10 +42,13 @@ class SplitFlapModule {
4142
static const int motorPins[]; // Array of motor pins
4243
static const int HallEffectPIN; // Hall Effect Sensor Pin (On PCF8575)
4344

44-
static const char chars[37]; // all characters in order
45-
static int charPositions[37]; // will be generated based on the characters and
46-
// the magnetPosition variable
47-
static const int numChars; // number of characters in module
45+
const char *chars; // pointer to active character set
46+
int charPositions[48]; // support up to 48 characters
47+
int numChars; // current number of characters
48+
int charSetSize;
49+
50+
static const char StandardChars[37];
51+
static const char ExtendedChars[48];
4852
};
4953

5054
// //PINs on the PCF8575 Board

src/web/settings.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
4242
<input
4343
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
4444
type="text"
45+
id="name"
4546
x-model="settings.name"
4647
placeholder="Enter a name for this display"
4748
/>
@@ -58,6 +59,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
5859
<input
5960
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
6061
type="text"
62+
id="mdns"
6163
x-model="settings.mdns"
6264
placeholder="Enter mDNS hostname"
6365
/>
@@ -74,6 +76,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
7476
<input
7577
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
7678
type="text"
79+
id="otaPass"
7780
x-model="settings.otaPass"
7881
placeholder="Enter OTA Password"
7982
/>
@@ -91,6 +94,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
9194
<select
9295
class="w-full p-3 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
9396
x-model="settings.timezone"
97+
id="timezone"
9498
>
9599
<template x-for="(name, zone) in timezones" :key="zone">
96100
<option
@@ -217,6 +221,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
217221
<input
218222
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
219223
type="number"
224+
id="moduleCount"
220225
x-model="settings.moduleCount"
221226
min="1"
222227
max="8"
@@ -229,12 +234,25 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
229234
x-text="errors.message"
230235
></div>
231236

237+
<label for="charset" class="block text-left text-lg mt-4"
238+
>Character Set</label
239+
>
240+
<select
241+
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-white"
242+
x-model.number="settings.charset"
243+
id="charset"
244+
>
245+
<option value="37">Standard (37)</option>
246+
<option value="48">Extended (48)</option>
247+
</select>
248+
232249
<label for="moduleAddresses" class="block text-left text-lg mt-4"
233250
>Module Addresses (comma-separated)</label
234251
>
235252
<input
236253
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
237254
type="text"
255+
id="moduleAddresses"
238256
x-model="settings.moduleAddresses"
239257
placeholder="Enter addresses"
240258
/>
@@ -251,6 +269,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
251269
<input
252270
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
253271
type="number"
272+
id="magnetPosition"
254273
x-model="settings.magnetPosition"
255274
placeholder="Enter magnet position"
256275
/>
@@ -267,6 +286,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
267286
<input
268287
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
269288
type="text"
289+
id="moduleOffsets"
270290
x-model="settings.moduleOffsets"
271291
placeholder="Enter module offsets"
272292
/>
@@ -283,6 +303,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
283303
<input
284304
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
285305
type="number"
306+
id="displayOffset"
286307
x-model="settings.displayOffset"
287308
placeholder="Enter display offset"
288309
/>
@@ -299,6 +320,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
299320
<input
300321
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
301322
type="number"
323+
id="sdaPin"
302324
x-model="settings.sdaPin"
303325
placeholder="Enter SDA pin"
304326
/>
@@ -315,6 +337,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
315337
<input
316338
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
317339
type="number"
340+
id="sclPin"
318341
x-model="settings.sclPin"
319342
placeholder="Enter SCL pin"
320343
/>
@@ -331,6 +354,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
331354
<input
332355
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
333356
type="number"
357+
id="stepsPerRot"
334358
x-model="settings.stepsPerRot"
335359
placeholder="Enter steps per rotation"
336360
/>
@@ -347,6 +371,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
347371
<input
348372
class="w-full p-3 mt-2 text-lg border border-gray-600 rounded-md text-center bg-gray-700 text-gray-100"
349373
type="number"
374+
id="maxVel"
350375
step="0.1"
351376
x-model="settings.maxVel"
352377
placeholder="Enter max velocity"
@@ -437,6 +462,7 @@ <h1 class="text-2xl font-bold mx-auto" x-text="header"></h1>
437462
save() {
438463
this.saving = true;
439464
this.errors = {};
465+
440466
fetch("/settings", {
441467
method: "POST",
442468
headers: { "Content-Type": "application/json" },

0 commit comments

Comments
 (0)