Arducam success! Finally

blog

If you have been following along here for a while you’ll know that I have had constant failures trying to get an Arducam Mega 3MP working with my IoT projects. The last attempt was:

https://blog.ciaopslabs.com/2025/07/13/arducam-mega-3mp-failed-attempt/

After getting my robot car working with a PS3 controller I was working towards getting the PS3 controller also working with my robot arm. At the moment the robot arm is connected to a Keyestudio KS0172 with a Keyestudio Sensor Shield/Expansion Board V5 for Arduino Leonardo attached. Unfortunately, the Keyestudio KS0172 lacks both Bluetooth and Wifi but I noticed the Keyestudio Sensor Shield/Expansion Board V5 for Arduino Leonardo actually has a dedicated SPI port like so:

image

Ah ha. I wonder if I can get that working with the Arducam? Spoiler alert, yes I can.

I have now come to realise probably the two biggest mistakes I have made with the Arducam Mega 3MP:

1. I thought it was a ‘streaming’ style camera. No it’s designed really just to take pictures

2. I need something to ‘read/download’ the images from the camera to actually see those images

With the camera connected to the Keyestudio Sensor Shield/Expansion Board V5 for Arduino Leonardo SPI port. As a reminder the camera connections are:

image

I used this piece of code on the Keyestudio KS0172:

https://github.com/directorcia/Azure/blob/master/Iot/Arducam/3MP/capture-image.cpp

to connect to the camera and allow a photo to be taken and stream it down the serial port on request. The documentation for this code is here:

https://github.com/directorcia/Azure/blob/master/Iot/Arducam/3MP/capture-image.md

I then had to write some Python code to actually initiate a photo being taken and extract the image from the camera over the USB/serial port and put it into a subdirectory on my machine. That code is here:

https://github.com/directorcia/Azure/blob/master/Iot/Arducam/3MP/capture-image.py

and the documentation for that is here:

https://github.com/directorcia/Azure/blob/master/Iot/Arducam/3MP/capture-image-py.md

and to execute this Python script I also needed to install Python on my machine, which is pretty easy in Visual Studio code by just adding the Python extension.

With all that in place and after a bit of back and forth to get the image to download correctly via the serial port I was indeed able to confirm that my Arducam Mega 3MP  is working properly and I can now use it to take photos.

Phew. That took a long time and a lot of effort. I think my major oversights, listed above, really held me back here along with the usual physical connection challenges. Now, I have a much better understanding of what the camera can and can’t do and what I need to actually see an image and most importantly the Arducam Mega 3MP is finally actually working!

Garage Sensor–Code

image

This is a follow on post to my:

Garage Sensor overview

and

Garage Sensor – Genesis

What I want to do here is spend some time looking at the code, which can be found here:

https://github.com/directorcia/Azure/blob/master/Iot/Arduino%20Uno%20R3/Garage%20distance/main.cpp

First steps are to have the include statements, which are:

#include <Wire.h>
#include “SparkFun_Alphanumeric_Display.h”
#include <VL53L1X.h>
#define DISPLAY_ADDRESS 0x70

I then need to create an instance of both the display and distance sensor, which is done via:

HT16K33 display;
VL53L1X sensor;

by default, the display will be at address 0x70 and the distance sensor will be 0x29 for I2C communications.

Next, I define some constants:

const uint16_t POT_PIN = A0;             // Analog pin for potentiometer
const uint16_t mid_point = 1380;         // Optimal distance from wall
const uint16_t adjust = 334;             // potentiometer midpoint value;
const uint16_t average_number = 10;      // Number for averaging distance readings

The pot to allow distance trimming will be at analog port 0. I define the mid point of this pot. Anything past the middle will extend (i.e. add) the optimal distance and anything below will subtract from my optimal distance. I found the total value range (i.e. low and high) of the pot simply by measuring the input from the pot in different positions directly from the analog port.

I define my optimal distance from the wall. Here, 1380 mm.

I define the number of repetition I want to use and then average my readings across as 10 here.

int potvalue;
int SWITCH_PIN = 8;                 // Debug Switch pin
int LED_UNDER_RED = 3;              // LED pin for under red
int LED_UNDER_YELLOW = 4;           // LED pin for under yellow
int LED_WHITE = 5;                  // LED pin for white
int LED_OVER_YELLOW = 6;            // LED pin for over yellow
int LED_OVER_RED = 7;               // LED pin for over red

Next I define a variable for my pot and the digital pins on which each LED is connected.

// Initialize I2C communication
Wire.begin();


// Initialize the display
if (display.begin(DISPLAY_ADDRESS) == false) {
   Serial.begin(9600);
   Serial.println(“Display not found. Check wiring.”);
   while (1);
}


// Initialize the VL53L1X sensor
sensor.setTimeout(500);
if (!sensor.init()) {
   Serial.begin(9600);
   Serial.println(“Failed to detect and initialize sensor!”);
   while (1);
}
sensor.startContinuous(50);
sensor.setDistanceMode(VL53L1X::Long);
sensor.setMeasurementTimingBudget(100);


// Clear the display
display.clear();
display.write(“Boot”);

In my setup look I start I2C communications, initialise the LED display along with the distance sensor.

// pinmodes
pinMode(SWITCH_PIN, INPUT_PULLUP);
pinMode(POT_PIN, INPUT);
pinMode(LED_UNDER_RED, OUTPUT);
pinMode(LED_UNDER_YELLOW, OUTPUT);
pinMode(LED_WHITE, OUTPUT);
pinMode(LED_OVER_YELLOW, OUTPUT);
pinMode(LED_OVER_RED, OUTPUT);
delay(100);
digitalWrite(LED_UNDER_RED, HIGH);
delay(100);
digitalWrite(LED_UNDER_YELLOW, HIGH);
delay(100);
digitalWrite(LED_WHITE, HIGH);
delay(100);
digitalWrite(LED_OVER_YELLOW, HIGH);
delay(100);
digitalWrite(LED_OVER_RED, HIGH);
delay(100);

I define my pin modes for the LEDS and set them all to high so I know they work on each boot.

// average pot readings
unint16_t sum = 0;
for (int i = 0; i < average_number; i++) {
   sum += analogRead(POT_PIN);
   delay(2);
}

I then take a number of analog readings from the location of the pot and average them to come to a single value to be used to adjust the optimal distance.

In my loop

// average distance readings
uint16_t sum = 0;
for (int i = 0; i < average_number; i++) {
     sum += sensor.read();
     delay(2);
}
uint16_t stableDistance = sum / average_number;

I firstly get an average distance from my sensor.

uint16_t midvalue = mid_point + potvalue – adjust;      // Adjusted midpoint value
uint16_t under_red = 0.8 * midvalue;                    // Under red zone
uint16_t under_yellow = 0.96 * midvalue;                // Under yellow zone
uint16_t over_yellow = 1.04 * midvalue;                 // Over yellow zone
uint16_t over_red = 1.2 * midvalue;                     // Over red zon
e

I use this average distance, less any adjustment via the pot to set the distance zones for each LED.

int reading = digitalRead(SWITCH_PIN);

I then see if the debug switch has been set.

if (reading == LOW) { // if Debug mode
     int adjustvalue;
    
     // average pot readings
     uint16_t sum = 0;
     for (int i = 0; i < average_number; i++) {
         sum += analogRead(POT_PIN);
         delay(2);
     }
     potvalue = sum / average_number;


    // Turn on all LEDs
     digitalWrite(LED_UNDER_RED, HIGH);
     digitalWrite(LED_UNDER_YELLOW, HIGH);
     digitalWrite(LED_WHITE, HIGH);
     digitalWrite(LED_OVER_YELLOW, HIGH);
     digitalWrite(LED_OVER_RED, HIGH);
     delay(100);


    // Display the pot value          
     snprintf(buffer, sizeof(buffer), “%4d”, (potvalue – adjust));
     display.write(buffer);

If it has then I turn on all the LEDs and display the pot value on the display. The idea is that in debug mode you adjust the pot to set any desired offset in distance for your unique circumstance.

If the debug switch isn’t on then I display the current distance on the 4 digit LED display and turn teh appropriate LED on the and the others off. I manage that in a case statement.

Thus, I keep looping through reporting distance from teh sensor to the 4 digit LED display and setting the proximity LEDs unless the debug switch is on. If the switch switch is set, then I display the pot setting so that it can be adjusted.

The idea is you power up the unit in debug mode, ensure all LEDs are working and adjust the pot to setting to create an offset from the define optimal of 1380mm. You then turn off debug mode and the 4 digit LED display will show the distance from the wall in mm and the proximity LEDs will display based on the range from the wall.