You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
12 KiB
546 lines
12 KiB
#include "Bakalarka.h"
|
|
|
|
#include "prescaler.h"
|
|
#include <stdio.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <avr/eeprom.h>
|
|
//#include <avr/wdt.h>
|
|
|
|
|
|
#include "rotary.h"
|
|
Rotary encoder = Rotary(ENC_A, ENC_B);
|
|
|
|
#include "DS1302/DS1302.h"
|
|
DS1302 rtc(DS_RST, DS_IO, DS_SCK);
|
|
|
|
#include "SPI/SPI.h"
|
|
#include "MFRC522/MFRC522.h"
|
|
MFRC522 mfrc522(RFID_SS, RFID_RST);
|
|
|
|
#include "PCD8544/PCD8544.h"
|
|
PCD8544 lcd(LCD_DC, LCD_RST, LCD_SS);
|
|
|
|
#include <Fat16.h>
|
|
#include <Fat16util.h>
|
|
// store error strings in flash to save RAM
|
|
#define error(s) error_P(PSTR(s))
|
|
SdCard card;
|
|
Fat16 file;
|
|
|
|
#include "SerialCommand/SerialCommand.h"
|
|
SerialCommand SCmd;
|
|
|
|
|
|
|
|
volatile uint32_t lastDebounce;
|
|
volatile int8_t currentMenuItem = 0;
|
|
volatile boolean button = false, cursor = false, reading = false;
|
|
uint16_t lastRefresh;
|
|
static uint8_t storageIndex EEMEM;
|
|
|
|
|
|
|
|
Menu storage = {"Storage", list, 0};
|
|
Menu add_storage = {"New file", addStorage, 0};
|
|
Menu date_time = {"Date/time", frontPage, 0};
|
|
Menu exit_menu = {"Exit", frontPage, 0};
|
|
//Menu s1 = {"s1", list, 0};
|
|
//Menu s2 = {"s2", list, 0};
|
|
//Menu s3 = {"s3", list, 0};
|
|
//Menu s4 = {"s4", list, 0};
|
|
|
|
Menu mainMenu = { "", NULL, 4, { &storage, &add_storage, &date_time, &exit_menu} };
|
|
|
|
|
|
// bitmaps
|
|
const byte icon_usb[20] PROGMEM = {
|
|
0x08, 0x1C, 0x3E, 0x3E, 0x1C, 0x08, 0x0C, 0x0E, 0x0A, 0x1A,
|
|
0x3B, 0x6B, 0x4B, 0xE8, 0xE8, 0xE8, 0x08, 0x3E, 0x1C, 0x08
|
|
};
|
|
|
|
const byte icon_battery_100[21] PROGMEM = {
|
|
0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81,
|
|
0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xFF, 0x3C, 0x3C
|
|
};
|
|
|
|
const byte icon_battery_75[21] PROGMEM = {
|
|
0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81,
|
|
0xBD, 0xBD, 0xBD, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x3C, 0x3C
|
|
};
|
|
|
|
const byte icon_battery_50[21] PROGMEM = {
|
|
0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81,
|
|
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x3C, 0x3C
|
|
};
|
|
|
|
const byte icon_battery_25[21] PROGMEM = {
|
|
0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0x81, 0x81, 0x81, 0x81,
|
|
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x3C, 0x3C
|
|
};
|
|
|
|
const byte icon_arrow[6] PROGMEM = {
|
|
0x00, 0xFE, 0x7C, 0x38, 0x10, 0x00
|
|
};
|
|
|
|
void setup(void) {
|
|
// There is 16HMz crystal used, halve it to reliable 8MHz@3V3
|
|
setClockPrescaler(CLOCK_PRESCALER_2);
|
|
|
|
// The fastest reliable baud rate is 38400
|
|
Serial.begin(38400);
|
|
|
|
lcd.begin();
|
|
lcd.clear();
|
|
|
|
// Rotary interrupt pin change enable
|
|
PCICR |= (1 << PCIE1);
|
|
PCMSK1 |= (1 << PCINT8) | (1 << PCINT9);
|
|
|
|
// RFID host interrupt request
|
|
attachInterrupt(0, RFIDInterrupt, FALLING);
|
|
// Encoder button interrupt handle
|
|
attachInterrupt(1, debounceInterrupt, FALLING);
|
|
// Encoder button pull-up
|
|
pinMode(BTN, INPUT_PULLUP);
|
|
|
|
|
|
SPI.begin(); // Init SPI bus
|
|
mfrc522.PCD_Init(); // Init MFRC522 card
|
|
|
|
if (!card.init(0, SD_SS))
|
|
error("card.init failed!");
|
|
|
|
if (!Fat16::init(&card))
|
|
error("Fat16::init failed!");
|
|
|
|
pinMode(SPKR, OUTPUT);
|
|
pinMode(VBAT_ADC_EN, OUTPUT);
|
|
|
|
// wdt_enable(WDTO_8S);
|
|
|
|
frontPage();
|
|
|
|
SCmd.addCommand("ls", ls);
|
|
SCmd.addCommand("touch", touch);
|
|
SCmd.addCommand("rm", rm);
|
|
// SCmd.addCommand("HELLO", SayHello); // Echos the string argument back
|
|
// SCmd.addCommand("P", process_command); // Converts two arguments to integers and echos them back
|
|
SCmd.addDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
|
|
|
|
sei();
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop(void) {
|
|
// wdt_reset();
|
|
// tone(SPKR, 1000, 100);
|
|
// topBar();
|
|
SCmd.readSerial();
|
|
|
|
if (millis() - lastRefresh > 1000) {
|
|
frontPage();
|
|
lastRefresh = millis();
|
|
}
|
|
|
|
|
|
if (button) {
|
|
button = false;
|
|
navigateMenu(&mainMenu);
|
|
return;
|
|
// uint8_t menuLenght = sizeof(menuItems) / sizeof(menuItems[0]);
|
|
// if (currentMenuItem >= menuLenght)
|
|
// currentMenuItem = 0;
|
|
// else if (currentMenuItem < 0)
|
|
// currentMenuItem = menuLenght - 1;
|
|
// lcd.setCursor(0, 0);
|
|
// uint8_t i = 0, offset = 0;
|
|
// if (currentMenuItem >= PCD8544_LINES)
|
|
// offset = currentMenuItem - PCD8544_LINES + 1;
|
|
// for (i = offset; i < menuLenght && i < PCD8544_LINES + offset; i++) {
|
|
// if (i == (uint8_t)currentMenuItem)
|
|
// lcd.bitmap(icon_arrow, 1, 6);
|
|
// else
|
|
// lcd.print(' ');
|
|
// lcd.print(menuItems[i]);
|
|
// lcd.clearRestOfLine();
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
// delay(1000);
|
|
// lcd.clear();
|
|
|
|
// topBar();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// delay(1000);
|
|
|
|
// if (Serial.available()) {
|
|
// Fat16::ls(LS_DATE | LS_SIZE);
|
|
// while(Serial.available())
|
|
// Serial.read();
|
|
// }
|
|
|
|
if (reading && mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
|
|
// // Look for new cards
|
|
// if ( ! mfrc522.PICC_IsNewCardPresent())
|
|
// return;
|
|
//
|
|
//
|
|
// // Select one of the cards
|
|
// if ( ! mfrc522.PICC_ReadCardSerial())
|
|
// return;
|
|
|
|
tone(SPKR, 5500, 70);
|
|
|
|
delay(1500);
|
|
reading = false;
|
|
|
|
if (file.open(eeprom_read_byte(&storageIndex), O_APPEND | O_WRITE)) {
|
|
Time t = rtc.time();
|
|
char buf[21];
|
|
snprintf(buf, sizeof buf, "%04d-%02d-%02d %02d:%02d:%02d;", t.yr, t.mon, t.day, t.hr, t.min, t.sec);
|
|
file.print(buf);
|
|
for (byte i = 0; i < mfrc522.uid.size; i++) {
|
|
file.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
|
|
file.print(mfrc522.uid.uidByte[i], HEX);
|
|
}
|
|
file.println(";");
|
|
file.close();
|
|
}
|
|
|
|
// lcd.setCursor(0, 1);
|
|
// for (byte i = 0; i < mfrc522.uid.size; i++) {
|
|
// lcd.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
|
|
// lcd.print(mfrc522.uid.uidByte[i], HEX);
|
|
// }
|
|
// lcd.clearRestOfLine();
|
|
//// Dump debug info about the card. PICC_HaltA() is automatically called.
|
|
// mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
|
|
}
|
|
|
|
delay(100);
|
|
|
|
}
|
|
|
|
ISR(PCINT1_vect) {
|
|
cursor = encoder.process();
|
|
if (cursor == DIR_CW)
|
|
currentMenuItem++;
|
|
else if (cursor == DIR_CCW)
|
|
currentMenuItem--;
|
|
|
|
}
|
|
|
|
void debounceInterrupt(void) {
|
|
if ((long) (micros() - lastDebounce) >= (long)DEBOUNCE * 1000) {
|
|
button = true;
|
|
lastDebounce = micros();
|
|
}
|
|
}
|
|
|
|
void RFIDInterrupt(void) {
|
|
if (!reading)
|
|
reading = true;
|
|
}
|
|
|
|
void error_P(const char* str) {
|
|
lcd.setCursor(1, 4);
|
|
lcd.print(PSTR("ERROR"));
|
|
PgmPrint("error: ");
|
|
SerialPrintln_P(str);
|
|
if (card.errorCode) {
|
|
PgmPrint("SD error: ");
|
|
Serial.println(card.errorCode, HEX);
|
|
}
|
|
while (1)
|
|
;
|
|
}
|
|
|
|
|
|
|
|
|
|
void navigateMenu(Menu *menu) {
|
|
if (menu->numSubmenus == 0) {
|
|
/* Execute the command */
|
|
lcd.clear();
|
|
menu->command();
|
|
return;
|
|
}
|
|
/* This is a menu, not a command, so show the menu and get the user's choice */
|
|
displayMenu(menu);
|
|
if (button) {
|
|
button = false;
|
|
menu = menu->submenu[currentMenuItem];
|
|
}
|
|
// menu = get_user_selection(menu);
|
|
navigateMenu(menu);
|
|
}
|
|
|
|
void displayMenu(Menu *menu) {
|
|
// uint8_t menuLenght = sizeof(menuItems) / sizeof(menuItems[0]);
|
|
if (currentMenuItem >= menu->numSubmenus)
|
|
currentMenuItem = 0;
|
|
else if (currentMenuItem < 0)
|
|
currentMenuItem = menu->numSubmenus - 1;
|
|
lcd.setCursor(0, 0);
|
|
uint8_t i = 0, offset = 0;
|
|
if (currentMenuItem >= PCD8544_LINES)
|
|
offset = currentMenuItem - PCD8544_LINES + 1;
|
|
for (i = offset; i < PCD8544_LINES + offset; i++) {
|
|
// clear rest of the menu screen if there are less items than LCD lines
|
|
if (i >= menu->numSubmenus) {
|
|
lcd.print(' ');
|
|
lcd.clearRestOfLine();
|
|
continue;
|
|
}
|
|
|
|
if (i == (uint8_t)currentMenuItem)
|
|
lcd.bitmap(icon_arrow, 1, 6);
|
|
else
|
|
lcd.print(' ');
|
|
lcd.print(menu->submenu[i]->title);
|
|
lcd.clearRestOfLine();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void list(void) {
|
|
cursor = DIR_CW;
|
|
while (!button) {
|
|
if (cursor) {
|
|
lcd.setCursor(0, 0);
|
|
dir_t d;
|
|
uint16_t i = 0;
|
|
boolean found = false;
|
|
uint8_t offset = 0;
|
|
if (currentMenuItem >= PCD8544_LINES)
|
|
offset = currentMenuItem - PCD8544_LINES + 1;
|
|
for (i = offset; Fat16::readDir(&d, &i, DIR_ATT_VOLUME_ID); i++) {
|
|
// if we are over the display, just seek the last file index
|
|
if (i >= (uint16_t)(PCD8544_LINES + offset))
|
|
continue;
|
|
if (i == (uint16_t) currentMenuItem) {
|
|
lcd.bitmap(icon_arrow, 1, 6);
|
|
found = true;
|
|
}
|
|
else
|
|
lcd.print(' ');
|
|
|
|
lcd.print(i);
|
|
lcd.print(':');
|
|
printDirName(d);
|
|
}
|
|
|
|
Serial.print("a: ");
|
|
if (!found) {
|
|
Serial.print("NOTfound, ");
|
|
if (cursor == DIR_CW) {
|
|
currentMenuItem++;
|
|
Serial.print("++");
|
|
} else if (cursor == DIR_CCW) {
|
|
currentMenuItem--;
|
|
Serial.print("--");
|
|
}
|
|
}
|
|
else
|
|
// this means that the screen will be refreshed
|
|
cursor = false;
|
|
|
|
|
|
if (currentMenuItem < 0) {
|
|
currentMenuItem = i - 1;
|
|
cursor = DIR_CCW;
|
|
Serial.print("i-1");
|
|
}
|
|
if ((uint16_t) currentMenuItem > i - 1) {
|
|
currentMenuItem = 0;
|
|
Serial.print("0");
|
|
cursor = DIR_CW;
|
|
}
|
|
|
|
Serial.print("; cmi: ");
|
|
Serial.print(currentMenuItem);
|
|
Serial.print(", li: ");
|
|
Serial.print(i);
|
|
Serial.print(", cursor: ");
|
|
Serial.print(cursor);
|
|
Serial.print(", button: ");
|
|
Serial.println(button);
|
|
|
|
}
|
|
}
|
|
|
|
Serial.println("Saved");
|
|
|
|
// save index of a file to store into to the EEPROM memory
|
|
eeprom_write_byte(&storageIndex, currentMenuItem);
|
|
button = false;
|
|
lcd.clear();
|
|
frontPage();
|
|
}
|
|
|
|
|
|
void addStorage(void) {
|
|
// file.writeError = false;
|
|
char buf[14];
|
|
for (unsigned short int i = 1; ; i++) {
|
|
snprintf(buf, sizeof buf, "file%d.csv", i);
|
|
if (file.open(buf, O_CREAT | O_WRITE | O_EXCL))
|
|
break;
|
|
}
|
|
file.close();
|
|
frontPage();
|
|
// snprintf(buf, sizeof buf, "FILE%d.CSV", 1);
|
|
|
|
}
|
|
|
|
|
|
void frontPage(void) {
|
|
|
|
// lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
// tone(SPKR, 5000, 50);
|
|
// USB plugged in
|
|
if (analogRead(STAT) < 512) {
|
|
lcd.bitmap(icon_usb, 1, 20);
|
|
}
|
|
// battery
|
|
else {
|
|
digitalWrite(VBAT_ADC_EN, LOW);
|
|
uint8_t vbat = (analogRead(VBAT_ADC) - 523) / 5;
|
|
if (vbat < 25)
|
|
lcd.bitmap(icon_battery_25, 1, 21);
|
|
else if (vbat < 50)
|
|
lcd.bitmap(icon_battery_50, 1, 21);
|
|
else if (vbat < 75)
|
|
lcd.bitmap(icon_battery_75, 1, 21);
|
|
else
|
|
lcd.bitmap(icon_battery_100, 1, 21);
|
|
digitalWrite(VBAT_ADC_EN, HIGH);
|
|
|
|
}
|
|
|
|
// Get the current time and date from the chip.
|
|
Time t = rtc.time();
|
|
|
|
char buf[24];
|
|
lcd.setCursor(9, 0);
|
|
snprintf(buf, sizeof(buf), "%02d:%02d", t.hr, t.min);
|
|
lcd.print(buf);
|
|
|
|
uint16_t i = eeprom_read_byte(&storageIndex);
|
|
dir_t d;
|
|
if (Fat16::readDir(&d, &i, DIR_ATT_VOLUME_ID)) {
|
|
lcd.setCursor(0, 2);
|
|
lcd.println("Saving to: ");
|
|
printDirName(d);
|
|
}
|
|
|
|
}
|
|
|
|
void printDirName(const dir_t& dir) {
|
|
uint8_t w = 0;
|
|
for (uint8_t i = 0; i < 11; i++) {
|
|
if (dir.name[i] == ' ')
|
|
continue;
|
|
if (i == 8) {
|
|
lcd.print('.');
|
|
w++;
|
|
}
|
|
lcd.print((char) dir.name[i]);
|
|
w++;
|
|
}
|
|
lcd.clearRestOfLine();
|
|
}
|
|
//
|
|
//void SayHello() {
|
|
// char *arg;
|
|
// arg = SCmd.next(); // Get the next argument from the SerialCommand object buffer
|
|
// if (arg != NULL) // As long as it existed, take it
|
|
// {
|
|
// Serial.print("Hello ");
|
|
// Serial.println(arg);
|
|
// } else {
|
|
// Serial.println("Hello, whoever you are");
|
|
// }
|
|
//}
|
|
//
|
|
//void process_command() {
|
|
// int aNumber;
|
|
// char *arg;
|
|
//
|
|
// Serial.println("We're in process_command");
|
|
// arg = SCmd.next();
|
|
// if (arg != NULL) {
|
|
// aNumber = atoi(arg); // Converts a char string to an integer
|
|
// Serial.print("First argument was: ");
|
|
// Serial.println(aNumber);
|
|
// } else {
|
|
// Serial.println("No arguments");
|
|
// }
|
|
//
|
|
// arg = SCmd.next();
|
|
// if (arg != NULL) {
|
|
// aNumber = atol(arg);
|
|
// Serial.print("Second argument was: ");
|
|
// Serial.println(aNumber);
|
|
// } else {
|
|
// Serial.println("No second argument");
|
|
// }
|
|
//
|
|
//}
|
|
|
|
|
|
|
|
void ls() {
|
|
Fat16::ls();
|
|
}
|
|
|
|
void touch() {
|
|
char *arg, fileName[14];
|
|
arg = SCmd.next();
|
|
snprintf(fileName, sizeof fileName, "%s.csv", arg);
|
|
PgmPrint("File '");
|
|
Serial.print(fileName);
|
|
if (!file.open(fileName, O_CREAT | O_WRITE))
|
|
PgmPrintln("' could not be created.");
|
|
else
|
|
PgmPrintln("' was created successfully");
|
|
|
|
}
|
|
|
|
void rm() {
|
|
char *arg, fileName[14];
|
|
arg = SCmd.next();
|
|
snprintf(fileName, sizeof fileName, "%s.csv", arg);
|
|
PgmPrint("File '");
|
|
Serial.print(fileName);
|
|
if (file.open(fileName, O_WRITE) && file.remove())
|
|
PgmPrintln("' was removed successfully");
|
|
else
|
|
PgmPrintln("' could not be removed.");
|
|
|
|
}
|
|
|
|
void mv() {
|
|
char *arg;
|
|
}
|
|
|
|
|
|
// This gets set as the default handler, and gets called when no other command matches.
|
|
void unrecognized() {
|
|
PgmPrintln("Unrecognized command. Possibilities are: ");
|
|
PgmPrintln("ls\t\t\t- display files");
|
|
PgmPrintln("touch FILE\t\t- create file FILE.csv");
|
|
Serial.println();
|
|
}
|
|
|