Merge pull request #36 from Elfish/main

Various changes to support AWTRIX 2 hardware
This commit is contained in:
Stephan Mühl
2023-04-03 12:40:46 +02:00
committed by GitHub
14 changed files with 230 additions and 138 deletions

View File

@@ -9,16 +9,17 @@ With HTTP, make GET request to `http://[IP]/api/stats`
## Update
Awtrix searches for an update every 1 Hour. If a new one is found it will be published to HA and in the stats.
You can start the update with update button in HA or:
| Topic | URL | Payload/Body | HTTP Header | HTTP method |
| --- | --- | --- |--- |--- |
| Topic | URL | Payload/Body | HTTP Header | HTTP method |
| --- | --- | --- | --- | --- |
| `[PREFIX]/doupdate` |`http://[IP]/api/doupdate` | JSON | empty payload/body | POST |
## Add custom app
create custom apps or notifications to display your own text and icons.
Have a look at [this section](custom?id=custom-apps-and-notifications)
| Topic | URL | Payload/Body | HTTP Header | HTTP method |
| --- | --- | --- |--- |--- |
| Topic | URL | Payload/Body | Query parameters | HTTP method |
| --- | --- | --- | --- | --- |
| `[PREFIX]/custom/[appname]` |`http://[IP]/api/custom` | JSON | name = [appname] | POST |
## Dismiss Notification

View File

@@ -26,6 +26,7 @@ lib_deps =
plerup/EspSoftwareSerial@^8.0.1
h2zero/NimBLE-Arduino@^1.4.1
[env:awtrix_upgrade]
platform = https://github.com/platformio/platform-espressif32.git
board = wemos_d1_mini32
@@ -36,6 +37,7 @@ framework = arduino
build_flags = -DAWTRIX_UPGRADE -D MQTT_MAX_PACKET_SIZE=1024
lib_deps =
adafruit/Adafruit BME280 Library@^2.2.2
plerup/EspSoftwareSerial@^8.0.1
bblanchon/ArduinoJson@^6.20.0
evert-arias/EasyButton@^2.0.1
fastled/FastLED@^3.5.0

View File

@@ -251,7 +251,13 @@ void AlarmApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state)
if (ALARM_SOUND != "")
{
if (!PeripheryManager.isPlaying())
{
#ifdef ULANZI
PeripheryManager.playFromFile("/MELODIES/" + ALARM_SOUND + ".txt");
#else
PeripheryManager.playFromFile(DFMINI_MP3_ALARM);
#endif
}
}
}
}
@@ -271,7 +277,13 @@ void TimerApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state)
if (TIMER_SOUND != "")
{
if (!PeripheryManager.isPlaying())
{
#ifdef ULANZI
PeripheryManager.playFromFile("/MELODIES/" + TIMER_SOUND + ".txt");
#else
PeripheryManager.playFromFile(DFMINI_MP3_TIMER);
#endif
}
}
}
}

View File

@@ -106,8 +106,10 @@ const char HAramClass[] PROGMEM = {"data_size"};
const char HAramUnit[] PROGMEM = {"B"};
// JSON properites
#ifdef ULANZI
const char BatKey[] PROGMEM = {"bat"};
const char BatRawKey[] PROGMEM = {"bat_raw"};
#endif
const char LuxKey[] PROGMEM = {"lux"};
const char LDRRawKey[] PROGMEM = {"ldr_raw"};
const char BrightnessKey[] PROGMEM = {"bri"};

View File

@@ -106,8 +106,10 @@ extern const char HAramClass[];
extern const char HAramUnit[];
// JSON properites
#ifdef ULANZI
extern const char BatKey[];
extern const char BatRawKey[];
#endif
extern const char LuxKey[];
extern const char LDRRawKey[];
extern const char BrightnessKey[];

View File

@@ -30,7 +30,12 @@ fs::File gifFile;
GifPlayer gif;
bool showGif;
CRGB leds[MATRIX_WIDTH * MATRIX_HEIGHT];
// Awtrix Big / Ulanzi
FastLED_NeoMatrix matrix(leds, 32, 8, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG);
// Awtrid Midi
//FastLED_NeoMatrix matrix(leds, 8, 8, 4, 1, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE);
// Awtrix Mini?
//FastLED_NeoMatrix(leds, 32, 8, NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG);
MatrixDisplayUi ui(&matrix);
@@ -271,7 +276,11 @@ void DisplayManager_::generateCustomPage(String name, const char *json)
if (doc.containsKey("sound"))
{
#ifdef ULANZI
customApp.sound = ("/" + doc["sound"].as<String>() + ".txt");
#else
customApp.sound = doc["sound"].as<String>();
#endif
}
else
{
@@ -390,7 +399,11 @@ void DisplayManager_::generateNotification(const char *json)
if (doc.containsKey("sound"))
{
#ifdef ULANZI
PeripheryManager.playFromFile("/MELODIES/" + doc["sound"].as<String>() + ".txt");
#else
PeripheryManager.playFromFile(doc["sound"].as<String>());
#endif
}
if (doc.containsKey("bar"))
@@ -832,17 +845,13 @@ void DisplayManager_::updateAppVector(const char *json)
callback = HumApp;
SHOW_HUM = show;
}
#ifdef ULANZI
else if (name == "bat")
{
callback = BatApp;
SHOW_BAT = show;
}
#endif
else
{
// If the app is not one of the built-in apps, check if it's already in the vector

View File

@@ -19,7 +19,9 @@ void startLittleFS()
{
if (LittleFS.begin())
{
#ifdef ULANZI
LittleFS.mkdir("/MELODIES");
#endif
LittleFS.mkdir("/ICONS");
}
else
@@ -32,31 +34,30 @@ void startLittleFS()
void loadDevSettings()
{
Serial.println("laodSettings");
File file = LittleFS.open("/dev.json", "r");
if (!file)
Serial.println("loadSettings");
if (LittleFS.exists("/dev.json"))
{
return;
}
DynamicJsonDocument doc(128);
DeserializationError error = deserializeJson(doc, file);
if (error)
{
Serial.println(F("Failed to read dev settings"));
return;
}
File file = LittleFS.open("/dev.json", "r");
DynamicJsonDocument doc(128);
DeserializationError error = deserializeJson(doc, file);
if (error)
{
Serial.println(F("Failed to read dev settings"));
return;
}
if (doc.containsKey("bootsound"))
{
BOOT_SOUND = doc["bootsound"].as<String>();
}
if (doc.containsKey("bootsound"))
{
BOOT_SOUND = doc["bootsound"].as<String>();
}
if (doc.containsKey("bootsound"))
{
UPPERCASE_LETTERS = doc["uppercase"].as<bool>();
}
if (doc.containsKey("bootsound"))
{
UPPERCASE_LETTERS = doc["uppercase"].as<bool>();
}
file.close();
file.close();
}
}
void loadSettings()
@@ -83,6 +84,11 @@ void loadSettings()
SHOW_BAT = Settings.getBool("BAT", true);
#endif
SOUND_ACTIVE = Settings.getBool("SOUND", true);
#ifndef ULANZI
//Settings.putUInt("VOL", VOLUME_PERCENT);
VOLUME_PERCENT = Settings.getUInt("VOL", 50);
VOLUME = map(VOLUME_PERCENT, 0, 100, 0, 30);
#endif
Settings.end();
uniqueID = getID();
MQTT_PREFIX = String(uniqueID);
@@ -112,6 +118,9 @@ void saveSettings()
Settings.putBool("BAT", SHOW_BAT);
#endif
Settings.putBool("SOUND", SOUND_ACTIVE);
#ifndef ULANZI
Settings.putUInt("VOL", VOLUME_PERCENT);
#endif
Settings.end();
}
@@ -185,7 +194,7 @@ bool ALARM_ACTIVE;
uint16_t TEXTCOLOR_565 = 0xFFFF;
bool SOUND_ACTIVE;
String BOOT_SOUND = "";
uint8_t VOLUME;
uint8_t VOLUME_PERCENT;
uint8_t VOLUME;
int MATRIX_LAYOUT;
bool UPDATE_AVAILABLE = false;

View File

@@ -68,8 +68,8 @@ extern bool START_ON_MONDAY;
extern bool IS_CELSIUS;
extern bool SOUND_ACTIVE;
extern String BOOT_SOUND;
extern uint8_t VOLUME;
extern uint8_t VOLUME_PERCENT;
extern uint8_t VOLUME;
extern int MATRIX_LAYOUT;
extern bool UPDATE_AVAILABLE;
void loadSettings();

View File

@@ -360,16 +360,16 @@ void MQTTManager_::setup()
humidity->setName(HAhumName);
humidity->setDeviceClass(HAhumClass);
humidity->setUnitOfMeasurement(HAhumUnit);
#ifdef ULANZI
sprintf(batID, HAbatID, macStr);
battery = new HASensor(batID);
battery->setIcon(HAbatIcon);
battery->setName(HAbatName);
battery->setDeviceClass(HAbatClass);
battery->setUnitOfMeasurement(HAbatUnit);
#endif
#endif
sprintf(luxID, HAluxID, macStr);
illuminance = new HASensor(luxID);
illuminance->setIcon(HAluxIcon);
@@ -458,8 +458,8 @@ void MQTTManager_::sendStats()
#ifdef ULANZI
snprintf(buffer, 5, "%d", BATTERY_PERCENT);
battery->setValue(buffer);
#endif
#endif
snprintf(buffer, 5, "%.0f", CURRENT_TEMP);
temperature->setValue(buffer);

View File

@@ -28,8 +28,12 @@ enum MenuState
WeekdayMenu,
TempMenu,
Appmenu,
#ifdef ULANZI
SoundMenu
#else
SoundMenu,
VolumeMenu
#endif
};
const char *menuItems[] PROGMEM = {
@@ -45,10 +49,17 @@ const char *menuItems[] PROGMEM = {
"TEMP",
"APPS",
"SOUND",
#ifndef ULANZI
"VOLUME" ,
#endif
"UPDATE"};
int8_t menuIndex = 0;
#ifdef ULANZI
uint8_t menuItemCount = 13;
#else
uint8_t menuItemCount = 14;
#endif
const char *timeFormat[] PROGMEM = {
"%H:%M:%S",
@@ -193,6 +204,10 @@ String MenuManager_::menutext()
break;
}
break;
#ifndef ULANZI
case VolumeMenu:
return String(VOLUME_PERCENT) + "%";
#endif
default:
break;
}
@@ -250,10 +265,13 @@ void MenuManager_::rightButton()
case TempMenu:
IS_CELSIUS = !IS_CELSIUS;
break;
#ifndef ULANZI
case VolumeMenu:
VOLUME_PERCENT = (VOLUME_PERCENT % 100) + 1;
VOLUME = map(VOLUME_PERCENT, 0, 100, 0, 30);
PeripheryManager.setVolume(VOLUME);
if ((VOLUME_PERCENT + 1) > 100)
VOLUME_PERCENT = 0;
else
VOLUME_PERCENT++;
#endif
default:
break;
}
@@ -314,10 +332,13 @@ void MenuManager_::leftButton()
case SoundMenu:
SOUND_ACTIVE = !SOUND_ACTIVE;
break;
#ifndef ULANZI
case VolumeMenu:
VOLUME_PERCENT = (VOLUME_PERCENT % 100) + 1;
VOLUME = map(VOLUME_PERCENT, 0, 100, 0, 30);
PeripheryManager.setVolume(VOLUME);
if ((VOLUME_PERCENT - 1) < 0)
VOLUME_PERCENT = 100;
else
VOLUME_PERCENT--;
#endif
default:
break;
}
@@ -372,11 +393,10 @@ void MenuManager_::selectButton()
currentState = SoundMenu;
break;
case 12:
#ifdef AWTRIX_UPGRADE
#ifndef ULANZI
currentState = VolumeMenu;
break;
break;
#endif
case 13:
if (UpdateManager.checkUpdate(true))
{
@@ -454,12 +474,6 @@ void MenuManager_::selectButtonLong()
DisplayManager.applyAllSettings();
saveSettings();
break;
case VolumeMenu:
#ifdef AWTRIX_UPGRADE
VOLUME = map(VOLUME_PERCENT, 0, 100, 0, 30);
saveSettings();
#endif
break;
case TimeFormatMenu:
TIME_FORMAT = timeFormat[timeFormatIndex];
saveSettings();
@@ -476,6 +490,13 @@ void MenuManager_::selectButtonLong()
DisplayManager.loadNativeApps();
saveSettings();
break;
#ifndef ULANZI
case VolumeMenu:
VOLUME = map(VOLUME_PERCENT, 0, 100, 0, 30);
PeripheryManager.setVolume(VOLUME);
saveSettings();
break;
#endif
default:
break;
}

View File

@@ -1,6 +1,13 @@
#include <PeripheryManager.h>
#ifdef ULANZI
#include <melody_player.h>
#include <melody_factory.h>
#include "Adafruit_SHT31.h"
#else
#include "Adafruit_BME280.h"
#include "SoftwareSerial.h"
#include <DFMiniMp3.h>
#endif
#include "Globals.h"
#include "DisplayManager.h"
#include "MQTTManager.h"
@@ -17,32 +24,37 @@
#define BUTTON_UP_PIN 26
#define BUTTON_DOWN_PIN 14
#define BUTTON_SELECT_PIN 27
#define I2C_SCL_PIN 22
#define I2C_SDA_PIN 21
#else
// Pinouts für das WEMOS_D1_MINI32-Environment
#define BUZZER_PIN -1
#define LDR_PIN A0
#define BUTTON_UP_PIN D0
#define BUTTON_DOWN_PIN D4
#define BUTTON_SELECT_PIN D8
#define BUTTON_DOWN_PIN D8
#define BUTTON_SELECT_PIN D4
#define DFPLAYER_RX D7
#define DFPLAYER_TX D5
#define I2C_SCL_PIN D1
#define I2C_SDA_PIN D3
#endif
#ifdef ULANZI
Adafruit_SHT31 sht31;
#else
class Mp3Notify
{
};
Adafruit_BME280 bme280;
SoftwareSerial mySoftwareSerial(D7, D5); // RX, TX
typedef DFMiniMp3<SoftwareSerial, Mp3Notify> DfMp3;
DfMp3 dfmp3(mySoftwareSerial);
TwoWire I2Cbme280 = TwoWire(0);
#endif
EasyButton button_left(BUTTON_UP_PIN);
EasyButton button_right(BUTTON_DOWN_PIN);
EasyButton button_select(BUTTON_SELECT_PIN);
#ifdef ULANZI
MelodyPlayer player(BUZZER_PIN, LOW);
#else
class Mp3Notify{};
SoftwareSerial mySoftwareSerial(DFPLAYER_RX, DFPLAYER_TX); // RX, TX
DFMiniMp3<SoftwareSerial, Mp3Notify> dfmp3(mySoftwareSerial);
#endif
#define USED_PHOTOCELL LightDependentResistor::GL5516
@@ -67,11 +79,6 @@ float sampleSum = 0.0;
float sampleAverage = 0.0;
float brightnessPercent = 0.0;
#ifdef awrtrix_upgrade
class Mp3Notify;
SoftwareSerial mySoftwareSerial(D7, D5); // RX, TX
#endif
// The getter for the instantiated singleton instance
PeripheryManager_ &PeripheryManager_::getInstance()
{
@@ -84,6 +91,7 @@ PeripheryManager_ &PeripheryManager = PeripheryManager.getInstance();
void left_button_pressed()
{
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
if (AP_MODE)
{
--MATRIX_LAYOUT;
@@ -101,6 +109,7 @@ void left_button_pressed()
void right_button_pressed()
{
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
if (AP_MODE)
{
++MATRIX_LAYOUT;
@@ -118,18 +127,21 @@ void right_button_pressed()
void select_button_pressed()
{
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
DisplayManager.selectButton();
MenuManager.selectButton();
}
void select_button_pressed_long()
{
PeripheryManager.playFromFile(DFMINI_MP3_CLICK);
DisplayManager.selectButtonLong();
MenuManager.selectButtonLong();
}
void select_button_tripple()
{
PeripheryManager.playFromFile(DFMINI_MP3_CLICK_ON);
if (MATRIX_OFF)
{
DisplayManager.MatrixState(true);
@@ -147,13 +159,14 @@ void PeripheryManager_::playBootSound()
if (BOOT_SOUND == "")
{
#ifdef ULANZI
// no standard sound
const int nNotes = 6;
String notes[nNotes] = {"E5", "C5", "G4", "E4", "G4", "C5"};
const int timeUnit = 150;
Melody melody = MelodyFactory.load("Bootsound", timeUnit, notes, nNotes);
player.playAsync(melody);
#else
// no standardsound
playFromFile(DFMINI_MP3_BOOT);
#endif
}
else
@@ -161,7 +174,7 @@ void PeripheryManager_::playBootSound()
#ifdef ULANZI
playFromFile("/MELODIES/" + BOOT_SOUND + ".txt");
#else
dfmp3.playMp3FolderTrack(BOOT_SOUND.toInt());
playFromFile(BOOT_SOUND);
#endif
}
}
@@ -171,16 +184,20 @@ void PeripheryManager_::stopSound()
#ifdef ULANZI
player.stop();
#else
dfmp3.stop();
dfmp3.stopAdvertisement();
delay(50);
dfmp3.stop();
#endif
}
#ifndef ULANZI
void PeripheryManager_::setVolume(uint8_t vol)
{
#ifdef AWTRIX_UPGRADE
uint8_t curVolume = dfmp3.getVolume(); // need to read volume in order to work. Donno why! :(
dfmp3.setVolume(vol);
#endif
delay(50);
}
#endif
void PeripheryManager_::playFromFile(String file)
{
@@ -199,11 +216,14 @@ bool PeripheryManager_::isPlaying()
#ifdef ULANZI
return player.isPlaying();
#else
return false;
if ((dfmp3.getStatus() & 0xff) == 0x01) // 0x01 = DfMp3_StatusState_Playing
return true;
else
return false;
#endif
}
void fistStart()
void firstStart()
{
#ifdef ULANZI
uint16_t ADCVALUE = analogRead(BATTERY_PIN);
@@ -226,8 +246,14 @@ void PeripheryManager_::setup()
{
startTime = millis();
pinMode(LDR_PIN, INPUT);
#ifdef ULANZI
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
#else
dfmp3.begin();
delay(100);
setVolume(VOLUME);
#endif
button_left.begin();
button_right.begin();
button_select.begin();
@@ -236,20 +262,21 @@ void PeripheryManager_::setup()
button_select.onPressed(select_button_pressed);
button_select.onPressedFor(1000, select_button_pressed_long);
button_select.onSequence(2, 300, select_button_tripple);
Wire.begin(21, 22);
#ifdef ULANZI
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
sht31.begin(0x44);
#else
bme280.begin();
I2Cbme280.begin(I2C_SDA_PIN, I2C_SCL_PIN);
bme280.begin(0x76, &I2Cbme280);
dfmp3.begin();
#endif
photocell.setPhotocellPositionOnGround(false);
fistStart();
firstStart();
}
void PeripheryManager_::tick()
{
MQTTManager.sendButton(0, button_left.read());
MQTTManager.sendButton(1, button_select.read());
MQTTManager.sendButton(2, button_right.read());
@@ -302,61 +329,59 @@ time_t lastAlarmTime = 0;
void PeripheryManager_::checkAlarms()
{
File file = LittleFS.open("/alarms.json", "r");
if (!file)
if (LittleFS.exists("/alarms.json"))
{
return;
}
DynamicJsonDocument doc(file.size() * 1.33);
DeserializationError error = deserializeJson(doc, file);
if (error)
{
Serial.println(F("Failed to read Alarm file"));
return;
}
JsonArray alarms = doc["alarms"];
file.close();
time_t now1 = time(nullptr);
struct tm *timeInfo;
timeInfo = localtime(&now1);
int currentHour = timeInfo->tm_hour;
int currentMinute = timeInfo->tm_min;
int currentDay = timeInfo->tm_wday - 1;
for (JsonObject alarm : alarms)
{
int alarmHour = alarm["hour"];
int alarmMinute = alarm["minute"];
String alarmDays = alarm["days"];
if (currentHour == alarmHour && currentMinute == alarmMinute && alarmDays.indexOf(String(currentDay)) != -1)
File file = LittleFS.open("/alarms.json", "r");
DynamicJsonDocument doc(file.size() * 1.33);
DeserializationError error = deserializeJson(doc, file);
if (error)
{
if (difftime(now1, lastAlarmTime) < MIN_ALARM_INTERVAL)
{
return;
}
Serial.println(F("Failed to read Alarm file"));
return;
}
JsonArray alarms = doc["alarms"];
file.close();
ALARM_ACTIVE = true;
lastAlarmTime = now1;
time_t now1 = time(nullptr);
struct tm *timeInfo;
timeInfo = localtime(&now1);
int currentHour = timeInfo->tm_hour;
int currentMinute = timeInfo->tm_min;
int currentDay = timeInfo->tm_wday - 1;
if (alarm.containsKey("sound"))
{
ALARM_SOUND = alarm["sound"].as<String>();
}
else
{
ALARM_SOUND = "";
}
for (JsonObject alarm : alarms)
{
int alarmHour = alarm["hour"];
int alarmMinute = alarm["minute"];
String alarmDays = alarm["days"];
if (alarm.containsKey("snooze"))
if (currentHour == alarmHour && currentMinute == alarmMinute && alarmDays.indexOf(String(currentDay)) != -1)
{
SNOOZE_TIME = alarm["snooze"].as<uint8_t>();
}
else
{
SNOOZE_TIME = 0;
if (difftime(now1, lastAlarmTime) < MIN_ALARM_INTERVAL)
{
return;
}
ALARM_ACTIVE = true;
lastAlarmTime = now1;
if (alarm.containsKey("sound"))
{
ALARM_SOUND = alarm["sound"].as<String>();
}
else
{
ALARM_SOUND = "";
}
if (alarm.containsKey("snooze"))
{
SNOOZE_TIME = alarm["snooze"].as<uint8_t>();
}
else
{
SNOOZE_TIME = 0;
}
}
}
}

View File

@@ -3,12 +3,13 @@
#include <Arduino.h>
#include <EasyButton.h>
#ifdef ULANZI
#include "Adafruit_SHT31.h"
#else
#include "Adafruit_BME280.h"
#include "SoftwareSerial.h"
#include <DFMiniMp3.h>
#ifndef ULANZI
#define DFMINI_MP3_BOOT "1"
#define DFMINI_MP3_ALARM "2"
#define DFMINI_MP3_TIMER "2"
#define DFMINI_MP3_CLICK "5"
#define DFMINI_MP3_CLICK_ON "3"
#define DFMINI_MP3_ENTER "4"
#endif
class PeripheryManager_
@@ -37,7 +38,9 @@ public:
void playFromFile(String file);
bool isPlaying();
void stopSound();
#ifndef ULANZI
void setVolume(uint8_t);
#endif
const char *readUptime();
};

View File

@@ -8,8 +8,13 @@
#include <Ticker.h>
#include "Globals.h"
#ifdef ULANZI
#define URL_fw_Version "https://raw.githubusercontent.com/Blueforcer/awtrix-light/main/version"
#define URL_fw_Bin "https://raw.githubusercontent.com/Blueforcer/awtrix-light/main/docs/flasher/firmware/firmware.bin"
#else
#define URL_fw_Version "todo"
#define URL_fw_Bin "todo"
#endif
Ticker UpdateTicker;

View File

@@ -59,6 +59,7 @@ void BootAnimation(void *parameter)
void setup()
{
PeripheryManager.setup();
delay(1000);
Serial.begin(115200);