Unverified Commit 1ca741eb authored by Me No Dev's avatar Me No Dev Committed by GitHub

Merge branch 'master' into release/v2.x

parents 8a458d10 12bd5253
This diff is collapsed.
......@@ -137,9 +137,19 @@ typedef unsigned int word;
void setup(void);
void loop(void);
// The default is using Real Hardware random number generator
// But when randomSeed() is called, it turns to Psedo random
// generator, exactly as done in Arduino mainstream
long random(long);
long random(long, long);
#endif
// Calling randomSeed() will make random()
// using pseudo random like in Arduino
void randomSeed(unsigned long);
// Allow the Application to decide if the random generator
// will use Real Hardware random generation (true - default)
// or Pseudo random generation (false) as in Arduino MainStream
void useRealRandomGenerator(bool useRandomHW);
#endif
long map(long, long, long, long, long);
#ifdef __cplusplus
......@@ -207,8 +217,6 @@ void setToneChannel(uint8_t channel = 0);
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);
void noTone(uint8_t _pin);
// WMath prototypes
long random(long);
#endif /* __cplusplus */
#include "pins_arduino.h"
......
......@@ -29,10 +29,22 @@ extern "C" {
}
#include "esp32-hal-log.h"
// Allows the user to choose between Real Hardware
// or Software Pseudo random generators for the
// Arduino random() functions
static bool s_useRandomHW = true;
void useRealRandomGenerator(bool useRandomHW) {
s_useRandomHW = useRandomHW;
}
// Calling randomSeed() will force the
// Pseudo Random generator like in
// Arduino mainstream API
void randomSeed(unsigned long seed)
{
if(seed != 0) {
srand(seed);
s_useRandomHW = false;
}
}
......@@ -46,7 +58,9 @@ long random( long howbig )
if (howbig < 0) {
return (random(0, -howbig));
}
return esp_random() % howbig;
// if randomSeed was called, fall back to software PRNG
uint32_t val = (s_useRandomHW) ? esp_random() : rand();
return val % howbig;
}
long random(long howsmall, long howbig)
......
......@@ -21,7 +21,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include "Arduino.h"
#include "WString.h"
#include "stdlib_noniso.h"
#include "esp32-hal-log.h"
......@@ -80,11 +80,7 @@ String::String(unsigned char value, unsigned char base) {
String::String(int value, unsigned char base) {
init();
char buf[2 + 8 * sizeof(int)];
if (base == 10) {
sprintf(buf, "%d", value);
} else {
itoa(value, buf, base);
}
*this = buf;
}
......@@ -98,11 +94,7 @@ String::String(unsigned int value, unsigned char base) {
String::String(long value, unsigned char base) {
init();
char buf[2 + 8 * sizeof(long)];
if (base==10) {
sprintf(buf, "%ld", value);
} else {
ltoa(value, buf, base);
}
*this = buf;
}
......@@ -140,11 +132,7 @@ String::String(double value, unsigned int decimalPlaces) {
String::String(long long value, unsigned char base) {
init();
char buf[2 + 8 * sizeof(long long)];
if (base==10) {
sprintf(buf, "%lld", value); // NOT SURE - NewLib Nano ... does it support %lld?
} else {
lltoa(value, buf, base);
}
*this = buf;
}
......@@ -159,9 +147,9 @@ String::~String() {
invalidate();
}
// /*********************************************/
// /* Memory Management */
// /*********************************************/
/*********************************************/
/* Memory Management */
/*********************************************/
inline void String::init(void) {
setSSO(false);
......@@ -221,8 +209,7 @@ bool String::changeBuffer(unsigned int maxStrLen) {
// Copy the SSO buffer into allocated space
memmove(newbuffer, sso.buff, sizeof(sso.buff));
}
if (newSize > oldSize)
{
if (newSize > oldSize) {
memset(newbuffer + oldSize, 0, newSize - oldSize);
}
setSSO(false);
......@@ -234,9 +221,9 @@ bool String::changeBuffer(unsigned int maxStrLen) {
return false;
}
// /*********************************************/
// /* Copy and Move */
// /*********************************************/
/*********************************************/
/* Copy and Move */
/*********************************************/
String & String::copy(const char *cstr, unsigned int length) {
if(!reserve(length)) {
......@@ -292,12 +279,10 @@ void String::move(String &rhs) {
String & String::operator =(const String &rhs) {
if(this == &rhs)
return *this;
if(rhs.buffer())
copy(rhs.buffer(), rhs.len());
else
invalidate();
return *this;
}
......@@ -320,7 +305,6 @@ String & String::operator =(const char *cstr) {
copy(cstr, strlen(cstr));
else
invalidate();
return *this;
}
......@@ -333,9 +317,9 @@ String & String::operator =(const __FlashStringHelper *pstr) {
return *this;
}
// /*********************************************/
// /* concat */
// /*********************************************/
/*********************************************/
/* concat */
/*********************************************/
bool String::concat(const String &s) {
// Special case if we're concatting ourself (s += s;) since we may end up
......@@ -388,12 +372,14 @@ bool String::concat(char c) {
bool String::concat(unsigned char num) {
char buf[1 + 3 * sizeof(unsigned char)];
return concat(buf, sprintf(buf, "%d", num));
utoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(int num) {
char buf[2 + 3 * sizeof(int)];
return concat(buf, sprintf(buf, "%d", num));
itoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(unsigned int num) {
......@@ -404,7 +390,8 @@ bool String::concat(unsigned int num) {
bool String::concat(long num) {
char buf[2 + 3 * sizeof(long)];
return concat(buf, sprintf(buf, "%ld", num));
ltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(unsigned long num) {
......@@ -413,6 +400,18 @@ bool String::concat(unsigned long num) {
return concat(buf, strlen(buf));
}
bool String::concat(long long num) {
char buf[2 + 3 * sizeof(long long)];
lltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(unsigned long long num) {
char buf[1 + 3 * sizeof(unsigned long long)];
ulltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(float num) {
char buf[20];
char* string = dtostrf(num, 4, 2, buf);
......@@ -425,17 +424,6 @@ bool String::concat(double num) {
return concat(string, strlen(string));
}
bool String::concat(long long num) {
char buf[2 + 3 * sizeof(long long)];
return concat(buf, sprintf(buf, "%lld", num)); // NOT SURE - NewLib Nano ... does it support %lld?
}
bool String::concat(unsigned long long num) {
char buf[1 + 3 * sizeof(unsigned long long)];
ulltoa(num, buf, 10);
return concat(buf, strlen(buf));
}
bool String::concat(const __FlashStringHelper * str) {
if (!str)
return false;
......@@ -546,9 +534,9 @@ StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHel
return a;
}
// /*********************************************/
// /* Comparison */
// /*********************************************/
/*********************************************/
/* Comparison */
/*********************************************/
int String::compareTo(const String &s) const {
if(!buffer() || !s.buffer()) {
......@@ -650,9 +638,9 @@ bool String::endsWith(const String &s2) const {
return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0;
}
// /*********************************************/
// /* Character Access */
// /*********************************************/
/*********************************************/
/* Character Access */
/*********************************************/
char String::charAt(unsigned int loc) const {
return operator[](loc);
......@@ -692,9 +680,9 @@ void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int ind
buf[n] = 0;
}
// /*********************************************/
// /* Search */
// /*********************************************/
/*********************************************/
/* Search */
/*********************************************/
int String::indexOf(char c) const {
return indexOf(c, 0);
......@@ -703,7 +691,7 @@ int String::indexOf(char c) const {
int String::indexOf(char ch, unsigned int fromIndex) const {
if(fromIndex >= len())
return -1;
const char* temp = strchr(buffer() + fromIndex, ch);
const char *temp = strchr(buffer() + fromIndex, ch);
if(temp == NULL)
return -1;
return temp - buffer();
......@@ -773,9 +761,9 @@ String String::substring(unsigned int left, unsigned int right) const {
return out;
}
// /*********************************************/
// /* Modification */
// /*********************************************/
/*********************************************/
/* Modification */
/*********************************************/
void String::replace(char find, char replace) {
if(!buffer())
......@@ -786,7 +774,7 @@ void String::replace(char find, char replace) {
}
}
void String::replace(const String& find, const String& replace) {
void String::replace(const String &find, const String &replace) {
if(len() == 0 || find.len() == 0)
return;
int diff = replace.len() - find.len();
......@@ -892,9 +880,9 @@ void String::trim(void) {
wbuffer()[newlen] = 0;
}
// /*********************************************/
// /* Parsing / Conversion */
// /*********************************************/
/*********************************************/
/* Parsing / Conversion */
/*********************************************/
long String::toInt(void) const {
if (buffer())
......@@ -908,8 +896,7 @@ float String::toFloat(void) const {
return 0;
}
double String::toDouble(void) const
{
double String::toDouble(void) const {
if (buffer())
return atof(buffer());
return 0.0;
......
......@@ -23,15 +23,13 @@
#define String_class_h
#ifdef __cplusplus
#include <pgmspace.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <pgmspace.h>
#include <stdint.h>
// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper;
// an abstract class used as a means to proide a unique pointer type
// but really has no body
......@@ -39,6 +37,10 @@ class __FlashStringHelper;
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper;
// The string class
class String {
// use a function pointer to allow for "if (s)" without the
......@@ -107,7 +109,7 @@ class String {
String & operator =(StringSumHelper &&rval);
#endif
// concatenate (works w/ built-in types)
// concatenate (works w/ built-in types, same as assignment)
// returns true on success, false on failure (in which case, the string
// is left unchanged). if the argument is null or invalid, the
......@@ -265,7 +267,6 @@ class String {
String substring(unsigned int beginIndex) const {
return substring(beginIndex, len());
}
;
String substring(unsigned int beginIndex, unsigned int endIndex) const;
// modification
......
......@@ -221,11 +221,15 @@ bool IRAM_ATTR timerFnWrapper(void *arg){
return false;
}
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
void timerAttachInterruptFlag(hw_timer_t *timer, void (*fn)(void), bool edge, int intr_alloc_flags){
if(edge){
log_w("EDGE timer interrupt is not supported! Setting to LEVEL...");
}
timer_isr_callback_add(timer->group, timer->num, timerFnWrapper, fn, 0);
timer_isr_callback_add(timer->group, timer->num, timerFnWrapper, fn, intr_alloc_flags);
}
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
timerAttachInterruptFlag(timer, fn, edge, 0);
}
void timerDetachInterrupt(hw_timer_t *timer){
......
......@@ -36,6 +36,7 @@ void timerEnd(hw_timer_t *timer);
void timerSetConfig(hw_timer_t *timer, uint32_t config);
uint32_t timerGetConfig(hw_timer_t *timer);
void timerAttachInterruptFlag(hw_timer_t *timer, void (*fn)(void), bool edge, int intr_alloc_flags);
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge);
void timerDetachInterrupt(hw_timer_t *timer);
......
......@@ -40,6 +40,7 @@
#include "esp32s2/rom/usb/chip_usb_dw_wrapper.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "hal/usb_serial_jtag_ll.h"
#include "hal/usb_phy_ll.h"
#include "esp32s3/rom/usb/usb_persist.h"
#include "esp32s3/rom/usb/usb_dc.h"
#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
......@@ -415,6 +416,7 @@ static void usb_switch_to_cdc_jtag(){
digitalWrite(USBPHY_DP_NUM, LOW);
// Initialize CDC+JTAG ISR to listen for BUS_RESET
usb_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG);
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
......
......@@ -615,7 +615,8 @@ void log_print_buf(const uint8_t *b, size_t len){
*/
unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
{
#ifndef CONFIG_IDF_TARGET_ESP32S3
// Baud rate detection only works for ESP32 and ESP32S2
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
if(uart == NULL) {
return 0;
}
......@@ -679,6 +680,8 @@ void uartStartDetectBaudrate(uart_t *uart) {
//hw->conf0.autobaud_en = 0;
//hw->conf0.autobaud_en = 1;
#elif CONFIG_IDF_TARGET_ESP32S3
log_e("ESP32-S3 baud rate detection is not supported.");
return;
#else
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
hw->auto_baud.glitch_filt = 0x08;
......@@ -694,7 +697,8 @@ uartDetectBaudrate(uart_t *uart)
return 0;
}
#ifndef CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 requires further testing - Baud rate detection returns wrong values
// Baud rate detection only works for ESP32 and ESP32S2
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
static bool uartStateDetectingBaudrate = false;
......@@ -707,22 +711,14 @@ uartDetectBaudrate(uart_t *uart)
if (!divisor) {
return 0;
}
// log_i(...) below has been used to check C3 baud rate detection results
//log_i("Divisor = %d\n", divisor);
//log_i("BAUD RATE based on Positive Pulse %d\n", getApbFrequency()/((hw->pospulse.min_cnt + 1)/2));
//log_i("BAUD RATE based on Negative Pulse %d\n", getApbFrequency()/((hw->negpulse.min_cnt + 1)/2));
#ifdef CONFIG_IDF_TARGET_ESP32C3
//hw->conf0.autobaud_en = 0;
#elif CONFIG_IDF_TARGET_ESP32S3
#else
uart_dev_t *hw = UART_LL_GET_HW(uart->num);
hw->auto_baud.en = 0;
#endif
uartStateDetectingBaudrate = false; // Initialize for the next round
unsigned long baudrate = getApbFrequency() / divisor;
//log_i("APB_FREQ = %d\nraw baudrate detected = %d", getApbFrequency(), baudrate);
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
......@@ -741,7 +737,11 @@ uartDetectBaudrate(uart_t *uart)
return default_rates[i];
#else
#ifdef CONFIG_IDF_TARGET_ESP32C3
log_e("ESP32-C3 baud rate detection is not supported.");
#else
log_e("ESP32-S3 baud rate detection is not supported.");
#endif
return 0;
#endif
}
......
......@@ -207,6 +207,7 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t
case ESP_GATTS_DISCONNECT_EVT: {
if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now.
m_pServerCallbacks->onDisconnect(this);
m_pServerCallbacks->onDisconnect(this, param);
}
if(m_connId == ESP_GATT_IF_NONE) {
return;
......
// This example demonstrates usage of BluetoothSerial method to retrieve MAC address of local BT device in various formats.
// By Tomas Pilny - 2023
#include "BluetoothSerial.h"
String device_name = "ESP32-example";
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
#if !defined(CONFIG_BT_SPP_ENABLED)
#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
#endif
BluetoothSerial SerialBT;
void setup() {
Serial.begin(115200);
SerialBT.begin(device_name); //Bluetooth device name
uint8_t mac_arr[6]; // Byte array to hold the MAC address from getBtAddress()
BTAddress mac_obj; // Object holding instance of BTAddress with the MAC (for more details see libraries/BluetoothSerial/src/BTAddress.h)
String mac_str; // String holding the text version of MAC in format AA:BB:CC:DD:EE:FF
SerialBT.getBtAddress(mac_arr); // Fill in the array
mac_obj = SerialBT.getBtAddressObject(); // Instantiate the object
mac_str = SerialBT.getBtAddressString(); // Copy the string
Serial.print("This device is instantiated with name "); Serial.println(device_name);
Serial.print("The mac address using byte array: ");
for(int i = 0; i < ESP_BD_ADDR_LEN-1; i++){
Serial.print(mac_arr[i], HEX); Serial.print(":");
}
Serial.println(mac_arr[ESP_BD_ADDR_LEN-1], HEX);
Serial.print("The mac address using BTAddress object using default method `toString()`: "); Serial.println(mac_obj.toString().c_str());
Serial.print("The mac address using BTAddress object using method `toString(true)`\n\twhich prints the MAC with capital letters: "); Serial.println(mac_obj.toString(true).c_str()); // This actually what is used inside the getBtAddressString()
Serial.print("The mac address using string: "); Serial.println(mac_str.c_str());
}
void loop(){
}
......@@ -44,7 +44,7 @@ BTAddress::BTAddress() {
*
* @param [in] stringAddress The hex representation of the address.
*/
BTAddress::BTAddress(std::string stringAddress) {
BTAddress::BTAddress(String stringAddress) {
if (stringAddress.length() != 17) return;
int data[6];
......@@ -86,20 +86,26 @@ esp_bd_addr_t *BTAddress::getNative() const {
/**
* @brief Convert a BT address to a string.
*
* A string representation of an address is in the format:
*
* @param [in] capital changes the letter size
* By default the parameter `capital` == false and the string representation of an address is in the format:
* ```
* xx:xx:xx:xx:xx:xx
* ```
*
* When the parameter `capital` == true the format uses capital letters:
* ```
* XX:XX:XX:XX:XX:XX
* ```
* @return The string representation of the address.
*/
std::string BTAddress::toString() const {
String BTAddress::toString(bool capital) const {
auto size = 18;
char *res = (char*)malloc(size);
if(capital){
snprintf(res, size, "%02X:%02X:%02X:%02X:%02X:%02X", m_address[0], m_address[1], m_address[2], m_address[3], m_address[4], m_address[5]);
}else{
snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[0], m_address[1], m_address[2], m_address[3], m_address[4], m_address[5]);
std::string ret(res);
}
String ret(res);
free(res);
return ret;
} // toString
......
......@@ -12,7 +12,7 @@
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED)
#include <esp_gap_bt_api.h> // ESP32 BT
#include <string>
#include <Arduino.h>
/**
......@@ -24,12 +24,12 @@ class BTAddress {
public:
BTAddress();
BTAddress(esp_bd_addr_t address);
BTAddress(std::string stringAddress);
BTAddress(String stringAddress);
bool equals(BTAddress otherAddress);
operator bool () const;
esp_bd_addr_t* getNative() const;
std::string toString() const;
String toString(bool capital = false) const;
private:
esp_bd_addr_t m_address;
......
......@@ -39,7 +39,7 @@ bool BTAdvertisedDeviceSet::haveRSSI() const { return m_haveRSSI; }
* @return A string representation of this device.
*/
std::string BTAdvertisedDeviceSet::toString() {
std::string res = "Name: " + getName() + ", Address: " + getAddress().toString();
std::string res = "Name: " + getName() + ", Address: " + std::string(getAddress().toString().c_str(), getAddress().toString().length());
if (haveCOD()) {
char val[6];
snprintf(val, sizeof(val), "%d", getCOD());
......
......@@ -84,7 +84,7 @@ void BTScanResultsSet::clear() {
}
bool BTScanResultsSet::add(BTAdvertisedDeviceSet advertisedDevice, bool unique) {
std::string key = advertisedDevice.getAddress().toString();
std::string key = std::string(advertisedDevice.getAddress().toString().c_str(), advertisedDevice.getAddress().toString().length());
if (!unique || m_vectorAdvertisedDevices.count(key) == 0) {
m_vectorAdvertisedDevices.insert(std::pair<std::string, BTAdvertisedDeviceSet>(key, advertisedDevice));
return true;
......
......@@ -661,8 +661,6 @@ static bool _init_bt(const char *deviceName)
}
}
// Why only master need this? Slave need this during pairing as well
// if (_isMaster && esp_bt_gap_register_callback(esp_bt_gap_cb) != ESP_OK) {
if (esp_bt_gap_register_callback(esp_bt_gap_cb) != ESP_OK) {
log_e("gap register failed");
return false;
......@@ -1183,4 +1181,31 @@ std::map<int, std::string> BluetoothSerial::getChannels(const BTAddress &remoteA
return sdpRecords;
}
/**
* @brief Gets the MAC address of local BT device in byte array.
*
* @param mac [out] The mac
*/
void BluetoothSerial::getBtAddress(uint8_t *mac) {
const uint8_t *dev_mac = esp_bt_dev_get_address();
memcpy(mac, dev_mac, ESP_BD_ADDR_LEN);
}
/**
* @brief Gets the MAC address of local BT device as BTAddress object.
*
* @return The BTAddress object.
*/
BTAddress BluetoothSerial::getBtAddressObject() {
uint8_t mac_arr[ESP_BD_ADDR_LEN];
getBtAddress(mac_arr);
return BTAddress(mac_arr);
}
/**
* @brief Gets the MAC address of local BT device as string.
*
* @return The BT MAC address string.
*/
String BluetoothSerial::getBtAddressString() {
return getBtAddressObject().toString(true);
}
#endif
......@@ -85,6 +85,9 @@ class BluetoothSerial: public Stream
const int MAX_INQ_TIME = (ESP_BT_GAP_MAX_INQ_LEN * INQ_TIME);
operator bool() const;
void getBtAddress(uint8_t *mac);
BTAddress getBtAddressObject();
String getBtAddressString();
private:
String local_name;
int timeoutTicks=0;
......
/* Basic Multi Threading Arduino Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
// Please read file README.md in the folder containing this example.
#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
#define ANALOG_INPUT_PIN A0
#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#define LED_BUILTIN 13 // Specify the on which is your LED
#endif
// define two tasks for Blink & AnalogRead
// Define two tasks for Blink & AnalogRead.
void TaskBlink( void *pvParameters );
void TaskAnalogReadA3( void *pvParameters );
void TaskAnalogRead( void *pvParameters );
TaskHandle_t analog_read_task_handle; // You can (don't have to) use this to be able to manipulate a task from somewhere else.
// the setup function runs once when you press reset or power the board
// The setup function runs once when you press reset or power on the board.
void setup() {
// initialize serial communication at 115200 bits per second:
// Initialize serial communication at 115200 bits per second:
Serial.begin(115200);
// Now set up two tasks to run independently.
xTaskCreatePinnedToCore(
// Set up two tasks to run independently.
uint32_t blink_delay = 1000; // Delay between changing state on LED pin
xTaskCreate(
TaskBlink
, "TaskBlink" // A name just for humans
, 1024 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL
, ARDUINO_RUNNING_CORE);
, "Task Blink" // A name just for humans
, 2048 // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
, (void*) &blink_delay // Task parameter which can modify the task behavior. This must be passed as pointer to void.
, 2 // Priority
, NULL // Task handle is not used here - simply pass NULL
);
// This variant of task creation can also specify on which core it will be run (only relevant for multi-core ESPs)
xTaskCreatePinnedToCore(
TaskAnalogReadA3
, "AnalogReadA3"
, 1024 // Stack size
, NULL
TaskAnalogRead
, "Analog Read"
, 2048 // Stack size
, NULL // When no parameter is used, simply pass NULL
, 1 // Priority
, NULL
, ARDUINO_RUNNING_CORE);
, &analog_read_task_handle // With task handle we will be able to manipulate with this task.
, ARDUINO_RUNNING_CORE // Core on which the task will run
);
Serial.printf("Basic Multi Threading Arduino Example\n");
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
}
void loop()
{
// Empty. Things are done in Tasks.
void loop(){
if(analog_read_task_handle != NULL){ // Make sure that the task actually exists
delay(10000);
vTaskDelete(analog_read_task_handle); // Delete task
analog_read_task_handle = NULL; // prevent calling vTaskDelete on non-existing task
}
}
/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/
void TaskBlink(void *pvParameters) // This is a task.
{
(void) pvParameters;
void TaskBlink(void *pvParameters){ // This is a task.
uint32_t blink_delay = *((uint32_t*)pvParameters);
/*
Blink
......@@ -64,20 +79,24 @@ void TaskBlink(void *pvParameters) // This is a task.
// initialize digital LED_BUILTIN on pin 13 as an output.
pinMode(LED_BUILTIN, OUTPUT);
for (;;) // A Task shall never return or exit.
{
for (;;){ // A Task shall never return or exit.
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
// arduino-esp32 has FreeRTOS configured to have a tick-rate of 1000Hz and portTICK_PERIOD_MS
// refers to how many milliseconds the period between each ticks is, ie. 1ms.
vTaskDelay(1000 / portTICK_PERIOD_MS ); // vTaskDelay wants ticks, not milliseconds
delay(blink_delay);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
vTaskDelay(1000 / portTICK_PERIOD_MS); // 1 second delay
delay(blink_delay);
}
}
void TaskAnalogReadA3(void *pvParameters) // This is a task.
{
void TaskAnalogRead(void *pvParameters){ // This is a task.
(void) pvParameters;
// Check if the given analog pin is usable - if not - delete this task
if(!adcAttachPin(ANALOG_INPUT_PIN)){
Serial.printf("TaskAnalogRead cannot work because the given pin %d cannot be used for ADC - the task will delete itself.\n", ANALOG_INPUT_PIN);
analog_read_task_handle = NULL; // Prevent calling vTaskDelete on non-existing task
vTaskDelete(NULL); // Delete this task
}
/*
AnalogReadSerial
......@@ -88,12 +107,11 @@ void TaskAnalogReadA3(void *pvParameters) // This is a task.
This example code is in the public domain.
*/
for (;;)
{
// read the input on analog pin A3:
int sensorValueA3 = analogRead(A3);
for (;;){
// read the input on analog pin:
int sensorValue = analogRead(ANALOG_INPUT_PIN);
// print out the value you read:
Serial.println(sensorValueA3);
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms delay
Serial.println(sensorValue);
delay(100); // 100ms delay
}
}
# Basic Multi Threading Example
This example demonstrates the basic usage of FreeRTOS Tasks for multi threading.
Please refer to other examples in this folder to better utilize their full potential and safeguard potential problems.
It is also advised to read the documentation on FreeRTOS web pages:
[https://www.freertos.org/a00106.html](https://www.freertos.org/a00106.html)
This example will blink the built-in LED and read analog data.
Additionally, this example demonstrates the usage of the task handle, simply by deleting the analog
read task after 10 seconds from the main loop by calling the function `vTaskDelete`.
### Theory:
A task is simply a function that runs when the operating system (FreeeRTOS) sees fit.
This task can have an infinite loop inside if you want to do some work periodically for the entirety of the program run.
This, however, can create a problem - no other task will ever run and also the Watch Dog will trigger and your program will restart.
A nice behaving tasks know when it is useless to keep the processor for itself and give it away for other tasks to be used.
This can be achieved in many ways, but the simplest is called `delay(`milliseconds)`.
During that delay, any other task may run and do its job.
When the delay runs out the Operating System gives the processor the task which can continue.
For other ways to yield the CPU in a task please see other examples in this folder.
It is also worth mentioning that two or more tasks running the same function will run them with separate stacks, so if you want to run the same code (which could be differentiated by the argument) there is no need to have multiple copies of the same function.
**Task creation has a few parameters you should understand:**
```
xTaskCreate(TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
```
- **pxTaskCode** is the name of your function which will run as a task
- **pcName** is a string of human-readable descriptions for your task
- **usStackDepth** is the number of words (word = 4B) available to the task. If you see an error similar to this "Debug exception reason: Stack canary watchpoint triggered (Task Blink)" you should increase it
- **pvParameters** is a parameter that will be passed to the task function - it must be explicitly converted to (void*) and in your function explicitly converted back to the intended data type.
- **uxPriority** is a number from 0 to configMAX_PRIORITIES which determines how the FreeRTOS will allow the tasks to run. 0 is the lowest priority.
- **pxCreatedTask** task handle is a pointer to the task which allows you to manipulate the task - delete it, suspend and resume.
If you don't need to do anything special with your task, simply pass NULL for this parameter.
You can read more about task control here: https://www.freertos.org/a00112.html
# Supported Targets
This example supports all SoCs.
### Hardware Connection
If your board does not have a built-in LED, please connect one to the pin specified by the `LED_BUILTIN` in the code (you can also change the number and connect it to the pin you desire).
Optionally you can connect the analog element to the pin. such as a variable resistor, analog input such as an audio signal, or any signal generator. However, if the pin is left unconnected it will receive background noise and you will also see a change in the signal when the pin is touched by a finger.
Please refer to the ESP-IDF ADC documentation for specific SoC for info on which pins are available:
[ESP32](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32/api-reference/peripherals/adc.html),
[ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32s2/api-reference/peripherals/adc.html),
[ESP32-S3](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32s3/api-reference/peripherals/adc.html),
[ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32c3/api-reference/peripherals/adc.html)
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
#### Using Platform IO
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
## Troubleshooting
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
/* Basic Multi Threading Arduino Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
// Please read file README.md in the folder containing this example.
#define USE_MUTEX
int shared_variable = 0;
SemaphoreHandle_t shared_var_mutex = NULL;
// Define a task function
void Task( void *pvParameters );
// The setup function runs once when you press reset or power on the board.
void setup() {
// Initialize serial communication at 115200 bits per second:
Serial.begin(115200);
while(!Serial) delay(100);
Serial.printf(" Task 0 | Task 1\n");
#ifdef USE_MUTEX
shared_var_mutex = xSemaphoreCreateMutex(); // Create the mutex
#endif
// Set up two tasks to run the same function independently.
static int task_number0 = 0;
xTaskCreate(
Task
, "Task 0" // A name just for humans
, 2048 // The stack size
, (void*)&task_number0 // Pass reference to a variable describing the task number
//, 5 // High priority
, 1 // priority
, NULL // Task handle is not used here - simply pass NULL
);
static int task_number1 = 1;
xTaskCreate(
Task
, "Task 1"
, 2048 // Stack size
, (void*)&task_number1 // Pass reference to a variable describing the task number
, 1 // Low priority
, NULL // Task handle is not used here - simply pass NULL
);
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
}
void loop(){
}
/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/
void Task(void *pvParameters){ // This is a task.
int task_num = *((int*)pvParameters);
Serial.printf("%s\n", task_num ? " Starting |" : " | Starting");
for (;;){ // A Task shall never return or exit.
#ifdef USE_MUTEX
if(shared_var_mutex != NULL){ // Sanity check if the mutex exists
// Try to take the mutex and wait indefintly if needed
if(xSemaphoreTake(shared_var_mutex, portMAX_DELAY) == pdTRUE){
// Mutex successfully taken
#endif
int new_value = random(1000);
char str0[32]; sprintf(str0, " %d <- %d |", shared_variable, new_value);
char str1[32]; sprintf(str1, " | %d <- %d", shared_variable, new_value);
Serial.printf("%s\n", task_num ? str0 : str1);
shared_variable = new_value;
delay(random(100)); // wait random time of max 100 ms - simulating some computation
sprintf(str0, " R: %d |", shared_variable);
sprintf(str1, " | R: %d", shared_variable);
Serial.printf("%s\n", task_num ? str0 : str1);
//Serial.printf("Task %d after write: reading %d\n", task_num, shared_variable);
if(shared_variable != new_value){
Serial.printf("%s\n", task_num ? " Mismatch! |" : " | Mismatch!");
//Serial.printf("Task %d: detected race condition - the value changed!\n", task_num);
}
#ifdef USE_MUTEX
xSemaphoreGive(shared_var_mutex); // After accessing the shared resource give the mutex and allow other processes to access it
}else{
// We could not obtain the semaphore and can therefore not access the shared resource safely.
} // mutex take
} // sanity check
#endif
delay(10); // Allow other task to be scheduled
} // Infinite loop
}
\ No newline at end of file
# Mutex Example
This example demonstrates the basic usage of FreeRTOS Mutually Exclusive Locks (Mutex) for securing access to shared resources in multi-threading.
Please refer to other examples in this folder to better understand the usage of tasks.
It is also advised to read the documentation on FreeRTOS web pages:
https://www.freertos.org/a00106.html
This example creates 2 tasks with the same implementation - they write into a shared variable and then read it and check if it is the same as what they have written.
In single-thread programming like on Arduino this is of no concern and will be always ok, however when multi-threading is used the execution of the task is switched by the FreeRTOS and the value can be rewritten from another task before reading again.
The tasks print write and read operation - each in their column for better reading. Task 0 is on the left and Task 1 is on the right.
Watch the writes and read in secure mode when using the mutex (default) as the results are as you would expect them.
Then try to comment the USE_MUTEX and watch again - there will be a lot of mismatches!
### Theory:
Mutex is a specialized version of Semaphore (please see the Semaphore example for more info).
In essence, the mutex is a variable whose value determines if the mute is taken (locked) or given (unlocked).
When two or more processes access the same resource (variable, peripheral, etc) it might happen, for example, that when one task starts to read a variable and the operating system (FreeRTOS) will schedule the execution of another task
which will write to this variable and when the previous task runs again it will read something different.
Mutexes and binary semaphores are very similar but have some subtle differences:
Mutexes include a priority inheritance mechanism, whereas binary semaphores do not.
This makes binary semaphores the better choice for implementing synchronization (between tasks or between tasks and an interrupt), and mutexes the better
choice for implementing simple mutual exclusion.
What is priority inheritance?
If a low-priority task holds the Mutex but gets interrupted by a Higher priority task, which
then tries to take the Mutex, the low-priority task will temporarily ‘inherit’ the high priority so a middle-priority task can't block the low-priority task, and thus also block the high priority task.
Semaphores don't have the logic to handle this, in part because Semaphores aren't 'owned' by the task that takes them.
A mutex can also be recursive - if a task that holds the mutex takes it again, it will succeed, and the mutex will be released
for other tasks only when it is given the same number of times that it was taken.
You can check the danger by commenting on the definition of USE_MUTEX which will disable the mutex and present the danger of concurrent access.
# Supported Targets
This example supports all ESP32 SoCs.
## How to Use Example
Flash and observe the serial output.
Comment the `USE_MUTEX` definition, save and flash again and observe the behavior of unprotected access to the shared variable.
* How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
#### Using Platform IO
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
## Example Log Output
The expected output of shared variables protected by mutex demonstrates mutually exclusive access from tasks - they do not interrupt each other and do not rewrite the value before the other task has read it back.
```
Task 0 | Task 1
| Starting
| 0 <- 227
Starting |
| R: 227
227 <- 737 |
R: 737 |
| 737 <- 282
| R: 282
282 <- 267 |
```
The output of unprotected access to shared variable - it happens often that a task is interrupted after writing and before reading the other task write a different value - a corruption occurred!
```
Task 0 | Task 1
| Starting
| 0 <- 333
Starting |
333 <- 620 |
R: 620 |
620 <- 244 |
| R: 244
| Mismatch!
| 244 <- 131
R: 131 |
Mismatch! |
131 <- 584 |
| R: 584
| Mismatch!
| 584 <- 134
| R: 134
| 134 <- 554
R: 554 |
Mismatch! |
554 <- 313 |
```
## Troubleshooting
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
/* Basic Multi Threading Arduino Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
// Please read file README.md in the folder containing this example./*
#define MAX_LINE_LENGTH (64)
// Define two tasks for reading and writing from and to the serial port.
void TaskWriteToSerial(void *pvParameters);
void TaskReadFromSerial(void *pvParameters);
// Define Queue handle
QueueHandle_t QueueHandle;
const int QueueElementSize = 10;
typedef struct{
char line[MAX_LINE_LENGTH];
uint8_t line_length;
} message_t;
// The setup function runs once when you press reset or power on the board.
void setup() {
// Initialize serial communication at 115200 bits per second:
Serial.begin(115200);
while(!Serial){delay(10);}
// Create the queue which will have <QueueElementSize> number of elements, each of size `message_t` and pass the address to <QueueHandle>.
QueueHandle = xQueueCreate(QueueElementSize, sizeof(message_t));
// Check if the queue was successfully created
if(QueueHandle == NULL){
Serial.println("Queue could not be created. Halt.");
while(1) delay(1000); // Halt at this point as is not possible to continue
}
// Set up two tasks to run independently.
xTaskCreate(
TaskWriteToSerial
, "Task Write To Serial" // A name just for humans
, 2048 // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
, NULL // No parameter is used
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL // Task handle is not used here
);
xTaskCreate(
TaskReadFromSerial
, "Task Read From Serial"
, 2048 // Stack size
, NULL // No parameter is used
, 1 // Priority
, NULL // Task handle is not used here
);
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
Serial.printf("\nAnything you write will return as echo.\nMaximum line length is %d characters (+ terminating '0').\nAnything longer will be sent as a separate line.\n\n", MAX_LINE_LENGTH-1);
}
void loop(){
// Loop is free to do any other work
delay(1000); // While not being used yield the CPU to other tasks
}
/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/
void TaskWriteToSerial(void *pvParameters){ // This is a task.
message_t message;
for (;;){ // A Task shall never return or exit.
// One approach would be to poll the function (uxQueueMessagesWaiting(QueueHandle) and call delay if nothing is waiting.
// The other approach is to use infinite time to wait defined by constant `portMAX_DELAY`:
if(QueueHandle != NULL){ // Sanity check just to make sure the queue actually exists
int ret = xQueueReceive(QueueHandle, &message, portMAX_DELAY);
if(ret == pdPASS){
// The message was successfully received - send it back to Serial port and "Echo: "
Serial.printf("Echo line of size %d: \"%s\"\n", message.line_length, message.line);
// The item is queued by copy, not by reference, so lets free the buffer after use.
}else if(ret == pdFALSE){
Serial.println("The `TaskWriteToSerial` was unable to receive data from the Queue");
}
} // Sanity check
} // Infinite loop
}
void TaskReadFromSerial(void *pvParameters){ // This is a task.
message_t message;
for (;;){
// Check if any data are waiting in the Serial buffer
message.line_length = Serial.available();
if(message.line_length > 0){
// Check if the queue exists AND if there is any free space in the queue
if(QueueHandle != NULL && uxQueueSpacesAvailable(QueueHandle) > 0){
int max_length = message.line_length < MAX_LINE_LENGTH ? message.line_length : MAX_LINE_LENGTH-1;
for(int i = 0; i < max_length; ++i){
message.line[i] = Serial.read();
}
message.line_length = max_length;
message.line[message.line_length] = 0; // Add the terminating nul char
// The line needs to be passed as pointer to void.
// The last parameter states how many milliseconds should wait (keep trying to send) if is not possible to send right away.
// When the wait parameter is 0 it will not wait and if the send is not possible the function will return errQUEUE_FULL
int ret = xQueueSend(QueueHandle, (void*) &message, 0);
if(ret == pdTRUE){
// The message was successfully sent.
}else if(ret == errQUEUE_FULL){
// Since we are checking uxQueueSpacesAvailable this should not occur, however if more than one task should
// write into the same queue it can fill-up between the test and actual send attempt
Serial.println("The `TaskReadFromSerial` was unable to send data into the Queue");
} // Queue send check
} // Queue sanity check
}else{
delay(100); // Allow other tasks to run when there is nothing to read
} // Serial buffer check
} // Infinite loop
}
# Queue Example
This example demonstrates the basic usage of FreeRTOS Queues which enables tasks to pass data between each other in a secure asynchronous way.
Please refer to other examples in this folder to better understand the usage of tasks.
It is also advised to read the documentation on FreeRTOS web pages:
[https://www.freertos.org/a00106.html](https://www.freertos.org/a00106.html)
This example reads data received on the serial port (sent by the user) pass it via queue to another task which will send it back on Serial Output.
### Theory:
A queue is a simple-to-use data structure (in the most basic way) controlled by `xQueueSend` and `xQueueReceive` functions.
Usually, one task writes into the queue and the other task reads from it.
Usage of queues enables the reading task to yield the CPU until there are data in the queue and therefore not waste precious computation time.
# Supported Targets
This example supports all ESP32 SoCs.
## How to Use Example
Flash and write anything to serial input.
* How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
#### Using Platform IO
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
## Example Log Output
```
Anything you write will return as echo.
Maximum line length is 63 characters (+ terminating '0').
Anything longer will be sent as a separate line.
```
< Input text "Short input"
``Echo line of size 11: "Short input"``
< Input text "An example of very long input which is longer than default 63 characters will be split."
```
Echo line of size 63: "An example of very long input which is longer than default 63 c"
Echo line of size 24: "haracters will be split."
```
## Troubleshooting
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
# Semaphore Example
This example demonstrates the basic usage of FreeRTOS Semaphores and queue sets for coordination between tasks for multi-threading.
Please refer to other examples in this folder to better understand the usage of tasks.
It is also advised to read the documentation on FreeRTOS web pages:
[https://www.freertos.org/a00106.html](https://www.freertos.org/a00106.html)
### Theory:
Semaphore is in essence a variable. Tasks can set the value, wait until one or more
semaphores are set and thus communicate between each other their state.
A binary semaphore is a semaphore that has a maximum count of 1, hence the 'binary' name.
A task can only 'take' the semaphore if it is available, and the semaphore is only available if its count is 1.
Semaphores can be controlled by any number of tasks. If you use semaphore as a one-way
signalization with only one task giving and only one task taking there is a much faster option
called Task Notifications - please see FreeRTOS documentation and read more about them: [https://www.freertos.org/RTOS-task-notifications.html](https://www.freertos.org/RTOS-task-notifications.html)
This example uses a semaphore to signal when a package is delivered to a warehouse by multiple
delivery trucks, and multiple workers are waiting to receive the package.
# Supported Targets
This example supports all ESP32 SoCs.
## How to Use Example
Read the code and try to understand it, then flash and observe the Serial output.
* How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
#### Using Platform IO
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
## Example Log Output
```
Anything you write will return as echo.
Maximum line length is 63 characters (+ terminating '0').
Anything longer will be sent as a separate line.
```
< Input text "Short input"
``Echo line of size 11: "Short input"``
< Input text "An example of very long input which is longer than default 63 characters will be split."
```
Echo line of size 63: "An example of very long input which is longer than default 63 c"
Echo line of size 24: "haracters will be split."
```
## Troubleshooting
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
/* Basic Multi Threading Arduino Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
// Please read file README.md in the folder containing this example.
#include <Arduino.h>
SemaphoreHandle_t package_delivered_semaphore;
void delivery_truck_task(void *pvParameters) {
int truck_number = (int) pvParameters;
while(1) {
// Wait for a package to be delivered
// ...
// Notify the warehouse that a package has been delivered
xSemaphoreGive(package_delivered_semaphore);
Serial.printf("Package delivered by truck: %d\n", truck_number);
//wait for some time
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void warehouse_worker_task(void *pvParameters) {
int worker_number = (int) pvParameters;
while(1) {
// Wait for a package to be delivered
xSemaphoreTake(package_delivered_semaphore, portMAX_DELAY);
Serial.printf("Package received by worker: %d\n", worker_number);
// Receive the package
// ...
}
}
void setup() {
Serial.begin(115200);
while(!Serial){ delay(100); }
// Create the semaphore
package_delivered_semaphore = xSemaphoreCreateCounting(10, 0);
// Create multiple delivery truck tasks
for (int i = 0; i < 5; i++) {
xTaskCreate(delivery_truck_task, "Delivery Truck", 2048, (void *)i, tskIDLE_PRIORITY, NULL);
}
// Create multiple warehouse worker tasks
for (int i = 0; i < 3; i++) {
xTaskCreate(warehouse_worker_task, "Warehouse Worker", 2048, (void *)i, tskIDLE_PRIORITY, NULL);
}
}
void loop() {
// Empty loop
}
\ No newline at end of file
......@@ -41,6 +41,7 @@
#include "lwip/dns.h"
extern void tcpipInit();
extern void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif); /* from WiFiGeneric */
#if ESP_IDF_VERSION_MAJOR > 3
......@@ -327,6 +328,9 @@ bool ETHClass::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_typ
return false;
}
/* attach to WiFiGeneric to receive events */
add_esp_interface_netif(ESP_IF_ETH, eth_netif);
if(esp_eth_start(eth_handle) != ESP_OK){
log_e("esp_eth_start failed");
return false;
......
......@@ -32,7 +32,11 @@ void setup() {
Serial.println("Configuring access point...");
// You can remove the password parameter if you want the AP to be open.
WiFi.softAP(ssid, password);
// a valid password must have more than 7 characters
if (!WiFi.softAP(ssid, password)) {
log_e("Soft AP creation failed.");
while(1);
}
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
......
......@@ -136,12 +136,6 @@ void wifi_softap_config(wifi_config_t *wifi_config, const char * ssid=NULL, cons
bool WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel, int ssid_hidden, int max_connection, bool ftm_responder)
{
if(!WiFi.enableAP(true)) {
// enable AP failed
log_e("enable AP first!");
return false;
}
if(!ssid || *ssid == 0) {
// fail SSID missing
log_e("SSID missing!");
......@@ -154,6 +148,13 @@ bool WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel,
return false;
}
// last step after checking the SSID and password
if(!WiFi.enableAP(true)) {
// enable AP failed
log_e("enable AP first!");
return false;
}
wifi_config_t conf;
wifi_config_t conf_current;
wifi_softap_config(&conf, ssid, passphrase, channel, WIFI_AUTH_WPA2_PSK, ssid_hidden, max_connection, ftm_responder);
......
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define EXTERNAL_NUM_INTERRUPTS 16
#define NUM_DIGITAL_PINS 40
#define NUM_ANALOG_INPUTS 16
#define analogInputToDigitalPin(p) (((p)<20)?(analogChannelToDigitalPin(p)):-1)
#define digitalPinToInterrupt(p) (((p)<40)?(p):-1)
#define digitalPinHasPWM(p) (p < 34)
typedef unsigned char uint8_t;
static const uint8_t LED_BUILTIN = 2;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t TX = 1;
static const uint8_t RX = 3;
static const uint8_t TX2 = 17;
static const uint8_t RX2 = 16;
static const uint8_t SDA = 21;
static const uint8_t SCL = 22;
static const uint8_t SS = 5;
static const uint8_t MOSI = 23;
static const uint8_t MISO = 19;
static const uint8_t SCK = 18;
static const uint8_t D0 = 3;
static const uint8_t D1 = 1;
static const uint8_t D2 = 25;
static const uint8_t D3 = 26;
static const uint8_t D4 = 27;
static const uint8_t D5 = 0;
static const uint8_t D6 = 14;
static const uint8_t D7 = 13;
static const uint8_t D8 = 5;
static const uint8_t D9 = 2;
static const uint8_t D10 = 17;
static const uint8_t D11 = 16;
static const uint8_t D12 = 4;
static const uint8_t D13 = 12;
static const uint8_t A0 = 36;
static const uint8_t A1 = 39;
static const uint8_t A2 = 34;
static const uint8_t A3 = 35;
static const uint8_t A4 = 15;
static const uint8_t A5 = 35;
static const uint8_t A6 = 4;
static const uint8_t A7 = 0;
static const uint8_t A8 = 2;
static const uint8_t A9 = 13;
static const uint8_t A10 = 12;
static const uint8_t A11 = 14;
static const uint8_t A12 = 27;
static const uint8_t A13 = 25;
static const uint8_t A14 = 26;
static const uint8_t T0 = 4;
static const uint8_t T1 = 0;
static const uint8_t T2 = 2;
static const uint8_t T3 = 15;
static const uint8_t T4 = 13;
static const uint8_t T5 = 12;
static const uint8_t T6 = 14;
static const uint8_t T7 = 27;
static const uint8_t T8 = 33;
static const uint8_t T9 = 32;
static const uint8_t DAC1 = 25;
static const uint8_t DAC2 = 26;
#endif /* Pins_Arduino_h */
......@@ -15,56 +15,58 @@
#define digitalPinToInterrupt(p) (((p)<40)?(p):-1)
#define digitalPinHasPWM(p) (p < 34)
static const uint8_t LED_BUILTIN = 25;
static const uint8_t LED_BUILTIN = 35;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
static const uint8_t KEY_BUILTIN = 0;
static const uint8_t TX = 1;
static const uint8_t RX = 3;
static const uint8_t TX = 43;
static const uint8_t RX = 44;
static const uint8_t SDA = 21;
static const uint8_t SCL = 22;
static const uint8_t SS = 5;
static const uint8_t MOSI = 23;
static const uint8_t MISO = 19;
static const uint8_t SCK = 18;
static const uint8_t SS = 8;
static const uint8_t MOSI = 10;
static const uint8_t MISO = 11;
static const uint8_t SCK = 9;
static const uint8_t A0 = 36;
static const uint8_t A1 = 37;
static const uint8_t A2 = 38;
static const uint8_t A3 = 39;
static const uint8_t A4 = 32;
static const uint8_t A5 = 33;
static const uint8_t A6 = 34;
static const uint8_t A7 = 35;
static const uint8_t A0 = 1;
static const uint8_t A1 = 2;
static const uint8_t A2 = 3;
static const uint8_t A3 = 4;
static const uint8_t A4 = 5;
static const uint8_t A5 = 6;
static const uint8_t A6 = 7;
static const uint8_t A7 = 8;
static const uint8_t A8 = 9;
static const uint8_t A9 = 10;
static const uint8_t A10 = 11;
static const uint8_t A11 = 12;
static const uint8_t A12 = 13;
static const uint8_t A13 = 14;
static const uint8_t A14 = 15;
static const uint8_t A15 = 16;
static const uint8_t A16 = 17;
static const uint8_t A17 = 18;
static const uint8_t A18 = 19;
static const uint8_t A19 = 20;
static const uint8_t A10 = 4;
static const uint8_t A11 = 0;
static const uint8_t A12 = 2;
static const uint8_t A13 = 15;
static const uint8_t A14 = 13;
static const uint8_t A15 = 12;
static const uint8_t A16 = 14;
static const uint8_t A17 = 27;
static const uint8_t A18 = 25;
static const uint8_t A19 = 26;
static const uint8_t T0 = 4;
static const uint8_t T1 = 0;
static const uint8_t T1 = 1;
static const uint8_t T2 = 2;
static const uint8_t T3 = 15;
static const uint8_t T4 = 13;
static const uint8_t T5 = 12;
static const uint8_t T6 = 14;
static const uint8_t T7 = 27;
static const uint8_t T8 = 33;
static const uint8_t T9 = 32;
static const uint8_t DAC1 = 25;
static const uint8_t DAC2 = 26;
static const uint8_t T3 = 3;
static const uint8_t T4 = 4;
static const uint8_t T5 = 5;
static const uint8_t T6 = 6;
static const uint8_t T7 = 7;
static const uint8_t T8 = 8;
static const uint8_t T9 = 9;
static const uint8_t T10 = 10;
static const uint8_t T11 = 11;
static const uint8_t T12 = 12;
static const uint8_t T13 = 13;
static const uint8_t T14 = 14;
static const uint8_t Vext = 36;
static const uint8_t LED = 35;
......
......@@ -32,10 +32,10 @@ static const uint8_t RX = 44;
static const uint8_t SDA = 41;
static const uint8_t SCL = 42;
static const uint8_t SS = 10;
static const uint8_t MOSI = 11;
static const uint8_t MISO = 13;
static const uint8_t SCK = 12;
static const uint8_t SS = 8;
static const uint8_t MOSI = 10;
static const uint8_t MISO = 11;
static const uint8_t SCK = 9;
static const uint8_t A0 = 1;
static const uint8_t A1 = 2;
......
......@@ -23,8 +23,13 @@ static const uint8_t LED_BUILTIN = 3;
static const uint8_t TX = 43;
static const uint8_t RX = 44;
static const uint8_t SDA = 11;
static const uint8_t SCL = 10;
static const uint8_t SDA = 8;
static const uint8_t SCL = 9;
static const uint8_t SS = 15;
static const uint8_t MOSI = 16;
static const uint8_t MISO = 17;
static const uint8_t SCK = 18;
static const uint8_t A0 = 1;
static const uint8_t A1 = 2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment