226 lines
5.3 KiB
C++
226 lines
5.3 KiB
C++
/*
|
|
* Stairs lighting
|
|
* Copyright 2017 (c) Mark van Renswoude
|
|
*
|
|
* https://git.x2software.net/pub/Stairs
|
|
*/
|
|
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
#include "global.h"
|
|
#include "config.h"
|
|
#include "display.h"
|
|
#include "protocol.h"
|
|
|
|
|
|
void setup()
|
|
{
|
|
settings.init();
|
|
|
|
// Set up I2C devices: the SSD1306 OLED display and PCA9685 LED PWM driver
|
|
Wire.begin();
|
|
display.init();
|
|
|
|
pwmDriver.begin();
|
|
pwmDriver.setPWMFreq(PWMDriverFrequency);
|
|
|
|
// At 16 Mhz we pick a baud rate with a very acceptable 0.2% error rate
|
|
// Source: http://www.robotroom.com/Asynchronous-Serial-Communication-2.html
|
|
Serial.begin(CommBaudRate);
|
|
pinMode(CommWriteEnablePin, OUTPUT);
|
|
comm.begin();
|
|
|
|
pinMode(PinButton, INPUT_PULLUP);
|
|
|
|
pinMode(5, OUTPUT);
|
|
}
|
|
|
|
|
|
|
|
// Forward declarations
|
|
void handleCommMessage();
|
|
void handlePing(uint8_t* data, uint8_t length);
|
|
void handleDisplayModuleIndex(uint8_t* data, uint8_t length);
|
|
void handleStartLink(uint8_t* data, uint8_t length);
|
|
void handleRequestLinkResponse(uint8_t* data, uint8_t length);
|
|
void handleStopLink(uint8_t* data, uint8_t length);
|
|
void handleSetPWM(uint8_t* data, uint8_t length);
|
|
void handleGetSensors(uint8_t* data, uint8_t length);
|
|
|
|
void checkButtonPress();
|
|
|
|
|
|
void loop()
|
|
{
|
|
currentTime = millis();
|
|
|
|
if (comm.update())
|
|
handleCommMessage();
|
|
|
|
checkButtonPress();
|
|
display.update();
|
|
}
|
|
|
|
|
|
|
|
void sendCommMessage(const uint8_t* data, uint8_t size)
|
|
{
|
|
digitalWrite(CommWriteEnablePin, HIGH);
|
|
comm.sendMsg(data, size);
|
|
|
|
|
|
// Wait for the hardware buffer to clear before turning
|
|
// off the write enable pin, or we'll cut off the message too early
|
|
// Straight from: http://www.gammon.com.au/forum/?id=11428
|
|
|
|
while (!(UCSR0A & (1 << UDRE0))) // Wait for empty transmit buffer
|
|
UCSR0A |= 1 << TXC0; // mark transmission not complete
|
|
|
|
while (!(UCSR0A & (1 << TXC0))); // Wait for the transmission to complete
|
|
|
|
|
|
digitalWrite(CommWriteEnablePin, LOW);
|
|
}
|
|
|
|
|
|
void handleCommMessage()
|
|
{
|
|
uint8_t* data = comm.getData();
|
|
uint8_t length = comm.getLength();
|
|
|
|
if (length == 0)
|
|
return;
|
|
|
|
if (state == State::WaitingForComm)
|
|
state = State::DisplayModuleIndex;
|
|
|
|
uint8_t command = *data; data++; length--;
|
|
uint8_t moduleIndex = ModuleIndexUndefined;
|
|
|
|
if ((command & MaskMessageType) == MaskModuleCommand)
|
|
{
|
|
if (!settings.hasModuleIndex())
|
|
// We're not linked yet
|
|
return;
|
|
|
|
moduleIndex = *data; data++; length--;
|
|
|
|
if (settings.getModuleIndex() != moduleIndex)
|
|
// This message is meant for another module
|
|
return;
|
|
}
|
|
|
|
switch (command)
|
|
{
|
|
case CommandPing: handlePing(data, length); break;
|
|
case CommandDisplayModuleIndex: handleDisplayModuleIndex(data, length); break;
|
|
case CommandStartLink: handleStartLink(data, length); break;
|
|
case ResponseRequestLink: handleRequestLinkResponse(data, length); break;
|
|
case CommandStopLink: handleStopLink(data, length); break;
|
|
case CommandSetPWM: handleSetPWM(data, length); break;
|
|
case CommandGetSensors: handleGetSensors(data, length); break;
|
|
|
|
default:
|
|
if ((command & MaskMessageType) == MaskModuleCommand)
|
|
{
|
|
// Sender expects a response from us
|
|
const uint8_t msg[] = { ResponseUhmWhat, moduleIndex };
|
|
sendCommMessage(msg, sizeof(msg));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void handlePing(uint8_t* data, uint8_t length)
|
|
{
|
|
const uint8_t msg[] = { ResponsePing, settings.getModuleIndex() };
|
|
sendCommMessage(msg, sizeof(msg));
|
|
}
|
|
|
|
|
|
void handleDisplayModuleIndex(uint8_t* data, uint8_t length)
|
|
{
|
|
if (state == State::DisplayOff)
|
|
state = State::DisplayModuleIndex;
|
|
}
|
|
|
|
|
|
void handleStartLink(uint8_t* data, uint8_t length)
|
|
{
|
|
state = State::Linking;
|
|
}
|
|
|
|
|
|
void handleRequestLinkResponse(uint8_t* data, uint8_t length)
|
|
{
|
|
if (length == 0)
|
|
return;
|
|
|
|
if (state != State::LinkingRequest)
|
|
return;
|
|
|
|
settings.setModuleIndex(*data);
|
|
state = State::LinkingSet;
|
|
}
|
|
|
|
|
|
void handleStopLink(uint8_t* data, uint8_t length)
|
|
{
|
|
if (state == State::Linking || state == State::LinkingRequest || state == State::LinkingSet)
|
|
state = State::DisplayModuleIndex;
|
|
}
|
|
|
|
|
|
void handleSetPWM(uint8_t* data, uint8_t length)
|
|
{
|
|
if (length < 5)
|
|
return;
|
|
|
|
uint8_t flags = *data; data++;
|
|
uint16_t value1 = *(reinterpret_cast<uint16_t*>(data)); data += 2;
|
|
uint16_t value2 = *(reinterpret_cast<uint16_t*>(data)); data += 2;
|
|
|
|
pwmDriver.setPin(0, value1);
|
|
pwmDriver.setPin(1, value2);
|
|
|
|
if ((flags & SetPWMFlagModuleLEDs) == SetPWMFlagModuleLEDs)
|
|
{
|
|
pwmDriver.setPin(2, value1);
|
|
pwmDriver.setPin(3, value2);
|
|
}
|
|
else
|
|
{
|
|
pwmDriver.setPin(2, 0);
|
|
pwmDriver.setPin(3, 0);
|
|
}
|
|
|
|
const uint8_t msg[] = { ResponseSetPWM, settings.getModuleIndex() };
|
|
sendCommMessage(msg, sizeof(msg));
|
|
}
|
|
|
|
|
|
void handleGetSensors(uint8_t* data, uint8_t length)
|
|
{
|
|
uint16_t value1 = analogRead(PinSensor1);
|
|
uint16_t value2 = analogRead(PinSensor2);
|
|
|
|
const uint8_t msg[] = { ResponseGetSensors, settings.getModuleIndex(), lowByte(value1), highByte(value1), lowByte(value2), highByte(value2) };
|
|
sendCommMessage(msg, sizeof(msg));
|
|
}
|
|
|
|
|
|
uint32_t lastButtonPress = 0;
|
|
|
|
void checkButtonPress()
|
|
{
|
|
if ((state == State::Linking || state == State::LinkingRequest) &&
|
|
digitalRead(PinButton) == HIGH &&
|
|
currentTime - lastButtonPress >= ButtonDebounceTime)
|
|
{
|
|
state = State::LinkingRequest;
|
|
|
|
const uint8_t msg[] = { CommandRequestLink };
|
|
sendCommMessage(msg, sizeof(msg));
|
|
|
|
lastButtonPress = currentTime;
|
|
}
|
|
} |