Files
seatalk-autopilot-remote-co…/AP-Remote-Software/ArduinoPilotNano433WindNGSoftSerial/ArduinoPilotNano433WindNGSoftSerial.ino

441 lines
14 KiB
C++

/*
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// Based on the AK-Homberger work
// By Laurent D.
// Version 1.0
#include <avr/pgmspace.h>
#include <SPI.h>
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <RCSwitch.h> // May impact the display ....
#include <SoftwareSerial.h>
/* PINNOUT */
// Definition Value // ATMEGA HW PIN // GPIO // Role
// ================================== // =============== // ==== // =========
// Hardware SERIAL RX // ATMEGA328 #2 // // SeaTalk
// Hardware SERIAL TX // ATMEGA328 #3 // // SeaTalk
#define PIN_SOFTSERIAL_RX 3 // ATMEGA328 #5 // 3 // Debug Serial RW
#define PIN_SOFTSERIAL_TX 4 // ATMEGA328 #6 // 4 // Debug Serial TX
#define PIN_RF_433 0 // ATMEGA328 #4 // 2 // Interrupt 0 , RF433 Input Data
#define PIN_LED 6 // ATMEGA328 #12 // 6 // LED PIN
#define PIN_BUZZ 7 // ATMEGA328 #13 // 7 // Buzzer PIN
// SSD1306 SCL // ATMEGA328 #28 // // oled SSD1306 SCL Pin
// SSD1306 SDA // ATMEGA328 #27 // // oled SSD1306 SDA Pin
/* OTHER CONFIGS */
#define I2C_ADDRESS 0x3C // Addresses can be 0x3D OR 0x3C depending of manufacturer
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define SSD1306_RST_PIN -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define Auto_Standby_Support 1 // Set this to 1 to support Standby and Auto for Key 5 and 6
#define KEY_DELAY 300 // 300 ms break between keys
#define BEEP_DURATION 150 // 150 ms beep time
#define DEBUG
String inputString = ""; // a String to hold incoming data
bool stringComplete = false; // whether the string is complete
const unsigned long Key_Minus_1 PROGMEM = 13721111; // Change value_rfs to individual value_rfs programmed to remote control
const unsigned long Key_Plus_1 PROGMEM = 13722222;
const unsigned long Key_Minus_10 PROGMEM = 13723333;
const unsigned long Key_Plus_10 PROGMEM = 13724444;
const unsigned long Key_Auto PROGMEM = 13725555;
const unsigned long Key_Standby PROGMEM = 13726666;
const unsigned long Key_Lamp0 PROGMEM = 13727777;
const unsigned long Key_Lamp1 PROGMEM = 13728888;
const unsigned long Key_Lamp2 PROGMEM = 13729999;
const unsigned long Key_Lamp3 PROGMEM = 13720000;
// Seatalk datagrams
const PROGMEM uint16_t ST_NMEA_BridgeID[] = { 0x190, 0x00, 0xA3 };
const PROGMEM uint16_t ST_Minus_1[] = { 0x186, 0x21, 0x05, 0xFA };
const PROGMEM uint16_t ST_Minus_10[] = { 0x186, 0x21, 0x06, 0xF9 };
const PROGMEM uint16_t ST_Plus_1[] = { 0x186, 0x21, 0x07, 0xF8 };
const PROGMEM uint16_t ST_Plus_10[] = { 0x186, 0x21, 0x08, 0xF7 };
const PROGMEM uint16_t ST_Auto[] = { 0x186, 0x21, 0x01, 0xFE };
const PROGMEM uint16_t ST_Standby[] = { 0x186, 0x21, 0x02, 0xFD };
const PROGMEM uint16_t ST_BeepOn[] = { 0x1A8, 0x53, 0x80, 0x00, 0x00, 0xD3 };
const PROGMEM uint16_t ST_BeepOff[] = { 0x1A8, 0x43, 0x80, 0x00, 0x00, 0xC3 };
const PROGMEM uint16_t ST_Lamp0[] = { 0x130, 0x00, 0x00 };
const PROGMEM uint16_t ST_Lamp1[] = { 0x130, 0x00, 0x04 };
const PROGMEM uint16_t ST_Lamp2[] = { 0x130, 0x00, 0x08 };
const PROGMEM uint16_t ST_Lamp3[] = { 0x130, 0x00, 0x0C };
boolean blink = true;
unsigned long wind_timer = 0; // timer for AWS display
unsigned long beep_timer2 = 0; // timer to stop alarm sound
unsigned long bridge_timer = 0; // timer to send ST Bridge ID every 10 seconds
unsigned long key_time = 0; // time of last key detected
unsigned long beep_time = 0; // timer for beep duration
bool beep_status = false;
uint32_t value_rf = 0;
char AWS[4];
RCSwitch mySwitch = RCSwitch();
SSD1306AsciiWire oled;
SoftwareSerial SerialSoft(PIN_SOFTSERIAL_RX, PIN_SOFTSERIAL_TX); // RX, TX
boolean sendDatagram(const uint16_t data[]) {
int i = 0; int j = 0;
boolean ok = true;
int bytes;
unsigned int inbyte;
unsigned int outbyte;
bytes = (pgm_read_byte_near(data + 1) & 0x0f) + 3; // Messege length is minimum 3, additional bytes in nibble 4
while (j < 5 ) { // CDMA/CD 5 tries
while (Serial.available ()) { // Wait for silence on the bus
inbyte = (Serial.read());
delay(3);
}
ok = true;
for (i = 0; (i < bytes) & (ok == true); i++) { // Write and listen to detect collisions
outbyte = pgm_read_word_near(data + i);
Serial.write(outbyte);
delay(3);
if (Serial.available ()) {
inbyte = Serial.read(); // Not what we sent, collision!
if (inbyte != outbyte) {
ok = false;
// SerialSoft.println("ST - I<>O");
}
}
else {
ok = false; // Nothing received
// SerialSoft.println("ST - Nothing in");
}
}
if ( ok )return ok;
j++; // Collision detected
#ifdef DEBUG
SerialSoft.println("Collision");
// Display("Collision");
#endif
delay(random(2, 50)); // Random wait for next try
}
Display("Send Error");
return false;
}
void Display(char *s) {
size_t tsize;
char dsp[12];
oled.clear();
oled.setFont(Cooper19);
tsize = oled.strWidth(s);
oled.setCursor((oled.displayWidth()-tsize)/2, 0);
oled.println(s);
snprintf(dsp, sizeof dsp, "AWS: %s", AWS);
tsize = oled.strWidth(dsp);
oled.setCursor((oled.displayWidth()-tsize)/2, 20);
oled.println(dsp);
oled.setFont(X11fixed7x14);
// ultoa(value_rf, dsp, 8);
snprintf(dsp, sizeof dsp, "%" PRIu32, value_rf);
tsize = oled.strWidth(dsp);
oled.setCursor((oled.displayWidth()-tsize)/2, 40);
oled.println(dsp);
}
void printByte(unsigned int data) {
SerialSoft.print("byte : ");
SerialSoft.print("0x");
uint8_t MSB=byte(data>>8);
uint8_t LSB=byte(data);
if (MSB<0x10) {SerialSoft.print("0");} SerialSoft.print(MSB,HEX); SerialSoft.print(" ");
if (LSB<0x10) {SerialSoft.print("0");} SerialSoft.print(LSB,HEX); SerialSoft.print(" ");
SerialSoft.println("");
}
// Receive apparent wind speed from bus
int checkWind(char * AWS) {
unsigned int xx;
unsigned int y;
unsigned int inbyte;
int wind = -1;
if (Serial.available ()) {
inbyte = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
if (inbyte == 0x111) { // AWS Seatalk command - See reference from Thomas Knauf
delay(3);
inbyte = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
if (inbyte == 0x01) { // AWS Setalk command
delay(3);
xx = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
delay(3);
y = Serial.read();
#ifdef DEBUG
printByte(inbyte);
#endif
wind = (xx & 0x7f) + (y / 10); // Wind speed
if (wind < 100) itoa (wind , AWS, 10); // Greater 100 must be a receive error
}
}
}
return wind;
}
void setup() {
SerialSoft.begin( 9600 ); // Serial out put for function checks with PC
Serial.begin( 4800, SERIAL_9N1 ); // Set the Seatalk modus - 9 bit
Serial.setTimeout(5);
#ifdef DEBUG
SerialSoft.println(F("Setup Begin"));
#endif
// reserve 20 bytes for the inputString:
inputString.reserve(20);
pinMode(PIN_LED, OUTPUT); // LED to show if keys are received
digitalWrite(PIN_LED, HIGH);
pinMode(PIN_BUZZ, OUTPUT); // Buzzer to show if keys are received
digitalWrite(PIN_BUZZ, LOW);
Wire.begin();
Wire.setClock(400000L);
#if SSD1306_RST_PIN >= 0
oled.begin(&Adafruit128x64, I2C_ADDRESS, SSD1306_RST_PIN);
#else // SSD1306_RST_PIN >= 0
oled.begin(&Adafruit128x64, I2C_ADDRESS);
#endif // SSD1306_RST_PIN >= 0
oled.setFont(Cooper19);
oled.clear();
oled.println("Starting");
mySwitch.enableReceive(PIN_RF_433); // RF Receiver on inerrupt 4 => that is pin 7 on Micro OR pin2 on Nano
sendDatagram(ST_NMEA_BridgeID); // Send NMEA Seatakl BridgeID to make Seatalk to Seatalk NG converter happy
#ifdef DEBUG
SerialSoft.println(F("Setup Complete"));
#endif
Display("Started");
}
// Beep on if key received
void BeepOn(void) {
if (beep_status == true) return; // Already On
sendDatagram(ST_BeepOn);
digitalWrite(PIN_BUZZ, HIGH);
//SerialSoft.println("On");
beep_time = millis();
beep_status = true;
}
// Beep off after BEEP_TIME
void BeepOff(void) {
if (beep_status == true && millis() > beep_time + BEEP_DURATION) {
sendDatagram(ST_BeepOff);
digitalWrite(PIN_BUZZ, LOW);
//SerialSoft.println("Off");
beep_status = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the hardware serial RX. This
routine is run between each time loop() runs, so using delay inside loop can
delay response. Multiple bytes of data may be available.
*/
void serialEvent() { // NOT WORKING IN SOFT SERIAL
while (SerialSoft.available()) {
// get the new byte:
SerialSoft.println("received from serial");
char inChar = (char)SerialSoft.read();
// add it to the inputString:
if ((inChar != '\n') && (inChar != '\r')) inputString += inChar;
// if the incoming character is a newline or CR, set a flag so the main loop can react
if ((inChar == '\n') || (inChar == '\r')) {
stringComplete = true;
}
}
}
void loop() {
AWS[0] = '\0';
// uint32_t value_rf = 0;
value_rf = 0;
if (millis() > wind_timer + 2000 ) {
// Display("No Wdata"); // Show --- after about two seconds when no wind data is received
wind_timer = millis();
}
if (millis() > beep_timer2 + 3000 ) {
sendDatagram(ST_BeepOff); // Additional Beep off after three seconds to avoid constant alarm
beep_timer2 = millis();
}
if (millis() > bridge_timer + 10000 ) {
#ifdef DEBUG
SerialSoft.println("Send Bridge ID");
#endif
sendDatagram(ST_NMEA_BridgeID); // Send NMEA Seatakl BridgeID every 10 seconds to make Seatalk to Seatalk NG converter happy
bridge_timer = millis();
}
if (checkWind(AWS) > -1) {
Display(AWS);
wind_timer = millis();
}
if (mySwitch.available()) {
value_rf = mySwitch.getReceivedValue();
mySwitch.resetAvailable();
}
serialEvent(); // Read serial to detect command from USB (until Newline or CR)
if (stringComplete) {
// Compare string received from SerialSoft
if(inputString == "-1" ) value_rf = Key_Minus_1;
if(inputString == "+1" ) value_rf = Key_Plus_1;
if(inputString == "-10") value_rf = Key_Minus_10;
if(inputString == "+10") value_rf = Key_Plus_10;
if(inputString == "A" ) value_rf = Key_Auto;
if(inputString == "S" ) value_rf = Key_Standby;
// clear the string and reset status
inputString = "";
stringComplete = false;
}
if (value_rf > 0 && millis() > key_time + KEY_DELAY) {
#ifdef DEBUG
oled.clear();
oled.println("Value : ");
oled.println(value_rf);
SerialSoft.print(F("rf_code ")); SerialSoft.println(value_rf);
#endif
key_time = millis(); // Remember time of last key received
digitalWrite(PIN_LED, blink); // LED on/off
blink = !blink; // Toggle LED to show received key
switch(value_rf) {
case Key_Minus_1:
Display("min1");
sendDatagram(ST_Minus_1);
BeepOn();
break;
case Key_Plus_1:
Display("plus1");
sendDatagram(ST_Plus_1);
BeepOn();
break;
case Key_Minus_10:
Display("min10");
sendDatagram(ST_Minus_10);
BeepOn();
break;
case Key_Plus_10:
Display("plus10");
sendDatagram(ST_Plus_10);
BeepOn();
break;
case Key_Auto:
if(Auto_Standby_Support == 1) {
Display("Auto");
sendDatagram(ST_Auto);
BeepOn();
}
break;
case Key_Standby:
if(Auto_Standby_Support == 1) {
Display("Stby");
sendDatagram(ST_Standby);
BeepOn();
}
break;
case Key_Lamp0:
Display("Lamp 0");
sendDatagram(ST_Lamp0);
BeepOn();
break;
case Key_Lamp1:
Display("Lamp 1");
sendDatagram(ST_Lamp1);
BeepOn();
break;
case Key_Lamp2:
Display("Lamp 2");
sendDatagram(ST_Lamp2);
BeepOn();
break;
case Key_Lamp3:
Display("Lamp 3");
sendDatagram(ST_Lamp3);
BeepOn();
break;
case 101:
Display("Beep ON");
sendDatagram(ST_BeepOn);
BeepOn();
break;
case 102:
Display("Beep OFF");
sendDatagram(ST_BeepOff);
BeepOn();
break;
}
}
BeepOff();
}