mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 11:36:59 +03:00
Merge remote-tracking branch 'origin/master' into httpd
Conflicts: components/wifi-manager/http_server.c
This commit is contained in:
@@ -238,6 +238,11 @@ python ${IDF_PATH}/components/esptool_py/esptool/esptool.py --chip esp32 --port
|
||||
make monitor
|
||||
|
||||
```
|
||||
|
||||
You can also manually download the recovery & initial boot
|
||||
```
|
||||
python ${IDF_PATH}/components/esptool_py/esptool/esptool.py --chip esp32 --port ${ESPPORT} --baud ${ESPBAUD} --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xd000 ./build/ota_data_initial.bin 0x1000 ./build/bootloader/bootloader.bin 0x10000 ./build/recovery.bin 0x8000 ./build/partitions.bin
|
||||
```
|
||||
|
||||
# Configuration
|
||||
1/ setup WiFi
|
||||
|
||||
@@ -613,7 +613,7 @@ CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
|
||||
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
|
||||
|
||||
@@ -613,7 +613,7 @@ CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
|
||||
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
|
||||
|
||||
@@ -614,7 +614,7 @@ CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
|
||||
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
|
||||
|
||||
@@ -613,7 +613,7 @@ CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
|
||||
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
|
||||
|
||||
@@ -613,7 +613,7 @@ CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
|
||||
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
|
||||
|
||||
@@ -607,7 +607,7 @@ CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
|
||||
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
|
||||
|
||||
145
components/display/SH1106.c
Normal file
145
components/display/SH1106.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
* 2020 Philippe G.
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
|
||||
#define SHADOW_BUFFER
|
||||
|
||||
static char TAG[] = "SH1106";
|
||||
|
||||
// Functions are not declared to minimize # of lines
|
||||
|
||||
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||
// well, unfortunately this driver is 132 colums but most displays are 128...
|
||||
if (Device->Width != 132) Start += 2;
|
||||
Device->WriteCommand( Device, 0x10 | (Start >> 4) );
|
||||
Device->WriteCommand( Device, 0x00 | (Start & 0x0f) );
|
||||
}
|
||||
|
||||
static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||
Device->WriteCommand( Device, 0xB0 | Start );
|
||||
}
|
||||
|
||||
static void Update( struct GDS_Device* Device ) {
|
||||
#ifdef SHADOW_BUFFER
|
||||
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
||||
int width = Device->Width, rows = Device->Height / 8;
|
||||
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
|
||||
|
||||
// by row, find first and last columns that have been updated
|
||||
for (int r = 0; r < rows; r++) {
|
||||
uint8_t first = 0, last;
|
||||
for (int c = 0; c < width; c++) {
|
||||
if (*iptr != *optr) {
|
||||
if (!first) first = c + 1;
|
||||
last = c ;
|
||||
}
|
||||
*optr++ = *iptr++;
|
||||
}
|
||||
|
||||
// now update the display by "byte rows"
|
||||
if (first--) {
|
||||
SetColumnAddress( Device, first, last );
|
||||
SetPageAddress( Device, r, r);
|
||||
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// SH1106 requires a page-by-page update and has no end Page/Column
|
||||
for (int i = 0; i < Device->Height / 8 ; i++) {
|
||||
SH1106_SetPageAddress( Device, i, 0);
|
||||
SH1106_SetColumnAddress( Device, 0, 0);
|
||||
SH1106_WriteData( Device, Device->Framebuffer + i*Device->Width, Device->Width );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
|
||||
static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
|
||||
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
|
||||
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
|
||||
|
||||
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
||||
Device->WriteCommand( Device, 0x81 );
|
||||
Device->WriteCommand( Device, Contrast );
|
||||
}
|
||||
|
||||
static bool Init( struct GDS_Device* Device ) {
|
||||
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
|
||||
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
||||
NullCheck( Device->Framebuffer, return false );
|
||||
|
||||
#ifdef SHADOW_BUFFER
|
||||
if (Device->IF == IF_I2C) Device->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||
else Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||
NullCheck( Device->Shadowbuffer, return false );
|
||||
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
||||
#endif
|
||||
|
||||
// need to be off and disable display RAM
|
||||
Device->DisplayOff( Device );
|
||||
Device->WriteCommand( Device, 0xA5 );
|
||||
|
||||
// charge pump regulator, do direct init
|
||||
Device->WriteCommand( Device, 0xAD );
|
||||
Device->WriteCommand( Device, 0x8B );
|
||||
|
||||
// COM pins HW config (alternative:EN) - some display might need something difference
|
||||
Device->WriteCommand( Device, 0xDA );
|
||||
Device->WriteCommand( Device, 1 << 4);
|
||||
|
||||
// MUX Ratio
|
||||
Device->WriteCommand( Device, 0xA8 );
|
||||
Device->WriteCommand( Device, Device->Height - 1);
|
||||
// Display Offset
|
||||
Device->WriteCommand( Device, 0xD3 );
|
||||
Device->WriteCommand( Device, 0 );
|
||||
// Display Start Line
|
||||
Device->WriteCommand( Device, 0x40 + 0x00 );
|
||||
Device->SetContrast( Device, 0x7F );
|
||||
// set flip modes
|
||||
Device->SetVFlip( Device, false );
|
||||
Device->SetHFlip( Device, false );
|
||||
// no Display Inversion
|
||||
Device->WriteCommand( Device, 0xA6 );
|
||||
// set Clocks
|
||||
Device->WriteCommand( Device, 0xD5 );
|
||||
Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 );
|
||||
|
||||
// gone with the wind
|
||||
Device->WriteCommand( Device, 0xA4 );
|
||||
Device->DisplayOn( Device );
|
||||
Device->Update( Device );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct GDS_Device SH1106 = {
|
||||
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
|
||||
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
|
||||
.DrawPixelFast = GDS_DrawPixelFast,
|
||||
.Update = Update, .Init = Init,
|
||||
};
|
||||
|
||||
struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) {
|
||||
if (!strcasestr(Driver, "SH1106")) return NULL;
|
||||
|
||||
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
|
||||
*Device = SH1106;
|
||||
ESP_LOGI(TAG, "SH1106 driver");
|
||||
|
||||
return Device;
|
||||
}
|
||||
146
components/display/SSD1306.c
Normal file
146
components/display/SSD1306.c
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
* 2020 Philippe G.
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
|
||||
#define SHADOW_BUFFER
|
||||
|
||||
static char TAG[] = "SSD1306";
|
||||
|
||||
// Functions are not deckared to minimize # of lines
|
||||
|
||||
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||
Device->WriteCommand( Device, 0x21 );
|
||||
Device->WriteCommand( Device, Start );
|
||||
Device->WriteCommand( Device, End );
|
||||
}
|
||||
static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||
Device->WriteCommand( Device, 0x22 );
|
||||
Device->WriteCommand( Device, Start );
|
||||
Device->WriteCommand( Device, End );
|
||||
}
|
||||
|
||||
static void Update( struct GDS_Device* Device ) {
|
||||
#ifdef SHADOW_BUFFER
|
||||
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
||||
int width = Device->Width, rows = Device->Height / 8;
|
||||
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
|
||||
|
||||
// by row, find first and last columns that have been updated
|
||||
for (int r = 0; r < rows; r++) {
|
||||
uint8_t first = 0, last;
|
||||
for (int c = 0; c < width; c++) {
|
||||
if (*iptr != *optr) {
|
||||
if (!first) first = c + 1;
|
||||
last = c ;
|
||||
}
|
||||
*optr++ = *iptr++;
|
||||
}
|
||||
|
||||
// now update the display by "byte rows"
|
||||
if (first--) {
|
||||
SetColumnAddress( Device, first, last );
|
||||
SetPageAddress( Device, r, r);
|
||||
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// automatic counter and end Page/Column
|
||||
SetColumnAddress( Device, 0, Device->Width - 1);
|
||||
SetPageAddress( Device, 0, Device->Height / 8 - 1);
|
||||
Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize );
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
|
||||
static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
|
||||
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
|
||||
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
|
||||
|
||||
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
|
||||
Device->WriteCommand( Device, 0x81 );
|
||||
Device->WriteCommand( Device, Contrast );
|
||||
}
|
||||
|
||||
static bool Init( struct GDS_Device* Device ) {
|
||||
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
|
||||
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
|
||||
NullCheck( Device->Framebuffer, return false );
|
||||
|
||||
#ifdef SHADOW_BUFFER
|
||||
if (Device->IF == IF_I2C) Device->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||
else Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||
NullCheck( Device->Shadowbuffer, return false );
|
||||
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
||||
#endif
|
||||
|
||||
// need to be off and disable display RAM
|
||||
Device->DisplayOff( Device );
|
||||
Device->WriteCommand( Device, 0xA5 );
|
||||
|
||||
// charge pump regulator, do direct init
|
||||
Device->WriteCommand( Device, 0x8D );
|
||||
Device->WriteCommand( Device, 0x14 );
|
||||
|
||||
// COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something difference
|
||||
Device->WriteCommand( Device, 0xDA );
|
||||
Device->WriteCommand( Device, ((Device->Height == 64 ? 1 : 0) << 4) | (0 < 5) );
|
||||
|
||||
// MUX Ratio
|
||||
Device->WriteCommand( Device, 0xA8 );
|
||||
Device->WriteCommand( Device, Device->Height - 1);
|
||||
// Display Offset
|
||||
Device->WriteCommand( Device, 0xD3 );
|
||||
Device->WriteCommand( Device, 0 );
|
||||
// Display Start Line
|
||||
Device->WriteCommand( Device, 0x40 + 0x00 );
|
||||
Device->SetContrast( Device, 0x7F );
|
||||
// set flip modes
|
||||
Device->SetVFlip( Device, false );
|
||||
Device->SetHFlip( Device, false );
|
||||
// no Display Inversion
|
||||
Device->WriteCommand( Device, 0xA6 );
|
||||
// set Clocks
|
||||
Device->WriteCommand( Device, 0xD5 );
|
||||
Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 );
|
||||
// set Adressing Mode Horizontal
|
||||
Device->WriteCommand( Device, 0x20 );
|
||||
Device->WriteCommand( Device, 0 );
|
||||
|
||||
// gone with the wind
|
||||
Device->WriteCommand( Device, 0xA4 );
|
||||
Device->DisplayOn( Device );
|
||||
Device->Update( Device );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct GDS_Device SSD1306 = {
|
||||
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
|
||||
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
|
||||
.DrawPixelFast = GDS_DrawPixelFast,
|
||||
.Update = Update, .Init = Init,
|
||||
};
|
||||
|
||||
struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) {
|
||||
if (!strcasestr(Driver, "SSD1306")) return NULL;
|
||||
|
||||
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
|
||||
*Device = SSD1306;
|
||||
ESP_LOGI(TAG, "SSD1306 driver");
|
||||
|
||||
return Device;
|
||||
}
|
||||
@@ -7,6 +7,6 @@
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
|
||||
COMPONENT_SRCDIRS := . tarablessd13x6 tarablessd13x6/fonts tarablessd13x6/ifaces
|
||||
COMPONENT_SRCDIRS := . core core/ifaces fonts
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_ADD_INCLUDEDIRS += ./tarablessd13x6
|
||||
COMPONENT_ADD_INCLUDEDIRS += ./core
|
||||
92
components/display/core/gds.c
Normal file
92
components/display/core/gds.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
* 2020 Philippe G.
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
|
||||
static struct GDS_Device Display;
|
||||
|
||||
static char TAG[] = "gds";
|
||||
|
||||
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc DetectFunc[] ) {
|
||||
for (int i = 0; DetectFunc[i]; i++) {
|
||||
if (DetectFunc[i](Driver, &Display)) {
|
||||
ESP_LOGD(TAG, "Detected driver %p", &Display);
|
||||
return &Display;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GDS_ClearExt(struct GDS_Device* Device, bool full, ...) {
|
||||
bool commit = true;
|
||||
|
||||
if (full) {
|
||||
GDS_Clear( Device, GDS_COLOR_BLACK );
|
||||
} else {
|
||||
va_list args;
|
||||
va_start(args, full);
|
||||
commit = va_arg(args, int);
|
||||
int x1 = va_arg(args, int), y1 = va_arg(args, int), x2 = va_arg(args, int), y2 = va_arg(args, int);
|
||||
if (x2 < 0) x2 = Device->Width - 1;
|
||||
if (y2 < 0) y2 = Device->Height - 1;
|
||||
GDS_ClearWindow( Device, x1, y1, x2, y2, GDS_COLOR_BLACK );
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
Device->Dirty = true;
|
||||
if (commit) GDS_Update(Device);
|
||||
}
|
||||
|
||||
void GDS_Clear( struct GDS_Device* Device, int Color ) {
|
||||
memset( Device->Framebuffer, Color, Device->FramebufferSize );
|
||||
}
|
||||
|
||||
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
|
||||
// cheap optimization on boundaries
|
||||
if (x1 == 0 && x2 == Device->Width - 1 && y1 % 8 == 0 && (y2 + 1) % 8 == 0) {
|
||||
memset( Device->Framebuffer + (y1 / 8) * Device->Width, 0, (y2 - y1 + 1) / 8 * Device->Width );
|
||||
} else {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
Device->DrawPixelFast( Device, x, y, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GDS_Update( struct GDS_Device* Device ) {
|
||||
if (Device->Dirty) Device->Update( Device );
|
||||
Device->Dirty = false;
|
||||
}
|
||||
|
||||
bool GDS_Reset( struct GDS_Device* Device ) {
|
||||
if ( Device->RSTPin >= 0 ) {
|
||||
gpio_set_level( Device->RSTPin, 0 );
|
||||
vTaskDelay( pdMS_TO_TICKS( 100 ) );
|
||||
gpio_set_level( Device->RSTPin, 1 );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { Device->SetContrast( Device, Contrast); }
|
||||
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { Device->SetHFlip( Device, On ); }
|
||||
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { Device->SetVFlip( Device, On ); }
|
||||
int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
|
||||
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
|
||||
void GDS_DisplayOn( struct GDS_Device* Device ) { Device->DisplayOn( Device ); }
|
||||
void GDS_DisplayOff( struct GDS_Device* Device ) { Device->DisplayOff( Device ); }
|
||||
30
components/display/core/gds.h
Normal file
30
components/display/core/gds.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _GDS_H_
|
||||
#define _GDS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GDS_COLOR_BLACK 0
|
||||
#define GDS_COLOR_WHITE 1
|
||||
#define GDS_COLOR_XOR 2
|
||||
|
||||
struct GDS_Device;
|
||||
struct GDS_FontDef;
|
||||
|
||||
typedef struct GDS_Device* (*GDS_DetectFunc)(char *Driver, struct GDS_Device *Device);
|
||||
|
||||
struct GDS_Device* GDS_AutoDetect( char *Driver, GDS_DetectFunc[] );
|
||||
|
||||
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
|
||||
void GDS_DisplayOn( struct GDS_Device* Device );
|
||||
void GDS_DisplayOff( struct GDS_Device* Device );
|
||||
void GDS_Update( struct GDS_Device* Device );
|
||||
void GDS_SetHFlip( struct GDS_Device* Device, bool On );
|
||||
void GDS_SetVFlip( struct GDS_Device* Device, bool On );
|
||||
int GDS_GetWidth( struct GDS_Device* Device );
|
||||
int GDS_GetHeight( struct GDS_Device* Device );
|
||||
void GDS_ClearExt( struct GDS_Device* Device, bool full, ...);
|
||||
void GDS_Clear( struct GDS_Device* Device, int Color );
|
||||
void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
|
||||
|
||||
#endif
|
||||
20
components/display/core/gds_default_if.h
Normal file
20
components/display/core/gds_default_if.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _GDS_DEFAULT_IF_H_
|
||||
#define _GDS_DEFAULT_IF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
bool GDS_I2CInit( int PortNumber, int SDA, int SCL );
|
||||
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin );
|
||||
|
||||
bool GDS_SPIInit( int SPI, int DC );
|
||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
292
components/display/core/gds_draw.c
Normal file
292
components/display/core/gds_draw.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
* 2020 Philippe G.
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <esp_attr.h>
|
||||
|
||||
#include "gds.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_draw.h"
|
||||
|
||||
static const unsigned char BitReverseTable256[] =
|
||||
{
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
};
|
||||
|
||||
__attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b ) {
|
||||
int Temp = *b;
|
||||
|
||||
*b = *a;
|
||||
*a = Temp;
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint32_t YBit = ( Y & 0x07 );
|
||||
uint8_t* FBOffset = NULL;
|
||||
|
||||
/*
|
||||
* We only need to modify the Y coordinate since the pitch
|
||||
* of the screen is the same as the width.
|
||||
* Dividing Y by 8 gives us which row the pixel is in but not
|
||||
* the bit position.
|
||||
*/
|
||||
Y>>= 3;
|
||||
|
||||
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
|
||||
|
||||
if ( Color == GDS_COLOR_XOR ) {
|
||||
*FBOffset ^= BIT( YBit );
|
||||
} else {
|
||||
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
|
||||
}
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint32_t YBit = ( Y & 0x07 );
|
||||
uint8_t* FBOffset = NULL;
|
||||
|
||||
/*
|
||||
* We only need to modify the Y coordinate since the pitch
|
||||
* of the screen is the same as the width.
|
||||
* Dividing Y by 8 gives us which row the pixel is in but not
|
||||
* the bit position.
|
||||
*/
|
||||
Y>>= 3;
|
||||
|
||||
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
|
||||
|
||||
if ( Color == GDS_COLOR_XOR ) {
|
||||
*FBOffset ^= BIT( YBit );
|
||||
} else {
|
||||
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color ) {
|
||||
int XEnd = x + Width;
|
||||
|
||||
Device->Dirty = true;
|
||||
|
||||
if (x < 0) x = 0;
|
||||
if (XEnd >= Device->Width) XEnd = Device->Width - 1;
|
||||
|
||||
if (y < 0) y = 0;
|
||||
else if (y >= Device->Height) x = Device->Height - 1;
|
||||
|
||||
for ( ; x < XEnd; x++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color ) {
|
||||
int YEnd = y + Height;
|
||||
|
||||
Device->Dirty = true;
|
||||
|
||||
for ( ; y < YEnd; y++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
GDS_DrawPixel( Device, x, y, Color );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void IRAM_ATTR DrawWideLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
|
||||
int dx = ( x1 - x0 );
|
||||
int dy = ( y1 - y0 );
|
||||
int Error = 0;
|
||||
int Incr = 1;
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
|
||||
if ( dy < 0 ) {
|
||||
Incr = -1;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
Error = ( dy * 2 ) - dx;
|
||||
|
||||
for ( ; x < x1; x++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
Error-= ( dx * 2 );
|
||||
y+= Incr;
|
||||
}
|
||||
|
||||
Error+= ( dy * 2 );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void IRAM_ATTR DrawTallLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
|
||||
int dx = ( x1 - x0 );
|
||||
int dy = ( y1 - y0 );
|
||||
int Error = 0;
|
||||
int Incr = 1;
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
|
||||
if ( dx < 0 ) {
|
||||
Incr = -1;
|
||||
dx = -dx;
|
||||
}
|
||||
|
||||
Error = ( dx * 2 ) - dy;
|
||||
|
||||
for ( ; y < y1; y++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
Error-= ( dy * 2 );
|
||||
x+= Incr;
|
||||
}
|
||||
|
||||
Error+= ( dx * 2 );
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR GDS_DrawLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color ) {
|
||||
if ( x0 == x1 ) {
|
||||
GDS_DrawVLine( Device, x0, y0, ( y1 - y0 ), Color );
|
||||
} else if ( y0 == y1 ) {
|
||||
GDS_DrawHLine( Device, x0, y0, ( x1 - x0 ), Color );
|
||||
} else {
|
||||
Device->Dirty = true;
|
||||
if ( abs( x1 - x0 ) > abs( y1 - y0 ) ) {
|
||||
/* Wide ( run > rise ) */
|
||||
if ( x0 > x1 ) {
|
||||
SwapInt( &x0, &x1 );
|
||||
SwapInt( &y0, &y1 );
|
||||
}
|
||||
|
||||
DrawWideLine( Device, x0, y0, x1, y1, Color );
|
||||
} else {
|
||||
/* Tall ( rise > run ) */
|
||||
if ( y0 > y1 ) {
|
||||
SwapInt( &y0, &y1 );
|
||||
SwapInt( &x0, &x1 );
|
||||
}
|
||||
|
||||
DrawTallLine( Device, x0, y0, x1, y1, Color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color, bool Fill ) {
|
||||
int Width = ( x2 - x1 );
|
||||
int Height = ( y2 - y1 );
|
||||
|
||||
Device->Dirty = true;
|
||||
|
||||
if ( Fill == false ) {
|
||||
/* Top side */
|
||||
GDS_DrawHLine( Device, x1, y1, Width, Color );
|
||||
|
||||
/* Bottom side */
|
||||
GDS_DrawHLine( Device, x1, y1 + Height, Width, Color );
|
||||
|
||||
/* Left side */
|
||||
GDS_DrawVLine( Device, x1, y1, Height, Color );
|
||||
|
||||
/* Right side */
|
||||
GDS_DrawVLine( Device, x1 + Width, y1, Height, Color );
|
||||
} else {
|
||||
/* Fill the box by drawing horizontal lines */
|
||||
for ( ; y1 <= y2; y1++ ) {
|
||||
GDS_DrawHLine( Device, x1, y1, Width, Color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************************
|
||||
* Process graphic display data from column-oriented data (MSbit first)
|
||||
*/
|
||||
void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height) {
|
||||
if (!Height) Height = Device->Height;
|
||||
if (!Width) Width = Device->Width;
|
||||
|
||||
// need to do row/col swap and bit-reverse
|
||||
int Rows = Height / 8;
|
||||
for (int r = 0; r < Rows; r++) {
|
||||
uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
|
||||
for (int c = Width; --c >= 0;) {
|
||||
*optr++ = BitReverseTable256[*iptr];;
|
||||
iptr += Rows;
|
||||
}
|
||||
}
|
||||
|
||||
Device->Dirty = true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Process graphic display data MSBit first
|
||||
* WARNING: this has not been tested yet
|
||||
*/
|
||||
/*
|
||||
static void draw_raw(int x1, int y1, int x2, int y2, bool by_column, bool MSb, u8_t *data) {
|
||||
// default end point to display size
|
||||
if (x2 == -1) x2 = Display.Width - 1;
|
||||
if (y2 == -1) y2 = Display.Height - 1;
|
||||
|
||||
display->dirty = true;
|
||||
|
||||
// not a boundary draw
|
||||
// same comment about bit depth
|
||||
if (y1 % 8 || y2 % 8 || x1 % 8 | x2 % 8) {
|
||||
ESP_LOGW(TAG, "can't write on non cols/rows boundaries for now");
|
||||
} else {
|
||||
// set addressing mode to match data
|
||||
if (by_column) {
|
||||
// copy the window and do row/col exchange
|
||||
for (int r = y1/8; r <= y2/8; r++) {
|
||||
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r;
|
||||
for (int c = x1; c <= x2; c++) {
|
||||
*optr++ = MSb ? BitReverseTable256[*iptr] : *iptr;
|
||||
iptr += (y2-y1)/8 + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// just copy the window inside the frame buffer
|
||||
for (int r = y1/8; r <= y2/8; r++) {
|
||||
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r*(x2-x1+1);
|
||||
for (int c = x1; c <= x2; c++) *optr++ = *iptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
26
components/display/core/gds_draw.h
Normal file
26
components/display/core/gds_draw.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _GDS_DRAW_H_
|
||||
#define _GDS_DRAW_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
void IRAM_ATTR GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Color );
|
||||
void IRAM_ATTR GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color );
|
||||
void IRAM_ATTR GDS_DrawLine( struct GDS_Device* Device, int x0, int y0, int x1, int y1, int Color );
|
||||
void IRAM_ATTR GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color, bool Fill );
|
||||
|
||||
// draw a bitmap with source 1-bit depth organized in column and col0 = bit7 of byte 0
|
||||
void IRAM_ATTR GDS_DrawBitmapCBR( struct GDS_Device* Device, uint8_t *Data, int Width, int Height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,15 +1,15 @@
|
||||
#ifndef _SSD13x6_ERR_H_
|
||||
#define _SSD13x6_ERR_H_
|
||||
#ifndef _GDS_ERR_H_
|
||||
#define _GDS_ERR_H_
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#define SSD13x6_DoAbort( )
|
||||
#define GDS_DoAbort( )
|
||||
|
||||
#if ! defined NullCheck
|
||||
#define NullCheck( ptr, retexpr ) { \
|
||||
if ( ptr == NULL ) { \
|
||||
ESP_LOGE( __FUNCTION__, "%s == NULL", #ptr ); \
|
||||
SSD13x6_DoAbort( ); \
|
||||
GDS_DoAbort( ); \
|
||||
retexpr; \
|
||||
} \
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
esp_err_t __err_rc = ( expr ); \
|
||||
if ( __err_rc != ESP_OK ) { \
|
||||
ESP_LOGE( __FUNCTION__, "%s != ESP_OK, result: %d", #expr, __err_rc ); \
|
||||
SSD13x6_DoAbort( ); \
|
||||
GDS_DoAbort( ); \
|
||||
retexpr; \
|
||||
} \
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
#define CheckBounds( expr, retexpr ) { \
|
||||
if ( expr ) { \
|
||||
ESP_LOGE( __FUNCTION__, "Line %d: %s", __LINE__, #expr ); \
|
||||
SSD13x6_DoAbort( ); \
|
||||
GDS_DoAbort( ); \
|
||||
retexpr; \
|
||||
} \
|
||||
}
|
||||
@@ -9,11 +9,13 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include "ssd13x6.h"
|
||||
#include "ssd13x6_draw.h"
|
||||
#include "ssd13x6_font.h"
|
||||
#include "gds.h"
|
||||
#include "gds_draw.h"
|
||||
#include "gds_font.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_err.h"
|
||||
|
||||
static int RoundUpFontHeight( const struct SSD13x6_FontDef* Font ) {
|
||||
static int RoundUpFontHeight( const struct GDS_FontDef* Font ) {
|
||||
int Height = Font->Height;
|
||||
|
||||
if ( ( Height % 8 ) != 0 ) {
|
||||
@@ -23,11 +25,11 @@ static int RoundUpFontHeight( const struct SSD13x6_FontDef* Font ) {
|
||||
return Height;
|
||||
}
|
||||
|
||||
static const uint8_t* GetCharPtr( const struct SSD13x6_FontDef* Font, char Character ) {
|
||||
static const uint8_t* GetCharPtr( const struct GDS_FontDef* Font, char Character ) {
|
||||
return &Font->FontData[ ( Character - Font->StartChar ) * ( ( Font->Width * ( RoundUpFontHeight( Font ) / 8 ) ) + 1 ) ];
|
||||
}
|
||||
|
||||
void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character, int x, int y, int Color ) {
|
||||
void GDS_FontDrawChar( struct GDS_Device* Device, char Character, int x, int y, int Color ) {
|
||||
const uint8_t* GlyphData = NULL;
|
||||
int GlyphColumnLen = 0;
|
||||
int CharStartX = 0;
|
||||
@@ -42,18 +44,15 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
|
||||
int YBit = 0;
|
||||
int i = 0;
|
||||
|
||||
NullCheck( DisplayHandle, return );
|
||||
NullCheck( DisplayHandle->Font, return );
|
||||
|
||||
NullCheck( ( GlyphData = GetCharPtr( DisplayHandle->Font, Character ) ), return );
|
||||
NullCheck( ( GlyphData = GetCharPtr( Device->Font, Character ) ), return );
|
||||
|
||||
if ( Character >= DisplayHandle->Font->StartChar && Character <= DisplayHandle->Font->EndChar ) {
|
||||
if ( Character >= Device->Font->StartChar && Character <= Device->Font->EndChar ) {
|
||||
/* The first byte in the glyph data is the width of the character in pixels, skip over */
|
||||
GlyphData++;
|
||||
GlyphColumnLen = RoundUpFontHeight( DisplayHandle->Font ) / 8;
|
||||
GlyphColumnLen = RoundUpFontHeight( Device->Font ) / 8;
|
||||
|
||||
CharWidth = SSD13x6_FontGetCharWidth( DisplayHandle, Character );
|
||||
CharHeight = SSD13x6_FontGetHeight( DisplayHandle );
|
||||
CharWidth = GDS_FontGetCharWidth( Device, Character );
|
||||
CharHeight = GDS_FontGetHeight( Device );
|
||||
|
||||
CharStartX = x;
|
||||
CharStartY = y;
|
||||
@@ -74,14 +73,15 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
|
||||
CharStartY+= OffsetY;
|
||||
|
||||
/* Do not attempt to draw if this character is entirely offscreen */
|
||||
if ( CharEndX < 0 || CharStartX >= DisplayHandle->Width || CharEndY < 0 || CharStartY >= DisplayHandle->Height ) {
|
||||
if ( CharEndX < 0 || CharStartX >= Device->Width || CharEndY < 0 || CharStartY >= Device->Height ) {
|
||||
ClipDebug( x, y );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do not attempt to draw past the end of the screen */
|
||||
CharEndX = ( CharEndX >= DisplayHandle->Width ) ? DisplayHandle->Width - 1 : CharEndX;
|
||||
CharEndY = ( CharEndY >= DisplayHandle->Height ) ? DisplayHandle->Height - 1 : CharEndY;
|
||||
CharEndX = ( CharEndX >= Device->Width ) ? Device->Width - 1 : CharEndX;
|
||||
CharEndY = ( CharEndY >= Device->Height ) ? Device->Height - 1 : CharEndY;
|
||||
Device->Dirty = true;
|
||||
|
||||
for ( x = CharStartX; x < CharEndX; x++ ) {
|
||||
for ( y = CharStartY, i = 0; y < CharEndY && i < CharHeight; y++, i++ ) {
|
||||
@@ -89,7 +89,7 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
|
||||
YBit = ( i + OffsetY ) & 0x07;
|
||||
|
||||
if ( GlyphData[ YByte ] & BIT( YBit ) ) {
|
||||
SSD13x6_DrawPixel( DisplayHandle, x, y, Color );
|
||||
GDS_DrawPixel( Device, x, y, Color );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,10 +98,7 @@ void SSD13x6_FontDrawChar( struct SSD13x6_Device* DisplayHandle, char Character,
|
||||
}
|
||||
}
|
||||
|
||||
bool SSD13x6_SetFont( struct SSD13x6_Device* Display, const struct SSD13x6_FontDef* Font ) {
|
||||
NullCheck( Display, return false );
|
||||
NullCheck( Font, return false );
|
||||
|
||||
bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font ) {
|
||||
Display->FontForceProportional = false;
|
||||
Display->FontForceMonospace = false;
|
||||
Display->Font = Font;
|
||||
@@ -109,41 +106,26 @@ bool SSD13x6_SetFont( struct SSD13x6_Device* Display, const struct SSD13x6_FontD
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSD13x6_FontForceProportional( struct SSD13x6_Device* Display, bool Force ) {
|
||||
NullCheck( Display, return );
|
||||
NullCheck( Display->Font, return );
|
||||
|
||||
void GDS_FontForceProportional( struct GDS_Device* Display, bool Force ) {
|
||||
Display->FontForceProportional = Force;
|
||||
}
|
||||
|
||||
void SSD13x6_FontForceMonospace( struct SSD13x6_Device* Display, bool Force ) {
|
||||
NullCheck( Display, return );
|
||||
NullCheck( Display->Font, return );
|
||||
|
||||
void GDS_FontForceMonospace( struct GDS_Device* Display, bool Force ) {
|
||||
Display->FontForceMonospace = Force;
|
||||
}
|
||||
|
||||
int SSD13x6_FontGetWidth( struct SSD13x6_Device* Display ) {
|
||||
NullCheck( Display, return 0 );
|
||||
NullCheck( Display->Font, return 0 );
|
||||
|
||||
int GDS_FontGetWidth( struct GDS_Device* Display ) {
|
||||
return Display->Font->Width;
|
||||
}
|
||||
|
||||
int SSD13x6_FontGetHeight( struct SSD13x6_Device* Display ) {
|
||||
NullCheck( Display, return 0 );
|
||||
NullCheck( Display->Font, return 0 );
|
||||
|
||||
int GDS_FontGetHeight( struct GDS_Device* Display ) {
|
||||
return Display->Font->Height;
|
||||
}
|
||||
|
||||
int SSD13x6_FontGetCharWidth( struct SSD13x6_Device* Display, char Character ) {
|
||||
int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character ) {
|
||||
const uint8_t* CharPtr = NULL;
|
||||
int Width = 0;
|
||||
|
||||
NullCheck( Display, return 0 );
|
||||
NullCheck( Display->Font, return 0 );
|
||||
|
||||
if ( Character >= Display->Font->StartChar && Character <= Display->Font->EndChar ) {
|
||||
CharPtr = GetCharPtr( Display->Font, Character );
|
||||
|
||||
@@ -161,82 +143,67 @@ int SSD13x6_FontGetCharWidth( struct SSD13x6_Device* Display, char Character ) {
|
||||
return Width;
|
||||
}
|
||||
|
||||
int SSD13x6_FontGetMaxCharsPerRow( struct SSD13x6_Device* Display ) {
|
||||
NullCheck( Display, return 0 );
|
||||
NullCheck( Display->Font, return 0 );
|
||||
|
||||
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display ) {
|
||||
return Display->Width / Display->Font->Width;
|
||||
}
|
||||
|
||||
int SSD13x6_FontGetMaxCharsPerColumn( struct SSD13x6_Device* Display ) {
|
||||
NullCheck( Display, return 0 );
|
||||
NullCheck( Display->Font, return 0 );
|
||||
|
||||
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display ) {
|
||||
return Display->Height / Display->Font->Height;
|
||||
}
|
||||
|
||||
int SSD13x6_FontGetCharHeight( struct SSD13x6_Device* Display ) {
|
||||
NullCheck( Display, return 0 );
|
||||
NullCheck( Display->Font, return 0 );
|
||||
|
||||
int GDS_FontGetCharHeight( struct GDS_Device* Display ) {
|
||||
return Display->Font->Height;
|
||||
}
|
||||
|
||||
int SSD13x6_FontMeasureString( struct SSD13x6_Device* Display, const char* Text ) {
|
||||
int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text ) {
|
||||
int Width = 0;
|
||||
int Len = 0;
|
||||
|
||||
NullCheck( Display, return 0 );
|
||||
NullCheck( Display->Font, return 0 );
|
||||
NullCheck( Text, return 0 );
|
||||
|
||||
for ( Len = strlen( Text ); Len >= 0; Len--, Text++ ) {
|
||||
if ( *Text >= Display->Font->StartChar && *Text <= Display->Font->EndChar ) {
|
||||
Width+= SSD13x6_FontGetCharWidth( Display, *Text );
|
||||
Width+= GDS_FontGetCharWidth( Display, *Text );
|
||||
}
|
||||
}
|
||||
|
||||
return Width;
|
||||
}
|
||||
|
||||
void SSD13x6_FontDrawString( struct SSD13x6_Device* Display, int x, int y, const char* Text, int Color ) {
|
||||
void GDS_FontDrawString( struct GDS_Device* Display, int x, int y, const char* Text, int Color ) {
|
||||
int Len = 0;
|
||||
int i = 0;
|
||||
|
||||
NullCheck( Display, return );
|
||||
NullCheck( Display->Font, return );
|
||||
NullCheck( Text, return );
|
||||
|
||||
for ( Len = strlen( Text ), i = 0; i < Len; i++ ) {
|
||||
SSD13x6_FontDrawChar( Display, *Text, x, y, Color );
|
||||
GDS_FontDrawChar( Display, *Text, x, y, Color );
|
||||
|
||||
x+= SSD13x6_FontGetCharWidth( Display, *Text );
|
||||
x+= GDS_FontGetCharWidth( Display, *Text );
|
||||
Text++;
|
||||
}
|
||||
}
|
||||
|
||||
void SSD13x6_FontDrawAnchoredString( struct SSD13x6_Device* Display, TextAnchor Anchor, const char* Text, int Color ) {
|
||||
void GDS_FontDrawAnchoredString( struct GDS_Device* Display, TextAnchor Anchor, const char* Text, int Color ) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
NullCheck( Display, return );
|
||||
NullCheck( Text, return );
|
||||
|
||||
SSD13x6_FontGetAnchoredStringCoords( Display, &x, &y, Anchor, Text );
|
||||
SSD13x6_FontDrawString( Display, x, y, Text, Color );
|
||||
GDS_FontGetAnchoredStringCoords( Display, &x, &y, Anchor, Text );
|
||||
GDS_FontDrawString( Display, x, y, Text, Color );
|
||||
}
|
||||
|
||||
void SSD13x6_FontGetAnchoredStringCoords( struct SSD13x6_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text ) {
|
||||
void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text ) {
|
||||
int StringWidth = 0;
|
||||
int StringHeight = 0;
|
||||
|
||||
NullCheck( Display, return );
|
||||
NullCheck( OutX, return );
|
||||
NullCheck( OutY, return );
|
||||
NullCheck( Text, return );
|
||||
|
||||
StringWidth = SSD13x6_FontMeasureString( Display, Text );
|
||||
StringHeight = SSD13x6_FontGetCharHeight( Display );
|
||||
StringWidth = GDS_FontMeasureString( Display, Text );
|
||||
StringHeight = GDS_FontGetCharHeight( Display );
|
||||
|
||||
switch ( Anchor ) {
|
||||
case TextAnchor_East: {
|
||||
91
components/display/core/gds_font.h
Normal file
91
components/display/core/gds_font.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef _GDS_FONT_H_
|
||||
#define _GDS_FONT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
/*
|
||||
* X-GLCD Font format:
|
||||
*
|
||||
* First byte of glyph is it's width in pixels.
|
||||
* Each data byte represents 8 pixels going down from top to bottom.
|
||||
*
|
||||
* Example glyph layout for a 16x16 font
|
||||
* 'a': [Glyph width][Pixel column 0][Pixel column 1] where the number of pixel columns is the font height divided by 8
|
||||
* 'b': [Glyph width][Pixel column 0][Pixel column 1]...
|
||||
* 'c': And so on...
|
||||
*/
|
||||
|
||||
struct GDS_FontDef {
|
||||
const uint8_t* FontData;
|
||||
|
||||
int Width;
|
||||
int Height;
|
||||
|
||||
int StartChar;
|
||||
int EndChar;
|
||||
|
||||
bool Monospace;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TextAnchor_East = 0,
|
||||
TextAnchor_West,
|
||||
TextAnchor_North,
|
||||
TextAnchor_South,
|
||||
TextAnchor_NorthEast,
|
||||
TextAnchor_NorthWest,
|
||||
TextAnchor_SouthEast,
|
||||
TextAnchor_SouthWest,
|
||||
TextAnchor_Center
|
||||
} TextAnchor;
|
||||
|
||||
bool GDS_SetFont( struct GDS_Device* Display, const struct GDS_FontDef* Font );
|
||||
|
||||
void GDS_FontForceProportional( struct GDS_Device* Display, bool Force );
|
||||
void GDS_FontForceMonospace( struct GDS_Device* Display, bool Force );
|
||||
|
||||
int GDS_FontGetWidth( struct GDS_Device* Display );
|
||||
int GDS_FontGetHeight( struct GDS_Device* Display );
|
||||
|
||||
int GDS_FontGetMaxCharsPerRow( struct GDS_Device* Display );
|
||||
int GDS_FontGetMaxCharsPerColumn( struct GDS_Device* Display );
|
||||
|
||||
int GDS_FontGetCharWidth( struct GDS_Device* Display, char Character );
|
||||
int GDS_FontGetCharHeight( struct GDS_Device* Display );
|
||||
int GDS_FontMeasureString( struct GDS_Device* Display, const char* Text );\
|
||||
|
||||
void GDS_FontDrawChar( struct GDS_Device* Display, char Character, int x, int y, int Color );
|
||||
void GDS_FontDrawString( struct GDS_Device* Display, int x, int y, const char* Text, int Color );
|
||||
void GDS_FontDrawAnchoredString( struct GDS_Device* Display, TextAnchor Anchor, const char* Text, int Color );
|
||||
void GDS_FontGetAnchoredStringCoords( struct GDS_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text );
|
||||
|
||||
extern const struct GDS_FontDef Font_droid_sans_fallback_11x13;
|
||||
extern const struct GDS_FontDef Font_droid_sans_fallback_15x17;
|
||||
extern const struct GDS_FontDef Font_droid_sans_fallback_24x28;
|
||||
|
||||
extern const struct GDS_FontDef Font_droid_sans_mono_7x13;
|
||||
extern const struct GDS_FontDef Font_droid_sans_mono_13x24;
|
||||
extern const struct GDS_FontDef Font_droid_sans_mono_16x31;
|
||||
|
||||
extern const struct GDS_FontDef Font_liberation_mono_9x15;
|
||||
extern const struct GDS_FontDef Font_liberation_mono_13x21;
|
||||
extern const struct GDS_FontDef Font_liberation_mono_17x30;
|
||||
|
||||
extern const struct GDS_FontDef Font_Tarable7Seg_16x32;
|
||||
extern const struct GDS_FontDef Font_Tarable7Seg_32x64;
|
||||
|
||||
extern const struct GDS_FontDef Font_line_1;
|
||||
extern const struct GDS_FontDef Font_line_2;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
146
components/display/core/gds_private.h
Normal file
146
components/display/core/gds_private.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
|
||||
#define GDS_CLIPDEBUG_NONE 0
|
||||
#define GDS_CLIPDEBUG_WARNING 1
|
||||
#define GDS_CLIPDEBUG_ERROR 2
|
||||
|
||||
#define SHADOW_BUFFER
|
||||
|
||||
#if CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_NONE
|
||||
/*
|
||||
* Clip silently with no console output.
|
||||
*/
|
||||
#define ClipDebug( x, y )
|
||||
#elif CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_WARNING
|
||||
/*
|
||||
* Log clipping to the console as a warning.
|
||||
*/
|
||||
#define ClipDebug( x, y ) { \
|
||||
ESP_LOGW( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED", __LINE__, x, y ); \
|
||||
}
|
||||
#elif CONFIG_GDS_CLIPDEBUG == GDS_CLIPDEBUG_ERROR
|
||||
/*
|
||||
* Log clipping as an error to the console.
|
||||
* Also invokes an abort with stack trace.
|
||||
*/
|
||||
#define ClipDebug( x, y ) { \
|
||||
ESP_LOGE( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED, ABORT", __LINE__, x, y ); \
|
||||
abort( ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define GDS_ALWAYS_INLINE __attribute__( ( always_inline ) )
|
||||
|
||||
#define MAX_LINES 8
|
||||
|
||||
#if ! defined BIT
|
||||
#define BIT( n ) ( 1 << n )
|
||||
#endif
|
||||
|
||||
struct GDS_Device;
|
||||
struct GDS_FontDef;
|
||||
|
||||
/*
|
||||
* These can optionally return a succeed/fail but are as of yet unused in the driver.
|
||||
*/
|
||||
typedef bool ( *WriteCommandProc ) ( struct GDS_Device* Device, uint8_t Command );
|
||||
typedef bool ( *WriteDataProc ) ( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
|
||||
|
||||
struct spi_device_t;
|
||||
typedef struct spi_device_t* spi_device_handle_t;
|
||||
|
||||
#define IF_SPI 0
|
||||
#define IF_I2C 1
|
||||
|
||||
struct GDS_Device {
|
||||
uint8_t IF;
|
||||
union {
|
||||
// I2C Specific
|
||||
struct {
|
||||
uint8_t Address;
|
||||
};
|
||||
// SPI specific
|
||||
struct {
|
||||
spi_device_handle_t SPIHandle;
|
||||
int8_t RSTPin;
|
||||
int8_t CSPin;
|
||||
};
|
||||
};
|
||||
|
||||
// cooked text mode
|
||||
struct {
|
||||
int16_t Y, Space;
|
||||
const struct GDS_FontDef* Font;
|
||||
} Lines[MAX_LINES];
|
||||
|
||||
uint16_t Width;
|
||||
uint16_t Height;
|
||||
uint8_t Depth;
|
||||
|
||||
uint8_t* Framebuffer, *Shadowbuffer;
|
||||
uint16_t FramebufferSize;
|
||||
bool Dirty;
|
||||
|
||||
// default fonts when using direct draw
|
||||
const struct GDS_FontDef* Font;
|
||||
bool FontForceProportional;
|
||||
bool FontForceMonospace;
|
||||
|
||||
// various driver-specific method
|
||||
bool (*Init)( struct GDS_Device* Device);
|
||||
void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast );
|
||||
void (*DisplayOn)( struct GDS_Device* Device );
|
||||
void (*DisplayOff)( struct GDS_Device* Device );
|
||||
void (*Update)( struct GDS_Device* Device );
|
||||
void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
void (*SetHFlip)( struct GDS_Device* Device, bool On );
|
||||
void (*SetVFlip)( struct GDS_Device* Device, bool On );
|
||||
|
||||
// interface-specific methods
|
||||
WriteCommandProc WriteCommand;
|
||||
WriteDataProc WriteData;
|
||||
};
|
||||
|
||||
bool GDS_Reset( struct GDS_Device* Device );
|
||||
|
||||
void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color );
|
||||
|
||||
inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
|
||||
bool Result = (
|
||||
( x >= 0 ) &&
|
||||
( x < Device->Width ) &&
|
||||
( y >= 0 ) &&
|
||||
( y < Device->Height )
|
||||
) ? true : false;
|
||||
|
||||
#if CONFIG_GDS_CLIPDEBUG > 0
|
||||
if ( Result == false ) {
|
||||
ClipDebug( x, y );
|
||||
}
|
||||
#endif
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int x, int y, int Color ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR GDS_DrawPixel4( struct GDS_Device* Device, int x, int y, int Color ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
195
components/display/core/gds_text.c
Normal file
195
components/display/core/gds_text.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* (c) 2004,2006 Richard Titmuss for SlimProtoLib
|
||||
* (c) Philippe G. 2019, philippe_44@outlook.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "esp_log.h"
|
||||
#include "gds.h"
|
||||
#include "gds_draw.h"
|
||||
#include "gds_text.h"
|
||||
#include "gds_private.h"
|
||||
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
static char TAG[] = "gds";
|
||||
|
||||
/****************************************************************************************
|
||||
* Set fonts for each line in text mode
|
||||
*/
|
||||
static const struct GDS_FontDef *GuessFont( struct GDS_Device *Device, int FontType) {
|
||||
switch(FontType) {
|
||||
case GDS_FONT_LINE_1:
|
||||
return &Font_line_1;
|
||||
case GDS_FONT_LINE_2:
|
||||
return &Font_line_2;
|
||||
case GDS_FONT_SMALL:
|
||||
return &Font_droid_sans_fallback_11x13;
|
||||
case GDS_FONT_MEDIUM:
|
||||
default:
|
||||
return &Font_droid_sans_fallback_15x17;
|
||||
case GDS_FONT_LARGE:
|
||||
return &Font_droid_sans_fallback_24x28;
|
||||
break;
|
||||
case GDS_FONT_SEGMENT:
|
||||
if (Device->Height == 32) return &Font_Tarable7Seg_16x32;
|
||||
else return &Font_Tarable7Seg_32x64;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************************
|
||||
* Set fonts for each line in text mode
|
||||
*/
|
||||
bool GDS_TextSetFontAuto(struct GDS_Device* Device, int N, int FontType, int Space) {
|
||||
const struct GDS_FontDef *Font = GuessFont( Device, FontType );
|
||||
return GDS_TextSetFont( Device, N, Font, Space );
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Set fonts for each line in text mode
|
||||
*/
|
||||
bool GDS_TextSetFont(struct GDS_Device* Device, int N, const struct GDS_FontDef *Font, int Space) {
|
||||
if (--N >= MAX_LINES) return false;
|
||||
|
||||
Device->Lines[N].Font = Font;
|
||||
|
||||
// re-calculate lines absolute position
|
||||
Device->Lines[N].Space = Space;
|
||||
Device->Lines[0].Y = Device->Lines[0].Space;
|
||||
for (int i = 1; i <= N; i++) Device->Lines[i].Y = Device->Lines[i-1].Y + Device->Lines[i-1].Font->Height + Device->Lines[i].Space;
|
||||
|
||||
ESP_LOGI(TAG, "Adding line %u at %d (height:%u)", N + 1, Device->Lines[N].Y, Device->Lines[N].Font->Height);
|
||||
|
||||
if (Device->Lines[N].Y + Device->Lines[N].Font->Height > Device->Height) {
|
||||
ESP_LOGW(TAG, "line does not fit display");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Text) {
|
||||
int Width, X = Pos;
|
||||
|
||||
// counting 1..n
|
||||
N--;
|
||||
|
||||
GDS_SetFont( Device, Device->Lines[N].Font );
|
||||
if (Attr & GDS_TEXT_MONOSPACE) GDS_FontForceMonospace( Device, true );
|
||||
|
||||
Width = GDS_FontMeasureString( Device, Text );
|
||||
|
||||
// adjusting position, erase only EoL for rigth-justified
|
||||
if (Pos == GDS_TEXT_RIGHT) X = Device->Width - Width - 1;
|
||||
else if (Pos == GDS_TEXT_CENTER) X = (Device->Width - Width) / 2;
|
||||
|
||||
// erase if requested
|
||||
if (Attr & GDS_TEXT_CLEAR) {
|
||||
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
|
||||
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->Width; c++)
|
||||
for (int y = Y_min; y < Y_max; y++)
|
||||
Device->DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
|
||||
}
|
||||
|
||||
GDS_FontDrawString( Device, X, Device->Lines[N].Y, Text, GDS_COLOR_WHITE );
|
||||
|
||||
ESP_LOGD(TAG, "displaying %s line %u (x:%d, attr:%u)", Text, N+1, X, Attr);
|
||||
|
||||
// update whole display if requested
|
||||
Device->Dirty = true;
|
||||
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
|
||||
|
||||
return Width + X < Device->Width;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Try to align string for better scrolling visual. there is probably much better to do
|
||||
*/
|
||||
int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max) {
|
||||
char Space[] = " ";
|
||||
int Len = strlen(String), Extra = 0, Boundary;
|
||||
|
||||
N--;
|
||||
|
||||
// we might already fit
|
||||
GDS_SetFont( Device, Device->Lines[N].Font );
|
||||
if (GDS_FontMeasureString( Device, String ) <= Device->Width) return 0;
|
||||
|
||||
// add some space for better visual
|
||||
strncat(String, Space, Max-Len);
|
||||
String[Max] = '\0';
|
||||
Len = strlen(String);
|
||||
|
||||
// mark the end of the extended string
|
||||
Boundary = GDS_FontMeasureString( Device, String );
|
||||
|
||||
// add a full display width
|
||||
while (Len < Max && GDS_FontMeasureString( Device, String ) - Boundary < Device->Width) {
|
||||
String[Len++] = String[Extra++];
|
||||
String[Len] = '\0';
|
||||
}
|
||||
|
||||
return Boundary;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
void GDS_TextPos(struct GDS_Device* Device, int FontType, int Where, int Attr, char *Text, ...) {
|
||||
va_list args;
|
||||
|
||||
TextAnchor Anchor = TextAnchor_Center;
|
||||
|
||||
if (Attr & GDS_TEXT_CLEAR) GDS_Clear( Device, GDS_COLOR_BLACK );
|
||||
|
||||
if (!Text) return;
|
||||
|
||||
va_start(args, Text);
|
||||
|
||||
switch(Where) {
|
||||
case GDS_TEXT_TOP_LEFT:
|
||||
default:
|
||||
Anchor = TextAnchor_NorthWest;
|
||||
break;
|
||||
case GDS_TEXT_MIDDLE_LEFT:
|
||||
Anchor = TextAnchor_West;
|
||||
break;
|
||||
case GDS_TEXT_BOTTOM_LEFT:
|
||||
Anchor = TextAnchor_SouthWest;
|
||||
break;
|
||||
case GDS_TEXT_CENTERED:
|
||||
Anchor = TextAnchor_Center;
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Displaying %s at %u with attribute %u", Text, Anchor, Attr);
|
||||
|
||||
GDS_SetFont( Device, GuessFont( Device, FontType ) );
|
||||
GDS_FontDrawAnchoredString( Device, Anchor, Text, GDS_COLOR_WHITE );
|
||||
|
||||
Device->Dirty = true;
|
||||
if (Attr & GDS_TEXT_UPDATE) GDS_Update( Device );
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
45
components/display/core/gds_text.h
Normal file
45
components/display/core/gds_text.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* (c) Philippe G. 2019, philippe_44@outlook.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gds_font.h"
|
||||
|
||||
#define GDS_TEXT_CLEAR 0x01
|
||||
#define GDS_TEXT_CLEAR_EOL 0x02
|
||||
#define GDS_TEXT_UPDATE 0x04
|
||||
#define GDS_TEXT_MONOSPACE 0x08
|
||||
|
||||
// these ones are for 'Pos' parameter of TextLine
|
||||
#define GDS_TEXT_LEFT 0
|
||||
#define GDS_TEXT_RIGHT 0xff00
|
||||
#define GDS_TEXT_CENTER 0xff01
|
||||
|
||||
// these ones are for the 'Where' parameter of TextPos
|
||||
enum { GDS_TEXT_TOP_LEFT, GDS_TEXT_MIDDLE_LEFT, GDS_TEXT_BOTTOM_LEFT, GDS_TEXT_CENTERED };
|
||||
|
||||
enum { GDS_FONT_DEFAULT, GDS_FONT_LINE_1, GDS_FONT_LINE_2, GDS_FONT_SEGMENT,
|
||||
GDS_FONT_TINY, GDS_FONT_SMALL, GDS_FONT_MEDIUM, GDS_FONT_LARGE, GDS_FONT_FONT_HUGE };
|
||||
|
||||
struct GDS_Device;
|
||||
|
||||
bool GDS_TextSetFontAuto(struct GDS_Device* Device, int N, int FontType, int Space);
|
||||
bool GDS_TextSetFont(struct GDS_Device* Device, int N, const struct GDS_FontDef *Font, int Space);
|
||||
bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Text);
|
||||
int GDS_TextStretch(struct GDS_Device* Device, int N, char *String, int Max);
|
||||
void GDS_TextPos(struct GDS_Device* Device, int FontType, int Where, int Attr, char *Text, ...);
|
||||
@@ -11,18 +11,19 @@
|
||||
#include <string.h>
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/gpio.h>
|
||||
#include "ssd13x6.h"
|
||||
#include "ssd13x6_default_if.h"
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_default_if.h"
|
||||
|
||||
static int I2CPortNumber;
|
||||
|
||||
static const int SSD13x6_I2C_COMMAND_MODE = 0x80;
|
||||
static const int SSD13x6_I2C_DATA_MODE = 0x40;
|
||||
static const int GDS_I2C_COMMAND_MODE = 0x80;
|
||||
static const int GDS_I2C_DATA_MODE = 0x40;
|
||||
|
||||
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength );
|
||||
static bool I2CDefaultWriteCommand( struct SSD13x6_Device* Display, SSDCmd Command );
|
||||
static bool I2CDefaultWriteData( struct SSD13x6_Device* Display, const uint8_t* Data, size_t DataLength );
|
||||
static bool I2CDefaultReset( struct SSD13x6_Device* Display );
|
||||
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
|
||||
static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
|
||||
|
||||
/*
|
||||
* Initializes the i2c master with the parameters specified
|
||||
@@ -30,7 +31,7 @@ static bool I2CDefaultReset( struct SSD13x6_Device* Display );
|
||||
*
|
||||
* Returns true on successful init of the i2c bus.
|
||||
*/
|
||||
bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL ) {
|
||||
bool GDS_I2CInit( int PortNumber, int SDA, int SCL ) {
|
||||
I2CPortNumber = PortNumber;
|
||||
|
||||
if (SDA != -1 && SCL != -1) {
|
||||
@@ -54,7 +55,7 @@ bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL ) {
|
||||
* Attaches a display to the I2C bus using default communication functions.
|
||||
*
|
||||
* Params:
|
||||
* DisplayHandle: Pointer to your SSD13x6_Device object
|
||||
* Device: Pointer to your GDS_Device object
|
||||
* Width: Width of display
|
||||
* Height: Height of display
|
||||
* I2CAddress: Address of your display
|
||||
@@ -62,26 +63,24 @@ bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL ) {
|
||||
*
|
||||
* Returns true on successful init of display.
|
||||
*/
|
||||
bool SSD13x6_I2CMasterAttachDisplayDefault( struct SSD13x6_Device* DeviceHandle, int Model, int Width, int Height, int I2CAddress, int RSTPin ) {
|
||||
NullCheck( DeviceHandle, return false );
|
||||
bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int I2CAddress, int RSTPin ) {
|
||||
NullCheck( Device, return false );
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
Device->WriteCommand = I2CDefaultWriteCommand;
|
||||
Device->WriteData = I2CDefaultWriteData;
|
||||
Device->Address = I2CAddress;
|
||||
Device->RSTPin = RSTPin;
|
||||
Device->IF = IF_I2C;
|
||||
Device->Width = Width;
|
||||
Device->Height = Height;
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 1 ), return false );
|
||||
GDS_Reset( Device );
|
||||
}
|
||||
|
||||
memset( DeviceHandle, 0, sizeof( struct SSD13x6_Device ) );
|
||||
DeviceHandle->Model = Model;
|
||||
|
||||
return SSD13x6_Init_I2C( DeviceHandle,
|
||||
Width,
|
||||
Height,
|
||||
I2CAddress,
|
||||
RSTPin,
|
||||
I2CDefaultWriteCommand,
|
||||
I2CDefaultWriteData,
|
||||
I2CDefaultReset
|
||||
);
|
||||
return Device->Init( Device );
|
||||
}
|
||||
|
||||
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength ) {
|
||||
@@ -91,7 +90,7 @@ static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Da
|
||||
NullCheck( Data, return false );
|
||||
|
||||
if ( ( CommandHandle = i2c_cmd_link_create( ) ) != NULL ) {
|
||||
ModeByte = ( IsCommand == true ) ? SSD13x6_I2C_COMMAND_MODE: SSD13x6_I2C_DATA_MODE;
|
||||
ModeByte = ( IsCommand == true ) ? GDS_I2C_COMMAND_MODE: GDS_I2C_DATA_MODE;
|
||||
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_start( CommandHandle ), goto error );
|
||||
ESP_ERROR_CHECK_NONFATAL( i2c_master_write_byte( CommandHandle, ( Address << 1 ) | I2C_MASTER_WRITE, true ), goto error );
|
||||
@@ -110,29 +109,16 @@ error:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool I2CDefaultWriteCommand( struct SSD13x6_Device* Display, SSDCmd Command ) {
|
||||
static bool I2CDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command ) {
|
||||
uint8_t CommandByte = ( uint8_t ) Command;
|
||||
|
||||
NullCheck( Display, return false );
|
||||
return I2CDefaultWriteBytes( Display->Address, true, ( const uint8_t* ) &CommandByte, 1 );
|
||||
|
||||
NullCheck( Device, return false );
|
||||
return I2CDefaultWriteBytes( Device->Address, true, ( const uint8_t* ) &CommandByte, 1 );
|
||||
}
|
||||
|
||||
static bool I2CDefaultWriteData( struct SSD13x6_Device* Display, const uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( Display, return false );
|
||||
static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( Device, return false );
|
||||
NullCheck( Data, return false );
|
||||
|
||||
return I2CDefaultWriteBytes( Display->Address, false, Data, DataLength );
|
||||
return I2CDefaultWriteBytes( Device->Address, false, Data, DataLength );
|
||||
}
|
||||
|
||||
static bool I2CDefaultReset( struct SSD13x6_Device* Display ) {
|
||||
NullCheck( Display, return false );
|
||||
|
||||
if ( Display->RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( Display->RSTPin, 0 ), return true );
|
||||
vTaskDelay( pdMS_TO_TICKS( 100 ) );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( Display->RSTPin, 1 ), return true );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
109
components/display/core/ifaces/default_if_spi.c
Normal file
109
components/display/core/ifaces/default_if_spi.c
Normal file
@@ -0,0 +1,109 @@
|
||||
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/task.h>
|
||||
#include "gds.h"
|
||||
#include "gds_err.h"
|
||||
#include "gds_private.h"
|
||||
#include "gds_default_if.h"
|
||||
|
||||
static const int GDS_SPI_Command_Mode = 0;
|
||||
static const int GDS_SPI_Data_Mode = 1;
|
||||
|
||||
static spi_host_device_t SPIHost;
|
||||
static int DCPin;
|
||||
|
||||
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength );
|
||||
static bool SPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command );
|
||||
static bool SPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength );
|
||||
|
||||
bool GDS_SPIInit( int SPI, int DC ) {
|
||||
SPIHost = SPI;
|
||||
DCPin = DC;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int CSPin, int RSTPin, int Speed ) {
|
||||
spi_device_interface_config_t SPIDeviceConfig;
|
||||
spi_device_handle_t SPIDevice;
|
||||
|
||||
NullCheck( Device, return false );
|
||||
|
||||
if (CSPin >= 0) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( CSPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
|
||||
}
|
||||
|
||||
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
|
||||
|
||||
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
|
||||
SPIDeviceConfig.spics_io_num = CSPin;
|
||||
SPIDeviceConfig.queue_size = 1;
|
||||
|
||||
ESP_ERROR_CHECK_NONFATAL( spi_bus_add_device( SPIHost, &SPIDeviceConfig, &SPIDevice ), return false );
|
||||
|
||||
Device->WriteCommand = SPIDefaultWriteCommand;
|
||||
Device->WriteData = SPIDefaultWriteData;
|
||||
Device->SPIHandle = SPIDevice;
|
||||
Device->RSTPin = RSTPin;
|
||||
Device->CSPin = CSPin;
|
||||
Device->IF = IF_SPI;
|
||||
Device->Width = Width;
|
||||
Device->Height = Height;
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 0 ), return false );
|
||||
GDS_Reset( Device );
|
||||
}
|
||||
|
||||
return Device->Init( Device );
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) {
|
||||
spi_transaction_t SPITransaction = { 0 };
|
||||
|
||||
NullCheck( SPIHandle, return false );
|
||||
NullCheck( Data, return false );
|
||||
|
||||
if ( DataLength > 0 ) {
|
||||
gpio_set_level( DCPin, WriteMode );
|
||||
|
||||
SPITransaction.length = DataLength * 8;
|
||||
SPITransaction.tx_buffer = Data;
|
||||
|
||||
// only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes)
|
||||
ESP_ERROR_CHECK_NONFATAL( spi_device_polling_transmit(SPIHandle, &SPITransaction), return false );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteCommand( struct GDS_Device* Device, uint8_t Command ) {
|
||||
static uint8_t CommandByte = 0;
|
||||
|
||||
NullCheck( Device, return false );
|
||||
NullCheck( Device->SPIHandle, return false );
|
||||
|
||||
CommandByte = Command;
|
||||
|
||||
return SPIDefaultWriteBytes( Device->SPIHandle, GDS_SPI_Command_Mode, &CommandByte, 1 );
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( Device, return false );
|
||||
NullCheck( Device->SPIHandle, return false );
|
||||
|
||||
return SPIDefaultWriteBytes( Device->SPIHandle, GDS_SPI_Data_Mode, Data, DataLength );
|
||||
}
|
||||
@@ -22,14 +22,15 @@
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "esp_log.h"
|
||||
#include "globdefs.h"
|
||||
#include "config.h"
|
||||
#include "tools.h"
|
||||
#include "display.h"
|
||||
|
||||
// here we should include all possible drivers
|
||||
extern struct display_s SSD13x6_display;
|
||||
|
||||
struct display_s *display = NULL;
|
||||
#include "gds.h"
|
||||
#include "gds_default_if.h"
|
||||
#include "gds_draw.h"
|
||||
#include "gds_text.h"
|
||||
#include "gds_font.h"
|
||||
|
||||
static const char *TAG = "display";
|
||||
|
||||
@@ -57,34 +58,74 @@ static EXT_RAM_ATTR struct {
|
||||
|
||||
static void displayer_task(void *args);
|
||||
|
||||
struct GDS_Device *display;
|
||||
struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device);
|
||||
struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device);
|
||||
GDS_DetectFunc drivers[] = { SH1106_Detect, SSD1306_Detect, NULL };
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
void display_init(char *welcome) {
|
||||
bool init = false;
|
||||
char *item = config_alloc_get(NVS_TYPE_STR, "display_config");
|
||||
char *config = config_alloc_get(NVS_TYPE_STR, "display_config");
|
||||
|
||||
if (item && *item) {
|
||||
char * drivername=strstr(item,"driver");
|
||||
if (!drivername || (drivername && (strcasestr(drivername,"SSD1306") || strcasestr(drivername,"SSD1326") || strcasestr(drivername,"SH1106")))) {
|
||||
display = &SSD13x6_display;
|
||||
if (display->init(item, welcome)) {
|
||||
init = true;
|
||||
ESP_LOGI(TAG, "Display initialization successful");
|
||||
} else {
|
||||
display = NULL;
|
||||
ESP_LOGW(TAG, "Display initialization failed");
|
||||
}
|
||||
if (!config) {
|
||||
ESP_LOGI(TAG, "no display");
|
||||
return false;
|
||||
}
|
||||
|
||||
int width = -1, height = -1;
|
||||
char *p, *drivername = strstr(config, "driver");
|
||||
|
||||
// query drivers to see if we have a match
|
||||
ESP_LOGI(TAG, "Trying to configure display with %s", config);
|
||||
display = GDS_AutoDetect(drivername ? drivername : "SSD1306", drivers);
|
||||
|
||||
if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
|
||||
|
||||
// so far so good
|
||||
if (display && width > 0 && height > 0) {
|
||||
// Detect driver interface
|
||||
if (strstr(config, "I2C") && i2c_system_port != -1) {
|
||||
int address = 0x3C;
|
||||
|
||||
if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1);
|
||||
|
||||
init = true;
|
||||
GDS_I2CInit( i2c_system_port, -1, -1 ) ;
|
||||
GDS_I2CAttachDevice( display, width, height, address, -1 );
|
||||
|
||||
ESP_LOGI(TAG, "Display is I2C on port %u", address);
|
||||
} else if (strstr(config, "SPI") && spi_system_host != -1) {
|
||||
int CS_pin = -1, speed = 0;
|
||||
|
||||
if ((p = strcasestr(config, "cs")) != NULL) CS_pin = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "speed")) != NULL) speed = atoi(strchr(p, '=') + 1);
|
||||
|
||||
init = true;
|
||||
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
|
||||
GDS_SPIAttachDevice( display, width, height, CS_pin, -1, speed );
|
||||
|
||||
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
|
||||
} else {
|
||||
ESP_LOGE(TAG,"Unknown display driver name in display config: %s",item);
|
||||
display = NULL;
|
||||
ESP_LOGI(TAG, "Unsupported display interface or serial link not configured");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "no display");
|
||||
}
|
||||
display = NULL;
|
||||
ESP_LOGW(TAG, "No display driver");
|
||||
}
|
||||
|
||||
if (init) {
|
||||
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
|
||||
static EXT_RAM_ATTR StackType_t xStack[DISPLAYER_STACK_SIZE] __attribute__ ((aligned (4)));
|
||||
|
||||
GDS_SetHFlip(display, strcasestr(config, "HFlip") ? true : false);
|
||||
GDS_SetVFlip(display, strcasestr(config, "VFlip") ? true : false);
|
||||
GDS_SetFont(display, &Font_droid_sans_fallback_15x17 );
|
||||
GDS_TextPos(display, GDS_FONT_MEDIUM, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, welcome);
|
||||
|
||||
// start the task that will handle scrolling & counting
|
||||
displayer.mutex = xSemaphoreCreateMutex();
|
||||
@@ -94,13 +135,13 @@ void display_init(char *welcome) {
|
||||
displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", DISPLAYER_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
|
||||
|
||||
// set lines for "fixed" text mode
|
||||
display->set_font(1, DISPLAY_FONT_LINE_1, -3);
|
||||
display->set_font(2, DISPLAY_FONT_LINE_2, -3);
|
||||
GDS_TextSetFontAuto(display, 1, GDS_FONT_LINE_1, -3);
|
||||
GDS_TextSetFontAuto(display, 2, GDS_FONT_LINE_2, -3);
|
||||
|
||||
displayer.metadata_config = config_alloc_get(NVS_TYPE_STR, "metadata_config");
|
||||
}
|
||||
|
||||
if (item) free(item);
|
||||
free(config);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
@@ -114,14 +155,14 @@ static void displayer_task(void *args) {
|
||||
while (1) {
|
||||
// suspend ourselves if nothing to do
|
||||
if (displayer.state < DISPLAYER_ACTIVE) {
|
||||
if (displayer.state == DISPLAYER_IDLE) display->line(2, 0, DISPLAY_CLEAR | DISPLAY_UPDATE, displayer.string);
|
||||
if (displayer.state == DISPLAYER_IDLE) GDS_TextLine(display, 2, 0, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, displayer.string);
|
||||
vTaskSuspend(NULL);
|
||||
scroll_sleep = 0;
|
||||
display->clear(true);
|
||||
display->line(1, DISPLAY_LEFT, DISPLAY_UPDATE, displayer.header);
|
||||
GDS_ClearExt(display, true);
|
||||
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_UPDATE, displayer.header);
|
||||
} else if (displayer.refresh) {
|
||||
// little trick when switching master while in IDLE and missing it
|
||||
display->line(1, DISPLAY_LEFT, DISPLAY_CLEAR | DISPLAY_UPDATE, displayer.header);
|
||||
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, displayer.header);
|
||||
displayer.refresh = false;
|
||||
}
|
||||
|
||||
@@ -140,7 +181,7 @@ static void displayer_task(void *args) {
|
||||
xSemaphoreGive(displayer.mutex);
|
||||
|
||||
// now display using safe copies, can be lengthy
|
||||
display->line(2, offset, DISPLAY_CLEAR | DISPLAY_UPDATE, string);
|
||||
GDS_TextLine(display, 2, offset, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, string);
|
||||
free(string);
|
||||
} else {
|
||||
scroll_sleep = DEFAULT_SLEEP;
|
||||
@@ -160,7 +201,7 @@ static void displayer_task(void *args) {
|
||||
xSemaphoreGive(displayer.mutex);
|
||||
if (displayer.elapsed < 3600) sprintf(counter, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60);
|
||||
else sprintf(counter, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60);
|
||||
display->line(1, DISPLAY_RIGHT, (DISPLAY_CLEAR | DISPLAY_ONLY_EOL) | DISPLAY_UPDATE, counter);
|
||||
GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, counter);
|
||||
timer_sleep = 1000;
|
||||
} else timer_sleep = max(1000 - elapsed, 0);
|
||||
} else timer_sleep = DEFAULT_SLEEP;
|
||||
@@ -240,7 +281,7 @@ void displayer_metadata(char *artist, char *album, char *title) {
|
||||
displayer.offset = 0;
|
||||
utf8_decode(displayer.string);
|
||||
ESP_LOGI(TAG, "playing %s", displayer.string);
|
||||
displayer.boundary = display->stretch(2, displayer.string, SCROLLABLE_SIZE);
|
||||
displayer.boundary = GDS_TextStretch(display, 2, displayer.string, SCROLLABLE_SIZE);
|
||||
|
||||
xSemaphoreGive(displayer.mutex);
|
||||
}
|
||||
@@ -258,7 +299,7 @@ void displayer_scroll(char *string, int speed) {
|
||||
displayer.offset = 0;
|
||||
strncpy(displayer.string, string, SCROLLABLE_SIZE);
|
||||
displayer.string[SCROLLABLE_SIZE] = '\0';
|
||||
displayer.boundary = display->stretch(2, displayer.string, SCROLLABLE_SIZE);
|
||||
displayer.boundary = GDS_TextStretch(display, 2, displayer.string, SCROLLABLE_SIZE);
|
||||
|
||||
xSemaphoreGive(displayer.mutex);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gds.h"
|
||||
|
||||
/*
|
||||
The displayer is not thread-safe and the caller must ensure use its own
|
||||
mutexes if it wants something better. Especially, text() line() and draw()
|
||||
@@ -32,44 +34,11 @@
|
||||
when this one (the main) wants to take control over display, it can signal
|
||||
that to others
|
||||
*/
|
||||
extern struct GDS_Device *display;
|
||||
|
||||
#define DISPLAY_CLEAR 0x01
|
||||
#define DISPLAY_ONLY_EOL 0x02
|
||||
#define DISPLAY_UPDATE 0x04
|
||||
#define DISPLAY_MONOSPACE 0x08
|
||||
|
||||
// these ones are for 'x' parameter of line() function
|
||||
#define DISPLAY_LEFT 0
|
||||
#define DISPLAY_RIGHT 0xff00
|
||||
#define DISPLAY_CENTER 0xff01
|
||||
|
||||
enum display_pos_e { DISPLAY_TOP_LEFT, DISPLAY_MIDDLE_LEFT, DISPLAY_BOTTOM_LEFT, DISPLAY_CENTERED };
|
||||
enum display_font_e { DISPLAY_FONT_DEFAULT,
|
||||
DISPLAY_FONT_LINE_1, DISPLAY_FONT_LINE_2, DISPLAY_FONT_SEGMENT,
|
||||
DISPLAY_FONT_TINY, DISPLAY_FONT_SMALL, DISPLAY_FONT_MEDIUM, DISPLAY_FONT_LARGE, DISPLAY_FONT_HUGE };
|
||||
|
||||
enum displayer_cmd_e { DISPLAYER_SHUTDOWN, DISPLAYER_ACTIVATE, DISPLAYER_SUSPEND, DISPLAYER_TIMER_PAUSE, DISPLAYER_TIMER_RUN };
|
||||
enum displayer_time_e { DISPLAYER_ELAPSED, DISPLAYER_REMAINING };
|
||||
|
||||
// don't change anything there w/o changing all drivers init code
|
||||
extern struct display_s {
|
||||
int width, height;
|
||||
bool dirty;
|
||||
bool (*init)(char *config, char *welcome);
|
||||
void (*clear)(bool full, ...);
|
||||
bool (*set_font)(int num, enum display_font_e font, int space);
|
||||
void (*on)(bool state);
|
||||
void (*brightness)(uint8_t level);
|
||||
void (*text)(enum display_font_e font, enum display_pos_e pos, int attribute, char *msg, ...);
|
||||
bool (*line)(int num, int x, int attribute, char *text);
|
||||
int (*stretch)(int num, char *string, int max);
|
||||
void (*update)(void);
|
||||
void (*draw_raw)(int x1, int y1, int x2, int y2, bool by_column, bool MSb, uint8_t *data);
|
||||
void (*draw_cbr)(uint8_t *data, int width, int height); // width and height is the # of rows/columns in data, as opposed to display height (0 = display width, 0 = display height)
|
||||
void (*draw_line)(int x1, int y1, int x2, int y2);
|
||||
void (*draw_box)( int x1, int y1, int x2, int y2, bool fill);
|
||||
} *display;
|
||||
|
||||
enum display_bus_cmd_e { DISPLAY_BUS_TAKE, DISPLAY_BUS_GIVE };
|
||||
bool (*display_bus)(void *from, enum display_bus_cmd_e cmd);
|
||||
|
||||
|
||||
@@ -1,446 +0,0 @@
|
||||
/*
|
||||
* (c) 2004,2006 Richard Titmuss for SlimProtoLib
|
||||
* (c) Philippe G. 2019, philippe_44@outlook.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "esp_log.h"
|
||||
#include "display.h"
|
||||
#include "globdefs.h"
|
||||
|
||||
#include "ssd13x6.h"
|
||||
#include "ssd13x6_draw.h"
|
||||
#include "ssd13x6_font.h"
|
||||
#include "ssd13x6_default_if.h"
|
||||
|
||||
#define I2C_ADDRESS 0x3C
|
||||
|
||||
static const char *TAG = "display";
|
||||
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
// handlers
|
||||
static bool init(char *config, char *welcome);
|
||||
static void clear(bool full, ...);
|
||||
static bool set_font(int num, enum display_font_e font, int space);
|
||||
static void text(enum display_font_e font, enum display_pos_e pos, int attribute, char *text, ...);
|
||||
static bool line(int num, int x, int attribute, char *text);
|
||||
static int stretch(int num, char *string, int max);
|
||||
static void draw_cbr(u8_t *data, int width, int height);
|
||||
static void draw_raw(int x1, int y1, int x2, int y2, bool by_column, bool MSb, u8_t *data);
|
||||
static void draw_line(int x1, int y1, int x2, int y2);
|
||||
static void draw_box( int x1, int y1, int x2, int y2, bool fill);
|
||||
static void brightness(u8_t level);
|
||||
static void on(bool state);
|
||||
static void update(void);
|
||||
|
||||
// display structure for others to use
|
||||
struct display_s SSD13x6_display = { 0, 0, true,
|
||||
init, clear, set_font, on, brightness,
|
||||
text, line, stretch, update, draw_raw, draw_cbr, draw_line, draw_box };
|
||||
|
||||
// SSD13x6 specific function
|
||||
static struct SSD13x6_Device Display;
|
||||
|
||||
static const unsigned char BitReverseTable256[] =
|
||||
{
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
};
|
||||
|
||||
#define MAX_LINES 8
|
||||
|
||||
static struct {
|
||||
int y, space;
|
||||
const struct SSD13x6_FontDef *font;
|
||||
} lines[MAX_LINES];
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static bool init(char *config, char *welcome) {
|
||||
bool res = true;
|
||||
int width = -1, height = -1, model = SSD1306;
|
||||
char *p;
|
||||
|
||||
ESP_LOGI(TAG, "Initializing display with config: %s",config);
|
||||
|
||||
// no time for smart parsing - this is for tinkerers
|
||||
if ((p = strcasestr(config, "width")) != NULL) width = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "height")) != NULL) height = atoi(strchr(p, '=') + 1);
|
||||
|
||||
if (strcasestr(config, "ssd1326")) model = SSD1326;
|
||||
else if (strcasestr(config, "sh1106")) model = SH1106;
|
||||
|
||||
if (width == -1 || height == -1) {
|
||||
ESP_LOGW(TAG, "No display configured %s [%d x %d]", config, width, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstr(config, "I2C") && i2c_system_port != -1) {
|
||||
int address = I2C_ADDRESS;
|
||||
|
||||
if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1);
|
||||
|
||||
SSD13x6_I2CMasterInitDefault( i2c_system_port, -1, -1 ) ;
|
||||
SSD13x6_I2CMasterAttachDisplayDefault( &Display, model, width, height, address, -1 );
|
||||
SSD13x6_SetHFlip( &Display, strcasestr(config, "HFlip") ? true : false);
|
||||
SSD13x6_SetVFlip( &Display, strcasestr(config, "VFlip") ? true : false);
|
||||
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_15x17 );
|
||||
SSD13x6_display.width = width;
|
||||
SSD13x6_display.height = height;
|
||||
|
||||
text(DISPLAY_FONT_MEDIUM, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, welcome);
|
||||
|
||||
ESP_LOGI(TAG, "Display is I2C on port %u", address);
|
||||
} else if (strstr(config, "SPI") && spi_system_host != -1) {
|
||||
int CS_pin = -1, speed = 0;
|
||||
|
||||
if ((p = strcasestr(config, "cs")) != NULL) CS_pin = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "speed")) != NULL) speed = atoi(strchr(p, '=') + 1);
|
||||
|
||||
SSD13x6_SPIMasterInitDefault( spi_system_host, spi_system_dc_gpio );
|
||||
SSD13x6_SPIMasterAttachDisplayDefault( &Display, model, width, height, CS_pin, -1, speed );
|
||||
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_15x17 );
|
||||
SSD13x6_display.width = width;
|
||||
SSD13x6_display.height = height;
|
||||
|
||||
text(DISPLAY_FONT_MEDIUM, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, welcome);
|
||||
|
||||
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
|
||||
|
||||
} else {
|
||||
res = false;
|
||||
ESP_LOGW(TAG, "Unknown display type or no serial interface configured");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static void clear(bool full, ...) {
|
||||
bool commit = true;
|
||||
|
||||
if (full) {
|
||||
SSD13x6_Clear( &Display, SSD_COLOR_BLACK );
|
||||
} else {
|
||||
va_list args;
|
||||
va_start(args, full);
|
||||
commit = va_arg(args, int);
|
||||
int x1 = va_arg(args, int), y1 = va_arg(args, int), x2 = va_arg(args, int), y2 = va_arg(args, int);
|
||||
if (x2 < 0) x2 = display->width - 1;
|
||||
if (y2 < 0) y2 = display->height - 1;
|
||||
SSD13x6_ClearWindow( &Display, x1, y1, x2, y2, SSD_COLOR_BLACK );
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
SSD13x6_display.dirty = true;
|
||||
if (commit) update();
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Set fonts for each line in text mode
|
||||
*/
|
||||
static bool set_font(int num, enum display_font_e font, int space) {
|
||||
if (--num >= MAX_LINES) return false;
|
||||
|
||||
switch(font) {
|
||||
case DISPLAY_FONT_LINE_1:
|
||||
lines[num].font = &Font_line_1;
|
||||
break;
|
||||
case DISPLAY_FONT_LINE_2:
|
||||
lines[num].font = &Font_line_2;
|
||||
break;
|
||||
case DISPLAY_FONT_SMALL:
|
||||
lines[num].font = &Font_droid_sans_fallback_11x13;
|
||||
break;
|
||||
case DISPLAY_FONT_MEDIUM:
|
||||
case DISPLAY_FONT_DEFAULT:
|
||||
default:
|
||||
lines[num].font = &Font_droid_sans_fallback_15x17;
|
||||
break;
|
||||
case DISPLAY_FONT_LARGE:
|
||||
lines[num].font = &Font_droid_sans_fallback_24x28;
|
||||
break;
|
||||
case DISPLAY_FONT_SEGMENT:
|
||||
if (Display.Height == 32) lines[num].font = &Font_Tarable7Seg_16x32;
|
||||
else lines[num].font = &Font_Tarable7Seg_32x64;
|
||||
break;
|
||||
}
|
||||
|
||||
// re-calculate lines absolute position
|
||||
lines[num].space = space;
|
||||
lines[0].y = lines[0].space;
|
||||
for (int i = 1; i <= num; i++) lines[i].y = lines[i-1].y + lines[i-1].font->Height + lines[i].space;
|
||||
|
||||
ESP_LOGI(TAG, "Adding line %u at %d (height:%u)", num + 1, lines[num].y, lines[num].font->Height);
|
||||
|
||||
if (lines[num].y + lines[num].font->Height > Display.Height) {
|
||||
ESP_LOGW(TAG, "line does not fit display");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static bool line(int num, int x, int attribute, char *text) {
|
||||
int width;
|
||||
|
||||
// counting 1..n
|
||||
num--;
|
||||
|
||||
SSD13x6_SetFont( &Display, lines[num].font );
|
||||
if (attribute & DISPLAY_MONOSPACE) SSD13x6_FontForceMonospace( &Display, true );
|
||||
|
||||
width = SSD13x6_FontMeasureString( &Display, text );
|
||||
|
||||
// adjusting position, erase only EoL for rigth-justified
|
||||
if (x == DISPLAY_RIGHT) x = Display.Width - width - 1;
|
||||
else if (x == DISPLAY_CENTER) x = (Display.Width - width) / 2;
|
||||
|
||||
// erase if requested
|
||||
if (attribute & DISPLAY_CLEAR) {
|
||||
int y_min = max(0, lines[num].y), y_max = max(0, lines[num].y + lines[num].font->Height);
|
||||
for (int c = (attribute & DISPLAY_ONLY_EOL) ? x : 0; c < Display.Width; c++)
|
||||
for (int y = y_min; y < y_max; y++)
|
||||
SSD13x6_DrawPixelFast( &Display, c, y, SSD_COLOR_BLACK );
|
||||
}
|
||||
|
||||
SSD13x6_FontDrawString( &Display, x, lines[num].y, text, SSD_COLOR_WHITE );
|
||||
|
||||
ESP_LOGD(TAG, "displaying %s line %u (x:%d, attr:%u)", text, num+1, x, attribute);
|
||||
|
||||
// update whole display if requested
|
||||
SSD13x6_display.dirty = true;
|
||||
if (attribute & DISPLAY_UPDATE) update();
|
||||
|
||||
return width + x < Display.Width;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Try to align string for better scrolling visual. there is probably much better to do
|
||||
*/
|
||||
static int stretch(int num, char *string, int max) {
|
||||
char space[] = " ";
|
||||
int len = strlen(string), extra = 0, boundary;
|
||||
|
||||
num--;
|
||||
|
||||
// we might already fit
|
||||
SSD13x6_SetFont( &Display, lines[num].font );
|
||||
if (SSD13x6_FontMeasureString( &Display, string ) <= Display.Width) return 0;
|
||||
|
||||
// add some space for better visual
|
||||
strncat(string, space, max-len);
|
||||
string[max] = '\0';
|
||||
len = strlen(string);
|
||||
|
||||
// mark the end of the extended string
|
||||
boundary = SSD13x6_FontMeasureString( &Display, string );
|
||||
|
||||
// add a full display width
|
||||
while (len < max && SSD13x6_FontMeasureString( &Display, string ) - boundary < Display.Width) {
|
||||
string[len++] = string[extra++];
|
||||
string[len] = '\0';
|
||||
}
|
||||
|
||||
return boundary;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static void text(enum display_font_e font, enum display_pos_e pos, int attribute, char *text, ...) {
|
||||
va_list args;
|
||||
|
||||
TextAnchor Anchor = TextAnchor_Center;
|
||||
|
||||
if (attribute & DISPLAY_CLEAR) SSD13x6_Clear( &Display, SSD_COLOR_BLACK );
|
||||
|
||||
if (!text) return;
|
||||
|
||||
va_start(args, text);
|
||||
|
||||
switch(font) {
|
||||
case DISPLAY_FONT_LINE_1:
|
||||
SSD13x6_SetFont( &Display, &Font_line_1 );
|
||||
break;
|
||||
case DISPLAY_FONT_LINE_2:
|
||||
SSD13x6_SetFont( &Display, &Font_line_2 );
|
||||
break;
|
||||
case DISPLAY_FONT_SMALL:
|
||||
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_11x13 );
|
||||
break;
|
||||
case DISPLAY_FONT_MEDIUM:
|
||||
case DISPLAY_FONT_DEFAULT:
|
||||
default:
|
||||
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_15x17 );
|
||||
break;
|
||||
case DISPLAY_FONT_LARGE:
|
||||
SSD13x6_SetFont( &Display, &Font_droid_sans_fallback_24x28 );
|
||||
break;
|
||||
case DISPLAY_FONT_SEGMENT:
|
||||
if (Display.Height == 32) SSD13x6_SetFont( &Display, &Font_Tarable7Seg_16x32 );
|
||||
else SSD13x6_SetFont( &Display, &Font_Tarable7Seg_32x64 );
|
||||
break;
|
||||
}
|
||||
|
||||
switch(pos) {
|
||||
case DISPLAY_TOP_LEFT:
|
||||
default:
|
||||
Anchor = TextAnchor_NorthWest;
|
||||
break;
|
||||
case DISPLAY_MIDDLE_LEFT:
|
||||
Anchor = TextAnchor_West;
|
||||
break;
|
||||
case DISPLAY_BOTTOM_LEFT:
|
||||
Anchor = TextAnchor_SouthWest;
|
||||
break;
|
||||
case DISPLAY_CENTERED:
|
||||
Anchor = TextAnchor_Center;
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "SSDD13x6 displaying %s at %u with attribute %u", text, Anchor, attribute);
|
||||
|
||||
SSD13x6_FontDrawAnchoredString( &Display, Anchor, text, SSD_COLOR_WHITE );
|
||||
|
||||
SSD13x6_display.dirty = true;
|
||||
if (attribute & DISPLAY_UPDATE) update();
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Process graphic display data from column-oriented data (MSbit first)
|
||||
*/
|
||||
static void draw_cbr(u8_t *data, int width, int height) {
|
||||
if (!height) height = Display.Height;
|
||||
if (!width) width = Display.Width;
|
||||
|
||||
// need to do row/col swap and bit-reverse
|
||||
int rows = height / 8;
|
||||
for (int r = 0; r < rows; r++) {
|
||||
uint8_t *optr = Display.Framebuffer + r*Display.Width, *iptr = data + r;
|
||||
for (int c = width; --c >= 0;) {
|
||||
*optr++ = BitReverseTable256[*iptr];;
|
||||
iptr += rows;
|
||||
}
|
||||
}
|
||||
|
||||
SSD13x6_display.dirty = true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Process graphic display data MSBit first
|
||||
* WARNING: this has not been tested yet
|
||||
*/
|
||||
static void draw_raw(int x1, int y1, int x2, int y2, bool by_column, bool MSb, u8_t *data) {
|
||||
// default end point to display size
|
||||
if (x2 == -1) x2 = Display.Width - 1;
|
||||
if (y2 == -1) y2 = Display.Height - 1;
|
||||
|
||||
display->dirty = true;
|
||||
|
||||
// not a boundary draw
|
||||
if (y1 % 8 || y2 % 8 || x1 % 8 | x2 % 8) {
|
||||
ESP_LOGW(TAG, "can't write on non cols/rows boundaries for now");
|
||||
} else {
|
||||
// set addressing mode to match data
|
||||
if (by_column) {
|
||||
// copy the window and do row/col exchange
|
||||
for (int r = y1/8; r <= y2/8; r++) {
|
||||
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r;
|
||||
for (int c = x1; c <= x2; c++) {
|
||||
*optr++ = MSb ? BitReverseTable256[*iptr] : *iptr;
|
||||
iptr += (y2-y1)/8 + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// just copy the window inside the frame buffer
|
||||
for (int r = y1/8; r <= y2/8; r++) {
|
||||
uint8_t *optr = Display.Framebuffer + r*Display.Width + x1, *iptr = data + r*(x2-x1+1);
|
||||
for (int c = x1; c <= x2; c++) *optr++ = *iptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Draw line
|
||||
*/
|
||||
static void draw_line( int x1, int y1, int x2, int y2) {
|
||||
SSD13x6_DrawLine( &Display, x1, y1, x2, y2, SSD_COLOR_WHITE );
|
||||
SSD13x6_display.dirty = true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Draw Box
|
||||
*/
|
||||
static void draw_box( int x1, int y1, int x2, int y2, bool fill) {
|
||||
SSD13x6_DrawBox( &Display, x1, y1, x2, y2, SSD_COLOR_WHITE, fill );
|
||||
SSD13x6_display.dirty = true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Brightness
|
||||
*/
|
||||
static void brightness(u8_t level) {
|
||||
SSD13x6_DisplayOn( &Display );
|
||||
SSD13x6_SetContrast( &Display, (uint8_t) level);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Display On/Off
|
||||
*/
|
||||
static void on(bool state) {
|
||||
if (state) SSD13x6_DisplayOn( &Display );
|
||||
else SSD13x6_DisplayOff( &Display );
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Update
|
||||
*/
|
||||
static void update(void) {
|
||||
if (SSD13x6_display.dirty) SSD13x6_Update( &Display );
|
||||
SSD13x6_display.dirty = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Fallback11x13[ ] = {
|
||||
0x05, 0x00, 0x10, 0xE4, 0x11, 0x00, 0x0E, 0x00, 0x02, 0xE4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_droid_sans_fallback_11x13 = {
|
||||
const struct GDS_FontDef Font_droid_sans_fallback_11x13 = {
|
||||
Droid_Sans_Fallback11x13,
|
||||
11,
|
||||
13,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Fallback15x17[ ] = {
|
||||
0x07, 0xC0, 0x00, 0x01, 0x00, 0x03, 0x01, 0x10, 0x8C, 0x00, 0x00, 0x70, 0x00, 0x00, 0x0C, 0x00, 0x10, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_droid_sans_fallback_15x17 = {
|
||||
const struct GDS_FontDef Font_droid_sans_fallback_15x17 = {
|
||||
Droid_Sans_Fallback15x17,
|
||||
15,
|
||||
17,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Fallback24x28[ ] = {
|
||||
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x0C, 0xE0, 0xF0, 0x03, 0x0C, 0xE0, 0x80, 0x0F, 0x0E, 0x00, 0x00, 0xFE, 0x07, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x7E, 0x00, 0xE0, 0x80, 0x0F, 0x00, 0xE0, 0xF0, 0x03, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_droid_sans_fallback_24x28 = {
|
||||
const struct GDS_FontDef Font_droid_sans_fallback_24x28 = {
|
||||
Droid_Sans_Fallback24x28,
|
||||
24,
|
||||
28,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Mono13x24[ ] = {
|
||||
0x0C, 0x00, 0x00, 0x00, 0x80, 0x00, 0xC0, 0x80, 0x07, 0xC0, 0x00, 0x1E, 0xC0, 0x18, 0xF8, 0xC0, 0x18, 0xC0, 0x7F, 0x00, 0x00, 0x1E, 0x00, 0xC0, 0x07, 0x18, 0xF8, 0x00, 0x18, 0x3E, 0x00, 0x80, 0x07, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_droid_sans_mono_13x24 = {
|
||||
const struct GDS_FontDef Font_droid_sans_mono_13x24 = {
|
||||
Droid_Sans_Mono13x24,
|
||||
13,
|
||||
24,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Mono16x31[ ] = {
|
||||
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x60, 0x00, 0x78, 0x00, 0x60, 0x00, 0xF8, 0x01, 0x60, 0x00, 0xE0, 0x07, 0x60, 0xE0, 0x00, 0x3F, 0x70, 0xE0, 0x00, 0xFC, 0x3C, 0x40, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0xE0, 0x07, 0x40, 0x00, 0xF8, 0x01, 0xE0, 0x00, 0x3F, 0x00, 0xE0, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_droid_sans_mono_16x31 = {
|
||||
const struct GDS_FontDef Font_droid_sans_mono_16x31 = {
|
||||
Droid_Sans_Mono16x31,
|
||||
16,
|
||||
31,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Droid_Sans_Mono7x13[ ] = {
|
||||
0x06, 0x00, 0x00, 0x30, 0x10, 0xC4, 0x10, 0x00, 0x0F, 0xC4, 0x01, 0x30, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_droid_sans_mono_7x13 = {
|
||||
const struct GDS_FontDef Font_droid_sans_mono_7x13 = {
|
||||
Droid_Sans_Mono7x13,
|
||||
7,
|
||||
13,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Liberation_Mono13x21[ ] = {
|
||||
0x0C, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x03, 0x10, 0x00, 0x0F, 0x10, 0x0C, 0x3C, 0x18, 0x00, 0xE0, 0x0D, 0x00, 0x80, 0x07, 0x00, 0xE0, 0x01, 0x0C, 0x3C, 0x00, 0x00, 0x0F, 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_liberation_mono_13x21 = {
|
||||
const struct GDS_FontDef Font_liberation_mono_13x21 = {
|
||||
Liberation_Mono13x21,
|
||||
13,
|
||||
21,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Liberation_Mono17x30[] = {
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x30, 0x00, 0x3F, 0x00, 0x30, 0x00, 0xFC, 0x01, 0x30, 0x38, 0xE0, 0x07, 0x38, 0x38, 0x00, 0x3F, 0x1C, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3F, 0x00, 0x38, 0xE0, 0x07, 0x00, 0x38, 0xFC, 0x01, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_liberation_mono_17x30 = {
|
||||
const struct GDS_FontDef Font_liberation_mono_17x30 = {
|
||||
Liberation_Mono17x30,
|
||||
17,
|
||||
30,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Liberation_Mono9x15[ ] = {
|
||||
0x08, 0x00, 0x00, 0x30, 0x40, 0xC6, 0x41, 0x06, 0x67, 0x00, 0x18, 0x06, 0x07, 0xC6, 0x01, 0x30, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_liberation_mono_9x15 = {
|
||||
const struct GDS_FontDef Font_liberation_mono_9x15 = {
|
||||
Liberation_Mono9x15,
|
||||
9,
|
||||
15,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -214,7 +214,7 @@ static const uint8_t Square721_BT11x14[] = {
|
||||
0x06, 0x00, 0x00, 0xE8, 0x03, 0xB0, 0x04, 0xA0, 0x04, 0xA0, 0x04, 0xE0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char è
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_line_1 = {
|
||||
const struct GDS_FontDef Font_line_1 = {
|
||||
Square721_BT11x14,
|
||||
11,
|
||||
14,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -237,7 +237,7 @@ static const uint8_t Archivo_Narrow18x24[] = {
|
||||
0x0A, 0x00, 0x01, 0x00, 0x00, 0x1F, 0xE0, 0x38, 0xFF, 0xE0, 0x38, 0xFC, 0xF7, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0x3F, 0x38, 0xF0, 0x0F, 0x38, 0xFF, 0x01, 0x00, 0x1F, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char ÿ
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_line_2 = {
|
||||
const struct GDS_FontDef Font_line_2 = {
|
||||
Archivo_Narrow18x24,
|
||||
18,
|
||||
24,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -109,7 +109,7 @@ static const uint8_t Tarable7Seg_16x32[ ] = {
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_Tarable7Seg_16x32 = {
|
||||
const struct GDS_FontDef Font_Tarable7Seg_16x32 = {
|
||||
Tarable7Seg_16x32,
|
||||
16,
|
||||
32,
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <ssd13x6_font.h>
|
||||
#include <gds_font.h>
|
||||
|
||||
//WARNING: This Font Require X-GLCD Lib.
|
||||
// You can not use it with MikroE GLCD Lib.
|
||||
@@ -109,7 +109,7 @@ static const uint8_t Tarable7Seg_32x64[ ] = {
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Code for char
|
||||
};
|
||||
|
||||
const struct SSD13x6_FontDef Font_Tarable7Seg_32x64 = {
|
||||
const struct GDS_FontDef Font_Tarable7Seg_32x64 = {
|
||||
Tarable7Seg_32x64,
|
||||
32,
|
||||
64,
|
||||
@@ -1,123 +0,0 @@
|
||||
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/task.h>
|
||||
#include "ssd13x6.h"
|
||||
#include "ssd13x6_default_if.h"
|
||||
|
||||
static const int SSD13x6_SPI_Command_Mode = 0;
|
||||
static const int SSD13x6_SPI_Data_Mode = 1;
|
||||
|
||||
static spi_host_device_t SPIHost;
|
||||
static int DCPin;
|
||||
|
||||
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength );
|
||||
static bool SPIDefaultWriteCommand( struct SSD13x6_Device* DeviceHandle, SSDCmd Command );
|
||||
static bool SPIDefaultWriteData( struct SSD13x6_Device* DeviceHandle, const uint8_t* Data, size_t DataLength );
|
||||
static bool SPIDefaultReset( struct SSD13x6_Device* DeviceHandle );
|
||||
|
||||
bool SSD13x6_SPIMasterInitDefault( int SPI, int DC ) {
|
||||
SPIHost = SPI;
|
||||
DCPin = DC;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SSD13x6_SPIMasterAttachDisplayDefault( struct SSD13x6_Device* DeviceHandle, int Model, int Width, int Height, int CSPin, int RSTPin, int Speed ) {
|
||||
spi_device_interface_config_t SPIDeviceConfig;
|
||||
spi_device_handle_t SPIDeviceHandle;
|
||||
|
||||
NullCheck( DeviceHandle, return false );
|
||||
|
||||
if (CSPin >= 0) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( CSPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( CSPin, 0 ), return false );
|
||||
}
|
||||
|
||||
memset( &SPIDeviceConfig, 0, sizeof( spi_device_interface_config_t ) );
|
||||
|
||||
SPIDeviceConfig.clock_speed_hz = Speed > 0 ? Speed : SPI_MASTER_FREQ_8M;
|
||||
SPIDeviceConfig.spics_io_num = CSPin;
|
||||
SPIDeviceConfig.queue_size = 1;
|
||||
|
||||
if ( RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_direction( RSTPin, GPIO_MODE_OUTPUT ), return false );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( RSTPin, 0 ), return false );
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK_NONFATAL( spi_bus_add_device( SPIHost, &SPIDeviceConfig, &SPIDeviceHandle ), return false );
|
||||
|
||||
memset( DeviceHandle, 0, sizeof( struct SSD13x6_Device ) );
|
||||
DeviceHandle->Model = Model;
|
||||
|
||||
return SSD13x6_Init_SPI( DeviceHandle,
|
||||
Width,
|
||||
Height,
|
||||
RSTPin,
|
||||
CSPin,
|
||||
SPIDeviceHandle,
|
||||
SPIDefaultWriteCommand,
|
||||
SPIDefaultWriteData,
|
||||
SPIDefaultReset
|
||||
);
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) {
|
||||
spi_transaction_t SPITransaction = { 0 };
|
||||
|
||||
NullCheck( SPIHandle, return false );
|
||||
NullCheck( Data, return false );
|
||||
|
||||
if ( DataLength > 0 ) {
|
||||
gpio_set_level( DCPin, WriteMode );
|
||||
|
||||
SPITransaction.length = DataLength * 8;
|
||||
SPITransaction.tx_buffer = Data;
|
||||
|
||||
// only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes)
|
||||
ESP_ERROR_CHECK_NONFATAL( spi_device_polling_transmit(SPIHandle, &SPITransaction), return false );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteCommand( struct SSD13x6_Device* DeviceHandle, SSDCmd Command ) {
|
||||
static uint8_t CommandByte = 0;
|
||||
|
||||
NullCheck( DeviceHandle, return false );
|
||||
NullCheck( DeviceHandle->SPIHandle, return false );
|
||||
|
||||
CommandByte = Command;
|
||||
|
||||
return SPIDefaultWriteBytes( DeviceHandle->SPIHandle, SSD13x6_SPI_Command_Mode, &CommandByte, 1 );
|
||||
}
|
||||
|
||||
static bool SPIDefaultWriteData( struct SSD13x6_Device* DeviceHandle, const uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( DeviceHandle, return false );
|
||||
NullCheck( DeviceHandle->SPIHandle, return false );
|
||||
|
||||
return SPIDefaultWriteBytes( DeviceHandle->SPIHandle, SSD13x6_SPI_Data_Mode, Data, DataLength );
|
||||
}
|
||||
|
||||
static bool SPIDefaultReset( struct SSD13x6_Device* DeviceHandle ) {
|
||||
NullCheck( DeviceHandle, return false );
|
||||
|
||||
if ( DeviceHandle->RSTPin >= 0 ) {
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( DeviceHandle->RSTPin, 0 ), return false );
|
||||
vTaskDelay( pdMS_TO_TICKS( 100 ) );
|
||||
ESP_ERROR_CHECK_NONFATAL( gpio_set_level( DeviceHandle->RSTPin, 1 ), return false );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <esp_heap_caps.h>
|
||||
|
||||
#include "ssd13x6.h"
|
||||
|
||||
#define SHADOW_BUFFER
|
||||
|
||||
// used by both but different
|
||||
static uint8_t SSDCmd_Set_Display_Start_Line;
|
||||
static uint8_t SSDCmd_Set_Display_Offset;
|
||||
static uint8_t SSDCmd_Set_Column_Address;
|
||||
static uint8_t SSDCmd_Set_Display_CLK;
|
||||
static uint8_t SSDCmd_Set_Page_Address;
|
||||
|
||||
// misc boundaries
|
||||
static uint8_t SSD13x6_Max_Col;
|
||||
static const uint8_t SSD13x6_Max_Row = 7;
|
||||
|
||||
static bool SSD13x6_Init( struct SSD13x6_Device* DeviceHandle, int Width, int Height );
|
||||
|
||||
int SSD13x6_GetCaps( struct SSD13x6_Device* DeviceHandle ) {
|
||||
if (DeviceHandle->Model == SH1106) return 0;
|
||||
else return CAPS_COLUMN_RANGE | CAPS_PAGE_RANGE | CAPS_ADDRESS_VERTICAL;
|
||||
}
|
||||
|
||||
bool SSD13x6_WriteCommand( struct SSD13x6_Device* DeviceHandle, SSDCmd SSDCommand ) {
|
||||
NullCheck( DeviceHandle->WriteCommand, return false );
|
||||
return ( DeviceHandle->WriteCommand ) ( DeviceHandle, SSDCommand );
|
||||
}
|
||||
|
||||
bool SSD13x6_WriteData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( DeviceHandle->WriteData, return false );
|
||||
return ( DeviceHandle->WriteData ) ( DeviceHandle, Data, DataLength );
|
||||
}
|
||||
|
||||
void SSD13x6_SetMuxRatio( struct SSD13x6_Device* DeviceHandle, uint8_t Ratio ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xA8 );
|
||||
SSD13x6_WriteCommand( DeviceHandle, Ratio );
|
||||
}
|
||||
|
||||
void SSD13x6_SetDisplayOffset( struct SSD13x6_Device* DeviceHandle, uint8_t Offset ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_Offset );
|
||||
SSD13x6_WriteCommand( DeviceHandle, Offset );
|
||||
}
|
||||
|
||||
void SSD13x6_SetDisplayStartLine( struct SSD13x6_Device* DeviceHandle, int Line ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_Start_Line + ( uint32_t ) ( Line & 0x1F ) );
|
||||
}
|
||||
|
||||
void SSD13x6_SetContrast( struct SSD13x6_Device* DeviceHandle, uint8_t Contrast ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0x81 );
|
||||
SSD13x6_WriteCommand( DeviceHandle, Contrast );
|
||||
}
|
||||
|
||||
void SSD13x6_EnableDisplayRAM( struct SSD13x6_Device* DeviceHandle ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xA4 );
|
||||
}
|
||||
|
||||
void SSD13x6_DisableDisplayRAM( struct SSD13x6_Device* DeviceHandle ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xA5 );
|
||||
}
|
||||
|
||||
void SSD13x6_SetInverted( struct SSD13x6_Device* DeviceHandle, bool Inverted ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, Inverted ? 0xA7 : 0xA6 );
|
||||
}
|
||||
|
||||
void SSD13x6_SetDisplayClocks( struct SSD13x6_Device* DeviceHandle, uint32_t DisplayClockDivider, uint32_t OSCFrequency ) {
|
||||
DisplayClockDivider&= 0x0F;
|
||||
OSCFrequency&= 0x0F;
|
||||
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Display_CLK );
|
||||
SSD13x6_WriteCommand( DeviceHandle, ( ( OSCFrequency << 4 ) | DisplayClockDivider ) );
|
||||
}
|
||||
|
||||
void SSD13x6_DisplayOn( struct SSD13x6_Device* DeviceHandle ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xAF );
|
||||
}
|
||||
|
||||
void SSD13x6_DisplayOff( struct SSD13x6_Device* DeviceHandle ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xAE );
|
||||
}
|
||||
|
||||
void SSD132x_ReMap( struct SSD13x6_Device* DeviceHandle ) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xA0 );
|
||||
SSD13x6_WriteCommand( DeviceHandle, DeviceHandle->ReMap );
|
||||
}
|
||||
|
||||
void SSD13x6_SetDisplayAddressMode( struct SSD13x6_Device* DeviceHandle, SSD13x6_AddressMode AddressMode ) {
|
||||
switch (DeviceHandle->Model) {
|
||||
case SH1106:
|
||||
// does not exist on SH1106
|
||||
break;
|
||||
case SSD1306:
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0x20 );
|
||||
SSD13x6_WriteCommand( DeviceHandle, AddressMode );
|
||||
break;
|
||||
case SSD1326:
|
||||
DeviceHandle->ReMap = (AddressMode == AddressMode_Horizontal) ? (DeviceHandle->ReMap & ~0x80) : (DeviceHandle->ReMap | 0x80);
|
||||
SSD132x_ReMap(DeviceHandle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SSD13x6_Update( struct SSD13x6_Device* DeviceHandle ) {
|
||||
#ifdef SHADOW_BUFFER
|
||||
// not sure the compiler does not have to redo all calculation in for loops, so local it is
|
||||
int width = DeviceHandle->Width, rows = DeviceHandle->Height / 8;
|
||||
uint8_t *optr = DeviceHandle->Shadowbuffer, *iptr = DeviceHandle->Framebuffer;
|
||||
|
||||
// by row, find first and last columns that have been updated
|
||||
for (int r = 0; r < rows; r++) {
|
||||
uint8_t first = 0, last;
|
||||
for (int c = 0; c < width; c++) {
|
||||
if (*iptr != *optr) {
|
||||
if (!first) first = c + 1;
|
||||
last = c ;
|
||||
}
|
||||
*optr++ = *iptr++;
|
||||
}
|
||||
|
||||
// now update the display by "byte rows"
|
||||
if (first--) {
|
||||
SSD13x6_SetColumnAddress( DeviceHandle, first, last );
|
||||
SSD13x6_SetPageAddress( DeviceHandle, r, r);
|
||||
SSD13x6_WriteData( DeviceHandle, DeviceHandle->Shadowbuffer + r*width + first, last - first + 1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (DeviceHandle->Model == SH1106) {
|
||||
// SH1106 requires a page-by-page update and has no end Page/Column
|
||||
for (int i = 0; i < DeviceHandle->Height / 8 ; i++) {
|
||||
SSD13x6_SetPageAddress( DeviceHandle, i, 0);
|
||||
SSD13x6_SetColumnAddress( DeviceHandle, 0, 0);
|
||||
SSD13x6_WriteData( DeviceHandle, DeviceHandle->Framebuffer + i*DeviceHandle->Width, DeviceHandle->Width );
|
||||
}
|
||||
} else {
|
||||
// others have an automatic counter and end Page/Column
|
||||
SSD13x6_SetColumnAddress( DeviceHandle, 0, DeviceHandle->Width - 1);
|
||||
SSD13x6_SetPageAddress( DeviceHandle, 0, DeviceHandle->Height / 8 - 1);
|
||||
SSD13x6_WriteData( DeviceHandle, DeviceHandle->Framebuffer, DeviceHandle->FramebufferSize );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SSD13x6_WriteRawData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength ) {
|
||||
NullCheck( Data, return );
|
||||
DataLength = DataLength > DeviceHandle->FramebufferSize ? DeviceHandle->FramebufferSize : DataLength;
|
||||
if ( DataLength > 0 ) SSD13x6_WriteData( DeviceHandle, Data, DataLength );
|
||||
}
|
||||
|
||||
void SSD13x6_SetHFlip( struct SSD13x6_Device* DeviceHandle, bool On ) {
|
||||
switch (DeviceHandle->Model) {
|
||||
case SH1106:
|
||||
case SSD1306:
|
||||
SSD13x6_WriteCommand( DeviceHandle, On ? 0xA1 : 0xA0 );
|
||||
break;
|
||||
case SSD1326:
|
||||
DeviceHandle->ReMap = On ? (DeviceHandle->ReMap | 0x01) : (DeviceHandle->ReMap & ~0x01);
|
||||
SSD132x_ReMap(DeviceHandle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SSD13x6_SetVFlip( struct SSD13x6_Device* DeviceHandle, bool On ) {
|
||||
switch (DeviceHandle->Model) {
|
||||
case SH1106:
|
||||
case SSD1306:
|
||||
SSD13x6_WriteCommand( DeviceHandle, On ? 0xC8 : 0xC0 );
|
||||
break;
|
||||
case SSD1326:
|
||||
DeviceHandle->ReMap = On ? (DeviceHandle->ReMap | 0x05) : (DeviceHandle->ReMap & ~0x05);
|
||||
SSD132x_ReMap( DeviceHandle );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SSD13x6_SetColumnAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End ) {
|
||||
CheckBounds( Start > SSD13x6_Max_Col, return );
|
||||
CheckBounds( End > SSD13x6_Max_Col, return );
|
||||
|
||||
// on SH1106, there is no "end column"
|
||||
if (DeviceHandle->Model == SH1106) {
|
||||
// well, unfortunately this driver is 132 colums but most displays are 128...
|
||||
if (DeviceHandle->Width != 132) Start += 2;
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0x10 | (Start >> 4) );
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0x00 | (Start & 0x0f) );
|
||||
} else {
|
||||
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Column_Address );
|
||||
SSD13x6_WriteCommand( DeviceHandle, Start );
|
||||
SSD13x6_WriteCommand( DeviceHandle, End );
|
||||
}
|
||||
}
|
||||
|
||||
void SSD13x6_SetPageAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End ) {
|
||||
NullCheck( DeviceHandle, return );
|
||||
|
||||
CheckBounds( Start > SSD13x6_Max_Row, return );
|
||||
CheckBounds( End > SSD13x6_Max_Row, return );
|
||||
|
||||
// on SH1106, there is no "end page"
|
||||
if (DeviceHandle->Model == SH1106) {
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xB0 | Start );
|
||||
} else {
|
||||
// in case of SSD1326, this is sub-optimal as it can address by line, not by page
|
||||
if (DeviceHandle->Model != SSD1306) {
|
||||
Start *= 8;
|
||||
End = (End + 1) * 8 - 1;
|
||||
}
|
||||
|
||||
SSD13x6_WriteCommand( DeviceHandle, SSDCmd_Set_Page_Address );
|
||||
SSD13x6_WriteCommand( DeviceHandle, Start );
|
||||
SSD13x6_WriteCommand( DeviceHandle, End );
|
||||
}
|
||||
}
|
||||
|
||||
bool SSD13x6_HWReset( struct SSD13x6_Device* DeviceHandle ) {
|
||||
NullCheck( DeviceHandle, return 0 );
|
||||
|
||||
if ( DeviceHandle->Reset != NULL ) {
|
||||
return ( DeviceHandle->Reset ) ( DeviceHandle );
|
||||
}
|
||||
|
||||
/* This should always return true if there is no reset callback as
|
||||
* no error would have occurred during the non existant reset.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SSD13x6_Init( struct SSD13x6_Device* DeviceHandle, int Width, int Height ) {
|
||||
DeviceHandle->Width = Width;
|
||||
DeviceHandle->Height = Height;
|
||||
|
||||
#ifdef SHADOW_BUFFER
|
||||
DeviceHandle->Shadowbuffer = heap_caps_malloc( DeviceHandle->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||
memset( DeviceHandle->Shadowbuffer, 0xFF, DeviceHandle->FramebufferSize );
|
||||
#endif
|
||||
|
||||
SSD13x6_HWReset( DeviceHandle );
|
||||
SSD13x6_DisplayOff( DeviceHandle );
|
||||
|
||||
if (DeviceHandle->Model == SSD1306 || DeviceHandle->Model == SH1106) {
|
||||
SSDCmd_Set_Display_Start_Line = 0x40;
|
||||
SSDCmd_Set_Display_Offset = 0xD3;
|
||||
SSDCmd_Set_Column_Address = 0x21,
|
||||
SSDCmd_Set_Display_CLK = 0xD5;
|
||||
SSDCmd_Set_Page_Address = 0x22;
|
||||
SSD13x6_Max_Col = 127;
|
||||
|
||||
if (DeviceHandle->Model == SSD1306) {
|
||||
// charge pump regulator, do direct init
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0x8D );
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0x14 );
|
||||
|
||||
// COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something difference
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xDA );
|
||||
SSD13x6_WriteCommand( DeviceHandle, ((Height == 64 ? 1 : 0) << 4) | (0 < 5) );
|
||||
|
||||
} else {
|
||||
// charge pump regulator, do direct init
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xAD );
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0x8B );
|
||||
|
||||
// COM pins HW config (alternative:EN) - some display might need something difference
|
||||
SSD13x6_WriteCommand( DeviceHandle, 0xDA );
|
||||
SSD13x6_WriteCommand( DeviceHandle, 1 << 4);
|
||||
}
|
||||
|
||||
} else if (DeviceHandle->Model == SSD1326) {
|
||||
SSDCmd_Set_Display_Start_Line = 0xA1;
|
||||
SSDCmd_Set_Display_Offset = 0xA2;
|
||||
SSDCmd_Set_Column_Address = 0x15;
|
||||
SSDCmd_Set_Display_CLK = 0xB3;
|
||||
SSDCmd_Set_Page_Address = 0x75; // not really a page but a row
|
||||
|
||||
SSD13x6_Max_Col = 255;
|
||||
|
||||
// no gray scale
|
||||
DeviceHandle->ReMap |= 0x10;
|
||||
SSD132x_ReMap( DeviceHandle );
|
||||
}
|
||||
|
||||
SSD13x6_SetMuxRatio( DeviceHandle, Height - 1 );
|
||||
SSD13x6_SetDisplayOffset( DeviceHandle, 0x00 );
|
||||
SSD13x6_SetDisplayStartLine( DeviceHandle, 0 );
|
||||
SSD13x6_SetContrast( DeviceHandle, 0x7F );
|
||||
SSD13x6_DisableDisplayRAM( DeviceHandle );
|
||||
SSD13x6_SetVFlip( DeviceHandle, false );
|
||||
SSD13x6_SetHFlip( DeviceHandle, false );
|
||||
SSD13x6_SetInverted( DeviceHandle, false );
|
||||
SSD13x6_SetDisplayClocks( DeviceHandle, 0, 8 );
|
||||
SSD13x6_SetDisplayAddressMode( DeviceHandle, AddressMode_Horizontal );
|
||||
SSD13x6_SetColumnAddress( DeviceHandle, 0, DeviceHandle->Width - 1 );
|
||||
SSD13x6_SetPageAddress( DeviceHandle, 0, ( DeviceHandle->Height / 8 ) - 1 );
|
||||
SSD13x6_EnableDisplayRAM( DeviceHandle );
|
||||
SSD13x6_DisplayOn( DeviceHandle );
|
||||
SSD13x6_Update( DeviceHandle );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SSD13x6_Init_I2C( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int I2CAddress, int ResetPin, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset ) {
|
||||
NullCheck( DeviceHandle, return false );
|
||||
NullCheck( WriteCommand, return false );
|
||||
NullCheck( WriteData, return false );
|
||||
|
||||
DeviceHandle->WriteCommand = WriteCommand;
|
||||
DeviceHandle->WriteData = WriteData;
|
||||
DeviceHandle->Reset = Reset;
|
||||
DeviceHandle->Address = I2CAddress;
|
||||
DeviceHandle->RSTPin = ResetPin;
|
||||
|
||||
DeviceHandle->FramebufferSize = ( Width * Height ) / 8;
|
||||
DeviceHandle->Framebuffer = calloc( 1, DeviceHandle->FramebufferSize );
|
||||
NullCheck( DeviceHandle->Framebuffer, return false );
|
||||
|
||||
return SSD13x6_Init( DeviceHandle, Width, Height );
|
||||
}
|
||||
|
||||
bool SSD13x6_Init_SPI( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int ResetPin, int CSPin, spi_device_handle_t SPIHandle, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset ) {
|
||||
NullCheck( DeviceHandle, return false );
|
||||
NullCheck( WriteCommand, return false );
|
||||
NullCheck( WriteData, return false );
|
||||
|
||||
DeviceHandle->WriteCommand = WriteCommand;
|
||||
DeviceHandle->WriteData = WriteData;
|
||||
DeviceHandle->Reset = Reset;
|
||||
DeviceHandle->SPIHandle = SPIHandle;
|
||||
DeviceHandle->RSTPin = ResetPin;
|
||||
DeviceHandle->CSPin = CSPin;
|
||||
|
||||
DeviceHandle->FramebufferSize = ( Width * Height ) / 8;
|
||||
#ifdef SHADOW_BUFFER
|
||||
DeviceHandle->Framebuffer = calloc( 1, DeviceHandle->FramebufferSize );
|
||||
#else
|
||||
DeviceHandle->Framebuffer = heap_caps_calloc( 1, DeviceHandle->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||
#endif
|
||||
NullCheck( DeviceHandle->Framebuffer, return false );
|
||||
|
||||
return SSD13x6_Init( DeviceHandle, Width, Height );
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
#ifndef _SSD13X6_H_
|
||||
#define _SSD13X6_H_
|
||||
|
||||
/* For uint(X)_t */
|
||||
#include <stdint.h>
|
||||
|
||||
/* For booooool */
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "ssd13x6_err.h"
|
||||
|
||||
#define SSD_ALWAYS_INLINE __attribute__( ( always_inline ) )
|
||||
|
||||
#define CAPS_COLUMN_RANGE 0x01
|
||||
#define CAPS_PAGE_RANGE 0x02
|
||||
#define CAPS_ADDRESS_VERTICAL 0x04
|
||||
|
||||
#if ! defined BIT
|
||||
#define BIT( n ) ( 1 << n )
|
||||
#endif
|
||||
|
||||
typedef uint8_t SSDCmd;
|
||||
|
||||
typedef enum {
|
||||
AddressMode_Horizontal = 0,
|
||||
AddressMode_Vertical,
|
||||
AddressMode_Page,
|
||||
AddressMode_Invalid
|
||||
} SSD13x6_AddressMode;
|
||||
|
||||
struct SSD13x6_Device;
|
||||
|
||||
/*
|
||||
* These can optionally return a succeed/fail but are as of yet unused in the driver.
|
||||
*/
|
||||
typedef bool ( *WriteCommandProc ) ( struct SSD13x6_Device* DeviceHandle, SSDCmd Command );
|
||||
typedef bool ( *WriteDataProc ) ( struct SSD13x6_Device* DeviceHandle, const uint8_t* Data, size_t DataLength );
|
||||
typedef bool ( *ResetProc ) ( struct SSD13x6_Device* DeviceHandle );
|
||||
|
||||
struct spi_device_t;
|
||||
typedef struct spi_device_t* spi_device_handle_t;
|
||||
|
||||
struct SSD13x6_FontDef;
|
||||
|
||||
struct SSD13x6_Device {
|
||||
/* I2C Specific */
|
||||
int Address;
|
||||
|
||||
/* SPI Specific */
|
||||
spi_device_handle_t SPIHandle;
|
||||
int RSTPin;
|
||||
int CSPin;
|
||||
|
||||
/* Everything else */
|
||||
int Width;
|
||||
int Height;
|
||||
|
||||
enum { SSD1306, SSD1326, SH1106 } Model;
|
||||
uint8_t ReMap;
|
||||
uint8_t* Framebuffer, *Shadowbuffer;
|
||||
int FramebufferSize;
|
||||
|
||||
WriteCommandProc WriteCommand;
|
||||
WriteDataProc WriteData;
|
||||
ResetProc Reset;
|
||||
|
||||
const struct SSD13x6_FontDef* Font;
|
||||
bool FontForceProportional;
|
||||
bool FontForceMonospace;
|
||||
};
|
||||
|
||||
void SSD13x6_SetMuxRatio( struct SSD13x6_Device* DeviceHandle, uint8_t Ratio );
|
||||
void SSD13x6_SetDisplayOffset( struct SSD13x6_Device* DeviceHandle, uint8_t Offset );
|
||||
void SSD13x6_SetDisplayStartLines( struct SSD13x6_Device* DeviceHandle );
|
||||
|
||||
void SSD13x6_SetSegmentRemap( struct SSD13x6_Device* DeviceHandle, bool Remap );
|
||||
|
||||
void SSD13x6_SetContrast( struct SSD13x6_Device* DeviceHandle, uint8_t Contrast );
|
||||
void SSD13x6_EnableDisplayRAM( struct SSD13x6_Device* DeviceHandle );
|
||||
void SSD13x6_DisableDisplayRAM( struct SSD13x6_Device* DeviceHandle );
|
||||
void SSD13x6_SetInverted( struct SSD13x6_Device* DeviceHandle, bool Inverted );
|
||||
void SSD13x6_SetHFlip( struct SSD13x6_Device* DeviceHandle, bool On );
|
||||
void SSD13x6_SetVFlip( struct SSD13x6_Device* DeviceHandle, bool On );
|
||||
void SSD13x6_DisplayOn( struct SSD13x6_Device* DeviceHandle );
|
||||
void SSD13x6_DisplayOff( struct SSD13x6_Device* DeviceHandle );
|
||||
void SSD13x6_SetDisplayAddressMode( struct SSD13x6_Device* DeviceHandle, SSD13x6_AddressMode AddressMode );
|
||||
void SSD13x6_Update( struct SSD13x6_Device* DeviceHandle );
|
||||
void SSD13x6_SetDisplayClocks( struct SSD13x6_Device* DeviceHandle, uint32_t DisplayClockDivider, uint32_t OSCFrequency );
|
||||
void SSD13x6_SetColumnAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End );
|
||||
void SSD13x6_SetPageAddress( struct SSD13x6_Device* DeviceHandle, uint8_t Start, uint8_t End );
|
||||
bool SSD13x6_HWReset( struct SSD13x6_Device* DeviceHandle );
|
||||
bool SSD13x6_Init_I2C( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int I2CAddress, int ResetPin, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset );
|
||||
bool SSD13x6_Init_SPI( struct SSD13x6_Device* DeviceHandle, int Width, int Height, int ResetPin, int CSPin, spi_device_handle_t SPIHandle, WriteCommandProc WriteCommand, WriteDataProc WriteData, ResetProc Reset );
|
||||
int SSD13x6_GetCaps( struct SSD13x6_Device* DeviceHandle );
|
||||
|
||||
void SSD13x6_WriteRawData( struct SSD13x6_Device* DeviceHandle, uint8_t* Data, size_t DataLength );
|
||||
|
||||
#endif
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef _SSD13x6_DEFAULT_IF_H_
|
||||
#define _SSD13x6_DEFAULT_IF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool SSD13x6_I2CMasterInitDefault( int PortNumber, int SDA, int SCL );
|
||||
bool SSD13x6_I2CMasterAttachDisplayDefault( struct SSD13x6_Device* DisplayHandle, int Model, int Width, int Height, int I2CAddress, int RSTPin );
|
||||
|
||||
bool SSD13x6_SPIMasterInitDefault( int SPI, int DC);
|
||||
bool SSD13x6_SPIMasterAttachDisplayDefault( struct SSD13x6_Device* DeviceHandle, int Model, int Width, int Height, int CSPin, int RSTPin, int Speed );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,253 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2018 Tara Keeling
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <esp_attr.h>
|
||||
|
||||
#include "ssd13x6.h"
|
||||
#include "ssd13x6_draw.h"
|
||||
|
||||
#undef NullCheck
|
||||
#define NullCheck(X,Y)
|
||||
|
||||
__attribute__( ( always_inline ) ) static inline bool IsPixelVisible( struct SSD13x6_Device* DeviceHandle, int x, int y ) {
|
||||
bool Result = (
|
||||
( x >= 0 ) &&
|
||||
( x < DeviceHandle->Width ) &&
|
||||
( y >= 0 ) &&
|
||||
( y < DeviceHandle->Height )
|
||||
) ? true : false;
|
||||
|
||||
#if CONFIG_SSD13x6_CLIPDEBUG > 0
|
||||
if ( Result == false ) {
|
||||
ClipDebug( x, y );
|
||||
}
|
||||
#endif
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
__attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b ) {
|
||||
int Temp = *b;
|
||||
|
||||
*b = *a;
|
||||
*a = Temp;
|
||||
}
|
||||
|
||||
inline void IRAM_ATTR SSD13x6_DrawPixelFast( struct SSD13x6_Device* DeviceHandle, int X, int Y, int Color ) {
|
||||
uint32_t YBit = ( Y & 0x07 );
|
||||
uint8_t* FBOffset = NULL;
|
||||
|
||||
/*
|
||||
* We only need to modify the Y coordinate since the pitch
|
||||
* of the screen is the same as the width.
|
||||
* Dividing Y by 8 gives us which row the pixel is in but not
|
||||
* the bit position.
|
||||
*/
|
||||
Y>>= 3;
|
||||
|
||||
FBOffset = DeviceHandle->Framebuffer + ( ( Y * DeviceHandle->Width ) + X );
|
||||
|
||||
if ( Color == SSD_COLOR_XOR ) {
|
||||
*FBOffset ^= BIT( YBit );
|
||||
} else {
|
||||
*FBOffset = ( Color == SSD_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SSD13x6_DrawPixel( struct SSD13x6_Device* DeviceHandle, int x, int y, int Color ) {
|
||||
NullCheck( DeviceHandle, return );
|
||||
|
||||
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
|
||||
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SSD13x6_DrawHLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Width, int Color ) {
|
||||
int XEnd = x + Width;
|
||||
|
||||
NullCheck( DeviceHandle, return );
|
||||
NullCheck( DeviceHandle->Framebuffer, return );
|
||||
|
||||
for ( ; x < XEnd; x++ ) {
|
||||
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
|
||||
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SSD13x6_DrawVLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Height, int Color ) {
|
||||
int YEnd = y + Height;
|
||||
|
||||
NullCheck( DeviceHandle, return );
|
||||
NullCheck( DeviceHandle->Framebuffer, return );
|
||||
|
||||
for ( ; y < YEnd; y++ ) {
|
||||
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
|
||||
SSD13x6_DrawPixel( DeviceHandle, x, y, Color );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void IRAM_ATTR DrawWideLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color ) {
|
||||
int dx = ( x1 - x0 );
|
||||
int dy = ( y1 - y0 );
|
||||
int Error = 0;
|
||||
int Incr = 1;
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
|
||||
if ( dy < 0 ) {
|
||||
Incr = -1;
|
||||
dy = -dy;
|
||||
}
|
||||
|
||||
Error = ( dy * 2 ) - dx;
|
||||
|
||||
for ( ; x < x1; x++ ) {
|
||||
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
|
||||
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
Error-= ( dx * 2 );
|
||||
y+= Incr;
|
||||
}
|
||||
|
||||
Error+= ( dy * 2 );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void IRAM_ATTR DrawTallLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color ) {
|
||||
int dx = ( x1 - x0 );
|
||||
int dy = ( y1 - y0 );
|
||||
int Error = 0;
|
||||
int Incr = 1;
|
||||
int x = x0;
|
||||
int y = y0;
|
||||
|
||||
if ( dx < 0 ) {
|
||||
Incr = -1;
|
||||
dx = -dx;
|
||||
}
|
||||
|
||||
Error = ( dx * 2 ) - dy;
|
||||
|
||||
for ( ; y < y1; y++ ) {
|
||||
if ( IsPixelVisible( DeviceHandle, x, y ) == true ) {
|
||||
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
Error-= ( dy * 2 );
|
||||
x+= Incr;
|
||||
}
|
||||
|
||||
Error+= ( dx * 2 );
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SSD13x6_DrawLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color ) {
|
||||
NullCheck( DeviceHandle, return );
|
||||
NullCheck( DeviceHandle->Framebuffer, return );
|
||||
|
||||
if ( x0 == x1 ) {
|
||||
SSD13x6_DrawVLine( DeviceHandle, x0, y0, ( y1 - y0 ), Color );
|
||||
} else if ( y0 == y1 ) {
|
||||
SSD13x6_DrawHLine( DeviceHandle, x0, y0, ( x1 - x0 ), Color );
|
||||
} else {
|
||||
if ( abs( x1 - x0 ) > abs( y1 - y0 ) ) {
|
||||
/* Wide ( run > rise ) */
|
||||
if ( x0 > x1 ) {
|
||||
SwapInt( &x0, &x1 );
|
||||
SwapInt( &y0, &y1 );
|
||||
}
|
||||
|
||||
DrawWideLine( DeviceHandle, x0, y0, x1, y1, Color );
|
||||
} else {
|
||||
/* Tall ( rise > run ) */
|
||||
if ( y0 > y1 ) {
|
||||
SwapInt( &y0, &y1 );
|
||||
SwapInt( &x0, &x1 );
|
||||
}
|
||||
|
||||
DrawTallLine( DeviceHandle, x0, y0, x1, y1, Color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SSD13x6_DrawBox( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color, bool Fill ) {
|
||||
int Width = ( x2 - x1 );
|
||||
int Height = ( y2 - y1 );
|
||||
|
||||
NullCheck( DeviceHandle, return );
|
||||
NullCheck( DeviceHandle->Framebuffer, return );
|
||||
|
||||
if ( Fill == false ) {
|
||||
/* Top side */
|
||||
SSD13x6_DrawHLine( DeviceHandle, x1, y1, Width, Color );
|
||||
|
||||
/* Bottom side */
|
||||
SSD13x6_DrawHLine( DeviceHandle, x1, y1 + Height, Width, Color );
|
||||
|
||||
/* Left side */
|
||||
SSD13x6_DrawVLine( DeviceHandle, x1, y1, Height, Color );
|
||||
|
||||
/* Right side */
|
||||
SSD13x6_DrawVLine( DeviceHandle, x1 + Width, y1, Height, Color );
|
||||
} else {
|
||||
/* Fill the box by drawing horizontal lines */
|
||||
for ( ; y1 <= y2; y1++ ) {
|
||||
SSD13x6_DrawHLine( DeviceHandle, x1, y1, Width, Color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSD13x6_Clear( struct SSD13x6_Device* DeviceHandle, int Color ) {
|
||||
NullCheck( DeviceHandle, return );
|
||||
NullCheck( DeviceHandle->Framebuffer, return );
|
||||
|
||||
memset( DeviceHandle->Framebuffer, Color, DeviceHandle->FramebufferSize );
|
||||
}
|
||||
|
||||
void SSD13x6_ClearWindow( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color ) {
|
||||
NullCheck( DeviceHandle, return );
|
||||
NullCheck( DeviceHandle->Framebuffer, return );
|
||||
|
||||
/*
|
||||
int xr = ((x1 - 1) / 8) + 1 ) * 8;
|
||||
int xl = (x2 / 8) * 8;
|
||||
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x < xr; x++) SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color);
|
||||
if (xl > xr) memset( DeviceHandle->Framebuffer + (y / 8) * DeviceHandle->Width + xr, 0, xl - xr );
|
||||
for (int x = xl; x <= x2; x++) SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color);
|
||||
}
|
||||
|
||||
return;
|
||||
*/
|
||||
|
||||
// cheap optimization on boundaries
|
||||
if (x1 == 0 && x2 == DeviceHandle->Width - 1 && y1 % 8 == 0 && (y2 + 1) % 8 == 0) {
|
||||
memset( DeviceHandle->Framebuffer + (y1 / 8) * DeviceHandle->Width, 0, (y2 - y1 + 1) / 8 * DeviceHandle->Width );
|
||||
} else {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
SSD13x6_DrawPixelFast( DeviceHandle, x, y, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#ifndef _SSD13x6_DRAW_H_
|
||||
#define _SSD13x6_DRAW_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define SSD13x6_CLIPDEBUG_NONE 0
|
||||
#define SSD13x6_CLIPDEBUG_WARNING 1
|
||||
#define SSD13x6_CLIPDEBUG_ERROR 2
|
||||
|
||||
#if CONFIG_SSD13x6_CLIPDEBUG == SSD13x6_CLIPDEBUG_NONE
|
||||
/*
|
||||
* Clip silently with no console output.
|
||||
*/
|
||||
#define ClipDebug( x, y )
|
||||
#elif CONFIG_SSD13x6_CLIPDEBUG == SSD13x6_CLIPDEBUG_WARNING
|
||||
/*
|
||||
* Log clipping to the console as a warning.
|
||||
*/
|
||||
#define ClipDebug( x, y ) { \
|
||||
ESP_LOGW( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED", __LINE__, x, y ); \
|
||||
}
|
||||
#elif CONFIG_SSD13x6_CLIPDEBUG == SSD13x6_CLIPDEBUG_ERROR
|
||||
/*
|
||||
* Log clipping as an error to the console.
|
||||
* Also invokes an abort with stack trace.
|
||||
*/
|
||||
#define ClipDebug( x, y ) { \
|
||||
ESP_LOGE( __FUNCTION__, "Line %d: Pixel at %d, %d CLIPPED, ABORT", __LINE__, x, y ); \
|
||||
abort( ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SSD_COLOR_BLACK 0
|
||||
#define SSD_COLOR_WHITE 1
|
||||
#define SSD_COLOR_XOR 2
|
||||
|
||||
void SSD13x6_Clear( struct SSD13x6_Device* DeviceHandle, int Color );
|
||||
void SSD13x6_ClearWindow( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color );
|
||||
void SSD13x6_DrawPixel( struct SSD13x6_Device* DeviceHandle, int X, int Y, int Color );
|
||||
void SSD13x6_DrawPixelFast( struct SSD13x6_Device* DeviceHandle, int X, int Y, int Color );
|
||||
void SSD13x6_DrawHLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Width, int Color );
|
||||
void SSD13x6_DrawVLine( struct SSD13x6_Device* DeviceHandle, int x, int y, int Height, int Color );
|
||||
void SSD13x6_DrawLine( struct SSD13x6_Device* DeviceHandle, int x0, int y0, int x1, int y1, int Color );
|
||||
void SSD13x6_DrawBox( struct SSD13x6_Device* DeviceHandle, int x1, int y1, int x2, int y2, int Color, bool Fill );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,91 +0,0 @@
|
||||
#ifndef _SSD13x6_FONT_H_
|
||||
#define _SSD13x6_FONT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct SSD13x6_Device;
|
||||
|
||||
/*
|
||||
* X-GLCD Font format:
|
||||
*
|
||||
* First byte of glyph is it's width in pixels.
|
||||
* Each data byte represents 8 pixels going down from top to bottom.
|
||||
*
|
||||
* Example glyph layout for a 16x16 font
|
||||
* 'a': [Glyph width][Pixel column 0][Pixel column 1] where the number of pixel columns is the font height divided by 8
|
||||
* 'b': [Glyph width][Pixel column 0][Pixel column 1]...
|
||||
* 'c': And so on...
|
||||
*/
|
||||
|
||||
struct SSD13x6_FontDef {
|
||||
const uint8_t* FontData;
|
||||
|
||||
int Width;
|
||||
int Height;
|
||||
|
||||
int StartChar;
|
||||
int EndChar;
|
||||
|
||||
bool Monospace;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TextAnchor_East = 0,
|
||||
TextAnchor_West,
|
||||
TextAnchor_North,
|
||||
TextAnchor_South,
|
||||
TextAnchor_NorthEast,
|
||||
TextAnchor_NorthWest,
|
||||
TextAnchor_SouthEast,
|
||||
TextAnchor_SouthWest,
|
||||
TextAnchor_Center
|
||||
} TextAnchor;
|
||||
|
||||
bool SSD13x6_SetFont( struct SSD13x6_Device* Display, const struct SSD13x6_FontDef* Font );
|
||||
|
||||
void SSD13x6_FontForceProportional( struct SSD13x6_Device* Display, bool Force );
|
||||
void SSD13x6_FontForceMonospace( struct SSD13x6_Device* Display, bool Force );
|
||||
|
||||
int SSD13x6_FontGetWidth( struct SSD13x6_Device* Display );
|
||||
int SSD13x6_FontGetHeight( struct SSD13x6_Device* Display );
|
||||
|
||||
int SSD13x6_FontGetMaxCharsPerRow( struct SSD13x6_Device* Display );
|
||||
int SSD13x6_FontGetMaxCharsPerColumn( struct SSD13x6_Device* Display );
|
||||
|
||||
int SSD13x6_FontGetCharWidth( struct SSD13x6_Device* Display, char Character );
|
||||
int SSD13x6_FontGetCharHeight( struct SSD13x6_Device* Display );
|
||||
int SSD13x6_FontMeasureString( struct SSD13x6_Device* Display, const char* Text );\
|
||||
|
||||
void SSD13x6_FontDrawChar( struct SSD13x6_Device* Display, char Character, int x, int y, int Color );
|
||||
void SSD13x6_FontDrawString( struct SSD13x6_Device* Display, int x, int y, const char* Text, int Color );
|
||||
void SSD13x6_FontDrawAnchoredString( struct SSD13x6_Device* Display, TextAnchor Anchor, const char* Text, int Color );
|
||||
void SSD13x6_FontGetAnchoredStringCoords( struct SSD13x6_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text );
|
||||
|
||||
extern const struct SSD13x6_FontDef Font_droid_sans_fallback_11x13;
|
||||
extern const struct SSD13x6_FontDef Font_droid_sans_fallback_15x17;
|
||||
extern const struct SSD13x6_FontDef Font_droid_sans_fallback_24x28;
|
||||
|
||||
extern const struct SSD13x6_FontDef Font_droid_sans_mono_7x13;
|
||||
extern const struct SSD13x6_FontDef Font_droid_sans_mono_13x24;
|
||||
extern const struct SSD13x6_FontDef Font_droid_sans_mono_16x31;
|
||||
|
||||
extern const struct SSD13x6_FontDef Font_liberation_mono_9x15;
|
||||
extern const struct SSD13x6_FontDef Font_liberation_mono_13x21;
|
||||
extern const struct SSD13x6_FontDef Font_liberation_mono_17x30;
|
||||
|
||||
extern const struct SSD13x6_FontDef Font_Tarable7Seg_16x32;
|
||||
extern const struct SSD13x6_FontDef Font_Tarable7Seg_32x64;
|
||||
|
||||
extern const struct SSD13x6_FontDef Font_line_1;
|
||||
extern const struct SSD13x6_FontDef Font_line_2;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -55,7 +55,7 @@ typedef struct raop_ctx_s {
|
||||
short unsigned port; // RTSP port for AirPlay
|
||||
int sock; // socket of the above
|
||||
struct in_addr peer; // IP of the iDevice (airplay sender)
|
||||
bool running, abort;
|
||||
bool running;
|
||||
#ifdef WIN32
|
||||
pthread_t thread, search_thread;
|
||||
#else
|
||||
@@ -63,6 +63,11 @@ typedef struct raop_ctx_s {
|
||||
StaticTask_t *xTaskBuffer;
|
||||
StackType_t xStack[RTSP_STACK_SIZE] __attribute__ ((aligned (4)));
|
||||
#endif
|
||||
/*
|
||||
Compiler/Execution bug: if this bool is next to 'running', the rtsp_thread
|
||||
loop sees 'running' being set to false from at first execution ...
|
||||
*/
|
||||
bool abort;
|
||||
unsigned char mac[6];
|
||||
int latency;
|
||||
struct {
|
||||
@@ -182,7 +187,7 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
|
||||
#ifdef WIN32
|
||||
getsockname(ctx->sock, (struct sockaddr *) &addr, &nlen);
|
||||
ctx->port = ntohs(addr.sin_port);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ctx->running = true;
|
||||
|
||||
@@ -214,7 +219,7 @@ void raop_delete(struct raop_ctx_s *ctx) {
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
void raop_delete(struct raop_ctx_s *ctx) {
|
||||
#ifdef WIN32
|
||||
#ifdef WIN32
|
||||
int sock;
|
||||
struct sockaddr addr;
|
||||
socklen_t nlen = sizeof(struct sockaddr);
|
||||
@@ -406,7 +411,7 @@ static void *rtsp_thread(void *arg) {
|
||||
if (n > 0) res = handle_rtsp(ctx, sock);
|
||||
|
||||
if (n < 0 || !res || ctx->abort) {
|
||||
abort_rtsp(ctx);
|
||||
abort_rtsp(ctx);
|
||||
closesocket(sock);
|
||||
LOG_INFO("RTSP close %u", sock);
|
||||
sock = -1;
|
||||
@@ -692,10 +697,10 @@ void abort_rtsp(raop_ctx_t *ctx) {
|
||||
ctx->active_remote.joiner = xTaskGetCurrentTaskHandle();
|
||||
ctx->active_remote.running = false;
|
||||
|
||||
xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY);
|
||||
xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY);
|
||||
vTaskDelete(ctx->active_remote.thread);
|
||||
vSemaphoreDelete(ctx->active_remote.thread);
|
||||
|
||||
|
||||
heap_caps_free(ctx->active_remote.xTaskBuffer);
|
||||
memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
|
||||
|
||||
|
||||
@@ -40,6 +40,55 @@ bool jack_inserted_svc(void);
|
||||
void (*spkfault_handler_svc)(bool inserted);
|
||||
bool spkfault_svc(void);
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
static void task_stats( void ) {
|
||||
#ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY
|
||||
static struct {
|
||||
TaskStatus_t *tasks;
|
||||
uint32_t total, n;
|
||||
} current, previous;
|
||||
|
||||
current.n = uxTaskGetNumberOfTasks();
|
||||
current.tasks = malloc( current.n * sizeof( TaskStatus_t ) );
|
||||
current.n = uxTaskGetSystemState( current.tasks, current.n, ¤t.total );
|
||||
|
||||
static EXT_RAM_ATTR char scratch[128+1];
|
||||
*scratch = '\0';
|
||||
|
||||
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
|
||||
uint32_t elapsed = current.total - previous.total;
|
||||
|
||||
for(int i = 0, n = 0; i < current.n; i++ ) {
|
||||
for (int j = 0; j < previous.n; j++) {
|
||||
if (current.tasks[i].xTaskNumber == previous.tasks[j].xTaskNumber) {
|
||||
n += sprintf(scratch + n, "%16s %2u%% s:%5u", current.tasks[i].pcTaskName,
|
||||
100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed,
|
||||
current.tasks[i].usStackHighWaterMark);
|
||||
if (i % 3 == 2 || i == current.n - 1) {
|
||||
ESP_LOGI(TAG, "%s", scratch);
|
||||
n = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (int i = 0, n = 0; i < current.n; i ++) {
|
||||
n += sprintf(scratch + n, "%16s s:%5u\t", current.tasks[i].pcTaskName, current.tasks[i].usStackHighWaterMark);
|
||||
if (i % 3 == 2 || i == current.n - 1) {
|
||||
ESP_LOGI(TAG, "%s", scratch);
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (previous.tasks) free(previous.tasks);
|
||||
previous = current;
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
*/
|
||||
@@ -49,6 +98,8 @@ static void monitor_callback(TimerHandle_t xTimer) {
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
|
||||
|
||||
task_stats();
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#include "squeezelite.h"
|
||||
#include "slimproto.h"
|
||||
#include "display.h"
|
||||
#include "gds.h"
|
||||
#include "gds_text.h"
|
||||
#include "gds_draw.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
@@ -85,8 +88,6 @@ struct ANIC_header {
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
extern struct outputstate output;
|
||||
|
||||
static struct {
|
||||
TaskHandle_t task;
|
||||
SemaphoreHandle_t mutex;
|
||||
@@ -177,7 +178,6 @@ static void visu_handler(u8_t *data, int len);
|
||||
|
||||
static void displayer_task(void* arg);
|
||||
|
||||
|
||||
/* scrolling undocumented information
|
||||
grfs
|
||||
B: screen number
|
||||
@@ -213,15 +213,15 @@ bool sb_display_init(void) {
|
||||
static EXT_RAM_ATTR StackType_t xStack[SCROLL_STACK_SIZE] __attribute__ ((aligned (4)));
|
||||
|
||||
// no display, just make sure we won't have requests
|
||||
if (!display || display->height == 0 || display->width == 0) {
|
||||
if (!display || GDS_GetWidth(display) <= 0 || GDS_GetHeight(display) <= 0) {
|
||||
LOG_INFO("no display for LMS");
|
||||
return false;
|
||||
}
|
||||
|
||||
// need to force height to 32 maximum
|
||||
displayer.width = display->width;
|
||||
displayer.height = min(display->height, SB_HEIGHT);
|
||||
SETD_width = display->width;
|
||||
displayer.width = GDS_GetWidth(display);
|
||||
displayer.height = min(GDS_GetHeight(display), SB_HEIGHT);
|
||||
SETD_width = displayer.width;
|
||||
|
||||
// create visu configuration
|
||||
visu.bar_gap = 1;
|
||||
@@ -277,6 +277,7 @@ static bool display_bus_handler(void *from, enum display_bus_cmd_e cmd) {
|
||||
|
||||
xSemaphoreGive(displayer.mutex);
|
||||
|
||||
// chain to rest of "bus"
|
||||
if (display_bus_chain) return (*display_bus_chain)(from, cmd);
|
||||
else return true;
|
||||
}
|
||||
@@ -330,8 +331,8 @@ static void send_server(void) {
|
||||
static void server(in_addr_t ip, u16_t hport, u16_t cport) {
|
||||
char msg[32];
|
||||
sprintf(msg, "%s:%hu", inet_ntoa(ip), hport);
|
||||
if (displayer.owned) display->text(DISPLAY_FONT_DEFAULT, DISPLAY_CENTERED, DISPLAY_CLEAR | DISPLAY_UPDATE, msg);
|
||||
SETD_width = display->width;
|
||||
if (displayer.owned) GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg);
|
||||
SETD_width = displayer.width;
|
||||
displayer.dirty = true;
|
||||
if (notify_chain) (*notify_chain)(ip, hport, cport);
|
||||
}
|
||||
@@ -426,8 +427,8 @@ static void show_display_buffer(char *ddram) {
|
||||
|
||||
LOG_DEBUG("\n\t%.40s\n\t%.40s", line1, line2);
|
||||
|
||||
display->line(1, DISPLAY_LEFT, DISPLAY_CLEAR, line1);
|
||||
display->line(2, DISPLAY_LEFT, DISPLAY_CLEAR | DISPLAY_UPDATE, line2);
|
||||
GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR, line1);
|
||||
GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, line2);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
@@ -495,13 +496,13 @@ static void grfe_handler( u8_t *data, int len) {
|
||||
if (displayer.owned) {
|
||||
// did we have something that might have write on the bottom of a SB_HEIGHT+ display
|
||||
if (displayer.dirty) {
|
||||
display->clear(true);
|
||||
GDS_ClearExt(display, true);
|
||||
displayer.dirty = false;
|
||||
}
|
||||
|
||||
// draw new frame
|
||||
display->draw_cbr(data + sizeof(struct grfe_packet), displayer.width, displayer.height);
|
||||
display->update();
|
||||
GDS_DrawBitmapCBR(display, data + sizeof(struct grfe_packet), displayer.width, displayer.height);
|
||||
GDS_Update(display);
|
||||
}
|
||||
|
||||
xSemaphoreGive(displayer.mutex);
|
||||
@@ -519,10 +520,10 @@ static void grfb_handler(u8_t *data, int len) {
|
||||
|
||||
LOG_INFO("brightness %hu", pkt->brightness);
|
||||
if (pkt->brightness < 0) {
|
||||
display->on(false);
|
||||
GDS_DisplayOff(display);
|
||||
} else {
|
||||
display->on(true);
|
||||
display->brightness(pkt->brightness);
|
||||
GDS_DisplayOn(display);
|
||||
GDS_SetContrast(display, pkt->brightness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,8 +604,8 @@ static void grfg_handler(u8_t *data, int len) {
|
||||
|
||||
// can only write if we really own display
|
||||
if (displayer.owned) {
|
||||
display->draw_cbr(scroller.frame, scroller.back.width, displayer.height);
|
||||
display->update();
|
||||
GDS_DrawBitmapCBR(display, scroller.frame, scroller.back.width, displayer.height);
|
||||
GDS_Update(display);
|
||||
}
|
||||
|
||||
// now we can active scrolling, but only if we are not on a small screen
|
||||
@@ -703,7 +704,7 @@ static void visu_update(void) {
|
||||
visu_export.level = 0;
|
||||
pthread_mutex_unlock(&visu_export.mutex);
|
||||
|
||||
display->clear(false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
|
||||
GDS_ClearExt(display, false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
|
||||
|
||||
for (int i = visu.n; --i >= 0;) {
|
||||
int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
|
||||
@@ -713,11 +714,11 @@ static void visu_update(void) {
|
||||
else if (visu.bars[i].max) visu.bars[i].max--;
|
||||
|
||||
for (int j = 0; j <= visu.bars[i].current; j += 2)
|
||||
display->draw_line( x1, y1 - j, x1 + visu.bar_width - 1, y1 - j);
|
||||
GDS_DrawLine(display, x1, y1 - j, x1 + visu.bar_width - 1, y1 - j, GDS_COLOR_WHITE);
|
||||
|
||||
if (visu.bars[i].max > 2) {
|
||||
display->draw_line( x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max);
|
||||
display->draw_line( x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1);
|
||||
GDS_DrawLine(display, x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max, GDS_COLOR_WHITE);
|
||||
GDS_DrawLine(display, x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1, GDS_COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -728,10 +729,9 @@ static void visu_update(void) {
|
||||
*/
|
||||
void spectrum_limits(int min, int n, int pos) {
|
||||
if (n / 2) {
|
||||
int i;
|
||||
float step = (DISPLAY_BW - min) * visu.spectrum_scale / (n/2);
|
||||
int step = ((DISPLAY_BW - min) * visu.spectrum_scale * 2) / n;
|
||||
visu.bars[pos].limit = min + step;
|
||||
for (i = 1; i < n/2; i++) visu.bars[pos+i].limit = visu.bars[pos+i-1].limit + step;
|
||||
for (int i = 1; i < n/2; i++) visu.bars[pos+i].limit = visu.bars[pos+i-1].limit + step;
|
||||
spectrum_limits(visu.bars[pos + n/2 - 1].limit, n/2, pos + n/2);
|
||||
} else {
|
||||
visu.bars[pos].limit = DISPLAY_BW;
|
||||
@@ -757,7 +757,7 @@ static void visu_handler( u8_t *data, int len) {
|
||||
visu.mode = pkt->which;
|
||||
|
||||
// little trick to clean the taller screens when switching visu
|
||||
if (visu.row >= SB_HEIGHT) display->clear(false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
|
||||
if (visu.row >= SB_HEIGHT) GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
|
||||
|
||||
if (visu.mode) {
|
||||
if (pkt->count >= 4) {
|
||||
@@ -768,17 +768,17 @@ static void visu_handler( u8_t *data, int len) {
|
||||
|
||||
visu.width = htonl(pkt->width);
|
||||
visu.height = pkt->height ? pkt->height : SB_HEIGHT;
|
||||
visu.col = pkt->col < 0 ? display->width + pkt->col : pkt->col;
|
||||
visu.row = pkt->row < 0 ? display->height + pkt->row : pkt->row;
|
||||
visu.col = pkt->col < 0 ? displayer.width + pkt->col : pkt->col;
|
||||
visu.row = pkt->row < 0 ? GDS_GetHeight(display) + pkt->row : pkt->row;
|
||||
visu.border = htonl(pkt->border);
|
||||
bars = htonl(pkt->bars);
|
||||
visu.spectrum_scale = htonl(pkt->spectrum_scale) / 100.;
|
||||
} else {
|
||||
// full screen visu, try to use bottom screen if available
|
||||
visu.width = display->width;
|
||||
visu.height = display->height > SB_HEIGHT ? display->height - SB_HEIGHT : display->height;
|
||||
visu.width = displayer.width;
|
||||
visu.height = GDS_GetHeight(display) > SB_HEIGHT ? GDS_GetHeight(display) - SB_HEIGHT : GDS_GetHeight(display);
|
||||
visu.col = visu.border = 0;
|
||||
visu.row = display->height - visu.height;
|
||||
visu.row = GDS_GetHeight(display) - visu.height;
|
||||
bars = htonl(pkt->full.bars);
|
||||
visu.spectrum_scale = htonl(pkt->full.spectrum_scale) / 100.;
|
||||
}
|
||||
@@ -812,7 +812,7 @@ static void visu_handler( u8_t *data, int len) {
|
||||
// reset bars maximum
|
||||
for (int i = visu.n; --i >= 0;) visu.bars[i].max = 0;
|
||||
|
||||
display->clear(false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
|
||||
GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1);
|
||||
|
||||
LOG_INFO("Visualizer with %u bars of width %d:%d:%d:%d (%w:%u,h:%u,c:%u,r:%u,s:%.02f)", visu.n, visu.bar_border, visu.bar_width, visu.bar_gap, visu.border, visu.width, visu.height, visu.col, visu.row, visu.spectrum_scale);
|
||||
} else {
|
||||
@@ -855,7 +855,7 @@ static void displayer_task(void *args) {
|
||||
memcpy(scroller.frame, scroller.back.frame, scroller.back.width * displayer.height / 8);
|
||||
for (int i = 0; i < scroller.width * displayer.height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * displayer.height / 8 + i];
|
||||
scroller.scrolled += scroller.by;
|
||||
if (displayer.owned) display->draw_cbr(scroller.frame, scroller.width, displayer.height);
|
||||
if (displayer.owned) GDS_DrawBitmapCBR(display, scroller.frame, scroller.width, displayer.height);
|
||||
|
||||
// short sleep & don't need background update
|
||||
scroller.wake = scroller.speed;
|
||||
@@ -885,7 +885,7 @@ static void displayer_task(void *args) {
|
||||
}
|
||||
|
||||
// need to make sure we own display
|
||||
if (displayer.owned) display->update();
|
||||
if (displayer.owned) GDS_Update(display);
|
||||
|
||||
// release semaphore and sleep what's needed
|
||||
xSemaphoreGive(displayer.mutex);
|
||||
|
||||
654
components/wifi-manager/http_server.c
Normal file
654
components/wifi-manager/http_server.c
Normal file
@@ -0,0 +1,654 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@file http_server.c
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for the HTTP server to run.
|
||||
|
||||
Contains the freeRTOS task for the HTTP listener and all necessary support
|
||||
function to process requests, decode URLs, serve files, etc. etc.
|
||||
|
||||
@note http_server task cannot run without the wifi_manager task!
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#include "http_server.h"
|
||||
#include "cmd_system.h"
|
||||
#include <inttypes.h>
|
||||
#include "squeezelite-ota.h"
|
||||
#include "nvs_utilities.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "cJSON.h"
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "config.h"
|
||||
|
||||
#define HTTP_STACK_SIZE (5*1024)
|
||||
|
||||
/* @brief tag used for ESP serial console messages */
|
||||
static const char TAG[] = "http_server";
|
||||
/* @brief task handle for the http server */
|
||||
static TaskHandle_t task_http_server = NULL;
|
||||
static StaticTask_t task_http_buffer;
|
||||
#if RECOVERY_APPLICATION
|
||||
static StackType_t task_http_stack[HTTP_STACK_SIZE];
|
||||
#else
|
||||
static StackType_t EXT_RAM_ATTR task_http_stack[HTTP_STACK_SIZE];
|
||||
#endif
|
||||
SemaphoreHandle_t http_server_config_mutex = NULL;
|
||||
|
||||
/**
|
||||
* @brief embedded binary data.
|
||||
* @see file "component.mk"
|
||||
* @see https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#embedding-binary-data
|
||||
*/
|
||||
extern const uint8_t style_css_start[] asm("_binary_style_css_start");
|
||||
extern const uint8_t style_css_end[] asm("_binary_style_css_end");
|
||||
extern const uint8_t jquery_gz_start[] asm("_binary_jquery_min_js_gz_start");
|
||||
extern const uint8_t jquery_gz_end[] asm("_binary_jquery_min_js_gz_end");
|
||||
extern const uint8_t popper_gz_start[] asm("_binary_popper_min_js_gz_start");
|
||||
extern const uint8_t popper_gz_end[] asm("_binary_popper_min_js_gz_end");
|
||||
extern const uint8_t bootstrap_js_gz_start[] asm("_binary_bootstrap_min_js_gz_start");
|
||||
extern const uint8_t bootstrap_js_gz_end[] asm("_binary_bootstrap_min_js_gz_end");
|
||||
extern const uint8_t bootstrap_css_gz_start[] asm("_binary_bootstrap_min_css_gz_start");
|
||||
extern const uint8_t bootstrap_css_gz_end[] asm("_binary_bootstrap_min_css_gz_end");
|
||||
extern const uint8_t code_js_start[] asm("_binary_code_js_start");
|
||||
extern const uint8_t code_js_end[] asm("_binary_code_js_end");
|
||||
extern const uint8_t index_html_start[] asm("_binary_index_html_start");
|
||||
extern const uint8_t index_html_end[] asm("_binary_index_html_end");
|
||||
|
||||
|
||||
/* const http headers stored in ROM */
|
||||
const static char http_hdr_template[] = "HTTP/1.1 200 OK\nContent-type: %s\nAccept-Ranges: bytes\nContent-Length: %d\nContent-Encoding: %s\nAccess-Control-Allow-Origin: *\n\n";
|
||||
const static char http_html_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/html\nAccess-Control-Allow-Origin: *\nAccept-Encoding: identity\n\n";
|
||||
const static char http_css_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/css\nCache-Control: public, max-age=31536000\nAccess-Control-Allow-Origin: *\n\n";
|
||||
const static char http_js_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\nAccess-Control-Allow-Origin: *\n\n";
|
||||
const static char http_400_hdr[] = "HTTP/1.1 400 Bad Request\nContent-Length: 0\n\n";
|
||||
const static char http_404_hdr[] = "HTTP/1.1 404 Not Found\nContent-Length: 0\n\n";
|
||||
const static char http_503_hdr[] = "HTTP/1.1 503 Service Unavailable\nContent-Length: 0\n\n";
|
||||
const static char http_ok_json_no_cache_hdr[] = "HTTP/1.1 200 OK\nContent-type: application/json\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nAccess-Control-Allow-Origin: *\nAccept-Encoding: identity\n\n";
|
||||
const static char http_redirect_hdr_start[] = "HTTP/1.1 302 Found\nLocation: http://";
|
||||
const static char http_redirect_hdr_end[] = "/\n\n";
|
||||
|
||||
|
||||
void http_server_start() {
|
||||
ESP_LOGD(TAG, "http_server_start ");
|
||||
if(task_http_server == NULL) {
|
||||
task_http_server = xTaskCreateStatic( (TaskFunction_t) &http_server, "http_server", HTTP_STACK_SIZE, NULL,
|
||||
WIFI_MANAGER_TASK_PRIORITY, task_http_stack, &task_http_buffer);
|
||||
}
|
||||
}
|
||||
void http_server(void *pvParameters) {
|
||||
http_server_config_mutex = xSemaphoreCreateMutex();
|
||||
struct netconn *conn, *newconn;
|
||||
err_t err;
|
||||
conn = netconn_new(NETCONN_TCP);
|
||||
netconn_bind(conn, IP_ADDR_ANY, 80);
|
||||
netconn_listen(conn);
|
||||
ESP_LOGI(TAG, "HTTP Server listening on 80/tcp");
|
||||
do {
|
||||
err = netconn_accept(conn, &newconn);
|
||||
if(err == ERR_OK) {
|
||||
http_server_netconn_serve(newconn);
|
||||
netconn_delete(newconn);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Error accepting new connection. Terminating HTTP server");
|
||||
}
|
||||
taskYIELD(); /* allows the freeRTOS scheduler to take over if needed. */
|
||||
} while(err == ERR_OK);
|
||||
|
||||
netconn_close(conn);
|
||||
netconn_delete(conn);
|
||||
vSemaphoreDelete(http_server_config_mutex);
|
||||
http_server_config_mutex = NULL;
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
|
||||
char* http_server_get_header(char *request, char *header_name, int *len) {
|
||||
*len = 0;
|
||||
char *ret = NULL;
|
||||
char *ptr = NULL;
|
||||
|
||||
ptr = strstr(request, header_name);
|
||||
if(ptr) {
|
||||
ret = ptr + strlen(header_name);
|
||||
ptr = ret;
|
||||
while (*ptr != '\0' && *ptr != '\n' && *ptr != '\r') {
|
||||
(*len)++;
|
||||
ptr++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
char* http_server_search_header(char *request, char *header_name, int *len, char ** parm_name, char ** next_position, char * bufEnd) {
|
||||
*len = 0;
|
||||
char *ret = NULL;
|
||||
char *ptr = NULL;
|
||||
int currentLength=0;
|
||||
|
||||
ESP_LOGV(TAG, "searching for header name: [%s]", header_name);
|
||||
ptr = strstr(request, header_name);
|
||||
|
||||
|
||||
if(ptr!=NULL && ptr<bufEnd) {
|
||||
ret = ptr + strlen(header_name);
|
||||
ptr = ret;
|
||||
currentLength=(int)(ptr-request);
|
||||
ESP_LOGV(TAG, "found string at %d", currentLength);
|
||||
|
||||
while (*ptr != '\0' && *ptr != '\n' && *ptr != '\r' && *ptr != ':' && ptr<bufEnd) {
|
||||
ptr++;
|
||||
}
|
||||
if(*ptr==':') {
|
||||
currentLength=(int)(ptr-ret);
|
||||
ESP_LOGV(TAG, "Found parameter name end, length : %d", currentLength);
|
||||
// save the parameter name: the string between header name and ":"
|
||||
*parm_name=malloc(currentLength+1);
|
||||
if(*parm_name==NULL) {
|
||||
ESP_LOGE(TAG, "Unable to allocate memory for new header name");
|
||||
return NULL;
|
||||
}
|
||||
memset(*parm_name, 0x00,currentLength+1);
|
||||
strncpy(*parm_name,ret,currentLength);
|
||||
ESP_LOGV(TAG, "Found parameter name : %s ", *parm_name);
|
||||
ptr++;
|
||||
while (*ptr == ' ' && ptr<bufEnd) {
|
||||
ptr++;
|
||||
}
|
||||
|
||||
}
|
||||
ret=ptr;
|
||||
while (*ptr != '\0' && *ptr != '\n' && *ptr != '\r'&& ptr<bufEnd) {
|
||||
(*len)++;
|
||||
ptr++;
|
||||
}
|
||||
// Terminate value inside its actual buffer so we can treat it as individual string
|
||||
*ptr='\0';
|
||||
currentLength=(int)(ptr-ret);
|
||||
ESP_LOGV(TAG, "Found parameter value end, length : %d, value: %s", currentLength,ret );
|
||||
|
||||
*next_position=++ptr;
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGD(TAG, "No more match for : %s", header_name);
|
||||
return NULL;
|
||||
}
|
||||
void http_server_send_resource_file(struct netconn *conn,const uint8_t * start, const uint8_t * end, char * content_type,char * encoding) {
|
||||
uint16_t len=end - start;
|
||||
size_t buff_length= sizeof(http_hdr_template)+strlen(content_type)+strlen(encoding);
|
||||
char * http_hdr=malloc(buff_length);
|
||||
if( http_hdr == NULL) {
|
||||
ESP_LOGE(TAG, "Cound not allocate %d bytes for headers.",buff_length);
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(http_hdr,0x00,buff_length);
|
||||
snprintf(http_hdr, buff_length-1,http_hdr_template,content_type,len,encoding);
|
||||
netconn_write(conn, http_hdr, strlen(http_hdr), NETCONN_NOCOPY);
|
||||
ESP_LOGD(TAG, "sending response : %s",http_hdr);
|
||||
netconn_write(conn, start, end - start, NETCONN_NOCOPY);
|
||||
free(http_hdr);
|
||||
}
|
||||
}
|
||||
|
||||
err_t http_server_send_config_json(struct netconn *conn) {
|
||||
char * json = config_alloc_get_json(false);
|
||||
if(json!=NULL){
|
||||
ESP_LOGD(TAG, "config json : %s",json );
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, json, strlen(json), NETCONN_NOCOPY);
|
||||
free(json);
|
||||
}
|
||||
else{
|
||||
ESP_LOGD(TAG, "Error retrieving config json string. ");
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void http_server_process_config(struct netconn *conn, char *inbuf) {
|
||||
|
||||
// Here, we are passed a buffer which contains the http request
|
||||
|
||||
// netbuf_data(inbuf, (void**)&buf, &buflen);
|
||||
// err = netconn_recv(conn, &inbuf);
|
||||
// if(err == ERR_OK) {
|
||||
//
|
||||
// /* extract the first line of the request */
|
||||
// char *save_ptr = buf;
|
||||
// char *line = strtok_r(save_ptr, new_line, &save_ptr);
|
||||
// ESP_LOGD(TAG, "Processing line %s",line);
|
||||
ESP_LOGD(TAG, "Processing request buffer: \n%s",inbuf);
|
||||
char *last = NULL;
|
||||
char *ptr = NULL;
|
||||
last = ptr = inbuf;
|
||||
bool bHeaders= true;
|
||||
while(ptr!=NULL && *ptr != '\0') {
|
||||
// Move to the end of the line, or to the end of the buffer
|
||||
if(bHeaders) {
|
||||
while (*ptr != '\0' && *ptr != '\n' && *ptr != '\r') {
|
||||
ptr++;
|
||||
}
|
||||
// terminate the header string
|
||||
if( *(ptr) == '\0' ) {
|
||||
ESP_LOGD(TAG, "End of buffer found");
|
||||
return;
|
||||
}
|
||||
*ptr = '\0';
|
||||
if( *(ptr+1) == '\n' ) {
|
||||
*(ptr+1)='\0';
|
||||
ptr+=2;
|
||||
}
|
||||
if(ptr==last) {
|
||||
ESP_LOGD(TAG, "Processing body. ");
|
||||
break;
|
||||
}
|
||||
if(strlen(last)>0) {
|
||||
ESP_LOGD(TAG, "Found Header Line %s ", last);
|
||||
//Content-Type: application/json
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG, "Found end of headers");
|
||||
bHeaders = false;
|
||||
}
|
||||
last=ptr;
|
||||
}
|
||||
else {
|
||||
//ESP_LOGD(TAG, "Body content: %s", last);
|
||||
//cJSON * json = cJSON_Parse(last);
|
||||
//cJSON_Delete(json);
|
||||
//todo: implement body json parsing
|
||||
// right now, body is coming as compressed, so we need some type of decompression to happen.
|
||||
return;
|
||||
}
|
||||
}
|
||||
return ;
|
||||
|
||||
}
|
||||
|
||||
void dump_net_buffer(void * buf, u16_t buflen) {
|
||||
char * curbuf = malloc(buflen+1);
|
||||
ESP_LOGV(TAG, "netconn buffer, length=%u",buflen);
|
||||
if(curbuf==NULL) {
|
||||
ESP_LOGE(TAG, "Unable to show netconn buffer. Malloc failed");
|
||||
}
|
||||
memset(curbuf,0x0, buflen+1);
|
||||
memcpy(curbuf,buf,buflen);
|
||||
ESP_LOGV(TAG, "netconn buffer content:\n%s",curbuf);
|
||||
free(curbuf);
|
||||
}
|
||||
|
||||
void http_server_netconn_serve(struct netconn *conn) {
|
||||
|
||||
struct netbuf *inbuf;
|
||||
char *buf = NULL;
|
||||
u16_t buflen = 0;
|
||||
err_t err;
|
||||
ip_addr_t remote_add;
|
||||
u16_t port;
|
||||
ESP_LOGV(TAG, "Serving page. Getting device AP address.");
|
||||
const char new_line[2] = "\n";
|
||||
char * ap_ip_address= config_alloc_get_default(NVS_TYPE_STR, "ap_ip_address", DEFAULT_AP_IP, 0);
|
||||
if(ap_ip_address==NULL){
|
||||
ESP_LOGE(TAG, "Unable to retrieve default AP IP Address");
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_close(conn);
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "Getting remote device IP address.");
|
||||
netconn_getaddr(conn, &remote_add, &port, 0);
|
||||
char * remote_address = strdup(ip4addr_ntoa(ip_2_ip4(&remote_add)));
|
||||
ESP_LOGD(TAG, "Local Access Point IP address is: %s. Remote device IP address is %s. Receiving request buffer", ap_ip_address, remote_address);
|
||||
|
||||
u16_t bufsize = 0;
|
||||
netconn_set_recvtimeout(conn, 50);
|
||||
while (netconn_recv(conn, &inbuf) == ERR_OK) {
|
||||
do {
|
||||
u8_t *rcvbuf;
|
||||
u16_t rcvlen;
|
||||
netbuf_data(inbuf, (void**)&rcvbuf, &rcvlen);
|
||||
dump_net_buffer(rcvbuf, rcvlen);
|
||||
if (buflen + rcvlen > bufsize) {
|
||||
bufsize += rcvlen - bufsize < 2048 ? 2048 : rcvlen - bufsize;
|
||||
buf = realloc(buf, bufsize);
|
||||
}
|
||||
memcpy(buf + buflen, rcvbuf, rcvlen);
|
||||
buflen += rcvlen;
|
||||
ESP_LOGI(TAG, "received netbuf of %hu", rcvlen);
|
||||
} while (netbuf_next(inbuf) != -1);
|
||||
netbuf_delete(inbuf);
|
||||
}
|
||||
|
||||
if(buflen) {
|
||||
ESP_LOGV(TAG, "Getting data buffer.");
|
||||
int lenH = 0;
|
||||
/* extract the first line of the request */
|
||||
char *save_ptr = buf;
|
||||
char *line = strtok_r(save_ptr, new_line, &save_ptr);
|
||||
char *temphost = http_server_get_header(save_ptr, "Host: ", &lenH);
|
||||
char * host = malloc(lenH+1);
|
||||
memset(host,0x00,lenH+1);
|
||||
if(lenH>0){
|
||||
strlcpy(host,temphost,lenH+1);
|
||||
}
|
||||
ESP_LOGD(TAG, "http_server_netconn_serve Host: [%s], host: [%s], Processing line [%s]",remote_address,host,line);
|
||||
|
||||
if(line) {
|
||||
|
||||
/* captive portal functionality: redirect to access point IP for HOST that are not the access point IP OR the STA IP */
|
||||
const char * host_name=NULL;
|
||||
if((err=tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &host_name )) !=ESP_OK) {
|
||||
ESP_LOGE(TAG, "Unable to get host name. Error: %s",esp_err_to_name(err));
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(TAG,"System host name %s, http requested host: %s.",host_name, host);
|
||||
}
|
||||
|
||||
/* determine if Host is from the STA IP address */
|
||||
wifi_manager_lock_sta_ip_string(portMAX_DELAY);
|
||||
bool access_from_sta_ip = lenH > 0?strcasestr(host, wifi_manager_get_sta_ip_string()):false;
|
||||
wifi_manager_unlock_sta_ip_string();
|
||||
bool access_from_host_name = (host_name!=NULL) && strcasestr(host,host_name);
|
||||
|
||||
if(lenH > 0 && !strcasestr(host, ap_ip_address) && !(access_from_sta_ip || access_from_host_name)) {
|
||||
ESP_LOGI(TAG, "Redirecting host [%s] to AP IP Address : %s",remote_address, ap_ip_address);
|
||||
netconn_write(conn, http_redirect_hdr_start, sizeof(http_redirect_hdr_start) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, ap_ip_address, strlen(ap_ip_address), NETCONN_NOCOPY);
|
||||
netconn_write(conn, http_redirect_hdr_end, sizeof(http_redirect_hdr_end) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
else {
|
||||
//static stuff
|
||||
/* default page */
|
||||
if(strstr(line, "GET / ")) {
|
||||
netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, index_html_start, index_html_end- index_html_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /code.js ")) {
|
||||
netconn_write(conn, http_js_hdr, sizeof(http_js_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, code_js_start, code_js_end - code_js_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /style.css ")) {
|
||||
netconn_write(conn, http_css_hdr, sizeof(http_css_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, style_css_start, style_css_end - style_css_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /jquery.js ")) {
|
||||
http_server_send_resource_file(conn,jquery_gz_start, jquery_gz_end, "text/javascript", "gzip" );
|
||||
}
|
||||
else if(strstr(line, "GET /popper.js ")) {
|
||||
http_server_send_resource_file(conn,popper_gz_start, popper_gz_end, "text/javascript", "gzip" );
|
||||
}
|
||||
else if(strstr(line, "GET /bootstrap.js ")) {
|
||||
http_server_send_resource_file(conn,bootstrap_js_gz_start, bootstrap_js_gz_end, "text/javascript", "gzip" );
|
||||
}
|
||||
else if(strstr(line, "GET /bootstrap.css ")) {
|
||||
http_server_send_resource_file(conn,bootstrap_css_gz_start, bootstrap_css_gz_end, "text/css", "gzip" );
|
||||
}
|
||||
|
||||
//dynamic stuff
|
||||
else if(strstr(line, "GET /scan.json ")) {
|
||||
ESP_LOGI(TAG, "Starting wifi scan");
|
||||
wifi_manager_scan_async();
|
||||
}
|
||||
else if(strstr(line, "GET /ap.json ")) {
|
||||
/* if we can get the mutex, write the last version of the AP list */
|
||||
ESP_LOGI(TAG, "Processing ap.json request");
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)) {
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
char *buff = wifi_manager_alloc_get_ap_list_json();
|
||||
wifi_manager_unlock_json_buffer();
|
||||
if(buff!=NULL){
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
free(buff);
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG, "Error retrieving ap list json string. ");
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /ap.json failed to obtain mutex");
|
||||
}
|
||||
/* request a wifi scan */
|
||||
ESP_LOGI(TAG, "Starting wifi scan");
|
||||
wifi_manager_scan_async();
|
||||
ESP_LOGI(TAG, "Done serving ap.json");
|
||||
}
|
||||
else if(strstr(line, "GET /config.json ")) {
|
||||
ESP_LOGI(TAG, "Serving config.json");
|
||||
ESP_LOGI(TAG, "About to get config from flash");
|
||||
http_server_send_config_json(conn);
|
||||
ESP_LOGD(TAG, "Done serving config.json");
|
||||
}
|
||||
else if(strstr(line, "POST /config.json ")) {
|
||||
ESP_LOGI(TAG, "Serving POST config.json");
|
||||
int lenA=0;
|
||||
char * last_parm=save_ptr;
|
||||
char * next_parm=save_ptr;
|
||||
char * last_parm_name=NULL;
|
||||
bool bErrorFound=false;
|
||||
bool bOTA=false;
|
||||
char * otaURL=NULL;
|
||||
// todo: implement json body parsing
|
||||
//http_server_process_config(conn,save_ptr);
|
||||
|
||||
while(last_parm!=NULL) {
|
||||
// Search will return
|
||||
ESP_LOGD(TAG, "Getting parameters from X-Custom headers");
|
||||
last_parm = http_server_search_header(next_parm, "X-Custom-", &lenA, &last_parm_name,&next_parm,buf+buflen);
|
||||
if(last_parm!=NULL && last_parm_name!=NULL) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: POST config.json, config %s=%s", last_parm_name, last_parm);
|
||||
if(strcmp(last_parm_name, "fwurl")==0) {
|
||||
// we're getting a request to do an OTA from that URL
|
||||
ESP_LOGW(TAG, "Found OTA request!");
|
||||
otaURL=strdup(last_parm);
|
||||
bOTA=true;
|
||||
}
|
||||
else {
|
||||
ESP_LOGV(TAG, "http_server_netconn_serve: POST config.json Storing parameter");
|
||||
if(config_set_value(NVS_TYPE_STR, last_parm_name , last_parm) != ESP_OK){
|
||||
ESP_LOGE(TAG, "Unable to save nvs value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(last_parm_name!=NULL) {
|
||||
free(last_parm_name);
|
||||
last_parm_name=NULL;
|
||||
}
|
||||
}
|
||||
if(bErrorFound) {
|
||||
netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY); //400 invalid request
|
||||
}
|
||||
else {
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
|
||||
if(bOTA) {
|
||||
|
||||
#if RECOVERY_APPLICATION
|
||||
ESP_LOGW(TAG, "Starting process OTA for url %s",otaURL);
|
||||
#else
|
||||
ESP_LOGW(TAG, "Restarting system to process OTA for url %s",otaURL);
|
||||
#endif
|
||||
wifi_manager_reboot_ota(otaURL);
|
||||
free(otaURL);
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Done Serving POST config.json");
|
||||
}
|
||||
else if(strstr(line, "POST /connect.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: POST /connect.json");
|
||||
bool found = false;
|
||||
int lenS = 0, lenP = 0, lenN = 0;
|
||||
char *ssid = NULL, *password = NULL;
|
||||
ssid = http_server_get_header(save_ptr, "X-Custom-ssid: ", &lenS);
|
||||
password = http_server_get_header(save_ptr, "X-Custom-pwd: ", &lenP);
|
||||
char * new_host_name_b = http_server_get_header(save_ptr, "X-Custom-host_name: ", &lenN);
|
||||
if(lenN > 0){
|
||||
lenN++;
|
||||
char * new_host_name = malloc(lenN);
|
||||
strlcpy(new_host_name, new_host_name_b, lenN);
|
||||
if(config_set_value(NVS_TYPE_STR, "host_name", new_host_name) != ESP_OK){
|
||||
ESP_LOGE(TAG, "Unable to save host name configuration");
|
||||
}
|
||||
free(new_host_name);
|
||||
}
|
||||
|
||||
if(ssid && lenS <= MAX_SSID_SIZE && password && lenP <= MAX_PASSWORD_SIZE) {
|
||||
wifi_config_t* config = wifi_manager_get_wifi_sta_config();
|
||||
memset(config, 0x00, sizeof(wifi_config_t));
|
||||
memcpy(config->sta.ssid, ssid, lenS);
|
||||
memcpy(config->sta.password, password, lenP);
|
||||
ESP_LOGD(TAG, "http_server_netconn_serve: wifi_manager_connect_async() call, with ssid: %s, password: %s", config->sta.ssid, config->sta.password);
|
||||
wifi_manager_connect_async();
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
|
||||
found = true;
|
||||
}
|
||||
else{
|
||||
ESP_LOGE(TAG, "SSID or Password invalid");
|
||||
}
|
||||
|
||||
|
||||
if(!found) {
|
||||
/* bad request the authentification header is not complete/not the correct format */
|
||||
netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "bad request the authentification header is not complete/not the correct format");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: done serving connect.json");
|
||||
}
|
||||
else if(strstr(line, "DELETE /connect.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: DELETE /connect.json");
|
||||
/* request a disconnection from wifi and forget about it */
|
||||
wifi_manager_disconnect_async();
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: done serving DELETE /connect.json");
|
||||
}
|
||||
else if(strstr(line, "POST /reboot_ota.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: POST reboot_ota.json");
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
|
||||
wifi_manager_reboot(OTA);
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: done serving POST reboot_ota.json");
|
||||
}
|
||||
else if(strstr(line, "POST /reboot.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: POST reboot.json");
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
|
||||
wifi_manager_reboot(RESTART);
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: done serving POST reboot.json");
|
||||
}
|
||||
else if(strstr(line, "POST /recovery.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: POST recovery.json");
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
|
||||
wifi_manager_reboot(RECOVERY);
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: done serving POST recovery.json");
|
||||
}
|
||||
else if(strstr(line, "GET /status.json ")) {
|
||||
ESP_LOGI(TAG, "Serving status.json");
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)) {
|
||||
char *buff = wifi_manager_alloc_get_ip_info_json();
|
||||
wifi_manager_unlock_json_buffer();
|
||||
if(buff) {
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
free(buff);
|
||||
}
|
||||
else {
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /status failed to obtain mutex");
|
||||
}
|
||||
ESP_LOGI(TAG, "Done Serving status.json");
|
||||
}
|
||||
else {
|
||||
netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "bad request from host: %s, request %s",remote_address, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG, "URL not found processing for remote host : %s",remote_address);
|
||||
netconn_write(conn, http_404_hdr, sizeof(http_404_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
free(host);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
free(ap_ip_address);
|
||||
free(remote_address);
|
||||
netconn_close(conn);
|
||||
/* free the buffer */
|
||||
|
||||
}
|
||||
|
||||
bool http_server_lock_json_object(TickType_t xTicksToWait) {
|
||||
ESP_LOGD(TAG, "Locking config json object");
|
||||
if(http_server_config_mutex) {
|
||||
if( xSemaphoreTake( http_server_config_mutex, xTicksToWait ) == pdTRUE ) {
|
||||
ESP_LOGV(TAG, "config Json object locked!");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ESP_LOGW(TAG, "Semaphore take failed. Unable to lock config Json object mutex");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ESP_LOGW(TAG, "Unable to lock config Json object mutex");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void http_server_unlock_json_object() {
|
||||
ESP_LOGD(TAG, "Unlocking json buffer!");
|
||||
xSemaphoreGive( http_server_config_mutex );
|
||||
}
|
||||
|
||||
void strreplace(char *src, char *str, char *rep)
|
||||
{
|
||||
char *p = strstr(src, str);
|
||||
if(p)
|
||||
{
|
||||
int len = strlen(src)+strlen(rep)-strlen(str);
|
||||
char r[len];
|
||||
memset(r, 0, len);
|
||||
if( p >= src ) {
|
||||
strncpy(r, src, p-src);
|
||||
r[p-src]='\0';
|
||||
strncat(r, rep, strlen(rep));
|
||||
strncat(r, p+strlen(str), p+strlen(str)-src+strlen(src));
|
||||
strcpy(src, r);
|
||||
strreplace(p+strlen(rep), str, rep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
# Automatically generated file. DO NOT EDIT.
|
||||
# Espressif IoT Development Framework (ESP-IDF) Project Configuration
|
||||
#
|
||||
|
||||
# DSP
|
||||
CONFIG_DSP_OPTIMIZED=y
|
||||
CONFIG_DSP_OPTIMIZATION=1
|
||||
CONFIG_DSP_MAX_FFT_SIZE_512=y
|
||||
CONFIG_IDF_TARGET_ESP32=y
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
|
||||
@@ -11,7 +16,12 @@ CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-"
|
||||
CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES=y
|
||||
CONFIG_APP_COMPILE_TIME_DATE=y
|
||||
|
||||
|
||||
|
||||
CONFIG_OTA_ALLOW_HTTP=y
|
||||
|
||||
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
|
||||
|
||||
|
||||
@@ -102,6 +112,7 @@ CONFIG_DEFAULT_AP_NETMASK="255.255.255.0"
|
||||
CONFIG_DEFAULT_AP_MAX_CONNECTIONS=4
|
||||
CONFIG_DEFAULT_AP_BEACON_INTERVAL=100
|
||||
CONFIG_DEFAULT_COMMAND_LINE="squeezelite -o I2S -b 500:2000 -d all=info -C 30"
|
||||
|
||||
CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
|
||||
|
||||
@@ -570,7 +581,7 @@ CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
|
||||
|
||||
CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2432
|
||||
CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
|
||||
@@ -599,6 +610,7 @@ CONFIG_LWIP_LOCAL_HOSTNAME="espressif"
|
||||
|
||||
CONFIG_LWIP_TIMERS_ONDEMAND=y
|
||||
CONFIG_LWIP_MAX_SOCKETS=16
|
||||
|
||||
CONFIG_LWIP_SO_REUSE=y
|
||||
CONFIG_LWIP_SO_REUSE_RXTOALL=y
|
||||
|
||||
@@ -775,6 +787,7 @@ CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y
|
||||
CONFIG_VFS_SUPPORT_TERMIOS=y
|
||||
CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1
|
||||
CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128
|
||||
|
||||
CONFIG_WL_SECTOR_SIZE_512=y
|
||||
#CONFIG_WL_SECTOR_SIZE_4096 is not defined
|
||||
CONFIG_WL_SECTOR_SIZE=512
|
||||
|
||||
Reference in New Issue
Block a user