Merge branch 'arendst:development' into development

This commit is contained in:
alexasf 2022-02-01 07:29:35 +03:00 committed by GitHub
commit 878d6ef02f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
112 changed files with 3583 additions and 5852 deletions

View File

@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.
- Command ``SspmScan`` to rescan Sonoff SPM modbus
- Support for MQ analog sensor for air quality by Francesco Adriani (#14581)
- Command ``SetOption134 1`` to disable PWM auto-phasing for lights by default (new behavior) (#14590)
- Increase PWM channels to 16 (Esp32 only)
### Changed
- BME68x-Sensor-API library from v3.5.9 to v4.4.7

View File

@ -1,6 +1,5 @@
# I2C devices
Tasmota supports several I2C devices but to use them they mostly need to be enabled at compile time to solve possible address conflicts.
Tasmota supports several I2C devices. To use them I2C and the device need to be enabled at compile time. I2C and some devices are supported also in the official releases. Devices can be de/-actived on runtime to solve possible address conflicts. (e.g. address 0x27 is used by multiple devices)
Using command ``I2cDriver`` individual drivers can be enabled or disabled at runtime allowing duplicate I2C addresses at compile time. Use the Index from the table below to control I2C drivers like ``I2cDriver10 0`` for disabling BMP support.
@ -41,7 +40,7 @@ Index | Define | Driver | Device | Address(es) | Description
19 | USE_SI1145 | xsns_24 | SI1147 | 0x60 | Ultra violet index and light sensor
20 | USE_LM75AD | xsns_26 | LM75AD | 0x48 - 0x4F | Temperature sensor
21 | USE_APDS9960 | xsns_27 | APDS9960 | 0x39 | Proximity ambient light RGB and gesture sensor
22 | USE_MCP230xx | xsns_29 | MCP23008 | 0x20 - 0x26 | 16-bit I/O expander
22 | USE_MCP230xx | xsns_29 | MCP23008 | 0x20 - 0x26 | 8-bit I/O expander
22 | USE_MCP230xx | xsns_29 | MCP23017 | 0x20 - 0x26 | 16-bit I/O expander
23 | USE_MPR121 | xsns_30 | MPR121 | 0x5A - 0x5D | Proximity capacitive touch sensor
24 | USE_CCS811 | xsns_31 | CCS811 | 0x5A | Gas (TVOC) and air quality sensor

35
boards/esp32s3.json Normal file
View File

@ -0,0 +1,35 @@
{
"build": {
"arduino":{
"ldscript": "esp32s3_out.ld"
},
"core": "esp32",
"extra_flags": "-DBOARD_HAS_PSRAM -DESP32_4M -DESP32S3",
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dout",
"mcu": "esp32s3",
"variant": "esp32s3",
"partitions": "esp32_partition_app1856k_spiffs320k.csv"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"espidf",
"arduino"
],
"name": "Espressif Generic ESP32-S3 4M Flash, Tasmota 1856k Code/OTA, 320k FS",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/",
"vendor": "Espressif"
}

View File

@ -52,6 +52,13 @@
#define HSPI_HOST SPI3_HOST
#define VSPI_HOST SPI3_HOST
#elif CONFIG_IDF_TARGET_ESP32S3
// SPI_HOST (SPI1_HOST) is not supported by the SPI Master and SPI Slave driver on ESP32-S2 and later
#define SPI_HOST SPI1_HOST
#define FSPI_HOST SPI2_HOST
#define HSPI_HOST SPI3_HOST
#define VSPI_HOST SPI3_HOST
#elif CONFIG_IDF_TARGET_ESP32C3
#define SPI_HOST SPI1_HOST
#define HSPI_HOST SPI2_HOST

View File

@ -18,6 +18,7 @@ const uint16_t ST7789_colors[]={ST7789_BLACK,ST7789_WHITE,ST7789_RED,ST7789_GREE
ST7789_LIGHTGREY,ST7789_DARKGREY,ST7789_ORANGE,ST7789_GREENYELLOW,ST7789_PINK};
#ifdef ESP32
#include "esp8266toEsp32.h"
#define ST7789_DIMMER
#endif
@ -258,11 +259,9 @@ void Arduino_ST7789::commonInit(const uint8_t *cmdList) {
}
if (_bp>=0) {
#define ESP32_PWM_CHANNEL 1
// #define ESP32_PWM_CHANNEL 1
#ifdef ST7789_DIMMER
ledcSetup(ESP32_PWM_CHANNEL,4000,8);
ledcAttachPin(_bp,ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL,128);
analogWrite(_bp, 128);
#else
pinMode(_bp, OUTPUT);
#endif
@ -575,7 +574,8 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
writecommand(ST7789_DISPON); //Display on
if (_bp>=0) {
#ifdef ST7789_DIMMER
ledcWrite(ESP32_PWM_CHANNEL,dimmer);
analogWrite(_bp, dimmer);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
#else
digitalWrite(_bp,HIGH);
#endif
@ -584,7 +584,8 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
writecommand(ST7789_DISPOFF);
if (_bp>=0) {
#ifdef ST7789_DIMMER
ledcWrite(ESP32_PWM_CHANNEL,0);
analogWrite(_bp, 0);
// ledcWrite(ESP32_PWM_CHANNEL,0);
#else
digitalWrite(_bp,LOW);
#endif
@ -598,7 +599,8 @@ void Arduino_ST7789::dim(uint8_t dim) {
if (dimmer>15) dimmer=15;
dimmer=((float)dimmer/15.0)*255.0;
#ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL,dimmer);
analogWrite(_bp, dimmer);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
#endif
}

View File

@ -59,10 +59,10 @@ uint16_t Renderer::GetColorFromIndex(uint8_t index) {
void Renderer::dim(uint8_t contrast) {
uint8_t contrast8 = ((uint32_t)contrast * 255) / 15;
dim8(contrast8, contrast8);
dim10(contrast8, contrast8 * 4);
}
void Renderer::dim8(uint8_t contrast, uint8_t contrast_gamma) {
void Renderer::dim10(uint8_t contrast, uint16_t contrast_gamma) {
}

View File

@ -72,7 +72,7 @@ public:
virtual void Begin(int16_t p1,int16_t p2,int16_t p3);
virtual void Updateframe();
virtual void dim(uint8_t contrast); // input has range 0..15
virtual void dim8(uint8_t contrast, uint8_t contrast_gamma); // input has range 0..255, second arg has gamma correction for PWM
virtual void dim10(uint8_t contrast, uint16_t contrast_gamma); // input has range 0..255, second arg has gamma correction for PWM with 10 bits resolution
virtual void pushColors(uint16_t *data, uint16_t len, boolean first);
virtual void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
virtual void invertDisplay(boolean i);

View File

@ -53,6 +53,7 @@
// ESP32 uses 2. SPI BUS, ESP8266 uses software spi
#ifdef ESP32
#include "esp8266toEsp32.h"
#undef ILI9341_2_DIMMER
#define ILI9341_2_DIMMER
#undef ESP32_PWM_CHANNEL
@ -248,9 +249,7 @@ void ILI9341_2::init(uint16_t width, uint16_t height) {
if (_bp >= 0) {
#ifdef ILI9341_2_DIMMER
ledcSetup(ESP32_PWM_CHANNEL, 4000, 8);
ledcAttachPin(_bp, ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL, 128);
analogWrite(_bp, 511);
#else
pinMode(_bp, OUTPUT);
#endif
@ -544,7 +543,8 @@ void ILI9341_2::DisplayOnff(int8_t on) {
SPI_END_TRANSACTION();
if (_bp >= 0) {
#ifdef ILI9341_2_DIMMER
ledcWrite(ESP32_PWM_CHANNEL, dimmer);
analogWrite(_bp, dimmer * 4);
// ledcWrite(ESP32_PWM_CHANNEL, dimmer);
#else
digitalWrite(_bp, HIGH);
#endif
@ -557,7 +557,8 @@ void ILI9341_2::DisplayOnff(int8_t on) {
SPI_END_TRANSACTION();
if (_bp >= 0) {
#ifdef ILI9341_2_DIMMER
ledcWrite(ESP32_PWM_CHANNEL, 0);
analogWrite(_bp, 0);
// ledcWrite(ESP32_PWM_CHANNEL, 0);
#else
digitalWrite(_bp, LOW);
#endif
@ -604,7 +605,8 @@ void ILI9341_2::dim(uint8_t dim) {
dimmer=((float)dimmer/15.0)*255.0;
#ifdef ESP32
if (_bp>=0) {
ledcWrite(ESP32_PWM_CHANNEL,dimmer);
analogWrite(_bp, dimmer * 4);
// ledcWrite(ESP32_PWM_CHANNEL,dimmer);
} else {
if (_hwspi>=2) {
//ili9342_dimm(dim);

View File

@ -20,6 +20,10 @@
#include <Arduino.h>
#include "uDisplay.h"
#ifdef ESP32
#include "esp8266toEsp32.h"
#endif
// #define UDSP_DEBUG
const uint16_t udisp_colors[]={UDISP_BLACK,UDISP_WHITE,UDISP_RED,UDISP_GREEN,UDISP_BLUE,UDISP_CYAN,UDISP_MAGENTA,\
@ -428,9 +432,7 @@ Renderer *uDisplay::Init(void) {
if (bpanel >= 0) {
#ifdef ESP32
ledcSetup(ESP32_PWM_CHANNEL, 977, 8); // use 10 bits resolution like in Light
ledcAttachPin(bpanel, ESP32_PWM_CHANNEL);
ledcWrite(ESP32_PWM_CHANNEL, 8); // 38/255 correspond roughly to 50% visual brighness (with Gamma)
analogWrite(bpanel, 32);
#else
pinMode(bpanel, OUTPUT);
digitalWrite(bpanel, HIGH);
@ -1353,7 +1355,8 @@ void uDisplay::DisplayOnff(int8_t on) {
if (dsp_on != 0xff) spi_command_one(dsp_on);
if (bpanel >= 0) {
#ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
analogWrite(bpanel, dimmer10_gamma);
// ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
#else
digitalWrite(bpanel, HIGH);
#endif
@ -1363,7 +1366,8 @@ void uDisplay::DisplayOnff(int8_t on) {
if (dsp_off != 0xff) spi_command_one(dsp_off);
if (bpanel >= 0) {
#ifdef ESP32
ledcWrite(ESP32_PWM_CHANNEL, 0);
analogWrite(bpanel, 0);
// ledcWrite(ESP32_PWM_CHANNEL, 0);
#else
digitalWrite(bpanel, LOW);
#endif
@ -1402,16 +1406,17 @@ void udisp_dimm(uint8_t dim);
// }
// dim is 0..255
void uDisplay::dim8(uint8_t dim, uint8_t dim_gamma) { // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller
void uDisplay::dim10(uint8_t dim, uint16_t dim_gamma) { // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller
dimmer8 = dim;
dimmer8_gamma = dim_gamma;
dimmer10_gamma = dim_gamma;
if (ep_mode) {
return;
}
#ifdef ESP32 // TODO should we also add a ESP8266 version for bpanel?
if (bpanel >= 0) { // is the BaclPanel GPIO configured
ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
analogWrite(bpanel, dimmer10_gamma);
// ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma);
} else if (dim_cbp) {
dim_cbp(dim);
}

View File

@ -74,9 +74,6 @@ enum uColorType { uCOLOR_BW, uCOLOR_COLOR };
#define SPI_DC_LOW if (spi_dc >= 0) GPIO_CLR_SLOW(spi_dc);
#define SPI_DC_HIGH if (spi_dc >= 0) GPIO_SET_SLOW(spi_dc);
#define ESP32_PWM_CHANNEL 1
#define LUTMAXSIZE 64
class uDisplay : public Renderer {
@ -93,7 +90,7 @@ class uDisplay : public Renderer {
uint16_t bgcol(void);
int8_t color_type(void);
// void dim(uint8_t dim); // original version with 4 bits resolution 0..15
virtual void dim8(uint8_t dim, uint8_t dim_gamma); // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller
virtual void dim10(uint8_t dim, uint16_t dim_gamma); // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller with 10 bits resolution
uint16_t GetColorFromIndex(uint8_t index);
void setRotation(uint8_t m);
void fillScreen(uint16_t color);
@ -186,7 +183,7 @@ class uDisplay : public Renderer {
int8_t bpanel; // backbanel GPIO, -1 if none
int8_t spi_miso;
uint8_t dimmer8; // 8 bits resolution, 0..255
uint8_t dimmer8_gamma; // 8 bits resolution, 0..255, gamma corrected
uint16_t dimmer10_gamma; // 10 bits resolution, 0..1023, gamma corrected
SPIClass *uspi;
uint8_t sspi;
SPISettings spiSettings;

View File

@ -0,0 +1,29 @@
Attention when updating library. Changes in lib needed!!
All OpenTherm constants shall be prepended with `OPTH_` to avoid conflicts with other libs.
See commit https://github.com/arendst/Tasmota/commit/960291729ccc7cb4da50108e5299d44a79cb06de
As of OpenTherm-0.9.0, hte list is:
OPTH_NONE
OPTH_SUCCESS
OPTH_INVALID
OPTH_TIMEOUT
OPTH_READ_DATA
OPTH_READ
OPTH_WRITE_DATA
OPTH_WRITE
OPTH_INVALID_DATA
OPTH_RESERVED
OPTH_READ_ACK
OPTH_WRITE_ACK
OPTH_DATA_INVALID
OPTH_UNKNOWN_DATA_ID
OPTH_NOT_INITIALIZED
OPTH_READY
OPTH_DELAY
OPTH_REQUEST_SENDING
OPTH_RESPONSE_WAITING
OPTH_RESPONSE_START_BIT
OPTH_RESPONSE_RECEIVING
OPTH_RESPONSE_READY
OPTH_RESPONSE_INVALID

View File

@ -50,7 +50,9 @@ extern "C"
#include <lwip/inet.h>
#include <lwip/netif.h>
#include <include/ClientContext.h>
#include <c_types.h>
#ifdef ESP8266
#include <c_types.h>
#endif
#include <coredecls.h>
#if !CORE_MOCK

View File

@ -43,8 +43,8 @@
#include "lwip/netif.h"
#ifdef ESP8266
#include <include/ClientContext.h>
#include "c_types.h"
#endif
#include "c_types.h"
#include <core_version.h>
#undef DEBUG_TLS

View File

@ -1,6 +0,0 @@
#pragma once
/**/
#include <stdint.h>
#ifndef ICACHE_FLASH_ATTR
#define ICACHE_FLASH_ATTR
#endif

View File

@ -1,3 +0,0 @@
//
// Compat with ESP32
//

View File

@ -13,58 +13,66 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//
#ifdef ESP32
#include "Arduino.h"
//#include "lwip/apps/sntp.h"
#include <nvs.h>
// See libraries\ESP32\examples\ResetReason.ino
#if ESP_IDF_VERSION_MAJOR > 3 // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/rtc.h"
#endif
#include <ESP8266WiFi.h>
#include "esp8266toEsp32.h"
#include "driver/ledc.h"
// Tasmota Logging
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
// ESP Stuff
// replicated from `tasmota.h`
#if defined(CONFIG_IDF_TARGET_ESP32)
const uint8_t MAX_PWMS = 16; // ESP32: 16 ledc PWM channels in total - TODO for now
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
const uint8_t MAX_PWMS = 8; // ESP32S2: 8 ledc PWM channels in total
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
const uint8_t MAX_PWMS = 8; // ESP32S2: 8 ledc PWM channels in total
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
const uint8_t MAX_PWMS = 6; // ESP32C3: 6 ledc PWM channels in total
#else
const uint8_t MAX_PWMS = 5; // Unknown - revert to 5 PWM max
#endif
// channel mapping
static uint8_t pwm_channel[MAX_PWMS];
static uint32_t pwm_frequency = 977; // Default 977Hz
static uint8_t pwm_bit_num = 10; // Default 1023
static bool pwm_impl_inited = false; // trigger initialization
/*********************************************************************************************\
* ESP32 analogWrite emulation support
\*********************************************************************************************/
#if CONFIG_IDF_TARGET_ESP32C3
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#else // other ESP32
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32
void _analogInit(void) {
if (pwm_impl_inited) return;
// set all channels to unaffected (255)
for (uint32_t i = 0; i < MAX_PWMS; i++) {
pwm_channel[i] = 255;
}
pwm_impl_inited = true;
}
uint32_t _analog_pin2chan(uint32_t pin) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if ((_pwm_channel[channel] < 99) && (_pwm_channel[channel] == pin)) {
int32_t _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated
_analogInit(); // make sure the mapping array is initialized
for (uint32_t channel = 0; channel < MAX_PWMS; channel++) {
if ((pwm_channel[channel] < 255) && (pwm_channel[channel] == pin)) {
return channel;
}
}
return 0;
return -1;
}
void _analogWriteFreqRange(void) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] < 99) {
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
_analogInit(); // make sure the mapping array is initialized
for (uint32_t channel = 0; channel < MAX_PWMS; channel++) {
if (pwm_channel[channel] < 255) {
ledcSetup(channel, pwm_frequency, pwm_bit_num);
}
}
}
@ -80,50 +88,43 @@ uint32_t _analogGetResolution(uint32_t x) {
}
void analogWriteRange(uint32_t range) {
_pwm_bit_num = _analogGetResolution(range);
pwm_bit_num = _analogGetResolution(range);
_analogWriteFreqRange();
}
void analogWriteFreq(uint32_t freq) {
_pwm_frequency = freq;
pwm_frequency = freq;
_analogWriteFreqRange();
}
bool analogAttach(uint32_t pin) {
int32_t analogAttach(uint32_t pin) { // returns ledc channel used, or -1 if failed
_analogInit(); // make sure the mapping array is initialized
// Find if pin is already attached
uint32_t channel;
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] == pin) {
// Already attached
// Serial.printf("PWM: Already attached pin %d to channel %d\n", pin, channel);
return true;
}
}
int32_t channel = _analog_pin2chan(pin);
if (channel >= 0) { return channel; }
// Find an empty channel
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (99 == _pwm_channel[channel]) {
_pwm_channel[channel] = pin;
ledcAttachPin(pin, channel + PWM_CHANNEL_OFFSET);
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
for (channel = 0; channel < MAX_PWMS; channel++) {
if (255 == pwm_channel[channel]) {
pwm_channel[channel] = pin;
ledcAttachPin(pin, channel);
ledcSetup(channel, pwm_frequency, pwm_bit_num);
// Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel);
return true;
return channel;
}
}
// No more channels available
return false;
AddLog(LOG_LEVEL_INFO, "PWM: no more PWM (ledc) channel for GPIO %i", pin);
return -1;
}
// void analogWrite(uint8_t pin, int val);
extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val)
{
uint32_t channel = _analog_pin2chan(pin);
if ( val >> (_pwm_bit_num-1) ) ++val;
ledcWrite(channel + PWM_CHANNEL_OFFSET, val);
// Serial.printf("write %d - %d\n",channel,val);
extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) {
analogWritePhase(pin, val, 0); // if unspecified, use phase = 0
}
/*
The primary goal of this library is to add phase control to PWM ledc
The primary goal of this function is to add phase control to PWM ledc
functions.
Phase control allows to stress less the power supply of LED lights.
@ -142,35 +143,28 @@ extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val)
implementation changes.
*/
#include "driver/ledc.h"
#ifdef SOC_LEDC_SUPPORT_HS_MODE
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
#else
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
#endif
// exported from Arduno Core
extern uint8_t channels_resolution[LEDC_CHANNELS];
extern uint8_t channels_resolution[MAX_PWMS];
void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase)
{
uint32_t chan = _analog_pin2chan(pin) + PWM_CHANNEL_OFFSET;
if (duty >> (_pwm_bit_num-1) ) ++duty;
if(chan >= LEDC_CHANNELS){
return;
int32_t chan = _analog_pin2chan(pin);
if (chan < 0) { // not yet allocated, try to allocate
chan = analogAttach(pin);
if (chan < 0) { return; } // failed
}
if (duty >> (pwm_bit_num-1) ) ++duty; // input is 0..1023 but PWM takes 0..1024 - so we skip at mid-range. It creates a small non-linearity
if (phase >> (pwm_bit_num-1) ) ++phase;
uint8_t group=(chan/8), channel=(chan%8);
//Fixing if all bits in resolution is set = LEDC FULL ON
uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
phase = phase % max_duty;
if(duty == max_duty){ // no sure whether this is needed anymore TODO
duty = max_duty + 1;
}
phase = phase & max_duty;
ledc_set_duty_with_hpoint((ledc_mode_t)group, (ledc_channel_t)channel, duty, phase);
ledc_update_duty((ledc_mode_t)group, (ledc_channel_t)channel);
}
#endif // ESP32

View File

@ -13,39 +13,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ESP8266TOESP32_H__
#define __ESP8266TOESP32_H__
#ifdef ESP32
// my debug Stuff
#define Serial_Debug1(p) Serial.printf p
#define Serial_DebugX(p)
//
// basics
//
// dummy defines
//#define SPIFFS_END (SPI_FLASH_SEC_SIZE * 200)
//#define SETTINGS_LOCATION SPIFFS_END
#include <Esp.h>
#if CONFIG_IDF_TARGET_ESP32C3
#define PWM_SUPPORTED_CHANNELS 6
#define PWM_CHANNEL_OFFSET 1 // Webcam uses channel 0, so we offset standard PWM
#else // other ESP32
#define PWM_SUPPORTED_CHANNELS 8
#define PWM_CHANNEL_OFFSET 2 // Webcam uses channel 0, so we offset standard PWM
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32
extern uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS];
extern uint32_t _pwm_frequency;
extern uint8_t _pwm_bit_num;
void _analogWriteFreqRange(void);
// input range is in full range, ledc needs bits
uint32_t _analogGetResolution(uint32_t x);
void analogWriteRange(uint32_t range);
void analogWriteFreq(uint32_t freq);
bool analogAttach(uint32_t pin);
int32_t analogAttach(uint32_t pin); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated
void analogWrite(uint8_t pin, int val);
// Extended version that also allows to change phase
@ -56,8 +38,6 @@ extern void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase = 0);
#define INPUT_PULLDOWN_16 INPUT_PULLUP
typedef double real64_t;
//
// Time and Timer
//
@ -71,7 +51,6 @@ typedef double real64_t;
// Serial minimal type to hold the config
typedef int SerConfu8;
typedef int SerialConfig;
//#define analogWrite(a, b)
//
// UDP
@ -79,9 +58,6 @@ typedef int SerialConfig;
//#define PortUdp_writestr(log_data) PortUdp.write((const uint8_t *)(log_data), strlen(log_data))
#define PortUdp_write(log_data, n) PortUdp.write((const uint8_t *)(log_data), n)
//
#define wifi_forceSleepBegin()
#undef LWIP_IPV6
#define REASON_DEFAULT_RST 0 // "Power on" normal startup by power on
@ -106,4 +82,5 @@ typedef int SerialConfig;
#define STATION_IF 0
#endif
#endif // ESP32
#endif // __ESP8266TOESP32_H__

View File

@ -1,8 +0,0 @@
#pragma once
/**/
#include <lwip/ip_addr.h>
/*
#ifndef ICACHE_FLASH_ATTR
#define ICACHE_FLASH_ATTR
#endif
*/

View File

@ -41,6 +41,7 @@ be_extern_native_module(webserver);
be_extern_native_module(flash);
be_extern_native_module(path);
be_extern_native_module(unishox);
be_extern_native_module(uuid);
be_extern_native_module(animate);
#ifdef USE_LVGL
be_extern_native_module(lv);
@ -118,6 +119,7 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
&be_native_module(light),
#endif
&be_native_module(uuid),
#ifdef USE_UNISHOX_COMPRESSION
&be_native_module(unishox),
#endif // USE_UNISHOX_COMPRESSION

View File

@ -41,12 +41,15 @@ extern const bcstring be_const_str_EVENT_DELETE;
extern const bcstring be_const_str_EVENT_DRAW_MAIN;
extern const bcstring be_const_str_EVENT_DRAW_PART_BEGIN;
extern const bcstring be_const_str_EVENT_DRAW_PART_END;
extern const bcstring be_const_str_EXTERNAL_I2S;
extern const bcstring be_const_str_False;
extern const bcstring be_const_str_GET;
extern const bcstring be_const_str_HTTP_GET;
extern const bcstring be_const_str_HTTP_POST;
extern const bcstring be_const_str_I2C_Driver;
extern const bcstring be_const_str_I2C_X3A;
extern const bcstring be_const_str_INTERNAL_DAC;
extern const bcstring be_const_str_INTERNAL_PDM;
extern const bcstring be_const_str_LVG_X3A_X20call_X20to_X20unsupported_X20callback;
extern const bcstring be_const_str_LVG_X3A_X20object_X3A;
extern const bcstring be_const_str_Leds;
@ -108,6 +111,7 @@ extern const bcstring be_const_str__X23init_X2Ebat;
extern const bcstring be_const_str__X23preinit_X2Ebe;
extern const bcstring be_const_str__X2502d_X25s_X2502d;
extern const bcstring be_const_str__X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d;
extern const bcstring be_const_str__X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x;
extern const bcstring be_const_str__X25s_X2Eautoconf;
extern const bcstring be_const_str__X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B;
extern const bcstring be_const_str__X26lt_X3BNone_X26gt_X3B;
@ -149,7 +153,7 @@ extern const bcstring be_const_str__X3Coption_X20value_X3D_X27reset_X27_X3E_X26l
extern const bcstring be_const_str__X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E;
extern const bcstring be_const_str__X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E;
extern const bcstring be_const_str__X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E;
extern const bcstring be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3E_X26_X23129668_X3B_X20Auto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E;
extern const bcstring be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E;
extern const bcstring be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20;
extern const bcstring be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dzip_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20;
extern const bcstring be_const_str__X3Cp_X3E_X3Csmall_X3E_X26nbsp_X3B_X28This_X20feature_X20requires_X20an_X20internet_X20connection_X29_X3C_X2Fsmall_X3E_X3C_X2Fp_X3E;
@ -333,6 +337,7 @@ extern const bcstring be_const_str_detect;
extern const bcstring be_const_str_detected_X20on_X20bus;
extern const bcstring be_const_str_digital_read;
extern const bcstring be_const_str_digital_write;
extern const bcstring be_const_str_dimmer;
extern const bcstring be_const_str_dirty;
extern const bcstring be_const_str_display;
extern const bcstring be_const_str_display_X2Eini;
@ -652,6 +657,7 @@ extern const bcstring be_const_str_set_channels;
extern const bcstring be_const_str_set_chg_current;
extern const bcstring be_const_str_set_dc_voltage;
extern const bcstring be_const_str_set_dcdc_enable;
extern const bcstring be_const_str_set_exten;
extern const bcstring be_const_str_set_first_time;
extern const bcstring be_const_str_set_gain;
extern const bcstring be_const_str_set_height;
@ -740,6 +746,7 @@ extern const bcstring be_const_str_unknown_X20instruction;
extern const bcstring be_const_str_update;
extern const bcstring be_const_str_upper;
extern const bcstring be_const_str_url_encode;
extern const bcstring be_const_str_uuid4;
extern const bcstring be_const_str_v;
extern const bcstring be_const_str_value;
extern const bcstring be_const_str_value_error;

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,17 @@
#include "be_constobj.h"
static be_define_const_map_slots(be_class_audio_output_i2s_map) {
{ be_const_key(INTERNAL_PDM, -1), be_const_int(AudioOutputI2S::INTERNAL_PDM) },
{ be_const_key(INTERNAL_DAC, 2), be_const_int(AudioOutputI2S::INTERNAL_DAC) },
{ be_const_key(init, -1), be_const_func(i2s_output_i2s_init) },
{ be_const_key(EXTERNAL_I2S, 1), be_const_int(AudioOutputI2S::EXTERNAL_I2S) },
{ be_const_key(deinit, -1), be_const_func(i2s_output_i2s_deinit) },
{ be_const_key(stop, -1), be_const_func(i2s_output_i2s_stop) },
};
static be_define_const_map(
be_class_audio_output_i2s_map,
3
6
);
BE_EXPORT_VARIABLE be_define_const_class(

View File

@ -0,0 +1,18 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libdisplay_map) {
{ be_const_key(dimmer, -1), be_const_func(be_ntv_display_dimmer) },
{ be_const_key(start, 0), be_const_func(be_ntv_display_start) },
};
static be_define_const_map(
m_libdisplay_map,
2
);
static be_define_const_module(
m_libdisplay,
"display"
);
BE_EXPORT_VARIABLE be_define_const_native_module(display);

View File

@ -46,19 +46,29 @@ int32_t be_audio_opus_decoder_deinit(struct bvm *vm) {
// decode(payload:bytes) -> pcm:bytes()
int32_t be_audio_opus_decoder_decode(struct bvm *vm) {
int32_t argc = be_top(vm);
be_call_c_func(vm, NULL, NULL, ".(bytes)");
be_call_c_func(vm, NULL, NULL, ".(bytes)[ii]");
OpusDecoder* st = (OpusDecoder*) be_convert_single_elt(vm, 1, NULL, NULL); // get value of '.p'
size_t frames_len;
const uint8_t * opus_frame = be_tobytes(vm, 2, &frames_len);
size_t bytes_len;
const uint8_t * opus_frame = be_tobytes(vm, 2, &bytes_len);
int32_t frame_start = 0;
int32_t frame_len = bytes_len;
if (argc >= 3) { frame_start = be_toint(vm, 3); if (frame_start < 0) frame_start = 0; }
if (argc >= 4) { frame_len = be_toint(vm, 4); }
int samples = opus_decoder_get_nb_samples(st, opus_frame, frames_len);
if (frame_start >= bytes_len) { frame_len = 0; } // send empty packet
else if (frame_len < 0) { frame_len = bytes_len - frame_start; } // send all packet, adjust len
else if (frame_start + frame_len > bytes_len) { frame_len = bytes_len - frame_start; } // len is too long, adjust
// adjust start
opus_frame = opus_frame + frame_start;
int samples = opus_decoder_get_nb_samples(st, opus_frame, frame_len);
// tasmota_log_C(LOG_LEVEL_DEBUG, "AUD: frame contains %i samples", samples);
// allocate a buffer for the content
void * pcm = be_pushbytes(vm, NULL, samples * 2);
int ret = opus_decode(st, opus_frame, frames_len, pcm, samples, 0);
int ret = opus_decode(st, opus_frame, frame_len, pcm, samples, 0);
if (ret != samples) { be_raisef(vm, "internal_error", "wrong number of frames %i (supposed to be %i", ret, samples); }
be_return(vm);

View File

@ -897,7 +897,7 @@ be_local_closure(Autoconf_web_add_config_button, /* name */
( &(const bvalue[ 3]) { /* constants */
/* K0 */ be_nested_str(webserver),
/* K1 */ be_nested_str(content_send),
/* K2 */ be_nested_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3E_X26_X23129668_X3B_X20Auto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E),
/* K2 */ be_nested_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E),
}),
&be_const_str_web_add_config_button,
&be_const_str_solidified,

View File

@ -9,21 +9,15 @@
#ifdef USE_DISPLAY
// Tasmota specific
extern int be_ntv_display_start(bvm *vm);
extern int be_ntv_display_dimmer(bvm *vm);
/********************************************************************
** Solidified module: display
********************************************************************/
be_local_module(display,
"display",
be_nested_map(1,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key(start, -1), be_const_func(be_ntv_display_start) },
}))
);
BE_EXPORT_VARIABLE be_define_const_native_module(display);
/********************************************************************/
/* @const_object_info_begin
module display (scope: global) {
start, func(be_ntv_display_start)
dimmer, func(be_ntv_display_dimmer)
}
@const_object_info_end */
#include "be_fixed_display.h"
#endif // USE_DISPLAY
#endif // USE_DISPLAY

File diff suppressed because it is too large Load Diff

View File

@ -147,6 +147,8 @@ int32_t be_audio_output_consume_silence(struct bvm *vm) {
return be_call_c_func(vm, (void*) &be_audio_output_consume_silence_ntv, "i", ".");
}
#include "AudioOutputI2S.h"
extern "C" {
#include "be_fixed_be_class_audio_output.h"
@ -211,6 +213,10 @@ class be_class_audio_file_source (scope: global, name: AudioFileSource) {
}
class be_class_audio_output_i2s (scope: global, name: AudioOutputI2S, super: be_class_audio_output) {
EXTERNAL_I2S, int(AudioOutputI2S::EXTERNAL_I2S)
INTERNAL_DAC, int(AudioOutputI2S::INTERNAL_DAC)
INTERNAL_PDM, int(AudioOutputI2S::INTERNAL_PDM)
init, func(i2s_output_i2s_init)
deinit, func(i2s_output_i2s_deinit)
stop, func(i2s_output_i2s_stop)

View File

@ -0,0 +1,77 @@
/********************************************************************
* uuid module
*
*******************************************************************/
#include "be_constobj.h"
/********************************************************************
** Solidified function: _anonymous_
********************************************************************/
be_local_closure(uuid__anonymous_, /* name */
be_nested_proto(
12, /* nstack */
0, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 5]) { /* constants */
/* K0 */ be_nested_str(string),
/* K1 */ be_nested_str(math),
/* K2 */ be_nested_str(format),
/* K3 */ be_nested_str(_X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x),
/* K4 */ be_nested_str(rand),
}),
&be_const_str__anonymous_,
&be_const_str_solidified,
( &(const binstruction[30]) { /* code */
0xA4020000, // 0000 IMPORT R0 K0
0xA4060200, // 0001 IMPORT R1 K1
0x8C080102, // 0002 GETMET R2 R0 K2
0x58100003, // 0003 LDCONST R4 K3
0x8C140304, // 0004 GETMET R5 R1 K4
0x7C140200, // 0005 CALL R5 1
0x8C180304, // 0006 GETMET R6 R1 K4
0x7C180200, // 0007 CALL R6 1
0x541EFFFE, // 0008 LDINT R7 65535
0x2C180C07, // 0009 AND R6 R6 R7
0x8C1C0304, // 000A GETMET R7 R1 K4
0x7C1C0200, // 000B CALL R7 1
0x54220FFE, // 000C LDINT R8 4095
0x2C1C0E08, // 000D AND R7 R7 R8
0x54223FFF, // 000E LDINT R8 16384
0x301C0E08, // 000F OR R7 R7 R8
0x8C200304, // 0010 GETMET R8 R1 K4
0x7C200200, // 0011 CALL R8 1
0x54263FFE, // 0012 LDINT R9 16383
0x2C201009, // 0013 AND R8 R8 R9
0x54267FFF, // 0014 LDINT R9 32768
0x30201009, // 0015 OR R8 R8 R9
0x8C240304, // 0016 GETMET R9 R1 K4
0x7C240200, // 0017 CALL R9 1
0x542AFFFE, // 0018 LDINT R10 65535
0x2C24120A, // 0019 AND R9 R9 R10
0x8C280304, // 001A GETMET R10 R1 K4
0x7C280200, // 001B CALL R10 1
0x7C081000, // 001C CALL R2 8
0x80040400, // 001D RET 1 R2
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified module: uuid
********************************************************************/
be_local_module(uuid,
"uuid",
be_nested_map(1,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_const_key(uuid4, -1), be_const_closure(uuid__anonymous__closure) },
}))
);
BE_EXPORT_VARIABLE be_define_const_native_module(uuid);
/********************************************************************/

View File

@ -141,7 +141,7 @@ autoconf_module.init = def (m)
# Displays a "Autocong" button on the configuration page
def web_add_config_button()
import webserver
webserver.content_send("<p><form id=ac action='ac' style='display: block;' method='get'><button>&#129668; Auto-configuration</button></form></p>")
webserver.content_send("<p><form id=ac action='ac' style='display: block;' method='get'><button>Auto-configuration</button></form></p>")
end

View File

@ -67,6 +67,11 @@ class AXP192 : I2C_Driver
end
end
# set EXTEN which enables external power on M5Stack, powering Hat with 5V
def set_exten(state)
self.write_bit(0x12, 6, state)
end
# set DCDC enable, 1/2/3
def set_dcdc_enable(dcdc, state)
if dcdc == 1 self.write_bit(0x12, 0, state) end

View File

@ -0,0 +1,15 @@
uuid = module("uuid")
uuid.uuid4 = def ()
import string
import math
return string.format("%08x-%04x-%04x-%04x-%04x%08x",
math.rand(),
math.rand() & 0xFFFF,
math.rand() & 0x0FFF | 0x4000,
math.rand() & 0x3FFF | 0x8000,
math.rand() & 0xFFFF,
math.rand() )
end
return uuid

View File

@ -26,12 +26,21 @@
extern "C" {
#endif
/*
#define LOG_LEVEL_DEBUG (0)
#define LOG_LEVEL_INFO (1)
#define LOG_LEVEL_WARN (2)
#define LOG_LEVEL_ERROR (3)
#define LOG_LEVEL_CRITICAL (4)
#define LOG_LEVEL_NONE (5)
*/
#define LOG_LEVEL_DEBUG (3)
#define LOG_LEVEL_INFO (2)
#define LOG_LEVEL_WARN (4)
#define LOG_LEVEL_ERROR (1)
#define LOG_LEVEL_CRITICAL (5)
#define LOG_LEVEL_NONE (0)
/* Up to 7 custom log levels. */
#define LOG_LEVEL_MAX (15)

View File

@ -0,0 +1,2 @@
Attention when updating library. Changes in lib needed!!
See https://github.com/arendst/Tasmota/commit/497d8218f9f92d9db4664fa519bbafbf78711db7

File diff suppressed because it is too large Load Diff

View File

@ -1,88 +0,0 @@
/*
THIS EXAMPLE IS REALLY JUST DOCUMENTATION TO GUIDE YOU THROUGH CHANGES IN
Adafruit_LvGL_Glue 2.0.
If youre coming to Adafruit_LvGL_Glue for the first time as a current user
of LittlevGL on other platforms, you're fine, there are no surprises here,
have a look at the other examples to see how to set up an Adafruit display
for your LittlevGL application.
BUT...if you've used a prior version of Adafruit_LvGL_Glue, and had written
Arduino sketches around it, you're unfortunately in for some extra work.
The "glue" hasn't changed at all, but LittlevGL has seen repeated overhauls,
and projects using earlier versions of Adafruit_LvGL_Glue will no longer
compile in the new system without substantial changes. Many function names,
constants, and styles in particular, will require updating. Many LittlevGL
projects are too much to fit on M0 (SAMD21) boards now -- it's best to work
with a device with more RAM -- M4 (SAMD51), nRF52 and ESP32 are currently
supported.
If desperate to get old code working, you can downgrade to lv_arduino 2.1.5
and Adafruit_LvGL_Glue 1.0.2, but this is NOT recommended when developing
for the long haul -- lv_arduino is altogether deprecated now and won't be
staging a comeback.
For starters, LittlevGL has moved to an entirely different Arduino library.
"lv_arduino" should be UNINSTALLED, and in its place, "lvgl" should be
INSTALLED. The latter is at version 7.11.0 as this is being written...if
there's a newer release, and if you find our glue examples failing to
compile, it's recommended to install that version until this library can be
updated. The LittlevGL developers' preference favors structural and
consistency upgrades over backwards compatibility -- and that's fine, their
project, their rules -- just explaining why this overhaul is necessary.
To repeat: in the Arduino Library Manager, uninstall lv_arduino, install
lvgl.
Also in the Arduino Library Manager, you'll see a related library called
"lv_examples" from the same developer. You can install that if you want, but
understand that this is not actually a library, nor will any of the examples
there compile in the Arduino IDE! But if you pick through these files
manually, there's C code you can dissect for insights in creating interfaces
with LittlevGL, and might create mash-ups with Adafruit_LvGL_Glue examples.
Adafruit_LvGL_Glue includes a lv_conf.file (LittlevGL configuration) that
should "just work" and enables some settings for best compatibility with
Adafruit displays. The only "gotcha" here is that user sketches MUST
#include Adafruit_LvGL_Glue.h BEFORE including lvgl.h, in order for
LittlevGL to pick up on the location of this header file.
BELOW IS A HYPOTHETICAL AND MINIMAL BUT ESSENTIALLY VALID ADAFRUIT_LVGL_GLUE
ARDUINO SKETCH. Please see the other examples for more realistic use. Actual
projects will have different display interfacing, backlight control, a set
of UI widgets and so forth.
*/
#include <Adafruit_LvGL_Glue.h> // Glue library header INCLUDE THIS FIRST!
#include <lvgl.h> // LittlevGL header
#include <Adafruit_ST7789.h> // Display-specific header
#define TFT_CS 1 // Display chip-select pin
#define TFT_DC 2 // Display data/command pin
#define TFT_RST 3 // Display reset pin
Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_RST); // TFT on default SPI port
Adafruit_LvGL_Glue glue;
void setup(void) {
Serial.begin(115200);
tft.init(240, 240); // Initialize display
// Initialize glue, passing in address of display
LvGLStatus status = glue.begin(&tft);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
// Create simple label centered on screen
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(label, "Hello Arduino!");
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
}
void loop(void) {
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}

View File

@ -1,58 +0,0 @@
// Minimal "Hello" example for LittlevGL on Adafruit CLUE. Requires
// LittlevGL, Adafruit_LvGL_Glue, Adafruit_GFX and Adafruit_ST7735
// libraries.
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <Adafruit_ST7789.h>
#define TFT_ROTATION 1 // Landscape orientation on CLUE
#define TFT_SPI SPI1 // CLUE display peripheral & pins
#define TFT_CS 31
#define TFT_DC 32
#define TFT_RST 33
#define TFT_BACKLIGHT 34
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
Adafruit_LvGL_Glue glue;
// This example sketch's LittlevGL UI-building calls are all in this
// function rather than in setup(), so simple programs can just
// copy-and-paste this sketch as a starting point, then embellish here:
void lvgl_setup(void) {
// Create simple label centered on screen
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(label, "Hello Arduino!");
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
}
void setup(void) {
Serial.begin(115200);
// Initialize display BEFORE glue setup
tft.init(240, 240);
tft.setRotation(TFT_ROTATION);
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, HIGH);
// Initialize glue, passing in address of display
LvGLStatus status = glue.begin(&tft);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
}
void loop(void) {
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}
// NOTE TO FUTURE SELF: this sketch is essentially the same as the Gizmo
// widgets example. If updating one sketch, make sure the other is kept
// in sync. Aside from screen setup, differences include backlight control
// and A/B button setup & read (CPX is active high, CLUE is active low).

View File

@ -1,74 +0,0 @@
// Minimal "Hello" example for LittlevGL on Adafruit TFT FeatherWings.
// Requires LittlevGL, Adafruit_LvGL_Glue, Adafruit_STMPE610, Adafruit_GFX
// and Adafruit_ILI9341 (2.4" TFT) or Adafruit_HX8357 (3.5") libraries.
// This example doesn't use any touchscreen input, but it's declared anyway
// so this sketch can be copied-and-pasted to serve as a starting point for
// other projects. If display is scrambled, check that correct FeatherWing
// type is selected below (set BIG_FEATHERWING to 0 or 1 as needed).
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
#define BIG_FEATHERWING 0 // Set this to 1 for 3.5" (480x320) FeatherWing!
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <Adafruit_STMPE610.h>
#ifdef ESP32
#define TFT_CS 15
#define TFT_DC 33
#define STMPE_CS 32
#else
#define TFT_CS 9
#define TFT_DC 10
#define STMPE_CS 6
#endif
#define TFT_ROTATION 1 // Landscape orientation on FeatherWing
#define TFT_RST -1
#if BIG_FEATHERWING
#include <Adafruit_HX8357.h>
Adafruit_HX8357 tft(TFT_CS, TFT_DC, TFT_RST);
#else
#include <Adafruit_ILI9341.h>
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
#endif
Adafruit_STMPE610 ts(STMPE_CS);
Adafruit_LvGL_Glue glue;
// This example sketch's LittlevGL UI-building calls are all in this
// function rather than in setup(), so simple programs can just
// copy-and-paste this sketch as a starting point, then embellish here:
void lvgl_setup(void) {
// Create simple label centered on screen
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(label, "Hello Arduino!");
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
}
void setup(void) {
Serial.begin(115200);
// Initialize display and touchscreen BEFORE glue setup
tft.begin();
tft.setRotation(TFT_ROTATION);
if(!ts.begin()) {
Serial.println("Couldn't start touchscreen controller");
for(;;);
}
// Initialize glue, passing in address of display & touchscreen
LvGLStatus status = glue.begin(&tft, &ts);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
}
void loop(void) {
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}

View File

@ -1,57 +0,0 @@
// Minimal "Hello" example for LittlevGL on TFT Gizmo display for Adafruit
// Circuit Playground Express or Circuit Playground Bluefruit. Requires
// LittlevGL, Adafruit_LvGL_Glue, Adafruit_GFX and Adafruit_ST7735 libraries.
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <Adafruit_ST7789.h>
#define TFT_ROTATION 2
#define TFT_CS A6
#define TFT_DC A7
#define TFT_RST -1
#define TFT_BACKLIGHT A3
#if defined(NRF52_SERIES) // Circuit Playground Bluefruit
#define TFT_SPI SPI
#else // Circuit Playground Express
#define TFT_SPI SPI1
#endif
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
Adafruit_LvGL_Glue glue;
// This example sketch's LittlevGL UI-building calls are all in this
// function rather than in setup(), so simple programs can just
// copy-and-paste this sketch as a starting point, then embellish here:
void lvgl_setup(void) {
// Create simple label centered on screen
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(label, "Hello Arduino!");
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
}
void setup(void) {
Serial.begin(115200);
// Initialize display BEFORE glue setup
tft.init(240, 240);
tft.setRotation(TFT_ROTATION);
pinMode(TFT_BACKLIGHT, OUTPUT);
analogWrite(TFT_BACKLIGHT, 255); // USE analogWrite() FOR GIZMO BACKLIGHT!
// Initialize glue, passing in address of display
LvGLStatus status = glue.begin(&tft);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
}
void loop(void) {
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}

View File

@ -1,74 +0,0 @@
// Minimal "Hello" example for LittlevGL on Adafruit PyPortal. Requires
// LittlevGL, Adafruit_LvGL_Glue, Adafruit Touchscreen, Adafruit_GFX and
// Adafruit_ILI9341 (PyPortal, PyPortal Pynt) or Adafruit_HX8357 (PyPortal
// Titano) libraries. This example doesn't use any touchscreen input, but
// it's declared anyway so this sketch can be copied-and-pasted to serve
// as a starting point for other projects. If display is scrambled, check
// that the correct board is selected -- PyPortal vs PyPortal Titano.
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <TouchScreen.h>
#define TFT_ROTATION 3 // Landscape orientation on PyPortal
#define TFT_D0 34 // PyPortal TFT pins
#define TFT_WR 26
#define TFT_DC 10
#define TFT_CS 11
#define TFT_RST 24
#define TFT_RD 9
#define TFT_BACKLIGHT 25
#define YP A4 // PyPortal touchscreen pins
#define XP A5
#define YM A6
#define XM A7
#if defined(ADAFRUIT_PYPORTAL_M4_TITANO)
#include <Adafruit_HX8357.h>
Adafruit_HX8357 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
TFT_RD);
#else
#include <Adafruit_ILI9341.h>
Adafruit_ILI9341 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
TFT_RD);
#endif
TouchScreen ts(XP, YP, XM, YM, 300);
Adafruit_LvGL_Glue glue;
// This example sketch's LittlevGL UI-building calls are all in this
// function rather than in setup(), so simple programs can just
// copy-and-paste this sketch as a starting point, then embellish here:
void lvgl_setup(void) {
// Create simple label centered on screen
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(label, "Hello Arduino!");
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
}
void setup(void) {
Serial.begin(115200);
// Initialize display BEFORE glue setup
tft.begin();
tft.setRotation(TFT_ROTATION);
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, HIGH);
// PyPortal touchscreen needs no init
// Initialize glue, passing in address of display & touchscreen
LvGLStatus status = glue.begin(&tft, &ts);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
}
void loop(void) {
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}

View File

@ -1,203 +0,0 @@
// A more functional example for LittlevGL on Adafruit CLUE. Lacking
// touchscreen input, interaction options are limited (but not impossible).
// In this case we'll pretend like it's a status display for something --
// the left and right buttons switch between three different tabs, each
// displaying different information. The code's a bit more complex than
// the hello_clue example, so best get that working before trying this.
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <Adafruit_ST7789.h>
#define TFT_ROTATION 1 // Landscape orientation on CLUE
#define TFT_SPI SPI1 // CLUE display peripheral & pins
#define TFT_CS 31
#define TFT_DC 32
#define TFT_RST 33
#define TFT_BACKLIGHT 34
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
Adafruit_LvGL_Glue glue;
lv_obj_t *tabview, // LittlevGL tabview object
*gauge, // Gauge object (on first of three tabs)
*chart, // Chart object (second tab)
*canvas; // Canvas object (third tab)
uint8_t active_tab = 0, // Index of currently-active tab (0-2)
prev_tab = 0; // Index of previously-active tab
lv_chart_series_t *series; // 'Series' data for the bar chart
// Canvas object is an image in memory. Although LittlevGL supports several
// internal image formats, only the TRUE_COLOR variety allows drawing
// operations. Fortunately nRF52840 has gobs of RAM for this. The canvas
// buffer is global because it's referenced both in the setup and main loop.
#define CANVAS_WIDTH 200 // Dimensions in pixels
#define CANVAS_HEIGHT 150
lv_color_t canvas_buffer[
LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];
lv_draw_line_dsc_t draw_dsc; // Drawing style (for canvas) is similarly global
void lvgl_setup(void) {
// Create a tabview object, by default this covers the full display.
tabview = lv_tabview_create(lv_disp_get_scr_act(NULL), NULL);
// The CLUE display has a lot of pixels and can't refresh very fast.
// To show off the tabview animation, let's slow it down to 1 second.
lv_tabview_set_anim_time(tabview, 1000);
// Because they're referenced any time an object is drawn, styles need
// to be permanent in scope; either declared globally (outside all
// functions), or static. The styles used on tabs are never modified after
// they're used here, so let's use static on those...
static lv_style_t tab_style, tab_background_style, indicator_style;
// This is the background style "behind" the tabs. This is what shows
// through for "off" (inactive) tabs -- a vertical green gradient,
// minimal padding around edges (zero at bottom).
lv_style_init(&tab_background_style);
lv_style_set_bg_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x408040));
lv_style_set_bg_grad_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x304030));
lv_style_set_bg_grad_dir(&tab_background_style, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
lv_style_set_pad_top(&tab_background_style, LV_STATE_DEFAULT, 2);
lv_style_set_pad_left(&tab_background_style, LV_STATE_DEFAULT, 2);
lv_style_set_pad_right(&tab_background_style, LV_STATE_DEFAULT, 2);
lv_style_set_pad_bottom(&tab_background_style, LV_STATE_DEFAULT, 0);
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BG, &tab_background_style);
// Style for tabs. Active tab is white with opaque background, inactive
// tabs are transparent so the background shows through (only the white
// text is seen). A little top & bottom padding reduces scrunchyness.
lv_style_init(&tab_style);
lv_style_set_pad_top(&tab_style, LV_STATE_DEFAULT, 3);
lv_style_set_pad_bottom(&tab_style, LV_STATE_DEFAULT, 10);
lv_style_set_bg_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_WHITE);
lv_style_set_bg_opa(&tab_style, LV_STATE_CHECKED, LV_OPA_100);
lv_style_set_text_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_GRAY);
lv_style_set_bg_opa(&tab_style, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_text_color(&tab_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BTN, &tab_style);
// Style for the small indicator bar that appears below the active tab.
lv_style_init(&indicator_style);
lv_style_set_bg_color(&indicator_style, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_style_set_size(&indicator_style, LV_STATE_DEFAULT, 5);
lv_obj_add_style(tabview, LV_TABVIEW_PART_INDIC, &indicator_style);
// Back to creating widgets...
// Add three tabs to the tabview
lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Gauge");
lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Chart");
lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Canvas");
// And then add stuff in each tab...
// The first tab holds a gauge. To keep the demo simple, let's just use
// the default style and range (0-100). See LittlevGL docs for options.
gauge = lv_gauge_create(tab1, NULL);
lv_obj_set_size(gauge, 186, 186);
lv_obj_align(gauge, NULL, LV_ALIGN_CENTER, 0, 0);
// Second tab, make a chart...
chart = lv_chart_create(tab2, NULL);
lv_obj_set_size(chart, 200, 180);
lv_obj_align(chart, NULL, LV_ALIGN_CENTER, 0, 0);
lv_chart_set_type(chart, LV_CHART_TYPE_COLUMN);
// For simplicity, we'll stick with the chart's default 10 data points:
series = lv_chart_add_series(chart, LV_COLOR_RED);
lv_chart_init_points(chart, series, 0);
// Make each column shift left as new values enter on right:
lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
// Third tab is a canvas, which we'll fill with random colored lines.
// LittlevGL draw functions only work on TRUE_COLOR canvas.
canvas = lv_canvas_create(tab3, NULL);
lv_canvas_set_buffer(canvas, canvas_buffer,
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
lv_obj_align(canvas, NULL, LV_ALIGN_CENTER, 0, 0);
lv_canvas_fill_bg(canvas, LV_COLOR_WHITE, LV_OPA_100);
// Set up canvas line-drawing style based on defaults.
// Later we'll change color settings when drawing each line.
lv_draw_line_dsc_init(&draw_dsc);
}
void setup(void) {
Serial.begin(115200);
// Initialize display BEFORE glue setup
tft.init(240, 240);
tft.setRotation(TFT_ROTATION);
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, HIGH);
// Initialize glue, passing in address of display
LvGLStatus status = glue.begin(&tft);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
// Enable "A" and "B" buttons as inputs
pinMode(PIN_BUTTON1, INPUT_PULLUP);
pinMode(PIN_BUTTON2, INPUT_PULLUP);
}
uint32_t prev_time = -1;
void loop(void) {
// Read left/right button inputs. Debouncing could be done better,
// but this'll do for a quick simple demo...
if(digitalRead(PIN_BUTTON1) == LOW) {
if(active_tab > 0) {
active_tab--;
}
while(digitalRead(PIN_BUTTON1) == LOW); // Wait for button release
} else if(digitalRead(PIN_BUTTON2) == LOW) {
if(active_tab < 2) {
active_tab++;
}
while(digitalRead(PIN_BUTTON2) == LOW); // Wait for button release
}
// Change active tab if button pressings happened
if(active_tab != prev_tab) {
lv_tabview_set_tab_act(tabview, active_tab, true);
prev_tab = active_tab;
}
// Make the gauge sweep a full sine wave over time
lv_gauge_set_value(gauge, 0, (int)(50.5 + sin(millis() / 1000.0) * 50.0));
// About 2X a second, things happen on the other two tabs...
uint32_t new_time = millis() / 500; // Current half-second
if(new_time != prev_time) { // freshly elapsed
prev_time = new_time;
// Add a new random item to the bar chart (old value shift left)
lv_chart_set_next(chart, series, random(100));
lv_chart_refresh(chart);
// Add a random line to the canvas
lv_point_t points[2];
points[0].x = random(CANVAS_WIDTH);
points[0].y = random(CANVAS_HEIGHT);
points[1].x = random(CANVAS_WIDTH);
points[1].y = random(CANVAS_HEIGHT);
draw_dsc.color.ch.red = random();
draw_dsc.color.ch.green = random();
draw_dsc.color.ch.blue = random();
lv_canvas_draw_line(canvas, points, 2, &draw_dsc);
// This forces the canvas to update (otherwise changes aren't
// seen unless leaving and returning to the canvas tab):
lv_canvas_set_buffer(canvas, canvas_buffer,
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
}
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}

View File

@ -1,261 +0,0 @@
// A more interesting example for LittlevGL on Adafruit TFT FeatherWings,
// showing use of the touchscreen. Code's a little more complex than the
// hello_featherwing example, so best get that working before trying this.
// By default, as written, on a 320x240 TFT FeatherWing the example is a
// pretend calculator keypad, while 480x320 TFT has a whole keyboard
// (though you'll probably need a stylus). These just seemed the right
// level of detail for their respective screens, but feel free to override
// and try either for yourself. If display is scrambled, check that
// correct FeatherWing type is selected below (set BIG_FEATHERWING to 0
// or 1 as needed).
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
// Feather M0 is no longer recommended for LittlevGL use. The calculator
// almost works (hangs after a few button presses) and keyboard won't run
// at all. A Feather M4 or other >32K RAM device is recommended.
#define BIG_FEATHERWING 0 // Set this to 1 for 3.5" (480x320) FeatherWing!
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <Adafruit_STMPE610.h>
#define DEMO_CALC 0
#define DEMO_TEXT 1
#ifdef ESP32
#define TFT_CS 15
#define TFT_DC 33
#define STMPE_CS 32
#else
#define TFT_CS 9
#define TFT_DC 10
#define STMPE_CS 6
#endif
#define TFT_RST -1
#if BIG_FEATHERWING
#include <Adafruit_HX8357.h>
Adafruit_HX8357 tft(TFT_CS, TFT_DC, TFT_RST);
#define DEMO DEMO_TEXT // On big TFT, do text/keyboard example
#else
#include <Adafruit_ILI9341.h>
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
#define DEMO DEMO_CALC // Smaller TFT, do keypad example
#endif
Adafruit_STMPE610 ts(STMPE_CS);
Adafruit_LvGL_Glue glue;
#if DEMO == DEMO_CALC
// "Pretend" calculator example. Please, PLEASE...do NOT implement the whole
// calculator and submit as a pull request, because it will NOT be merged!
// This sketch is meant only to be illustrative and not functional, just
// showing LittlevGL + Adafruit display/touch tied together with a modest
// amount of code. Even a simple but working calc would require WAY more
// code, distracting from that core idea (and is a waste of hardware).
// Daiso has better calculators for $1.50.
#define TFT_ROTATION 0 // Portrait orientation
lv_obj_t *digits_label = NULL; // LittlevGL label object showing digits
String digits = "0"; // Current digits string value
bool hasDecimal = false; // Only allow one decimal point
const char *buttons[] = { // Button matrix labels
"7", "8", "9", "/", "\n",
"4", "5", "6", "x", "\n",
"1", "2", "3", "-", "\n",
"0", ".", "=", "+", "" };
// This function processes events from the button matrix
void button_event_handler(lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_VALUE_CHANGED) {
const char *txt = lv_btnmatrix_get_active_btn_text(obj);
if(txt) { // NULL if pressed in frame area outside buttons
if(txt[0] == '.') {
// Decimal button pressed. Add decimal point to "digits" string
// if it's not too long and there's no decimal present yet...
if((digits.length() < 15) && !hasDecimal) {
digits += '.';
hasDecimal = true;
}
} else if((txt[0] >= '0') && (txt[0] <= '9')) {
// Number button (0-9) pressed. If there's nothing currently
// being displayed, take the digit literally. Otherwise, append
// the new digit if the "digits" string is not too long.
if(digits.equals("0")) {
digits = txt[0];
} else if(digits.length() < 15) {
digits += txt[0];
}
} else {
// Any other button, just reset the calculator display.
// It's all just pretend.
digits = "0";
hasDecimal = false;
}
if(digits_label != NULL) {
lv_label_set_text(digits_label, digits.c_str());
}
}
}
}
void lvgl_setup(void) {
// Just using the default styles for everything here, keeping
// it basic. See the official LittlevGL docs and examples for
// insights on applying styles.
// The calculator digits are held inside a LvGL container object
// as this gives us a little more control over positioning.
lv_obj_t *container = lv_cont_create(lv_scr_act(), NULL);
lv_cont_set_fit(container, LV_FIT_NONE); // Don't auto fit
lv_obj_set_size(container, tft.width(), 50); // Full width x 50 px
// Calculator digits are just a text label inside the container,
// refreshed whenever the global "digits" string changes.
digits_label = lv_label_create(container, NULL);
lv_label_set_text(digits_label, digits.c_str());
lv_obj_align(digits_label, NULL, LV_ALIGN_IN_TOP_LEFT, 20, 10);
lv_label_set_long_mode(digits_label, LV_LABEL_LONG_CROP);
lv_obj_set_size(digits_label, tft.width() - 40, 30);
lv_label_set_align(digits_label, LV_LABEL_ALIGN_RIGHT);
// Fill the remaining space with the button matrix.
lv_obj_t *button_matrix = lv_btnmatrix_create(lv_scr_act(), NULL);
lv_btnmatrix_set_map(button_matrix, buttons);
lv_obj_align(button_matrix, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 50);
lv_obj_set_size(button_matrix, tft.width(), tft.height() - 50);
lv_obj_set_event_cb(button_matrix, button_event_handler);
}
#else // Keyboard demo
// Keyboard example, lets you enter and edit text in a field. Even on a
// 480x320 TFT it requires a stylus to be even half useful (fingertip
// is possible if very patient), but having the option of a keyboard at
// all on this device is pretty nifty!
#define TFT_ROTATION 1 // Landscape orientation
lv_obj_t *textarea,
*keyboard = NULL; // Created/deleted as needed
#if LV_USE_ANIMATION
// Called after keyboard slides closed - deletes keyboard object
void delete_keyboard(lv_anim_t * a) {
lv_obj_del((lv_obj_t *)a->var);
keyboard = NULL;
}
#endif
// Called when the close or ok button is pressed on the keyboard
void keyboard_event_handler(lv_obj_t *obj, lv_event_t event) {
lv_keyboard_def_event_cb(keyboard, event);
if(event == LV_EVENT_APPLY || event == LV_EVENT_CANCEL) {
#if LV_USE_ANIMATION
// If animation is enabled, make keyboard slide away
lv_anim_path_t path;
lv_anim_path_init(&path);
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
lv_anim_set_var(&a, keyboard);
lv_anim_set_time(&a, 300);
lv_anim_set_values(&a, lv_obj_get_y(keyboard), LV_VER_RES); // start, end
lv_anim_set_path(&a, &path);
lv_anim_set_ready_cb(&a, delete_keyboard);
lv_anim_start(&a);
#else
lv_obj_del(keyboard);
keyboard = NULL;
#endif
}
}
// Other clicks in the text area
void text_area_event_handler(lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_CLICKED) {
// Unsure why, but text area has an initial clicked event on
// creation, causing the keyboard to appear. This is a hacky
// workaround that just ignores the first click event, so
// subsequent actual clicks bring up the keyboard.
static bool first = true;
if(first) {
first = false;
return;
}
if(keyboard == NULL) {
// If not present, create keyboard object at bottom of screen
keyboard = lv_keyboard_create(lv_scr_act(), NULL);
lv_obj_set_size(keyboard, tft.width(), tft.height() * 7 / 16);
lv_obj_align(keyboard, textarea, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_keyboard_set_textarea(keyboard, textarea);
lv_keyboard_set_cursor_manage(keyboard, true);
lv_obj_set_event_cb(keyboard, keyboard_event_handler);
#if LV_USE_ANIMATION
// If animation is enabled, make keyboard slide into place
lv_anim_path_t path;
lv_anim_path_init(&path);
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
lv_anim_set_var(&a, keyboard);
lv_anim_set_time(&a, 300);
lv_anim_set_values(&a, LV_VER_RES, lv_obj_get_y(keyboard)); // start, end
lv_anim_set_path(&a, &path);
lv_anim_start(&a);
#endif
}
}
}
void lvgl_setup(void) {
textarea = lv_textarea_create(lv_scr_act(), NULL);
lv_obj_set_size(textarea, LV_HOR_RES, LV_VER_RES); // Whole screen
lv_obj_align(textarea, NULL, LV_ALIGN_CENTER, 0, 0);
lv_textarea_set_text(textarea, "This text is editable.");
lv_obj_set_event_cb(textarea, text_area_event_handler);
lv_textarea_set_cursor_pos(textarea, LV_TEXTAREA_CURSOR_LAST);
}
#endif // end calculator / keyboard examples
void setup(void) {
Serial.begin(115200);
// Initialize display and touchscreen BEFORE glue setup
tft.begin();
tft.setRotation(TFT_ROTATION);
if(!ts.begin()) {
Serial.println("Couldn't start touchscreen controller");
for(;;);
}
// Initialize glue, passing in address of display & touchscreen
LvGLStatus status = glue.begin(&tft, &ts);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
}
void loop(void) {
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}
// NOTE TO FUTURE SELF: this sketch is largely similar to the PyPortal
// widgets example. If updating/bugfixing one sketch, make sure the other
// is kept in sync.

View File

@ -1,225 +0,0 @@
// A more functional example for LittlevGL on Adafruit Circuit Playground
// Bluefruit (NOT Express!) with TFT Gizmo display. Lacking touchscreen
// input, interaction options are limited (but not impossible). In this
// case we'll pretend like it's a status display for something -- the left
// and right buttons (on the back if looking at the screen) switch between
// three different tabs, each displaying different information. The code's
// a bit more complex than the hello_gizmo example, so best get that
// working before trying this. This example is a bit too much for the
// Circuit Playground Express to handle now, but simpler LittlevGL
// programs should be possible.
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
#if !defined(NRF52_SERIES) // Circuit Playground Bluefruit
#error "This example is for Circuit Playground Bluefruit only."
#endif
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <Adafruit_ST7789.h>
#define TFT_ROTATION 2
#define TFT_CS A6
#define TFT_DC A7
#define TFT_RST -1
#define TFT_BACKLIGHT A3
#if defined(NRF52_SERIES) // Circuit Playground Bluefruit
#define TFT_SPI SPI
#else // Circuit Playground Express
#define TFT_SPI SPI1
// Though this example won't work on CP Express, including this
// here anyway for reference, if copying over to a simpler sketch.
#endif
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
Adafruit_LvGL_Glue glue;
lv_obj_t *tabview, // LittlevGL tabview object
*gauge, // Gauge object (on first of three tabs)
*chart, // Chart object (second tab)
*canvas; // Canvas object (third tab)
uint8_t active_tab = 0, // Index of currently-active tab (0-2)
prev_tab = 0; // Index of previously-active tab
lv_chart_series_t *series; // 'Series' data for the bar chart
// Canvas object is an image in memory. Although LittlevGL supports several
// internal image formats, only the TRUE_COLOR variety allows drawing
// operations. Fortunately nRF52840 has gobs of RAM for this. The canvas
// buffer is global because it's referenced both in the setup and main loop.
// Circuit Playground Express (M0) doesn't have enough RAM for this.
#define CANVAS_WIDTH 200 // Dimensions in pixels
#define CANVAS_HEIGHT 150
lv_color_t canvas_buffer[
LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];
lv_draw_line_dsc_t draw_dsc; // Drawing style (for canvas) is similarly global
void lvgl_setup(void) {
// Create a tabview object, by default this covers the full display.
tabview = lv_tabview_create(lv_disp_get_scr_act(NULL), NULL);
// The Gizmo display has a lot of pixels and can't refresh very fast.
// To show off the tabview animation, let's slow it down to 1 second.
lv_tabview_set_anim_time(tabview, 1000);
// Because they're referenced any time an object is drawn, styles need
// to be permanent in scope; either declared globally (outside all
// functions), or static. The styles used on tabs are never modified after
// they're used here, so let's use static on those...
static lv_style_t tab_style, tab_background_style, indicator_style;
// This is the background style "behind" the tabs. This is what shows
// through for "off" (inactive) tabs -- a vertical green gradient,
// minimal padding around edges (zero at bottom).
lv_style_init(&tab_background_style);
lv_style_set_bg_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x408040));
lv_style_set_bg_grad_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x304030));
lv_style_set_bg_grad_dir(&tab_background_style, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
lv_style_set_pad_top(&tab_background_style, LV_STATE_DEFAULT, 2);
lv_style_set_pad_left(&tab_background_style, LV_STATE_DEFAULT, 2);
lv_style_set_pad_right(&tab_background_style, LV_STATE_DEFAULT, 2);
lv_style_set_pad_bottom(&tab_background_style, LV_STATE_DEFAULT, 0);
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BG, &tab_background_style);
// Style for tabs. Active tab is white with opaque background, inactive
// tabs are transparent so the background shows through (only the white
// text is seen). A little top & bottom padding reduces scrunchyness.
lv_style_init(&tab_style);
lv_style_set_pad_top(&tab_style, LV_STATE_DEFAULT, 3);
lv_style_set_pad_bottom(&tab_style, LV_STATE_DEFAULT, 10);
lv_style_set_bg_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_WHITE);
lv_style_set_bg_opa(&tab_style, LV_STATE_CHECKED, LV_OPA_100);
lv_style_set_text_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_GRAY);
lv_style_set_bg_opa(&tab_style, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_style_set_text_color(&tab_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BTN, &tab_style);
// Style for the small indicator bar that appears below the active tab.
lv_style_init(&indicator_style);
lv_style_set_bg_color(&indicator_style, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_style_set_size(&indicator_style, LV_STATE_DEFAULT, 5);
lv_obj_add_style(tabview, LV_TABVIEW_PART_INDIC, &indicator_style);
// Back to creating widgets...
// Add three tabs to the tabview
lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Gauge");
lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Chart");
lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Canvas");
// And then add stuff in each tab...
// The first tab holds a gauge. To keep the demo simple, let's just use
// the default style and range (0-100). See LittlevGL docs for options.
gauge = lv_gauge_create(tab1, NULL);
lv_obj_set_size(gauge, 186, 186);
lv_obj_align(gauge, NULL, LV_ALIGN_CENTER, 0, 0);
// Second tab, make a chart...
chart = lv_chart_create(tab2, NULL);
lv_obj_set_size(chart, 200, 180);
lv_obj_align(chart, NULL, LV_ALIGN_CENTER, 0, 0);
lv_chart_set_type(chart, LV_CHART_TYPE_COLUMN);
// For simplicity, we'll stick with the chart's default 10 data points:
series = lv_chart_add_series(chart, LV_COLOR_RED);
lv_chart_init_points(chart, series, 0);
// Make each column shift left as new values enter on right:
lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
// Third tab is a canvas, which we'll fill with random colored lines.
// LittlevGL draw functions only work on TRUE_COLOR canvas.
canvas = lv_canvas_create(tab3, NULL);
lv_canvas_set_buffer(canvas, canvas_buffer,
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
lv_obj_align(canvas, NULL, LV_ALIGN_CENTER, 0, 0);
lv_canvas_fill_bg(canvas, LV_COLOR_WHITE, LV_OPA_100);
// Set up canvas line-drawing style based on defaults.
// Later we'll change color settings when drawing each line.
lv_draw_line_dsc_init(&draw_dsc);
}
void setup(void) {
Serial.begin(115200);
// Initialize display BEFORE glue setup
tft.init(240, 240);
tft.setRotation(TFT_ROTATION);
analogWrite(TFT_BACKLIGHT, 255); // USE analogWrite() FOR GIZMO BACKLIGHT!
// Initialize glue, passing in address of display
LvGLStatus status = glue.begin(&tft);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
// Enable "A" and "B" buttons as inputs
pinMode(4, INPUT_PULLDOWN); // Left (A) button
pinMode(5, INPUT_PULLDOWN); // Right (B) button
}
uint32_t prev_time = -1;
void loop(void) {
// Read left/right button inputs. Debouncing could be done better,
// but this'll do for a quick simple demo. The arrangements of the
// buttons is intentionally flipped here (pin 5, the "right" button,
// moves one tab left, pin 4 the opposite) so the button placements
// make sense while looking at the screen on the reverse side.
if(digitalRead(5) == HIGH) {
if(active_tab > 0) {
active_tab--;
}
while(digitalRead(5) == HIGH); // Wait for button release
} else if(digitalRead(4) == HIGH) {
if(active_tab < 2) {
active_tab++;
}
while(digitalRead(4) == HIGH); // Wait for button release
}
// Change active tab if button pressings happened
if(active_tab != prev_tab) {
lv_tabview_set_tab_act(tabview, active_tab, true);
prev_tab = active_tab;
}
// Make the gauge sweep a full sine wave over timeCLUE
lv_gauge_set_value(gauge, 0, (int)(50.5 + sin(millis() / 1000.0) * 50.0));
// About 2X a second, things happen on the other two tabs...
uint32_t new_time = millis() / 500; // Current half-second
if(new_time != prev_time) { // freshly elapsed
prev_time = new_time;
// Add a new random item to the bar chart (old value shift left)
lv_chart_set_next(chart, series, random(100));
lv_chart_refresh(chart);
// Add a random line to the canvas
lv_point_t points[2];
points[0].x = random(CANVAS_WIDTH);
points[0].y = random(CANVAS_HEIGHT);
points[1].x = random(CANVAS_WIDTH);
points[1].y = random(CANVAS_HEIGHT);
draw_dsc.color.ch.red = random();
draw_dsc.color.ch.green = random();
draw_dsc.color.ch.blue = random();
lv_canvas_draw_line(canvas, points, 2, &draw_dsc);
// This forces the canvas to update (otherwise changes aren't
// seen unless leaving and returning to the canvas tab):
lv_canvas_set_buffer(canvas, canvas_buffer,
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
}
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}
// NOTE TO FUTURE SELF: this sketch is essentially the same as the CLUE
// widgets example. If updating one sketch, make sure the other is kept
// in sync. Aside from screen setup, differences include backlight control
// and A/B button setup & read (CPX is active high, CLUE is active low).

View File

@ -1,255 +0,0 @@
// A more interesting example for LittlevGL on Adafruit PyPortal, showing
// use of the touchscreen. Code's a little more complex than the
// hello_pyportal example, so best get that working before trying this.
// By default, as written, on regular (320x240) PyPortal the example is a
// pretend calculator keypad, while PyPortal Titano (480x320) has a whole
// keyboard (though you'll probably need a stylus). These just seemed the
// right level of detail for their respective screens, but feel free to
// override and try either for yourself.
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
#include <lvgl.h>
#include <TouchScreen.h>
#define DEMO_CALC 0
#define DEMO_TEXT 1
#define TFT_D0 34 // PyPortal TFT pins
#define TFT_WR 26
#define TFT_DC 10
#define TFT_CS 11
#define TFT_RST 24
#define TFT_RD 9
#define TFT_BACKLIGHT 25
#define YP A4 // PyPortal touchscreen pins
#define XP A5
#define YM A6
#define XM A7
#if defined(ADAFRUIT_PYPORTAL_M4_TITANO)
#include <Adafruit_HX8357.h>
Adafruit_HX8357 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
TFT_RD);
#define DEMO DEMO_TEXT // On Titano, do text/keyboard example
#else
#include <Adafruit_ILI9341.h>
Adafruit_ILI9341 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
TFT_RD);
#define DEMO DEMO_CALC // Smaller PyPortal, do keypad example
#endif
TouchScreen ts(XP, YP, XM, YM, 300);
Adafruit_LvGL_Glue glue;
#if DEMO == DEMO_CALC
// "Pretend" calculator example. Please, PLEASE...do NOT implement the whole
// calculator and submit as a pull request, because it will NOT be merged!
// This sketch is meant only to be illustrative and not functional, just
// showing LittlevGL + Adafruit display/touch tied together with a modest
// amount of code. Even a simple but working calc would require WAY more
// code, distracting from that core idea (and is a waste of PyPortal).
// Daiso has better calculators for $1.50.
#define TFT_ROTATION 0 // Portrait orientation on PyPortal (USB top)
lv_obj_t *digits_label = NULL; // LittlevGL label object showing digits
String digits = "0"; // Current digits string value
bool hasDecimal = false; // Only allow one decimal point
const char *buttons[] = { // Button matrix labels
"7", "8", "9", "/", "\n",
"4", "5", "6", "x", "\n",
"1", "2", "3", "-", "\n",
"0", ".", "=", "+", "" };
// This function processes events from the button matrix
void button_event_handler(lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_VALUE_CHANGED) {
const char *txt = lv_btnmatrix_get_active_btn_text(obj);
if(txt) { // NULL if pressed in frame area outside buttons
if(txt[0] == '.') {
// Decimal button pressed. Add decimal point to "digits" string
// if it's not too long and there's no decimal present yet...
if((digits.length() < 15) && !hasDecimal) {
digits += '.';
hasDecimal = true;
}
} else if((txt[0] >= '0') && (txt[0] <= '9')) {
// Number button (0-9) pressed. If there's nothing currently
// being displayed, take the digit literally. Otherwise, append
// the new digit if the "digits" string is not too long.
if(digits.equals("0")) {
digits = txt[0];
} else if(digits.length() < 15) {
digits += txt[0];
}
} else {
// Any other button, just reset the calculator display.
// It's all just pretend.
digits = "0";
hasDecimal = false;
}
if(digits_label != NULL) {
lv_label_set_text(digits_label, digits.c_str());
}
}
}
}
void lvgl_setup(void) {
// Just using the default styles for everything here, keeping
// it basic. See the official LittlevGL docs and examples for
// insights on applying styles.
// The calculator digits are held inside a LvGL container object
// as this gives us a little more control over positioning.
lv_obj_t *container = lv_cont_create(lv_scr_act(), NULL);
lv_cont_set_fit(container, LV_FIT_NONE); // Don't auto fit
lv_obj_set_size(container, tft.width(), 50); // Full width x 50 px
// Calculator digits are just a text label inside the container,
// refreshed whenever the global "digits" string changes.
digits_label = lv_label_create(container, NULL);
lv_label_set_text(digits_label, digits.c_str());
lv_obj_align(digits_label, NULL, LV_ALIGN_IN_TOP_LEFT, 20, 10);
lv_label_set_long_mode(digits_label, LV_LABEL_LONG_CROP);
lv_obj_set_size(digits_label, tft.width() - 40, 30);
lv_label_set_align(digits_label, LV_LABEL_ALIGN_RIGHT);
// Fill the remaining space with the button matrix.
lv_obj_t *button_matrix = lv_btnmatrix_create(lv_scr_act(), NULL);
lv_btnmatrix_set_map(button_matrix, buttons);
lv_obj_align(button_matrix, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 50);
lv_obj_set_size(button_matrix, tft.width(), tft.height() - 50);
lv_obj_set_event_cb(button_matrix, button_event_handler);
}
#else // Keyboard demo
// Keyboard example, lets you enter and edit text in a field. Even on a
// PyPortal Titano it requires a stylus to be even half useful (fingertip
// is possible if very patient), but having the option of a keyboard at
// all on this device is pretty nifty!
#define TFT_ROTATION 3 // Landscape orientation on PyPortal (USB right)
lv_obj_t *textarea,
*keyboard = NULL; // Created/deleted as needed
#if LV_USE_ANIMATION
// Called after keyboard slides closed - deletes keyboard object
void delete_keyboard(lv_anim_t * a) {
lv_obj_del((lv_obj_t *)a->var);
keyboard = NULL;
}
#endif
// Called when the close or ok button is pressed on the keyboard
void keyboard_event_handler(lv_obj_t *obj, lv_event_t event) {
lv_keyboard_def_event_cb(keyboard, event);
if(event == LV_EVENT_APPLY || event == LV_EVENT_CANCEL) {
#if LV_USE_ANIMATION
// If animation is enabled, make keyboard slide away
lv_anim_path_t path;
lv_anim_path_init(&path);
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
lv_anim_set_var(&a, keyboard);
lv_anim_set_time(&a, 300);
lv_anim_set_values(&a, lv_obj_get_y(keyboard), LV_VER_RES); // start, end
lv_anim_set_path(&a, &path);
lv_anim_set_ready_cb(&a, delete_keyboard);
lv_anim_start(&a);
#else
lv_obj_del(keyboard);
keyboard = NULL;
#endif
}
}
// Other clicks in the text area
void text_area_event_handler(lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_CLICKED) {
// Unsure why, but text area has an initial clicked event on
// creation, causing the keyboard to appear. This is a hacky
// workaround that just ignores the first click event, so
// subsequent actual clicks bring up the keyboard.
static bool first = true;
if(first) {
first = false;
return;
}
if(keyboard == NULL) {
// If not present, create keyboard object at bottom of screen
keyboard = lv_keyboard_create(lv_scr_act(), NULL);
lv_obj_set_size(keyboard, tft.width(), tft.height() * 7 / 16);
lv_obj_align(keyboard, textarea, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_keyboard_set_textarea(keyboard, textarea);
lv_keyboard_set_cursor_manage(keyboard, true);
lv_obj_set_event_cb(keyboard, keyboard_event_handler);
#if LV_USE_ANIMATION
// If animation is enabled, make keyboard slide into place
lv_anim_path_t path;
lv_anim_path_init(&path);
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
lv_anim_set_var(&a, keyboard);
lv_anim_set_time(&a, 300);
lv_anim_set_values(&a, LV_VER_RES, lv_obj_get_y(keyboard)); // start, end
lv_anim_set_path(&a, &path);
lv_anim_start(&a);
#endif
}
}
}
void lvgl_setup(void) {
textarea = lv_textarea_create(lv_scr_act(), NULL);
lv_obj_set_size(textarea, LV_HOR_RES, LV_VER_RES); // Whole screen
lv_obj_align(textarea, NULL, LV_ALIGN_CENTER, 0, 0);
lv_textarea_set_text(textarea, "This text is editable.");
lv_obj_set_event_cb(textarea, text_area_event_handler);
lv_textarea_set_cursor_pos(textarea, LV_TEXTAREA_CURSOR_LAST);
}
#endif // end calculator / keyboard examples
void setup(void) {
Serial.begin(115200);
// Initialize display BEFORE glue setup
tft.begin();
tft.setRotation(TFT_ROTATION);
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, HIGH);
// PyPortal touchscreen needs no init
// Initialize glue, passing in address of display & touchscreen
LvGLStatus status = glue.begin(&tft, &ts);
if(status != LVGL_OK) {
Serial.printf("Glue error %d\r\n", (int)status);
for(;;);
}
lvgl_setup(); // Call UI-building function above
}
void loop(void) {
lv_task_handler(); // Call LittleVGL task handler periodically
delay(5);
}
// NOTE TO FUTURE SELF: this sketch is largely similar to the FeatherWing
// widgets example. If updating/bugfixing one sketch, make sure the other
// is kept in sync.

View File

@ -43,7 +43,7 @@ int lv0_init(bvm *vm);
int lv0_init(bvm *vm) {
// "+_p" indicates that there must be an non NULL argument, either passed as comptr or returned by the function
// Here, there is no function, so calling the constructor without a non-null comptr argument is rejected
return be_call_c_func(vm, NULL, "+_p", NULL);
return be_call_c_func(vm, NULL, "=_p", NULL);
}
/*********************************************************************************************\

View File

@ -52,3 +52,23 @@ debug_init_break = tbreak setup
build_unflags = ${core32solo1.build_unflags}
build_flags = ${core32solo1.build_flags}
monitor_filters = esp32_exception_decoder
; *** pre alpha S3 Version
[env:tasmota32s3]
extends = env:tasmota32_base
platform = https://github.com/Jason2866/platform-espressif32.git#IDF44/ESP32-S3
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/617/framework-arduinoespressif32-v4.4_dev-3cfa267e25.tar.gz
board = esp32s3
build_flags = ${env:tasmota32_base.build_flags}
lib_extra_dirs =
lib/lib_basic
lib/lib_ssl
lib/libesp32
lib_ignore =
TTGO TWatch Library
NimBLE-Arduino
Micro-RTSP
epdiy
NeoPixelBus
SPI
SD

View File

@ -22,6 +22,7 @@ lib_ignore =
ESP RainMaker
WiFiProv
USB
SD_MMC
ESP32 Azure IoT Arduino
ESP32 Async UDP
ESP32 BLE Arduino

View File

@ -41,6 +41,9 @@ class AXP192_M5StickC : AXP192
self.set_dcdc_enable(1, true)
self.set_dcdc_enable(3, true)
# enable external power on HAT connector (5V)
self.set_exten(true)
# Set temperature protection
self.write8(0x39, 0xFC)

Binary file not shown.

View File

@ -0,0 +1,52 @@
var antiburn = module('antiburn')
antiburn.init = def (m)
class Antiburn
var scr_original
var scr_antiburn
var running
static colors = [
0x000000,
0xff0000,
0x00ff00,
0x0000ff,
0xffffff
]
def init()
self.running = false
end
def start()
if self.running
return
else
lv.start()
self.scr_original = lv.scr_act()
self.scr_antiburn = lv.obj(0)
lv.scr_load(self.scr_antiburn)
self.scr_antiburn.add_event_cb(/->self.stop(), lv.EVENT_PRESSED, 0)
self.running = true
self.cycle(0)
end
end
def cycle(i)
if !self.running return end
if i < 30
self.scr_antiburn.set_style_bg_color(lv.color_hex(self.colors[i % 5]), 0)
tasmota.set_timer(1000, /->self.cycle(i+1))
else
self.stop()
end
end
def stop()
if self.running && self.scr_antiburn != nil
lv.scr_load(self.scr_original)
self.running = false
self.scr_antiburn.del()
self.scr_antiburn = nil
end
end
end
return Antiburn()
end
return antiburn

View File

@ -0,0 +1,20 @@
# Register the command 'Antiburn'
# Module loaded in memory only when the command is first used
var wd = tasmota.wd
lv.antiburn = def()
import sys
var path = sys.path()
path.push(wd)
import antiburn
path.pop()
antiburn.start()
end
tasmota.add_cmd("Antiburn",
def ()
lv.antiburn()
tasmota.resp_cmnd_done()
end
)

View File

@ -0,0 +1,28 @@
#
# H_bridge class in Berry to pilot a H-bridge device
#
class H_bridge
var gpio1, gpio2
var max
# init(phy_gpio1, phy_gpio2) - intialize H-bridge with the 2 GPIOs used to control it
def init(gpio1, gpio2)
self.gpio1 = gpio1
self.gpio2 = gpio2
self.max = 1023 # max value of duty
end
# set the value of both PWM values
def set(v1, v2)
if v1 < 0 v1 = 0 end
if v2 < 0 v2 = 0 end
if v1 + v2 > self.max
raise "value_error", "the sum of duties must not exceed 100%"
end
import gpio
gpio.set_pwm(self.gpio1, v1, 0)
gpio.set_pwm(self.gpio2, v2, v1) # dephase by value v1
end
end

View File

@ -0,0 +1,28 @@
:H,WT32-SC01,480,320,16,SPI,1,*,*,*,*,*,*,*,80
:S,2,1,1,0,40,20
:I
01,80
F0,1,C3
F0,1,96
C5,1,1C
36,1,48
3A,1,55
B0,1,80
B4,1,01
B6,3,80,02,3B
B7,1,C6
F0,1,69
F0,1,3C
11,80
29,80
:o,28
:O,29
:R,36
:0,28,00,00,01
:1,88,00,00,02
:2,E8,00,00,03
:3,48,00,00,00
:i,20,21
:A,2A,2B,2C
:TI1,38,*,*
#

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 - RCLK"
#define D_GPIO_SHIFT595_OE "74x595 - OE"
#define D_GPIO_SHIFT595_SER "74x595 - SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -852,6 +852,8 @@
#define D_GPIO_SHIFT595_RCLK "74x595 RCLK"
#define D_GPIO_SHIFT595_OE "74x595 OE"
#define D_GPIO_SHIFT595_SER "74x595 SER"
#define D_SENSOR_CM11_TX "CM110x TX"
#define D_SENSOR_CM11_RX "CM110x RX"
// Units
#define D_UNIT_AMPERE "安培"

View File

@ -724,6 +724,7 @@
// -- Serial sensors ------------------------------
//#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
//#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
//#define USE_CM110x // Add support for CM110x CO2 sensors (+2k7code)
#define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1)
#define CO2_HIGH 1200 // Above this CO2 value show red light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1)
//#define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code)
@ -747,7 +748,7 @@
//#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
//#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+3k1 code, +132 bytes RAM)
// #define USE_FLOG // Add support for GPS logging in OTA's Flash (Experimental) (+2k9 code, +8 bytes RAM)
//#define USE_HM10 // (ESP8266 only) Add support for HM-10 as a BLE-bridge (+9k3 code)
//#define USE_HM10 // (ESP8266 only) Add support for HM-10 as a BLE-bridge (+17k code)
//#define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
//#define USE_TASMOTA_CLIENT // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k6 code, 64 mem)
#define USE_TASMOTA_CLIENT_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants
@ -793,7 +794,7 @@
#define DDSU666_SPEED 9600 // Chint DDSU666 Modbus RS485 serial speed (default: 9600 baud)
//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code)
#define SOLAXX1_SPEED 9600 // Solax X1 Modbus RS485 serial speed (default: 9600 baud)
#define SOLAXX1_PV2 // Solax X1 using second PV
// #define SOLAXX1_PV2 // Solax X1 using second PV
//#define USE_LE01MR // Add support for F&F LE-01MR Modbus energy monitor (+1k code)
#define LE01MR_SPEED 9600 // LE-01MR modbus baudrate (default: 9600)
#define LE01MR_ADDR 1 // LE-01MR modbus address (default: 0x01)

View File

@ -474,7 +474,7 @@ typedef struct {
TimeRule tflag[2]; // 2E2
uint16_t pwm_frequency; // 2E6
power_t power; // 2E8
uint16_t pwm_value[MAX_PWMS]; // 2EC
uint16_t pwm_value[MAX_PWMS_LEGACY];// 2EC
int16_t altitude; // 2F6
uint16_t tele_period; // 2F8
uint8_t display_rotate; // 2FA
@ -579,7 +579,7 @@ typedef struct {
uint8_t ex_my_adc0; // 495 Free since 9.0.0.1
uint16_t light_pixels; // 496
uint8_t light_color[5]; // 498
uint8_t light_color[LST_MAX]; // 498 LST_MAX = 5
uint8_t light_correction; // 49D
uint8_t light_dimmer; // 49E
uint8_t rule_enabled; // 49F
@ -614,7 +614,8 @@ typedef struct {
uint32_t ipv4_rgx_address; // 558
uint32_t ipv4_rgx_subnetmask; // 55C
uint8_t free_560[92]; // 560
uint16_t pwm_value_ext[16-5]; // 560 Extension to pwm_value to store up to 16 PWM for ESP32. This array stores values 5..15
uint8_t free_560[70]; // 576
SysMBitfield1 flag2; // 5BC
uint32_t pulse_counter[MAX_COUNTERS]; // 5C0

View File

@ -184,10 +184,11 @@ bool RtcRebootValid(void) {
extern "C" {
#include "spi_flash.h"
}
#include "eboot_command.h"
#ifdef ESP8266
#include "eboot_command.h"
extern "C" uint32_t _FS_start; // 1M = 0x402fb000, 2M = 0x40300000, 4M = 0x40300000
const uint32_t FLASH_FS_START = (((uint32_t)&_FS_start - 0x40200000) / SPI_FLASH_SEC_SIZE);
uint32_t SETTINGS_LOCATION = FLASH_FS_START -1; // 0xFA, 0x0FF or 0x0FF
@ -1094,7 +1095,7 @@ void SettingsDefaultSet2(void) {
Settings->pwm_frequency = PWM_FREQ;
Settings->pwm_range = PWM_RANGE;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
for (uint32_t i = 0; i < LST_MAX; i++) {
Settings->light_color[i] = DEFAULT_LIGHT_COMPONENT;
// Settings->pwm_value[i] = 0;
}

View File

@ -1523,56 +1523,6 @@ void CmndTemplate(void)
if (!error) { TemplateJson(); }
}
void CmndPwm(void)
{
if (TasmotaGlobal.pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings->pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) {
Settings->pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload;
analogWrite(Pin(GPIO_PWM1, XdrvMailbox.index -1), bitRead(TasmotaGlobal.pwm_inverted, XdrvMailbox.index -1) ? Settings->pwm_range - XdrvMailbox.payload : XdrvMailbox.payload);
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
ResponseJsonEnd();
}
}
void CmndPwmfrequency(void)
{
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) {
Settings->pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload;
analogWriteFreq(Settings->pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c)
#ifdef USE_LIGHT
LightReapplyColor();
LightAnimate();
#endif // USE_LIGHT
}
ResponseCmndNumber(Settings->pwm_frequency);
}
void CmndPwmrange(void) {
// Support only 8 (=255), 9 (=511) and 10 (=1023) bits resolution
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) {
uint32_t pwm_range = XdrvMailbox.payload;
uint32_t pwm_resolution = 0;
while (pwm_range) {
pwm_resolution++;
pwm_range >>= 1;
}
pwm_range = (1 << pwm_resolution) - 1;
uint32_t old_pwm_range = Settings->pwm_range;
Settings->pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : pwm_range;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (Settings->pwm_value[i] > Settings->pwm_range) {
Settings->pwm_value[i] = Settings->pwm_range;
}
}
if (Settings->pwm_range != old_pwm_range) { // On ESP32 this prevents loss of duty state
analogWriteRange(Settings->pwm_range); // Default is 1023 (Arduino.h)
}
}
ResponseCmndNumber(Settings->pwm_range);
}
void CmndButtonDebounce(void)
{
if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) {

View File

@ -144,7 +144,7 @@ void CrashDumpClear(void)
}
}
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
/**
* Save crash information in RTC memory
* This function is called automatically if ESP8266 suffers an exception

View File

@ -126,6 +126,8 @@ String GetDeviceHardware(void) {
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/rtc.h"
#else
@ -277,6 +279,8 @@ extern "C" {
#include "esp32/rom/spi_flash.h"
#elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#include "esp32s2/rom/spi_flash.h"
#elif CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#include "esp32s3/rom/spi_flash.h"
#elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#include "esp32c3/rom/spi_flash.h"
#else
@ -524,12 +528,14 @@ float CpuTemperature(void) {
return t;
*/
#else
// Currently (20210801) repeated calls to temperatureRead() on ESP32C3 and ESP32S2 result in IDF error messages
static float t = NAN;
if (isnan(t)) {
t = (float)temperatureRead(); // In Celsius
}
return t;
#ifndef CONFIG_IDF_TARGET_ESP32S3
// Currently (20210801) repeated calls to temperatureRead() on ESP32C3 and ESP32S2 result in IDF error messages
static float t = NAN;
if (isnan(t)) {
t = (float)temperatureRead(); // In Celsius
}
return t;
#endif
#endif
}

View File

@ -788,7 +788,9 @@ void ResponseAppendFeatures(void)
#ifdef USE_SDM230
feature8 |= 0x00100000; // xnrg_21_sdm230.ino
#endif
// feature8 |= 0x00200000;
#ifdef USE_CM110x
feature8 |= 0x00200000; // xsns_95_cm110x.ino
#endif
// feature8 |= 0x00400000;
// feature8 |= 0x00800000;

273
tasmota/support_pwm.ino Normal file
View File

@ -0,0 +1,273 @@
/*
support_pwm.ino - command support for Tasmota
Copyright (C) 2021 Theo Arends & Stephan Hadinger
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/>.
*/
/***********************************************************************\
* PWM Control for ESP32
\***********************************************************************/
#ifdef ESP32
// All changes in PWM have been applied, rearm all change indicators
void PwmRearmChanges(void) {
for (uint32_t i = 0; i < MAX_PWMS; i++) {
// Init expected changes
TasmotaGlobal.pwm_value[i] = -1; // no change wanted
TasmotaGlobal.pwm_phase[i] = -1; // no change wanted
}
}
// Load PWM values from settings and intiliaze values
// void PwmLoadFromSettings(void) {
// for (uint32_t i = 0; i < MAX_PWMS; i++) {
// if (i < MAX_PWMS_LEGACY) {
// TasmotaGlobal.pwm_cur_value[i] = Settings->pwm_value[i]; // retrieve in Legacy pool for 0..4
// } else {
// TasmotaGlobal.pwm_cur_value[i] = Settings->pwm_value_ext[i - MAX_PWMS_LEGACY]; // retrieve in Legacy pool for 5..15
// }
// TasmotaGlobal.pwm_cur_phase[i] = 0; // no phase shift for now, will be recomputed at first push to GPIOs
// }
// PwmRearmChanges(); // reset expected changes
// }
// Copy current values to Settings
void PwmSaveToSettings(void) {
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (i < MAX_PWMS_LEGACY) {
Settings->pwm_value[i] = TasmotaGlobal.pwm_cur_value[i]; // store in Legacy pool for 0..4
} else {
Settings->pwm_value_ext[i - MAX_PWMS_LEGACY] = TasmotaGlobal.pwm_cur_value[i]; // retrieve in Legacy pool for 5..15
}
}
}
/***********************************************************************\
* PWM Control for ESP32
\***********************************************************************/
// Apply PWM expected values to actual GPIO PWM
// As input, `TasmotaGlobal.pwm_value[]` and `TasmotaGlobal.pwm_phase[]` contain the new expected values
// or `-1` if no change.
// Auto-phasing is recomputed, and changes are applied to GPIO if there is a physical GPIO configured and an actual change needed
//
void PwmApplyGPIO(void) {
uint32_t pwm_phase_accumulator = 0; // dephase each PWM channel with the value of the previous
for (uint32_t i = 0; i < MAX_PWMS; i++) {
// compute `pwm_val`, the virtual value of PWM (not taking into account inverted)
uint32_t pwm_val = TasmotaGlobal.pwm_cur_value[i]; // logical value of PWM, 0..1023
if (TasmotaGlobal.pwm_value[i] >= 0) { pwm_val = TasmotaGlobal.pwm_value[i]; } // new value explicitly specified
if (pwm_val > Settings->pwm_range) { pwm_val = Settings->pwm_range; } // prevent overflow
// gpio_val : actual value of GPIO, taking into account inversion
uint32_t gpio_val = bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - pwm_val : pwm_val;
// compute phase
uint32_t pwm_phase = TasmotaGlobal.pwm_cur_phase[i]; // pwm_phase is the logical phase of the active pulse, ignoring inverted
uint32_t gpio_phase = pwm_phase; // gpio is the physical phase taking into account inverted
if (TasmotaGlobal.pwm_phase[i] >= 0) {
pwm_phase = TasmotaGlobal.pwm_phase[i]; // if explicit set explicitly,
} else if (Settings->flag5.pwm_force_same_phase) {
pwm_phase = 0; // if auto-phase is off
} else {
// compute auto-phase
pwm_phase = pwm_phase_accumulator;
uint32_t pwm_phase_invert = bitRead(TasmotaGlobal.pwm_inverted, i) ? pwm_val : 0; // move phase if inverted
gpio_phase = (pwm_phase + pwm_phase_invert) & Settings->pwm_range;
// accumulate phase for next GPIO
pwm_phase_accumulator = (pwm_phase + pwm_val) & Settings->pwm_range;
}
// apply new values to GPIO if GPIO is set
if (PinUsed(GPIO_PWM1, i)) {
if ((pwm_val != TasmotaGlobal.pwm_cur_value[i]) || (pwm_phase != TasmotaGlobal.pwm_cur_phase[i])) {
// GPIO has PWM and there is a chnage to apply, apply it
analogWritePhase(Pin(GPIO_PWM1, i), gpio_val, gpio_phase);
}
}
// set new current values
TasmotaGlobal.pwm_cur_value[i] = pwm_val;
TasmotaGlobal.pwm_cur_phase[i] = pwm_phase;
}
// AddLog(LOG_LEVEL_INFO, "PWM: Val=%03X-%03X-%03X-%03X-%03X Phase=%03X-%03X-%03X-%03X-%03X Range=%03X",
// TasmotaGlobal.pwm_cur_value[0], TasmotaGlobal.pwm_cur_value[1], TasmotaGlobal.pwm_cur_value[2], TasmotaGlobal.pwm_cur_value[3],
// TasmotaGlobal.pwm_cur_value[4],
// TasmotaGlobal.pwm_cur_phase[0], TasmotaGlobal.pwm_cur_phase[1], TasmotaGlobal.pwm_cur_phase[2], TasmotaGlobal.pwm_cur_phase[3],
// TasmotaGlobal.pwm_cur_phase[4],
// Settings->pwm_range
// );
PwmSaveToSettings(); // copy to Settings
PwmRearmChanges(); // reset expected changes
}
void CmndPwm(void)
{
if (TasmotaGlobal.pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings->pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) {
TasmotaGlobal.pwm_value[XdrvMailbox.index - 1] = XdrvMailbox.payload;
PwmApplyGPIO();
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
ResponseJsonEnd();
}
}
void GpioInitPwm(void) {
PwmRearmChanges();
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogAttach(Pin(GPIO_PWM1, i));
if (i < TasmotaGlobal.light_type) {
// force PWM GPIOs to black
TasmotaGlobal.pwm_value[i] = 0;
} else {
TasmotaGlobal.pwm_present = true;
if (i < MAX_PWMS_LEGACY) {
TasmotaGlobal.pwm_value[i] = Settings->pwm_value[i];
} else {
TasmotaGlobal.pwm_value[i] = Settings->pwm_value_ext[i - MAX_PWMS_LEGACY];
}
}
}
}
PwmApplyGPIO(); // apply all changes
}
/********************************************************************************************/
void ResetPwm(void)
{
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
TasmotaGlobal.pwm_value[i] = 0;
}
PwmApplyGPIO();
}
#else // now for ESP8266
void PwmRearmChanges(void) {}
void CmndPwm(void)
{
if (TasmotaGlobal.pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings->pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) {
uint32_t pwm_index = XdrvMailbox.index - 1;
if (pwm_index < MAX_PWMS_LEGACY) { // write in the appropriate settings pool
Settings->pwm_value[pwm_index] = XdrvMailbox.payload;
} else {
Settings->pwm_value_ext[pwm_index - MAX_PWMS_LEGACY] = XdrvMailbox.payload;
}
analogWrite(Pin(GPIO_PWM1, pwm_index), bitRead(TasmotaGlobal.pwm_inverted, pwm_index) ? Settings->pwm_range - XdrvMailbox.payload : XdrvMailbox.payload);
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
ResponseJsonEnd();
}
}
void GpioInitPwm(void) {
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
if (i < TasmotaGlobal.light_type) {
// force PWM GPIOs to low or high mode if belongs to the light (always <5), see #7165
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
} else {
TasmotaGlobal.pwm_present = true;
if (i < MAX_PWMS_LEGACY) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
} else {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value_ext[i] : Settings->pwm_value_ext[i]);
}
}
}
}
}
/********************************************************************************************/
void ResetPwm(void)
{
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
// analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
}
}
}
#endif // ESP8266
void CmndPwmfrequency(void)
{
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) {
Settings->pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload;
analogWriteFreq(Settings->pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c)
#ifdef USE_LIGHT
LightReapplyColor();
LightAnimate();
#endif // USE_LIGHT
}
ResponseCmndNumber(Settings->pwm_frequency);
}
void CmndPwmrange(void) {
// Support only 8 (=255), 9 (=511) and 10 (=1023) bits resolution
if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) {
uint32_t pwm_range = XdrvMailbox.payload;
uint32_t pwm_resolution = 0;
while (pwm_range) {
pwm_resolution++;
pwm_range >>= 1;
}
pwm_range = (1 << pwm_resolution) - 1;
uint32_t old_pwm_range = Settings->pwm_range;
Settings->pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : pwm_range;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (i < MAX_PWMS_LEGACY) {
if (Settings->pwm_value[i] > Settings->pwm_range) {
Settings->pwm_value[i] = Settings->pwm_range;
}
} else {
if (Settings->pwm_value_ext[i - MAX_PWMS_LEGACY] > Settings->pwm_range) {
Settings->pwm_value_ext[i - MAX_PWMS_LEGACY] = Settings->pwm_range;
}
}
}
if (Settings->pwm_range != old_pwm_range) { // On ESP32 this prevents loss of duty state
analogWriteRange(Settings->pwm_range); // Default is 1023 (Arduino.h)
}
}
ResponseCmndNumber(Settings->pwm_range);
}
void MqttShowPWMState(void)
{
ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{"));
bool first = true;
for (uint32_t i = 0; i < MAX_PWMS; i++) { // TODO
if (PinUsed(GPIO_PWM1, i)) {
uint32_t pwm_val = (i < MAX_PWMS_LEGACY) ? Settings->pwm_value[i] : Settings->pwm_value_ext[i - MAX_PWMS_LEGACY];
ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, pwm_val);
first = false;
}
}
ResponseJsonEnd();
}

View File

@ -444,7 +444,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state)
pwm = changeUIntScale((uint16_t)(state ? Settings->ledpwm_on : Settings->ledpwm_off), 0, 255, 0, Settings->pwm_range); // linear
#endif //USE_LIGHT
#ifdef ESP32
if (analogAttach(Pin(GPIO_LED1, led)))
if (analogAttach(Pin(GPIO_LED1, led)) >= 0)
#endif
analogWrite(Pin(GPIO_LED1, led), bitRead(TasmotaGlobal.led_inverted, led) ? Settings->pwm_range - pwm : pwm);
} else {
@ -735,19 +735,6 @@ void StopAllPowerBlink(void)
}
}
void MqttShowPWMState(void)
{
ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{"));
bool first = true;
for (uint32_t i = 0; i < MAX_PWMS; i++) {
if (PinUsed(GPIO_PWM1, i)) {
ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings->pwm_value[i]);
first = false;
}
}
ResponseJsonEnd();
}
void MqttShowState(void)
{
char stemp1[TOPSZ];
@ -1670,17 +1657,6 @@ void SerialInput(void)
}
}
/********************************************************************************************/
void ResetPwm(void)
{
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
// analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
}
}
}
/********************************************************************************************/
@ -1825,7 +1801,7 @@ void GpioInit(void)
mpin -= (AGPIO(GPIO_HEARTBEAT_INV) - AGPIO(GPIO_HEARTBEAT));
}
else if ((mpin >= AGPIO(GPIO_PWM1_INV)) && (mpin < (AGPIO(GPIO_PWM1_INV) + MAX_PWMS))) {
bitSet(TasmotaGlobal.pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV));
bitSet(TasmotaGlobal.pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV)); // PWMi are later converted to PMW, but marked as inverted in TasmotaGlobal.pwm_inverted
mpin -= (AGPIO(GPIO_PWM1_INV) - AGPIO(GPIO_PWM1));
}
else if (XdrvCall(FUNC_PIN_STATE)) {
@ -2002,23 +1978,7 @@ void GpioInit(void)
#endif // USE_SONOFF_SC
#endif // ESP8266
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
#ifdef ESP8266
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
#endif // ESP8266
#ifdef ESP32
analogAttach(Pin(GPIO_PWM1, i));
#endif // ESP32
if (TasmotaGlobal.light_type) {
// force PWM GPIOs to low or high mode, see #7165
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
} else {
TasmotaGlobal.pwm_present = true;
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
}
}
}
GpioInitPwm();
for (uint32_t i = 0; i < MAX_RELAYS; i++) {
if (PinUsed(GPIO_REL1, i)) {

View File

@ -60,7 +60,23 @@ const uint8_t MAX_KEYS = 8; // Max number of keys or buttons (up
const uint8_t MAX_INTERLOCKS_SET = 14; // Max number of interlock groups (MAX_RELAYS / 2)
const uint8_t MAX_SWITCHES_SET = 28; // Max number of switches
const uint8_t MAX_LEDS = 4; // Max number of leds
const uint8_t MAX_PWMS = 5; // Max number of PWM channels
const uint8_t MAX_PWMS_LEGACY = 5; // Max number of PWM channels in first settings block - Legacy limit for ESP8266, but extended for ESP32 (see below)
#ifdef ESP32
// Max number of PWM channels (total including extended) - ESP32 only
#if defined(CONFIG_IDF_TARGET_ESP32)
const uint8_t MAX_PWMS = 16; // ESP32: 16 ledc PWM channels in total - TODO for now
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
const uint8_t MAX_PWMS = 8; // ESP32S2: 8 ledc PWM channels in total
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
const uint8_t MAX_PWMS = 8; // ESP32S3: 8 ledc PWM channels in total
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
const uint8_t MAX_PWMS = 6; // ESP32C3: 6 ledc PWM channels in total
#else
const uint8_t MAX_PWMS = 5; // Unknown - revert to 5 PWM max
#endif
#else
const uint8_t MAX_PWMS = 5; // (not used on ESP8266)
#endif
const uint8_t MAX_COUNTERS = 4; // Max number of counter sensors
const uint8_t MAX_TIMERS = 16; // Max number of Timers
const uint8_t MAX_PULSETIMERS = 8; // Max number of supported pulse timers
@ -88,6 +104,8 @@ const uint8_t MAX_I2S = 2; // Max number of Hardware I2S contro
const uint8_t MAX_RMT = 8; // Max number or RMT channels (ESP32 only)
#elif CONFIG_IDF_TARGET_ESP32S2
const uint8_t MAX_RMT = 4; // Max number or RMT channels (ESP32S2 only)
#elif CONFIG_IDF_TARGET_ESP32S3
const uint8_t MAX_RMT = 1; // Max number or RMT channels (ESP32S3 only)
#elif CONFIG_IDF_TARGET_ESP32C3
const uint8_t MAX_RMT = 2; // Max number or RMT channels (ESP32C3 only)
#else

View File

@ -154,6 +154,13 @@ struct TasmotaGlobal_t {
bool enable_logging; // Enable logging
StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits)
uint16_t pwm_inverted; // PWM inverted flag (1 = inverted) - extended to 16 bits for ESP32
#ifdef ESP32
int16_t pwm_cur_value[MAX_PWMS]; // Current effective values of PWMs as applied to GPIOs
int16_t pwm_cur_phase[MAX_PWMS]; // Current phase values of PWMs as applied to GPIOs
int16_t pwm_value[MAX_PWMS]; // Wanted values of PWMs after update - -1 means no change
int16_t pwm_phase[MAX_PWMS]; // Wanted phase of PWMs after update - -1 means no change
#endif // ESP32
uint8_t init_state; // Tasmota init state
uint8_t heartbeat_inverted; // Heartbeat pulse inverted flag
uint8_t spi_enabled; // SPI configured
@ -172,7 +179,7 @@ struct TasmotaGlobal_t {
uint8_t led_inverted; // LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t led_power; // LED power state
uint8_t ledlnk_inverted; // Link LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t pwm_inverted; // PWM inverted flag (1 = inverted)
// uint8_t pwm_inverted; // PWM inverted flag (1 = inverted) -- TODO
uint8_t energy_driver; // Energy monitor configured
uint8_t light_driver; // Light module configured
uint8_t light_type; // Light types

View File

@ -340,6 +340,7 @@
//#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
//#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
//#define USE_CM110x // Add support for CM110x CO2 sensors (+2k7 code)
#ifndef CO2_LOW
#define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1)
#endif
@ -476,6 +477,7 @@
#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
#define USE_CM110x // Add support for CM110x CO2 sensors (+2k7 code)
#ifndef CO2_LOW
#define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1)
#endif

View File

@ -181,6 +181,7 @@ enum UserSelectablePins {
GPIO_OPTION_E, // Emulated module
GPIO_SDM230_TX, GPIO_SDM230_RX, // SDM230 Serial interface
GPIO_ADC_MQ, // Analog MQ Sensor
GPIO_CM11_TXD, GPIO_CM11_RXD, // CM11 Serial interface
GPIO_SENSOR_END };
enum ProgramSelectablePins {
@ -400,7 +401,8 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_SOLAXX1_RTS "|"
D_SENSOR_OPTION " E|"
D_SENSOR_SDM230_TX "|" D_SENSOR_SDM230_RX "|"
D_SENSOR_ADC_MQ
D_SENSOR_ADC_MQ "|"
D_SENSOR_CM11_TX "|" D_SENSOR_CM11_RX "|"
;
const char kSensorNamesFixed[] PROGMEM =
@ -448,8 +450,8 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_CNTR1) + MAX_COUNTERS, // Counters
AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS,
#endif
AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White
AGPIO(GPIO_PWM1_INV) + MAX_PWMS,
AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White
AGPIO(GPIO_PWM1_INV) + MAX_PWMS, // or extended PWM for ESP32
#ifdef USE_BUZZER
AGPIO(GPIO_BUZZER), // Buzzer
AGPIO(GPIO_BUZZER_INV), // Inverted buzzer
@ -934,6 +936,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_MAX7219CS),
#endif // USE_DISPLAY_MAX7219
#ifdef USE_CM110x
AGPIO(GPIO_CM11_TXD), // CM110x Serial interface
AGPIO(GPIO_CM11_RXD), // CM110x Serial interface
#endif
/*-------------------------------------------------------------------------------------------*\
* ESP32 specifics
\*-------------------------------------------------------------------------------------------*/

Some files were not shown because too many files have changed in this diff Show More