Adafruit_PyCamera/Adafruit_PyCamera.cpp
2024-02-20 14:36:50 -05:00

880 lines
30 KiB
C++

#include "Adafruit_PyCamera.h"
static uint16_t *jpeg_buffer = NULL;
/**************************************************************************/
/**
* @brief Outputs the buffer for JPEG decoding.
*
* @param x The x-coordinate where the bitmap starts.
* @param y The y-coordinate where the bitmap starts.
* @param w The width of the bitmap.
* @param h The height of the bitmap.
* @param bitmap Pointer to the bitmap data.
*
* @return true if the buffer is successfully outputted, false otherwise.
*
* @details This function is used as a callback for JPEG decoding. It outputs
* the decoded bitmap to a specified location in the `jpeg_buffer`. The function
* checks for the validity of `jpeg_buffer` and ensures that the drawing
* operations stay within the bounds of the buffer.
*/
/**************************************************************************/
bool buffer_output(int16_t x, int16_t y, uint16_t w, uint16_t h,
uint16_t *bitmap) {
if (!jpeg_buffer)
return false;
// Serial.printf("Drawing [%d, %d] to (%d, %d)\n", w, h, x, y);
for (int xi = x; xi < x + w; xi++) {
if (xi >= 240)
continue;
if (xi < 0)
continue;
for (int yi = y; yi < y + h; yi++) {
if (yi >= 240)
continue;
if (yi < 0)
continue;
jpeg_buffer[yi * 240 + xi] = bitmap[(yi - y) * w + (xi - x)];
}
}
return true;
}
/**************************************************************************/
/**
* @brief Construct a new Adafruit_PyCamera object.
*
* @details Initializes the display with specified TFT parameters.
*
*/
/**************************************************************************/
Adafruit_PyCamera::Adafruit_PyCamera(void)
: Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RESET) {}
/**************************************************************************/
/**
* @brief Initializes the PyCamera.
*
* @details Sets up the speaker, Neopixel, Neopixel Ring, shutter button, and
* performs I2C scan. Initializes the display, expander, camera, frame size, SD
* card (if detected), and accelerometer. Creates a new framebuffer for the
* camera.
*
* @return true if initialization is successful, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::begin() {
Serial.println("Init PyCamera obj");
// Setup and turn off speaker
pinMode(SPEAKER, OUTPUT);
digitalWrite(SPEAKER, LOW);
// Setup and turn off Neopixel
pixel.setPin(PIN_NEOPIXEL);
pixel.updateLength(1);
pixel.begin();
pixel.setBrightness(50);
setNeopixel(0x0);
// Setup and turn off Neopixel Ring
ring.setPin(A1);
ring.updateType(NEO_GRBW + NEO_KHZ800);
ring.updateLength(8);
ring.begin();
ring.setBrightness(255);
setRing(0x0);
// boot button is also shutter
pinMode(SHUTTER_BUTTON, INPUT_PULLUP);
I2Cscan();
if (!initDisplay())
return false;
if (!initExpander())
return false;
if (!initCamera(true))
return false;
if (!setFramesize(FRAMESIZE_240X240))
return false;
if (SDdetected())
initSD();
if (!initAccel())
return false;
fb = new PyCameraFB(240, 240);
_timestamp = millis();
return true;
}
/**************************************************************************/
/**
* @brief Initializes the SD card.
*
* @details Checks for SD card presence and attempts initialization. Performs a
* power reset, reinitializes SPI for SD card communication, and checks for
* errors during SD card initialization. Also lists files on the SD card if
* initialization is successful.
*
* @return true if SD card is successfully initialized, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::initSD(void) {
if (!SDdetected()) {
Serial.println("No SD card inserted");
return false;
}
Serial.println("SD card inserted, trying to init");
// power reset
aw.pinMode(AWEXP_SD_PWR, OUTPUT);
aw.digitalWrite(AWEXP_SD_PWR, HIGH); // turn off
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, LOW);
Serial.println("De-initing SPI");
SPI.end();
pinMode(SCK, OUTPUT);
digitalWrite(SCK, LOW);
pinMode(MOSI, OUTPUT);
digitalWrite(MISO, LOW);
pinMode(MISO, OUTPUT);
digitalWrite(MISO, LOW);
delay(50);
Serial.println("Re-init SPI");
digitalWrite(SD_CS, HIGH);
SPI.begin();
aw.digitalWrite(AWEXP_SD_PWR, LOW); // turn on
delay(100);
if (!sd.begin(SD_CS, SD_SCK_MHZ(4))) {
if (sd.card()->errorCode()) {
Serial.printf("SD card init failure with code 0x%x data %d\n",
sd.card()->errorCode(), (int)sd.card()->errorData());
} else if (sd.vol()->fatType() == 0) {
Serial.println("Can't find a valid FAT16/FAT32 partition.");
} else {
Serial.println("SD begin failed, can't determine error type");
}
aw.digitalWrite(AWEXP_SD_PWR, HIGH); // turn off power
return false;
}
Serial.println("Card successfully initialized");
uint32_t size = sd.card()->cardSize();
if (size == 0) {
Serial.println("Can't determine the card size");
} else {
uint32_t sizeMB = 0.000512 * size + 0.5;
Serial.printf("Card size: %d MB FAT%d\n", (int)sizeMB, sd.vol()->fatType());
}
Serial.println("Files found (date time size name):");
sd.ls(LS_R | LS_DATE | LS_SIZE);
return true;
}
/**************************************************************************/
/**
* @brief Ends the SD card session.
*
* @details Powers off the SD card to end the session.
*/
/**************************************************************************/
void Adafruit_PyCamera::endSD() {
// aw.pinMode(AWEXP_SD_PWR, OUTPUT);
// aw.digitalWrite(AWEXP_SD_PWR, HIGH); // start off
}
/**************************************************************************/
/**
* @brief Initializes the AW9523 I/O expander.
*
* @details Sets up the AW9523 expander, configuring speaker, SD power, and SD
* detection pins.
*
* @return true if the expander is successfully initialized, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::initExpander(void) {
Serial.print("Init AW9523...");
if (!aw.begin(0x58)) {
Serial.println("AW9523 not found!");
return false;
}
Serial.println("OK!");
aw.pinMode(AWEXP_SPKR_SD, OUTPUT);
aw.digitalWrite(AWEXP_SPKR_SD, LOW); // start muted
aw.pinMode(AWEXP_SD_PWR, OUTPUT);
aw.digitalWrite(AWEXP_SD_PWR, LOW); // start SD powered
aw.pinMode(AWEXP_SD_DET, INPUT);
return true;
}
/**************************************************************************/
/**
* @brief Initializes the display.
*
* @details This method sets up the display for the PyCamera. It starts by
* initializing the backlight control, then initializes the ST7789 screen with
* the specified dimensions and rotation. Finally, it fills the screen with a
* green color and turns on the backlight.
*
* @return true if the display is successfully initialized, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::initDisplay(void) {
Serial.print("Init display....");
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, LOW);
init(240, 240); // Initialize ST7789 screen
setRotation(1);
fillScreen(ST77XX_GREEN);
digitalWrite(TFT_BACKLIGHT, HIGH);
Serial.println("done!");
return true;
}
/**************************************************************************/
/**
* @brief Sets the frame size for the camera.
*
* @details Configures the camera to use the specified frame size. If the frame
* size cannot be set, it outputs an error message with the error code.
*
* @param framesize The desired frame size to set for the camera.
* @return true if the frame size is successfully set, false if there is an
* error.
*/
/**************************************************************************/
bool Adafruit_PyCamera::setFramesize(framesize_t framesize) {
uint8_t ret = camera->set_framesize(camera, framesize);
if (ret != 0) {
Serial.printf("Could not set resolution: error 0x%x\n", ret);
return false;
}
return true;
}
/**************************************************************************/
/**
* @brief Sets a special effect on the camera.
*
* @details Applies a specified special effect to the camera's output. If the
* effect cannot be set, it outputs an error message with the error code.
*
* @param effect The special effect identifier to apply.
* @return true if the special effect is successfully set, false if there is an
* error.
*/
/**************************************************************************/
bool Adafruit_PyCamera::setSpecialEffect(uint8_t effect) {
uint8_t ret = camera->set_special_effect(camera, effect);
if (ret != 0) {
Serial.printf("Could not set effect: error 0x%x\n", ret);
return false;
}
return true;
}
/**************************************************************************/
/**
* @brief Initializes the camera module.
*
* @details Configures and initializes the camera with specified settings.
* It sets up various camera parameters like LEDC channel and timer, pin
* configuration, XCLK frequency, frame buffer location, pixel format, frame
* size, and JPEG quality. It also handles the hardware reset if specified.
* After configuration, it initializes the camera and checks for errors. If
* successful, it retrieves the camera sensor information and sets horizontal
* mirror and vertical flip settings.
*
* @param hwreset Flag to determine if a hardware reset is needed.
* @return true if the camera is successfully initialized, false if there is an
* error.
*/
/**************************************************************************/
bool Adafruit_PyCamera::initCamera(bool hwreset) {
Serial.print("Config camera...");
Wire.begin();
if (hwreset) {
}
camera_config.ledc_channel = LEDC_CHANNEL_0;
camera_config.ledc_timer = LEDC_TIMER_0;
camera_config.pin_d0 = Y2_GPIO_NUM;
camera_config.pin_d1 = Y3_GPIO_NUM;
camera_config.pin_d2 = Y4_GPIO_NUM;
camera_config.pin_d3 = Y5_GPIO_NUM;
camera_config.pin_d4 = Y6_GPIO_NUM;
camera_config.pin_d5 = Y7_GPIO_NUM;
camera_config.pin_d6 = Y8_GPIO_NUM;
camera_config.pin_d7 = Y9_GPIO_NUM;
camera_config.pin_xclk = XCLK_GPIO_NUM;
camera_config.pin_pclk = PCLK_GPIO_NUM;
camera_config.pin_vsync = VSYNC_GPIO_NUM;
camera_config.pin_href = HREF_GPIO_NUM;
camera_config.pin_sccb_sda = -1;
camera_config.pin_sccb_scl = -1;
// use the built in I2C port
camera_config.sccb_i2c_port = 0; // use the 'first' i2c port
camera_config.pin_pwdn = PWDN_GPIO_NUM;
camera_config.pin_reset = RESET_GPIO_NUM;
camera_config.xclk_freq_hz = 20000000;
camera_config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
camera_config.fb_location = CAMERA_FB_IN_PSRAM;
Serial.print("Config format...");
/*
// using RGB565 for immediate blitting
camera_config.pixel_format = PIXFORMAT_RGB565;
camera_config.frame_size = FRAMESIZE_240X240;
camera_config.fb_count = 1;
*/
camera_config.pixel_format = PIXFORMAT_JPEG;
camera_config.frame_size =
FRAMESIZE_UXGA; // start with biggest possible image supported!!! do not
// change this
camera_config.jpeg_quality = 4;
camera_config.fb_count = 2;
Serial.print("Initializing...");
// camera init
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x\n\r", err);
return false;
}
Serial.println("OK");
camera = esp_camera_sensor_get();
Serial.printf("Found camera PID %04X\n\r", camera->id.PID);
camera->set_hmirror(camera, 0);
camera->set_vflip(camera, 1);
return true;
}
/**************************************************************************/
/**
* @brief Reads the battery voltage.
*
* @details Measures the battery voltage through the BATT_MONITOR pin.
* The reading is scaled to account for the voltage divider and ADC resolution.
*
* @return The battery voltage in volts.
*/
/**************************************************************************/
float Adafruit_PyCamera::readBatteryVoltage(void) {
return analogRead(BATT_MONITOR) * 2.0 * 3.3 / 4096;
}
/**************************************************************************/
/**
* @brief Checks if an SD card is detected.
*
* @details Reads the state of the SD card detection pin using the AW9523
* expander. A high state indicates that an SD card is present.
*
* @return true if an SD card is detected, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::SDdetected(void) {
return aw.digitalRead(AWEXP_SD_DET);
}
/**************************************************************************/
/**
* @brief Reads the current state of the buttons.
*
* @details Retrieves the state of all buttons connected to the AW9523 expander
* and the shutter button. The state is updated and stored in the button_state
* variable. The previous state is stored in last_button_state.
*
* @return The current state of the buttons as a 32-bit unsigned integer.
*/
/**************************************************************************/
uint32_t Adafruit_PyCamera::readButtons(void) {
last_button_state = button_state;
button_state = aw.inputGPIO() & AW_INPUTS_MASK;
button_state |= (bool)digitalRead(SHUTTER_BUTTON);
return button_state;
}
/**************************************************************************/
/**
* @brief Checks if a button was just pressed.
*
* @details Determines if the specified button has transitioned from a
* non-pressed to a pressed state since the last read. This function is useful
* for detecting button press events.
*
* @param button_pin The pin number of the button to check.
* @return true if the button was just pressed, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::justPressed(uint8_t button_pin) {
return ((last_button_state & (1UL << button_pin)) && // was not pressed before
!(button_state & (1UL << button_pin))); // and is pressed now
}
/**************************************************************************/
/**
* @brief Checks if a button was just released.
*
* @details Determines if the specified button has transitioned from a pressed
* to a non-pressed state since the last read. This function is useful for
* detecting button release events.
*
* @param button_pin The pin number of the button to check.
* @return true if the button was just released, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::justReleased(uint8_t button_pin) {
return (!(last_button_state & (1UL << button_pin)) && // was pressed before
(button_state & (1UL << button_pin))); // and isnt pressed now
}
/**************************************************************************/
/**
* @brief Plays a tone through the speaker.
*
* @details Generates a tone of a specified frequency and duration through the
* speaker. It unmutes the speaker before playing the tone and mutes it again
* after the tone is played. The function uses a blocking delay for the duration
* of the tone.
*
* @param tonefreq The frequency of the tone in Hertz.
* @param tonetime The duration of the tone in milliseconds.
*/
/**************************************************************************/
void Adafruit_PyCamera::speaker_tone(uint32_t tonefreq, uint32_t tonetime) {
aw.digitalWrite(AWEXP_SPKR_SD, HIGH); // un-mute
tone(SPEAKER, tonefreq, tonetime); // tone1 - B5
delay(tonetime);
aw.digitalWrite(AWEXP_SPKR_SD, LOW); // mute
}
/**************************************************************************/
/**
* @brief Captures a photo and saves it to an SD card.
*
* @details This function captures a photo with the camera at the specified
* resolution, and saves it to the SD card with a filename based on the provided
* base name. It handles SD card detection, initialization, and file creation.
* The function also manages camera frame buffer acquisition and release, and
* sets the camera resolution.
*
* @param filename_base Base name for the file to be saved. The function appends
* a numerical suffix to create a unique filename.
* @param framesize The resolution at which the photo should be captured.
* @return true if the photo is successfully captured and saved, false
* otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::takePhoto(const char *filename_base,
framesize_t framesize) {
bool ok = false;
File file;
// esp_err_t res = ESP_OK;
if (!SDdetected()) {
Serial.println("No SD card inserted");
return false;
}
if (!sd.card() || (sd.card()->cardSize() == 0)) {
Serial.println("No SD card found");
// try to initialize?
if (!initSD())
return false;
}
// we're probably going to succeed in saving the file so we should
// change rez now since we need to grab two frames worth to clear out a cache
Serial.println("Reconfiguring resolution");
if (!setFramesize(framesize))
return false;
// capture and toss first internal buffer
frame = esp_camera_fb_get();
if (!frame) {
esp_camera_fb_return(frame);
setFramesize(FRAMESIZE_240X240);
Serial.println("Couldnt capture first frame");
return false;
}
Serial.printf("\t\t\tSnapped 1st %d bytes (%d x %d) in %d ms\n\r", frame->len,
frame->width, frame->height, (int)timestamp());
esp_camera_fb_return(frame);
// capture and toss second internal buffer
frame = esp_camera_fb_get();
if (!frame) {
esp_camera_fb_return(frame);
Serial.println("Couldnt capture second frame");
setFramesize(FRAMESIZE_240X240);
return false;
}
Serial.printf("\t\t\tSnapped 2nd %d bytes (%d x %d) in %d ms\n\r", frame->len,
frame->width, frame->height, (int)timestamp());
char fullfilename[64];
for (int inc = 0; inc <= 1000; inc++) {
if (inc == 1000)
return false;
snprintf(fullfilename, sizeof(fullfilename), "%s%03d.jpg", filename_base,
inc);
if (!sd.exists(fullfilename))
break;
}
// Create the file
if (file.open(fullfilename, FILE_WRITE)) {
if (file.write(frame->buf, frame->len)) {
Serial.printf("Saved JPEG to filename %s\n\r", fullfilename);
file.close();
// check what we wrote!
sd.ls(LS_R | LS_DATE | LS_SIZE);
ok = true;
} else {
Serial.println("Couldn't write JPEG data to file");
}
}
// even if it doesnt work out, reset camera size and close file
esp_camera_fb_return(frame);
file.close();
setFramesize(FRAMESIZE_240X240);
return ok;
}
/**************************************************************************/
/**
* @brief Returns the time elapsed since the last call to this function.
*
* @details This function calculates the time difference (in milliseconds)
* between the current time and the last time this function was called. It
* updates the internal timestamp to the current time at each call.
*
* @return The time elapsed (in milliseconds) since the last call to this
* function.
*/
/**************************************************************************/
uint32_t Adafruit_PyCamera::timestamp(void) {
uint32_t delta = millis() - _timestamp;
_timestamp = millis();
return delta;
}
/**************************************************************************/
/**
* @brief Prints a timestamped message to the Serial output.
*
* @details This function prints a message to the Serial output, prefixed with a
* timestamp. The timestamp represents the time elapsed in milliseconds since
* the last call to `timestamp()` function. It is useful for debugging and
* performance measurement.
*
* @param msg The message to be printed along with the timestamp.
*/
/**************************************************************************/
void Adafruit_PyCamera::timestampPrint(const char *msg) {
Serial.printf("%s: %d ms elapsed\n\r", msg, (int)timestamp());
}
/**************************************************************************/
/**
* @brief Captures a frame from the camera and processes it.
*
* @details This function captures a frame from the camera and processes it
* based on the current pixel format setting. It handles both JPEG and RGB565
* formats. For JPEG, it scales and draws the image onto a framebuffer. For
* RGB565, it flips the endians of the frame buffer. This function is essential
* for capturing and displaying camera frames.
*
* @return bool Returns true if the frame was successfully captured and
* processed, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::captureFrame(void) {
// Serial.println("Capturing...");
// esp_err_t res = ESP_OK;
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
int64_t fr_start = esp_timer_get_time();
#endif
frame = esp_camera_fb_get();
if (!frame) {
ESP_LOGE(TAG, "Camera frame capture failed");
return false;
}
/*
Serial.printf("\t\t\tFramed %d bytes (%d x %d) in %d ms\n\r",
frame->len,
frame->width, frame->height,
timestamp());
*/
if (camera_config.pixel_format == PIXFORMAT_JPEG) {
// Serial.print("JPEG");
// create the framebuffer if we haven't yet
if (!fb->getBuffer() || !jpeg_buffer) {
jpeg_buffer = (uint16_t *)malloc(240 * 240 * 2);
fb->setFB(jpeg_buffer);
}
uint16_t w = 0, h = 0, scale = 1;
int xoff = 0, yoff = 0;
TJpgDec.getJpgSize(&w, &h, frame->buf, frame->len);
if (w <= 240 || h <= 240) {
scale = 1;
xoff = yoff = 0;
} else if (w <= 480 || h <= 480) {
scale = 2;
xoff = (480 - w) / 2;
yoff = (480 - h) / 2;
} else if (w <= 960 || h <= 960) {
scale = 4;
} else {
scale = 8;
}
// Serial.printf(" size: %d x %d, scale %d\n\r", w, h, scale);
TJpgDec.setJpgScale(scale);
TJpgDec.setCallback(buffer_output);
TJpgDec.drawJpg(xoff, yoff, frame->buf, frame->len);
fb->setFB(jpeg_buffer);
} else if (camera_config.pixel_format == PIXFORMAT_RGB565) {
// flip endians
uint8_t temp;
for (uint32_t i = 0; i < frame->len; i += 2) {
temp = frame->buf[i + 0];
frame->buf[i + 0] = frame->buf[i + 1];
frame->buf[i + 1] = temp;
}
fb->setFB((uint16_t *)frame->buf);
}
return true;
}
/**************************************************************************/
/**
* @brief Blits the current frame buffer to the display.
*
* @details This function draws the current frame buffer onto the display at the
* specified coordinates. It is used to update the display with the latest
* camera frame. After drawing, it returns the frame buffer to the camera for
* reuse.
*/
/**************************************************************************/
void Adafruit_PyCamera::blitFrame(void) {
drawRGBBitmap(0, 0, (uint16_t *)fb->getBuffer(), 240, 240);
esp_camera_fb_return(frame);
}
/**************************************************************************/
/**
* @brief Blits the current frame buffer to the display.
*
* @details This function returns the frame buffer to the camera for
* reuse. Used when you are drawing your own bitmap to the framebuffer
* in user code.
*/
/**************************************************************************/
void Adafruit_PyCamera::refresh(void) { esp_camera_fb_return(frame); }
/**************************************************************************/
/**
* @brief Initializes the accelerometer.
*
* @details This function initializes the accelerometer by setting up the I2C
* device, checking the chip ID, and configuring the control registers for
* normal mode, data rate, resolution, and range. It ensures that the
* accelerometer is ready for data reading.
*
* @return bool Returns true if the accelerometer is successfully initialized,
* false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::initAccel(void) {
lis_dev = new Adafruit_I2CDevice(0x19, &Wire);
if (!lis_dev->begin()) {
return false;
}
Adafruit_BusIO_Register _chip_id =
Adafruit_BusIO_Register(lis_dev, LIS3DH_REG_WHOAMI, 1);
if (_chip_id.read() != 0x33) {
return false;
}
Adafruit_BusIO_Register _ctrl1 =
Adafruit_BusIO_Register(lis_dev, LIS3DH_REG_CTRL1, 1);
_ctrl1.write(0x07); // enable all axes, normal mode
Adafruit_BusIO_RegisterBits data_rate_bits =
Adafruit_BusIO_RegisterBits(&_ctrl1, 4, 4);
data_rate_bits.write(0b0111); // set to 400Hz update
Adafruit_BusIO_Register _ctrl4 =
Adafruit_BusIO_Register(lis_dev, LIS3DH_REG_CTRL4, 1);
_ctrl4.write(0x88); // High res & BDU enabled
Adafruit_BusIO_RegisterBits range_bits =
Adafruit_BusIO_RegisterBits(&_ctrl4, 2, 4);
range_bits.write(0b11);
Serial.println("Found LIS3DH");
return true;
}
/**************************************************************************/
/**
* @brief Reads accelerometer data.
*
* @details This function reads the X, Y, and Z acceleration data from the
* accelerometer. It sets up the register address for auto-increment to read
* consecutive data registers and then reads the 6 bytes of data corresponding
* to the X, Y, and Z axes.
*
* @param[out] x Pointer to store the X-axis acceleration data.
* @param[out] y Pointer to store the Y-axis acceleration data.
* @param[out] z Pointer to store the Z-axis acceleration data.
* @return bool Returns true if the data is successfully read, false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::readAccelData(int16_t *x, int16_t *y, int16_t *z) {
uint8_t register_address = LIS3DH_REG_OUT_X_L;
register_address |= 0x80; // set [7] for auto-increment
Adafruit_BusIO_Register xl_data =
Adafruit_BusIO_Register(lis_dev, register_address, 6);
uint8_t buffer[6];
if (!xl_data.read(buffer, 6))
return false;
*x = buffer[0];
*x |= ((uint16_t)buffer[1]) << 8;
*y = buffer[2];
*y |= ((uint16_t)buffer[3]) << 8;
*z = buffer[4];
*z |= ((uint16_t)buffer[5]) << 8;
return true;
}
/**************************************************************************/
/**
* @brief Reads accelerometer data and converts it to g-force values.
*
* @details This function reads the raw accelerometer data for X, Y, and Z axes
* using readAccelData() and then converts these values to g-force. The
* conversion factor depends on the accelerometer's sensitivity setting (here
* assumed for 16G range).
*
* @param[out] x_g Pointer to store the X-axis acceleration in g-force.
* @param[out] y_g Pointer to store the Y-axis acceleration in g-force.
* @param[out] z_g Pointer to store the Z-axis acceleration in g-force.
* @return bool Returns true if the data is successfully read and converted,
* false otherwise.
*/
/**************************************************************************/
bool Adafruit_PyCamera::readAccelData(float *x_g, float *y_g, float *z_g) {
int16_t x, y, z;
if (!readAccelData(&x, &y, &z))
return false;
uint8_t lsb_value = 48; // for 16G
*x_g = lsb_value * ((float)x / LIS3DH_LSB16_TO_KILO_LSB10);
*y_g = lsb_value * ((float)y / LIS3DH_LSB16_TO_KILO_LSB10);
*z_g = lsb_value * ((float)z / LIS3DH_LSB16_TO_KILO_LSB10);
return true;
}
/**************************************************************************/
/**
* @brief Scans the I2C bus and prints the addresses of all connected devices.
*
* @details This function iterates through all possible I2C addresses (0x00 to
* 0x7F) and attempts to initiate a transmission to each. If a device
* acknowledges the transmission, its address is printed to the Serial output.
* This is useful for debugging and identifying connected I2C devices.
*/
/**************************************************************************/
void Adafruit_PyCamera::I2Cscan(void) {
Wire.begin();
Serial.print("I2C Scan: ");
for (int addr = 0; addr <= 0x7F; addr++) {
Wire.beginTransmission(addr);
bool found = (Wire.endTransmission() == 0);
if (found) {
Serial.print("0x");
Serial.print(addr, HEX);
Serial.print(", ");
}
}
Serial.println();
}
/**************************************************************************/
/**
* @brief Sets the color of the Neopixel.
*
* @param c The color to set the Neopixel to, in 32-bit RGB format.
*
* @details This function sets the color of the Neopixel LED. It uses the `fill`
* method of the Adafruit_NeoPixel class to set all pixels to the specified
* color and then calls `show` to update the LED with the new color.
*/
/**************************************************************************/
void Adafruit_PyCamera::setNeopixel(uint32_t c) {
pixel.fill(c);
pixel.show(); // Initialize all pixels to 'off'
}
/**************************************************************************/
/**
* @brief Sets the color of the Neopixel Ring.
*
* @param c The color to set the Neopixel Ring to, in 32-bit RGB format.
*
* @details This function sets the color of the Neopixel Ring. It uses the
* `fill` method of the Adafruit_NeoPixel class to set all pixels in the ring to
* the specified color and then calls `show` to update the ring with the new
* color.
*/
/**************************************************************************/
void Adafruit_PyCamera::setRing(uint32_t c) {
ring.fill(c);
ring.show(); // Initialize all pixels to 'off'
}
/**************************************************************************/
/*!
@brief Input a value 0 to 255 to get a color value. The colours are a
transition r - g - b - back to r.
@param WheelPos The position in the wheel, from 0 to 255
@returns The 0xRRGGBB color
*/
/**************************************************************************/
uint32_t Adafruit_PyCamera::Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return Adafruit_NeoPixel::Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return Adafruit_NeoPixel::Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return Adafruit_NeoPixel::Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}