Bluetooth Sensors: Pt. 5

Today’s Goal

Today I am going to switch gears a bit and work on the sensors side of things. A good first goal is to use the HC08 to communicate from my Adafruit Trinket (a very small Arduino board) to my Artik board.

To begin with, I need to add the headers to the board, so I fire up my soldering iron and do that. This is going to be interesting because the board doesn’t actually have a UART interface, so I’m going to have to bitbang the bytes. Or, hopefully, I’ll have a library do the bitbanging and I’ll just tell it what to send.

Once the headers are in place, it’s time to plug it in and see if I can get to it from the Arduino IDE.

Arduino IDE

This part was a little less fun. As it turns out, I had to install some drivers from Adafruit. Fortunately the Adafruit tutorial was pretty useful, so this wasn’t too bad. One interesting thing is that I did have to select a port before programming the trinket; despite all the caveats that a serial port wasn’t used for programming the board (it isn’t), the IDE will complain if you have no serial port selected. This actually took me about 15 minutes to figure out, which is no fun.

int led = 1; // blink 'digital' pin 1 - AKA the built in red LED
 
// the setup routine runs once when you press reset:
void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
 
}
 
// the loop routine runs over and over again forever:
void loop() {
    digitalWrite(led, HIGH); 
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
}

This works as expected, thankfully.

Serial Port

Well, the trinket doesn’t have an on-board UART interface. This means we’re going to have to use GPIOs to fake it. We’re going to disregard the CTS/DTR pins and just use the Tx/Rx pins for simplicity. The pinout of the 3V trinket that I’m using is:

3V trinket pinout

Note that this image is taken from Adafruit (link).

What I’m thinking is that I want to use the I2C pins (PB0 and PB2) to communicate with the temperature and humidity sensor, and PB1 is connected to the LED which leaves us PB3 and PB4 to use for UART. This should work for us.

I start by looking for a library to do the bitbanging. This wouldn’t be a bad exercise to write myself, but it’s also not terribly interesting to me right now. After a very brief internet search, I find SoftwareSerial. This looks like what I want: I can specify the pin numbers to use for TX and RX, and it will function very similarly to the normal serial ports on Arduinos.

For this first sketch, I’m going to keep the LED blinking just so that I know it’s alive, and add just a bit more code to handle the serial port:

First serial sketch

#include <SoftwareSerial.h>

int led = 1; // blink 'digital' pin 1 - AKA the built in red LED

#define UART_RX 3
#define UART_TX 4

#define HC08_BAUD_RATE 9600

SoftwareSerial mySerial =  SoftwareSerial(UART_RX, UART_TX);

// the setup routine runs once when you press reset:
void setup() {
  pinMode(led, OUTPUT);
  pinMode(UART_RX, INPUT);
  pinMode(UART_TX, OUTPUT);
  mySerial.begin(HC08_BAUD_RATE);
}

// the loop routine runs over and over again forever:
int n = 0;
void loop() {
  char buf[16] = {0};

  sprintf(buf, "number: %d", n);
  digitalWrite(led, HIGH); 
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000);
  mySerial.print(buf);
  n++;
}

This is pretty straight-forward, building on top of the last sketch. I simply allocate a small 16-char buffer on the stack to use as a string buffer, and use the standard library function sprintf() to format the string. Since this is Arduino, I can probably also use String(), but for starters, I’m going with what I know.

The reason for this buffer is because if I write to the serial port in multiple writes (e.g. mySerial.print("Value: "); mySerial.print(n);), the GATT attribute updates twice, which causes my Bluetooth program on the Artik board to get notified of the update twice. This isn’t what I want.

Connecting the HC08

I connected the HC08 to the Trinket, and went over to my Artik program from last time, and sure enough, the output is exactly as expected. Working on the first try – excellent!

2018/07/03 23:52:03 Message: number: 249, From: DA:E2:6D:19:97:3B
2018/07/03 23:52:05 Message: number: 250, From: DA:E2:6D:19:97:3B
2018/07/03 23:52:07 Message: number: 251, From: DA:E2:6D:19:97:3B
2018/07/03 23:52:09 Message: number: 252, From: DA:E2:6D:19:97:3B
2018/07/03 23:52:11 Message: number: 253, From: DA:E2:6D:19:97:3B
...

The messages are coming in every two seconds because the Trinket code is still blinking the LED (on for one second, off for one second). This is a great start!

Arduino/Trinket Extending

I’m not going to hook up the Si7021 chips tonight, but I can start putting some wrapper functions in place. The Si7021 measures both temperature and relative humidity, so I’m going to stub out those functions for now to return real-looking but fake values.

Stubs

I stubbed out these functions using fixed-point types. There are two reasons for this. First: generally I am opposed to floating point numbers on microcontrollers–operations on them tend to be slow, and can be unstable if used incorrectly (like floating point numbers everywhere). Secondly, the sprintf() function call in the Arduino library is super stripped down and can’t convert a floating point number to a string.

So I use a reasonable fixed-point number. I decided to use a factor of 1000 (units then in millis) for both temperature and relative humidity. This is probably more than I need: I don’t expect temperature or humidity values to need anywhere near that level of precision (in fact, the sensor is almost certainly not that precise), and it makes the math pretty easy.

bool getTemperature(long *temperature_milli_C) {
  if (temperature_milli_C == NULL)
    return false;

  *temperature_milli_C = 19400;
  return true;
}

bool getRelativeHumidity(long *relativeHumidity_percent_milli) {
  if (relativeHumidity_percent_milli == NULL)
    return false;

  *relativeHumidity_percent_milli = 11300;
  return true;
}

These functions are super simple right now, and simply return filler values. I decided to pass in the pointers so that I can return a simple true or false to indicate whether the call succeeds (in practice, I expect that there can be errors in retrieving the readings from the sensor).

Final Code

The final code for this evening is as follows:

#include <SoftwareSerial.h>

int led = 1; // blink 'digital' pin 1 - AKA the built in red LED

#define UART_RX 3
#define UART_TX 4

#define HC08_BAUD_RATE 9600

SoftwareSerial mySerial =  SoftwareSerial(UART_RX, UART_TX);

// the setup routine runs once when you press reset:
void setup() {
  pinMode(led, OUTPUT);
  pinMode(UART_RX, INPUT);
  pinMode(UART_TX, OUTPUT);
  mySerial.begin(HC08_BAUD_RATE);
}

bool getTemperature(long *temperature_milli_C) {
  if (temperature_milli_C == NULL)
    return false;

  *temperature_milli_C = 19400;
  return true;
}

bool getRelativeHumidity(long *relativeHumidity_percent_milli) {
  if (relativeHumidity_percent_milli == NULL)
    return false;

  *relativeHumidity_percent_milli = 11300;
  return true;
}

void loop() {
  char buf[16] = {0};
  long temperature_C, relativeHumidity_percent;

  if (getTemperature(&temperature_C) && getRelativeHumidity(&relativeHumidity_percent)) {
    sprintf(buf, "%ld,%ld", temperature_C, relativeHumidity_percent);
    mySerial.print(buf);
  }

  digitalWrite(led, HIGH); 
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000);
}

Summary

Today I set up my Adafruit trinket that will be the brains behind my sensors. Fortunately it went smoothly and I was able to hook one right up to my Bluetooth module and send data to my Artik code pretty simply. Along the way I noticed some problems in my Artik code that will have to be addressed at some point (notably that the reconnection doesn’t work and there can be weird cases when the Artik tries to connect to a Bluetooth device it knows about but that isn’t currently available).

For next time, I’ll probably add in the Si7021 temperature and humidity sensor and try to finish up my sensor setup!