This is my code for controlling servo motor using distance sensor in this case JSN-SR04T.
The technology I am using is LoRaWAN. And here is a breakdown of my code and what I require help in.
What does my code do? How does it connect through LoRaWAN?
TLDR my code sends an uplink message to chirpstack network server, however the uplink message and it requires me to receive a downlink payload containing stuff like rssi from the chirpstack network server. So that my data payload is received by chirpstack, and can be transported to an application server such as thingsboard.
My code works fine, but I am having trouble sending payload to the chirpstack network server to be recorded before the servo motor turns. So the idea is when a low distance is detected I want to send a uplink message and receive a downlink payload before it activates the servo motor.
code
#include "Arduino.h" // Include the Arduino core library
#include <WiFi.h> // Include the WiFi library
#include "images.h" // Include custom images header file
#include <LoRaWan_APP.h> // Include the LoRaWAN application library
#include <Wire.h> // Include the Wire library for I2C communication
#include "HT_SSD1306Wire.h" // Include the SSD1306 OLED library
#include <AsyncDelay.h> // Include the AsyncDelay library for non-blocking delays
#include <jsnsr04t.h> // Include the JSN-SR04T ultrasonic sensor library
#include <ESP32Servo.h> // Include the ESP32 Servo library
#include <WiFiClientSecure.h> // Include the WiFiClientSecure library for HTTPS connections
#include <UniversalTelegramBot.h> // Include the UniversalTelegramBot library for Telegram bot
// WIFI parameters
const char* ssid = "CEPL"; // WiFi SSID
const char* password = "CEPL2014"; // WiFi password
String site = "27 Mandai Estate #04-01 Meeting Room"; // Site description
// ------- Telegram configuration --------
#define BOT_TOKEN "693031724:AAGqHc0pQk_d3dIbRDrZAtqqJRF5EXCckVE" // Telegram bot token from Botfather
#define CHAT_ID "-1001183332514" // Telegram chat ID for the SG Support Channel
// OTAA parameters for LoRaWAN
uint8_t devEui[] = { 0x22, 0x32, 0x33, 0x00, 0x00, 0x88, 0x88, 0x02 }; // Device EUI
uint8_t appEui[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Application EUI
uint8_t appKey[] = { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x66, 0x01 }; // Application key
uint8_t nwkSKey[] = { 0x15, 0xb1, 0xd0, 0xef, 0xa4, 0x63, 0xdf, 0xbe, 0x3d, 0x11, 0x18, 0x1e, 0x1e, 0xc7, 0xda, 0x85 }; // Network session key
uint8_t appSKey[] = { 0xd7, 0x2c, 0x78, 0x75, 0x8c, 0xdc, 0xca, 0xbf, 0x55, 0xee, 0x4a, 0x77, 0x8d, 0x16, 0xef, 0x67 }; // Application session key
uint32_t devAddr = 0x007e6ae1; // Device address
// LoRa parameters
#define RF_FREQUENCY 923000000 // LoRa frequency in Hz
#define TX_OUTPUT_POWER 10 // LoRa transmit power in dBm
#define LORA_BANDWIDTH 0 // LoRa bandwidth (0: 125 kHz)
#define LORA_SPREADING_FACTOR 7 // LoRa spreading factor (7..12)
#define LORA_CODINGRATE 1 // LoRa coding rate (1: 4/5)
#define LORA_PREAMBLE_LENGTH 8 // LoRa preamble length
#define LORA_SYMBOL_TIMEOUT 0 // LoRa symbol timeout
#define LORA_FIX_LENGTH_PAYLOAD_ON false // Fixed length payload
#define LORA_IQ_INVERSION_ON false // LoRa IQ inversion
#define RX_TIMEOUT_VALUE 1000 // Receive timeout value
#define BUFFER_SIZE 30 // Payload buffer size
WiFiClientSecure espClient; // Secure WiFi client for HTTPS
UniversalTelegramBot bot(BOT_TOKEN, espClient); // Initialize Telegram bot
char txpacket[BUFFER_SIZE]; // Transmit packet buffer
char rxpacket[BUFFER_SIZE]; // Receive packet buffer
static RadioEvents_t RadioEvents; // Radio events structure
void OnTxDone(void); // On transmit done event handler
void OnTxTimeout(void); // On transmit timeout event handler
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); // On receive done event handler
typedef enum {
LOWPOWER,
STATE_RX,
STATE_TX
} States_t; // LoRa states enumeration
int16_t txNumber; // Transmit packet number
int16_t rxNumber; // Receive packet number
States_t state; // Current state
bool sleepMode = false; // Sleep mode flag
int16_t Rssi, rxSize; // RSSI and receive size
String rssi = "RSSI --"; // RSSI string for display
String packSize = "--"; // Packet size string for display
String packet; // Packet string
String send_num; // Send number string
unsigned int counter = 0; // Counter
bool receiveflag = false; // LoRa receiver flag
long lastSendTime = 0; // Last send time
int interval = 1000; // Interval between sends
uint64_t chipid; // ESP32 chip ID
int16_t RssiDetection = 0; // RSSI detection
// LoRa event handlers
void OnTxDone(void) {
Serial.print("TX done......"); // Print message on transmit done
state = STATE_RX; // Set state to receive
}
void OnTxTimeout(void) {
Radio.Sleep(); // Put radio to sleep on timeout
Serial.print("TX Timeout......"); // Print message on transmit timeout
state = STATE_TX; // Set state to transmit
}
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) {
rxNumber++; // Increment receive packet number
Rssi = rssi; // Update RSSI value
rxSize = size; // Update receive size
memcpy(rxpacket, payload, size); // Copy payload to receive packet buffer
rxpacket[size] = '\0'; // Null-terminate the receive packet
Radio.Sleep(); // Put radio to sleep
Serial.printf("\r\nreceived packet \"%s\" with Rssi %d , length %d\r\n", rxpacket, Rssi, rxSize); // Print received packet info
Serial.println("wait to send next packet"); // Print wait message
receiveflag = true; // Set receive flag
state = STATE_TX; // Set state to transmit
// Check if the received packet contains the reset command
if (size == 1 && payload[0] == 0x01) {
Serial.println("Reset command received. Triggering soft reset..."); // Print reset message
delay(1000); // Wait for message to be fully processed
ESP.restart(); // Perform soft reset
}
}
void lora_init(void) {
Mcu.begin(HELTEC_BOARD, SLOW_CLK_TPYE); // Initialize MCU with Heltec board and slow clock type
txNumber = 0; // Initialize transmit packet number
Rssi = 0; // Initialize RSSI
rxNumber = 0; // Initialize receive packet number
RadioEvents.TxDone = OnTxDone; // Set transmit done event handler
RadioEvents.TxTimeout = OnTxTimeout; // Set transmit timeout event handler
RadioEvents.RxDone = OnRxDone; // Set receive done event handler
Radio.Init(&RadioEvents); // Initialize radio with event handlers
Radio.SetChannel(RF_FREQUENCY); // Set radio channel
Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000); // Set radio transmit configuration
Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true); // Set radio receive configuration
state = STATE_TX; // Set state to transmit
}
// OLED display
SSD1306Wire factory_display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // Initialize OLED display
// LoRaWAN parameters
uint16_t userChannelsMask[6] = { 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 }; // Channel mask
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION; // LoRaWAN region
DeviceClass_t loraWanClass = CLASS_C; // Device class
uint32_t appTxDutyCycle = 15000; // Application transmit duty cycle
bool overTheAirActivation = true; // Over-the-air activation flag
bool loraWanAdr = true; // Adaptive data rate flag
bool isTxConfirmed = true; // Confirmed transmission flag
uint8_t appPort = 2; // Application port
uint8_t confirmedNbTrials = 4; // Number of confirmed transmission trials
// JSN-SR04T Integration
#define ECHO_PIN 45 // Echo pin for ultrasonic sensor
#define TRIGGER_PIN 46 // Trigger pin for ultrasonic sensor
JsnSr04T ultrasonicSensor(ECHO_PIN, TRIGGER_PIN, LOG_LEVEL_VERBOSE); // Initialize JSN-SR04T sensor
AsyncDelay measureDelay; // Initialize async delay for non-blocking measurements
// Servo motor integration
#define SERVO_PIN1 47 // Servo motor 1 pin
#define SERVO_PIN2 48 // Servo motor 2 pin
Servo servo1; // Initialize servo motor 1
Servo servo2; // Initialize servo motor 2
static void prepareTxFrame(uint8_t port) {
appDataSize = 8; // Increase the payload size to 8 bytes
// Original data
appData[0] = 'A';
appData[1] = 'B';
appData[2] = 'C';
appData[3] = 'D';
// Get the distance measurement
int distance = ultrasonicSensor.readDistance();
// Convert the distance to bytes and fill the payload
appData[4] = (distance >> 8) & 0xFF;
appData[5] = distance & 0xFF;
}
String LoRa_data; // LoRa data string
uint16_t num = 0; // Number of received packets
bool LoRaDownLink = false; // LoRa downlink flag
uint32_t LoRadonwlinkTime; // LoRa downlink time
void downLinkDataHandle(McpsIndication_t *mcpsIndication) {
LoRa_data = ""; // Clear LoRa data string
Serial.printf("+REV DATA:%s,RXSIZE %d,PORT %d\r\n", mcpsIndication->RxSlot ? "RXWIN2" : "RXWIN1", mcpsIndication->BufferSize, mcpsIndication->Port); // Print received data info
Serial.print("+REV DATA:");
for (uint8_t i = 0; i < mcpsIndication->BufferSize; i++) {
Serial.printf("%d", mcpsIndication->Buffer[i]); // Print received buffer data
LoRa_data = LoRa_data + (String)(char)mcpsIndication->Buffer[i]; // Append received buffer data to LoRa data string
}
LoRaDownLink = true; // Set LoRa downlink flag
LoRadonwlinkTime = millis(); // Update downlink time
num++; // Increment number of received packets
Serial.println();
Serial.print(num);
Serial.print(":");
Serial.println(LoRa_data); // Print received LoRa data
// Check if the received packet contains the reset command
if (mcpsIndication->Port == 99 && mcpsIndication->BufferSize == 1 && mcpsIndication->Buffer[0] == 0x01) {
Serial.println("Reset command received. Triggering soft reset..."); // Print reset message
delay(1000); // Wait for message to be fully processed
ESP.restart(); // Perform soft reset
}
}
void logo() {
factory_display.clear(); // Clear OLED display
factory_display.drawXbm(0, 5, logo_width, logo_height, (const unsigned char *)logo_bits); // Draw logo on OLED display
factory_display.display(); // Update OLED display
}
void WIFISetUp(void) {
Serial.begin(115200); // Begin serial communication at 115200 baud
while (!Serial); // Wait for serial communication to begin
Mcu.begin(HELTEC_BOARD, SLOW_CLK_TPYE); // Initialize MCU with Heltec board and slow clock type
Serial.println();
Serial.println("Booting Sketch..."); // Print boot message
WiFi.disconnect(true); // Disconnect WiFi
delay(100); // Wait for disconnection
WiFi.mode(WIFI_STA); // Set WiFi mode to station
WiFi.setAutoReconnect(true); // Enable auto reconnect
WiFi.begin("CEPL", "CEPL2014"); // Begin WiFi connection
delay(100); // Wait for connection
byte count = 0; // Initialize retry count
while (WiFi.status() != WL_CONNECTED && count < 10) { // Retry connection 10 times
count++; // Increment retry count
delay(500); // Wait between retries
factory_display.drawString(0, 0, "Connecting..."); // Display connecting message
factory_display.display(); // Update display
}
factory_display.clear(); // Clear OLED display
if (WiFi.status() == WL_CONNECTED) { // If connected
factory_display.drawString(0, 0, "Connecting...OK."); // Display connection success message
factory_display.display(); // Update display
// delay(500);
} else { // If connection failed
factory_display.clear(); // Clear OLED display
factory_display.drawString(0, 0, "Connecting...Failed"); // Display connection failure message
factory_display.display(); // Update display
// while(1);
}
factory_display.drawString(0, 10, "WIFI Setup done"); // Display WiFi setup done message
factory_display.display(); // Update display
delay(500); // Wait
}
void WIFIScan(unsigned int value) {
unsigned int i; // Loop counter
WiFi.mode(WIFI_STA); // Set WiFi mode to station
for (i = 0; i < value; i++) { // Repeat scan value times
factory_display.drawString(0, 20, "Scan start..."); // Display scan start message
factory_display.display(); // Update display
int n = WiFi.scanNetworks(); // Scan WiFi networks
factory_display.drawString(0, 30, "Scan done"); // Display scan done message
factory_display.display(); // Update display
delay(500); // Wait
factory_display.clear(); // Clear OLED display
if (n == 0) { // If no networks found
factory_display.clear(); // Clear OLED display
factory_display.drawString(0, 0, "no network found"); // Display no network found message
factory_display.display(); // Update display
// while(1);
} else { // If networks found
factory_display.drawString(0, 0, (String)n); // Display number of networks found
factory_display.drawString(14, 0, "networks found:"); // Display networks found message
factory_display.display(); // Update display
delay(500); // Wait
for (int i = 0; i < n; ++i) { // Loop through networks
// Print SSID and RSSI for each network found
factory_display.drawString(0, (i + 1) * 9, (String)(i + 1)); // Display network number
factory_display.drawString(6, (i + 1) * 9, ":"); // Display colon
factory_display.drawString(12, (i + 1) * 9, (String)(WiFi.SSID(i))); // Display SSID
factory_display.drawString(90, (i + 1) * 9, " ("); // Display open parenthesis
factory_display.drawString(98, (i + 1) * 9, (String)(WiFi.RSSI(i))); // Display RSSI
factory_display.drawString(114, (i + 1) * 9, ")"); // Display close parenthesis
// factory_display.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
delay(10); // Wait
}
}
factory_display.display(); // Update display
delay(800); // Wait
factory_display.clear(); // Clear OLED display
}
}
bool resendflag = false; // Resend flag
bool deepsleepflag = false; // Deep sleep flag
bool interrupt_flag = false; // Interrupt flag
void interrupt_GPIO0() {
interrupt_flag = true; // Set interrupt flag
}
void interrupt_handle(void) {
if (interrupt_flag) { // If interrupt flag is set
interrupt_flag = false; // Clear interrupt flag
if (digitalRead(0) == 0) { // If GPIO0 is low
if (rxNumber <= 2) { // If receive packet number is less than or equal to 2
resendflag = true; // Set resend flag
} else {
deepsleepflag = true; // Set deep sleep flag
}
}
}
}
void VextON(void) {
pinMode(Vext, OUTPUT); // Set Vext pin as output
digitalWrite(Vext, LOW); // Set Vext pin low
}
void VextOFF(void) { // Vext default OFF
pinMode(Vext, OUTPUT); // Set Vext pin as output
digitalWrite(Vext, HIGH); // Set Vext pin high
}
void sendTelegramMessage() {
espClient.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
String message = String("Device started") + " @ " + site; // Create start message
bot.sendMessage(CHAT_ID, message, "Markdown"); // Send start message to Telegram
Serial.println("Device started"); // Print start message
}
void sendTelegramMessagePest() {
loraConnect(); // Connect to LoRa
espClient.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
String message = String("Pest Detected") + " @ " + site; // Create pest detected message
Serial.println("Pest Detected"); // Print pest detected message
bot.sendMessage(CHAT_ID, message, "Markdown"); // Send pest detected message to Telegram
}
void setup() {
Serial.begin(115200); // Begin serial communication at 115200 baud
VextON(); // Turn on Vext
delay(100); // Wait
factory_display.init(); // Initialize OLED display
factory_display.clear(); // Clear OLED display
factory_display.display(); // Update OLED display
logo(); // Display logo
delay(300); // Wait
factory_display.clear(); // Clear OLED display
WIFISetUp(); // Set up WiFi
WiFi.disconnect(); // Disconnect WiFi
WiFi.mode(WIFI_STA); // Set WiFi mode to station
delay(100); // Wait
WIFIScan(1); // Scan WiFi networks
chipid = ESP.getEfuseMac(); // Get ESP32 chip ID
Serial.printf("ESP32ChipID=%04X", (uint16_t)(chipid >> 32)); // Print high 4 bytes of chip ID
Serial.printf("%08X\n", (uint32_t)chipid); // Print low 4 bytes of chip ID
attachInterrupt(0, interrupt_GPIO0, FALLING); // Attach interrupt to GPIO0
lora_init(); // Initialize LoRa
packet = "waiting lora data!"; // Set initial packet message
factory_display.drawString(0, 10, packet); // Display packet message
factory_display.display(); // Update OLED display
delay(100); // Wait
factory_display.clear(); // Clear OLED display
pinMode(LED, OUTPUT); // Set LED pin as output
digitalWrite(LED, LOW); // Turn off LED
// Initialize JSN-SR04T sensor
ultrasonicSensor.begin(Serial); // Begin ultrasonic sensor with serial communication
measureDelay.start(60000, AsyncDelay::MILLIS); // Start async delay for measurements
// Initialize servos
servo1.attach(SERVO_PIN1); // Attach servo 1 to pin
servo2.attach(SERVO_PIN2); // Attach servo 2 to pin
servo1.write(90); // Set servo 1 to neutral position
servo2.write(90); // Set servo 2 to neutral position
Serial.println("Connecting to WiFi"); // Print WiFi connection message
WiFi.begin(ssid, password); // Begin WiFi connection
while (WiFi.status() != WL_CONNECTED) { // While not connected
delay(500); // Wait
Serial.print("."); // Print dot
}
Serial.println("\nWiFi connected"); // Print WiFi connected message
Serial.print("IP address: "); // Print IP address message
Serial.println(WiFi.localIP()); // Print IP address
sendTelegramMessage(); // Send Telegram start message
servo1.write(0); // Move servo 1 backward
Serial.println("Backward"); // Print backward message
delay(140); // Wait
servo1.write(90); // Set servo 1 to neutral position
Serial.println("Stall"); // Print stall message
delay(10000); // Wait
}
void loraConnect(void) {
switch (deviceState) { // Switch based on device state
case DEVICE_STATE_INIT: { // If initializing
#if (LORAWAN_DEVEUI_AUTO)
LoRaWAN.generateDeveuiByChipID(); // Generate device EUI by chip ID
#endif
LoRaWAN.init(loraWanClass, loraWanRegion); // Initialize LoRaWAN
LoRaWAN.setDefaultDR(3); // Set default data rate
break;
}
case DEVICE_STATE_JOIN: { // If joining
LoRaWAN.join(); // Join LoRaWAN network
break;
}
case DEVICE_STATE_SEND: { // If sending
prepareTxFrame(appPort); // Prepare the frame with updated payload
LoRaWAN.send(); // Send LoRaWAN frame
deviceState = DEVICE_STATE_CYCLE; // Set state to cycle
break;
}
case DEVICE_STATE_CYCLE: { // If cycling
txDutyCycleTime = appTxDutyCycle + randr(-APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND); // Calculate transmit duty cycle time
LoRaWAN.cycle(txDutyCycleTime); // Set LoRaWAN cycle
deviceState = DEVICE_STATE_SLEEP; // Set state to sleep
break;
}
case DEVICE_STATE_SLEEP: { // If sleeping
LoRaWAN.sleep(loraWanClass); // Set LoRaWAN to sleep
break;
}
default: { // Default case
deviceState = DEVICE_STATE_INIT; // Set state to initialize
break;
}
}
}
void loop() {
interrupt_handle(); // Handle interrupts
loraConnect(); // Connect to LoRa
interrupt_handle(); // Handle interrupts
if (deepsleepflag) { // If deep sleep flag is set
VextOFF(); // Turn off Vext
Radio.Sleep(); // Put radio to sleep
SPI.end(); // End SPI communication
pinMode(RADIO_DIO_1, ANALOG); // Set radio DIO 1 to analog
pinMode(RADIO_NSS, ANALOG); // Set radio NSS to analog
pinMode(RADIO_RESET, ANALOG); // Set radio reset to analog
pinMode(RADIO_BUSY, ANALOG); // Set radio busy to analog
pinMode(LORA_CLK, ANALOG); // Set LoRa clock to analog
pinMode(LORA_MISO, ANALOG); // Set LoRa MISO to analog
pinMode(LORA_MOSI, ANALOG); // Set LoRa MOSI to analog
esp_sleep_enable_timer_wakeup(600 * 1000 * (uint64_t)1000); // Enable timer wakeup for deep sleep
esp_deep_sleep_start(); // Start deep sleep
}
if (resendflag) { // If resend flag is set
state = STATE_TX; // Set state to transmit
resendflag = false; // Clear resend flag
}
if (receiveflag && (state == LOWPOWER)) { // If receive flag is set and state is low power
receiveflag = false; // Clear receive flag
packet = "R_data:"; // Set packet message
int i = 0;
while (i < rxSize) { // Loop through received packet
packet += rxpacket[i]; // Append received packet data to packet message
i++;
}
packSize = "R_Size: "; // Set packet size message
packSize += String(rxSize, DEC); // Append packet size to message
packSize += " R_rssi: "; // Append RSSI message
packSize += String(Rssi, DEC); // Append RSSI value to message
send_num = "send num: "; // Set send number message
send_num += String(txNumber, DEC); // Append send number to message
factory_display.drawString(0, 10, packet); // Display packet message
factory_display.drawString(0, 20, packSize); // Display packet size message
factory_display.drawString(0, 50, send_num); // Display send number message
factory_display.display(); // Update OLED display
delay(10); // Wait
factory_display.clear(); // Clear OLED display
if ((rxNumber % 2) == 0) { // If receive packet number is even
digitalWrite(LED, HIGH); // Turn on LED
}
}
switch (state) { // Switch based on state
case STATE_TX:
delay(1000); // Wait
txNumber++; // Increment transmit packet number
sprintf(txpacket, "start %d,Rssi:%d", txNumber, Rssi); // Create transmit packet message
Serial.printf("\r\nsending packet \"%s\" , length %d\r\n", txpacket, strlen(txpacket)); // Print transmit packet message
Radio.Send((uint8_t *)txpacket, strlen(txpacket)); // Send transmit packet
state = LOWPOWER; // Set state to low power
break;
case STATE_RX:
Serial.println("into RX mode"); // Print receive mode message
Radio.Rx(0); // Set radio to receive mode
state = LOWPOWER; // Set state to low power
break;
case LOWPOWER:
Radio.IrqProcess(); // Process radio interrupts
break;
default:
break;
}
// JSN-SR04T Measurement
if (measureDelay.isExpired()) { // If measurement delay is expired
int distance = ultrasonicSensor.readDistance(); // Get distance measurement
Serial.printf("Distance: %d cm\n", distance); // Print distance measurement
if (distance < 21) { // If distance is less than 21 cm
sendTelegramMessagePest(); // Send Telegram pest detected message
servo1.write(180); // Move servo 1 forward
Serial.println("Forward"); // Print forward message
delay(120); // Wait
servo1.write(90); // Set servo 1 to neutral position
Serial.println("Done"); // Print done message
delay(300000); // Wait
}
measureDelay.repeat(); // Repeat measurement delay
}
}
If anyone can help me, that will be great. Hope to hear your speedy reply. Thanks for taking a look at my problem.