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_name: firmware.bin
asset_content_type: application/octet-stream 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 - name: Commit
id: commit id: commit
uses: stefanzweifel/git-auto-commit-action@v4 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 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) ![GitHub Sponsors](https://img.shields.io/github/sponsors/blueforcer?style=flat-square)
![Discord](https://img.shields.io/discord/546407049148366859?label=Discord&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) ![GIF-230324_161917](https://user-images.githubusercontent.com/31169771/227567565-3780ee83-1158-4371-9390-fd03a7873496.gif)
</div> </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. 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 - Onscreen menu where you can change your settings directly on the device
- Pre-installed Apps like time, date, temperature, humidity and battery - Pre-installed Apps like time, date, temperature, humidity and battery
- Add customapps without recompiling straight from your Smarthome. - Add customapps without recompiling straight from your Smarthome.
- Noitification support
- Easy to use icon system - Easy to use icon system
- Powerful MQTT commands - Powerful MQTT commands
- HTTP API
- RTTTL melody player - RTTTL melody player
- Integrated filebrowser - Integrated filebrowser
- No cloud - 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 Starting is easy as 1-2-3
[with the documentation](https://blueforcer.github.io/awtrix-light/) [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 # Community
Join the discord channel with over 1500 Users Join the discord channel with over 1500 Users
https://discord.gg/cyBCpdx 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 # 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). 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. 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. 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. 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. 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. 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! 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 - Features
- [Apps](apps.md) - [Apps](apps.md)
- [Alarm clock](alarm.md) - [Alarm clock](alarm.md)
- [Timer](timer.md)
- [Icons](icons.md) - [Icons](icons.md)
- [Sounds](sounds.md) - [Sounds](sounds.md)
- [Hidden features](dev.md)
- MQTT - API
- [MQTT/HTTP](api.md)
- [Custom Pages & Notifications](custom.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. 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 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 ## JSON Properties
@@ -9,12 +11,12 @@ The JSON object has the following properties:
| Key | Type | Description | Default | | 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. | | | `text` | string | The text to display on the page. | |
| `icon` | string | The icon ID or filename (without extension) 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 | | `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 | | `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] | | `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 | | `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). | | | `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 ## Delete a custom app
To delelte a custom page simply send a empty payload to the same topic 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", "name": "AWTRIX Light",
"version": "0.43", "version": "0.47",
"home_assistant_domain": "AwtrixLight", "home_assistant_domain": "AwtrixLight",
"funding_url": "https://blueforcer.de", "funding_url": "https://blueforcer.de",
"new_install_prompt_erase": true, "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. | | `WEEKDAY` | Allows selection of start of week. |
| `TEMP` | Allows selection of temperature system (°C or °F). | | `TEMP` | Allows selection of temperature system (°C or °F). |
| `APPS` | Allows to enable or disable internal apps | | `APPS` | Allows to enable or disable internal apps |
| `SOUND` | Allows to enable or disable sound output |
| `UPDATE` | Check and download new firmware if available. | | `UPDATE` | Check and download new firmware if available. |

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ lib_deps =
fastled/FastLED@^3.5.0 fastled/FastLED@^3.5.0
marcmerlin/FastLED NeoMatrix@^1.2 marcmerlin/FastLED NeoMatrix@^1.2
knolleary/PubSubClient@^2.8 knolleary/PubSubClient@^2.8
plerup/EspSoftwareSerial@^8.0.1
[env:awtrix_upgrade] [env:awtrix_upgrade]
platform = https://github.com/platformio/platform-espressif32.git platform = https://github.com/platformio/platform-espressif32.git
@@ -37,4 +38,5 @@ lib_deps =
evert-arias/EasyButton@^2.0.1 evert-arias/EasyButton@^2.0.1
fastled/FastLED@^3.5.0 fastled/FastLED@^3.5.0
marcmerlin/FastLED NeoMatrix@^1.2 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 isGif;
bool rainbow; bool rainbow;
bool soundPlayed; bool soundPlayed;
uint16_t duration = 0;
String sound; String sound;
int16_t repeat = 0; int16_t repeat = 0;
int16_t currentRepeat = 0; int16_t currentRepeat = 0;
@@ -61,7 +62,7 @@ struct Notification
bool isGif; bool isGif;
bool flag = false; bool flag = false;
unsigned long startime = 0; unsigned long startime = 0;
unsigned long duration = 0; uint16_t duration = 0;
int16_t repeat = -1; int16_t repeat = -1;
bool hold = false; bool hold = false;
byte pushIcon = 0; byte pushIcon = 0;
@@ -104,7 +105,7 @@ int findAppIndexByName(const String &name)
return -1; 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) if (notify.flag)
return; 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) if (notify.flag)
return; 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) if (notify.flag)
return; 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) if (notify.flag)
return; return;
@@ -215,7 +216,7 @@ void HumApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, i
} }
#ifdef ULANZI #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) if (notify.flag)
return; 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 // Abort if notify.flag is set
if (notify.flag) if (notify.flag)
@@ -304,8 +305,8 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState
return; return;
} }
// reset custom App properties if last App // reset custom App properties if last frame
if (lastApp) if (lastFrame)
{ {
ca->iconWasPushed = false; ca->iconWasPushed = false;
ca->scrollposition = 9; ca->scrollposition = 9;
@@ -313,6 +314,14 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState
ca->scrollDelay = 0; ca->scrollDelay = 0;
} }
if (!DisplayManager.appIsSwitching)
{
if (ca->duration > 0)
{
DisplayManager.setAppTime(ca->duration);
}
}
CURRENT_APP = ca->name; CURRENT_APP = ca->name;
currentCustomApp = name; currentCustomApp = name;
@@ -634,64 +643,126 @@ void NotifyApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state)
DisplayManager.getInstance().resetTextColor(); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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) 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) if (notify.flag)
return; return;

View File

@@ -69,6 +69,17 @@ const char HAtransID[] PROGMEM = {"%s_tra"};
const char HAtransName[] PROGMEM = {"Transition"}; const char HAtransName[] PROGMEM = {"Transition"};
const char HAtransIcon[] PROGMEM = {"mdi:swap-vertical"}; 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 HAsigID[] PROGMEM = {"%s_sig"};
const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"}; const char HAsigIcon[] PROGMEM = {"mdi:sun-wireless"};
const char HAsigName[] PROGMEM = {"WiFi strength"}; const char HAsigName[] PROGMEM = {"WiFi strength"};
@@ -104,3 +115,4 @@ const char TempKey[] PROGMEM = {"temp"};
const char HumKey[] PROGMEM = {"hum"}; const char HumKey[] PROGMEM = {"hum"};
const char UpTimeKey[] PROGMEM = {"uptime"}; const char UpTimeKey[] PROGMEM = {"uptime"};
const char SignalStrengthKey[] PROGMEM = {"wifi_signal"}; 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 HAupName[];
extern const char HAupClass[]; extern const char HAupClass[];
extern const char HAdoUpID[];
extern const char HAdoUpName[];
extern const char HAdoUpIcon[];
extern const char HAtransID[]; extern const char HAtransID[];
extern const char HAtransName[]; extern const char HAtransName[];
extern const char HAtransIcon[]; 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 HAbtnLID[];
extern const char HAbtnLName[]; extern const char HAbtnLName[];
@@ -106,5 +115,5 @@ extern const char TempKey[];
extern const char HumKey[]; extern const char HumKey[];
extern const char UpTimeKey[]; extern const char UpTimeKey[];
extern const char SignalStrengthKey[]; extern const char SignalStrengthKey[];
extern const char UpdateKey[];
#endif #endif

View File

@@ -13,6 +13,7 @@
#include "ServerManager.h" #include "ServerManager.h"
#include "MenuManager.h" #include "MenuManager.h"
#include "Apps.h" #include "Apps.h"
#include "Dictionary.h"
Ticker AlarmTicker; Ticker AlarmTicker;
Ticker TimerTicker; Ticker TimerTicker;
@@ -26,8 +27,6 @@ Ticker TimerTicker;
#define MATRIX_WIDTH 32 #define MATRIX_WIDTH 32
#define MATRIX_HEIGHT 8 #define MATRIX_HEIGHT 8
bool appIsSwitching;
GifPlayer gif; GifPlayer gif;
CRGB leds[MATRIX_WIDTH * MATRIX_HEIGHT]; CRGB leds[MATRIX_WIDTH * MATRIX_HEIGHT];
@@ -214,7 +213,7 @@ void pushCustomApp(String name, int position)
if (customApps.count(name) == 0) if (customApps.count(name) == 0)
{ {
++customPagesCount; ++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 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 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) auto it = std::find_if(Apps.begin(), Apps.end(), [&name](const std::pair<String, AppCallback> &appPair)
{ return appPair.first == name; }); { 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) 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)); customApps.erase(customApps.find(name));
removeCustomApp(name); removeCustomApp(name);
return; return;
@@ -295,6 +293,7 @@ void DisplayManager_::generateCustomPage(String name, const char *json)
customApp.barSize = 0; 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; int pos = doc.containsKey("pos") ? doc["pos"].as<uint8_t>() : -1;
customApp.rainbow = doc.containsKey("rainbow") ? doc["rainbow"] : false; customApp.rainbow = doc.containsKey("rainbow") ? doc["rainbow"] : false;
customApp.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0; customApp.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0;
@@ -519,6 +518,7 @@ void DisplayManager_::loadNativeApps()
void DisplayManager_::setup() void DisplayManager_::setup()
{ {
TJpgDec.setCallback(jpg_output); TJpgDec.setCallback(jpg_output);
FastLED.addLeds<NEOPIXEL, MATRIX_PIN>(leds, MATRIX_WIDTH * MATRIX_HEIGHT); FastLED.addLeds<NEOPIXEL, MATRIX_PIN>(leds, MATRIX_WIDTH * MATRIX_HEIGHT);
gif.setMatrix(&matrix); gif.setMatrix(&matrix);
@@ -536,17 +536,17 @@ void DisplayManager_::tick()
} }
else else
{ {
ui.update();
if (ui.getUiState()->appState == IN_TRANSITION && !appIsSwitching) if (ui.getUiState()->appState == IN_TRANSITION && !appIsSwitching)
{ {
appIsSwitching = true; appIsSwitching = true;
MQTTManager.setCurrentApp(CURRENT_APP);
} }
else if (ui.getUiState()->appState == FIXED && appIsSwitching) else if (ui.getUiState()->appState == FIXED && appIsSwitching)
{ {
appIsSwitching = false; appIsSwitching = false;
MQTTManager.setCurrentApp(CURRENT_APP); MQTTManager.setCurrentApp(CURRENT_APP);
setAppTime(TIME_PER_APP);
} }
ui.update();
} }
} }
@@ -788,7 +788,8 @@ void DisplayManager_::updateAppVector(const char *json)
bool show = true; bool show = true;
int position = -1; int position = -1;
if (app.containsKey("show")) if (app.containsKey("show"))
{ {
show = app["show"].as<bool>(); show = app["show"].as<bool>();
} }
@@ -819,13 +820,17 @@ void DisplayManager_::updateAppVector(const char *json)
callback = HumApp; callback = HumApp;
SHOW_HUM = show; SHOW_HUM = show;
} }
#ifdef ULANZI #ifdef ULANZI
else if (name == "bat") else if (name == "bat")
{ {
callback = BatApp; callback = BatApp;
SHOW_BAT = show; SHOW_BAT = show;
} }
#endif #endif
else else
{ {
// If the app is not one of the built-in apps, check if it's already in the vector // 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); Apps = std::move(newApps);
ui.setApps(Apps); ui.setApps(Apps);
saveSettings(); 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: public:
static DisplayManager_ &getInstance(); static DisplayManager_ &getInstance();
bool appIsSwitching;
void setup(); void setup();
void tick(); void tick();
void clear(); void clear();
@@ -48,7 +49,7 @@ public:
void setFPS(uint8_t); void setFPS(uint8_t);
void MatrixState(bool); void MatrixState(bool);
void generateNotification(const char *json); 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); void printText(int16_t x, int16_t y, const char *text, bool centered, bool ignoreUppercase);
bool setAutoTransition(bool active); bool setAutoTransition(bool active);
void switchToApp(const char *json); 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 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 drawBarChart(int16_t x, int16_t y, const int data[], byte dataSize, bool withIcon, uint16_t color);
void updateAppVector(const char *json); void updateAppVector(const char *json);
void setMatrixLayout(int layout);
void setAppTime(uint16_t duration);
String getStat();
}; };
extern DisplayManager_ &DisplayManager; extern DisplayManager_ &DisplayManager;

View File

@@ -1,6 +1,8 @@
#include "Globals.h" #include "Globals.h"
#include "Preferences.h" #include "Preferences.h"
#include <WiFi.h> #include <WiFi.h>
#include <ArduinoJson.h>
#include <LittleFS.h>
Preferences Settings; Preferences Settings;
@@ -13,8 +15,53 @@ char *getID()
return macStr; 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() void loadSettings()
{ {
startLittleFS();
Settings.begin("awtrix", false); Settings.begin("awtrix", false);
MATRIX_FPS = Settings.getUChar("FPS", 23); MATRIX_FPS = Settings.getUChar("FPS", 23);
BRIGHTNESS = Settings.getUChar("BRI", 120); BRIGHTNESS = Settings.getUChar("BRI", 120);
@@ -31,12 +78,15 @@ void loadSettings()
SHOW_DATE = Settings.getBool("DAT", true); SHOW_DATE = Settings.getBool("DAT", true);
SHOW_TEMP = Settings.getBool("TEMP", true); SHOW_TEMP = Settings.getBool("TEMP", true);
SHOW_HUM = Settings.getBool("HUM", true); SHOW_HUM = Settings.getBool("HUM", true);
MATRIX_LAYOUT = Settings.getUInt("MAT", 0);
#ifdef ULANZI #ifdef ULANZI
SHOW_BAT = Settings.getBool("BAT", true); SHOW_BAT = Settings.getBool("BAT", true);
#endif #endif
SOUND_ACTIVE = Settings.getBool("SOUND", true);
Settings.end(); Settings.end();
uniqueID = getID(); uniqueID = getID();
MQTT_PREFIX = String(uniqueID); MQTT_PREFIX = String(uniqueID);
loadDevSettings();
} }
void saveSettings() void saveSettings()
@@ -57,9 +107,11 @@ void saveSettings()
Settings.putBool("DAT", SHOW_DATE); Settings.putBool("DAT", SHOW_DATE);
Settings.putBool("TEMP", SHOW_TEMP); Settings.putBool("TEMP", SHOW_TEMP);
Settings.putBool("HUM", SHOW_HUM); Settings.putBool("HUM", SHOW_HUM);
Settings.putUInt("MAT", MATRIX_LAYOUT);
#ifdef ULANZI #ifdef ULANZI
Settings.putBool("BAT", SHOW_BAT); Settings.putBool("BAT", SHOW_BAT);
#endif #endif
Settings.putBool("SOUND", SOUND_ACTIVE);
Settings.end(); Settings.end();
} }
@@ -69,7 +121,7 @@ IPAddress gateway;
IPAddress subnet; IPAddress subnet;
IPAddress primaryDNS; IPAddress primaryDNS;
IPAddress secondaryDNS; IPAddress secondaryDNS;
const char *VERSION = "0.45"; const char *VERSION = "0.47";
String MQTT_HOST = ""; String MQTT_HOST = "";
uint16_t MQTT_PORT = 1883; uint16_t MQTT_PORT = 1883;
String MQTT_USER; String MQTT_USER;
@@ -130,4 +182,10 @@ bool AP_MODE;
bool MATRIX_OFF; bool MATRIX_OFF;
bool TIMER_ACTIVE; bool TIMER_ACTIVE;
bool ALARM_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 String DATE_FORMAT;
extern bool START_ON_MONDAY; extern bool START_ON_MONDAY;
extern bool IS_CELSIUS; 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 loadSettings();
void saveSettings(); void saveSettings();
#endif // Globals_H #endif // Globals_H

View File

@@ -6,8 +6,8 @@
#include <WiFi.h> #include <WiFi.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "Dictionary.h" #include "Dictionary.h"
#include "PeripheryManager.h"
unsigned long startTime; #include "UpdateManager.h"
WiFiClient espClient; WiFiClient espClient;
uint8_t lastBrightness; uint8_t lastBrightness;
@@ -37,6 +37,8 @@ HASensor *ram = nullptr;
HABinarySensor *btnleft = nullptr; HABinarySensor *btnleft = nullptr;
HABinarySensor *btnmid = nullptr; HABinarySensor *btnmid = nullptr;
HABinarySensor *btnright = nullptr; HABinarySensor *btnright = nullptr;
HABinarySensor *update = nullptr;
HAButton *doUpdate = nullptr;
// The getter for the instantiated singleton instance // The getter for the instantiated singleton instance
MQTTManager_ &MQTTManager_::getInstance() MQTTManager_ &MQTTManager_::getInstance()
@@ -62,6 +64,11 @@ void onButtonCommand(HAButton *sender)
{ {
DisplayManager.previousApp(); DisplayManager.previousApp();
} }
else if (sender == doUpdate)
{
if (UPDATE_AVAILABLE)
UpdateManager.updateFirmware();
}
} }
void onSwitchCommand(bool state, HASwitch *sender) void onSwitchCommand(bool state, HASwitch *sender)
@@ -184,6 +191,12 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length)
DisplayManager.previousApp(); DisplayManager.previousApp();
return; return;
} }
if (strTopic == MQTT_PREFIX + "/doupdate")
{
if (UPDATE_AVAILABLE)
UpdateManager.updateFirmware();
return;
}
else if (strTopic.startsWith(MQTT_PREFIX + "/custom")) 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() void onMqttConnected()
{ {
String prefix = MQTT_PREFIX; String prefix = MQTT_PREFIX;
const char* topics[] PROGMEM = { const char *topics[] PROGMEM = {
"/brightness", "/brightness",
"/notify/dismiss", "/notify/dismiss",
"/notify", "/notify",
@@ -213,15 +226,17 @@ void onMqttConnected()
"/settings", "/settings",
"/previousapp", "/previousapp",
"/nextapp", "/nextapp",
"/doupdate",
"/nextapp", "/nextapp",
"/apps" "/apps"};
}; for (const char *topic : topics)
for (const char* topic : topics) { {
String fullTopic = prefix + topic; String fullTopic = prefix + topic;
mqtt.subscribe(fullTopic.c_str()); mqtt.subscribe(fullTopic.c_str());
} }
Serial.println(F("MQTT Connected")); Serial.println(F("MQTT Connected"));
} }
void connect() void connect()
{ {
mqtt.onMessage(onMqttMessage); mqtt.onMessage(onMqttMessage);
@@ -240,14 +255,14 @@ void connect()
} }
char matID[40], briID[40]; 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 #ifdef ULANZI
char batID[40]; char batID[40];
#endif #endif
void MQTTManager_::setup() void MQTTManager_::setup()
{ {
startTime = millis();
if (HA_DISCOVERY) if (HA_DISCOVERY)
{ {
Serial.println(F("Starting Homeassistant discorvery")); Serial.println(F("Starting Homeassistant discorvery"));
@@ -262,6 +277,8 @@ void MQTTManager_::setup()
device.setManufacturer(HAmanufacturer); device.setManufacturer(HAmanufacturer);
device.setModel(HAmodel); device.setModel(HAmodel);
device.setAvailability(true); device.setAvailability(true);
device.enableSharedAvailability();
device.enableLastWill();
String uniqueIDWithSuffix; String uniqueIDWithSuffix;
@@ -296,6 +313,12 @@ void MQTTManager_::setup()
dismiss->setIcon(HAbtnaIcon); dismiss->setIcon(HAbtnaIcon);
dismiss->setName(HAbtnaName); dismiss->setName(HAbtnaName);
sprintf(doUpdateID, HAdoUpID, macStr);
doUpdate = new HAButton(doUpdateID);
doUpdate->setIcon(HAdoUpIcon);
doUpdate->setName(HAdoUpName);
doUpdate->onCommand(onButtonCommand);
sprintf(transID, HAtransID, macStr); sprintf(transID, HAtransID, macStr);
transition = new HASwitch(transID); transition = new HASwitch(transID);
transition->setIcon(HAtransIcon); transition->setIcon(HAtransIcon);
@@ -370,6 +393,12 @@ void MQTTManager_::setup()
btnleft = new HABinarySensor(btnLID); btnleft = new HABinarySensor(btnLID);
btnleft->setName(HAbtnLName); btnleft->setName(HAbtnLName);
sprintf(updateID, HAupdateID, macStr);
update = new HABinarySensor(updateID);
update->setIcon(HAupdateIcon);
update->setName(HAupdateName);
update->setDeviceClass(HAupdateClass);
sprintf(btnMID, HAbtnMID, macStr); sprintf(btnMID, HAbtnMID, macStr);
btnmid = new HABinarySensor(btnMID); btnmid = new HABinarySensor(btnMID);
btnmid->setName(HAbtnMName); btnmid->setName(HAbtnMName);
@@ -418,22 +447,6 @@ void MQTTManager_::setCurrentApp(String value)
curApp->setValue(value.c_str()); 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() void MQTTManager_::sendStats()
{ {
if (HA_DISCOVERY) if (HA_DISCOVERY)
@@ -466,33 +479,17 @@ void MQTTManager_::sendStats()
int freeHeapBytes = ESP.getFreeHeap(); int freeHeapBytes = ESP.getFreeHeap();
itoa(freeHeapBytes, rambuffer, 10); itoa(freeHeapBytes, rambuffer, 10);
ram->setValue(rambuffer); ram->setValue(rambuffer);
uptime->setValue(readUptime()); uptime->setValue(PeripheryManager.readUptime());
version->setValue(VERSION); version->setValue(VERSION);
transition->setState(AUTO_TRANSITION, false); transition->setState(AUTO_TRANSITION, false);
update->setState(UPDATE_AVAILABLE, false);
} }
else else
{ {
} }
StaticJsonDocument<200> doc; publish(StatsTopic, DisplayManager.getStat().c_str());
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());
} }
void MQTTManager_::sendButton(byte btn, bool state) void MQTTManager_::sendButton(byte btn, bool state)

View File

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

View File

@@ -16,8 +16,6 @@
#include <LightDependentResistor.h> #include <LightDependentResistor.h>
#include <MenuManager.h> #include <MenuManager.h>
#define SOUND_OFF false
#ifdef ULANZI #ifdef ULANZI
// Pinouts für das ULANZI-Environment // Pinouts für das ULANZI-Environment
#define BATTERY_PIN 34 #define BATTERY_PIN 34
@@ -43,8 +41,16 @@
#ifdef ULANZI #ifdef ULANZI
Adafruit_SHT31 sht31; Adafruit_SHT31 sht31;
#else #else
class Mp3Notify
{
};
Adafruit_BME280 bme280; Adafruit_BME280 bme280;
SoftwareSerial mySoftwareSerial(D7, D5); // RX, TX
typedef DFMiniMp3<SoftwareSerial, Mp3Notify> DfMp3;
DfMp3 dfmp3(mySoftwareSerial);
#endif #endif
EasyButton button_left(BUTTON_UP_PIN); EasyButton button_left(BUTTON_UP_PIN);
EasyButton button_right(BUTTON_DOWN_PIN); EasyButton button_right(BUTTON_DOWN_PIN);
EasyButton button_select(BUTTON_SELECT_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_BatTempHum = 10000;
const unsigned long interval_LDR = 100; const unsigned long interval_LDR = 100;
int total = 0; int total = 0;
unsigned long startTime;
const int LDRReadings = 10; const int LDRReadings = 10;
int TotalLDRReadings[LDRReadings]; int TotalLDRReadings[LDRReadings];
@@ -81,6 +88,11 @@ float sampleSum = 0.0;
float sampleAverage = 0.0; float sampleAverage = 0.0;
float brightnessPercent = 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 // The getter for the instantiated singleton instance
PeripheryManager_ &PeripheryManager_::getInstance() PeripheryManager_ &PeripheryManager_::getInstance()
{ {
@@ -93,14 +105,36 @@ PeripheryManager_ &PeripheryManager = PeripheryManager.getInstance();
void left_button_pressed() void left_button_pressed()
{ {
DisplayManager.leftButton(); if (AP_MODE)
MenuManager.leftButton(); {
--MATRIX_LAYOUT;
if (MATRIX_LAYOUT < 0)
MATRIX_LAYOUT = 2;
saveSettings();
ESP.restart();
}
else
{
DisplayManager.leftButton();
MenuManager.leftButton();
}
} }
void right_button_pressed() void right_button_pressed()
{ {
DisplayManager.rightButton(); if (AP_MODE)
MenuManager.rightButton(); {
++MATRIX_LAYOUT;
if (MATRIX_LAYOUT > 2)
MATRIX_LAYOUT = 0;
saveSettings();
ESP.restart();
}
else
{
DisplayManager.rightButton();
MenuManager.rightButton();
}
} }
void select_button_pressed() void select_button_pressed()
@@ -129,17 +163,28 @@ void select_button_tripple()
void PeripheryManager_::playBootSound() void PeripheryManager_::playBootSound()
{ {
if (SOUND_OFF) if (!SOUND_ACTIVE)
return; return;
if (BOOT_SOUND == "")
{
#ifdef ULANZI #ifdef ULANZI
const int nNotes = 6; const int nNotes = 6;
String notes[nNotes] = {"E5", "C5", "G4", "E4", "G4", "C5"}; String notes[nNotes] = {"E5", "C5", "G4", "E4", "G4", "C5"};
const int timeUnit = 150; const int timeUnit = 150;
Melody melody = MelodyFactory.load("Nice Melody", timeUnit, notes, nNotes); Melody melody = MelodyFactory.load("Bootsound", timeUnit, notes, nNotes);
player.playAsync(melody); player.playAsync(melody);
#else #else
playFromFile(DFMINI_MP3_BOOT); // no standardsound
#endif #endif
}
else
{
#ifdef ULANZI
playFromFile("/MELODIES/" + BOOT_SOUND + ".txt");
#else
dfmp3.playMp3FolderTrack(BOOT_SOUND.toInt());
#endif
}
} }
void PeripheryManager_::stopSound() void PeripheryManager_::stopSound()
@@ -153,10 +198,16 @@ void PeripheryManager_::stopSound()
#endif #endif
} }
void PeripheryManager_::setVolume(uint8_t vol)
{
#ifdef AWTRIX_UPGRADE
dfmp3.setVolume(vol);
#endif
}
void PeripheryManager_::playFromFile(String file) void PeripheryManager_::playFromFile(String file)
{ {
if (SOUND_OFF) if (!SOUND_ACTIVE)
return; return;
#ifdef ULANZI #ifdef ULANZI
Melody melody = MelodyFactory.loadRtttlFile(file); Melody melody = MelodyFactory.loadRtttlFile(file);
@@ -188,7 +239,7 @@ void firstStart()
CURRENT_TEMP -= 9.0; CURRENT_TEMP -= 9.0;
#else #else
CURRENT_TEMP = bme280.readTemperature(); CURRENT_TEMP = bme280.readTemperature();
CURRENT_HUM = 0; CURRENT_HUM = bme280.readHumidity();
#endif #endif
uint16_t LDRVALUE = analogRead(LDR_PIN); uint16_t LDRVALUE = analogRead(LDR_PIN);
@@ -199,6 +250,7 @@ void firstStart()
void PeripheryManager_::setup() void PeripheryManager_::setup()
{ {
startTime = millis();
pinMode(LDR_PIN, INPUT); pinMode(LDR_PIN, INPUT);
#ifdef ULANZI #ifdef ULANZI
pinMode(BUZZER_PIN, OUTPUT); pinMode(BUZZER_PIN, OUTPUT);
@@ -221,6 +273,7 @@ void PeripheryManager_::setup()
sht31.begin(0x44); sht31.begin(0x44);
#else #else
bme280.begin(); bme280.begin();
dfmp3.begin();
#endif #endif
photocell.setPhotocellPositionOnGround(false); photocell.setPhotocellPositionOnGround(false);
firstStart(); 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); void playFromFile(String file);
bool isPlaying(); bool isPlaying();
void stopSound(); void stopSound();
void setVolume(uint8_t);
const char *readUptime();
}; };
extern PeripheryManager_ &PeripheryManager; extern PeripheryManager_ &PeripheryManager;

View File

@@ -9,26 +9,11 @@
#include <LittleFS.h> #include <LittleFS.h>
#include <WiFi.h> #include <WiFi.h>
#include "DisplayManager.h" #include "DisplayManager.h"
#include "UpdateManager.h"
WebServer server(80); WebServer server(80);
FSWebServer mws(LittleFS, server); FSWebServer mws(LittleFS, server);
bool FSOPEN; 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 // The getter for the instantiated singleton instance
ServerManager_ &ServerManager_::getInstance() ServerManager_ &ServerManager_::getInstance()
@@ -49,26 +34,26 @@ void versionHandler()
void saveHandler() void saveHandler()
{ {
WebServerClass *webRequest = mws.getRequest(); WebServerClass *webRequest = mws.getRequest();
Serial.println("Save");
ServerManager.getInstance().loadSettings(); ServerManager.getInstance().loadSettings();
webRequest->send(200); webRequest->send(200);
} }
void ServerManager_::setup() 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)) 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; NET_STATIC = false;
if (NET_STATIC) if (NET_STATIC)
{ {
WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS); 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)); isConnected = !(myIP == IPAddress(192, 168, 4, 1));
Serial.println(myIP.toString()); Serial.println(myIP.toString());
if (isConnected) if (isConnected)
{ {
mws.addOptionBox("Network"); mws.addOptionBox("Network");
mws.addOption("Static IP", NET_STATIC); mws.addOption("Static IP", NET_STATIC);
mws.addOption("Local IP", NET_IP); mws.addOption("Local IP", NET_IP);
@@ -91,11 +76,32 @@ void ServerManager_::setup()
mws.addHTML(custom_html, "icon_html"); mws.addHTML(custom_html, "icon_html");
mws.addCSS(custom_css); mws.addCSS(custom_css);
mws.addJavascript(custom_script); mws.addJavascript(custom_script);
mws.addOptionBox("General"); mws.addHandler("/save", HTTP_POST, saveHandler);
mws.addOption("Uppercase letters", UPPERCASE_LETTERS); mws.addHandler("/api/notify", HTTP_POST, []()
mws.addHandler("/save", HTTP_GET, saveHandler); {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.addHandler("/version", HTTP_GET, versionHandler);
mws.begin(); mws.begin();
@@ -129,12 +135,10 @@ uint16_t stringToColor(const String &str)
String gStr = str.substring(comma1 + 1, comma2); String gStr = str.substring(comma1 + 1, comma2);
String bStr = str.substring(comma2 + 1); String bStr = str.substring(comma2 + 1);
int r = rStr.toInt(); int r = rStr.toInt();
int g = gStr.toInt(); int g = gStr.toInt();
int b = bStr.toInt(); int b = bStr.toInt();
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
{ {
return 0xFFFF; return 0xFFFF;
@@ -164,9 +168,6 @@ String colorToString(uint16_t color)
void ServerManager_::loadSettings() void ServerManager_::loadSettings()
{ {
if (!FSOPEN)
startLittleFS();
if (LittleFS.exists("/config.json")) if (LittleFS.exists("/config.json"))
{ {
File file = LittleFS.open("/config.json", "r"); File file = LittleFS.open("/config.json", "r");
@@ -189,7 +190,6 @@ void ServerManager_::loadSettings()
NET_SN = doc["Subnet"].as<String>(); NET_SN = doc["Subnet"].as<String>();
NET_PDNS = doc["Primary DNS"].as<String>(); NET_PDNS = doc["Primary DNS"].as<String>();
NET_SDNS = doc["Secondary DNS"].as<String>(); NET_SDNS = doc["Secondary DNS"].as<String>();
UPPERCASE_LETTERS = doc["Uppercase letters"];
file.close(); file.close();
DisplayManager.applyAllSettings(); DisplayManager.applyAllSettings();
Serial.println(F("Configuration loaded")); Serial.println(F("Configuration loaded"));

View File

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

View File

@@ -1,20 +1,34 @@
#include <UpdateManager.h>
#include <WiFi.h> #include <WiFi.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include <HTTPUpdate.h> #include <HTTPUpdate.h>
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#include "cert.h" #include "cert.h"
#include "DisplayManager.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_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" #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_started()
{ {
} }
void update_finished() void update_finished()
{ {
} }
void update_progress(int cur, int total) void update_progress(int cur, int total)
@@ -29,7 +43,7 @@ void update_error(int err)
DisplayManager.show(); DisplayManager.show();
} }
void updateFirmware() void UpdateManager_::updateFirmware()
{ {
WiFiClientSecure client; WiFiClientSecure client;
client.setCACert(rootCACertificate); client.setCACert(rootCACertificate);
@@ -56,13 +70,15 @@ void updateFirmware()
} }
} }
bool UpdateManager_::checkUpdate(bool withScreen)
bool FirmwareVersionCheck()
{ {
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; String payload;
int httpCode; int httpCode;
String fwurl = ""; String fwurl = "";
@@ -103,20 +119,34 @@ bool FirmwareVersionCheck()
payload.trim(); payload.trim();
if (payload.equals(VERSION)) if (payload.equals(VERSION))
{ {
Serial.printf("\nDevice already on latest firmware version:%s\n", VERSION); UPDATE_AVAILABLE = false;
DisplayManager.clear(); Serial.printf("\nDevice already on latest firmware version: %s\n", VERSION);
DisplayManager.printText(0, 6, "NO UP :(", true, true); if (withScreen)
DisplayManager.show(); {
delay(1000); DisplayManager.clear();
DisplayManager.printText(0, 6, "NO UP :(", true, true);
DisplayManager.show();
delay(1000);
}
return 0; return 0;
} }
else else
{ {
Serial.println(payload); UPDATE_AVAILABLE = true;
Serial.println("New firmware detected");
DisplayManager.printText(0, 6, payload.c_str(), true, true);
return 1; return 1;
} }
} }
UPDATE_AVAILABLE = false;
return 0; 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 "MQTTManager.h"
#include "ServerManager.h" #include "ServerManager.h"
#include "Globals.h" #include "Globals.h"
#include "UpdateManager.h"
TaskHandle_t taskHandle; TaskHandle_t taskHandle;
volatile bool StopTask = false; volatile bool StopTask = false;
@@ -73,13 +74,23 @@ void setup()
{ {
MQTTManager.setup(); MQTTManager.setup();
DisplayManager.loadNativeApps(); 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 else
{ {
AP_MODE = true; AP_MODE = true;
StopTask = true;
} }
StopTask = true;
delay(200); delay(200);
DisplayManager.clearMatrix(); DisplayManager.clearMatrix();
} }
@@ -88,9 +99,9 @@ void loop()
{ {
ServerManager.tick(); ServerManager.tick();
DisplayManager.tick(); DisplayManager.tick();
PeripheryManager.tick();
if (ServerManager.isConnected) if (ServerManager.isConnected)
{ {
PeripheryManager.tick();
MQTTManager.tick(); MQTTManager.tick();
} }
} }

View File

@@ -1 +1 @@
0.43 0.47