This commit is contained in:
Elfish
2023-04-02 12:06:17 +02:00
44 changed files with 1677 additions and 1155 deletions

39
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,39 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
<!-- Make sure to read the README before opening an issue. [https://xaviml.github.io/controllerx/faq](https://blueforcer.github.io/awtrix-light/#/README) -->
# Bug report
## Describe the bug
Add a description of the bug. Detail the expected behaviour in contrast with the behaviour you're observing.
## Additional information
- Devices involved:
- Model: Ulanzi Awtrix Smart Pixel Clock 2882 (TC001)
- AWTRIX-LIGHT version: [ eg. v0.45 ]
## To Reproduce
Steps to reproduce the behavior:
## Expected behavior
A clear and concise description of what you expected to happen.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Logs
```text
(optional) Add relevant logs which could help tackle the problem.
```
## Additional context
Add any other context about the problem here.

View File

@@ -0,0 +1,28 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE REQUEST]"
labels: feature request
assignees: ''
---
<!-- Make sure to read the README before opening an issue. [https://xaviml.github.io/controllerx/faq](https://blueforcer.github.io/awtrix-light/#/README) -->
# Feature Request
### Is your feature request related to a problem?
If so, then add a clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
### Describe the solution / feature you'd like
A clear and concise description of what you want to happen.
### Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
### Additional context
Add any other context or screenshots about the feature request here.

30
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,30 @@
---
name: Question
about: Open an issue to ask something
title: "[QUESTION]"
labels: question
assignees: ''
---
<!-- Make sure to read the README before opening an issue. [https://xaviml.github.io/controllerx/faq](https://blueforcer.github.io/awtrix-light/#/README) -->
# Question
Add here your question.
## Additional information
- Devices involved:
- Model: Ulanzi Awtrix Smart Pixel Clock 2882 (TC001)
- AWTRIX-LIGHT version: [ eg. v0.45 ]
## Logs
```text
(optional) Add relevant logs which could help tackle the problem.
```
## Additional context
Add any other context about the problem here.

View File

@@ -89,14 +89,6 @@ jobs:
asset_name: firmware.bin
asset_content_type: application/octet-stream
- name: Discord notification
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@master
with:
args: 'https://github.com/Blueforcer/awtrix-light/releases/tag/${{ steps.version_number.outputs.firmware_version }}'
- name: Commit
id: commit
uses: stefanzweifel/git-auto-commit-action@v4

View File

@@ -14,12 +14,17 @@
![GitHub all releases](https://img.shields.io/github/downloads/blueforcer/awtrix-light/total?style=flat-square)
![GitHub Sponsors](https://img.shields.io/github/sponsors/blueforcer?style=flat-square)
![Discord](https://img.shields.io/discord/546407049148366859?label=Discord&style=flat-square)
[![Compile](https://github.com/Blueforcer/awtrix-light/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/Blueforcer/awtrix-light/actions/workflows/main.yml)
![GIF-230324_161917](https://user-images.githubusercontent.com/31169771/227567565-3780ee83-1158-4371-9390-fd03a7873496.gif)
</div>
AWTRIX Light is an opensource custom firmware for the [Ulanzi Smart Pixel clock TC001](https://www.ulanzi.com/products/ulanzi-pixel-smart-clock-2882) which lives on your smarthome.
AWTRIX Light is an opensource custom firmware for the [Ulanzi Smart Pixel clock TC001](https://www.ulanzi.com/products/ulanzi-pixel-smart-clock-2882)
AWL meant to be a companion for your smarthome like HomeAssistant, IOBroker, NodeRed and so on.
Even if you don't have a Smarthome system, but would like to experiment with NodeRed or N8N, you are still welcome to join us.
There are many users who are willing to help you!
It is ready to use straight out of the box, with time, date, temperature, and humidity pages pre-installed. You don't need to do anything other than turning it on to start using these features.
@@ -35,8 +40,10 @@ Join the thousands of satisfied awtrix users who have already chosen Awtrix 2 an
- Onscreen menu where you can change your settings directly on the device
- Pre-installed Apps like time, date, temperature, humidity and battery
- Add customapps without recompiling straight from your Smarthome.
- Noitification support
- Easy to use icon system
- Powerful MQTT commands
- HTTP API
- RTTTL melody player
- Integrated filebrowser
- No cloud
@@ -47,7 +54,15 @@ Join the thousands of satisfied awtrix users who have already chosen Awtrix 2 an
Starting is easy as 1-2-3
[with the documentation](https://blueforcer.github.io/awtrix-light/)
# Contributors
<a href="https://github.com/Blueforcer/awtrix-light/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Blueforcer/awtrix-light" />
</a>
# Community
Join the discord channel with over 1500 Users
https://discord.gg/cyBCpdx
# Support
If you like my work, please consider supporting the project.
https://github.com/sponsors/Blueforcer

View File

@@ -1,17 +1,21 @@
# AWTRIX-LIGHT
If you like my work, please consider supporting the project.
https://github.com/sponsors/Blueforcer
https://paypal.me/blueforcer
Awtrix Light is a custom firmware for the [Ulanzi Smart Pixel clock](https://www.ulanzi.com/products/ulanzi-pixel-smart-clock-2882).
That offers a simple and user-friendly interface, making it the perfect solution for non-techies who want to enjoy the benefits of the popular awtrix system.
Awtrix Light is a custom firmware for the [Ulanzi Smart Pixel clock](https://www.ulanzi.com/products/ulanzi-pixel-smart-clock-2882).
It meant to be a companion for your smarthome like HomeAssistant, IOBroker, NodeRed and so on.
Even if you don't have a Smarthome system, but would like to experiment, you are still welcome to join us. There are many users who are willing to help you!
It is ready to use straight out of the box, with time, date, temperature, and humidity apps pre-installed. You don't need to do anything other than turning it on to start using these features.
During the development of Awtrix Light, usability and simplicity are my top priorities. My aim is to make it easy for non-tech-savvy users to benefit from Awtrix Light without any headaches or hours of scripting.
Awtrix Light is ready to use straight out of the box, without the need for a single line of code or commands.
Awtrix Light is ready to use straight out of the box, without the need for a single line of code.
However, for those with more advanced skills, the customization options available with custom apps allow you to take Awtrix Light to its full potential.
With Awtrix Light, you can effortlessly bring your ideas to life and enjoy a hassle-free experience.
Join the thousands of satisfied awtrix users who have already chosen Awtrix 2 and Awtrix Light and experience the difference today!
https://discord.gg/cyBCpdx
https://discord.gg/cyBCpdx
## Support
If you like my work, please consider supporting the project.
https://github.com/sponsors/Blueforcer
https://paypal.me/blueforcer

View File

@@ -11,10 +11,11 @@
- Features
- [Apps](apps.md)
- [Alarm clock](alarm.md)
- [Timer](timer.md)
- [Icons](icons.md)
- [Sounds](sounds.md)
- [Hidden features](dev.md)
- MQTT
- API
- [MQTT/HTTP](api.md)
- [Custom Pages & Notifications](custom.md)
- [Commands](mqtt.md)

189
docs/api.md Normal file
View File

@@ -0,0 +1,189 @@
# MQTT / HTTP API
## Status
In MQTT awtrix send its stats every 10s to `[PREFIX]/stats`
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 |
| --- | --- | --- |--- |--- |
| `[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 |
| --- | --- | --- |--- |--- |
| `[PREFIX]/custom/[appname]` |`http://[IP]/api/custom` | JSON | name = [appname] | POST |
## Dismiss Notification
Dismiss a notification which was set to "hold"=true.
| Topic | URL | Payload/Body | HTTP method |
| --- | --- | --- | --- |
| `[PREFIX]/custom/[appname]` |`http://[IP]/api/notify/dismiss` | empty payload/body | POST |
## Switch Apps
Switch to next or previous app.
| Topic | URL | Payload/Body | HTTP method |
| --- | --- | --- | --- |
| `[PREFIX]/nextapp` | `http://[IP]/api/nextapp` | empty payload/body | POST |
| `[PREFIX]/previousapp` | `http://[IP]/api/previousapp` | empty payload/body | POST |
## Switch to Specific App
Switch to a specific app by name.
| Topic | URL | Payload/Body | HTTP method |
| --- | --- | --- | --- |
| `[PREFIX]/switch` | `http://[IP]/api/switch` | `{"name":"time"}` | POST |
Built-in app names are:
- `time`
- `date`
- `temp`
- `hum`
- `bat`
For custom apps, use the name you set in the topic or http request header.
In MQTT for example, if `[PREFIX]/custom/test` is your topic, then `test` is the name.
## Add/remove and rearange apps
| Topic | URL | Payload/Body | HTTP method |
| --- | --- | --- | --- |
| `[PREFIX]/apps`|`http://[IP]/api/apps`| json | POST |
!> This function provides users with the ability to manage the apps on their device by adding, removing, and rearranging them. However, as it is an experimental feature, caution should be exercised, particularly when attempting to rearrange multiple apps at once, as this can lead to unintended consequences due to the resulting shifts in position of other apps.
By using this function, users can add or remove native apps, as well as custom apps, from the device.
However, it is important to note that custom apps are only temporarily loaded into memory and cannot be added again using this function.
To add a custom app again, you must send it to awtrix via mqtt again.
Additionally, you can rearrange the position of all apps on the device by specifying a new position in the JSON array.
This provides flexibility in organizing apps according to personal preference.
The JSON payload is an array of objects, where each object represents an app to be displayed on awtrix. Each app object contains the following fields:
### JSON Properties
| Property | Description |Default |
|----------|-------------|-------------|
| name | The name of the app. If it's a native app, it can be one of "time", "date", "temp", "hum", or "bat". For custom apps, use the name you set in the topic. For example, if `[PREFIX]/custom/test` is your topic, then `test` is the name. | |
| show | A boolean indicating whether the app should be shown on the screen or not. If not present, the app is considered active by default. | true |
| pos | An integer indicating the position of the app in the list. If not present, the app will be added to the end of the list. | Last Item |
> You can also just send the information for one app.
```json
[
{
"name":"time",
"show":true,
"pos":3
},
{
"name":"date",
"pos":0
},
{
"name":"temp",
"pos":2
},
{
"name":"hum",
"show":true,
"pos":0
},
{
"name":"bat",
"show":false
},
{
"name":"github",
"show":true,
"pos":4
}
]
```
In this example,
- The "time" app is active and should be displayed in position 3.
- The "date" app should be displayed in position 0.
- The "temp" app should be displayed in position 2.
- The "hum" app should be displayed at first position.
- The "bat" app is inactive and will be removed,
- and the "github" app is active and should be displayed in position 4.
## Add timer
create custom apps or notifications to display your own text and icons.
have a look at [this section](custom)
| Topic | URL | Payload/Body |HTTP method |
| --- | --- | --- |--- |
| `[PREFIX]/custom/[appname]` |`http://[IP]/api/notify/dismiss` | empty payload/body | POST |
## Change Settings
Change various settings related to the app display.
| Topic | URL | Payload/Body |HTTP method |
| --- | --- | --- |--- |
| `[PREFIX]/settings` |`http://[IP]/api/settings`| JSON | POST |
#### JSON Properties
Each property is optional; you do not need to send all.
| Key | Type | Description | Value Range | Default |
| --- | --- | --- | --- | --- |
| `apptime` | number | Determines the duration an app is displayed in milliseconds. | Any positive integer value. | 7000 |
| `transition` | number | The time the transition to the next app takes in milliseconds. | Any positive integer value. | 500 |
| `textcolor` | string | A color in hexadecimal format. | Any valid 6-digit hexadecimal color value, e.g. "#FF0000" for red. | N/A |
| `fps` | number | Determines the frame rate at which the matrix is updated. | Any positive integer value. | 23 |
| `brightness` | number | Determines the brightness of the matrix. | An integer between 0 and 255. | N/A |
| `autobrightness` | boolean | Determines if automatic brightness control is active. | `true` or `false`. | N/A |
| `autotransition` | boolean | Determines if automatic switching to the next app is active. | `true` or `false`. | N/A |
## Timer
With AWTRIX Light, you can set a timer using MQTT. Simply send a JSON object to the topic **[PREFIX]/timer** to start a timer.
When the timer goes off, the display will show a notification, and you can dismiss the timer by pressing the middle button.
#### JSON Properties
The JSON object has the following properties:
| Key | Type | Description |
| --- | ---- | ----------- |
| `hours` | number | The number of hours after midnight when the timer should be triggered. |
| `minutes` | number | The number of minutes after the hour when the timer should be triggered. |
| `seconds` | number | The number of seconds after the minute when the timer should be triggered. |
| `sound` | string | The name of the sound file (without extension) to play when the timer is triggered. |
Each value is optional, so you can set a timer for just minutes, or any combination of hours, minutes, and seconds. If you only want to start a timer in some minutes, just send the minutes.
## Example
Here's an example JSON object to start a timer for 1 hour, 30 minutes, and 10 seconds, with the sound "friends":
```json
{
"hours": 1,
"minutes": 30,
"seconds": 10,
"sound": "friends"
}
```

View File

@@ -1,7 +1,9 @@
# Custom Apps & Notifications
# Custom Apps and Notifications
With AWTRIX Light, you can create custom apps or notifications to display your own text and icons.
Simply send a JSON object to the topic "awtrixlight/custom/[page]" where [page] is a the name of your page (without spaces).
With AWTRIX Light, you can create custom apps or notifications to display your own text and icons.
With MQTT simply send a JSON object to the topic `[PREFIX]/custom/[page]` where [page] is a the name of your page (without spaces).
With the [HTTP API](api?id=add-custom-app) you have to set the appname in the request header (`name = Appname`)
## JSON Properties
@@ -9,12 +11,12 @@ The JSON object has the following properties:
| Key | Type | Description | Default |
| --- | ---- | ----------- | ------- |
| `pos` | number | defines the position of your custompage in the loop, starting at 0 for the first position. This will only apply with your first push. You cant change the position afterwards with [this function](mqtt?id=addremove-and-rearange-apps) |
| `pos` | number | defines the position of your custompage in the loop, starting at 0 for the first position. This will only apply with your first push. You cant change the position afterwards with [this function](api?id=addremove-and-rearange-apps) |
| `text` | string | The text to display on the page. | |
| `icon` | string | The icon ID or filename (without extension) to display on the page. | |
| `repeat` | number | Sets how many times the text should be scrolled through the matrix before the display ends. | 1 |
| `rainbow` | boolean | Fades each letter in the text differently through the entire RGB spectrum. | false |
| `duration` | number | Sets how long the page should be displayed. | 5 |
| `duration` | number | Sets how long the notification should be displayed. | 5 |
| `color` | string | A color hex string for the text color, or an array of R,G,B values | "#FFFFFF" or [255,255,0] |
| `hold` | boolean | Set it to true, to hold your notification on top until you press the middle button or dismiss it via HomeAssistant. This key only belongs to notification. | false |
| `sound` | string | The filename of your RTTTL ringtone file (without extension). | |
@@ -41,5 +43,6 @@ Here's an example JSON object to display the text "Hello, AWTRIX Light!" with th
}
```
## Delete a custom page
To delelte a custom page simply send a empty payload to the same topic
## Delete a custom app
To delete a custom app simply send a empty payload/body to the same topic/url.
You can also use [this API](api?id=addremove-and-rearange-apps)

15
docs/dev.md Normal file
View File

@@ -0,0 +1,15 @@
# Hidden Features
Ok, now they are no longer hidden :).
This section contains small setting options that the majority of users do not change or change very rarely and therefore saved the effort of creating an elaborate settings interface.
Create a `dev.json` in your filemanager.
## JSON Properties
The JSON object has the following properties:
| Key | Type | Description | Default |
| --- | ---- | ----------- | ------- |
| `bootsound` | string | Uses a custom melodie from the MELODIES folder | |
| `uppercase` | boolean | Print every character in uppercase | true |

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "AWTRIX Light",
"version": "0.43",
"version": "0.47",
"home_assistant_domain": "AwtrixLight",
"funding_url": "https://blueforcer.de",
"new_install_prompt_erase": true,

View File

@@ -1,121 +0,0 @@
# MQTT Commands
## Dismiss Notification
Dismiss a notification which was set to "hold"=true.
| Topic | Payload |
| --- | --- |
| `[PREFIX]/notify/dismiss` | empty payload |
## Switch Apps
Switch to next or previous app.
| Topic | Payload |
| --- | --- |
| `[PREFIX]/nextapp` | empty payload |
| `[PREFIX]/previousapp` | empty payload |
## Switch to Specific App
Switch to a specific app by name.
| Topic | Payload |
| --- | --- |
| `[PREFIX]/switch` | `{"name":"time"}` |
Built-in app names are:
- `time`
- `date`
- `temp`
- `hum`
- `bat`
For custom apps, use the name you set in the topic. For example, if `[PREFIX]/custom/test` is your topic, then `test` is the name.
## Add/remove and rearange apps
| Topic |
| --- |
| `[PREFIX]/apps` |
!> This function provides users with the ability to manage the apps on their device by adding, removing, and rearranging them. However, as it is an experimental feature, caution should be exercised, particularly when attempting to rearrange multiple apps at once, as this can lead to unintended consequences due to the resulting shifts in position of other apps.
By using this function, users can add or remove native apps, as well as custom apps, from the device.
However, it is important to note that custom apps are only temporarily loaded into memory and cannot be added again using this function.
To add a custom app again, you must send it to awtrix via mqtt again.
Additionally, you can rearrange the position of all apps on the device by specifying a new position in the JSON array.
This provides flexibility in organizing apps according to personal preference.
The JSON payload is an array of objects, where each object represents an app to be displayed on awtrix. Each app object contains the following fields:
`"name"`: The name of the app ("time", "date", "temp", "hum", "bat") are the native apps.
For custom apps, use the name you set in the topic. For example, if `[PREFIX]/custom/test` is your topic, then `test` is the name.
`"show"`: A boolean indicating whether the app should be shown on the screen or not. If not present, the app is considered active by default.
`"pos"`: An integer indicating the position of the app in the list. If not present, the app will be added to the end of the list.
> You can also just send the information for one app.
```json
[
{
"name":"time",
"show":true,
"pos":3
},
{
"name":"date",
"pos":0
},
{
"name":"temp",
"pos":2
},
{
"name":"hum",
"show":true,
"pos":0
},
{
"name":"bat",
"show":false
},
{
"name":"github",
"show":true,
"pos":4
}
]
```
In this example,
- The "time" app is active and should be displayed in position 3.
- The "date" app should be displayed in position 0.
- The "temp" app should be displayed in position 2.
- The "hum" app should be displayed at first position.
- The "bat" app is inactive and will be removed,
- and the "github" app is active and should be displayed in position 4.
## Change Settings
Change various settings related to the app display.
| Topic | Payload |
| --- | --- |
| `[PREFIX]/settings` | JSON properties |
Each property is optional; you do not need to send all.
| Key | Type | Description | Value Range | Default |
| --- | --- | --- | --- | --- |
| `apptime` | number | Determines the duration an app is displayed in milliseconds. | Any positive integer value. | 7000 |
| `transition` | number | The time the transition to the next app takes in milliseconds. | Any positive integer value. | 500 |
| `textcolor` | string | A color in hexadecimal format. | Any valid 6-digit hexadecimal color value, e.g. "#FF0000" for red. | N/A |
| `fps` | number | Determines the frame rate at which the matrix is updated. | Any positive integer value. | 23 |
| `brightness` | number | Determines the brightness of the matrix. | An integer between 0 and 255. | N/A |
| `autobrightness` | boolean | Determines if automatic brightness control is active. | `true` or `false`. | N/A |
| `autotransition` | boolean | Determines if automatic switching to the next app is active. | `true` or `false`. | N/A |

View File

@@ -18,5 +18,6 @@ Hold down the middle button for 2s to exit the current menu and to save your set
| `WEEKDAY` | Allows selection of start of week. |
| `TEMP` | Allows selection of temperature system (°C or °F). |
| `APPS` | Allows to enable or disable internal apps |
| `SOUND` | Allows to enable or disable sound output |
| `UPDATE` | Check and download new firmware if available. |

View File

@@ -2,7 +2,7 @@
# Quick start
1. :computer: Connect your device to your PC or Mac and [use the online flahser](flasher.md)
2. :signal_strength: After flashing, Awtrix will open an access point with the name "AWTRIX LIGHT". Connect with PW "12345678".
3. :mag: Open a browser and navigate to 192.168.4.1. Enter your WiFi information and reboot the clock.
4. :clock1: Your clock is accessible via http://awtrixlight.local/.
3. :mag: Open a browser and navigate to 192.168.4.1. Enter your WiFi information and connect to your WiFi.
4. :clock1: Your clock is accessible via the IP adress you see at connect.
5. :gear: Set up your MQTT and other options in the web interface.
6. :heavy_check_mark: You're ready to go.

View File

@@ -1,31 +0,0 @@
# Timer
With AWTRIX Light, you can set a timer using MQTT. Simply send a JSON object to the topic **[PREFIX]/timer** to start a timer.
When the timer goes off, the display will show a notification, and you can dismiss the timer by pressing the middle button.
## JSON Properties
The JSON object has the following properties:
| Key | Type | Description |
| --- | ---- | ----------- |
| `hours` | number | The number of hours after midnight when the timer should be triggered. |
| `minutes` | number | The number of minutes after the hour when the timer should be triggered. |
| `seconds` | number | The number of seconds after the minute when the timer should be triggered. |
| `sound` | string | The name of the sound file (without extension) to play when the timer is triggered. |
Each value is optional, so you can set a timer for just minutes, or any combination of hours, minutes, and seconds. If you only want to start a timer in some minutes, just send the minutes.
## Example
Here's an example JSON object to start a timer for 1 hour, 30 minutes, and 10 seconds, with the sound "friends":
```json
{
"hours": 1,
"minutes": 30,
"seconds": 10,
"sound": "friends"
}
```

View File

@@ -1,2 +0,0 @@
github: [makuna]
custom: ["https://paypal.me/MakunaGithub"]

View File

@@ -1,105 +0,0 @@
// this example will play a track and then every 60 seconds
// it will play an advertisement
//
// it expects the sd card to contain the following mp3 files
// but doesn't care whats in them
//
// sd:/01/001.mp3 - the song to play, the longer the better
// sd:/advert/0001.mp3 - the advertisement to interrupt the song, keep it short
#include <SoftwareSerial.h>
#include <DFMiniMp3.h>
// implement a notification class,
// its member methods will get called
//
class Mp3Notify
{
public:
static void PrintlnSourceAction(DfMp3_PlaySources source, const char* action)
{
if (source & DfMp3_PlaySources_Sd)
{
Serial.print("SD Card, ");
}
if (source & DfMp3_PlaySources_Usb)
{
Serial.print("USB Disk, ");
}
if (source & DfMp3_PlaySources_Flash)
{
Serial.print("Flash, ");
}
Serial.println(action);
}
static void OnError(uint16_t errorCode)
{
// see DfMp3_Error for code meaning
Serial.println();
Serial.print("Com Error ");
Serial.println(errorCode);
}
static void OnPlayFinished(DfMp3_PlaySources source, uint16_t track)
{
Serial.print("Play finished for #");
Serial.println(track);
}
static void OnPlaySourceOnline(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "online");
}
static void OnPlaySourceInserted(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "inserted");
}
static void OnPlaySourceRemoved(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "removed");
}
};
// instance a DFMiniMp3 object,
// defined with the above notification class and the hardware serial class
//
DFMiniMp3<HardwareSerial, Mp3Notify> mp3(Serial1);
// Some arduino boards only have one hardware serial port, so a software serial port is needed instead.
// comment out the above definition and uncomment these lines
//SoftwareSerial secondarySerial(10, 11); // RX, TX
//DFMiniMp3<SoftwareSerial, Mp3Notify> mp3(secondarySerial);
uint32_t lastAdvert; // track time for last advertisement
void setup()
{
Serial.begin(115200);
Serial.println("initializing...");
mp3.begin();
uint16_t volume = mp3.getVolume();
Serial.print("volume was ");
Serial.println(volume);
mp3.setVolume(24);
volume = mp3.getVolume();
Serial.print(" and changed to ");
Serial.println(volume);
Serial.println("track 1 from folder 1");
mp3.playFolderTrack(1, 1); // sd:/01/001.mp3
lastAdvert = millis();
}
void loop()
{
uint32_t now = millis();
if ((now - lastAdvert) > 60000)
{
// interrupt the song and play the advertisement, it will
// return to the song when its done playing automatically
mp3.playAdvertisement(1); // sd:/advert/0001.mp3
lastAdvert = now;
}
mp3.loop();
}

View File

@@ -1,121 +0,0 @@
// this example will play a track and then
// every five seconds play another track
//
// it expects the sd card to contain these three mp3 files
// but doesn't care whats in them
//
// sd:/mp3/0001.mp3
// sd:/mp3/0002.mp3
// sd:/mp3/0003.mp3
#include <SoftwareSerial.h>
#include <DFMiniMp3.h>
// implement a notification class,
// its member methods will get called
//
class Mp3Notify
{
public:
static void PrintlnSourceAction(DfMp3_PlaySources source, const char* action)
{
if (source & DfMp3_PlaySources_Sd)
{
Serial.print("SD Card, ");
}
if (source & DfMp3_PlaySources_Usb)
{
Serial.print("USB Disk, ");
}
if (source & DfMp3_PlaySources_Flash)
{
Serial.print("Flash, ");
}
Serial.println(action);
}
static void OnError(uint16_t errorCode)
{
// see DfMp3_Error for code meaning
Serial.println();
Serial.print("Com Error ");
Serial.println(errorCode);
}
static void OnPlayFinished(DfMp3_PlaySources source, uint16_t track)
{
Serial.print("Play finished for #");
Serial.println(track);
}
static void OnPlaySourceOnline(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "online");
}
static void OnPlaySourceInserted(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "inserted");
}
static void OnPlaySourceRemoved(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "removed");
}
};
// instance a DFMiniMp3 object,
// defined with the above notification class and the hardware serial class
//
DFMiniMp3<HardwareSerial, Mp3Notify> mp3(Serial1);
// Some arduino boards only have one hardware serial port, so a software serial port is needed instead.
// comment out the above definition and uncomment these lines
//SoftwareSerial secondarySerial(10, 11); // RX, TX
//DFMiniMp3<SoftwareSerial, Mp3Notify> mp3(secondarySerial);
void setup()
{
Serial.begin(115200);
Serial.println("initializing...");
mp3.begin();
uint16_t volume = mp3.getVolume();
Serial.print("volume ");
Serial.println(volume);
mp3.setVolume(24);
uint16_t count = mp3.getTotalTrackCount(DfMp3_PlaySource_Sd);
Serial.print("files ");
Serial.println(count);
Serial.println("starting...");
}
void waitMilliseconds(uint16_t msWait)
{
uint32_t start = millis();
while ((millis() - start) < msWait)
{
// calling mp3.loop() periodically allows for notifications
// to be handled without interrupts
mp3.loop();
delay(1);
}
}
void loop()
{
Serial.println("track 1");
mp3.playMp3FolderTrack(1); // sd:/mp3/0001.mp3
waitMilliseconds(5000);
Serial.println("track 2");
mp3.playMp3FolderTrack(2); // sd:/mp3/0002.mp3
waitMilliseconds(5000);
Serial.println("track 3");
mp3.playMp3FolderTrack(3); // sd:/mp3/0002.mp3
waitMilliseconds(5000);
}

View File

@@ -1,100 +0,0 @@
// this example will play a random track from all on the sd
//
// it expects the sd card to contain some mp3 files
#include <SoftwareSerial.h>
#include <DFMiniMp3.h>
// implement a notification class,
// its member methods will get called
//
class Mp3Notify
{
public:
static void PrintlnSourceAction(DfMp3_PlaySources source, const char* action)
{
if (source & DfMp3_PlaySources_Sd)
{
Serial.print("SD Card, ");
}
if (source & DfMp3_PlaySources_Usb)
{
Serial.print("USB Disk, ");
}
if (source & DfMp3_PlaySources_Flash)
{
Serial.print("Flash, ");
}
Serial.println(action);
}
static void OnError(uint16_t errorCode)
{
// see DfMp3_Error for code meaning
Serial.println();
Serial.print("Com Error ");
Serial.println(errorCode);
}
static void OnPlayFinished(DfMp3_PlaySources source, uint16_t track)
{
Serial.print("Play finished for #");
Serial.println(track);
}
static void OnPlaySourceOnline(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "online");
}
static void OnPlaySourceInserted(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "inserted");
}
static void OnPlaySourceRemoved(DfMp3_PlaySources source)
{
PrintlnSourceAction(source, "removed");
}
};
// instance a DFMiniMp3 object,
// defined with the above notification class and the hardware serial class
//
DFMiniMp3<HardwareSerial, Mp3Notify> mp3(Serial1);
// Some arduino boards only have one hardware serial port, so a software serial port is needed instead.
// comment out the above definition and uncomment these lines
//SoftwareSerial secondarySerial(10, 11); // RX, TX
//DFMiniMp3<SoftwareSerial, Mp3Notify> mp3(secondarySerial);
void setup()
{
Serial.begin(115200);
Serial.println("initializing...");
mp3.begin();
mp3.reset();
// show some properties and set the volume
uint16_t volume = mp3.getVolume();
Serial.print("volume ");
Serial.println(volume);
mp3.setVolume(24);
uint16_t count = mp3.getTotalTrackCount(DfMp3_PlaySource_Sd);
Serial.print("files ");
Serial.println(count);
uint16_t mode = mp3.getPlaybackMode();
Serial.print("playback mode ");
Serial.println(mode);
Serial.println("starting...");
mp3.playRandomTrackFromAll(); // random of all folders on sd
}
void loop()
{
// calling mp3.loop() periodically allows for notifications
// to be handled without interrupts
mp3.loop();
}

View File

@@ -1,473 +1,473 @@
/**
** The original 3x5 font is licensed under the 3-clause BSD license:
**
** Copyright 1999 Brian J. Swetland
** Copyright 1999 Vassilii Khachaturov
** Portions (of vt100.c/vt100.h) copyright Dan Marks
** Modifications for Awtrix for improved readability and LaMetric Style Copyright 2023 Blueforcer
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions, and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions, and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the authors may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Modifications to TomThumb for improved readability are from Robey Pointer,
** see:
** http://robey.lag.net/2010/01/23/tiny-monospace-font.html
**
** Modifications for Awtrix for improved readability and LaMetric Style are from
** Blueforcer, Yann and hollyghost
**
** The original author does not have any objection to relicensing of Robey
** Pointer's modifications (in this file) in a more permissive license. See
** the discussion at the above blog, and also here:
** http://opengameart.org/forumtopic/how-to-submit-art-using-the-3-clause-bsd-license
**
** Feb 21, 2016: Conversion from Linux BDF --> Adafruit GFX font,
** with the help of this Python script:
** https://gist.github.com/skelliam/322d421f028545f16f6d
** William Skellenger (williamj@skellenger.net)
** Twitter: @skelliam
**
*/
// AwtrixFont Version 20230215
const uint8_t AwtrixBitmaps[] PROGMEM = {
0x00, /*[0] 0x20 space */
0x80, 0x80, 0x80, 0x00, 0x80, /*[1] 0x21 exclam */
0xA0, 0xA0, /*[2] 0x22 quotedbl */
0xA0, 0xE0, 0xA0, 0xE0, 0xA0, /*[3] 0x23 numbersign */
0x60, 0xC0, 0x60, 0xC0, 0x40, /*[4] 0x24 dollar */
0xA0, 0x20, 0x40, 0x80, 0xA0, /*[5] 0x25 percent */
0xC0, 0xC0, 0xE0, 0xA0, 0x60, /*[6] 0x26 ampersand */
0x80, 0x80, /*[7] 0x27 quotesingle */
0x40, 0x80, 0x80, 0x80, 0x40, /*[8] 0x28 parenleft */
0x80, 0x40, 0x40, 0x40, 0x80, /*[9] 0x29 parenright */
0xA0, 0x40, 0xA0, /*[10] 0x2A asterisk */
0x40, 0xE0, 0x40, /*[11] 0x2B plus */
0x40, 0x80, /*[12] 0x2C comma */
0xE0, /*[13] 0x2D hyphen */
0x80, /*[14] 0x2E period */
0x20, 0x20, 0x40, 0x80, 0x80, /*[15] 0x2F slash */
0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /*[16] 0x30 zero */
0x40, 0xC0, 0x40, 0x40, 0xE0, /*[17] 0x31 one */
0xE0, 0x20, 0xE0, 0x80, 0xE0, /*[18] 0x32 two */
0xE0, 0x20, 0xE0, 0x20, 0xE0, /*[19] 0x33 three */
0xA0, 0xA0, 0xE0, 0x20, 0x20, /*[20] 0x34 four */
0xE0, 0x80, 0xE0, 0x20, 0xE0, /*[21] 0x35 five */
0xE0, 0x80, 0xE0, 0xA0, 0xE0, /*[22] 0x36 six */
0xE0, 0x20, 0x20, 0x20, 0x20, /*[23] 0x37 seven */
0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /*[24] 0x38 eight */
0xE0, 0xA0, 0xE0, 0x20, 0xE0, /*[25] 0x39 nine */
0x80, 0x00, 0x80, /*[26] 0x3A colon */
0x40, 0x00, 0x40, 0x80, /*[27] 0x3B semicolon */
0x20, 0x40, 0x80, 0x40, 0x20, /*[28] 0x3C less */
0xE0, 0x00, 0xE0, /*[29] 0x3D equal */
0x80, 0x40, 0x20, 0x40, 0x80, /*[30] 0x3E greater */
0xE0, 0x20, 0x40, 0x00, 0x40, /*[31] 0x3F question */
0x40, 0xA0, 0xE0, 0x80, 0x60, /*[32] 0x40 at */
0xC0, 0xA0, 0xE0, 0xA0, 0xA0, /*[33] 0x41 A */
0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /*[34] 0x42 B */
0x40, 0xA0, 0x80, 0xA0, 0x40, /*[35] 0x43 C */
0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /*[36] 0x44 D */
0xE0, 0x80, 0xE0, 0x80, 0xE0, /*[37] 0x45 E */
0xE0, 0x80, 0xE0, 0x80, 0x80, /*[38] 0x46 F */
0x60, 0x80, 0xA0, 0xA0, 0x60, /*[39] 0x47 G */
0xA0, 0xA0, 0xE0, 0xA0, 0xA0, /*[40] 0x48 H */
0x80, 0x80, 0x80, 0x80, 0x80, /*[41] 0x49 I */
0x20, 0x20, 0x20, 0xA0, 0x40, /*[42] 0x4A J */
0xA0, 0xA0, 0xC0, 0xA0, 0xA0, /*[43] 0x4B K */
0x80, 0x80, 0x80, 0x80, 0xE0, /*[44] 0x4C L */
0x88, 0xD8, 0xA8, 0x88, 0x88, /*[45] 0x4D M */
0x90, 0xD0, 0xB0, 0x90, 0x90, /*[46] 0x4E N */
0x40, 0xA0, 0xA0, 0xA0, 0x40, /*[47] 0x4F O */
0xE0, 0xA0, 0xC0, 0x80, 0x80, /*[48] 0x50 P */
0x40, 0xA0, 0xA0, 0xA0, 0x70, /*[49] 0x51 Q */
0xE0, 0xA0, 0xC0, 0xA0, 0xA0, /*[50] 0x52 R */
0xE0, 0x80, 0xE0, 0x20, 0xE0, /*[51] 0x53 S */
0xE0, 0x40, 0x40, 0x40, 0x40, /*[52] 0x54 T */
0xA0, 0xA0, 0xA0, 0xA0, 0xE0, /*[53] 0x55 U */
0xA0, 0xA0, 0xA0, 0xA0, 0x40, /*[54] 0x56 V */
0x88, 0x88, 0x88, 0xA8, 0x50, /*[55] 0x57 W */
0xA0, 0xA0, 0x40, 0xA0, 0xA0, /*[56] 0x58 X */
0xA0, 0xA0, 0xE0, 0x20, 0xC0, /*[57] 0x59 Y */
0xE0, 0x20, 0x40, 0x80, 0xE0, /*[58] 0x5A Z */
0xE0, 0x80, 0x80, 0x80, 0xE0, /*[59] 0x5B bracketleft */
0x80, 0x40, 0x20, /*[60] 0x5C backslash */
0xE0, 0x20, 0x20, 0x20, 0xE0, /*[61] 0x5D bracketright */
0x40, 0xA0, /*[62] 0x5E asciicircum */
0xE0, /*[63] 0x5F underscore */
0x80, 0x40, /*[64] 0x60 grave */
0xC0, 0x60, 0xA0, 0xE0, /*[65] 0x61 a */
0x80, 0xC0, 0xA0, 0xA0, 0xC0, /*[66] 0x62 b */
0x60, 0x80, 0x80, 0x60, /*[67] 0x63 c */
0x20, 0x60, 0xA0, 0xA0, 0x60, /*[68] 0x64 d */
0x60, 0xA0, 0xC0, 0x60, /*[69] 0x65 e */
0x20, 0x40, 0xE0, 0x40, 0x40, /*[70] 0x66 f */
0x60, 0xA0, 0xE0, 0x20, 0x40, /*[71] 0x67 g */
0x80, 0xC0, 0xA0, 0xA0, 0xA0, /*[72] 0x68 h */
0x80, 0x00, 0x80, 0x80, 0x80, /*[73] 0x69 i */
0x20, 0x00, 0x20, 0x20, 0xA0, 0x40, /*[74] 0x6A j */
0x80, 0xA0, 0xC0, 0xC0, 0xA0, /*[75] 0x6B k */
0xC0, 0x40, 0x40, 0x40, 0xE0, /*[76] 0x6C l */
0xE0, 0xE0, 0xE0, 0xA0, /*[77] 0x6D m */
0xC0, 0xA0, 0xA0, 0xA0, /*[78] 0x6E n */
0x40, 0xA0, 0xA0, 0x40, /*[79] 0x6F o */
0xC0, 0xA0, 0xA0, 0xC0, 0x80, /*[80] 0x70 p */
0x60, 0xA0, 0xA0, 0x60, 0x20, /*[81] 0x71 q */
0x60, 0x80, 0x80, 0x80, /*[82] 0x72 r */
0x60, 0xC0, 0x60, 0xC0, /*[83] 0x73 s */
0x40, 0xE0, 0x40, 0x40, 0x60, /*[84] 0x74 t */
0xA0, 0xA0, 0xA0, 0x60, /*[85] 0x75 u */
0xA0, 0xA0, 0xE0, 0x40, /*[86] 0x76 v */
0xA0, 0xE0, 0xE0, 0xE0, /*[87] 0x77 w */
0xA0, 0x40, 0x40, 0xA0, /*[88] 0x78 x */
0xA0, 0xA0, 0x60, 0x20, 0x40, /*[89] 0x79 y */
0xE0, 0x60, 0xC0, 0xE0, /*[90] 0x7A z */
0x60, 0x40, 0x80, 0x40, 0x60, /*[91] 0x7B braceleft */
0x80, 0x80, 0x00, 0x80, 0x80, /*[92] 0x7C bar */
0xC0, 0x40, 0x20, 0x40, 0xC0, /*[93] 0x7D braceright */
0x60, 0xC0, /*[94] 0x7E asciitilde */
0x80, 0x00, 0x80, 0x80, 0x80, /*[95] 0xA1 exclamdown */
0x40, 0xE0, 0x80, 0xE0, 0x40, /*[96] 0xA2 cent */
0x60, 0x40, 0xE0, 0x40, 0xE0, /*[97] 0xA3 sterling */
0xA0, 0x40, 0xE0, 0x40, 0xA0, /*[98] 0xA4 currency */
0xA0, 0xA0, 0x40, 0xE0, 0x40, /*[99] 0xA5 yen */
0x80, 0x80, 0x00, 0x80, 0x80, /*[100] 0xA6 brokenbar */
0x60, 0x40, 0xA0, 0x40, 0xC0, /*[101] 0xA7 section */
0xA0, /*[102] 0xA8 dieresis */
0x60, 0x80, 0x60, /*[103] 0xA9 copyright */
0x60, 0xA0, 0xE0, 0x00, 0xE0, /*[104] 0xAA ordfeminine */
0x40, 0x80, 0x40, /*[105] 0xAB guillemotleft */
0xE0, 0x20, /*[106] 0xAC logicalnot */
0xC0, /*[107] 0xAD softhyphen */
0xC0, 0xC0, 0xA0, /*[108] 0xAE registered */
0xE0, /*[109] 0xAF macron */
0xC0, 0xC0, 0x00, /*[110] 0xB0 degree */
0x40, 0xE0, 0x40, 0x00, 0xE0, /*[111] 0xB1 plusminus */
0xC0, 0x40, 0x60, /*[112] 0xB2 twosuperior */
0xE0, 0x60, 0xE0, /*[113] 0xB3 threesuperior */
0x40, 0x80, /*[114] 0xB4 acute */
0xA0, 0xA0, 0xA0, 0xC0, 0x80, /*[115] 0xB5 mu */
0x60, 0xA0, 0x60, 0x60, 0x60, /*[116] 0xB6 paragraph */
0xE0, 0xE0, 0xE0, /*[117] 0xB7 periodcentered */
0x40, 0x20, 0xC0, /*[118] 0xB8 cedilla */
0x80, 0x80, 0x80, /*[119] 0xB9 onesuperior */
0x40, 0xA0, 0x40, 0x00, 0xE0, /*[120] 0xBA ordmasculine */
0x80, 0x40, 0x80, /*[121] 0xBB guillemotright */
0x80, 0x80, 0x00, 0x60, 0x20, /*[122] 0xBC onequarter */
0x80, 0x80, 0x00, 0xC0, 0x60, /*[123] 0xBD onehalf */
0xC0, 0xC0, 0x00, 0x60, 0x20, /*[124] 0xBE threequarters */
0x40, 0x00, 0x40, 0x80, 0xE0, /*[125] 0xBF questiondown */
0x40, 0x20, 0x40, 0xE0, 0xA0, /*[126] 0xC0 Agrave */
0x40, 0x80, 0x40, 0xE0, 0xA0, /*[127] 0xC1 Aacute */
0xE0, 0x00, 0x40, 0xE0, 0xA0, /*[128] 0xC2 Acircumflex */
0x60, 0xC0, 0x40, 0xE0, 0xA0, /*[129] 0xC3 Atilde */
0xA0, 0x40, 0xA0, 0xE0, 0xA0, /*[130] 0xC4 Adieresis */
0xC0, 0xC0, 0xA0, 0xE0, 0xA0, /*[131] 0xC5 Aring */
0x60, 0xC0, 0xE0, 0xC0, 0xE0, /*[132] 0xC6 AE */
0x60, 0x80, 0x80, 0x60, 0x20, 0x40, /*[133] 0xC7 Ccedilla */
0x40, 0x20, 0xE0, 0xC0, 0xE0, /*[134] 0xC8 Egrave */
0x40, 0x80, 0xE0, 0xC0, 0xE0, /*[135] 0xC9 Eacute */
0xE0, 0x00, 0xE0, 0xC0, 0xE0, /*[136] 0xCA Ecircumflex */
0xA0, 0x00, 0xE0, 0xC0, 0xE0, /*[137] 0xCB Edieresis */
0x40, 0x20, 0xE0, 0x40, 0xE0, /*[138] 0xCC Igrave */
0x40, 0x80, 0xE0, 0x40, 0xE0, /*[139] 0xCD Iacute */
0xE0, 0x00, 0xE0, 0x40, 0xE0, /*[140] 0xCE Icircumflex */
0xA0, 0x00, 0xE0, 0x40, 0xE0, /*[141] 0xCF Idieresis */
0xC0, 0xA0, 0xE0, 0xA0, 0xC0, /*[142] 0xD0 Eth */
0xC0, 0x60, 0xA0, 0xE0, 0xA0, /*[143] 0xD1 Ntilde */
0x40, 0x20, 0xE0, 0xA0, 0xE0, /*[144] 0xD2 Ograve */
0x40, 0x80, 0xE0, 0xA0, 0xE0, /*[145] 0xD3 Oacute */
0xE0, 0x00, 0xE0, 0xA0, 0xE0, /*[146] 0xD4 Ocircumflex */
0xC0, 0x60, 0xE0, 0xA0, 0xE0, /*[147] 0xD5 Otilde */
0xA0, 0x00, 0xE0, 0xA0, 0xE0, /*[148] 0xD6 Odieresis */
0xA0, 0x40, 0xA0, /*[149] 0xD7 multiply */
0x60, 0xA0, 0xE0, 0xA0, 0xC0, /*[150] 0xD8 Oslash */
0x80, 0x40, 0xA0, 0xA0, 0xE0, /*[151] 0xD9 Ugrave */
0x20, 0x40, 0xA0, 0xA0, 0xE0, /*[152] 0xDA Uacute */
0xE0, 0x00, 0xA0, 0xA0, 0xE0, /*[153] 0xDB Ucircumflex */
0xA0, 0x00, 0xA0, 0xA0, 0xE0, /*[154] 0xDC Udieresis */
0x20, 0x40, 0xA0, 0xE0, 0x40, /*[155] 0xDD Yacute */
0x80, 0xE0, 0xA0, 0xE0, 0x80, /*[156] 0xDE Thorn */
0x60, 0xA0, 0xC0, 0xA0, 0xC0, 0x80, /*[157] 0xDF germandbls */
0x40, 0x20, 0x60, 0xA0, 0xE0, /*[158] 0xE0 agrave */
0x40, 0x80, 0x60, 0xA0, 0xE0, /*[159] 0xE1 aacute */
0xE0, 0x00, 0x60, 0xA0, 0xE0, /*[160] 0xE2 acircumflex */
0x60, 0xC0, 0x60, 0xA0, 0xE0, /*[161] 0xE3 atilde */
0xA0, 0x00, 0x60, 0xA0, 0xE0, /*[162] 0xE4 adieresis */
0x60, 0x60, 0x60, 0xA0, 0xE0, /*[163] 0xE5 aring */
0x60, 0xE0, 0xE0, 0xC0, /*[164] 0xE6 ae */
0x60, 0x80, 0x60, 0x20, 0x40, /*[165] 0xE7 ccedilla */
0x40, 0x20, 0x60, 0xE0, 0x60, /*[166] 0xE8 egrave */
0x40, 0x80, 0x60, 0xE0, 0x60, /*[167] 0xE9 eacute */
0xE0, 0x00, 0x60, 0xE0, 0x60, /*[168] 0xEA ecircumflex */
0xA0, 0x00, 0x60, 0xE0, 0x60, /*[169] 0xEB edieresis */
0x80, 0x40, 0x80, 0x80, 0x80, /*[170] 0xEC igrave */
0x40, 0x80, 0x40, 0x40, 0x40, /*[171] 0xED iacute */
0xE0, 0x00, 0x40, 0x40, 0x40, /*[172] 0xEE icircumflex */
0xA0, 0x00, 0x40, 0x40, 0x40, /*[173] 0xEF idieresis */
0x60, 0xC0, 0x60, 0xA0, 0x60, /*[174] 0xF0 eth */
0xC0, 0x60, 0xC0, 0xA0, 0xA0, /*[175] 0xF1 ntilde */
0x40, 0x20, 0x40, 0xA0, 0x40, /*[176] 0xF2 ograve */
0x40, 0x80, 0x40, 0xA0, 0x40, /*[177] 0xF3 oacute */
0xE0, 0x00, 0x40, 0xA0, 0x40, /*[178] 0xF4 ocircumflex */
0xC0, 0x60, 0x40, 0xA0, 0x40, /*[179] 0xF5 otilde */
0xA0, 0x00, 0x40, 0xA0, 0x40, /*[180] 0xF6 odieresis */
0x40, 0x00, 0xE0, 0x00, 0x40, /*[181] 0xF7 divide */
0x60, 0xE0, 0xA0, 0xC0, /*[182] 0xF8 oslash */
0x80, 0x40, 0xA0, 0xA0, 0x60, /*[183] 0xF9 ugrave */
0x20, 0x40, 0xA0, 0xA0, 0x60, /*[184] 0xFA uacute */
0xE0, 0x00, 0xA0, 0xA0, 0x60, /*[185] 0xFB ucircumflex */
0xA0, 0x00, 0xA0, 0xA0, 0x60, /*[186] 0xFC udieresis */
0x20, 0x40, 0xA0, 0x60, 0x20, 0x40, /*[187] 0xFD yacute */
0x80, 0xC0, 0xA0, 0xC0, 0x80, /*[188] 0xFE thorn */
0xA0, 0x00, 0xA0, 0x60, 0x20, 0x40, /*[189] 0xFF ydieresis */
0x00, /*[190] 0x11D gcircumflex */
0x60, 0xC0, 0xE0, 0xC0, 0x60, /*[191] 0x152 OE */
0x60, 0xE0, 0xC0, 0xE0, /*[192] 0x153 oe */
0xA0, 0x60, 0xC0, 0x60, 0xC0, /*[193] 0x160 Scaron */
0xA0, 0x60, 0xC0, 0x60, 0xC0, /*[194] 0x161 scaron */
0xA0, 0x00, 0xA0, 0x40, 0x40, /*[195] 0x178 Ydieresis */
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /*[196] 0x17D Zcaron */
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /*[197] 0x17E zcaron */
0x00, /*[198] 0xEA4 uni0EA4 */
0x00, /*[199] 0x13A0 uni13A0 */
0x80, /*[200] 0x2022 bullet */
0xA0, /*[201] 0x2026 ellipsis */
0x60, 0xE0, 0xE0, 0xC0, 0x60, /*[202] 0x20AC Euro */
0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /*[203] 0xFFFD uniFFFD */
};
/* {offset, width, height, advance cursor, x offset, y offset} */
const GFXglyph AwtrixFontGlyphs[] PROGMEM = {
{0, 8, 1, 2, 0, -5}, /*[0] 0x20 space */
{1, 8, 5, 2, 0, -5}, /*[1] 0x21 exclam */
{6, 8, 2, 4, 0, -5}, /*[2] 0x22 quotedbl */
{8, 8, 5, 4, 0, -5}, /*[3] 0x23 numbersign */
{13, 8, 5, 4, 0, -5}, /*[4] 0x24 dollar */
{18, 8, 5, 4, 0, -5}, /*[5] 0x25 percent */
{23, 8, 5, 4, 0, -5}, /*[6] 0x26 ampersand */
{28, 8, 2, 2, 0, -5}, /*[7] 0x27 quotesingle */
{30, 8, 5, 3, 0, -5}, /*[8] 0x28 parenleft */
{35, 8, 5, 3, 0, -5}, /*[9] 0x29 parenright */
{40, 8, 3, 4, 0, -5}, /*[10] 0x2A asterisk */
{43, 8, 3, 4, 0, -4}, /*[11] 0x2B plus */
{46, 8, 2, 3, 0, -2}, /*[12] 0x2C comma */
{48, 8, 1, 4, 0, -3}, /*[13] 0x2D hyphen */
{49, 8, 1, 2, 0, -1}, /*[14] 0x2E period */
{50, 8, 5, 4, 0, -5}, /*[15] 0x2F slash */
{55, 8, 5, 4, 0, -5}, /*[16] 0x30 zero */
{60, 8, 5, 4, 0, -5}, /*[17] 0x31 one */
{65, 8, 5, 4, 0, -5}, /*[18] 0x32 two */
{70, 8, 5, 4, 0, -5}, /*[19] 0x33 three */
{75, 8, 5, 4, 0, -5}, /*[20] 0x34 four */
{80, 8, 5, 4, 0, -5}, /*[21] 0x35 five */
{85, 8, 5, 4, 0, -5}, /*[22] 0x36 six */
{90, 8, 5, 4, 0, -5}, /*[23] 0x37 seven */
{95, 8, 5, 4, 0, -5}, /*[24] 0x38 eight */
{100, 8, 5, 4, 0, -5}, /*[25] 0x39 nine */
{105, 8, 3, 2, 0, -4}, /*[26] 0x3A colon */
{108, 8, 4, 3, 0, -4}, /*[27] 0x3B semicolon */
{112, 8, 5, 4, 0, -5}, /*[28] 0x3C less */
{117, 8, 3, 4, 0, -4}, /*[29] 0x3D equal */
{120, 8, 5, 4, 0, -5}, /*[30] 0x3E greater */
{125, 8, 5, 4, 0, -5}, /*[31] 0x3F question */
{130, 8, 5, 4, 0, -5}, /*[32] 0x40 at */
{135, 8, 5, 4, 0, -5}, /*[33] 0x41 A */
{140, 8, 5, 4, 0, -5}, /*[34] 0x42 B */
{145, 8, 5, 4, 0, -5}, /*[35] 0x43 C */
{150, 8, 5, 4, 0, -5}, /*[36] 0x44 D */
{155, 8, 5, 4, 0, -5}, /*[37] 0x45 E */
{160, 8, 5, 4, 0, -5}, /*[38] 0x46 F */
{165, 8, 5, 4, 0, -5}, /*[39] 0x47 G */
{170, 8, 5, 4, 0, -5}, /*[40] 0x48 H */
{175, 8, 5, 2, 0, -5}, /*[41] 0x49 I */
{180, 8, 5, 4, 0, -5}, /*[42] 0x4A J */
{185, 8, 5, 4, 0, -5}, /*[43] 0x4B K */
{190, 8, 5, 4, 0, -5}, /*[44] 0x4C L */
{195, 8, 5, 6, 0, -5}, /*[45] 0x4D M */
{200, 8, 5, 5, 0, -5}, /*[46] 0x4E N */
{205, 8, 5, 4, 0, -5}, /*[47] 0x4F O */
{210, 8, 5, 4, 0, -5}, /*[48] 0x50 P */
{215, 8, 5, 5, 0, -5}, /*[49] 0x51 Q */
{220, 8, 5, 4, 0, -5}, /*[50] 0x52 R */
{225, 8, 5, 4, 0, -5}, /*[51] 0x53 S */
{230, 8, 5, 4, 0, -5}, /*[52] 0x54 T */
{235, 8, 5, 4, 0, -5}, /*[53] 0x55 U */
{240, 8, 5, 4, 0, -5}, /*[54] 0x56 V */
{245, 8, 5, 6, 0, -5}, /*[55] 0x57 W */
{250, 8, 5, 4, 0, -5}, /*[56] 0x58 X */
{255, 8, 5, 4, 0, -5}, /*[57] 0x59 Y */
{260, 8, 5, 4, 0, -5}, /*[58] 0x5A Z */
{265, 8, 5, 4, 0, -5}, /*[59] 0x5B bracketleft */
{270, 8, 3, 4, 0, -4}, /*[60] 0x5C backslash */
{273, 8, 5, 4, 0, -5}, /*[61] 0x5D bracketright */
{278, 8, 2, 4, 0, -5}, /*[62] 0x5E asciicircum */
{280, 8, 1, 4, 0, -1}, /*[63] 0x5F underscore */
{281, 8, 2, 3, 0, -5}, /*[64] 0x60 grave */
{283, 8, 4, 4, 0, -4}, /*[65] 0x61 a */
{287, 8, 5, 4, 0, -5}, /*[66] 0x62 b */
{292, 8, 4, 4, 0, -4}, /*[67] 0x63 c */
{296, 8, 5, 4, 0, -5}, /*[68] 0x64 d */
{301, 8, 4, 4, 0, -4}, /*[69] 0x65 e */
{305, 8, 5, 4, 0, -5}, /*[70] 0x66 f */
{310, 8, 5, 4, 0, -4}, /*[71] 0x67 g */
{315, 8, 5, 4, 0, -5}, /*[72] 0x68 h */
{320, 8, 5, 2, 0, -5}, /*[73] 0x69 i */
{325, 8, 6, 4, 0, -5}, /*[74] 0x6A j */
{331, 8, 5, 4, 0, -5}, /*[75] 0x6B k */
{336, 8, 5, 4, 0, -5}, /*[76] 0x6C l */
{341, 8, 4, 4, 0, -4}, /*[77] 0x6D m */
{345, 8, 4, 4, 0, -4}, /*[78] 0x6E n */
{349, 8, 4, 4, 0, -4}, /*[79] 0x6F o */
{353, 8, 5, 4, 0, -4}, /*[80] 0x70 p */
{358, 8, 5, 4, 0, -4}, /*[81] 0x71 q */
{363, 8, 4, 4, 0, -4}, /*[82] 0x72 r */
{367, 8, 4, 4, 0, -4}, /*[83] 0x73 s */
{371, 8, 5, 4, 0, -5}, /*[84] 0x74 t */
{376, 8, 4, 4, 0, -4}, /*[85] 0x75 u */
{380, 8, 4, 4, 0, -4}, /*[86] 0x76 v */
{384, 8, 4, 4, 0, -4}, /*[87] 0x77 w */
{388, 8, 4, 4, 0, -4}, /*[88] 0x78 x */
{392, 8, 5, 4, 0, -4}, /*[89] 0x79 y */
{397, 8, 4, 4, 0, -4}, /*[90] 0x7A z */
{401, 8, 5, 4, 0, -5}, /*[91] 0x7B braceleft */
{406, 8, 5, 2, 0, -5}, /*[92] 0x7C bar */
{411, 8, 5, 4, 0, -5}, /*[93] 0x7D braceright */
{416, 8, 2, 4, 0, -5}, /*[94] 0x7E asciitilde */
{418, 8, 5, 2, 0, -5}, /*[95] 0xA1 exclamdown */
{423, 8, 5, 4, 0, -5}, /*[96] 0xA2 cent */
{428, 8, 5, 4, 0, -5}, /*[97] 0xA3 sterling */
{433, 8, 5, 4, 0, -5}, /*[98] 0xA4 currency */
{438, 8, 5, 4, 0, -5}, /*[99] 0xA5 yen */
{443, 8, 5, 2, 0, -5}, /*[100] 0xA6 brokenbar */
{448, 8, 5, 4, 0, -5}, /*[101] 0xA7 section */
{453, 8, 1, 4, 0, -5}, /*[102] 0xA8 dieresis */
{454, 8, 3, 4, 0, -5}, /*[103] 0xA9 copyright */
{457, 8, 5, 4, 0, -5}, /*[104] 0xAA ordfeminine */
{462, 8, 3, 3, 0, -5}, /*[105] 0xAB guillemotleft */
{465, 8, 2, 4, 0, -4}, /*[106] 0xAC logicalnot */
{467, 8, 1, 3, 0, -3}, /*[107] 0xAD softhyphen */
{468, 8, 3, 4, 0, -5}, /*[108] 0xAE registered */
{471, 8, 1, 4, 0, -5}, /*[109] 0xAF macron */
{472, 8, 3, 4, 0, -5}, /*[110] 0xB0 degree */
{475, 8, 5, 4, 0, -5}, /*[111] 0xB1 plusminus */
{480, 8, 3, 4, 0, -5}, /*[112] 0xB2 twosuperior */
{483, 8, 3, 4, 0, -5}, /*[113] 0xB3 threesuperior */
{486, 8, 2, 3, 0, -5}, /*[114] 0xB4 acute */
{488, 8, 5, 4, 0, -5}, /*[115] 0xB5 mu */
{493, 8, 5, 4, 0, -5}, /*[116] 0xB6 paragraph */
{498, 8, 3, 4, 0, -4}, /*[117] 0xB7 periodcentered */
{501, 8, 3, 4, 0, -3}, /*[118] 0xB8 cedilla */
{504, 8, 3, 2, 0, -5}, /*[119] 0xB9 onesuperior */
{507, 8, 5, 4, 0, -5}, /*[120] 0xBA ordmasculine */
{512, 8, 3, 3, 0, -5}, /*[121] 0xBB guillemotright */
{515, 8, 5, 4, 0, -5}, /*[122] 0xBC onequarter */
{520, 8, 5, 4, 0, -5}, /*[123] 0xBD onehalf */
{525, 8, 5, 4, 0, -5}, /*[124] 0xBE threequarters */
{530, 8, 5, 4, 0, -5}, /*[125] 0xBF questiondown */
{535, 8, 5, 4, 0, -5}, /*[126] 0xC0 Agrave */
{540, 8, 5, 4, 0, -5}, /*[127] 0xC1 Aacute */
{545, 8, 5, 4, 0, -5}, /*[128] 0xC2 Acircumflex */
{550, 8, 5, 4, 0, -5}, /*[129] 0xC3 Atilde */
{555, 8, 5, 4, 0, -5}, /*[130] 0xC4 Adieresis */
{560, 8, 5, 4, 0, -5}, /*[131] 0xC5 Aring */
{565, 8, 5, 4, 0, -5}, /*[132] 0xC6 AE */
{570, 8, 6, 4, 0, -5}, /*[133] 0xC7 Ccedilla */
{576, 8, 5, 4, 0, -5}, /*[134] 0xC8 Egrave */
{581, 8, 5, 4, 0, -5}, /*[135] 0xC9 Eacute */
{586, 8, 5, 4, 0, -5}, /*[136] 0xCA Ecircumflex */
{591, 8, 5, 4, 0, -5}, /*[137] 0xCB Edieresis */
{596, 8, 5, 4, 0, -5}, /*[138] 0xCC Igrave */
{601, 8, 5, 4, 0, -5}, /*[139] 0xCD Iacute */
{606, 8, 5, 4, 0, -5}, /*[140] 0xCE Icircumflex */
{611, 8, 5, 4, 0, -5}, /*[141] 0xCF Idieresis */
{616, 8, 5, 4, 0, -5}, /*[142] 0xD0 Eth */
{621, 8, 5, 4, 0, -5}, /*[143] 0xD1 Ntilde */
{626, 8, 5, 4, 0, -5}, /*[144] 0xD2 Ograve */
{631, 8, 5, 4, 0, -5}, /*[145] 0xD3 Oacute */
{636, 8, 5, 4, 0, -5}, /*[146] 0xD4 Ocircumflex */
{641, 8, 5, 4, 0, -5}, /*[147] 0xD5 Otilde */
{646, 8, 5, 4, 0, -5}, /*[148] 0xD6 Odieresis */
{651, 8, 3, 4, 0, -4}, /*[149] 0xD7 multiply */
{654, 8, 5, 4, 0, -5}, /*[150] 0xD8 Oslash */
{659, 8, 5, 4, 0, -5}, /*[151] 0xD9 Ugrave */
{664, 8, 5, 4, 0, -5}, /*[152] 0xDA Uacute */
{669, 8, 5, 4, 0, -5}, /*[153] 0xDB Ucircumflex */
{674, 8, 5, 4, 0, -5}, /*[154] 0xDC Udieresis */
{679, 8, 5, 4, 0, -5}, /*[155] 0xDD Yacute */
{684, 8, 5, 4, 0, -5}, /*[156] 0xDE Thorn */
{689, 8, 6, 4, 0, -5}, /*[157] 0xDF germandbls */
{695, 8, 5, 4, 0, -5}, /*[158] 0xE0 agrave */
{700, 8, 5, 4, 0, -5}, /*[159] 0xE1 aacute */
{705, 8, 5, 4, 0, -5}, /*[160] 0xE2 acircumflex */
{710, 8, 5, 4, 0, -5}, /*[161] 0xE3 atilde */
{715, 8, 5, 4, 0, -5}, /*[162] 0xE4 adieresis */
{720, 8, 5, 4, 0, -5}, /*[163] 0xE5 aring */
{725, 8, 4, 4, 0, -4}, /*[164] 0xE6 ae */
{729, 8, 5, 4, 0, -4}, /*[165] 0xE7 ccedilla */
{734, 8, 5, 4, 0, -5}, /*[166] 0xE8 egrave */
{739, 8, 5, 4, 0, -5}, /*[167] 0xE9 eacute */
{744, 8, 5, 4, 0, -5}, /*[168] 0xEA ecircumflex */
{749, 8, 5, 4, 0, -5}, /*[169] 0xEB edieresis */
{754, 8, 5, 3, 0, -5}, /*[170] 0xEC igrave */
{759, 8, 5, 3, 0, -5}, /*[171] 0xED iacute */
{764, 8, 5, 4, 0, -5}, /*[172] 0xEE icircumflex */
{769, 8, 5, 4, 0, -5}, /*[173] 0xEF idieresis */
{774, 8, 5, 4, 0, -5}, /*[174] 0xF0 eth */
{779, 8, 5, 4, 0, -5}, /*[175] 0xF1 ntilde */
{784, 8, 5, 4, 0, -5}, /*[176] 0xF2 ograve */
{789, 8, 5, 4, 0, -5}, /*[177] 0xF3 oacute */
{794, 8, 5, 4, 0, -5}, /*[178] 0xF4 ocircumflex */
{799, 8, 5, 4, 0, -5}, /*[179] 0xF5 otilde */
{804, 8, 5, 4, 0, -5}, /*[180] 0xF6 odieresis */
{809, 8, 5, 4, 0, -5}, /*[181] 0xF7 divide */
{814, 8, 4, 4, 0, -4}, /*[182] 0xF8 oslash */
{818, 8, 5, 4, 0, -5}, /*[183] 0xF9 ugrave */
{823, 8, 5, 4, 0, -5}, /*[184] 0xFA uacute */
{828, 8, 5, 4, 0, -5}, /*[185] 0xFB ucircumflex */
{833, 8, 5, 4, 0, -5}, /*[186] 0xFC udieresis */
{838, 8, 6, 4, 0, -5}, /*[187] 0xFD yacute */
{844, 8, 5, 4, 0, -4}, /*[188] 0xFE thorn */
{849, 8, 6, 4, 0, -5}, /*[189] 0xFF ydieresis */
{855, 8, 1, 2, 0, -1}, /*[190] 0x11D gcircumflex */
{856, 8, 5, 4, 0, -5}, /*[191] 0x152 OE */
{861, 8, 4, 4, 0, -4}, /*[192] 0x153 oe */
{865, 8, 5, 4, 0, -5}, /*[193] 0x160 Scaron */
{870, 8, 5, 4, 0, -5}, /*[194] 0x161 scaron */
{875, 8, 5, 4, 0, -5}, /*[195] 0x178 Ydieresis */
{880, 8, 5, 4, 0, -5}, /*[196] 0x17D Zcaron */
{885, 8, 5, 4, 0, -5}, /*[197] 0x17E zcaron */
{890, 8, 1, 2, 0, -1}, /*[198] 0xEA4 uni0EA4 */
{891, 8, 1, 2, 0, -1}, /*[199] 0x13A0 uni13A0 */
{892, 8, 1, 2, 0, -3}, /*[200] 0x2022 bullet */
{893, 8, 1, 4, 0, -1}, /*[201] 0x2026 ellipsis */
{894, 8, 5, 4, 0, -5}, /*[202] 0x20AC Euro */
{899, 8, 5, 4, 0, -5}, /*[203] 0xFFFD uniFFFD */
};
const GFXfont AwtrixFont PROGMEM = {
(uint8_t *)AwtrixBitmaps,
(GFXglyph *)AwtrixFontGlyphs,
0x20, 0xFF, 6};
/**
** The original 3x5 font is licensed under the 3-clause BSD license:
**
** Copyright 1999 Brian J. Swetland
** Copyright 1999 Vassilii Khachaturov
** Portions (of vt100.c/vt100.h) copyright Dan Marks
** Modifications for Awtrix for improved readability and LaMetric Style Copyright 2023 Blueforcer
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions, and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions, and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the authors may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Modifications to TomThumb for improved readability are from Robey Pointer,
** see:
** http://robey.lag.net/2010/01/23/tiny-monospace-font.html
**
** Modifications for Awtrix for improved readability and LaMetric Style are from
** Blueforcer, Yann and hollyghost
**
** The original author does not have any objection to relicensing of Robey
** Pointer's modifications (in this file) in a more permissive license. See
** the discussion at the above blog, and also here:
** http://opengameart.org/forumtopic/how-to-submit-art-using-the-3-clause-bsd-license
**
** Feb 21, 2016: Conversion from Linux BDF --> Adafruit GFX font,
** with the help of this Python script:
** https://gist.github.com/skelliam/322d421f028545f16f6d
** William Skellenger (williamj@skellenger.net)
** Twitter: @skelliam
**
*/
// AwtrixFont Version 20230215
const uint8_t AwtrixBitmaps[] PROGMEM = {
0x00, /*[0] 0x20 space */
0x80, 0x80, 0x80, 0x00, 0x80, /*[1] 0x21 exclam */
0xA0, 0xA0, /*[2] 0x22 quotedbl */
0xA0, 0xE0, 0xA0, 0xE0, 0xA0, /*[3] 0x23 numbersign */
0x60, 0xC0, 0x60, 0xC0, 0x40, /*[4] 0x24 dollar */
0xA0, 0x20, 0x40, 0x80, 0xA0, /*[5] 0x25 percent */
0xC0, 0xC0, 0xE0, 0xA0, 0x60, /*[6] 0x26 ampersand */
0x80, 0x80, /*[7] 0x27 quotesingle */
0x40, 0x80, 0x80, 0x80, 0x40, /*[8] 0x28 parenleft */
0x80, 0x40, 0x40, 0x40, 0x80, /*[9] 0x29 parenright */
0xA0, 0x40, 0xA0, /*[10] 0x2A asterisk */
0x40, 0xE0, 0x40, /*[11] 0x2B plus */
0x40, 0x80, /*[12] 0x2C comma */
0xE0, /*[13] 0x2D hyphen */
0x80, /*[14] 0x2E period */
0x20, 0x20, 0x40, 0x80, 0x80, /*[15] 0x2F slash */
0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /*[16] 0x30 zero */
0x40, 0xC0, 0x40, 0x40, 0xE0, /*[17] 0x31 one */
0xE0, 0x20, 0xE0, 0x80, 0xE0, /*[18] 0x32 two */
0xE0, 0x20, 0xE0, 0x20, 0xE0, /*[19] 0x33 three */
0xA0, 0xA0, 0xE0, 0x20, 0x20, /*[20] 0x34 four */
0xE0, 0x80, 0xE0, 0x20, 0xE0, /*[21] 0x35 five */
0xE0, 0x80, 0xE0, 0xA0, 0xE0, /*[22] 0x36 six */
0xE0, 0x20, 0x20, 0x20, 0x20, /*[23] 0x37 seven */
0xE0, 0xA0, 0xE0, 0xA0, 0xE0, /*[24] 0x38 eight */
0xE0, 0xA0, 0xE0, 0x20, 0xE0, /*[25] 0x39 nine */
0x80, 0x00, 0x80, /*[26] 0x3A colon */
0x40, 0x00, 0x40, 0x80, /*[27] 0x3B semicolon */
0x20, 0x40, 0x80, 0x40, 0x20, /*[28] 0x3C less */
0xE0, 0x00, 0xE0, /*[29] 0x3D equal */
0x80, 0x40, 0x20, 0x40, 0x80, /*[30] 0x3E greater */
0xE0, 0x20, 0x40, 0x00, 0x40, /*[31] 0x3F question */
0x40, 0xA0, 0xE0, 0x80, 0x60, /*[32] 0x40 at */
0xC0, 0xA0, 0xE0, 0xA0, 0xA0, /*[33] 0x41 A */
0xC0, 0xA0, 0xC0, 0xA0, 0xC0, /*[34] 0x42 B */
0x40, 0xA0, 0x80, 0xA0, 0x40, /*[35] 0x43 C */
0xC0, 0xA0, 0xA0, 0xA0, 0xC0, /*[36] 0x44 D */
0xE0, 0x80, 0xE0, 0x80, 0xE0, /*[37] 0x45 E */
0xE0, 0x80, 0xE0, 0x80, 0x80, /*[38] 0x46 F */
0x60, 0x80, 0xA0, 0xA0, 0x60, /*[39] 0x47 G */
0xA0, 0xA0, 0xE0, 0xA0, 0xA0, /*[40] 0x48 H */
0x80, 0x80, 0x80, 0x80, 0x80, /*[41] 0x49 I */
0x20, 0x20, 0x20, 0xA0, 0x40, /*[42] 0x4A J */
0xA0, 0xA0, 0xC0, 0xA0, 0xA0, /*[43] 0x4B K */
0x80, 0x80, 0x80, 0x80, 0xE0, /*[44] 0x4C L */
0x88, 0xD8, 0xA8, 0x88, 0x88, /*[45] 0x4D M */
0x90, 0xD0, 0xB0, 0x90, 0x90, /*[46] 0x4E N */
0x40, 0xA0, 0xA0, 0xA0, 0x40, /*[47] 0x4F O */
0xE0, 0xA0, 0xC0, 0x80, 0x80, /*[48] 0x50 P */
0x40, 0xA0, 0xA0, 0xA0, 0x70, /*[49] 0x51 Q */
0xE0, 0xA0, 0xC0, 0xA0, 0xA0, /*[50] 0x52 R */
0xE0, 0x80, 0xE0, 0x20, 0xE0, /*[51] 0x53 S */
0xE0, 0x40, 0x40, 0x40, 0x40, /*[52] 0x54 T */
0xA0, 0xA0, 0xA0, 0xA0, 0xE0, /*[53] 0x55 U */
0xA0, 0xA0, 0xA0, 0xA0, 0x40, /*[54] 0x56 V */
0x88, 0x88, 0x88, 0xA8, 0x50, /*[55] 0x57 W */
0xA0, 0xA0, 0x40, 0xA0, 0xA0, /*[56] 0x58 X */
0xA0, 0xA0, 0xE0, 0x20, 0xC0, /*[57] 0x59 Y */
0xE0, 0x20, 0x40, 0x80, 0xE0, /*[58] 0x5A Z */
0xE0, 0x80, 0x80, 0x80, 0xE0, /*[59] 0x5B bracketleft */
0x80, 0x40, 0x20, /*[60] 0x5C backslash */
0xE0, 0x20, 0x20, 0x20, 0xE0, /*[61] 0x5D bracketright */
0x40, 0xA0, /*[62] 0x5E asciicircum */
0xE0, /*[63] 0x5F underscore */
0x80, 0x40, /*[64] 0x60 grave */
0xC0, 0x60, 0xA0, 0xE0, /*[65] 0x61 a */
0x80, 0xC0, 0xA0, 0xA0, 0xC0, /*[66] 0x62 b */
0x60, 0x80, 0x80, 0x60, /*[67] 0x63 c */
0x20, 0x60, 0xA0, 0xA0, 0x60, /*[68] 0x64 d */
0x60, 0xA0, 0xC0, 0x60, /*[69] 0x65 e */
0x20, 0x40, 0xE0, 0x40, 0x40, /*[70] 0x66 f */
0x60, 0xA0, 0xE0, 0x20, 0x40, /*[71] 0x67 g */
0x80, 0xC0, 0xA0, 0xA0, 0xA0, /*[72] 0x68 h */
0x80, 0x00, 0x80, 0x80, 0x80, /*[73] 0x69 i */
0x20, 0x00, 0x20, 0x20, 0xA0, 0x40, /*[74] 0x6A j */
0x80, 0xA0, 0xC0, 0xC0, 0xA0, /*[75] 0x6B k */
0xC0, 0x40, 0x40, 0x40, 0xE0, /*[76] 0x6C l */
0xE0, 0xE0, 0xE0, 0xA0, /*[77] 0x6D m */
0xC0, 0xA0, 0xA0, 0xA0, /*[78] 0x6E n */
0x40, 0xA0, 0xA0, 0x40, /*[79] 0x6F o */
0xC0, 0xA0, 0xA0, 0xC0, 0x80, /*[80] 0x70 p */
0x60, 0xA0, 0xA0, 0x60, 0x20, /*[81] 0x71 q */
0x60, 0x80, 0x80, 0x80, /*[82] 0x72 r */
0x60, 0xC0, 0x60, 0xC0, /*[83] 0x73 s */
0x40, 0xE0, 0x40, 0x40, 0x60, /*[84] 0x74 t */
0xA0, 0xA0, 0xA0, 0x60, /*[85] 0x75 u */
0xA0, 0xA0, 0xE0, 0x40, /*[86] 0x76 v */
0xA0, 0xE0, 0xE0, 0xE0, /*[87] 0x77 w */
0xA0, 0x40, 0x40, 0xA0, /*[88] 0x78 x */
0xA0, 0xA0, 0x60, 0x20, 0x40, /*[89] 0x79 y */
0xE0, 0x60, 0xC0, 0xE0, /*[90] 0x7A z */
0x60, 0x40, 0x80, 0x40, 0x60, /*[91] 0x7B braceleft */
0x80, 0x80, 0x00, 0x80, 0x80, /*[92] 0x7C bar */
0xC0, 0x40, 0x20, 0x40, 0xC0, /*[93] 0x7D braceright */
0x60, 0xC0, /*[94] 0x7E asciitilde */
0x80, 0x00, 0x80, 0x80, 0x80, /*[95] 0xA1 exclamdown */
0x40, 0xE0, 0x80, 0xE0, 0x40, /*[96] 0xA2 cent */
0x60, 0x40, 0xE0, 0x40, 0xE0, /*[97] 0xA3 sterling */
0xA0, 0x40, 0xE0, 0x40, 0xA0, /*[98] 0xA4 currency */
0xA0, 0xA0, 0x40, 0xE0, 0x40, /*[99] 0xA5 yen */
0x80, 0x80, 0x00, 0x80, 0x80, /*[100] 0xA6 brokenbar */
0x60, 0x40, 0xA0, 0x40, 0xC0, /*[101] 0xA7 section */
0xA0, /*[102] 0xA8 dieresis */
0x60, 0x80, 0x60, /*[103] 0xA9 copyright */
0x60, 0xA0, 0xE0, 0x00, 0xE0, /*[104] 0xAA ordfeminine */
0x40, 0x80, 0x40, /*[105] 0xAB guillemotleft */
0xE0, 0x20, /*[106] 0xAC logicalnot */
0xC0, /*[107] 0xAD softhyphen */
0xC0, 0xC0, 0xA0, /*[108] 0xAE registered */
0xE0, /*[109] 0xAF macron */
0xC0, 0xC0, 0x00, /*[110] 0xB0 degree */
0x40, 0xE0, 0x40, 0x00, 0xE0, /*[111] 0xB1 plusminus */
0xC0, 0x40, 0x60, /*[112] 0xB2 twosuperior */
0xE0, 0x60, 0xE0, /*[113] 0xB3 threesuperior */
0x40, 0x80, /*[114] 0xB4 acute */
0xA0, 0xA0, 0xA0, 0xC0, 0x80, /*[115] 0xB5 mu */
0x60, 0xA0, 0x60, 0x60, 0x60, /*[116] 0xB6 paragraph */
0xE0, 0xE0, 0xE0, /*[117] 0xB7 periodcentered */
0x40, 0x20, 0xC0, /*[118] 0xB8 cedilla */
0x80, 0x80, 0x80, /*[119] 0xB9 onesuperior */
0x40, 0xA0, 0x40, 0x00, 0xE0, /*[120] 0xBA ordmasculine */
0x80, 0x40, 0x80, /*[121] 0xBB guillemotright */
0x80, 0x80, 0x00, 0x60, 0x20, /*[122] 0xBC onequarter */
0x80, 0x80, 0x00, 0xC0, 0x60, /*[123] 0xBD onehalf */
0xC0, 0xC0, 0x00, 0x60, 0x20, /*[124] 0xBE threequarters */
0x40, 0x00, 0x40, 0x80, 0xE0, /*[125] 0xBF questiondown */
0x40, 0x20, 0x40, 0xE0, 0xA0, /*[126] 0xC0 Agrave */
0x40, 0x80, 0x40, 0xE0, 0xA0, /*[127] 0xC1 Aacute */
0xE0, 0x00, 0x40, 0xE0, 0xA0, /*[128] 0xC2 Acircumflex */
0x60, 0xC0, 0x40, 0xE0, 0xA0, /*[129] 0xC3 Atilde */
0xA0, 0x40, 0xA0, 0xE0, 0xA0, /*[130] 0xC4 Adieresis */
0xC0, 0xC0, 0xA0, 0xE0, 0xA0, /*[131] 0xC5 Aring */
0x60, 0xC0, 0xE0, 0xC0, 0xE0, /*[132] 0xC6 AE */
0x60, 0x80, 0x80, 0x60, 0x20, 0x40, /*[133] 0xC7 Ccedilla */
0x40, 0x20, 0xE0, 0xC0, 0xE0, /*[134] 0xC8 Egrave */
0x40, 0x80, 0xE0, 0xC0, 0xE0, /*[135] 0xC9 Eacute */
0xE0, 0x00, 0xE0, 0xC0, 0xE0, /*[136] 0xCA Ecircumflex */
0xA0, 0x00, 0xE0, 0xC0, 0xE0, /*[137] 0xCB Edieresis */
0x40, 0x20, 0xE0, 0x40, 0xE0, /*[138] 0xCC Igrave */
0x40, 0x80, 0xE0, 0x40, 0xE0, /*[139] 0xCD Iacute */
0xE0, 0x00, 0xE0, 0x40, 0xE0, /*[140] 0xCE Icircumflex */
0xA0, 0x00, 0xE0, 0x40, 0xE0, /*[141] 0xCF Idieresis */
0xC0, 0xA0, 0xE0, 0xA0, 0xC0, /*[142] 0xD0 Eth */
0xC0, 0x60, 0xA0, 0xE0, 0xA0, /*[143] 0xD1 Ntilde */
0x40, 0x20, 0xE0, 0xA0, 0xE0, /*[144] 0xD2 Ograve */
0x40, 0x80, 0xE0, 0xA0, 0xE0, /*[145] 0xD3 Oacute */
0xE0, 0x00, 0xE0, 0xA0, 0xE0, /*[146] 0xD4 Ocircumflex */
0xC0, 0x60, 0xE0, 0xA0, 0xE0, /*[147] 0xD5 Otilde */
0xA0, 0x00, 0xE0, 0xA0, 0xE0, /*[148] 0xD6 Odieresis */
0xA0, 0x40, 0xA0, /*[149] 0xD7 multiply */
0x60, 0xA0, 0xE0, 0xA0, 0xC0, /*[150] 0xD8 Oslash */
0x80, 0x40, 0xA0, 0xA0, 0xE0, /*[151] 0xD9 Ugrave */
0x20, 0x40, 0xA0, 0xA0, 0xE0, /*[152] 0xDA Uacute */
0xE0, 0x00, 0xA0, 0xA0, 0xE0, /*[153] 0xDB Ucircumflex */
0xA0, 0x00, 0xA0, 0xA0, 0xE0, /*[154] 0xDC Udieresis */
0x20, 0x40, 0xA0, 0xE0, 0x40, /*[155] 0xDD Yacute */
0x80, 0xE0, 0xA0, 0xE0, 0x80, /*[156] 0xDE Thorn */
0x60, 0xA0, 0xC0, 0xA0, 0xC0, 0x80, /*[157] 0xDF germandbls */
0x40, 0x20, 0x60, 0xA0, 0xE0, /*[158] 0xE0 agrave */
0x40, 0x80, 0x60, 0xA0, 0xE0, /*[159] 0xE1 aacute */
0xE0, 0x00, 0x60, 0xA0, 0xE0, /*[160] 0xE2 acircumflex */
0x60, 0xC0, 0x60, 0xA0, 0xE0, /*[161] 0xE3 atilde */
0xA0, 0x00, 0x60, 0xA0, 0xE0, /*[162] 0xE4 adieresis */
0x60, 0x60, 0x60, 0xA0, 0xE0, /*[163] 0xE5 aring */
0x60, 0xE0, 0xE0, 0xC0, /*[164] 0xE6 ae */
0x60, 0x80, 0x60, 0x20, 0x40, /*[165] 0xE7 ccedilla */
0x40, 0x20, 0x60, 0xE0, 0x60, /*[166] 0xE8 egrave */
0x40, 0x80, 0x60, 0xE0, 0x60, /*[167] 0xE9 eacute */
0xE0, 0x00, 0x60, 0xE0, 0x60, /*[168] 0xEA ecircumflex */
0xA0, 0x00, 0x60, 0xE0, 0x60, /*[169] 0xEB edieresis */
0x80, 0x40, 0x80, 0x80, 0x80, /*[170] 0xEC igrave */
0x40, 0x80, 0x40, 0x40, 0x40, /*[171] 0xED iacute */
0xE0, 0x00, 0x40, 0x40, 0x40, /*[172] 0xEE icircumflex */
0xA0, 0x00, 0x40, 0x40, 0x40, /*[173] 0xEF idieresis */
0x60, 0xC0, 0x60, 0xA0, 0x60, /*[174] 0xF0 eth */
0xC0, 0x60, 0xC0, 0xA0, 0xA0, /*[175] 0xF1 ntilde */
0x40, 0x20, 0x40, 0xA0, 0x40, /*[176] 0xF2 ograve */
0x40, 0x80, 0x40, 0xA0, 0x40, /*[177] 0xF3 oacute */
0xE0, 0x00, 0x40, 0xA0, 0x40, /*[178] 0xF4 ocircumflex */
0xC0, 0x60, 0x40, 0xA0, 0x40, /*[179] 0xF5 otilde */
0xA0, 0x00, 0x40, 0xA0, 0x40, /*[180] 0xF6 odieresis */
0x40, 0x00, 0xE0, 0x00, 0x40, /*[181] 0xF7 divide */
0x60, 0xE0, 0xA0, 0xC0, /*[182] 0xF8 oslash */
0x80, 0x40, 0xA0, 0xA0, 0x60, /*[183] 0xF9 ugrave */
0x20, 0x40, 0xA0, 0xA0, 0x60, /*[184] 0xFA uacute */
0xE0, 0x00, 0xA0, 0xA0, 0x60, /*[185] 0xFB ucircumflex */
0xA0, 0x00, 0xA0, 0xA0, 0x60, /*[186] 0xFC udieresis */
0x20, 0x40, 0xA0, 0x60, 0x20, 0x40, /*[187] 0xFD yacute */
0x80, 0xC0, 0xA0, 0xC0, 0x80, /*[188] 0xFE thorn */
0xA0, 0x00, 0xA0, 0x60, 0x20, 0x40, /*[189] 0xFF ydieresis */
0x00, /*[190] 0x11D gcircumflex */
0x60, 0xC0, 0xE0, 0xC0, 0x60, /*[191] 0x152 OE */
0x60, 0xE0, 0xC0, 0xE0, /*[192] 0x153 oe */
0xA0, 0x60, 0xC0, 0x60, 0xC0, /*[193] 0x160 Scaron */
0xA0, 0x60, 0xC0, 0x60, 0xC0, /*[194] 0x161 scaron */
0xA0, 0x00, 0xA0, 0x40, 0x40, /*[195] 0x178 Ydieresis */
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /*[196] 0x17D Zcaron */
0xA0, 0xE0, 0x60, 0xC0, 0xE0, /*[197] 0x17E zcaron */
0x00, /*[198] 0xEA4 uni0EA4 */
0x00, /*[199] 0x13A0 uni13A0 */
0x80, /*[200] 0x2022 bullet */
0xA0, /*[201] 0x2026 ellipsis */
0x60, 0xE0, 0xE0, 0xC0, 0x60, /*[202] 0x20AC Euro */
0xE0, 0xA0, 0xA0, 0xA0, 0xE0, /*[203] 0xFFFD uniFFFD */
};
/* {offset, width, height, advance cursor, x offset, y offset} */
const GFXglyph AwtrixFontGlyphs[] PROGMEM = {
{0, 8, 1, 2, 0, -5}, /*[0] 0x20 space */
{1, 8, 5, 2, 0, -5}, /*[1] 0x21 exclam */
{6, 8, 2, 4, 0, -5}, /*[2] 0x22 quotedbl */
{8, 8, 5, 4, 0, -5}, /*[3] 0x23 numbersign */
{13, 8, 5, 4, 0, -5}, /*[4] 0x24 dollar */
{18, 8, 5, 4, 0, -5}, /*[5] 0x25 percent */
{23, 8, 5, 4, 0, -5}, /*[6] 0x26 ampersand */
{28, 8, 2, 2, 0, -5}, /*[7] 0x27 quotesingle */
{30, 8, 5, 3, 0, -5}, /*[8] 0x28 parenleft */
{35, 8, 5, 3, 0, -5}, /*[9] 0x29 parenright */
{40, 8, 3, 4, 0, -5}, /*[10] 0x2A asterisk */
{43, 8, 3, 4, 0, -4}, /*[11] 0x2B plus */
{46, 8, 2, 3, 0, -2}, /*[12] 0x2C comma */
{48, 8, 1, 4, 0, -3}, /*[13] 0x2D hyphen */
{49, 8, 1, 2, 0, -1}, /*[14] 0x2E period */
{50, 8, 5, 4, 0, -5}, /*[15] 0x2F slash */
{55, 8, 5, 4, 0, -5}, /*[16] 0x30 zero */
{60, 8, 5, 4, 0, -5}, /*[17] 0x31 one */
{65, 8, 5, 4, 0, -5}, /*[18] 0x32 two */
{70, 8, 5, 4, 0, -5}, /*[19] 0x33 three */
{75, 8, 5, 4, 0, -5}, /*[20] 0x34 four */
{80, 8, 5, 4, 0, -5}, /*[21] 0x35 five */
{85, 8, 5, 4, 0, -5}, /*[22] 0x36 six */
{90, 8, 5, 4, 0, -5}, /*[23] 0x37 seven */
{95, 8, 5, 4, 0, -5}, /*[24] 0x38 eight */
{100, 8, 5, 4, 0, -5}, /*[25] 0x39 nine */
{105, 8, 3, 2, 0, -4}, /*[26] 0x3A colon */
{108, 8, 4, 3, 0, -4}, /*[27] 0x3B semicolon */
{112, 8, 5, 4, 0, -5}, /*[28] 0x3C less */
{117, 8, 3, 4, 0, -4}, /*[29] 0x3D equal */
{120, 8, 5, 4, 0, -5}, /*[30] 0x3E greater */
{125, 8, 5, 4, 0, -5}, /*[31] 0x3F question */
{130, 8, 5, 4, 0, -5}, /*[32] 0x40 at */
{135, 8, 5, 4, 0, -5}, /*[33] 0x41 A */
{140, 8, 5, 4, 0, -5}, /*[34] 0x42 B */
{145, 8, 5, 4, 0, -5}, /*[35] 0x43 C */
{150, 8, 5, 4, 0, -5}, /*[36] 0x44 D */
{155, 8, 5, 4, 0, -5}, /*[37] 0x45 E */
{160, 8, 5, 4, 0, -5}, /*[38] 0x46 F */
{165, 8, 5, 4, 0, -5}, /*[39] 0x47 G */
{170, 8, 5, 4, 0, -5}, /*[40] 0x48 H */
{175, 8, 5, 2, 0, -5}, /*[41] 0x49 I */
{180, 8, 5, 4, 0, -5}, /*[42] 0x4A J */
{185, 8, 5, 4, 0, -5}, /*[43] 0x4B K */
{190, 8, 5, 4, 0, -5}, /*[44] 0x4C L */
{195, 8, 5, 6, 0, -5}, /*[45] 0x4D M */
{200, 8, 5, 5, 0, -5}, /*[46] 0x4E N */
{205, 8, 5, 4, 0, -5}, /*[47] 0x4F O */
{210, 8, 5, 4, 0, -5}, /*[48] 0x50 P */
{215, 8, 5, 5, 0, -5}, /*[49] 0x51 Q */
{220, 8, 5, 4, 0, -5}, /*[50] 0x52 R */
{225, 8, 5, 4, 0, -5}, /*[51] 0x53 S */
{230, 8, 5, 4, 0, -5}, /*[52] 0x54 T */
{235, 8, 5, 4, 0, -5}, /*[53] 0x55 U */
{240, 8, 5, 4, 0, -5}, /*[54] 0x56 V */
{245, 8, 5, 6, 0, -5}, /*[55] 0x57 W */
{250, 8, 5, 4, 0, -5}, /*[56] 0x58 X */
{255, 8, 5, 4, 0, -5}, /*[57] 0x59 Y */
{260, 8, 5, 4, 0, -5}, /*[58] 0x5A Z */
{265, 8, 5, 4, 0, -5}, /*[59] 0x5B bracketleft */
{270, 8, 3, 4, 0, -4}, /*[60] 0x5C backslash */
{273, 8, 5, 4, 0, -5}, /*[61] 0x5D bracketright */
{278, 8, 2, 4, 0, -5}, /*[62] 0x5E asciicircum */
{280, 8, 1, 4, 0, -1}, /*[63] 0x5F underscore */
{281, 8, 2, 3, 0, -5}, /*[64] 0x60 grave */
{283, 8, 4, 4, 0, -4}, /*[65] 0x61 a */
{287, 8, 5, 4, 0, -5}, /*[66] 0x62 b */
{292, 8, 4, 4, 0, -4}, /*[67] 0x63 c */
{296, 8, 5, 4, 0, -5}, /*[68] 0x64 d */
{301, 8, 4, 4, 0, -4}, /*[69] 0x65 e */
{305, 8, 5, 4, 0, -5}, /*[70] 0x66 f */
{310, 8, 5, 4, 0, -4}, /*[71] 0x67 g */
{315, 8, 5, 4, 0, -5}, /*[72] 0x68 h */
{320, 8, 5, 2, 0, -5}, /*[73] 0x69 i */
{325, 8, 6, 4, 0, -5}, /*[74] 0x6A j */
{331, 8, 5, 4, 0, -5}, /*[75] 0x6B k */
{336, 8, 5, 4, 0, -5}, /*[76] 0x6C l */
{341, 8, 4, 4, 0, -4}, /*[77] 0x6D m */
{345, 8, 4, 4, 0, -4}, /*[78] 0x6E n */
{349, 8, 4, 4, 0, -4}, /*[79] 0x6F o */
{353, 8, 5, 4, 0, -4}, /*[80] 0x70 p */
{358, 8, 5, 4, 0, -4}, /*[81] 0x71 q */
{363, 8, 4, 4, 0, -4}, /*[82] 0x72 r */
{367, 8, 4, 4, 0, -4}, /*[83] 0x73 s */
{371, 8, 5, 4, 0, -5}, /*[84] 0x74 t */
{376, 8, 4, 4, 0, -4}, /*[85] 0x75 u */
{380, 8, 4, 4, 0, -4}, /*[86] 0x76 v */
{384, 8, 4, 4, 0, -4}, /*[87] 0x77 w */
{388, 8, 4, 4, 0, -4}, /*[88] 0x78 x */
{392, 8, 5, 4, 0, -4}, /*[89] 0x79 y */
{397, 8, 4, 4, 0, -4}, /*[90] 0x7A z */
{401, 8, 5, 4, 0, -5}, /*[91] 0x7B braceleft */
{406, 8, 5, 2, 0, -5}, /*[92] 0x7C bar */
{411, 8, 5, 4, 0, -5}, /*[93] 0x7D braceright */
{416, 8, 2, 4, 0, -5}, /*[94] 0x7E asciitilde */
{418, 8, 5, 2, 0, -5}, /*[95] 0xA1 exclamdown */
{423, 8, 5, 4, 0, -5}, /*[96] 0xA2 cent */
{428, 8, 5, 4, 0, -5}, /*[97] 0xA3 sterling */
{433, 8, 5, 4, 0, -5}, /*[98] 0xA4 currency */
{438, 8, 5, 4, 0, -5}, /*[99] 0xA5 yen */
{443, 8, 5, 2, 0, -5}, /*[100] 0xA6 brokenbar */
{448, 8, 5, 4, 0, -5}, /*[101] 0xA7 section */
{453, 8, 1, 4, 0, -5}, /*[102] 0xA8 dieresis */
{454, 8, 3, 4, 0, -5}, /*[103] 0xA9 copyright */
{457, 8, 5, 4, 0, -5}, /*[104] 0xAA ordfeminine */
{462, 8, 3, 3, 0, -5}, /*[105] 0xAB guillemotleft */
{465, 8, 2, 4, 0, -4}, /*[106] 0xAC logicalnot */
{467, 8, 1, 3, 0, -3}, /*[107] 0xAD softhyphen */
{468, 8, 3, 4, 0, -5}, /*[108] 0xAE registered */
{471, 8, 1, 4, 0, -5}, /*[109] 0xAF macron */
{472, 8, 3, 4, 0, -5}, /*[110] 0xB0 degree */
{475, 8, 5, 4, 0, -5}, /*[111] 0xB1 plusminus */
{480, 8, 3, 4, 0, -5}, /*[112] 0xB2 twosuperior */
{483, 8, 3, 4, 0, -5}, /*[113] 0xB3 threesuperior */
{486, 8, 2, 3, 0, -5}, /*[114] 0xB4 acute */
{488, 8, 5, 4, 0, -5}, /*[115] 0xB5 mu */
{493, 8, 5, 4, 0, -5}, /*[116] 0xB6 paragraph */
{498, 8, 3, 4, 0, -4}, /*[117] 0xB7 periodcentered */
{501, 8, 3, 4, 0, -3}, /*[118] 0xB8 cedilla */
{504, 8, 3, 2, 0, -5}, /*[119] 0xB9 onesuperior */
{507, 8, 5, 4, 0, -5}, /*[120] 0xBA ordmasculine */
{512, 8, 3, 3, 0, -5}, /*[121] 0xBB guillemotright */
{515, 8, 5, 4, 0, -5}, /*[122] 0xBC onequarter */
{520, 8, 5, 4, 0, -5}, /*[123] 0xBD onehalf */
{525, 8, 5, 4, 0, -5}, /*[124] 0xBE threequarters */
{530, 8, 5, 4, 0, -5}, /*[125] 0xBF questiondown */
{535, 8, 5, 4, 0, -5}, /*[126] 0xC0 Agrave */
{540, 8, 5, 4, 0, -5}, /*[127] 0xC1 Aacute */
{545, 8, 5, 4, 0, -5}, /*[128] 0xC2 Acircumflex */
{550, 8, 5, 4, 0, -5}, /*[129] 0xC3 Atilde */
{555, 8, 5, 4, 0, -5}, /*[130] 0xC4 Adieresis */
{560, 8, 5, 4, 0, -5}, /*[131] 0xC5 Aring */
{565, 8, 5, 4, 0, -5}, /*[132] 0xC6 AE */
{570, 8, 6, 4, 0, -5}, /*[133] 0xC7 Ccedilla */
{576, 8, 5, 4, 0, -5}, /*[134] 0xC8 Egrave */
{581, 8, 5, 4, 0, -5}, /*[135] 0xC9 Eacute */
{586, 8, 5, 4, 0, -5}, /*[136] 0xCA Ecircumflex */
{591, 8, 5, 4, 0, -5}, /*[137] 0xCB Edieresis */
{596, 8, 5, 4, 0, -5}, /*[138] 0xCC Igrave */
{601, 8, 5, 4, 0, -5}, /*[139] 0xCD Iacute */
{606, 8, 5, 4, 0, -5}, /*[140] 0xCE Icircumflex */
{611, 8, 5, 4, 0, -5}, /*[141] 0xCF Idieresis */
{616, 8, 5, 4, 0, -5}, /*[142] 0xD0 Eth */
{621, 8, 5, 4, 0, -5}, /*[143] 0xD1 Ntilde */
{626, 8, 5, 4, 0, -5}, /*[144] 0xD2 Ograve */
{631, 8, 5, 4, 0, -5}, /*[145] 0xD3 Oacute */
{636, 8, 5, 4, 0, -5}, /*[146] 0xD4 Ocircumflex */
{641, 8, 5, 4, 0, -5}, /*[147] 0xD5 Otilde */
{646, 8, 5, 4, 0, -5}, /*[148] 0xD6 Odieresis */
{651, 8, 3, 4, 0, -4}, /*[149] 0xD7 multiply */
{654, 8, 5, 4, 0, -5}, /*[150] 0xD8 Oslash */
{659, 8, 5, 4, 0, -5}, /*[151] 0xD9 Ugrave */
{664, 8, 5, 4, 0, -5}, /*[152] 0xDA Uacute */
{669, 8, 5, 4, 0, -5}, /*[153] 0xDB Ucircumflex */
{674, 8, 5, 4, 0, -5}, /*[154] 0xDC Udieresis */
{679, 8, 5, 4, 0, -5}, /*[155] 0xDD Yacute */
{684, 8, 5, 4, 0, -5}, /*[156] 0xDE Thorn */
{689, 8, 6, 4, 0, -5}, /*[157] 0xDF germandbls */
{695, 8, 5, 4, 0, -5}, /*[158] 0xE0 agrave */
{700, 8, 5, 4, 0, -5}, /*[159] 0xE1 aacute */
{705, 8, 5, 4, 0, -5}, /*[160] 0xE2 acircumflex */
{710, 8, 5, 4, 0, -5}, /*[161] 0xE3 atilde */
{715, 8, 5, 4, 0, -5}, /*[162] 0xE4 adieresis */
{720, 8, 5, 4, 0, -5}, /*[163] 0xE5 aring */
{725, 8, 4, 4, 0, -4}, /*[164] 0xE6 ae */
{729, 8, 5, 4, 0, -4}, /*[165] 0xE7 ccedilla */
{734, 8, 5, 4, 0, -5}, /*[166] 0xE8 egrave */
{739, 8, 5, 4, 0, -5}, /*[167] 0xE9 eacute */
{744, 8, 5, 4, 0, -5}, /*[168] 0xEA ecircumflex */
{749, 8, 5, 4, 0, -5}, /*[169] 0xEB edieresis */
{754, 8, 5, 3, 0, -5}, /*[170] 0xEC igrave */
{759, 8, 5, 3, 0, -5}, /*[171] 0xED iacute */
{764, 8, 5, 4, 0, -5}, /*[172] 0xEE icircumflex */
{769, 8, 5, 4, 0, -5}, /*[173] 0xEF idieresis */
{774, 8, 5, 4, 0, -5}, /*[174] 0xF0 eth */
{779, 8, 5, 4, 0, -5}, /*[175] 0xF1 ntilde */
{784, 8, 5, 4, 0, -5}, /*[176] 0xF2 ograve */
{789, 8, 5, 4, 0, -5}, /*[177] 0xF3 oacute */
{794, 8, 5, 4, 0, -5}, /*[178] 0xF4 ocircumflex */
{799, 8, 5, 4, 0, -5}, /*[179] 0xF5 otilde */
{804, 8, 5, 4, 0, -5}, /*[180] 0xF6 odieresis */
{809, 8, 5, 4, 0, -5}, /*[181] 0xF7 divide */
{814, 8, 4, 4, 0, -4}, /*[182] 0xF8 oslash */
{818, 8, 5, 4, 0, -5}, /*[183] 0xF9 ugrave */
{823, 8, 5, 4, 0, -5}, /*[184] 0xFA uacute */
{828, 8, 5, 4, 0, -5}, /*[185] 0xFB ucircumflex */
{833, 8, 5, 4, 0, -5}, /*[186] 0xFC udieresis */
{838, 8, 6, 4, 0, -5}, /*[187] 0xFD yacute */
{844, 8, 5, 4, 0, -4}, /*[188] 0xFE thorn */
{849, 8, 6, 4, 0, -5}, /*[189] 0xFF ydieresis */
{855, 8, 1, 2, 0, -1}, /*[190] 0x11D gcircumflex */
{856, 8, 5, 4, 0, -5}, /*[191] 0x152 OE */
{861, 8, 4, 4, 0, -4}, /*[192] 0x153 oe */
{865, 8, 5, 4, 0, -5}, /*[193] 0x160 Scaron */
{870, 8, 5, 4, 0, -5}, /*[194] 0x161 scaron */
{875, 8, 5, 4, 0, -5}, /*[195] 0x178 Ydieresis */
{880, 8, 5, 4, 0, -5}, /*[196] 0x17D Zcaron */
{885, 8, 5, 4, 0, -5}, /*[197] 0x17E zcaron */
{890, 8, 1, 2, 0, -1}, /*[198] 0xEA4 uni0EA4 */
{891, 8, 1, 2, 0, -1}, /*[199] 0x13A0 uni13A0 */
{892, 8, 1, 2, 0, -3}, /*[200] 0x2022 bullet */
{893, 8, 1, 4, 0, -1}, /*[201] 0x2026 ellipsis */
{894, 8, 5, 4, 0, -5}, /*[202] 0x20AC Euro */
{899, 8, 5, 4, 0, -5}, /*[203] 0xFFFD uniFFFD */
};
const GFXfont AwtrixFont PROGMEM = {
(uint8_t *)AwtrixBitmaps,
(GFXglyph *)AwtrixFontGlyphs,
0x20, 0xFF, 6};

View File

@@ -0,0 +1,320 @@
const uint8_t Fallout[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xF8,
0xF9, 0xCF, 0x9C, 0xF9, 0xCF, 0x9F, 0xF9, 0xF0, 0x3E, 0x3E, 0x1F, 0x1F,
0x0F, 0x8F, 0x87, 0xC7, 0xC3, 0xE3, 0xE7, 0xFF, 0xF3, 0xFF, 0xF8, 0x7C,
0x7C, 0x3E, 0x3E, 0x1F, 0x1F, 0x3F, 0xFF, 0xFF, 0xFF, 0xF3, 0xE3, 0xE1,
0xF1, 0xF0, 0xF8, 0xF8, 0x7C, 0x7C, 0x3E, 0x3E, 0x00, 0x07, 0xC0, 0x0F,
0x80, 0xFF, 0xF9, 0xFF, 0xF3, 0xFF, 0xFF, 0x00, 0x3E, 0x00, 0x1F, 0xF8,
0x3F, 0xF0, 0x7F, 0xE0, 0x00, 0xF8, 0x01, 0xFF, 0xFF, 0x1F, 0xFE, 0x3F,
0xFC, 0x03, 0xE0, 0x07, 0xC0, 0xF8, 0x01, 0x8F, 0x80, 0x18, 0xF8, 0x0F,
0x8F, 0x80, 0xF8, 0xF8, 0x0F, 0x80, 0x03, 0xE0, 0x00, 0x3E, 0x00, 0x1F,
0x00, 0x01, 0xF0, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x00, 0x7C, 0x00, 0x3E,
0x01, 0xF3, 0xE0, 0x1F, 0x3E, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xF0,
0x07, 0xF0, 0x03, 0xF8, 0x0F, 0x8F, 0x87, 0xC7, 0xC3, 0xE3, 0xE0, 0x3F,
0x80, 0x1F, 0xC0, 0x7F, 0x9F, 0x3F, 0xCF, 0x9F, 0xE7, 0xFE, 0x7F, 0x9F,
0x3F, 0xCF, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF8, 0x7F, 0x9F, 0x3F, 0xCF,
0x80, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xC1, 0xF3, 0xE0, 0xF8, 0x3E, 0x3E,
0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0x3E, 0x0F, 0x83, 0xE0,
0x1F, 0x07, 0xC0, 0xF8, 0x3E, 0x03, 0xE0, 0xF8, 0x3E, 0x01, 0xF0, 0x7C,
0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x3E, 0x0F, 0x83, 0xE3, 0xE0, 0xF8,
0x00, 0x3E, 0x3E, 0x1F, 0x1F, 0x0F, 0x8F, 0x80, 0xFE, 0x00, 0x7F, 0x07,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xF0, 0x03, 0xF8, 0x0F, 0x8F,
0x87, 0xC7, 0xC3, 0xE3, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E,
0x00, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x0F, 0x80,
0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0x3E, 0x7C, 0xF9, 0xF3, 0xFF, 0x3E,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
0x0E, 0x00, 0x1C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x3E, 0x00, 0x7C,
0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0xF8, 0x01, 0xF0, 0x0F, 0x80, 0x1F,
0x00, 0x3E, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x3F, 0xF0, 0x7F, 0xE3, 0xE0,
0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x3F, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xF8, 0xFF, 0xF1, 0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0x9F,
0xF8, 0x3F, 0xF0, 0x07, 0xC1, 0xF3, 0xFC, 0xFF, 0x3F, 0xFF, 0xFF, 0xFC,
0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07,
0xC0, 0x3F, 0xF0, 0x7F, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xE0, 0x07,
0xC0, 0x0F, 0x83, 0xF8, 0x07, 0xF0, 0x0F, 0xE0, 0xF8, 0x01, 0xF0, 0x0F,
0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x3F, 0xF0, 0x7F,
0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xE0, 0x07, 0xC0, 0x0F, 0x83, 0xF8,
0x07, 0xF0, 0x0F, 0xE0, 0x00, 0xF8, 0x01, 0xFF, 0x83, 0xFF, 0x07, 0xFE,
0x0F, 0x9F, 0xF8, 0x3F, 0xF0, 0x01, 0xFE, 0x03, 0xFC, 0x1F, 0xF8, 0x3F,
0xF0, 0x7F, 0xE7, 0xC7, 0xCF, 0x8F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0x7F,
0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00,
0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0xFE,
0x3F, 0xFC, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xFF,
0x83, 0xFF, 0x07, 0xFE, 0x0F, 0x9F, 0xF8, 0x3F, 0xF0, 0x07, 0xF0, 0x0F,
0xE0, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x1F, 0x00, 0x3E, 0x00, 0x7F, 0xF8,
0xFF, 0xF1, 0xFF, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xFE,
0x0F, 0x9F, 0xF8, 0x3F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xC1,
0xFF, 0x83, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0xF8, 0x01, 0xF0, 0x03, 0xE0,
0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07,
0xC0, 0x3F, 0xF0, 0x7F, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07,
0xFE, 0x0F, 0x9F, 0xF8, 0x3F, 0xF0, 0x7F, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF,
0x83, 0xFF, 0x07, 0xFE, 0x0F, 0x9F, 0xF8, 0x3F, 0xF0, 0x3F, 0xF0, 0x7F,
0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0x9F, 0xFF,
0x3F, 0xFE, 0x7F, 0xFC, 0x00, 0xF8, 0x01, 0xF0, 0x1F, 0x00, 0x3E, 0x00,
0x7C, 0x1F, 0xE0, 0x3F, 0xC0, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x3F,
0xFF, 0xFF, 0xE0, 0x3E, 0x7C, 0xF9, 0xF3, 0xE0, 0x00, 0x00, 0x00, 0x00,
0xF9, 0xF3, 0xE7, 0xCF, 0xFC, 0xF8, 0x01, 0xF0, 0x1F, 0x07, 0xC0, 0x7C,
0x07, 0xC3, 0xE0, 0x3E, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0x3E, 0x03, 0xE0,
0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x1F, 0x01, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFC, 0xF8, 0x0F, 0x80, 0x3E, 0x03, 0xE0, 0x3E, 0x00, 0x7C,
0x07, 0xC0, 0x1F, 0x01, 0xF0, 0x1F, 0x07, 0xC0, 0x7C, 0x3E, 0x03, 0xE0,
0x3E, 0x0F, 0x80, 0xF8, 0x00, 0x3F, 0xF0, 0x7F, 0xE3, 0xE0, 0xFF, 0xC1,
0xFF, 0x83, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0xF8, 0x01, 0xF0, 0x03, 0xE0,
0x1F, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x07,
0xC0, 0x3F, 0xFE, 0x1F, 0xFF, 0x3E, 0x03, 0x9F, 0x01, 0xCF, 0x80, 0xE7,
0xCF, 0xFF, 0xE7, 0xFF, 0xF3, 0xFF, 0xF9, 0xFF, 0xFC, 0xFF, 0xFE, 0x7F,
0xFF, 0x3F, 0xFF, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0x7F, 0xE0, 0x3F,
0xF0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x18, 0x00, 0x0F, 0xF0, 0x00, 0xFF,
0x00, 0x0F, 0xF0, 0x03, 0xFF, 0x00, 0x3F, 0xF0, 0x1F, 0x1F, 0x01, 0xF1,
0xF0, 0x1F, 0x1F, 0x07, 0xFF, 0xF0, 0x7F, 0xFF, 0x3E, 0x01, 0xF3, 0xE0,
0x1F, 0x3E, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xF0, 0xFF, 0xFE, 0x7F,
0xFF, 0x3E, 0x03, 0x9F, 0x01, 0xCF, 0x80, 0xE7, 0xC0, 0x7F, 0xE0, 0x3F,
0xFF, 0xFC, 0xFF, 0xFE, 0x7F, 0xFF, 0x3E, 0x03, 0xFF, 0x01, 0xFF, 0x80,
0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xFF, 0xFC, 0xFF, 0xFE, 0x00, 0x3F, 0xFE,
0x7F, 0xFF, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C,
0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00,
0x3E, 0x00, 0x1F, 0xFF, 0x3F, 0xFE, 0xFF, 0xFE, 0x7F, 0xFF, 0x3E, 0x03,
0x9F, 0x01, 0xCF, 0x80, 0xE7, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8,
0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F,
0xE0, 0x3F, 0xFF, 0xFC, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7F,
0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, 0xC0, 0x0F, 0x80,
0x1F, 0x00, 0x3E, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07,
0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x00, 0x3F,
0xFE, 0x1F, 0xFF, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03,
0xE0, 0x01, 0xF0, 0x7F, 0xF8, 0x3F, 0xFC, 0x1F, 0xFE, 0x03, 0xFF, 0x01,
0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3E, 0x7F, 0xFC, 0x3F, 0xFE, 0x00,
0xF8, 0x0E, 0x7C, 0x07, 0x3E, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0,
0x7F, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xFF,
0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F,
0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00,
0x7C, 0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xFE, 0x03, 0xFF,
0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3E, 0x7F, 0xFC, 0x3F, 0xFE,
0x00, 0xF8, 0x0E, 0x7C, 0x07, 0x3E, 0x0F, 0x9F, 0x07, 0xCF, 0x83, 0xE7,
0xCF, 0x83, 0xE7, 0xC1, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3E, 0x7C,
0x1F, 0x3E, 0x0F, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF9, 0xF0, 0x1F, 0xF8,
0x0F, 0x80, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F,
0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0,
0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE,
0x7F, 0xFF, 0x3E, 0x73, 0x9F, 0x39, 0xCF, 0x9C, 0xE7, 0xCE, 0x7F, 0xE7,
0x3F, 0xF3, 0x9F, 0xF9, 0xCF, 0xFC, 0xE7, 0xFE, 0x73, 0xFF, 0x39, 0xFF,
0x9C, 0xFF, 0xCE, 0x7F, 0xE7, 0x3F, 0xF3, 0x9F, 0xF9, 0xCF, 0x80, 0xF8,
0x0E, 0x7C, 0x07, 0x3F, 0x83, 0xFF, 0xC1, 0xFF, 0xE0, 0xFF, 0xFE, 0x7F,
0xFF, 0x3F, 0xF3, 0xFF, 0xF9, 0xFF, 0xFC, 0xFF, 0xFE, 0x0F, 0xFF, 0x07,
0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0x80,
0x3F, 0xFE, 0x1F, 0xFF, 0x3E, 0x03, 0x9F, 0x01, 0xCF, 0x80, 0xE7, 0xC0,
0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE, 0x03, 0xFF,
0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3E, 0x7F, 0xFC, 0x3F, 0xFE,
0x00, 0xFF, 0xFE, 0x7F, 0xFF, 0x3E, 0x03, 0x9F, 0x01, 0xCF, 0x80, 0xE7,
0xC0, 0x7F, 0xE0, 0x3F, 0xFF, 0xFC, 0xFF, 0xFE, 0x7F, 0xFF, 0x3E, 0x00,
0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8,
0x00, 0x00, 0x3F, 0xFE, 0x1F, 0xFF, 0x3E, 0x03, 0x9F, 0x01, 0xCF, 0x80,
0xE7, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xFE,
0x03, 0xFF, 0x01, 0xFF, 0x9C, 0xFF, 0xCE, 0x7F, 0xE7, 0x3E, 0x7F, 0xFC,
0x3F, 0xFE, 0x00, 0x1F, 0xC0, 0x0F, 0xE0, 0xFF, 0xFE, 0x7F, 0xFF, 0x3E,
0x03, 0x9F, 0x01, 0xCF, 0x80, 0xE7, 0xC0, 0x7F, 0xE0, 0x3F, 0xFF, 0xFC,
0xFF, 0xFE, 0x7F, 0xFF, 0x3E, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0,
0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0x80, 0x3F, 0xFE, 0x1F, 0xFF,
0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0x7F,
0xFC, 0x3F, 0xFE, 0x1F, 0xFF, 0x00, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8,
0x00, 0x7C, 0x00, 0x3F, 0xFF, 0xFC, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFF,
0xFC, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01, 0xF0, 0x03, 0xE0,
0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01,
0xF0, 0x03, 0xE0, 0x07, 0xC0, 0xF8, 0x0E, 0x7C, 0x07, 0x3E, 0x03, 0xFF,
0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F,
0xFC, 0x07, 0xFE, 0x03, 0xFF, 0x01, 0xFF, 0x80, 0xFF, 0xC0, 0x7F, 0xE0,
0x3E, 0x7F, 0xFF, 0x3F, 0xFF, 0x80, 0xF8, 0x01, 0x8F, 0x80, 0x18, 0xF8,
0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xF3, 0xE0, 0xF8, 0x3E, 0x0F, 0x83,
0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x07, 0xFE, 0x00, 0x7F, 0xE0,
0x07, 0xFE, 0x00, 0x7F, 0xE0, 0x07, 0xFE, 0x00, 0x1F, 0x00, 0x01, 0xF0,
0x00, 0xF9, 0xCE, 0x7C, 0xE7, 0x3E, 0x73, 0xFF, 0x39, 0xFF, 0x9C, 0xFF,
0xCE, 0x7F, 0xE7, 0x3F, 0xF3, 0x9F, 0xF9, 0xCF, 0xFC, 0xE7, 0xFE, 0x73,
0xFF, 0x39, 0xFF, 0x9C, 0xFF, 0xCE, 0x7F, 0xE7, 0x3F, 0xFF, 0xFC, 0xFF,
0xFE, 0x00, 0xF8, 0x01, 0x8F, 0x80, 0x18, 0xF8, 0x01, 0xFF, 0x80, 0x1F,
0xF8, 0x01, 0xF3, 0xE0, 0xF8, 0x3E, 0x0F, 0x80, 0x7F, 0xE0, 0x07, 0xFE,
0x00, 0x7F, 0xE0, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0xF8, 0x01, 0xFF, 0x80,
0x1F, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x01, 0xF0, 0xF8, 0x01, 0xFC,
0x00, 0xCF, 0x83, 0xE7, 0xC1, 0xF3, 0xE0, 0xF8, 0x3F, 0xF0, 0x1F, 0xF8,
0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x1F,
0x00, 0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xFF, 0xFE,
0x7F, 0xFF, 0x00, 0x03, 0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x01, 0xF0, 0x00,
0xF8, 0x0F, 0xE0, 0x07, 0xF0, 0x03, 0xF8, 0x0F, 0x80, 0x07, 0xC0, 0x0F,
0x80, 0x07, 0xC0, 0x03, 0xE0, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF,
0xFF, 0xFF, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F,
0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xFF, 0xFF, 0xC0, 0xC0, 0x00, 0x60,
0x00, 0x3E, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0x01, 0xF0, 0x00, 0xF8, 0x00,
0x0F, 0x80, 0x07, 0xC0, 0x03, 0xE0, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x03,
0xE0, 0x01, 0xF0, 0x00, 0xF8, 0x00, 0x1F, 0x00, 0x0F, 0x80, 0xFF, 0xFF,
0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C, 0x1F, 0x07, 0xC1, 0xF0, 0x7C,
0x1F, 0x07, 0xC1, 0xF0, 0x7F, 0xFF, 0xFF, 0xC0, 0x01, 0xC0, 0x00, 0xE0,
0x01, 0xFC, 0x00, 0xFE, 0x00, 0x7F, 0x01, 0xF1, 0xF0, 0xF8, 0xF9, 0xF0,
0x1F, 0xF8, 0x0F, 0xFC, 0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFC, 0xF9, 0xF3,
0xE7, 0xCF, 0x87, 0xCF, 0x80, 0x3F, 0xFE, 0x7F, 0xFF, 0xE0, 0xFF, 0xC1,
0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0x7C,
0xFF, 0xF9, 0xFF, 0xF0, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F,
0x80, 0x1F, 0xFE, 0x3F, 0xFC, 0x7C, 0x1F, 0xF8, 0x3F, 0xF0, 0x7F, 0xE0,
0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0xFF, 0xF8, 0xFF, 0xF0,
0x3F, 0xF3, 0xFF, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80,
0xF8, 0x0F, 0x80, 0x3F, 0xF3, 0xFF, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8,
0x01, 0xF0, 0x03, 0xE7, 0xFF, 0xCF, 0xFF, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0,
0x7F, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0x9F, 0xFF,
0x3F, 0xFE, 0x3F, 0xF0, 0x7F, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF,
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0xF8, 0x01, 0xF0, 0x00, 0xFF, 0xC1, 0xFF,
0x80, 0x07, 0xF0, 0x7F, 0x3E, 0x03, 0xE0, 0x3E, 0x0F, 0xFF, 0xFF, 0xF3,
0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03,
0xE0, 0x3E, 0x00, 0x3F, 0xFE, 0x7F, 0xFF, 0xE0, 0xFF, 0xC1, 0xFF, 0x83,
0xFF, 0x07, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0x7C, 0xFF, 0xF9,
0xFF, 0xF0, 0x03, 0xE0, 0x07, 0xCF, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0xF8,
0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x1F, 0xFE, 0x3F, 0xFC,
0x7C, 0x1F, 0xF8, 0x3F, 0xF0, 0x7F, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF,
0x07, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xF0, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F,
0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0x01, 0xF0, 0x1F, 0xF9, 0xFF, 0x9F,
0x3F, 0xC3, 0xFC, 0x3F, 0xC0, 0xF8, 0x01, 0xF0, 0x03, 0xE0, 0x07, 0xC0,
0x0F, 0x80, 0x1F, 0x07, 0xFE, 0x0F, 0xFC, 0xF8, 0xF9, 0xF1, 0xF3, 0xE3,
0xFF, 0x07, 0xFE, 0x0F, 0x9F, 0x1F, 0x3E, 0x3E, 0x7C, 0x7C, 0x1F, 0xF8,
0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
0xFF, 0xFE, 0x7F, 0xFF, 0x3E, 0x73, 0xFF, 0x39, 0xFF, 0x9C, 0xFF, 0xCE,
0x7F, 0xE7, 0x3F, 0xF3, 0x9F, 0xF9, 0xCF, 0xFC, 0xE7, 0xFE, 0x73, 0xFF,
0x39, 0xF0, 0xFF, 0xF1, 0xFF, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF,
0x07, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0x7F, 0xE0, 0xFF, 0xC1,
0xF0, 0x3F, 0xF0, 0x7F, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07,
0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0x7C, 0xFF, 0xC1, 0xFF, 0x80,
0xFF, 0xF1, 0xFF, 0xE3, 0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xFE,
0x0F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0x7F, 0xFF, 0xC7, 0xFF, 0x8F, 0x80,
0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x00, 0x3F, 0xFE, 0x7F, 0xFF,
0xE0, 0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8,
0x3F, 0xF0, 0x7C, 0xFF, 0xF9, 0xFF, 0xF0, 0x03, 0xE0, 0x07, 0xC0, 0x0F,
0x80, 0x1F, 0x00, 0x3E, 0xF9, 0xFF, 0x9F, 0xFF, 0xCF, 0xFC, 0xFF, 0xCF,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0x3F, 0xF0,
0x7F, 0xE3, 0xE0, 0x07, 0xC0, 0x0F, 0x80, 0x07, 0xFE, 0x0F, 0xFC, 0x00,
0x1F, 0x00, 0x3E, 0x00, 0x7C, 0xFF, 0xC1, 0xFF, 0x80, 0x07, 0xC0, 0x0F,
0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x1F, 0xFF, 0xFF, 0xFF, 0x83, 0xE0,
0x07, 0xC0, 0x0F, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x01,
0xF0, 0x03, 0xE0, 0x07, 0xC0, 0xF8, 0x3F, 0xF0, 0x7F, 0xE0, 0xFF, 0xC1,
0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3F, 0xF0, 0x7C,
0xFF, 0xF9, 0xFF, 0xF0, 0xF8, 0x0E, 0x7C, 0x07, 0x3E, 0x03, 0xFF, 0x01,
0xFF, 0x80, 0xF9, 0xF1, 0xF0, 0xF8, 0xF8, 0x7C, 0x7C, 0x3E, 0x3E, 0x1F,
0x1F, 0x01, 0xFC, 0x00, 0xFE, 0x00, 0xF9, 0xCE, 0x7C, 0xE7, 0x3E, 0x73,
0xFF, 0x39, 0xFF, 0x9C, 0xFF, 0xCE, 0x7F, 0xE7, 0x3F, 0xF3, 0x9F, 0xF9,
0xCF, 0xFC, 0xE7, 0xFF, 0xFF, 0x9F, 0xFF, 0xC0, 0xF8, 0x3F, 0xF0, 0x7C,
0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x00, 0xF8, 0x01, 0xF0, 0x1F, 0xF8, 0x3F,
0xF0, 0x7F, 0xE3, 0xE0, 0xFF, 0xC1, 0xF0, 0xF8, 0x3F, 0xF0, 0x7F, 0xE0,
0xFF, 0xC1, 0xFF, 0x83, 0xFF, 0x07, 0xFE, 0x0F, 0xFC, 0x1F, 0xF8, 0x3F,
0xF0, 0x7C, 0xFF, 0xF9, 0xFF, 0xF0, 0x03, 0xE0, 0x07, 0xCF, 0xFC, 0x1F,
0xF8, 0x3F, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0xC0, 0x0F, 0x80, 0x1F,
0x00, 0xF8, 0x01, 0xF0, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x03, 0xFF, 0xFF,
0xFF, 0xF0, 0x07, 0xF0, 0x7F, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E,
0x0F, 0x80, 0xF8, 0x0F, 0x80, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E,
0x00, 0x7F, 0x07, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFE, 0x0F, 0xE0, 0x07, 0xC0, 0x7C,
0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x1F, 0x01, 0xF0, 0x1F, 0x07, 0xC0, 0x7C,
0x07, 0xC0, 0x7C, 0x07, 0xCF, 0xE0, 0xFE, 0x00, 0x3E, 0x0E, 0x7C, 0x1F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3E, 0x30, 0x7C, 0x00 };
const GFXglyph FalloutGlyphs[] PROGMEM = {
{ 0, 1, 1, 12, 0, 0 }, // 0x20 ' '
{ 1, 5, 17, 7, 0, -16 }, // 0x21 '!'
{ 12, 12, 5, 15, 0, -19 }, // 0x22 '"'
{ 20, 17, 17, 20, 0, -16 }, // 0x23 '#'
{ 57, 15, 17, 17, 0, -16 }, // 0x24 '$'
{ 89, 20, 17, 22, 0, -16 }, // 0x25 '%'
{ 132, 17, 17, 20, 0, -16 }, // 0x26 '&'
{ 169, 5, 5, 7, 0, -19 }, // 0x27 '''
{ 173, 10, 17, 12, 0, -16 }, // 0x28 '('
{ 195, 10, 17, 12, 0, -16 }, // 0x29 ')'
{ 217, 17, 13, 20, 0, -14 }, // 0x2A '*'
{ 245, 15, 13, 17, 0, -14 }, // 0x2B '+'
{ 270, 7, 7, 10, 0, -4 }, // 0x2C ','
{ 277, 15, 3, 17, 0, -9 }, // 0x2D '-'
{ 283, 5, 5, 7, 0, -4 }, // 0x2E '.'
{ 287, 15, 17, 17, 0, -16 }, // 0x2F '/'
{ 319, 15, 17, 17, 0, -16 }, // 0x30 '0'
{ 351, 10, 17, 12, 0, -16 }, // 0x31 '1'
{ 373, 15, 17, 17, 0, -16 }, // 0x32 '2'
{ 405, 15, 17, 17, 0, -16 }, // 0x33 '3'
{ 437, 15, 17, 17, 0, -16 }, // 0x34 '4'
{ 469, 15, 17, 17, 0, -16 }, // 0x35 '5'
{ 501, 15, 17, 17, 0, -16 }, // 0x36 '6'
{ 533, 15, 17, 17, 0, -16 }, // 0x37 '7'
{ 565, 15, 17, 17, 0, -16 }, // 0x38 '8'
{ 597, 15, 17, 17, 0, -16 }, // 0x39 '9'
{ 629, 5, 15, 7, 0, -14 }, // 0x3A ':'
{ 639, 7, 17, 10, 0, -14 }, // 0x3B ';'
{ 654, 12, 17, 15, 0, -16 }, // 0x3C '<'
{ 680, 15, 10, 17, 0, -11 }, // 0x3D '='
{ 699, 12, 17, 15, 0, -16 }, // 0x3E '>'
{ 725, 15, 17, 17, 0, -16 }, // 0x3F '?'
{ 757, 17, 17, 20, 0, -16 }, // 0x40 '@'
{ 794, 20, 17, 22, 0, -16 }, // 0x41 'A'
{ 837, 17, 17, 20, 0, -16 }, // 0x42 'B'
{ 874, 15, 17, 17, 0, -16 }, // 0x43 'C'
{ 906, 17, 17, 20, 0, -16 }, // 0x44 'D'
{ 943, 15, 17, 17, 0, -16 }, // 0x45 'E'
{ 975, 15, 17, 17, 0, -16 }, // 0x46 'F'
{ 1007, 17, 17, 20, 0, -16 }, // 0x47 'G'
{ 1044, 17, 17, 20, 0, -16 }, // 0x48 'H'
{ 1081, 5, 17, 7, 0, -16 }, // 0x49 'I'
{ 1092, 17, 17, 20, 0, -16 }, // 0x4A 'J'
{ 1129, 17, 17, 20, 0, -16 }, // 0x4B 'K'
{ 1166, 15, 17, 17, 0, -16 }, // 0x4C 'L'
{ 1198, 17, 17, 20, 0, -16 }, // 0x4D 'M'
{ 1235, 17, 17, 20, 0, -16 }, // 0x4E 'N'
{ 1272, 17, 17, 20, 0, -16 }, // 0x4F 'O'
{ 1309, 17, 17, 20, 0, -16 }, // 0x50 'P'
{ 1346, 17, 19, 20, 0, -16 }, // 0x51 'Q'
{ 1387, 17, 17, 20, 0, -16 }, // 0x52 'R'
{ 1424, 17, 17, 20, 0, -16 }, // 0x53 'S'
{ 1461, 15, 17, 17, 0, -16 }, // 0x54 'T'
{ 1493, 17, 17, 20, 0, -16 }, // 0x55 'U'
{ 1530, 20, 17, 22, 0, -16 }, // 0x56 'V'
{ 1573, 17, 17, 20, 0, -16 }, // 0x57 'W'
{ 1610, 20, 17, 22, 0, -16 }, // 0x58 'X'
{ 1653, 17, 17, 20, 0, -16 }, // 0x59 'Y'
{ 1690, 17, 17, 20, 0, -16 }, // 0x5A 'Z'
{ 1727, 10, 17, 12, 0, -16 }, // 0x5B '['
{ 1749, 17, 17, 20, 0, -16 }, // 0x5C '\'
{ 1786, 10, 17, 12, 0, -16 }, // 0x5D ']'
{ 1808, 17, 10, 20, 0, -16 }, // 0x5E '^'
{ 1830, 15, 2, 17, 0, -1 }, // 0x5F '_'
{ 1834, 7, 7, 10, 0, -16 }, // 0x60 '`'
{ 1841, 15, 12, 17, 0, -11 }, // 0x61 'a'
{ 1864, 15, 17, 17, 0, -16 }, // 0x62 'b'
{ 1896, 12, 12, 15, 0, -11 }, // 0x63 'c'
{ 1914, 15, 17, 17, 0, -16 }, // 0x64 'd'
{ 1946, 15, 12, 17, 0, -11 }, // 0x65 'e'
{ 1969, 12, 17, 15, 0, -16 }, // 0x66 'f'
{ 1995, 15, 17, 17, 0, -11 }, // 0x67 'g'
{ 2027, 15, 17, 17, 0, -16 }, // 0x68 'h'
{ 2059, 5, 12, 7, 0, -11 }, // 0x69 'i'
{ 2067, 12, 17, 15, 0, -11 }, // 0x6A 'j'
{ 2093, 15, 17, 17, 0, -16 }, // 0x6B 'k'
{ 2125, 5, 17, 7, 0, -16 }, // 0x6C 'l'
{ 2136, 17, 12, 20, 0, -11 }, // 0x6D 'm'
{ 2162, 15, 12, 17, 0, -11 }, // 0x6E 'n'
{ 2185, 15, 12, 17, 0, -11 }, // 0x6F 'o'
{ 2208, 15, 17, 17, 0, -11 }, // 0x70 'p'
{ 2240, 15, 17, 17, 0, -11 }, // 0x71 'q'
{ 2272, 12, 12, 15, 0, -11 }, // 0x72 'r'
{ 2290, 15, 12, 17, 0, -11 }, // 0x73 's'
{ 2313, 15, 17, 17, 0, -16 }, // 0x74 't'
{ 2345, 15, 12, 17, 0, -11 }, // 0x75 'u'
{ 2368, 17, 12, 20, 0, -11 }, // 0x76 'v'
{ 2394, 17, 12, 20, 0, -11 }, // 0x77 'w'
{ 2420, 15, 12, 17, 0, -11 }, // 0x78 'x'
{ 2443, 15, 17, 17, 0, -11 }, // 0x79 'y'
{ 2475, 15, 12, 17, 0, -11 }, // 0x7A 'z'
{ 2498, 12, 17, 15, 0, -16 }, // 0x7B '{'
{ 2524, 5, 22, 7, 0, -19 }, // 0x7C '|'
{ 2538, 12, 17, 15, 0, -16 }, // 0x7D '}'
{ 2564, 15, 7, 17, 0, -11 } }; // 0x7E '~'
const GFXfont Fallout__1_20pt7b PROGMEM = {
(uint8_t *)Fallout__1_20pt7bBitmaps,
(GFXglyph *)Fallout__1_20pt7bGlyphs,
0x20, 0x7E, 38 };
// Approx. 3250 bytes

View File

@@ -28,7 +28,7 @@
*/
#include "MatrixDisplayUi.h"
#include "AwtrixFont.h"
#include "Fonts/AwtrixFont.h"
MatrixDisplayUi::MatrixDisplayUi(FastLED_NeoMatrix *matrix)
{

View File

@@ -102,7 +102,6 @@ private:
uint8_t updateInterval = 33;
uint8_t getnextAppNumber();
void drawIndicator();
void drawApp();
void drawOverlays();
void tick();
@@ -110,7 +109,6 @@ private:
public:
MatrixDisplayUi(FastLED_NeoMatrix *matrix);
uint8_t AppCount = 0;
/**
* Initialise the display

View File

@@ -24,6 +24,12 @@ void FSWebServer::addHandler(const Uri &uri, HTTPMethod method, WebServerClass::
webserver->on(uri, method, fn);
}
void FSWebServer::onNotFound(WebServerClass::THandlerFunction fn)
{
webserver->onNotFound(fn);
}
void FSWebServer::addHandler(const Uri &uri, WebServerClass::THandlerFunction handler)
{
webserver->on(uri, HTTP_ANY, handler);
@@ -163,6 +169,7 @@ IPAddress FSWebServer::startWiFi(uint32_t timeout, const char *apSSID, const cha
WiFi.begin(_ssid, _pass);
Serial.print(F("Connecting to "));
Serial.println(_ssid);
uint32_t startTime = millis();
while (WiFi.status() != WL_CONNECTED)
{
@@ -170,6 +177,8 @@ IPAddress FSWebServer::startWiFi(uint32_t timeout, const char *apSSID, const cha
Serial.print(".");
if (WiFi.status() == WL_CONNECTED)
{
WiFi.setAutoReconnect(true);
WiFi.persistent(true);
ip = WiFi.localIP();
return ip;
}
@@ -917,4 +926,4 @@ void FSWebServer::handleStatus()
webserver->send(200, "application/json", json);
}
#endif // INCLUDE_EDIT_HTM
#endif // INCLUDE_EDIT_HTM

View File

@@ -5,7 +5,7 @@
#include <memory>
#include <typeinfo>
#include <base64.h>
//#include <FS.h>
// #include <FS.h>
#include <LittleFS.h>
#define INCLUDE_EDIT_HTM
@@ -82,7 +82,8 @@ public:
bool begin(const char *path = nullptr);
void run();
void onNotFound(WebServerClass::THandlerFunction fn);
void addHandler(const Uri &uri, HTTPMethod method, WebServerClass::THandlerFunction fn);
void addHandler(const Uri &uri, WebServerClass::THandlerFunction handler);
@@ -255,13 +256,12 @@ public:
return true;
}
template <typename T>
bool saveOptionValue(const char *label, T val)
{
// Öffne die Datei im Lesemodus, um den Inhalt des Dokuments beizubehalten
File file = m_filesystem->open("/config.json", "r");
DynamicJsonDocument doc(file.size()* 1.33);
DynamicJsonDocument doc(file.size() * 1.33);
if (file)
{

View File

@@ -22,6 +22,7 @@ lib_deps =
fastled/FastLED@^3.5.0
marcmerlin/FastLED NeoMatrix@^1.2
knolleary/PubSubClient@^2.8
plerup/EspSoftwareSerial@^8.0.1
[env:awtrix_upgrade]
platform = https://github.com/platformio/platform-espressif32.git
@@ -37,4 +38,5 @@ lib_deps =
evert-arias/EasyButton@^2.0.1
fastled/FastLED@^3.5.0
marcmerlin/FastLED NeoMatrix@^1.2
knolleary/PubSubClient@^2.8
knolleary/PubSubClient@^2.8
plerup/EspSoftwareSerial@^8.0.1

View File

@@ -36,6 +36,7 @@ struct CustomApp
bool isGif;
bool rainbow;
bool soundPlayed;
uint16_t duration = 0;
String sound;
int16_t repeat = 0;
int16_t currentRepeat = 0;
@@ -61,7 +62,7 @@ struct Notification
bool isGif;
bool flag = false;
unsigned long startime = 0;
unsigned long duration = 0;
uint16_t duration = 0;
int16_t repeat = -1;
bool hold = false;
byte pushIcon = 0;
@@ -104,7 +105,7 @@ int findAppIndexByName(const String &name)
return -1;
}
void TimeApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void TimeApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
if (notify.flag)
return;
@@ -152,7 +153,7 @@ void TimeApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x,
}
}
void DateApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void DateApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
if (notify.flag)
return;
@@ -180,7 +181,7 @@ void DateApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x,
}
}
void TempApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void TempApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
if (notify.flag)
return;
@@ -201,7 +202,7 @@ void TempApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x,
}
}
void HumApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void HumApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
if (notify.flag)
return;
@@ -215,7 +216,7 @@ void HumApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, i
}
#ifdef ULANZI
void BatApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void BatApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
if (notify.flag)
return;
@@ -287,7 +288,7 @@ else
}
}
void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
// Abort if notify.flag is set
if (notify.flag)
@@ -304,8 +305,8 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState
return;
}
// reset custom App properties if last App
if (lastApp)
// reset custom App properties if last frame
if (lastFrame)
{
ca->iconWasPushed = false;
ca->scrollposition = 9;
@@ -313,6 +314,14 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState
ca->scrollDelay = 0;
}
if (!DisplayManager.appIsSwitching)
{
if (ca->duration > 0)
{
DisplayManager.setAppTime(ca->duration);
}
}
CURRENT_APP = ca->name;
currentCustomApp = name;
@@ -634,64 +643,126 @@ void NotifyApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state)
DisplayManager.getInstance().resetTextColor();
}
void CApp1(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
// Unattractive to have a function for every customapp wich does the same, but currently still no other option found TODO
void CApp1(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp1);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp2(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp2(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp2);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp3(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp3(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp3);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp4(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp4(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp4);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp5(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp5(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp5);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp6(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp6(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp6);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp7(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp7(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp7);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp8(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp8(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp8);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp9(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp9(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp9);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp10(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void CApp10(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp10);
ShowCustomApp(name, matrix, state, x, y, firstApp, lastApp);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp11(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp11);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp12(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp12);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp13(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp13);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp14(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp14);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp15(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp15);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp16(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp16);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp17(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp17);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp18(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp18);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp19(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp19);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
void CApp20(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
String name = getAppNameByFunction(CApp20);
ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame);
}
const uint16_t *getWeatherIcon(int code)
@@ -708,7 +779,7 @@ const uint16_t *getWeatherIcon(int code)
}
}
void WeatherApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp)
void WeatherApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame)
{
if (notify.flag)
return;

View File

@@ -69,6 +69,17 @@ const char HAtransID[] PROGMEM = {"%s_tra"};
const char HAtransName[] PROGMEM = {"Transition"};
const char HAtransIcon[] PROGMEM = {"mdi:swap-vertical"};
const char HAupdateID[] PROGMEM = {"%s_upd"};
const char HAupdateName[] PROGMEM = {"Update"};
const char HAupdateClass[] PROGMEM = {"update"};
const char HAupdateIcon[] PROGMEM = {"mdi:update"};
const char HAdoUpID[] PROGMEM = {"%s_doupd"};
const char HAdoUpName[] PROGMEM = {"Start Update"};
const char HAdoUpIcon[] PROGMEM = {"mdi:update"};
const char HAsigID[] PROGMEM = {"%s_sig"};
const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"};
const char HAsigName[] PROGMEM = {"WiFi strength"};
@@ -104,3 +115,4 @@ const char TempKey[] PROGMEM = {"temp"};
const char HumKey[] PROGMEM = {"hum"};
const char UpTimeKey[] PROGMEM = {"uptime"};
const char SignalStrengthKey[] PROGMEM = {"wifi_signal"};
const char UpdateKey[] PROGMEM = {"up_available"};

View File

@@ -77,10 +77,19 @@ extern const char HAupID[];
extern const char HAupName[];
extern const char HAupClass[];
extern const char HAdoUpID[];
extern const char HAdoUpName[];
extern const char HAdoUpIcon[];
extern const char HAtransID[];
extern const char HAtransName[];
extern const char HAtransIcon[];
extern const char HAupdateID[];
extern const char HAupdateName[];
extern const char HAupdateClass[];
extern const char HAupdateIcon[];
extern const char HAbtnLID[];
extern const char HAbtnLName[];
@@ -106,5 +115,5 @@ extern const char TempKey[];
extern const char HumKey[];
extern const char UpTimeKey[];
extern const char SignalStrengthKey[];
extern const char UpdateKey[];
#endif

View File

@@ -13,6 +13,7 @@
#include "ServerManager.h"
#include "MenuManager.h"
#include "Apps.h"
#include "Dictionary.h"
Ticker AlarmTicker;
Ticker TimerTicker;
@@ -26,8 +27,6 @@ Ticker TimerTicker;
#define MATRIX_WIDTH 32
#define MATRIX_HEIGHT 8
bool appIsSwitching;
GifPlayer gif;
CRGB leds[MATRIX_WIDTH * MATRIX_HEIGHT];
@@ -214,7 +213,7 @@ void pushCustomApp(String name, int position)
if (customApps.count(name) == 0)
{
++customPagesCount;
void (*customApps[10])(FastLED_NeoMatrix *, MatrixDisplayUiState *, int16_t, int16_t, bool, bool) = {CApp1, CApp2, CApp3, CApp4, CApp5, CApp6, CApp7, CApp8, CApp9, CApp10};
void (*customApps[20])(FastLED_NeoMatrix *, MatrixDisplayUiState *, int16_t, int16_t, bool, bool) = {CApp1, CApp2, CApp3, CApp4, CApp5, CApp6, CApp7, CApp8, CApp9, CApp10, CApp11, CApp12, CApp13, CApp14, CApp15, CApp16, CApp17, CApp18, CApp19, CApp20};
if (position < 0) // Insert at the end of the vector
{
@@ -230,6 +229,7 @@ void pushCustomApp(String name, int position)
}
ui.setApps(Apps); // Add Apps
DisplayManager.getInstance().setAutoTransition(true);
}
}
@@ -238,17 +238,15 @@ void removeCustomApp(const String &name)
auto it = std::find_if(Apps.begin(), Apps.end(), [&name](const std::pair<String, AppCallback> &appPair)
{ return appPair.first == name; });
if (it != Apps.end())
{
Apps.erase(it);
ui.setApps(Apps);
}
Apps.erase(it);
ui.setApps(Apps);
}
void DisplayManager_::generateCustomPage(String name, const char *json)
{
if (json == "" && customApps.count(name))
if (strcmp(json, "") == 0 && customApps.count(name))
{
Serial.println("delete");
customApps.erase(customApps.find(name));
removeCustomApp(name);
return;
@@ -295,6 +293,7 @@ void DisplayManager_::generateCustomPage(String name, const char *json)
customApp.barSize = 0;
}
customApp.duration = doc.containsKey("duration") ? doc["duration"].as<int>() * 1000 : 0;
int pos = doc.containsKey("pos") ? doc["pos"].as<uint8_t>() : -1;
customApp.rainbow = doc.containsKey("rainbow") ? doc["rainbow"] : false;
customApp.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0;
@@ -519,6 +518,7 @@ void DisplayManager_::loadNativeApps()
void DisplayManager_::setup()
{
TJpgDec.setCallback(jpg_output);
FastLED.addLeds<NEOPIXEL, MATRIX_PIN>(leds, MATRIX_WIDTH * MATRIX_HEIGHT);
gif.setMatrix(&matrix);
@@ -536,17 +536,17 @@ void DisplayManager_::tick()
}
else
{
ui.update();
if (ui.getUiState()->appState == IN_TRANSITION && !appIsSwitching)
{
appIsSwitching = true;
MQTTManager.setCurrentApp(CURRENT_APP);
}
else if (ui.getUiState()->appState == FIXED && appIsSwitching)
{
appIsSwitching = false;
MQTTManager.setCurrentApp(CURRENT_APP);
setAppTime(TIME_PER_APP);
}
ui.update();
}
}
@@ -788,7 +788,8 @@ void DisplayManager_::updateAppVector(const char *json)
bool show = true;
int position = -1;
if (app.containsKey("show"))
if (app.containsKey("show"))
{
show = app["show"].as<bool>();
}
@@ -819,13 +820,17 @@ 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
@@ -884,4 +889,33 @@ void DisplayManager_::updateAppVector(const char *json)
Apps = std::move(newApps);
ui.setApps(Apps);
saveSettings();
}
String DisplayManager_::getStat()
{
StaticJsonDocument<200> doc;
char buffer[5];
#ifdef ULANZI
doc[BatKey] = BATTERY_PERCENT;
doc[BatRawKey] = BATTERY_RAW;
#endif
snprintf(buffer, 5, "%.0f", CURRENT_LUX);
doc[LuxKey] = buffer;
doc[LDRRawKey] = LDR_RAW;
doc[BrightnessKey] = BRIGHTNESS;
snprintf(buffer, 5, "%.0f", CURRENT_TEMP);
doc[TempKey] = buffer;
snprintf(buffer, 5, "%.0f", CURRENT_HUM);
doc[HumKey] = buffer;
doc[UpTimeKey] = PeripheryManager.readUptime();
doc[SignalStrengthKey] = WiFi.RSSI();
doc[UpdateKey] = UPDATE_AVAILABLE;
String jsonString;
serializeJson(doc, jsonString);
return jsonString;
}
void DisplayManager_::setAppTime(uint16_t duration)
{
ui.setTimePerApp(duration);
}

View File

@@ -26,6 +26,7 @@ private:
public:
static DisplayManager_ &getInstance();
bool appIsSwitching;
void setup();
void tick();
void clear();
@@ -48,7 +49,7 @@ public:
void setFPS(uint8_t);
void MatrixState(bool);
void generateNotification(const char *json);
void generateCustomPage(String, const char *json);
void generateCustomPage(String name, const char *json);
void printText(int16_t x, int16_t y, const char *text, bool centered, bool ignoreUppercase);
bool setAutoTransition(bool active);
void switchToApp(const char *json);
@@ -60,6 +61,9 @@ public:
void drawBMP(int16_t x, int16_t y, const uint16_t bitmap[], int16_t w, int16_t h);
void drawBarChart(int16_t x, int16_t y, const int data[], byte dataSize, bool withIcon, uint16_t color);
void updateAppVector(const char *json);
void setMatrixLayout(int layout);
void setAppTime(uint16_t duration);
String getStat();
};
extern DisplayManager_ &DisplayManager;

View File

@@ -1,6 +1,8 @@
#include "Globals.h"
#include "Preferences.h"
#include <WiFi.h>
#include <ArduinoJson.h>
#include <LittleFS.h>
Preferences Settings;
@@ -13,8 +15,53 @@ char *getID()
return macStr;
}
void startLittleFS()
{
if (LittleFS.begin())
{
LittleFS.mkdir("/MELODIES");
LittleFS.mkdir("/ICONS");
}
else
{
Serial.println("ERROR on mounting LittleFS. It will be formmatted!");
LittleFS.format();
ESP.restart();
}
}
void loadDevSettings()
{
Serial.println("laodSettings");
File file = LittleFS.open("/dev.json", "r");
if (!file)
{
return;
}
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"))
{
UPPERCASE_LETTERS = doc["uppercase"].as<bool>();
}
file.close();
}
void loadSettings()
{
startLittleFS();
Settings.begin("awtrix", false);
MATRIX_FPS = Settings.getUChar("FPS", 23);
BRIGHTNESS = Settings.getUChar("BRI", 120);
@@ -31,12 +78,15 @@ void loadSettings()
SHOW_DATE = Settings.getBool("DAT", true);
SHOW_TEMP = Settings.getBool("TEMP", true);
SHOW_HUM = Settings.getBool("HUM", true);
MATRIX_LAYOUT = Settings.getUInt("MAT", 0);
#ifdef ULANZI
SHOW_BAT = Settings.getBool("BAT", true);
#endif
SOUND_ACTIVE = Settings.getBool("SOUND", true);
Settings.end();
uniqueID = getID();
MQTT_PREFIX = String(uniqueID);
loadDevSettings();
}
void saveSettings()
@@ -57,9 +107,11 @@ void saveSettings()
Settings.putBool("DAT", SHOW_DATE);
Settings.putBool("TEMP", SHOW_TEMP);
Settings.putBool("HUM", SHOW_HUM);
Settings.putUInt("MAT", MATRIX_LAYOUT);
#ifdef ULANZI
Settings.putBool("BAT", SHOW_BAT);
#endif
Settings.putBool("SOUND", SOUND_ACTIVE);
Settings.end();
}
@@ -69,7 +121,7 @@ IPAddress gateway;
IPAddress subnet;
IPAddress primaryDNS;
IPAddress secondaryDNS;
const char *VERSION = "0.45";
const char *VERSION = "0.47";
String MQTT_HOST = "";
uint16_t MQTT_PORT = 1883;
String MQTT_USER;
@@ -130,4 +182,10 @@ bool AP_MODE;
bool MATRIX_OFF;
bool TIMER_ACTIVE;
bool ALARM_ACTIVE;
uint16_t TEXTCOLOR_565 = 0xFFFF;
uint16_t TEXTCOLOR_565 = 0xFFFF;
bool SOUND_ACTIVE;
String BOOT_SOUND = "";
uint8_t VOLUME;
uint8_t VOLUME_PERCENT;
int MATRIX_LAYOUT;
bool UPDATE_AVAILABLE = false;

View File

@@ -66,7 +66,12 @@ extern String TIME_FORMAT;
extern String DATE_FORMAT;
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 int MATRIX_LAYOUT;
extern bool UPDATE_AVAILABLE;
void loadSettings();
void saveSettings();
#endif // Globals_H

View File

@@ -6,8 +6,8 @@
#include <WiFi.h>
#include <ArduinoJson.h>
#include "Dictionary.h"
unsigned long startTime;
#include "PeripheryManager.h"
#include "UpdateManager.h"
WiFiClient espClient;
uint8_t lastBrightness;
@@ -37,6 +37,8 @@ HASensor *ram = nullptr;
HABinarySensor *btnleft = nullptr;
HABinarySensor *btnmid = nullptr;
HABinarySensor *btnright = nullptr;
HABinarySensor *update = nullptr;
HAButton *doUpdate = nullptr;
// The getter for the instantiated singleton instance
MQTTManager_ &MQTTManager_::getInstance()
@@ -62,6 +64,11 @@ void onButtonCommand(HAButton *sender)
{
DisplayManager.previousApp();
}
else if (sender == doUpdate)
{
if (UPDATE_AVAILABLE)
UpdateManager.updateFirmware();
}
}
void onSwitchCommand(bool state, HASwitch *sender)
@@ -184,6 +191,12 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
DisplayManager.previousApp();
return;
}
if (strTopic == MQTT_PREFIX + "/doupdate")
{
if (UPDATE_AVAILABLE)
UpdateManager.updateFirmware();
return;
}
else if (strTopic.startsWith(MQTT_PREFIX + "/custom"))
{
@@ -203,7 +216,7 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
void onMqttConnected()
{
String prefix = MQTT_PREFIX;
const char* topics[] PROGMEM = {
const char *topics[] PROGMEM = {
"/brightness",
"/notify/dismiss",
"/notify",
@@ -213,15 +226,17 @@ void onMqttConnected()
"/settings",
"/previousapp",
"/nextapp",
"/doupdate",
"/nextapp",
"/apps"
};
for (const char* topic : topics) {
"/apps"};
for (const char *topic : topics)
{
String fullTopic = prefix + topic;
mqtt.subscribe(fullTopic.c_str());
}
Serial.println(F("MQTT Connected"));
}
void connect()
{
mqtt.onMessage(onMqttMessage);
@@ -240,14 +255,14 @@ void connect()
}
char matID[40], briID[40];
char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40], transID[40];
char btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40], transID[40], updateID[40], doUpdateID[40];
#ifdef ULANZI
char batID[40];
#endif
void MQTTManager_::setup()
{
startTime = millis();
if (HA_DISCOVERY)
{
Serial.println(F("Starting Homeassistant discorvery"));
@@ -262,6 +277,8 @@ void MQTTManager_::setup()
device.setManufacturer(HAmanufacturer);
device.setModel(HAmodel);
device.setAvailability(true);
device.enableSharedAvailability();
device.enableLastWill();
String uniqueIDWithSuffix;
@@ -296,6 +313,12 @@ void MQTTManager_::setup()
dismiss->setIcon(HAbtnaIcon);
dismiss->setName(HAbtnaName);
sprintf(doUpdateID, HAdoUpID, macStr);
doUpdate = new HAButton(doUpdateID);
doUpdate->setIcon(HAdoUpIcon);
doUpdate->setName(HAdoUpName);
doUpdate->onCommand(onButtonCommand);
sprintf(transID, HAtransID, macStr);
transition = new HASwitch(transID);
transition->setIcon(HAtransIcon);
@@ -370,6 +393,12 @@ void MQTTManager_::setup()
btnleft = new HABinarySensor(btnLID);
btnleft->setName(HAbtnLName);
sprintf(updateID, HAupdateID, macStr);
update = new HABinarySensor(updateID);
update->setIcon(HAupdateIcon);
update->setName(HAupdateName);
update->setDeviceClass(HAupdateClass);
sprintf(btnMID, HAbtnMID, macStr);
btnmid = new HABinarySensor(btnMID);
btnmid->setName(HAbtnMName);
@@ -418,22 +447,6 @@ void MQTTManager_::setCurrentApp(String value)
curApp->setValue(value.c_str());
}
const char *readUptime()
{
static char uptime[25]; // Make the array static to keep it from being destroyed when the function returns
unsigned long currentTime = millis();
unsigned long elapsedTime = currentTime - startTime;
unsigned long uptimeSeconds = elapsedTime / 1000;
unsigned long uptimeMinutes = uptimeSeconds / 60;
unsigned long uptimeHours = uptimeMinutes / 60;
unsigned long uptimeDays = uptimeHours / 24;
unsigned long hours = uptimeHours % 24;
unsigned long minutes = uptimeMinutes % 60;
unsigned long seconds = uptimeSeconds % 60;
sprintf(uptime, "P%dDT%dH%dM%dS", uptimeDays, hours, minutes, seconds);
return uptime;
}
void MQTTManager_::sendStats()
{
if (HA_DISCOVERY)
@@ -466,33 +479,17 @@ void MQTTManager_::sendStats()
int freeHeapBytes = ESP.getFreeHeap();
itoa(freeHeapBytes, rambuffer, 10);
ram->setValue(rambuffer);
uptime->setValue(readUptime());
uptime->setValue(PeripheryManager.readUptime());
version->setValue(VERSION);
transition->setState(AUTO_TRANSITION, false);
update->setState(UPDATE_AVAILABLE, false);
}
else
{
}
StaticJsonDocument<200> doc;
char buffer[5];
#ifdef ULANZI
doc[BatKey] = BATTERY_PERCENT;
doc[BatRawKey] = BATTERY_RAW;
#endif
snprintf(buffer, 5, "%.0f", CURRENT_LUX);
doc[LuxKey] = buffer;
doc[LDRRawKey] = LDR_RAW;
doc[BrightnessKey] = BRIGHTNESS;
snprintf(buffer, 5, "%.0f", CURRENT_TEMP);
doc[TempKey] = buffer;
snprintf(buffer, 5, "%.0f", CURRENT_HUM);
doc[HumKey] = buffer;
doc[UpTimeKey] = readUptime();
doc[SignalStrengthKey] = WiFi.RSSI();
String jsonString;
serializeJson(doc, jsonString);
publish(StatsTopic, jsonString.c_str());
publish(StatsTopic, DisplayManager.getStat().c_str());
}
void MQTTManager_::sendButton(byte btn, bool state)

View File

@@ -3,8 +3,10 @@
#include <Globals.h>
#include <ServerManager.h>
#include <DisplayManager.h>
#include <updater.h>
#include <PeripheryManager.h>
//#include <update.h>
#include <icons.h>
#include <UpdateManager.h>
String menuText;
int menuSelection;
@@ -26,6 +28,8 @@ enum MenuState
WeekdayMenu,
TempMenu,
Appmenu,
SoundMenu,
VolumeMenu
};
const char *menuItems[] PROGMEM = {
@@ -40,10 +44,11 @@ const char *menuItems[] PROGMEM = {
"WEEKDAY",
"TEMP",
"APPS",
"SOUND",
"UPDATE"};
int8_t menuIndex = 0;
uint8_t menuItemCount = 12;
uint8_t menuItemCount = 13;
const char *timeFormat[] PROGMEM = {
"%H:%M:%S",
@@ -75,14 +80,13 @@ const char *appsItems[][2] PROGMEM = {
{"13", "time"},
{"1158", "date"},
{"234", "temp"},
#ifdef ULANZI
#ifdef ULANZI
{"2075", "hum"},
{"1486", "bat"}};
#else
#else
{"2075", "hum"}};
#endif
int8_t appsIndex;
uint8_t appsCount = 5;
@@ -134,6 +138,8 @@ String MenuManager_::menutext()
return "0x" + String(textColors[currentColor], HEX);
case SwitchMenu:
return AUTO_TRANSITION ? "ON" : "OFF";
case SoundMenu:
return SOUND_ACTIVE ? "ON" : "OFF";
case TspeedMenu:
return String(TIME_PER_TRANSITION / 1000.0, 1) + "s";
case AppTimeMenu:
@@ -238,9 +244,16 @@ void MenuManager_::rightButton()
case WeekdayMenu:
START_ON_MONDAY = !START_ON_MONDAY;
break;
case SoundMenu:
SOUND_ACTIVE = !SOUND_ACTIVE;
break;
case TempMenu:
IS_CELSIUS = !IS_CELSIUS;
break;
case VolumeMenu:
VOLUME_PERCENT = (VOLUME_PERCENT % 100) + 1;
VOLUME = map(VOLUME_PERCENT, 0, 100, 0, 30);
PeripheryManager.setVolume(VOLUME);
default:
break;
}
@@ -298,6 +311,13 @@ void MenuManager_::leftButton()
case TempMenu:
IS_CELSIUS = !IS_CELSIUS;
break;
case SoundMenu:
SOUND_ACTIVE = !SOUND_ACTIVE;
break;
case VolumeMenu:
VOLUME_PERCENT = (VOLUME_PERCENT % 100) + 1;
VOLUME = map(VOLUME_PERCENT, 0, 100, 0, 30);
PeripheryManager.setVolume(VOLUME);
default:
break;
}
@@ -349,9 +369,18 @@ void MenuManager_::selectButton()
currentState = Appmenu;
break;
case 11:
if (FirmwareVersionCheck())
currentState = SoundMenu;
break;
case 12:
#ifdef AWTRIX_UPGRADE
currentState = VolumeMenu;
break;
#endif
case 13:
if (UpdateManager.checkUpdate(true))
{
updateFirmware();
UpdateManager.updateFirmware();
}
break;
default:
@@ -425,6 +454,12 @@ 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();
@@ -433,6 +468,7 @@ void MenuManager_::selectButtonLong()
DATE_FORMAT = dateFormat[dateFormatIndex];
saveSettings();
case WeekdayMenu:
case SoundMenu:
case TempMenu:
saveSettings();
break;

View File

@@ -16,8 +16,6 @@
#include <LightDependentResistor.h>
#include <MenuManager.h>
#define SOUND_OFF false
#ifdef ULANZI
// Pinouts für das ULANZI-Environment
#define BATTERY_PIN 34
@@ -43,8 +41,16 @@
#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);
#endif
EasyButton button_left(BUTTON_UP_PIN);
EasyButton button_right(BUTTON_DOWN_PIN);
EasyButton button_select(BUTTON_SELECT_PIN);
@@ -74,6 +80,7 @@ unsigned long previousMillis_LDR = 0;
const unsigned long interval_BatTempHum = 10000;
const unsigned long interval_LDR = 100;
int total = 0;
unsigned long startTime;
const int LDRReadings = 10;
int TotalLDRReadings[LDRReadings];
@@ -81,6 +88,11 @@ 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()
{
@@ -93,14 +105,36 @@ PeripheryManager_ &PeripheryManager = PeripheryManager.getInstance();
void left_button_pressed()
{
DisplayManager.leftButton();
MenuManager.leftButton();
if (AP_MODE)
{
--MATRIX_LAYOUT;
if (MATRIX_LAYOUT < 0)
MATRIX_LAYOUT = 2;
saveSettings();
ESP.restart();
}
else
{
DisplayManager.leftButton();
MenuManager.leftButton();
}
}
void right_button_pressed()
{
DisplayManager.rightButton();
MenuManager.rightButton();
if (AP_MODE)
{
++MATRIX_LAYOUT;
if (MATRIX_LAYOUT > 2)
MATRIX_LAYOUT = 0;
saveSettings();
ESP.restart();
}
else
{
DisplayManager.rightButton();
MenuManager.rightButton();
}
}
void select_button_pressed()
@@ -129,17 +163,28 @@ void select_button_tripple()
void PeripheryManager_::playBootSound()
{
if (SOUND_OFF)
if (!SOUND_ACTIVE)
return;
if (BOOT_SOUND == "")
{
#ifdef ULANZI
const int nNotes = 6;
String notes[nNotes] = {"E5", "C5", "G4", "E4", "G4", "C5"};
const int timeUnit = 150;
Melody melody = MelodyFactory.load("Nice Melody", timeUnit, notes, nNotes);
player.playAsync(melody);
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
playFromFile(DFMINI_MP3_BOOT);
// no standardsound
#endif
}
else
{
#ifdef ULANZI
playFromFile("/MELODIES/" + BOOT_SOUND + ".txt");
#else
dfmp3.playMp3FolderTrack(BOOT_SOUND.toInt());
#endif
}
}
void PeripheryManager_::stopSound()
@@ -153,10 +198,16 @@ void PeripheryManager_::stopSound()
#endif
}
void PeripheryManager_::setVolume(uint8_t vol)
{
#ifdef AWTRIX_UPGRADE
dfmp3.setVolume(vol);
#endif
}
void PeripheryManager_::playFromFile(String file)
{
if (SOUND_OFF)
if (!SOUND_ACTIVE)
return;
#ifdef ULANZI
Melody melody = MelodyFactory.loadRtttlFile(file);
@@ -188,7 +239,7 @@ void firstStart()
CURRENT_TEMP -= 9.0;
#else
CURRENT_TEMP = bme280.readTemperature();
CURRENT_HUM = 0;
CURRENT_HUM = bme280.readHumidity();
#endif
uint16_t LDRVALUE = analogRead(LDR_PIN);
@@ -199,6 +250,7 @@ void firstStart()
void PeripheryManager_::setup()
{
startTime = millis();
pinMode(LDR_PIN, INPUT);
#ifdef ULANZI
pinMode(BUZZER_PIN, OUTPUT);
@@ -221,6 +273,7 @@ void PeripheryManager_::setup()
sht31.begin(0x44);
#else
bme280.begin();
dfmp3.begin();
#endif
photocell.setPhotocellPositionOnGround(false);
firstStart();
@@ -340,3 +393,19 @@ void PeripheryManager_::checkAlarms()
}
}
}
const char *PeripheryManager_::readUptime()
{
static char uptime[25]; // Make the array static to keep it from being destroyed when the function returns
unsigned long currentTime = millis();
unsigned long elapsedTime = currentTime - startTime;
unsigned long uptimeSeconds = elapsedTime / 1000;
unsigned long uptimeMinutes = uptimeSeconds / 60;
unsigned long uptimeHours = uptimeMinutes / 60;
unsigned long uptimeDays = uptimeHours / 24;
unsigned long hours = uptimeHours % 24;
unsigned long minutes = uptimeMinutes % 60;
unsigned long seconds = uptimeSeconds % 60;
sprintf(uptime, "P%dDT%dH%dM%dS", uptimeDays, hours, minutes, seconds);
return uptime;
}

View File

@@ -35,6 +35,8 @@ public:
void playFromFile(String file);
bool isPlaying();
void stopSound();
void setVolume(uint8_t);
const char *readUptime();
};
extern PeripheryManager_ &PeripheryManager;

View File

@@ -9,26 +9,11 @@
#include <LittleFS.h>
#include <WiFi.h>
#include "DisplayManager.h"
#include "UpdateManager.h"
WebServer server(80);
FSWebServer mws(LittleFS, server);
bool FSOPEN;
void startLittleFS()
{
if (LittleFS.begin())
{
LittleFS.mkdir("/MELODIES");
LittleFS.mkdir("/ICONS");
FSOPEN = true;
}
else
{
Serial.println("ERROR on mounting LittleFS. It will be formmatted!");
LittleFS.format();
ESP.restart();
}
}
// The getter for the instantiated singleton instance
ServerManager_ &ServerManager_::getInstance()
@@ -49,26 +34,26 @@ void versionHandler()
void saveHandler()
{
WebServerClass *webRequest = mws.getRequest();
Serial.println("Save");
ServerManager.getInstance().loadSettings();
webRequest->send(200);
}
void ServerManager_::setup()
{
if (!local_IP.fromString(NET_IP) || !gateway.fromString(NET_GW) || !subnet.fromString(NET_SN) || !primaryDNS.fromString(NET_PDNS) || !secondaryDNS.fromString(NET_SDNS))
NET_STATIC = false;
if (NET_STATIC)
{
WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);
}
IPAddress myIP = mws.startWiFi(10000, uniqueID, "12345678");
myIP = mws.startWiFi(10000, uniqueID, "12345678");
isConnected = !(myIP == IPAddress(192, 168, 4, 1));
Serial.println(myIP.toString());
if (isConnected)
{
mws.addOptionBox("Network");
mws.addOption("Static IP", NET_STATIC);
mws.addOption("Local IP", NET_IP);
@@ -91,11 +76,32 @@ void ServerManager_::setup()
mws.addHTML(custom_html, "icon_html");
mws.addCSS(custom_css);
mws.addJavascript(custom_script);
mws.addOptionBox("General");
mws.addOption("Uppercase letters", UPPERCASE_LETTERS);
mws.addHandler("/save", HTTP_GET, saveHandler);
mws.addHandler("/save", HTTP_POST, saveHandler);
mws.addHandler("/api/notify", HTTP_POST, []()
{DisplayManager.generateNotification(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/nextapp", HTTP_POST, []()
{DisplayManager.nextApp(); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/previousapp", HTTP_POST, []()
{DisplayManager.previousApp(); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/timer", HTTP_POST, []()
{ DisplayManager.gererateTimer(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/notify/dismiss", HTTP_POST, []()
{ DisplayManager.dismissNotify(); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/apps", HTTP_POST, []()
{ DisplayManager.updateAppVector(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/switch", HTTP_POST, []()
{ DisplayManager.switchToApp(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/settings", HTTP_POST, []()
{ DisplayManager.setNewSettings(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/custom", HTTP_POST, []()
{ DisplayManager.generateCustomPage(mws.webserver->arg("name"),mws.webserver->arg("plain").c_str()); mws.webserver->send(200,"OK"); });
mws.addHandler("/api/stats", HTTP_GET, []()
{ mws.webserver->sendContent(DisplayManager.getStat()); });
mws.addHandler("/api/doupdate", HTTP_POST, []()
{ if (UPDATE_AVAILABLE)
UpdateManager.updateFirmware(); mws.webserver->send(200,"OK"); });
Serial.println("Webserver loaded");
}
mws.addHandler("/version", HTTP_GET, versionHandler);
mws.begin();
@@ -129,12 +135,10 @@ uint16_t stringToColor(const String &str)
String gStr = str.substring(comma1 + 1, comma2);
String bStr = str.substring(comma2 + 1);
int r = rStr.toInt();
int g = gStr.toInt();
int b = bStr.toInt();
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
{
return 0xFFFF;
@@ -164,9 +168,6 @@ String colorToString(uint16_t color)
void ServerManager_::loadSettings()
{
if (!FSOPEN)
startLittleFS();
if (LittleFS.exists("/config.json"))
{
File file = LittleFS.open("/config.json", "r");
@@ -189,7 +190,6 @@ void ServerManager_::loadSettings()
NET_SN = doc["Subnet"].as<String>();
NET_PDNS = doc["Primary DNS"].as<String>();
NET_SDNS = doc["Secondary DNS"].as<String>();
UPPERCASE_LETTERS = doc["Uppercase letters"];
file.close();
DisplayManager.applyAllSettings();
Serial.println(F("Configuration loaded"));

View File

@@ -14,9 +14,9 @@ public:
void tick();
void loadSettings();
bool isConnected;
void SaveSettings();
IPAddress myIP;
};
extern ServerManager_ &ServerManager;
#endif
#endif

View File

@@ -1,20 +1,34 @@
#include <UpdateManager.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <WiFiClientSecure.h>
#include "cert.h"
#include "DisplayManager.h"
#include <Ticker.h>
#include "Globals.h"
#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"
Ticker UpdateTicker;
// The getter for the instantiated singleton instance
UpdateManager_ &UpdateManager_::getInstance()
{
static UpdateManager_ instance;
return instance;
}
// Initialize the global shared instance
UpdateManager_ &UpdateManager = UpdateManager.getInstance();
void update_started()
{
}
void update_finished()
{
}
void update_progress(int cur, int total)
@@ -29,7 +43,7 @@ void update_error(int err)
DisplayManager.show();
}
void updateFirmware()
void UpdateManager_::updateFirmware()
{
WiFiClientSecure client;
client.setCACert(rootCACertificate);
@@ -56,13 +70,15 @@ void updateFirmware()
}
}
bool FirmwareVersionCheck()
bool UpdateManager_::checkUpdate(bool withScreen)
{
if (withScreen)
{
DisplayManager.clear();
DisplayManager.printText(0, 6, "CHECK", true, true);
DisplayManager.show();
}
DisplayManager.clear();
DisplayManager.printText(0, 6, "CHECK", true, true);
DisplayManager.show();
String payload;
int httpCode;
String fwurl = "";
@@ -103,20 +119,34 @@ bool FirmwareVersionCheck()
payload.trim();
if (payload.equals(VERSION))
{
Serial.printf("\nDevice already on latest firmware version:%s\n", VERSION);
DisplayManager.clear();
DisplayManager.printText(0, 6, "NO UP :(", true, true);
DisplayManager.show();
delay(1000);
UPDATE_AVAILABLE = false;
Serial.printf("\nDevice already on latest firmware version: %s\n", VERSION);
if (withScreen)
{
DisplayManager.clear();
DisplayManager.printText(0, 6, "NO UP :(", true, true);
DisplayManager.show();
delay(1000);
}
return 0;
}
else
{
Serial.println(payload);
Serial.println("New firmware detected");
DisplayManager.printText(0, 6, payload.c_str(), true, true);
UPDATE_AVAILABLE = true;
return 1;
}
}
UPDATE_AVAILABLE = false;
return 0;
}
void checkUpdateNoReturn()
{
Serial.println("Check Update");
UpdateManager.getInstance().checkUpdate(false);
}
void UpdateManager_::setup()
{
UpdateTicker.attach(3600, checkUpdateNoReturn);
}

18
src/UpdateManager.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef UpdateManager_h
#define UpdateManager_h
#include <Arduino.h>
class UpdateManager_
{
private:
UpdateManager_() = default;
public:
static UpdateManager_ &getInstance();
void setup();
bool checkUpdate(bool);
void updateFirmware();
};
extern UpdateManager_ &UpdateManager;
#endif

View File

@@ -36,6 +36,7 @@
#include "MQTTManager.h"
#include "ServerManager.h"
#include "Globals.h"
#include "UpdateManager.h"
TaskHandle_t taskHandle;
volatile bool StopTask = false;
@@ -73,13 +74,23 @@ void setup()
{
MQTTManager.setup();
DisplayManager.loadNativeApps();
UpdateManager.setup();
UpdateManager.checkUpdate(false);
StopTask = true;
float x = 4;
while (x >= -85)
{
DisplayManager.HSVtext(x, 6, ("AWTRIX " + ServerManager.myIP.toString()).c_str(), true);
x -= 0.18;
}
}
else
{
AP_MODE = true;
StopTask = true;
}
StopTask = true;
delay(200);
DisplayManager.clearMatrix();
}
@@ -88,9 +99,9 @@ void loop()
{
ServerManager.tick();
DisplayManager.tick();
PeripheryManager.tick();
if (ServerManager.isConnected)
{
PeripheryManager.tick();
MQTTManager.tick();
}
}

View File

@@ -1 +1 @@
0.43
0.47