Skip to content

Instantly share code, notes, and snippets.

@ematt
Last active December 9, 2019 11:16
Show Gist options
  • Save ematt/466972970b0a406163306f447bce37ef to your computer and use it in GitHub Desktop.
Save ematt/466972970b0a406163306f447bce37ef to your computer and use it in GitHub Desktop.
An LED matrix display controlled by ESP32.
/*
* 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
}
}
@ematt
Copy link
Author

ematt commented Dec 9, 2019

TODO:

  • Use arrows for BluetoothSerial include

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment