Browse Source

menus and volume control

main
Hendrik Langer 7 years ago
parent
commit
5a17a55dbc
  1. 64
      src/main.cpp
  2. 2
      src/main.h
  3. 2
      src/mp3.cpp
  4. 2
      src/mp3.h
  5. 24
      src/rotary.cpp
  6. 5
      src/rotary.h
  7. 161
      src/screen.cpp
  8. 76
      src/screen.h
  9. 42
      src/selectionlist.cpp
  10. 44
      src/selectionlist.h

64
src/main.cpp

@ -9,11 +9,11 @@
* 5V O O 5V * 5V O O 5V
* 3V3 O O 3V3 * 3V3 O O 3V3
* GND O < 36 only input, no pullups * GND O < 36 only input, no pullups
* RX * * 17 * RX * ? * 17
* TX * < 38 ROTARY_A only input, no pullups * TX * < 38 ROTARY_A only input, no pullups
* RST * BUTTON < 39 ROTARY_B only input, no pullups * RST * BUTTON < 39 ROTARY_B only input, no pullups
* 0 * BUTTON < 34 only input, no pullups * 0 * BUTTON < 34 only input, no pullups
* 22 * < 35 only input, no pullups * ROTARY_BTN 22 * < 35 only input, no pullups
* BME280_SDO 19 * LoRa_MISO ? * 32 * BME280_SDO 19 * LoRa_MISO ? * 32
* BME280_CS 23 * ? * 33 * BME280_CS 23 * ? * 33
* 18 x LoRa_CS * 25 MAX98_DIN * 18 x LoRa_CS * 25 MAX98_DIN
@ -51,9 +51,8 @@
#include "main.h" #include "main.h"
#include "mp3.h" #include "mp3.h"
#include "BME280.h" #include "BME280.h"
#include "image.h"
#include "rotary.h" #include "rotary.h"
#include "selectionlist.h" #include "screen.h"
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16); U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16);
@ -67,11 +66,7 @@ char timeStr[20];
BME280 bme280; BME280 bme280;
MP3 mp3; MP3 mp3;
Rotary rotary; Rotary rotary;
SelectionList menu; Screen* screen;
uint32_t lastButtonPress = 0;
u8g2_uint_t titleStr_offset = 0;
//Variables for the sensor and the battery //Variables for the sensor and the battery
static const int buttonPin = 0; static const int buttonPin = 0;
@ -80,7 +75,7 @@ static const int SensorPin = 0;
static const int BatteryPin = 34; static const int BatteryPin = 34;
static const int rotaryPinA = 38; static const int rotaryPinA = 38;
static const int rotaryPinB = 39; static const int rotaryPinB = 39;
static const int rotaryPinButton = 36; static const int rotaryPinButton = 22;
int sensorValue = 0; int sensorValue = 0;
//The batteryLimit defines the point at which the battery is considered empty. //The batteryLimit defines the point at which the battery is considered empty.
int batteryLimit = 3300; int batteryLimit = 3300;
@ -138,6 +133,7 @@ void setup() {
u8g2.begin(); u8g2.begin();
delay(50); delay(50);
screen = new MainScreen();
u8g2.clearBuffer(); // clear the internal memory u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
u8g2.drawStr(2,10,"esp32-node by hendrik"); // write something to the internal memory u8g2.drawStr(2,10,"esp32-node by hendrik"); // write something to the internal memory
@ -272,11 +268,10 @@ void suspendESP(uint16_t packetId) {
} }
void rotation(int i, int direction) { void rotation(int i, int direction, int buttonPressed) {
Serial.println(i); if (buttonPressed == 1) screen->select();
mp3.setVolume(i); if (direction == 1) screen->next();
if (direction == 1) menu.next(); else if (direction == -1) screen->previous();
else if (direction == -1) menu.previous();
} }
@ -285,44 +280,7 @@ void loop()
timeClient.update(); timeClient.update();
timeClient.getFormattedTime().toCharArray(timeStr, 50); timeClient.getFormattedTime().toCharArray(timeStr, 50);
if (digitalRead(buttonPin) == LOW) { screen->draw();
if(millis() - lastButtonPress >= 1000) {
Serial.println("Button pressed");
lastButtonPress = millis();
if (mp3.playing) {
mp3.stop();
} else {
mp3.start();
}
}
}
menu.draw();
/*
u8g2.clearBuffer(); // clear the internal memory
if (digitalRead(buttonPin) == HIGH) {
u8g2.setFont(u8g2_font_inb19_mf);
u8g2.drawStr(0, 20, timeStr);
char weather[32];
u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font
sprintf(weather, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure());
u8g2.drawUTF8(0, 30, weather);
u8g2.setFont(u8g2_font_prospero_bold_nbp_tf); // choose a suitable font
u8g2_uint_t width = u8g2.getUTF8Width(titleStr); // calculate the pixel width of the text
// repeated drawing of the scrolling text...
u8g2.drawUTF8(titleStr_offset, 54, titleStr); // draw the scolling text
titleStr_offset-=4; // scroll by one pixel
if ( (u8g2_uint_t)titleStr_offset < (u8g2_uint_t)-width )
titleStr_offset = 0; // start over again // u8g2.getDisplayWidth()
} else {
u8g2.drawXBMP(0,0, IMG_1872_width, IMG_1872_height, IMG_1872_bits);
}
u8g2.sendBuffer();
// bme280.printValues();
*/
delay(200); delay(200);
} }

2
src/main.h

@ -7,4 +7,4 @@ void transmitStatus();
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total); void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total);
void suspendESP(uint16_t packetId); void suspendESP(uint16_t packetId);
void loop(); void loop();
void rotation(int i, int direction); void rotation(int i, int direction, int buttonPressed);

2
src/mp3.cpp

@ -49,7 +49,7 @@ void MP3::start() {
xTaskCreate( xTaskCreate(
&cTaskWrapper, /* Task function. */ &cTaskWrapper, /* Task function. */
"audioTask", /* String with name of task. */ "audioTask", /* String with name of task. */
2048, /* Stack size in words. */ 3072, /* Stack size in words. */
this, /* Parameter passed as input of the task */ this, /* Parameter passed as input of the task */
tskIDLE_PRIORITY+1, /* Priority of the task. */ tskIDLE_PRIORITY+1, /* Priority of the task. */
&audioTaskHandle); /* Task handle. */ &audioTaskHandle); /* Task handle. */

2
src/mp3.h

@ -32,7 +32,7 @@ class MP3 {
void mp3_decoder_task(void*); void mp3_decoder_task(void*);
static void cTaskWrapper(void*); static void cTaskWrapper(void*);
static constexpr int preallocateBufferSize = 5*1024; static constexpr int preallocateBufferSize = 8*1024;
static constexpr int preallocateCodecSize = 29192; // MP3 codec max mem needed static constexpr int preallocateCodecSize = 29192; // MP3 codec max mem needed
void *preallocateBuffer = NULL; void *preallocateBuffer = NULL;
void *preallocateCodec = NULL; void *preallocateCodec = NULL;

24
src/rotary.cpp

@ -21,18 +21,19 @@ bool Rotary::begin(uint8_t pinA, uint8_t pinB, uint8_t pinButton) {
this->pinButton = pinButton; this->pinButton = pinButton;
pinMode(pinA, INPUT_PULLUP); pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP); pinMode(pinB, INPUT_PULLUP);
pinMode(pinButton, INPUT_PULLUP); pinMode(pinButton, INPUT_PULLDOWN);
xTaskCreate( xTaskCreate(
&cTaskWrapper, /* Task function. */ &cTaskWrapper, /* Task function. */
"encoderTask", /* String with name of task. */ "encoderTask", /* String with name of task. */
1024, /* Stack size in words. */ 2048, /* Stack size in words. */
this, /* Parameter passed as input of the task */ this, /* Parameter passed as input of the task */
tskIDLE_PRIORITY+2, /* Priority of the task. */ tskIDLE_PRIORITY+2, /* Priority of the task. */
&taskHandle); /* Task handle. */ &taskHandle); /* Task handle. */
attachInterrupt(digitalPinToInterrupt(pinA), doEncoder, CHANGE); attachInterrupt(digitalPinToInterrupt(pinA), doEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(pinB), doEncoder, CHANGE); attachInterrupt(digitalPinToInterrupt(pinB), doEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(pinButton), doEncoder, CHANGE);
return true; return true;
} }
@ -48,6 +49,19 @@ void Rotary::task(void *pvParameters) {
ulNotificationValue = ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); ulNotificationValue = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
delay(2); // wait until bounce settled delay(2); // wait until bounce settled
if (digitalRead(pinButton) == HIGH) {
Serial.println("button");
buttonPressed = true;
if (callback) callback(value, 0, 1);
continue;
} else {
if (buttonPressed) {
buttonPressed = false;
if (callback) callback(value, 0, -1);
continue;
}
}
static uint8_t old_AB = 0; static uint8_t old_AB = 0;
// grey code // grey code
// http://hades.mech.northwestern.edu/index.php/Rotary_Encoder // http://hades.mech.northwestern.edu/index.php/Rotary_Encoder
@ -67,10 +81,10 @@ void Rotary::task(void *pvParameters) {
debouncePulses = 0; debouncePulses = 0;
if (encoderPos > encoderPosOld+1) { if (encoderPos > encoderPosOld+1) {
value++; // if the value has at least changed for 2 value++; // if the value has at least changed for 2
if (callback) callback(value, 1); if (callback) callback(value, 1, (int)buttonPressed);
} else if (encoderPos < encoderPosOld-1) { } else if (encoderPos < encoderPosOld-1) {
value--; value--;
if (callback) callback(value, -1); if (callback) callback(value, -1, (int)buttonPressed);
} }
// otherwise skip // otherwise skip
encoderPosOld = encoderPos; encoderPosOld = encoderPos;
@ -80,7 +94,7 @@ void Rotary::task(void *pvParameters) {
vTaskDelete(NULL); vTaskDelete(NULL);
} }
bool Rotary::registerCallback(std::function<void(int, int)> callback) { bool Rotary::registerCallback(std::function<void(int, int, int)> callback) {
this->callback = callback; this->callback = callback;
return true; return true;
} }

5
src/rotary.h

@ -16,11 +16,12 @@ class Rotary {
public: public:
Rotary(); Rotary();
bool begin(uint8_t pinA, uint8_t pinB, uint8_t pinButton); bool begin(uint8_t pinA, uint8_t pinB, uint8_t pinButton);
bool registerCallback(std::function<void(int, int)> callback); bool registerCallback(std::function<void(int, int, int)> callback);
static void IRAM_ATTR doEncoder(void); static void IRAM_ATTR doEncoder(void);
static Rotary* instance; static Rotary* instance;
volatile int encoderPos = 0; volatile int encoderPos = 0;
int encoderPosOld = 0; int encoderPosOld = 0;
bool buttonPressed = false;
int value = 0; int value = 0;
uint32_t debounceMillis; uint32_t debounceMillis;
uint32_t debounceDelay = 1; uint32_t debounceDelay = 1;
@ -31,7 +32,7 @@ class Rotary {
uint8_t pinA; uint8_t pinA;
uint8_t pinB; uint8_t pinB;
uint8_t pinButton; uint8_t pinButton;
std::function<void(int, int)> callback = nullptr; std::function<void(int, int, int)> callback = nullptr;
TaskHandle_t taskHandle; TaskHandle_t taskHandle;
void task(void*); void task(void*);
static void cTaskWrapper(void*); static void cTaskWrapper(void*);

161
src/screen.cpp

@ -0,0 +1,161 @@
#include "screen.h"
void Screen::next() { Serial.println("unhandled next"); }
void Screen::previous() { Serial.println("unhandled previous"); }
uint8_t Screen::select() { Serial.println("unhandled select"); }
SelectionList::SelectionList() {}
SelectionList::SelectionList(const char* title, uint8_t start_pos, const char* string_list) {}
void SelectionList::draw() {
uint8_t item_x_offset = 15;
uint8_t header_height = 12;
uint8_t item_height = 12;
uint8_t num_active_item = current_pos - top_item;
u8g2.clearBuffer();
/* draw Title */
u8g2.setFont(u8g2_font_guildenstern_nbp_tr);
u8g2.drawUTF8(2,header_height-2,title);
u8g2.drawLine(0,header_height,u8g2.getDisplayWidth(),header_height);
/* draw menu items */
u8g2.setFont(u8g2_font_9x18B_tr);
for (int i=0; i<visible; i++) {
u8g2.drawUTF8(item_x_offset,header_height+2+(i+1)*item_height,string_list[top_item+i].c_str());
}
u8g2.drawFrame(0,header_height+1+num_active_item*item_height,u8g2.getDisplayWidth(),item_height+2 );
u8g2.sendBuffer(); // transfer internal memory to the display
}
void SelectionList::next() {
if (current_pos < string_list.size()-1) current_pos++;
if (current_pos > top_item+visible-1) top_item++;
}
void SelectionList::previous() {
if (current_pos > 0 ) current_pos--;
if (current_pos < top_item) top_item--;
}
uint8_t SelectionList::select() {
return current_pos;
}
namespace {
void Drawgauge(int x, byte y, byte r, byte p, int v, int minVal, int maxVal ) {
int n=(r/100.00)*p; // calculate needle percent lenght
float gs_rad=-1.572; //-90 degrees in radiant
float ge_rad=0;
float i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad);
int xp = x+(sin(i) * n);
int yp = y-(cos(i) * n);
u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT );
u8g2.drawLine(x,y,xp,yp);
gs_rad=0;
ge_rad=1.572; //90 degrees in radiant
i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad);
xp = x+(cos(i) * n);
yp = y-(sin(i) * n);
u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_RIGHT );
u8g2.drawLine(x,y,xp,yp);
gs_rad=-1.572;
ge_rad=1.572;
i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad);
xp = x+(sin(i) * n);
yp = y-(cos(i) * n);
u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT|U8G2_DRAW_UPPER_RIGHT );
u8g2.drawLine(x,y,xp,yp);
gs_rad=-3.142;
ge_rad=1.572;
i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad);
xp = x+(sin(i) * n);
yp = y-(cos(i) * n);
u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT|U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_LEFT );
u8g2.drawLine(x,y,xp,yp);
gs_rad=-1.572;
ge_rad=3.141;
i=((v-minVal)*(ge_rad-gs_rad)/(maxVal-minVal)+gs_rad);
xp = x+(sin(i) * n);
yp = y-(cos(i) * n);
u8g2.drawCircle(x,y,r, U8G2_DRAW_UPPER_LEFT|U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_RIGHT );
u8g2.drawLine(x,y,xp,yp);
}
}
void MainScreen::draw() {
u8g2.clearBuffer(); // clear the internal memory
if(millis() - lastVolumeChange <= 1000) {
//u8g2.drawXBMP(0,0, IMG_1872_width, IMG_1872_height, IMG_1872_bits);
u8g2.setFont(u8g2_font_inb19_mf);
u8g2.drawStr(20, 20, "Volume");
u8g2.drawRBox(10, 28, 108, 22, 5);
u8g2.setDrawColor(0);
u8g2.drawBox(14+volume, 31, 100-volume, 14);
char volumeStr[6];
sprintf(volumeStr, "%d %%", volume);
u8g2.setFont(u8g2_font_crox3cb_mf);
u8g2.setFontMode(1);
u8g2.setDrawColor(2);
u8g2.drawStr(40, 45, volumeStr);
u8g2.setFontMode(0);
u8g2.setDrawColor(1);
} else {
u8g2.setFont(u8g2_font_inb19_mf);
u8g2.drawStr(0, 20, timeStr);
char weather[32];
u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font
sprintf(weather, "%.1f°C %.1f%% %.0fhPa", bme280.readTemperature(), bme280.readHumidity(), bme280.readPressure());
u8g2.drawUTF8(0, 30, weather);
u8g2.setFont(u8g2_font_prospero_bold_nbp_tf); // choose a suitable font
u8g2_uint_t width = u8g2.getUTF8Width(titleStr); // calculate the pixel width of the text
// repeated drawing of the scrolling text...
u8g2.drawUTF8(titleStr_offset, 54, titleStr); // draw the scolling text
titleStr_offset-=4; // scroll by one pixel
if ( (u8g2_uint_t)titleStr_offset < (u8g2_uint_t)-width )
titleStr_offset = 0; // start over again // u8g2.getDisplayWidth()
}
u8g2.sendBuffer();
// bme280.printValues();
}
uint8_t MainScreen::select() {
if(millis() - lastButtonPress >= 1000) {
Serial.println("Button pressed");
lastButtonPress = millis();
if (mp3.playing) {
mp3.stop();
} else {
mp3.start();
}
}
}
void MainScreen::next() {
if (volume < 100) volume++;
lastVolumeChange = millis();
mp3.setVolume(volume);
Serial.printf("volume: %d\n", volume);
}
void MainScreen::previous() {
if (volume > 0) volume--;
lastVolumeChange = millis();
mp3.setVolume(volume);
Serial.printf("volume: %d\n", volume);
}

76
src/screen.h

@ -0,0 +1,76 @@
#ifndef _SCREEN_H
#define _SCREEN_H
#include <Arduino.h>
#include <vector>
#include <string>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#include "mp3.h"
#include "BME280.h"
#include "image.h"
extern U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2;
extern char timeStr[];
extern BME280 bme280;
extern MP3 mp3;
class Screen {
public:
// Screen();
virtual void draw(void) = 0;
virtual void next(void);
virtual void previous(void);
virtual uint8_t select(void);
const char* title = "Test Screen";
private:
};
class SelectionList : public Screen {
public:
SelectionList();
SelectionList(const char*, uint8_t, const char*);
void draw(void) override;
void next(void) override;
void previous(void) override;
uint8_t select(void) override;
const char* title = "Cloud Types";
private:
std::vector<std::string> string_list = {
"Altocumulus\n",
"Altostratus\n",
"Cirrocumulus\n",
"Cirrostratus\n",
"Cirrus\n",
"Cumulonimbus\n",
"Cumulus\n",
"Nimbostratus\n",
"Stratocumulus\n",
"Stratus" };
uint8_t current_pos = 0;
uint8_t top_item = 0;
uint8_t visible = 4;
};
class MainScreen : public Screen {
public:
void draw(void) override;
void next(void) override;
void previous(void) override;
uint8_t select(void) override;
const char* title = "Main Screen";
private:
uint32_t lastButtonPress = 0;
uint32_t lastVolumeChange = 0;
u8g2_uint_t titleStr_offset = 0;
uint8_t volume = 20;
};
#endif /* _SCREEN_H */

42
src/selectionlist.cpp

@ -1,42 +0,0 @@
#include "selectionlist.h"
SelectionList::SelectionList() {}
SelectionList::SelectionList(const char* title, uint8_t start_pos, const char* string_list) {}
void SelectionList::draw() {
uint8_t item_x_offset = 15;
uint8_t header_height = 12;
uint8_t item_height = 12;
uint8_t num_active_item = current_pos - top_item;
u8g2.clearBuffer();
/* draw Title */
u8g2.setFont(u8g2_font_guildenstern_nbp_tr);
u8g2.drawUTF8(2,header_height-2,title);
u8g2.drawLine(0,header_height,u8g2.getDisplayWidth(),header_height);
/* draw menu items */
u8g2.setFont(u8g2_font_9x18B_tr);
for (int i=0; i<visible; i++) {
u8g2.drawUTF8(item_x_offset,header_height+2+(i+1)*item_height,string_list[top_item+i].c_str());
}
u8g2.drawFrame(0,header_height+1+num_active_item*item_height,u8g2.getDisplayWidth(),item_height+2 );
u8g2.sendBuffer(); // transfer internal memory to the display
}
void SelectionList::next() {
if (current_pos < string_list.size()-1) current_pos++;
if (current_pos > top_item+visible-1) top_item++;
}
void SelectionList::previous() {
if (current_pos > 0 ) current_pos--;
if (current_pos < top_item) top_item--;
}
uint8_t SelectionList::select() {
return current_pos;
}

44
src/selectionlist.h

@ -1,44 +0,0 @@
#ifndef _SELECTIONLIST_H
#define _SELECTIONLIST_H
#include <Arduino.h>
#include <vector>
#include <string>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
extern U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2;
class SelectionList {
public:
SelectionList();
SelectionList(const char*, uint8_t, const char*);
void draw(void);
void next(void);
void previous(void);
uint8_t select(void);
private:
const char* title = "Cloud Types";
std::vector<std::string> string_list = {
"Altocumulus\n",
"Altostratus\n",
"Cirrocumulus\n",
"Cirrostratus\n",
"Cirrus\n",
"Cumulonimbus\n",
"Cumulus\n",
"Nimbostratus\n",
"Stratocumulus\n",
"Stratus" };
uint8_t current_pos = 0;
uint8_t top_item = 0;
uint8_t visible = 4;
};
#endif /* _SELECTIONLIST_H */
Loading…
Cancel
Save