ESP8266 Call Button

It's NDN Community Meeting again and this time I'm officially presenting HomeCam at the demo session. I'm the sole presenter of this project. Normally, I have to stay at my table to show my project to the audience. However, I don't want to miss the chance of seeing what others have been doing. To solve this dilemma, I come up with an idea: a call button.

I put a big button on my table. It is labelled as: if I'm not here, press the button to send an Interest. Then, I wear a battery powered light ring on my body. Whenever someone presses the button, it lights up for 15 seconds.

me wearing a light ring at NDNcomm demo session

How It Works

Both the button and the light ring are based on ESP8266. The light ring unit acts as WiFi access point and NDN producer. The button unit acts as WiFi station and NDN consumer. When the button is pressed, the consumer transmits a signed Interest, and the producer turns on the light for 15 seconds after verifying the signature. Since the light ring unit is battery-powered, it enters deep sleep mode if there's no connected WiFi client.

Video demo:

Assembly

The light ring consists of:

It's wired as follows:

  • HUZZAH JST socket connects to the battery.
  • HUZZAH "3V" pin connects to NeoPixel "power" pad.
  • HUZZAH "GND" pin connects to NeoPixel "ground" pad.
  • HUZZAH "14" pin connects to NeoPixel "input" pad.
  • HUZZAH "16" pin connects to HUZZAH "RST" pin, allowing waking up from deep sleep.

Several rubber bands keep the hardware together. I then hang the unit on my neck with a rope.

The button unit is a Witty Cloud board, connected to a button that I salvaged from a door bell. The button connects between pin 4 and GND, which works same as the tiny USR button on the top layer of the Witty Cloud board. The unit is powered by USB.

Arduino Code

Library: esp8266ndn

Light ring unit sketch: LightRing.ino

#include <Adafruit_NeoPixel.h>
#include <esp8266ndn.h>
#include <ESP8266WiFi.h>

Adafruit_NeoPixel g_strip = Adafruit_NeoPixel(18, 14, NEO_GRB + NEO_KHZ800);

ndn::DigestKey g_ringPri;
const uint8_t CTRL_PUBKEY[] PROGMEM  {      0x04,
  0x0C, 0xD8, 0xDC, 0xB5, 0x1C, 0x97, 0x78, 0x67, 0xA8, 0xB4, 0xED, 0x62, 0x96, 0x6D, 0xFC, 0x6D,
  0xFE, 0xA6, 0x26, 0x44, 0x62, 0x51, 0x0B, 0xA0, 0x4A, 0xFC, 0x30, 0xBC, 0x91, 0x5E, 0x5D, 0xD8,
  0x43, 0x9D, 0xE2, 0x5B, 0xD0, 0xA9, 0xB8, 0x3C, 0x7A, 0x01, 0xF4, 0xB6, 0x13, 0x8E, 0x36, 0x5F,
  0x00, 0x42, 0x80, 0x45, 0xF0, 0x69, 0x7C, 0x59, 0x48, 0xFA, 0x28, 0xA6, 0x3A, 0x2B, 0xDB, 0x35,
};
ndn::EcPublicKey g_ctrlPub;

ndn::UdpTransport g_transport;
ndn::Face g_face(g_transport);

void ledSetColor(uint32_t c)
{
  Serial.print(F("set-color "));
  Serial.println(c, HEX);

  for (uint16_t i = 0; i < g_strip.numPixels(); ++i) {
    g_strip.setPixelColor(i, c);
  }
  g_strip.show();
}

void ledSetup()
{
  g_strip.begin();
  uint32_t c = 0;
  ledSetColor(c);
}

unsigned long g_lastCommand = 0;

bool handleLightInterest(ndn::SimpleProducer::Context& ctx, const ndn::InterestLite& interest)
{
  if (interest.getName().size() != 4 ||
      interest.getName().get(1).getValue().size() != 3 ||
      !ctx.face.verifyInterest(g_ctrlPub)) {
    return false;
  }

  uint32_t c = 0;
  memcpy(&c, interest.getName().get(1).getValue().buf(), 3);
  ctx.endpointId = 0;

  ndn::DataWCB<4, 0> data;
  data.setName(interest.getName());
  ctx.sendData(data);
  delay(1);

  g_lastCommand = millis();
  ledSetColor(c);
  return true;
}

ndn::NameWCB<1> g_lightPrefix;
ndn::SimpleProducer lightProducer(g_face, g_lightPrefix, &handleLightInterest);

void sleepIfIdle()
{
  static unsigned long idleSince = 0;
  if (WiFi.softAPgetStationNum() > 0) {
    idleSince = 0;
    digitalWrite(0, HIGH);
    return;
  }
  unsigned long now = millis();
  if (idleSince == 0) {
    idleSince = now;
    digitalWrite(0, LOW);
  }
  if (now - idleSince > 10000) {
    Serial.println(F("deep-sleep"));
    digitalWrite(0, HIGH);
    digitalWrite(2, HIGH);
    ESP.deepSleep(50000000);
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println();
  ndn::setLogOutput(Serial);

  WiFi.persistent(false);
  WiFi.mode(WIFI_AP);
  WiFi.softAP("ring");
  delay(1000);

  if (!g_transport.beginMulticast()) {
    ESP.restart();
  }

  g_face.enableTracing(Serial);
  g_face.setSigningKey(g_ringPri);

  g_lightPrefix.append("light");
  g_ctrlPub.import(CTRL_PUBKEY);

  ledSetup();
  Serial.println(F("ready"));

  pinMode(0, OUTPUT);
  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);
}

void loop()
{
  g_face.loop();
  if (g_lastCommand + 15000 < millis()) {
    ledSetColor(0x000000);
    sleepIfIdle();
  }
}

Button unit sketch: LightCtrl.ino

#include <esp8266ndn.h>
#include <ESP8266WiFi.h>

const uint8_t CTRL_PVTKEY[] PROGMEM  {
  0x7D, 0x4C, 0x3A, 0x33, 0xDB, 0x0E, 0x4A, 0x50, 0xB4, 0xE1, 0x73, 0xF3, 0x9C, 0xB2, 0xE3, 0x06,
  0x83, 0xA2, 0x9F, 0xF1, 0x78, 0xBB, 0xE2, 0x49, 0x96, 0x51, 0x55, 0x4B, 0xB4, 0x36, 0x09, 0xEA,
};
ndn::NameWCB<1> g_ctrlKeyName;
ndn::EcPrivateKey g_ctrlPri(g_ctrlKeyName);
ndn::UdpTransport g_transport;
ndn::Face g_face(g_transport);

ndn::InterestWCB<4, 0> g_lightInterest;
ndn::SimpleConsumer g_lightConsumer(g_face, g_lightInterest, 2000);

void ledSetColor(uint32_t c)
{
  analogWrite(13, (c >> 0) & 0xff);
  analogWrite(12, (c >> 8) & 0xff);
  analogWrite(15, (c >>16) & 0xff);
}

void ledSetup()
{
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(15, OUTPUT);
  analogWriteRange(255);
  ledSetColor(0x000000);
}

void update()
{
  uint32_t c = random(0xffffff);
  Serial.print(F("set-color "));
  Serial.println(c, HEX);

  ndn::NameLite& name = g_lightConsumer.interest.getName();
  name.clear();
  name.append("light");
  name.append(reinterpret_cast<const uint8_t*>(&c), 3);

  digitalWrite(2, LOW);
  g_lightConsumer.sendSignedInterest();
  auto result = g_lightConsumer.waitForResult();
  if (result == ndn::SimpleConsumer::Result::DATA) {
    ledSetColor(c);
  }
  digitalWrite(2, HIGH);
}

void setup()
{
  Serial.begin(115200);
  Serial.println();
  ndn::setLogOutput(Serial);

  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin("ring");
  int timeout = 75;
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    if (--timeout <= 0) {
      Serial.println(F("WiFi timeout"));
      ESP.restart();
    }
  }
  Serial.println(WiFi.localIP());

  if (!g_transport.beginMulticast()) {
    ESP.restart();
  }

  g_face.enableTracing(Serial);
  g_ctrlKeyName.append("ctrl");
  g_ctrlPri.import(CTRL_PVTKEY);
  g_face.setSigningKey(g_ctrlPri);

  Serial.println(F("ready"));

  pinMode(4, INPUT);
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  ledSetup();
}

void loop()
{
  g_face.loop();

  if (digitalRead(4) == 0) {
    update();
    while (digitalRead(4) == 0)
      ;
  }
}

Tags: ESP8266 NDN