Skip to content

Week 14, Interface and Application Programming

Group assignment:

Compare as many tool options as possible.

Document your work on the group work page and reflect on your individual page what you learned.

Individual assignment:

Write an application for the embedded board that you made. that interfaces a user with an input and/or output device(s)

Group Assignment

There are just an absolute ton of different workflows to create applications to interact with hardware. Not having a software background, I am drawn to platforms that are easy to navigate and/or have a lot of sample code and tutorials. I felt especially tool limited this week as I really wanted my device to be BLE controlled and it seems like a lot of those platforms are more native ground up development.

Link to group page

Write an application for the embedded board

As I just finished up the color changing silocone vignette device in molding week, I wanted to add app functionality to the device to send custom colors to the device.

After some false starts and frustrations with different workflows, I ended up building an Android color picker to send custom colors to my lens hood.

Android app controlling the lights on the vignette snoot.

Screenshot of the app.

Beginnings and Frustrations

The development started really well. I had locked into MIT App Inventor early on and thought that would be a good way to get the functionality I wanted. I had used it briefyly before and knew it had a BLE module, so it seemed like the perfect platform.

There were also some good tutorials on how to make a color picker and also how to communicate with the XIAO board with BLE. So I headed down that road.

Color Picker Tutorial from hay kel

BLE Control of LEDs with App Inventor and XIAO from channel

Color Picker

I started by exploring App Inventor by building a color picker. I followed the tutorial above to build out a page with RGB sliders that would change the color of a canvas element on the app to the mixed color. It was straightforward and allow me to explore the interface again.

Build of color picker app in App Inventor

Testing BLE Communication

Then I moved over to testing the BLE communication. I downloaded the XIAO code that was linked out from the Science Fun video above where he turned 3 LEDs on and off with the app. I setup a breadboard and hooked up red, green, and blue discreet LEDs to D7, D8, and D9 on a XIAO ESP32 C3.

Breadboard setup to test code.

Code

I updated the code to change to the correct pin numbers and uploaded to the board. This code uses the ArduinoBLE library and has one service setup with 3 characteristics, one for each LED. This ended up being the backbone of my code for what I eventually produced.

wk14_LED_BLE_test.ino
#include <ArduinoBLE.h>

BLEService LEDService("19b20000-e8f2-537e-4f6c-d104768a1214"); // Service UUID
BLEIntCharacteristic redCharacteristic("19b20001-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);
BLEIntCharacteristic greenCharacteristic("19b20002-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);
BLEIntCharacteristic blueCharacteristic("19b20003-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);

// for C3 20,8,9
// for S3 44, 7, 8


const int redPIN = 20;                                        // pin to use for the red LED (D11)
const int greenPIN = 8;                                      // pin to use for the green LED (D12)
const int bluePIN = 9;                                       // pin to use for the blue LED (D13)

void setup() {
  Serial.begin(115200);

  pinMode(redPIN, OUTPUT);                                      //set pins as outputs and set LED's HIGH (HIGH is off for this board)
  pinMode(greenPIN, OUTPUT);
  pinMode(bluePIN, OUTPUT);

  digitalWrite(redPIN, HIGH);
  digitalWrite(greenPIN, HIGH);
  digitalWrite(bluePIN, HIGH);
  delay(3000);
  digitalWrite(redPIN, LOW);
  digitalWrite(greenPIN, LOW);
  digitalWrite(bluePIN, LOW);

  if (!BLE.begin()) {                                           // begin initialization
    while (1);                                                  // wait until initialization complete
  }

  BLE.setLocalName("Arduino");                                  // set advertised local name
  BLE.setAdvertisedService(LEDService);                         // set advertised service UUID
  LEDService.addCharacteristic(redCharacteristic);              // add the red characteristic to the service
  LEDService.addCharacteristic(greenCharacteristic);            // add the green characteristic to the service
  LEDService.addCharacteristic(blueCharacteristic);             // add the blue characteristic to the service
  BLE.addService(LEDService);                                   // add service
  BLE.advertise();                                              // start advertising
}

void loop() {
  BLEDevice central = BLE.central();                            // listen for BLE devices to connect:

  if (central) {                                                // if a central is connected to peripheral:

                                                                // put code here to preform 1 time when device is connected

    while (central.connected()) {                               // while the central is still connected to peripheral
                                                                // if there is an update from Android App, change light ON (0) or OFF (1) 
      if (redCharacteristic.written()) digitalWrite(redPIN, redCharacteristic.value());
      Serial.print("Red Value:");
      Serial.println(redCharacteristic.value());
      if (greenCharacteristic.written()) digitalWrite(greenPIN, greenCharacteristic.value());
      Serial.print("Green Value:");
      Serial.println(greenCharacteristic.value());
      if (blueCharacteristic.written()) digitalWrite(bluePIN, blueCharacteristic.value());   
    }

  }                                                             //end of while loop
                                                                // you can put code here for what to do when not connected
}                                                               // end of loop

Testing

I used the BLE Scanner app to test the functionality. From the code, the LEDs should turn on and off by recieving a 1 or a 0 from the app. Initially, I could get them to turn on but not off. Eventually I realized that I had to send the values as a text string and not a hex value. Then it started acting normally.

BLE connect screen showing the services being broadcast that I could write to

App Inventor Build

Boosted by the positive test from the code, I proceded with the App Inventor build. I followed the tutorial from the Science Fun video, but only set it up for just the red channel to facilitate testing. I thought if I could get that going it would be trivial to add the green and blue channels.

My initial code in App Inventor to turn red LED on and off with a button.

I built this and pushed the app to my phone. Unfortunately, the device would not connect to the BLE so I could not test the button. Then I did a little bit of research and realized that the BLE module for App Inventor is not supported on iOS.

Since there are no Android devices in my household, this realization kicked off a couple of days of frustration.

Back to Bluefruit Connect

I had used Bluefruit Connect just last week and used the buttons on the UART controller to change the color of the LEDs on the lens hood. I had not implemented the color picker as the data from that part of the app were coming across strangely. I thought I could just debug and fix the data stream and use BC for my work this week. However, it was a false dawn.

For some reason I could not get BC to connect to my device at all. I had a very lengthy co-engineering session with Chat GPT trying to troubleshoot getting any data to come across over UART. I went through many iterations of sample code, and I could never see any data transmitted from the app to the XIAO. Weirdly, I could see data sent from XIAO on the phone.

Weirdly, at the same time, I realized that my device and code from Week 13 was completely code rotted and would not work. I even tried a different iOS phone in my house and nothing worked. I also tried to downgrade the ESP32 library inside of the Arduino IDE and it was all futile. I even tested the app on an Adafruit Bluefruit Circuit Playground (Adafruit board and Adafruit app) with code that weeks ago was fine. It also did not work.

So, after hours of frustration here, I abandoned this as a viable pathway going forward.

Some ChatGPT prompts used during troubleshooting:

"how can i make a color picker app that will send a custom color over BLE to a string of neopixels on a XIAO esp32C3 board"

"can you write uart code for XIAO esp32 c3 to get color data from bluefruit connect"

"the code wont compile..undefined reference to esp_spp_enhanced_init'"

"in the bluefruit connect app it says "uart protocol can not be initialized'"

"it is still not activating the neopixel when i send the color from the app.. it seems like the "if (newColor)" part of the code is not triggering"

"it did not compile. the error is "'void UARTServerCallbacks::onWrite(NimBLECharacteristic*)' marked 'override', but does not override" "

"this is still not working.. when i send the color from the app it does not seem to get into the if (rxValue.length() >= 8 && rxValue[0] == '!' && rxValue[1] == 'C') part of the code"

"it is still not turning on the neopixel on when I send the color value from the app."

"ok.. the neopixel does work on pin 10.. i am not seeing any values come through on the serial monitor from the ble"

"i am still not seeing anything come through on the serial port.. is there another app we can send data from to test the BLE code"

Sample of troubleshooting with GPT. I went through a number of iterations to try to get Bluefruit to work.

App Inventor Development

After some introspection, I realized that a lot of frustration would go away if I had an Android device. I had a very old one in my house and when I tried to download App Inventor, it was still struggling to load it after an hour of trying. It was then I realized that I could get one for $30 at Walmart. So, I went and bought a Motorola Moto G Play 4G 2024, 64GB phone. After setting it up and downloading App Inventor onto it, things started working!

Toggle an LED

I started by revisiting the BLE code and single button app from above. I uploaded the code to my XIAO ESP32 C3 and pushed the app to my Android phone. It worked easily and proved that I could get BLE data from the app to the device and create a change.

Blinking LED with App Inventor.

Analog Output to an LED

Then I moved forward to see if I could get the LED to change brightness based on sending data from a slider widget from the app. The theory here was that if I could successfully send values from 0-255 to the device to change the brightness of a discreet LED that I could use the same routine on a Neopixel to mix colors.

App Inventor

Inside of App Inventor I created a slider widget. I then set the output range from 0-255 with 100 steps. I changed the colors and added a display text to show the actual value from the slider. Then I had the button grab the slider location and send it to the red characteristic when it was pushed.

Coding the slider for App Inventor.

Code to send color to red characteristic.

Code

I updated the BLE code on the XIAO to do an analog write based on the incoming data. The BLE part of the code including the characteristics was unchanged.

wk14_LED_BLE_analog.ino
#include <ArduinoBLE.h>

BLEService LEDService("19b20000-e8f2-537e-4f6c-d104768a1214"); // Service UUID
BLEIntCharacteristic redCharacteristic("19b20001-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);
BLEIntCharacteristic greenCharacteristic("19b20002-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);
BLEIntCharacteristic blueCharacteristic("19b20003-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);


// for C3 20,8,9
// for S3 44, 7, 8

const int redPIN = 20;                                        // pin to use for the red LED (D11)
const int greenPIN = 8;                                      // pin to use for the green LED (D12)
const int bluePIN = 9;                                       // pin to use for the blue LED (D13)

void setup() {

  Serial.begin(115200);

  //set pins as outputs and set LED's HIGH (HIGH is off for this board)
  pinMode(redPIN, OUTPUT);  
  pinMode(greenPIN, OUTPUT);
  pinMode(bluePIN, OUTPUT);

  digitalWrite(redPIN, HIGH);
  digitalWrite(greenPIN, HIGH);
  digitalWrite(bluePIN, HIGH);

  delay(3000);

  digitalWrite(redPIN, LOW);
  digitalWrite(greenPIN, LOW);
  digitalWrite(bluePIN, LOW);

  if (!BLE.begin()) {                                           // begin initialization
    while (1);                                                  // wait until initialization complete
  }

  BLE.setLocalName("Arduino");                                  // set advertised local name
  BLE.setAdvertisedService(LEDService);                         // set advertised service UUID
  LEDService.addCharacteristic(redCharacteristic);              // add the red characteristic to the service
  LEDService.addCharacteristic(greenCharacteristic);            // add the green characteristic to the service
  LEDService.addCharacteristic(blueCharacteristic);             // add the blue characteristic to the service
  BLE.addService(LEDService);                                   // add service
  BLE.advertise();                                              // start advertising
}

void loop() {
  BLEDevice central = BLE.central();                            // listen for BLE devices to connect:

  if (central) {                                                // if a central is connected to peripheral:

                                                                // put code here to preform 1 time when device is connected

    while (central.connected()) {                               // while the central is still connected to peripheral
                                                                // if there is an update from Android App, change light ON (0) or OFF (1) 
      if (redCharacteristic.written()) analogWrite(redPIN, redCharacteristic.value());
      Serial.print("Red Value:");
      Serial.println(redCharacteristic.value());

      if (greenCharacteristic.written()) analogWrite(greenPIN, greenCharacteristic.value());
      Serial.print("Green Value:");
      Serial.println(greenCharacteristic.value());

      if (blueCharacteristic.written()) analogWrite(bluePIN, blueCharacteristic.value()); 
      Serial.print("Blue Value:");
      Serial.println(blueCharacteristic.value());  
    }

     Serial.print("RGBs :");
     Serial.print(redCharacteristic.value());
     Serial.print(", ");
     Serial.print(greenCharacteristic.value());
     Serial.print(", ");
     Serial.println(blueCharacteristic.value());
  }                                                             //end of while loop
                                                                // you can put code here for what to do when not connected
}                                                               // end of loop

Testing

With the code loaded, I pushed the app to my Android phone and tested. The XIAO connected easily to the phone and I was able to move the slider and push the button to send new values to the LED and it was visibly changing brightness based on the value.

Changing LED brightness with slider widget.

3 LEDs

I did a short exploration to make sure that the app and code would change the brightness of 3 different LEDs.

Setup

I hooked up the additional LEDs to the breadboard with 220 ohm resistors hooked up to pins D7, D8 and D9.

App Inventor

I copied and pasted the sliders and readouts for each of the green and blue channels and updated the button to aggregate the values from each slider.

Code to aggregate the slider values and send over BLE.

Code

This test used the same code from the last test as it had all 3 channels baked into it.

Testing

I pushed the new app to the Android phone and tested. I was indeed able to manipulate the brightness of all 3 LEDs and push with the button. This showed that 3 channels of data could be mixed and sent at the same time. So was a perfect prelude to making it work for Neopixels.

Changing LED brightness for all 3 LEDs with slider widgets.

Adding Neopixel Control and Updating App

Then I moved forward with making the app control Neopixels. It was a relatively straightforward addition of the Neopixel library to the existing code and using the BLE characteristics as the RGB components being sent to the Neopixels. I also took the time to make the app look a bit less makery.

Setup

I moved the XIAO C3 to my development board that was assembled in Week 9 and is attached to the molded lens hook from Week 13. It has a single onboard Neopixel and a connector for an additional string of Neopixels.

App Inventor

I mostly just did some cleanup and style work on the app for this iteration. I added my logo to the pages. I also added some code for the send button to turn the color of whatever the mixed color from the sliders would be so the user can have a preview of what is being sent to the device. I also got rid of the second half of the button code that would hold it down.

Full code for the BLE page of the app.

Design side of the BLE screen.

I also added a home screen with buttons to get into the vignette mode or the rainbow mode. I will build out a page to turn on white LEDs to project rainbows for the final project.

Code

The code was update to incorporate the Neopixel library, but is quite similar to what was used throughout the LED testing phases with the same service and characteristics.

wk14_LED_BLE_analog.ino
#include <ArduinoBLE.h>
#include <Adafruit_NeoPixel.h>

#define NEOPIXEL_PIN 10
#define LED_COUNT    43

Adafruit_NeoPixel strip(LED_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

BLEService LEDService("19b20000-e8f2-537e-4f6c-d104768a1214"); // Service UUID
BLEIntCharacteristic redCharacteristic("19b20001-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);
BLEIntCharacteristic greenCharacteristic("19b20002-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);
BLEIntCharacteristic blueCharacteristic("19b20003-e8f2-537e-4f6c-d104768a1214", BLERead | BLEWrite | BLENotify);


// for C3 20,8,9
// for S3 44, 7, 8

const int redPIN = 20;                                        // pin to use for the red LED (D11)
const int greenPIN = 8;                                      // pin to use for the green LED (D12)
const int bluePIN = 9;                                       // pin to use for the blue LED (D13)

uint8_t r = 0, g = 0, b = 0;

void setup() {

  Serial.begin(115200);


  strip.setPixelColor(0, strip.Color(19, 65, 52));
  strip.show();

  if (!BLE.begin()) {                                           // begin initialization
    while (1);                                                  // wait until initialization complete
  }

  BLE.setLocalName("Arduino");                                  // set advertised local name
  BLE.setAdvertisedService(LEDService);                         // set advertised service UUID
  LEDService.addCharacteristic(redCharacteristic);              // add the red characteristic to the service
  LEDService.addCharacteristic(greenCharacteristic);            // add the green characteristic to the service
  LEDService.addCharacteristic(blueCharacteristic);             // add the blue characteristic to the service
  BLE.addService(LEDService);                                   // add service
  BLE.advertise();                                              // start advertising
}

void loop() {
  BLEDevice central = BLE.central();                            // listen for BLE devices to connect:

  if (central) {                                                // if a central is connected to peripheral:

                                                                // put code here to preform 1 time when device is connected

    while (central.connected()) {                               // while the central is still connected to peripheral
                                                                // if there is an update from Android App, change light ON (0) or OFF (1) 
      if (redCharacteristic.written()) analogWrite(redPIN, redCharacteristic.value());
      r=redCharacteristic.value();
      Serial.print("Red Value:");
      Serial.println(redCharacteristic.value());

      if (greenCharacteristic.written()) analogWrite(greenPIN, greenCharacteristic.value());
      g=greenCharacteristic.value();
      Serial.print("Green Value:");
      Serial.println(greenCharacteristic.value());

      if (blueCharacteristic.written()) analogWrite(bluePIN, blueCharacteristic.value()); 
      b= blueCharacteristic.value();
      Serial.print("Blue Value:");
      Serial.println(blueCharacteristic.value());  

      Serial.printf("Color: R=%d G=%d B=%d\n", r, g, b);

       for (int j = 0; j <= LED_COUNT; j++){

        // First LED is RGB (even though strip is NEO_GRB by default)
        strip.setPixelColor(j,r, g, b);
        strip.show();
        }

    }

     Serial.print("RGBs :");
     Serial.print(redCharacteristic.value());
     Serial.print(", ");
     Serial.print(greenCharacteristic.value());
     Serial.print(", ");
     Serial.println(blueCharacteristic.value());
  }                                                             //end of while loop
                                                                // you can put code here for what to do when not connected
}                                                               // end of loop

Testing

I used the QR scanner in App Inventor to start the app on the Android device and pushed the code to the XIAO. As can be seen from the video, I was able to mix colors and send to the device seamlessly. Once it was vetted inside of Inventor, I downloaded the .apk file and installed it on my phone so that I can run it without being connected to App Inventor.

Cromira app working on device.

Cromira APK