diff --git a/platformio.ini b/platformio.ini index 87eb0afb7..a39de33c1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,7 +2,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = tbeam +;default_envs = tbeam ;default_envs = pico ;default_envs = tbeam-s3-core ;default_envs = tbeam0.7 @@ -29,6 +29,7 @@ default_envs = tbeam ;default_envs = meshtastic-dr-dev ;default_envs = m5stack-coreink ;default_envs = rak4631 +default_envs = rak4631_mqtt_json ;default_envs = rak2560 ;default_envs = rak10701 ;default_envs = wio-e5 diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index d14c7a923..63bdd5bba 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -379,13 +379,13 @@ void MQTT::sendSubscriptions() std::string topic = cryptTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? -#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 +// #ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJSON ### if (moduleConfig.mqtt.json_enabled == true) { std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+"; LOG_INFO("Subscribing to %s\n", topicDecoded.c_str()); pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right? } -#endif // ARCH_NRF52 +// #endif // ARCH_NRF52 } } #if !MESHTASTIC_EXCLUDE_PKI @@ -480,7 +480,7 @@ void MQTT::publishQueuedMessages() publish(topic.c_str(), bytes, numBytes, false); -#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 +// #ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### if (moduleConfig.mqtt.json_enabled) { // handle json topic auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet); @@ -496,7 +496,7 @@ void MQTT::publishQueuedMessages() publish(topicJson.c_str(), jsonString.c_str(), false); } } -#endif // ARCH_NRF52 +// #endif // ARCH_NRF52 mqttPool.release(env); } } @@ -562,7 +562,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & publish(topic.c_str(), bytes, numBytes, false); -#ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 +// #ifndef ARCH_NRF52 // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ### if (moduleConfig.mqtt.json_enabled) { // handle json topic auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); @@ -573,7 +573,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & publish(topicJson.c_str(), jsonString.c_str(), false); } } -#endif // ARCH_NRF52 +// #endif // ARCH_NRF52 } else { LOG_INFO("MQTT not connected, queueing packet\n"); if (mqttQueue.numFree() == 0) { diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index e00dde024..227205d84 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -1,3 +1,4 @@ +#ifndef NRF52_USE_JSON #include "MeshPacketSerializer.h" #include "JSON.h" #include "NodeDB.h" @@ -353,4 +354,5 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa delete value; return jsonStr; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/serialization/MeshPacketSerializer_nRF52.cpp b/src/serialization/MeshPacketSerializer_nRF52.cpp new file mode 100644 index 000000000..7302e0915 --- /dev/null +++ b/src/serialization/MeshPacketSerializer_nRF52.cpp @@ -0,0 +1,325 @@ +#ifdef NRF52_USE_JSON +#warning 'Using nRF52 Serializer' + +#include "MeshPacketSerializer.h" +#include "ArduinoJson.h" +#include "NodeDB.h" +#include "mesh/generated/meshtastic/mqtt.pb.h" +#include "mesh/generated/meshtastic/telemetry.pb.h" +#include "modules/RoutingModule.h" +#include +#include +#include "mesh/generated/meshtastic/remote_hardware.pb.h" + +StaticJsonDocument<512> jsonObj; +// StaticJsonDocument<512> msgPayload; + +std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog) +{ + // the created jsonObj is immutable after creation, so + // we need to do the heavy lifting before assembling it. + std::string msgType; + // JSONObject jsonObj; + jsonObj.clear(); + + if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + // JSONObject msgPayload; + switch (mp->decoded.portnum) { + case meshtastic_PortNum_TEXT_MESSAGE_APP: { + msgType = "text"; + // convert bytes to string + if (shouldLog) + LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size); + + char payloadStr[(mp->decoded.payload.size) + 1]; + memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); + payloadStr[mp->decoded.payload.size] = 0; // null terminated string + // check if this is a JSON payload + StaticJsonDocument<512> text_doc; + DeserializationError error = deserializeJson(text_doc, payloadStr); + if (error) { + // if it isn't, then we need to create a json object + // with the string as the value + if (shouldLog) + LOG_INFO("text message payload is of type plaintext\n"); + jsonObj["payload"]["text"] = payloadStr; + // jsonObj["payload"] = msgPayload; + } else { + // if it is, then we can just use the json object + if (shouldLog) + LOG_INFO("text message payload is of type json\n"); + jsonObj["payload"] = text_doc; + } + break; + } + case meshtastic_PortNum_TELEMETRY_APP: { + msgType = "telemetry"; + meshtastic_Telemetry scratch; + meshtastic_Telemetry *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) { + decoded = &scratch; + if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) { + jsonObj["payload"]["battery_level"] = (unsigned int)decoded->variant.device_metrics.battery_level; + jsonObj["payload"]["voltage"] = decoded->variant.device_metrics.voltage; + jsonObj["payload"]["channel_utilization"] = decoded->variant.device_metrics.channel_utilization; + jsonObj["payload"]["air_util_tx"] = decoded->variant.device_metrics.air_util_tx; + jsonObj["payload"]["uptime_seconds"] = (unsigned int)decoded->variant.device_metrics.uptime_seconds; + } else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) { + jsonObj["payload"]["temperature"] = decoded->variant.environment_metrics.temperature; + jsonObj["payload"]["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity; + jsonObj["payload"]["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure; + jsonObj["payload"]["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance; + jsonObj["payload"]["voltage"] = decoded->variant.environment_metrics.voltage; + jsonObj["payload"]["current"] = decoded->variant.environment_metrics.current; + jsonObj["payload"]["lux"] = decoded->variant.environment_metrics.lux; + jsonObj["payload"]["white_lux"] = decoded->variant.environment_metrics.white_lux; + jsonObj["payload"]["iaq"] = (uint)decoded->variant.environment_metrics.iaq; + jsonObj["payload"]["wind_speed"] = decoded->variant.environment_metrics.wind_speed; + jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction; + jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust; + jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull; + } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { + jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard; + jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard; + jsonObj["payload"]["pm100"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_standard; + jsonObj["payload"]["pm10_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_environmental; + jsonObj["payload"]["pm25_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_environmental; + jsonObj["payload"]["pm100_e"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_environmental; + } else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) { + jsonObj["payload"]["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage; + jsonObj["payload"]["current_ch1"] = decoded->variant.power_metrics.ch1_current; + jsonObj["payload"]["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage; + jsonObj["payload"]["current_ch2"] = decoded->variant.power_metrics.ch2_current; + jsonObj["payload"]["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage; + jsonObj["payload"]["current_ch3"] = decoded->variant.power_metrics.ch3_current; + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for telemetry message!\n"); + } + break; + } + case meshtastic_PortNum_NODEINFO_APP: { + msgType = "nodeinfo"; + meshtastic_User scratch; + meshtastic_User *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) { + decoded = &scratch; + jsonObj["payload"]["id"] = decoded->id; + jsonObj["payload"]["longname"] = decoded->long_name; + jsonObj["payload"]["shortname"] = decoded->short_name; + jsonObj["payload"]["hardware"] = decoded->hw_model; + jsonObj["payload"]["role"] = (int)decoded->role; + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for nodeinfo message!\n"); + } + break; + } + case meshtastic_PortNum_POSITION_APP: { + msgType = "position"; + meshtastic_Position scratch; + meshtastic_Position *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) { + decoded = &scratch; + if ((int)decoded->time) { + jsonObj["payload"]["time"] = (unsigned int)decoded->time; + } + if ((int)decoded->timestamp) { + jsonObj["payload"]["timestamp"] = (unsigned int)decoded->timestamp; + } + jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i; + jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i; + if ((int)decoded->altitude) { + jsonObj["payload"]["altitude"] = (int)decoded->altitude; + } + if ((int)decoded->ground_speed) { + jsonObj["payload"]["ground_speed"] = (unsigned int)decoded->ground_speed; + } + if (int(decoded->ground_track)) { + jsonObj["payload"]["ground_track"] = (unsigned int)decoded->ground_track; + } + if (int(decoded->sats_in_view)) { + jsonObj["payload"]["sats_in_view"] = (unsigned int)decoded->sats_in_view; + } + if ((int)decoded->PDOP) { + jsonObj["payload"]["PDOP"] = (int)decoded->PDOP; + } + if ((int)decoded->HDOP) { + jsonObj["payload"]["HDOP"] = (int)decoded->HDOP; + } + if ((int)decoded->VDOP) { + jsonObj["payload"]["VDOP"] = (int)decoded->VDOP; + } + if ((int)decoded->precision_bits) { + jsonObj["payload"]["precision_bits"] = (int)decoded->precision_bits; + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for position message!\n"); + } + break; + } + case meshtastic_PortNum_WAYPOINT_APP: { + msgType = "position"; + meshtastic_Waypoint scratch; + meshtastic_Waypoint *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) { + decoded = &scratch; + jsonObj["payload"]["id"] = (unsigned int)decoded->id; + jsonObj["payload"]["name"] = decoded->name; + jsonObj["payload"]["description"] = decoded->description; + jsonObj["payload"]["expire"] = (unsigned int)decoded->expire; + jsonObj["payload"]["locked_to"] = (unsigned int)decoded->locked_to; + jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i; + jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i; + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for position message!\n"); + } + break; + } + case meshtastic_PortNum_NEIGHBORINFO_APP: { + msgType = "neighborinfo"; + meshtastic_NeighborInfo scratch; + meshtastic_NeighborInfo *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg, + &scratch)) { + decoded = &scratch; + jsonObj["payload"]["node_id"] = (unsigned int)decoded->node_id; + jsonObj["payload"]["node_broadcast_interval_secs"] = (unsigned int)decoded->node_broadcast_interval_secs; + jsonObj["payload"]["last_sent_by_id"] = (unsigned int)decoded->last_sent_by_id; + jsonObj["payload"]["neighbors_count"] = decoded->neighbors_count; + + JsonArray neighbors = jsonObj.createNestedArray("neighbors"); + JsonObject neighbors_0 = neighbors.createNestedObject(); + + for (uint8_t i = 0; i < decoded->neighbors_count; i++) { + neighbors_0["node_id"] = (unsigned int)decoded->neighbors[i].node_id; + neighbors_0["snr"] = (int)decoded->neighbors[i].snr; + neighbors_0.clear(); + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for neighborinfo message!\n"); + } + break; + } + case meshtastic_PortNum_TRACEROUTE_APP: { + if (mp->decoded.request_id) { // Only report the traceroute response + msgType = "traceroute"; + meshtastic_RouteDiscovery scratch; + meshtastic_RouteDiscovery *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg, + &scratch)) { + decoded = &scratch; + JsonArray route = jsonObj.createNestedArray("route"); + + route.add(mp->to); + for (uint8_t i = 0; i < decoded->route_count; i++) { + route.add(decoded->route[i]); + } + route.add(mp->from); // Ended at the original destination (source of response) + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for traceroute message!\n"); + } + } + break; + } + case meshtastic_PortNum_DETECTION_SENSOR_APP: { + msgType = "detection"; + char payloadStr[(mp->decoded.payload.size) + 1]; + memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); + payloadStr[mp->decoded.payload.size] = 0; // null terminated string + jsonObj["payload"]["text"] = payloadStr; + break; + } + case meshtastic_PortNum_REMOTE_HARDWARE_APP: { + meshtastic_HardwareMessage scratch; + meshtastic_HardwareMessage *decoded = NULL; + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_HardwareMessage_msg, + &scratch)) { + decoded = &scratch; + if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) { + msgType = "gpios_changed"; + jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value; + } else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) { + msgType = "gpios_read_reply"; + jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value; + jsonObj["payload"]["gpio_mask"] = (unsigned int)decoded->gpio_mask; + } + } else if (shouldLog) { + LOG_ERROR("Error decoding protobuf for RemoteHardware message!\n"); + } + break; + } + // add more packet types here if needed + default: + break; + } + } else if (shouldLog) { + LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n"); + } + + jsonObj["id"] = (unsigned int)mp->id; + jsonObj["timestamp"] = (unsigned int)mp->rx_time; + jsonObj["to"] = (unsigned int)mp->to; + jsonObj["from"] = (unsigned int)mp->from; + jsonObj["channel"] = (unsigned int)mp->channel; + jsonObj["type"] = msgType.c_str(); + jsonObj["sender"] = owner.id; + if (mp->rx_rssi != 0) + jsonObj["rssi"] = (int)mp->rx_rssi; + if (mp->rx_snr != 0) + jsonObj["snr"] = (float)mp->rx_snr; + if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { + jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit); + jsonObj["hop_start"] = (unsigned int)(mp->hop_start); + } + + // serialize and write it to the stream + + Serial.printf("serialized json message: \r\n"); + serializeJson(jsonObj, Serial); + Serial.println(""); + + std::string jsonStr = ""; + serializeJson(jsonObj, jsonStr); + + if (shouldLog) + LOG_INFO("serialized json message: %s\n", jsonStr.c_str()); + + return jsonStr; +} + +std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPacket *mp) +{ + jsonObj["id"] = (unsigned int)mp->id; + jsonObj["time_ms"] = (double)millis(); + jsonObj["timestamp"] = (unsigned int)mp->rx_time; + jsonObj["to"] = (unsigned int)mp->to; + jsonObj["from"] = (unsigned int)mp->from; + jsonObj["channel"] = (unsigned int)mp->channel; + jsonObj["want_ack"] = mp->want_ack; + + if (mp->rx_rssi != 0) + jsonObj["rssi"] = (int)mp->rx_rssi; + if (mp->rx_snr != 0) + jsonObj["snr"] = (float)mp->rx_snr; + if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { + jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit); + jsonObj["hop_start"] = (unsigned int)(mp->hop_start); + } + jsonObj["size"] = (unsigned int)mp->encrypted.size; + auto encryptedStr = bytesToHex(mp->encrypted.bytes, mp->encrypted.size); + jsonObj["bytes"] = encryptedStr.c_str(); + + // serialize and write it to the stream + std::string jsonStr = ""; + serializeJson(jsonObj, jsonStr); + + return jsonStr; +} +#endif \ No newline at end of file diff --git a/variants/rak4631_mqtt_json/platformio.ini b/variants/rak4631_mqtt_json/platformio.ini new file mode 100644 index 000000000..5d459a91a --- /dev/null +++ b/variants/rak4631_mqtt_json/platformio.ini @@ -0,0 +1,58 @@ +; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921 +[env:rak4631_mqtt_json] +extends = nrf52840_base +board = wiscore_rak4631 +board_check = true +build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631_mqtt_json -D RAK_4631 + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DEINK_DISPLAY_MODEL=GxEPD2_213_BN + -DEINK_WIDTH=250 + -DEINK_HEIGHT=122 + -DNRF52_USE_JSON=1 +; -DMESHTASTIC_EXCLUDE_GPS=1 + -DMESHTASTIC_EXCLUDE_WIFI=1 +; -DMESHTASTIC_EXCLUDE_SCREEN=1 + -DMESHTASTIC_EXCLUDE_PKI=1 + -DMESHTASTIC_EXCLUDE_POWER_FSM=1 +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631_mqtt_json> + + + +lib_deps = + ${nrf52840_base.lib_deps} + ${networking_base.lib_deps} + melopero/Melopero RV3028@^1.1.0 + https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2 + rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 + https://github.com/meshtastic/RAK12034-BMX160.git#4821355fb10390ba8557dc43ca29a023bcfbb9d9 + bblanchon/ArduinoJson @ 6.21.4 +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds +;upload_protocol = jlink + +; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!) +; programming time is about the same as the bootloader version. +; For information on this see the meshtastic developers documentation for "Development on the NRF52" +[env:rak4631_mqtt_json_dbg] +extends = env:rak4631 +board_level = extra + +; if the builtin version of openocd has a buggy version of semihosting, so use the external version +; platform_packages = platformio/tool-openocd@^3.1200.0 + +build_flags = + ${env:rak4631_mqtt_json.build_flags} + -D USE_SEMIHOSTING + +lib_deps = + ${env:rak4631_mqtt_json.lib_deps} + https://github.com/geeksville/Armduino-Semihosting.git#35b538fdf208c3530c1434cd099a08e486672ee4 + +; NOTE: the pyocd support for semihosting is buggy. So I switched to using the builtin platformio support for the stlink adapter which worked much better. +; However the built in openocd version in platformio has buggy support for TCP to semihosting. +; +; So I'm now trying the external openocd - but the openocd scripts for nrf52.cfg assume you are using a DAP adapter not an STLINK adapter. +; In theory I could change those scripts. But for now I'm trying going back to a DAP adapter but with the external openocd. + +upload_protocol = stlink +; eventually use platformio/tool-pyocd@^2.3600.0 instad +;upload_protocol = custom +;upload_command = pyocd flash -t nrf52840 $UPLOADERFLAGS $SOURCE \ No newline at end of file diff --git a/variants/rak4631_mqtt_json/variant.cpp b/variants/rak4631_mqtt_json/variant.cpp new file mode 100644 index 000000000..e84b60b3b --- /dev/null +++ b/variants/rak4631_mqtt_json/variant.cpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library 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 library 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 +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + // LED1 & LED2 + pinMode(PIN_LED1, OUTPUT); + ledOff(PIN_LED1); + + pinMode(PIN_LED2, OUTPUT); + ledOff(PIN_LED2); + + // 3V3 Power Rail + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); +} diff --git a/variants/rak4631_mqtt_json/variant.h b/variants/rak4631_mqtt_json/variant.h new file mode 100644 index 000000000..bc5541336 --- /dev/null +++ b/variants/rak4631_mqtt_json/variant.h @@ -0,0 +1,273 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library 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 library 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 +*/ + +#ifndef _VARIANT_RAK4630_ +#define _VARIANT_RAK4630_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (35) +#define PIN_LED2 (36) + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 + +#define LED_STATE_ON 1 // State when LED is litted + +/* + * Buttons + */ + +#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion +#define BUTTON_NEED_PULLUP +#define PIN_BUTTON2 12 +#define PIN_BUTTON3 24 +#define PIN_BUTTON4 25 + +/* + * Analog pins + */ +#define PIN_A0 (5) +#define PIN_A1 (31) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (30) +#define PIN_A5 (31) +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; +static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Other pins +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + +static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +// Connected to Jlink CDC +#define PIN_SERIAL2_RX (8) +#define PIN_SERIAL2_TX (6) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (45) +#define PIN_SPI_MOSI (44) +#define PIN_SPI_SCK (43) + +#define PIN_SPI1_MISO (29) // (0 + 29) +#define PIN_SPI1_MOSI (30) // (0 + 30) +#define PIN_SPI1_SCK (3) // (0 + 3) + +static const uint8_t SS = 42; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +/* + * eink display pins + */ + +#define PIN_EINK_CS (0 + 26) +#define PIN_EINK_BUSY (0 + 4) +#define PIN_EINK_DC (0 + 17) +#define PIN_EINK_RES (-1) +#define PIN_EINK_SCLK (0 + 3) +#define PIN_EINK_MOSI (0 + 30) // also called SDI + +// #define USE_EINK + +// RAKRGB +#define HAS_NCP5623 + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (13) +#define PIN_WIRE_SCL (14) + +// QSPI Pins +#define PIN_QSPI_SCK 3 +#define PIN_QSPI_CS 26 +#define PIN_QSPI_IO0 30 +#define PIN_QSPI_IO1 29 +#define PIN_QSPI_IO2 28 +#define PIN_QSPI_IO3 2 + +// On-board QSPI Flash +#define EXTERNAL_FLASH_DEVICES IS25LP080D +#define EXTERNAL_FLASH_USE_QSPI + +/* @note RAK5005-O GPIO mapping to RAK4631 GPIO ports + RAK5005-O <-> nRF52840 + IO1 <-> P0.17 (Arduino GPIO number 17) + IO2 <-> P1.02 (Arduino GPIO number 34) + IO3 <-> P0.21 (Arduino GPIO number 21) + IO4 <-> P0.04 (Arduino GPIO number 4) + IO5 <-> P0.09 (Arduino GPIO number 9) + IO6 <-> P0.10 (Arduino GPIO number 10) + IO7 <-> P0.28 (Arduino GPIO number 28) + SW1 <-> P0.01 (Arduino GPIO number 1) + A0 <-> P0.04/AIN2 (Arduino Analog A2 + A1 <-> P0.31/AIN7 (Arduino Analog A7 + SPI_CS <-> P0.26 (Arduino GPIO number 26) + */ + +// RAK4630 LoRa module + +/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ ) + +P1.10 NSS SPI NSS (Arduino GPIO number 42) +P1.11 SCK SPI CLK (Arduino GPIO number 43) +P1.12 MOSI SPI MOSI (Arduino GPIO number 44) +P1.13 MISO SPI MISO (Arduino GPIO number 45) +P1.14 BUSY BUSY signal (Arduino GPIO number 46) +P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47) +P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38) + +Important for successful SX1262 initialization: + +* Setup DIO2 to control the antenna switch +* Setup DIO3 to control the TCXO power supply +* Setup the SX1262 to use it's DCDC regulator and not the LDO +* RAK4630 schematics show GPIO P1.07 connected to the antenna switch, but it should not be initialized, as DIO2 will do the +control of the antenna switch + +SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG + +*/ + +#define DETECTION_SENSOR_EN 4 + +#define USE_SX1262 +#define SX126X_CS (42) +#define SX126X_DIO1 (47) +#define SX126X_BUSY (46) +#define SX126X_RESET (38) +// #define SX126X_TXEN (39) +// #define SX126X_RXEN (37) +#define SX126X_POWER_EN (37) +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// Testing USB detection +#define NRF_APM + +// enables 3.3V periphery like GPS or IO Module +// Do not toggle this for GPS power savings +#define PIN_3V3_EN (34) + +// RAK1910 GPS module +// If using the wisblock GPS module and pluged into Port A on WisBlock base +// IO1 is hooked to PPS (pin 12 on header) = gpio 17 +// IO2 is hooked to GPS RESET = gpio 34, but it can not be used to this because IO2 is ALSO used to control 3V3_S power (1 is on). +// Therefore must be 1 to keep peripherals powered +// Power is on the controllable 3V3_S rail +// #define PIN_GPS_RESET (34) +// #define PIN_GPS_EN PIN_3V3_EN +#define PIN_GPS_PPS (17) // Pulse per second input from the GPS + +#define GPS_RX_PIN PIN_SERIAL1_RX +#define GPS_TX_PIN PIN_SERIAL1_TX + +// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press + +// RAK12002 RTC Module +#define RV3028_RTC (uint8_t)0b1010010 + +// RAK18001 Buzzer in Slot C +// #define PIN_BUZZER 21 // IO3 is PWM2 +// NEW: set this via protobuf instead! + +// Battery +// The battery sense is hooked to pin A0 (5) +#define BATTERY_PIN PIN_A0 +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.73 + +#define HAS_RTC 1 + +#define HAS_ETHERNET 1 + +#define RAK_4631 1 + +#define PIN_ETHERNET_RESET 21 +#define PIN_ETHERNET_SS PIN_EINK_CS +#define ETH_SPI_PORT SPI1 +#define AQ_SET_PIN 10 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif \ No newline at end of file