Merge remote-tracking branch 'origin/master' into httpd

Conflicts:
	components/wifi-manager/http_server.c
This commit is contained in:
Sebastien
2020-02-24 16:15:34 -05:00
53 changed files with 2291 additions and 1698 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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;
}

View 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;
}

View File

@@ -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

View 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 ); }

View 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

View 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

View 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++;
}
}
}
}
*/

View 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

View File

@@ -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; \
} \
}

View File

@@ -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: {

View 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

View 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 );
}
}

View 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);
}

View 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, ...);

View File

@@ -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;
}

View 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 );
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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 );
}

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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));

View File

@@ -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, &current.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();
}
/****************************************************************************************

View File

@@ -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);

View 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);
}
}
}

View File

@@ -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