447 lines
9.7 KiB
C++
447 lines
9.7 KiB
C++
/*
|
|
* UNO Monitor Receiver v.0.1.0 (4-bit LCD Version)
|
|
* Kidacro <kidacro@archamedis.net>
|
|
* https://kidacro.archamedis.net/git/kidacro/Arduino/src/branch/main/uno-stats-monitor
|
|
*
|
|
* uno_reciever.ino - Arduino Receiver
|
|
*
|
|
* Waits for input over serial connection and displays on LCD & LED
|
|
* Receives IR events and reports them over serial connection to a daemon
|
|
*
|
|
* Input Schema:
|
|
* Start char is: '#'
|
|
* End char is: '@'
|
|
* 2nd char denotes which line number/mode to use
|
|
*
|
|
* Setting Schema:
|
|
* LCDLength:LCDRows:LCDDelay:LEDBrightness:LEDDelay
|
|
* 16:2:5:1:3
|
|
*
|
|
* Example:
|
|
* Line 1: #1Archamedis Status@
|
|
* Line 2: #299% free / etc.@
|
|
* LAVG -: #-24@
|
|
* LSET 0: #016:2:5:1:3@
|
|
|
|
* Output Schema:
|
|
* Multimedia Controls use single characters to denote which button was pressed
|
|
* P power
|
|
* M mute
|
|
* m mode
|
|
* p pause/play
|
|
* b back
|
|
* f forward
|
|
* e eq
|
|
* - vol down
|
|
* + vol up
|
|
* r return
|
|
* u usb scan
|
|
* 0 number 0
|
|
* 1 number 1
|
|
* 2 number 2
|
|
* 3 number 3
|
|
* 4 number 4
|
|
* 5 number 5
|
|
* 6 number 6
|
|
* 7 number 7
|
|
* 8 number 8
|
|
* 9 number 9
|
|
* R repeat
|
|
* Z other button pressed
|
|
*/
|
|
|
|
#include <LiquidCrystal.h>
|
|
#include <LedControl.h>
|
|
#include <IRremote.h>
|
|
|
|
/* LCD Pinout:
|
|
* LCD RS pin to digital pin 12
|
|
* LCD Enable pin to digital pin 11
|
|
* LCD D4 pin to digital pin 5
|
|
* LCD D5 pin to digital pin 4
|
|
* LCD D6 pin to digital pin 3
|
|
* LCD D7 pin to digital pin 2
|
|
* LCD R/W pin to ground
|
|
* LCD VSS pin to ground
|
|
* LCD VCC pin to 5V
|
|
* 10K potentiometer:
|
|
* ends to +5V and ground
|
|
* wiper to LCD VO pin (pin 3)
|
|
*/
|
|
|
|
/* LED Pinout:
|
|
* LED DataIn(DIN) to digital pin 10
|
|
* LED LOAD(CS) to digital pin 9
|
|
* LED CLK to digital pin 8
|
|
* LED VSS pin to 5V
|
|
* LED GND to ground
|
|
*/
|
|
|
|
/* IR Pinout
|
|
* IR Signal to digital pin 13
|
|
* IR VSS pin to 5V
|
|
* IR GND to ground
|
|
*/
|
|
|
|
// LCD vars
|
|
const int LCDRS = 12, LCDEN = 11, LCDD4 = 5, LCDD5 = 4, LCDD6 = 3, LCDD7 = 2;
|
|
int LCDLength = 16;
|
|
int LCDRows = 2;
|
|
int LCDDelay = 10;
|
|
LiquidCrystal LCD(LCDRS, LCDEN, LCDD4, LCDD5, LCDD6, LCDD7);
|
|
|
|
// LED vars
|
|
const int LEDIN = 10, LEDCS = 9, LEDCLK = 8;
|
|
LedControl LEDlc=LedControl(LEDIN,LEDCLK,LEDCS,1);
|
|
int LEDBrightness = 8; // default = 8 / max = 15
|
|
unsigned long LEDDelay=10;
|
|
|
|
// IR vars
|
|
const int IRSIG = 13;
|
|
IRrecv Irrecv(IRSIG);
|
|
decode_results IRresults;
|
|
|
|
// Serial vars
|
|
const byte SERnumChars = 32;
|
|
char SERrecvChars[SERnumChars];
|
|
boolean SERnewData = false;
|
|
|
|
// Debugging
|
|
boolean DEBUG = false;
|
|
|
|
void LEDSetup(void)
|
|
{
|
|
/*
|
|
The MAX72XX is in power-saving mode on startup,
|
|
we have to do a wakeup call
|
|
*/
|
|
LEDlc.shutdown(0,false);
|
|
|
|
/* Set the brightness */
|
|
LEDlc.setIntensity(0,LEDBrightness);
|
|
|
|
/* and clear the display */
|
|
LEDlc.clearDisplay(0);
|
|
}
|
|
|
|
void LcdSetup(void)
|
|
{
|
|
// set up the LCD's number of columns and rows:
|
|
LCD.begin(LCDLength, LCDRows);
|
|
|
|
// clear
|
|
LCD.clear();
|
|
}
|
|
|
|
void IRSetup(void)
|
|
{
|
|
// start the receiver
|
|
Irrecv.enableIRIn();
|
|
}
|
|
|
|
// dynamic lines & character wiping (LCDLength)
|
|
void LcdLd(int line = 1, boolean wipe = false)
|
|
{
|
|
char myline[LCDLength];
|
|
int x = 0;
|
|
|
|
// physical line #'s to logical
|
|
if(line > 0) {
|
|
line--;
|
|
}
|
|
|
|
// don't go past last row
|
|
if(line > LCDRows) {
|
|
line = LCDRows;
|
|
}
|
|
|
|
delay(LCDDelay);
|
|
LCD.setCursor(0, line); // Define the cursor position as the 1st position of the specified line
|
|
|
|
if(wipe) {
|
|
for(x=0;x<LCDLength;x++) {
|
|
myline[x] = ' ';
|
|
}
|
|
|
|
LcdWrite(myline);
|
|
LCD.setCursor(0, line);
|
|
}
|
|
|
|
delay(LCDDelay);
|
|
}
|
|
|
|
void LcdIntro(void)
|
|
{
|
|
// Print a "intro" message to the LCD.
|
|
LcdLd(1);
|
|
LCD.print("UnoMonitor 0.1.0");
|
|
LcdLd(2);
|
|
LCD.print("Waiting for data");
|
|
}
|
|
|
|
void LcdCls(void)
|
|
{
|
|
delay(LCDDelay);
|
|
LCD.clear(); // The screen is empty
|
|
LcdLd(1); // the cursor position is zeroed
|
|
delay(LCDDelay);
|
|
}
|
|
|
|
/*
|
|
TODO: returns starting position of text or 0 for 1st position
|
|
int LcdCenter(int mylen)
|
|
{
|
|
int mypos = 0;
|
|
|
|
return mypos;
|
|
}*/
|
|
|
|
void LcdWrite(String mymessage)
|
|
{
|
|
int i = 0;
|
|
unsigned int mylen = mymessage.length();
|
|
|
|
// trunicate string to lcd line length
|
|
if(mylen > LCDLength) {
|
|
mylen = LCDLength;
|
|
}
|
|
|
|
// Debug input and length
|
|
if(DEBUG) {
|
|
Serial.println("\n\nString: ");
|
|
Serial.println(mymessage);
|
|
Serial.println(mymessage.length());
|
|
}
|
|
|
|
// Output string
|
|
//LCD.print(mymessage);
|
|
|
|
// Output character by character
|
|
for(i=0; i < mylen; i++) {
|
|
char c = mymessage[i];
|
|
delay(LCDDelay);
|
|
LCD.print(c);
|
|
delay(LCDDelay);
|
|
}
|
|
}
|
|
|
|
// TODO: make segment_row & segment_col dynamic and configurable
|
|
void LedIntro() {
|
|
for(int row=0;row<8;row++) {
|
|
for(int col=0;col<8;col++) {
|
|
delay(LEDDelay);
|
|
LEDlc.setLed(0,row,col,true);
|
|
delay(LEDDelay);
|
|
for(int i=0;i<col;i++) {
|
|
LEDlc.setLed(0,row,col,false);
|
|
delay(LEDDelay);
|
|
LEDlc.setLed(0,row,col,true);
|
|
delay(LEDDelay);
|
|
}
|
|
}
|
|
}
|
|
LEDlc.clearDisplay(0);
|
|
}
|
|
|
|
void LedShowLoad(const char *load)
|
|
{
|
|
int myload = atoi(load);
|
|
int segments = 0;
|
|
|
|
LEDlc.clearDisplay(0);
|
|
|
|
/*
|
|
Armchair math begin:
|
|
100% = 8
|
|
87.5% = 7
|
|
75% = 6
|
|
62.5% = 5
|
|
50% = 4
|
|
37.5% = 3
|
|
25% = 2
|
|
12.5% = 1
|
|
0% = 0
|
|
*/
|
|
|
|
if(myload < 12) segments = 0;
|
|
if(myload >= 12) segments = 1;
|
|
if(myload >= 25) segments = 2;
|
|
if(myload >= 37) segments = 3;
|
|
if(myload >= 50) segments = 4;
|
|
if(myload >= 62) segments = 5;
|
|
if(myload >= 75) segments = 6;
|
|
if(myload >= 87) segments = 7;
|
|
if(myload >= 95) segments = 8;
|
|
|
|
// segments as rows
|
|
for(int row=0;row<8;row++) {
|
|
for(int col=0;col<segments;col++) {
|
|
delay(LEDDelay);
|
|
LEDlc.setLed(0,row,col,true);
|
|
delay(LEDDelay);
|
|
for(int i=0;i<col;i++) {
|
|
LEDlc.setLed(0,row,col,false);
|
|
delay(LEDDelay);
|
|
LEDlc.setLed(0,row,col,true);
|
|
delay(LEDDelay);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// IR translator
|
|
// TODO: read codes from setup program for custom remotes
|
|
void IRtranslate()
|
|
{
|
|
switch(IRresults.value) {
|
|
case 0xFFA25D: Serial.println("P"); break; // power
|
|
case 0xFFE21D: Serial.println("M"); break; // mute
|
|
case 0xFF629D: Serial.println("m"); break; // mode
|
|
case 0xFF22DD: Serial.println("p"); break; // pause/play
|
|
case 0xFF02FD: Serial.println("b"); break; // back
|
|
case 0xFFC23D: Serial.println("f"); break; // forward
|
|
case 0xFFE01F: Serial.println("e"); break; // eq
|
|
case 0xFFA857: Serial.println("-"); break; // vol down
|
|
case 0xFF906F: Serial.println("+"); break; // vol up
|
|
case 0xFF9867: Serial.println("r"); break; // return
|
|
case 0xFFB04F: Serial.println("u"); break; // usb scan
|
|
case 0xFF6897: Serial.println("0"); break; // number 0
|
|
case 0xFF30CF: Serial.println("1"); break; // number 1
|
|
case 0xFF18E7: Serial.println("2"); break; // number 2
|
|
case 0xFF7A85: Serial.println("3"); break; // number 3
|
|
case 0xFF10EF: Serial.println("4"); break; // number 4
|
|
case 0xFF38C7: Serial.println("5"); break; // number 5
|
|
case 0xFF5AA5: Serial.println("6"); break; // number 6
|
|
case 0xFF42BD: Serial.println("7"); break; // number 7
|
|
case 0xFF4AB5: Serial.println("8"); break; // number 8
|
|
case 0xFF52AD: Serial.println("9"); break; // number 9
|
|
case 0xFFFFFFFF: Serial.println("R");break; // repeat
|
|
|
|
default:
|
|
Serial.println("Z"); // other button
|
|
}
|
|
|
|
// Do not get immediate repeat keypress
|
|
delay(500);
|
|
}
|
|
|
|
// serial receive
|
|
void SERrecv()
|
|
{
|
|
static boolean recvInProgress = false;
|
|
static byte ndx = 0;
|
|
char startMarker = '#';
|
|
char endMarker = '@';
|
|
char rc;
|
|
|
|
while (Serial.available() > 0 && SERnewData == false) {
|
|
// process serial signal
|
|
rc = Serial.read();
|
|
|
|
if(recvInProgress == true) {
|
|
if(rc != endMarker) {
|
|
SERrecvChars[ndx] = rc;
|
|
ndx++;
|
|
if (ndx >= SERnumChars) {
|
|
ndx = SERnumChars - 1;
|
|
}
|
|
}
|
|
else {
|
|
SERrecvChars[ndx] = '\0'; // terminate the string
|
|
recvInProgress = false;
|
|
ndx = 0;
|
|
SERnewData = true;
|
|
}
|
|
}
|
|
else if(rc == startMarker) {
|
|
recvInProgress = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// process new data
|
|
// data schema:
|
|
// 1st character denotes function:
|
|
// 1 - print on 1st line as is
|
|
// 2 - print on 2nd line as is
|
|
// - - display load on LCD matrix
|
|
// 0 - set a setting
|
|
void procNewData()
|
|
{
|
|
int line = 0;
|
|
|
|
if(SERnewData == true) {
|
|
if(DEBUG) {
|
|
Serial.print("Receiver says: ");
|
|
Serial.println(SERrecvChars);
|
|
}
|
|
|
|
// print to given line on LCD
|
|
if(SERrecvChars[0] != '-' && SERrecvChars[0] != '0') {
|
|
line = SERrecvChars[0] - '0';
|
|
LcdLd(line,true);
|
|
memmove(SERrecvChars, SERrecvChars+1, strlen(SERrecvChars));
|
|
LcdWrite(SERrecvChars);
|
|
}
|
|
|
|
// show load on LED matrix
|
|
if(SERrecvChars[0] == '-') {
|
|
memmove(SERrecvChars, SERrecvChars+1, strlen(SERrecvChars));
|
|
LedShowLoad(SERrecvChars);
|
|
}
|
|
|
|
// load new settings
|
|
if(SERrecvChars[0] == '0') {
|
|
memmove(SERrecvChars, SERrecvChars+1, strlen(SERrecvChars));
|
|
LoadSettings(SERrecvChars);
|
|
}
|
|
|
|
SERnewData = false;
|
|
}
|
|
}
|
|
|
|
// settings schema:
|
|
// LCDLength:LCDRows:LCDDelay:LEDBrightness:LEDDelay
|
|
// 16:2:5:1:3
|
|
void LoadSettings(char* mysettings)
|
|
{
|
|
LCDLength = atoi(strtok(mysettings, ":"));
|
|
LCDRows = atoi(strtok(NULL, ":"));
|
|
LCDDelay = atoi(strtok(NULL, ":"));
|
|
LEDBrightness = atoi(strtok(NULL, ":"));
|
|
LEDDelay = atoi(strtok(NULL, ":"));
|
|
|
|
LEDlc.setIntensity(0,LEDBrightness);
|
|
}
|
|
|
|
void setup (void)
|
|
{
|
|
// init HW
|
|
LcdSetup();
|
|
LEDSetup();
|
|
IRSetup();
|
|
|
|
// open serial port
|
|
Serial.begin(9600);
|
|
|
|
// display intros
|
|
LcdIntro();
|
|
LedIntro();
|
|
}
|
|
|
|
// main()
|
|
void loop (void)
|
|
{
|
|
// process IR signal
|
|
if(Irrecv.decode(&IRresults)) {
|
|
IRtranslate(); // process result
|
|
Irrecv.resume(); // receive the next command
|
|
}
|
|
|
|
// process serial input
|
|
SERrecv();
|
|
|
|
// process new data
|
|
procNewData();
|
|
}
|