Last active
December 9, 2019 11:16
-
-
Save ematt/466972970b0a406163306f447bce37ef to your computer and use it in GitHub Desktop.
An LED matrix display controlled by ESP32.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright © 2019 <ematt> | |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | |
documentation files (the “Software”), to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all copies or substantial portions | |
of the Software. | |
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED | |
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
DEALINGS IN THE SOFTWARE. | |
*/ | |
/* An LED matrix display controlled by ESP32. | |
* | |
* This sketch contains the glue logic bethween Serial/Bluetooth interface an MD_Parola library. | |
* | |
* The last settings are saved to EEPROM and used at boot. | |
* | |
* Commands: | |
* Commands send to ESP32 has to begin with '#' and end with '$'. The first byte after '#' represent the command. | |
* All data received must be ASCII characters. | |
* | |
* Set text: #1<Text to show>$ | |
* eg. : #1Hello world!$ to show 'Hello world!' on the panel | |
* | |
* Set speed: #2<speed value>$ | |
* The speed is controlled by the delay bethween frames. The higher the value the slower the animation gets | |
* eg. : #2100$ to set the default speed | |
* | |
* Set brightness: #3<brightness value>$ | |
* Brightness is represented by a number from 0 to 15. The higher the value the higher the brightenss | |
* eg. : #315$ to set the brightness to max | |
* | |
* | |
*/ | |
#include <MD_Parola.h> | |
// https://github.com/MajicDesigns/MD_Parola | |
#include <MD_MAX72xx.h> | |
// https://github.com/MajicDesigns/MD_MAX72xx | |
#include <SPI.h> | |
#include <Preferences.h> | |
#include "BluetoothSerial.h" | |
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) | |
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it | |
#endif | |
#define MAX_DEVICES 3 // Number of modules connected | |
#define CLK_PIN 14// SPI SCK pin // Verde | |
#define CS_PIN 32// connected to pin 10 on UNO //Albastru | |
#define DATA_PIN 15// SPI MOSI pin on UNO // Galben | |
MD_Parola P = MD_Parola(MD_MAX72XX::GENERIC_HW, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); // SPI config | |
BluetoothSerial SerialBT; | |
#define BUF_SIZE 75 // Maximum of 75 characters | |
char curMessage[BUF_SIZE] = { "Waiting command" }; // used to hold current message | |
char curMessageBg[BUF_SIZE]; // used to hold current message | |
#define SPEED_TIME 100 | |
#define PAUSE_TIME 0 | |
void panelStringSet(const char *s, const unsigned char len); | |
void panelSpeedSet(const unsigned int desiredSpeed); | |
void panelIntensitySet(const unsigned char desiredIntensity); | |
const char COMMAND_START_CHAR = '#'; | |
const char COMMAND_END_CHAR = '$'; | |
// # CMD DATA $ | |
enum { | |
COMMAND_NONE = 0, | |
/* COMMAND_TURN_OFF, | |
COMMAND_TURN_ON, | |
COMMAND_PAUSE, | |
COMMAND_RESUME, | |
*/ | |
COMMAND_STRING_SET = '1', | |
COMMAND_SPEED_SET, | |
COMMAND_INTENSITY_SET, | |
}; | |
const unsigned int MAGIC_NUMBER = 0xABCDEFAC; | |
typedef struct { | |
unsigned int magicNumber; | |
char message[sizeof(curMessage)]; | |
unsigned int animationSpeed; | |
unsigned char panelIntensity; | |
} settings_t; | |
settings_t settings = { | |
MAGIC_NUMBER, | |
{"Waiting commang..."}, | |
100, | |
1 | |
}; | |
Preferences prefs; | |
void settingsSetup() | |
{ | |
char settingsBuffer[sizeof(settings_t)]; | |
prefs.begin("ledDisplay"); | |
prefs.getBytes("ledDisplay", settingsBuffer, sizeof(settingsBuffer)); | |
settings_t *readSettings = (settings_t*)(settingsBuffer); | |
if(readSettings->magicNumber != MAGIC_NUMBER) | |
{ | |
// Saved setting are corrupted, rewrite with default values | |
settingsSave(settings); | |
} | |
else | |
{ | |
settings = *readSettings; | |
} | |
} | |
void settingsSave(settings_t& settingToSave) | |
{ | |
prefs.putBytes("ledDisplay", (char*)&settingToSave, sizeof(settingToSave)); | |
} | |
void processCommCommandBuffer(char command, char *data, unsigned char dataLen) | |
{ | |
bool newSettings = false; | |
switch (command) | |
{ | |
case COMMAND_STRING_SET: | |
{ | |
unsigned char strLen = dataLen % (BUF_SIZE -1); | |
data[strLen] = '\0'; | |
panelStringSet(data, strLen); | |
memcpy(settings.message, data, sizeof(settings.message)); | |
newSettings = true; | |
} | |
break; | |
case COMMAND_SPEED_SET: | |
{ | |
char desiredSpeedString[5]; | |
desiredSpeedString[4] = '\0'; | |
memcpy(desiredSpeedString, data, 4); | |
int desiredSpeed = atoi(desiredSpeedString); | |
if (desiredSpeed != 0) | |
{ | |
panelSpeedSet(desiredSpeed); | |
settings.animationSpeed = desiredSpeed; | |
newSettings = true; | |
} | |
else | |
{ | |
//error | |
} | |
} | |
break; | |
case COMMAND_INTENSITY_SET: | |
{ | |
char desiredIntensityString[3]; | |
desiredIntensityString[2] = '\0'; | |
memcpy(desiredIntensityString, data, 2); | |
int desiredIntensity = atoi(desiredIntensityString); | |
panelIntensitySet(desiredIntensity); | |
settings.panelIntensity = desiredIntensity; | |
newSettings = true; | |
} | |
break; | |
default: | |
break; | |
} | |
if(newSettings) | |
{ | |
settingsSave(settings); | |
} | |
} | |
void serialCommProcessChar(int c) | |
{ | |
static enum { COMM_IDLE, COMM_RECEIVING, COMM_DONE } state = COMM_IDLE; | |
static unsigned char bufferIndex = 0; | |
static char commBuffer[BUF_SIZE + sizeof(COMMAND_START_CHAR) + sizeof(COMMAND_END_CHAR) + 1]; // for command | |
Serial.println((char)c); | |
switch (state) | |
{ | |
case COMM_IDLE: | |
if (c == COMMAND_START_CHAR) | |
state = COMM_RECEIVING; | |
bufferIndex = 0; | |
break; | |
case COMM_RECEIVING: | |
if (c == COMMAND_END_CHAR) | |
{ | |
state = COMM_DONE; | |
break; | |
} | |
commBuffer[bufferIndex % sizeof(commBuffer)] = c; | |
bufferIndex++; | |
if (bufferIndex >= sizeof(commBuffer)) | |
state = COMM_DONE; | |
break; | |
case COMM_DONE: | |
processCommCommandBuffer(commBuffer[0], &commBuffer[1], bufferIndex - 1); | |
state = COMM_IDLE; | |
break; | |
} | |
} | |
bool panelSwitchMsgPending = false; | |
void panelStringSet(const char *s, const unsigned char len) | |
{ | |
strncpy(curMessageBg, s, sizeof(curMessageBg)); | |
curMessageBg[BUF_SIZE - 1] = '\0'; | |
panelSwitchMsgPending = true; | |
} | |
void panelSpeedSet(const unsigned int desiredSpeed) | |
{ | |
P.setSpeed((uint16_t)desiredSpeed); | |
} | |
void panelIntensitySet(const unsigned char desiredIntensity) | |
{ | |
if(desiredIntensity > 15) | |
return; | |
P.setIntensity(desiredIntensity); | |
} | |
void setup() | |
{ | |
Serial.begin(9600); | |
settingsSetup(); | |
P.begin(); // Start Parola | |
strcpy(curMessage, settings.message); | |
// configure Parola | |
P.displayText(curMessage, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT); | |
P.setIntensity(settings.panelIntensity); | |
P.setSpeed(settings.animationSpeed); | |
SerialBT.begin("LED panel"); //Bluetooth device name | |
} | |
void loop() { | |
if (Serial.available()) { | |
int inByte = Serial.read(); | |
serialCommProcessChar(inByte); | |
} | |
if (SerialBT.available()) { | |
int inByte = SerialBT.read(); | |
serialCommProcessChar(inByte); | |
} | |
if (P.displayAnimate()) // If finished displaying message | |
{ | |
if (panelSwitchMsgPending) | |
{ | |
panelSwitchMsgPending = false; | |
strcpy(curMessage, curMessageBg); | |
P.setTextBuffer(curMessage); | |
} | |
P.displayReset(); // Reset and display it again | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TODO: