├── LICENSE ├── README.md ├── examples ├── Battery │ └── Battery.ino ├── Battery_OMGS3 │ └── Battery_OMGS3.ino ├── LED │ └── LED.ino └── LightSensor │ └── LightSensor.ino ├── library.properties └── src └── UMS3.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Unexpected Maker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unexpected Maker ESP32-S3 Arduino Helper Library 2 | 3 | This is the helper library for all the Unexpected Maker [ESP32-S3 boards](https://esp32s3.com). 4 | 5 | Examples can be found in the [examples directory](./examples/), these can also be loaded from the examples menu in the Arduino IDE. 6 | 7 | ## Installation 8 | 9 | This library can be installed through the Arduino library manager or manually from github by following [the instructions here](https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries). 10 | 11 | ## List of functions 12 | 13 | ```c++ 14 | 15 | // Initializes all UM board peripherals 16 | void begin(); 17 | 18 | // Set LDO2 on or off (ProS3 and FeatherS3 only) 19 | void setLDO2Power(bool on); 20 | 21 | // Set neopixel power on or off (On ProS3 and Feather it sets LDO2 on) 22 | void setPixelPower(bool on); 23 | 24 | // Set 7x7 RGB LED matrix power on or off 25 | // Only available on the FeatherS3 Neo 26 | void setMatrixPower(bool on); 27 | 28 | // Set neopixel color 29 | void setPixelColor(uint8_t r, uint8_t g, uint8_t b); 30 | void setPixelColor(uint32_t rgb); 31 | 32 | // Set neopixel brightness 33 | void setPixelBrightness(uint8_t brightness); 34 | 35 | // Pack r,g,b (0-255) into a 32bit rgb color 36 | static uint32_t color(uint8_t r, uint8_t g, uint8_t b); 37 | 38 | // Convert a color wheel angle (0-255) to a 32bit rgb color 39 | static uint32_t colorWheel(uint8_t pos); 40 | 41 | // Set the blue LED on or off 42 | // Only available on the FeatherS3 and FeatherS3 Neo 43 | void setBlueLED(bool on); 44 | 45 | // Toggle the blue LED 46 | // Only available on the FeatherS3 and FeatherS3 Neo 47 | void toggleBlueLED(); 48 | 49 | // Get the battery voltage in volts 50 | // On the OMGS3, this function gets the voltage from the MAX17048 otherwise, it reads it from the ADC pin. 51 | // Not available on the NanoS3 52 | float getBatteryVoltage(); 53 | 54 | // Get the light sensor in volts (0-3.3) 55 | // Only available on the FeatherS3 and FeatherS3 Neo 56 | float getLightSensorVoltage(); 57 | 58 | // Detect if VBUS (USB power) is present 59 | // Not available on the NanoS3 60 | bool getVbusPresent(); 61 | ``` 62 | -------------------------------------------------------------------------------- /examples/Battery/Battery.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | UMS3 ums3; 4 | 5 | void setup() 6 | { 7 | Serial.begin(115200); 8 | 9 | // Delay to allow native USB to kick in to get serial output 10 | delay(2000); 11 | 12 | // Initialize all board peripherals, call this first 13 | ums3.begin(); 14 | 15 | // Brightness is 0-255. We set it to 1/3 brightness here 16 | ums3.setPixelBrightness(255 / 3); 17 | } 18 | 19 | // Gets the battery voltage and shows it using the neopixel LED. 20 | // These values are all approximate, you should do your own testing and 21 | // find values that work for you. 22 | void checkBattery() 23 | { 24 | // Get the battery voltage, corrected for the on-board voltage divider 25 | // Full should be around 4.2v and empty should be around 3v 26 | float battery = ums3.getBatteryVoltage(); 27 | 28 | if (ums3.getVbusPresent()) 29 | { 30 | // If USB power is present 31 | if (battery < 4.0) 32 | { 33 | // Charging - blue 34 | ums3.setPixelColor(0x0000FF); 35 | } 36 | else 37 | { 38 | // Close to full - off 39 | ums3.setPixelColor(0x000000); 40 | } 41 | 42 | Serial.printf("Running from 5V - Battery: %fV\n", battery); 43 | } 44 | else 45 | { 46 | // Else, USB power is not present (running from battery) 47 | if (battery < 3.1) 48 | { 49 | // Uncomment the following line to sleep when the battery is critically low 50 | // esp_deep_sleep_start(); 51 | } 52 | else if (battery < 3.3) 53 | { 54 | // Below 3.3v - red 55 | ums3.setPixelColor(0xFF0000); 56 | } 57 | else if (battery < 3.6) 58 | { 59 | // Below 3.6v (around 50%) - orange 60 | ums3.setPixelColor(0xFF8800); 61 | } 62 | else 63 | { 64 | // Above 3.6v - green 65 | ums3.setPixelColor(0x00FF00); 66 | } 67 | 68 | Serial.printf("Running from Battery: %fV\n", battery); 69 | } 70 | } 71 | 72 | // Store the millis of the last battery check 73 | unsigned long lastBatteryCheck = 0; 74 | // Define the battery check interval as one second 75 | #define BATTERY_CHECK_INTERVAL 1000 76 | 77 | void loop() 78 | { 79 | if (lastBatteryCheck == 0 || millis() - lastBatteryCheck > BATTERY_CHECK_INTERVAL) 80 | { 81 | checkBattery(); 82 | lastBatteryCheck = millis(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/Battery_OMGS3/Battery_OMGS3.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | UMS3 ums3; 4 | 5 | void setup() 6 | { 7 | Serial.begin(115200); 8 | 9 | // Delay to allow native USB to kick in to get serial output 10 | delay(2000); 11 | 12 | // Initialize all board peripherals, call this first 13 | ums3.begin(); 14 | Serial.println("begin"); 15 | 16 | // Brightness is 0-255. We set it to 1/3 brightness here 17 | ums3.setPixelBrightness(255 / 3); 18 | 19 | // We initialise the I2C peripheral outside of the helper library and pass the reference in 20 | // In case you want to use the BUS for other I2C peripherals as well. 21 | Wire.begin(); 22 | ums3.FG_setup(Wire); 23 | 24 | // Print the MAX17048 FG version 25 | Serial.printf("MAX17048 version: %d\n", ums3.FG_version()); 26 | } 27 | 28 | // Gets the battery voltage and shows it using the neopixel LED. 29 | // These values are all approximate, you should do your own testing and 30 | // find values that work for you. 31 | void checkBattery() 32 | { 33 | // Get the battery voltage, corrected for the on-board voltage divider 34 | // Full should be around 4.2v and empty should be around 3v 35 | float battery = ums3.getBatteryVoltage(); 36 | 37 | if (ums3.getVbusPresent()) 38 | { 39 | // If USB power is present 40 | if (battery < 4.0) 41 | { 42 | // Charging - blue 43 | ums3.setPixelColor(0x0000FF); 44 | } 45 | else 46 | { 47 | // Close to full - off 48 | ums3.setPixelColor(0x000000); 49 | } 50 | 51 | Serial.printf("Running from 5V - Battery: %fV\n", battery); 52 | } 53 | else 54 | { 55 | // Else, USB power is not present (running from battery) 56 | if (battery < 3.1) 57 | { 58 | // Uncomment the following line to sleep when the battery is critically low 59 | // esp_deep_sleep_start(); 60 | } 61 | else if (battery < 3.3) 62 | { 63 | // Below 3.3v - red 64 | ums3.setPixelColor(0xFF0000); 65 | } 66 | else if (battery < 3.6) 67 | { 68 | // Below 3.6v (around 50%) - orange 69 | ums3.setPixelColor(0xFF8800); 70 | } 71 | else 72 | { 73 | // Above 3.6v - green 74 | ums3.setPixelColor(0x00FF00); 75 | } 76 | 77 | Serial.printf("Running from Battery: %fV\n", battery); 78 | } 79 | } 80 | 81 | // Store the millis of the last battery check 82 | unsigned long lastBatteryCheck = 0; 83 | // Define the battery check interval as five seconds 84 | #define BATTERY_CHECK_INTERVAL 5000 85 | 86 | void loop() 87 | { 88 | if (lastBatteryCheck == 0 || millis() - lastBatteryCheck > BATTERY_CHECK_INTERVAL) 89 | { 90 | checkBattery(); 91 | lastBatteryCheck = millis(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/LED/LED.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | UMS3 ums3; 4 | 5 | void setup() { 6 | // Initialize all board peripherals, call this first 7 | ums3.begin(); 8 | 9 | // Brightness is 0-255. We set it to 1/3 brightness here 10 | ums3.setPixelBrightness(255 / 3); 11 | 12 | // Enable the power to the RGB LED. 13 | // Off by default so it doesn't use current when the LED is not required. 14 | ums3.setPixelPower(true); 15 | } 16 | 17 | int color = 0; 18 | 19 | void loop() { 20 | // colorWheel cycles red, orange, ..., back to red at 256 21 | ums3.setPixelColor(UMS3::colorWheel(color)); 22 | color++; 23 | 24 | // On the feathers3, toggle the LED twice per cycle 25 | #ifdef ARDUINO_FEATHERS3 26 | if (color % 128 == 0) { 27 | ums3.toggleBlueLED(); 28 | } 29 | #endif 30 | 31 | delay(15); 32 | } -------------------------------------------------------------------------------- /examples/LightSensor/LightSensor.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | UMS3 ums3; 4 | 5 | void setup() 6 | { 7 | Serial.begin(115200); 8 | 9 | // Delay to allow native USB to kick in to get serial output 10 | delay(2000); 11 | 12 | // Initialize all board peripherals, call this first 13 | ums3.begin(); 14 | } 15 | 16 | void loop() 17 | { 18 | // Light sensor voltage goes up to about 3.3v 19 | float light = ums3.getLightSensorVoltage(); 20 | 21 | // View this with the arduino serial plotter (in the tools menu) 22 | Serial.println(light); 23 | 24 | delay(50); 25 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=UMS3 Helper 2 | version=2.0.0 3 | author=UnexpectedMaker 4 | maintainer=UnexpectedMaker 5 | sentence=Helper library for UnexpectedMaker S3 boards 6 | paragraph=Supports NanoS3, OMGS3, TinyS3, ProS3, FeatherS3 & FeatherS3 Neo 7 | category=Other 8 | url=https://github.com/UnexpectedMaker/esp32s3 9 | architectures=esp32 10 | includes=UMS3.h -------------------------------------------------------------------------------- /src/UMS3.h: -------------------------------------------------------------------------------- 1 | // Updated for ESP32 Arduino Core 3.0.0 support 2 | // Oct 19, 2024 - Seon Rozenblum (Unexpected Maker) 3 | // 4 | // Added OMGS3 and FeatherS3 support 5 | // Dec 3, 2024 - Seon Rozenblum (Unexpected Maker) 6 | // 7 | #ifndef _UMS3_H 8 | #define _UMS3_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #if defined(ARDUINO_OMGS3) 15 | #include 16 | #endif 17 | 18 | #if defined(ARDUINO_TINYS3) || defined(ARDUINO_PROS3) 19 | #define VBAT_ADC_CHANNEL ADC1_GPIO10_CHANNEL 20 | #define VBAT_ADC_PIN 10 21 | #elif defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3NEO) 22 | #define VBAT_ADC_CHANNEL ADC1_GPIO2_CHANNEL 23 | #define ALS_ADC_CHANNEL ADC1_GPIO4_CHANNEL 24 | #define VBAT_ADC_PIN 2 25 | #define ALS_ADC_PIN 4 26 | #elif defined(ARDUINO_NANOS3) || defined(ARDUINO_OMGS3) 27 | #else 28 | #error \ 29 | "The board you have selected is not compatible with the UMS3 helper library" 30 | #endif 31 | 32 | class UMS3 33 | { 34 | #if defined(ARDUINO_OMGS3) 35 | enum class MAX17048_REG 36 | { 37 | VCELL = 0x02, 38 | SOC = 0x04, 39 | MODE = 0x06, 40 | VERSION = 0x08, 41 | HIBRT = 0x0A, 42 | CONFIG = 0x0C, 43 | VALRT = 0x14, 44 | CRATE = 0x16, 45 | VRESET_ID = 0x18, 46 | STATUS = 0x1A, 47 | TABLE = 0x40, 48 | CMD = 0xFE 49 | }; 50 | 51 | const uint8_t I2C_ADDR = 0x36; 52 | #endif 53 | 54 | public: 55 | UMS3() : brightness(255) {} 56 | 57 | void begin() 58 | { 59 | // RGB_PWR is LDO2 on boards that have it 60 | pinMode(RGB_PWR, OUTPUT); 61 | 62 | #if ESP_ARDUINO_VERSION_MAJOR < 3 63 | rmt = rmtInit(RGB_DATA, RMT_TX_MODE, RMT_MEM_64); 64 | rmtSetTick(rmt, 25); 65 | #else 66 | rmtInit(RGB_DATA, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000); 67 | #endif 68 | 69 | #if defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3_NEO) 70 | pinMode(LED_BUILTIN, OUTPUT); 71 | #endif 72 | 73 | #if defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3_NEO) || defined(ARDUINO_TINYS3) || defined(ARDUINO_PROS3) 74 | #if ESP_ARDUINO_VERSION_MAJOR < 3 75 | esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_2_5, ADC_WIDTH_BIT_12, 0, 76 | &adc_cal); 77 | adc1_config_channel_atten(VBAT_ADC_CHANNEL, ADC_ATTEN_DB_2_5); 78 | #else 79 | analogSetPinAttenuation(VBAT_ADC_PIN, ADC_2_5db); 80 | #endif 81 | #endif 82 | 83 | #if defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3_NEO) 84 | #if ESP_ARDUINO_VERSION_MAJOR < 3 85 | adc1_config_channel_atten(ALS_ADC_CHANNEL, ADC_ATTEN_DB_12); 86 | #else 87 | analogSetPinAttenuation(ALS_ADC_PIN, ADC_11db); 88 | #endif 89 | #endif 90 | 91 | #if defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3_NEO) || defined(ARDUINO_TINYS3) || defined(ARDUINO_PROS3) || defined(ARDUINO_OMGS3) 92 | pinMode(VBUS_SENSE, INPUT); 93 | #endif 94 | } 95 | 96 | #if defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) 97 | void setLDO2Power(bool on) { digitalWrite(LDO2, on); } 98 | #endif 99 | 100 | #if defined(ARDUINO_FEATHERS3NEO) 101 | void setMatrixPower(bool on) { digitalWrite(RGB_MATRIX_PWR, on); } 102 | #endif 103 | 104 | void setPixelPower(bool on) { digitalWrite(RGB_PWR, on); } 105 | 106 | void setPixelColor(uint8_t r, uint8_t g, uint8_t b) 107 | { 108 | pixel_color[0] = g; 109 | pixel_color[1] = r; 110 | pixel_color[2] = b; 111 | writePixel(); 112 | } 113 | 114 | void setPixelColor(uint32_t rgb) { setPixelColor(rgb >> 16, rgb >> 8, rgb); } 115 | 116 | void setPixelBrightness(uint8_t brightness) 117 | { 118 | this->brightness = brightness; 119 | writePixel(); 120 | } 121 | 122 | void writePixel() 123 | { 124 | setPixelPower(true); 125 | while (micros() - next_rmt_write < 350) 126 | { 127 | yield(); 128 | } 129 | int index = 0; 130 | for (auto chan : pixel_color) 131 | { 132 | uint8_t value = chan * (brightness + 1) >> 8; 133 | for (int bit = 7; bit >= 0; bit--) 134 | { 135 | if ((value >> bit) & 1) 136 | { 137 | #if ESP_ARDUINO_VERSION_MAJOR < 3 138 | rmt_data[index].level0 = 1; 139 | rmt_data[index].duration0 = 32; // 800ns 140 | rmt_data[index].level1 = 0; 141 | rmt_data[index].duration1 = 18; // 450ns 142 | } 143 | else 144 | { 145 | rmt_data[index].level0 = 1; 146 | rmt_data[index].duration0 = 16; // 400ns 147 | rmt_data[index].level1 = 0; 148 | rmt_data[index].duration1 = 34; // 850ns 149 | #else 150 | rmt_data[index].level0 = 1; 151 | rmt_data[index].duration0 = 8; 152 | rmt_data[index].level1 = 0; 153 | rmt_data[index].duration1 = 4; 154 | } 155 | else 156 | { 157 | rmt_data[index].level0 = 1; 158 | rmt_data[index].duration0 = 4; 159 | rmt_data[index].level1 = 0; 160 | rmt_data[index].duration1 = 8; 161 | #endif 162 | } 163 | index++; 164 | } 165 | } 166 | #if ESP_ARDUINO_VERSION_MAJOR < 3 167 | rmtWrite(rmt, rmt_data, 3 * 8); 168 | #else 169 | rmtWrite(RGB_DATA, rmt_data, 3 * 8, RMT_WAIT_FOR_EVER); 170 | #endif 171 | next_rmt_write = micros(); 172 | } 173 | 174 | static uint32_t color(uint8_t r, uint8_t g, uint8_t b) 175 | { 176 | return (r << 16) | (g << 8) | b; 177 | } 178 | 179 | static uint32_t colorWheel(uint8_t pos) 180 | { 181 | if (pos < 85) 182 | { 183 | return color(255 - pos * 3, pos * 3, 0); 184 | } 185 | else if (pos < 170) 186 | { 187 | pos -= 85; 188 | return color(0, 255 - pos * 3, pos * 3); 189 | } 190 | else 191 | { 192 | pos -= 170; 193 | return color(pos * 3, 0, 255 - pos * 3); 194 | } 195 | } 196 | 197 | #if defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3NEO) 198 | void setBlueLED(bool on) { digitalWrite(LED_BUILTIN, on); } 199 | 200 | void toggleBlueLED() { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); } 201 | #endif 202 | 203 | #if defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3NEO) || defined(ARDUINO_TINYS3) || defined(ARDUINO_PROS3) 204 | float getBatteryVoltage() 205 | { 206 | #if ESP_ARDUINO_VERSION_MAJOR < 3 207 | uint32_t raw = adc1_get_raw(VBAT_ADC_CHANNEL); 208 | uint32_t millivolts = esp_adc_cal_raw_to_voltage(raw, &adc_cal); 209 | #else 210 | uint32_t millivolts = analogReadMilliVolts(VBAT_ADC_PIN); 211 | #endif 212 | const uint32_t upper_divider = 442; 213 | const uint32_t lower_divider = 160; 214 | return (float)(upper_divider + lower_divider) / lower_divider / 1000 * 215 | millivolts; 216 | } 217 | 218 | bool getVbusPresent() { return digitalRead(VBUS_SENSE); } 219 | #endif 220 | 221 | #if defined(ARDUINO_FEATHERS3) || defined(ARDUINO_FEATHERS3NEO) 222 | float getLightSensorVoltage() 223 | { 224 | #if ESP_ARDUINO_VERSION_MAJOR < 3 225 | uint32_t raw = adc1_get_raw(ALS_ADC_CHANNEL); 226 | uint32_t millivolts = esp_adc_cal_raw_to_voltage(raw, &adc_cal); 227 | return (float)millivolts / 1000.0f; 228 | #else 229 | uint32_t millivolts = analogReadMilliVolts(ALS_ADC_PIN); 230 | return (float)(millivolts); 231 | #endif 232 | } 233 | #endif 234 | 235 | #if defined(ARDUINO_OMGS3) 236 | void FG_setup(TwoWire &w) { wire = &w; } 237 | 238 | float getBatteryVoltage() 239 | { 240 | return ((float)i2c_read(MAX17048_REG::VCELL) * 78.125f / 1000000.f); 241 | } 242 | 243 | bool getVbusPresent() { return digitalRead(VBUS_SENSE); } 244 | 245 | uint8_t FG_version() { return (uint8_t)i2c_read(MAX17048_REG::VERSION); } 246 | #endif 247 | 248 | private: 249 | #if ESP_ARDUINO_VERSION_MAJOR < 3 250 | rmt_obj_t *rmt; 251 | #endif 252 | 253 | rmt_data_t rmt_data[3 * 8]; 254 | unsigned long next_rmt_write; 255 | uint8_t pixel_color[3]; 256 | uint8_t brightness; 257 | esp_adc_cal_characteristics_t adc_cal; 258 | 259 | #if defined(ARDUINO_OMGS3) 260 | /* I2C communication for MAX17048 FG*/ 261 | void i2c_write(const MAX17048_REG reg) 262 | { 263 | wire->beginTransmission(I2C_ADDR); 264 | wire->write((uint8_t)reg); 265 | wire->endTransmission(); 266 | } 267 | 268 | void i2c_write(const MAX17048_REG reg, const uint16_t data) 269 | { 270 | wire->beginTransmission(I2C_ADDR); 271 | wire->write((uint8_t)reg); 272 | wire->write((data & 0xFF00) >> 8); 273 | wire->write((data & 0x00FF) >> 0); 274 | wire->endTransmission(); 275 | } 276 | 277 | uint16_t i2c_read(const MAX17048_REG reg) 278 | { 279 | i2c_write(reg); 280 | wire->requestFrom((uint8_t)I2C_ADDR, (uint8_t)2); // 2byte R/W only 281 | uint16_t data = (uint16_t)((wire->read() << 8) & 0xFF00); 282 | data |= (uint16_t)(wire->read() & 0x00FF); 283 | return data; 284 | } 285 | 286 | TwoWire *wire; 287 | 288 | #endif 289 | }; 290 | 291 | #endif 292 | --------------------------------------------------------------------------------