Skip to content

Commit

Permalink
Implement Wifi setup (issue #6)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxbbraun committed May 4, 2019
1 parent 0d25763 commit 7cd77d6
Show file tree
Hide file tree
Showing 10 changed files with 3,804 additions and 102 deletions.
49 changes: 36 additions & 13 deletions client/Client.ino
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include <Arduino.h>
#include "Display.h"
#include "ErrorImage.h"
#include "Network.h"
#include "Power.h"

Expand All @@ -12,8 +11,17 @@
// The baud rate for the serial connection.
const long kSerialSpeed = 115200;

// The GPIO pin used to reset Wifi settings.
const uint8_t kWifiResetPin = 23;

// The base URL for server requests.
const String kServerBaseUrl = "https://accent.ink/";
const String kBaseUrl = "https://accent.ink";

// The URL for the next wake time endpoint.
const String kNextEndpoint = kBaseUrl + "/next";

// The URL for the e-paper display image endpoint.
const String kEpdEndpoint = kBaseUrl + "/epd";

// The size in bytes of the streaming HTTP response and image buffers.
const size_t kStreamBufferSize = 1024;
Expand All @@ -29,25 +37,40 @@ Power power;
void setup() {
Serial.begin(kSerialSpeed);

// Connect to the Wifi access point.
network.connectWifi();
// Check if the Wifi reset pin has been connected to GND.
pinMode(kWifiResetPin, INPUT_PULLUP);
delay(1); // Wait for pull-up to become active.
if (digitalRead(kWifiResetPin) == LOW) {
network.ResetWifi();
}

// Connect to Wifi or start the setup flow.
if (!network.ConnectWifi()) {
display.ShowWifiSetup();
network.StartWifiSetupServer();
return;
}

// Show the latest image.
display.initialize();
display.Initialize();
if (downloadImage()) {
display.update();
display.Update();
}

// Go to sleep until the next refresh.
scheduleSleep();
}

void loop() {
// The setup() function only returns if there was an error.
display.showStatic(kErrorImage, sizeof(kErrorImage));
if (network.HandleWifiSetupServer()) {
// Continue to loop.
return;
}

// Falling through means there was an error.
Serial.println("Restarting after error");
power.deepSleep(kRestartDelayMs);
display.ShowError();
power.DeepSleep(kRestartDelayMs);
}

// Streams the image from the server and sends it to the display in chunks.
Expand All @@ -56,7 +79,7 @@ bool downloadImage() {
HTTPClient http;

// Request the current image from the server.
if (!network.httpGet(&http, kServerBaseUrl + "epd")) {
if (!network.HttpGet(&http, kEpdEndpoint)) {
return false;
}

Expand All @@ -79,7 +102,7 @@ bool downloadImage() {
Serial.printf("Read %d bytes (%lu total)\n", count, total_count);

// Send the buffer to the display.
display.load(buffer, count);
display.Load(buffer, count);
} while (stream->available() > 0);

Serial.println("Download complete");
Expand All @@ -93,7 +116,7 @@ void scheduleSleep() {
HTTPClient http;

// Request the next wake time from the server.
if (!network.httpGet(&http, kServerBaseUrl + "next")) {
if (!network.HttpGet(&http, kNextEndpoint)) {
return;
}

Expand All @@ -102,5 +125,5 @@ void scheduleSleep() {
http.end();
Serial.printf("Sleep server response: %s\n", delay_ms_str.c_str());
uint64_t delay_ms = strtoull(delay_ms_str.c_str(), nullptr, 10);
power.deepSleep(delay_ms);
power.DeepSleep(delay_ms);
}
115 changes: 61 additions & 54 deletions client/Display.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "Display.h"

#include "ErrorImage.h"
#include "WifiImage.h"

// The SPI pin definitions.
#define PIN_SPI_SCK 13
#define PIN_SPI_DIN 14
Expand All @@ -11,7 +14,7 @@
// The length in bytes per chunk when sending a static image.
const size_t kStaticImageChunkLength = 1024;

void Display::initialize() {
void Display::Initialize() {
Serial.println("Initializing display");

// Initialize SPI.
Expand All @@ -25,96 +28,83 @@ void Display::initialize() {
digitalWrite(PIN_SPI_SCK, LOW);

// Initialize the display.
reset();
sendCommandArgs(0x01, "\x37\x00"); // POWER_SETTING
sendCommandArgs(0x00, "\xCF\x08"); // PANEL_SETTING
sendCommandArgs(0x06, "\xC7\xCC\x28"); // BOOSTER_SOFT_START
sendCommand(0x4); // POWER_ON
waitForIdle();
sendCommandArgs(0x30, "\x3C"); // PLL_CONTROL
sendCommandArgs(0x41, "\x00"); // TEMPERATURE_CALIBRATION
sendCommandArgs(0x50, "\x77"); // VCOM_AND_DATA_INTERVAL_SETTING
sendCommandArgs(0x60, "\x22"); // TCON_SETTING
sendCommandArgs(0x61, "\x02\x80\x01\x80"); // TCON_RESOLUTION
sendCommandArgs(0x82, "\x1E"); // VCM_DC_SETTING
sendCommandArgs(0xE5, "\x03"); // FLASH MODE
sendCommand(0x10); // DATA_START_TRANSMISSION_1
Reset();
SendCommandArgs(0x01, "\x37\x00"); // POWER_SETTING
SendCommandArgs(0x00, "\xCF\x08"); // PANEL_SETTING
SendCommandArgs(0x06, "\xC7\xCC\x28"); // BOOSTER_SOFT_START
SendCommand(0x4); // POWER_ON
WaitForIdle();
SendCommandArgs(0x30, "\x3C"); // PLL_CONTROL
SendCommandArgs(0x41, "\x00"); // TEMPERATURE_CALIBRATION
SendCommandArgs(0x50, "\x77"); // VCOM_AND_DATA_INTERVAL_SETTING
SendCommandArgs(0x60, "\x22"); // TCON_SETTING
SendCommandArgs(0x61, "\x02\x80\x01\x80"); // TCON_RESOLUTION
SendCommandArgs(0x82, "\x1E"); // VCM_DC_SETTING
SendCommandArgs(0xE5, "\x03"); // FLASH MODE
SendCommand(0x10); // DATA_START_TRANSMISSION_1
delay(2);
}

void Display::load(const char* image_data, size_t length) {
void Display::Load(const char* image_data, size_t length) {
Serial.printf("Loading image data: %d bytes\n", length);

// Look at the image data one byte at a time, which is 4 input pixels.
for (int i = 0; i < length; i++) {
// 4 input pixels.
const char p1 = convertPixel(image_data[i], 0xC0, 6);
const char p2 = convertPixel(image_data[i], 0x30, 4);
const char p3 = convertPixel(image_data[i], 0x0C, 2);
const char p4 = convertPixel(image_data[i], 0x03, 0);
const char p1 = ConvertPixel(image_data[i], 0xC0, 6);
const char p2 = ConvertPixel(image_data[i], 0x30, 4);
const char p3 = ConvertPixel(image_data[i], 0x0C, 2);
const char p4 = ConvertPixel(image_data[i], 0x03, 0);

// 2 output pixels.
sendData((p1 << 4) | p2);
sendData((p3 << 4) | p4);
SendData((p1 << 4) | p2);
SendData((p3 << 4) | p4);
}
}

void Display::update() {
void Display::Update() {
// Refresh.
Serial.println("Refreshing image");
sendCommand(0x12); // DISPLAY_REFRESH
SendCommand(0x12); // DISPLAY_REFRESH
delay(100);
waitForIdle();
WaitForIdle();

// Sleep.
Serial.println("Suspending display");
sendCommand(0x02); // POWER_OFF
waitForIdle();
sendCommandArgs(0x07, "\xA5"); // DEEP_SLEEP
SendCommand(0x02); // POWER_OFF
WaitForIdle();
SendCommandArgs(0x07, "\xA5"); // DEEP_SLEEP
}

void Display::showStatic(const char* image_data, unsigned long length) {
Serial.println("Showing static image");
void Display::ShowError() { ShowStatic(kErrorImage, sizeof(kErrorImage)); }

initialize();
void Display::ShowWifiSetup() { ShowStatic(kWifiImage, sizeof(kWifiImage)); }

const char* image_ptr = image_data;
const char* image_end = image_ptr + length - 1 /* null terminator */;
do {
size_t chunk_length = min(kStaticImageChunkLength,
static_cast<size_t>(image_end - image_ptr));
load(image_ptr, chunk_length);
image_ptr += chunk_length;
} while (image_ptr < image_end);

update();
}

void Display::reset() {
void Display::Reset() {
digitalWrite(PIN_SPI_RST, LOW);
delay(200);
digitalWrite(PIN_SPI_RST, HIGH);
delay(200);
}

void Display::sendCommandArgs(char command, const char* args...) {
sendCommand(command);
void Display::SendCommandArgs(char command, const char* args...) {
SendCommand(command);
for (; *args != '\0'; ++args) {
sendData(*args);
SendData(*args);
}
}

void Display::sendCommand(char command) {
void Display::SendCommand(char command) {
digitalWrite(PIN_SPI_DC, LOW);
sendSpi(command);
SendSpi(command);
}

void Display::sendData(char data) {
void Display::SendData(char data) {
digitalWrite(PIN_SPI_DC, HIGH);
sendSpi(data);
SendSpi(data);
}

void Display::sendSpi(char data) {
void Display::SendSpi(char data) {
digitalWrite(PIN_SPI_CS, LOW);
for (int i = 0; i < 8; ++i) {
if ((data & 0x80) == 0) {
Expand All @@ -129,13 +119,13 @@ void Display::sendSpi(char data) {
digitalWrite(PIN_SPI_CS, HIGH);
}

void Display::waitForIdle() {
void Display::WaitForIdle() {
while (digitalRead(PIN_SPI_BUSY) == LOW /* busy */) {
delay(100);
}
}

char Display::convertPixel(char input, char mask, int shift) {
char Display::ConvertPixel(char input, char mask, int shift) {
const char value = (input & mask) >> shift;
switch (value) {
case 0x0:
Expand All @@ -152,3 +142,20 @@ char Display::convertPixel(char input, char mask, int shift) {
return 0x0;
}
}

void Display::ShowStatic(const char* image_data, unsigned long length) {
Serial.println("Showing static image");

Initialize();

const char* image_ptr = image_data;
const char* image_end = image_ptr + length - 1 /* null terminator */;
do {
size_t chunk_length = min(kStaticImageChunkLength,
static_cast<size_t>(image_end - image_ptr));
Load(image_ptr, chunk_length);
image_ptr += chunk_length;
} while (image_ptr < image_end);

Update();
}
30 changes: 18 additions & 12 deletions client/Display.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,44 @@
class Display {
public:
// Initializes the display for the next update.
void initialize();
void Initialize();

// Loads partial image data onto the display.
void load(const char* image_data, size_t length);
void Load(const char* image_data, size_t length);

// Shows the loaded image and sends the display to sleep.
void update();
void Update();

// Initializes, loads, and shows a static image.
void showStatic(const char* image, unsigned long length);
// Shows the error image.
void ShowError();

// Shows the Wifi setup image.
void ShowWifiSetup();

private:
// Wakes up the display from sleep.
void reset();
void Reset();

// Sends a command with arguments.
void sendCommandArgs(char command, const char* args...);
void SendCommandArgs(char command, const char* args...);

// Sends one byte as a command.
void sendCommand(char command);
void SendCommand(char command);

// Sends one byte as data.
void sendData(char data);
void SendData(char data);

// Sends one byte over SPI.
void sendSpi(char data);
void SendSpi(char data);

// Waits until the display is ready.
void waitForIdle();
void WaitForIdle();

// Converts one pixel from 2-bit input encoding to 4-bit output encoding.
char convertPixel(char input, char mask, int shift);
char ConvertPixel(char input, char mask, int shift);

// Initializes, loads, and shows a static image.
void ShowStatic(const char* image, unsigned long length);
};

#endif // DISPLAY_H
Loading

0 comments on commit 7cd77d6

Please sign in to comment.