Created
January 9, 2020 21:53
-
-
Save cyrus007/9896625139585b7ed17d389460e0fd7f to your computer and use it in GitHub Desktop.
ESP32 BLE server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* "ESP32 BLE Server" | |
* | |
* Control RGB LED and two servos using the Android app "Bluetooth Remote". | |
* 1) create a BLE Device; | |
* 2) create a BLE Server; | |
* 3) create a BLE UART Service with one characteristic for TX | |
* and one for RX using the following UUIDs: | |
* 6E400001-B5A3-F393-E0A9-E50E24DCCA9E for the Service, | |
* 6E400002-B5A3-F393-E0A9-E50E24DCCA9E for the TX Characteristic (Property = Notify), | |
* 6E400003-B5A3-F393-E0A9-E50E24DCCA9E for the RX Characteristic (Property = Write); | |
* 4) create a BLE Characteristics (TX, RX) on the Service; | |
* 5) start the UART Service; | |
* 6) start advertising. | |
* | |
* ======================================================================================== | |
* | |
* RGB LED connected to pins: | |
* Red > 22 | |
* Green > 21 | |
* Blue > 23 | |
* | |
* SERVOS pins: 32, 33 | |
* | |
* DS3231 Specifications | |
* Operating voltage: 3.3 V to 5.5 V | |
* Clock accuracy: 2 ppm | |
* On-chip temperature sensor with an accuracy of ±3C | |
* I2C bus interface maximum speed: 400 kHz (5V) | |
* Pins: | |
* SDA 16 | |
* SCL 17 | |
* | |
* ======================================================================================== | |
* | |
* Android app "Bluetooth Remote" (2.2.5): | |
* https://drive.google.com/open?id=1nheO9eKtFGp--oWFJB4eTAi3OwBfCRFa | |
* | |
* YouTube: | |
* Part I, "RGB LED control using the Android app" https://youtu.be/bcwOw9kcuzQ | |
* Part II, "Servo motors control via Android app" https://youtu.be/GKpnU84tFXg | |
* ESP32 + RTC DS3231, Bluetooth control alarm clock https://youtu.be/5dGQzNKRUz8 | |
* | |
* ESP32 projects page: | |
* https://dlapaev.wixsite.com/esp32arduino/esp32-projects | |
* | |
* © Dmitriy Lapayev, 2019 | |
* | |
* ======================================================================================== | |
*/ | |
#include <BLEDevice.h> | |
#include <BLEServer.h> | |
#include <BLEUtils.h> | |
#include <BLE2902.h> | |
#include <driver/adc.h> | |
#include <driver/dac.h> | |
#include <ESP32_Servo.h> | |
#include <Preferences.h> | |
// DS3231 RTC connected via I2C | |
#include <DS3231Esp32.h> | |
// Debug trigger | |
const bool DEBUG = true; | |
#define TIME_TO_SLEEP 900 | |
#define TIME_BEFORE_SLEEP 300000 | |
unsigned long time_for_sleep = TIME_BEFORE_SLEEP; | |
#define TIME_UPDATE 1000 | |
unsigned long time_for_update = TIME_UPDATE; | |
// DS3231 RTC | |
DS3231 rtc; | |
#define SDA_PIN 16 | |
#define SCL_PIN 17 | |
uint8_t time_date[3] = {0, 0, 0}; | |
uint8_t alarm_time[2] = {0, 0}; | |
float temperatureDs = 0; | |
char roundFloatBuff[8] = {0}; | |
// Task handle | |
TaskHandle_t xTaskAlert = NULL; | |
bool runAlertTask = false; | |
bool notifyTime = false; | |
// BLE Device namе | |
const char* DEV_NAME = "Esp32(UART)"; | |
// BLE Server | |
BLEServer* pServer = NULL; | |
// TX Characteristic | |
BLECharacteristic* pTxCharacteristic; | |
bool deviceConnected = false; | |
bool oldDeviceConnected = false; | |
// For generating UUIDs: https://www.uuidgenerator.net | |
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" | |
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" | |
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" | |
/* Preferences */ | |
Preferences preferences; | |
const char* APP_NAME_KEY = "esp_ble_uart"; | |
char rgb_color_key[] = "color_comp_0"; | |
// Data array of joysticks (4 channels) | |
#define NUMB_JOY_CHLS 4 | |
// [XL, YL, XR, YR] | |
uint8_t joystick_data[NUMB_JOY_CHLS] = {50, 50, 50, 50}; | |
char indexDelimiter = -1; | |
#define MAX12BIT_VALUE 4095 | |
// GPIO 34, ADC1 channel 6 | |
#define VOLTAGE_SENSOR ADC1_CHANNEL_6 | |
float voltage = 0; | |
// GPIO 35, ADC1 channel 7 | |
#define THERMISTOR_CH ADC1_CHANNEL_7 | |
float therm_res = 0; | |
typedef enum { | |
UNSPECIFIED = 0, | |
JOYSTICK = 1, | |
COLOR = 2, | |
SET_TIME = 3, | |
SET_DATE = 4, | |
SET_ALARM = 5 | |
} command_type; | |
command_type commType = UNSPECIFIED; | |
/* SERVOS */ | |
// create two servo objects | |
Servo servo1; | |
Servo servo2; | |
// Published values for SG90 servos; adjust if needed | |
uint16_t minUs = 400; | |
uint16_t maxUs = 2400; | |
// Servos Pins | |
#define SERVO_PIN_1 32 | |
#define SERVO_PIN_2 33 | |
/* RGB LED */ | |
#define NUMBER_CHANNELS 3 | |
// 3 channels (0 - 15) | |
#define LEDC_CHANNEL_R 4 | |
#define LEDC_CHANNEL_G 5 | |
#define LEDC_CHANNEL_B 6 | |
// LED Channels array | |
const uint8_t LED_CHANNELS[NUMBER_CHANNELS] = { | |
LEDC_CHANNEL_R, | |
LEDC_CHANNEL_G, | |
LEDC_CHANNEL_B | |
}; | |
// LED Pins (12, 13, 14) | |
#define LED_PIN_R 22 | |
#define LED_PIN_G 21 | |
#define LED_PIN_B 23 | |
// LED Pins array | |
const uint8_t LED_PINS_ARR[NUMBER_CHANNELS] = { | |
LED_PIN_R, | |
LED_PIN_G, | |
LED_PIN_B | |
}; | |
// 13 bit precission for LEDC timer | |
#define LEDC_TIMER_13_BIT 13 | |
#define MAX_13BIT_VALUE 8191 | |
// 5000 Hz as a LEDC base frequency (5 kHz PWM) | |
#define LEDC_BASE_FREQ 5000 | |
// 8 bit precission | |
#define LEDC_TIMER_8_BIT 8 | |
#define MAX_8BIT_VALUE 255 | |
#define MAX_7BIT_VALUE 127 | |
// An array for storing RGB-LED color | |
uint8_t current_color[NUMBER_CHANNELS] = {0, 0, MAX_7BIT_VALUE}; | |
// DAC Audio | |
#define MAX_VOLUME_VALUE 248 | |
#define DELAY_PAUSE 50 | |
/** | |
* Arduino like analogWrite value | |
* has to be between 0 and max value 255. | |
* (13 bit precission for LEDC timer) | |
*/ | |
void ledWrite(const uint8_t CHANNEL, const uint8_t VALUE) { | |
// calculate duty, 8191 from 2 ^ 13 - 1, write duty to LEDC | |
ledcWrite(CHANNEL, (MAX_13BIT_VALUE / MAX_8BIT_VALUE) * VALUE); | |
} | |
void writeCurrentColor(void) { | |
for (uint8_t i = 0; i < NUMBER_CHANNELS; i++) { | |
ledcWrite(LED_CHANNELS[i], current_color[i]); | |
} | |
} | |
void getColorFromPrefs(void) { | |
// Preferences, read-only: true | |
preferences.begin(APP_NAME_KEY, true); | |
for (uint8_t i = 0; i < NUMBER_CHANNELS; i++) { | |
rgb_color_key[11] = 48 + i; | |
current_color[i] = preferences.getUChar(rgb_color_key, current_color[i]); | |
} | |
// Close the Preferences | |
preferences.end(); | |
} | |
void saveColorToPrefs(void) { | |
// Preferences, read-only: false | |
preferences.begin(APP_NAME_KEY, false); | |
for (uint8_t i = 0; i < NUMBER_CHANNELS; i++) { | |
rgb_color_key[11] = 48 + i; | |
preferences.putUChar(rgb_color_key, current_color[i]); | |
} | |
// Close the Preferences | |
preferences.end(); | |
} | |
/** Server Callbacks. */ | |
class MyServerCallbacks: public BLEServerCallbacks { | |
void onConnect(BLEServer* pServer) { | |
deviceConnected = true; | |
}; | |
void onDisconnect(BLEServer* pServer) { | |
deviceConnected = false; | |
} | |
}; | |
/** Bluetooth LE Characteristi Callbacks. */ | |
class MyCallbacks: public BLECharacteristicCallbacks { | |
void onWrite(BLECharacteristic* pCharacteristic) { | |
std::string rxCommand = pCharacteristic -> getValue(); | |
String comm = ""; | |
if (rxCommand.length() > 0) { | |
Serial.print("\n--- onWrite: Core ID = "); | |
Serial.print(xPortGetCoreID()); | |
Serial.println(" ---"); | |
//printLine(); | |
Serial.print("Received Value: "); | |
for (int i = 0; i < rxCommand.length(); i++) { | |
Serial.print(rxCommand[i]); | |
// cut off the first two and the last | |
if (i > 1 && i < rxCommand.length() - 1) { | |
if (rxCommand[i] == ' ') break; | |
comm += rxCommand[i]; | |
} | |
} | |
Serial.println(); | |
if (rxCommand.length() > 1) { | |
runAlertTask = false; | |
switch (rxCommand[1]) { | |
case 'j': | |
parsingJoystickData(comm); | |
commType = JOYSTICK; | |
break; | |
case 'c': | |
parsingColorData(comm); | |
commType = COLOR; | |
break; | |
case 't': | |
parsingTimeDate(comm); | |
setTime(time_date); | |
commType = SET_TIME; | |
break; | |
case 'd': | |
parsingTimeDate(comm); | |
setDate(time_date); | |
commType = SET_DATE; | |
break; | |
case 'a': | |
parsingAlarmTime(comm); | |
commType = SET_ALARM; | |
break; | |
case 'f': | |
// off | |
rtc.armAlarm2(false); | |
commType = SET_ALARM; | |
break; | |
case 'n': | |
// on | |
rtc.armAlarm2(true); | |
commType = SET_ALARM; | |
break; | |
} | |
} | |
else if (rxCommand.length() == 1) { | |
switch (rxCommand[0]) { | |
case '0': | |
notifyTime = false; | |
break; | |
// show time, date, voltage | |
case '1': | |
notifyTime = true; | |
break; | |
// restart | |
case '2': | |
//ESP.restart(); | |
break; | |
// sleep | |
case '3': | |
toDeepSleep(true); | |
break; | |
} | |
} | |
printLine(); | |
} | |
} | |
void parsingJoystickData(const String J_STR) { | |
// xL | |
joystick_data[0] = 180 - 1.8f * getValue(J_STR, ':', 0).toInt(); | |
// yL | |
joystick_data[1] = 180 - 1.8f * getValue(J_STR, ':', 1).toInt(); | |
// xR | |
joystick_data[2] = 180 - 1.8f * getValue(J_STR, ':', 2).toInt(); | |
// yR | |
joystick_data[3] = 180 - 1.8f * getValue(J_STR, ':', 3).toInt(); | |
printJoystickData("Joystick:"); | |
servo1.write(joystick_data[0]); | |
servo2.write(joystick_data[1]); | |
} | |
String getValue(const String str, const char separator, const int index) { | |
int found = 0; | |
int strIndex[] = {0, -1}; | |
const int maxIndex = str.length() - 1; | |
for (int i = 0; i <= maxIndex && found <= index; i++) { | |
if (str.charAt(i) == separator || i == maxIndex) { | |
found++; | |
strIndex[0] = strIndex[1] + 1; | |
strIndex[1] = (i == maxIndex) ? i + 1 : i; | |
} | |
} | |
return found > index ? str.substring(strIndex[0], strIndex[1]) : ""; | |
} | |
void printJoystickData(char* title) { | |
Serial.print(title); | |
Serial.print(" left["); | |
Serial.print(joystick_data[0]); | |
Serial.print("x"); | |
Serial.print(joystick_data[1]); | |
Serial.print("] right["); | |
Serial.print(joystick_data[2]); | |
Serial.print("x"); | |
Serial.print(joystick_data[3]); | |
Serial.println("]"); | |
} | |
void printLine(void) { | |
for (uint8_t i = 0; i < 28; i++) Serial.print("-"); | |
Serial.println(); | |
} | |
void parsingColorData(const String J_STR) { | |
indexDelimiter = J_STR.indexOf(":"); | |
char indexSec = J_STR.lastIndexOf(":"); | |
current_color[0] = J_STR.substring(0, indexDelimiter).toInt(); | |
current_color[1] = J_STR.substring(indexDelimiter + 1, indexSec).toInt(); | |
current_color[2] = J_STR.substring(indexSec + 1).toInt(); | |
saveColorToPrefs(); | |
writeCurrentColor(); | |
} | |
void parsingTimeDate(const String DT) { | |
indexDelimiter = DT.indexOf(":"); | |
char indexSec = DT.lastIndexOf(":"); | |
time_date[0] = DT.substring(0, indexDelimiter).toInt(); | |
time_date[1] = DT.substring(indexDelimiter + 1, indexSec).toInt(); | |
time_date[2] = DT.substring(indexSec + 1).toInt(); | |
} | |
void parsingAlarmTime(const String ALATM_STR) { | |
indexDelimiter = ALATM_STR.indexOf(":"); | |
alarm_time[0] = ALATM_STR.substring(0, indexDelimiter).toInt(); | |
alarm_time[1] = ALATM_STR.substring(indexDelimiter + 1).toInt(); | |
rtc.setAlarm2(0, alarm_time[0], alarm_time[1], DS3231_MATCH_H_M, true); | |
} | |
/** | |
* Time setting with seconds correction. | |
*/ | |
void setTime(uint8_t time_arr[3]) { | |
rtc.getDateTime(); | |
time_arr[2]++; | |
if (time_arr[2] > 59) { | |
time_arr[2] -= 60; | |
time_arr[1]++; | |
} | |
rtc.setDateTime(rtc.dt.year, rtc.dt.month, rtc.dt.day, time_arr[0], time_arr[1], time_arr[2]); | |
} | |
void setDate(const uint8_t DT[3]) { | |
rtc.setDateTime(2000 + DT[2], DT[1], DT[0], 0, 0, 0); | |
} | |
}; | |
/* --- SETUP --- */ | |
void setup() { | |
/* Initialize Serial interface */ | |
Serial.begin(115200); | |
/* Setup timer and attach timer to a Led pins */ | |
for (uint8_t i = 0; i < NUMBER_CHANNELS; i++) { | |
// channel timer | |
ledcSetup(LED_CHANNELS[i], LEDC_BASE_FREQ, LEDC_TIMER_8_BIT); | |
// assign RGB led pin to channel | |
ledcAttachPin(LED_PINS_ARR[i], LED_CHANNELS[i]); | |
// For 8-bit resolution duty cycle is 0 - 255, | |
// test high output of all leds in sequence | |
ledcWrite(LED_CHANNELS[i], MAX_8BIT_VALUE); | |
delay(250); | |
ledcWrite(LED_CHANNELS[i], 0); | |
} | |
getColorFromPrefs(); | |
/* Attach timer to a Servos pins */ | |
servo1.attach(SERVO_PIN_1, minUs, maxUs); | |
servo2.attach(SERVO_PIN_2, minUs, maxUs); | |
// ADC1 Config: 12 bits wide (range 0-4095) | |
adc1_config_width(ADC_WIDTH_12Bit); | |
// Voltage sensor | |
adc1_config_channel_atten(VOLTAGE_SENSOR, ADC_ATTEN_0db); | |
// Thermistor, 11dB attenuation (ADC_ATTEN_11db) gives full-scale voltage 3.9V | |
adc1_config_channel_atten(THERMISTOR_CH, ADC_ATTEN_11db); | |
/* Initialize DS3231 */ | |
Serial.println("\nInitialize RTC DS3231:"); | |
rtc.begin(SDA_PIN, SCL_PIN, 100000); | |
Serial.print("I2C bus clock rate: "); | |
Serial.print(Wire.getClock() * 0.001); | |
Serial.println(" kHz"); | |
// Set sketch compiling time | |
// rtc.setDateTime(__DATE__, __TIME__); | |
// Set the oscillator | |
setOscillator(); | |
// Print alarms state | |
printAlarmState(); | |
/* - BLE Server - */ | |
// Create a BLE Device | |
BLEDevice::init(DEV_NAME); | |
// Create a BLE Server | |
pServer = BLEDevice::createServer(); | |
pServer->setCallbacks(new MyServerCallbacks()); | |
// Create a BLE Service | |
BLEService* pService = pServer -> createService(SERVICE_UUID); | |
// Create a BLE Characteristic (TX) | |
pTxCharacteristic = pService -> createCharacteristic( CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY); | |
// Add descriptor | |
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml | |
pTxCharacteristic -> addDescriptor(new BLE2902()); | |
// Create a BLE Characteristic (RX) | |
BLECharacteristic* pRxCharacteristic = pService -> createCharacteristic( CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE); | |
pRxCharacteristic -> setCallbacks(new MyCallbacks()); | |
// Start the Service | |
pService -> start(); | |
// Start advertising | |
pServer -> getAdvertising() -> start(); | |
Serial.println("\nWaiting a client connection...\n"); | |
setWaitingState(); | |
runAlertTask = false; | |
} | |
/* --- LOOP --- */ | |
void loop() { | |
// Device is connected | |
if (deviceConnected && commType != UNSPECIFIED) { | |
switch(commType) { | |
case JOYSTICK: | |
pTxCharacteristic->setValue(getTxJoystickValue(joystick_data)); | |
break; | |
case COLOR: | |
pTxCharacteristic->setValue(getTxColorValue(current_color)); | |
break; | |
case SET_TIME: | |
pTxCharacteristic->setValue(getTxTimeDateValue(time_date, true)); | |
break; | |
case SET_DATE: | |
pTxCharacteristic->setValue(getTxTimeDateValue(time_date, false)); | |
break; | |
case SET_ALARM: | |
pTxCharacteristic->setValue(getTxAlarmValue(alarm_time)); | |
break; | |
} | |
commType = UNSPECIFIED; | |
pTxCharacteristic->notify(); | |
// bluetooth stack will go into congestion, if too many packets are sent | |
delay(10); | |
} | |
// Device disconnecting | |
if (!deviceConnected && oldDeviceConnected) { | |
// waiting state | |
setWaitingState(); | |
// give the bluetooth stack the chance to get things ready | |
delay(500); | |
// restart advertising | |
pServer->startAdvertising(); | |
Serial.println("\nRestart advertising\n"); | |
oldDeviceConnected = deviceConnected; | |
} | |
// Connecting | |
if (deviceConnected && !oldDeviceConnected) { | |
// do stuff here on connecting | |
oldDeviceConnected = deviceConnected; | |
writeCurrentColor(); | |
delay(400); | |
dacSoundAlert(3, 1); | |
Serial.println("\nConnected!\n"); | |
pTxCharacteristic->setValue("Welcome!"); | |
pTxCharacteristic->notify(); | |
// COLOR or ... | |
commType = SET_ALARM; | |
time_for_sleep = 0; | |
dacSoundAlert(6, 1); | |
} | |
// Updating every second (Core ID = 1) | |
if (time_for_update <= millis()) { | |
if (!runAlertTask && time_for_sleep > 0 && time_for_sleep <= millis()) { | |
toDeepSleep(true); | |
} | |
else { | |
time_for_update = millis() + TIME_UPDATE; | |
showDataTimeVoltage(); | |
if (rtc.isAlarm2() && !runAlertTask) { | |
Serial.println("Alarm 2 is active!"); | |
createAlertTask("150"); | |
} | |
} | |
} | |
} | |
void setWaitingState(void) { | |
// Led "Green" setLedColor(r, g, b) | |
setLedColor(0, MAX_7BIT_VALUE, 0); | |
time_for_sleep = millis() + TIME_BEFORE_SLEEP; | |
} | |
void showDataTimeVoltage(void) { | |
getCurrent(); | |
getVoltage(); | |
} | |
void getCurrent(void) { | |
rtc.getDateTime(); | |
// Date | |
if (DEBUG) { | |
Serial.print(rtc.getDayOfWeekNameRus(rtc.dt.dayOfWeek)); | |
Serial.print(' '); | |
Serial.print(rtc.dt.day, DEC); | |
Serial.print(' '); | |
Serial.print(rtc.dt.month, DEC); | |
Serial.print('.'); | |
Serial.print(rtc.dt.year, DEC); | |
Serial.print(" | "); | |
} | |
// Time | |
time_date[0] = rtc.dt.hour; | |
time_date[1] = rtc.dt.minute; | |
time_date[2] = rtc.dt.second; | |
if (DEBUG) { | |
Serial.print(time_date[0], DEC); | |
Serial.print(':'); | |
Serial.print(time_date[1], DEC); | |
Serial.print(':'); | |
Serial.print(time_date[2], DEC); | |
} | |
// Temperature: | |
temperatureDs = rtc.readTemperature(); | |
if (DEBUG) { | |
Serial.print(" | T = "); | |
Serial.print(temperatureDs); | |
Serial.println("C"); | |
} | |
// Notify time | |
if (notifyTime) { | |
pTxCharacteristic->setValue(getDateTimeStr(time_date, true) + getTemperature(temperatureDs - 3.05)); | |
pTxCharacteristic->notify(); | |
} | |
} | |
void getVoltage(void) { | |
uint16_t sensorsValues[2] = {0, 0}; | |
for (unsigned char i = 0; i < 10; i++) { | |
sensorsValues[0] += adc1_get_voltage(VOLTAGE_SENSOR); | |
sensorsValues[1] += adc1_get_voltage(THERMISTOR_CH); | |
delay(1); | |
} | |
/* Voltage sensor, 0.768 ~ 0.790 */ | |
float dv = 0.768; | |
voltage = dv * sensorsValues[0] / MAX12BIT_VALUE; | |
if (voltage < 4.1) { | |
voltage = voltage * (0.035 * (4.1 - voltage) + 1); | |
} | |
if (DEBUG) { | |
Serial.print("Voltage: "); | |
Serial.print(voltage); | |
Serial.print("V, "); | |
Serial.print(getCapacityPercent(voltage)); | |
Serial.println("%"); | |
} | |
/* Thermistor */ | |
therm_res = 11150 / ((MAX12BIT_VALUE * 10.0 / sensorsValues[1]) - 1); | |
if (DEBUG) { | |
Serial.print("Thermistor: "); | |
Serial.print(therm_res * 0.001); | |
Serial.print("k, "); | |
} | |
float therm_temp = getThermTemperature(therm_res); | |
// "Deep Sleep" mode | |
if (voltage > 2.4 && voltage < 3.1) { | |
toDeepSleep(false); | |
} | |
else { | |
if (notifyTime) { | |
pTxCharacteristic->setValue(getVoltagePrc(voltage) + getTemperature(therm_temp)); | |
pTxCharacteristic->notify(); | |
delay(10); | |
} | |
// Sound alert "low battery voltage" (if the battery capacity is less than 10%) | |
// or "high battery temperature" | |
if ((voltage > 2.4 && voltage < 3.2) || therm_temp > 45.5) { | |
pTxCharacteristic->setValue("Battery alert!"); | |
pTxCharacteristic->notify(); | |
dacSoundAlert(4, 1); | |
} | |
} | |
} | |
void toDeepSleep(bool awake) { | |
dacSoundAlert(2, 1); | |
if (DEBUG) Serial.println("Sleep start!"); | |
// ESP32 to wake up after TIME_TO_SLEEP seconds | |
if (awake) esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000); | |
esp_deep_sleep_start(); | |
} | |
/** | |
* Playing a repetitive sound alert. | |
* | |
* Params: | |
* Signal frequency index | |
* DFR (1-8) | |
* | |
* Number of repetitions | |
* REPCNT (1-255) | |
* | |
*/ | |
void dacSoundAlert(const unsigned char DFR, const unsigned char REPCNT) { | |
if (REPCNT == 0 || DFR == 0) return; | |
unsigned char i = 0; | |
const unsigned int DUR_FACT = 40 * DFR; | |
dac_output_enable(DAC_CHANNEL_1); | |
dac_output_enable(DAC_CHANNEL_2); | |
for (int rc = 0; rc < REPCNT; rc++) { | |
for (int cnt = 0; cnt < DUR_FACT; cnt++) { | |
for (i = 0; i < MAX_VOLUME_VALUE; i += DFR) { | |
dac_output_voltage(DAC_CHANNEL_1, i); | |
dac_output_voltage(DAC_CHANNEL_2, i); | |
} | |
for (i = MAX_VOLUME_VALUE; i > 0; i -= DFR) { | |
dac_output_voltage(DAC_CHANNEL_1, i); | |
dac_output_voltage(DAC_CHANNEL_2, i); | |
} | |
} | |
dac_output_voltage(DAC_CHANNEL_1, 0); | |
dac_output_voltage(DAC_CHANNEL_2, 0); | |
delay(DELAY_PAUSE); | |
} | |
dac_output_disable(DAC_CHANNEL_1); | |
dac_output_disable(DAC_CHANNEL_2); | |
} | |
void printAlarmState(void) { | |
Serial.print("Alarm_1 is "); | |
Serial.print(rtc.isArmed1() ? "triggered:" : "disarmed."); | |
printAlarmHm(rtc.getAlarm1()); | |
bool isArmed = rtc.isArmed2(); | |
alarm_time[0] = rtc.getAlarm2().hour; | |
alarm_time[1] = rtc.getAlarm2().minute; | |
if (!isWakeupBySleep()) { | |
rtc.setAlarm2(0, alarm_time[0], alarm_time[1], DS3231_MATCH_H_M, isArmed); | |
} | |
Serial.print("Alarm_2 is "); | |
Serial.print (rtc.isArmed2() ? "triggered:" : "disarmed."); | |
printAlarmHm(rtc.getAlarm2()); | |
} | |
void printAlarmHm(RTCAlarmTime a) { | |
Serial.print(' '); | |
Serial.print(a.hour, DEC); | |
Serial.print(':'); | |
Serial.print(a.minute, DEC); | |
Serial.print(':'); | |
Serial.println(a.second, DEC); | |
} | |
void setOscillator(void) { | |
// disable 32kHz | |
rtc.enable32kHz(false); | |
// Select output as rate to 1Hz | |
rtc.setOutput(DS3231_1HZ); | |
// Enable output | |
rtc.enableOutput(false); | |
// Check config | |
Serial.print("Oscilator 32 kHz: "); | |
Serial.println(rtc.is32kHz() ? "on" : "off"); | |
Serial.print("Oscilator 1 HZ: "); | |
Serial.println(rtc.isOutput() ? "enabled" : "disabled"); | |
} | |
/** | |
* Method to print the reason by which ESP32 has been awaken from sleep. | |
* Return "true" if wake up was caused by deep sleep. | |
*/ | |
bool isWakeupBySleep(void) { | |
const esp_sleep_wakeup_cause_t wakeupReason = esp_sleep_get_wakeup_cause(); | |
Serial.println(); | |
switch(wakeupReason) { | |
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break; | |
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break; | |
case 3 : Serial.println("Wakeup caused by timer"); break; | |
case 4 : Serial.println("Wakeup caused by touchpad"); break; | |
case 5 : Serial.println("Wakeup caused by ULP program"); break; | |
default : Serial.println("Wakeup was not caused by deep sleep"); break; | |
} | |
return wakeupReason > 0 && wakeupReason < 5; | |
} | |
/** | |
* Create alert task (run on the core 0). | |
* delay_ms > "100" | |
*/ | |
void createAlertTask(char* delay_ms) { | |
runAlertTask = true; | |
xTaskCreatePinnedToCore( | |
alertTask, /* Function to implement the task */ | |
"Alert_Task", /* Name of the task */ | |
8192, /* Stack size in words */ | |
(void*)delay_ms, /* Task input parameter */ | |
1, /* Priority of the task */ | |
&xTaskAlert, /* Task handle */ | |
0 /* Core where the task should run */ | |
); | |
configASSERT(xTaskAlert); | |
} | |
/** Alert task. */ | |
void alertTask(void* parameters) { | |
Serial.print("--- Alert Task, Core ID = "); | |
// Get the core that the task was pinned to | |
Serial.print(xTaskGetAffinity(xTaskAlert)); | |
Serial.println(" ---"); | |
bool dbl = false; | |
uint16_t delayms = String((char*)parameters).toInt() - DELAY_PAUSE; | |
while(runAlertTask) { | |
// Red | |
setLedColor(MAX_7BIT_VALUE, 0, 0); | |
dacSoundAlert(3, dbl ? 2 : 1); | |
if (!dbl) delay(delayms); | |
// Blue | |
setLedColor(0, 0, MAX_7BIT_VALUE); | |
dacSoundAlert(6, 1); | |
delay(delayms); | |
dbl = !dbl; | |
} | |
deviceConnected ? writeCurrentColor() : setLedColor(0, MAX_7BIT_VALUE, 0); | |
vTaskDelete(NULL); | |
} | |
void setLedColor(const uint8_t RED, const uint8_t GREEN, const uint8_t BLUE) { | |
ledcWrite(LEDC_CHANNEL_R, RED); | |
ledcWrite(LEDC_CHANNEL_G, GREEN); | |
ledcWrite(LEDC_CHANNEL_B, BLUE); | |
} | |
std::string getTxJoystickValue(const uint8_t VALUES[NUMB_JOY_CHLS]) { | |
char str[6] = ""; | |
char* intStr = ""; | |
std::string txValue = "J ["; | |
for (uint8_t i = 0; i < NUMB_JOY_CHLS; i++) { | |
intStr = itoa(VALUES[i], str, 10); | |
txValue += std::string(intStr); | |
if (i < NUMB_JOY_CHLS - 1) txValue += ','; | |
} | |
return txValue + "]"; | |
} | |
std::string getTxColorValue(const uint8_t VALUES[NUMBER_CHANNELS]) { | |
char str[6] = ""; | |
char* intStr = ""; | |
std::string txValue = "Color ("; | |
for (uint8_t i = 0; i < NUMBER_CHANNELS; i++) { | |
intStr = itoa(VALUES[i], str, 10); | |
txValue += std::string(intStr); | |
if (i < NUMBER_CHANNELS - 1) txValue += ", "; | |
} | |
return txValue + ")"; | |
} | |
std::string getTxTimeDateValue(const uint8_t VALUES[3], const bool TIME) { | |
std::string txValue = ""; | |
if (TIME) { | |
txValue = "Set time: "; | |
} | |
else { | |
txValue = "Set date: "; | |
} | |
return txValue + getDateTimeStr(VALUES, TIME); | |
} | |
std::string getDateTimeStr(const uint8_t VALUES[3], const bool TIME) { | |
char str[6] = ""; | |
char* intStr = ""; | |
std::string retStr = ""; | |
for (uint8_t i = 0; i < 3; i++) { | |
// year | |
if (!TIME && i == 2) { | |
intStr = itoa(2000 + VALUES[i], str, 10); | |
} | |
else { | |
if (VALUES[i] < 10) retStr += '0'; | |
intStr = itoa(VALUES[i], str, 10); | |
} | |
retStr += std::string(intStr); | |
if (i < 2) { | |
if (TIME) retStr += ":"; | |
else retStr += "."; | |
} | |
} | |
return retStr; | |
} | |
std::string getTxAlarmValue(const uint8_t VALUES[2]) { | |
char str[6] = ""; | |
char* intStr = itoa(VALUES[0], str, 10); | |
std::string txValue = "Alarm: "; | |
if (VALUES[0] < 10) txValue += '0'; | |
txValue += std::string(intStr); | |
txValue += ':'; | |
intStr = itoa(VALUES[1], str, 10); | |
if (VALUES[1] < 10) txValue += '0'; | |
txValue += std::string(intStr); | |
return txValue + (rtc.isArmed2() ? " On" : " Off"); | |
} | |
std::string getTemperature(float temp) { | |
std::string tempValue = ' ' + std::string(roundFloatValue(temp, 0, true)) + 'C'; | |
return tempValue; | |
} | |
std::string getVoltagePrc(float volt) { | |
char str[6] = ""; | |
char* intStr = itoa(getCapacityPercent(volt), str, 10); | |
std::string voltStr = std::string(roundFloatValue(volt, 2, false)) + "V, "; | |
return voltStr + std::string(intStr) + '%'; | |
} | |
unsigned char getCapacityPercent(float volt) { | |
// Capacity: 3.1 - 4.05 V as 100 % | |
int prcCharge = floor((volt - 3.1) / 0.0095); | |
if (prcCharge > 100) prcCharge = 100; | |
else if (prcCharge < 0) prcCharge = 0; | |
return prcCharge; | |
} | |
/** | |
* Rounding of the float value. | |
*/ | |
char* roundFloatValue(float value, unsigned char precision, bool addPlus) { | |
// 3 is mininum width | |
// precision 1: 00.0 | |
// precision 0: 000 | |
// float value is copied onto buff | |
dtostrf(value, 3, precision, roundFloatBuff); | |
if (value > -0.5 && value < 0.0) { | |
roundFloatBuff[1] = ' '; | |
} | |
else if (addPlus && value > 0.5) { | |
if (value < 9.5) roundFloatBuff[1] = '+'; | |
else if (value < 99.5) roundFloatBuff[0] = '+'; | |
} | |
return roundFloatBuff; | |
} | |
/** | |
* Returns the temperature | |
* based on the current resistance of the thermistor. | |
*/ | |
float getThermTemperature(float resistance) { | |
float steinhart = resistance / 15000; | |
// ln(R/R2) | |
steinhart = log(steinhart); | |
// ln(R/R2) / B | |
steinhart /= 3750; | |
steinhart += 1 / (25 + 273.15); | |
steinhart = 1 / steinhart; | |
steinhart -= 273.15; | |
if (DEBUG) { | |
Serial.print("temperature: "); | |
Serial.print(steinhart); | |
Serial.println("℃"); | |
Serial.println(); | |
} | |
return steinhart; | |
} | |
// 06.2018, 09-10.2018, 01.2019 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@cyrus007 where is <DS3231Esp32.h>?